xref: /rk3399_rockchip-uboot/drivers/misc/misc_decompress.c (revision 2bce72c867415b4a0d75f2222eb048451a7832cf)
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>
8656bdb59SJoseph Chen #include <dm/uclass.h>
9656bdb59SJoseph 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 
18656bdb59SJoseph Chen static u32 misc_decomp_async, misc_decomp_sync;
19656bdb59SJoseph Chen 
20656bdb59SJoseph Chen static void decomp_set_flags(u32 *flags, u8 comp)
21656bdb59SJoseph Chen {
22656bdb59SJoseph Chen 	if (comp == IH_COMP_GZIP)
23656bdb59SJoseph Chen 		*flags |= DECOM_GZIP;
24656bdb59SJoseph Chen 	else if (comp == IH_COMP_LZ4)
25656bdb59SJoseph Chen 		*flags |= DECOM_LZ4;
26656bdb59SJoseph Chen }
27656bdb59SJoseph Chen 
28656bdb59SJoseph Chen void misc_decompress_async(u8 comp)
29656bdb59SJoseph Chen {
30656bdb59SJoseph Chen 	decomp_set_flags(&misc_decomp_async, comp);
31656bdb59SJoseph Chen }
32656bdb59SJoseph Chen 
33656bdb59SJoseph Chen void misc_decompress_sync(u8 comp)
34656bdb59SJoseph Chen {
35656bdb59SJoseph Chen 	decomp_set_flags(&misc_decomp_sync, comp);
36656bdb59SJoseph Chen }
37656bdb59SJoseph 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 
66656bdb59SJoseph Chen static u32 misc_get_data_size(unsigned long src, unsigned long len, u32 cap)
67656bdb59SJoseph Chen {
68656bdb59SJoseph Chen 	if (cap == DECOM_GZIP)
69656bdb59SJoseph Chen 		return *(u32 *)(src + len - 4);
70656bdb59SJoseph Chen 
71656bdb59SJoseph Chen 	return 0;
72656bdb59SJoseph Chen }
73656bdb59SJoseph Chen 
745313a4d0SJoseph Chen static struct udevice *misc_decompress_get_device(u32 capability)
75cc05bcfaSJason Zhu {
76374c241cSJoseph Chen 	return misc_get_device_by_capability(capability);
77cc05bcfaSJason Zhu }
78cc05bcfaSJason Zhu 
795313a4d0SJoseph Chen static int misc_decompress_start(struct udevice *dev, unsigned long dst,
80*2bce72c8SJoseph Chen 				 unsigned long src, unsigned long src_len,
81*2bce72c8SJoseph Chen 				 u32 flags)
82cc05bcfaSJason Zhu {
83cc05bcfaSJason Zhu 	struct decom_param param;
84cc05bcfaSJason Zhu 
85cc05bcfaSJason Zhu 	param.addr_dst = dst;
86cc05bcfaSJason Zhu 	param.addr_src = src;
87*2bce72c8SJoseph Chen 	param.flags = flags;
88cc05bcfaSJason Zhu 	if (misc_gzip_parse_header((unsigned char *)src, 0xffff) > 0) {
895b7d3298SJason Zhu 		param.mode = DECOM_GZIP;
90cc05bcfaSJason Zhu 	} else {
91cc05bcfaSJason Zhu 		printf("Unsupported decompression format.\n");
92cc05bcfaSJason Zhu 		return -EPERM;
93cc05bcfaSJason Zhu 	}
94cc05bcfaSJason Zhu 
95e1e885d3SJoseph Chen 	param.size_src = src_len;
96e1e885d3SJoseph Chen 	param.size_dst = misc_get_data_size(src, src_len, param.mode);
97e1e885d3SJoseph Chen 
98e1e885d3SJoseph Chen 	if (!param.size_src || !param.size_dst)
99656bdb59SJoseph Chen 		return -EINVAL;
100656bdb59SJoseph Chen 
101cc05bcfaSJason Zhu 	return misc_ioctl(dev, IOCTL_REQ_START, &param);
102cc05bcfaSJason Zhu }
103cc05bcfaSJason Zhu 
1045313a4d0SJoseph Chen static int misc_decompress_stop(struct udevice *dev)
105cc05bcfaSJason Zhu {
106cc05bcfaSJason Zhu 	return misc_ioctl(dev, IOCTL_REQ_STOP, NULL);
107cc05bcfaSJason Zhu }
108cc05bcfaSJason Zhu 
1095313a4d0SJoseph Chen static bool misc_decompress_is_complete(struct udevice *dev)
110cc05bcfaSJason Zhu {
111809af6baSJason Zhu 	if (misc_ioctl(dev, IOCTL_REQ_POLL, NULL))
112809af6baSJason Zhu 		return false;
113809af6baSJason Zhu 	else
114809af6baSJason Zhu 		return true;
115cc05bcfaSJason Zhu }
1164298c19dSJason Zhu 
1175313a4d0SJoseph Chen static int misc_decompress_data_size(struct udevice *dev, u64 *size, u32 cap)
1184298c19dSJason Zhu {
119656bdb59SJoseph Chen 	struct decom_param param;
1204298c19dSJason Zhu 	int ret;
1214298c19dSJason Zhu 
122656bdb59SJoseph Chen 	param.mode = cap;
123656bdb59SJoseph Chen 	param.size_dst = 0; /* clear */
124656bdb59SJoseph Chen 
125656bdb59SJoseph Chen 	ret = misc_ioctl(dev, IOCTL_REQ_DATA_SIZE, &param);
126656bdb59SJoseph Chen 	if (!ret)
127656bdb59SJoseph Chen 		*size = param.size_dst;
128656bdb59SJoseph Chen 
129656bdb59SJoseph Chen 	return ret;
130656bdb59SJoseph Chen }
131656bdb59SJoseph Chen 
132656bdb59SJoseph Chen static int misc_decompress_finish(struct udevice *dev, u32 cap)
133656bdb59SJoseph Chen {
134656bdb59SJoseph Chen 	int timeout = 10000;
1354298c19dSJason Zhu 
1364298c19dSJason Zhu 	while (!misc_decompress_is_complete(dev)) {
1374298c19dSJason Zhu 		if (timeout < 0)
1384298c19dSJason Zhu 			return -EIO;
1394298c19dSJason Zhu 		timeout--;
1404298c19dSJason Zhu 		udelay(10);
1414298c19dSJason Zhu 	}
1424298c19dSJason Zhu 
143656bdb59SJoseph Chen 	return misc_decompress_stop(dev);
144656bdb59SJoseph Chen }
1454298c19dSJason Zhu 
146656bdb59SJoseph Chen int misc_decompress_cleanup(void)
147656bdb59SJoseph Chen {
148656bdb59SJoseph Chen 	const struct misc_ops *ops;
149656bdb59SJoseph Chen 	struct udevice *dev;
150656bdb59SJoseph Chen 	struct uclass *uc;
151656bdb59SJoseph Chen 	int ret;
152656bdb59SJoseph Chen 	u32 cap;
153656bdb59SJoseph Chen 
154656bdb59SJoseph Chen 	ret = uclass_get(UCLASS_MISC, &uc);
155656bdb59SJoseph Chen 	if (ret)
156656bdb59SJoseph Chen 		return 0;
157656bdb59SJoseph Chen 
158656bdb59SJoseph Chen 	/* use "find_" */
159656bdb59SJoseph Chen 	for (uclass_find_first_device(UCLASS_MISC, &dev);
160656bdb59SJoseph Chen 	     dev;
161656bdb59SJoseph Chen 	     uclass_find_next_device(&dev)) {
162656bdb59SJoseph Chen 		ops = device_get_ops(dev);
163656bdb59SJoseph Chen 		if (!ops || !ops->ioctl)
164656bdb59SJoseph Chen 			continue;
165656bdb59SJoseph Chen 		else if (ops->ioctl(dev, IOCTL_REQ_CAPABILITY, &cap))
166656bdb59SJoseph Chen 			continue;
167656bdb59SJoseph Chen 		else if (misc_decomp_async & cap)
168656bdb59SJoseph Chen 			continue;
169656bdb59SJoseph Chen 
170656bdb59SJoseph Chen 		if (misc_decomp_sync & cap) {
17183537505SJoseph Chen 			ret = misc_decompress_finish(dev, cap);
172656bdb59SJoseph Chen 			if (ret) {
173656bdb59SJoseph Chen 				printf("Failed to stop decompress: %s, ret=%d\n",
174656bdb59SJoseph Chen 				       dev->name, ret);
175656bdb59SJoseph Chen 				return ret;
176656bdb59SJoseph Chen 			}
177656bdb59SJoseph Chen 		}
178656bdb59SJoseph Chen 	}
179656bdb59SJoseph Chen 
180656bdb59SJoseph Chen 	return 0;
181656bdb59SJoseph Chen }
182656bdb59SJoseph Chen 
183656bdb59SJoseph Chen int misc_decompress_process(unsigned long dst, unsigned long src,
184656bdb59SJoseph Chen 			    unsigned long src_len, u32 cap, bool sync,
185*2bce72c8SJoseph Chen 			    u64 *size, u32 flags)
186656bdb59SJoseph Chen {
187656bdb59SJoseph Chen 	struct udevice *dev;
188656bdb59SJoseph Chen 	int ret;
189656bdb59SJoseph Chen 
190656bdb59SJoseph Chen 	dev = misc_decompress_get_device(cap);
191656bdb59SJoseph Chen 	if (!dev)
192656bdb59SJoseph Chen 		return -ENODEV;
193656bdb59SJoseph Chen 
194656bdb59SJoseph Chen 	/* Wait last finish */
195656bdb59SJoseph Chen 	ret = misc_decompress_finish(dev, cap);
196656bdb59SJoseph Chen 	if (ret)
197656bdb59SJoseph Chen 		return ret;
198656bdb59SJoseph Chen 
199*2bce72c8SJoseph Chen 	ret = misc_decompress_start(dev, dst, src, src_len, flags);
200656bdb59SJoseph Chen 	if (ret)
201656bdb59SJoseph Chen 		return ret;
202656bdb59SJoseph Chen 
203656bdb59SJoseph Chen 	/*
204656bdb59SJoseph Chen 	 * Wait this round finish ?
205656bdb59SJoseph Chen 	 *
206656bdb59SJoseph Chen 	 * If sync, return original data length after decompress done.
207656bdb59SJoseph Chen 	 * otherwise return from compressed file information.
208656bdb59SJoseph Chen 	 */
209656bdb59SJoseph Chen 	if (sync) {
210656bdb59SJoseph Chen 		ret = misc_decompress_finish(dev, cap);
211656bdb59SJoseph Chen 		if (ret)
212656bdb59SJoseph Chen 			return ret;
213656bdb59SJoseph Chen 		if (size)
214656bdb59SJoseph Chen 			ret = misc_decompress_data_size(dev, size, cap);
215656bdb59SJoseph Chen 	} else {
216656bdb59SJoseph Chen 		if (size)
217656bdb59SJoseph Chen 			*size = misc_get_data_size(src, src_len, cap);
218656bdb59SJoseph Chen 	}
219656bdb59SJoseph Chen 
220656bdb59SJoseph Chen 	return ret;
2214298c19dSJason Zhu }
222