Further extensions for VxWorks wrapper
[yaffs2.git] / yaffs_mtdif_multi.c
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 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
26 #include "uapi/linux/major.h"
27 #endif
28
29 #include "yaffs_trace.h"
30 #include "yaffs_guts.h"
31 #include "yaffs_linux.h"
32
33 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
34 #define MTD_OPS_AUTO_OOB MTD_OOB_AUTO
35 #endif
36
37
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)
44 #endif
45
46
47
48 int nandmtd_erase_block(struct yaffs_dev *dev, int block_no)
49 {
50         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
51         u32 addr =
52             ((loff_t) block_no) * dev->param.total_bytes_per_chunk *
53             dev->param.chunks_per_block;
54         struct erase_info ei;
55         int retval = 0;
56
57         ei.mtd = mtd;
58         ei.addr = addr;
59         ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block;
60         ei.time = 1000;
61         ei.retries = 2;
62         ei.callback = NULL;
63         ei.priv = (u_long) dev;
64
65         retval = mtd_erase(mtd, &ei);
66
67         if (retval == 0)
68                 return YAFFS_OK;
69
70         return YAFFS_FAIL;
71 }
72
73
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)
77 {
78         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
79         loff_t addr;
80         struct mtd_oob_ops ops;
81         int retval;
82
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);
86
87         if (!data || !data_len) {
88                 data = NULL;
89                 data_len = 0;
90         }
91
92         if (!oob || !oob_len) {
93                 oob = NULL;
94                 oob_len = 0;
95         }
96
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;
104
105         retval = mtd_write_oob(mtd, addr, &ops);
106         if (retval) {
107                 yaffs_trace(YAFFS_TRACE_MTD,
108                         "write_oob failed, chunk %d, mtd error %d",
109                         nand_chunk, retval);
110         }
111         return retval ? YAFFS_FAIL : YAFFS_OK;
112 }
113
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)
118 {
119         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
120         loff_t addr;
121         struct mtd_oob_ops ops;
122         int retval;
123
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;
129         ops.datbuf = data;
130         ops.oobbuf = oob;
131
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.
135          */
136         ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
137 #endif
138         /* Read page and oob using MTD.
139          * Check status and determine ECC result.
140          */
141         retval = mtd_read_oob(mtd, addr, &ops);
142         if (retval)
143                 yaffs_trace(YAFFS_TRACE_MTD,
144                         "read_oob failed, chunk %d, mtd error %d",
145                         nand_chunk, retval);
146
147         switch (retval) {
148         case 0:
149                 /* no error */
150                 if(ecc_result)
151                         *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
152                 break;
153
154         case -EUCLEAN:
155                 /* MTD's ECC fixed the data */
156                 if(ecc_result)
157                         *ecc_result = YAFFS_ECC_RESULT_FIXED;
158                 dev->n_ecc_fixed++;
159                 break;
160
161         case -EBADMSG:
162         default:
163                 /* MTD's ECC could not fix the data */
164                 dev->n_ecc_unfixed++;
165                 if(ecc_result)
166                         *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
167                 return YAFFS_FAIL;
168         }
169
170         return YAFFS_OK;
171 }
172
173 static  int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)
174 {
175         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
176
177         loff_t addr;
178         struct erase_info ei;
179         int retval = 0;
180         u32 block_size;
181
182         block_size = dev->param.total_bytes_per_chunk *
183                      dev->param.chunks_per_block;
184         addr = ((loff_t) block_no) * block_size;
185
186         ei.mtd = mtd;
187         ei.addr = addr;
188         ei.len = block_size;
189         ei.time = 1000;
190         ei.retries = 2;
191         ei.callback = NULL;
192         ei.priv = (u_long) dev;
193
194         retval = mtd_erase(mtd, &ei);
195
196         if (retval == 0)
197                 return YAFFS_OK;
198
199         return YAFFS_FAIL;
200 }
201
202 static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)
203 {
204         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
205         int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
206         int retval;
207
208         yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);
209
210         retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no);
211         return (retval) ? YAFFS_FAIL : YAFFS_OK;
212 }
213
214 static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)
215 {
216         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
217         int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
218         int retval;
219
220         yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no);
221
222         retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no);
223         return (retval) ? YAFFS_FAIL : YAFFS_OK;
224 }
225
226 static int yaffs_mtd_initialise(struct yaffs_dev *dev)
227 {
228         return YAFFS_OK;
229 }
230
231 static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)
232 {
233         return YAFFS_OK;
234 }
235
236
237 void yaffs_mtd_drv_install(struct yaffs_dev *dev)
238 {
239         struct yaffs_driver *drv = &dev->drv;
240
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;
248 }
249
250
251 struct mtd_info * yaffs_get_mtd_device(dev_t sdev)
252 {
253         struct mtd_info *mtd;
254
255         mtd = yaffs_get_mtd_device(sdev);
256
257         /* Check it's an mtd device..... */
258         if (MAJOR(sdev) != MTD_BLOCK_MAJOR)
259                 return NULL;    /* This isn't an mtd device */
260
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",
265                         mtd->type);
266                 return NULL;
267         }
268
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);
274 #else
275         yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size);
276 #endif
277
278         return mtd;
279 }
280
281 int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
282 {
283         if (yaffs_version == 2) {
284                 if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
285                      mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) &&
286                     !inband_tags) {
287                         yaffs_trace(YAFFS_TRACE_ALWAYS,
288                                 "MTD device does not have the right page sizes"
289                         );
290                         return -1;
291                 }
292         } else {
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"
297                         );
298                         return -1;
299                 }
300         }
301
302         return 0;
303 }
304
305
306 void yaffs_put_mtd_device(struct mtd_info *mtd)
307 {
308         if(mtd)
309                 put_mtd_device(mtd);
310 }