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