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#include "yaffs_guts.h"
15#include "yaffs_tagscompat.h"
16#include "yaffs_ecc.h"
17#include "yaffs_getblockinfo.h"
18#include "yaffs_trace.h"
19
20static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
21
22
23/********** Tags ECC calculations  *********/
24
25
26void yaffs_calc_tags_ecc(struct yaffs_tags *tags)
27{
28	/* Calculate an ecc */
29	unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
30	unsigned i, j;
31	unsigned ecc = 0;
32	unsigned bit = 0;
33
34	tags->ecc = 0;
35
36	for (i = 0; i < 8; i++) {
37		for (j = 1; j & 0xff; j <<= 1) {
38			bit++;
39			if (b[i] & j)
40				ecc ^= bit;
41		}
42	}
43	tags->ecc = ecc;
44}
45
46int yaffs_check_tags_ecc(struct yaffs_tags *tags)
47{
48	unsigned ecc = tags->ecc;
49
50	yaffs_calc_tags_ecc(tags);
51
52	ecc ^= tags->ecc;
53
54	if (ecc && ecc <= 64) {
55		/* TODO: Handle the failure better. Retire? */
56		unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes;
57
58		ecc--;
59
60		b[ecc / 8] ^= (1 << (ecc & 7));
61
62		/* Now recvalc the ecc */
63		yaffs_calc_tags_ecc(tags);
64
65		return 1;	/* recovered error */
66	} else if (ecc) {
67		/* Wierd ecc failure value */
68		/* TODO Need to do somethiong here */
69		return -1;	/* unrecovered error */
70	}
71	return 0;
72}
73
74/********** Tags **********/
75
76static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr,
77				     struct yaffs_tags *tags_ptr)
78{
79	union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
80
81	yaffs_calc_tags_ecc(tags_ptr);
82
83	spare_ptr->tb0 = tu->as_bytes[0];
84	spare_ptr->tb1 = tu->as_bytes[1];
85	spare_ptr->tb2 = tu->as_bytes[2];
86	spare_ptr->tb3 = tu->as_bytes[3];
87	spare_ptr->tb4 = tu->as_bytes[4];
88	spare_ptr->tb5 = tu->as_bytes[5];
89	spare_ptr->tb6 = tu->as_bytes[6];
90	spare_ptr->tb7 = tu->as_bytes[7];
91}
92
93static void yaffs_get_tags_from_spare(struct yaffs_dev *dev,
94				      struct yaffs_spare *spare_ptr,
95				      struct yaffs_tags *tags_ptr)
96{
97	union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr;
98	int result;
99
100	tu->as_bytes[0] = spare_ptr->tb0;
101	tu->as_bytes[1] = spare_ptr->tb1;
102	tu->as_bytes[2] = spare_ptr->tb2;
103	tu->as_bytes[3] = spare_ptr->tb3;
104	tu->as_bytes[4] = spare_ptr->tb4;
105	tu->as_bytes[5] = spare_ptr->tb5;
106	tu->as_bytes[6] = spare_ptr->tb6;
107	tu->as_bytes[7] = spare_ptr->tb7;
108
109	result = yaffs_check_tags_ecc(tags_ptr);
110	if (result > 0)
111		dev->n_tags_ecc_fixed++;
112	else if (result < 0)
113		dev->n_tags_ecc_unfixed++;
114}
115
116static void yaffs_spare_init(struct yaffs_spare *spare)
117{
118	memset(spare, 0xff, sizeof(struct yaffs_spare));
119}
120
121static int yaffs_wr_nand(struct yaffs_dev *dev,
122			 int nand_chunk, const u8 *data,
123			 struct yaffs_spare *spare)
124{
125	int data_size = dev->data_bytes_per_chunk;
126
127	return dev->drv.drv_write_chunk_fn(dev, nand_chunk,
128				data, data_size,
129				(u8 *) spare, sizeof(*spare));
130}
131
132static int yaffs_rd_chunk_nand(struct yaffs_dev *dev,
133			       int nand_chunk,
134			       u8 *data,
135			       struct yaffs_spare *spare,
136			       enum yaffs_ecc_result *ecc_result,
137			       int correct_errors)
138{
139	int ret_val;
140	struct yaffs_spare local_spare;
141	int data_size;
142	int spare_size;
143	int ecc_result1, ecc_result2;
144	u8 calc_ecc[3];
145
146	if (!spare) {
147		/* If we don't have a real spare, then we use a local one. */
148		/* Need this for the calculation of the ecc */
149		spare = &local_spare;
150	}
151	data_size = dev->data_bytes_per_chunk;
152	spare_size = sizeof(struct yaffs_spare);
153
154	if (dev->param.use_nand_ecc)
155		return dev->drv.drv_read_chunk_fn(dev, nand_chunk,
156						data, data_size,
157						(u8 *) spare, spare_size,
158						ecc_result);
159
160
161	/* Handle the ECC at this level. */
162
163	ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk,
164						 data, data_size,
165						 (u8 *)spare, spare_size,
166						NULL);
167	if (!data || !correct_errors)
168		return ret_val;
169
170	/* Do ECC correction if needed. */
171	yaffs_ecc_calc(data, calc_ecc);
172	ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc);
173	yaffs_ecc_calc(&data[256], calc_ecc);
174	ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc);
175
176	if (ecc_result1 > 0) {
177		yaffs_trace(YAFFS_TRACE_ERROR,
178			"**>>yaffs ecc error fix performed on chunk %d:0",
179			nand_chunk);
180		dev->n_ecc_fixed++;
181	} else if (ecc_result1 < 0) {
182		yaffs_trace(YAFFS_TRACE_ERROR,
183			"**>>yaffs ecc error unfixed on chunk %d:0",
184			nand_chunk);
185		dev->n_ecc_unfixed++;
186	}
187
188	if (ecc_result2 > 0) {
189		yaffs_trace(YAFFS_TRACE_ERROR,
190			"**>>yaffs ecc error fix performed on chunk %d:1",
191			nand_chunk);
192		dev->n_ecc_fixed++;
193	} else if (ecc_result2 < 0) {
194		yaffs_trace(YAFFS_TRACE_ERROR,
195			"**>>yaffs ecc error unfixed on chunk %d:1",
196			nand_chunk);
197		dev->n_ecc_unfixed++;
198	}
199
200	if (ecc_result1 || ecc_result2) {
201		/* We had a data problem on this page */
202		yaffs_handle_rd_data_error(dev, nand_chunk);
203	}
204
205	if (ecc_result1 < 0 || ecc_result2 < 0)
206		*ecc_result = YAFFS_ECC_RESULT_UNFIXED;
207	else if (ecc_result1 > 0 || ecc_result2 > 0)
208		*ecc_result = YAFFS_ECC_RESULT_FIXED;
209	else
210		*ecc_result = YAFFS_ECC_RESULT_NO_ERROR;
211
212	return ret_val;
213}
214
215/*
216 * Functions for robustisizing
217 */
218
219static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk)
220{
221	int flash_block = nand_chunk / dev->param.chunks_per_block;
222
223	/* Mark the block for retirement */
224	yaffs_get_block_info(dev, flash_block + dev->block_offset)->
225		needs_retiring = 1;
226	yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
227		"**>>Block %d marked for retirement",
228		flash_block);
229
230	/* TODO:
231	 * Just do a garbage collection on the affected block
232	 * then retire the block
233	 * NB recursion
234	 */
235}
236
237static int yaffs_tags_compat_wr(struct yaffs_dev *dev,
238			 int nand_chunk,
239			 const u8 *data, const struct yaffs_ext_tags *ext_tags)
240{
241	struct yaffs_spare spare;
242	struct yaffs_tags tags;
243
244	yaffs_spare_init(&spare);
245
246	if (ext_tags->is_deleted)
247		spare.page_status = 0;
248	else {
249		tags.obj_id = ext_tags->obj_id;
250		tags.chunk_id = ext_tags->chunk_id;
251
252		tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1);
253
254		if (dev->data_bytes_per_chunk >= 1024)
255			tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3;
256		else
257			tags.n_bytes_msb = 3;
258
259		tags.serial_number = ext_tags->serial_number;
260
261		if (!dev->param.use_nand_ecc && data) {
262			yaffs_ecc_calc(data, spare.ecc1);
263			yaffs_ecc_calc(&data[256], spare.ecc2);
264		}
265
266		yaffs_load_tags_to_spare(&spare, &tags);
267	}
268	return yaffs_wr_nand(dev, nand_chunk, data, &spare);
269}
270
271static int yaffs_tags_compat_rd(struct yaffs_dev *dev,
272			 int nand_chunk,
273			 u8 *data, struct yaffs_ext_tags *ext_tags)
274{
275	struct yaffs_spare spare;
276	struct yaffs_tags tags;
277	enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN;
278	static struct yaffs_spare spare_ff;
279	static int init;
280	int deleted;
281
282	if (!init) {
283		memset(&spare_ff, 0xff, sizeof(spare_ff));
284		init = 1;
285	}
286
287	if (!yaffs_rd_chunk_nand(dev, nand_chunk,
288					data, &spare, &ecc_result, 1))
289		return YAFFS_FAIL;
290
291	/* ext_tags may be NULL */
292	if (!ext_tags)
293		return YAFFS_OK;
294
295	deleted = (hweight8(spare.page_status) < 7) ? 1 : 0;
296
297	ext_tags->is_deleted = deleted;
298	ext_tags->ecc_result = ecc_result;
299	ext_tags->block_bad = 0;	/* We're reading it */
300	/* therefore it is not a bad block */
301	ext_tags->chunk_used =
302		memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0;
303
304	if (ext_tags->chunk_used) {
305		yaffs_get_tags_from_spare(dev, &spare, &tags);
306		ext_tags->obj_id = tags.obj_id;
307		ext_tags->chunk_id = tags.chunk_id;
308		ext_tags->n_bytes = tags.n_bytes_lsb;
309
310		if (dev->data_bytes_per_chunk >= 1024)
311			ext_tags->n_bytes |=
312				(((unsigned)tags.n_bytes_msb) << 10);
313
314		ext_tags->serial_number = tags.serial_number;
315	}
316
317	return YAFFS_OK;
318}
319
320static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block)
321{
322	struct yaffs_spare spare;
323
324	memset(&spare, 0xff, sizeof(struct yaffs_spare));
325
326	spare.block_status = 'Y';
327
328	yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL,
329		      &spare);
330	yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1,
331		      NULL, &spare);
332
333	return YAFFS_OK;
334}
335
336static int yaffs_tags_compat_query_block(struct yaffs_dev *dev,
337				  int block_no,
338				  enum yaffs_block_state *state,
339				  u32 *seq_number)
340{
341	struct yaffs_spare spare0, spare1;
342	static struct yaffs_spare spare_ff;
343	static int init;
344	enum yaffs_ecc_result dummy;
345
346	if (!init) {
347		memset(&spare_ff, 0xff, sizeof(spare_ff));
348		init = 1;
349	}
350
351	*seq_number = 0;
352
353	/* Look for bad block markers in the first two chunks */
354	yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block,
355			    NULL, &spare0, &dummy, 0);
356	yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1,
357			    NULL, &spare1, &dummy, 0);
358
359	if (hweight8(spare0.block_status & spare1.block_status) < 7)
360		*state = YAFFS_BLOCK_STATE_DEAD;
361	else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0)
362		*state = YAFFS_BLOCK_STATE_EMPTY;
363	else
364		*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
365
366	return YAFFS_OK;
367}
368
369void yaffs_tags_compat_install(struct yaffs_dev *dev)
370{
371	if(dev->param.is_yaffs2)
372		return;
373	if(!dev->tagger.write_chunk_tags_fn)
374		dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr;
375	if(!dev->tagger.read_chunk_tags_fn)
376		dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd;
377	if(!dev->tagger.query_block_fn)
378		dev->tagger.query_block_fn = yaffs_tags_compat_query_block;
379	if(!dev->tagger.mark_bad_fn)
380		dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad;
381}
382