mtd: rawnand: Allow selection of ECC byte ordering at runtime
authorBoris Brezillon <boris.brezillon@bootlin.com>
Tue, 4 Sep 2018 14:23:28 +0000 (16:23 +0200)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 3 Oct 2018 09:12:25 +0000 (11:12 +0200)
Currently, the selection of ECC byte ordering for software hamming is
done at compilation time, which doesn't make sense when ECC byte
calculation is done in hardware and byte ordering is forced by the
hardware engine.
In this case, only the correction is done in software and we want to
force the byte-ordering no matter the value of CONFIG_MTD_NAND_ECC_SMC.

This is typically the case for the FSMC (Smart Media ordering), TMIO and
TXX9NDFMC (regular byte ordering) blocks.

For all other use cases (pure software implementation, SM FTL and
nandecctest), we keep selecting the byte ordering based on the
CONFIG_MTD_NAND_ECC_SMC value. It might not be ideal for SM FTL (I'd
expect Smart Media ordering to be employed by the Smart Media FTL), but
this option doesn't seem to be enabled in the existing _defconfig, so
I can't tell setting sm_order to true is the right choice.

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
drivers/mtd/nand/raw/fsmc_nand.c
drivers/mtd/nand/raw/nand_base.c
drivers/mtd/nand/raw/nand_ecc.c
drivers/mtd/nand/raw/tmio_nand.c
drivers/mtd/nand/raw/txx9ndfmc.c
drivers/mtd/sm_ftl.c
drivers/mtd/tests/mtd_nandecctest.c
include/linux/mtd/nand_ecc.h
include/linux/mtd/rawnand.h

index f9874fc72f3017bfc4ade3ae3f5d7eb5558b5679..70ac8d875218e5010a30ad020f944586eee8241c 100644 (file)
@@ -949,6 +949,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand)
                nand->ecc.correct = nand_correct_data;
                nand->ecc.bytes = 3;
                nand->ecc.strength = 1;
+               nand->ecc.options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
                break;
 
        case NAND_ECC_SOFT:
index dc3955da04269131b7ca3580000868b48fa7e482..05bd0779fe9bf7eae08acca31b7ba30f7592b9b1 100644 (file)
@@ -5045,6 +5045,10 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
                        ecc->size = 256;
                ecc->bytes = 3;
                ecc->strength = 1;
+
+               if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC))
+                       ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
+
                return 0;
        case NAND_ECC_BCH:
                if (!mtd_nand_has_bch()) {
index 93df8e73f5775e77f8504c191eb3584c61db2ef4..4f434753305865d672c79a703219d56a953557b6 100644 (file)
@@ -132,9 +132,10 @@ static const char addressbits[256] = {
  * @buf:       input buffer with raw data
  * @eccsize:   data bytes per ECC step (256 or 512)
  * @code:      output buffer with ECC
+ * @sm_order:  Smart Media byte ordering
  */
 void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
-                      unsigned char *code)
+                         unsigned char *code, bool sm_order)
 {
        int i;
        const uint32_t *bp = (uint32_t *)buf;
@@ -330,45 +331,26 @@ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
         * possible, but benchmarks showed that on the system this is developed
         * the code below is the fastest
         */
-#ifdef CONFIG_MTD_NAND_ECC_SMC
-       code[0] =
-           (invparity[rp7] << 7) |
-           (invparity[rp6] << 6) |
-           (invparity[rp5] << 5) |
-           (invparity[rp4] << 4) |
-           (invparity[rp3] << 3) |
-           (invparity[rp2] << 2) |
-           (invparity[rp1] << 1) |
-           (invparity[rp0]);
-       code[1] =
-           (invparity[rp15] << 7) |
-           (invparity[rp14] << 6) |
-           (invparity[rp13] << 5) |
-           (invparity[rp12] << 4) |
-           (invparity[rp11] << 3) |
-           (invparity[rp10] << 2) |
-           (invparity[rp9] << 1)  |
-           (invparity[rp8]);
-#else
-       code[1] =
-           (invparity[rp7] << 7) |
-           (invparity[rp6] << 6) |
-           (invparity[rp5] << 5) |
-           (invparity[rp4] << 4) |
-           (invparity[rp3] << 3) |
-           (invparity[rp2] << 2) |
-           (invparity[rp1] << 1) |
-           (invparity[rp0]);
-       code[0] =
-           (invparity[rp15] << 7) |
-           (invparity[rp14] << 6) |
-           (invparity[rp13] << 5) |
-           (invparity[rp12] << 4) |
-           (invparity[rp11] << 3) |
-           (invparity[rp10] << 2) |
-           (invparity[rp9] << 1)  |
-           (invparity[rp8]);
-#endif
+       if (sm_order) {
+               code[0] = (invparity[rp7] << 7) | (invparity[rp6] << 6) |
+                         (invparity[rp5] << 5) | (invparity[rp4] << 4) |
+                         (invparity[rp3] << 3) | (invparity[rp2] << 2) |
+                         (invparity[rp1] << 1) | (invparity[rp0]);
+               code[1] = (invparity[rp15] << 7) | (invparity[rp14] << 6) |
+                         (invparity[rp13] << 5) | (invparity[rp12] << 4) |
+                         (invparity[rp11] << 3) | (invparity[rp10] << 2) |
+                         (invparity[rp9] << 1) | (invparity[rp8]);
+       } else {
+               code[1] = (invparity[rp7] << 7) | (invparity[rp6] << 6) |
+                         (invparity[rp5] << 5) | (invparity[rp4] << 4) |
+                         (invparity[rp3] << 3) | (invparity[rp2] << 2) |
+                         (invparity[rp1] << 1) | (invparity[rp0]);
+               code[0] = (invparity[rp15] << 7) | (invparity[rp14] << 6) |
+                         (invparity[rp13] << 5) | (invparity[rp12] << 4) |
+                         (invparity[rp11] << 3) | (invparity[rp10] << 2) |
+                         (invparity[rp9] << 1) | (invparity[rp8]);
+       }
+
        if (eccsize_mult == 1)
                code[2] =
                    (invparity[par & 0xf0] << 7) |
@@ -401,7 +383,9 @@ EXPORT_SYMBOL(__nand_calculate_ecc);
 int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
                       unsigned char *code)
 {
-       __nand_calculate_ecc(buf, chip->ecc.size, code);
+       bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
+
+       __nand_calculate_ecc(buf, chip->ecc.size, code, sm_order);
 
        return 0;
 }
