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 <chenjh@rock-chips.com>
Change-Id: Ie84b2a6174d04592110333d66667da66f98f07f6
This commit is contained in:
Joseph Chen 2020-06-22 16:09:57 +08:00 committed by Jianhong Chen
parent 01b57c0600
commit 656bdb598a
3 changed files with 168 additions and 31 deletions

View File

@ -4,8 +4,9 @@
*/
#include <common.h>
#include <dm.h>
#include <dm/uclass.h>
#include <misc.h>
#include <dm/uclass.h>
#include <dm/uclass-internal.h>
#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, &param);
}
@ -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, &param);
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;
}

View File

@ -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;

View File

@ -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_ */