d358a43a06a387ba42a50c346e2fb01505371aaf
[yaffs2.git] / direct / basic-test / yaffs_norif1.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 NOR in yaffs1 mode.
16  */
17
18 /* First set up for M18 with 1k chunks and 16-byte spares.
19  *
20  * NB We're using the oddball M18 modes of operation here
21  * The chip is 64MB based at 0x0000, but YAFFS only going to use the top half
22  * ie. YAFFS will be from 32MB to 64MB.
23  *
24  * The M18 has two ways of writing data. Every Programming Region (1kbytes)
25  * can be programmed in two modes:
26  * * Object Mode 1024 bytes of write once data.
27  * * Control Mode: 512bytes of bit-writeable data.
28  *    This is arranged as 32 * (16 bytes of bit-writable followed by 16 bytes of "dont touch")
29  *
30  * The block size is 256kB, making 128 blocks in the 32MB YAFFS area.
31  * Each block comprises:
32  *   Offset   0k: 248 x 1k data pages
33  *   Offset 248k: 248 x 32-byte spare areas implemented as 16 bytes of spare followed by 16 bytes untouched)
34  *   Offset 248k + (248 * 32): Format marker
35  *
36  */
37
38 #include "yaffs_norif1.h"
39
40 #include "yportenv.h"
41 #include "yaffs_trace.h"
42
43 #include "yaffs_flashif.h"
44 #include "yaffs_guts.h"
45
46 #define SPARE_BYTES_PER_CHUNK   16
47 #define M18_SKIP                16
48 #define PROG_REGION_SIZE        1024
49 #define BLOCK_SIZE_IN_BYTES     (256*1024)
50 #define CHUNKS_PER_BLOCK        248
51 #define SPARE_AREA_OFFSET       (CHUNKS_PER_BLOCK * PROG_REGION_SIZE)
52
53 #define FORMAT_OFFSET           (SPARE_AREA_OFFSET + CHUNKS_PER_BLOCK * (SPARE_BYTES_PER_CHUNK + M18_SKIP))
54
55 #define FORMAT_VALUE            0x1234
56
57 #define DATA_BYTES_PER_CHUNK    1024
58 #define BLOCKS_IN_DEVICE        (8*1024/256)
59
60
61 #define YNOR_PREMARKER          (0xF6)
62 #define YNOR_POSTMARKER         (0xF0)
63
64
65 #if 1
66
67 /* Compile this for a simulation */
68 #include "ynorsim.h"
69 #define ynorif1_FlashInit() ynorsim_initialise()
70 #define ynorif1_FlashDeinit() ynorsim_shutdown()
71 #define ynorif1_FlashWrite32(addr,buf,nwords) ynorsim_wr32(addr,buf,nwords)
72 #define ynorif1_FlashRead32(addr,buf,nwords) ynorsim_rd32(addr,buf,nwords)
73 #define ynorif1_FlashEraseBlock(addr) ynorsim_erase(addr)
74 #define DEVICE_BASE     ynorsim_get_base()
75 #else
76
77 /* Compile this for running on blob, hacked for yaffs access */
78 #include "../blob/yflashrw.h"
79 #define ynorif1_FlashInit()  do{} while(0)
80 #define ynorif1_FlashDeinit() do {} while(0)
81 #define ynorif1_FlashWrite32(addr,buf,nwords) Y_FlashWrite(addr,buf,nwords)
82 #define ynorif1_FlashRead32(addr,buf,nwords)  Y_FlashRead(addr,buf,nwords)
83 #define ynorif1_FlashEraseBlock(addr)         Y_FlashErase(addr,BLOCK_SIZE_IN_BYTES)
84 #define DEVICE_BASE     (32 * 1024 * 1024)
85 #endif
86
87 static u32 *Block2Addr(struct yaffs_dev *dev, int blockNumber)
88 {
89         u32 addr;
90         dev=dev;
91
92         addr = (u32) DEVICE_BASE;
93         addr += blockNumber * BLOCK_SIZE_IN_BYTES;
94
95         return (u32 *) addr;
96 }
97
98 static u32 *Block2FormatAddr(struct yaffs_dev *dev,int blockNumber)
99 {
100         u32 addr;
101
102         addr = (u32) Block2Addr(dev,blockNumber);
103         addr += FORMAT_OFFSET;
104
105         return (u32 *)addr;
106 }
107
108 static u32 *Chunk2DataAddr(struct yaffs_dev *dev,int chunk_id)
109 {
110         unsigned block;
111         unsigned chunkInBlock;
112         u32  addr;
113
114         block = chunk_id/dev->param.chunks_per_block;
115         chunkInBlock = chunk_id % dev->param.chunks_per_block;
116
117         addr = (u32) Block2Addr(dev,block);
118         addr += chunkInBlock * DATA_BYTES_PER_CHUNK;
119
120         return (u32 *)addr;
121 }
122
123 static u32 *Chunk2SpareAddr(struct yaffs_dev *dev,int chunk_id)
124 {
125         unsigned block;
126         unsigned chunkInBlock;
127         u32 addr;
128
129         block = chunk_id/dev->param.chunks_per_block;
130         chunkInBlock = chunk_id % dev->param.chunks_per_block;
131
132         addr = (u32) Block2Addr(dev,block);
133         addr += SPARE_AREA_OFFSET;
134         addr += chunkInBlock * (SPARE_BYTES_PER_CHUNK + M18_SKIP);
135         return (u32 *)addr;
136 }
137
138
139 static void ynorif1_AndBytes(u8*target, const u8   *src, int nbytes)
140 {
141         while(nbytes > 0){
142                 *target &= *src;
143                 target++;
144                 src++;
145                 nbytes--;
146         }
147 }
148
149 static int ynorif1_WriteChunkToNAND(struct yaffs_dev *dev,int nand_chunk,
150                                     const u8 *data, int data_len,
151                                     const u8 *oob, int oob_len)
152 {
153         u32 *dataAddr = Chunk2DataAddr(dev,nand_chunk);
154         u32 *spareAddr = Chunk2SpareAddr(dev,nand_chunk);
155
156         struct yaffs_spare *spare = (struct yaffs_spare *)oob;
157         struct yaffs_spare tmpSpare;
158
159         (void) oob_len;
160
161         /* We should only be getting called for one of 3 reasons:
162          * Writing a chunk: data and spare will not be NULL
163          * Writing a deletion marker: data will be NULL, spare not NULL
164          * Writing a bad block marker: data will be NULL, spare not NULL
165          */
166
167         if(sizeof(struct yaffs_spare) != 16)
168                 BUG();
169
170         if(data && oob)
171         {
172                 if(spare->page_status != 0xff)
173                         BUG();
174                 /* Write a pre-marker */
175                 memset(&tmpSpare,0xff,sizeof(tmpSpare));
176                 tmpSpare.page_status = YNOR_PREMARKER;
177                 ynorif1_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(struct yaffs_spare)/4);
178
179                 /* Write the data */
180                 ynorif1_FlashWrite32(dataAddr,(u32 *)data, data_len/ 4);
181
182
183                 memcpy(&tmpSpare,spare,sizeof(struct yaffs_spare));
184
185                 /* Write the real tags, but override the premarker*/
186                 tmpSpare.page_status = YNOR_PREMARKER;
187                 ynorif1_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(struct yaffs_spare)/4);
188
189                 /* Write a post-marker */
190                 tmpSpare.page_status = YNOR_POSTMARKER;
191                 ynorif1_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(tmpSpare)/4);
192
193         } else if(spare){
194                 /* This has to be a read-modify-write operation to handle NOR-ness */
195
196                 ynorif1_FlashRead32(spareAddr,(u32 *)&tmpSpare,16/ 4);
197
198                 ynorif1_AndBytes((u8 *)&tmpSpare,(u8 *)spare,sizeof(struct yaffs_spare));
199
200                 ynorif1_FlashWrite32(spareAddr,(u32 *)&tmpSpare,16/ 4);
201         }
202         else {
203                 BUG();
204         }
205
206
207         return YAFFS_OK;
208
209 }
210
211 static int ynorif1_ReadChunkFromNAND(struct yaffs_dev *dev,int nand_chunk,
212                                         u8 *data, int data_len,
213                                         u8 *oob, int oob_len,
214                                         enum yaffs_ecc_result *ecc_result)
215 {
216         struct yaffs_spare *spare = (struct yaffs_spare *)oob;
217
218         u32 *dataAddr = Chunk2DataAddr(dev,nand_chunk);
219         u32 *spareAddr = Chunk2SpareAddr(dev,nand_chunk);
220
221         if(data)
222         {
223                 ynorif1_FlashRead32(dataAddr,(u32 *)data,dev->param.total_bytes_per_chunk / 4);
224         }
225
226         if(oob)
227         {
228                 ynorif1_FlashRead32(spareAddr,(u32 *)spare, oob_len/ 4);
229
230                 /* If the page status is YNOR_POSTMARKER then it was written properly
231                  * so change that to 0xFF so that the rest of yaffs is happy.
232                  */
233                 if(spare->page_status == YNOR_POSTMARKER)
234                         spare->page_status = 0xFF;
235                 else if(spare->page_status != 0xff &&
236                         (spare->page_status | YNOR_PREMARKER) != 0xff)
237                         spare->page_status = YNOR_PREMARKER;
238         }
239
240         if(ecc_result)
241                 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
242
243         return YAFFS_OK;
244
245 }
246
247
248 static int ynorif1_FormatBlock(struct yaffs_dev *dev, int blockNumber)
249 {
250         u32 *blockAddr = Block2Addr(dev,blockNumber);
251         u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
252         u32 formatValue = FORMAT_VALUE;
253
254         ynorif1_FlashEraseBlock(blockAddr);
255         ynorif1_FlashWrite32(formatAddr,&formatValue,1);
256
257         return YAFFS_OK;
258 }
259
260 static int ynorif1_UnformatBlock(struct yaffs_dev *dev, int blockNumber)
261 {
262         u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
263         u32 formatValue = 0;
264
265         ynorif1_FlashWrite32(formatAddr,&formatValue,1);
266
267         return YAFFS_OK;
268 }
269
270 static int ynorif1_IsBlockFormatted(struct yaffs_dev *dev, int blockNumber)
271 {
272         u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
273         u32 formatValue;
274
275
276         ynorif1_FlashRead32(formatAddr,&formatValue,1);
277
278         return (formatValue == FORMAT_VALUE);
279 }
280
281 static int ynorif1_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber)
282 {
283
284         if(blockNumber < 0 || blockNumber >= BLOCKS_IN_DEVICE)
285         {
286                 yaffs_trace(YAFFS_TRACE_ALWAYS,
287                         "Attempt to erase non-existant block %d\n",
288                         blockNumber);
289                 return YAFFS_FAIL;
290         }
291         else
292         {
293                 ynorif1_UnformatBlock(dev,blockNumber);
294                 ynorif1_FormatBlock(dev,blockNumber);
295                 return YAFFS_OK;
296         }
297
298 }
299
300 static int ynorif1_InitialiseNAND(struct yaffs_dev *dev)
301 {
302         int i;
303
304         ynorif1_FlashInit();
305         /* Go through the blocks formatting them if they are not formatted */
306         for(i = dev->param.start_block; i <= dev->param.end_block; i++){
307                 if(!ynorif1_IsBlockFormatted(dev,i)){
308                         ynorif1_FormatBlock(dev,i);
309                 }
310         }
311         return YAFFS_OK;
312 }
313
314 static int ynorif1_Deinitialise_flash_fn(struct yaffs_dev *dev)
315 {
316         dev=dev;
317         ynorif1_FlashDeinit();
318
319         return YAFFS_OK;
320 }
321
322 void ynorif1_install_drv(struct yaffs_dev *dev)
323 {
324         struct yaffs_param *param = &dev->param;
325         struct yaffs_driver *drv = &dev->drv;
326
327         param->total_bytes_per_chunk = 1024;
328         param->chunks_per_block =248;
329         param->n_reserved_blocks = 2;
330         param->start_block = 0; // Can use block 0
331         param->end_block = 31; // Last block
332         param->use_nand_ecc = 0; // use YAFFS's ECC
333         drv->drv_write_chunk_fn = ynorif1_WriteChunkToNAND;
334         drv->drv_read_chunk_fn = ynorif1_ReadChunkFromNAND;
335         drv->drv_erase_fn = ynorif1_EraseBlockInNAND;
336         drv->drv_initialise_fn = ynorif1_InitialiseNAND;
337         drv->drv_deinitialise_fn = ynorif1_Deinitialise_flash_fn;
338 }
339