summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/atmel_nand.c
diff options
context:
space:
mode:
authorJosh Wu <josh.wu@atmel.com>2014-11-10 15:24:00 +0800
committerTom Rini <trini@ti.com>2014-11-17 08:47:18 -0500
commit7df4486d04204e654b5965267bb6c645b3c13052 (patch)
tree83ba7570391fc725ca7a14097f6b1b7bb62610ae /drivers/mtd/nand/atmel_nand.c
parent5b15fd980bfcb49e2b9bc711339cbd6e8731aef8 (diff)
mtd: atmel_nand: runtime to build gf table for pmecc
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>
Diffstat (limited to 'drivers/mtd/nand/atmel_nand.c')
-rw-r--r--drivers/mtd/nand/atmel_nand.c75
1 files changed, 74 insertions, 1 deletions
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 3b6093a81b..620b6e8ff9 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -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;