yaffs: Clean up bitfield types
[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
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
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
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         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);
84
85         if (!data || !data_len) {
86                 data = NULL;
87                 data_len = 0;
88         }
89
90         if (!oob || !oob_len) {
91                 oob = NULL;
92                 oob_len = 0;
93         }
94
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;
99         ops.ooblen = oob_len;
100         ops.datbuf = (u8 *)data;
101         ops.oobbuf = (u8 *)oob;
102
103         retval = mtd_write_oob(mtd, addr, &ops);
104         if (retval) {
105                 yaffs_trace(YAFFS_TRACE_MTD,
106                         "write_oob failed, chunk %d, mtd error %d",
107                         nand_chunk, retval);
108         }
109         return retval ? YAFFS_FAIL : YAFFS_OK;
110 }
111
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)
116 {
117         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
118         loff_t addr;
119         struct mtd_oob_ops ops;
120         int retval;
121
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;
127         ops.datbuf = data;
128         ops.oobbuf = oob;
129
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.
133          */
134         ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
135 #endif
136         /* Read page and oob using MTD.
137          * Check status and determine ECC result.
138          */
139         retval = mtd_read_oob(mtd, addr, &ops);
140         if (retval)
141                 yaffs_trace(YAFFS_TRACE_MTD,
142                         "read_oob failed, chunk %d, mtd error %d",
143                         nand_chunk, retval);
144
145         switch (retval) {
146         case 0:
147                 /* no error */
148                 if(ecc_result)
149                         *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
150                 break;
151
152         case -EUCLEAN:
153                 /* MTD's ECC fixed the data */
154                 if(ecc_result)
155                         *ecc_result = YAFFS_ECC_RESULT_FIXED;
156                 dev->n_ecc_fixed++;
157                 break;
158
159         case -EBADMSG:
160         default:
161                 /* MTD's ECC could not fix the data */
162                 dev->n_ecc_unfixed++;
163                 if(ecc_result)
164                         *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
165                 return YAFFS_FAIL;
166         }
167
168         return YAFFS_OK;
169 }
170
171 static  int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)
172 {
173         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
174
175         loff_t addr;
176         struct erase_info ei;
177         int retval = 0;
178         u32 block_size;
179
180         block_size = dev->param.total_bytes_per_chunk *
181                      dev->param.chunks_per_block;
182         addr = ((loff_t) block_no) * block_size;
183
184         ei.mtd = mtd;
185         ei.addr = addr;
186         ei.len = block_size;
187         ei.time = 1000;
188         ei.retries = 2;
189         ei.callback = NULL;
190         ei.priv = (u_long) dev;
191
192         retval = mtd_erase(mtd, &ei);
193
194         if (retval == 0)
195                 return YAFFS_OK;
196
197         return YAFFS_FAIL;
198 }
199
200 static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)
201 {
202         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
203         int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
204         int retval;
205
206         yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);
207
208         retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no);
209         return (retval) ? YAFFS_FAIL : YAFFS_OK;
210 }
211
212 static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)
213 {
214         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
215         int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk;
216         int retval;
217
218         yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no);
219
220         retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no);
221         return (retval) ? YAFFS_FAIL : YAFFS_OK;
222 }
223
224 static int yaffs_mtd_initialise(struct yaffs_dev *dev)
225 {
226         return YAFFS_OK;
227 }
228
229 static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)
230 {
231         return YAFFS_OK;
232 }
233
234
235 void yaffs_mtd_drv_install(struct yaffs_dev *dev)
236 {
237         struct yaffs_driver *drv = &dev->drv;
238
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;
246 }
247
248
249 struct mtd_info * yaffs_get_mtd_device(dev_t sdev)
250 {
251         struct mtd_info *mtd;
252
253         mtd = yaffs_get_mtd_device(sdev);
254
255         /* Check it's an mtd device..... */
256         if (MAJOR(sdev) != MTD_BLOCK_MAJOR)
257                 return NULL;    /* This isn't an mtd device */
258
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",
263                         mtd->type);
264                 return NULL;
265         }
266
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);
272 #else
273         yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size);
274 #endif
275
276         return mtd;
277 }
278
279 int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags)
280 {
281         if (yaffs_version == 2) {
282                 if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
283                      mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) &&
284                     !inband_tags) {
285                         yaffs_trace(YAFFS_TRACE_ALWAYS,
286                                 "MTD device does not have the right page sizes"
287                         );
288                         return -1;
289                 }
290         } else {
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"
295                         );
296                         return -1;
297                 }
298         }
299
300         return 0;
301 }
302
303
304 void yaffs_put_mtd_device(struct mtd_info *mtd)
305 {
306         if(mtd)
307                 put_mtd_device(mtd);
308 }