aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBastian Hecht <hechtb@googlemail.com>2012-05-14 08:14:46 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2012-07-06 13:17:04 -0400
commit6667a6d58e25d351d8fce7a628a8c9c139a8bdc9 (patch)
treeaf1edb613a317991a0451ea1dd5a52b531b18d83
parent623c55caa37203ece6b4450daa0d2d058255da30 (diff)
mtd: sh_flctl: Restructure the hardware ECC handling
There are multiple reasons for a rewrite: - a race exists: when _4ECCEND is set, _4ECCFA may become true too meanwhile, which is lost and a non-correctable error is treated as correctable. - the ECC statistics don't get properly propagated to the base code. - empty pages would get marked as corrupted The rewrite resolves the issues and I hope it gives a more explicit code flow structure. Signed-off-by: Bastian Hecht <hechtb@gmail.com> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/nand/sh_flctl.c121
-rw-r--r--include/linux/mtd/sh_flctl.h10
2 files changed, 88 insertions, 43 deletions
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index 96e242adda6a..bc50e83336bb 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -165,27 +165,56 @@ static void wait_wfifo_ready(struct sh_flctl *flctl)
165 timeout_error(flctl, __func__); 165 timeout_error(flctl, __func__);
166} 166}
167 167
168static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) 168static enum flctl_ecc_res_t wait_recfifo_ready
169 (struct sh_flctl *flctl, int sector_number)
169{ 170{
170 uint32_t timeout = LOOP_TIMEOUT_MAX; 171 uint32_t timeout = LOOP_TIMEOUT_MAX;
171 int checked[4];
172 void __iomem *ecc_reg[4]; 172 void __iomem *ecc_reg[4];
173 int i; 173 int i;
174 int state = FL_SUCCESS;
174 uint32_t data, size; 175 uint32_t data, size;
175 176
176 memset(checked, 0, sizeof(checked)); 177 /*
177 178 * First this loops checks in FLDTCNTR if we are ready to read out the
179 * oob data. This is the case if either all went fine without errors or
180 * if the bottom part of the loop corrected the errors or marked them as
181 * uncorrectable and the controller is given time to push the data into
182 * the FIFO.
183 */
178 while (timeout--) { 184 while (timeout--) {
185 /* check if all is ok and we can read out the OOB */
179 size = readl(FLDTCNTR(flctl)) >> 24; 186 size = readl(FLDTCNTR(flctl)) >> 24;
180 if (size & 0xFF) 187 if ((size & 0xFF) == 4)
181 return 0; /* success */ 188 return state;
189
190 /* check if a correction code has been calculated */
191 if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) {
192 /*
193 * either we wait for the fifo to be filled or a
194 * correction pattern is being generated
195 */
196 udelay(1);
197 continue;
198 }
182 199
183 if (readl(FL4ECCCR(flctl)) & _4ECCFA) 200 /* check for an uncorrectable error */
184 return 1; /* can't correct */ 201 if (readl(FL4ECCCR(flctl)) & _4ECCFA) {
202 /* check if we face a non-empty page */
203 for (i = 0; i < 512; i++) {
204 if (flctl->done_buff[i] != 0xff) {
205 state = FL_ERROR; /* can't correct */
206 break;
207 }
208 }
185 209
186 udelay(1); 210 if (state == FL_SUCCESS)
187 if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) 211 dev_dbg(&flctl->pdev->dev,
212 "reading empty sector %d, ecc error ignored\n",
213 sector_number);
214
215 writel(0, FL4ECCCR(flctl));
188 continue; 216 continue;
217 }
189 218
190 /* start error correction */ 219 /* start error correction */
191 ecc_reg[0] = FL4ECCRESULT0(flctl); 220 ecc_reg[0] = FL4ECCRESULT0(flctl);
@@ -194,28 +223,26 @@ static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number)
194 ecc_reg[3] = FL4ECCRESULT3(flctl); 223 ecc_reg[3] = FL4ECCRESULT3(flctl);
195 224
196 for (i = 0; i < 3; i++) { 225 for (i = 0; i < 3; i++) {
226 uint8_t org;
227 int index;
228
197 data = readl(ecc_reg[i]); 229 data = readl(ecc_reg[i]);
198 if (data != INIT_FL4ECCRESULT_VAL && !checked[i]) {
199 uint8_t org;
200 int index;
201
202 if (flctl->page_size)
203 index = (512 * sector_number) +
204 (data >> 16);
205 else
206 index = data >> 16;
207
208 org = flctl->done_buff[index];
209 flctl->done_buff[index] = org ^ (data & 0xFF);
210 checked[i] = 1;
211 }
212 }
213 230
231 if (flctl->page_size)
232 index = (512 * sector_number) +
233 (data >> 16);
234 else
235 index = data >> 16;
236
237 org = flctl->done_buff[index];
238 flctl->done_buff[index] = org ^ (data & 0xFF);
239 }
240 state = FL_REPAIRABLE;
214 writel(0, FL4ECCCR(flctl)); 241 writel(0, FL4ECCCR(flctl));
215 } 242 }
216 243
217 timeout_error(flctl, __func__); 244 timeout_error(flctl, __func__);
218 return 1; /* timeout */ 245 return FL_TIMEOUT; /* timeout */
219} 246}
220 247
221static void wait_wecfifo_ready(struct sh_flctl *flctl) 248static void wait_wecfifo_ready(struct sh_flctl *flctl)
@@ -259,20 +286,23 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
259 } 286 }
260} 287}
261 288
262static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff, int sector) 289static enum flctl_ecc_res_t read_ecfiforeg
290 (struct sh_flctl *flctl, uint8_t *buff, int sector)
263{ 291{
264 int i; 292 int i;
293 enum flctl_ecc_res_t res;
265 unsigned long *ecc_buf = (unsigned long *)buff; 294 unsigned long *ecc_buf = (unsigned long *)buff;
266 void *fifo_addr = (void *)FLECFIFO(flctl);
267 295
268 for (i = 0; i < 4; i++) { 296 res = wait_recfifo_ready(flctl , sector);
269 if (wait_recfifo_ready(flctl , sector)) 297
270 return 1; 298 if (res != FL_ERROR) {
271 ecc_buf[i] = readl(fifo_addr); 299 for (i = 0; i < 4; i++) {
272 ecc_buf[i] = be32_to_cpu(ecc_buf[i]); 300 ecc_buf[i] = readl(FLECFIFO(flctl));
301 ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
302 }
273 } 303 }
274 304
275 return 0; 305 return res;
276} 306}
277 307
278static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) 308static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
@@ -367,6 +397,7 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
367{ 397{
368 struct sh_flctl *flctl = mtd_to_flctl(mtd); 398 struct sh_flctl *flctl = mtd_to_flctl(mtd);
369 int sector, page_sectors; 399 int sector, page_sectors;
400 enum flctl_ecc_res_t ecc_result;
370 401
371 page_sectors = flctl->page_size ? 4 : 1; 402 page_sectors = flctl->page_size ? 4 : 1;
372 403
@@ -382,17 +413,27 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
382 start_translation(flctl); 413 start_translation(flctl);
383 414
384 for (sector = 0; sector < page_sectors; sector++) { 415 for (sector = 0; sector < page_sectors; sector++) {
385 int ret;
386 read_fiforeg(flctl, 512, 512 * sector); 416 read_fiforeg(flctl, 512, 512 * sector);
387 417
388 ret = read_ecfiforeg(flctl, 418 ecc_result = read_ecfiforeg(flctl,
389 &flctl->done_buff[mtd->writesize + 16 * sector], 419 &flctl->done_buff[mtd->writesize + 16 * sector],
390 sector); 420 sector);
391 421
392 if (ret) 422 switch (ecc_result) {
393 flctl->hwecc_cant_correct[sector] = 1; 423 case FL_REPAIRABLE:
394 424 dev_info(&flctl->pdev->dev,
395 writel(0x0, FL4ECCCR(flctl)); 425 "applied ecc on page 0x%x", page_addr);
426 flctl->mtd.ecc_stats.corrected++;
427 break;
428 case FL_ERROR:
429 dev_warn(&flctl->pdev->dev,
430 "page 0x%x contains corrupted data\n",
431 page_addr);
432 flctl->mtd.ecc_stats.failed++;
433 break;
434 default:
435 ;
436 }
396 } 437 }
397 438
398 wait_completion(flctl); 439 wait_completion(flctl);
diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
index 3feaae062feb..01e4b15b280e 100644
--- a/include/linux/mtd/sh_flctl.h
+++ b/include/linux/mtd/sh_flctl.h
@@ -129,9 +129,15 @@
129#define _4ECCEND (0x1 << 1) /* 4 symbols end */ 129#define _4ECCEND (0x1 << 1) /* 4 symbols end */
130#define _4ECCEXST (0x1 << 0) /* 4 symbols exist */ 130#define _4ECCEXST (0x1 << 0) /* 4 symbols exist */
131 131
132#define INIT_FL4ECCRESULT_VAL 0x03FF03FF
133#define LOOP_TIMEOUT_MAX 0x00010000 132#define LOOP_TIMEOUT_MAX 0x00010000
134 133
134enum flctl_ecc_res_t {
135 FL_SUCCESS,
136 FL_REPAIRABLE,
137 FL_ERROR,
138 FL_TIMEOUT
139};
140
135struct sh_flctl { 141struct sh_flctl {
136 struct mtd_info mtd; 142 struct mtd_info mtd;
137 struct nand_chip chip; 143 struct nand_chip chip;
@@ -151,8 +157,6 @@ struct sh_flctl {
151 uint32_t flcmncr_base; /* base value of FLCMNCR */ 157 uint32_t flcmncr_base; /* base value of FLCMNCR */
152 uint32_t flintdmacr_base; /* irq enable bits */ 158 uint32_t flintdmacr_base; /* irq enable bits */
153 159
154 int hwecc_cant_correct[4];
155
156 unsigned page_size:1; /* NAND page size (0 = 512, 1 = 2048) */ 160 unsigned page_size:1; /* NAND page size (0 = 512, 1 = 2048) */
157 unsigned hwecc:1; /* Hardware ECC (0 = disabled, 1 = enabled) */ 161 unsigned hwecc:1; /* Hardware ECC (0 = disabled, 1 = enabled) */
158 unsigned holden:1; /* Hardware has FLHOLDCR and HOLDEN is set */ 162 unsigned holden:1; /* Hardware has FLHOLDCR and HOLDEN is set */