b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | /* |
| 2 | * Command for accessing SPI flash. |
| 3 | * |
| 4 | * Copyright (C) 2008 Atmel Corporation |
| 5 | * |
| 6 | * SPDX-License-Identifier: GPL-2.0+ |
| 7 | */ |
| 8 | |
| 9 | #include <common.h> |
| 10 | #include <malloc.h> |
| 11 | #include <spi.h> |
| 12 | #include <spi_flash_chip.h> |
| 13 | //#include <spi_nand.h> |
| 14 | #include <asm/io.h> |
| 15 | #include <linux/mtd/mtd.h> |
| 16 | |
| 17 | #ifndef CONFIG_SF_DEFAULT_SPEED |
| 18 | # define CONFIG_SF_DEFAULT_SPEED 1000000 |
| 19 | #endif |
| 20 | #ifndef CONFIG_SF_DEFAULT_MODE |
| 21 | # define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 |
| 22 | #endif |
| 23 | #ifndef CONFIG_SF_DEFAULT_CS |
| 24 | # define CONFIG_SF_DEFAULT_CS 0 |
| 25 | #endif |
| 26 | #ifndef CONFIG_SF_DEFAULT_BUS |
| 27 | # define CONFIG_SF_DEFAULT_BUS 0 |
| 28 | #endif |
| 29 | |
| 30 | static int bit_count(u_char n) |
| 31 | { |
| 32 | u_char c = 0; |
| 33 | |
| 34 | for (c = 0; n; ++c) |
| 35 | n &= (n - 1); |
| 36 | return c ; |
| 37 | } |
| 38 | |
| 39 | static int raw_access(struct spi_flash_chip *chip, const char *addr, |
| 40 | loff_t off, ulong count, int read) |
| 41 | { |
| 42 | struct mtd_info *mtd = chip->mtd; |
| 43 | int ret = 0; |
| 44 | |
| 45 | while (count--) { |
| 46 | /* Raw access */ |
| 47 | struct mtd_oob_ops ops = { |
| 48 | .datbuf = (u8 *)addr, |
| 49 | .oobbuf = ((u8 *)addr) + mtd->writesize, |
| 50 | .len = mtd->writesize, |
| 51 | .ooblen = mtd->oobsize, |
| 52 | .mode = MTD_OPS_RAW, |
| 53 | }; |
| 54 | |
| 55 | if (read) |
| 56 | ret = mtd_read_oob(mtd, off, &ops); |
| 57 | else |
| 58 | ret = mtd_write_oob(mtd, off, &ops); |
| 59 | |
| 60 | if (ret) { |
| 61 | printf("%s: error at offset %llx, ret %d\n", |
| 62 | __func__, (long long)off, ret); |
| 63 | break; |
| 64 | } |
| 65 | |
| 66 | addr += mtd->writesize + mtd->oobsize; |
| 67 | off += mtd->writesize; |
| 68 | } |
| 69 | |
| 70 | return ret; |
| 71 | } |
| 72 | |
| 73 | /* |
| 74 | * This function computes the length argument for the erase command. |
| 75 | * The length on which the command is to operate can be given in two forms: |
| 76 | * 1. <cmd> offset len - operate on <'offset', 'len') |
| 77 | * 2. <cmd> offset +len - operate on <'offset', 'round_up(len)') |
| 78 | * If the second form is used and the length doesn't fall on the |
| 79 | * sector boundary, than it will be adjusted to the next sector boundary. |
| 80 | * If it isn't in the flash, the function will fail (return -1). |
| 81 | * Input: |
| 82 | * arg: length specification (i.e. both command arguments) |
| 83 | * Output: |
| 84 | * len: computed length for operation |
| 85 | * Return: |
| 86 | * 1: success |
| 87 | * -1: failure (bad format, bad address). |
| 88 | */ |
| 89 | static int sf_parse_len_arg(struct spi_flash_chip *chip, char *arg, ulong *len) |
| 90 | { |
| 91 | struct mtd_info *mtd = chip->mtd; |
| 92 | char *ep; |
| 93 | char round_up_len; /* indicates if the "+length" form used */ |
| 94 | ulong len_arg; |
| 95 | |
| 96 | round_up_len = 0; |
| 97 | if (*arg == '+') { |
| 98 | round_up_len = 1; |
| 99 | ++arg; |
| 100 | } |
| 101 | |
| 102 | len_arg = simple_strtoul(arg, &ep, 16); |
| 103 | if (ep == arg || *ep != '\0') |
| 104 | return -1; |
| 105 | |
| 106 | if (round_up_len && mtd->erasesize > 0) |
| 107 | *len = ROUND(len_arg, mtd->erasesize); |
| 108 | else |
| 109 | *len = len_arg; |
| 110 | |
| 111 | return 1; |
| 112 | } |
| 113 | |
| 114 | static int spi_flash_dump(struct spi_flash_chip *chip, ulong off, |
| 115 | int only_oob, int repeat) |
| 116 | { |
| 117 | struct mtd_info *mtd = chip->mtd; |
| 118 | struct mtd_oob_ops ops; |
| 119 | int i; |
| 120 | u_char *datbuf, *oobbuf, *p; |
| 121 | static loff_t last; |
| 122 | int ret = 0; |
| 123 | size_t retlen; |
| 124 | |
| 125 | if (repeat) |
| 126 | off = last + mtd->writesize; |
| 127 | |
| 128 | last = off; |
| 129 | |
| 130 | datbuf = memalign(ARCH_DMA_MINALIGN, mtd->writesize); |
| 131 | if (!datbuf) { |
| 132 | puts("No memory for page buffer\n"); |
| 133 | return 1; |
| 134 | } |
| 135 | |
| 136 | if (mtd->oobsize) { |
| 137 | oobbuf = memalign(ARCH_DMA_MINALIGN, mtd->oobsize); |
| 138 | if (!oobbuf) { |
| 139 | puts("No memory for page buffer\n"); |
| 140 | ret = 1; |
| 141 | goto free_dat; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | off &= ~(mtd->writesize - 1); |
| 146 | loff_t addr = (loff_t) off; |
| 147 | if (mtd->oobsize) { |
| 148 | memset(&ops, 0, sizeof(ops)); |
| 149 | ops.datbuf = datbuf; |
| 150 | ops.oobbuf = oobbuf; |
| 151 | ops.len = mtd->writesize; |
| 152 | ops.ooblen = mtd->oobsize; |
| 153 | ops.mode = MTD_OPS_RAW; |
| 154 | i = mtd_read_oob(mtd, addr, &ops); |
| 155 | if (i < 0 && i != -EUCLEAN) { |
| 156 | printf("Error (%d) reading page %08lx\n", i, off); |
| 157 | ret = 1; |
| 158 | goto free_all; |
| 159 | } |
| 160 | } else { |
| 161 | ret = mtd_read(mtd, addr, mtd->writesize, &retlen, datbuf); |
| 162 | if ((ret < 0 && ret != -EUCLEAN) || retlen != mtd->writesize) |
| 163 | return -1; |
| 164 | } |
| 165 | printf("Page %08lx dump:\n", off); |
| 166 | |
| 167 | if (!only_oob) { |
| 168 | i = mtd->writesize >> 4; |
| 169 | p = datbuf; |
| 170 | |
| 171 | while (i--) { |
| 172 | printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" |
| 173 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", |
| 174 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], |
| 175 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], |
| 176 | p[15]); |
| 177 | p += 16; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | if (mtd->oobsize) { |
| 182 | puts("OOB:\n"); |
| 183 | i = mtd->oobsize >> 3; |
| 184 | p = oobbuf; |
| 185 | while (i--) { |
| 186 | printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n", |
| 187 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); |
| 188 | p += 8; |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | free_all: |
| 193 | if (mtd->oobsize) |
| 194 | free(oobbuf); |
| 195 | free_dat: |
| 196 | free(datbuf); |
| 197 | |
| 198 | return ret; |
| 199 | } |
| 200 | |
| 201 | static int spi_flash_mark_biterr(struct spi_flash_chip *chip, ulong off, int bad_ct) |
| 202 | { |
| 203 | struct mtd_info *mtd = chip->mtd; |
| 204 | int i, j, k, m, bit_ct; |
| 205 | u_char *datbuf, *oobbuf, *p; |
| 206 | int ret = 0; |
| 207 | |
| 208 | datbuf = memalign(ARCH_DMA_MINALIGN, mtd->writesize); |
| 209 | if (!datbuf) { |
| 210 | puts("No memory for page buffer\n"); |
| 211 | return 1; |
| 212 | } |
| 213 | |
| 214 | oobbuf = memalign(ARCH_DMA_MINALIGN, mtd->oobsize); |
| 215 | if (!oobbuf) { |
| 216 | puts("No memory for page buffer\n"); |
| 217 | ret = 1; |
| 218 | goto free_dat; |
| 219 | } |
| 220 | off &= ~(mtd->writesize - 1); |
| 221 | loff_t addr = (loff_t) off; |
| 222 | struct mtd_oob_ops ops; |
| 223 | memset(&ops, 0, sizeof(ops)); |
| 224 | ops.datbuf = datbuf; |
| 225 | ops.oobbuf = oobbuf; |
| 226 | ops.len = mtd->writesize; |
| 227 | ops.ooblen = mtd->oobsize; |
| 228 | ops.mode = MTD_OPS_RAW; |
| 229 | ret = mtd_read_oob(mtd, addr, &ops); |
| 230 | if (ret < 0 && ret != -EUCLEAN) { |
| 231 | printf("Error (%d) reading page %08lx\n", i, off); |
| 232 | ret = 1; |
| 233 | goto free_all; |
| 234 | } |
| 235 | |
| 236 | i = 0; |
| 237 | bit_ct = 0; |
| 238 | while (1) { |
| 239 | j = bit_count(datbuf[i]); |
| 240 | if (bit_ct + j <= bad_ct) { |
| 241 | datbuf[i] = 0; |
| 242 | } else { |
| 243 | j = bad_ct -bit_ct; |
| 244 | m = k = 0; |
| 245 | while (1) { |
| 246 | if (datbuf[i] & (1 << k)) { |
| 247 | datbuf[i] &= ~(1 << k); |
| 248 | if (++m >= j) |
| 249 | break; |
| 250 | } |
| 251 | k++; |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | bit_ct += j; |
| 256 | if (bit_ct >= bad_ct) |
| 257 | break; |
| 258 | |
| 259 | if (++i >= mtd->writesize) { |
| 260 | printf("No enough bit set in this page\n"); |
| 261 | ret = 1; |
| 262 | goto free_all; |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | ret = mtd_write_oob(mtd, addr, &ops); |
| 267 | if (ret) { |
| 268 | printf("%s: write error at offset %llx, ret %d\n", |
| 269 | __func__, (long long)addr, ret); |
| 270 | ret = 1; |
| 271 | } |
| 272 | |
| 273 | free_all: |
| 274 | free(oobbuf); |
| 275 | free_dat: |
| 276 | free(datbuf); |
| 277 | |
| 278 | return ret; |
| 279 | } |
| 280 | |
| 281 | struct spi_flash_chip *spi_flash_chip_probe(int dev) |
| 282 | { |
| 283 | struct spi_flash_chip *chip; |
| 284 | int ret; |
| 285 | |
| 286 | ret = spi_flash_select_dev(dev); |
| 287 | if (ret) |
| 288 | chip = NULL; |
| 289 | else |
| 290 | chip = cur_sf_chip; |
| 291 | return chip; |
| 292 | } |
| 293 | |
| 294 | void spi_flash_chip_free(struct spi_flash_chip *chip) |
| 295 | { |
| 296 | } |
| 297 | |
| 298 | |
| 299 | int spi_flash_cmd_write_yaffs(struct spi_flash_chip *chip, u32 offset, |
| 300 | size_t len, const void *buf) |
| 301 | { |
| 302 | struct mtd_info *mtd = chip->mtd; |
| 303 | size_t retlen; |
| 304 | size_t leftlen = len; |
| 305 | size_t writelen; |
| 306 | size_t block_len, block_off; |
| 307 | u_char *p_buffer = buf; |
| 308 | loff_t block_start; |
| 309 | u32 writeoffset; |
| 310 | int ret = 0; |
| 311 | bool end; |
| 312 | |
| 313 | while (leftlen > 0) { |
| 314 | if (offset >= chip->size) |
| 315 | return -1; |
| 316 | |
| 317 | writeoffset = offset; |
| 318 | writelen = 0; |
| 319 | end = false; |
| 320 | if (mtd->type == MTD_NANDFLASH) { |
| 321 | while ((writelen < leftlen) && !end) { |
| 322 | block_start = offset & ~(loff_t)(mtd->erasesize - 1); |
| 323 | block_off = offset & (mtd->erasesize - 1); |
| 324 | block_len = mtd->erasesize - block_off; |
| 325 | |
| 326 | if (!mtd_block_isbad(mtd, block_start)) |
| 327 | writelen += block_len; |
| 328 | else |
| 329 | end = true; |
| 330 | offset += block_len; |
| 331 | } |
| 332 | } else |
| 333 | writelen = leftlen; |
| 334 | |
| 335 | if (writelen) { |
| 336 | int page, pages; |
| 337 | size_t pagesize = mtd->writesize; |
| 338 | size_t pagesize_oob = pagesize + mtd->oobsize; |
| 339 | struct mtd_oob_ops ops; |
| 340 | |
| 341 | writelen = min(writelen, leftlen); |
| 342 | |
| 343 | ops.len = pagesize; |
| 344 | ops.ooblen = mtd->oobsize; |
| 345 | ops.mode = MTD_OPS_AUTO_OOB; |
| 346 | ops.ooboffs = 0; |
| 347 | |
| 348 | pages = writelen / pagesize_oob; |
| 349 | for (page = 0; page < pages; page++) { |
| 350 | ops.datbuf = p_buffer; |
| 351 | ops.oobbuf = ops.datbuf + pagesize; |
| 352 | |
| 353 | ret = mtd_write_oob(mtd, writeoffset, &ops); |
| 354 | if (ret != 0) |
| 355 | break; |
| 356 | |
| 357 | writeoffset += pagesize; |
| 358 | p_buffer += pagesize_oob; |
| 359 | } |
| 360 | |
| 361 | leftlen -= writelen; |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | return ret; |
| 366 | } |
| 367 | |
| 368 | |
| 369 | int spi_flash_cmd_write_ops(struct spi_flash_chip *chip, u32 offset, |
| 370 | size_t len, const void *buf) |
| 371 | { |
| 372 | struct mtd_info *mtd = chip->mtd; |
| 373 | size_t retlen; |
| 374 | size_t leftlen = len; |
| 375 | size_t writelen; |
| 376 | size_t block_len, block_off; |
| 377 | loff_t block_start; |
| 378 | u32 writeoffset; |
| 379 | int ret = 0; |
| 380 | bool end; |
| 381 | |
| 382 | while (leftlen > 0) { |
| 383 | if (offset >= chip->size) |
| 384 | return -1; |
| 385 | |
| 386 | writeoffset = offset; |
| 387 | writelen = 0; |
| 388 | end = false; |
| 389 | if (mtd->type == MTD_NANDFLASH) { |
| 390 | while ((writelen < leftlen) && !end) { |
| 391 | block_start = offset & ~(loff_t)(mtd->erasesize - 1); |
| 392 | block_off = offset & (mtd->erasesize - 1); |
| 393 | block_len = mtd->erasesize - block_off; |
| 394 | |
| 395 | if (!mtd_block_isbad(mtd, block_start)) |
| 396 | writelen += block_len; |
| 397 | else |
| 398 | end = true; |
| 399 | offset += block_len; |
| 400 | } |
| 401 | } else |
| 402 | writelen = leftlen; |
| 403 | |
| 404 | if (writelen) { |
| 405 | writelen = min(writelen, leftlen); |
| 406 | ret = mtd_write(mtd, writeoffset, writelen, &retlen, buf + (len - leftlen)); |
| 407 | if (ret || writelen != retlen) |
| 408 | return -1; |
| 409 | leftlen -= writelen; |
| 410 | } |
| 411 | } |
| 412 | |
| 413 | return ret; |
| 414 | } |
| 415 | |
| 416 | int spi_flash_cmd_erase_ops(struct spi_flash_chip *chip, u32 offset, size_t len, bool spread) |
| 417 | { |
| 418 | struct mtd_info *mtd = chip->mtd; |
| 419 | struct erase_info instr = { |
| 420 | .callback = NULL, |
| 421 | }; |
| 422 | size_t leftlen; |
| 423 | size_t eraselen; |
| 424 | u32 eraseoffset; |
| 425 | u64 endaddr; |
| 426 | int ret = 0; |
| 427 | bool end; |
| 428 | |
| 429 | if (offset & (mtd->erasesize - 1)) { |
| 430 | printf("%s: Unaligned address\n", __func__); |
| 431 | return -EINVAL; |
| 432 | } |
| 433 | |
| 434 | if (len & (mtd->erasesize - 1)) { |
| 435 | printf("%s: warn: len=0x%x not block aligned\n", __func__, len); |
| 436 | len = roundup(len, mtd->erasesize); |
| 437 | } |
| 438 | |
| 439 | leftlen = len; |
| 440 | endaddr = offset + len; |
| 441 | if (endaddr > chip->size) { |
| 442 | printf("%s: Erase past end of device\n", __func__); |
| 443 | return -EINVAL; |
| 444 | } |
| 445 | |
| 446 | while (leftlen > 0) { |
| 447 | if (offset >= chip->size) |
| 448 | return -1; |
| 449 | |
| 450 | eraseoffset = offset; |
| 451 | eraselen = 0; |
| 452 | end = false; |
| 453 | if (mtd->type == MTD_NANDFLASH) { |
| 454 | while ((eraselen < leftlen) && !end) { |
| 455 | if (!mtd_block_isbad(mtd, offset)) |
| 456 | eraselen += mtd->erasesize; |
| 457 | else |
| 458 | end = true; |
| 459 | offset += mtd->erasesize; |
| 460 | } |
| 461 | } else { |
| 462 | eraselen = len; |
| 463 | offset += len; |
| 464 | } |
| 465 | if (eraselen) { |
| 466 | instr.addr = eraseoffset; |
| 467 | instr.len = eraselen; |
| 468 | instr.mtd = mtd; |
| 469 | ret = mtd_erase(mtd, &instr); |
| 470 | if (ret) |
| 471 | return -1; |
| 472 | } |
| 473 | if (spread) |
| 474 | leftlen -= eraselen; |
| 475 | else |
| 476 | leftlen = endaddr - offset; |
| 477 | } |
| 478 | |
| 479 | return ret; |
| 480 | } |
| 481 | |
| 482 | int spi_flash_cmd_read_ops(struct spi_flash_chip *chip, u32 offset, |
| 483 | size_t len, void *data) |
| 484 | { |
| 485 | struct mtd_info *mtd = chip->mtd; |
| 486 | size_t retlen; |
| 487 | size_t leftlen = len; |
| 488 | size_t readlen; |
| 489 | size_t block_len, block_off; |
| 490 | loff_t block_start; |
| 491 | u32 readoffset; |
| 492 | int ret = 0; |
| 493 | bool end; |
| 494 | |
| 495 | while (leftlen > 0) { |
| 496 | if (offset >= chip->size) |
| 497 | return -1; |
| 498 | |
| 499 | readoffset = offset; |
| 500 | readlen = 0; |
| 501 | end = false; |
| 502 | if (mtd->type == MTD_NANDFLASH) { |
| 503 | while ((readlen < leftlen) && !end) { |
| 504 | block_start = offset & ~(loff_t)(mtd->erasesize - 1); |
| 505 | block_off = offset & (mtd->erasesize - 1); |
| 506 | block_len = mtd->erasesize - block_off; |
| 507 | |
| 508 | if (!mtd_block_isbad(mtd, block_start)) |
| 509 | readlen += block_len; |
| 510 | else |
| 511 | end = true; |
| 512 | offset += block_len; |
| 513 | } |
| 514 | } else |
| 515 | readlen = leftlen; |
| 516 | if (readlen) { |
| 517 | readlen = min(readlen, leftlen); |
| 518 | ret = mtd_read(mtd, readoffset, readlen, &retlen, data + (len - leftlen)); |
| 519 | if ((ret < 0 && ret != -EUCLEAN) || readlen != retlen) |
| 520 | return -1; |
| 521 | |
| 522 | leftlen -= readlen; |
| 523 | } |
| 524 | } |
| 525 | return ret; |
| 526 | } |
| 527 | |
| 528 | /** |
| 529 | * Update an area of SPI flash by erasing and writing any blocks which need |
| 530 | * to change. Existing blocks with the correct data are left unchanged. |
| 531 | * |
| 532 | * @param flash flash context pointer |
| 533 | * @param offset flash offset to write |
| 534 | * @param len number of bytes to write |
| 535 | * @param buf buffer to write from |
| 536 | * @return 0 if ok, 1 on error |
| 537 | */ |
| 538 | static int spi_flash_update(struct spi_flash_chip *chip, u32 offset, |
| 539 | size_t len, const char *buf) |
| 540 | { |
| 541 | int ret = 0; |
| 542 | |
| 543 | ret = spi_flash_cmd_erase_ops(chip, offset, len, true); |
| 544 | if (ret) { |
| 545 | printf("SPI-FLASH: %zu bytes @ %#x Erased: ERROR\n", |
| 546 | (size_t)len, (u32)offset); |
| 547 | return ret; |
| 548 | } |
| 549 | ret = spi_flash_cmd_write_ops(chip, offset, len, buf); |
| 550 | if (ret) { |
| 551 | printf("SPI-FLASH: %zu bytes @ %#x Written: ERROR\n", |
| 552 | (size_t)len, (u32)offset); |
| 553 | } |
| 554 | return ret; |
| 555 | } |
| 556 | |
| 557 | static int do_spi_flash_get_info(struct spi_flash_chip *chip, |
| 558 | int argc, char * const argv[]) |
| 559 | { |
| 560 | struct mtd_info *mtd = chip->mtd; |
| 561 | unsigned long addr; |
| 562 | struct spi_flash_info *info; |
| 563 | char *endp; |
| 564 | |
| 565 | if (argc < 2) |
| 566 | return -1; |
| 567 | |
| 568 | addr = simple_strtoul(argv[1], &endp, 16); |
| 569 | if (*argv[1] == 0 || *endp != 0) |
| 570 | return -1; |
| 571 | |
| 572 | info = (struct spi_flash_info *)addr; |
| 573 | info->totalsize = chip->size; |
| 574 | info->erasesize = mtd->erasesize; |
| 575 | info->pagesize = mtd->writesize; |
| 576 | |
| 577 | #if 0 |
| 578 | printf("flash info: totalsize=0x%x erase_size=0x%x page_size=0x%x\n", |
| 579 | (unsigned)info->totalsize, (unsigned)info->erasesize, |
| 580 | (unsigned)info->pagesize); |
| 581 | #endif |
| 582 | return 0; |
| 583 | } |
| 584 | |
| 585 | static int do_spi_flash_read_write(struct spi_flash_chip *chip, |
| 586 | int argc, char * const argv[]) |
| 587 | { |
| 588 | struct mtd_info *mtd = chip->mtd; |
| 589 | unsigned long addr; |
| 590 | unsigned long offset; |
| 591 | unsigned long len; |
| 592 | int data_len; |
| 593 | void *buf; |
| 594 | char *endp; |
| 595 | char *cmd, *s; |
| 596 | int ret = 1; |
| 597 | |
| 598 | if (argc < 4) |
| 599 | return -1; |
| 600 | |
| 601 | cmd = argv[0]; |
| 602 | addr = simple_strtoul(argv[1], &endp, 16); |
| 603 | if (*argv[1] == 0 || *endp != 0) |
| 604 | return -1; |
| 605 | offset = simple_strtoul(argv[2], &endp, 16); |
| 606 | if (*argv[2] == 0 || *endp != 0) |
| 607 | return -1; |
| 608 | len = simple_strtoul(argv[3], &endp, 16); |
| 609 | if (*argv[3] == 0 || *endp != 0) |
| 610 | return -1; |
| 611 | |
| 612 | if (mtd->protect_enabled && |
| 613 | ((offset <= mtd->protect_start && |
| 614 | offset + len > mtd->protect_start) || |
| 615 | (offset >= mtd->protect_start && |
| 616 | offset < mtd->protect_end))) { |
| 617 | printf("error: protected ared start=0x%x end=0x%x\n", |
| 618 | mtd->protect_start, mtd->protect_end); |
| 619 | return -EACCES; |
| 620 | } |
| 621 | |
| 622 | data_len = len; |
| 623 | if (strcmp(argv[0], "update") == 0) { |
| 624 | if (offset + len > chip->size) { |
| 625 | printf("ERROR: attempting %s past flash size (%#llx)\n", |
| 626 | argv[0], chip->size); |
| 627 | return 1; |
| 628 | } |
| 629 | |
| 630 | buf = map_physmem(addr, len, MAP_WRBACK); |
| 631 | if (!buf) { |
| 632 | puts("Failed to map physical memory\n"); |
| 633 | return 1; |
| 634 | } |
| 635 | |
| 636 | ret = spi_flash_update(chip, offset, len, buf); |
| 637 | printf("SPI-FLASH: %zu bytes @ %#x Updated: %s\n", |
| 638 | (size_t)len, (u32)offset, ret ? "ERROR" : "OK"); |
| 639 | } else if (strncmp(argv[0], "read", 4) == 0 || |
| 640 | strncmp(argv[0], "write", 5) == 0) { |
| 641 | int read; |
| 642 | int raw = 0; |
| 643 | int yaffs = 0; |
| 644 | |
| 645 | read = strncmp(argv[0], "read", 4) == 0; |
| 646 | s = strchr(cmd, '.'); |
| 647 | if (s && !strcmp(s, ".raw")) { |
| 648 | raw = 1; |
| 649 | |
| 650 | if (len * mtd->writesize + offset > chip->size) { |
| 651 | puts("ERROR: Offset exceeds device limit\n"); |
| 652 | return 1; |
| 653 | } |
| 654 | |
| 655 | data_len = len * (mtd->writesize + mtd->oobsize); |
| 656 | } else if (s && !strcmp(s, ".yaffs")) { |
| 657 | yaffs = 1; |
| 658 | } else { |
| 659 | if (offset + len > chip->size) { |
| 660 | printf("ERROR: attempting %s past flash size (%#llx)\n", |
| 661 | argv[0], chip->size); |
| 662 | return 1; |
| 663 | } |
| 664 | } |
| 665 | |
| 666 | buf = map_physmem(addr, data_len, MAP_WRBACK); |
| 667 | if (!buf) { |
| 668 | puts("Failed to map physical memory\n"); |
| 669 | return 1; |
| 670 | } |
| 671 | |
| 672 | if (!s) { |
| 673 | if (read) |
| 674 | ret = spi_flash_cmd_read_ops(chip, offset, |
| 675 | len, buf); |
| 676 | else |
| 677 | ret = spi_flash_cmd_write_ops(chip, offset, |
| 678 | len, buf); |
| 679 | } else if (raw) { |
| 680 | ret = raw_access(chip, buf, offset, len, read); |
| 681 | } else if (yaffs) { |
| 682 | ret = spi_flash_cmd_write_yaffs(chip, offset, len, buf); |
| 683 | } else { |
| 684 | printf("Unknown spi_flash command suffix '%s'.\n", s); |
| 685 | ret = 1; |
| 686 | goto exit; |
| 687 | } |
| 688 | |
| 689 | if (ret == -EUCLEAN) |
| 690 | ret = 0; |
| 691 | |
| 692 | if ( len > 0x8000) |
| 693 | printf("SPI-FLASH: %zu %s @ %#x %s: %s\n", (size_t)len, |
| 694 | raw ? "pages" : "bytes", (u32)offset, |
| 695 | read ? "Read" : "Written", |
| 696 | (ret < 0 && ret != -EUCLEAN) ? "ERROR" : "OK"); |
| 697 | } |
| 698 | exit: |
| 699 | unmap_physmem(buf, data_len); |
| 700 | return ret == 0 ? 0 : 1; |
| 701 | } |
| 702 | |
| 703 | static int do_spi_flash_erase(struct spi_flash_chip *chip, |
| 704 | int argc, char * const argv[], bool spread) |
| 705 | { |
| 706 | struct mtd_info *mtd = chip->mtd; |
| 707 | unsigned long offset; |
| 708 | unsigned long len; |
| 709 | char *endp; |
| 710 | int ret; |
| 711 | |
| 712 | if (argc < 3) |
| 713 | return -1; |
| 714 | |
| 715 | offset = simple_strtoul(argv[1], &endp, 16); |
| 716 | if (*argv[1] == 0 || *endp != 0) |
| 717 | return -1; |
| 718 | |
| 719 | ret = sf_parse_len_arg(chip, argv[2], &len); |
| 720 | if (ret != 1) |
| 721 | return -1; |
| 722 | |
| 723 | if (mtd->protect_enabled && |
| 724 | ((offset <= mtd->protect_start && |
| 725 | offset + len > mtd->protect_start) || |
| 726 | (offset >= mtd->protect_start && |
| 727 | offset < mtd->protect_end))) { |
| 728 | printf("error: protected ared start=0x%x end=0x%x\n", |
| 729 | mtd->protect_start, mtd->protect_end); |
| 730 | return -EACCES; |
| 731 | } |
| 732 | |
| 733 | /* Consistency checking */ |
| 734 | if (offset + len > chip->size) { |
| 735 | printf("ERROR: attempting %s past flash size (%#llx)\n", |
| 736 | argv[0], chip->size); |
| 737 | return 1; |
| 738 | } |
| 739 | |
| 740 | ret = spi_flash_cmd_erase_ops(chip, offset, len, spread); |
| 741 | printf("SPI-FLASH: %zu bytes @ %#x Erased: %s\n", |
| 742 | (size_t)len, (u32)offset, ret ? "ERROR" : "OK"); |
| 743 | |
| 744 | return ret == 0 ? 0 : 1; |
| 745 | } |
| 746 | |
| 747 | static int do_spi_flash_show_bad(struct spi_flash_chip *chip) |
| 748 | { |
| 749 | struct mtd_info *mtd = chip->mtd; |
| 750 | loff_t offset; |
| 751 | |
| 752 | for (offset = 0; offset < chip->size; offset += mtd->erasesize) { |
| 753 | if(mtd_block_isbad(chip, offset)) |
| 754 | printf("Bad block at 0x%#llx\n", offset); |
| 755 | } |
| 756 | |
| 757 | return 0; |
| 758 | } |
| 759 | |
| 760 | static int do_spi_flash_mark_bad(struct spi_flash_chip *chip, |
| 761 | int argc, char * const argv[]) |
| 762 | { |
| 763 | int ret; |
| 764 | loff_t offset; |
| 765 | char *endp; |
| 766 | |
| 767 | if (argc < 2) |
| 768 | return -1; |
| 769 | |
| 770 | offset = simple_strtoul(argv[1], &endp, 16); |
| 771 | if (*argv[1] == 0 || *endp != 0) |
| 772 | return -1; |
| 773 | |
| 774 | ret = mtd_block_markbad(chip->mtd, offset); |
| 775 | if (!ret) { |
| 776 | printf("SPI-FLASH: 0x%#llx marked as bad block\n", offset); |
| 777 | } |
| 778 | |
| 779 | return ret == 0 ? 0 : 1; |
| 780 | } |
| 781 | |
| 782 | static int do_spi_flash_dump(struct spi_flash_chip *chip, |
| 783 | int argc, char * const argv[]) |
| 784 | { |
| 785 | int ret; |
| 786 | loff_t offset; |
| 787 | char *endp; |
| 788 | |
| 789 | if (argc < 2) |
| 790 | return -1; |
| 791 | |
| 792 | offset = simple_strtoul(argv[1], &endp, 16); |
| 793 | if (*argv[1] == 0 || *endp != 0) |
| 794 | return -1; |
| 795 | |
| 796 | ret = spi_flash_dump(chip, offset, 0, 0); |
| 797 | return ret == 0 ? 0 : 1; |
| 798 | } |
| 799 | |
| 800 | static int do_spi_flash_probe(int argc, char * const argv[]) |
| 801 | { |
| 802 | int dev = 0; |
| 803 | char *endp; |
| 804 | |
| 805 | if (argc >= 2) { |
| 806 | dev = simple_strtoul(argv[1], &endp, 16); |
| 807 | if (*argv[1] == 0 || *endp != 0) |
| 808 | return -1; |
| 809 | } |
| 810 | |
| 811 | return spi_flash_select_dev(dev); |
| 812 | } |
| 813 | |
| 814 | static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, |
| 815 | char * const argv[]) |
| 816 | { |
| 817 | struct spi_flash_chip *chip; |
| 818 | const char *cmd; |
| 819 | int ret; |
| 820 | |
| 821 | /* need at least two arguments */ |
| 822 | if (argc < 2) |
| 823 | goto usage; |
| 824 | |
| 825 | cmd = argv[1]; |
| 826 | --argc; |
| 827 | ++argv; |
| 828 | |
| 829 | if (strcmp(cmd, "probe") == 0) { |
| 830 | ret = do_spi_flash_probe(argc, argv); |
| 831 | goto done; |
| 832 | } |
| 833 | |
| 834 | /* The remaining commands require a selected device */ |
| 835 | chip = cur_sf_chip; |
| 836 | if (!chip) { |
| 837 | puts("No SPI flash selected. Please run `spi_flash probe'\n"); |
| 838 | return 1; |
| 839 | } |
| 840 | |
| 841 | if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0 || |
| 842 | strcmp(cmd, "update") == 0) |
| 843 | ret = do_spi_flash_read_write(chip, argc, argv); |
| 844 | else if (strcmp(cmd, "erase") == 0) |
| 845 | ret = do_spi_flash_erase(chip, argc, argv, false); |
| 846 | else if (strcmp(cmd, "erase.spread") == 0) |
| 847 | ret = do_spi_flash_erase(chip, argc, argv, true); |
| 848 | else if (strcmp(cmd, "info") == 0) |
| 849 | ret = do_spi_flash_get_info(chip, argc, argv); |
| 850 | else if (strcmp(cmd, "bad") == 0) |
| 851 | ret = do_spi_flash_show_bad(chip); |
| 852 | else if (strcmp(cmd, "markbad") == 0) |
| 853 | ret = do_spi_flash_mark_bad(chip, argc, argv); |
| 854 | else if (strcmp(cmd, "dump") == 0) |
| 855 | ret = do_spi_flash_dump(chip, argc, argv); |
| 856 | else if (strcmp(cmd, "biterr") == 0) { |
| 857 | int bad_bit, addr; |
| 858 | |
| 859 | argc -= 1; |
| 860 | argv += 1; |
| 861 | |
| 862 | if (argc <= 0) |
| 863 | goto usage; |
| 864 | |
| 865 | bad_bit = 1; |
| 866 | if (argc > 0) |
| 867 | addr = (ulong)simple_strtoul(argv[0], NULL, 16); |
| 868 | if (argc >= 2) |
| 869 | bad_bit = (ulong)simple_strtoul(argv[1], NULL, 16); |
| 870 | |
| 871 | ret = spi_flash_mark_biterr(chip, addr, bad_bit); |
| 872 | printf(" %s\n", ret ? "Failed" : "Passed"); |
| 873 | |
| 874 | return ret; |
| 875 | } else |
| 876 | ret = -1; |
| 877 | |
| 878 | done: |
| 879 | if (ret != -1) |
| 880 | return ret; |
| 881 | |
| 882 | usage: |
| 883 | return CMD_RET_USAGE; |
| 884 | } |
| 885 | |
| 886 | |
| 887 | U_BOOT_CMD( |
| 888 | spi_flash, 5, 1, do_spi_flash, |
| 889 | "SPI NAND/NOR flash sub-system", |
| 890 | "probe [[bus:]cs] [hz] [mode] - init flash device on given SPI bus\n" |
| 891 | " and chip select\n" |
| 892 | "spi_flash read addr offset len - read `len' bytes starting at\n" |
| 893 | " `offset' to memory at `addr', skipping bad blocks.\n" |
| 894 | "spi_flash write addr offset len - write `len' bytes from memory\n" |
| 895 | " at `addr' to flash at `offset', skipping bad blocks.\n" |
| 896 | "spi_flash erase[.spread] offset [+]len - erase `len' bytes from `offset'\n" |
| 897 | " `+len' round up `len' to block size\n" |
| 898 | " With '.spread', erase enough for given file size, otherwise,\n" |
| 899 | " 'size' includes skipped bad blocks.\n" |
| 900 | "spi_flash info addr - get flash info, and save to addr\n" |
| 901 | "spi_flash update addr offset len - erase and write `len' bytes from memory\n" |
| 902 | " at `addr' to flash at `offset'\n" |
| 903 | "spi_flash bad - show bad blocks\n" |
| 904 | "spi_flash markbad offset - mark block include `offset' as bad block\n" |
| 905 | ); |