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>
This commit is contained in:
Josh Wu 2014-11-10 15:24:00 +08:00 committed by Tom Rini
parent 5b15fd980b
commit 7df4486d04
2 changed files with 78 additions and 1 deletions

View 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;

View 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