2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2011 Aleph One Ltd.
5 * for Toby Churchill Ltd and Brightstar Engineering
7 * Created by Charles Manning <charles@aleph1.co.uk>
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.
15 * This is an interface module for handling NAND in yaffs2 mode.
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.
22 * This driver is designed for use in yaffs2 mode with a 2k page size and
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.
31 #include "yaffs_nand_drv.h"
33 #include "yaffs_trace.h"
35 #include "nand_store.h"
36 #include "yaffs_flashif.h"
37 #include "yaffs_guts.h"
39 #include "yaffs_ecc.h"
42 struct nand_chip *chip;
47 static inline struct nand_chip *dev_to_chip(struct yaffs_dev *dev)
49 struct nand_context *ctxt =
50 (struct nand_context *)(dev->driver_context);
54 static inline u8 *dev_to_buffer(struct yaffs_dev *dev)
56 struct nand_context *ctxt =
57 (struct nand_context *)(dev->driver_context);
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)
65 struct nand_chip *chip = dev_to_chip(dev);
66 u8 *buffer = dev_to_buffer(dev);
68 struct nanddrv_transfer tr[2];
75 /* Calc ECC and marshall the oob bytes into the buffer */
77 memset(buffer, 0xff, chip->spare_bytes_per_page);
79 for(i = 0, e = buffer + 2; i < chip->data_bytes_per_page; i+=256, e+=3)
80 yaffs_ecc_calc(data + i, e);
82 memcpy(buffer + 26, oob, oob_len);
84 /* Set up and execute transfer */
88 tr[0].nbytes = data_len;
90 tr[1].buffer = buffer;
91 tr[1].offset = chip->data_bytes_per_page;
92 tr[1].nbytes = chip->spare_bytes_per_page;
94 if(nanddrv_write_tr(chip, nand_chunk, tr, 2) == 0)
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)
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;
111 enum yaffs_ecc_result ecc_result;
119 trp->nbytes = data_len;
125 trp->buffer = buffer;
126 trp->offset = chip->data_bytes_per_page;
127 trp->nbytes = chip->spare_bytes_per_page;
132 ret = nanddrv_read_tr(chip, nand_chunk, tr, n_tr);
136 *ecc_result_out = YAFFS_ECC_RESULT_UNKNOWN;
140 /* Do ECC and marshalling */
142 memcpy(oob, buffer + 26, oob_len);
144 ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
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);
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;
160 *ecc_result_out = ecc_result;
165 static int yaffs_nand_drv_EraseBlock(struct yaffs_dev *dev, int block_no)
167 struct nand_chip *chip = dev_to_chip(dev);
169 if(nanddrv_erase(chip, block_no) == 0)
175 static int yaffs_nand_drv_MarkBad(struct yaffs_dev *dev, int block_no)
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];
182 memset(buffer, 0xff, chip->spare_bytes_per_page);
186 tr[0].buffer = buffer;
187 tr[0].offset = chip->data_bytes_per_page;
188 tr[0].nbytes = chip->spare_bytes_per_page;
190 if(nanddrv_write_tr(chip, nand_chunk, tr, 1) == 0)
196 static int yaffs_nand_drv_CheckBad(struct yaffs_dev *dev, int block_no)
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;
203 struct nanddrv_transfer tr[1];
205 memset(buffer, 0, chip->spare_bytes_per_page);
207 tr[0].buffer = buffer;
208 tr[0].offset = chip->data_bytes_per_page;
209 tr[0].nbytes = chip->spare_bytes_per_page;
211 ret = nanddrv_read_tr(chip, nand_chunk, tr, 1);
213 /* Check that bad block marker is not set */
214 if(yaffs_hweight8(buffer[0]) + yaffs_hweight8(buffer[1]) < 14)
221 static int yaffs_nand_drv_Initialise(struct yaffs_dev *dev)
223 struct nand_chip *chip = dev_to_chip(dev);
228 static int yaffs_nand_drv_Deinitialise(struct yaffs_dev *dev)
230 struct nand_chip *chip = dev_to_chip(dev);
235 #include "nandsim_file.h"
237 struct yaffs_dev *yaffs_nandsim_install_drv(const char *name,
238 const char *file_name,
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;
249 dev = malloc(sizeof(struct yaffs_dev));
250 ctxt = malloc(sizeof(struct nand_context));
251 name_copy = strdup(name);
253 if(!dev || !ctxt || !name_copy)
256 chip = nandsim_file_init(file_name, n_blocks, 64, 2048, 64, 0);
260 buffer = malloc(chip->spare_bytes_per_page);
268 memset(dev, 0, sizeof(*dev));
269 memset(ctxt, 0, sizeof(*ctxt));
271 param->name = name_copy;
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;
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;
291 ctxt->buffer = buffer;
292 dev->driver_context = (void *) ctxt;
294 yaffs_add_device(dev);