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"
26 #include "yaffs_trace.h"
27 #include "yaffs_guts.h"
28 #include "yaffs_linux.h"
31 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
32 #define MTD_OPS_AUTO_OOB MTD_OOB_AUTO
36 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0))
37 #define mtd_erase(m, ei) (m)->erase(m, ei)
38 #define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops)
39 #define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops)
40 #define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs)
41 #define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs)
46 int nandmtd_erase_block(struct yaffs_dev
*dev
, int block_no
)
48 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
50 ((loff_t
) block_no
) * dev
->param
.total_bytes_per_chunk
*
51 dev
->param
.chunks_per_block
;
57 ei
.len
= dev
->param
.total_bytes_per_chunk
* dev
->param
.chunks_per_block
;
61 ei
.priv
= (u_long
) dev
;
63 retval
= mtd_erase(mtd
, &ei
);
72 static int yaffs_mtd_write(struct yaffs_dev
*dev
, int nand_chunk
,
73 const u8
*data
, int data_len
,
74 const u8
*oob
, int oob_len
)
76 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
78 struct mtd_oob_ops ops
;
81 yaffs_trace(YAFFS_TRACE_MTD
,
82 "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n",
83 dev
, nand_chunk
, data
, data_len
, oob
, oob_len
);
85 if (!data
|| !data_len
) {
90 if (!oob
|| !oob_len
) {
95 addr
= ((loff_t
) nand_chunk
) * dev
->param
.total_bytes_per_chunk
;
96 memset(&ops
, 0, sizeof(ops
));
97 ops
.mode
= MTD_OPS_AUTO_OOB
;
98 ops
.len
= (data
) ? data_len
: 0;
100 ops
.datbuf
= (u8
*)data
;
101 ops
.oobbuf
= (u8
*)oob
;
103 retval
= mtd_write_oob(mtd
, addr
, &ops
);
105 yaffs_trace(YAFFS_TRACE_MTD
,
106 "write_oob failed, chunk %d, mtd error %d",
109 return retval
? YAFFS_FAIL
: YAFFS_OK
;
112 static int yaffs_mtd_read(struct yaffs_dev
*dev
, int nand_chunk
,
113 u8
*data
, int data_len
,
114 u8
*oob
, int oob_len
,
115 enum yaffs_ecc_result
*ecc_result
)
117 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
119 struct mtd_oob_ops ops
;
122 addr
= ((loff_t
) nand_chunk
) * dev
->param
.total_bytes_per_chunk
;
123 memset(&ops
, 0, sizeof(ops
));
124 ops
.mode
= MTD_OPS_AUTO_OOB
;
125 ops
.len
= (data
) ? data_len
: 0;
126 ops
.ooblen
= oob_len
;
130 #if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))
131 /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
132 * help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
134 ops
.len
= (ops
.datbuf
) ? ops
.len
: ops
.ooblen
;
136 /* Read page and oob using MTD.
137 * Check status and determine ECC result.
139 retval
= mtd_read_oob(mtd
, addr
, &ops
);
141 yaffs_trace(YAFFS_TRACE_MTD
,
142 "read_oob failed, chunk %d, mtd error %d",
149 *ecc_result
= YAFFS_ECC_RESULT_NO_ERROR
;
153 /* MTD's ECC fixed the data */
155 *ecc_result
= YAFFS_ECC_RESULT_FIXED
;
161 /* MTD's ECC could not fix the data */
162 dev
->n_ecc_unfixed
++;
164 *ecc_result
= YAFFS_ECC_RESULT_UNFIXED
;
171 static int yaffs_mtd_erase(struct yaffs_dev
*dev
, int block_no
)
173 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
176 struct erase_info ei
;
180 block_size
= dev
->param
.total_bytes_per_chunk
*
181 dev
->param
.chunks_per_block
;
182 addr
= ((loff_t
) block_no
) * block_size
;
190 ei
.priv
= (u_long
) dev
;
192 retval
= mtd_erase(mtd
, &ei
);
200 static int yaffs_mtd_mark_bad(struct yaffs_dev
*dev
, int block_no
)
202 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
203 int blocksize
= dev
->param
.chunks_per_block
* dev
->param
.total_bytes_per_chunk
;
206 yaffs_trace(YAFFS_TRACE_BAD_BLOCKS
, "marking block %d bad", block_no
);
208 retval
= mtd_block_markbad(mtd
, (loff_t
) blocksize
* block_no
);
209 return (retval
) ? YAFFS_FAIL
: YAFFS_OK
;
212 static int yaffs_mtd_check_bad(struct yaffs_dev
*dev
, int block_no
)
214 struct mtd_info
*mtd
= yaffs_dev_to_mtd(dev
);
215 int blocksize
= dev
->param
.chunks_per_block
* dev
->param
.total_bytes_per_chunk
;
218 yaffs_trace(YAFFS_TRACE_MTD
, "checking block %d bad", block_no
);
220 retval
= mtd_block_isbad(mtd
, (loff_t
) blocksize
* block_no
);
221 return (retval
) ? YAFFS_FAIL
: YAFFS_OK
;
224 static int yaffs_mtd_initialise(struct yaffs_dev
*dev
)
229 static int yaffs_mtd_deinitialise(struct yaffs_dev
*dev
)
235 void yaffs_mtd_drv_install(struct yaffs_dev
*dev
)
237 struct yaffs_driver
*drv
= &dev
->drv
;
239 drv
->drv_write_chunk_fn
= yaffs_mtd_write
;
240 drv
->drv_read_chunk_fn
= yaffs_mtd_read
;
241 drv
->drv_erase_fn
= yaffs_mtd_erase
;
242 drv
->drv_mark_bad_fn
= yaffs_mtd_mark_bad
;
243 drv
->drv_check_bad_fn
= yaffs_mtd_check_bad
;
244 drv
->drv_initialise_fn
= yaffs_mtd_initialise
;
245 drv
->drv_deinitialise_fn
= yaffs_mtd_deinitialise
;
249 struct mtd_info
* yaffs_get_mtd_device(dev_t sdev
)
251 struct mtd_info
*mtd
;
253 mtd
= yaffs_get_mtd_device(sdev
);
255 /* Check it's an mtd device..... */
256 if (MAJOR(sdev
) != MTD_BLOCK_MAJOR
)
257 return NULL
; /* This isn't an mtd device */
259 /* Check it's NAND */
260 if (mtd
->type
!= MTD_NANDFLASH
) {
261 yaffs_trace(YAFFS_TRACE_ALWAYS
,
262 "yaffs: MTD device is not NAND it's type %d",
267 yaffs_trace(YAFFS_TRACE_OS
, " %s %d", WRITE_SIZE_STR
, WRITE_SIZE(mtd
));
268 yaffs_trace(YAFFS_TRACE_OS
, " oobsize %d", mtd
->oobsize
);
269 yaffs_trace(YAFFS_TRACE_OS
, " erasesize %d", mtd
->erasesize
);
270 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
271 yaffs_trace(YAFFS_TRACE_OS
, " size %u", mtd
->size
);
273 yaffs_trace(YAFFS_TRACE_OS
, " size %lld", mtd
->size
);
279 int yaffs_verify_mtd(struct mtd_info
*mtd
, int yaffs_version
, int inband_tags
)
281 if (yaffs_version
== 2) {
282 if ((WRITE_SIZE(mtd
) < YAFFS_MIN_YAFFS2_CHUNK_SIZE
||
283 mtd
->oobsize
< YAFFS_MIN_YAFFS2_SPARE_SIZE
) &&
285 yaffs_trace(YAFFS_TRACE_ALWAYS
,
286 "MTD device does not have the right page sizes"
291 if (WRITE_SIZE(mtd
) < YAFFS_BYTES_PER_CHUNK
||
292 mtd
->oobsize
!= YAFFS_BYTES_PER_SPARE
) {
293 yaffs_trace(YAFFS_TRACE_ALWAYS
,
294 "MTD device does not support have the right page sizes"
304 void yaffs_put_mtd_device(struct mtd_info
*mtd
)