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