From 656bdb598ae7fe182050b4f584850f7520d0d0d3 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Mon, 22 Jun 2020 16:09:57 +0800 Subject: [PATCH] misc: decompress: add/update API - Support get gunzip data size from src data; - Support sync decompress for this round; - Support return the gunzip data size of compressed image. - Add misc_decompress_cleanup() for waiting last decompress done. Signed-off-by: Joseph Chen Change-Id: Ie84b2a6174d04592110333d66667da66f98f07f6 --- drivers/misc/misc_decompress.c | 147 +++++++++++++++++++++++++---- drivers/misc/rockchip_decompress.c | 38 ++++++-- include/misc.h | 14 ++- 3 files changed, 168 insertions(+), 31 deletions(-) diff --git a/drivers/misc/misc_decompress.c b/drivers/misc/misc_decompress.c index 2e065a053a..6188135846 100644 --- a/drivers/misc/misc_decompress.c +++ b/drivers/misc/misc_decompress.c @@ -4,8 +4,9 @@ */ #include #include -#include #include +#include +#include #define HEAD_CRC 2 #define EXTRA_FIELD 4 @@ -14,6 +15,26 @@ #define RESERVED 0xe0 #define DEFLATED 8 +static u32 misc_decomp_async, misc_decomp_sync; + +static void decomp_set_flags(u32 *flags, u8 comp) +{ + if (comp == IH_COMP_GZIP) + *flags |= DECOM_GZIP; + else if (comp == IH_COMP_LZ4) + *flags |= DECOM_LZ4; +} + +void misc_decompress_async(u8 comp) +{ + decomp_set_flags(&misc_decomp_async, comp); +} + +void misc_decompress_sync(u8 comp) +{ + decomp_set_flags(&misc_decomp_sync, comp); +} + static int misc_gzip_parse_header(const unsigned char *src, unsigned long len) { int i, flags; @@ -42,19 +63,26 @@ static int misc_gzip_parse_header(const unsigned char *src, unsigned long len) return i; } +static u32 misc_get_data_size(unsigned long src, unsigned long len, u32 cap) +{ + if (cap == DECOM_GZIP) + return *(u32 *)(src + len - 4); + + return 0; +} + struct udevice *misc_decompress_get_device(u32 capability) { return misc_get_device_by_capability(capability); } -int misc_decompress_start(struct udevice *dev, unsigned long src, - unsigned long dst, unsigned long size) +int misc_decompress_start(struct udevice *dev, unsigned long dst, + unsigned long src, unsigned long src_len) { struct decom_param param; param.addr_dst = dst; param.addr_src = src; - param.size = size; if (misc_gzip_parse_header((unsigned char *)src, 0xffff) > 0) { param.mode = DECOM_GZIP; } else { @@ -62,6 +90,10 @@ int misc_decompress_start(struct udevice *dev, unsigned long src, return -EPERM; } + param.size_src = misc_get_data_size(src, src_len, param.mode); + if (!param.size_src) + return -EINVAL; + return misc_ioctl(dev, IOCTL_REQ_START, ¶m); } @@ -78,18 +110,24 @@ bool misc_decompress_is_complete(struct udevice *dev) return true; } -int misc_decompress_process(unsigned long src, - unsigned long dst, - unsigned long limit_size, - u32 cap) +int misc_decompress_data_size(struct udevice *dev, u64 *size, u32 cap) { - struct udevice *dev; - int timeout = 10000; + struct decom_param param; int ret; - dev = misc_decompress_get_device(cap); - if (!dev) - return -EIO; + param.mode = cap; + param.size_dst = 0; /* clear */ + + ret = misc_ioctl(dev, IOCTL_REQ_DATA_SIZE, ¶m); + if (!ret) + *size = param.size_dst; + + return ret; +} + +static int misc_decompress_finish(struct udevice *dev, u32 cap) +{ + int timeout = 10000; while (!misc_decompress_is_complete(dev)) { if (timeout < 0) @@ -98,9 +136,82 @@ int misc_decompress_process(unsigned long src, udelay(10); } - ret = misc_decompress_stop(dev); - if (ret) - return -EIO; - - return misc_decompress_start(dev, src, dst, limit_size); + return misc_decompress_stop(dev); +} + +int misc_decompress_cleanup(void) +{ + const struct misc_ops *ops; + struct udevice *dev; + struct uclass *uc; + int ret; + u32 cap; + + ret = uclass_get(UCLASS_MISC, &uc); + if (ret) + return 0; + + /* use "find_" */ + for (uclass_find_first_device(UCLASS_MISC, &dev); + dev; + uclass_find_next_device(&dev)) { + ops = device_get_ops(dev); + if (!ops || !ops->ioctl) + continue; + else if (ops->ioctl(dev, IOCTL_REQ_CAPABILITY, &cap)) + continue; + else if (misc_decomp_async & cap) + continue; + + if (misc_decomp_sync & cap) { + ret = ops->ioctl(dev, IOCTL_REQ_STOP, NULL); + if (ret) { + printf("Failed to stop decompress: %s, ret=%d\n", + dev->name, ret); + return ret; + } + } + } + + return 0; +} + +int misc_decompress_process(unsigned long dst, unsigned long src, + unsigned long src_len, u32 cap, bool sync, + u64 *size) +{ + struct udevice *dev; + int ret; + + dev = misc_decompress_get_device(cap); + if (!dev) + return -ENODEV; + + /* Wait last finish */ + ret = misc_decompress_finish(dev, cap); + if (ret) + return ret; + + ret = misc_decompress_start(dev, dst, src, src_len); + if (ret) + return ret; + + /* + * Wait this round finish ? + * + * If sync, return original data length after decompress done. + * otherwise return from compressed file information. + */ + if (sync) { + ret = misc_decompress_finish(dev, cap); + if (ret) + return ret; + if (size) + ret = misc_decompress_data_size(dev, size, cap); + } else { + if (size) + *size = misc_get_data_size(src, src_len, cap); + } + + return ret; } diff --git a/drivers/misc/rockchip_decompress.c b/drivers/misc/rockchip_decompress.c index fb6934aa32..a7e3b3a1d2 100644 --- a/drivers/misc/rockchip_decompress.c +++ b/drivers/misc/rockchip_decompress.c @@ -90,8 +90,8 @@ static int rockchip_decom_start(struct udevice *dev, void *buf) { struct rockchip_decom_priv *priv = dev_get_priv(dev); struct decom_param *param = (struct decom_param *)buf; - unsigned int limit_lo = param->size & 0xffffffff; - unsigned int limit_hi = param->size >> 32; + unsigned int limit_lo = param->size_src & 0xffffffff; + unsigned int limit_hi = param->size_src >> 32; priv->done = false; @@ -102,13 +102,12 @@ static int rockchip_decom_start(struct udevice *dev, void *buf) writel(LZ4_CONT_CSUM_CHECK_EN | LZ4_HEAD_CSUM_CHECK_EN | LZ4_BLOCK_CSUM_CHECK_EN | - DECOM_LZ4_MODE, priv->base + DECOM_CTRL); - - if (param->mode == DECOM_GZIP) + DECOM_LZ4_MODE, + priv->base + DECOM_CTRL); + else if (param->mode == DECOM_GZIP) writel(DECOM_DEFLATE_MODE | DECOM_GZIP_MODE, priv->base + DECOM_CTRL); - - if (param->mode == DECOM_ZLIB) + else if (param->mode == DECOM_ZLIB) writel(DECOM_DEFLATE_MODE | DECOM_ZLIB_MODE, priv->base + DECOM_CTRL); @@ -118,8 +117,11 @@ static int rockchip_decom_start(struct udevice *dev, void *buf) writel(limit_lo, priv->base + DECOM_LMTSL); writel(limit_hi, priv->base + DECOM_LMTSH); +#ifdef CONFIG_IRQ writel(DECOM_INT_MASK, priv->base + DECOM_IEN); +#endif writel(DECOM_ENABLE, priv->base + DECOM_ENR); + priv->idle_check_once = true; return 0; @@ -128,13 +130,14 @@ static int rockchip_decom_start(struct udevice *dev, void *buf) static int rockchip_decom_stop(struct udevice *dev) { struct rockchip_decom_priv *priv = dev_get_priv(dev); +#ifdef CONFIG_IRQ int irq_status; irq_status = readl(priv->base + DECOM_ISR); /* clear interrupts */ if (irq_status) writel(irq_status, priv->base + DECOM_ISR); - +#endif writel(DECOM_DISABLE, priv->base + DECOM_ENR); return 0; @@ -161,6 +164,19 @@ static int rockchip_decom_capability(u32 *buf) return 0; } +static int rockchip_decom_data_size(struct udevice *dev, u64 *buf) +{ + struct rockchip_decom_priv *priv = dev_get_priv(dev); + struct decom_param *param = (struct decom_param *)buf; + u32 sizel, sizeh; + + sizel = readl(priv->base + DECOM_TSIZEL); + sizeh = readl(priv->base + DECOM_TSIZEH); + param->size_dst = sizel | ((u64)sizeh << 32); + + return 0; +} + /* Caller must fill in param @buf which represent struct decom_param */ static int rockchip_decom_ioctl(struct udevice *dev, unsigned long request, void *buf) @@ -180,6 +196,12 @@ static int rockchip_decom_ioctl(struct udevice *dev, unsigned long request, case IOCTL_REQ_CAPABILITY: ret = rockchip_decom_capability(buf); break; + case IOCTL_REQ_DATA_SIZE: + ret = rockchip_decom_data_size(dev, buf); + break; + default: + printf("Unsupported ioctl: %ld\n", (ulong)request); + break; } return ret; diff --git a/include/misc.h b/include/misc.h index 29e800c341..ac91fc09da 100644 --- a/include/misc.h +++ b/include/misc.h @@ -16,6 +16,7 @@ #define IOCTL_REQ_STOP _IO('m', 0x02) #define IOCTL_REQ_POLL _IO('m', 0x03) #define IOCTL_REQ_CAPABILITY _IO('m', 0x04) +#define IOCTL_REQ_DATA_SIZE _IO('m', 0x05) enum misc_mode { DECOM_LZ4 = BIT(0), @@ -149,7 +150,8 @@ int misc_otp_write(struct udevice *dev, int offset, const void *buf, int size); struct decom_param { unsigned long addr_src; unsigned long addr_dst; - u64 size; + u64 size_src; + u64 size_dst; /* to be filled for output */ enum misc_mode mode; }; @@ -158,9 +160,11 @@ int misc_decompress_start(struct udevice *dev, unsigned long src, unsigned long dst, unsigned long size); int misc_decompress_stop(struct udevice *dev); bool misc_decompress_is_complete(struct udevice *dev); -int misc_decompress_process(unsigned long src, - unsigned long dst, - unsigned long limit_size, - u32 cap); +void misc_decompress_async(u8 comp); +void misc_decompress_sync(u8 comp); +int misc_decompress_cleanup(void); +int misc_decompress_process(unsigned long dst, unsigned long src, + unsigned long src_len, u32 cap, bool sync, + u64 *size); #endif /* _MISC_H_ */