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 *********/
26 void yaffs_calc_tags_ecc(struct yaffs_tags
*tags
)
28 /* Calculate an ecc */
29 unsigned char *b
= ((union yaffs_tags_union
*)tags
)->as_bytes
;
36 for (i
= 0; i
< 8; i
++) {
37 for (j
= 1; j
& 0xff; j
<<= 1) {
46 int yaffs_check_tags_ecc(struct yaffs_tags
*tags
)
48 unsigned ecc
= tags
->ecc
;
50 yaffs_calc_tags_ecc(tags
);
54 if (ecc
&& ecc
<= 64) {
55 /* TODO: Handle the failure better. Retire? */
56 unsigned char *b
= ((union yaffs_tags_union
*)tags
)->as_bytes
;
60 b
[ecc
/ 8] ^= (1 << (ecc
& 7));
62 /* Now recvalc the ecc */
63 yaffs_calc_tags_ecc(tags
);
65 return 1; /* recovered error */
67 /* Wierd ecc failure value */
68 /* TODO Need to do somethiong here */
69 return -1; /* unrecovered error */
74 /********** Tags **********/
76 static void yaffs_load_tags_to_spare(struct yaffs_spare
*spare_ptr
,
77 struct yaffs_tags
*tags_ptr
)
79 union yaffs_tags_union
*tu
= (union yaffs_tags_union
*)tags_ptr
;
81 yaffs_calc_tags_ecc(tags_ptr
);
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];
93 static void yaffs_get_tags_from_spare(struct yaffs_dev
*dev
,
94 struct yaffs_spare
*spare_ptr
,
95 struct yaffs_tags
*tags_ptr
)
97 union yaffs_tags_union
*tu
= (union yaffs_tags_union
*)tags_ptr
;
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
;
109 result
= yaffs_check_tags_ecc(tags_ptr
);
111 dev
->n_tags_ecc_fixed
++;
113 dev
->n_tags_ecc_unfixed
++;
116 static void yaffs_spare_init(struct yaffs_spare
*spare
)
118 memset(spare
, 0xff, sizeof(struct yaffs_spare
));
121 static int yaffs_wr_nand(struct yaffs_dev
*dev
,
122 int nand_chunk
, const u8
*data
,
123 struct yaffs_spare
*spare
)
125 int data_size
= dev
->data_bytes_per_chunk
;
127 return dev
->drv
.drv_write_chunk_fn(dev
, nand_chunk
,
129 (u8
*) spare
, sizeof(*spare
));
132 static int yaffs_rd_chunk_nand(struct yaffs_dev
*dev
,
135 struct yaffs_spare
*spare
,
136 enum yaffs_ecc_result
*ecc_result
,
140 struct yaffs_spare local_spare
;
143 int ecc_result1
, ecc_result2
;
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
;
151 data_size
= dev
->data_bytes_per_chunk
;
152 spare_size
= sizeof(struct yaffs_spare
);
154 if (dev
->param
.use_nand_ecc
)
155 return dev
->drv
.drv_read_chunk_fn(dev
, nand_chunk
,
157 (u8
*) spare
, spare_size
,
161 /* Handle the ECC at this level. */
163 ret_val
= dev
->drv
.drv_read_chunk_fn(dev
, nand_chunk
,
165 (u8
*)spare
, spare_size
,
167 if (!data
|| !correct_errors
)
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
);
176 if (ecc_result1
> 0) {
177 yaffs_trace(YAFFS_TRACE_ERROR
,
178 "**>>yaffs ecc error fix performed on chunk %d:0",
181 } else if (ecc_result1
< 0) {
182 yaffs_trace(YAFFS_TRACE_ERROR
,
183 "**>>yaffs ecc error unfixed on chunk %d:0",
185 dev
->n_ecc_unfixed
++;
188 if (ecc_result2
> 0) {
189 yaffs_trace(YAFFS_TRACE_ERROR
,
190 "**>>yaffs ecc error fix performed on chunk %d:1",
193 } else if (ecc_result2
< 0) {
194 yaffs_trace(YAFFS_TRACE_ERROR
,
195 "**>>yaffs ecc error unfixed on chunk %d:1",
197 dev
->n_ecc_unfixed
++;
200 if (ecc_result1
|| ecc_result2
) {
201 /* We had a data problem on this page */
202 yaffs_handle_rd_data_error(dev
, nand_chunk
);
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
;
210 *ecc_result
= YAFFS_ECC_RESULT_NO_ERROR
;
216 * Functions for robustisizing
219 static void yaffs_handle_rd_data_error(struct yaffs_dev
*dev
, int nand_chunk
)
221 int flash_block
= nand_chunk
/ dev
->param
.chunks_per_block
;
223 /* Mark the block for retirement */
224 yaffs_get_block_info(dev
, flash_block
+ dev
->block_offset
)->
226 yaffs_trace(YAFFS_TRACE_ERROR
| YAFFS_TRACE_BAD_BLOCKS
,
227 "**>>Block %d marked for retirement",
231 * Just do a garbage collection on the affected block
232 * then retire the block
237 static int yaffs_tags_compat_wr(struct yaffs_dev
*dev
,
239 const u8
*data
, const struct yaffs_ext_tags
*ext_tags
)
241 struct yaffs_spare spare
;
242 struct yaffs_tags tags
;
244 yaffs_spare_init(&spare
);
246 if (ext_tags
->is_deleted
)
247 spare
.page_status
= 0;
249 tags
.obj_id
= ext_tags
->obj_id
;
250 tags
.chunk_id
= ext_tags
->chunk_id
;
252 tags
.n_bytes_lsb
= ext_tags
->n_bytes
& (1024 - 1);
254 if (dev
->data_bytes_per_chunk
>= 1024)
255 tags
.n_bytes_msb
= (ext_tags
->n_bytes
>> 10) & 3;
257 tags
.n_bytes_msb
= 3;
259 tags
.serial_number
= ext_tags
->serial_number
;
261 if (!dev
->param
.use_nand_ecc
&& data
) {
262 yaffs_ecc_calc(data
, spare
.ecc1
);
263 yaffs_ecc_calc(&data
[256], spare
.ecc2
);
266 yaffs_load_tags_to_spare(&spare
, &tags
);
268 return yaffs_wr_nand(dev
, nand_chunk
, data
, &spare
);
271 static int yaffs_tags_compat_rd(struct yaffs_dev
*dev
,
273 u8
*data
, struct yaffs_ext_tags
*ext_tags
)
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
;
283 memset(&spare_ff
, 0xff, sizeof(spare_ff
));
287 if (!yaffs_rd_chunk_nand(dev
, nand_chunk
,
288 data
, &spare
, &ecc_result
, 1))
291 /* ext_tags may be NULL */
295 deleted
= (hweight8(spare
.page_status
) < 7) ? 1 : 0;
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;
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
;
310 if (dev
->data_bytes_per_chunk
>= 1024)
312 (((unsigned)tags
.n_bytes_msb
) << 10);
314 ext_tags
->serial_number
= tags
.serial_number
;
320 static int yaffs_tags_compat_mark_bad(struct yaffs_dev
*dev
, int flash_block
)
322 struct yaffs_spare spare
;
324 memset(&spare
, 0xff, sizeof(struct yaffs_spare
));
326 spare
.block_status
= 'Y';
328 yaffs_wr_nand(dev
, flash_block
* dev
->param
.chunks_per_block
, NULL
,
330 yaffs_wr_nand(dev
, flash_block
* dev
->param
.chunks_per_block
+ 1,
336 static int yaffs_tags_compat_query_block(struct yaffs_dev
*dev
,
338 enum yaffs_block_state
*state
,
341 struct yaffs_spare spare0
, spare1
;
342 static struct yaffs_spare spare_ff
;
344 enum yaffs_ecc_result dummy
;
347 memset(&spare_ff
, 0xff, sizeof(spare_ff
));
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);
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
;
364 *state
= YAFFS_BLOCK_STATE_NEEDS_SCAN
;
369 void yaffs_tags_compat_install(struct yaffs_dev
*dev
)
371 if(dev
->param
.is_yaffs2
)
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
;