@@ -413,12 +397,13 @@ EXPORT_SYMBOL(nand_calculate_ecc);
  * @read_ecc:  ECC from the chip
  * @calc_ecc:  the ECC calculated from raw data
  * @eccsize:   data bytes per ECC step (256 or 512)
+ * @sm_order:  Smart Media byte order
  *
  * Detect and correct a 1 bit error for eccsize byte block
  */
 int __nand_correct_data(unsigned char *buf,
                        unsigned char *read_ecc, unsigned char *calc_ecc,
-                       unsigned int eccsize)
+                       unsigned int eccsize, bool sm_order)
 {
        unsigned char b0, b1, b2, bit_addr;
        unsigned int byte_addr;
@@ -430,13 +415,14 @@ int __nand_correct_data(unsigned char *buf,
         * we might need the xor result  more than once,
         * so keep them in a local var
        */
-#ifdef CONFIG_MTD_NAND_ECC_SMC
-       b0 = read_ecc[0] ^ calc_ecc[0];
-       b1 = read_ecc[1] ^ calc_ecc[1];
-#else
-       b0 = read_ecc[1] ^ calc_ecc[1];
-       b1 = read_ecc[0] ^ calc_ecc[0];
-#endif
+       if (sm_order) {
+               b0 = read_ecc[0] ^ calc_ecc[0];
+               b1 = read_ecc[1] ^ calc_ecc[1];
+       } else {
+               b0 = read_ecc[1] ^ calc_ecc[1];
+               b1 = read_ecc[0] ^ calc_ecc[0];
+       }
+
        b2 = read_ecc[2] ^ calc_ecc[2];
 
        /* check if there are any bitfaults */
@@ -500,7 +486,10 @@ EXPORT_SYMBOL(__nand_correct_data);
 int nand_correct_data(struct nand_chip *chip, unsigned char *buf,
                      unsigned char *read_ecc, unsigned char *calc_ecc)
 {
-       return __nand_correct_data(buf, read_ecc, calc_ecc, chip->ecc.size);
+       bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
+
+       return __nand_correct_data(buf, read_ecc, calc_ecc, chip->ecc.size,
+                                  sm_order);
 }
 EXPORT_SYMBOL(nand_correct_data);
 
index 3297621241d2d5822c61fe5fd3680e86cb79e719..f3b59e649b7d040eaa8466d7503ae3281261fa6d 100644 (file)
@@ -295,10 +295,11 @@ static int tmio_nand_correct_data(struct nand_chip *chip, unsigned char *buf,
        int r0, r1;
 
        /* assume ecc.size = 512 and ecc.bytes = 6 */
-       r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256);
+       r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256, false);
        if (r0 < 0)
                return r0;
-       r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256);
+       r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256,
+                                false);
        if (r1 < 0)
                return r1;
        return r0 + r1;
index 3a99c8e3f944fdd1cf2b7f57993451b30573015e..ddf0420c09976a39a10dddbbad9cc48edb94610b 100644 (file)
@@ -198,7 +198,8 @@ static int txx9ndfmc_correct_data(struct nand_chip *chip, unsigned char *buf,
        int stat;
 
        for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) {
-               stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256);
+               stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256,
+                                          false);
                if (stat < 0)
                        return stat;
                corrected += stat;
