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 85*c402731fSJon Lin static bool get_mtd_blk_map_address(struct mtd_info *mtd, loff_t *off) 86*c402731fSJon Lin { 87*c402731fSJon Lin bool mapped; 88*c402731fSJon Lin loff_t offset = *off; 89*c402731fSJon Lin size_t block_offset = offset & (mtd->erasesize - 1); 90*c402731fSJon Lin 91*c402731fSJon Lin mapped = false; 92*c402731fSJon Lin if (!mtd_map_blk_table || 93*c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 94*c402731fSJon Lin MTD_BLK_TABLE_BLOCK_UNKNOWN || 95*c402731fSJon Lin mtd_map_blk_table[(u64)offset >> mtd->erasesize_shift] == 96*c402731fSJon Lin 0xffffffff) 97*c402731fSJon Lin return mapped; 98*c402731fSJon Lin 99*c402731fSJon Lin mapped = true; 100*c402731fSJon Lin *off = (loff_t)(((u32)mtd_map_blk_table[(u64)offset >> 101*c402731fSJon Lin mtd->erasesize_shift] << mtd->erasesize_shift) + block_offset); 102*c402731fSJon Lin 103*c402731fSJon Lin return mapped; 104*c402731fSJon Lin } 105*c402731fSJon Lin 1061f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset, 1071f21bf61SJon Lin size_t *length, size_t *actual, 1081f21bf61SJon Lin loff_t lim, u_char *buffer) 1091f21bf61SJon Lin { 1101f21bf61SJon Lin size_t left_to_read = *length; 1111f21bf61SJon Lin u_char *p_buffer = buffer; 1121f21bf61SJon Lin u32 erasesize = mtd->erasesize; 1131f21bf61SJon Lin int rval; 1141f21bf61SJon Lin 1151f21bf61SJon Lin while (left_to_read > 0) { 1161f21bf61SJon Lin size_t block_offset = offset & (erasesize - 1); 1171f21bf61SJon Lin size_t read_length; 1181f21bf61SJon Lin 1191f21bf61SJon Lin if (offset >= mtd->size) 1201f21bf61SJon Lin return 0; 1211f21bf61SJon Lin 122*c402731fSJon Lin if (!get_mtd_blk_map_address(mtd, &offset)) { 1231f21bf61SJon Lin if (mtd_block_isbad(mtd, offset & ~(erasesize - 1))) { 1241f21bf61SJon Lin printf("Skip bad block 0x%08llx\n", 1251f21bf61SJon Lin offset & ~(erasesize - 1)); 1261f21bf61SJon Lin offset += erasesize - block_offset; 1271f21bf61SJon Lin continue; 1281f21bf61SJon Lin } 1291f21bf61SJon Lin } 1301f21bf61SJon Lin 1311f21bf61SJon Lin if (left_to_read < (erasesize - block_offset)) 1321f21bf61SJon Lin read_length = left_to_read; 1331f21bf61SJon Lin else 1341f21bf61SJon Lin read_length = erasesize - block_offset; 1351f21bf61SJon Lin 136*c402731fSJon Lin rval = mtd_read(mtd, offset, read_length, &read_length, 1371f21bf61SJon Lin p_buffer); 1381f21bf61SJon Lin if (rval && rval != -EUCLEAN) { 1391f21bf61SJon Lin printf("NAND read from offset %llx failed %d\n", 140*c402731fSJon Lin offset, rval); 1411f21bf61SJon Lin *length -= left_to_read; 1421f21bf61SJon Lin return rval; 1431f21bf61SJon Lin } 1441f21bf61SJon Lin 1451f21bf61SJon Lin left_to_read -= read_length; 1461f21bf61SJon Lin offset += read_length; 1471f21bf61SJon Lin p_buffer += read_length; 1481f21bf61SJon Lin } 1491f21bf61SJon Lin 1501f21bf61SJon Lin return 0; 1511f21bf61SJon Lin } 1521f21bf61SJon Lin 15322dccd11SJason Zhu char *mtd_part_parse(void) 15422dccd11SJason Zhu { 15522dccd11SJason Zhu char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0}; 15622dccd11SJason Zhu u32 length, data_len = MTD_PART_INFO_MAX_SIZE; 15722dccd11SJason Zhu struct blk_desc *dev_desc; 15822dccd11SJason Zhu disk_partition_t info; 15922dccd11SJason Zhu char *mtd_part_info_p; 160c9289eddSJason Zhu struct mtd_info *mtd; 16122dccd11SJason Zhu char *mtd_part_info; 16222dccd11SJason Zhu int ret; 16322dccd11SJason Zhu int p; 16422dccd11SJason Zhu 16522dccd11SJason Zhu dev_desc = rockchip_get_bootdev(); 16622dccd11SJason Zhu if (!dev_desc) 16722dccd11SJason Zhu return NULL; 16822dccd11SJason Zhu 169c9289eddSJason Zhu mtd = (struct mtd_info *)dev_desc->bdev->priv; 170ec6d4288SJason Zhu if (!mtd) 171ec6d4288SJason Zhu return NULL; 172ec6d4288SJason Zhu 17322dccd11SJason Zhu mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char)); 17422dccd11SJason Zhu if (!mtd_part_info) { 17522dccd11SJason Zhu printf("%s: Fail to malloc!", __func__); 17622dccd11SJason Zhu return NULL; 17722dccd11SJason Zhu } 17822dccd11SJason Zhu 17922dccd11SJason Zhu mtd_part_info_p = mtd_part_info; 18022dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "%s%s:", 18122dccd11SJason Zhu MTD_PART_NAND_HEAD, 18222dccd11SJason Zhu dev_desc->product); 18322dccd11SJason Zhu data_len -= strlen(mtd_part_info_p); 18422dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p); 18522dccd11SJason Zhu 18622dccd11SJason Zhu for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { 18722dccd11SJason Zhu ret = part_get_info(dev_desc, p, &info); 18822dccd11SJason Zhu if (ret) 18922dccd11SJason Zhu break; 19022dccd11SJason Zhu 19122dccd11SJason Zhu debug("name is %s, start addr is %x\n", info.name, 19222dccd11SJason Zhu (int)(size_t)info.start); 19322dccd11SJason Zhu 19422dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 19522dccd11SJason Zhu (int)(size_t)info.size << 9, 19622dccd11SJason Zhu (int)(size_t)info.start << 9, 19722dccd11SJason Zhu info.name); 19822dccd11SJason Zhu snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1, 19922dccd11SJason Zhu "0x%x@0x%x(%s)", 20022dccd11SJason Zhu (int)(size_t)info.size << 9, 20122dccd11SJason Zhu (int)(size_t)info.start << 9, 20222dccd11SJason Zhu info.name); 20322dccd11SJason Zhu strcat(mtd_part_info, ","); 20422dccd11SJason Zhu if (part_get_info(dev_desc, p + 1, &info)) { 205c9289eddSJason Zhu /* Nand flash is erased by block and gpt table just 206c9289eddSJason Zhu * resserve 33 sectors for the last partition. This 207c9289eddSJason Zhu * will erase the backup gpt table by user program, 208c9289eddSJason Zhu * so reserve one block. 209c9289eddSJason Zhu */ 210c9289eddSJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 211c9289eddSJason Zhu (int)(size_t)(info.size - 212c9289eddSJason Zhu (info.size - 1) % 213c9289eddSJason Zhu (mtd->erasesize >> 9) - 1) << 9, 21422dccd11SJason Zhu (int)(size_t)info.start << 9, 21522dccd11SJason Zhu info.name); 21622dccd11SJason Zhu break; 21722dccd11SJason Zhu } 21822dccd11SJason Zhu length = strlen(mtd_part_info_temp); 21922dccd11SJason Zhu data_len -= length; 22022dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + length + 1; 22122dccd11SJason Zhu memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE); 22222dccd11SJason Zhu } 22322dccd11SJason Zhu 22422dccd11SJason Zhu return mtd_part_info; 22522dccd11SJason Zhu } 22622dccd11SJason Zhu 227054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start, 228054229abSJason Zhu lbaint_t blkcnt, void *dst) 229054229abSJason Zhu { 230054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 2310dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 2326e8ac5a8SJason Zhu loff_t off = (loff_t)(start * 512); 2336e8ac5a8SJason Zhu size_t rwsize = blkcnt * 512; 2340dccd0d8SJason Zhu #endif 2356e8ac5a8SJason Zhu struct mtd_info *mtd; 236bbb83f58SJason Zhu int ret = 0; 237054229abSJason Zhu 238054229abSJason Zhu if (!desc) 23939e38ab3SJason Zhu return ret; 240054229abSJason Zhu 2416e8ac5a8SJason Zhu mtd = desc->bdev->priv; 2426e8ac5a8SJason Zhu if (!mtd) 2436e8ac5a8SJason Zhu return 0; 2446e8ac5a8SJason Zhu 245054229abSJason Zhu if (blkcnt == 0) 246054229abSJason Zhu return 0; 247054229abSJason Zhu 248054229abSJason Zhu if (desc->devnum == BLK_MTD_NAND) { 24939e38ab3SJason Zhu #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD) 2508cf6fca4SJason Zhu mtd = dev_get_priv(udev->parent); 2518cf6fca4SJason Zhu if (!mtd) 2528cf6fca4SJason Zhu return 0; 2538cf6fca4SJason Zhu 2546e8ac5a8SJason Zhu ret = nand_read_skip_bad(mtd, off, &rwsize, 2556e8ac5a8SJason Zhu NULL, mtd->size, 256054229abSJason Zhu (u_char *)(dst)); 2571f21bf61SJon Lin #else 2581f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 2591f21bf61SJon Lin NULL, mtd->size, 2601f21bf61SJon Lin (u_char *)(dst)); 2611f21bf61SJon Lin #endif 2626e8ac5a8SJason Zhu if (!ret) 263054229abSJason Zhu return blkcnt; 2646e8ac5a8SJason Zhu else 2656e8ac5a8SJason Zhu return 0; 266054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NAND) { 2671f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 268bbb83f58SJason Zhu NULL, mtd->size, 269bbb83f58SJason Zhu (u_char *)(dst)); 270bbb83f58SJason Zhu if (!ret) 2716e8ac5a8SJason Zhu return blkcnt; 2726e8ac5a8SJason Zhu else 273054229abSJason Zhu return 0; 274054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NOR) { 2750dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD) 2760dccd0d8SJason Zhu size_t retlen_nor; 2770dccd0d8SJason Zhu 2780dccd0d8SJason Zhu mtd_read(mtd, off, rwsize, &retlen_nor, dst); 2790dccd0d8SJason Zhu if (retlen_nor == rwsize) 2800dccd0d8SJason Zhu return blkcnt; 2810dccd0d8SJason Zhu else 2820dccd0d8SJason Zhu #endif 283054229abSJason Zhu return 0; 284054229abSJason Zhu } else { 285054229abSJason Zhu return 0; 286054229abSJason Zhu } 287054229abSJason Zhu } 288054229abSJason Zhu 289054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start, 290054229abSJason Zhu lbaint_t blkcnt, const void *src) 291054229abSJason Zhu { 292054229abSJason Zhu /* Not implemented */ 293054229abSJason Zhu return 0; 294054229abSJason Zhu } 295054229abSJason Zhu 296054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start, 297054229abSJason Zhu lbaint_t blkcnt) 298054229abSJason Zhu { 299054229abSJason Zhu /* Not implemented */ 300054229abSJason Zhu return 0; 301054229abSJason Zhu } 302054229abSJason Zhu 303054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev) 304054229abSJason Zhu { 3056e8ac5a8SJason Zhu struct mtd_info *mtd = dev_get_uclass_priv(udev->parent); 306054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 3071d39542fSJason Zhu int ret, i; 308054229abSJason Zhu 309c9289eddSJason Zhu desc->bdev->priv = mtd; 310054229abSJason Zhu sprintf(desc->vendor, "0x%.4x", 0x2207); 311e6482de4SJason Zhu memcpy(desc->product, mtd->name, strlen(mtd->name)); 312054229abSJason Zhu memcpy(desc->revision, "V1.00", sizeof("V1.00")); 313f1892190SJason Zhu if (mtd->type == MTD_NANDFLASH) { 314f3ba630bSJason Zhu if (desc->devnum == BLK_MTD_NAND) 315f3ba630bSJason Zhu mtd = dev_get_priv(udev->parent); 3161d39542fSJason Zhu /* 3171d39542fSJason Zhu * Find the first useful block in the end, 3181d39542fSJason Zhu * and it is the end lba of the nand storage. 3191d39542fSJason Zhu */ 3201d39542fSJason Zhu for (i = 0; i < (mtd->size / mtd->erasesize); i++) { 3211d39542fSJason Zhu ret = mtd_block_isbad(mtd, 3221d39542fSJason Zhu mtd->size - mtd->erasesize * (i + 1)); 3231d39542fSJason Zhu if (!ret) { 3241d39542fSJason Zhu desc->lba = (mtd->size >> 9) - 3251d39542fSJason Zhu (mtd->erasesize >> 9) * i; 3261d39542fSJason Zhu break; 3271d39542fSJason Zhu } 3281d39542fSJason Zhu } 329f1892190SJason Zhu } else { 330f1892190SJason Zhu desc->lba = mtd->size >> 9; 331f1892190SJason Zhu } 332054229abSJason Zhu 3331d39542fSJason Zhu debug("MTD: desc->lba is %lx\n", desc->lba); 3341d39542fSJason Zhu 335054229abSJason Zhu return 0; 336054229abSJason Zhu } 337054229abSJason Zhu 338054229abSJason Zhu static const struct blk_ops mtd_blk_ops = { 339054229abSJason Zhu .read = mtd_dread, 340054229abSJason Zhu #ifndef CONFIG_SPL_BUILD 341054229abSJason Zhu .write = mtd_dwrite, 342054229abSJason Zhu .erase = mtd_derase, 343054229abSJason Zhu #endif 344054229abSJason Zhu }; 345054229abSJason Zhu 346054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = { 347054229abSJason Zhu .name = "mtd_blk", 348054229abSJason Zhu .id = UCLASS_BLK, 349054229abSJason Zhu .ops = &mtd_blk_ops, 350054229abSJason Zhu .probe = mtd_blk_probe, 351054229abSJason Zhu }; 352