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
25 #include "yaffs_nor_drv.h"
28 #include "yaffs_trace.h"
30 #include "yaffs_flashif.h"
31 #include "yaffs_guts.h"
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
53 /* Compile this for a simulation */
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); } \
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)
69 /* Compile this to hook up to read hardware */
70 #include "../blob/yflashrw.h"
71 #define nor_drv_FlashInit() \
74 #define nor_drv_FlashDeinit() \
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)
84 static u32
*Block2Addr(struct yaffs_dev
*dev
, int blockNumber
)
90 addr
= (u8
*)DEVICE_BASE
;
91 addr
+= blockNumber
* BLOCK_SIZE_IN_BYTES
;
96 static u32
*Block2FormatAddr(struct yaffs_dev
*dev
, int blockNumber
)
100 addr
= (u8
*)Block2Addr(dev
, blockNumber
);
101 addr
+= FORMAT_OFFSET
;
106 static u32
*Chunk2DataAddr(struct yaffs_dev
*dev
, int chunk_id
)
109 unsigned chunkInBlock
;
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
;
121 static u32
*Chunk2SpareAddr(struct yaffs_dev
*dev
, int chunk_id
)
125 addr
= (u8
*)Chunk2DataAddr(dev
, chunk_id
);
126 addr
+= SPARE_AREA_OFFSET
;
131 static void nor_drv_AndBytes(u8
*target
, const u8
*src
, int 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
;
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
) {
164 if (spare
->page_status
!= 0xff) {
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
));
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
));
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
));
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
);
210 nor_drv_FlashRead32(dataAddr
, (u32
*)data
, dev
->param
.total_bytes_per_chunk
/ sizeof(u32
));
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
;
228 *ecc_result
= YAFFS_ECC_RESULT_NO_ERROR
;
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);
247 static int nor_drv_UnformatBlock(struct yaffs_dev
*dev
, int blockNumber
)
249 u32
*formatAddr
= Block2FormatAddr(dev
, blockNumber
);
252 nor_drv_FlashWrite32(formatAddr
, &formatValue
, 1);
257 static int nor_drv_IsBlockFormatted(struct yaffs_dev
*dev
, int blockNumber
)
259 u32
*formatAddr
= Block2FormatAddr(dev
, blockNumber
);
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",
276 nor_drv_UnformatBlock(dev
, blockNumber
);
277 nor_drv_FormatBlock(dev
, blockNumber
);
282 static int nor_drv_InitialiseNAND(struct yaffs_dev
*dev
)
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
);
296 static int nor_drv_Deinitialise_flash_fn(struct yaffs_dev
*dev
)
300 nor_drv_FlashDeinit();
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
) {
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
);