xref: /rk3399_rockchip-uboot/arch/arm/mach-rockchip/resource_img.c (revision e091b6c996a68a6a0faa2bd3ffdd90b3ba5f44ce)
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 <android_ab.h>
9 #include <android_bootloader.h>
10 #include <android_image.h>
11 #include <boot_rkimg.h>
12 #include <bmp_layout.h>
13 #include <malloc.h>
14 #include <asm/io.h>
15 #include <asm/unaligned.h>
16 #include <dm/ofnode.h>
17 #include <linux/list.h>
18 #include <asm/arch/resource_img.h>
19 
20 DECLARE_GLOBAL_DATA_PTR;
21 
22 #define PART_RESOURCE			"resource"
23 #define RESOURCE_MAGIC			"RSCE"
24 #define RESOURCE_MAGIC_SIZE		4
25 #define RESOURCE_VERSION		0
26 #define CONTENT_VERSION			0
27 #define ENTRY_TAG			"ENTR"
28 #define ENTRY_TAG_SIZE			4
29 #define MAX_FILE_NAME_LEN		220
30 #define MAX_HASH_LEN			32
31 #define DTB_FILE			"rk-kernel.dtb"
32 
33 /*
34  *         resource image structure
35  * ----------------------------------------------
36  * |                                            |
37  * |    header  (1 block)                       |
38  * |                                            |
39  * ---------------------------------------------|
40  * |                      |                     |
41  * |    entry0  (1 block) |                     |
42  * |                      |                     |
43  * ------------------------                     |
44  * |                      |                     |
45  * |    entry1  (1 block) | contents (n blocks) |
46  * |                      |                     |
47  * ------------------------                     |
48  * |    ......            |                     |
49  * ------------------------                     |
50  * |                      |                     |
51  * |    entryn  (1 block) |                     |
52  * |                      |                     |
53  * ----------------------------------------------
54  * |                                            |
55  * |    file0  (x blocks)                       |
56  * |                                            |
57  * ----------------------------------------------
58  * |                                            |
59  * |    file1  (y blocks)                       |
60  * |                                            |
61  * ----------------------------------------------
62  * |                   ......                   |
63  * |---------------------------------------------
64  * |                                            |
65  * |    filen  (z blocks)                       |
66  * |                                            |
67  * ----------------------------------------------
68  */
69 
70 /**
71  * struct resource_image_header
72  *
73  * @magic: should be "RSCE"
74  * @version: resource image version, current is 0
75  * @c_version: content version, current is 0
76  * @blks: the size of the header ( 1 block = 512 bytes)
77  * @c_offset: contents offset(by block) in the image
78  * @e_blks: the size(by block) of the entry in the contents
79  * @e_num: numbers of the entrys.
80  */
81 
82 struct resource_img_hdr {
83 	char		magic[4];
84 	uint16_t	version;
85 	uint16_t	c_version;
86 	uint8_t		blks;
87 	uint8_t		c_offset;
88 	uint8_t		e_blks;
89 	uint32_t	e_nums;
90 };
91 
92 struct resource_entry {
93 	char		tag[4];
94 	char		name[MAX_FILE_NAME_LEN];
95 	char		hash[MAX_HASH_LEN];
96 	uint32_t	hash_size;
97 	uint32_t	f_offset;	/* Sector offset */
98 	uint32_t	f_size;		/* Bytes */
99 };
100 
101 struct resource_file {
102 	char		name[MAX_FILE_NAME_LEN];
103 	char		hash[MAX_HASH_LEN];
104 	uint32_t	hash_size;
105 	uint32_t	f_offset;	/* Sector offset */
106 	uint32_t	f_size;		/* Bytes */
107 	struct list_head link;
108 	/* Sector base of resource when ram=false, byte base when ram=true */
109 	uint32_t	rsce_base;
110 	bool		ram;
111 };
112 
113 static LIST_HEAD(entrys_head);
114 
115 int resource_image_check_header(void *rsce_hdr)
116 {
117 	struct resource_img_hdr *hdr = rsce_hdr;
118 	int ret;
119 
120 	ret = memcmp(RESOURCE_MAGIC, hdr->magic, RESOURCE_MAGIC_SIZE);
121 	if (ret) {
122 		debug("bad resource image magic: %s\n",
123 		      hdr->magic ? hdr->magic : "none");
124 		ret = -EINVAL;
125 	}
126 
127 	debug("resource image header:\n");
128 	debug("magic:%s\n", hdr->magic);
129 	debug("version:%d\n", hdr->version);
130 	debug("c_version:%d\n", hdr->c_version);
131 	debug("blks:%d\n", hdr->blks);
132 	debug("c_offset:%d\n", hdr->c_offset);
133 	debug("e_blks:%d\n", hdr->e_blks);
134 	debug("e_num:%d\n", hdr->e_nums);
135 
136 	return ret;
137 }
138 
139 static int add_file_to_list(struct resource_entry *entry,
140 			    int rsce_base, bool ram)
141 {
142 	struct resource_file *file;
143 
144 	if (memcmp(entry->tag, ENTRY_TAG, ENTRY_TAG_SIZE)) {
145 		printf("invalid entry tag\n");
146 		return -ENOENT;
147 	}
148 
149 	file = malloc(sizeof(*file));
150 	if (!file) {
151 		printf("out of memory\n");
152 		return -ENOMEM;
153 	}
154 
155 	strcpy(file->name, entry->name);
156 	file->rsce_base = rsce_base;
157 	file->f_offset = entry->f_offset;
158 	file->f_size = entry->f_size;
159 	file->hash_size = entry->hash_size;
160 	file->ram = ram;
161 	memcpy(file->hash, entry->hash, entry->hash_size);
162 	list_add_tail(&file->link, &entrys_head);
163 
164 	debug("entry: %p, %18s, base: 0x%08x, offset: 0x%08x, size: 0x%08x\n",
165 	      entry, file->name, file->rsce_base, file->f_offset, file->f_size);
166 
167 	return 0;
168 }
169 
170 static int replace_resource_entry(const char *f_name, uint32_t base,
171 				  uint32_t f_offset, uint32_t f_size)
172 {
173 	struct resource_entry *entry;
174 	struct resource_file *file;
175 	struct list_head *node;
176 
177 	if (!f_name || !f_size)
178 		return -EINVAL;
179 
180 	entry = calloc(1, sizeof(*entry));
181 	if (!entry)
182 		return -ENOMEM;
183 
184 	strcpy(entry->tag, ENTRY_TAG);
185 	strcpy(entry->name, f_name);
186 	entry->f_offset = f_offset;
187 	entry->f_size = f_size;
188 	entry->hash_size = 0;
189 
190 	/* Delete exist entry, then add this new */
191 	list_for_each(node, &entrys_head) {
192 		file = list_entry(node, struct resource_file, link);
193 		if (!strcmp(file->name, entry->name)) {
194 			list_del(&file->link);
195 			free(file);
196 			break;
197 		}
198 	}
199 
200 	add_file_to_list(entry, base, false);
201 	free(entry);
202 
203 	return 0;
204 }
205 
206 static int read_bmp(struct blk_desc *dev_desc, const char *name,
207 		    disk_partition_t *part, uint32_t offset,
208 		    uint32_t *size)
209 {
210 	struct bmp_header *header;
211 	u32 blk_start, blk_offset;
212 	u32 filesz;
213 	int ret;
214 
215 	blk_offset = DIV_ROUND_UP(offset, dev_desc->blksz);
216 	blk_start = part->start + blk_offset;
217 	header = memalign(ARCH_DMA_MINALIGN, dev_desc->blksz);
218 	if (!header) {
219 		ret = -ENOMEM;
220 		goto out;
221 	}
222 
223 	if (blk_dread(dev_desc, blk_start, 1, header) != 1) {
224 		ret = -EIO;
225 		goto out;
226 	}
227 
228 	if (header->signature[0] != 'B' || header->signature[1] != 'M') {
229 		ret = -EINVAL;
230 		goto out;
231 	}
232 
233 	filesz = get_unaligned_le32(&header->file_size);
234 	ret = replace_resource_entry(name, blk_start, blk_offset, filesz);
235 	if (!ret) {
236 		printf("LOGO: %s\n", name);
237 		if (size)
238 			*size = filesz;
239 	}
240 out:
241 	free(header);
242 
243 	return ret;
244 }
245 
246 /*
247  * Add logo.bmp and logo_kernel.bmp from "logo" parititon
248  *
249  * Provide a "logo" partition for user to store logo.bmp and
250  * logo_kernel.bmp, so that the user can update them from
251  * kernel or user-space dynamically.
252  *
253  * "logo" partition layout, do not change order:
254  *
255  *   |----------------------| 0x00
256  *   | raw logo.bmp	    |
257  *   |----------------------| N*512-byte aligned
258  *   | raw logo_kernel.bmp  |
259  *   |----------------------|
260  *
261  * N: the sector count of logo.bmp
262  */
263 static int read_logo_bmps(struct blk_desc *dev_desc)
264 {
265 	disk_partition_t part;
266 	u32 filesz;
267 
268 	if (part_get_info_by_name(dev_desc, PART_LOGO, &part) < 0)
269 		return -ENODEV;
270 
271 	if (!read_bmp(dev_desc, "logo.bmp", &part, 0, &filesz))
272 		read_bmp(dev_desc, "logo_kernel.bmp", &part, filesz, NULL);
273 
274 	return 0;
275 }
276 
277 int resource_create_ram_list(struct blk_desc *dev_desc, void *rsce_hdr)
278 {
279 	struct resource_img_hdr *hdr = rsce_hdr;
280 	struct resource_entry *entry;
281 	int e_num, size;
282 	void *data;
283 	int ret = 0;
284 
285 	if (resource_image_check_header(hdr)) {
286 		ret = -EINVAL;
287 		goto out;
288 	}
289 
290 	data = (void *)((ulong)hdr + hdr->c_offset * dev_desc->blksz);
291 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
292 		size = e_num * hdr->e_blks * dev_desc->blksz;
293 		entry = (struct resource_entry *)(data + size);
294 		add_file_to_list(entry, (ulong)hdr, true);
295 	}
296 out:
297 	read_logo_bmps(dev_desc);
298 
299 	return ret;
300 }
301 
302 static int resource_create_list(struct blk_desc *dev_desc, int rsce_base)
303 {
304 	struct resource_img_hdr *hdr;
305 	struct resource_entry *entry;
306 	int blknum, e_num;
307 	void *data = NULL;
308 	int ret = 0;
309 	int size;
310 
311 	hdr = memalign(ARCH_DMA_MINALIGN, dev_desc->blksz);
312 	if (!hdr)
313 		return -ENOMEM;
314 
315 	if (blk_dread(dev_desc, rsce_base, 1, hdr) != 1) {
316 		printf("Failed to read resource hdr\n");
317 		ret = -EIO;
318 		goto err;
319 	}
320 
321 	if (resource_image_check_header(hdr)) {
322 		if (fdt_check_header(hdr)) {
323 			printf("No valid resource or dtb file\n");
324 			ret = -EINVAL;
325 			goto err;
326 		} else {
327 			free(hdr);
328 			return replace_resource_entry(DTB_FILE, rsce_base,
329 						      0, fdt_totalsize(hdr));
330 		}
331 	}
332 
333 	blknum = hdr->e_blks * hdr->e_nums;
334 	data = memalign(ARCH_DMA_MINALIGN, blknum * dev_desc->blksz);
335 	if (!data) {
336 		ret = -ENOMEM;
337 		goto err;
338 	}
339 
340 	if (blk_dread(dev_desc, rsce_base + hdr->c_offset,
341 		      blknum, data) != blknum) {
342 		printf("Failed to read resource entries\n");
343 		ret = -EIO;
344 		goto err;
345 	}
346 
347 	/*
348 	 * Add all file into resource file list, and load what we want from
349 	 * storage when we really need it.
350 	 */
351 	for (e_num = 0; e_num < hdr->e_nums; e_num++) {
352 		size = e_num * hdr->e_blks * dev_desc->blksz;
353 		entry = (struct resource_entry *)(data + size);
354 		add_file_to_list(entry, rsce_base, false);
355 	}
356 
357 err:
358 	if (data)
359 		free(data);
360 	if (hdr)
361 		free(hdr);
362 
363 	read_logo_bmps(dev_desc);
364 
365 	return ret;
366 }
367 
368 static int read_dtb_from_android(struct blk_desc *dev_desc,
369 				 struct andr_img_hdr *hdr,
370 				 ulong rsce_base)
371 {
372 	ulong dtb_offset = 0;
373 	ulong dtb_size = 0;
374 
375 	if (!hdr || hdr->header_version <= 1) {
376 		return 0;
377 	} else if (hdr->header_version == 2) {
378 		dtb_offset += hdr->page_size;
379 		dtb_offset += ALIGN(hdr->kernel_size, hdr->page_size);
380 		dtb_offset += ALIGN(hdr->ramdisk_size, hdr->page_size);
381 		dtb_offset += ALIGN(hdr->recovery_dtbo_size, hdr->page_size) +
382 			      ALIGN(hdr->second_size, hdr->page_size);
383 		dtb_size = hdr->dtb_size;
384 	} else if (hdr->header_version == 3) {
385 		dtb_offset += ALIGN(VENDOR_BOOT_HDR_SIZE,
386 				    hdr->vendor_page_size) +
387 			      ALIGN(hdr->vendor_ramdisk_size,
388 				    hdr->vendor_page_size);
389 		dtb_size = hdr->dtb_size;
390 	}
391 
392 	if (!dtb_size)
393 		return 0;
394 
395 	/*
396 	 * boot_img_hdr_v2,3 feature.
397 	 *
398 	 * If dtb position is present, replace the old with new one if
399 	 * we don't need to verify DTB hash from resource.img file entry.
400 	 */
401 	dtb_offset = DIV_ROUND_UP(dtb_offset, dev_desc->blksz);
402 #ifndef CONFIG_ROCKCHIP_DTB_VERIFY
403 	if (replace_resource_entry(DTB_FILE, rsce_base, dtb_offset, dtb_size))
404 		printf("Failed to load dtb from v2 dtb position\n");
405 	else
406 #endif
407 		env_update("bootargs", "androidboot.dtb_idx=0");
408 
409 	return 0;
410 }
411 
412 static int get_resource_base_sector(struct blk_desc *dev_desc,
413 				    struct andr_img_hdr **ret_hdr)
414 {
415 	disk_partition_t part;
416 	int rsce_base = 0;
417 #ifdef CONFIG_ANDROID_BOOT_IMAGE
418 	struct andr_img_hdr *hdr;
419 	u32 os_ver = 0, os_lvl;
420 	const char *part_boot = PART_BOOT;
421 
422 	/*
423 	 * Anyway, we must read android hdr firstly from boot/recovery partition
424 	 * to get the 'os_version' for android_bcb_msg_sector_offset(), in order
425 	 * to confirm BCB message offset of *MISC* partition.
426 	 */
427 #ifdef CONFIG_ANDROID_AB
428 	part_boot = ab_can_find_recovery_part() ? PART_RECOVERY : PART_BOOT;
429 #endif
430 
431 	if (part_get_info_by_name(dev_desc, part_boot, &part) < 0)
432 		goto resource_part;
433 
434 	hdr = populate_andr_img_hdr(dev_desc, &part);
435 	if (hdr) {
436 		os_ver = hdr->os_version >> 11;
437 		os_lvl = hdr->os_version & ((1U << 11) - 1);
438 		if (os_ver)
439 			gd->bd->bi_andr_version = hdr->os_version;
440 	}
441 
442 #ifndef CONFIG_ANDROID_AB
443 	/* Get boot mode from misc and read if recovery mode */
444 	if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY) {
445 		if (hdr)
446 			free(hdr);
447 
448 		if (part_get_info_by_name(dev_desc, PART_RECOVERY, &part) < 0)
449 			goto resource_part;
450 
451 		hdr = populate_andr_img_hdr(dev_desc, &part);
452 		if (!hdr)
453 			goto resource_part;
454 	}
455 #endif
456 	/* If Android v012, getting resource from second position ! */
457 	if (hdr) {
458 		if (os_ver)
459 			printf("Android %u.%u, Build %u.%u, v%d\n",
460 			       (os_ver >> 14) & 0x7F, (os_ver >> 7) & 0x7F,
461 			       (os_lvl >> 4) + 2000, os_lvl & 0x0F,
462 			       hdr->header_version);
463 		*ret_hdr = hdr;
464 		if (hdr->header_version < 3) {
465 			rsce_base = part.start * dev_desc->blksz;
466 			rsce_base += hdr->page_size;
467 			rsce_base += ALIGN(hdr->kernel_size, hdr->page_size);
468 			rsce_base += ALIGN(hdr->ramdisk_size, hdr->page_size);
469 			rsce_base = DIV_ROUND_UP(rsce_base, dev_desc->blksz);
470 			goto finish;
471 		}
472 	}
473 resource_part:
474 #endif
475 	/* resource partition */
476 	if (part_get_info_by_name(dev_desc, PART_RESOURCE, &part) < 0) {
477 		printf("No resource partition\n");
478 		return -ENODEV;
479 	} else {
480 		rsce_base = part.start;
481 	}
482 #ifdef CONFIG_ANDROID_BOOT_IMAGE
483 finish:
484 #endif
485 	printf("Found DTB in %s part\n", part.name);
486 
487 	return rsce_base;
488 }
489 
490 /*
491  * There are: logo/battery pictures and dtb file in the resource image by default.
492  *
493  * This function does:
494  *
495  * 1. Get resource image base sector from: boot/recovery(AOSP) > resource(RK)
496  * 2. Create resource files list(addition: add logo bmps)
497  * 3. Add dtb from android v2 dtb pos, override the old one from resource file
498  */
499 static int init_resource_list(void)
500 {
501 	struct andr_img_hdr *hdr = NULL;
502 	struct blk_desc *dev_desc;
503 	int rsce_base;
504 
505 	dev_desc = rockchip_get_bootdev();
506 	if (!dev_desc) {
507 		printf("No dev_desc!\n");
508 		return -ENODEV;
509 	}
510 
511 	rsce_base = get_resource_base_sector(dev_desc, &hdr);
512 	if (rsce_base > 0) {
513 		if (resource_create_list(dev_desc, rsce_base))
514 			printf("Failed to create resource list\n");
515 	}
516 
517 	/* override the resource dtb with android dtb if need */
518 	return read_dtb_from_android(dev_desc, hdr, rsce_base);
519 }
520 
521 static struct resource_file *get_file_info(const char *name)
522 {
523 	struct resource_file *file;
524 	struct list_head *node;
525 
526 	if (list_empty(&entrys_head)) {
527 		if (init_resource_list())
528 			return NULL;
529 	}
530 
531 	list_for_each(node, &entrys_head) {
532 		file = list_entry(node, struct resource_file, link);
533 		if (!strcmp(file->name, name))
534 			return file;
535 	}
536 
537 	return NULL;
538 }
539 
540 /*
541  * read file from resource partition
542  * @buf: destination buf to store file data;
543  * @name: file name
544  * @offset: blocks offset in the file, 1 block = 512 bytes
545  * @len: the size(by bytes) of file to read.
546  */
547 int rockchip_read_resource_file(void *buf, const char *name,
548 				int offset, int len)
549 {
550 	struct resource_file *file;
551 	struct blk_desc *dev_desc;
552 	int ret = 0;
553 	int blks;
554 	ulong src;
555 
556 	file = get_file_info(name);
557 	if (!file) {
558 		printf("No file: %s\n", name);
559 		return -ENOENT;
560 	}
561 
562 	dev_desc = rockchip_get_bootdev();
563 	if (!dev_desc) {
564 		printf("No dev_desc!\n");
565 		return -ENODEV;
566 	}
567 
568 	if (len <= 0 || len > file->f_size)
569 		len = file->f_size;
570 
571 	if (file->ram) {
572 		src = file->rsce_base +
573 			(file->f_offset + offset) * dev_desc->blksz;
574 		memcpy(buf, (char *)src, len);
575 		ret = len;
576 	} else {
577 		blks = DIV_ROUND_UP(len, dev_desc->blksz);
578 		ret = blk_dread(dev_desc,
579 				file->rsce_base + file->f_offset + offset,
580 				blks, buf);
581 		ret = (ret != blks) ? -EIO : len;
582 	}
583 
584 	return ret;
585 }
586 
587 #ifdef CONFIG_ROCKCHIP_HWID_DTB
588 #define is_digit(c)		((c) >= '0' && (c) <= '9')
589 #define is_abcd(c)		((c) >= 'a' && (c) <= 'd')
590 #define is_equal(c)		((c) == '=')
591 
592 #define KEY_WORDS_ADC_CTRL	"#_"
593 #define KEY_WORDS_ADC_CH	"_ch"
594 #define KEY_WORDS_GPIO		"#gpio"
595 #define MAX_ADC_CH_NR		10
596 #define MAX_GPIO_NR		10
597 
598 #ifdef CONFIG_ROCKCHIP_GPIO_V2
599 #define GPIO_SWPORT_DDR		0x08
600 #define GPIO_EXT_PORT		0x70
601 #define WMSK_SETBIT(n)		(n << 16 | n)
602 #define WMSK_CLRBIT(n)		(n << 16)
603 #define REG_PLUS4(off, n)	(off + (n >= BIT(16) ? 4 : 0))
604 #define BIT_SUB16(n)		(n >= BIT(16) ? (n >> 16) : n)
605 
606 static int gpio_read(fdt_addr_t gpio_addr,
607 		     int gpio_bank, int gpio_pin)
608 {
609 	uint32_t offset, bit;
610 
611 	bit = gpio_bank * 8 + gpio_pin;
612 
613 	offset = REG_PLUS4(GPIO_SWPORT_DDR, bit);
614 	bit = BIT_SUB16(bit);
615 	writel(WMSK_CLRBIT(bit), gpio_addr + offset);
616 
617 	return readl(gpio_addr + GPIO_EXT_PORT);
618 }
619 
620 #else
621 #define GPIO_SWPORT_DDR		0x04
622 #define GPIO_EXT_PORT		0x50
623 
624 static int gpio_read(fdt_addr_t gpio_addr,
625 		     int gpio_bank, int gpio_pin)
626 {
627 	uint32_t val;
628 
629 	val = readl(gpio_addr + GPIO_SWPORT_DDR);
630 	val &= ~(1 << (gpio_bank * 8 + gpio_pin));
631 	writel(val, gpio_addr + GPIO_SWPORT_DDR);
632 
633 	return readl(gpio_addr + GPIO_EXT_PORT);
634 }
635 #endif
636 
637 /*
638  * How to make it works ?
639  *
640  * 1. pack dtb into rockchip resource.img, require:
641  *    (1) file name end with ".dtb";
642  *    (2) file name contains key words, like: ...#_[controller]_ch[channel]=[value]...dtb
643  *	  @controller: adc controller name in dts, eg. "saradc", ...;
644  *	  @channel: adc channel;
645  *	  @value: adc value;
646  *    eg: ...#_saradc_ch1=223#_saradc_ch2=650....dtb
647  *
648  * 2. U-Boot dtsi about adc controller node:
649  *    (1) enable "u-boot,dm-pre-reloc;";
650  *    (2) must set status "okay";
651  */
652 static int rockchip_read_dtb_by_adc(const char *file_name)
653 {
654 	static int cached_v[MAX_ADC_CH_NR];
655 	int offset_ctrl = strlen(KEY_WORDS_ADC_CTRL);
656 	int offset_ch = strlen(KEY_WORDS_ADC_CH);
657 	int ret, channel, len = 0, found = 0, margin = 30;
658 	char *stradc, *strch, *p;
659 	char adc_v_string[10];
660 	char dev_name[32];
661 	uint32_t raw_adc;
662 	ulong dtb_adc;
663 
664 	debug("%s: %s\n", __func__, file_name);
665 
666 	/* Invalid format ? */
667 	stradc = strstr(file_name, KEY_WORDS_ADC_CTRL);
668 	while (stradc) {
669 		debug("   - substr: %s\n", stradc);
670 
671 		/* Parse controller name */
672 		strch = strstr(stradc, KEY_WORDS_ADC_CH);
673 		len = strch - (stradc + offset_ctrl);
674 		strlcpy(dev_name, stradc + offset_ctrl, len + 1);
675 
676 		/* Parse adc channel */
677 		p = strch + offset_ch;
678 		if (is_digit(*p) && is_equal(*(p + 1))) {
679 			channel = *p - '0';
680 		} else {
681 			debug("   - invalid format: %s\n", stradc);
682 			return -EINVAL;
683 		}
684 
685 		/*
686 		 * Read raw adc value
687 		 *
688 		 * It doesn't need to read adc value every loop, reading once
689 		 * is enough. We use cached_v[] to save what we have read, zero
690 		 * means not read before.
691 		 */
692 		if (cached_v[channel] == 0) {
693 			ret = adc_channel_single_shot(dev_name,
694 						      channel, &raw_adc);
695 			if (ret) {
696 				debug("   - failed to read adc, ret=%d\n", ret);
697 				return ret;
698 			}
699 			cached_v[channel] = raw_adc;
700 		}
701 
702 		/* Parse dtb adc value */
703 		p = strch + offset_ch + 2;	/* 2: channel and '=' */
704 		while (*p && is_digit(*p)) {
705 			len++;
706 			p++;
707 		}
708 		strlcpy(adc_v_string, strch + offset_ch + 2, len + 1);
709 		dtb_adc = simple_strtoul(adc_v_string, NULL, 10);
710 
711 		if (abs(dtb_adc - cached_v[channel]) <= margin) {
712 			found = 1;
713 			stradc = strstr(p, KEY_WORDS_ADC_CTRL);
714 		} else {
715 			found = 0;
716 			break;
717 		}
718 
719 		debug("   - parse: controller=%s, channel=%d, dtb_adc=%ld, read=%d %s\n",
720 		      dev_name, channel, dtb_adc, cached_v[channel], found ? "(Y)" : "");
721 	}
722 
723 	return found ? 0 : -ENOENT;
724 }
725 
726 static int gpio_parse_base_address(fdt_addr_t *gpio_base_addr)
727 {
728 	static int initialized;
729 	ofnode parent, node;
730 	int idx = 0;
731 
732 	if (initialized)
733 		return 0;
734 
735 	parent = ofnode_path("/pinctrl");
736 	if (!ofnode_valid(parent)) {
737 		debug("   - Can't find pinctrl node\n");
738 		return -EINVAL;
739 	}
740 
741 	ofnode_for_each_subnode(node, parent) {
742 		if (!ofnode_get_property(node, "gpio-controller", NULL)) {
743 			debug("   - Not gpio controller node\n");
744 			continue;
745 		}
746 		gpio_base_addr[idx] = ofnode_get_addr(node);
747 		debug("   - gpio%d: 0x%x\n", idx, (uint32_t)gpio_base_addr[idx]);
748 		idx++;
749 	}
750 
751 	if (idx == 0) {
752 		debug("   - parse gpio address failed\n");
753 		return -EINVAL;
754 	}
755 
756 	initialized = 1;
757 
758 	return 0;
759 }
760 
761 /*
762  * How to make it works ?
763  *
764  * 1. pack dtb into rockchip resource.img, require:
765  *    (1) file name end with ".dtb";
766  *    (2) file name contains key words, like: ...#gpio[pin]=[value]...dtb
767  *	  @pin: gpio name, eg. 0a2 means GPIO0A2;
768  *	  @value: gpio level, 0 or 1;
769  *    eg: ...#gpio0a6=1#gpio1c2=0....dtb
770  *
771  * 2. U-Boot dtsi about gpio node:
772  *    (1) enable "u-boot,dm-pre-reloc;" for [all] gpio node;
773  *    (2) set all gpio status "disabled"(Because we just want their property);
774  */
775 static int rockchip_read_dtb_by_gpio(const char *file_name)
776 {
777 	static uint32_t cached_v[MAX_GPIO_NR];
778 	static fdt_addr_t gpio_base_addr[MAX_GPIO_NR];
779 	int ret, found = 0, offset = strlen(KEY_WORDS_GPIO);
780 	uint8_t port, pin, bank, lvl, val;
781 	char *strgpio, *p;
782 	uint32_t bit;
783 
784 	debug("[*] %s\n", file_name);
785 
786 	/* Parse gpio address */
787 	memset(gpio_base_addr, 0, sizeof(gpio_base_addr));
788 	ret = gpio_parse_base_address(gpio_base_addr);
789 	if (ret) {
790 		debug("   - Can't parse gpio base address: %d\n", ret);
791 		return ret;
792 	}
793 
794 	strgpio = strstr(file_name, KEY_WORDS_GPIO);
795 	while (strgpio) {
796 		debug("   - substr: %s\n", strgpio);
797 
798 		p = strgpio + offset;
799 
800 		/* Invalid format ? */
801 		if (!(is_digit(*(p + 0)) && is_abcd(*(p + 1)) &&
802 		      is_digit(*(p + 2)) && is_equal(*(p + 3)) &&
803 		      is_digit(*(p + 4)))) {
804 			debug("   - invalid format: %s\n", strgpio);
805 			return -EINVAL;
806 		}
807 
808 		/* Read gpio value */
809 		port = *(p + 0) - '0';
810 		bank = *(p + 1) - 'a';
811 		pin  = *(p + 2) - '0';
812 		lvl  = *(p + 4) - '0';
813 
814 		/*
815 		 * It doesn't need to read gpio value every loop, reading once
816 		 * is enough. We use cached_v[] to save what we have read, zero
817 		 * means not read before.
818 		 */
819 		if (cached_v[port] == 0) {
820 			if (!gpio_base_addr[port]) {
821 				debug("   - can't find gpio%d base address\n", port);
822 				return 0;
823 			}
824 			cached_v[port] = gpio_read(gpio_base_addr[port], bank, pin);
825 			debug("   - gpio-val[%d]: 0x%08x\n", port, cached_v[port]);
826 		}
827 
828 		/* Verify result */
829 		bit = bank * 8 + pin;
830 		val = cached_v[port] & (1 << bit) ? 1 : 0;
831 		if (val == !!lvl) {
832 			found = 1;
833 			strgpio = strstr(p, KEY_WORDS_GPIO);
834 		} else {
835 			found = 0;
836 			debug("   - parse: gpio%d%c%d=%d, read=%d %s\n",
837 			      port, bank + 'a', pin, lvl, val, found ? "(Y)" : "(N)");
838 			break;
839 		}
840 
841 		debug("   - parse: gpio%d%c%d=%d, read=%d %s\n",
842 		      port, bank + 'a', pin, lvl, val, found ? "(Y)" : "(N)");
843 	}
844 
845 	debug("   # result: %s\n", found ? "OK" : "Try next one ..");
846 
847 	return found ? 0 : -ENOENT;
848 }
849 
850 /* Get according to hardware id(GPIO/ADC) */
851 static struct resource_file *rockchip_read_hwid_dtb(void)
852 {
853 	struct resource_file *file;
854 	struct list_head *node;
855 
856 	if (list_empty(&entrys_head)) {
857 		if (init_resource_list())
858 			return NULL;
859 	}
860 
861 	/* Find dtb file according to hardware id(GPIO/ADC) */
862 	list_for_each(node, &entrys_head) {
863 		file = list_entry(node, struct resource_file, link);
864 		if (!strstr(file->name, ".dtb"))
865 			continue;
866 
867 		if (strstr(file->name, KEY_WORDS_ADC_CTRL) &&
868 		    strstr(file->name, KEY_WORDS_ADC_CH) &&
869 		    !rockchip_read_dtb_by_adc(file->name)) {
870 			return file;
871 		} else if (strstr(file->name, KEY_WORDS_GPIO) &&
872 			   !rockchip_read_dtb_by_gpio(file->name)) {
873 			return file;
874 		}
875 	}
876 
877 	return NULL;
878 }
879 #endif
880 
881 int rockchip_read_resource_dtb(void *fdt_addr, char **hash, int *hash_size)
882 {
883 	struct resource_file *file;
884 	int ret;
885 
886 #ifdef CONFIG_ROCKCHIP_HWID_DTB
887 	file = rockchip_read_hwid_dtb();
888 	/* If dtbs matched hardware id(GPIO/ADC) not found, try the default */
889 	if (!file)
890 		file = get_file_info(DTB_FILE);
891 #else
892 	file = get_file_info(DTB_FILE);
893 #endif
894 	if (!file)
895 		return -ENODEV;
896 
897 	ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0);
898 	if (ret < 0)
899 		return ret;
900 
901 	if (fdt_check_header(fdt_addr))
902 		return -EBADF;
903 
904 	*hash = file->hash;
905 	*hash_size = file->hash_size;
906 	printf("DTB: %s\n", file->name);
907 
908 	return 0;
909 }
910