1 /* 2 * (C) Copyright 2019 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <blk.h> 9 #include <boot_rkimg.h> 10 #include <dm.h> 11 #include <errno.h> 12 #include <malloc.h> 13 #include <nand.h> 14 #include <part.h> 15 #include <dm/device-internal.h> 16 17 #define MTD_PART_NAND_HEAD "mtdparts=" 18 #define MTD_PART_INFO_MAX_SIZE 512 19 #define MTD_SINGLE_PART_INFO_MAX_SIZE 40 20 21 static int *mtd_map_blk_table; 22 23 int mtd_blk_map_table_init(struct blk_desc *desc, 24 loff_t offset, 25 size_t length) 26 { 27 u32 blk_total, blk_begin, blk_cnt; 28 struct mtd_info *mtd = NULL; 29 int i, j; 30 31 if (!desc) 32 return -ENODEV; 33 34 if (desc->devnum == BLK_MTD_NAND) { 35 #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD) 36 mtd = dev_get_priv(desc->bdev->parent); 37 #endif 38 } else if (desc->devnum == BLK_MTD_SPI_NAND) { 39 #if defined(CONFIG_MTD_SPI_NAND) && !defined(CONFIG_SPL_BUILD) 40 mtd = desc->bdev->priv; 41 #endif 42 } 43 44 if (!mtd) { 45 return -ENODEV; 46 } else { 47 blk_total = (mtd->size + mtd->erasesize - 1) / mtd->erasesize; 48 if (!mtd_map_blk_table) { 49 mtd_map_blk_table = (int *)malloc(blk_total * 4); 50 for (i = 0; i < blk_total; i++) 51 mtd_map_blk_table[i] = i; 52 } 53 54 blk_begin = (u32)offset / mtd->erasesize; 55 blk_cnt = ((u32)(offset % mtd->erasesize + length) / mtd->erasesize); 56 if ((blk_begin + blk_cnt) > blk_total) 57 blk_cnt = blk_total - blk_begin; 58 j = 0; 59 /* should not across blk_cnt */ 60 for (i = 0; i < blk_cnt; i++) { 61 if (j >= blk_cnt) 62 mtd_map_blk_table[blk_begin + i] = -1; 63 for (; j < blk_cnt; j++) { 64 if (!mtd_block_isbad(mtd, (blk_begin + j) * mtd->erasesize)) { 65 mtd_map_blk_table[blk_begin + i] = blk_begin + j; 66 j++; 67 if (j == blk_cnt) 68 j++; 69 break; 70 } 71 } 72 } 73 74 return 0; 75 } 76 } 77 78 static __maybe_unused int mtd_map_read(struct mtd_info *mtd, loff_t offset, 79 size_t *length, size_t *actual, 80 loff_t lim, u_char *buffer) 81 { 82 size_t left_to_read = *length; 83 u_char *p_buffer = buffer; 84 u32 erasesize = mtd->erasesize; 85 int rval; 86 87 while (left_to_read > 0) { 88 size_t block_offset = offset & (erasesize - 1); 89 size_t read_length; 90 loff_t mapped_offset; 91 bool mapped; 92 93 if (offset >= mtd->size) 94 return 0; 95 96 mapped_offset = offset; 97 mapped = false; 98 if (mtd_map_blk_table) { 99 mapped = true; 100 mapped_offset = (loff_t)((u32)mtd_map_blk_table[(u64)offset / 101 erasesize] * erasesize + block_offset); 102 } 103 104 if (!mapped) { 105 if (mtd_block_isbad(mtd, offset & ~(erasesize - 1))) { 106 printf("Skip bad block 0x%08llx\n", 107 offset & ~(erasesize - 1)); 108 offset += erasesize - block_offset; 109 continue; 110 } 111 } 112 113 if (left_to_read < (erasesize - block_offset)) 114 read_length = left_to_read; 115 else 116 read_length = erasesize - block_offset; 117 118 rval = mtd_read(mtd, mapped_offset, read_length, &read_length, 119 p_buffer); 120 if (rval && rval != -EUCLEAN) { 121 printf("NAND read from offset %llx failed %d\n", 122 mapped_offset, rval); 123 *length -= left_to_read; 124 return rval; 125 } 126 127 left_to_read -= read_length; 128 offset += read_length; 129 p_buffer += read_length; 130 } 131 132 return 0; 133 } 134 135 char *mtd_part_parse(void) 136 { 137 char mtd_part_info_temp[MTD_SINGLE_PART_INFO_MAX_SIZE] = {0}; 138 u32 length, data_len = MTD_PART_INFO_MAX_SIZE; 139 struct blk_desc *dev_desc; 140 disk_partition_t info; 141 char *mtd_part_info_p; 142 struct mtd_info *mtd; 143 char *mtd_part_info; 144 int ret; 145 int p; 146 147 dev_desc = rockchip_get_bootdev(); 148 if (!dev_desc) 149 return NULL; 150 151 mtd = (struct mtd_info *)dev_desc->bdev->priv; 152 if (!mtd) 153 return NULL; 154 155 mtd_part_info = (char *)calloc(MTD_PART_INFO_MAX_SIZE, sizeof(char)); 156 if (!mtd_part_info) { 157 printf("%s: Fail to malloc!", __func__); 158 return NULL; 159 } 160 161 mtd_part_info_p = mtd_part_info; 162 snprintf(mtd_part_info_p, data_len - 1, "%s%s:", 163 MTD_PART_NAND_HEAD, 164 dev_desc->product); 165 data_len -= strlen(mtd_part_info_p); 166 mtd_part_info_p = mtd_part_info_p + strlen(mtd_part_info_p); 167 168 for (p = 1; p < MAX_SEARCH_PARTITIONS; p++) { 169 ret = part_get_info(dev_desc, p, &info); 170 if (ret) 171 break; 172 173 debug("name is %s, start addr is %x\n", info.name, 174 (int)(size_t)info.start); 175 176 snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 177 (int)(size_t)info.size << 9, 178 (int)(size_t)info.start << 9, 179 info.name); 180 snprintf(mtd_part_info_temp, MTD_SINGLE_PART_INFO_MAX_SIZE - 1, 181 "0x%x@0x%x(%s)", 182 (int)(size_t)info.size << 9, 183 (int)(size_t)info.start << 9, 184 info.name); 185 strcat(mtd_part_info, ","); 186 if (part_get_info(dev_desc, p + 1, &info)) { 187 /* Nand flash is erased by block and gpt table just 188 * resserve 33 sectors for the last partition. This 189 * will erase the backup gpt table by user program, 190 * so reserve one block. 191 */ 192 snprintf(mtd_part_info_p, data_len - 1, "0x%x@0x%x(%s)", 193 (int)(size_t)(info.size - 194 (info.size - 1) % 195 (mtd->erasesize >> 9) - 1) << 9, 196 (int)(size_t)info.start << 9, 197 info.name); 198 break; 199 } 200 length = strlen(mtd_part_info_temp); 201 data_len -= length; 202 mtd_part_info_p = mtd_part_info_p + length + 1; 203 memset(mtd_part_info_temp, 0, MTD_SINGLE_PART_INFO_MAX_SIZE); 204 } 205 206 return mtd_part_info; 207 } 208 209 ulong mtd_dread(struct udevice *udev, lbaint_t start, 210 lbaint_t blkcnt, void *dst) 211 { 212 struct blk_desc *desc = dev_get_uclass_platdata(udev); 213 #if defined(CONFIG_NAND) || defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_SPI_FLASH_MTD) 214 loff_t off = (loff_t)(start * 512); 215 size_t rwsize = blkcnt * 512; 216 #endif 217 struct mtd_info *mtd; 218 int ret = 0; 219 220 if (!desc) 221 return ret; 222 223 mtd = desc->bdev->priv; 224 if (!mtd) 225 return 0; 226 227 if (blkcnt == 0) 228 return 0; 229 230 if (desc->devnum == BLK_MTD_NAND) { 231 #if defined(CONFIG_NAND) && !defined(CONFIG_SPL_BUILD) 232 mtd = dev_get_priv(udev->parent); 233 if (!mtd) 234 return 0; 235 236 ret = nand_read_skip_bad(mtd, off, &rwsize, 237 NULL, mtd->size, 238 (u_char *)(dst)); 239 #else 240 ret = mtd_map_read(mtd, off, &rwsize, 241 NULL, mtd->size, 242 (u_char *)(dst)); 243 #endif 244 if (!ret) 245 return blkcnt; 246 else 247 return 0; 248 } else if (desc->devnum == BLK_MTD_SPI_NAND) { 249 ret = mtd_map_read(mtd, off, &rwsize, 250 NULL, mtd->size, 251 (u_char *)(dst)); 252 if (!ret) 253 return blkcnt; 254 else 255 return 0; 256 } else if (desc->devnum == BLK_MTD_SPI_NOR) { 257 #if defined(CONFIG_SPI_FLASH_MTD) || defined(CONFIG_SPL_BUILD) 258 size_t retlen_nor; 259 260 mtd_read(mtd, off, rwsize, &retlen_nor, dst); 261 if (retlen_nor == rwsize) 262 return blkcnt; 263 else 264 #endif 265 return 0; 266 } else { 267 return 0; 268 } 269 } 270 271 ulong mtd_dwrite(struct udevice *udev, lbaint_t start, 272 lbaint_t blkcnt, const void *src) 273 { 274 /* Not implemented */ 275 return 0; 276 } 277 278 ulong mtd_derase(struct udevice *udev, lbaint_t start, 279 lbaint_t blkcnt) 280 { 281 /* Not implemented */ 282 return 0; 283 } 284 285 static int mtd_blk_probe(struct udevice *udev) 286 { 287 struct mtd_info *mtd = dev_get_uclass_priv(udev->parent); 288 struct blk_desc *desc = dev_get_uclass_platdata(udev); 289 int ret, i; 290 291 desc->bdev->priv = mtd; 292 sprintf(desc->vendor, "0x%.4x", 0x2207); 293 memcpy(desc->product, mtd->name, strlen(mtd->name)); 294 memcpy(desc->revision, "V1.00", sizeof("V1.00")); 295 if (mtd->type == MTD_NANDFLASH) { 296 if (desc->devnum == BLK_MTD_NAND) 297 mtd = dev_get_priv(udev->parent); 298 /* 299 * Find the first useful block in the end, 300 * and it is the end lba of the nand storage. 301 */ 302 for (i = 0; i < (mtd->size / mtd->erasesize); i++) { 303 ret = mtd_block_isbad(mtd, 304 mtd->size - mtd->erasesize * (i + 1)); 305 if (!ret) { 306 desc->lba = (mtd->size >> 9) - 307 (mtd->erasesize >> 9) * i; 308 break; 309 } 310 } 311 } else { 312 desc->lba = mtd->size >> 9; 313 } 314 315 debug("MTD: desc->lba is %lx\n", desc->lba); 316 317 return 0; 318 } 319 320 static const struct blk_ops mtd_blk_ops = { 321 .read = mtd_dread, 322 #ifndef CONFIG_SPL_BUILD 323 .write = mtd_dwrite, 324 .erase = mtd_derase, 325 #endif 326 }; 327 328 U_BOOT_DRIVER(mtd_blk) = { 329 .name = "mtd_blk", 330 .id = UCLASS_BLK, 331 .ops = &mtd_blk_ops, 332 .probe = mtd_blk_probe, 333 }; 334