Fix broken comment
[yaffs2.git] / direct / test-framework / yaffs_m18_drv.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_m18_drv.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 * \
54                                 (SPARE_BYTES_PER_CHUNK + M18_SKIP))
55
56 #define FORMAT_VALUE            0x1234
57
58 #define DATA_BYTES_PER_CHUNK    1024
59 #define BLOCKS_IN_DEVICE        (8*1024/256)
60
61
62 #define YNOR_PREMARKER          (0xF6)
63 #define YNOR_POSTMARKER         (0xF0)
64
65
66 #if 1
67
68 /* Compile this for a simulation */
69 #include "ynorsim.h"
70
71 static struct nor_sim *nor_sim;
72
73 #define m18_drv_FlashInit() do {nor_sim = ynorsim_initialise("emfile-m18", BLOCKS_IN_DEVICE, BLOCK_SIZE_IN_BYTES); } while(0)
74 #define m18_drv_FlashDeinit() ynorsim_shutdown(nor_sim)
75 #define m18_drv_FlashWrite32(addr,buf,nwords) ynorsim_wr32(nor_sim,addr,buf,nwords)
76 #define m18_drv_FlashRead32(addr,buf,nwords) ynorsim_rd32(nor_sim,addr,buf,nwords)
77 #define m18_drv_FlashEraseBlock(addr) ynorsim_erase(nor_sim,addr)
78 #define DEVICE_BASE     ynorsim_get_base(nor_sim)
79
80 #else
81
82 /* Compile this to hook up to read hardware */
83 #include "../blob/yflashrw.h"
84 #define m18_drv_FlashInit()  do{} while(0)
85 #define m18_drv_FlashDeinit() do {} while(0)
86 #define m18_drv_FlashWrite32(addr,buf,nwords) Y_FlashWrite(addr,buf,nwords)
87 #define m18_drv_FlashRead32(addr,buf,nwords)  Y_FlashRead(addr,buf,nwords)
88 #define m18_drv_FlashEraseBlock(addr)         Y_FlashErase(addr,BLOCK_SIZE_IN_BYTES)
89 #define DEVICE_BASE     (32 * 1024 * 1024)
90 #endif
91
92
93 static u32 *Block2Addr(struct yaffs_dev *dev, int blockNumber)
94 {
95         u8 *addr;
96         dev=dev;
97
98         addr = (u8*) DEVICE_BASE;
99         addr += blockNumber * BLOCK_SIZE_IN_BYTES;
100
101         return (u32 *) addr;
102 }
103
104 static u32 *Block2FormatAddr(struct yaffs_dev *dev,int blockNumber)
105 {
106         u8 *addr;
107
108         addr = (u8*) Block2Addr(dev,blockNumber);
109         addr += FORMAT_OFFSET;
110
111         return (u32 *)addr;
112 }
113
114 static u32 *Chunk2DataAddr(struct yaffs_dev *dev,int chunk_id)
115 {
116         unsigned block;
117         unsigned chunkInBlock;
118         u8 *addr;
119
120         block = chunk_id/dev->param.chunks_per_block;
121         chunkInBlock = chunk_id % dev->param.chunks_per_block;
122
123         addr = (u8*) Block2Addr(dev,block);
124         addr += chunkInBlock * DATA_BYTES_PER_CHUNK;
125
126         return (u32 *)addr;
127 }
128
129 static u32 *Chunk2SpareAddr(struct yaffs_dev *dev,int chunk_id)
130 {
131         unsigned block;
132         unsigned chunkInBlock;
133         u8 *addr;
134
135         block = chunk_id/dev->param.chunks_per_block;
136         chunkInBlock = chunk_id % dev->param.chunks_per_block;
137
138         addr = (u8*) Block2Addr(dev,block);
139         addr += SPARE_AREA_OFFSET;
140         addr += chunkInBlock * (SPARE_BYTES_PER_CHUNK + M18_SKIP);
141         return (u32 *)addr;
142 }
143
144
145 static void m18_drv_AndBytes(u8*target, const u8   *src, int nbytes)
146 {
147         while(nbytes > 0){
148                 *target &= *src;
149                 target++;
150                 src++;
151                 nbytes--;
152         }
153 }
154
155 static int m18_drv_WriteChunkToNAND(struct yaffs_dev *dev,int nand_chunk,
156                                     const u8 *data, int data_len,
157                                     const u8 *oob, int oob_len)
158 {
159         u32 *dataAddr = Chunk2DataAddr(dev,nand_chunk);
160         u32 *spareAddr = Chunk2SpareAddr(dev,nand_chunk);
161
162         struct yaffs_spare *spare = (struct yaffs_spare *)oob;
163         struct yaffs_spare tmpSpare;
164
165         (void) oob_len;
166
167         /* We should only be getting called for one of 3 reasons:
168          * Writing a chunk: data and spare will not be NULL
169          * Writing a deletion marker: data will be NULL, spare not NULL
170          * Writing a bad block marker: data will be NULL, spare not NULL
171          */
172
173         if(sizeof(struct yaffs_spare) != 16)
174                 BUG();
175
176         if(data && oob)
177         {
178                 if(spare->page_status != 0xff)
179                         BUG();
180                 /* Write a pre-marker */
181                 memset(&tmpSpare,0xff,sizeof(tmpSpare));
182                 tmpSpare.page_status = YNOR_PREMARKER;
183                 m18_drv_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(struct yaffs_spare)/4);
184
185                 /* Write the data */
186                 m18_drv_FlashWrite32(dataAddr,(u32 *)data, data_len/ 4);
187
188
189                 memcpy(&tmpSpare,spare,sizeof(struct yaffs_spare));
190
191                 /* Write the real tags, but override the premarker*/
192                 tmpSpare.page_status = YNOR_PREMARKER;
193                 m18_drv_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(struct yaffs_spare)/4);
194
195                 /* Write a post-marker */
196                 tmpSpare.page_status = YNOR_POSTMARKER;
197                 m18_drv_FlashWrite32(spareAddr,(u32 *)&tmpSpare,sizeof(tmpSpare)/4);
198
199         } else if(spare){
200                 /* This has to be a read-modify-write operation to handle NOR-ness */
201
202                 m18_drv_FlashRead32(spareAddr,(u32 *)&tmpSpare,16/ 4);
203
204                 m18_drv_AndBytes((u8 *)&tmpSpare,(u8 *)spare,sizeof(struct yaffs_spare));
205
206                 m18_drv_FlashWrite32(spareAddr,(u32 *)&tmpSpare,16/ 4);
207         }
208         else {
209                 BUG();
210         }
211
212
213         return YAFFS_OK;
214
215 }
216
217 static int m18_drv_ReadChunkFromNAND(struct yaffs_dev *dev,int nand_chunk,
218                                         u8 *data, int data_len,
219                                         u8 *oob, int oob_len,
220                                         enum yaffs_ecc_result *ecc_result)
221 {
222         struct yaffs_spare *spare = (struct yaffs_spare *)oob;
223
224         u32 *dataAddr = Chunk2DataAddr(dev,nand_chunk);
225         u32 *spareAddr = Chunk2SpareAddr(dev,nand_chunk);
226
227         if(data)
228         {
229                 m18_drv_FlashRead32(dataAddr,(u32 *)data,dev->param.total_bytes_per_chunk / 4);
230         }
231
232         if(oob)
233         {
234                 m18_drv_FlashRead32(spareAddr,(u32 *)spare, oob_len/ 4);
235
236                 /* If the page status is YNOR_POSTMARKER then it was written properly
237                  * so change that to 0xFF so that the rest of yaffs is happy.
238                  */
239                 if(spare->page_status == YNOR_POSTMARKER)
240                         spare->page_status = 0xFF;
241                 else if(spare->page_status != 0xff &&
242                         (spare->page_status | YNOR_PREMARKER) != 0xff)
243                         spare->page_status = YNOR_PREMARKER;
244         }
245
246         if(ecc_result)
247                 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
248
249         return YAFFS_OK;
250
251 }
252
253
254 static int m18_drv_FormatBlock(struct yaffs_dev *dev, int blockNumber)
255 {
256         u32 *blockAddr = Block2Addr(dev,blockNumber);
257         u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
258         u32 formatValue = FORMAT_VALUE;
259
260         m18_drv_FlashEraseBlock(blockAddr);
261         m18_drv_FlashWrite32(formatAddr,&formatValue,1);
262
263         return YAFFS_OK;
264 }
265
266 static int m18_drv_UnformatBlock(struct yaffs_dev *dev, int blockNumber)
267 {
268         u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
269         u32 formatValue = 0;
270
271         m18_drv_FlashWrite32(formatAddr,&formatValue,1);
272
273         return YAFFS_OK;
274 }
275
276 static int m18_drv_IsBlockFormatted(struct yaffs_dev *dev, int blockNumber)
277 {
278         u32 *formatAddr = Block2FormatAddr(dev,blockNumber);
279         u32 formatValue;
280
281
282         m18_drv_FlashRead32(formatAddr,&formatValue,1);
283
284         return (formatValue == FORMAT_VALUE);
285 }
286
287 static int m18_drv_EraseBlockInNAND(struct yaffs_dev *dev, int blockNumber)
288 {
289
290         if(blockNumber < 0 || blockNumber >= BLOCKS_IN_DEVICE)
291         {
292                 yaffs_trace(YAFFS_TRACE_ALWAYS,
293                         "Attempt to erase non-existant block %d\n",
294                         blockNumber);
295                 return YAFFS_FAIL;
296         }
297         else
298         {
299                 m18_drv_UnformatBlock(dev,blockNumber);
300                 m18_drv_FormatBlock(dev,blockNumber);
301                 return YAFFS_OK;
302         }
303
304 }
305
306 static int m18_drv_InitialiseNAND(struct yaffs_dev *dev)
307 {
308         int i;
309
310         m18_drv_FlashInit();
311         /* Go through the blocks formatting them if they are not formatted */
312         for(i = dev->param.start_block; i <= dev->param.end_block; i++){
313                 if(!m18_drv_IsBlockFormatted(dev,i)){
314                         m18_drv_FormatBlock(dev,i);
315                 }
316         }
317         return YAFFS_OK;
318 }
319
320 static int m18_drv_Deinitialise_flash_fn(struct yaffs_dev *dev)
321 {
322         dev=dev;
323
324         m18_drv_FlashDeinit();
325
326         return YAFFS_OK;
327 }
328
329
330 struct yaffs_dev *yaffs_m18_install_drv(const char *name)
331 {
332
333         struct yaffs_dev *dev = malloc(sizeof(struct yaffs_dev));
334         char *name_copy = strdup(name);
335         struct yaffs_param *param;
336         struct yaffs_driver *drv;
337
338
339         if(!dev || !name_copy) {
340                 free(name_copy);
341                 free(dev);
342                 return NULL;
343         }
344
345         param = &dev->param;
346         drv = &dev->drv;
347
348         memset(dev, 0, sizeof(*dev));
349
350         param->name = name_copy;
351
352         param->total_bytes_per_chunk = 1024;
353         param->chunks_per_block =248;
354         param->n_reserved_blocks = 2;
355         param->start_block = 0; // Can use block 0
356         param->end_block = 31; // Last block
357         param->use_nand_ecc = 0; // use YAFFS's ECC
358
359         drv->drv_write_chunk_fn = m18_drv_WriteChunkToNAND;
360         drv->drv_read_chunk_fn = m18_drv_ReadChunkFromNAND;
361         drv->drv_erase_fn = m18_drv_EraseBlockInNAND;
362         drv->drv_initialise_fn = m18_drv_InitialiseNAND;
363         drv->drv_deinitialise_fn = m18_drv_Deinitialise_flash_fn;
364
365         param->n_caches = 10;
366         param->disable_soft_del = 1;
367
368         dev->driver_context = (void *) 1;       // Used to identify the device in fstat.
369
370         yaffs_add_device(dev);
371
372         return NULL;
373 }