2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2018 Aleph One Ltd.
6 * Created by Charles Manning <charles@aleph1.co.uk>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
15 #include "yaffs_mtdif.h"
17 #include "linux/mtd/mtd.h"
18 #include "linux/types.h"
19 #include "linux/time.h"
20 #include "linux/mtd/nand.h"
21 #include "linux/kernel.h"
22 #include "linux/version.h"
23 #include "linux/types.h"
24 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
25 #include "uapi/linux/major.h"
28 #include "yaffs_trace.h"
29 #include "yaffs_guts.h"
30 #include "yaffs_linux.h"
32 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
33 #define MTD_OPS_AUTO_OOB MTD_OOB_AUTO
37 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
38 #define mtd_erase(m, ei) (m)->erase(m, ei)
39 #define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops)
40 #define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops)
41 #define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs)
42 #define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs)
47 int nandmtd_erase_block(struct yaffs_dev *dev, int block_no)
49 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
51 ((loff_t) block_no) * dev->param.total_bytes_per_chunk *
52 dev->param.chunks_per_block;
58 ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block;
62 ei.priv = (u_long) dev;
64 retval = mtd_erase(mtd, &ei);
73 static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk,
74 const u8 *data, int data_len,
75 const u8 *oob, int oob_len)
77 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
79 struct mtd_oob_ops ops;
82 yaffs_trace(YAFFS_TRACE_MTD,
83 "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n",
84 dev, nand_chunk, data, data_len, oob, oob_len);
86 if (!data || !data_len) {
91 if (!oob || !oob_len) {
96 addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
97 memset(&ops, 0, sizeof(ops));
98 ops.mode = MTD_OPS_AUTO_OOB;
99 ops.len = (data) ? data_len : 0;
100 ops.ooblen = oob_len;
101 ops.datbuf = (u8 *)data;
102 ops.oobbuf = (u8 *)oob;
104 retval = mtd_write_oob(mtd, addr, &ops);
106 yaffs_trace(YAFFS_TRACE_MTD,
107 "write_oob failed, chunk %d, mtd error %d",
110 return retval ? YAFFS_FAIL : YAFFS_OK;
113 static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk,
114 u8 *data, int data_len,
115 u8 *oob, int oob_len,
116 enum yaffs_ecc_result *ecc_result)
118 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
120 struct mtd_oob_ops ops;
123 addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
124 memset(&ops, 0, sizeof(ops));
125 ops.mode = MTD_OPS_AUTO_OOB;
126 ops.len = (data) ? data_len : 0;
127 ops.ooblen = oob_len;
131 #if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))
132 /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
133 * help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
135 ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
137 /* Read page and oob using MTD.
138 * Check status and determine ECC result.
140 retval = mtd_read_oob(mtd, addr, &ops);
142 yaffs_trace(YAFFS_TRACE_MTD,
143 "read_oob failed, chunk %d, mtd error %d",
150 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
154 /* MTD's ECC fixed the data */
156 *ecc_result = YAFFS_ECC_RESULT_FIXED;
162 /* MTD's ECC could not fix the data */
163 dev->n_ecc_unfixed++;
165 *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
172 static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)
174 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
177 struct erase_info ei;
181 block_size = dev->param.total_bytes_per_chunk *
182 dev->param.chunks_per_block;
183 addr = ((loff_t) block_no) * block_size;
191 ei.priv = (u_long) dev;
193 retval = mtd_erase(mtd, &ei);
201 static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)
203 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
204 int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
207 yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);
209 retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no);
210 return (retval) ? YAFFS_FAIL : YAFFS_OK;
213 static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)
215 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
216 int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
219 yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no);
221 retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no);
222 return (retval) ? YAFFS_FAIL : YAFFS_OK;
225 static int yaffs_mtd_initialise(struct yaffs_dev *dev)
230 static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)
236 void yaffs_mtd_drv_install(struct yaffs_dev *dev)
238 struct yaffs_driver *drv = &dev->drv;
240 drv->drv_write_chunk_fn = yaffs_mtd_write;
241 drv->drv_read_chunk_fn = yaffs_mtd_read;
242 drv->drv_erase_fn = yaffs_mtd_erase;
243 drv->drv_mark_bad_fn = yaffs_mtd_mark_bad;
244 drv->drv_check_bad_fn = yaffs_mtd_check_bad;
245 drv->drv_initialise_fn = yaffs_mtd_initialise;
246 drv->drv_deinitialise_fn = yaffs_mtd_deinitialise;
250 struct mtd_info * yaffs_get_mtd_device(dev_t sdev)
252 struct mtd_info *mtd;
254 mtd = yaffs_get_mtd_device(sdev);
256 /* Check it's an mtd device..... */
257 if (MAJOR(sdev) != MTD_BLOCK_MAJOR)
258 return NULL; /* This isn't an mtd device */
260 /* Check it's NAND */
261 if (mtd->type != MTD_NANDFLASH) {
262 yaffs_trace(YAFFS_TRACE_ALWAYS,
263 "yaffs: MTD device is not NAND it's type %d",
268 yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd));
269 yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize);
270 yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize);
271 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
272 yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size);
274 yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size);
280 int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
282 if (yaffs_version == 2) {
283 if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
284 mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) &&
286 yaffs_trace(YAFFS_TRACE_ALWAYS,
287 "MTD device does not have the right page sizes"
292 if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK ||
293 mtd->oobsize != YAFFS_BYTES_PER_SPARE) {
294 yaffs_trace(YAFFS_TRACE_ALWAYS,
295 "MTD device does not support have the right page sizes"
305 void yaffs_put_mtd_device(struct mtd_info *mtd)