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