]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
mtd: nand: kirkwood: add RS-ECC encoding support
authorLothar Waßmann <LW@KARO-electronics.de>
Fri, 28 Jul 2017 10:51:14 +0000 (12:51 +0200)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 28 Jul 2017 10:51:14 +0000 (12:51 +0200)
The ROM code of the Kirkwood processors uses a
for large page NAND devices.

Currently all data in NAND is protected with 1-bit Hamming ECC
protection. This is incompatible with the default behaviour of the ROM
code for large page NAND devices, which expects a Reed-Solomon ECC
scheme. Booting accidentally works, since the ROM code tries to read
the NAND without ECC after several tries with ECC.
But in case of a single bit error in the first page in NAND, booting
fails, since the boot image header cannot be read by the CPU.

Add support for writing to flash using the RS-ECC scheme found in the
openocd source code supplied by Marvell.

drivers/mtd/nand/Makefile
drivers/mtd/nand/kirkwood_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_ecc_rs.c [new file with mode: 0644]
include/configs/tk71.h
include/linux/mtd/nand.h
include/linux/mtd/nand_ecc.h

index 1d1b6286510b3c3c7b2d769f2243e929ab7411b2..575ca9262f1b18ef32f0eb978af8a66581834f99 100644 (file)
@@ -52,6 +52,9 @@ COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
 COBJS-$(CONFIG_NAND_JZ4740) += jz4740_nand.o
 COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o
 COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o
+ifneq ($(CONFIG_NAND_KIRKWOOD),)
+       COBJS-$(CONFIG_NAND_ECC_SOFT_RS) += nand_ecc_rs.o
+endif
 COBJS-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o
 COBJS-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o
 COBJS-$(CONFIG_NAND_MXC) += mxc_nand.o
index bdab5aa795befc221e6c62d11c2b4a961d7ab499..1cdf286f6076f934871c823cbe1f58c53c761279 100644 (file)
 #include <asm/io.h>
 #include <asm/arch/kirkwood.h>
 #include <nand.h>
+#include <linux/mtd/nand_ecc.h>
+
+#ifndef CONFIG_NAND_ECC_ALGO
+#define CONFIG_NAND_ECC_ALGO   NAND_ECC_SOFT
+#endif
 
 /* NAND Flash Soc registers */
 struct kwnandf_registers {
@@ -71,10 +76,32 @@ void kw_nand_select_chip(struct mtd_info *mtd, int chip)
        writel(data, &nf_reg->ctrl);
 }
 
+#ifdef CONFIG_NAND_ECC_SOFT_RS
+static struct nand_ecclayout kw_nand_oob_rs = {
+       .eccbytes = 40,
+       .eccpos = {
+               24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+               34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+               44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+               54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+       },
+       .oobfree = {
+               { .offset = 2, .length = 22, },
+       },
+};
+#endif
+
 int board_nand_init(struct nand_chip *nand)
 {
        nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
+#ifndef CONFIG_NAND_ECC_SOFT_RS
        nand->ecc.mode = NAND_ECC_SOFT;
+#else
+       nand->ecc.mode = NAND_ECC_SOFT_RS;
+       nand->ecc.layout = &kw_nand_oob_rs;
+       nand->ecc.size = 512;
+       nand->ecc.bytes = 10;
+#endif
        nand->cmd_ctrl = kw_nand_hwcontrol;
        nand->chip_delay = 40;
        nand->select_chip = kw_nand_select_chip;
index bfd668fa0ac5425f21361971ca5f0efc26bff4f7..47755b1f25ab843926ad49b1e38efaf251b5b3fb 100644 (file)
@@ -3081,6 +3081,25 @@ int nand_scan_tail(struct mtd_info *mtd)
 
                break;
 
+#ifdef CONFIG_NAND_ECC_SOFT_RS
+       case NAND_ECC_SOFT_RS:
+               chip->ecc.calculate = nand_rs_calculate_ecc;
+               chip->ecc.correct = nand_rs_correct_data;
+               chip->ecc.read_page = nand_read_page_swecc;
+               chip->ecc.read_subpage = nand_read_subpage;
+               chip->ecc.write_page = nand_write_page_swecc;
+               chip->ecc.read_page_raw = nand_read_page_raw;
+               chip->ecc.write_page_raw = nand_write_page_raw;
+               chip->ecc.read_oob = nand_read_oob_std;
+               chip->ecc.write_oob = nand_write_oob_std;
+               if (!chip->ecc.size && mtd->oobsize >= 64) {
+                       chip->ecc.size = 512;
+                       chip->ecc.bytes = 10;
+                       printf("Using default ecc size %u and eccbytes %u for OOB size %u\n",
+                              chip->ecc.size, chip->ecc.bytes, mtd->oobsize);
+               }
+               break;
+#endif
        case NAND_ECC_NONE:
                printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
                       "This is not recommended !!\n");
