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