mtd: atmel_nand: runtime to build gf table for pmecc
authorJosh Wu <josh.wu@atmel.com>
Mon, 10 Nov 2014 07:24:00 +0000 (15:24 +0800)
committerTom Rini <trini@ti.com>
Mon, 17 Nov 2014 13:47:18 +0000 (08:47 -0500)
As in SAMA5D4 SoC, the gf table in ROM code can not be seen.
So, when we try to use PMECC, we need to build it when do
initialization.
Add a macro NO_GALOIS_TABLE_IN_ROM in soc header file. If it
is defined we will build gf table runtime.

The PMECC use the BCH algorithm, so based on the build_gf_tables()
function in lib/bch.c, we can build the Galois Field lookup table.

Signed-off-by: Josh Wu <josh.wu@atmel.com>
Signed-off-by: Bo Shen <voice.shen@atmel.com>
Signed-off-by: Andreas Bießmann <andreas.devel@googlemail.com>
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/atmel_nand_ecc.h

index 3b6093a81b1dac898d059004bd992eeaacf75278..620b6e8ff9a4cfac6745eb6477718ce14a93d05f 100644 (file)
@@ -763,6 +763,62 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host,
 }
 #endif
 
+#if defined(NO_GALOIS_TABLE_IN_ROM)
+static uint16_t *pmecc_galois_table;
+static inline int deg(unsigned int poly)
+{
+       /* polynomial degree is the most-significant bit index */
+       return fls(poly) - 1;
+}
+
+static int build_gf_tables(int mm, unsigned int poly,
+                          int16_t *index_of, int16_t *alpha_to)
+{
+       unsigned int i, x = 1;
+       const unsigned int k = 1 << deg(poly);
+       unsigned int nn = (1 << mm) - 1;
+
+       /* primitive polynomial must be of degree m */
+       if (k != (1u << mm))
+               return -EINVAL;
+
+       for (i = 0; i < nn; i++) {
+               alpha_to[i] = x;
+               index_of[x] = i;
+               if (i && (x == 1))
+                       /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
+                       return -EINVAL;
+               x <<= 1;
+               if (x & k)
+                       x ^= poly;
+       }
+
+       alpha_to[nn] = 1;
+       index_of[0] = 0;
+
+       return 0;
+}
+
+static uint16_t *create_lookup_table(int sector_size)
+{
+       int degree = (sector_size == 512) ?
+                       PMECC_GF_DIMENSION_13 :
+                       PMECC_GF_DIMENSION_14;
+       unsigned int poly = (sector_size == 512) ?
+                       PMECC_GF_13_PRIMITIVE_POLY :
+                       PMECC_GF_14_PRIMITIVE_POLY;
+       int table_size = (sector_size == 512) ?
+                       PMECC_INDEX_TABLE_SIZE_512 :
+                       PMECC_INDEX_TABLE_SIZE_1024;
+
+       int16_t *addr = kzalloc(2 * table_size * sizeof(uint16_t), GFP_KERNEL);
+       if (addr && build_gf_tables(degree, poly, addr, addr + table_size))
+               return NULL;
+
+       return (uint16_t *)addr;
+}
+#endif
+
 static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
                struct mtd_info *mtd)
 {
@@ -810,11 +866,18 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
        sector_size = host->pmecc_sector_size;
 
        /* TODO: need check whether cap & sector_size is validate */
-
+#if defined(NO_GALOIS_TABLE_IN_ROM)
+       /*
+        * As pmecc_rom_base is the begin of the gallois field table, So the
+        * index offset just set as 0.
+        */
+       host->pmecc_index_table_offset = 0;
+#else
        if (host->pmecc_sector_size == 512)
                host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512;
        else
                host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_1024;
+#endif
 
        MTDDEBUG(MTD_DEBUG_LEVEL1,
                "Initialize PMECC params, cap: %d, sector: %d\n",
@@ -823,7 +886,17 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
        host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC;
        host->pmerrloc = (struct pmecc_errloc_regs __iomem *)
                        ATMEL_BASE_PMERRLOC;
+#if defined(NO_GALOIS_TABLE_IN_ROM)
+       pmecc_galois_table = create_lookup_table(host->pmecc_sector_size);
+       if (!pmecc_galois_table) {
+               dev_err(host->dev, "out of memory\n");
+               return -ENOMEM;
+       }
+
+       host->pmecc_rom_base = (void __iomem *)pmecc_galois_table;
+#else
        host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM;
+#endif
 
        /* ECC is calculated for the whole page (1 step) */
        nand->ecc.size = mtd->writesize;
index 92d4ec59fd29f38bf02491f0bdb137dcbc7961de..eac860d13ca73638131f175fd28d6a38e47d738b 100644 (file)
@@ -141,6 +141,10 @@ struct pmecc_errloc_regs {
 #define PMECC_GF_DIMENSION_13                  13
 #define PMECC_GF_DIMENSION_14                  14
 
+/* Primitive Polynomial used by PMECC */
+#define PMECC_GF_13_PRIMITIVE_POLY             0x201b
+#define PMECC_GF_14_PRIMITIVE_POLY             0x4443
+
 #define PMECC_INDEX_TABLE_SIZE_512             0x2000
 #define PMECC_INDEX_TABLE_SIZE_1024            0x4000