diff --git a/drivers/mtd/nand/nand_ecc_rs.c b/drivers/mtd/nand/nand_ecc_rs.c
new file mode 100644 (file)
index 0000000..30a0406
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Reed-Solomon ECC handling for the Marvell Kirkwood SOC
+ * Copyright (C) 2017 Lothar Waßmann <LW@KARO-electronics.de>
+ *
+ * derived from openocd src/flash/nand/ecc_kw.c:
+ * Copyright (C) 2009 Marvell Semiconductor, Inc.
+ *
+ * Authors: Lennert Buytenhek <buytenh@wantstofly.org>
+ *          Nicolas Pitre <nico@fluxnic.net>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 or (at your option) any
+ * later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#include <common.h>
+#include <asm-generic/errno.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+
+/*****************************************************************************
+ * Arithmetic in GF(2^10) ("F") modulo x^10 + x^3 + 1.
+ *
+ * For multiplication, a discrete log/exponent table is used, with
+ * primitive element x (F is a primitive field, so x is primitive).
+ */
+#define MODPOLY 0x409          /* x^10 + x^3 + 1 in binary */
+
+/*
+ * Maps an integer a [0..1022] to a polynomial b = gf_exp[a] in
+ * GF(2^10) mod x^10 + x^3 + 1 such that b = x ^ a.  There's two
+ * identical copies of this array back-to-back so that we can save
+ * the mod 1023 operation when doing a GF multiplication.
+ */
+static uint16_t gf_exp[1023 + 1023];
+
+/*
+ * Maps a polynomial b in GF(2^10) mod x^10 + x^3 + 1 to an index
+ * a = gf_log[b] in [0..1022] such that b = x ^ a.
+ */
+static uint16_t gf_log[1024];
+
+static void gf_build_log_exp_table(void)
+{
+       int i;
+       int p_i;
+
+       /*
+        * p_i = x ^ i
+        *
+        * Initialise to 1 for i = 0.
+        */
+       p_i = 1;
+
+       for (i = 0; i < 1023; i++) {
+               gf_exp[i] = p_i;
+               gf_exp[i + 1023] = p_i;
+               gf_log[p_i] = i;
+
+               /*
+                * p_i = p_i * x
+                */
+               p_i <<= 1;
+               if (p_i & (1 << 10))
+                       p_i ^= MODPOLY;
+       }
+#ifdef DEBUG
+       for (i = 0; i < ARRAY_SIZE(gf_log); i++) {
+               printf("exp[%03x]=%4d log[%03x]=%4d\n", i, gf_exp[i], i, gf_log[i]);
+       }
+       for (; i < ARRAY_SIZE(gf_exp); i++) {
+               printf("exp[%03x]=%4d\n", i, gf_exp[i]);
+       }
+#endif
+}
+
+/*****************************************************************************
+ * Reed-Solomon code
+ *
+ * This implements a (1023,1015) Reed-Solomon ECC code over GF(2^10)
+ * mod x^10 + x^3 + 1, shortened to (520,512).  The ECC data consists
+ * of 8 10-bit symbols, or 10 8-bit bytes.
+ *
+ * Given 512 bytes of data, computes 10 bytes of ECC.
+ *
+ * This is done by converting the 512 bytes to 512 10-bit symbols
+ * (elements of F), interpreting those symbols as a polynomial in F[X]
+ * by taking symbol 0 as the coefficient of X^8 and symbol 511 as the
+ * coefficient of X^519, and calculating the residue of that polynomial
+ * divided by the generator polynomial, which gives us the 8 ECC symbols
+ * as the remainder.  Finally, we convert the 8 10-bit ECC symbols to 10
+ * 8-bit bytes.
+ *
+ * The generator polynomial is hardcoded, as that is faster, but it
+ * can be computed by taking the primitive element a = x (in F), and
+ * constructing a polynomial in F[X] with roots a, a^2, a^3, ..., a^8
+ * by multiplying the minimal polynomials for those roots (which are
+ * just 'x - a^i' for each i).
+ *
+ * Note: due to unfortunate circumstances, the bootrom in the Kirkwood SOC
+ * expects the ECC to be computed backward, i.e. from the last byte down
+ * to the first one.
+ */
+int nand_rs_calculate_ecc(struct mtd_info *mtd, const uint8_t *data,
+                         uint8_t *ecc)
+{
+       unsigned int r7, r6, r5, r4, r3, r2, r1, r0;
+       int i;
+       static int tables_initialized;
+
+       if (!tables_initialized) {
+               printf("Using RS-ECC\n");
+               gf_build_log_exp_table();
+               tables_initialized = 1;
+       }
+
+       /*
+        * Load bytes 504..511 of the data into r.
+        */
+       r0 = data[504];
+       r1 = data[505];
+       r2 = data[506];
+       r3 = data[507];
+       r4 = data[508];
+       r5 = data[509];
+       r6 = data[510];
+       r7 = data[511];
+
+       /*
+        * Shift bytes 503..0 (in that order) into r0, followed
+        * by eight zero bytes, while reducing the polynomial by the
+        * generator polynomial in every step.
+        */
+       for (i = 503; i >= -8; i--) {
+               unsigned int d = (i >= 0) ? data[i] : 0;
+
+               if (r7) {
+                       uint16_t *t = &gf_exp[gf_log[r7]];
+
+                       r7 = r6 ^ t[0x21c]; // 540
+                       r6 = r5 ^ t[0x181]; // 385
+                       r5 = r4 ^ t[0x18e]; // 398
+                       r4 = r3 ^ t[0x25f]; // 607
+                       r3 = r2 ^ t[0x197]; // 407
+                       r2 = r1 ^ t[0x193]; // 403
+                       r1 = r0 ^ t[0x237]; // 567
+                       r0 = d  ^ t[0x024]; //  36
+               } else {
+                       r7 = r6;
+                       r6 = r5;
+                       r5 = r4;
+                       r4 = r3;
+                       r3 = r2;
+                       r2 = r1;
+                       r1 = r0;
+                       r0 = d;
+               }
+       }
+
+       ecc[0] = r0;
+       ecc[1] = (r0 >> 8) | (r1 << 2);
+       ecc[2] = (r1 >> 6) | (r2 << 4);
+       ecc[3] = (r2 >> 4) | (r3 << 6);
+       ecc[4] = (r3 >> 2);
+       ecc[5] = r4;
+       ecc[6] = (r4 >> 8) | (r5 << 2);
+       ecc[7] = (r5 >> 6) | (r6 << 4);
+       ecc[8] = (r6 >> 4) | (r7 << 6);
+       ecc[9] = (r7 >> 2);
+
+       debug("ECC: %03x %03x %03x %03x %03x %03x %03x %03x\n",
+             r0, r1, r2, r3, r4, r5, r6, r7);
+
+       return 0;
+}
+
+int nand_rs_correct_data(struct mtd_info *mtd, u_char *dat,
+                        u_char *read_ecc, u_char *calc_ecc)
+{
+       static int once;
+       if (!once) {
+               printf("Reading NAND with RS-ECC not supported\n");
+               once++;
+       }
+       return -EINVAL;
+}
index 88f49e7ddc5641062bda5060f74a9ec63b0c1dfc..03fd994d6ae3b2d392e0e814b65f53995a2255fd 100644 (file)
@@ -73,6 +73,9 @@
  * NAND flash
  */
 #ifdef CONFIG_CMD_NAND
+#define CONFIG_NAND_ECC_SOFT_RS
+#define CONFIG_NAND_ECC_BCH
+#define CONFIG_BCH
 #define CONFIG_MTD_DEVICE
 #define CONFIG_MTD_PARTITIONS
 #define CONFIG_JFFS2_NAND
index 82704de0835561907e5d77467c7c27458890f2e4..e0ad9ae37df0557d713a5c0a5e13b9ed6d00fb34 100644 (file)
@@ -131,6 +131,7 @@ typedef enum {
        NAND_ECC_HW_SYNDROME,
        NAND_ECC_HW_OOB_FIRST,
        NAND_ECC_SOFT_BCH,
+       NAND_ECC_SOFT_RS,
 } nand_ecc_modes_t;
 
 /*
index 090da505425d77d105acb6133d63dfaaa7c95b7b..dc1ddeda9a45c681cc34022e6e138e38af36a6e2 100644 (file)
@@ -25,4 +25,11 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code
  */
 int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
 
+#ifdef CONFIG_NAND_ECC_SOFT_RS
+/*
+ * Calculate 10 byte RS ECC syndrome for 512 byte block
+ */
+int nand_rs_calculate_ecc(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc);
+#endif
+
 #endif /* __MTD_NAND_ECC_H__ */