1 /* 2 * Copyright (c) 2006-2018, RT-Thread Development Team 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Change Logs: 7 * Date Author Notes 8 * 2018-09-10 heyuanjie87 first version 9 */ 10 11 #include <rtdevice.h> 12 13 #define MTDTONAND(x) ((rt_nand_t*)(x)) 14 #define NOTALIGNED(x) ((x & (chip->page_size - 1)) != 0) 15 #ifndef min 16 #define min(a,b) (a>b? b:a) 17 #endif 18 19 static uint8_t *nand_fill_oob(rt_nand_t *chip, uint8_t *oob, size_t len, struct mtd_io_desc *desc) 20 { 21 rt_memset(chip->oob_poi, 0xff, chip->oobsize); 22 23 switch (desc->mode) 24 { 25 case MTD_OPM_PLACE_OOB: 26 case MTD_OPM_RAW: 27 rt_memcpy(chip->oob_poi + desc->ooboffs, oob, len); 28 return oob + len; 29 30 case MTD_OPM_AUTO_OOB: 31 { 32 const struct mtd_oob_region *free = chip->freelayout; 33 uint32_t boffs; 34 size_t bytes; 35 36 bytes = min(len, free->length); 37 boffs = free->offset; 38 39 rt_memcpy(chip->oob_poi + boffs, oob, bytes); 40 oob += bytes; 41 42 return oob; 43 } 44 } 45 46 return NULL; 47 } 48 49 static uint8_t *nand_transfer_oob(rt_nand_t *chip, uint8_t *oob, struct mtd_io_desc *desc, size_t len) 50 { 51 switch (desc->mode) 52 { 53 case MTD_OPM_PLACE_OOB: 54 case MTD_OPM_RAW: 55 rt_memcpy(oob, chip->oob_poi + desc->ooboffs, len); 56 return oob + len; 57 58 case MTD_OPM_AUTO_OOB: 59 { 60 struct mtd_oob_region *free = (struct mtd_oob_region *)chip->freelayout; 61 uint32_t boffs = 0, roffs = desc->ooboffs; 62 size_t bytes = 0; 63 64 for (; free->length && len; free++, len -= bytes) 65 { 66 /* Read request not from offset 0? */ 67 if (roffs) 68 { 69 if (roffs >= free->length) 70 { 71 roffs -= free->length; 72 continue; 73 } 74 boffs = free->offset + roffs; 75 bytes = min(len, (free->length - roffs)); 76 roffs = 0; 77 } 78 else 79 { 80 bytes = min(len, free->length); 81 boffs = free->offset; 82 } 83 84 rt_memcpy(oob, chip->oob_poi + boffs, bytes); 85 oob += bytes; 86 } 87 88 return oob; 89 } 90 } 91 92 return NULL; 93 } 94 95 static int nand_read_page_raw(rt_nand_t *chip, uint8_t *buf, int oob_required, int page) 96 { 97 chip->ops->read_buf(chip, buf, chip->page_size); 98 99 if (oob_required) 100 chip->ops->read_buf(chip, chip->oob_poi, chip->oobsize); 101 102 return 0; 103 } 104 105 static int nand_write_page_raw(rt_nand_t *chip, const uint8_t *buf, int oob_required, int page) 106 { 107 chip->ops->write_buf(chip, buf, chip->page_size); 108 109 if (oob_required) 110 chip->ops->write_buf(chip, chip->oob_poi, chip->oobsize); 111 112 return 0; 113 } 114 115 static int nand_write_page_hwecc(rt_nand_t *chip, const uint8_t *buf, int oob_required, int page) 116 { 117 uint16_t i; 118 uint16_t stepsize = chip->ecc.stepsize; 119 uint16_t eccbytes = chip->ecc.bytes; 120 uint16_t eccsteps = chip->ecc._step; 121 uint16_t eccpos = chip->ecc.layout->offset; 122 uint8_t *ecc_calc = chip->buffers.ecccalc; 123 const uint8_t *p = buf; 124 125 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += stepsize) 126 { 127 chip->ops->cmdfunc(chip, NAND_CMD_ECC_EN, 0, 0); 128 chip->ops->write_buf(chip, p, stepsize); 129 chip->ecc.calculate(chip, p, &ecc_calc[i]); 130 chip->ops->cmdfunc(chip, NAND_CMD_ECC_DIS, 0, 0); 131 } 132 133 rt_memcpy(&chip->oob_poi[eccpos], ecc_calc, chip->ecc.layout->length); 134 135 chip->ops->write_buf(chip, chip->oob_poi, chip->oobsize); 136 137 return 0; 138 } 139 140 static int nand_read_page_hwecc(rt_nand_t *chip, uint8_t *buf, int oob_required, int page) 141 { 142 uint16_t i; 143 uint16_t eccsize = chip->ecc.stepsize; 144 uint16_t eccbytes = chip->ecc.bytes; 145 uint16_t eccsteps = chip->ecc._step; 146 uint16_t eccpos = chip->ecc.layout->offset; 147 uint8_t *p = buf; 148 uint8_t *ecc_calc = chip->buffers.ecccalc; 149 uint8_t *ecc_code = chip->buffers.ecccode; 150 int ret = 0; 151 152 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) 153 { 154 chip->ops->cmdfunc(chip, NAND_CMD_ECC_EN, 0, 0); 155 chip->ops->read_buf(chip, p, eccsize); 156 chip->ecc.calculate(chip, p, &ecc_calc[i]); 157 chip->ops->cmdfunc(chip, NAND_CMD_ECC_DIS, 0, 0); 158 } 159 160 chip->ops->read_buf(chip, chip->oob_poi, chip->oobsize); 161 rt_memcpy(ecc_code, &chip->oob_poi[eccpos], chip->ecc.layout->length); 162 163 eccsteps = chip->ecc._step; 164 p = buf; 165 166 for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) 167 { 168 int stat; 169 170 stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]); 171 if (stat != 0) 172 ret = -1; 173 } 174 175 return ret; 176 } 177 178 static int nand_write_page(rt_nand_t *chip, const uint8_t *buf, 179 int oob_required, int page, int raw) 180 { 181 int status; 182 183 chip->ops->cmdfunc(chip, NAND_CMD_PAGE_WR0, page, 0x00); 184 185 if (raw) 186 { 187 nand_write_page_raw(chip, buf, oob_required, page); 188 } 189 else 190 { 191 chip->write_page(chip, buf, oob_required, page); 192 } 193 194 status = chip->ops->cmdfunc(chip, NAND_CMD_PAGE_WR1, -1, -1); 195 196 return status; 197 } 198 199 static int nand_do_read_desc(rt_nand_t *chip, loff_t from, struct mtd_io_desc *desc) 200 { 201 int page, bytes; 202 char oob_required; 203 char ecc_fail = 0; 204 int ret = 0; 205 uint32_t readlen = desc->datlen; 206 uint16_t oobreadlen = desc->ooblen; 207 uint16_t max_oobsize = desc->mode == MTD_OPM_AUTO_OOB ? 208 chip->freelayout->length : chip->oobsize; 209 210 uint8_t *oob, *buf, *notalign = 0; 211 212 /* Reject reads, which are not page aligned */ 213 if (NOTALIGNED(from)) 214 { 215 return -EINVAL; 216 } 217 218 buf = desc->datbuf; 219 if (NOTALIGNED(desc->datlen) && !chip->pagebuf) 220 { 221 chip->pagebuf = rt_malloc(chip->page_size); 222 if (!chip->pagebuf) 223 return -ENOMEM; 224 } 225 226 page = (int)(from / chip->page_size); 227 228 oob = desc->oobbuf; 229 oob_required = oob ? 1 : 0; 230 231 while (1) 232 { 233 bytes = min(chip->page_size, readlen); 234 235 chip->ops->cmdfunc(chip, NAND_CMD_PAGE_RD, page, 0x00); 236 if (NOTALIGNED(bytes)) 237 { 238 notalign = buf; 239 buf = chip->pagebuf; 240 } 241 /* 242 * Now read the page into the buffer. Absent an error, 243 * the read methods return max bitflips per ecc step. 244 */ 245 if (desc->mode == MTD_OPM_RAW) 246 { 247 ret = nand_read_page_raw(chip, buf, oob_required, page); 248 } 249 else 250 { 251 ret = chip->read_page(chip, buf, oob_required, page); 252 } 253 254 if (ret != 0) 255 { 256 ret = -EBADMSG; 257 break; 258 } 259 260 if (oob) 261 { 262 int toread = min(oobreadlen, max_oobsize); 263 264 if (toread) 265 { 266 oob = nand_transfer_oob(chip, oob, desc, toread); 267 oobreadlen -= toread; 268 } 269 } 270 271 if (notalign) 272 { 273 rt_memcpy(notalign, buf, bytes); 274 } 275 276 buf += bytes; 277 readlen -= bytes; 278 279 if (!readlen) 280 break; 281 282 page++; 283 } 284 285 desc->datretlen = desc->datlen - (size_t)readlen; 286 if (oob) 287 desc->oobretlen = desc->ooblen - oobreadlen; 288 289 return ret; 290 } 291 292 /* 293 * write with ECC 294 * 295 */ 296 static int nand_do_write_desc(rt_nand_t *chip, loff_t to, struct mtd_io_desc *desc) 297 { 298 int page; 299 uint16_t writelen = desc->datlen; 300 uint16_t oob_required = desc->oobbuf ? 1 : 0; 301 uint16_t oobwritelen = desc->ooblen; 302 uint16_t oobmaxlen = desc->mode == MTD_OPM_AUTO_OOB ? 303 chip->freelayout->length : chip->oobsize; 304 305 uint8_t *oob = desc->oobbuf; 306 uint8_t *buf = desc->datbuf; 307 int ret; 308 309 if (!writelen) 310 return 0; 311 312 /* Reject writes, which are not page aligned */ 313 if (NOTALIGNED(to)) 314 { 315 return -EINVAL; 316 } 317 318 page = (int)(to / chip->page_size); 319 320 /* Don't allow multipage oob writes with offset */ 321 if (oob && desc->ooboffs && (desc->ooboffs + desc->ooblen > oobmaxlen)) 322 { 323 ret = -EINVAL; 324 goto err_out; 325 } 326 327 if (NOTALIGNED(desc->datlen) && !chip->pagebuf) 328 { 329 chip->pagebuf = rt_malloc(chip->page_size); 330 if (!chip->pagebuf) 331 return -ENOMEM; 332 } 333 334 while (1) 335 { 336 uint16_t bytes = min(chip->page_size, writelen); 337 338 if (oob) 339 { 340 size_t len = min(oobwritelen, oobmaxlen); 341 oob = nand_fill_oob(chip, oob, len, desc); 342 oobwritelen -= len; 343 } 344 else 345 { 346 /* We still need to erase leftover OOB data */ 347 rt_memset(chip->oob_poi, 0xff, chip->oobsize); 348 } 349 350 if (NOTALIGNED(bytes)) 351 { 352 uint8_t *dbtmp = buf; 353 buf = chip->pagebuf; 354 rt_memset(&buf[bytes], 0xff, chip->page_size - bytes); 355 rt_memcpy(buf, dbtmp, bytes); 356 } 357 ret = nand_write_page(chip, buf, oob_required, page, (desc->mode == MTD_OPM_RAW)); 358 if (ret) 359 break; 360 361 writelen -= bytes; 362 if (!writelen) 363 break; 364 365 buf += bytes; 366 page++; 367 } 368 369 desc->datretlen = desc->datlen - writelen; 370 if (oob) 371 desc->oobretlen = desc->ooblen; 372 373 err_out: 374 375 return ret; 376 } 377 378 static int nand_read_oob_std(rt_nand_t *chip, int page) 379 { 380 chip->ops->cmdfunc(chip, NAND_CMD_PAGE_RD, page, chip->page_size); 381 chip->ops->read_buf(chip, chip->oob_poi, chip->oobsize); 382 383 return 0; 384 } 385 386 /* 387 * read one page of OOB 388 */ 389 static int nand_only_read_oob(rt_nand_t *chip, loff_t from, struct mtd_io_desc *desc) 390 { 391 int page; 392 int readlen = desc->ooblen; 393 int len; 394 uint8_t *buf = desc->oobbuf; 395 int ret = 0; 396 397 if (desc->mode == MTD_OPM_AUTO_OOB) 398 len = chip->freelayout->length; 399 else 400 len = chip->oobsize; 401 402 if (desc->ooboffs >= len) //attempt to start read outside oob 403 { 404 return -EINVAL; 405 } 406 407 page = (int)(from / chip->page_size); 408 409 ret = nand_read_oob_std(chip, page); 410 if (ret == 0) 411 { 412 len = min(len, readlen); 413 buf = nand_transfer_oob(chip, buf, desc, len); 414 desc->oobretlen = len; 415 } 416 417 return ret; 418 } 419 420 static int nand_write_oob_std(rt_nand_t *chip, int page) 421 { 422 int status; 423 424 chip->ops->cmdfunc(chip, NAND_CMD_PAGE_WR0, page, chip->page_size); 425 chip->ops->write_buf(chip, chip->oob_poi, chip->oobsize); 426 /* Send command to program the OOB data */ 427 status = chip->ops->cmdfunc(chip, NAND_CMD_PAGE_WR1, -1, -1); 428 429 return status & NAND_STATUS_FAIL ? -EIO : 0; 430 } 431 432 static int nand_only_write_oob(rt_nand_t *chip, loff_t to, struct mtd_io_desc *desc) 433 { 434 int page, ret, len; 435 436 if (desc->mode == MTD_OPM_AUTO_OOB) 437 len = chip->freelayout->length; 438 else 439 len = chip->oobsize; 440 441 /* Do not allow write past end of page */ 442 if ((desc->ooboffs + desc->ooblen) > len) 443 { 444 return -EINVAL; 445 } 446 447 if (desc->ooblen == 0) 448 { 449 return -EINVAL; 450 } 451 452 /* get page */ 453 page = (int)(to / chip->page_size); 454 455 nand_fill_oob(chip, desc->oobbuf, desc->ooblen, desc); 456 457 ret = nand_write_oob_std(chip, page); 458 if (ret == 0) 459 desc->oobretlen = len; 460 461 return ret; 462 } 463 464 static int nand_erase(rt_mtd_t *mtd, loff_t addr, size_t size) 465 { 466 rt_nand_t *chip; 467 int status; 468 int page; 469 uint32_t blksize; 470 471 chip = MTDTONAND(mtd); 472 blksize = mtd->block_size; 473 page = addr / chip->page_size; 474 475 while (size >= blksize) 476 { 477 status = chip->ops->cmdfunc(chip, NAND_CMD_BLK_ERASE, page, 0); 478 if (status & NAND_STATUS_FAIL) 479 { 480 break; 481 } 482 size -= blksize; 483 page += chip->pages_pb; 484 } 485 486 return size; 487 } 488 489 static int nand_read(rt_mtd_t *mtd, loff_t from, struct mtd_io_desc *desc) 490 { 491 int ret = -ENOTSUP; 492 rt_nand_t *chip; 493 494 chip = MTDTONAND(mtd); 495 496 switch (desc->mode) 497 { 498 case MTD_OPM_PLACE_OOB: 499 case MTD_OPM_AUTO_OOB: 500 case MTD_OPM_RAW: 501 break; 502 503 default: 504 goto out; 505 } 506 507 if (!desc->datbuf || !desc->datlen) 508 ret = nand_only_read_oob(chip, from, desc); 509 else 510 ret = nand_do_read_desc(chip, from, desc); 511 512 out: 513 514 return ret; 515 } 516 517 static int nand_write(rt_mtd_t *mtd, loff_t to, struct mtd_io_desc *desc) 518 { 519 int ret = -ENOTSUP; 520 rt_nand_t *chip; 521 522 chip = MTDTONAND(mtd); 523 524 switch (desc->mode) 525 { 526 case MTD_OPM_PLACE_OOB: 527 case MTD_OPM_AUTO_OOB: 528 case MTD_OPM_RAW: 529 break; 530 531 default: 532 goto out; 533 } 534 535 if (!desc->datbuf || !desc->datlen) 536 ret = nand_only_write_oob(chip, to, desc); 537 else 538 ret = nand_do_write_desc(chip, to, desc); 539 540 out: 541 542 return ret; 543 } 544 545 static int nand_block_isbad(rt_mtd_t *mtd, uint32_t blk) 546 { 547 int ret; 548 rt_nand_t *chip = MTDTONAND(mtd); 549 550 if (chip->ops->isbad) 551 { 552 ret = chip->ops->isbad(chip, blk); 553 } 554 else 555 { 556 int page; 557 558 page = blk * chip->pages_pb; 559 nand_read_oob_std(chip, page); 560 ret = chip->oob_poi[0] != 0xFF; 561 } 562 563 return ret; 564 } 565 566 static int nand_block_markbad(rt_mtd_t *mtd, uint32_t blk) 567 { 568 int ret; 569 rt_nand_t *chip; 570 571 chip = MTDTONAND(mtd); 572 573 if (chip->ops->markbad) 574 { 575 ret = chip->ops->markbad(chip, blk); 576 } 577 else 578 { 579 int page; 580 581 page = blk * chip->pages_pb; 582 rt_memset(chip->oob_poi, 0xff, chip->oobsize); 583 chip->oob_poi[0] = 0; 584 ret = nand_write_oob_std(chip, page); 585 } 586 587 return ret; 588 } 589 590 static const struct mtd_ops _ops = 591 { 592 nand_erase, 593 nand_read, 594 nand_write, 595 nand_block_isbad, 596 nand_block_markbad, 597 }; 598 599 int rt_mtd_nand_init(rt_nand_t *nand, int blk_size, int page_size, int oob_size) 600 { 601 uint8_t *buf; 602 603 buf = rt_malloc(oob_size * 3); 604 if (buf == RT_NULL) 605 return -ENOMEM; 606 607 nand->oob_poi = buf; 608 buf += oob_size; 609 nand->buffers.ecccalc = buf; 610 buf += oob_size; 611 nand->buffers.ecccode = buf; 612 nand->pagebuf = 0; /* alloc when unaligen access */ 613 614 nand->pages_pb = blk_size / page_size; 615 nand->ecc._step = page_size / nand->ecc.stepsize; 616 nand->page_size = page_size; 617 nand->oobsize = oob_size; 618 619 nand->parent.type = MTD_TYPE_NAND; 620 nand->parent.ops = &_ops; 621 nand->parent.sector_size = page_size; 622 nand->parent.block_size = blk_size; 623 nand->parent.oob_size = oob_size; 624 625 switch (nand->ecc.mode) 626 { 627 case NAND_ECCM_NONE: 628 { 629 nand->read_page = nand_read_page_raw; 630 nand->write_page = nand_write_page_raw; 631 }break; 632 case NAND_ECCM_HW: 633 { 634 nand->read_page = nand_read_page_hwecc; 635 nand->write_page = nand_write_page_hwecc; 636 }break; 637 default: 638 { 639 rt_free(buf); 640 return -1; 641 } 642 } 643 644 return 0; 645 } 646