xref: /openbmc/u-boot/fs/yaffs2/yaffs_nandif.c (revision 753ac610880e6e563d0384bb114f8b41df89e520)
1*753ac610SCharles Manning /*
2*753ac610SCharles Manning  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3*753ac610SCharles Manning  *
4*753ac610SCharles Manning  * Copyright (C) 2002-2011 Aleph One Ltd.
5*753ac610SCharles Manning  *   for Toby Churchill Ltd and Brightstar Engineering
6*753ac610SCharles Manning  *
7*753ac610SCharles Manning  * Created by Charles Manning <charles@aleph1.co.uk>
8*753ac610SCharles Manning  *
9*753ac610SCharles Manning  * This program is free software; you can redistribute it and/or modify
10*753ac610SCharles Manning  * it under the terms of the GNU General Public License version 2 as
11*753ac610SCharles Manning  * published by the Free Software Foundation.
12*753ac610SCharles Manning  */
13*753ac610SCharles Manning 
14*753ac610SCharles Manning #include "yportenv.h"
15*753ac610SCharles Manning #include "yaffs_guts.h"
16*753ac610SCharles Manning 
17*753ac610SCharles Manning 
18*753ac610SCharles Manning #include "yaffs_nandif.h"
19*753ac610SCharles Manning #include "yaffs_packedtags2.h"
20*753ac610SCharles Manning 
21*753ac610SCharles Manning #include "yramsim.h"
22*753ac610SCharles Manning 
23*753ac610SCharles Manning #include "yaffs_trace.h"
24*753ac610SCharles Manning #include "yaffsfs.h"
25*753ac610SCharles Manning 
26*753ac610SCharles Manning 
27*753ac610SCharles Manning /* NB For use with inband tags....
28*753ac610SCharles Manning  * We assume that the data buffer is of size totalBytersPerChunk so that
29*753ac610SCharles Manning  * we can also use it to load the tags.
30*753ac610SCharles Manning  */
ynandif_WriteChunkWithTagsToNAND(struct yaffs_dev * dev,int nand_chunk,const u8 * data,const struct yaffs_ext_tags * tags)31*753ac610SCharles Manning int ynandif_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk,
32*753ac610SCharles Manning 				      const u8 *data,
33*753ac610SCharles Manning 				      const struct yaffs_ext_tags *tags)
34*753ac610SCharles Manning {
35*753ac610SCharles Manning 
36*753ac610SCharles Manning 	int retval = 0;
37*753ac610SCharles Manning 	struct yaffs_packed_tags2 pt;
38*753ac610SCharles Manning 	void *spare;
39*753ac610SCharles Manning 	unsigned spareSize = 0;
40*753ac610SCharles Manning 	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
41*753ac610SCharles Manning 
42*753ac610SCharles Manning 	yaffs_trace(YAFFS_TRACE_MTD,
43*753ac610SCharles Manning 		"nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p",
44*753ac610SCharles Manning 		nand_chunk, data, tags);
45*753ac610SCharles Manning 
46*753ac610SCharles Manning 
47*753ac610SCharles Manning 	/* For yaffs2 writing there must be both data and tags.
48*753ac610SCharles Manning 	 * If we're using inband tags, then the tags are stuffed into
49*753ac610SCharles Manning 	 * the end of the data buffer.
50*753ac610SCharles Manning 	 */
51*753ac610SCharles Manning 
52*753ac610SCharles Manning 	if (dev->param.inband_tags) {
53*753ac610SCharles Manning 		struct yaffs_packed_tags2_tags_only *pt2tp;
54*753ac610SCharles Manning 
55*753ac610SCharles Manning 		pt2tp = (struct yaffs_packed_tags2_tags_only *)
56*753ac610SCharles Manning 			(data + dev->data_bytes_per_chunk);
57*753ac610SCharles Manning 		yaffs_pack_tags2_tags_only(pt2tp, tags);
58*753ac610SCharles Manning 		spare = NULL;
59*753ac610SCharles Manning 		spareSize = 0;
60*753ac610SCharles Manning 	} else {
61*753ac610SCharles Manning 		yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
62*753ac610SCharles Manning 		spare = &pt;
63*753ac610SCharles Manning 		spareSize = sizeof(struct yaffs_packed_tags2);
64*753ac610SCharles Manning 	}
65*753ac610SCharles Manning 
66*753ac610SCharles Manning 	retval = geometry->writeChunk(dev, nand_chunk,
67*753ac610SCharles Manning 				data, dev->param.total_bytes_per_chunk,
68*753ac610SCharles Manning 				spare, spareSize);
69*753ac610SCharles Manning 
70*753ac610SCharles Manning 	return retval;
71*753ac610SCharles Manning }
72*753ac610SCharles Manning 
ynandif_ReadChunkWithTagsFromNAND(struct yaffs_dev * dev,int nand_chunk,u8 * data,struct yaffs_ext_tags * tags)73*753ac610SCharles Manning int ynandif_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk,
74*753ac610SCharles Manning 				       u8 *data, struct yaffs_ext_tags *tags)
75*753ac610SCharles Manning {
76*753ac610SCharles Manning 	struct yaffs_packed_tags2 pt;
77*753ac610SCharles Manning 	int localData = 0;
78*753ac610SCharles Manning 	void *spare = NULL;
79*753ac610SCharles Manning 	unsigned spareSize;
80*753ac610SCharles Manning 	int retval = 0;
81*753ac610SCharles Manning 	int eccStatus; /* 0 = ok, 1 = fixed, -1 = unfixed */
82*753ac610SCharles Manning 	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
83*753ac610SCharles Manning 
84*753ac610SCharles Manning 	yaffs_trace(YAFFS_TRACE_MTD,
85*753ac610SCharles Manning 		"nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p",
86*753ac610SCharles Manning 		nand_chunk, data, tags);
87*753ac610SCharles Manning 
88*753ac610SCharles Manning 	if (!tags) {
89*753ac610SCharles Manning 		spare = NULL;
90*753ac610SCharles Manning 		spareSize = 0;
91*753ac610SCharles Manning 	} else if (dev->param.inband_tags) {
92*753ac610SCharles Manning 
93*753ac610SCharles Manning 		if (!data) {
94*753ac610SCharles Manning 			localData = 1;
95*753ac610SCharles Manning 			data = yaffs_get_temp_buffer(dev);
96*753ac610SCharles Manning 		}
97*753ac610SCharles Manning 		spare = NULL;
98*753ac610SCharles Manning 		spareSize = 0;
99*753ac610SCharles Manning 	} else {
100*753ac610SCharles Manning 		spare = &pt;
101*753ac610SCharles Manning 		spareSize = sizeof(struct yaffs_packed_tags2);
102*753ac610SCharles Manning 	}
103*753ac610SCharles Manning 
104*753ac610SCharles Manning 	retval = geometry->readChunk(dev, nand_chunk,
105*753ac610SCharles Manning 				 data,
106*753ac610SCharles Manning 				 data ? dev->param.total_bytes_per_chunk : 0,
107*753ac610SCharles Manning 				 spare, spareSize,
108*753ac610SCharles Manning 				 &eccStatus);
109*753ac610SCharles Manning 
110*753ac610SCharles Manning 	if (dev->param.inband_tags) {
111*753ac610SCharles Manning 		if (tags) {
112*753ac610SCharles Manning 			struct yaffs_packed_tags2_tags_only *pt2tp;
113*753ac610SCharles Manning 			pt2tp = (struct yaffs_packed_tags2_tags_only *)
114*753ac610SCharles Manning 					&data[dev->data_bytes_per_chunk];
115*753ac610SCharles Manning 			yaffs_unpack_tags2_tags_only(tags, pt2tp);
116*753ac610SCharles Manning 		}
117*753ac610SCharles Manning 	} else {
118*753ac610SCharles Manning 		if (tags)
119*753ac610SCharles Manning 			yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
120*753ac610SCharles Manning 	}
121*753ac610SCharles Manning 
122*753ac610SCharles Manning 	if (tags && tags->chunk_used) {
123*753ac610SCharles Manning 		if (eccStatus < 0 ||
124*753ac610SCharles Manning 		   tags->ecc_result == YAFFS_ECC_RESULT_UNFIXED)
125*753ac610SCharles Manning 			tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
126*753ac610SCharles Manning 		else if (eccStatus > 0 ||
127*753ac610SCharles Manning 			     tags->ecc_result == YAFFS_ECC_RESULT_FIXED)
128*753ac610SCharles Manning 			tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
129*753ac610SCharles Manning 		else
130*753ac610SCharles Manning 			tags->ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
131*753ac610SCharles Manning 	}
132*753ac610SCharles Manning 
133*753ac610SCharles Manning 	if (localData)
134*753ac610SCharles Manning 		yaffs_release_temp_buffer(dev, data);
135*753ac610SCharles Manning 
136*753ac610SCharles Manning 	return retval;
137*753ac610SCharles Manning }
138*753ac610SCharles Manning 
ynandif_MarkNANDBlockBad(struct yaffs_dev * dev,int blockId)139*753ac610SCharles Manning int ynandif_MarkNANDBlockBad(struct yaffs_dev *dev, int blockId)
140*753ac610SCharles Manning {
141*753ac610SCharles Manning 	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
142*753ac610SCharles Manning 
143*753ac610SCharles Manning 	return geometry->markBlockBad(dev, blockId);
144*753ac610SCharles Manning }
145*753ac610SCharles Manning 
ynandif_EraseBlockInNAND(struct yaffs_dev * dev,int blockId)146*753ac610SCharles Manning int ynandif_EraseBlockInNAND(struct yaffs_dev *dev, int blockId)
147*753ac610SCharles Manning {
148*753ac610SCharles Manning 	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
149*753ac610SCharles Manning 
150*753ac610SCharles Manning 	return geometry->eraseBlock(dev, blockId);
151*753ac610SCharles Manning 
152*753ac610SCharles Manning }
153*753ac610SCharles Manning 
154*753ac610SCharles Manning 
ynandif_IsBlockOk(struct yaffs_dev * dev,int blockId)155*753ac610SCharles Manning static int ynandif_IsBlockOk(struct yaffs_dev *dev, int blockId)
156*753ac610SCharles Manning {
157*753ac610SCharles Manning 	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
158*753ac610SCharles Manning 
159*753ac610SCharles Manning 	return geometry->checkBlockOk(dev, blockId);
160*753ac610SCharles Manning }
161*753ac610SCharles Manning 
ynandif_QueryNANDBlock(struct yaffs_dev * dev,int blockId,enum yaffs_block_state * state,u32 * seq_number)162*753ac610SCharles Manning int ynandif_QueryNANDBlock(struct yaffs_dev *dev, int blockId,
163*753ac610SCharles Manning 		enum yaffs_block_state *state, u32 *seq_number)
164*753ac610SCharles Manning {
165*753ac610SCharles Manning 	unsigned chunkNo;
166*753ac610SCharles Manning 	struct yaffs_ext_tags tags;
167*753ac610SCharles Manning 
168*753ac610SCharles Manning 	*seq_number = 0;
169*753ac610SCharles Manning 
170*753ac610SCharles Manning 	chunkNo = blockId * dev->param.chunks_per_block;
171*753ac610SCharles Manning 
172*753ac610SCharles Manning 	if (!ynandif_IsBlockOk(dev, blockId)) {
173*753ac610SCharles Manning 		*state = YAFFS_BLOCK_STATE_DEAD;
174*753ac610SCharles Manning 	} else {
175*753ac610SCharles Manning 		ynandif_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &tags);
176*753ac610SCharles Manning 
177*753ac610SCharles Manning 		if (!tags.chunk_used) {
178*753ac610SCharles Manning 			*state = YAFFS_BLOCK_STATE_EMPTY;
179*753ac610SCharles Manning 		} else {
180*753ac610SCharles Manning 			*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
181*753ac610SCharles Manning 			*seq_number = tags.seq_number;
182*753ac610SCharles Manning 		}
183*753ac610SCharles Manning 	}
184*753ac610SCharles Manning 
185*753ac610SCharles Manning 	return YAFFS_OK;
186*753ac610SCharles Manning }
187*753ac610SCharles Manning 
188*753ac610SCharles Manning 
ynandif_InitialiseNAND(struct yaffs_dev * dev)189*753ac610SCharles Manning int ynandif_InitialiseNAND(struct yaffs_dev *dev)
190*753ac610SCharles Manning {
191*753ac610SCharles Manning 	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
192*753ac610SCharles Manning 
193*753ac610SCharles Manning 	geometry->initialise(dev);
194*753ac610SCharles Manning 
195*753ac610SCharles Manning 	return YAFFS_OK;
196*753ac610SCharles Manning }
197*753ac610SCharles Manning 
ynandif_Deinitialise_flash_fn(struct yaffs_dev * dev)198*753ac610SCharles Manning int ynandif_Deinitialise_flash_fn(struct yaffs_dev *dev)
199*753ac610SCharles Manning {
200*753ac610SCharles Manning 	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context);
201*753ac610SCharles Manning 
202*753ac610SCharles Manning 	geometry->deinitialise(dev);
203*753ac610SCharles Manning 
204*753ac610SCharles Manning 	return YAFFS_OK;
205*753ac610SCharles Manning }
206*753ac610SCharles Manning 
207*753ac610SCharles Manning 
208*753ac610SCharles Manning struct yaffs_dev *
yaffs_add_dev_from_geometry(const YCHAR * name,const struct ynandif_Geometry * geometry)209*753ac610SCharles Manning 	yaffs_add_dev_from_geometry(const YCHAR *name,
210*753ac610SCharles Manning 					const struct ynandif_Geometry *geometry)
211*753ac610SCharles Manning {
212*753ac610SCharles Manning 	YCHAR *clonedName = malloc(sizeof(YCHAR) *
213*753ac610SCharles Manning 				(strnlen(name, YAFFS_MAX_NAME_LENGTH)+1));
214*753ac610SCharles Manning 	struct yaffs_dev *dev = malloc(sizeof(struct yaffs_dev));
215*753ac610SCharles Manning 	struct yaffs_param *param;
216*753ac610SCharles Manning 
217*753ac610SCharles Manning 	if (dev && clonedName) {
218*753ac610SCharles Manning 		memset(dev, 0, sizeof(struct yaffs_dev));
219*753ac610SCharles Manning 		strcpy(clonedName, name);
220*753ac610SCharles Manning 
221*753ac610SCharles Manning 		param = &dev->param;
222*753ac610SCharles Manning 
223*753ac610SCharles Manning 		param->name = clonedName;
224*753ac610SCharles Manning 		param->write_chunk_tags_fn = ynandif_WriteChunkWithTagsToNAND;
225*753ac610SCharles Manning 		param->read_chunk_tags_fn = ynandif_ReadChunkWithTagsFromNAND;
226*753ac610SCharles Manning 		param->erase_fn = ynandif_EraseBlockInNAND;
227*753ac610SCharles Manning 		param->initialise_flash_fn = ynandif_InitialiseNAND;
228*753ac610SCharles Manning 		param->query_block_fn = ynandif_QueryNANDBlock;
229*753ac610SCharles Manning 		param->bad_block_fn = ynandif_MarkNANDBlockBad;
230*753ac610SCharles Manning 		param->n_caches = 20;
231*753ac610SCharles Manning 		param->start_block = geometry->start_block;
232*753ac610SCharles Manning 		param->end_block   = geometry->end_block;
233*753ac610SCharles Manning 		param->total_bytes_per_chunk  = geometry->dataSize;
234*753ac610SCharles Manning 		param->spare_bytes_per_chunk  = geometry->spareSize;
235*753ac610SCharles Manning 		param->inband_tags		  = geometry->inband_tags;
236*753ac610SCharles Manning 		param->chunks_per_block	  = geometry->pagesPerBlock;
237*753ac610SCharles Manning 		param->use_nand_ecc		  = geometry->hasECC;
238*753ac610SCharles Manning 		param->is_yaffs2		  = geometry->useYaffs2;
239*753ac610SCharles Manning 		param->n_reserved_blocks	  = 5;
240*753ac610SCharles Manning 		dev->driver_context		  = (void *)geometry;
241*753ac610SCharles Manning 
242*753ac610SCharles Manning 		yaffs_add_device(dev);
243*753ac610SCharles Manning 
244*753ac610SCharles Manning 		return dev;
245*753ac610SCharles Manning 	}
246*753ac610SCharles Manning 
247*753ac610SCharles Manning 	free(dev);
248*753ac610SCharles Manning 	free(clonedName);
249*753ac610SCharles Manning 
250*753ac610SCharles Manning 	return NULL;
251*753ac610SCharles Manning }
252