Clean up obsolete CVS version stamps and some other messiness.
[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 int nandmtd_erase_block(struct yaffs_dev *dev, int block_no)
37 {
38         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
39         u32 addr =
40             ((loff_t) block_no) * dev->param.total_bytes_per_chunk *
41             dev->param.chunks_per_block;
42         struct erase_info ei;
43         int retval = 0;
44
45         ei.mtd = mtd;
46         ei.addr = addr;
47         ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block;
48         ei.time = 1000;
49         ei.retries = 2;
50         ei.callback = NULL;
51         ei.priv = (u_long) dev;
52
53         retval = mtd->erase(mtd, &ei);
54
55         if (retval == 0)
56                 return YAFFS_OK;
57
58         return YAFFS_FAIL;
59 }
60
61
62 static  int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk,
63                                    const u8 *data, int data_len,
64                                    const u8 *oob, int oob_len)
65 {
66         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
67         loff_t addr;
68         struct mtd_oob_ops ops;
69         int retval;
70
71         addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
72         memset(&ops, 0, sizeof(ops));
73         ops.mode = MTD_OPS_AUTO_OOB;
74         ops.len = (data) ? data_len : 0;
75         ops.ooblen = oob_len;
76         ops.datbuf = (u8 *)data;
77         ops.oobbuf = (u8 *)oob;
78
79         retval = mtd->write_oob(mtd, addr, &ops);
80         if (retval) {
81                 yaffs_trace(YAFFS_TRACE_MTD,
82                         "write_oob failed, chunk %d, mtd error %d",
83                         nand_chunk, retval);
84         }
85         return retval ? YAFFS_FAIL : YAFFS_OK;
86 }
87
88 static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk,
89                                    u8 *data, int data_len,
90                                    u8 *oob, int oob_len,
91                                    enum yaffs_ecc_result *ecc_result)
92 {
93         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
94         loff_t addr;
95         struct mtd_oob_ops ops;
96         int retval;
97
98         addr = ((loff_t) nand_chunk) * dev->data_bytes_per_chunk;
99         memset(&ops, 0, sizeof(ops));
100         ops.mode = MTD_OPS_AUTO_OOB;
101         ops.len = (data) ? data_len : 0;
102         ops.ooblen = oob_len;
103         ops.datbuf = data;
104         ops.oobbuf = oob;
105
106 #if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20))
107         /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
108          * help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
109          */
110         ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
111 #endif
112         /* Read page and oob using MTD.
113          * Check status and determine ECC result.
114          */
115         retval = mtd->read_oob(mtd, addr, &ops);
116         if (retval)
117                 yaffs_trace(YAFFS_TRACE_MTD,
118                         "read_oob failed, chunk %d, mtd error %d",
119                         nand_chunk, retval);
120
121         switch (retval) {
122         case 0:
123                 /* no error */
124                 if(ecc_result)
125                         *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
126                 break;
127
128         case -EUCLEAN:
129                 /* MTD's ECC fixed the data */
130                 if(ecc_result)
131                         *ecc_result = YAFFS_ECC_RESULT_FIXED;
132                 dev->n_ecc_fixed++;
133                 break;
134
135         case -EBADMSG:
136         default:
137                 /* MTD's ECC could not fix the data */
138                 dev->n_ecc_unfixed++;
139                 if(ecc_result)
140                         *ecc_result = YAFFS_ECC_RESULT_UNFIXED;
141                 return YAFFS_FAIL;
142         }
143
144         return YAFFS_OK;
145 }
146
147 static  int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no)
148 {
149         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
150
151         loff_t addr;
152         struct erase_info ei;
153         int retval = 0;
154         u32 block_size;
155
156         block_size = dev->param.total_bytes_per_chunk *
157                      dev->param.chunks_per_block;
158         addr = ((loff_t) block_no) * block_size;
159
160         ei.mtd = mtd;
161         ei.addr = addr;
162         ei.len = block_size;
163         ei.time = 1000;
164         ei.retries = 2;
165         ei.callback = NULL;
166         ei.priv = (u_long) dev;
167
168         retval = mtd->erase(mtd, &ei);
169
170         if (retval == 0)
171                 return YAFFS_OK;
172
173         return YAFFS_FAIL;
174 }
175
176 static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no)
177 {
178         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
179         int blocksize = dev->param.chunks_per_block * dev->data_bytes_per_chunk;
180         int retval;
181
182         yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no);
183
184         retval = mtd->block_markbad(mtd, (loff_t) blocksize * block_no);
185         return (retval) ? YAFFS_FAIL : YAFFS_OK;
186 }
187
188 static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no)
189 {
190         struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
191         int blocksize = dev->param.chunks_per_block * dev->data_bytes_per_chunk;
192         int retval;
193
194         yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "checking block %d bad", block_no);
195
196         retval = mtd->block_isbad(mtd, (loff_t) blocksize * block_no);
197         return (retval) ? YAFFS_FAIL : YAFFS_OK;
198 }
199
200 static int yaffs_mtd_initialise(struct yaffs_dev *dev)
201 {
202         return YAFFS_OK;
203 }
204
205 static int yaffs_mtd_deinitialise(struct yaffs_dev *dev)
206 {
207         return YAFFS_OK;
208 }
209
210
211
212 void yaffs_mtd_drv_install(struct yaffs_dev *dev)
213 {
214         struct yaffs_param *param = &dev->param;
215
216         param->drv_write_chunk_fn = yaffs_mtd_write;
217         param->drv_read_chunk_fn = yaffs_mtd_read;
218         param->drv_erase_fn = yaffs_mtd_erase;
219         param->drv_mark_bad_fn = yaffs_mtd_mark_bad;
220         param->drv_check_bad_fn = yaffs_mtd_check_bad;
221         param->drv_initialise_fn = yaffs_mtd_initialise;
222         param->drv_deinitialise_fn = yaffs_mtd_deinitialise;
223 }