1/*
2 * YAFFS: Yet another FFS. A NAND-flash specific file system.
3 * yaffs_ramem.c  NAND emulation on top of a chunk of RAM
4 *
5 * Copyright (C) 2002 Aleph One Ltd.
6 *   for Toby Churchill Ltd and Brightstar Engineering
7 *
8 * Created by Charles Manning <charles@aleph1.co.uk>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 */
15 //yaffs_ramem2k.c: RAM emulation in-kernel for 2K pages (YAFFS2)
16
17
18const char *yaffs_ramem2k_c_version = "$Id: yaffs_ramem2k.c,v 1.1 2005/08/09 01:00:37 charles Exp $";
19
20#ifndef __KERNEL__
21#define CONFIG_YAFFS_RAM_ENABLED
22#else
23#include <linux/config.h>
24#endif
25
26#ifdef CONFIG_YAFFS_RAM_ENABLED
27
28#include "yportenv.h"
29
30#include "yaffs_nandemul2k.h"
31#include "yaffs_guts.h"
32#include "yaffsinterface.h"
33#include "devextras.h"
34#include "yaffs_packedtags2.h"
35
36
37
38#define EM_SIZE_IN_MEG (32)
39#define PAGE_DATA_SIZE  (2048)
40#define PAGE_SPARE_SIZE (64)
41#define PAGES_PER_BLOCK (64)
42
43
44
45#define EM_SIZE_IN_BYTES (EM_SIZE_IN_MEG * (1<<20))
46
47#define PAGE_TOTAL_SIZE (PAGE_DATA_SIZE+PAGE_SPARE_SIZE)
48
49#define BLOCK_TOTAL_SIZE (PAGES_PER_BLOCK * PAGE_TOTAL_SIZE)
50
51#define BLOCKS_PER_MEG ((1<<20)/(PAGES_PER_BLOCK * PAGE_DATA_SIZE))
52
53
54typedef struct
55{
56	__u8 data[PAGE_TOTAL_SIZE]; // Data + spare
57	int empty;      // is this empty?
58} nandemul_Page;
59
60
61typedef struct
62{
63	nandemul_Page *page[PAGES_PER_BLOCK];
64	int damaged;
65} nandemul_Block;
66
67
68
69typedef struct
70{
71	nandemul_Block**block;
72	int nBlocks;
73} nandemul_Device;
74
75static nandemul_Device ned;
76
77static int sizeInMB = EM_SIZE_IN_MEG;
78
79
80static void nandemul_yield(int n)
81{
82#ifdef __KERNEL__
83	if(n > 0) schedule_timeout(n);
84#endif
85
86}
87
88
89static void nandemul_ReallyEraseBlock(int blockNumber)
90{
91	int i;
92
93	nandemul_Block *blk;
94
95	if(blockNumber < 0 || blockNumber >= ned.nBlocks)
96	{
97		return;
98	}
99
100	blk = ned.block[blockNumber];
101
102	for(i = 0; i < PAGES_PER_BLOCK; i++)
103	{
104		memset(blk->page[i],0xff,sizeof(nandemul_Page));
105		blk->page[i]->empty = 1;
106	}
107	nandemul_yield(2);
108}
109
110
111static int nandemul2k_CalcNBlocks(void)
112{
113	return EM_SIZE_IN_MEG * BLOCKS_PER_MEG;
114}
115
116
117
118static int  CheckInit(void)
119{
120	static int initialised = 0;
121
122	int i,j;
123
124	int fail = 0;
125	int nBlocks;
126
127	int nAllocated = 0;
128
129	if(initialised)
130	{
131		return YAFFS_OK;
132	}
133
134
135	ned.nBlocks = nBlocks = nandemul2k_CalcNBlocks();
136
137
138	ned.block = YMALLOC(sizeof(nandemul_Block*) * nBlocks );
139
140	if(!ned.block) return YAFFS_FAIL;
141
142
143
144
145
146	for(i=fail=0; i <nBlocks; i++)
147	{
148
149		nandemul_Block *blk;
150
151		if(!(blk = ned.block[i] = YMALLOC(sizeof(nandemul_Block))))
152		{
153		 fail = 1;
154		}
155		else
156		{
157			for(j = 0; j < PAGES_PER_BLOCK; j++)
158			{
159				if((blk->page[j] = YMALLOC(sizeof(nandemul_Page))) == 0)
160				{
161					fail = 1;
162				}
163			}
164			nandemul_ReallyEraseBlock(i);
165			ned.block[i]->damaged = 0;
166			nAllocated++;
167		}
168	}
169
170	if(fail)
171	{
172		//Todo thump pages
173
174		for(i = 0; i < nAllocated; i++)
175		{
176			YFREE(ned.block[i]);
177		}
178		YFREE(ned.block);
179
180		T(YAFFS_TRACE_ALWAYS,("Allocation failed, could only allocate %dMB of %dMB requested.\n",
181		   nAllocated/64,sizeInMB));
182		return 0;
183	}
184
185	ned.nBlocks = nBlocks;
186
187	initialised = 1;
188
189	return 1;
190}
191
192int nandemul2k_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_ExtendedTags *tags)
193{
194	int blk;
195	int pg;
196	int i;
197
198	__u8 *x;
199
200
201	blk = chunkInNAND/PAGES_PER_BLOCK;
202	pg = chunkInNAND%PAGES_PER_BLOCK;
203
204
205	if(data)
206	{
207		x = ned.block[blk]->page[pg]->data;
208
209		for(i = 0; i < PAGE_DATA_SIZE; i++)
210		{
211			x[i] &=data[i];
212		}
213
214		ned.block[blk]->page[pg]->empty = 0;
215	}
216
217
218	if(tags)
219	{
220		x = &ned.block[blk]->page[pg]->data[PAGE_DATA_SIZE];
221
222		yaffs_PackTags2((yaffs_PackedTags2 *)x,tags);
223
224	}
225
226	if(tags || data)
227	{
228		nandemul_yield(1);
229	}
230
231	return YAFFS_OK;
232}
233
234
235int nandemul2k_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_ExtendedTags *tags)
236{
237	int blk;
238	int pg;
239
240	__u8 *x;
241
242
243
244	blk = chunkInNAND/PAGES_PER_BLOCK;
245	pg = chunkInNAND%PAGES_PER_BLOCK;
246
247
248	if(data)
249	{
250		memcpy(data,ned.block[blk]->page[pg]->data,PAGE_DATA_SIZE);
251	}
252
253
254	if(tags)
255	{
256		x = &ned.block[blk]->page[pg]->data[PAGE_DATA_SIZE];
257
258		yaffs_UnpackTags2(tags,(yaffs_PackedTags2 *)x);
259	}
260
261	return YAFFS_OK;
262}
263
264
265static int nandemul2k_CheckChunkErased(yaffs_Device *dev,int chunkInNAND)
266{
267	int blk;
268	int pg;
269	int i;
270
271
272
273	blk = chunkInNAND/PAGES_PER_BLOCK;
274	pg = chunkInNAND%PAGES_PER_BLOCK;
275
276
277	for(i = 0; i < PAGE_TOTAL_SIZE; i++)
278	{
279		if(ned.block[blk]->page[pg]->data[i] != 0xFF)
280		{
281			return YAFFS_FAIL;
282		}
283	}
284
285	return YAFFS_OK;
286
287}
288
289int nandemul2k_EraseBlockInNAND(yaffs_Device *dev, int blockNumber)
290{
291
292
293	if(blockNumber < 0 || blockNumber >= ned.nBlocks)
294	{
295		T(YAFFS_TRACE_ALWAYS,("Attempt to erase non-existant block %d\n",blockNumber));
296	}
297	else if(ned.block[blockNumber]->damaged)
298	{
299		T(YAFFS_TRACE_ALWAYS,("Attempt to erase damaged block %d\n",blockNumber));
300	}
301	else
302	{
303		nandemul_ReallyEraseBlock(blockNumber);
304	}
305
306	return YAFFS_OK;
307}
308
309int nandemul2k_InitialiseNAND(yaffs_Device *dev)
310{
311	CheckInit();
312	return YAFFS_OK;
313}
314
315int nandemul2k_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
316{
317
318	__u8 *x;
319
320	x = &ned.block[blockNo]->page[0]->data[PAGE_DATA_SIZE];
321
322	memset(x,0,sizeof(yaffs_PackedTags2));
323
324
325	return YAFFS_OK;
326
327}
328
329int nandemul2k_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState *state, int *sequenceNumber)
330{
331	yaffs_ExtendedTags tags;
332	int chunkNo;
333
334	*sequenceNumber = 0;
335
336	chunkNo = blockNo * dev->nChunksPerBlock;
337
338	nandemul2k_ReadChunkWithTagsFromNAND(dev,chunkNo,NULL,&tags);
339	if(tags.blockBad)
340	{
341		*state = YAFFS_BLOCK_STATE_DEAD;
342	}
343	else if(!tags.chunkUsed)
344	{
345		*state = YAFFS_BLOCK_STATE_EMPTY;
346	}
347	else if(tags.chunkUsed)
348	{
349		*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
350		*sequenceNumber = tags.sequenceNumber;
351	}
352	return YAFFS_OK;
353}
354
355int nandemul2k_GetBytesPerChunk(void) { return PAGE_DATA_SIZE;}
356
357int nandemul2k_GetChunksPerBlock(void) { return PAGES_PER_BLOCK; }
358int nandemul2k_GetNumberOfBlocks(void) {return nandemul2k_CalcNBlocks();}
359
360
361#endif //YAFFS_RAM_ENABLED
362
363