2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2011 Aleph One Ltd.
5 * for Toby Churchill Ltd and Brightstar Engineering
7 * Created by Charles Manning <charles@aleph1.co.uk>
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.
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"
20 static void yaffs_handle_rd_data_error(struct yaffs_dev
*dev
, int nand_chunk
);
23 /********** Tags ECC calculations *********/
25 void yaffs_calc_ecc(const u8
*data
, struct yaffs_spare
*spare
)
27 yaffs_ecc_calc(data
, spare
->ecc1
);
28 yaffs_ecc_calc(&data
[256], spare
->ecc2
);
31 void yaffs_calc_tags_ecc(struct yaffs_tags
*tags
)
33 /* Calculate an ecc */
34 unsigned char *b
= ((union yaffs_tags_union
*)tags
)->as_bytes
;
41 for (i
= 0; i
< 8; i
++) {
42 for (j
= 1; j
& 0xff; j
<<= 1) {
51 int yaffs_check_tags_ecc(struct yaffs_tags
*tags
)
53 unsigned ecc
= tags
->ecc
;
55 yaffs_calc_tags_ecc(tags
);
59 if (ecc
&& ecc
<= 64) {
60 /* TODO: Handle the failure better. Retire? */
61 unsigned char *b
= ((union yaffs_tags_union
*)tags
)->as_bytes
;
65 b
[ecc
/ 8] ^= (1 << (ecc
& 7));
67 /* Now recvalc the ecc */
68 yaffs_calc_tags_ecc(tags
);
70 return 1; /* recovered error */
72 /* Wierd ecc failure value */
73 /* TODO Need to do somethiong here */
74 return -1; /* unrecovered error */
79 /********** Tags **********/
81 static void yaffs_load_tags_to_spare(struct yaffs_spare
*spare_ptr
,
82 struct yaffs_tags
*tags_ptr
)
84 union yaffs_tags_union
*tu
= (union yaffs_tags_union
*)tags_ptr
;
86 yaffs_calc_tags_ecc(tags_ptr
);
88 spare_ptr
->tb0
= tu
->as_bytes
[0];
89 spare_ptr
->tb1
= tu
->as_bytes
[1];
90 spare_ptr
->tb2
= tu
->as_bytes
[2];
91 spare_ptr
->tb3
= tu
->as_bytes
[3];
92 spare_ptr
->tb4
= tu
->as_bytes
[4];
93 spare_ptr
->tb5
= tu
->as_bytes
[5];
94 spare_ptr
->tb6
= tu
->as_bytes
[6];
95 spare_ptr
->tb7
= tu
->as_bytes
[7];
98 static void yaffs_get_tags_from_spare(struct yaffs_dev
*dev
,
99 struct yaffs_spare
*spare_ptr
,
100 struct yaffs_tags
*tags_ptr
)
102 union yaffs_tags_union
*tu
= (union yaffs_tags_union
*)tags_ptr
;
105 tu
->as_bytes
[0] = spare_ptr
->tb0
;
106 tu
->as_bytes
[1] = spare_ptr
->tb1
;
107 tu
->as_bytes
[2] = spare_ptr
->tb2
;
108 tu
->as_bytes
[3] = spare_ptr
->tb3
;
109 tu
->as_bytes
[4] = spare_ptr
->tb4
;
110 tu
->as_bytes
[5] = spare_ptr
->tb5
;
111 tu
->as_bytes
[6] = spare_ptr
->tb6
;
112 tu
->as_bytes
[7] = spare_ptr
->tb7
;
114 result
= yaffs_check_tags_ecc(tags_ptr
);
116 dev
->n_tags_ecc_fixed
++;
118 dev
->n_tags_ecc_unfixed
++;
121 static void yaffs_spare_init(struct yaffs_spare
*spare
)
123 memset(spare
, 0xff, sizeof(struct yaffs_spare
));
126 static int yaffs_wr_nand(struct yaffs_dev
*dev
,
127 int nand_chunk
, const u8
*data
,
128 struct yaffs_spare
*spare
)
130 if (nand_chunk
< dev
->param
.start_block
* dev
->param
.chunks_per_block
) {
131 yaffs_trace(YAFFS_TRACE_ERROR
,
132 "**>> yaffs chunk %d is not valid",
137 return dev
->param
.write_chunk_fn(dev
, nand_chunk
, data
, spare
);
140 static int yaffs_rd_chunk_nand(struct yaffs_dev
*dev
,
143 struct yaffs_spare
*spare
,
144 enum yaffs_ecc_result
*ecc_result
,
148 struct yaffs_spare local_spare
;
151 /* If we don't have a real spare, then we use a local one. */
152 /* Need this for the calculation of the ecc */
153 spare
= &local_spare
;
156 if (!dev
->param
.use_nand_ecc
) {
158 dev
->param
.read_chunk_fn(dev
, nand_chunk
, data
, spare
);
159 if (data
&& correct_errors
) {
160 /* Do ECC correction */
161 /* Todo handle any errors */
162 int ecc_result1
, ecc_result2
;
165 yaffs_ecc_calc(data
, calc_ecc
);
167 yaffs_ecc_correct(data
, spare
->ecc1
, calc_ecc
);
168 yaffs_ecc_calc(&data
[256], calc_ecc
);
170 yaffs_ecc_correct(&data
[256], spare
->ecc2
,
173 if (ecc_result1
> 0) {
174 yaffs_trace(YAFFS_TRACE_ERROR
,
175 "**>>yaffs ecc error fix performed on chunk %d:0",
178 } else if (ecc_result1
< 0) {
179 yaffs_trace(YAFFS_TRACE_ERROR
,
180 "**>>yaffs ecc error unfixed on chunk %d:0",
182 dev
->n_ecc_unfixed
++;
185 if (ecc_result2
> 0) {
186 yaffs_trace(YAFFS_TRACE_ERROR
,
187 "**>>yaffs ecc error fix performed on chunk %d:1",
190 } else if (ecc_result2
< 0) {
191 yaffs_trace(YAFFS_TRACE_ERROR
,
192 "**>>yaffs ecc error unfixed on chunk %d:1",
194 dev
->n_ecc_unfixed
++;
197 if (ecc_result1
|| ecc_result2
) {
198 /* We had a data problem on this page */
199 yaffs_handle_rd_data_error(dev
, nand_chunk
);
202 if (ecc_result1
< 0 || ecc_result2
< 0)
203 *ecc_result
= YAFFS_ECC_RESULT_UNFIXED
;
204 else if (ecc_result1
> 0 || ecc_result2
> 0)
205 *ecc_result
= YAFFS_ECC_RESULT_FIXED
;
207 *ecc_result
= YAFFS_ECC_RESULT_NO_ERROR
;
210 /* Must allocate enough memory for spare+2*sizeof(int) */
211 /* for ecc results from device. */
212 struct yaffs_nand_spare nspare
;
214 memset(&nspare
, 0, sizeof(nspare
));
216 ret_val
= dev
->param
.read_chunk_fn(dev
, nand_chunk
, data
,
217 (struct yaffs_spare
*)
219 memcpy(spare
, &nspare
, sizeof(struct yaffs_spare
));
220 if (data
&& correct_errors
) {
221 if (nspare
.eccres1
> 0) {
222 yaffs_trace(YAFFS_TRACE_ERROR
,
223 "**>>mtd ecc error fix performed on chunk %d:0",
225 } else if (nspare
.eccres1
< 0) {
226 yaffs_trace(YAFFS_TRACE_ERROR
,
227 "**>>mtd ecc error unfixed on chunk %d:0",
231 if (nspare
.eccres2
> 0) {
232 yaffs_trace(YAFFS_TRACE_ERROR
,
233 "**>>mtd ecc error fix performed on chunk %d:1",
235 } else if (nspare
.eccres2
< 0) {
236 yaffs_trace(YAFFS_TRACE_ERROR
,
237 "**>>mtd ecc error unfixed on chunk %d:1",
241 if (nspare
.eccres1
|| nspare
.eccres2
) {
242 /* We had a data problem on this page */
243 yaffs_handle_rd_data_error(dev
, nand_chunk
);
246 if (nspare
.eccres1
< 0 || nspare
.eccres2
< 0)
247 *ecc_result
= YAFFS_ECC_RESULT_UNFIXED
;
248 else if (nspare
.eccres1
> 0 || nspare
.eccres2
> 0)
249 *ecc_result
= YAFFS_ECC_RESULT_FIXED
;
251 *ecc_result
= YAFFS_ECC_RESULT_NO_ERROR
;
259 * Functions for robustisizing
262 static void yaffs_handle_rd_data_error(struct yaffs_dev
*dev
, int nand_chunk
)
264 int flash_block
= nand_chunk
/ dev
->param
.chunks_per_block
;
266 /* Mark the block for retirement */
267 yaffs_get_block_info(dev
, flash_block
+ dev
->block_offset
)->
269 yaffs_trace(YAFFS_TRACE_ERROR
| YAFFS_TRACE_BAD_BLOCKS
,
270 "**>>Block %d marked for retirement",
274 * Just do a garbage collection on the affected block
275 * then retire the block
280 int yaffs_tags_compat_wr(struct yaffs_dev
*dev
,
282 const u8
*data
, const struct yaffs_ext_tags
*ext_tags
)
284 struct yaffs_spare spare
;
285 struct yaffs_tags tags
;
287 yaffs_spare_init(&spare
);
289 if (ext_tags
->is_deleted
)
290 spare
.page_status
= 0;
292 tags
.obj_id
= ext_tags
->obj_id
;
293 tags
.chunk_id
= ext_tags
->chunk_id
;
295 tags
.n_bytes_lsb
= ext_tags
->n_bytes
& (1024 - 1);
297 if (dev
->data_bytes_per_chunk
>= 1024)
298 tags
.n_bytes_msb
= (ext_tags
->n_bytes
>> 10) & 3;
300 tags
.n_bytes_msb
= 3;
302 tags
.serial_number
= ext_tags
->serial_number
;
304 if (!dev
->param
.use_nand_ecc
&& data
)
305 yaffs_calc_ecc(data
, &spare
);
307 yaffs_load_tags_to_spare(&spare
, &tags
);
309 return yaffs_wr_nand(dev
, nand_chunk
, data
, &spare
);
312 int yaffs_tags_compat_rd(struct yaffs_dev
*dev
,
314 u8
*data
, struct yaffs_ext_tags
*ext_tags
)
316 struct yaffs_spare spare
;
317 struct yaffs_tags tags
;
318 enum yaffs_ecc_result ecc_result
= YAFFS_ECC_RESULT_UNKNOWN
;
319 static struct yaffs_spare spare_ff
;
324 memset(&spare_ff
, 0xff, sizeof(spare_ff
));
328 if (!yaffs_rd_chunk_nand(dev
, nand_chunk
,
329 data
, &spare
, &ecc_result
, 1))
332 /* ext_tags may be NULL */
336 deleted
= (hweight8(spare
.page_status
) < 7) ? 1 : 0;
338 ext_tags
->is_deleted
= deleted
;
339 ext_tags
->ecc_result
= ecc_result
;
340 ext_tags
->block_bad
= 0; /* We're reading it */
341 /* therefore it is not a bad block */
342 ext_tags
->chunk_used
=
343 memcmp(&spare_ff
, &spare
, sizeof(spare_ff
)) ? 1 : 0;
345 if (ext_tags
->chunk_used
) {
346 yaffs_get_tags_from_spare(dev
, &spare
, &tags
);
347 ext_tags
->obj_id
= tags
.obj_id
;
348 ext_tags
->chunk_id
= tags
.chunk_id
;
349 ext_tags
->n_bytes
= tags
.n_bytes_lsb
;
351 if (dev
->data_bytes_per_chunk
>= 1024)
353 (((unsigned)tags
.n_bytes_msb
) << 10);
355 ext_tags
->serial_number
= tags
.serial_number
;
361 int yaffs_tags_compat_mark_bad(struct yaffs_dev
*dev
, int flash_block
)
363 struct yaffs_spare spare
;
365 memset(&spare
, 0xff, sizeof(struct yaffs_spare
));
367 spare
.block_status
= 'Y';
369 yaffs_wr_nand(dev
, flash_block
* dev
->param
.chunks_per_block
, NULL
,
371 yaffs_wr_nand(dev
, flash_block
* dev
->param
.chunks_per_block
+ 1,
377 int yaffs_tags_compat_query_block(struct yaffs_dev
*dev
,
379 enum yaffs_block_state
*state
,
382 struct yaffs_spare spare0
, spare1
;
383 static struct yaffs_spare spare_ff
;
385 enum yaffs_ecc_result dummy
;
388 memset(&spare_ff
, 0xff, sizeof(spare_ff
));
394 yaffs_rd_chunk_nand(dev
, block_no
* dev
->param
.chunks_per_block
, NULL
,
396 yaffs_rd_chunk_nand(dev
, block_no
* dev
->param
.chunks_per_block
+ 1,
397 NULL
, &spare1
, &dummy
, 1);
399 if (hweight8(spare0
.block_status
& spare1
.block_status
) < 7)
400 *state
= YAFFS_BLOCK_STATE_DEAD
;
401 else if (memcmp(&spare_ff
, &spare0
, sizeof(spare_ff
)) == 0)
402 *state
= YAFFS_BLOCK_STATE_EMPTY
;
404 *state
= YAFFS_BLOCK_STATE_NEEDS_SCAN
;