Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[MTD] [NAND] nand_ecc.c: fix big endian, strengthen test, add printk

This patch for nand_ecc.c fixes three issues

- fix code so it also works on big endian architectures
- added a printk in case of an uncorrectable ecc error
- strengthen the test for correctable errors (decreasing the chance
that multiple bit faults by accident will be seen as correctable)

Note: the big endian code is only tested in a testbed (running on big endian
hardware) as I cannot rebuild and test a big endian kernel at the moment.
However the only thing that can go wrong is if <asm/byteorder.h> does not
give __BIG_ENDIAN in that case. In my eyes very unlikely.

Signed-off-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

authored by

frans and committed by
David Woodhouse
1077be58 75caf6b5

+33 -11
+33 -11
drivers/mtd/nand/nand_ecc.c
··· 43 43 #include <linux/kernel.h> 44 44 #include <linux/module.h> 45 45 #include <linux/mtd/nand_ecc.h> 46 + #include <asm/byteorder.h> 46 47 #else 47 48 #include <stdint.h> 48 49 struct mtd_info; ··· 52 51 #define MODULE_LICENSE(x) /* x */ 53 52 #define MODULE_AUTHOR(x) /* x */ 54 53 #define MODULE_DESCRIPTION(x) /* x */ 54 + 55 + #define printk printf 56 + #define KERN_ERR "" 55 57 #endif 56 58 57 59 /* ··· 277 273 /* 278 274 * we also need to calculate the row parity for rp0..rp3 279 275 * This is present in par, because par is now 280 - * rp3 rp3 rp2 rp2 276 + * rp3 rp3 rp2 rp2 in little endian and 277 + * rp2 rp2 rp3 rp3 in big endian 281 278 * as well as 282 - * rp1 rp0 rp1 rp0 279 + * rp1 rp0 rp1 rp0 in little endian and 280 + * rp0 rp1 rp0 rp1 in big endian 283 281 * First calculate rp2 and rp3 284 - * (and yes: rp2 = (par ^ rp3) & 0xff; but doing that did not 285 - * give a performance improvement) 286 282 */ 283 + #ifdef __BIG_ENDIAN 284 + rp2 = (par >> 16); 285 + rp2 ^= (rp2 >> 8); 286 + rp2 &= 0xff; 287 + rp3 = par & 0xffff; 288 + rp3 ^= (rp3 >> 8); 289 + rp3 &= 0xff; 290 + #else 287 291 rp3 = (par >> 16); 288 292 rp3 ^= (rp3 >> 8); 289 293 rp3 &= 0xff; 290 294 rp2 = par & 0xffff; 291 295 rp2 ^= (rp2 >> 8); 292 296 rp2 &= 0xff; 297 + #endif 293 298 294 299 /* reduce par to 16 bits then calculate rp1 and rp0 */ 295 300 par ^= (par >> 16); 301 + #ifdef __BIG_ENDIAN 302 + rp0 = (par >> 8) & 0xff; 303 + rp1 = (par & 0xff); 304 + #else 296 305 rp1 = (par >> 8) & 0xff; 297 306 rp0 = (par & 0xff); 307 + #endif 298 308 299 309 /* finally reduce par to 8 bits */ 300 310 par ^= (par >> 8); ··· 399 381 int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, 400 382 unsigned char *read_ecc, unsigned char *calc_ecc) 401 383 { 402 - int nr_bits; 403 384 unsigned char b0, b1, b2; 404 385 unsigned char byte_addr, bit_addr; 405 386 ··· 418 401 419 402 /* check if there are any bitfaults */ 420 403 421 - /* count nr of bits; use table lookup, faster than calculating it */ 422 - nr_bits = bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]; 423 - 424 404 /* repeated if statements are slightly more efficient than switch ... */ 425 405 /* ordered in order of likelihood */ 426 - if (nr_bits == 0) 406 + 407 + if ((b0 | b1 | b2) == 0) 427 408 return 0; /* no error */ 428 - if (nr_bits == 11) { /* correctable error */ 409 + 410 + if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) && 411 + (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) && 412 + (((b2 ^ (b2 >> 1)) & 0x54) == 0x54)) { /* single bit error */ 429 413 /* 430 414 * rp15/13/11/9/7/5/3/1 indicate which byte is the faulty byte 431 415 * cp 5/3/1 indicate the faulty bit. ··· 448 430 /* flip the bit */ 449 431 buf[byte_addr] ^= (1 << bit_addr); 450 432 return 1; 433 + 451 434 } 452 - if (nr_bits == 1) 435 + /* count nr of bits; use table lookup, faster than calculating it */ 436 + if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) 453 437 return 1; /* error in ecc data; no action needed */ 438 + 439 + printk(KERN_ERR "uncorrectable error : "); 454 440 return -1; 455 441 } 456 442 EXPORT_SYMBOL(nand_correct_data);