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 21*1f21bf61SJon Lin static int *mtd_map_blk_table; 22*1f21bf61SJon Lin 23*1f21bf61SJon Lin int mtd_blk_map_table_init(struct blk_desc *desc, 24*1f21bf61SJon Lin loff_t offset, 25*1f21bf61SJon Lin size_t length) 26*1f21bf61SJon Lin { 27*1f21bf61SJon Lin u32 blk_total, blk_begin, blk_cnt; 28*1f21bf61SJon Lin struct mtd_info *mtd = NULL; 29*1f21bf61SJon Lin int i, j; 30*1f21bf61SJon Lin 31*1f21bf61SJon Lin if (!desc) 32*1f21bf61SJon Lin return -ENODEV; 33*1f21bf61SJon Lin 34*1f21bf61SJon Lin if (desc->devnum == BLK_MTD_NAND) { 35*1f21bf61SJon Lin #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD) 36*1f21bf61SJon Lin mtd = dev_get_priv(desc->bdev->parent); 37*1f21bf61SJon Lin #endif 38*1f21bf61SJon Lin } else if (desc->devnum == BLK_MTD_SPI_NAND) { 39*1f21bf61SJon Lin #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD) 40*1f21bf61SJon Lin mtd = desc->bdev->priv; 41*1f21bf61SJon Lin #endif 42*1f21bf61SJon Lin } 43*1f21bf61SJon Lin 44*1f21bf61SJon Lin if (!mtd) { 45*1f21bf61SJon Lin return -ENODEV; 46*1f21bf61SJon Lin } else { 47*1f21bf61SJon Lin blk_total = mtd->size / mtd->erasesize; 48*1f21bf61SJon Lin if (!mtd_map_blk_table) { 49*1f21bf61SJon Lin mtd_map_blk_table = (int *)malloc(blk_total * 4); 50*1f21bf61SJon Lin for (i = 0; i < blk_total; i++) 51*1f21bf61SJon Lin mtd_map_blk_table[i] = i; 52*1f21bf61SJon Lin } 53*1f21bf61SJon Lin 54*1f21bf61SJon Lin blk_begin = (u32)offset / mtd->erasesize; 55*1f21bf61SJon Lin blk_cnt = (u32)length / mtd->erasesize; 56*1f21bf61SJon Lin j = 0; 57*1f21bf61SJon Lin /* should not across blk_cnt */ 58*1f21bf61SJon Lin for (i = 0; i < blk_cnt; i++) { 59*1f21bf61SJon Lin if (j >= blk_cnt) 60*1f21bf61SJon Lin mtd_map_blk_table[blk_begin + i] = -1; 61*1f21bf61SJon Lin for (; j < blk_cnt; j++) { 62*1f21bf61SJon Lin if (!mtd_block_isbad(mtd, (blk_begin + j) * mtd->erasesize)) { 63*1f21bf61SJon Lin mtd_map_blk_table[blk_begin + i] = blk_begin + j; 64*1f21bf61SJon Lin j++; 65*1f21bf61SJon Lin if (j == blk_cnt) 66*1f21bf61SJon Lin j++; 67*1f21bf61SJon Lin break; 68*1f21bf61SJon Lin } 69*1f21bf61SJon Lin } 70*1f21bf61SJon Lin } 71*1f21bf61SJon Lin 72*1f21bf61SJon Lin return 0; 73*1f21bf61SJon Lin } 74*1f21bf61SJon Lin } 75*1f21bf61SJon Lin 76*1f21bf61SJon Lin static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset, 77*1f21bf61SJon Lin size_t *length, size_t *actual, 78*1f21bf61SJon Lin loff_t lim, u_char *buffer) 79*1f21bf61SJon Lin { 80*1f21bf61SJon Lin size_t left_to_read = *length; 81*1f21bf61SJon Lin u_char *p_buffer = buffer; 82*1f21bf61SJon Lin u32 erasesize = mtd->erasesize; 83*1f21bf61SJon Lin int rval; 84*1f21bf61SJon Lin 85*1f21bf61SJon Lin while (left_to_read > 0) { 86*1f21bf61SJon Lin size_t block_offset = offset & (erasesize - 1); 87*1f21bf61SJon Lin size_t read_length; 88*1f21bf61SJon Lin loff_t mapped_offset; 89*1f21bf61SJon Lin 90*1f21bf61SJon Lin if (offset >= mtd->size) 91*1f21bf61SJon Lin return 0; 92*1f21bf61SJon Lin 93*1f21bf61SJon Lin mapped_offset = offset; 94*1f21bf61SJon Lin if (mtd_map_blk_table) { 95*1f21bf61SJon Lin mapped_offset = (loff_t)((u32)mtd_map_blk_table[(u64)offset / 96*1f21bf61SJon Lin erasesize] * erasesize + block_offset); 97*1f21bf61SJon Lin } else { 98*1f21bf61SJon Lin if (mtd_block_isbad(mtd, offset & ~(erasesize - 1))) { 99*1f21bf61SJon Lin printf("Skip bad block 0x%08llx\n", 100*1f21bf61SJon Lin offset & ~(erasesize - 1)); 101*1f21bf61SJon Lin offset += erasesize - block_offset; 102*1f21bf61SJon Lin continue; 103*1f21bf61SJon Lin } 104*1f21bf61SJon Lin } 105*1f21bf61SJon Lin 106*1f21bf61SJon Lin if (left_to_read < (erasesize - block_offset)) 107*1f21bf61SJon Lin read_length = left_to_read; 108*1f21bf61SJon Lin else 109*1f21bf61SJon Lin read_length = erasesize - block_offset; 110*1f21bf61SJon Lin 111*1f21bf61SJon Lin rval = mtd_read(mtd, mapped_offset, read_length, &read_length, 112*1f21bf61SJon Lin p_buffer); 113*1f21bf61SJon Lin if (rval && rval != -EUCLEAN) { 114*1f21bf61SJon Lin printf("NAND read from offset %llx failed %d\n", 115*1f21bf61SJon Lin mapped_offset, rval); 116*1f21bf61SJon Lin *length -= left_to_read; 117*1f21bf61SJon Lin return rval; 118*1f21bf61SJon Lin } 119*1f21bf61SJon Lin 120*1f21bf61SJon Lin left_to_read -= read_length; 121*1f21bf61SJon Lin offset += read_length; 122*1f21bf61SJon Lin p_buffer += read_length; 123*1f21bf61SJon Lin } 124*1f21bf61SJon Lin 125*1f21bf61SJon Lin return 0; 126*1f21bf61SJon Lin } 127*1f21bf61SJon Lin 12822dccd11SJason Zhu char *mtd_part_parse(void) 12922dccd11SJason Zhu { 13022dccd11SJason Zhu char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0}; 13122dccd11SJason Zhu u32 length, data_len = MTD_PART_INFO_MAX_SIZE; 13222dccd11SJason Zhu struct blk_desc *dev_desc; 13322dccd11SJason Zhu disk_partition_t info; 13422dccd11SJason Zhu char *mtd_part_info_p; 135c9289eddSJason Zhu struct mtd_info *mtd; 13622dccd11SJason Zhu char *mtd_part_info; 13722dccd11SJason Zhu int ret; 13822dccd11SJason Zhu int p; 13922dccd11SJason Zhu 14022dccd11SJason Zhu dev_desc = rockchip_get_bootdev(); 14122dccd11SJason Zhu if (!dev_desc) 14222dccd11SJason Zhu return NULL; 14322dccd11SJason Zhu 144c9289eddSJason Zhu mtd = (struct mtd_info *)dev_desc->bdev->priv; 145ec6d4288SJason Zhu if (!mtd) 146ec6d4288SJason Zhu return NULL; 147ec6d4288SJason Zhu 14822dccd11SJason Zhu mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char)); 14922dccd11SJason Zhu if (!mtd_part_info) { 15022dccd11SJason Zhu printf("%s: Fail to malloc!", __func__); 15122dccd11SJason Zhu return NULL; 15222dccd11SJason Zhu } 15322dccd11SJason Zhu 15422dccd11SJason Zhu mtd_part_info_p = mtd_part_info; 15522dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "%s%s:", 15622dccd11SJason Zhu MTD_PART_NAND_HEAD, 15722dccd11SJason Zhu dev_desc->product); 15822dccd11SJason Zhu data_len -= strlen(mtd_part_info_p); 15922dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p); 16022dccd11SJason Zhu 16122dccd11SJason Zhu for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { 16222dccd11SJason Zhu ret = part_get_info(dev_desc, p, &info); 16322dccd11SJason Zhu if (ret) 16422dccd11SJason Zhu break; 16522dccd11SJason Zhu 16622dccd11SJason Zhu debug("name is %s, start addr is %x\n", info.name, 16722dccd11SJason Zhu (int)(size_t)info.start); 16822dccd11SJason Zhu 16922dccd11SJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 17022dccd11SJason Zhu (int)(size_t)info.size << 9, 17122dccd11SJason Zhu (int)(size_t)info.start << 9, 17222dccd11SJason Zhu info.name); 17322dccd11SJason Zhu snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1, 17422dccd11SJason Zhu "0x%x@0x%x(%s)", 17522dccd11SJason Zhu (int)(size_t)info.size << 9, 17622dccd11SJason Zhu (int)(size_t)info.start << 9, 17722dccd11SJason Zhu info.name); 17822dccd11SJason Zhu strcat(mtd_part_info, ","); 17922dccd11SJason Zhu if (part_get_info(dev_desc, p + 1, &info)) { 180c9289eddSJason Zhu /* Nand flash is erased by block and gpt table just 181c9289eddSJason Zhu * resserve 33 sectors for the last partition. This 182c9289eddSJason Zhu * will erase the backup gpt table by user program, 183c9289eddSJason Zhu * so reserve one block. 184c9289eddSJason Zhu */ 185c9289eddSJason Zhu snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 186c9289eddSJason Zhu (int)(size_t)(info.size - 187c9289eddSJason Zhu (info.size - 1) % 188c9289eddSJason Zhu (mtd->erasesize >> 9) - 1) << 9, 18922dccd11SJason Zhu (int)(size_t)info.start << 9, 19022dccd11SJason Zhu info.name); 19122dccd11SJason Zhu break; 19222dccd11SJason Zhu } 19322dccd11SJason Zhu length = strlen(mtd_part_info_temp); 19422dccd11SJason Zhu data_len -= length; 19522dccd11SJason Zhu mtd_part_info_p = mtd_part_info_p + length + 1; 19622dccd11SJason Zhu memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE); 19722dccd11SJason Zhu } 19822dccd11SJason Zhu 19922dccd11SJason Zhu return mtd_part_info; 20022dccd11SJason Zhu } 20122dccd11SJason Zhu 202054229abSJason Zhu ulong mtd_dread(struct udevice *udev, lbaint_t start, 203054229abSJason Zhu lbaint_t blkcnt, void *dst) 204054229abSJason Zhu { 205054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 2060dccd0d8SJason Zhu #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 2076e8ac5a8SJason Zhu loff_t off = (loff_t)(start * 512); 2086e8ac5a8SJason Zhu size_t rwsize = blkcnt * 512; 2090dccd0d8SJason Zhu #endif 2106e8ac5a8SJason Zhu struct mtd_info *mtd; 211bbb83f58SJason Zhu int ret = 0; 212054229abSJason Zhu 213054229abSJason Zhu if (!desc) 21439e38ab3SJason Zhu return ret; 215054229abSJason Zhu 2166e8ac5a8SJason Zhu mtd = desc->bdev->priv; 2176e8ac5a8SJason Zhu if (!mtd) 2186e8ac5a8SJason Zhu return 0; 2196e8ac5a8SJason Zhu 220054229abSJason Zhu if (blkcnt == 0) 221054229abSJason Zhu return 0; 222054229abSJason Zhu 223054229abSJason Zhu if (desc->devnum == BLK_MTD_NAND) { 22439e38ab3SJason Zhu #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD) 2258cf6fca4SJason Zhu mtd = dev_get_priv(udev->parent); 2268cf6fca4SJason Zhu if (!mtd) 2278cf6fca4SJason Zhu return 0; 2288cf6fca4SJason Zhu 2296e8ac5a8SJason Zhu ret = nand_read_skip_bad(mtd, off, &rwsize, 2306e8ac5a8SJason Zhu NULL, mtd->size, 231054229abSJason Zhu (u_char *)(dst)); 232*1f21bf61SJon Lin #else 233*1f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 234*1f21bf61SJon Lin NULL, mtd->size, 235*1f21bf61SJon Lin (u_char *)(dst)); 236*1f21bf61SJon Lin #endif 2376e8ac5a8SJason Zhu if (!ret) 238054229abSJason Zhu return blkcnt; 2396e8ac5a8SJason Zhu else 2406e8ac5a8SJason Zhu return 0; 241054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NAND) { 242*1f21bf61SJon Lin ret = mtd_map_read(mtd, off, &rwsize, 243bbb83f58SJason Zhu NULL, mtd->size, 244bbb83f58SJason Zhu (u_char *)(dst)); 245bbb83f58SJason Zhu if (!ret) 2466e8ac5a8SJason Zhu return blkcnt; 2476e8ac5a8SJason Zhu else 248054229abSJason Zhu return 0; 249054229abSJason Zhu } else if (desc->devnum == BLK_MTD_SPI_NOR) { 2500dccd0d8SJason Zhu #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD) 2510dccd0d8SJason Zhu size_t retlen_nor; 2520dccd0d8SJason Zhu 2530dccd0d8SJason Zhu mtd_read(mtd, off, rwsize, &retlen_nor, dst); 2540dccd0d8SJason Zhu if (retlen_nor == rwsize) 2550dccd0d8SJason Zhu return blkcnt; 2560dccd0d8SJason Zhu else 2570dccd0d8SJason Zhu #endif 258054229abSJason Zhu return 0; 259054229abSJason Zhu } else { 260054229abSJason Zhu return 0; 261054229abSJason Zhu } 262054229abSJason Zhu } 263054229abSJason Zhu 264054229abSJason Zhu ulong mtd_dwrite(struct udevice *udev, lbaint_t start, 265054229abSJason Zhu lbaint_t blkcnt, const void *src) 266054229abSJason Zhu { 267054229abSJason Zhu /* Not implemented */ 268054229abSJason Zhu return 0; 269054229abSJason Zhu } 270054229abSJason Zhu 271054229abSJason Zhu ulong mtd_derase(struct udevice *udev, lbaint_t start, 272054229abSJason Zhu lbaint_t blkcnt) 273054229abSJason Zhu { 274054229abSJason Zhu /* Not implemented */ 275054229abSJason Zhu return 0; 276054229abSJason Zhu } 277054229abSJason Zhu 278054229abSJason Zhu static int mtd_blk_probe(struct udevice *udev) 279054229abSJason Zhu { 2806e8ac5a8SJason Zhu struct mtd_info *mtd = dev_get_uclass_priv(udev->parent); 281054229abSJason Zhu struct blk_desc *desc = dev_get_uclass_platdata(udev); 2821d39542fSJason Zhu int ret, i; 283054229abSJason Zhu 284c9289eddSJason Zhu desc->bdev->priv = mtd; 285054229abSJason Zhu sprintf(desc->vendor, "0x%.4x", 0x2207); 286e6482de4SJason Zhu memcpy(desc->product, mtd->name, strlen(mtd->name)); 287054229abSJason Zhu memcpy(desc->revision, "V1.00", sizeof("V1.00")); 288f1892190SJason Zhu if (mtd->type == MTD_NANDFLASH) { 289f3ba630bSJason Zhu if (desc->devnum == BLK_MTD_NAND) 290f3ba630bSJason Zhu mtd = dev_get_priv(udev->parent); 2911d39542fSJason Zhu /* 2921d39542fSJason Zhu * Find the first useful block in the end, 2931d39542fSJason Zhu * and it is the end lba of the nand storage. 2941d39542fSJason Zhu */ 2951d39542fSJason Zhu for (i = 0; i < (mtd->size / mtd->erasesize); i++) { 2961d39542fSJason Zhu ret = mtd_block_isbad(mtd, 2971d39542fSJason Zhu mtd->size - mtd->erasesize * (i + 1)); 2981d39542fSJason Zhu if (!ret) { 2991d39542fSJason Zhu desc->lba = (mtd->size >> 9) - 3001d39542fSJason Zhu (mtd->erasesize >> 9) * i; 3011d39542fSJason Zhu break; 3021d39542fSJason Zhu } 3031d39542fSJason Zhu } 304f1892190SJason Zhu } else { 305f1892190SJason Zhu desc->lba = mtd->size >> 9; 306f1892190SJason Zhu } 307054229abSJason Zhu 3081d39542fSJason Zhu debug("MTD: desc->lba is %lx\n", desc->lba); 3091d39542fSJason Zhu 310054229abSJason Zhu return 0; 311054229abSJason Zhu } 312054229abSJason Zhu 313054229abSJason Zhu static const struct blk_ops mtd_blk_ops = { 314054229abSJason Zhu .read = mtd_dread, 315054229abSJason Zhu #ifndef CONFIG_SPL_BUILD 316054229abSJason Zhu .write = mtd_dwrite, 317054229abSJason Zhu .erase = mtd_derase, 318054229abSJason Zhu #endif 319054229abSJason Zhu }; 320054229abSJason Zhu 321054229abSJason Zhu U_BOOT_DRIVER(mtd_blk) = { 322054229abSJason Zhu .name = "mtd_blk", 323054229abSJason Zhu .id = UCLASS_BLK, 324054229abSJason Zhu .ops = &mtd_blk_ops, 325054229abSJason Zhu .probe = mtd_blk_probe, 326054229abSJason Zhu }; 327