xref: /rk3399_rockchip-uboot/drivers/misc/misc_decompress.c (revision 656bdb598ae7fe182050b4f584850f7520d0d0d3)
1cc05bcfaSJason Zhu // SPDX-License-Identifier:     GPL-2.0+
2cc05bcfaSJason Zhu /*
3cc05bcfaSJason Zhu  * Copyright (C) 2020 Rockchip Electronics Co., Ltd
4cc05bcfaSJason Zhu  */
5cc05bcfaSJason Zhu #include <common.h>
6cc05bcfaSJason Zhu #include <dm.h>
7cc05bcfaSJason Zhu #include <misc.h>
8*656bdb59SJoseph Chen #include <dm/uclass.h>
9*656bdb59SJoseph Chen #include <dm/uclass-internal.h>
10cc05bcfaSJason Zhu 
11cc05bcfaSJason Zhu #define HEAD_CRC		2
12cc05bcfaSJason Zhu #define EXTRA_FIELD		4
13cc05bcfaSJason Zhu #define ORIG_NAME		8
14cc05bcfaSJason Zhu #define COMMENT			0x10
15cc05bcfaSJason Zhu #define RESERVED		0xe0
16cc05bcfaSJason Zhu #define DEFLATED		8
17cc05bcfaSJason Zhu 
18*656bdb59SJoseph Chen static u32 misc_decomp_async, misc_decomp_sync;
19*656bdb59SJoseph Chen 
20*656bdb59SJoseph Chen static void decomp_set_flags(u32 *flags, u8 comp)
21*656bdb59SJoseph Chen {
22*656bdb59SJoseph Chen 	if (comp == IH_COMP_GZIP)
23*656bdb59SJoseph Chen 		*flags |= DECOM_GZIP;
24*656bdb59SJoseph Chen 	else if (comp == IH_COMP_LZ4)
25*656bdb59SJoseph Chen 		*flags |= DECOM_LZ4;
26*656bdb59SJoseph Chen }
27*656bdb59SJoseph Chen 
28*656bdb59SJoseph Chen void misc_decompress_async(u8 comp)
29*656bdb59SJoseph Chen {
30*656bdb59SJoseph Chen 	decomp_set_flags(&misc_decomp_async, comp);
31*656bdb59SJoseph Chen }
32*656bdb59SJoseph Chen 
33*656bdb59SJoseph Chen void misc_decompress_sync(u8 comp)
34*656bdb59SJoseph Chen {
35*656bdb59SJoseph Chen 	decomp_set_flags(&misc_decomp_sync, comp);
36*656bdb59SJoseph Chen }
37*656bdb59SJoseph Chen 
38cc05bcfaSJason Zhu static int misc_gzip_parse_header(const unsigned char *src, unsigned long len)
39cc05bcfaSJason Zhu {
40cc05bcfaSJason Zhu 	int i, flags;
41cc05bcfaSJason Zhu 
42cc05bcfaSJason Zhu 	/* skip header */
43cc05bcfaSJason Zhu 	i = 10;
44cc05bcfaSJason Zhu 	flags = src[3];
45cc05bcfaSJason Zhu 	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
46cc05bcfaSJason Zhu 		debug("Error: Bad gzipped data\n");
47cc05bcfaSJason Zhu 		return (-1);
48cc05bcfaSJason Zhu 	}
49cc05bcfaSJason Zhu 	if ((flags & EXTRA_FIELD) != 0)
50cc05bcfaSJason Zhu 		i = 12 + src[10] + (src[11] << 8);
51cc05bcfaSJason Zhu 	if ((flags & ORIG_NAME) != 0)
52cc05bcfaSJason Zhu 		while (src[i++] != 0)
53cc05bcfaSJason Zhu 			;
54cc05bcfaSJason Zhu 	if ((flags & COMMENT) != 0)
55cc05bcfaSJason Zhu 		while (src[i++] != 0)
56cc05bcfaSJason Zhu 			;
57cc05bcfaSJason Zhu 	if ((flags & HEAD_CRC) != 0)
58cc05bcfaSJason Zhu 		i += 2;
59cc05bcfaSJason Zhu 	if (i >= len) {
60cc05bcfaSJason Zhu 		puts("Error: gunzip out of data in header\n");
61cc05bcfaSJason Zhu 		return (-1);
62cc05bcfaSJason Zhu 	}
63cc05bcfaSJason Zhu 	return i;
64cc05bcfaSJason Zhu }
65cc05bcfaSJason Zhu 
66*656bdb59SJoseph Chen static u32 misc_get_data_size(unsigned long src, unsigned long len, u32 cap)
67*656bdb59SJoseph Chen {
68*656bdb59SJoseph Chen 	if (cap == DECOM_GZIP)
69*656bdb59SJoseph Chen 		return *(u32 *)(src + len - 4);
70*656bdb59SJoseph Chen 
71*656bdb59SJoseph Chen 	return 0;
72*656bdb59SJoseph Chen }
73*656bdb59SJoseph Chen 
74cc05bcfaSJason Zhu struct udevice *misc_decompress_get_device(u32 capability)
75cc05bcfaSJason Zhu {
76374c241cSJoseph Chen 	return misc_get_device_by_capability(capability);
77cc05bcfaSJason Zhu }
78cc05bcfaSJason Zhu 
79*656bdb59SJoseph Chen int misc_decompress_start(struct udevice *dev, unsigned long dst,
80*656bdb59SJoseph Chen 			  unsigned long src, unsigned long src_len)
81cc05bcfaSJason Zhu {
82cc05bcfaSJason Zhu 	struct decom_param param;
83cc05bcfaSJason Zhu 
84cc05bcfaSJason Zhu 	param.addr_dst = dst;
85cc05bcfaSJason Zhu 	param.addr_src = src;
86cc05bcfaSJason Zhu 	if (misc_gzip_parse_header((unsigned char *)src, 0xffff) > 0) {
875b7d3298SJason Zhu 		param.mode = DECOM_GZIP;
88cc05bcfaSJason Zhu 	} else {
89cc05bcfaSJason Zhu 		printf("Unsupported decompression format.\n");
90cc05bcfaSJason Zhu 		return -EPERM;
91cc05bcfaSJason Zhu 	}
92cc05bcfaSJason Zhu 
93*656bdb59SJoseph Chen 	param.size_src = misc_get_data_size(src, src_len, param.mode);
94*656bdb59SJoseph Chen 	if (!param.size_src)
95*656bdb59SJoseph Chen 		return -EINVAL;
96*656bdb59SJoseph Chen 
97cc05bcfaSJason Zhu 	return misc_ioctl(dev, IOCTL_REQ_START, &param);
98cc05bcfaSJason Zhu }
99cc05bcfaSJason Zhu 
100cc05bcfaSJason Zhu int misc_decompress_stop(struct udevice *dev)
101cc05bcfaSJason Zhu {
102cc05bcfaSJason Zhu 	return misc_ioctl(dev, IOCTL_REQ_STOP, NULL);
103cc05bcfaSJason Zhu }
104cc05bcfaSJason Zhu 
105809af6baSJason Zhu bool misc_decompress_is_complete(struct udevice *dev)
106cc05bcfaSJason Zhu {
107809af6baSJason Zhu 	if (misc_ioctl(dev, IOCTL_REQ_POLL, NULL))
108809af6baSJason Zhu 		return false;
109809af6baSJason Zhu 	else
110809af6baSJason Zhu 		return true;
111cc05bcfaSJason Zhu }
1124298c19dSJason Zhu 
113*656bdb59SJoseph Chen int misc_decompress_data_size(struct udevice *dev, u64 *size, u32 cap)
1144298c19dSJason Zhu {
115*656bdb59SJoseph Chen 	struct decom_param param;
1164298c19dSJason Zhu 	int ret;
1174298c19dSJason Zhu 
118*656bdb59SJoseph Chen 	param.mode = cap;
119*656bdb59SJoseph Chen 	param.size_dst = 0; /* clear */
120*656bdb59SJoseph Chen 
121*656bdb59SJoseph Chen 	ret = misc_ioctl(dev, IOCTL_REQ_DATA_SIZE, &param);
122*656bdb59SJoseph Chen 	if (!ret)
123*656bdb59SJoseph Chen 		*size = param.size_dst;
124*656bdb59SJoseph Chen 
125*656bdb59SJoseph Chen 	return ret;
126*656bdb59SJoseph Chen }
127*656bdb59SJoseph Chen 
128*656bdb59SJoseph Chen static int misc_decompress_finish(struct udevice *dev, u32 cap)
129*656bdb59SJoseph Chen {
130*656bdb59SJoseph Chen 	int timeout = 10000;
1314298c19dSJason Zhu 
1324298c19dSJason Zhu 	while (!misc_decompress_is_complete(dev)) {
1334298c19dSJason Zhu 		if (timeout < 0)
1344298c19dSJason Zhu 			return -EIO;
1354298c19dSJason Zhu 		timeout--;
1364298c19dSJason Zhu 		udelay(10);
1374298c19dSJason Zhu 	}
1384298c19dSJason Zhu 
139*656bdb59SJoseph Chen 	return misc_decompress_stop(dev);
140*656bdb59SJoseph Chen }
1414298c19dSJason Zhu 
142*656bdb59SJoseph Chen int misc_decompress_cleanup(void)
143*656bdb59SJoseph Chen {
144*656bdb59SJoseph Chen 	const struct misc_ops *ops;
145*656bdb59SJoseph Chen 	struct udevice *dev;
146*656bdb59SJoseph Chen 	struct uclass *uc;
147*656bdb59SJoseph Chen 	int ret;
148*656bdb59SJoseph Chen 	u32 cap;
149*656bdb59SJoseph Chen 
150*656bdb59SJoseph Chen 	ret = uclass_get(UCLASS_MISC, &uc);
151*656bdb59SJoseph Chen 	if (ret)
152*656bdb59SJoseph Chen 		return 0;
153*656bdb59SJoseph Chen 
154*656bdb59SJoseph Chen 	/* use "find_" */
155*656bdb59SJoseph Chen 	for (uclass_find_first_device(UCLASS_MISC, &dev);
156*656bdb59SJoseph Chen 	     dev;
157*656bdb59SJoseph Chen 	     uclass_find_next_device(&dev)) {
158*656bdb59SJoseph Chen 		ops = device_get_ops(dev);
159*656bdb59SJoseph Chen 		if (!ops || !ops->ioctl)
160*656bdb59SJoseph Chen 			continue;
161*656bdb59SJoseph Chen 		else if (ops->ioctl(dev, IOCTL_REQ_CAPABILITY, &cap))
162*656bdb59SJoseph Chen 			continue;
163*656bdb59SJoseph Chen 		else if (misc_decomp_async & cap)
164*656bdb59SJoseph Chen 			continue;
165*656bdb59SJoseph Chen 
166*656bdb59SJoseph Chen 		if (misc_decomp_sync & cap) {
167*656bdb59SJoseph Chen 			ret = ops->ioctl(dev, IOCTL_REQ_STOP, NULL);
168*656bdb59SJoseph Chen 			if (ret) {
169*656bdb59SJoseph Chen 				printf("Failed to stop decompress: %s, ret=%d\n",
170*656bdb59SJoseph Chen 				       dev->name, ret);
171*656bdb59SJoseph Chen 				return ret;
172*656bdb59SJoseph Chen 			}
173*656bdb59SJoseph Chen 		}
174*656bdb59SJoseph Chen 	}
175*656bdb59SJoseph Chen 
176*656bdb59SJoseph Chen 	return 0;
177*656bdb59SJoseph Chen }
178*656bdb59SJoseph Chen 
179*656bdb59SJoseph Chen int misc_decompress_process(unsigned long dst, unsigned long src,
180*656bdb59SJoseph Chen 			    unsigned long src_len, u32 cap, bool sync,
181*656bdb59SJoseph Chen 			    u64 *size)
182*656bdb59SJoseph Chen {
183*656bdb59SJoseph Chen 	struct udevice *dev;
184*656bdb59SJoseph Chen 	int ret;
185*656bdb59SJoseph Chen 
186*656bdb59SJoseph Chen 	dev = misc_decompress_get_device(cap);
187*656bdb59SJoseph Chen 	if (!dev)
188*656bdb59SJoseph Chen 		return -ENODEV;
189*656bdb59SJoseph Chen 
190*656bdb59SJoseph Chen 	/* Wait last finish */
191*656bdb59SJoseph Chen 	ret = misc_decompress_finish(dev, cap);
192*656bdb59SJoseph Chen 	if (ret)
193*656bdb59SJoseph Chen 		return ret;
194*656bdb59SJoseph Chen 
195*656bdb59SJoseph Chen 	ret = misc_decompress_start(dev, dst, src, src_len);
196*656bdb59SJoseph Chen 	if (ret)
197*656bdb59SJoseph Chen 		return ret;
198*656bdb59SJoseph Chen 
199*656bdb59SJoseph Chen 	/*
200*656bdb59SJoseph Chen 	 * Wait this round finish ?
201*656bdb59SJoseph Chen 	 *
202*656bdb59SJoseph Chen 	 * If sync, return original data length after decompress done.
203*656bdb59SJoseph Chen 	 * otherwise return from compressed file information.
204*656bdb59SJoseph Chen 	 */
205*656bdb59SJoseph Chen 	if (sync) {
206*656bdb59SJoseph Chen 		ret = misc_decompress_finish(dev, cap);
207*656bdb59SJoseph Chen 		if (ret)
208*656bdb59SJoseph Chen 			return ret;
209*656bdb59SJoseph Chen 		if (size)
210*656bdb59SJoseph Chen 			ret = misc_decompress_data_size(dev, size, cap);
211*656bdb59SJoseph Chen 	} else {
212*656bdb59SJoseph Chen 		if (size)
213*656bdb59SJoseph Chen 			*size = misc_get_data_size(src, src_len, cap);
214*656bdb59SJoseph Chen 	}
215*656bdb59SJoseph Chen 
216*656bdb59SJoseph Chen 	return ret;
2174298c19dSJason Zhu }
218