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