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> 1222dccd11SJason Zhu #include <malloc.h> 13054229abSJason Zhu #include <nand.h> 1422dccd11SJason Zhu #include <part.h> 153fb7bf02SJason Zhu #include <spi.h> 16054229abSJason Zhu #include <dm/device-internal.h> 173fb7bf02SJason Zhu #include <linux/mtd/spi-nor.h> 186524556dSJon Lin #ifdef CONFIG_NAND 196524556dSJon Lin #include <linux/mtd/nand.h> 206524556dSJon Lin #endif 21054229abSJason Zhu 2222dccd11SJason Zhu #define MTD_PART_NAND_HEAD "mtdparts=" 2304a8326aSJason Zhu #define MTD_ROOT_PART_NUM "ubi.mtd=" 2404a8326aSJason Zhu #define MTD_ROOT_PART_NAME "root=ubi0:rootfs" 2522dccd11SJason Zhu #define MTD_PART_INFO_MAX_SIZE 512 2622dccd11SJason Zhu #define MTD_SINGLE_PART_INFO_MAX_SIZE 40 2722dccd11SJason Zhu 28c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_UNKNOWN (-2) 29c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_SHIFT (-1) 30c9e94690SJon Lin 311f21bf61SJon Lin static int *mtd_map_blk_table; 321f21bf61SJon Lin 331f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc, 341f21bf61SJon Lin loff_t offset, 351f21bf61SJon Lin size_t length) 361f21bf61SJon Lin { 371f21bf61SJon Lin u32 blk_total, blk_begin, blk_cnt; 381f21bf61SJon Lin struct mtd_info *mtd = NULL; 391f21bf61SJon Lin int i, j; 401f21bf61SJon Lin 411f21bf61SJon Lin if (!desc) 421f21bf61SJon Lin return -ENODEV; 431f21bf61SJon Lin 446524556dSJon Lin switch (desc->devnum) { 456524556dSJon Lin case BLK_MTD_NAND: 466524556dSJon Lin case BLK_MTD_SPI_NAND: 471f21bf61SJon Lin mtd = desc->bdev->priv; 486524556dSJon Lin break; 496524556dSJon Lin default: 506524556dSJon Lin break; 511f21bf61SJon Lin } 521f21bf61SJon Lin 531f21bf61SJon Lin if (!mtd) { 541f21bf61SJon Lin return -ENODEV; 551f21bf61SJon Lin } else { 56b4e07918SJon Lin blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift; 571f21bf61SJon Lin if (!mtd_map_blk_table) { 580f1dc487SJon Lin mtd_map_blk_table = (int *)malloc(blk_total * sizeof(int)); 596524556dSJon Lin if (!mtd_map_blk_table) 606524556dSJon Lin return -ENOMEM; 610f1dc487SJon Lin for (i = 0; i < blk_total; i++) 620f1dc487SJon Lin mtd_map_blk_table[i] = MTD_BLK_TABLE_BLOCK_UNKNOWN; 631f21bf61SJon Lin } 641f21bf61SJon Lin 65b4e07918SJon Lin blk_begin = (u32)offset >> mtd->erasesize_shift; 66e091dc9dSJon Lin blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length + \ 67e091dc9dSJon Lin mtd->erasesize - 1) >> mtd->erasesize_shift); 680f1dc487SJon Lin if (blk_begin >= blk_total) { 690f1dc487SJon Lin pr_err("map table blk begin[%d] overflow\n", blk_begin); 700f1dc487SJon Lin return -EINVAL; 710f1dc487SJon Lin } 72d6290238SJon Lin if ((blk_begin + blk_cnt) > blk_total) 73d6290238SJon Lin blk_cnt = blk_total - blk_begin; 74c9e94690SJon Lin 75c9e94690SJon Lin if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN) 76c9e94690SJon Lin return 0; 77c9e94690SJon Lin 781f21bf61SJon Lin j = 0; 791f21bf61SJon Lin /* should not across blk_cnt */ 801f21bf61SJon Lin for (i = 0; i < blk_cnt; i++) { 811f21bf61SJon Lin if (j >= blk_cnt) 82c9e94690SJon Lin mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT; 831f21bf61SJon Lin for (; j < blk_cnt; j++) { 84b4e07918SJon Lin if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) { 851f21bf61SJon Lin mtd_map_blk_table[blk_begin + i] = blk_begin + j; 861f21bf61SJon Lin j++; 871f21bf61SJon Lin if (j == blk_cnt) 881f21bf61SJon Lin j++; 891f21bf61SJon Lin break; 901f21bf61SJon Lin } 911f21bf61SJon Lin } 921f21bf61SJon Lin } 931f21bf61SJon Lin 941f21bf61SJon Lin return 0; 951f21bf61SJon Lin } 961f21bf61SJon Lin } 971f21bf61SJon Lin 98c402731fSJon Lin static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off) 99c402731fSJon Lin { 100c402731fSJon Lin bool mapped; 101c402731fSJon Lin loff_t offset = *off; 102c402731fSJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 103c402731fSJon Lin 104c402731fSJon Lin mapped = false; 105c402731fSJon Lin if (!mtd_map_blk_table || 106c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 107c402731fSJon Lin MTD_BLK_TABLE_BLOCK_UNKNOWN || 108c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 109c402731fSJon Lin 0xffffffff) 110c402731fSJon Lin return mapped; 111c402731fSJon Lin 112c402731fSJon Lin mapped = true; 113c402731fSJon Lin *off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >> 114c402731fSJon Lin mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset); 115c402731fSJon Lin 116c402731fSJon Lin return mapped; 117c402731fSJon Lin } 118c402731fSJon Lin 119a07b97f2SJason Zhu void mtd_blk_map_partitions(struct blk_desc *desc) 120a07b97f2SJason Zhu { 121a07b97f2SJason Zhu disk_partition_t info; 122a07b97f2SJason Zhu int i, ret; 123a07b97f2SJason Zhu 124a07b97f2SJason Zhu if (!desc) 125a07b97f2SJason Zhu return; 126a07b97f2SJason Zhu 127a07b97f2SJason Zhu if (desc->if_type != IF_TYPE_MTD) 128a07b97f2SJason Zhu return; 129a07b97f2SJason Zhu 130a07b97f2SJason Zhu for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) { 131a07b97f2SJason Zhu ret = part_get_info(desc, i, &info); 132a07b97f2SJason Zhu if (ret != 0) 133a07b97f2SJason Zhu continue; 134a07b97f2SJason Zhu 135a07b97f2SJason Zhu if (mtd_blk_map_table_init(desc, 136a07b97f2SJason Zhu info.start << 9, 137a07b97f2SJason Zhu info.size << 9)) { 1389ee38883SJon Lin pr_debug("mtd block map table fail\n"); 139a07b97f2SJason Zhu } 140a07b97f2SJason Zhu } 141a07b97f2SJason Zhu } 142a07b97f2SJason Zhu 1431f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset, 1441f21bf61SJon Lin size_t *length, size_t *actual, 1451f21bf61SJon Lin loff_t lim, u_char *buffer) 1461f21bf61SJon Lin { 1471f21bf61SJon Lin size_t left_to_read = *length; 1481f21bf61SJon Lin u_char *p_buffer = buffer; 1491f21bf61SJon Lin int rval; 1501f21bf61SJon Lin 1511f21bf61SJon Lin while (left_to_read > 0) { 152360a2911SJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 1531f21bf61SJon Lin size_t read_length; 154360a2911SJon Lin loff_t mapped_offset; 1551f21bf61SJon Lin 1561f21bf61SJon Lin if (offset >= mtd->size) 1571f21bf61SJon Lin return 0; 1581f21bf61SJon Lin 159360a2911SJon Lin mapped_offset = offset; 160360a2911SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 161360a2911SJon Lin if (mtd_block_isbad(mtd, mapped_offset & 162360a2911SJon Lin ~(mtd->erasesize - 1))) { 163360a2911SJon Lin printf("Skipping bad block 0x%08llx\n", 164360a2911SJon Lin offset & ~(mtd->erasesize - 1)); 165360a2911SJon Lin offset += mtd->erasesize - block_offset; 1661f21bf61SJon Lin continue; 1671f21bf61SJon Lin } 1681f21bf61SJon Lin } 1691f21bf61SJon Lin 170360a2911SJon Lin if (left_to_read < (mtd->erasesize - block_offset)) 1711f21bf61SJon Lin read_length = left_to_read; 1721f21bf61SJon Lin else 173360a2911SJon Lin read_length = mtd->erasesize - block_offset; 1741f21bf61SJon Lin 175360a2911SJon Lin rval = mtd_read(mtd, mapped_offset, read_length, &read_length, 1761f21bf61SJon Lin p_buffer); 1771f21bf61SJon Lin if (rval && rval != -EUCLEAN) { 1781f21bf61SJon Lin printf("NAND read from offset %llx failed %d\n", 179c402731fSJon Lin offset, rval); 1801f21bf61SJon Lin *length -= left_to_read; 1811f21bf61SJon Lin return rval; 1821f21bf61SJon Lin } 1831f21bf61SJon Lin 1841f21bf61SJon Lin left_to_read -= read_length; 1851f21bf61SJon Lin offset += read_length; 1861f21bf61SJon Lin p_buffer += read_length; 1871f21bf61SJon Lin } 1881f21bf61SJon Lin 1891f21bf61SJon Lin return 0; 1901f21bf61SJon Lin } 1911f21bf61SJon Lin 1929ee38883SJon Lin static __maybe_unused int mtd_map_write(struct mtd_info *mtd, loff_t offset, 1939ee38883SJon Lin size_t *length, size_t *actual, 1949ee38883SJon Lin loff_t lim, u_char *buffer, int flags) 1959ee38883SJon Lin { 1969ee38883SJon Lin int rval = 0, blocksize; 1979ee38883SJon Lin size_t left_to_write = *length; 1989ee38883SJon Lin u_char *p_buffer = buffer; 1999ee38883SJon Lin struct erase_info ei; 2009ee38883SJon Lin 2019ee38883SJon Lin blocksize = mtd->erasesize; 2029ee38883SJon Lin 2039ee38883SJon Lin /* 2049ee38883SJon Lin * nand_write() handles unaligned, partial page writes. 2059ee38883SJon Lin * 2069ee38883SJon Lin * We allow length to be unaligned, for convenience in 2079ee38883SJon Lin * using the $filesize variable. 2089ee38883SJon Lin * 2099ee38883SJon Lin * However, starting at an unaligned offset makes the 2109ee38883SJon Lin * semantics of bad block skipping ambiguous (really, 2119ee38883SJon Lin * you should only start a block skipping access at a 2129ee38883SJon Lin * partition boundary). So don't try to handle that. 2139ee38883SJon Lin */ 2149ee38883SJon Lin if ((offset & (mtd->writesize - 1)) != 0) { 2159ee38883SJon Lin printf("Attempt to write non page-aligned data\n"); 2169ee38883SJon Lin *length = 0; 2179ee38883SJon Lin return -EINVAL; 2189ee38883SJon Lin } 2199ee38883SJon Lin 2209ee38883SJon Lin while (left_to_write > 0) { 2219ee38883SJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 2229ee38883SJon Lin size_t write_size, truncated_write_size; 2239ee38883SJon Lin loff_t mapped_offset; 2249ee38883SJon Lin 2259ee38883SJon Lin if (offset >= mtd->size) 2269ee38883SJon Lin return 0; 2279ee38883SJon Lin 2289ee38883SJon Lin mapped_offset = offset; 2299ee38883SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 2309ee38883SJon Lin if (mtd_block_isbad(mtd, mapped_offset & 2319ee38883SJon Lin ~(mtd->erasesize - 1))) { 2329ee38883SJon Lin printf("Skipping bad block 0x%08llx\n", 2339ee38883SJon Lin offset & ~(mtd->erasesize - 1)); 2349ee38883SJon Lin offset += mtd->erasesize - block_offset; 2359ee38883SJon Lin continue; 2369ee38883SJon Lin } 2379ee38883SJon Lin } 2389ee38883SJon Lin 2399ee38883SJon Lin if (!(mapped_offset & mtd->erasesize_mask)) { 2409ee38883SJon Lin memset(&ei, 0, sizeof(struct erase_info)); 2419ee38883SJon Lin ei.addr = mapped_offset; 2429ee38883SJon Lin ei.len = mtd->erasesize; 2439ee38883SJon Lin rval = mtd_erase(mtd, &ei); 2449ee38883SJon Lin if (rval) { 2459ee38883SJon Lin pr_info("error %d while erasing %llx\n", rval, 2469ee38883SJon Lin mapped_offset); 2479ee38883SJon Lin return rval; 2489ee38883SJon Lin } 2499ee38883SJon Lin } 2509ee38883SJon Lin 2519ee38883SJon Lin if (left_to_write < (blocksize - block_offset)) 2529ee38883SJon Lin write_size = left_to_write; 2539ee38883SJon Lin else 2549ee38883SJon Lin write_size = blocksize - block_offset; 2559ee38883SJon Lin 2569ee38883SJon Lin truncated_write_size = write_size; 2579ee38883SJon Lin rval = mtd_write(mtd, mapped_offset, truncated_write_size, 2589ee38883SJon Lin (size_t *)(&truncated_write_size), p_buffer); 2599ee38883SJon Lin 2609ee38883SJon Lin offset += write_size; 2619ee38883SJon Lin p_buffer += write_size; 2629ee38883SJon Lin 2639ee38883SJon Lin if (rval != 0) { 2649ee38883SJon Lin printf("NAND write to offset %llx failed %d\n", 2659ee38883SJon Lin offset, rval); 2669ee38883SJon Lin *length -= left_to_write; 2679ee38883SJon Lin return rval; 2689ee38883SJon Lin } 2699ee38883SJon Lin 2709ee38883SJon Lin left_to_write -= write_size; 2719ee38883SJon Lin } 2729ee38883SJon Lin 2739ee38883SJon Lin return 0; 2749ee38883SJon Lin } 2759ee38883SJon Lin 276*338697c5SJon Lin static __maybe_unused int mtd_map_erase(struct mtd_info *mtd, loff_t offset, 277*338697c5SJon Lin size_t length) 278*338697c5SJon Lin { 279*338697c5SJon Lin struct erase_info ei; 280*338697c5SJon Lin loff_t pos, len; 281*338697c5SJon Lin int ret; 282*338697c5SJon Lin 283*338697c5SJon Lin pos = offset; 284*338697c5SJon Lin len = length; 285*338697c5SJon Lin 286*338697c5SJon Lin if ((pos & mtd->erasesize_mask) || (len & mtd->erasesize_mask)) { 287*338697c5SJon Lin pr_err("Attempt to erase non block-aligned data, pos= %llx, len= %llx\n", 288*338697c5SJon Lin pos, len); 289*338697c5SJon Lin 290*338697c5SJon Lin return -EINVAL; 291*338697c5SJon Lin } 292*338697c5SJon Lin 293*338697c5SJon Lin while (len) { 294*338697c5SJon Lin if (mtd_block_isbad(mtd, pos) || mtd_block_isreserved(mtd, pos)) { 295*338697c5SJon Lin pr_debug("attempt to erase a bad/reserved block @%llx\n", 296*338697c5SJon Lin pos); 297*338697c5SJon Lin pos += mtd->erasesize; 298*338697c5SJon Lin continue; 299*338697c5SJon Lin } 300*338697c5SJon Lin 301*338697c5SJon Lin memset(&ei, 0, sizeof(struct erase_info)); 302*338697c5SJon Lin ei.addr = pos; 303*338697c5SJon Lin ei.len = mtd->erasesize; 304*338697c5SJon Lin ret = mtd_erase(mtd, &ei); 305*338697c5SJon Lin if (ret) { 306*338697c5SJon Lin pr_err("map_erase error %d while erasing %llx\n", ret, 307*338697c5SJon Lin pos); 308*338697c5SJon Lin return ret; 309*338697c5SJon Lin } 310*338697c5SJon Lin 311*338697c5SJon Lin pos += mtd->erasesize; 312*338697c5SJon Lin len -= mtd->erasesize; 313*338697c5SJon Lin } 314*338697c5SJon Lin 315*338697c5SJon Lin return 0; 316*338697c5SJon Lin } 317*338697c5SJon Lin 31822dccd11SJason Zhu char *mtd_part_parse(void) 31922dccd11SJason Zhu { 32022dccd11SJason Zhu char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0}; 32122dccd11SJason Zhu u32 length, data_len = MTD_PART_INFO_MAX_SIZE; 32204a8326aSJason Zhu char mtd_root_part_info[30] = {0}; 32322dccd11SJason Zhu struct blk_desc *dev_desc; 32422dccd11SJason Zhu disk_partition_t info; 32522dccd11SJason Zhu char *mtd_part_info_p; 326c9289eddSJason Zhu struct mtd_info *mtd; 32722dccd11SJason Zhu char *mtd_part_info; 32822dccd11SJason Zhu int ret; 32922dccd11SJason Zhu int p; 33022dccd11SJason Zhu 33122dccd11SJason Zhu dev_desc = rockchip_get_bootdev(); 33222dccd11SJason Zhu if (!dev_desc) 33322dccd11SJason Zhu return NULL; 33422dccd11SJason Zhu 335c9289eddSJason Zhu mtd = (struct mtd_info *)dev_desc->bdev->priv; 336ec6d4288SJason Zhu if (!mtd) 337ec6d4288SJason Zhu return NULL; 338ec6d4288SJason Zhu 33904a8326aSJason Zhu p = part_get_info_by_name(dev_desc, PART_SYSTEM, &info); 34004a8326aSJason Zhu if (p > 0) { 34104a8326aSJason Zhu snprintf(mtd_root_part_info, 30, "%s%d %s", MTD_ROOT_PART_NUM, p - 1, MTD_ROOT_PART_NAME); 34204a8326aSJason Zhu env_update("bootargs", mtd_root_part_info); 34304a8326aSJason Zhu } 34404a8326aSJason Zhu 34522dccd11SJason Zhu mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char)); 34622dccd11SJason Zhu if (!mtd_part_info) { 34722dccd11SJason Zhu printf("%s: Fail to malloc!", __func__); 34822dccd11SJason Zhu return NULL; 34922dccd11SJason Zhu } 35022dccd11SJason Zhu 35122dccd11SJason Zhu mtd_part_info_p = mtd_part_info; 35222dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "%s%s:", 35322dccd11SJason Zhu MTD_PART_NAND_HEAD, 35422dccd11SJason Zhu dev_desc->product); 35522dccd11SJason Zhu data_len -= strlen(mtd_part_info_p); 35622dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p); 35722dccd11SJason Zhu 35822dccd11SJason Zhu for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { 35922dccd11SJason Zhu ret = part_get_info(dev_desc, p, &info); 36022dccd11SJason Zhu if (ret) 36122dccd11SJason Zhu break; 36222dccd11SJason Zhu 36322dccd11SJason Zhu debug("name is %s, start addr is %x\n", info.name, 36422dccd11SJason Zhu (int)(size_t)info.start); 36522dccd11SJason Zhu 36622dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 36722dccd11SJason Zhu (int)(size_t)info.size << 9, 36822dccd11SJason Zhu (int)(size_t)info.start << 9, 36922dccd11SJason Zhu info.name); 37022dccd11SJason Zhu snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1, 37122dccd11SJason Zhu "0x%x@0x%x(%s)", 37222dccd11SJason Zhu (int)(size_t)info.size << 9, 37322dccd11SJason Zhu (int)(size_t)info.start << 9, 37422dccd11SJason Zhu info.name); 37522dccd11SJason Zhu strcat(mtd_part_info, ","); 37622dccd11SJason Zhu if (part_get_info(dev_desc, p + 1, &info)) { 377c9289eddSJason Zhu /* Nand flash is erased by block and gpt table just 378c9289eddSJason Zhu * resserve 33 sectors for the last partition. This 379c9289eddSJason Zhu * will erase the backup gpt table by user program, 380c9289eddSJason Zhu * so reserve one block. 381c9289eddSJason Zhu */ 382c9289eddSJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 383c9289eddSJason Zhu (int)(size_t)(info.size - 384c9289eddSJason Zhu (info.size - 1) % 385c9289eddSJason Zhu (mtd->erasesize >> 9) - 1) << 9, 38622dccd11SJason Zhu (int)(size_t)info.start << 9, 38722dccd11SJason Zhu info.name); 38822dccd11SJason Zhu break; 38922dccd11SJason Zhu } 39022dccd11SJason Zhu length = strlen(mtd_part_info_temp); 39122dccd11SJason Zhu data_len -= length; 39222dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + length + 1; 39322dccd11SJason Zhu memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE); 39422dccd11SJason Zhu } 39522dccd11SJason Zhu 39622dccd11SJason Zhu return mtd_part_info; 39722dccd11SJason Zhu } 39822dccd11SJason Zhu 399054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start, 400054229abSJason Zhu lbaint_t blkcnt, void *dst) 401054229abSJason Zhu { 402054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 4030dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 4046e8ac5a8SJason Zhu loff_t off = (loff_t)(start * 512); 4056e8ac5a8SJason Zhu size_t rwsize = blkcnt * 512; 4060dccd0d8SJason Zhu #endif 4076e8ac5a8SJason Zhu struct mtd_info *mtd; 408bbb83f58SJason Zhu int ret = 0; 409054229abSJason Zhu 410054229abSJason Zhu if (!desc) 41139e38ab3SJason Zhu return ret; 412054229abSJason Zhu 4136e8ac5a8SJason Zhu mtd = desc->bdev->priv; 4146e8ac5a8SJason Zhu if (!mtd) 4156e8ac5a8SJason Zhu return 0; 4166e8ac5a8SJason Zhu 417054229abSJason Zhu if (blkcnt == 0) 418054229abSJason Zhu return 0; 419054229abSJason Zhu 4209ee38883SJon Lin pr_debug("mtd dread %s %lx %lx\n", mtd->name, start, blkcnt); 4219ee38883SJon Lin 422054229abSJason Zhu if (desc->devnum == BLK_MTD_NAND) { 4231f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 4241f21bf61SJon Lin NULL, mtd->size, 4251f21bf61SJon Lin (u_char *)(dst)); 4266e8ac5a8SJason Zhu if (!ret) 427054229abSJason Zhu return blkcnt; 4286e8ac5a8SJason Zhu else 4296e8ac5a8SJason Zhu return 0; 430054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NAND) { 4311f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 432bbb83f58SJason Zhu NULL, mtd->size, 433bbb83f58SJason Zhu (u_char *)(dst)); 434bbb83f58SJason Zhu if (!ret) 4356e8ac5a8SJason Zhu return blkcnt; 4366e8ac5a8SJason Zhu else 437054229abSJason Zhu return 0; 438054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NOR) { 4390dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD) 4403fb7bf02SJason Zhu struct spi_nor *nor = (struct spi_nor *)mtd->priv; 4413fb7bf02SJason Zhu struct spi_slave *spi = nor->spi; 4420dccd0d8SJason Zhu size_t retlen_nor; 4430dccd0d8SJason Zhu 4443fb7bf02SJason Zhu if (desc->op_flag == BLK_PRE_RW) 4453fb7bf02SJason Zhu spi->mode |= SPI_DMA_PREPARE; 4460dccd0d8SJason Zhu mtd_read(mtd, off, rwsize, &retlen_nor, dst); 4473fb7bf02SJason Zhu if (desc->op_flag == BLK_PRE_RW) 4483fb7bf02SJason Zhu spi->mode |= SPI_DMA_PREPARE; 4493fb7bf02SJason Zhu 4500dccd0d8SJason Zhu if (retlen_nor == rwsize) 4510dccd0d8SJason Zhu return blkcnt; 4520dccd0d8SJason Zhu else 4530dccd0d8SJason Zhu #endif 454054229abSJason Zhu return 0; 455054229abSJason Zhu } else { 456054229abSJason Zhu return 0; 457054229abSJason Zhu } 458054229abSJason Zhu } 459054229abSJason Zhu 460054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start, 461054229abSJason Zhu lbaint_t blkcnt, const void *src) 462054229abSJason Zhu { 4639ee38883SJon Lin struct blk_desc *desc = dev_get_uclass_platdata(udev); 4649ee38883SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 4659ee38883SJon Lin loff_t off = (loff_t)(start * 512); 4669ee38883SJon Lin size_t rwsize = blkcnt * 512; 4679ee38883SJon Lin #endif 4689ee38883SJon Lin struct mtd_info *mtd; 4699ee38883SJon Lin int ret = 0; 4709ee38883SJon Lin 4719ee38883SJon Lin if (!desc) 4729ee38883SJon Lin return ret; 4739ee38883SJon Lin 4749ee38883SJon Lin mtd = desc->bdev->priv; 4759ee38883SJon Lin if (!mtd) 4769ee38883SJon Lin return 0; 4779ee38883SJon Lin 4789ee38883SJon Lin pr_debug("mtd dwrite %s %lx %lx\n", mtd->name, start, blkcnt); 4799ee38883SJon Lin 4809ee38883SJon Lin if (blkcnt == 0) 4819ee38883SJon Lin return 0; 4829ee38883SJon Lin 4839ee38883SJon Lin if (desc->devnum == BLK_MTD_NAND || 4849ee38883SJon Lin desc->devnum == BLK_MTD_SPI_NAND || 4859ee38883SJon Lin desc->devnum == BLK_MTD_SPI_NOR) { 486853fc11fSJon Lin if (desc->op_flag == BLK_MTD_CONT_WRITE) { 487853fc11fSJon Lin ret = mtd_map_write(mtd, off, &rwsize, 488853fc11fSJon Lin NULL, mtd->size, 489853fc11fSJon Lin (u_char *)(src), 0); 490853fc11fSJon Lin if (!ret) 491853fc11fSJon Lin return blkcnt; 492853fc11fSJon Lin else 493853fc11fSJon Lin return 0; 494853fc11fSJon Lin } else { 49542439462SJon Lin lbaint_t off_aligned, alinged; 49642439462SJon Lin size_t rwsize_aligned; 49742439462SJon Lin u8 *p_buf; 49842439462SJon Lin 49942439462SJon Lin alinged = off & mtd->erasesize_mask; 50042439462SJon Lin off_aligned = off - alinged; 50142439462SJon Lin rwsize_aligned = rwsize + alinged; 50242439462SJon Lin rwsize_aligned = (rwsize_aligned + mtd->erasesize - 1) & 50342439462SJon Lin ~(mtd->erasesize - 1); 50442439462SJon Lin 50542439462SJon Lin p_buf = malloc(rwsize_aligned); 50642439462SJon Lin if (!p_buf) { 50742439462SJon Lin printf("%s: Fail to malloc!", __func__); 50842439462SJon Lin return 0; 50942439462SJon Lin } 51042439462SJon Lin 51142439462SJon Lin ret = mtd_map_read(mtd, off_aligned, &rwsize_aligned, 51242439462SJon Lin NULL, mtd->size, 51342439462SJon Lin (u_char *)(p_buf)); 51442439462SJon Lin if (ret) { 51542439462SJon Lin free(p_buf); 51642439462SJon Lin return 0; 51742439462SJon Lin } 51842439462SJon Lin 51942439462SJon Lin memcpy(p_buf + alinged, src, rwsize); 52042439462SJon Lin 52142439462SJon Lin ret = mtd_map_write(mtd, off_aligned, &rwsize_aligned, 52242439462SJon Lin NULL, mtd->size, 52342439462SJon Lin (u_char *)(p_buf), 0); 52442439462SJon Lin free(p_buf); 52542439462SJon Lin if (!ret) 52642439462SJon Lin return blkcnt; 52742439462SJon Lin else 52842439462SJon Lin return 0; 52942439462SJon Lin } 5309ee38883SJon Lin } else { 5319ee38883SJon Lin return 0; 5329ee38883SJon Lin } 5339ee38883SJon Lin 534054229abSJason Zhu return 0; 535054229abSJason Zhu } 536054229abSJason Zhu 537054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start, 538054229abSJason Zhu lbaint_t blkcnt) 539054229abSJason Zhu { 540*338697c5SJon Lin struct blk_desc *desc = dev_get_uclass_platdata(udev); 541*338697c5SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 542*338697c5SJon Lin loff_t off = (loff_t)(start * 512); 543*338697c5SJon Lin size_t len = blkcnt * 512; 544*338697c5SJon Lin #endif 545*338697c5SJon Lin struct mtd_info *mtd; 546*338697c5SJon Lin int ret = 0; 547*338697c5SJon Lin 548*338697c5SJon Lin if (!desc) 549*338697c5SJon Lin return ret; 550*338697c5SJon Lin 551*338697c5SJon Lin mtd = desc->bdev->priv; 552*338697c5SJon Lin if (!mtd) 553*338697c5SJon Lin return 0; 554*338697c5SJon Lin 555*338697c5SJon Lin pr_debug("mtd derase %s %lx %lx\n", mtd->name, start, blkcnt); 556*338697c5SJon Lin 557*338697c5SJon Lin if (blkcnt == 0) 558*338697c5SJon Lin return 0; 559*338697c5SJon Lin 560*338697c5SJon Lin if (desc->devnum == BLK_MTD_NAND || 561*338697c5SJon Lin desc->devnum == BLK_MTD_SPI_NAND) { 562*338697c5SJon Lin ret = mtd_map_erase(mtd, off, len); 563*338697c5SJon Lin if (ret) 564*338697c5SJon Lin return ret; 565*338697c5SJon Lin } else { 566*338697c5SJon Lin return 0; 567*338697c5SJon Lin } 568*338697c5SJon Lin 569054229abSJason Zhu return 0; 570054229abSJason Zhu } 571054229abSJason Zhu 572054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev) 573054229abSJason Zhu { 5746524556dSJon Lin struct mtd_info *mtd; 575054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 5766524556dSJon Lin int ret, i = 0; 5776524556dSJon Lin 5786524556dSJon Lin mtd = dev_get_uclass_priv(udev->parent); 5796524556dSJon Lin if (mtd->type == MTD_NANDFLASH && desc->devnum == BLK_MTD_NAND) { 5806524556dSJon Lin #ifndef CONFIG_SPL_BUILD 5816524556dSJon Lin mtd = dev_get_priv(udev->parent); 5826524556dSJon Lin #endif 5836524556dSJon Lin } 584054229abSJason Zhu 585c9289eddSJason Zhu desc->bdev->priv = mtd; 586054229abSJason Zhu sprintf(desc->vendor, "0x%.4x", 0x2207); 587e6482de4SJason Zhu memcpy(desc->product, mtd->name, strlen(mtd->name)); 588054229abSJason Zhu memcpy(desc->revision, "V1.00", sizeof("V1.00")); 589f1892190SJason Zhu if (mtd->type == MTD_NANDFLASH) { 5906524556dSJon Lin #ifdef CONFIG_NAND 591f3ba630bSJason Zhu if (desc->devnum == BLK_MTD_NAND) 5926524556dSJon Lin i = NAND_BBT_SCAN_MAXBLOCKS; 5936524556dSJon Lin else if (desc->devnum == BLK_MTD_SPI_NAND) 5946524556dSJon Lin i = NANDDEV_BBT_SCAN_MAXBLOCKS; 5956524556dSJon Lin #endif 5966524556dSJon Lin 5971d39542fSJason Zhu /* 5981d39542fSJason Zhu * Find the first useful block in the end, 5991d39542fSJason Zhu * and it is the end lba of the nand storage. 6001d39542fSJason Zhu */ 6016524556dSJon Lin for (; i < (mtd->size / mtd->erasesize); i++) { 6021d39542fSJason Zhu ret = mtd_block_isbad(mtd, 6031d39542fSJason Zhu mtd->size - mtd->erasesize * (i + 1)); 6041d39542fSJason Zhu if (!ret) { 6051d39542fSJason Zhu desc->lba = (mtd->size >> 9) - 6061d39542fSJason Zhu (mtd->erasesize >> 9) * i; 6071d39542fSJason Zhu break; 6081d39542fSJason Zhu } 6091d39542fSJason Zhu } 610f1892190SJason Zhu } else { 611f1892190SJason Zhu desc->lba = mtd->size >> 9; 612f1892190SJason Zhu } 613054229abSJason Zhu 6141d39542fSJason Zhu debug("MTD: desc->lba is %lx\n", desc->lba); 6151d39542fSJason Zhu 616054229abSJason Zhu return 0; 617054229abSJason Zhu } 618054229abSJason Zhu 619054229abSJason Zhu static const struct blk_ops mtd_blk_ops = { 620054229abSJason Zhu .read = mtd_dread, 621054229abSJason Zhu #ifndef CONFIG_SPL_BUILD 622054229abSJason Zhu .write = mtd_dwrite, 623054229abSJason Zhu .erase = mtd_derase, 624054229abSJason Zhu #endif 625054229abSJason Zhu }; 626054229abSJason Zhu 627054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = { 628054229abSJason Zhu .name = "mtd_blk", 629054229abSJason Zhu .id = UCLASS_BLK, 630054229abSJason Zhu .ops = &mtd_blk_ops, 631054229abSJason Zhu .probe = mtd_blk_probe, 632054229abSJason Zhu }; 633