From 0b8272efce1ff662baa6b4abdcf9a981b7605985 Mon Sep 17 00:00:00 2001 From: Simon Xue Date: Wed, 11 Mar 2020 17:23:13 +0800 Subject: [PATCH] soc: rockchip: hw-decompress: Add support rockchip hardware decompress Change-Id: Ib882919642a6c72b0292292bc0fd35d7a3d78dee Signed-off-by: Simon Xue Signed-off-by: Tao Huang --- drivers/soc/rockchip/Kconfig | 6 + drivers/soc/rockchip/Makefile | 1 + drivers/soc/rockchip/rockchip_decompress.c | 239 +++++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 drivers/soc/rockchip/rockchip_decompress.c diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index bd5d593a9615..f84187ddfc04 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -34,6 +34,12 @@ config ROCKCHIP_GRF In a lot of cases there also need to be default settings initialized to make some of them conform to expectations of the kernel. +config ROCKCHIP_HW_DECOMPRESS + bool "Rockchip HardWare Decompress Support" + help + This driver support Decompress IP built-in Rockchip SoC, support + LZ4, GZIP, ZLIB. + config ROCKCHIP_IPA bool "Rockchip IPA support" depends on THERMAL && OF diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index 88c893e1dd77..445d794739d6 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_ROCKCHIP_CPUINFO) += rockchip-cpuinfo.o obj-$(CONFIG_ROCKCHIP_GRF) += grf.o +obj-$(CONFIG_ROCKCHIP_HW_DECOMPRESS) += rockchip_decompress.o obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o obj-$(CONFIG_FIQ_DEBUGGER) += rk_fiq_debugger.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += sdmmc_vendor_storage.o diff --git a/drivers/soc/rockchip/rockchip_decompress.c b/drivers/soc/rockchip/rockchip_decompress.c new file mode 100644 index 000000000000..8ec1f8cffb77 --- /dev/null +++ b/drivers/soc/rockchip/rockchip_decompress.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Rockchip Electronics Co., Ltd + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DECOM_CTRL 0x0 +#define DECOM_ENR 0x4 +#define DECOM_RADDR 0x8 +#define DECOM_WADDR 0xc +#define DECOM_UDDSL 0x10 +#define DECOM_UDDSH 0x14 +#define DECOM_TXTHR 0x18 +#define DECOM_RXTHR 0x1c +#define DECOM_SLEN 0x20 +#define DECOM_STAT 0x24 +#define DECOM_ISR 0x28 +#define DECOM_IEN 0x2c +#define DECOM_AXI_STAT 0x30 +#define DECOM_TSIZEL 0x34 +#define DECOM_TSIZEH 0x38 +#define DECOM_MGNUM 0x3c +#define DECOM_FRAME 0x40 +#define DECOM_DICTID 0x44 +#define DECOM_CSL 0x48 +#define DECOM_CSH 0x4c + +#define LZ4_HEAD_CSUM_CHECK_EN BIT(1) +#define LZ4_BLOCK_CSUM_CHECK_EN BIT(2) +#define LZ4_CONT_CSUM_CHECK_EN BIT(3) + +#define DSOLIEN BIT(19) +#define ZDICTEIEN BIT(18) +#define GCMEIEN BIT(17) +#define GIDEIEN BIT(16) +#define CCCEIEN BIT(15) +#define BCCEIEN BIT(14) +#define HCCEIEN BIT(13) +#define CSEIEN BIT(12) +#define DICTEIEN BIT(11) +#define VNEIEN BIT(10) +#define WNEIEN BIT(9) +#define RDCEIEN BIT(8) +#define WRCEIEN BIT(7) +#define DISEIEN BIT(6) +#define LENEIEN BIT(5) +#define LITEIEN BIT(4) +#define SQMEIEN BIT(3) +#define SLCIEN BIT(2) +#define HDEIEN BIT(1) +#define DSIEN BIT(0) + +#define DECOM_STOP BIT(0) +#define DECOM_COMPLETE BIT(0) +#define DECOM_GZIP_MODE BIT(4) +#define DECOM_ZLIB_MODE BIT(5) +#define DECOM_DEFLATE_MODE BIT(0) + +#define DECOM_ENABLE 0x1 +#define DECOM_DISABLE 0x0 + +#define DECOM_INT_MASK \ + (DSOLIEN | ZDICTEIEN | GCMEIEN | GIDEIEN | \ + CCCEIEN | BCCEIEN | HCCEIEN | CSEIEN | \ + DICTEIEN | VNEIEN | WNEIEN | RDCEIEN | WRCEIEN | \ + DISEIEN | LENEIEN | LITEIEN | SQMEIEN | SLCIEN | \ + HDEIEN | DSIEN) + +enum decom_mod { + LZ4_MOD, + GZIP_MOD, + ZLIB_MOD, +}; + +struct rk_decom { + struct device *dev; + int irq; + int num_clocks; + struct clk_bulk_data *clocks; + void __iomem *regs; + phys_addr_t mem_start; + size_t mem_size; +}; + +static DECLARE_WAIT_QUEUE_HEAD(initrd_decom_done); +static bool initrd_continue; + +void __init wait_initrd_hw_decom_done(void) +{ + wait_event(initrd_decom_done, initrd_continue); +} + +static irqreturn_t rk_decom_irq_handler(int irq, void *priv) +{ + struct rk_decom *rk_dec = priv; + u32 irq_status; + u32 decom_status; + + irq_status = readl(rk_dec->regs + DECOM_ISR); + /* clear interrupts */ + writel(irq_status, rk_dec->regs + DECOM_ISR); + if (irq_status & DECOM_STOP) { + decom_status = readl(rk_dec->regs + DECOM_STAT); + if (decom_status & DECOM_COMPLETE) { + initrd_continue = true; + wake_up(&initrd_decom_done); + dev_info(rk_dec->dev, "decom completed\n"); + } else { + dev_info(rk_dec->dev, + "decom failed, irq_status = 0x%x, decom_status = 0x%x\n", + irq_status, decom_status); + } + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t rk_decom_irq_thread(int irq, void *priv) +{ + struct rk_decom *rk_dec = priv; + + if (initrd_continue) { + void *start, *end; + + /* + * Now it is safe to free reserve memory that + * store the origin ramdisk file + */ + start = phys_to_virt(rk_dec->mem_start); + end = start + rk_dec->mem_size; + free_reserved_area(start, end, -1, "ramdisk gzip archive"); + } + + clk_bulk_disable_unprepare(rk_dec->num_clocks, rk_dec->clocks); + + return IRQ_HANDLED; +} + +static int rockchip_decom_probe(struct platform_device *pdev) +{ + struct rk_decom *rk_dec; + struct resource *res = NULL; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *mem; + struct resource reg; + int ret = 0; + + rk_dec = devm_kzalloc(dev, sizeof(*rk_dec), GFP_KERNEL); + if (!rk_dec) + return -ENOMEM; + + rk_dec->dev = dev; + rk_dec->irq = platform_get_irq(pdev, 0); + if (rk_dec->irq < 0) { + dev_err(dev, "failed to get rk_dec irq\n"); + return -ENOENT; + } + + mem = of_parse_phandle(np, "memory-region", 0); + if (!mem) { + dev_err(dev, "missing \"memory-region\" property\n"); + return -ENODEV; + } + + ret = of_address_to_resource(mem, 0, ®); + if (ret) { + dev_err(dev, "missing \"reg\" property\n"); + return -ENODEV; + } + + rk_dec->mem_start = reg.start; + rk_dec->mem_size = resource_size(®); + + rk_dec->num_clocks = devm_clk_bulk_get_all(dev, &rk_dec->clocks); + if (rk_dec->num_clocks < 0) { + dev_err(dev, "failed to get decompress clock\n"); + return -ENODEV; + } + + ret = clk_bulk_prepare_enable(rk_dec->num_clocks, rk_dec->clocks); + if (ret) + return ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rk_dec->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(rk_dec->regs)) { + ret = PTR_ERR(rk_dec->regs); + goto disable_clk; + } + + dev_set_drvdata(dev, rk_dec); + + ret = devm_request_threaded_irq(dev, rk_dec->irq, rk_decom_irq_handler, + rk_decom_irq_thread, IRQF_ONESHOT, + dev_name(dev), rk_dec); + if (ret < 0) { + dev_err(dev, "failed to attach decompress irq\n"); + goto disable_clk; + } + + return 0; + +disable_clk: + clk_bulk_disable_unprepare(rk_dec->num_clocks, rk_dec->clocks); + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id rockchip_decom_dt_match[] = { + { .compatible = "rockchip,hw-decompress" }, + {}, +}; +#endif + +static struct platform_driver rk_decom_driver = { + .driver = { + .name = "rockchip_hw_decompress", + .of_match_table = rockchip_decom_dt_match, + }, + .probe = rockchip_decom_probe, +}; + +static int __init rockchip_hw_decompress_init(void) +{ + return platform_driver_register(&rk_decom_driver); +} + +pure_initcall(rockchip_hw_decompress_init);