diff mbox

[U-Boot,v2,1/3] mtd: atmel_nand: runtime to build gf table for pmecc

Message ID 1414549973-23558-2-git-send-email-voice.shen@atmel.com
State Superseded, archived
Delegated to: Andreas Bießmann
Headers show

Commit Message

Bo Shen Oct. 29, 2014, 2:32 a.m. UTC
From: Josh Wu <josh.wu@atmel.com>

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>
---
Changes in v2:
  - rewrite the gf table build function by Josh.

 drivers/mtd/nand/atmel_nand.c     | 75 ++++++++++++++++++++++++++++++++++++++-
 drivers/mtd/nand/atmel_nand_ecc.h |  4 +++
 2 files changed, 78 insertions(+), 1 deletion(-)

Comments

Andreas Bießmann Oct. 29, 2014, 3:06 a.m. UTC | #1
Hi Bo,

On 29.10.14 03:32, Bo Shen wrote:
> From: Josh Wu <josh.wu@atmel.com>
> 
> 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>
> ---
> Changes in v2:
>   - rewrite the gf table build function by Josh.
> 
>  drivers/mtd/nand/atmel_nand.c     | 75 ++++++++++++++++++++++++++++++++++++++-
>  drivers/mtd/nand/atmel_nand_ecc.h |  4 +++
>  2 files changed, 78 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index 9114a86..20fcecb 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -762,6 +762,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)
>  {
> @@ -809,11 +865,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",
> @@ -822,7 +885,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;
> diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
> index 92d4ec5..eac860d 100644
> --- a/drivers/mtd/nand/atmel_nand_ecc.h
> +++ b/drivers/mtd/nand/atmel_nand_ecc.h
> @@ -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
>  
> 

looks way better! I'll apply this these days.

Best regards

Andreas Bießmann
diff mbox

Patch

diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 9114a86..20fcecb 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -762,6 +762,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)
 {
@@ -809,11 +865,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",
@@ -822,7 +885,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;
diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
index 92d4ec5..eac860d 100644
--- a/drivers/mtd/nand/atmel_nand_ecc.h
+++ b/drivers/mtd/nand/atmel_nand_ecc.h
@@ -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