adm5120: mark the rb1xx subtarget as broken
[openwrt/staging/dedeckeh.git] / target / linux / generic / files / fs / yaffs2 / yaffs_tagscompat.c
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
20 static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk);
21
22
23 /********** Tags ECC calculations *********/
24
25
26 void 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
46 int 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
76 static 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
93 static 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
116 static void yaffs_spare_init(struct yaffs_spare *spare)
117 {
118 memset(spare, 0xff, sizeof(struct yaffs_spare));
119 }
120
121 static 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
132 static 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
219 static 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
237 static 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
271 static 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
320 static 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
336 static 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
369 void 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 }