Merge branch 'master' of ssh://www.aleph1.co.uk/home/aleph1/git/yaffs2
[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 0
147         if(data) {
148                 for(i = 0, e = buffer + 2; i < chip->data_bytes_per_page; i+=256, e+=3) {
149                         yaffs_ecc_calc(data + i, read_ecc);
150                         ret = yaffs_ecc_correct(data + i, e, read_ecc);
151                         if (ret < 0)
152                                 ecc_result = YAFFS_ECC_RESULT_UNFIXED;
153                         else if( ret > 0 && ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
154                                 ecc_result = YAFFS_ECC_RESULT_FIXED;
155                 }
156         }
157 #endif
158
159         if (ecc_result_out)
160                 *ecc_result_out = ecc_result;
161
162         return YAFFS_OK;
163 }
164
165 static int yaffs_nand_drv_EraseBlock(struct yaffs_dev *dev, int block_no)
166 {
167         struct nand_chip *chip = dev_to_chip(dev);
168
169         if(nanddrv_erase(chip, block_no) == 0)
170                 return YAFFS_OK;
171         else
172                 return YAFFS_FAIL;
173 }
174
175 static int yaffs_nand_drv_MarkBad(struct yaffs_dev *dev, int block_no)
176 {
177         struct nand_chip *chip = dev_to_chip(dev);
178         u8 *buffer = dev_to_buffer(dev);
179         int nand_chunk = block_no * chip->pages_per_block;
180         struct nanddrv_transfer tr[1];
181
182         memset(buffer, 0xff, chip->spare_bytes_per_page);
183         buffer[0] = 'Y';
184         buffer[1] = 'Y';
185
186         tr[0].buffer = buffer;
187         tr[0].offset = chip->data_bytes_per_page;
188         tr[0].nbytes = chip->spare_bytes_per_page;
189
190         if(nanddrv_write_tr(chip, nand_chunk, tr, 1) == 0)
191                 return YAFFS_OK;
192         else
193                 return YAFFS_FAIL;
194 }
195
196 static int yaffs_nand_drv_CheckBad(struct yaffs_dev *dev, int block_no)
197 {
198         struct nand_chip *chip = dev_to_chip(dev);
199         u8 *buffer = dev_to_buffer(dev);
200         int nand_chunk = block_no * chip->pages_per_block;
201         int ret;
202
203         struct nanddrv_transfer tr[1];
204
205         memset(buffer, 0, chip->spare_bytes_per_page);
206
207         tr[0].buffer = buffer;
208         tr[0].offset = chip->data_bytes_per_page;
209         tr[0].nbytes = chip->spare_bytes_per_page;
210
211         ret = nanddrv_read_tr(chip, nand_chunk, tr, 1);
212
213         /* Check that bad block marker is not set */
214         if(yaffs_hweight8(buffer[0]) + yaffs_hweight8(buffer[1]) < 14)
215                 return YAFFS_FAIL;
216         else
217                 return YAFFS_OK;
218
219 }
220
221 static int yaffs_nand_drv_Initialise(struct yaffs_dev *dev)
222 {
223         struct nand_chip *chip = dev_to_chip(dev);
224
225         return YAFFS_OK;
226 }
227
228 static int yaffs_nand_drv_Deinitialise(struct yaffs_dev *dev)
229 {
230         struct nand_chip *chip = dev_to_chip(dev);
231
232         return YAFFS_OK;
233 }
234
235 #include "nandsim_file.h"
236
237 struct yaffs_dev *yaffs_nandsim_install_drv(const char *name,
238                                         const char *file_name,
239                                         int n_blocks)
240 {
241         struct yaffs_dev *dev;
242         char *name_copy = NULL;
243         struct yaffs_param *param;
244         struct yaffs_driver *drv;
245         struct nand_chip *chip = NULL;
246         struct nand_context *ctxt = NULL;
247         u8 *buffer = NULL;
248
249         dev = malloc(sizeof(struct yaffs_dev));
250         ctxt = malloc(sizeof(struct nand_context));
251         name_copy = strdup(name);
252
253         if(!dev || !ctxt || !name_copy)
254                 goto fail;
255
256         chip = nandsim_file_init(file_name, n_blocks, 64, 2048, 64, 0);
257         if(!chip)
258                 goto fail;
259
260         buffer = malloc(chip->spare_bytes_per_page);
261
262         if(!buffer)
263                 goto fail;
264
265         param = &dev->param;
266         drv = &dev->drv;
267
268         memset(dev, 0, sizeof(*dev));
269         memset(ctxt, 0, sizeof(*ctxt));
270
271         param->name = name_copy;
272
273         param->total_bytes_per_chunk = chip->data_bytes_per_page;
274         param->chunks_per_block = chip->pages_per_block;
275         param->n_reserved_blocks = 5;
276         param->start_block = 0; // Can use block 0
277         param->end_block = chip->blocks - 1; // Last block
278         param->is_yaffs2 = 1;
279         param->use_nand_ecc = 1;
280         param->n_caches = 10;
281
282         drv->drv_write_chunk_fn = yaffs_nand_drv_WriteChunk;
283         drv->drv_read_chunk_fn = yaffs_nand_drv_ReadChunk;
284         drv->drv_erase_fn = yaffs_nand_drv_EraseBlock;
285         drv->drv_mark_bad_fn = yaffs_nand_drv_MarkBad;
286         drv->drv_check_bad_fn = yaffs_nand_drv_CheckBad;
287         drv->drv_initialise_fn = yaffs_nand_drv_Initialise;
288         drv->drv_deinitialise_fn = yaffs_nand_drv_Deinitialise;
289
290         ctxt->chip = chip;
291         ctxt->buffer = buffer;
292         dev->driver_context = (void *) ctxt;
293
294         yaffs_add_device(dev);
295
296         return dev;
297
298 fail:
299         free(dev);
300         free(ctxt);
301         free(name_copy);
302         free(buffer);
303         return NULL;
304 }