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