2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2011 Aleph One Ltd.
5 * for Toby Churchill Ltd and Brightstar Engineering
7 * Created by Charles Manning <charles@aleph1.co.uk>
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.
16 #include "yaffs_mtdif.h"
18 #include "linux/mtd/mtd.h"
19 #include "linux/types.h"
20 #include "linux/time.h"
21 #include "linux/mtd/nand.h"
22 #include "linux/kernel.h"
23 #include "linux/version.h"
24 #include "linux/types.h"
25 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
26 #include "uapi/linux/major.h"
29 #include "yaffs_trace.h"
30 #include "yaffs_guts.h"
31 #include "yaffs_linux.h"
33 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
34 #define MTD_OPS_AUTO_OOB MTD_OOB_AUTO
38 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
39 #define mtd_erase(m, ei) (m)->erase(m, ei)
40 #define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops)
41 #define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops)
42 #define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs)
43 #define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs)
48 int nandmtd_erase_block(struct yaffs_dev
*dev
, int block_no
)
50 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
52 ((loff_t
) block_no
) * dev
->param
.total_bytes_per_chunk
*
53 dev
->param
.chunks_per_block
;
59 ei
.len
= dev
->param
.total_bytes_per_chunk
* dev
->param
.chunks_per_block
;
63 ei
.priv
= (u_long
) dev
;
65 retval
= mtd_erase(mtd
, &ei
);
74 static int yaffs_mtd_write(struct yaffs_dev
*dev
, int nand_chunk
,
75 const u8
*data
, int data_len
,
76 const u8
*oob
, int oob_len
)
78 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
80 struct mtd_oob_ops ops
;
83 yaffs_trace(YAFFS_TRACE_MTD
,
84 "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n",
85 dev
, nand_chunk
, data
, data_len
, oob
, oob_len
);
87 if (!data
|| !data_len
) {
92 if (!oob
|| !oob_len
) {
97 addr
= ((loff_t
) nand_chunk
) * dev
->param
.total_bytes_per_chunk
;
98 memset(&ops
, 0, sizeof(ops
));
99 ops
.mode
= MTD_OPS_AUTO_OOB
;
100 ops
.len
= (data
) ? data_len
: 0;
101 ops
.ooblen
= oob_len
;
102 ops
.datbuf
= (u8
*)data
;
103 ops
.oobbuf
= (u8
*)oob
;
105 retval
= mtd_write_oob(mtd
, addr
, &ops
);
107 yaffs_trace(YAFFS_TRACE_MTD
,
108 "write_oob failed, chunk %d, mtd error %d",
111 return retval
? YAFFS_FAIL
: YAFFS_OK
;
114 static int yaffs_mtd_read(struct yaffs_dev
*dev
, int nand_chunk
,
115 u8
*data
, int data_len
,
116 u8
*oob
, int oob_len
,
117 enum yaffs_ecc_result
*ecc_result
)
119 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
121 struct mtd_oob_ops ops
;
124 addr
= ((loff_t
) nand_chunk
) * dev
->param
.total_bytes_per_chunk
;
125 memset(&ops
, 0, sizeof(ops
));
126 ops
.mode
= MTD_OPS_AUTO_OOB
;
127 ops
.len
= (data
) ? data_len
: 0;
128 ops
.ooblen
= oob_len
;
132 #if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))
133 /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
134 * help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
136 ops
.len
= (ops
.datbuf
) ? ops
.len
: ops
.ooblen
;
138 /* Read page and oob using MTD.
139 * Check status and determine ECC result.
141 retval
= mtd_read_oob(mtd
, addr
, &ops
);
143 yaffs_trace(YAFFS_TRACE_MTD
,
144 "read_oob failed, chunk %d, mtd error %d",
151 *ecc_result
= YAFFS_ECC_RESULT_NO_ERROR
;
155 /* MTD's ECC fixed the data */
157 *ecc_result
= YAFFS_ECC_RESULT_FIXED
;
163 /* MTD's ECC could not fix the data */
164 dev
->n_ecc_unfixed
++;
166 *ecc_result
= YAFFS_ECC_RESULT_UNFIXED
;
173 static int yaffs_mtd_erase(struct yaffs_dev
*dev
, int block_no
)
175 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
178 struct erase_info ei
;
182 block_size
= dev
->param
.total_bytes_per_chunk
*
183 dev
->param
.chunks_per_block
;
184 addr
= ((loff_t
) block_no
) * block_size
;
192 ei
.priv
= (u_long
) dev
;
194 retval
= mtd_erase(mtd
, &ei
);
202 static int yaffs_mtd_mark_bad(struct yaffs_dev
*dev
, int block_no
)
204 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
205 int blocksize
= dev
->param
.chunks_per_block
* dev
->param
.total_bytes_per_chunk
;
208 yaffs_trace(YAFFS_TRACE_BAD_BLOCKS
, "marking block %d bad", block_no
);
210 retval
= mtd_block_markbad(mtd
, (loff_t
) blocksize
* block_no
);
211 return (retval
) ? YAFFS_FAIL
: YAFFS_OK
;
214 static int yaffs_mtd_check_bad(struct yaffs_dev
*dev
, int block_no
)
216 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
217 int blocksize
= dev
->param
.chunks_per_block
* dev
->param
.total_bytes_per_chunk
;
220 yaffs_trace(YAFFS_TRACE_MTD
, "checking block %d bad", block_no
);
222 retval
= mtd_block_isbad(mtd
, (loff_t
) blocksize
* block_no
);
223 return (retval
) ? YAFFS_FAIL
: YAFFS_OK
;
226 static int yaffs_mtd_initialise(struct yaffs_dev
*dev
)
231 static int yaffs_mtd_deinitialise(struct yaffs_dev
*dev
)
237 void yaffs_mtd_drv_install(struct yaffs_dev
*dev
)
239 struct yaffs_driver
*drv
= &dev
->drv
;
241 drv
->drv_write_chunk_fn
= yaffs_mtd_write
;
242 drv
->drv_read_chunk_fn
= yaffs_mtd_read
;
243 drv
->drv_erase_fn
= yaffs_mtd_erase
;
244 drv
->drv_mark_bad_fn
= yaffs_mtd_mark_bad
;
245 drv
->drv_check_bad_fn
= yaffs_mtd_check_bad
;
246 drv
->drv_initialise_fn
= yaffs_mtd_initialise
;
247 drv
->drv_deinitialise_fn
= yaffs_mtd_deinitialise
;
251 struct mtd_info
* yaffs_get_mtd_device(dev_t sdev
)
253 struct mtd_info
*mtd
;
255 mtd
= yaffs_get_mtd_device(sdev
);
257 /* Check it's an mtd device..... */
258 if (MAJOR(sdev
) != MTD_BLOCK_MAJOR
)
259 return NULL
; /* This isn't an mtd device */
261 /* Check it's NAND */
262 if (mtd
->type
!= MTD_NANDFLASH
) {
263 yaffs_trace(YAFFS_TRACE_ALWAYS
,
264 "yaffs: MTD device is not NAND it's type %d",
269 yaffs_trace(YAFFS_TRACE_OS
, " %s %d", WRITE_SIZE_STR
, WRITE_SIZE(mtd
));
270 yaffs_trace(YAFFS_TRACE_OS
, " oobsize %d", mtd
->oobsize
);
271 yaffs_trace(YAFFS_TRACE_OS
, " erasesize %d", mtd
->erasesize
);
272 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
273 yaffs_trace(YAFFS_TRACE_OS
, " size %u", mtd
->size
);
275 yaffs_trace(YAFFS_TRACE_OS
, " size %lld", mtd
->size
);
281 int yaffs_verify_mtd(struct mtd_info
*mtd
, int yaffs_version
, int inband_tags
)
283 if (yaffs_version
== 2) {
284 if ((WRITE_SIZE(mtd
) < YAFFS_MIN_YAFFS2_CHUNK_SIZE
||
285 mtd
->oobsize
< YAFFS_MIN_YAFFS2_SPARE_SIZE
) &&
287 yaffs_trace(YAFFS_TRACE_ALWAYS
,
288 "MTD device does not have the right page sizes"
293 if (WRITE_SIZE(mtd
) < YAFFS_BYTES_PER_CHUNK
||
294 mtd
->oobsize
!= YAFFS_BYTES_PER_SPARE
) {
295 yaffs_trace(YAFFS_TRACE_ALWAYS
,
296 "MTD device does not support have the right page sizes"
306 void yaffs_put_mtd_device(struct mtd_info
*mtd
)