1054229abSJason Zhu /* 2054229abSJason Zhu * (C) Copyright 2019 Rockchip Electronics Co., Ltd 3054229abSJason Zhu * 4054229abSJason Zhu * SPDX-License-Identifier: GPL-2.0+ 5054229abSJason Zhu */ 6054229abSJason Zhu 7054229abSJason Zhu #include <common.h> 822dccd11SJason Zhu #include <blk.h> 922dccd11SJason Zhu #include <boot_rkimg.h> 10054229abSJason Zhu #include <dm.h> 11054229abSJason Zhu #include <errno.h> 12661bcdfeSJason Zhu #include <image.h> 1322dccd11SJason Zhu #include <malloc.h> 14054229abSJason Zhu #include <nand.h> 1522dccd11SJason Zhu #include <part.h> 163fb7bf02SJason Zhu #include <spi.h> 17054229abSJason Zhu #include <dm/device-internal.h> 183fb7bf02SJason Zhu #include <linux/mtd/spi-nor.h> 196524556dSJon Lin #ifdef CONFIG_NAND 206524556dSJon Lin #include <linux/mtd/nand.h> 216524556dSJon Lin #endif 22054229abSJason Zhu 2322dccd11SJason Zhu #define MTD_PART_NAND_HEAD "mtdparts=" 2404a8326aSJason Zhu #define MTD_ROOT_PART_NUM "ubi.mtd=" 25*cc0876a8SWeiwen Chen #define MTD_ROOT_PART_NAME_UBIFS "root=ubi0:rootfs" 26*cc0876a8SWeiwen Chen #define MTD_ROOT_PART_NAME_SQUASHFS "root=/dev/ubiblock0_0" 2722dccd11SJason Zhu #define MTD_PART_INFO_MAX_SIZE 512 2822dccd11SJason Zhu #define MTD_SINGLE_PART_INFO_MAX_SIZE 40 2922dccd11SJason Zhu 30c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_UNKNOWN (-2) 31c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_SHIFT (-1) 32c9e94690SJon Lin 331f21bf61SJon Lin static int *mtd_map_blk_table; 341f21bf61SJon Lin 351f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc, 361f21bf61SJon Lin loff_t offset, 371f21bf61SJon Lin size_t length) 381f21bf61SJon Lin { 391f21bf61SJon Lin u32 blk_total, blk_begin, blk_cnt; 401f21bf61SJon Lin struct mtd_info *mtd = NULL; 411f21bf61SJon Lin int i, j; 421f21bf61SJon Lin 431f21bf61SJon Lin if (!desc) 441f21bf61SJon Lin return -ENODEV; 451f21bf61SJon Lin 466524556dSJon Lin switch (desc->devnum) { 476524556dSJon Lin case BLK_MTD_NAND: 486524556dSJon Lin case BLK_MTD_SPI_NAND: 491f21bf61SJon Lin mtd = desc->bdev->priv; 506524556dSJon Lin break; 516524556dSJon Lin default: 526524556dSJon Lin break; 531f21bf61SJon Lin } 541f21bf61SJon Lin 551f21bf61SJon Lin if (!mtd) { 561f21bf61SJon Lin return -ENODEV; 571f21bf61SJon Lin } else { 58b4e07918SJon Lin blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift; 591f21bf61SJon Lin if (!mtd_map_blk_table) { 600f1dc487SJon Lin mtd_map_blk_table = (int *)malloc(blk_total * sizeof(int)); 616524556dSJon Lin if (!mtd_map_blk_table) 626524556dSJon Lin return -ENOMEM; 630f1dc487SJon Lin for (i = 0; i < blk_total; i++) 640f1dc487SJon Lin mtd_map_blk_table[i] = MTD_BLK_TABLE_BLOCK_UNKNOWN; 651f21bf61SJon Lin } 661f21bf61SJon Lin 67b4e07918SJon Lin blk_begin = (u32)offset >> mtd->erasesize_shift; 68e091dc9dSJon Lin blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length + \ 69e091dc9dSJon Lin mtd->erasesize - 1) >> mtd->erasesize_shift); 700f1dc487SJon Lin if (blk_begin >= blk_total) { 710f1dc487SJon Lin pr_err("map table blk begin[%d] overflow\n", blk_begin); 720f1dc487SJon Lin return -EINVAL; 730f1dc487SJon Lin } 74d6290238SJon Lin if ((blk_begin + blk_cnt) > blk_total) 75d6290238SJon Lin blk_cnt = blk_total - blk_begin; 76c9e94690SJon Lin 77c9e94690SJon Lin if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN) 78c9e94690SJon Lin return 0; 79c9e94690SJon Lin 801f21bf61SJon Lin j = 0; 811f21bf61SJon Lin /* should not across blk_cnt */ 821f21bf61SJon Lin for (i = 0; i < blk_cnt; i++) { 831f21bf61SJon Lin if (j >= blk_cnt) 84c9e94690SJon Lin mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT; 851f21bf61SJon Lin for (; j < blk_cnt; j++) { 86b4e07918SJon Lin if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) { 871f21bf61SJon Lin mtd_map_blk_table[blk_begin + i] = blk_begin + j; 881f21bf61SJon Lin j++; 891f21bf61SJon Lin if (j == blk_cnt) 901f21bf61SJon Lin j++; 911f21bf61SJon Lin break; 921f21bf61SJon Lin } 931f21bf61SJon Lin } 941f21bf61SJon Lin } 951f21bf61SJon Lin 961f21bf61SJon Lin return 0; 971f21bf61SJon Lin } 981f21bf61SJon Lin } 991f21bf61SJon Lin 100c402731fSJon Lin static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off) 101c402731fSJon Lin { 102c402731fSJon Lin bool mapped; 103c402731fSJon Lin loff_t offset = *off; 104c402731fSJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 105c402731fSJon Lin 106c402731fSJon Lin mapped = false; 107c402731fSJon Lin if (!mtd_map_blk_table || 108c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 109c402731fSJon Lin MTD_BLK_TABLE_BLOCK_UNKNOWN || 110c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 111c402731fSJon Lin 0xffffffff) 112c402731fSJon Lin return mapped; 113c402731fSJon Lin 114c402731fSJon Lin mapped = true; 115c402731fSJon Lin *off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >> 116c402731fSJon Lin mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset); 117c402731fSJon Lin 118c402731fSJon Lin return mapped; 119c402731fSJon Lin } 120c402731fSJon Lin 121a07b97f2SJason Zhu void mtd_blk_map_partitions(struct blk_desc *desc) 122a07b97f2SJason Zhu { 123a07b97f2SJason Zhu disk_partition_t info; 124a07b97f2SJason Zhu int i, ret; 125a07b97f2SJason Zhu 126a07b97f2SJason Zhu if (!desc) 127a07b97f2SJason Zhu return; 128a07b97f2SJason Zhu 129a07b97f2SJason Zhu if (desc->if_type != IF_TYPE_MTD) 130a07b97f2SJason Zhu return; 131a07b97f2SJason Zhu 132a07b97f2SJason Zhu for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) { 133a07b97f2SJason Zhu ret = part_get_info(desc, i, &info); 134a07b97f2SJason Zhu if (ret != 0) 135a07b97f2SJason Zhu continue; 136a07b97f2SJason Zhu 137a07b97f2SJason Zhu if (mtd_blk_map_table_init(desc, 138a07b97f2SJason Zhu info.start << 9, 139a07b97f2SJason Zhu info.size << 9)) { 1409ee38883SJon Lin pr_debug("mtd block map table fail\n"); 141a07b97f2SJason Zhu } 142a07b97f2SJason Zhu } 143a07b97f2SJason Zhu } 144a07b97f2SJason Zhu 145661bcdfeSJason Zhu void mtd_blk_map_fit(struct blk_desc *desc, ulong sector, void *fit) 146661bcdfeSJason Zhu { 147661bcdfeSJason Zhu struct mtd_info *mtd = NULL; 148661bcdfeSJason Zhu int totalsize = 0; 149661bcdfeSJason Zhu 150661bcdfeSJason Zhu if (desc->if_type != IF_TYPE_MTD) 151661bcdfeSJason Zhu return; 152661bcdfeSJason Zhu 153661bcdfeSJason Zhu if (desc->devnum == BLK_MTD_NAND) { 154661bcdfeSJason Zhu #if defined(CONFIG_NAND) 155661bcdfeSJason Zhu mtd = dev_get_priv(desc->bdev->parent); 156661bcdfeSJason Zhu #endif 157661bcdfeSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NAND) { 158661bcdfeSJason Zhu #if defined(CONFIG_MTD_SPI_NAND) 159661bcdfeSJason Zhu mtd = desc->bdev->priv; 160661bcdfeSJason Zhu #endif 161661bcdfeSJason Zhu } 162661bcdfeSJason Zhu 163661bcdfeSJason Zhu #ifdef CONFIG_SPL_FIT 164661bcdfeSJason Zhu if (fit_get_totalsize(fit, &totalsize)) 165661bcdfeSJason Zhu debug("Can not find /totalsize node.\n"); 166661bcdfeSJason Zhu #endif 167661bcdfeSJason Zhu if (mtd && totalsize) { 168661bcdfeSJason Zhu if (mtd_blk_map_table_init(desc, sector << 9, totalsize + (size_t)mtd->erasesize)) 169661bcdfeSJason Zhu debug("Map block table fail.\n"); 170661bcdfeSJason Zhu } 171661bcdfeSJason Zhu } 172661bcdfeSJason Zhu 1731f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset, 1741f21bf61SJon Lin size_t *length, size_t *actual, 1751f21bf61SJon Lin loff_t lim, u_char *buffer) 1761f21bf61SJon Lin { 1771f21bf61SJon Lin size_t left_to_read = *length; 1781f21bf61SJon Lin u_char *p_buffer = buffer; 1791f21bf61SJon Lin int rval; 1801f21bf61SJon Lin 1811f21bf61SJon Lin while (left_to_read > 0) { 182360a2911SJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 1831f21bf61SJon Lin size_t read_length; 184360a2911SJon Lin loff_t mapped_offset; 1851f21bf61SJon Lin 1861f21bf61SJon Lin if (offset >= mtd->size) 1871f21bf61SJon Lin return 0; 1881f21bf61SJon Lin 189360a2911SJon Lin mapped_offset = offset; 190360a2911SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 191360a2911SJon Lin if (mtd_block_isbad(mtd, mapped_offset & 192360a2911SJon Lin ~(mtd->erasesize - 1))) { 193360a2911SJon Lin printf("Skipping bad block 0x%08llx\n", 194360a2911SJon Lin offset & ~(mtd->erasesize - 1)); 195360a2911SJon Lin offset += mtd->erasesize - block_offset; 1961f21bf61SJon Lin continue; 1971f21bf61SJon Lin } 1981f21bf61SJon Lin } 1991f21bf61SJon Lin 200360a2911SJon Lin if (left_to_read < (mtd->erasesize - block_offset)) 2011f21bf61SJon Lin read_length = left_to_read; 2021f21bf61SJon Lin else 203360a2911SJon Lin read_length = mtd->erasesize - block_offset; 2041f21bf61SJon Lin 205360a2911SJon Lin rval = mtd_read(mtd, mapped_offset, read_length, &read_length, 2061f21bf61SJon Lin p_buffer); 2071f21bf61SJon Lin if (rval && rval != -EUCLEAN) { 2081f21bf61SJon Lin printf("NAND read from offset %llx failed %d\n", 209c402731fSJon Lin offset, rval); 2101f21bf61SJon Lin *length -= left_to_read; 2111f21bf61SJon Lin return rval; 2121f21bf61SJon Lin } 2131f21bf61SJon Lin 2141f21bf61SJon Lin left_to_read -= read_length; 2151f21bf61SJon Lin offset += read_length; 2161f21bf61SJon Lin p_buffer += read_length; 2171f21bf61SJon Lin } 2181f21bf61SJon Lin 2191f21bf61SJon Lin return 0; 2201f21bf61SJon Lin } 2211f21bf61SJon Lin 2229ee38883SJon Lin static __maybe_unused int mtd_map_write(struct mtd_info *mtd, loff_t offset, 2239ee38883SJon Lin size_t *length, size_t *actual, 2249ee38883SJon Lin loff_t lim, u_char *buffer, int flags) 2259ee38883SJon Lin { 2269ee38883SJon Lin int rval = 0, blocksize; 2279ee38883SJon Lin size_t left_to_write = *length; 2289ee38883SJon Lin u_char *p_buffer = buffer; 2299ee38883SJon Lin struct erase_info ei; 2309ee38883SJon Lin 2319ee38883SJon Lin blocksize = mtd->erasesize; 2329ee38883SJon Lin 2339ee38883SJon Lin /* 2349ee38883SJon Lin * nand_write() handles unaligned, partial page writes. 2359ee38883SJon Lin * 2369ee38883SJon Lin * We allow length to be unaligned, for convenience in 2379ee38883SJon Lin * using the $filesize variable. 2389ee38883SJon Lin * 2399ee38883SJon Lin * However, starting at an unaligned offset makes the 2409ee38883SJon Lin * semantics of bad block skipping ambiguous (really, 2419ee38883SJon Lin * you should only start a block skipping access at a 2429ee38883SJon Lin * partition boundary). So don't try to handle that. 2439ee38883SJon Lin */ 2449ee38883SJon Lin if ((offset & (mtd->writesize - 1)) != 0) { 2459ee38883SJon Lin printf("Attempt to write non page-aligned data\n"); 2469ee38883SJon Lin *length = 0; 2479ee38883SJon Lin return -EINVAL; 2489ee38883SJon Lin } 2499ee38883SJon Lin 2509ee38883SJon Lin while (left_to_write > 0) { 2519ee38883SJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 2529ee38883SJon Lin size_t write_size, truncated_write_size; 2539ee38883SJon Lin loff_t mapped_offset; 2549ee38883SJon Lin 2559ee38883SJon Lin if (offset >= mtd->size) 2569ee38883SJon Lin return 0; 2579ee38883SJon Lin 2589ee38883SJon Lin mapped_offset = offset; 2599ee38883SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 2609ee38883SJon Lin if (mtd_block_isbad(mtd, mapped_offset & 2619ee38883SJon Lin ~(mtd->erasesize - 1))) { 2629ee38883SJon Lin printf("Skipping bad block 0x%08llx\n", 2639ee38883SJon Lin offset & ~(mtd->erasesize - 1)); 2649ee38883SJon Lin offset += mtd->erasesize - block_offset; 2659ee38883SJon Lin continue; 2669ee38883SJon Lin } 2679ee38883SJon Lin } 2689ee38883SJon Lin 2699ee38883SJon Lin if (!(mapped_offset & mtd->erasesize_mask)) { 2709ee38883SJon Lin memset(&ei, 0, sizeof(struct erase_info)); 2719ee38883SJon Lin ei.addr = mapped_offset; 2729ee38883SJon Lin ei.len = mtd->erasesize; 2739ee38883SJon Lin rval = mtd_erase(mtd, &ei); 2749ee38883SJon Lin if (rval) { 2759ee38883SJon Lin pr_info("error %d while erasing %llx\n", rval, 2769ee38883SJon Lin mapped_offset); 2779ee38883SJon Lin return rval; 2789ee38883SJon Lin } 2799ee38883SJon Lin } 2809ee38883SJon Lin 2819ee38883SJon Lin if (left_to_write < (blocksize - block_offset)) 2829ee38883SJon Lin write_size = left_to_write; 2839ee38883SJon Lin else 2849ee38883SJon Lin write_size = blocksize - block_offset; 2859ee38883SJon Lin 2869ee38883SJon Lin truncated_write_size = write_size; 2879ee38883SJon Lin rval = mtd_write(mtd, mapped_offset, truncated_write_size, 2889ee38883SJon Lin (size_t *)(&truncated_write_size), p_buffer); 2899ee38883SJon Lin 2909ee38883SJon Lin offset += write_size; 2919ee38883SJon Lin p_buffer += write_size; 2929ee38883SJon Lin 2939ee38883SJon Lin if (rval != 0) { 2949ee38883SJon Lin printf("NAND write to offset %llx failed %d\n", 2959ee38883SJon Lin offset, rval); 2969ee38883SJon Lin *length -= left_to_write; 2979ee38883SJon Lin return rval; 2989ee38883SJon Lin } 2999ee38883SJon Lin 3009ee38883SJon Lin left_to_write -= write_size; 3019ee38883SJon Lin } 3029ee38883SJon Lin 3039ee38883SJon Lin return 0; 3049ee38883SJon Lin } 3059ee38883SJon Lin 306338697c5SJon Lin static __maybe_unused int mtd_map_erase(struct mtd_info *mtd, loff_t offset, 307338697c5SJon Lin size_t length) 308338697c5SJon Lin { 309338697c5SJon Lin struct erase_info ei; 310338697c5SJon Lin loff_t pos, len; 311338697c5SJon Lin int ret; 312338697c5SJon Lin 313338697c5SJon Lin pos = offset; 314338697c5SJon Lin len = length; 315338697c5SJon Lin 316338697c5SJon Lin if ((pos & mtd->erasesize_mask) || (len & mtd->erasesize_mask)) { 317338697c5SJon Lin pr_err("Attempt to erase non block-aligned data, pos= %llx, len= %llx\n", 318338697c5SJon Lin pos, len); 319338697c5SJon Lin 320338697c5SJon Lin return -EINVAL; 321338697c5SJon Lin } 322338697c5SJon Lin 323338697c5SJon Lin while (len) { 324bd676549SJon Lin loff_t mapped_offset; 325bd676549SJon Lin 326bd676549SJon Lin mapped_offset = pos; 327bd676549SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 328338697c5SJon Lin if (mtd_block_isbad(mtd, pos) || mtd_block_isreserved(mtd, pos)) { 329338697c5SJon Lin pr_debug("attempt to erase a bad/reserved block @%llx\n", 330338697c5SJon Lin pos); 331338697c5SJon Lin pos += mtd->erasesize; 332338697c5SJon Lin continue; 333338697c5SJon Lin } 334bd676549SJon Lin } 335338697c5SJon Lin 336338697c5SJon Lin memset(&ei, 0, sizeof(struct erase_info)); 337bd676549SJon Lin ei.addr = mapped_offset; 338338697c5SJon Lin ei.len = mtd->erasesize; 339338697c5SJon Lin ret = mtd_erase(mtd, &ei); 340338697c5SJon Lin if (ret) { 341338697c5SJon Lin pr_err("map_erase error %d while erasing %llx\n", ret, 342338697c5SJon Lin pos); 343338697c5SJon Lin return ret; 344338697c5SJon Lin } 345338697c5SJon Lin 346338697c5SJon Lin pos += mtd->erasesize; 347338697c5SJon Lin len -= mtd->erasesize; 348338697c5SJon Lin } 349338697c5SJon Lin 350338697c5SJon Lin return 0; 351338697c5SJon Lin } 352338697c5SJon Lin 35322dccd11SJason Zhu char *mtd_part_parse(void) 35422dccd11SJason Zhu { 35522dccd11SJason Zhu char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0}; 35622dccd11SJason Zhu u32 length, data_len = MTD_PART_INFO_MAX_SIZE; 357*cc0876a8SWeiwen Chen char mtd_root_part_info[40] = {0}; 35822dccd11SJason Zhu struct blk_desc *dev_desc; 35922dccd11SJason Zhu disk_partition_t info; 36022dccd11SJason Zhu char *mtd_part_info_p; 361c9289eddSJason Zhu struct mtd_info *mtd; 36222dccd11SJason Zhu char *mtd_part_info; 36322dccd11SJason Zhu int ret; 36422dccd11SJason Zhu int p; 36522dccd11SJason Zhu 36622dccd11SJason Zhu dev_desc = rockchip_get_bootdev(); 36722dccd11SJason Zhu if (!dev_desc) 36822dccd11SJason Zhu return NULL; 36922dccd11SJason Zhu 370c9289eddSJason Zhu mtd = (struct mtd_info *)dev_desc->bdev->priv; 371ec6d4288SJason Zhu if (!mtd) 372ec6d4288SJason Zhu return NULL; 373ec6d4288SJason Zhu 37404a8326aSJason Zhu p = part_get_info_by_name(dev_desc, PART_SYSTEM, &info); 37504a8326aSJason Zhu if (p > 0) { 376*cc0876a8SWeiwen Chen if (strstr(env_get("bootargs"), "rootfstype=squashfs")) 377*cc0876a8SWeiwen Chen snprintf(mtd_root_part_info, ARRAY_SIZE(mtd_root_part_info), "%s%d %s", 378*cc0876a8SWeiwen Chen MTD_ROOT_PART_NUM, p - 1, MTD_ROOT_PART_NAME_SQUASHFS); 379*cc0876a8SWeiwen Chen else 380*cc0876a8SWeiwen Chen snprintf(mtd_root_part_info, ARRAY_SIZE(mtd_root_part_info), "%s%d %s", 381*cc0876a8SWeiwen Chen MTD_ROOT_PART_NUM, p - 1, MTD_ROOT_PART_NAME_UBIFS); 38204a8326aSJason Zhu env_update("bootargs", mtd_root_part_info); 38304a8326aSJason Zhu } 38404a8326aSJason Zhu 38522dccd11SJason Zhu mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char)); 38622dccd11SJason Zhu if (!mtd_part_info) { 38722dccd11SJason Zhu printf("%s: Fail to malloc!", __func__); 38822dccd11SJason Zhu return NULL; 38922dccd11SJason Zhu } 39022dccd11SJason Zhu 39122dccd11SJason Zhu mtd_part_info_p = mtd_part_info; 39222dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "%s%s:", 39322dccd11SJason Zhu MTD_PART_NAND_HEAD, 39422dccd11SJason Zhu dev_desc->product); 39522dccd11SJason Zhu data_len -= strlen(mtd_part_info_p); 39622dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p); 39722dccd11SJason Zhu 39822dccd11SJason Zhu for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { 39922dccd11SJason Zhu ret = part_get_info(dev_desc, p, &info); 40022dccd11SJason Zhu if (ret) 40122dccd11SJason Zhu break; 40222dccd11SJason Zhu 40322dccd11SJason Zhu debug("name is %s, start addr is %x\n", info.name, 40422dccd11SJason Zhu (int)(size_t)info.start); 40522dccd11SJason Zhu 40622dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 40722dccd11SJason Zhu (int)(size_t)info.size << 9, 40822dccd11SJason Zhu (int)(size_t)info.start << 9, 40922dccd11SJason Zhu info.name); 41022dccd11SJason Zhu snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1, 41122dccd11SJason Zhu "0x%x@0x%x(%s)", 41222dccd11SJason Zhu (int)(size_t)info.size << 9, 41322dccd11SJason Zhu (int)(size_t)info.start << 9, 41422dccd11SJason Zhu info.name); 41522dccd11SJason Zhu strcat(mtd_part_info, ","); 41669bb6ffaSJon Lin if (part_get_info(dev_desc, p + 1, &info) && 41769bb6ffaSJon Lin (info.size + info.start + 33) == dev_desc->lba) { 418ce9d2743SJon Lin if (dev_desc->devnum == BLK_MTD_SPI_NOR) { 419ce9d2743SJon Lin /* Nor is 64KB erase block(kernel) and gpt table just 420ce9d2743SJon Lin * resserve 33 sectors for the last partition. This 421ce9d2743SJon Lin * will erase the backup gpt table by user program, 422ce9d2743SJon Lin * so reserve one block. 423ce9d2743SJon Lin */ 424ce9d2743SJon Lin snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 425ce9d2743SJon Lin (int)(size_t)(info.size - 426ce9d2743SJon Lin (info.size - 1) % 427ce9d2743SJon Lin (0x10000 >> 9) - 1) << 9, 428ce9d2743SJon Lin (int)(size_t)info.start << 9, 429ce9d2743SJon Lin info.name); 430ce9d2743SJon Lin break; 431ce9d2743SJon Lin } else { 432c9289eddSJason Zhu /* Nand flash is erased by block and gpt table just 433c9289eddSJason Zhu * resserve 33 sectors for the last partition. This 434c9289eddSJason Zhu * will erase the backup gpt table by user program, 435c9289eddSJason Zhu * so reserve one block. 436c9289eddSJason Zhu */ 437c9289eddSJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 438c9289eddSJason Zhu (int)(size_t)(info.size - 439c9289eddSJason Zhu (info.size - 1) % 440c9289eddSJason Zhu (mtd->erasesize >> 9) - 1) << 9, 44122dccd11SJason Zhu (int)(size_t)info.start << 9, 44222dccd11SJason Zhu info.name); 44322dccd11SJason Zhu break; 44422dccd11SJason Zhu } 445ce9d2743SJon Lin } 44622dccd11SJason Zhu length = strlen(mtd_part_info_temp); 44722dccd11SJason Zhu data_len -= length; 44822dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + length + 1; 44922dccd11SJason Zhu memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE); 45022dccd11SJason Zhu } 45122dccd11SJason Zhu 45222dccd11SJason Zhu return mtd_part_info; 45322dccd11SJason Zhu } 45422dccd11SJason Zhu 455054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start, 456054229abSJason Zhu lbaint_t blkcnt, void *dst) 457054229abSJason Zhu { 458054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 4590dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 4606e8ac5a8SJason Zhu loff_t off = (loff_t)(start * 512); 4616e8ac5a8SJason Zhu size_t rwsize = blkcnt * 512; 4620dccd0d8SJason Zhu #endif 4636e8ac5a8SJason Zhu struct mtd_info *mtd; 464bbb83f58SJason Zhu int ret = 0; 465054229abSJason Zhu 466054229abSJason Zhu if (!desc) 46739e38ab3SJason Zhu return ret; 468054229abSJason Zhu 4696e8ac5a8SJason Zhu mtd = desc->bdev->priv; 4706e8ac5a8SJason Zhu if (!mtd) 4716e8ac5a8SJason Zhu return 0; 4726e8ac5a8SJason Zhu 473054229abSJason Zhu if (blkcnt == 0) 474054229abSJason Zhu return 0; 475054229abSJason Zhu 4769ee38883SJon Lin pr_debug("mtd dread %s %lx %lx\n", mtd->name, start, blkcnt); 4779ee38883SJon Lin 478054229abSJason Zhu if (desc->devnum == BLK_MTD_NAND) { 4791f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 4801f21bf61SJon Lin NULL, mtd->size, 4811f21bf61SJon Lin (u_char *)(dst)); 4826e8ac5a8SJason Zhu if (!ret) 483054229abSJason Zhu return blkcnt; 4846e8ac5a8SJason Zhu else 4856e8ac5a8SJason Zhu return 0; 486054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NAND) { 4871f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 488bbb83f58SJason Zhu NULL, mtd->size, 489bbb83f58SJason Zhu (u_char *)(dst)); 490bbb83f58SJason Zhu if (!ret) 4916e8ac5a8SJason Zhu return blkcnt; 4926e8ac5a8SJason Zhu else 493054229abSJason Zhu return 0; 494054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NOR) { 4950dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD) 4963fb7bf02SJason Zhu struct spi_nor *nor = (struct spi_nor *)mtd->priv; 4973fb7bf02SJason Zhu struct spi_slave *spi = nor->spi; 4980dccd0d8SJason Zhu size_t retlen_nor; 4990dccd0d8SJason Zhu 5003fb7bf02SJason Zhu if (desc->op_flag == BLK_PRE_RW) 5013fb7bf02SJason Zhu spi->mode |= SPI_DMA_PREPARE; 5020dccd0d8SJason Zhu mtd_read(mtd, off, rwsize, &retlen_nor, dst); 5033fb7bf02SJason Zhu if (desc->op_flag == BLK_PRE_RW) 5043fb7bf02SJason Zhu spi->mode |= SPI_DMA_PREPARE; 5053fb7bf02SJason Zhu 5060dccd0d8SJason Zhu if (retlen_nor == rwsize) 5070dccd0d8SJason Zhu return blkcnt; 5080dccd0d8SJason Zhu else 5090dccd0d8SJason Zhu #endif 510054229abSJason Zhu return 0; 511054229abSJason Zhu } else { 512054229abSJason Zhu return 0; 513054229abSJason Zhu } 514054229abSJason Zhu } 515054229abSJason Zhu 516a0166cc6SJason Zhu #if CONFIG_IS_ENABLED(MTD_WRITE) 517054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start, 518054229abSJason Zhu lbaint_t blkcnt, const void *src) 519054229abSJason Zhu { 5209ee38883SJon Lin struct blk_desc *desc = dev_get_uclass_platdata(udev); 5219ee38883SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 5229ee38883SJon Lin loff_t off = (loff_t)(start * 512); 5239ee38883SJon Lin size_t rwsize = blkcnt * 512; 5249ee38883SJon Lin #endif 5259ee38883SJon Lin struct mtd_info *mtd; 5269ee38883SJon Lin int ret = 0; 5279ee38883SJon Lin 5289ee38883SJon Lin if (!desc) 5299ee38883SJon Lin return ret; 5309ee38883SJon Lin 5319ee38883SJon Lin mtd = desc->bdev->priv; 5329ee38883SJon Lin if (!mtd) 5339ee38883SJon Lin return 0; 5349ee38883SJon Lin 5359ee38883SJon Lin pr_debug("mtd dwrite %s %lx %lx\n", mtd->name, start, blkcnt); 5369ee38883SJon Lin 5379ee38883SJon Lin if (blkcnt == 0) 5389ee38883SJon Lin return 0; 5399ee38883SJon Lin 5409ee38883SJon Lin if (desc->devnum == BLK_MTD_NAND || 5419ee38883SJon Lin desc->devnum == BLK_MTD_SPI_NAND || 5429ee38883SJon Lin desc->devnum == BLK_MTD_SPI_NOR) { 543853fc11fSJon Lin if (desc->op_flag == BLK_MTD_CONT_WRITE) { 544853fc11fSJon Lin ret = mtd_map_write(mtd, off, &rwsize, 545853fc11fSJon Lin NULL, mtd->size, 546853fc11fSJon Lin (u_char *)(src), 0); 547853fc11fSJon Lin if (!ret) 548853fc11fSJon Lin return blkcnt; 549853fc11fSJon Lin else 550853fc11fSJon Lin return 0; 551853fc11fSJon Lin } else { 55242439462SJon Lin lbaint_t off_aligned, alinged; 55342439462SJon Lin size_t rwsize_aligned; 55442439462SJon Lin u8 *p_buf; 55542439462SJon Lin 55642439462SJon Lin alinged = off & mtd->erasesize_mask; 55742439462SJon Lin off_aligned = off - alinged; 55842439462SJon Lin rwsize_aligned = rwsize + alinged; 55942439462SJon Lin rwsize_aligned = (rwsize_aligned + mtd->erasesize - 1) & 56042439462SJon Lin ~(mtd->erasesize - 1); 56142439462SJon Lin 56242439462SJon Lin p_buf = malloc(rwsize_aligned); 56342439462SJon Lin if (!p_buf) { 56442439462SJon Lin printf("%s: Fail to malloc!", __func__); 56542439462SJon Lin return 0; 56642439462SJon Lin } 56742439462SJon Lin 56842439462SJon Lin ret = mtd_map_read(mtd, off_aligned, &rwsize_aligned, 56942439462SJon Lin NULL, mtd->size, 57042439462SJon Lin (u_char *)(p_buf)); 57142439462SJon Lin if (ret) { 57242439462SJon Lin free(p_buf); 57342439462SJon Lin return 0; 57442439462SJon Lin } 57542439462SJon Lin 57642439462SJon Lin memcpy(p_buf + alinged, src, rwsize); 57742439462SJon Lin 57842439462SJon Lin ret = mtd_map_write(mtd, off_aligned, &rwsize_aligned, 57942439462SJon Lin NULL, mtd->size, 58042439462SJon Lin (u_char *)(p_buf), 0); 58142439462SJon Lin free(p_buf); 58242439462SJon Lin if (!ret) 58342439462SJon Lin return blkcnt; 58442439462SJon Lin else 58542439462SJon Lin return 0; 58642439462SJon Lin } 5879ee38883SJon Lin } else { 5889ee38883SJon Lin return 0; 5899ee38883SJon Lin } 5909ee38883SJon Lin 591054229abSJason Zhu return 0; 592054229abSJason Zhu } 593054229abSJason Zhu 594054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start, 595054229abSJason Zhu lbaint_t blkcnt) 596054229abSJason Zhu { 597338697c5SJon Lin struct blk_desc *desc = dev_get_uclass_platdata(udev); 598338697c5SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 599338697c5SJon Lin loff_t off = (loff_t)(start * 512); 600338697c5SJon Lin size_t len = blkcnt * 512; 601338697c5SJon Lin #endif 602338697c5SJon Lin struct mtd_info *mtd; 603338697c5SJon Lin int ret = 0; 604338697c5SJon Lin 605338697c5SJon Lin if (!desc) 606338697c5SJon Lin return ret; 607338697c5SJon Lin 608338697c5SJon Lin mtd = desc->bdev->priv; 609338697c5SJon Lin if (!mtd) 610338697c5SJon Lin return 0; 611338697c5SJon Lin 612338697c5SJon Lin pr_debug("mtd derase %s %lx %lx\n", mtd->name, start, blkcnt); 613338697c5SJon Lin 614338697c5SJon Lin if (blkcnt == 0) 615338697c5SJon Lin return 0; 616338697c5SJon Lin 617338697c5SJon Lin if (desc->devnum == BLK_MTD_NAND || 6189148182dSJon Lin desc->devnum == BLK_MTD_SPI_NAND || 6199148182dSJon Lin desc->devnum == BLK_MTD_SPI_NOR) { 620338697c5SJon Lin ret = mtd_map_erase(mtd, off, len); 621338697c5SJon Lin if (ret) 622338697c5SJon Lin return ret; 623338697c5SJon Lin } else { 624338697c5SJon Lin return 0; 625338697c5SJon Lin } 626338697c5SJon Lin 627054229abSJason Zhu return 0; 628054229abSJason Zhu } 629a0166cc6SJason Zhu #endif 630054229abSJason Zhu 631054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev) 632054229abSJason Zhu { 6336524556dSJon Lin struct mtd_info *mtd; 634054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 6356524556dSJon Lin int ret, i = 0; 6366524556dSJon Lin 6376524556dSJon Lin mtd = dev_get_uclass_priv(udev->parent); 6386524556dSJon Lin if (mtd->type == MTD_NANDFLASH && desc->devnum == BLK_MTD_NAND) { 6396524556dSJon Lin #ifndef CONFIG_SPL_BUILD 6406524556dSJon Lin mtd = dev_get_priv(udev->parent); 6416524556dSJon Lin #endif 6426524556dSJon Lin } 643054229abSJason Zhu 644c9289eddSJason Zhu desc->bdev->priv = mtd; 645054229abSJason Zhu sprintf(desc->vendor, "0x%.4x", 0x2207); 646a5c69560SJon Lin if (strncmp(mtd->name, "nand", 4) == 0) 647a5c69560SJon Lin memcpy(desc->product, "rk-nand", strlen("rk-nand")); 648a5c69560SJon Lin else 649e6482de4SJason Zhu memcpy(desc->product, mtd->name, strlen(mtd->name)); 650054229abSJason Zhu memcpy(desc->revision, "V1.00", sizeof("V1.00")); 651f1892190SJason Zhu if (mtd->type == MTD_NANDFLASH) { 6526524556dSJon Lin #ifdef CONFIG_NAND 653f3ba630bSJason Zhu if (desc->devnum == BLK_MTD_NAND) 6546524556dSJon Lin i = NAND_BBT_SCAN_MAXBLOCKS; 6556524556dSJon Lin else if (desc->devnum == BLK_MTD_SPI_NAND) 6566524556dSJon Lin i = NANDDEV_BBT_SCAN_MAXBLOCKS; 6576524556dSJon Lin #endif 6586524556dSJon Lin 6591d39542fSJason Zhu /* 6601d39542fSJason Zhu * Find the first useful block in the end, 6611d39542fSJason Zhu * and it is the end lba of the nand storage. 6621d39542fSJason Zhu */ 6636524556dSJon Lin for (; i < (mtd->size / mtd->erasesize); i++) { 6641d39542fSJason Zhu ret = mtd_block_isbad(mtd, 6651d39542fSJason Zhu mtd->size - mtd->erasesize * (i + 1)); 6661d39542fSJason Zhu if (!ret) { 6671d39542fSJason Zhu desc->lba = (mtd->size >> 9) - 6681d39542fSJason Zhu (mtd->erasesize >> 9) * i; 6691d39542fSJason Zhu break; 6701d39542fSJason Zhu } 6711d39542fSJason Zhu } 672f1892190SJason Zhu } else { 673f1892190SJason Zhu desc->lba = mtd->size >> 9; 674f1892190SJason Zhu } 675054229abSJason Zhu 6761d39542fSJason Zhu debug("MTD: desc->lba is %lx\n", desc->lba); 6771d39542fSJason Zhu 678054229abSJason Zhu return 0; 679054229abSJason Zhu } 680054229abSJason Zhu 681054229abSJason Zhu static const struct blk_ops mtd_blk_ops = { 682054229abSJason Zhu .read = mtd_dread, 683a0166cc6SJason Zhu #if CONFIG_IS_ENABLED(MTD_WRITE) 684054229abSJason Zhu .write = mtd_dwrite, 685054229abSJason Zhu .erase = mtd_derase, 686054229abSJason Zhu #endif 687054229abSJason Zhu }; 688054229abSJason Zhu 689054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = { 690054229abSJason Zhu .name = "mtd_blk", 691054229abSJason Zhu .id = UCLASS_BLK, 692054229abSJason Zhu .ops = &mtd_blk_ops, 693054229abSJason Zhu .probe = mtd_blk_probe, 694054229abSJason Zhu }; 695