yaffs_mtdif.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*
  2. * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
  3. *
  4. * Copyright (C) 2002-2011 Aleph One Ltd.
  5. * for Toby Churchill Ltd and Brightstar Engineering
  6. *
  7. * Created by Charles Manning <charles@aleph1.co.uk>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2 as
  11. * published by the Free Software Foundation.
  12. */
  13. #include "yportenv.h"
  14. #include "yaffs_mtdif.h"
  15. #include "linux/mtd/mtd.h"
  16. #include "linux/types.h"
  17. #include "linux/time.h"
  18. #include "linux/mtd/nand.h"
  19. #include "linux/kernel.h"
  20. #include "linux/version.h"
  21. #include "linux/types.h"
  22. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
  23. #include "uapi/linux/major.h"
  24. #endif
  25. #include "yaffs_trace.h"
  26. #include "yaffs_guts.h"
  27. #include "yaffs_linux.h"
  28. #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
  29. #define MTD_OPS_AUTO_OOB MTD_OOB_AUTO
  30. #endif
  31. #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
  32. #define mtd_erase(m, ei) (m)->erase(m, ei)
  33. #define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops)
  34. #define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops)
  35. #define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs)
  36. #define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs)
  37. #endif
  38. int nandmtd_erase_block(struct yaffs_dev *dev, int block_no)
  39. {
  40. struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
  41. u32 addr =
  42. ((loff_t) block_no) * dev->param.total_bytes_per_chunk *
  43. dev->param.chunks_per_block;
  44. struct erase_info ei;
  45. int retval = 0;
  46. ei.mtd = mtd;
  47. ei.addr = addr;
  48. ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block;
  49. ei.time = 1000;
  50. ei.retries = 2;
  51. ei.callback = NULL;
  52. ei.priv = (u_long) dev;
  53. retval = mtd_erase(mtd, &ei);
  54. if (retval == 0)
  55. return YAFFS_OK;
  56. return YAFFS_FAIL;
  57. }
  58. static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk,
  59. const u8 *data, int data_len,
  60. const u8 *oob, int oob_len)
  61. {
  62. struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
  63. loff_t addr;
  64. struct mtd_oob_ops ops;
  65. int retval;
  66. yaffs_trace(YAFFS_TRACE_MTD,
  67. "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n",
  68. dev, nand_chunk, data, data_len, oob, oob_len);
  69. if (!data || !data_len) {
  70. data = NULL;
  71. data_len = 0;
  72. }
  73. if (!oob || !oob_len) {
  74. oob = NULL;
  75. oob_len = 0;
  76. }
  77. addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
  78. memset(&ops, 0, sizeof(ops));
  79. ops.mode = MTD_OPS_AUTO_OOB;
  80. ops.len = (data) ? data_len : 0;
  81. ops.ooblen = oob_len;
  82. ops.datbuf = (u8 *)data;
  83. ops.oobbuf = (u8 *)oob;
  84. retval = mtd_write_oob(mtd, addr, &ops);
  85. if (retval) {
  86. yaffs_trace(YAFFS_TRACE_MTD,
  87. "write_oob failed, chunk %d, mtd error %d",
  88. nand_chunk, retval);
  89. }
  90. return retval ? YAFFS_FAIL : YAFFS_OK;
  91. }
  92. static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk,
  93. u8 *data, int data_len,
  94. u8 *oob, int oob_len,
  95. enum yaffs_ecc_result *ecc_result)
  96. {
  97. struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
  98. loff_t addr;
  99. struct mtd_oob_ops ops;
  100. int retval;
  101. addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
  102. memset(&ops, 0, sizeof(ops));
  103. ops.mode = MTD_OPS_AUTO_OOB;
  104. ops.len = (data) ? data_len : 0;
  105. ops.ooblen = oob_len;
  106. ops.datbuf = data;
  107. ops.oobbuf = oob;
  108. #if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))
  109. /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
  110. * help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
  111. */
  112. ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
  113. #endif
  114. /* Read page and oob using MTD.
  115. * Check status and determine ECC result.
  116. */
  117. retval = mtd_read_oob(mtd, addr, &ops);
  118. if (retval)
  119. yaffs_trace(YAFFS_TRACE_MTD,
  120. "read_oob failed, chunk %d, mtd error %d",
  121. nand_chunk, retval);
  122. switch (retval) {
  123. case 0:
  124. /* no error */
  125. if(ecc_result)
  126. *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
  127. break;
  128. case -EUCLEAN:
  129. /* MTD's ECC fixed the data */
  130. if(ecc_result)
  131. *ecc_result = YAFFS_ECC_RESULT_FIXED;
  132. dev->n_ecc_fixed++;
  133. break;
  134. case -EBADMSG:
  135. default:
  136. /* MTD's ECC could not fix the data */
  137. dev->n_ecc_unfixed++;
  138. if(ecc_result)
  139. *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
  140. return YAFFS_FAIL;
  141. }
  142. return YAFFS_OK;
  143. }
  144. static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)
  145. {
  146. struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
  147. loff_t addr;
  148. struct erase_info ei;
  149. int retval = 0;
  150. u32 block_size;
  151. block_size = dev->param.total_bytes_per_chunk *
  152. dev->param.chunks_per_block;
  153. addr = ((loff_t) block_no) * block_size;
  154. ei.mtd = mtd;
  155. ei.addr = addr;
  156. ei.len = block_size;
  157. ei.time = 1000;
  158. ei.retries = 2;
  159. ei.callback = NULL;
  160. ei.priv = (u_long) dev;
  161. retval = mtd_erase(mtd, &ei);
  162. if (retval == 0)
  163. return YAFFS_OK;
  164. return YAFFS_FAIL;
  165. }
  166. static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)
  167. {
  168. struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
  169. int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
  170. int retval;
  171. yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);
  172. retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no);
  173. return (retval) ? YAFFS_FAIL : YAFFS_OK;
  174. }
  175. static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)
  176. {
  177. struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
  178. int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
  179. int retval;
  180. yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no);
  181. retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no);
  182. return (retval) ? YAFFS_FAIL : YAFFS_OK;
  183. }
  184. static int yaffs_mtd_initialise(struct yaffs_dev *dev)
  185. {
  186. return YAFFS_OK;
  187. }
  188. static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)
  189. {
  190. return YAFFS_OK;
  191. }
  192. void yaffs_mtd_drv_install(struct yaffs_dev *dev)
  193. {
  194. struct yaffs_driver *drv = &dev->drv;
  195. drv->drv_write_chunk_fn = yaffs_mtd_write;
  196. drv->drv_read_chunk_fn = yaffs_mtd_read;
  197. drv->drv_erase_fn = yaffs_mtd_erase;
  198. drv->drv_mark_bad_fn = yaffs_mtd_mark_bad;
  199. drv->drv_check_bad_fn = yaffs_mtd_check_bad;
  200. drv->drv_initialise_fn = yaffs_mtd_initialise;
  201. drv->drv_deinitialise_fn = yaffs_mtd_deinitialise;
  202. }
  203. struct mtd_info * yaffs_get_mtd_device(dev_t sdev)
  204. {
  205. struct mtd_info *mtd;
  206. mtd = yaffs_get_mtd_device(sdev);
  207. /* Check it's an mtd device..... */
  208. if (MAJOR(sdev) != MTD_BLOCK_MAJOR)
  209. return NULL; /* This isn't an mtd device */
  210. /* Check it's NAND */
  211. if (mtd->type != MTD_NANDFLASH) {
  212. yaffs_trace(YAFFS_TRACE_ALWAYS,
  213. "yaffs: MTD device is not NAND it's type %d",
  214. mtd->type);
  215. return NULL;
  216. }
  217. yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd));
  218. yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize);
  219. yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize);
  220. #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
  221. yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size);
  222. #else
  223. yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size);
  224. #endif
  225. return mtd;
  226. }
  227. int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
  228. {
  229. if (yaffs_version == 2) {
  230. if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
  231. mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) &&
  232. !inband_tags) {
  233. yaffs_trace(YAFFS_TRACE_ALWAYS,
  234. "MTD device does not have the right page sizes"
  235. );
  236. return -1;
  237. }
  238. } else {
  239. if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK ||
  240. mtd->oobsize != YAFFS_BYTES_PER_SPARE) {
  241. yaffs_trace(YAFFS_TRACE_ALWAYS,
  242. "MTD device does not support have the right page sizes"
  243. );
  244. return -1;
  245. }
  246. }
  247. return 0;
  248. }
  249. void yaffs_put_mtd_device(struct mtd_info *mtd)
  250. {
  251. if(mtd)
  252. put_mtd_device(mtd);
  253. }