mtdnand.c 14 KB

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