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