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