xref: /rk3399_rockchip-uboot/drivers/misc/misc_decompress.c (revision 5313a4d09b47dac4fc2ff1276273e248eb416cc3)
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 
74*5313a4d0SJoseph 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 
79*5313a4d0SJoseph Chen static int misc_decompress_start(struct udevice *dev, unsigned long dst,
80656bdb59SJoseph 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 
93e1e885d3SJoseph Chen 	param.size_src = src_len;
94e1e885d3SJoseph Chen 	param.size_dst = misc_get_data_size(src, src_len, param.mode);
95e1e885d3SJoseph Chen 
96e1e885d3SJoseph Chen 	if (!param.size_src || !param.size_dst)
97656bdb59SJoseph Chen 		return -EINVAL;
98656bdb59SJoseph Chen 
99cc05bcfaSJason Zhu 	return misc_ioctl(dev, IOCTL_REQ_START, &param);
100cc05bcfaSJason Zhu }
101cc05bcfaSJason Zhu 
102*5313a4d0SJoseph Chen static int misc_decompress_stop(struct udevice *dev)
103cc05bcfaSJason Zhu {
104cc05bcfaSJason Zhu 	return misc_ioctl(dev, IOCTL_REQ_STOP, NULL);
105cc05bcfaSJason Zhu }
106cc05bcfaSJason Zhu 
107*5313a4d0SJoseph Chen static bool misc_decompress_is_complete(struct udevice *dev)
108cc05bcfaSJason Zhu {
109809af6baSJason Zhu 	if (misc_ioctl(dev, IOCTL_REQ_POLL, NULL))
110809af6baSJason Zhu 		return false;
111809af6baSJason Zhu 	else
112809af6baSJason Zhu 		return true;
113cc05bcfaSJason Zhu }
1144298c19dSJason Zhu 
115*5313a4d0SJoseph Chen static int misc_decompress_data_size(struct udevice *dev, u64 *size, u32 cap)
1164298c19dSJason Zhu {
117656bdb59SJoseph Chen 	struct decom_param param;
1184298c19dSJason Zhu 	int ret;
1194298c19dSJason Zhu 
120656bdb59SJoseph Chen 	param.mode = cap;
121656bdb59SJoseph Chen 	param.size_dst = 0; /* clear */
122656bdb59SJoseph Chen 
123656bdb59SJoseph Chen 	ret = misc_ioctl(dev, IOCTL_REQ_DATA_SIZE, &param);
124656bdb59SJoseph Chen 	if (!ret)
125656bdb59SJoseph Chen 		*size = param.size_dst;
126656bdb59SJoseph Chen 
127656bdb59SJoseph Chen 	return ret;
128656bdb59SJoseph Chen }
129656bdb59SJoseph Chen 
130656bdb59SJoseph Chen static int misc_decompress_finish(struct udevice *dev, u32 cap)
131656bdb59SJoseph Chen {
132656bdb59SJoseph Chen 	int timeout = 10000;
1334298c19dSJason Zhu 
1344298c19dSJason Zhu 	while (!misc_decompress_is_complete(dev)) {
1354298c19dSJason Zhu 		if (timeout < 0)
1364298c19dSJason Zhu 			return -EIO;
1374298c19dSJason Zhu 		timeout--;
1384298c19dSJason Zhu 		udelay(10);
1394298c19dSJason Zhu 	}
1404298c19dSJason Zhu 
141656bdb59SJoseph Chen 	return misc_decompress_stop(dev);
142656bdb59SJoseph Chen }
1434298c19dSJason Zhu 
144656bdb59SJoseph Chen int misc_decompress_cleanup(void)
145656bdb59SJoseph Chen {
146656bdb59SJoseph Chen 	const struct misc_ops *ops;
147656bdb59SJoseph Chen 	struct udevice *dev;
148656bdb59SJoseph Chen 	struct uclass *uc;
149656bdb59SJoseph Chen 	int ret;
150656bdb59SJoseph Chen 	u32 cap;
151656bdb59SJoseph Chen 
152656bdb59SJoseph Chen 	ret = uclass_get(UCLASS_MISC, &uc);
153656bdb59SJoseph Chen 	if (ret)
154656bdb59SJoseph Chen 		return 0;
155656bdb59SJoseph Chen 
156656bdb59SJoseph Chen 	/* use "find_" */
157656bdb59SJoseph Chen 	for (uclass_find_first_device(UCLASS_MISC, &dev);
158656bdb59SJoseph Chen 	     dev;
159656bdb59SJoseph Chen 	     uclass_find_next_device(&dev)) {
160656bdb59SJoseph Chen 		ops = device_get_ops(dev);
161656bdb59SJoseph Chen 		if (!ops || !ops->ioctl)
162656bdb59SJoseph Chen 			continue;
163656bdb59SJoseph Chen 		else if (ops->ioctl(dev, IOCTL_REQ_CAPABILITY, &cap))
164656bdb59SJoseph Chen 			continue;
165656bdb59SJoseph Chen 		else if (misc_decomp_async & cap)
166656bdb59SJoseph Chen 			continue;
167656bdb59SJoseph Chen 
168656bdb59SJoseph Chen 		if (misc_decomp_sync & cap) {
16983537505SJoseph Chen 			ret = misc_decompress_finish(dev, cap);
170656bdb59SJoseph Chen 			if (ret) {
171656bdb59SJoseph Chen 				printf("Failed to stop decompress: %s, ret=%d\n",
172656bdb59SJoseph Chen 				       dev->name, ret);
173656bdb59SJoseph Chen 				return ret;
174656bdb59SJoseph Chen 			}
175656bdb59SJoseph Chen 		}
176656bdb59SJoseph Chen 	}
177656bdb59SJoseph Chen 
178656bdb59SJoseph Chen 	return 0;
179656bdb59SJoseph Chen }
180656bdb59SJoseph Chen 
181656bdb59SJoseph Chen int misc_decompress_process(unsigned long dst, unsigned long src,
182656bdb59SJoseph Chen 			    unsigned long src_len, u32 cap, bool sync,
183656bdb59SJoseph Chen 			    u64 *size)
184656bdb59SJoseph Chen {
185656bdb59SJoseph Chen 	struct udevice *dev;
186656bdb59SJoseph Chen 	int ret;
187656bdb59SJoseph Chen 
188656bdb59SJoseph Chen 	dev = misc_decompress_get_device(cap);
189656bdb59SJoseph Chen 	if (!dev)
190656bdb59SJoseph Chen 		return -ENODEV;
191656bdb59SJoseph Chen 
192656bdb59SJoseph Chen 	/* Wait last finish */
193656bdb59SJoseph Chen 	ret = misc_decompress_finish(dev, cap);
194656bdb59SJoseph Chen 	if (ret)
195656bdb59SJoseph Chen 		return ret;
196656bdb59SJoseph Chen 
197656bdb59SJoseph Chen 	ret = misc_decompress_start(dev, dst, src, src_len);
198656bdb59SJoseph Chen 	if (ret)
199656bdb59SJoseph Chen 		return ret;
200656bdb59SJoseph Chen 
201656bdb59SJoseph Chen 	/*
202656bdb59SJoseph Chen 	 * Wait this round finish ?
203656bdb59SJoseph Chen 	 *
204656bdb59SJoseph Chen 	 * If sync, return original data length after decompress done.
205656bdb59SJoseph Chen 	 * otherwise return from compressed file information.
206656bdb59SJoseph Chen 	 */
207656bdb59SJoseph Chen 	if (sync) {
208656bdb59SJoseph Chen 		ret = misc_decompress_finish(dev, cap);
209656bdb59SJoseph Chen 		if (ret)
210656bdb59SJoseph Chen 			return ret;
211656bdb59SJoseph Chen 		if (size)
212656bdb59SJoseph Chen 			ret = misc_decompress_data_size(dev, size, cap);
213656bdb59SJoseph Chen 	} else {
214656bdb59SJoseph Chen 		if (size)
215656bdb59SJoseph Chen 			*size = misc_get_data_size(src, src_len, cap);
216656bdb59SJoseph Chen 	}
217656bdb59SJoseph Chen 
218656bdb59SJoseph Chen 	return ret;
2194298c19dSJason Zhu }
220