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> 13479a1588SJon Lin #include <linux/log2.h> 1422dccd11SJason Zhu #include <malloc.h> 15054229abSJason Zhu #include <nand.h> 1622dccd11SJason Zhu #include <part.h> 173fb7bf02SJason Zhu #include <spi.h> 18054229abSJason Zhu #include <dm/device-internal.h> 196af5fcf6SJon Lin #include <linux/mtd/spinand.h> 203fb7bf02SJason Zhu #include <linux/mtd/spi-nor.h> 216524556dSJon Lin #ifdef CONFIG_NAND 226524556dSJon Lin #include <linux/mtd/nand.h> 236524556dSJon Lin #endif 24054229abSJason Zhu 251a3d87beSJon Lin // #define MTD_BLK_VERBOSE 261a3d87beSJon Lin 2722dccd11SJason Zhu #define MTD_PART_NAND_HEAD "mtdparts=" 2822dccd11SJason Zhu #define MTD_PART_INFO_MAX_SIZE 512 2922dccd11SJason Zhu #define MTD_SINGLE_PART_INFO_MAX_SIZE 40 3022dccd11SJason Zhu 31c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_UNKNOWN (-2) 32c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_SHIFT (-1) 33c9e94690SJon Lin 34106a71f0SJon Lin #define FACTORY_UNKNOWN_LBA (0xffffffff - 34) 35106a71f0SJon Lin 361f21bf61SJon Lin static int *mtd_map_blk_table; 371f21bf61SJon Lin 38bee19e1dSJon Lin #if CONFIG_IS_ENABLED(SUPPORT_USBPLUG) 39bee19e1dSJon Lin static loff_t usbplug_dummy_partition_write_last_addr; 40bee19e1dSJon Lin static loff_t usbplug_dummy_partition_write_seek; 41bee19e1dSJon Lin static loff_t usbplug_dummy_partition_read_last_addr; 42bee19e1dSJon Lin static loff_t usbplug_dummy_partition_read_seek; 43bee19e1dSJon Lin #endif 44bee19e1dSJon Lin 451f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc, 461f21bf61SJon Lin loff_t offset, 471f21bf61SJon Lin size_t length) 481f21bf61SJon Lin { 491f21bf61SJon Lin u32 blk_total, blk_begin, blk_cnt; 501f21bf61SJon Lin struct mtd_info *mtd = NULL; 511f21bf61SJon Lin int i, j; 521f21bf61SJon Lin 531f21bf61SJon Lin if (!desc) 541f21bf61SJon Lin return -ENODEV; 551f21bf61SJon Lin 566524556dSJon Lin switch (desc->devnum) { 576524556dSJon Lin case BLK_MTD_NAND: 586524556dSJon Lin case BLK_MTD_SPI_NAND: 591f21bf61SJon Lin mtd = desc->bdev->priv; 606524556dSJon Lin break; 616524556dSJon Lin default: 626524556dSJon Lin break; 631f21bf61SJon Lin } 641f21bf61SJon Lin 651f21bf61SJon Lin if (!mtd) { 661f21bf61SJon Lin return -ENODEV; 671f21bf61SJon Lin } else { 68b4e07918SJon Lin blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift; 691f21bf61SJon Lin if (!mtd_map_blk_table) { 700f1dc487SJon Lin mtd_map_blk_table = (int *)malloc(blk_total * sizeof(int)); 716524556dSJon Lin if (!mtd_map_blk_table) 726524556dSJon Lin return -ENOMEM; 730f1dc487SJon Lin for (i = 0; i < blk_total; i++) 740f1dc487SJon Lin mtd_map_blk_table[i] = MTD_BLK_TABLE_BLOCK_UNKNOWN; 751f21bf61SJon Lin } 761f21bf61SJon Lin 77b4e07918SJon Lin blk_begin = (u32)offset >> mtd->erasesize_shift; 78e091dc9dSJon Lin blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length + \ 79e091dc9dSJon Lin mtd->erasesize - 1) >> mtd->erasesize_shift); 800f1dc487SJon Lin if (blk_begin >= blk_total) { 810f1dc487SJon Lin pr_err("map table blk begin[%d] overflow\n", blk_begin); 820f1dc487SJon Lin return -EINVAL; 830f1dc487SJon Lin } 84d6290238SJon Lin if ((blk_begin + blk_cnt) > blk_total) 85d6290238SJon Lin blk_cnt = blk_total - blk_begin; 86c9e94690SJon Lin 87c9e94690SJon Lin if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN) 88c9e94690SJon Lin return 0; 89c9e94690SJon Lin 901f21bf61SJon Lin j = 0; 911f21bf61SJon Lin /* should not across blk_cnt */ 921f21bf61SJon Lin for (i = 0; i < blk_cnt; i++) { 931f21bf61SJon Lin if (j >= blk_cnt) 94c9e94690SJon Lin mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT; 951f21bf61SJon Lin for (; j < blk_cnt; j++) { 96b4e07918SJon Lin if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) { 971f21bf61SJon Lin mtd_map_blk_table[blk_begin + i] = blk_begin + j; 981f21bf61SJon Lin j++; 991f21bf61SJon Lin if (j == blk_cnt) 1001f21bf61SJon Lin j++; 1011f21bf61SJon Lin break; 1021f21bf61SJon Lin } 1031f21bf61SJon Lin } 1041f21bf61SJon Lin } 1051f21bf61SJon Lin 1061f21bf61SJon Lin return 0; 1071f21bf61SJon Lin } 1081f21bf61SJon Lin } 1091f21bf61SJon Lin 110c402731fSJon Lin static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off) 111c402731fSJon Lin { 112c402731fSJon Lin bool mapped; 113c402731fSJon Lin loff_t offset = *off; 114c402731fSJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 115c402731fSJon Lin 116c402731fSJon Lin mapped = false; 117c402731fSJon Lin if (!mtd_map_blk_table || 118c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 119c402731fSJon Lin MTD_BLK_TABLE_BLOCK_UNKNOWN || 120c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 121c402731fSJon Lin 0xffffffff) 122c402731fSJon Lin return mapped; 123c402731fSJon Lin 124c402731fSJon Lin mapped = true; 125c402731fSJon Lin *off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >> 126c402731fSJon Lin mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset); 127c402731fSJon Lin 128c402731fSJon Lin return mapped; 129c402731fSJon Lin } 130c402731fSJon Lin 131a07b97f2SJason Zhu void mtd_blk_map_partitions(struct blk_desc *desc) 132a07b97f2SJason Zhu { 133a07b97f2SJason Zhu disk_partition_t info; 134a07b97f2SJason Zhu int i, ret; 135a07b97f2SJason Zhu 136a07b97f2SJason Zhu if (!desc) 137a07b97f2SJason Zhu return; 138a07b97f2SJason Zhu 139a07b97f2SJason Zhu if (desc->if_type != IF_TYPE_MTD) 140a07b97f2SJason Zhu return; 141a07b97f2SJason Zhu 142a07b97f2SJason Zhu for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) { 143a07b97f2SJason Zhu ret = part_get_info(desc, i, &info); 144a07b97f2SJason Zhu if (ret != 0) 14535d02c8bSJon Lin break; 146a07b97f2SJason Zhu 147a07b97f2SJason Zhu if (mtd_blk_map_table_init(desc, 148a07b97f2SJason Zhu info.start << 9, 149a07b97f2SJason Zhu info.size << 9)) { 1509ee38883SJon Lin pr_debug("mtd block map table fail\n"); 151a07b97f2SJason Zhu } 152a07b97f2SJason Zhu } 153a07b97f2SJason Zhu } 154a07b97f2SJason Zhu 155661bcdfeSJason Zhu void mtd_blk_map_fit(struct blk_desc *desc, ulong sector, void *fit) 156661bcdfeSJason Zhu { 157661bcdfeSJason Zhu struct mtd_info *mtd = NULL; 158661bcdfeSJason Zhu int totalsize = 0; 159661bcdfeSJason Zhu 160661bcdfeSJason Zhu if (desc->if_type != IF_TYPE_MTD) 161661bcdfeSJason Zhu return; 162661bcdfeSJason Zhu 163661bcdfeSJason Zhu if (desc->devnum == BLK_MTD_NAND) { 164661bcdfeSJason Zhu #if defined(CONFIG_NAND) 165661bcdfeSJason Zhu mtd = dev_get_priv(desc->bdev->parent); 166661bcdfeSJason Zhu #endif 167661bcdfeSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NAND) { 168661bcdfeSJason Zhu #if defined(CONFIG_MTD_SPI_NAND) 169661bcdfeSJason Zhu mtd = desc->bdev->priv; 170661bcdfeSJason Zhu #endif 171661bcdfeSJason Zhu } 172661bcdfeSJason Zhu 173661bcdfeSJason Zhu #ifdef CONFIG_SPL_FIT 174661bcdfeSJason Zhu if (fit_get_totalsize(fit, &totalsize)) 175661bcdfeSJason Zhu debug("Can not find /totalsize node.\n"); 176661bcdfeSJason Zhu #endif 177661bcdfeSJason Zhu if (mtd && totalsize) { 178661bcdfeSJason Zhu if (mtd_blk_map_table_init(desc, sector << 9, totalsize + (size_t)mtd->erasesize)) 179661bcdfeSJason Zhu debug("Map block table fail.\n"); 180661bcdfeSJason Zhu } 181661bcdfeSJason Zhu } 182661bcdfeSJason Zhu 1831f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset, 1841f21bf61SJon Lin size_t *length, size_t *actual, 1851f21bf61SJon Lin loff_t lim, u_char *buffer) 1861f21bf61SJon Lin { 1871f21bf61SJon Lin size_t left_to_read = *length; 1881f21bf61SJon Lin u_char *p_buffer = buffer; 1891f21bf61SJon Lin int rval; 1901f21bf61SJon Lin 191bee19e1dSJon Lin #if CONFIG_IS_ENABLED(SUPPORT_USBPLUG) 192bee19e1dSJon Lin if (usbplug_dummy_partition_read_last_addr != offset) 193bee19e1dSJon Lin usbplug_dummy_partition_read_seek = 0; 194bee19e1dSJon Lin usbplug_dummy_partition_read_last_addr = offset + left_to_read; 195bee19e1dSJon Lin offset += usbplug_dummy_partition_read_seek; 196bee19e1dSJon Lin #endif 197bee19e1dSJon Lin 1981f21bf61SJon Lin while (left_to_read > 0) { 199360a2911SJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 2001f21bf61SJon Lin size_t read_length; 201360a2911SJon Lin loff_t mapped_offset; 2021f21bf61SJon Lin 2031f21bf61SJon Lin if (offset >= mtd->size) 2041f21bf61SJon Lin return 0; 2051f21bf61SJon Lin 206360a2911SJon Lin mapped_offset = offset; 207360a2911SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 208360a2911SJon Lin if (mtd_block_isbad(mtd, mapped_offset & 209360a2911SJon Lin ~(mtd->erasesize - 1))) { 210bee19e1dSJon Lin printf("Skipping bad block 0x%08x in read\n", 211bee19e1dSJon Lin (u32)(offset & ~(mtd->erasesize - 1))); 212360a2911SJon Lin offset += mtd->erasesize - block_offset; 213bee19e1dSJon Lin #if CONFIG_IS_ENABLED(SUPPORT_USBPLUG) 214bee19e1dSJon Lin usbplug_dummy_partition_read_seek += mtd->erasesize; 215bee19e1dSJon Lin #endif 2161f21bf61SJon Lin continue; 2171f21bf61SJon Lin } 2181f21bf61SJon Lin } 2191f21bf61SJon Lin 220360a2911SJon Lin if (left_to_read < (mtd->erasesize - block_offset)) 2211f21bf61SJon Lin read_length = left_to_read; 2221f21bf61SJon Lin else 223360a2911SJon Lin read_length = mtd->erasesize - block_offset; 2241f21bf61SJon Lin 225360a2911SJon Lin rval = mtd_read(mtd, mapped_offset, read_length, &read_length, 2261f21bf61SJon Lin p_buffer); 2271f21bf61SJon Lin if (rval && rval != -EUCLEAN) { 22868fb1b3bSJon Lin printf("NAND read from offset %x failed %d\n", 22968fb1b3bSJon Lin (u32)offset, rval); 2301f21bf61SJon Lin *length -= left_to_read; 2311f21bf61SJon Lin return rval; 2321f21bf61SJon Lin } 2331f21bf61SJon Lin 2341f21bf61SJon Lin left_to_read -= read_length; 2351f21bf61SJon Lin offset += read_length; 2361f21bf61SJon Lin p_buffer += read_length; 2371f21bf61SJon Lin } 2381f21bf61SJon Lin 2391f21bf61SJon Lin return 0; 2401f21bf61SJon Lin } 2411f21bf61SJon Lin 2429ee38883SJon Lin static __maybe_unused int mtd_map_write(struct mtd_info *mtd, loff_t offset, 2439ee38883SJon Lin size_t *length, size_t *actual, 2449ee38883SJon Lin loff_t lim, u_char *buffer, int flags) 2459ee38883SJon Lin { 2469ee38883SJon Lin int rval = 0, blocksize; 2479ee38883SJon Lin size_t left_to_write = *length; 2489ee38883SJon Lin u_char *p_buffer = buffer; 2499ee38883SJon Lin struct erase_info ei; 2509ee38883SJon Lin 2519ee38883SJon Lin blocksize = mtd->erasesize; 2529ee38883SJon Lin 253bee19e1dSJon Lin #if CONFIG_IS_ENABLED(SUPPORT_USBPLUG) 254bee19e1dSJon Lin if (usbplug_dummy_partition_write_last_addr != offset) 255bee19e1dSJon Lin usbplug_dummy_partition_write_seek = 0; 256bee19e1dSJon Lin usbplug_dummy_partition_write_last_addr = offset + left_to_write; 257bee19e1dSJon Lin offset += usbplug_dummy_partition_write_seek; 258bee19e1dSJon Lin #endif 259bee19e1dSJon Lin 2609ee38883SJon Lin /* 2619ee38883SJon Lin * nand_write() handles unaligned, partial page writes. 2629ee38883SJon Lin * 2639ee38883SJon Lin * We allow length to be unaligned, for convenience in 2649ee38883SJon Lin * using the $filesize variable. 2659ee38883SJon Lin * 2669ee38883SJon Lin * However, starting at an unaligned offset makes the 2679ee38883SJon Lin * semantics of bad block skipping ambiguous (really, 2689ee38883SJon Lin * you should only start a block skipping access at a 2699ee38883SJon Lin * partition boundary). So don't try to handle that. 2709ee38883SJon Lin */ 2719ee38883SJon Lin if ((offset & (mtd->writesize - 1)) != 0) { 2729ee38883SJon Lin printf("Attempt to write non page-aligned data\n"); 2739ee38883SJon Lin *length = 0; 2749ee38883SJon Lin return -EINVAL; 2759ee38883SJon Lin } 2769ee38883SJon Lin 2779ee38883SJon Lin while (left_to_write > 0) { 2789ee38883SJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 2799ee38883SJon Lin size_t write_size, truncated_write_size; 2809ee38883SJon Lin loff_t mapped_offset; 2819ee38883SJon Lin 2829ee38883SJon Lin if (offset >= mtd->size) 2839ee38883SJon Lin return 0; 2849ee38883SJon Lin 2859ee38883SJon Lin mapped_offset = offset; 2869ee38883SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 2879ee38883SJon Lin if (mtd_block_isbad(mtd, mapped_offset & 2889ee38883SJon Lin ~(mtd->erasesize - 1))) { 289bee19e1dSJon Lin printf("Skipping bad block 0x%08x in write\n", 290bee19e1dSJon Lin (u32)(offset & ~(mtd->erasesize - 1))); 2919ee38883SJon Lin offset += mtd->erasesize - block_offset; 292bee19e1dSJon Lin #if CONFIG_IS_ENABLED(SUPPORT_USBPLUG) 293bee19e1dSJon Lin usbplug_dummy_partition_write_seek += mtd->erasesize; 294bee19e1dSJon Lin #endif 2959ee38883SJon Lin continue; 2969ee38883SJon Lin } 2979ee38883SJon Lin } 2989ee38883SJon Lin 2999ee38883SJon Lin if (!(mapped_offset & mtd->erasesize_mask)) { 3009ee38883SJon Lin memset(&ei, 0, sizeof(struct erase_info)); 3019ee38883SJon Lin ei.addr = mapped_offset; 3029ee38883SJon Lin ei.len = mtd->erasesize; 3039ee38883SJon Lin rval = mtd_erase(mtd, &ei); 3049ee38883SJon Lin if (rval) { 3059ee38883SJon Lin pr_info("error %d while erasing %llx\n", rval, 3069ee38883SJon Lin mapped_offset); 3079ee38883SJon Lin return rval; 3089ee38883SJon Lin } 3099ee38883SJon Lin } 3109ee38883SJon Lin 3119ee38883SJon Lin if (left_to_write < (blocksize - block_offset)) 3129ee38883SJon Lin write_size = left_to_write; 3139ee38883SJon Lin else 3149ee38883SJon Lin write_size = blocksize - block_offset; 3159ee38883SJon Lin 3169ee38883SJon Lin truncated_write_size = write_size; 3179ee38883SJon Lin rval = mtd_write(mtd, mapped_offset, truncated_write_size, 3189ee38883SJon Lin (size_t *)(&truncated_write_size), p_buffer); 3199ee38883SJon Lin 3209ee38883SJon Lin offset += write_size; 3219ee38883SJon Lin p_buffer += write_size; 3229ee38883SJon Lin 3239ee38883SJon Lin if (rval != 0) { 3249ee38883SJon Lin printf("NAND write to offset %llx failed %d\n", 3259ee38883SJon Lin offset, rval); 3269ee38883SJon Lin *length -= left_to_write; 3279ee38883SJon Lin return rval; 3289ee38883SJon Lin } 3299ee38883SJon Lin 3309ee38883SJon Lin left_to_write -= write_size; 3319ee38883SJon Lin } 3329ee38883SJon Lin 3339ee38883SJon Lin return 0; 3349ee38883SJon Lin } 3359ee38883SJon Lin 336338697c5SJon Lin static __maybe_unused int mtd_map_erase(struct mtd_info *mtd, loff_t offset, 337338697c5SJon Lin size_t length) 338338697c5SJon Lin { 339338697c5SJon Lin struct erase_info ei; 340338697c5SJon Lin loff_t pos, len; 341338697c5SJon Lin int ret; 342338697c5SJon Lin 343338697c5SJon Lin pos = offset; 344338697c5SJon Lin len = length; 345338697c5SJon Lin 346338697c5SJon Lin if ((pos & mtd->erasesize_mask) || (len & mtd->erasesize_mask)) { 347338697c5SJon Lin pr_err("Attempt to erase non block-aligned data, pos= %llx, len= %llx\n", 348338697c5SJon Lin pos, len); 349338697c5SJon Lin 350338697c5SJon Lin return -EINVAL; 351338697c5SJon Lin } 352338697c5SJon Lin 353338697c5SJon Lin while (len) { 354bd676549SJon Lin loff_t mapped_offset; 355bd676549SJon Lin 356bd676549SJon Lin mapped_offset = pos; 357bd676549SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 358338697c5SJon Lin if (mtd_block_isbad(mtd, pos) || mtd_block_isreserved(mtd, pos)) { 359338697c5SJon Lin pr_debug("attempt to erase a bad/reserved block @%llx\n", 360338697c5SJon Lin pos); 361338697c5SJon Lin pos += mtd->erasesize; 362338697c5SJon Lin continue; 363338697c5SJon Lin } 364bd676549SJon Lin } 365338697c5SJon Lin 366338697c5SJon Lin memset(&ei, 0, sizeof(struct erase_info)); 367bd676549SJon Lin ei.addr = mapped_offset; 368338697c5SJon Lin ei.len = mtd->erasesize; 369338697c5SJon Lin ret = mtd_erase(mtd, &ei); 370338697c5SJon Lin if (ret) { 371338697c5SJon Lin pr_err("map_erase error %d while erasing %llx\n", ret, 372338697c5SJon Lin pos); 373338697c5SJon Lin return ret; 374338697c5SJon Lin } 375338697c5SJon Lin 376338697c5SJon Lin pos += mtd->erasesize; 377338697c5SJon Lin len -= mtd->erasesize; 378338697c5SJon Lin } 379338697c5SJon Lin 380338697c5SJon Lin return 0; 381338697c5SJon Lin } 382338697c5SJon Lin 383c4fe67c2SJason Zhu char *mtd_part_parse(struct blk_desc *dev_desc) 38422dccd11SJason Zhu { 38522dccd11SJason Zhu char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0}; 38622dccd11SJason Zhu u32 length, data_len = MTD_PART_INFO_MAX_SIZE; 38722dccd11SJason Zhu disk_partition_t info; 38822dccd11SJason Zhu char *mtd_part_info_p; 389c9289eddSJason Zhu struct mtd_info *mtd; 39022dccd11SJason Zhu char *mtd_part_info; 39122dccd11SJason Zhu int ret; 39222dccd11SJason Zhu int p; 39322dccd11SJason Zhu 394c4fe67c2SJason Zhu #ifndef CONFIG_SPL_BUILD 39522dccd11SJason Zhu dev_desc = rockchip_get_bootdev(); 396c4fe67c2SJason Zhu #endif 39722dccd11SJason Zhu if (!dev_desc) 39822dccd11SJason Zhu return NULL; 39922dccd11SJason Zhu 400c9289eddSJason Zhu mtd = (struct mtd_info *)dev_desc->bdev->priv; 401ec6d4288SJason Zhu if (!mtd) 402ec6d4288SJason Zhu return NULL; 403ec6d4288SJason Zhu 40422dccd11SJason Zhu mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char)); 40522dccd11SJason Zhu if (!mtd_part_info) { 40622dccd11SJason Zhu printf("%s: Fail to malloc!", __func__); 40722dccd11SJason Zhu return NULL; 40822dccd11SJason Zhu } 40922dccd11SJason Zhu 41022dccd11SJason Zhu mtd_part_info_p = mtd_part_info; 41122dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "%s%s:", 41222dccd11SJason Zhu MTD_PART_NAND_HEAD, 41322dccd11SJason Zhu dev_desc->product); 41422dccd11SJason Zhu data_len -= strlen(mtd_part_info_p); 41522dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p); 41622dccd11SJason Zhu 41722dccd11SJason Zhu for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { 41822dccd11SJason Zhu ret = part_get_info(dev_desc, p, &info); 41922dccd11SJason Zhu if (ret) 42022dccd11SJason Zhu break; 42122dccd11SJason Zhu 42222dccd11SJason Zhu debug("name is %s, start addr is %x\n", info.name, 42322dccd11SJason Zhu (int)(size_t)info.start); 42422dccd11SJason Zhu 42522dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 42622dccd11SJason Zhu (int)(size_t)info.size << 9, 42722dccd11SJason Zhu (int)(size_t)info.start << 9, 42822dccd11SJason Zhu info.name); 42922dccd11SJason Zhu snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1, 43022dccd11SJason Zhu "0x%x@0x%x(%s)", 43122dccd11SJason Zhu (int)(size_t)info.size << 9, 43222dccd11SJason Zhu (int)(size_t)info.start << 9, 43322dccd11SJason Zhu info.name); 43422dccd11SJason Zhu strcat(mtd_part_info, ","); 435a3e58bf5SJon Lin if (part_get_info(dev_desc, p + 1, &info)) { 436a3e58bf5SJon Lin /* Partition with grow tag in parameter will be resized */ 437106a71f0SJon Lin if ((info.size + info.start + 64) >= dev_desc->lba || 438106a71f0SJon Lin (info.size + info.start - 1) == FACTORY_UNKNOWN_LBA) { 439ce9d2743SJon Lin if (dev_desc->devnum == BLK_MTD_SPI_NOR) { 440ce9d2743SJon Lin /* Nor is 64KB erase block(kernel) and gpt table just 441ce9d2743SJon Lin * resserve 33 sectors for the last partition. This 442ce9d2743SJon Lin * will erase the backup gpt table by user program, 443ce9d2743SJon Lin * so reserve one block. 444ce9d2743SJon Lin */ 445ce9d2743SJon Lin snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 446ce9d2743SJon Lin (int)(size_t)(info.size - 447ce9d2743SJon Lin (info.size - 1) % 448ce9d2743SJon Lin (0x10000 >> 9) - 1) << 9, 449ce9d2743SJon Lin (int)(size_t)info.start << 9, 450ce9d2743SJon Lin info.name); 451ce9d2743SJon Lin break; 452ce9d2743SJon Lin } else { 453c9289eddSJason Zhu /* Nand flash is erased by block and gpt table just 454c9289eddSJason Zhu * resserve 33 sectors for the last partition. This 455c9289eddSJason Zhu * will erase the backup gpt table by user program, 456c9289eddSJason Zhu * so reserve one block. 457c9289eddSJason Zhu */ 458c9289eddSJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 459c9289eddSJason Zhu (int)(size_t)(info.size - 460c9289eddSJason Zhu (info.size - 1) % 461c9289eddSJason Zhu (mtd->erasesize >> 9) - 1) << 9, 46222dccd11SJason Zhu (int)(size_t)info.start << 9, 46322dccd11SJason Zhu info.name); 46422dccd11SJason Zhu break; 46522dccd11SJason Zhu } 466a3e58bf5SJon Lin } else { 467a3e58bf5SJon Lin snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1, 468a3e58bf5SJon Lin "0x%x@0x%x(%s)", 469a3e58bf5SJon Lin (int)(size_t)info.size << 9, 470a3e58bf5SJon Lin (int)(size_t)info.start << 9, 471a3e58bf5SJon Lin info.name); 472a3e58bf5SJon Lin break; 473a3e58bf5SJon Lin } 474ce9d2743SJon Lin } 47522dccd11SJason Zhu length = strlen(mtd_part_info_temp); 47622dccd11SJason Zhu data_len -= length; 47722dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + length + 1; 47822dccd11SJason Zhu memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE); 47922dccd11SJason Zhu } 48022dccd11SJason Zhu 48122dccd11SJason Zhu return mtd_part_info; 48222dccd11SJason Zhu } 48322dccd11SJason Zhu 484054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start, 485054229abSJason Zhu lbaint_t blkcnt, void *dst) 486054229abSJason Zhu { 487054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 4880dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 4896e8ac5a8SJason Zhu loff_t off = (loff_t)(start * 512); 4906e8ac5a8SJason Zhu size_t rwsize = blkcnt * 512; 4910dccd0d8SJason Zhu #endif 4926e8ac5a8SJason Zhu struct mtd_info *mtd; 493bbb83f58SJason Zhu int ret = 0; 4941a3d87beSJon Lin #ifdef MTD_BLK_VERBOSE 4951a3d87beSJon Lin ulong us = 1; 4961a3d87beSJon Lin #endif 497054229abSJason Zhu 498054229abSJason Zhu if (!desc) 49939e38ab3SJason Zhu return ret; 500054229abSJason Zhu 5016e8ac5a8SJason Zhu mtd = desc->bdev->priv; 5026e8ac5a8SJason Zhu if (!mtd) 5036e8ac5a8SJason Zhu return 0; 5046e8ac5a8SJason Zhu 505054229abSJason Zhu if (blkcnt == 0) 506054229abSJason Zhu return 0; 507054229abSJason Zhu 5081a3d87beSJon Lin #ifdef MTD_BLK_VERBOSE 5091a3d87beSJon Lin us = get_ticks(); 5101a3d87beSJon Lin #endif 511054229abSJason Zhu if (desc->devnum == BLK_MTD_NAND) { 5121f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 5131f21bf61SJon Lin NULL, mtd->size, 5141f21bf61SJon Lin (u_char *)(dst)); 5156e8ac5a8SJason Zhu if (!ret) 5161a3d87beSJon Lin ret = blkcnt; 517054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NAND) { 5186af5fcf6SJon Lin #if defined(CONFIG_MTD_SPI_NAND) 5196af5fcf6SJon Lin struct spinand_device *spinand = mtd_to_spinand(mtd); 5206af5fcf6SJon Lin struct spi_slave *spi = spinand->slave; 5216af5fcf6SJon Lin size_t retlen_nand; 5226af5fcf6SJon Lin 5236af5fcf6SJon Lin if (desc->op_flag == BLK_PRE_RW) { 5246af5fcf6SJon Lin spi->mode |= SPI_DMA_PREPARE; 5256af5fcf6SJon Lin ret = mtd_read(mtd, off, rwsize, 5266af5fcf6SJon Lin &retlen_nand, (u_char *)(dst)); 5276af5fcf6SJon Lin spi->mode &= ~SPI_DMA_PREPARE; 5286af5fcf6SJon Lin if (retlen_nand == rwsize) 5296af5fcf6SJon Lin ret = blkcnt; 5306af5fcf6SJon Lin } else { 5316af5fcf6SJon Lin if (spinand->support_cont_read) 5326af5fcf6SJon Lin ret = mtd_read(mtd, off, rwsize, 5336af5fcf6SJon Lin &retlen_nand, 5346af5fcf6SJon Lin (u_char *)(dst)); 5356af5fcf6SJon Lin else 5361f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 537bbb83f58SJason Zhu NULL, mtd->size, 538bbb83f58SJason Zhu (u_char *)(dst)); 539bbb83f58SJason Zhu if (!ret) 5401a3d87beSJon Lin ret = blkcnt; 5416af5fcf6SJon Lin } 5426af5fcf6SJon Lin #endif 543054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NOR) { 5440dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD) 5453fb7bf02SJason Zhu struct spi_nor *nor = (struct spi_nor *)mtd->priv; 5463fb7bf02SJason Zhu struct spi_slave *spi = nor->spi; 5470dccd0d8SJason Zhu size_t retlen_nor; 5480dccd0d8SJason Zhu 5493fb7bf02SJason Zhu if (desc->op_flag == BLK_PRE_RW) 5503fb7bf02SJason Zhu spi->mode |= SPI_DMA_PREPARE; 5511a3d87beSJon Lin ret = mtd_read(mtd, off, rwsize, &retlen_nor, dst); 5523fb7bf02SJason Zhu if (desc->op_flag == BLK_PRE_RW) 553bfdb06edSJon Lin spi->mode &= ~SPI_DMA_PREPARE; 5543fb7bf02SJason Zhu 5550dccd0d8SJason Zhu if (retlen_nor == rwsize) 5561a3d87beSJon Lin ret = blkcnt; 5570dccd0d8SJason Zhu #endif 558054229abSJason Zhu } 5591a3d87beSJon Lin #ifdef MTD_BLK_VERBOSE 56013ceb2afSXuhui Lin us = (get_ticks() - us) / (gd->arch.timer_rate_hz / 1000000); 5611a3d87beSJon Lin pr_err("mtd dread %s %lx %lx cost %ldus: %ldMB/s\n\n", mtd->name, start, blkcnt, us, (blkcnt / 2) / ((us + 999) / 1000)); 5621a3d87beSJon Lin #else 5631a3d87beSJon Lin pr_debug("mtd dread %s %lx %lx\n\n", mtd->name, start, blkcnt); 5641a3d87beSJon Lin #endif 5651a3d87beSJon Lin 5661a3d87beSJon Lin return ret; 567054229abSJason Zhu } 568054229abSJason Zhu 569a0166cc6SJason Zhu #if CONFIG_IS_ENABLED(MTD_WRITE) 570054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start, 571054229abSJason Zhu lbaint_t blkcnt, const void *src) 572054229abSJason Zhu { 5739ee38883SJon Lin struct blk_desc *desc = dev_get_uclass_platdata(udev); 5749ee38883SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 5759ee38883SJon Lin loff_t off = (loff_t)(start * 512); 5769ee38883SJon Lin size_t rwsize = blkcnt * 512; 5779ee38883SJon Lin #endif 5789ee38883SJon Lin struct mtd_info *mtd; 5799ee38883SJon Lin int ret = 0; 5809ee38883SJon Lin 5819ee38883SJon Lin if (!desc) 5829ee38883SJon Lin return ret; 5839ee38883SJon Lin 5849ee38883SJon Lin mtd = desc->bdev->priv; 5859ee38883SJon Lin if (!mtd) 5869ee38883SJon Lin return 0; 5879ee38883SJon Lin 5889ee38883SJon Lin pr_debug("mtd dwrite %s %lx %lx\n", mtd->name, start, blkcnt); 5899ee38883SJon Lin 5909ee38883SJon Lin if (blkcnt == 0) 5919ee38883SJon Lin return 0; 5929ee38883SJon Lin 593af0bf855SJon Lin if (desc->op_flag & BLK_MTD_CONT_WRITE && 594af0bf855SJon Lin (start == 1 || ((desc->lba - start) <= 33))) { 595af0bf855SJon Lin printf("Write in GPT area, lba=%ld cnt=%ld\n", start, blkcnt); 596f4688965SJon Lin desc->op_flag &= ~BLK_MTD_CONT_WRITE; 597f4688965SJon Lin } 598f4688965SJon Lin 5999ee38883SJon Lin if (desc->devnum == BLK_MTD_NAND || 6009ee38883SJon Lin desc->devnum == BLK_MTD_SPI_NAND || 6019ee38883SJon Lin desc->devnum == BLK_MTD_SPI_NOR) { 602bee19e1dSJon Lin if (desc->op_flag & BLK_MTD_CONT_WRITE) { 603853fc11fSJon Lin ret = mtd_map_write(mtd, off, &rwsize, 604853fc11fSJon Lin NULL, mtd->size, 605853fc11fSJon Lin (u_char *)(src), 0); 606853fc11fSJon Lin if (!ret) 607853fc11fSJon Lin return blkcnt; 608853fc11fSJon Lin else 609853fc11fSJon Lin return 0; 610853fc11fSJon Lin } else { 61142439462SJon Lin lbaint_t off_aligned, alinged; 61242439462SJon Lin size_t rwsize_aligned; 61342439462SJon Lin u8 *p_buf; 61442439462SJon Lin 61542439462SJon Lin alinged = off & mtd->erasesize_mask; 61642439462SJon Lin off_aligned = off - alinged; 61742439462SJon Lin rwsize_aligned = rwsize + alinged; 61842439462SJon Lin rwsize_aligned = (rwsize_aligned + mtd->erasesize - 1) & 61942439462SJon Lin ~(mtd->erasesize - 1); 62042439462SJon Lin 62142439462SJon Lin p_buf = malloc(rwsize_aligned); 62242439462SJon Lin if (!p_buf) { 62342439462SJon Lin printf("%s: Fail to malloc!", __func__); 62442439462SJon Lin return 0; 62542439462SJon Lin } 62642439462SJon Lin 62742439462SJon Lin ret = mtd_map_read(mtd, off_aligned, &rwsize_aligned, 62842439462SJon Lin NULL, mtd->size, 62942439462SJon Lin (u_char *)(p_buf)); 63042439462SJon Lin if (ret) { 63142439462SJon Lin free(p_buf); 63242439462SJon Lin return 0; 63342439462SJon Lin } 63442439462SJon Lin 63542439462SJon Lin memcpy(p_buf + alinged, src, rwsize); 63642439462SJon Lin 63742439462SJon Lin ret = mtd_map_write(mtd, off_aligned, &rwsize_aligned, 63842439462SJon Lin NULL, mtd->size, 63942439462SJon Lin (u_char *)(p_buf), 0); 64042439462SJon Lin free(p_buf); 64142439462SJon Lin if (!ret) 64242439462SJon Lin return blkcnt; 64342439462SJon Lin else 64442439462SJon Lin return 0; 64542439462SJon Lin } 6469ee38883SJon Lin } else { 6479ee38883SJon Lin return 0; 6489ee38883SJon Lin } 6499ee38883SJon Lin 650054229abSJason Zhu return 0; 651054229abSJason Zhu } 652054229abSJason Zhu 653054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start, 654054229abSJason Zhu lbaint_t blkcnt) 655054229abSJason Zhu { 656338697c5SJon Lin struct blk_desc *desc = dev_get_uclass_platdata(udev); 657338697c5SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 658338697c5SJon Lin loff_t off = (loff_t)(start * 512); 659338697c5SJon Lin size_t len = blkcnt * 512; 660338697c5SJon Lin #endif 661338697c5SJon Lin struct mtd_info *mtd; 662338697c5SJon Lin int ret = 0; 663338697c5SJon Lin 664338697c5SJon Lin if (!desc) 665338697c5SJon Lin return ret; 666338697c5SJon Lin 667338697c5SJon Lin mtd = desc->bdev->priv; 668338697c5SJon Lin if (!mtd) 669338697c5SJon Lin return 0; 670338697c5SJon Lin 671338697c5SJon Lin pr_debug("mtd derase %s %lx %lx\n", mtd->name, start, blkcnt); 672e84cdeddSJon Lin len = round_up(len, mtd->erasesize); 673338697c5SJon Lin 674338697c5SJon Lin if (blkcnt == 0) 675338697c5SJon Lin return 0; 676338697c5SJon Lin 677338697c5SJon Lin if (desc->devnum == BLK_MTD_NAND || 6789148182dSJon Lin desc->devnum == BLK_MTD_SPI_NAND || 6799148182dSJon Lin desc->devnum == BLK_MTD_SPI_NOR) { 680338697c5SJon Lin ret = mtd_map_erase(mtd, off, len); 681338697c5SJon Lin if (ret) 682338697c5SJon Lin return ret; 683338697c5SJon Lin } else { 684338697c5SJon Lin return 0; 685338697c5SJon Lin } 686338697c5SJon Lin 6877182d4beSJon Lin return blkcnt; 688054229abSJason Zhu } 689a0166cc6SJason Zhu #endif 690054229abSJason Zhu 691054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev) 692054229abSJason Zhu { 6936524556dSJon Lin struct mtd_info *mtd; 694054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 6956524556dSJon Lin int ret, i = 0; 6966524556dSJon Lin 6976524556dSJon Lin mtd = dev_get_uclass_priv(udev->parent); 6986524556dSJon Lin if (mtd->type == MTD_NANDFLASH && desc->devnum == BLK_MTD_NAND) { 6996524556dSJon Lin #ifndef CONFIG_SPL_BUILD 7006524556dSJon Lin mtd = dev_get_priv(udev->parent); 7016524556dSJon Lin #endif 7026524556dSJon Lin } 703054229abSJason Zhu 704479a1588SJon Lin /* Fill mtd devices information */ 705479a1588SJon Lin if (is_power_of_2(mtd->erasesize)) 706479a1588SJon Lin mtd->erasesize_shift = ffs(mtd->erasesize) - 1; 707479a1588SJon Lin else 708479a1588SJon Lin mtd->erasesize_shift = 0; 709479a1588SJon Lin 710479a1588SJon Lin if (is_power_of_2(mtd->writesize)) 711479a1588SJon Lin mtd->writesize_shift = ffs(mtd->writesize) - 1; 712479a1588SJon Lin else 713479a1588SJon Lin mtd->writesize_shift = 0; 714479a1588SJon Lin 715479a1588SJon Lin mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; 716479a1588SJon Lin mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; 717479a1588SJon Lin 718c9289eddSJason Zhu desc->bdev->priv = mtd; 719054229abSJason Zhu sprintf(desc->vendor, "0x%.4x", 0x2207); 720a5c69560SJon Lin if (strncmp(mtd->name, "nand", 4) == 0) 721a5c69560SJon Lin memcpy(desc->product, "rk-nand", strlen("rk-nand")); 722a5c69560SJon Lin else 723e6482de4SJason Zhu memcpy(desc->product, mtd->name, strlen(mtd->name)); 724054229abSJason Zhu memcpy(desc->revision, "V1.00", sizeof("V1.00")); 725f1892190SJason Zhu if (mtd->type == MTD_NANDFLASH) { 7266524556dSJon Lin #ifdef CONFIG_NAND 727f3ba630bSJason Zhu if (desc->devnum == BLK_MTD_NAND) 7286524556dSJon Lin i = NAND_BBT_SCAN_MAXBLOCKS; 729*ab7b5b70SJon Lin #endif 730*ab7b5b70SJon Lin #ifdef CONFIG_MTD_SPI_NAND 731*ab7b5b70SJon Lin if (desc->devnum == BLK_MTD_SPI_NAND) 7326524556dSJon Lin i = NANDDEV_BBT_SCAN_MAXBLOCKS; 7336524556dSJon Lin #endif 7346524556dSJon Lin 7351d39542fSJason Zhu /* 7361d39542fSJason Zhu * Find the first useful block in the end, 7371d39542fSJason Zhu * and it is the end lba of the nand storage. 7381d39542fSJason Zhu */ 7396524556dSJon Lin for (; i < (mtd->size / mtd->erasesize); i++) { 7401d39542fSJason Zhu ret = mtd_block_isbad(mtd, 7411d39542fSJason Zhu mtd->size - mtd->erasesize * (i + 1)); 7421d39542fSJason Zhu if (!ret) { 7431d39542fSJason Zhu desc->lba = (mtd->size >> 9) - 7441d39542fSJason Zhu (mtd->erasesize >> 9) * i; 7451d39542fSJason Zhu break; 7461d39542fSJason Zhu } 7471d39542fSJason Zhu } 748f1892190SJason Zhu } else { 749f1892190SJason Zhu desc->lba = mtd->size >> 9; 750f1892190SJason Zhu } 751054229abSJason Zhu 7521d39542fSJason Zhu debug("MTD: desc->lba is %lx\n", desc->lba); 7531d39542fSJason Zhu 754054229abSJason Zhu return 0; 755054229abSJason Zhu } 756054229abSJason Zhu 757054229abSJason Zhu static const struct blk_ops mtd_blk_ops = { 758054229abSJason Zhu .read = mtd_dread, 759a0166cc6SJason Zhu #if CONFIG_IS_ENABLED(MTD_WRITE) 760054229abSJason Zhu .write = mtd_dwrite, 761054229abSJason Zhu .erase = mtd_derase, 762054229abSJason Zhu #endif 763054229abSJason Zhu }; 764054229abSJason Zhu 765054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = { 766054229abSJason Zhu .name = "mtd_blk", 767054229abSJason Zhu .id = UCLASS_BLK, 768054229abSJason Zhu .ops = &mtd_blk_ops, 769054229abSJason Zhu .probe = mtd_blk_probe, 770054229abSJason Zhu }; 771