Add back temporarily disabled ECC in nand driver
[yaffs2.git] / direct / test-framework / yaffs_nand_drv.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 /*
15  * This is an interface module for handling NAND in yaffs2 mode.
16  */
17
18 /* This code calls a driver that accesses "generic" NAND. In the simulator
19  * this is direceted at a file-backed NAND simulator. The NAND access functions
20  * should also work with real NAND.
21  *
22  * This driver is designed for use in yaffs2 mode with a 2k page size and
23  * 64 bytes of spare.
24  *
25  * The spare ares is used as follows:
26  * offset 0: 2 bytes bad block marker.
27  * offset 2: 8x3 bytes of ECC over the data.
28  * offset 26: rest available to store Yaffs tags etc.
29  */
30
31 #include "yaffs_nand_drv.h"
32 #include "yportenv.h"
33 #include "yaffs_trace.h"
34
35 #include "nand_store.h"
36 #include "yaffs_flashif.h"
37 #include "yaffs_guts.h"
38 #include "nanddrv.h"
39 #include "yaffs_ecc.h"
40
41 struct nand_context {
42         struct nand_chip *chip;
43         u8 *buffer;
44 };
45
46
47 static inline struct nand_chip *dev_to_chip(struct yaffs_dev *dev)
48 {
49         struct nand_context *ctxt =
50                 (struct nand_context *)(dev->driver_context);
51         return ctxt->chip;
52 }
53
54 static inline u8 *dev_to_buffer(struct yaffs_dev *dev)
55 {
56         struct nand_context *ctxt =
57                 (struct nand_context *)(dev->driver_context);
58         return ctxt->buffer;
59 }
60
61 static int yaffs_nand_drv_WriteChunk(struct yaffs_dev *dev, int nand_chunk,
62                                    const u8 *data, int data_len,
63                                    const u8 *oob, int oob_len)
64 {
65         struct nand_chip *chip = dev_to_chip(dev);
66         u8 *buffer = dev_to_buffer(dev);
67         u8 *e;
68         struct nanddrv_transfer tr[2];
69         int i;
70
71         if(!data || !oob)
72                 return YAFFS_FAIL;
73
74
75         /* Calc ECC and marshall the oob bytes into the buffer */
76
77         memset(buffer, 0xff, chip->spare_bytes_per_page);
78
79         for(i = 0, e = buffer + 2; i < chip->data_bytes_per_page; i+=256, e+=3)
80                 yaffs_ecc_calc(data + i, e);
81
82         memcpy(buffer + 26, oob, oob_len);
83
84         /* Set up and execute transfer */
85
86         tr[0].buffer = data;
87         tr[0].offset = 0;
88         tr[0].nbytes = data_len;
89
90         tr[1].buffer = buffer;
91         tr[1].offset = chip->data_bytes_per_page;
92         tr[1].nbytes = chip->spare_bytes_per_page;
93
94         if(nanddrv_write_tr(chip, nand_chunk, tr, 2) == 0)
95                 return YAFFS_OK;
96         else
97                 return YAFFS_FAIL;
98 }
99
100 static int yaffs_nand_drv_ReadChunk(struct yaffs_dev *dev, int nand_chunk,
101                                    u8 *data, int data_len,
102                                    u8 *oob, int oob_len,
103                                    enum yaffs_ecc_result *ecc_result_out)
104 {
105         struct nand_chip *chip = dev_to_chip(dev);
106         u8 *buffer = dev_to_buffer(dev);
107         struct nanddrv_transfer tr[2];
108         struct nanddrv_transfer *trp = tr;
109         int n_tr = 0;
110         int ret;
111         enum yaffs_ecc_result ecc_result;
112         int i;
113         u8 *e;
114         u8 read_ecc[3];
115
116         if(data) {
117                 trp->buffer = data;
118                 trp->offset = 0;
119                 trp->nbytes = data_len;
120                 n_tr++;
121                 trp++;
122         }
123
124
125         trp->buffer = buffer;
126         trp->offset = chip->data_bytes_per_page;
127         trp->nbytes = chip->spare_bytes_per_page;
128         n_tr++;
129         trp++;
130
131
132         ret = nanddrv_read_tr(chip, nand_chunk, tr, n_tr);
133
134         if(ret < 0) {
135                 if (ecc_result_out)
136                         *ecc_result_out = YAFFS_ECC_RESULT_UNKNOWN;
137                 return YAFFS_FAIL;
138         }
139
140         /* Do ECC and marshalling */
141         if(oob)
142                 memcpy(oob, buffer + 26, oob_len);
143
144         ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
145
146         if(data) {
147                 for(i = 0, e = buffer + 2; i < chip->data_bytes_per_page; i+=256, e+=3) {
148                         yaffs_ecc_calc(data + i, read_ecc);
149                         ret = yaffs_ecc_correct(data + i, e, read_ecc);
150                         if (ret < 0)
151                                 ecc_result = YAFFS_ECC_RESULT_UNFIXED;
152                         else if( ret > 0 && ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
153                                 ecc_result = YAFFS_ECC_RESULT_FIXED;
154                 }
155         }
156
157         if (ecc_result_out)
158                 *ecc_result_out = ecc_result;
159
160         return YAFFS_OK;
161 }
162
163 static int yaffs_nand_drv_EraseBlock(struct yaffs_dev *dev, int block_no)
164 {
165         struct nand_chip *chip = dev_to_chip(dev);
166
167         if(nanddrv_erase(chip, block_no) == 0)
168                 return YAFFS_OK;
169         else
170                 return YAFFS_FAIL;
171 }
172
173 static int yaffs_nand_drv_MarkBad(struct yaffs_dev *dev, int block_no)
174 {
175         struct nand_chip *chip = dev_to_chip(dev);
176         u8 *buffer = dev_to_buffer(dev);
177         int nand_chunk = block_no * chip->pages_per_block;
178         struct nanddrv_transfer tr[1];
179
180         memset(buffer, 0xff, chip->spare_bytes_per_page);
181         buffer[0] = 'Y';
182         buffer[1] = 'Y';
183
184         tr[0].buffer = buffer;
185         tr[0].offset = chip->data_bytes_per_page;
186         tr[0].nbytes = chip->spare_bytes_per_page;
187
188         if(nanddrv_write_tr(chip, nand_chunk, tr, 1) == 0)
189                 return YAFFS_OK;
190         else
191                 return YAFFS_FAIL;
192 }
193
194 static int yaffs_nand_drv_CheckBad(struct yaffs_dev *dev, int block_no)
195 {
196         struct nand_chip *chip = dev_to_chip(dev);
197         u8 *buffer = dev_to_buffer(dev);
198         int nand_chunk = block_no * chip->pages_per_block;
199         int ret;
200
201         struct nanddrv_transfer tr[1];
202
203         memset(buffer, 0, chip->spare_bytes_per_page);
204
205         tr[0].buffer = buffer;
206         tr[0].offset = chip->data_bytes_per_page;
207         tr[0].nbytes = chip->spare_bytes_per_page;
208
209         ret = nanddrv_read_tr(chip, nand_chunk, tr, 1);
210
211         /* Check that bad block marker is not set */
212         if(yaffs_hweight8(buffer[0]) + yaffs_hweight8(buffer[1]) < 14)
213                 return YAFFS_FAIL;
214         else
215                 return YAFFS_OK;
216
217 }
218
219 static int yaffs_nand_drv_Initialise(struct yaffs_dev *dev)
220 {
221         struct nand_chip *chip = dev_to_chip(dev);
222
223         return YAFFS_OK;
224 }
225
226 static int yaffs_nand_drv_Deinitialise(struct yaffs_dev *dev)
227 {
228         struct nand_chip *chip = dev_to_chip(dev);
229
230         return YAFFS_OK;
231 }
232
233 #include "nandsim_file.h"
234
235 struct yaffs_dev *yaffs_nandsim_install_drv(const char *name,
236                                         const char *file_name,
237                                         int n_blocks)
238 {
239         struct yaffs_dev *dev;
240         char *name_copy = NULL;
241         struct yaffs_param *param;
242         struct yaffs_driver *drv;
243         struct nand_chip *chip = NULL;
244         struct nand_context *ctxt = NULL;
245         u8 *buffer = NULL;
246
247         dev = malloc(sizeof(struct yaffs_dev));
248         ctxt = malloc(sizeof(struct nand_context));
249         name_copy = strdup(name);
250
251         if(!dev || !ctxt || !name_copy)
252                 goto fail;
253
254         chip = nandsim_file_init(file_name, n_blocks, 64, 2048, 64, 0);
255         if(!chip)
256                 goto fail;
257
258         buffer = malloc(chip->spare_bytes_per_page);
259
260         if(!buffer)
261                 goto fail;
262
263         param = &dev->param;
264         drv = &dev->drv;
265
266         memset(dev, 0, sizeof(*dev));
267         memset(ctxt, 0, sizeof(*ctxt));
268
269         param->name = name_copy;
270
271         param->total_bytes_per_chunk = chip->data_bytes_per_page;
272         param->chunks_per_block = chip->pages_per_block;
273         param->n_reserved_blocks = 5;
274         param->start_block = 0; // Can use block 0
275         param->end_block = chip->blocks - 1; // Last block
276         param->is_yaffs2 = 1;
277         param->use_nand_ecc = 1;
278         param->n_caches = 10;
279
280         drv->drv_write_chunk_fn = yaffs_nand_drv_WriteChunk;
281         drv->drv_read_chunk_fn = yaffs_nand_drv_ReadChunk;
282         drv->drv_erase_fn = yaffs_nand_drv_EraseBlock;
283         drv->drv_mark_bad_fn = yaffs_nand_drv_MarkBad;
284         drv->drv_check_bad_fn = yaffs_nand_drv_CheckBad;
285         drv->drv_initialise_fn = yaffs_nand_drv_Initialise;
286         drv->drv_deinitialise_fn = yaffs_nand_drv_Deinitialise;
287
288         ctxt->chip = chip;
289         ctxt->buffer = buffer;
290         dev->driver_context = (void *) ctxt;
291
292         yaffs_add_device(dev);
293
294         return dev;
295
296 fail:
297         free(dev);
298         free(ctxt);
299         free(name_copy);
300         free(buffer);
301         return NULL;
302 }