• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
14 #include "yportenv.h"
15 
16 #include "yaffs_mtdif.h"
17 
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 
26 #include "yaffs_trace.h"
27 #include "yaffs_guts.h"
28 #include "yaffs_linux.h"
29 
30 
31 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
32 #define MTD_OPS_AUTO_OOB MTD_OOB_AUTO
33 #endif
34 
35 
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)
42 #endif
43 
44 
45 
nandmtd_erase_block(struct yaffs_dev * dev,int block_no)46 int nandmtd_erase_block(struct yaffs_dev *dev, int block_no)
47 {
48 	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
49 	u32 addr =
50 	    ((loff_t) block_no) * dev->param.total_bytes_per_chunk *
51 	    dev->param.chunks_per_block;
52 	struct erase_info ei;
53 	int retval = 0;
54 
55 	ei.mtd = mtd;
56 	ei.addr = addr;
57 	ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block;
58 	ei.time = 1000;
59 	ei.retries = 2;
60 	ei.callback = NULL;
61 	ei.priv = (u_long) dev;
62 
63 	retval = mtd_erase(mtd, &ei);
64 
65 	if (retval == 0)
66 		return YAFFS_OK;
67 
68 	return YAFFS_FAIL;
69 }
70 
71 
yaffs_mtd_write(struct yaffs_dev * dev,int nand_chunk,const u8 * data,int data_len,const u8 * oob,int oob_len)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)
75 {
76 	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
77 	loff_t addr;
78 	struct mtd_oob_ops ops;
79 	int retval;
80 
81 	addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
82 	memset(&ops, 0, sizeof(ops));
83 	ops.mode = MTD_OPS_AUTO_OOB;
84 	ops.len = (data) ? data_len : 0;
85 	ops.ooblen = oob_len;
86 	ops.datbuf = (u8 *)data;
87 	ops.oobbuf = (u8 *)oob;
88 
89 	retval = mtd_write_oob(mtd, addr, &ops);
90 	if (retval) {
91 		yaffs_trace(YAFFS_TRACE_MTD,
92 			"write_oob failed, chunk %d, mtd error %d",
93 			nand_chunk, retval);
94 	}
95 	return retval ? YAFFS_FAIL : YAFFS_OK;
96 }
97 
yaffs_mtd_read(struct yaffs_dev * dev,int nand_chunk,u8 * data,int data_len,u8 * oob,int oob_len,enum yaffs_ecc_result * ecc_result)98 static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk,
99 				   u8 *data, int data_len,
100 				   u8 *oob, int oob_len,
101 				   enum yaffs_ecc_result *ecc_result)
102 {
103 	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
104 	loff_t addr;
105 	struct mtd_oob_ops ops;
106 	int retval;
107 
108 	addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
109 	memset(&ops, 0, sizeof(ops));
110 	ops.mode = MTD_OPS_AUTO_OOB;
111 	ops.len = (data) ? data_len : 0;
112 	ops.ooblen = oob_len;
113 	ops.datbuf = data;
114 	ops.oobbuf = oob;
115 
116 #if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))
117 	/* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
118 	 * help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
119 	 */
120 	ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
121 #endif
122 	/* Read page and oob using MTD.
123 	 * Check status and determine ECC result.
124 	 */
125 	retval = mtd_read_oob(mtd, addr, &ops);
126 	if (retval)
127 		yaffs_trace(YAFFS_TRACE_MTD,
128 			"read_oob failed, chunk %d, mtd error %d",
129 			nand_chunk, retval);
130 
131 	switch (retval) {
132 	case 0:
133 		/* no error */
134 		if(ecc_result)
135 			*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
136 		break;
137 
138 	case -EUCLEAN:
139 		/* MTD's ECC fixed the data */
140 		if(ecc_result)
141 			*ecc_result = YAFFS_ECC_RESULT_FIXED;
142 		dev->n_ecc_fixed++;
143 		break;
144 
145 	case -EBADMSG:
146 	default:
147 		/* MTD's ECC could not fix the data */
148 		dev->n_ecc_unfixed++;
149 		if(ecc_result)
150 			*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
151 		return YAFFS_FAIL;
152 	}
153 
154 	return YAFFS_OK;
155 }
156 
yaffs_mtd_erase(struct yaffs_dev * dev,int block_no)157 static 	int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)
158 {
159 	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
160 
161 	loff_t addr;
162 	struct erase_info ei;
163 	int retval = 0;
164 	u32 block_size;
165 
166 	block_size = dev->param.total_bytes_per_chunk *
167 		     dev->param.chunks_per_block;
168 	addr = ((loff_t) block_no) * block_size;
169 
170 	ei.mtd = mtd;
171 	ei.addr = addr;
172 	ei.len = block_size;
173 	ei.time = 1000;
174 	ei.retries = 2;
175 	ei.callback = NULL;
176 	ei.priv = (u_long) dev;
177 
178 	retval = mtd_erase(mtd, &ei);
179 
180 	if (retval == 0)
181 		return YAFFS_OK;
182 
183 	return YAFFS_FAIL;
184 }
185 
yaffs_mtd_mark_bad(struct yaffs_dev * dev,int block_no)186 static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)
187 {
188 	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
189 	int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
190 	int retval;
191 
192 	yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);
193 
194 	retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no);
195 	return (retval) ? YAFFS_FAIL : YAFFS_OK;
196 }
197 
yaffs_mtd_check_bad(struct yaffs_dev * dev,int block_no)198 static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)
199 {
200 	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
201 	int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
202 	int retval;
203 
204 	yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "checking block %d bad", block_no);
205 
206 	retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no);
207 	return (retval) ? YAFFS_FAIL : YAFFS_OK;
208 }
209 
yaffs_mtd_initialise(struct yaffs_dev * dev)210 static int yaffs_mtd_initialise(struct yaffs_dev *dev)
211 {
212 	return YAFFS_OK;
213 }
214 
yaffs_mtd_deinitialise(struct yaffs_dev * dev)215 static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)
216 {
217 	return YAFFS_OK;
218 }
219 
220 
yaffs_mtd_drv_install(struct yaffs_dev * dev)221 void yaffs_mtd_drv_install(struct yaffs_dev *dev)
222 {
223 	struct yaffs_driver *drv = &dev->drv;
224 
225 	drv->drv_write_chunk_fn = yaffs_mtd_write;
226 	drv->drv_read_chunk_fn = yaffs_mtd_read;
227 	drv->drv_erase_fn = yaffs_mtd_erase;
228 	drv->drv_mark_bad_fn = yaffs_mtd_mark_bad;
229 	drv->drv_check_bad_fn = yaffs_mtd_check_bad;
230 	drv->drv_initialise_fn = yaffs_mtd_initialise;
231 	drv->drv_deinitialise_fn = yaffs_mtd_deinitialise;
232 }
233 
234 
yaffs_get_mtd_device(dev_t sdev)235 struct mtd_info * yaffs_get_mtd_device(dev_t sdev)
236 {
237 	struct mtd_info *mtd;
238 
239 	mtd = yaffs_get_mtd_device(sdev);
240 
241 	/* Check it's an mtd device..... */
242 	if (MAJOR(sdev) != MTD_BLOCK_MAJOR)
243 		return NULL;	/* This isn't an mtd device */
244 
245 	/* Check it's NAND */
246 	if (mtd->type != MTD_NANDFLASH) {
247 		yaffs_trace(YAFFS_TRACE_ALWAYS,
248 			"yaffs: MTD device is not NAND it's type %d",
249 			mtd->type);
250 		return NULL;
251 	}
252 
253 	yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd));
254 	yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize);
255 	yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize);
256 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
257 	yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size);
258 #else
259 	yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size);
260 #endif
261 
262 	return mtd;
263 }
264 
yaffs_verify_mtd(struct mtd_info * mtd,int yaffs_version,int inband_tags)265 int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
266 {
267 	if (yaffs_version == 2) {
268 		if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
269 		     mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) &&
270 		    !inband_tags) {
271 			yaffs_trace(YAFFS_TRACE_ALWAYS,
272 				"MTD device does not have the right page sizes"
273 			);
274 			return -1;
275 		}
276 	} else {
277 		if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK ||
278 		    mtd->oobsize != YAFFS_BYTES_PER_SPARE) {
279 			yaffs_trace(YAFFS_TRACE_ALWAYS,
280 				"MTD device does not support have the right page sizes"
281 			);
282 			return -1;
283 		}
284 	}
285 
286 	return 0;
287 }
288 
289 
yaffs_put_mtd_device(struct mtd_info * mtd)290 void yaffs_put_mtd_device(struct mtd_info *mtd)
291 {
292 	if(mtd)
293 		put_mtd_device(mtd);
294 }
295