xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/resource_img.c (revision c2ba77d93f696c0ccb8f2b653571104e7b4afb4e)
1 /*
2  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 #include <common.h>
7 #include <adc.h>
8 #include <asm/io.h>
9 #include <malloc.h>
10 #include <linux/list.h>
11 #include <asm/arch/resource_img.h>
12 #include <boot_rkimg.h>
13 #include <dm/ofnode.h>
14 #ifdef CONFIG_ANDROID_AB
15 #include <android_avb/libavb_ab.h>
16 #include <android_avb/rk_avb_ops_user.h>
17 #endif
18 #ifdef CONFIG_ANDROID_BOOT_IMAGE
19 #include <android_bootloader.h>
20 #include <android_image.h>
21 #endif
22 
23 DECLARE_GLOBAL_DATA_PTR;
24 
25 #define PART_RESOURCE			"resource"
26 #define RESOURCE_MAGIC			"RSCE"
27 #define RESOURCE_MAGIC_SIZE		4
28 #define RESOURCE_VERSION		0
29 #define CONTENT_VERSION			0
30 #define ENTRY_TAG			"ENTR"
31 #define ENTRY_TAG_SIZE			4
32 #define MAX_FILE_NAME_LEN		256
33 
34 /*
35  *         resource image structure
36  * ----------------------------------------------
37  * |                                            |
38  * |    header  (1 block)                       |
39  * |                                            |
40  * ---------------------------------------------|
41  * |                      |                     |
42  * |    entry0  (1 block) |                     |
43  * |                      |                     |
44  * ------------------------                     |
45  * |                      |                     |
46  * |    entry1  (1 block) | contents (n blocks) |
47  * |                      |                     |
48  * ------------------------                     |
49  * |    ......            |                     |
50  * ------------------------                     |
51  * |                      |                     |
52  * |    entryn  (1 block) |                     |
53  * |                      |                     |
54  * ----------------------------------------------
55  * |                                            |
56  * |    file0  (x blocks)                       |
57  * |                                            |
58  * ----------------------------------------------
59  * |                                            |
60  * |    file1  (y blocks)                       |
61  * |                                            |
62  * ----------------------------------------------
63  * |                   ......                   |
64  * |---------------------------------------------
65  * |                                            |
66  * |    filen  (z blocks)                       |
67  * |                                            |
68  * ----------------------------------------------
69  */
70 
71 /**
72  * struct resource_image_header
73  *
74  * @magic: should be "RSCE"
75  * @version: resource image version, current is 0
76  * @c_version: content version, current is 0
77  * @blks: the size of the header ( 1 block = 512 bytes)
78  * @c_offset: contents offset(by block) in the image
79  * @e_blks: the size(by block) of the entry in the contents
80  * @e_num: numbers of the entrys.
81  */
82 
83 struct resource_img_hdr {
84 	char		magic[4];
85 	uint16_t	version;
86 	uint16_t	c_version;
87 	uint8_t		blks;
88 	uint8_t		c_offset;
89 	uint8_t		e_blks;
90 	uint32_t	e_nums;
91 };
92 
93 struct resource_entry {
94 	char		tag[4];
95 	char		name[MAX_FILE_NAME_LEN];
96 	uint32_t	f_offset;
97 	uint32_t	f_size;
98 };
99 
100 struct resource_file {
101 	char		name[MAX_FILE_NAME_LEN];
102 	uint32_t	f_offset;
103 	uint32_t	f_size;
104 	struct list_head link;
105 	uint32_t 	rsce_base;	/* Base addr of resource */
106 };
107 
108 static LIST_HEAD(entrys_head);
109 
110 static int resource_image_check_header(const struct resource_img_hdr *hdr)
111 {
112 	int ret;
113 
114 	ret = memcmp(RESOURCE_MAGIC, hdr->magic, RESOURCE_MAGIC_SIZE);
115 	if (ret) {
116 		printf("bad resource image magic: %s\n",
117 		       hdr->magic ? hdr->magic : "none");
118 		ret = -EINVAL;
119 	}
120 	debug("resource image header:\n");
121 	debug("magic:%s\n", hdr->magic);
122 	debug("version:%d\n", hdr->version);
123 	debug("c_version:%d\n", hdr->c_version);
124 	debug("blks:%d\n", hdr->blks);
125 	debug("c_offset:%d\n", hdr->c_offset);
126 	debug("e_blks:%d\n", hdr->e_blks);
127 	debug("e_num:%d\n", hdr->e_nums);
128 
129 	return ret;
130 }
131 
132 static int add_file_to_list(struct resource_entry *entry, int rsce_base)
133 {
134 	struct resource_file *file;
135 
136 	if (memcmp(entry->tag, ENTRY_TAG, ENTRY_TAG_SIZE)) {
137 		printf("invalid entry tag\n");
138 		return -ENOENT;
139 	}
140 	file = malloc(sizeof(*file));
141 	if (!file) {
142 		printf("out of memory\n");
143 		return -ENOMEM;
144 	}
145 	strcpy(file->name, entry->name);
146 	file->rsce_base = rsce_base;
147 	file->f_offset = entry->f_offset;
148 	file->f_size = entry->f_size;
149 	list_add_tail(&file->link, &entrys_head);
150 	debug("entry:%p  %s offset:%d size:%d\n",
151 	      entry, file->name, file->f_offset, file->f_size);
152 
153 	return 0;
154 }
155 
156 static int init_resource_list(struct resource_img_hdr *hdr)
157 {
158 	struct resource_entry *entry;
159 	void *content;
160 	int size;
161 	int ret;
162 	int e_num;
163 	int offset = 0;
164 	int resource_found = 0;
165 	struct blk_desc *dev_desc;
166 	disk_partition_t part_info;
167 	char *boot_partname = PART_BOOT;
168 
169 /*
170  * Primary detect AOSP format image, try to get resource image from
171  * boot/recovery partition. If not, it's an RK format image and try
172  * to get from resource partition.
173  */
174 #ifdef CONFIG_ANDROID_BOOT_IMAGE
175 	struct andr_img_hdr *andr_hdr;
176 #endif
177 
178 	if (hdr) {
179 		content = (void *)((char *)hdr
180 				   + (hdr->c_offset) * RK_BLK_SIZE);
181 		for (e_num = 0; e_num < hdr->e_nums; e_num++) {
182 			size = e_num * hdr->e_blks * RK_BLK_SIZE;
183 			entry = (struct resource_entry *)(content + size);
184 			add_file_to_list(entry, offset);
185 		}
186 		return 0;
187 	}
188 
189 	dev_desc = rockchip_get_bootdev();
190 	if (!dev_desc) {
191 		printf("%s: dev_desc is NULL!\n", __func__);
192 		return -ENODEV;
193 	}
194 	hdr = memalign(ARCH_DMA_MINALIGN, RK_BLK_SIZE);
195 	if (!hdr) {
196 		printf("%s: out of memory!\n", __func__);
197 		return -ENOMEM;
198 	}
199 
200 #ifdef CONFIG_ANDROID_BOOT_IMAGE
201 	/* Get boot mode from misc */
202 #ifndef CONFIG_ANDROID_AB
203 	if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY)
204 		boot_partname = PART_RECOVERY;
205 #endif
206 
207 	/* Read boot/recovery and chenc if this is an AOSP img */
208 #ifdef CONFIG_ANDROID_AB
209 	char slot_suffix[3] = {0};
210 
211 	if (rk_avb_get_current_slot(slot_suffix))
212 		goto out;
213 	boot_partname = android_str_append(boot_partname, slot_suffix);
214 	if (boot_partname == NULL)
215 		goto out;
216 #endif
217 	ret = part_get_info_by_name(dev_desc, boot_partname, &part_info);
218 	if (ret < 0) {
219 		printf("%s: failed to get %s part, ret=%d\n",
220 		       __func__, boot_partname, ret);
221 		/* RKIMG can support part table without 'boot' */
222 		goto next;
223 	}
224 
225 	/*
226 	 * Only read header and check magic, is a AOSP format image?
227 	 * If so, get resource image from second part.
228 	 */
229 	andr_hdr = (void *)hdr;
230 	ret = blk_dread(dev_desc, part_info.start, 1, andr_hdr);
231 	if (ret != 1) {
232 		printf("%s: failed to read %s hdr, ret=%d\n",
233 		       __func__, part_info.name, ret);
234 		goto out;
235 	}
236 	ret = android_image_check_header(andr_hdr);
237 	if (!ret) {
238 		debug("%s: Load resource from %s second pos\n",
239 		      __func__, part_info.name);
240 		/* Read resource from second offset */
241 		offset = part_info.start * RK_BLK_SIZE;
242 		offset += andr_hdr->page_size;
243 		offset += ALIGN(andr_hdr->kernel_size, andr_hdr->page_size);
244 		offset += ALIGN(andr_hdr->ramdisk_size, andr_hdr->page_size);
245 		offset = offset / RK_BLK_SIZE;
246 
247 		resource_found = 1;
248 	}
249 next:
250 #endif
251 	/*
252 	 * If not found resource image in AOSP format images(boot/recovery part),
253 	 * try to read RK format images(resource part).
254 	 */
255 	if (!resource_found) {
256 		debug("%s: Load resource from resource part\n", __func__);
257 		/* Read resource from Rockchip Resource partition */
258 		boot_partname = PART_RESOURCE;
259 		ret = part_get_info_by_name(dev_desc, boot_partname, &part_info);
260 		if (ret < 0) {
261 			printf("%s: failed to get resource part, ret=%d\n",
262 			       __func__, ret);
263 			goto out;
264 		}
265 		offset = part_info.start;
266 	}
267 
268 	/* Only read header and check magic */
269 	ret = blk_dread(dev_desc, offset, 1, hdr);
270 	if (ret != 1) {
271 		printf("%s: failed to read resource hdr, ret=%d\n",
272 		       __func__, ret);
273 		goto out;
274 	}
275 
276 	ret = resource_image_check_header(hdr);
277 	if (ret < 0)
278 		goto out;
279 
280 	content = memalign(ARCH_DMA_MINALIGN,
281 			   hdr->e_blks * hdr->e_nums * RK_BLK_SIZE);
282 	if (!content) {
283 		printf("%s: failed to alloc memory for content\n", __func__);
284 		goto out;
285 	}
286 
287 	/* Read all entries from resource image */
288 	ret = blk_dread(dev_desc, offset + hdr->c_offset,
289 			hdr->e_blks * hdr->e_nums, content);
290 	if (ret != (hdr->e_blks * hdr->e_nums)) {
291 		printf("%s: failed to read resource entries, ret=%d\n",
292 		       __func__, ret);
293 		goto err;
294 	}
295 
296 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
297 		size = e_num * hdr->e_blks * RK_BLK_SIZE;
298 		entry = (struct resource_entry *)(content + size);
299 		add_file_to_list(entry, offset);
300 	}
301 
302 	printf("Load FDT from %s part\n", boot_partname);
303 err:
304 	free(content);
305 out:
306 	free(hdr);
307 
308 	return 0;
309 }
310 
311 static struct resource_file *get_file_info(struct resource_img_hdr *hdr,
312 					   const char *name)
313 {
314 	struct resource_file *file;
315 	struct list_head *node;
316 
317 	if (list_empty(&entrys_head))
318 		init_resource_list(hdr);
319 
320 	list_for_each(node, &entrys_head) {
321 		file = list_entry(node, struct resource_file, link);
322 		if (!strcmp(file->name, name))
323 			return file;
324 	}
325 
326 	return NULL;
327 }
328 
329 int rockchip_get_resource_file(void *buf, const char *name)
330 {
331 	struct resource_file *file;
332 
333 	file = get_file_info(buf, name);
334 
335 	return file->f_offset;
336 }
337 
338 /*
339  * read file from resource partition
340  * @buf: destination buf to store file data;
341  * @name: file name
342  * @offset: blocks offset in the file, 1 block = 512 bytes
343  * @len: the size(by bytes) of file to read.
344  */
345 int rockchip_read_resource_file(void *buf, const char *name,
346 				int offset, int len)
347 {
348 	struct resource_file *file;
349 	int ret = 0;
350 	int blks;
351 	struct blk_desc *dev_desc;
352 
353 	file = get_file_info(NULL, name);
354 	if (!file) {
355 		printf("Can't find file:%s\n", name);
356 		return -ENOENT;
357 	}
358 
359 	if (len <= 0 || len > file->f_size)
360 		len = file->f_size;
361 	blks = DIV_ROUND_UP(len, RK_BLK_SIZE);
362 	dev_desc = rockchip_get_bootdev();
363 	if (!dev_desc) {
364 		printf("%s: dev_desc is NULL!\n", __func__);
365 		return -ENODEV;
366 	}
367 	ret = blk_dread(dev_desc, file->rsce_base + file->f_offset + offset,
368 			blks, buf);
369 	if (ret != blks)
370 		ret = -EIO;
371 	else
372 		ret = len;
373 
374 	return ret;
375 }
376 
377 #define is_digit(c)		((c) >= '0' && (c) <= '9')
378 #define is_abcd(c)		((c) >= 'a' && (c) <= 'd')
379 #define is_equal(c)		((c) == '=')
380 
381 #define DTB_FILE		"rk-kernel.dtb"
382 #define KEY_WORDS_ADC_CTRL	"#_"
383 #define KEY_WORDS_ADC_CH	"_ch"
384 #define KEY_WORDS_GPIO		"#gpio"
385 #define GPIO_EXT_PORT		0x50
386 #define MAX_ADC_CH_NR		10
387 #define MAX_GPIO_NR		10
388 
389 #ifdef CONFIG_ADC
390 /*
391  * How to make it works ?
392  *
393  * 1. pack dtb into rockchip resource.img, require:
394  *    (1) file name end with ".dtb";
395  *    (2) file name contains key words, like: ...#_[controller]_ch[channel]=[value]...dtb
396  *	  @controller: adc controller name in dts, eg. "saradc", ...;
397  *	  @channel: adc channel;
398  *	  @value: adc value;
399  *    eg: ...#_saradc_ch1=223#_saradc_ch2=650....dtb
400  *
401  * 2. U-Boot dtsi about adc controller node:
402  *    (1) enable "u-boot,dm-pre-reloc;";
403  *    (2) must set status "okay";
404  */
405 static int rockchip_read_dtb_by_adc(const char *file_name)
406 {
407 	static int cached_v[MAX_ADC_CH_NR];
408 	int offset_ctrl = strlen(KEY_WORDS_ADC_CTRL);
409 	int offset_ch = strlen(KEY_WORDS_ADC_CH);
410 	int ret, channel, len = 0, found = 0, margin = 30;
411 	uint32_t raw_adc;
412 	unsigned long dtb_adc;
413 	char *stradc, *strch, *p;
414 	char adc_v_string[10];
415 	char dev_name[32];
416 
417 	debug("%s: %s\n", __func__, file_name);
418 
419 	/* Invalid format ? */
420 	stradc = strstr(file_name, KEY_WORDS_ADC_CTRL);
421 	while (stradc) {
422 		debug("   - substr: %s\n", stradc);
423 
424 		/* Parse controller name */
425 		strch = strstr(stradc, KEY_WORDS_ADC_CH);
426 		len = strch - (stradc + offset_ctrl);
427 		strlcpy(dev_name, stradc + offset_ctrl, len + 1);
428 
429 		/* Parse adc channel */
430 		p = strch + offset_ch;
431 		if (is_digit(*p) && is_equal(*(p + 1))) {
432 			channel = *p - '0';
433 		} else {
434 			debug("   - invalid format: %s\n", stradc);
435 			return -EINVAL;
436 		}
437 
438 		/*
439 		 * Read raw adc value
440 		 *
441 		 * It doesn't need to read adc value every loop, reading once
442 		 * is enough. We use cached_v[] to save what we have read, zero
443 		 * means not read before.
444 		 */
445 		if (cached_v[channel] == 0) {
446 			ret = adc_channel_single_shot(dev_name,
447 						      channel, &raw_adc);
448 			if (ret) {
449 				debug("   - failed to read adc, ret=%d\n", ret);
450 				return ret;
451 			}
452 			cached_v[channel] = raw_adc;
453 		}
454 
455 		/* Parse dtb adc value */
456 		p = strch + offset_ch + 2;	/* 2: channel and '=' */
457 		while (*p && is_digit(*p)) {
458 			len++;
459 			p++;
460 		}
461 		strlcpy(adc_v_string, strch + offset_ch + 2, len + 1);
462 		dtb_adc = simple_strtoul(adc_v_string, NULL, 10);
463 
464 		if (abs(dtb_adc - cached_v[channel]) <= margin) {
465 			found = 1;
466 			stradc = strstr(p, KEY_WORDS_ADC_CTRL);
467 		} else {
468 			found = 0;
469 			break;
470 		}
471 
472 		debug("   - parse: controller=%s, channel=%d, dtb_adc=%ld, read=%d %s\n",
473 		      dev_name, channel, dtb_adc, cached_v[channel], found ? "(Y)" : "");
474 	}
475 
476 	return found ? 0 : -ENOENT;
477 }
478 #else
479 static int rockchip_read_dtb_by_adc(const char *file_name)
480 {
481 	return  -ENOENT;
482 }
483 #endif
484 
485 static int gpio_parse_base_address(fdt_addr_t *gpio_base_addr)
486 {
487 	static int initial;
488 	ofnode parent, node;
489 	int i = 0;
490 
491 	if (initial)
492 		return 0;
493 
494 	parent = ofnode_path("/pinctrl");
495 	if (!ofnode_valid(parent)) {
496 		debug("   - Can't find pinctrl node\n");
497 		return -EINVAL;
498 	}
499 
500 	ofnode_for_each_subnode(node, parent) {
501 		if (!ofnode_get_property(node, "gpio-controller", NULL)) {
502 			debug("   - Can't find gpio-controller\n");
503 			continue;
504 		}
505 
506 		gpio_base_addr[i++] = ofnode_get_addr(node);
507 		debug("   - gpio%d: 0x%x\n", i - 1, (uint32_t)gpio_base_addr[i - 1]);
508 	}
509 
510 	if (i == 0) {
511 		debug("   - parse gpio address failed\n");
512 		return -EINVAL;
513 	}
514 
515 	initial = 1;
516 
517 	return 0;
518 }
519 
520 /*
521  * How to make it works ?
522  *
523  * 1. pack dtb into rockchip resource.img, require:
524  *    (1) file name end with ".dtb";
525  *    (2) file name contains key words, like: ...#gpio[pin]=[value]...dtb
526  *	  @pin: gpio name, eg. 0a2 means GPIO0A2;
527  *	  @value: gpio level, 0 or 1;
528  *    eg: ...#gpio0a6=1#gpio1c2=0....dtb
529  *
530  * 2. U-Boot dtsi about gpio node:
531  *    (1) enable "u-boot,dm-pre-reloc;" for all gpio node;
532  *    (2) set all gpio status "disabled"(Because we just want their property);
533  */
534 static int rockchip_read_dtb_by_gpio(const char *file_name)
535 {
536 	static uint32_t cached_v[MAX_GPIO_NR];
537 	fdt_addr_t gpio_base_addr[MAX_GPIO_NR];
538 	int ret, found = 0, offset = strlen(KEY_WORDS_GPIO);
539 	uint8_t port, pin, bank, lvl, val;
540 	char *strgpio, *p;
541 	uint32_t bit;
542 
543 	debug("%s\n", file_name);
544 
545 	strgpio = strstr(file_name, KEY_WORDS_GPIO);
546 	while (strgpio) {
547 		debug("   - substr: %s\n", strgpio);
548 
549 		p = strgpio + offset;
550 
551 		/* Invalid format ? */
552 		if (!(is_digit(*(p + 0)) && is_abcd(*(p + 1)) &&
553 		      is_digit(*(p + 2)) && is_equal(*(p + 3)) &&
554 		      is_digit(*(p + 4)))) {
555 			debug("   - invalid format: %s\n", strgpio);
556 			return -EINVAL;
557 		}
558 
559 		/* Parse gpio address */
560 		ret = gpio_parse_base_address(gpio_base_addr);
561 		if (ret) {
562 			debug("   - Can't parse gpio base address: %d\n", ret);
563 			return ret;
564 		}
565 
566 		/* Read gpio value */
567 		port = *(p + 0) - '0';
568 		bank = *(p + 1) - 'a';
569 		pin  = *(p + 2) - '0';
570 		lvl  = *(p + 4) - '0';
571 
572 		/*
573 		 * It doesn't need to read gpio value every loop, reading once
574 		 * is enough. We use cached_v[] to save what we have read, zero
575 		 * means not read before.
576 		 */
577 		if (cached_v[port] == 0)
578 			cached_v[port] =
579 				readl(gpio_base_addr[port] + GPIO_EXT_PORT);
580 
581 		/* Verify result */
582 		bit = bank * 8 + pin;
583 		val = cached_v[port] & (1 << bit) ? 1 : 0;
584 
585 		if (val == !!lvl) {
586 			found = 1;
587 			strgpio = strstr(p, KEY_WORDS_GPIO);
588 		} else {
589 			found = 0;
590 			break;
591 		}
592 
593 		debug("   - parse: gpio%d%c%d=%d, read=%d %s\n",
594 		      port, bank + 'a', pin, lvl, val, found ? "(Y)" : "");
595 	}
596 
597 	return found ? 0 : -ENOENT;
598 }
599 
600 int rockchip_read_dtb_file(void *fdt_addr)
601 {
602 	struct resource_file *file;
603 	struct list_head *node;
604 	char *dtb_name = DTB_FILE;
605 	int ret;
606 
607 	if (list_empty(&entrys_head))
608 		init_resource_list(NULL);
609 
610 	list_for_each(node, &entrys_head) {
611 		file = list_entry(node, struct resource_file, link);
612 		if (!strstr(file->name, ".dtb"))
613 			continue;
614 
615 		if (strstr(file->name, KEY_WORDS_ADC_CTRL) &&
616 		    strstr(file->name, KEY_WORDS_ADC_CH) &&
617 		    !rockchip_read_dtb_by_adc(file->name)) {
618 			dtb_name = file->name;
619 			break;
620 		} else if (strstr(file->name, KEY_WORDS_GPIO) &&
621 			   !rockchip_read_dtb_by_gpio(file->name)) {
622 			dtb_name = file->name;
623 			break;
624 		}
625 	}
626 
627 	printf("DTB: %s\n", dtb_name);
628 
629 	ret = rockchip_read_resource_file((void *)fdt_addr, dtb_name, 0, 0);
630 	if (ret < 0)
631 		return ret;
632 
633 #if defined(CONFIG_CMD_DTIMG) && defined(CONFIG_OF_LIBFDT_OVERLAY)
634 	android_fdt_overlay_apply((void *)fdt_addr);
635 #endif
636 
637 	return ret;
638 }
639