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> 15054229abSJason Zhu #include <dm/device-internal.h> 16054229abSJason Zhu 1722dccd11SJason Zhu #define MTD_PART_NAND_HEAD "mtdparts=" 1822dccd11SJason Zhu #define MTD_PART_INFO_MAX_SIZE 512 1922dccd11SJason Zhu #define MTD_SINGLE_PART_INFO_MAX_SIZE 40 2022dccd11SJason Zhu 21c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_UNKNOWN (-2) 22c9e94690SJon Lin #define MTD_BLK_TABLE_BLOCK_SHIFT (-1) 23c9e94690SJon Lin 241f21bf61SJon Lin static int *mtd_map_blk_table; 251f21bf61SJon Lin 261f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc, 271f21bf61SJon Lin loff_t offset, 281f21bf61SJon Lin size_t length) 291f21bf61SJon Lin { 301f21bf61SJon Lin u32 blk_total, blk_begin, blk_cnt; 311f21bf61SJon Lin struct mtd_info *mtd = NULL; 321f21bf61SJon Lin int i, j; 331f21bf61SJon Lin 341f21bf61SJon Lin if (!desc) 351f21bf61SJon Lin return -ENODEV; 361f21bf61SJon Lin 371f21bf61SJon Lin if (desc->devnum == BLK_MTD_NAND) { 381f21bf61SJon Lin #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD) 391f21bf61SJon Lin mtd = dev_get_priv(desc->bdev->parent); 401f21bf61SJon Lin #endif 411f21bf61SJon Lin } else if (desc->devnum == BLK_MTD_SPI_NAND) { 421f21bf61SJon Lin #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD) 431f21bf61SJon Lin mtd = desc->bdev->priv; 441f21bf61SJon Lin #endif 451f21bf61SJon Lin } 461f21bf61SJon Lin 471f21bf61SJon Lin if (!mtd) { 481f21bf61SJon Lin return -ENODEV; 491f21bf61SJon Lin } else { 50b4e07918SJon Lin blk_total = (mtd->size + mtd->erasesize - 1) >> mtd->erasesize_shift; 511f21bf61SJon Lin if (!mtd_map_blk_table) { 521f21bf61SJon Lin mtd_map_blk_table = (int *)malloc(blk_total * 4); 53c9e94690SJon Lin memset(mtd_map_blk_table, MTD_BLK_TABLE_BLOCK_UNKNOWN, 54c9e94690SJon Lin blk_total * 4); 551f21bf61SJon Lin } 561f21bf61SJon Lin 57b4e07918SJon Lin blk_begin = (u32)offset >> mtd->erasesize_shift; 58b4e07918SJon Lin blk_cnt = ((u32)((offset & mtd->erasesize_mask) + length) >> mtd->erasesize_shift); 59d6290238SJon Lin if ((blk_begin + blk_cnt) > blk_total) 60d6290238SJon Lin blk_cnt = blk_total - blk_begin; 61c9e94690SJon Lin 62c9e94690SJon Lin if (mtd_map_blk_table[blk_begin] != MTD_BLK_TABLE_BLOCK_UNKNOWN) 63c9e94690SJon Lin return 0; 64c9e94690SJon Lin 651f21bf61SJon Lin j = 0; 661f21bf61SJon Lin /* should not across blk_cnt */ 671f21bf61SJon Lin for (i = 0; i < blk_cnt; i++) { 681f21bf61SJon Lin if (j >= blk_cnt) 69c9e94690SJon Lin mtd_map_blk_table[blk_begin + i] = MTD_BLK_TABLE_BLOCK_SHIFT; 701f21bf61SJon Lin for (; j < blk_cnt; j++) { 71b4e07918SJon Lin if (!mtd_block_isbad(mtd, (blk_begin + j) << mtd->erasesize_shift)) { 721f21bf61SJon Lin mtd_map_blk_table[blk_begin + i] = blk_begin + j; 731f21bf61SJon Lin j++; 741f21bf61SJon Lin if (j == blk_cnt) 751f21bf61SJon Lin j++; 761f21bf61SJon Lin break; 771f21bf61SJon Lin } 781f21bf61SJon Lin } 791f21bf61SJon Lin } 801f21bf61SJon Lin 811f21bf61SJon Lin return 0; 821f21bf61SJon Lin } 831f21bf61SJon Lin } 841f21bf61SJon Lin 85c402731fSJon Lin static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off) 86c402731fSJon Lin { 87c402731fSJon Lin bool mapped; 88c402731fSJon Lin loff_t offset = *off; 89c402731fSJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 90c402731fSJon Lin 91c402731fSJon Lin mapped = false; 92c402731fSJon Lin if (!mtd_map_blk_table || 93c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 94c402731fSJon Lin MTD_BLK_TABLE_BLOCK_UNKNOWN || 95c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 96c402731fSJon Lin 0xffffffff) 97c402731fSJon Lin return mapped; 98c402731fSJon Lin 99c402731fSJon Lin mapped = true; 100c402731fSJon Lin *off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >> 101c402731fSJon Lin mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset); 102c402731fSJon Lin 103c402731fSJon Lin return mapped; 104c402731fSJon Lin } 105c402731fSJon Lin 106a07b97f2SJason Zhu void mtd_blk_map_partitions(struct blk_desc *desc) 107a07b97f2SJason Zhu { 108a07b97f2SJason Zhu disk_partition_t info; 109a07b97f2SJason Zhu int i, ret; 110a07b97f2SJason Zhu 111a07b97f2SJason Zhu if (!desc) 112a07b97f2SJason Zhu return; 113a07b97f2SJason Zhu 114a07b97f2SJason Zhu if (desc->if_type != IF_TYPE_MTD) 115a07b97f2SJason Zhu return; 116a07b97f2SJason Zhu 117a07b97f2SJason Zhu for (i = 1; i < MAX_SEARCH_PARTITIONS; i++) { 118a07b97f2SJason Zhu ret = part_get_info(desc, i, &info); 119a07b97f2SJason Zhu if (ret != 0) 120a07b97f2SJason Zhu continue; 121a07b97f2SJason Zhu 122a07b97f2SJason Zhu if (mtd_blk_map_table_init(desc, 123a07b97f2SJason Zhu info.start << 9, 124a07b97f2SJason Zhu info.size << 9)) { 125*9ee38883SJon Lin pr_debug("mtd block map table fail\n"); 126a07b97f2SJason Zhu } 127a07b97f2SJason Zhu } 128a07b97f2SJason Zhu } 129a07b97f2SJason Zhu 1301f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset, 1311f21bf61SJon Lin size_t *length, size_t *actual, 1321f21bf61SJon Lin loff_t lim, u_char *buffer) 1331f21bf61SJon Lin { 1341f21bf61SJon Lin size_t left_to_read = *length; 1351f21bf61SJon Lin u_char *p_buffer = buffer; 1361f21bf61SJon Lin int rval; 1371f21bf61SJon Lin 1381f21bf61SJon Lin while (left_to_read > 0) { 139360a2911SJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 1401f21bf61SJon Lin size_t read_length; 141360a2911SJon Lin loff_t mapped_offset; 1421f21bf61SJon Lin 1431f21bf61SJon Lin if (offset >= mtd->size) 1441f21bf61SJon Lin return 0; 1451f21bf61SJon Lin 146360a2911SJon Lin mapped_offset = offset; 147360a2911SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 148360a2911SJon Lin if (mtd_block_isbad(mtd, mapped_offset & 149360a2911SJon Lin ~(mtd->erasesize - 1))) { 150360a2911SJon Lin printf("Skipping bad block 0x%08llx\n", 151360a2911SJon Lin offset & ~(mtd->erasesize - 1)); 152360a2911SJon Lin offset += mtd->erasesize - block_offset; 1531f21bf61SJon Lin continue; 1541f21bf61SJon Lin } 1551f21bf61SJon Lin } 1561f21bf61SJon Lin 157360a2911SJon Lin if (left_to_read < (mtd->erasesize - block_offset)) 1581f21bf61SJon Lin read_length = left_to_read; 1591f21bf61SJon Lin else 160360a2911SJon Lin read_length = mtd->erasesize - block_offset; 1611f21bf61SJon Lin 162360a2911SJon Lin rval = mtd_read(mtd, mapped_offset, read_length, &read_length, 1631f21bf61SJon Lin p_buffer); 1641f21bf61SJon Lin if (rval && rval != -EUCLEAN) { 1651f21bf61SJon Lin printf("NAND read from offset %llx failed %d\n", 166c402731fSJon Lin offset, rval); 1671f21bf61SJon Lin *length -= left_to_read; 1681f21bf61SJon Lin return rval; 1691f21bf61SJon Lin } 1701f21bf61SJon Lin 1711f21bf61SJon Lin left_to_read -= read_length; 1721f21bf61SJon Lin offset += read_length; 1731f21bf61SJon Lin p_buffer += read_length; 1741f21bf61SJon Lin } 1751f21bf61SJon Lin 1761f21bf61SJon Lin return 0; 1771f21bf61SJon Lin } 1781f21bf61SJon Lin 179*9ee38883SJon Lin static __maybe_unused int mtd_map_write(struct mtd_info *mtd, loff_t offset, 180*9ee38883SJon Lin size_t *length, size_t *actual, 181*9ee38883SJon Lin loff_t lim, u_char *buffer, int flags) 182*9ee38883SJon Lin { 183*9ee38883SJon Lin int rval = 0, blocksize; 184*9ee38883SJon Lin size_t left_to_write = *length; 185*9ee38883SJon Lin u_char *p_buffer = buffer; 186*9ee38883SJon Lin struct erase_info ei; 187*9ee38883SJon Lin 188*9ee38883SJon Lin blocksize = mtd->erasesize; 189*9ee38883SJon Lin 190*9ee38883SJon Lin /* 191*9ee38883SJon Lin * nand_write() handles unaligned, partial page writes. 192*9ee38883SJon Lin * 193*9ee38883SJon Lin * We allow length to be unaligned, for convenience in 194*9ee38883SJon Lin * using the $filesize variable. 195*9ee38883SJon Lin * 196*9ee38883SJon Lin * However, starting at an unaligned offset makes the 197*9ee38883SJon Lin * semantics of bad block skipping ambiguous (really, 198*9ee38883SJon Lin * you should only start a block skipping access at a 199*9ee38883SJon Lin * partition boundary). So don't try to handle that. 200*9ee38883SJon Lin */ 201*9ee38883SJon Lin if ((offset & (mtd->writesize - 1)) != 0) { 202*9ee38883SJon Lin printf("Attempt to write non page-aligned data\n"); 203*9ee38883SJon Lin *length = 0; 204*9ee38883SJon Lin return -EINVAL; 205*9ee38883SJon Lin } 206*9ee38883SJon Lin 207*9ee38883SJon Lin while (left_to_write > 0) { 208*9ee38883SJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 209*9ee38883SJon Lin size_t write_size, truncated_write_size; 210*9ee38883SJon Lin loff_t mapped_offset; 211*9ee38883SJon Lin 212*9ee38883SJon Lin if (offset >= mtd->size) 213*9ee38883SJon Lin return 0; 214*9ee38883SJon Lin 215*9ee38883SJon Lin mapped_offset = offset; 216*9ee38883SJon Lin if (!get_mtd_blk_map_address(mtd, &mapped_offset)) { 217*9ee38883SJon Lin if (mtd_block_isbad(mtd, mapped_offset & 218*9ee38883SJon Lin ~(mtd->erasesize - 1))) { 219*9ee38883SJon Lin printf("Skipping bad block 0x%08llx\n", 220*9ee38883SJon Lin offset & ~(mtd->erasesize - 1)); 221*9ee38883SJon Lin offset += mtd->erasesize - block_offset; 222*9ee38883SJon Lin continue; 223*9ee38883SJon Lin } 224*9ee38883SJon Lin } 225*9ee38883SJon Lin 226*9ee38883SJon Lin if (!(mapped_offset & mtd->erasesize_mask)) { 227*9ee38883SJon Lin memset(&ei, 0, sizeof(struct erase_info)); 228*9ee38883SJon Lin ei.addr = mapped_offset; 229*9ee38883SJon Lin ei.len = mtd->erasesize; 230*9ee38883SJon Lin rval = mtd_erase(mtd, &ei); 231*9ee38883SJon Lin if (rval) { 232*9ee38883SJon Lin pr_info("error %d while erasing %llx\n", rval, 233*9ee38883SJon Lin mapped_offset); 234*9ee38883SJon Lin return rval; 235*9ee38883SJon Lin } 236*9ee38883SJon Lin } 237*9ee38883SJon Lin 238*9ee38883SJon Lin if (left_to_write < (blocksize - block_offset)) 239*9ee38883SJon Lin write_size = left_to_write; 240*9ee38883SJon Lin else 241*9ee38883SJon Lin write_size = blocksize - block_offset; 242*9ee38883SJon Lin 243*9ee38883SJon Lin truncated_write_size = write_size; 244*9ee38883SJon Lin rval = mtd_write(mtd, mapped_offset, truncated_write_size, 245*9ee38883SJon Lin (size_t *)(&truncated_write_size), p_buffer); 246*9ee38883SJon Lin 247*9ee38883SJon Lin offset += write_size; 248*9ee38883SJon Lin p_buffer += write_size; 249*9ee38883SJon Lin 250*9ee38883SJon Lin if (rval != 0) { 251*9ee38883SJon Lin printf("NAND write to offset %llx failed %d\n", 252*9ee38883SJon Lin offset, rval); 253*9ee38883SJon Lin *length -= left_to_write; 254*9ee38883SJon Lin return rval; 255*9ee38883SJon Lin } 256*9ee38883SJon Lin 257*9ee38883SJon Lin left_to_write -= write_size; 258*9ee38883SJon Lin } 259*9ee38883SJon Lin 260*9ee38883SJon Lin return 0; 261*9ee38883SJon Lin } 262*9ee38883SJon Lin 26322dccd11SJason Zhu char *mtd_part_parse(void) 26422dccd11SJason Zhu { 26522dccd11SJason Zhu char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0}; 26622dccd11SJason Zhu u32 length, data_len = MTD_PART_INFO_MAX_SIZE; 26722dccd11SJason Zhu struct blk_desc *dev_desc; 26822dccd11SJason Zhu disk_partition_t info; 26922dccd11SJason Zhu char *mtd_part_info_p; 270c9289eddSJason Zhu struct mtd_info *mtd; 27122dccd11SJason Zhu char *mtd_part_info; 27222dccd11SJason Zhu int ret; 27322dccd11SJason Zhu int p; 27422dccd11SJason Zhu 27522dccd11SJason Zhu dev_desc = rockchip_get_bootdev(); 27622dccd11SJason Zhu if (!dev_desc) 27722dccd11SJason Zhu return NULL; 27822dccd11SJason Zhu 279c9289eddSJason Zhu mtd = (struct mtd_info *)dev_desc->bdev->priv; 280ec6d4288SJason Zhu if (!mtd) 281ec6d4288SJason Zhu return NULL; 282ec6d4288SJason Zhu 28322dccd11SJason Zhu mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char)); 28422dccd11SJason Zhu if (!mtd_part_info) { 28522dccd11SJason Zhu printf("%s: Fail to malloc!", __func__); 28622dccd11SJason Zhu return NULL; 28722dccd11SJason Zhu } 28822dccd11SJason Zhu 28922dccd11SJason Zhu mtd_part_info_p = mtd_part_info; 29022dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "%s%s:", 29122dccd11SJason Zhu MTD_PART_NAND_HEAD, 29222dccd11SJason Zhu dev_desc->product); 29322dccd11SJason Zhu data_len -= strlen(mtd_part_info_p); 29422dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p); 29522dccd11SJason Zhu 29622dccd11SJason Zhu for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { 29722dccd11SJason Zhu ret = part_get_info(dev_desc, p, &info); 29822dccd11SJason Zhu if (ret) 29922dccd11SJason Zhu break; 30022dccd11SJason Zhu 30122dccd11SJason Zhu debug("name is %s, start addr is %x\n", info.name, 30222dccd11SJason Zhu (int)(size_t)info.start); 30322dccd11SJason Zhu 30422dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 30522dccd11SJason Zhu (int)(size_t)info.size << 9, 30622dccd11SJason Zhu (int)(size_t)info.start << 9, 30722dccd11SJason Zhu info.name); 30822dccd11SJason Zhu snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1, 30922dccd11SJason Zhu "0x%x@0x%x(%s)", 31022dccd11SJason Zhu (int)(size_t)info.size << 9, 31122dccd11SJason Zhu (int)(size_t)info.start << 9, 31222dccd11SJason Zhu info.name); 31322dccd11SJason Zhu strcat(mtd_part_info, ","); 31422dccd11SJason Zhu if (part_get_info(dev_desc, p + 1, &info)) { 315c9289eddSJason Zhu /* Nand flash is erased by block and gpt table just 316c9289eddSJason Zhu * resserve 33 sectors for the last partition. This 317c9289eddSJason Zhu * will erase the backup gpt table by user program, 318c9289eddSJason Zhu * so reserve one block. 319c9289eddSJason Zhu */ 320c9289eddSJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 321c9289eddSJason Zhu (int)(size_t)(info.size - 322c9289eddSJason Zhu (info.size - 1) % 323c9289eddSJason Zhu (mtd->erasesize >> 9) - 1) << 9, 32422dccd11SJason Zhu (int)(size_t)info.start << 9, 32522dccd11SJason Zhu info.name); 32622dccd11SJason Zhu break; 32722dccd11SJason Zhu } 32822dccd11SJason Zhu length = strlen(mtd_part_info_temp); 32922dccd11SJason Zhu data_len -= length; 33022dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + length + 1; 33122dccd11SJason Zhu memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE); 33222dccd11SJason Zhu } 33322dccd11SJason Zhu 33422dccd11SJason Zhu return mtd_part_info; 33522dccd11SJason Zhu } 33622dccd11SJason Zhu 337054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start, 338054229abSJason Zhu lbaint_t blkcnt, void *dst) 339054229abSJason Zhu { 340054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 3410dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 3426e8ac5a8SJason Zhu loff_t off = (loff_t)(start * 512); 3436e8ac5a8SJason Zhu size_t rwsize = blkcnt * 512; 3440dccd0d8SJason Zhu #endif 3456e8ac5a8SJason Zhu struct mtd_info *mtd; 346bbb83f58SJason Zhu int ret = 0; 347054229abSJason Zhu 348054229abSJason Zhu if (!desc) 34939e38ab3SJason Zhu return ret; 350054229abSJason Zhu 3516e8ac5a8SJason Zhu mtd = desc->bdev->priv; 3526e8ac5a8SJason Zhu if (!mtd) 3536e8ac5a8SJason Zhu return 0; 3546e8ac5a8SJason Zhu 355054229abSJason Zhu if (blkcnt == 0) 356054229abSJason Zhu return 0; 357054229abSJason Zhu 358*9ee38883SJon Lin pr_debug("mtd dread %s %lx %lx\n", mtd->name, start, blkcnt); 359*9ee38883SJon Lin 360054229abSJason Zhu if (desc->devnum == BLK_MTD_NAND) { 36139e38ab3SJason Zhu #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD) 3628cf6fca4SJason Zhu mtd = dev_get_priv(udev->parent); 3638cf6fca4SJason Zhu if (!mtd) 3648cf6fca4SJason Zhu return 0; 3658cf6fca4SJason Zhu 3666e8ac5a8SJason Zhu ret = nand_read_skip_bad(mtd, off, &rwsize, 3676e8ac5a8SJason Zhu NULL, mtd->size, 368054229abSJason Zhu (u_char *)(dst)); 3691f21bf61SJon Lin #else 3701f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 3711f21bf61SJon Lin NULL, mtd->size, 3721f21bf61SJon Lin (u_char *)(dst)); 3731f21bf61SJon Lin #endif 3746e8ac5a8SJason Zhu if (!ret) 375054229abSJason Zhu return blkcnt; 3766e8ac5a8SJason Zhu else 3776e8ac5a8SJason Zhu return 0; 378054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NAND) { 3791f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 380bbb83f58SJason Zhu NULL, mtd->size, 381bbb83f58SJason Zhu (u_char *)(dst)); 382bbb83f58SJason Zhu if (!ret) 3836e8ac5a8SJason Zhu return blkcnt; 3846e8ac5a8SJason Zhu else 385054229abSJason Zhu return 0; 386054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NOR) { 3870dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD) 3880dccd0d8SJason Zhu size_t retlen_nor; 3890dccd0d8SJason Zhu 3900dccd0d8SJason Zhu mtd_read(mtd, off, rwsize, &retlen_nor, dst); 3910dccd0d8SJason Zhu if (retlen_nor == rwsize) 3920dccd0d8SJason Zhu return blkcnt; 3930dccd0d8SJason Zhu else 3940dccd0d8SJason Zhu #endif 395054229abSJason Zhu return 0; 396054229abSJason Zhu } else { 397054229abSJason Zhu return 0; 398054229abSJason Zhu } 399054229abSJason Zhu } 400054229abSJason Zhu 401054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start, 402054229abSJason Zhu lbaint_t blkcnt, const void *src) 403054229abSJason Zhu { 404*9ee38883SJon Lin struct blk_desc *desc = dev_get_uclass_platdata(udev); 405*9ee38883SJon Lin #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 406*9ee38883SJon Lin loff_t off = (loff_t)(start * 512); 407*9ee38883SJon Lin size_t rwsize = blkcnt * 512; 408*9ee38883SJon Lin #endif 409*9ee38883SJon Lin struct mtd_info *mtd; 410*9ee38883SJon Lin int ret = 0; 411*9ee38883SJon Lin 412*9ee38883SJon Lin if (!desc) 413*9ee38883SJon Lin return ret; 414*9ee38883SJon Lin 415*9ee38883SJon Lin mtd = desc->bdev->priv; 416*9ee38883SJon Lin if (!mtd) 417*9ee38883SJon Lin return 0; 418*9ee38883SJon Lin 419*9ee38883SJon Lin pr_debug("mtd dwrite %s %lx %lx\n", mtd->name, start, blkcnt); 420*9ee38883SJon Lin 421*9ee38883SJon Lin if (blkcnt == 0) 422*9ee38883SJon Lin return 0; 423*9ee38883SJon Lin 424*9ee38883SJon Lin if (desc->devnum == BLK_MTD_NAND || 425*9ee38883SJon Lin desc->devnum == BLK_MTD_SPI_NAND || 426*9ee38883SJon Lin desc->devnum == BLK_MTD_SPI_NOR) { 427*9ee38883SJon Lin ret = mtd_map_write(mtd, off, &rwsize, 428*9ee38883SJon Lin NULL, mtd->size, 429*9ee38883SJon Lin (u_char *)(src), 0); 430*9ee38883SJon Lin if (!ret) 431*9ee38883SJon Lin return blkcnt; 432*9ee38883SJon Lin else 433*9ee38883SJon Lin return 0; 434*9ee38883SJon Lin } else { 435*9ee38883SJon Lin return 0; 436*9ee38883SJon Lin } 437*9ee38883SJon Lin 438054229abSJason Zhu return 0; 439054229abSJason Zhu } 440054229abSJason Zhu 441054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start, 442054229abSJason Zhu lbaint_t blkcnt) 443054229abSJason Zhu { 444054229abSJason Zhu /* Not implemented */ 445054229abSJason Zhu return 0; 446054229abSJason Zhu } 447054229abSJason Zhu 448054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev) 449054229abSJason Zhu { 4506e8ac5a8SJason Zhu struct mtd_info *mtd = dev_get_uclass_priv(udev->parent); 451054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 4521d39542fSJason Zhu int ret, i; 453054229abSJason Zhu 454c9289eddSJason Zhu desc->bdev->priv = mtd; 455054229abSJason Zhu sprintf(desc->vendor, "0x%.4x", 0x2207); 456e6482de4SJason Zhu memcpy(desc->product, mtd->name, strlen(mtd->name)); 457054229abSJason Zhu memcpy(desc->revision, "V1.00", sizeof("V1.00")); 458f1892190SJason Zhu if (mtd->type == MTD_NANDFLASH) { 459f3ba630bSJason Zhu if (desc->devnum == BLK_MTD_NAND) 460f3ba630bSJason Zhu mtd = dev_get_priv(udev->parent); 4611d39542fSJason Zhu /* 4621d39542fSJason Zhu * Find the first useful block in the end, 4631d39542fSJason Zhu * and it is the end lba of the nand storage. 4641d39542fSJason Zhu */ 4651d39542fSJason Zhu for (i = 0; i < (mtd->size / mtd->erasesize); i++) { 4661d39542fSJason Zhu ret = mtd_block_isbad(mtd, 4671d39542fSJason Zhu mtd->size - mtd->erasesize * (i + 1)); 4681d39542fSJason Zhu if (!ret) { 4691d39542fSJason Zhu desc->lba = (mtd->size >> 9) - 4701d39542fSJason Zhu (mtd->erasesize >> 9) * i; 4711d39542fSJason Zhu break; 4721d39542fSJason Zhu } 4731d39542fSJason Zhu } 474f1892190SJason Zhu } else { 475f1892190SJason Zhu desc->lba = mtd->size >> 9; 476f1892190SJason Zhu } 477054229abSJason Zhu 4781d39542fSJason Zhu debug("MTD: desc->lba is %lx\n", desc->lba); 4791d39542fSJason Zhu 480054229abSJason Zhu return 0; 481054229abSJason Zhu } 482054229abSJason Zhu 483054229abSJason Zhu static const struct blk_ops mtd_blk_ops = { 484054229abSJason Zhu .read = mtd_dread, 485054229abSJason Zhu #ifndef CONFIG_SPL_BUILD 486054229abSJason Zhu .write = mtd_dwrite, 487054229abSJason Zhu .erase = mtd_derase, 488054229abSJason Zhu #endif 489054229abSJason Zhu }; 490054229abSJason Zhu 491054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = { 492054229abSJason Zhu .name = "mtd_blk", 493054229abSJason Zhu .id = UCLASS_BLK, 494054229abSJason Zhu .ops = &mtd_blk_ops, 495054229abSJason Zhu .probe = mtd_blk_probe, 496054229abSJason Zhu }; 497