Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / flight / pios / posix / libraries / yaffs2 / yaffs_nor_drv.c
blob7c9989a3b76a35f8f5e1037ecbb99b1c57f0dbf0
1 /*
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 NOR in yaffs1 mode.
18 /* This code is intended to be used with "regular" NOR that is bit-modifyable.
20 * Each "chunk" is a contguous area of 512 + 16 bytes.
21 * This makes 248 such chunks with some space left over where a format markerr
22 * is stored.
25 #include "yaffs_nor_drv.h"
27 #include "yportenv.h"
28 #include "yaffs_trace.h"
30 #include "yaffs_flashif.h"
31 #include "yaffs_guts.h"
33 /* Tunable data */
34 #define DATA_BYTES_PER_CHUNK 512
35 #define SPARE_BYTES_PER_CHUNK 16
36 #define BLOCK_SIZE_IN_BYTES (128 * 1024)
37 #define BYTES_IN_DEVICE (4 * 1024 * 1024)
39 #define BYTES_PER_CHUNK (DATA_BYTES_PER_CHUNK + SPARE_BYTES_PER_CHUNK)
40 #define SPARE_AREA_OFFSET DATA_BYTES_PER_CHUNK
41 #define CHUNKS_PER_BLOCK (BLOCK_SIZE_IN_BYTES / BYTES_PER_CHUNK)
43 #define BLOCKS_IN_DEVICE (BYTES_IN_DEVICE / BLOCK_SIZE_IN_BYTES)
45 #define YNOR_PREMARKER (0xF6)
46 #define YNOR_POSTMARKER (0xF0)
48 #define FORMAT_OFFSET (CHUNKS_PER_BLOCK * BYTES_PER_CHUNK)
49 #define FORMAT_VALUE 0x1234
51 #if 1
53 /* Compile this for a simulation */
54 #include "ynorsim.h"
56 static struct nor_sim *nor_sim;
58 #define nor_drv_FlashInit() \
59 do { nor_sim = ynorsim_initialise("emfile-nor", BLOCKS_IN_DEVICE, BLOCK_SIZE_IN_BYTES); } \
60 while (0)
61 #define nor_drv_FlashDeinit() ynorsim_shutdown(nor_sim)
62 #define nor_drv_FlashWrite32(addr, buf, nwords) ynorsim_wr32(nor_sim, addr, buf, nwords)
63 #define nor_drv_FlashRead32(addr, buf, nwords) ynorsim_rd32(nor_sim, addr, buf, nwords)
64 #define nor_drv_FlashEraseBlock(addr) ynorsim_erase(nor_sim, addr)
65 #define DEVICE_BASE ynorsim_get_base(nor_sim)
67 #else
69 /* Compile this to hook up to read hardware */
70 #include "../blob/yflashrw.h"
71 #define nor_drv_FlashInit() \
72 do {} \
73 while (0)
74 #define nor_drv_FlashDeinit() \
75 do {} \
76 while (0)
77 #define nor_drv_FlashWrite32(addr, buf, nwords) Y_FlashWrite(addr, buf, nwords)
78 #define nor_drv_FlashRead32(addr, buf, nwords) Y_FlashRead(addr, buf, nwords)
79 #define nor_drv_FlashEraseBlock(addr) Y_FlashErase(addr, BLOCK_SIZE_IN_BYTES)
80 #define DEVICE_BASE (32 * 1024 * 1024)
81 #endif /* if 1 */
84 static u32 *Block2Addr(struct yaffs_dev *dev, int blockNumber)
86 u8 *addr;
88 dev = dev;
90 addr = (u8 *)DEVICE_BASE;
91 addr += blockNumber * BLOCK_SIZE_IN_BYTES;
93 return (u32 *)addr;
96 static u32 *Block2FormatAddr(struct yaffs_dev *dev, int blockNumber)
98 u8 *addr;
100 addr = (u8 *)Block2Addr(dev, blockNumber);
101 addr += FORMAT_OFFSET;
103 return (u32 *)addr;
106 static u32 *Chunk2DataAddr(struct yaffs_dev *dev, int chunk_id)
108 unsigned block;
109 unsigned chunkInBlock;
110 u8 *addr;
112 block = chunk_id / dev->param.chunks_per_block;
113 chunkInBlock = chunk_id % dev->param.chunks_per_block;
115 addr = (u8 *)Block2Addr(dev, block);
116 addr += chunkInBlock * BYTES_PER_CHUNK;
118 return (u32 *)addr;
121 static u32 *Chunk2SpareAddr(struct yaffs_dev *dev, int chunk_id)
123 u8 *addr;
125 addr = (u8 *)Chunk2DataAddr(dev, chunk_id);
126 addr += SPARE_AREA_OFFSET;
127 return (u32 *)addr;
131 static void nor_drv_AndBytes(u8 *target, const u8 *src, int nbytes)
133 while (nbytes > 0) {
134 *target &= *src;
135 target++;
136 src++;
137 nbytes--;
141 static int nor_drv_WriteChunkToNAND(struct yaffs_dev *dev, int nand_chunk,
142 const u8 *data, int data_len,
143 const u8 *oob, int oob_len)
145 u32 *dataAddr = Chunk2DataAddr(dev, nand_chunk);
146 u32 *spareAddr = Chunk2SpareAddr(dev, nand_chunk);
148 struct yaffs_spare *spare = (struct yaffs_spare *)oob;
149 struct yaffs_spare tmpSpare;
151 (void)oob_len;
153 /* We should only be getting called for one of 3 reasons:
154 * Writing a chunk: data and spare will not be NULL
155 * Writing a deletion marker: data will be NULL, spare not NULL
156 * Writing a bad block marker: data will be NULL, spare not NULL
159 if (sizeof(struct yaffs_spare) != SPARE_BYTES_PER_CHUNK) {
160 BUG();
163 if (data && oob) {
164 if (spare->page_status != 0xff) {
165 BUG();
167 /* Write a pre-marker */
168 memset(&tmpSpare, 0xff, sizeof(tmpSpare));
169 tmpSpare.page_status = YNOR_PREMARKER;
170 nor_drv_FlashWrite32(spareAddr, (u32 *)&tmpSpare, sizeof(struct yaffs_spare) / sizeof(u32));
172 /* Write the data */
173 nor_drv_FlashWrite32(dataAddr, (u32 *)data, data_len / sizeof(u32));
175 memcpy(&tmpSpare, spare, sizeof(struct yaffs_spare));
177 /* Write the real tags, but override the premarker*/
178 tmpSpare.page_status = YNOR_PREMARKER;
179 nor_drv_FlashWrite32(spareAddr, (u32 *)&tmpSpare, sizeof(struct yaffs_spare) / sizeof(u32));
181 /* Write a post-marker */
182 tmpSpare.page_status = YNOR_POSTMARKER;
183 nor_drv_FlashWrite32(spareAddr, (u32 *)&tmpSpare, sizeof(tmpSpare) / sizeof(u32));
184 } else if (spare) {
185 /* This has to be a read-modify-write operation to handle NOR-ness */
187 nor_drv_FlashRead32(spareAddr, (u32 *)&tmpSpare, sizeof(struct yaffs_spare) / sizeof(u32));
189 nor_drv_AndBytes((u8 *)&tmpSpare, (u8 *)spare, sizeof(struct yaffs_spare));
191 nor_drv_FlashWrite32(spareAddr, (u32 *)&tmpSpare, sizeof(struct yaffs_spare) / sizeof(u32));
192 } else {
193 BUG();
196 return YAFFS_OK;
199 static int nor_drv_ReadChunkFromNAND(struct yaffs_dev *dev, int nand_chunk,
200 u8 *data, int data_len,
201 u8 *oob, int oob_len,
202 enum yaffs_ecc_result *ecc_result)
204 struct yaffs_spare *spare = (struct yaffs_spare *)oob;
206 u32 *dataAddr = Chunk2DataAddr(dev, nand_chunk);
207 u32 *spareAddr = Chunk2SpareAddr(dev, nand_chunk);
209 if (data) {
210 nor_drv_FlashRead32(dataAddr, (u32 *)data, dev->param.total_bytes_per_chunk / sizeof(u32));
213 if (oob) {
214 nor_drv_FlashRead32(spareAddr, (u32 *)spare, oob_len / sizeof(u32));
216 /* If the page status is YNOR_POSTMARKER then it was written properly
217 * so change that to 0xFF so that the rest of yaffs is happy.
219 if (spare->page_status == YNOR_POSTMARKER) {
220 spare->page_status = 0xff;
221 } else if (spare->page_status != 0xff &&
222 (spare->page_status | YNOR_PREMARKER) != 0xff) {
223 spare->page_status = YNOR_PREMARKER;
227 if (ecc_result) {
228 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
231 return YAFFS_OK;
235 static int nor_drv_FormatBlock(struct yaffs_dev *dev, int blockNumber)
237 u32 *blockAddr = Block2Addr(dev, blockNumber);
238 u32 *formatAddr = Block2FormatAddr(dev, blockNumber);
239 u32 formatValue = FORMAT_VALUE;
241 nor_drv_FlashEraseBlock(blockAddr);
242 nor_drv_FlashWrite32(formatAddr, &formatValue, 1);
244 return YAFFS_OK;
247 static int nor_drv_UnformatBlock(struct yaffs_dev *dev, int blockNumber)
249 u32 *formatAddr = Block2FormatAddr(dev, blockNumber);
250 u32 formatValue = 0;
252 nor_drv_FlashWrite32(formatAddr, &formatValue, 1);
254 return YAFFS_OK;
257 static int nor_drv_IsBlockFormatted(struct yaffs_dev *dev, int blockNumber)
259 u32 *formatAddr = Block2FormatAddr(dev, blockNumber);
260 u32 formatValue;
263 nor_drv_FlashRead32(formatAddr, &formatValue, 1);
265 return formatValue == FORMAT_VALUE;
268 static int nor_drv_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber)
270 if (blockNumber < 0 || blockNumber >= BLOCKS_IN_DEVICE) {
271 yaffs_trace(YAFFS_TRACE_ALWAYS,
272 "Attempt to erase non-existant block %d\n",
273 blockNumber);
274 return YAFFS_FAIL;
275 } else {
276 nor_drv_UnformatBlock(dev, blockNumber);
277 nor_drv_FormatBlock(dev, blockNumber);
278 return YAFFS_OK;
282 static int nor_drv_InitialiseNAND(struct yaffs_dev *dev)
284 int i;
286 nor_drv_FlashInit();
287 /* Go through the blocks formatting them if they are not formatted */
288 for (i = dev->param.start_block; i <= dev->param.end_block; i++) {
289 if (!nor_drv_IsBlockFormatted(dev, i)) {
290 nor_drv_FormatBlock(dev, i);
293 return YAFFS_OK;
296 static int nor_drv_Deinitialise_flash_fn(struct yaffs_dev *dev)
298 dev = dev;
300 nor_drv_FlashDeinit();
302 return YAFFS_OK;
306 struct yaffs_dev *yaffs_nor_install_drv(const char *name)
308 struct yaffs_dev *dev = malloc(sizeof(struct yaffs_dev));
309 char *name_copy = strdup(name);
310 struct yaffs_param *param;
311 struct yaffs_driver *drv;
314 if (!dev || !name_copy) {
315 free(name_copy);
316 free(dev);
317 return NULL;
320 param = &dev->param;
321 drv = &dev->drv;
323 memset(dev, 0, sizeof(*dev));
325 param->name = name_copy;
327 param->total_bytes_per_chunk = DATA_BYTES_PER_CHUNK;
328 param->chunks_per_block = CHUNKS_PER_BLOCK;
329 param->n_reserved_blocks = 2;
330 param->start_block = 0; // Can use block 0
331 param->end_block = BLOCKS_IN_DEVICE - 1; // Last block
332 param->use_nand_ecc = 0; // use YAFFS's ECC
333 param->disable_soft_del = 1;
335 drv->drv_write_chunk_fn = nor_drv_WriteChunkToNAND;
336 drv->drv_read_chunk_fn = nor_drv_ReadChunkFromNAND;
337 drv->drv_erase_fn = nor_drv_EraseBlockInNAND;
338 drv->drv_initialise_fn = nor_drv_InitialiseNAND;
339 drv->drv_deinitialise_fn = nor_drv_Deinitialise_flash_fn;
341 param->n_caches = 10;
342 param->disable_soft_del = 1;
344 dev->driver_context = (void *)nor_sim;
346 yaffs_add_device(dev);
348 return NULL;