index f3bd86e136033d80e791fc996f0d9516d5e54d48..89227b1d036afd1ab8d51a26ab070f90c56687be 100644 (file)
@@ -221,14 +221,18 @@ static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob)
 {
        uint8_t ecc[3];
 
-       __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc);
-       if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE) < 0)
+       __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc,
+                            IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
+       if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE,
+                               IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC)) < 0)
                return -EIO;
 
        buffer += SM_SMALL_PAGE;
 
-       __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc);
-       if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE) < 0)
+       __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc,
+                            IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
+       if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE,
+                               IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC)) < 0)
                return -EIO;
        return 0;
 }
@@ -393,11 +397,13 @@ restart:
                }
 
                if (ftl->smallpagenand) {
-                       __nand_calculate_ecc(buf + boffset,
-                                               SM_SMALL_PAGE, oob.ecc1);
+                       __nand_calculate_ecc(buf + boffset, SM_SMALL_PAGE,
+                                       oob.ecc1,
+                                       IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
 
                        __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE,
-                                               SM_SMALL_PAGE, oob.ecc2);
+                                       SM_SMALL_PAGE, oob.ecc2,
+                                       IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
                }
                if (!sm_write_sector(ftl, zone, block, boffset,
                                                        buf + boffset, &oob))
index 88b6c81cebbe68123da96775b3a42c3f96da4a11..c71523e945806c1e2f9147a6971b67679932be70 100644 (file)
@@ -121,8 +121,10 @@ static int no_bit_error_verify(void *error_data, void *error_ecc,
        unsigned char calc_ecc[3];
        int ret;
 
-       __nand_calculate_ecc(error_data, size, calc_ecc);
-       ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size);
+       __nand_calculate_ecc(error_data, size, calc_ecc,
+                            IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
+       ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
+                                 IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
        if (ret == 0 && !memcmp(correct_data, error_data, size))
                return 0;
 
@@ -149,8 +151,10 @@ static int single_bit_error_correct(void *error_data, void *error_ecc,
        unsigned char calc_ecc[3];
        int ret;
 
-       __nand_calculate_ecc(error_data, size, calc_ecc);
-       ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size);
+       __nand_calculate_ecc(error_data, size, calc_ecc,
+                            IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
+       ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
+                                 IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
        if (ret == 1 && !memcmp(correct_data, error_data, size))
                return 0;
 
@@ -184,8 +188,10 @@ static int double_bit_error_detect(void *error_data, void *error_ecc,
        unsigned char calc_ecc[3];
        int ret;
 
-       __nand_calculate_ecc(error_data, size, calc_ecc);
-       ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size);
+       __nand_calculate_ecc(error_data, size, calc_ecc,
+                            IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
+       ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size,
+                                 IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
 
        return (ret == -EBADMSG) ? 0 : -EINVAL;
 }
@@ -259,7 +265,8 @@ static int nand_ecc_test_run(const size_t size)
        }
 
        prandom_bytes(correct_data, size);
-       __nand_calculate_ecc(correct_data, size, correct_ecc);
+       __nand_calculate_ecc(correct_data, size, correct_ecc,
+                            IS_ENABLED(CONFIG_MTD_NAND_ECC_SMC));
 
        for (i = 0; i < ARRAY_SIZE(nand_ecc_test); i++) {
                nand_ecc_test[i].prepare(error_data, error_ecc,
index b81fecd5e719a4f45dded3036b944e369af80ab9..0b3bb156c3446ee0dd33234156848a311235bf64 100644 (file)
@@ -19,7 +19,7 @@ struct nand_chip;
  * Calculate 3 byte ECC code for eccsize byte block
  */
 void __nand_calculate_ecc(const u_char *dat, unsigned int eccsize,
-                               u_char *ecc_code);
+                         u_char *ecc_code, bool sm_order);
 
 /*
  * Calculate 3 byte ECC code for 256/512 byte block
@@ -31,7 +31,7 @@ int nand_calculate_ecc(struct nand_chip *chip, const u_char *dat,
  * Detect and correct a 1 bit error for eccsize byte block
  */
 int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc,
-                       unsigned int eccsize);
+                       unsigned int eccsize, bool sm_order);
 
 /*
  * Detect and correct a 1 bit error for 256/512 byte block
index 68d09e01fa5607dd434efc6616d3fa9e9b520d3a..e10b126e148f0a9f8e045ffbfa9b016f9adaed17 100644 (file)
@@ -121,6 +121,12 @@ enum nand_ecc_algo {
 #define NAND_ECC_GENERIC_ERASED_CHECK  BIT(0)
 #define NAND_ECC_MAXIMIZE              BIT(1)
 
+/*
+ * When using software implementation of Hamming, we can specify which byte
+ * ordering should be used.
+ */
+#define NAND_ECC_SOFT_HAMMING_SM_ORDER BIT(2)
+
 /*
  * Option constants for bizarre disfunctionality and real
  * features.