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