All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/5] optimize and clean-up of OMAP NAND and ELM driver
@ 2013-07-15 14:55 ` Pekon Gupta
  0 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, benoit.cousson, avinashphilipk, balbi, linux-mtd,
	linux-omap, Pekon Gupta

This patch series builds the base for adding support for newer H/W based ECC
schemes for NAND flash. This patch cleans-up redundant code among various
ECC schemes and improves scalability and readability.
This patch affects following data-paths: 
[PATCH 1/5] NAND chip->ecc.correct(): Detects and corrects ECC errors on read accesses.
[PATCH 2/5] NAND chip->ecc.calculate(): Calculates and fetches ECC syndrome from controller.
[PATCH 3/5] NAND chip->ecc.hwctl(): Configures H/W controller for Read/Write data acccesses.
[PATCH 4/5] ELM  add checks and updates code for scalability.
[PATCH 5/5] NAND code clean-up to improve readability.


Pekon Gupta (5):
  mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes
  mtd: nand: omap: optimize chip->ecc.calculate() for H/W ECC schemes
  mtd: nand: omap: optimize chip->ecc.hwctl() for H/W ECC schemes
  mtd: devices: elm: add checks ELM H/W constrains, driver code cleanup
  mtd: nand: omap: code clean-up and white-space fixes

 drivers/mtd/devices/elm.c         | 114 ++---
 drivers/mtd/nand/omap2.c          | 906 ++++++++++++++------------------------
 include/linux/platform_data/elm.h |   6 +-
 3 files changed, 411 insertions(+), 615 deletions(-)

-- 
1.8.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v1 0/5] optimize and clean-up of OMAP NAND and ELM driver
@ 2013-07-15 14:55 ` Pekon Gupta
  0 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, avinashphilipk, balbi, linux-mtd, Pekon Gupta,
	benoit.cousson, linux-omap

This patch series builds the base for adding support for newer H/W based ECC
schemes for NAND flash. This patch cleans-up redundant code among various
ECC schemes and improves scalability and readability.
This patch affects following data-paths: 
[PATCH 1/5] NAND chip->ecc.correct(): Detects and corrects ECC errors on read accesses.
[PATCH 2/5] NAND chip->ecc.calculate(): Calculates and fetches ECC syndrome from controller.
[PATCH 3/5] NAND chip->ecc.hwctl(): Configures H/W controller for Read/Write data acccesses.
[PATCH 4/5] ELM  add checks and updates code for scalability.
[PATCH 5/5] NAND code clean-up to improve readability.


Pekon Gupta (5):
  mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes
  mtd: nand: omap: optimize chip->ecc.calculate() for H/W ECC schemes
  mtd: nand: omap: optimize chip->ecc.hwctl() for H/W ECC schemes
  mtd: devices: elm: add checks ELM H/W constrains, driver code cleanup
  mtd: nand: omap: code clean-up and white-space fixes

 drivers/mtd/devices/elm.c         | 114 ++---
 drivers/mtd/nand/omap2.c          | 906 ++++++++++++++------------------------
 include/linux/platform_data/elm.h |   6 +-
 3 files changed, 411 insertions(+), 615 deletions(-)

-- 
1.8.1

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v1 1/5] mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes
  2013-07-15 14:55 ` Pekon Gupta
@ 2013-07-15 14:55   ` Pekon Gupta
  -1 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, benoit.cousson, avinashphilipk, balbi, linux-mtd,
	linux-omap, Pekon Gupta

chip->ecc.correct() is used for detecting and correcting bit-flips during read
operations. In omap2-nand driver this is done usingt following functions:

- omap_correct_data(): for H/W based HAM1_ECC schemes
	(Un-Touched in current patch)

- omap_elm_correct_data(): for H/W based BCHx_ECC scheme
	Current implementation of this function is not scalable for newer ECC
	schemes because:
	- It depends on a specific byte-position in OOB area (reserved as 0x00)
	  to differentiates between programmed-pages and erased-pages.
	  This reserved byte-position cannot be accomodated in all ECC schemes.
	- Current code is not scalable for future ECC schemes due to tweaks for
	  BCH4_ECC and BCH8_ECC at multiple places.
	- It checks for bit-flips in Erased-pages using check_erased_page().
	  This is over-work, as sanity of Erased-page can be verified by just
	  comparing them to a pre-defined ECC-syndrome for all_0xFF data.

	This patch optimizes omap_elm_correct_data() in following ways:
	(1) Removes dependency on specific reserved-byte (0x00) in OOB area,
	    instead Erased-page is identified by matching calc_ecc with a
	    pre-defined ECC syndrome of all(0xFF) data
	(2) merges common code for BCH4_ECC and BCH8_ECC for scalability.
	(3) handles incorrect elm_error_location beyond data+oob buffer.
	(4) removes check_erased_page(): Bit-flips in erased-page are handled
	    in same way as for programmed-page

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/nand/omap2.c | 249 ++++++++++++++---------------------------------
 1 file changed, 75 insertions(+), 174 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index e9ef743..5321bdcc 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -176,6 +176,7 @@ struct omap_nand_info {
 	int				gpmc_cs;
 	unsigned long			phys_base;
 	unsigned long			mem_size;
+	enum omap_ecc			ecc_opt;
 	struct completion		comp;
 	struct dma_chan			*dma;
 	int				gpmc_irq_fifo;
@@ -1307,219 +1308,118 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
 }
 
 /**
- * erased_sector_bitflips - count bit flips
- * @data:	data sector buffer
- * @oob:	oob buffer
- * @info:	omap_nand_info
- *
- * Check the bit flips in erased page falls below correctable level.
- * If falls below, report the page as erased with correctable bit
- * flip, else report as uncorrectable page.
- */
-static int erased_sector_bitflips(u_char *data, u_char *oob,
-		struct omap_nand_info *info)
-{
-	int flip_bits = 0, i;
-
-	for (i = 0; i < info->nand.ecc.size; i++) {
-		flip_bits += hweight8(~data[i]);
-		if (flip_bits > info->nand.ecc.strength)
-			return 0;
-	}
-
-	for (i = 0; i < info->nand.ecc.bytes - 1; i++) {
-		flip_bits += hweight8(~oob[i]);
-		if (flip_bits > info->nand.ecc.strength)
-			return 0;
-	}
-
-	/*
-	 * Bit flips falls in correctable level.
-	 * Fill data area with 0xFF
-	 */
-	if (flip_bits) {
-		memset(data, 0xFF, info->nand.ecc.size);
-		memset(oob, 0xFF, info->nand.ecc.bytes);
-	}
-
-	return flip_bits;
-}
-
-/**
  * omap_elm_correct_data - corrects page data area in case error reported
  * @mtd:	MTD device structure
  * @data:	page data
  * @read_ecc:	ecc read from nand flash
- * @calc_ecc:	ecc read from HW ECC registers
- *
- * Calculated ecc vector reported as zero in case of non-error pages.
- * In case of error/erased pages non-zero error vector is reported.
- * In case of non-zero ecc vector, check read_ecc at fixed offset
- * (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not.
- * To handle bit flips in this data, count the number of 0's in
- * read_ecc[x] and check if it greater than 4. If it is less, it is
- * programmed page, else erased page.
- *
- * 1. If page is erased, check with standard ecc vector (ecc vector
- * for erased page to find any bit flip). If check fails, bit flip
- * is present in erased page. Count the bit flips in erased page and
- * if it falls under correctable level, report page with 0xFF and
- * update the correctable bit information.
- * 2. If error is reported on programmed page, update elm error
- * vector and correct the page with ELM error correction routine.
- *
+ * @calc_ecc:	ecc calculated after reading Data and OOB regions from flash
+ * As calc_ecc is calculated over both main & oob, so calc_ecc would be
+ * non-zero only in following cases:
+ * - bit-flips in data or oob region
+ * - erase page, where no ECC is written in OOB area
+ *   However, erased_pages can be differentiated from corrupted pages
+ *   by comparing the calculated ECC with pre-defined syndrome ECC_of_ALL(0xFF)
+ *   Bit-flips in erased-pages would also be caught by comparing, calc_ecc
+ *   with ECC_of_ALL(0xFF)
  */
 static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
 				u_char *read_ecc, u_char *calc_ecc)
 {
 	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 			mtd);
-	int eccsteps = info->nand.ecc.steps;
-	int i , j, stat = 0;
-	int eccsize, eccflag, ecc_vector_size;
+	enum omap_ecc ecc_opt = info->ecc_opt;
+	struct nand_chip *chip = mtd->priv;
+	int eccsteps = chip->ecc.steps;
+	int eccsize  = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int i , j, stat = 0, ret = 0, flag_read_ecc;
 	struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
-	u_char *ecc_vec = calc_ecc;
-	u_char *spare_ecc = read_ecc;
-	u_char *erased_ecc_vec;
-	enum bch_ecc type;
+	u_char *ecc;
 	bool is_error_reported = false;
+	u32 bit_pos, byte_pos, error_max, pos;
 
 	/* Initialize elm error vector to zero */
 	memset(err_vec, 0, sizeof(err_vec));
 
-	if (info->nand.ecc.strength == BCH8_MAX_ERROR) {
-		type = BCH8_ECC;
-		erased_ecc_vec = bch8_vector;
-	} else {
-		type = BCH4_ECC;
-		erased_ecc_vec = bch4_vector;
-	}
-
-	ecc_vector_size = info->nand.ecc.bytes;
-
-	/*
-	 * Remove extra byte padding for BCH8 RBL
-	 * compatibility and erased page handling
-	 */
-	eccsize = ecc_vector_size - 1;
-
 	for (i = 0; i < eccsteps ; i++) {
-		eccflag = 0;	/* initialize eccflag */
-
-		/*
-		 * Check any error reported,
-		 * In case of error, non zero ecc reported.
-		 */
-
-		for (j = 0; (j < eccsize); j++) {
-			if (calc_ecc[j] != 0) {
-				eccflag = 1; /* non zero ecc, error present */
+		flag_read_ecc = 0;
+		ecc = calc_ecc + (i * eccbytes);
+		/* check calc_ecc */
+		for (j = 0; j < eccbytes; j++) {
+			if (*(ecc + j) != 0x00) {
+				flag_read_ecc = 1;
 				break;
 			}
 		}
-
-		if (eccflag == 1) {
-			/*
-			 * Set threshold to minimum of 4, half of ecc.strength/2
-			 * to allow max bit flip in byte to 4
-			 */
-			unsigned int threshold = min_t(unsigned int, 4,
-					info->nand.ecc.strength / 2);
-
-			/*
-			 * Check data area is programmed by counting
-			 * number of 0's at fixed offset in spare area.
-			 * Checking count of 0's against threshold.
-			 * In case programmed page expects at least threshold
-			 * zeros in byte.
-			 * If zeros are less than threshold for programmed page/
-			 * zeros are more than threshold erased page, either
-			 * case page reported as uncorrectable.
-			 */
-			if (hweight8(~read_ecc[eccsize]) >= threshold) {
-				/*
-				 * Update elm error vector as
-				 * data area is programmed
-				 */
-				err_vec[i].error_reported = true;
-				is_error_reported = true;
-			} else {
-				/* Error reported in erased page */
-				int bitflip_count;
-				u_char *buf = &data[info->nand.ecc.size * i];
-
-				if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) {
-					bitflip_count = erased_sector_bitflips(
-							buf, read_ecc, info);
-
-					if (bitflip_count)
-						stat += bitflip_count;
-					else
-						return -EINVAL;
-				}
+		/* check if its a erased-page */
+		if (flag_read_ecc) {
+			switch (ecc_opt) {
+			case OMAP_ECC_BCH8_CODE_HW:
+				if (memcmp(ecc, bch8_vector, eccbytes))
+					err_vec[i].error_reported = true;
+				break;
+			case OMAP_ECC_BCH4_CODE_HW:
+				if (memcmp(ecc, bch4_vector, eccbytes))
+					err_vec[i].error_reported = true;
+				break;
+			default:
+				pr_err("%s: invalid configuration",
+								 DRIVER_NAME);
+				return -EINVAL;
 			}
 		}
-
-		/* Update the ecc vector */
-		calc_ecc += ecc_vector_size;
-		read_ecc += ecc_vector_size;
+		/* page definitely has bit-flips */
+		if (err_vec[i].error_reported)
+			is_error_reported = true;
 	}
 
-	/* Check if any error reported */
 	if (!is_error_reported)
 		return 0;
+	/* detect bit-flips using ELM module */
+	elm_decode_bch_error_page(info->elm_dev, calc_ecc, err_vec);
 
-	/* Decode BCH error using ELM module */
-	elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
-
+	/* correct bit-flip */
 	for (i = 0; i < eccsteps; i++) {
-		if (err_vec[i].error_reported) {
+		if (err_vec[i].error_uncorrectable) {
+			ret = -EBADMSG;
+		} else if (err_vec[i].error_reported) {
 			for (j = 0; j < err_vec[i].error_count; j++) {
-				u32 bit_pos, byte_pos, error_max, pos;
-
-				if (type == BCH8_ECC)
-					error_max = BCH8_ECC_MAX;
-				else
-					error_max = BCH4_ECC_MAX;
-
-				if (info->nand.ecc.strength == BCH8_MAX_ERROR)
-					pos = err_vec[i].error_loc[j];
-				else
+				switch (ecc_opt) {
+				case OMAP_ECC_BCH4_CODE_HW:
+					error_max = SECTOR_BYTES +
+							(eccbytes - 1);
 					/* Add 4 to take care 4 bit padding */
 					pos = err_vec[i].error_loc[j] +
-						BCH4_BIT_PAD;
-
-				/* Calculate bit position of error */
+							BCH4_BIT_PAD;
+					break;
+				case OMAP_ECC_BCH8_CODE_HW:
+					error_max = SECTOR_BYTES +
+							(eccbytes - 1);
+					pos = err_vec[i].error_loc[j];
+					break;
+				default:
+					return -EINVAL;
+				}
+				/* Calculate bit & byte bit-flip position */
 				bit_pos = pos % 8;
-
-				/* Calculate byte position of error */
-				byte_pos = (error_max - pos - 1) / 8;
-
-				if (pos < error_max) {
-					if (byte_pos < 512)
-						data[byte_pos] ^= 1 << bit_pos;
-					else
-						spare_ecc[byte_pos - 512] ^=
+				byte_pos = error_max - (pos / 8) - 1;
+				if (byte_pos < SECTOR_BYTES)
+					data[byte_pos] ^= 1 << bit_pos;
+				else if (byte_pos < error_max)
+					read_ecc[byte_pos - SECTOR_BYTES] ^=
 							1 << bit_pos;
-				}
-				/* else, not interested to correct ecc */
+				else
+					ret = -EBADMSG;
 			}
 		}
-
 		/* Update number of correctable errors */
 		stat += err_vec[i].error_count;
-
 		/* Update page data with sector size */
-		data += info->nand.ecc.size;
-		spare_ecc += ecc_vector_size;
+		data	 += eccsize;
+		read_ecc += eccbytes;
 	}
 
-	for (i = 0; i < eccsteps; i++)
-		/* Return error if uncorrectable error present */
-		if (err_vec[i].error_uncorrectable)
-			return -EINVAL;
-
-	return stat;
+	return (ret < 0) ? ret : stat;
 }
 
 /**
@@ -1677,13 +1577,14 @@ static int omap_nand_probe(struct platform_device *pdev)
 	info->pdev		= pdev;
 	info->gpmc_cs		= pdata->cs;
 	info->reg		= pdata->reg;
+	info->ecc_opt		= pdata->ecc_opt;
 
 	info->nand.options	= NAND_BUSWIDTH_AUTO;
 	info->nand.options	|= NAND_SKIP_BBTSCAN;
+	info->nand.ecc.priv	= NULL;
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
 	info->of_node		= pdata->of_node;
 #endif
-	info->nand.ecc.priv			= NULL;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (res == NULL) {
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 1/5] mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes
@ 2013-07-15 14:55   ` Pekon Gupta
  0 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, avinashphilipk, balbi, linux-mtd, Pekon Gupta,
	benoit.cousson, linux-omap

chip->ecc.correct() is used for detecting and correcting bit-flips during read
operations. In omap2-nand driver this is done usingt following functions:

- omap_correct_data(): for H/W based HAM1_ECC schemes
	(Un-Touched in current patch)

- omap_elm_correct_data(): for H/W based BCHx_ECC scheme
	Current implementation of this function is not scalable for newer ECC
	schemes because:
	- It depends on a specific byte-position in OOB area (reserved as 0x00)
	  to differentiates between programmed-pages and erased-pages.
	  This reserved byte-position cannot be accomodated in all ECC schemes.
	- Current code is not scalable for future ECC schemes due to tweaks for
	  BCH4_ECC and BCH8_ECC at multiple places.
	- It checks for bit-flips in Erased-pages using check_erased_page().
	  This is over-work, as sanity of Erased-page can be verified by just
	  comparing them to a pre-defined ECC-syndrome for all_0xFF data.

	This patch optimizes omap_elm_correct_data() in following ways:
	(1) Removes dependency on specific reserved-byte (0x00) in OOB area,
	    instead Erased-page is identified by matching calc_ecc with a
	    pre-defined ECC syndrome of all(0xFF) data
	(2) merges common code for BCH4_ECC and BCH8_ECC for scalability.
	(3) handles incorrect elm_error_location beyond data+oob buffer.
	(4) removes check_erased_page(): Bit-flips in erased-page are handled
	    in same way as for programmed-page

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/nand/omap2.c | 249 ++++++++++++++---------------------------------
 1 file changed, 75 insertions(+), 174 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index e9ef743..5321bdcc 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -176,6 +176,7 @@ struct omap_nand_info {
 	int				gpmc_cs;
 	unsigned long			phys_base;
 	unsigned long			mem_size;
+	enum omap_ecc			ecc_opt;
 	struct completion		comp;
 	struct dma_chan			*dma;
 	int				gpmc_irq_fifo;
@@ -1307,219 +1308,118 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
 }
 
 /**
- * erased_sector_bitflips - count bit flips
- * @data:	data sector buffer
- * @oob:	oob buffer
- * @info:	omap_nand_info
- *
- * Check the bit flips in erased page falls below correctable level.
- * If falls below, report the page as erased with correctable bit
- * flip, else report as uncorrectable page.
- */
-static int erased_sector_bitflips(u_char *data, u_char *oob,
-		struct omap_nand_info *info)
-{
-	int flip_bits = 0, i;
-
-	for (i = 0; i < info->nand.ecc.size; i++) {
-		flip_bits += hweight8(~data[i]);
-		if (flip_bits > info->nand.ecc.strength)
-			return 0;
-	}
-
-	for (i = 0; i < info->nand.ecc.bytes - 1; i++) {
-		flip_bits += hweight8(~oob[i]);
-		if (flip_bits > info->nand.ecc.strength)
-			return 0;
-	}
-
-	/*
-	 * Bit flips falls in correctable level.
-	 * Fill data area with 0xFF
-	 */
-	if (flip_bits) {
-		memset(data, 0xFF, info->nand.ecc.size);
-		memset(oob, 0xFF, info->nand.ecc.bytes);
-	}
-
-	return flip_bits;
-}
-
-/**
  * omap_elm_correct_data - corrects page data area in case error reported
  * @mtd:	MTD device structure
  * @data:	page data
  * @read_ecc:	ecc read from nand flash
- * @calc_ecc:	ecc read from HW ECC registers
- *
- * Calculated ecc vector reported as zero in case of non-error pages.
- * In case of error/erased pages non-zero error vector is reported.
- * In case of non-zero ecc vector, check read_ecc at fixed offset
- * (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not.
- * To handle bit flips in this data, count the number of 0's in
- * read_ecc[x] and check if it greater than 4. If it is less, it is
- * programmed page, else erased page.
- *
- * 1. If page is erased, check with standard ecc vector (ecc vector
- * for erased page to find any bit flip). If check fails, bit flip
- * is present in erased page. Count the bit flips in erased page and
- * if it falls under correctable level, report page with 0xFF and
- * update the correctable bit information.
- * 2. If error is reported on programmed page, update elm error
- * vector and correct the page with ELM error correction routine.
- *
+ * @calc_ecc:	ecc calculated after reading Data and OOB regions from flash
+ * As calc_ecc is calculated over both main & oob, so calc_ecc would be
+ * non-zero only in following cases:
+ * - bit-flips in data or oob region
+ * - erase page, where no ECC is written in OOB area
+ *   However, erased_pages can be differentiated from corrupted pages
+ *   by comparing the calculated ECC with pre-defined syndrome ECC_of_ALL(0xFF)
+ *   Bit-flips in erased-pages would also be caught by comparing, calc_ecc
+ *   with ECC_of_ALL(0xFF)
  */
 static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
 				u_char *read_ecc, u_char *calc_ecc)
 {
 	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 			mtd);
-	int eccsteps = info->nand.ecc.steps;
-	int i , j, stat = 0;
-	int eccsize, eccflag, ecc_vector_size;
+	enum omap_ecc ecc_opt = info->ecc_opt;
+	struct nand_chip *chip = mtd->priv;
+	int eccsteps = chip->ecc.steps;
+	int eccsize  = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int i , j, stat = 0, ret = 0, flag_read_ecc;
 	struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
-	u_char *ecc_vec = calc_ecc;
-	u_char *spare_ecc = read_ecc;
-	u_char *erased_ecc_vec;
-	enum bch_ecc type;
+	u_char *ecc;
 	bool is_error_reported = false;
+	u32 bit_pos, byte_pos, error_max, pos;
 
 	/* Initialize elm error vector to zero */
 	memset(err_vec, 0, sizeof(err_vec));
 
-	if (info->nand.ecc.strength == BCH8_MAX_ERROR) {
-		type = BCH8_ECC;
-		erased_ecc_vec = bch8_vector;
-	} else {
-		type = BCH4_ECC;
-		erased_ecc_vec = bch4_vector;
-	}
-
-	ecc_vector_size = info->nand.ecc.bytes;
-
-	/*
-	 * Remove extra byte padding for BCH8 RBL
-	 * compatibility and erased page handling
-	 */
-	eccsize = ecc_vector_size - 1;
-
 	for (i = 0; i < eccsteps ; i++) {
-		eccflag = 0;	/* initialize eccflag */
-
-		/*
-		 * Check any error reported,
-		 * In case of error, non zero ecc reported.
-		 */
-
-		for (j = 0; (j < eccsize); j++) {
-			if (calc_ecc[j] != 0) {
-				eccflag = 1; /* non zero ecc, error present */
+		flag_read_ecc = 0;
+		ecc = calc_ecc + (i * eccbytes);
+		/* check calc_ecc */
+		for (j = 0; j < eccbytes; j++) {
+			if (*(ecc + j) != 0x00) {
+				flag_read_ecc = 1;
 				break;
 			}
 		}
-
-		if (eccflag == 1) {
-			/*
-			 * Set threshold to minimum of 4, half of ecc.strength/2
-			 * to allow max bit flip in byte to 4
-			 */
-			unsigned int threshold = min_t(unsigned int, 4,
-					info->nand.ecc.strength / 2);
-
-			/*
-			 * Check data area is programmed by counting
-			 * number of 0's at fixed offset in spare area.
-			 * Checking count of 0's against threshold.
-			 * In case programmed page expects at least threshold
-			 * zeros in byte.
-			 * If zeros are less than threshold for programmed page/
-			 * zeros are more than threshold erased page, either
-			 * case page reported as uncorrectable.
-			 */
-			if (hweight8(~read_ecc[eccsize]) >= threshold) {
-				/*
-				 * Update elm error vector as
-				 * data area is programmed
-				 */
-				err_vec[i].error_reported = true;
-				is_error_reported = true;
-			} else {
-				/* Error reported in erased page */
-				int bitflip_count;
-				u_char *buf = &data[info->nand.ecc.size * i];
-
-				if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) {
-					bitflip_count = erased_sector_bitflips(
-							buf, read_ecc, info);
-
-					if (bitflip_count)
-						stat += bitflip_count;
-					else
-						return -EINVAL;
-				}
+		/* check if its a erased-page */
+		if (flag_read_ecc) {
+			switch (ecc_opt) {
+			case OMAP_ECC_BCH8_CODE_HW:
+				if (memcmp(ecc, bch8_vector, eccbytes))
+					err_vec[i].error_reported = true;
+				break;
+			case OMAP_ECC_BCH4_CODE_HW:
+				if (memcmp(ecc, bch4_vector, eccbytes))
+					err_vec[i].error_reported = true;
+				break;
+			default:
+				pr_err("%s: invalid configuration",
+								 DRIVER_NAME);
+				return -EINVAL;
 			}
 		}
-
-		/* Update the ecc vector */
-		calc_ecc += ecc_vector_size;
-		read_ecc += ecc_vector_size;
+		/* page definitely has bit-flips */
+		if (err_vec[i].error_reported)
+			is_error_reported = true;
 	}
 
-	/* Check if any error reported */
 	if (!is_error_reported)
 		return 0;
+	/* detect bit-flips using ELM module */
+	elm_decode_bch_error_page(info->elm_dev, calc_ecc, err_vec);
 
-	/* Decode BCH error using ELM module */
-	elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
-
+	/* correct bit-flip */
 	for (i = 0; i < eccsteps; i++) {
-		if (err_vec[i].error_reported) {
+		if (err_vec[i].error_uncorrectable) {
+			ret = -EBADMSG;
+		} else if (err_vec[i].error_reported) {
 			for (j = 0; j < err_vec[i].error_count; j++) {
-				u32 bit_pos, byte_pos, error_max, pos;
-
-				if (type == BCH8_ECC)
-					error_max = BCH8_ECC_MAX;
-				else
-					error_max = BCH4_ECC_MAX;
-
-				if (info->nand.ecc.strength == BCH8_MAX_ERROR)
-					pos = err_vec[i].error_loc[j];
-				else
+				switch (ecc_opt) {
+				case OMAP_ECC_BCH4_CODE_HW:
+					error_max = SECTOR_BYTES +
+							(eccbytes - 1);
 					/* Add 4 to take care 4 bit padding */
 					pos = err_vec[i].error_loc[j] +
-						BCH4_BIT_PAD;
-
-				/* Calculate bit position of error */
+							BCH4_BIT_PAD;
+					break;
+				case OMAP_ECC_BCH8_CODE_HW:
+					error_max = SECTOR_BYTES +
+							(eccbytes - 1);
+					pos = err_vec[i].error_loc[j];
+					break;
+				default:
+					return -EINVAL;
+				}
+				/* Calculate bit & byte bit-flip position */
 				bit_pos = pos % 8;
-
-				/* Calculate byte position of error */
-				byte_pos = (error_max - pos - 1) / 8;
-
-				if (pos < error_max) {
-					if (byte_pos < 512)
-						data[byte_pos] ^= 1 << bit_pos;
-					else
-						spare_ecc[byte_pos - 512] ^=
+				byte_pos = error_max - (pos / 8) - 1;
+				if (byte_pos < SECTOR_BYTES)
+					data[byte_pos] ^= 1 << bit_pos;
+				else if (byte_pos < error_max)
+					read_ecc[byte_pos - SECTOR_BYTES] ^=
 							1 << bit_pos;
-				}
-				/* else, not interested to correct ecc */
+				else
+					ret = -EBADMSG;
 			}
 		}
-
 		/* Update number of correctable errors */
 		stat += err_vec[i].error_count;
-
 		/* Update page data with sector size */
-		data += info->nand.ecc.size;
-		spare_ecc += ecc_vector_size;
+		data	 += eccsize;
+		read_ecc += eccbytes;
 	}
 
-	for (i = 0; i < eccsteps; i++)
-		/* Return error if uncorrectable error present */
-		if (err_vec[i].error_uncorrectable)
-			return -EINVAL;
-
-	return stat;
+	return (ret < 0) ? ret : stat;
 }
 
 /**
@@ -1677,13 +1577,14 @@ static int omap_nand_probe(struct platform_device *pdev)
 	info->pdev		= pdev;
 	info->gpmc_cs		= pdata->cs;
 	info->reg		= pdata->reg;
+	info->ecc_opt		= pdata->ecc_opt;
 
 	info->nand.options	= NAND_BUSWIDTH_AUTO;
 	info->nand.options	|= NAND_SKIP_BBTSCAN;
+	info->nand.ecc.priv	= NULL;
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
 	info->of_node		= pdata->of_node;
 #endif
-	info->nand.ecc.priv			= NULL;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (res == NULL) {
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 2/5] mtd: nand: omap: optimize chip->ecc.calculate() for H/W ECC schemes
  2013-07-15 14:55 ` Pekon Gupta
@ 2013-07-15 14:55   ` Pekon Gupta
  -1 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, benoit.cousson, avinashphilipk, balbi, linux-mtd,
	linux-omap, Pekon Gupta

chip->ecc.calculate() is used for calculating and fetching of ECC syndrome by
processing the data passed during Read/Write accesses.

All H/W based ECC schemes supported in omap2-nand driver use GPMC controller
to calculate ECC syndrome. But each BCHx_ECC scheme implements its own function
to process and fetch ECC syndrom from GPMC controller.

This patch tries to merges the common code for different BCHx_ECC schemes into
single omap_calculate_ecc_bch(), And adds schemes specific post-possessing
after fetching ECC-syndrome. This removes redundant code and adds scalability
for future ECC-schemes. This patch:
- [un-touched]	omap_calculate_ecc():		Used for HAM1_ECC
- [merged]	omap3_calculate_ecc_bch4():	Used for BCH4_HW_DETECTION_SW
- [merged]	omap3_calculate_ecc_bch8():	Used for BCH8_HW_DETECTION_SW
- [merged]	omap3_calculate_ecc_bch():	Used for BCH4_HW and BCH8_HW
- [new]		omap_calculate_ecc_bch():	Now used for all BCHx_ECC

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/nand/omap2.c | 249 ++++++++++++++++++-----------------------------
 1 file changed, 95 insertions(+), 154 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 5321bdcc..7f7a2ee 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -151,7 +151,9 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
 	0xac, 0x6b, 0xff, 0x99, 0x7b};
 static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
 #endif
-
+static u8  bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
+static u8  bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+				0x97, 0x79, 0xe5, 0x24, 0xb5};
 /* oob info generated runtime depending on ecc algorithm and layout selected */
 static struct nand_ecclayout omap_oobinfo;
 /* Define some generic bad / good block scan pattern which are used
@@ -950,9 +952,11 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 	u32 val;
 
 	val = readl(info->reg.gpmc_ecc_config);
-	if (((val >> ECC_CONFIG_CS_SHIFT)  & ~CS_MASK) != info->gpmc_cs)
+	if (((val >> 1) & 0x07) != info->gpmc_cs) {
+		pr_err("%s: invalid ECC configuration for chip-select=%d",
+				DRIVER_NAME, info->gpmc_cs);
 		return -EINVAL;
-
+	}
 	/* read ecc result */
 	val = readl(info->reg.gpmc_ecc1_result);
 	*ecc_code++ = val;          /* P128e, ..., P1e */
@@ -1141,172 +1145,109 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
 	writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
 }
 
-#ifdef CONFIG_MTD_NAND_ECC_BCH
 /**
- * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat,
-				    u_char *ecc_code)
-{
-	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
-						   mtd);
-	unsigned long nsectors, val1, val2;
-	int i;
-
-	nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
-	for (i = 0; i < nsectors; i++) {
-
-		/* Read hw-computed remainder */
-		val1 = readl(info->reg.gpmc_bch_result0[i]);
-		val2 = readl(info->reg.gpmc_bch_result1[i]);
-
-		/*
-		 * Add constant polynomial to remainder, in order to get an ecc
-		 * sequence of 0xFFs for a buffer filled with 0xFFs; and
-		 * left-justify the resulting polynomial.
-		 */
-		*ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF);
-		*ecc_code++ = 0x13 ^ ((val2 >>  4) & 0xFF);
-		*ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
-		*ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF);
-		*ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF);
-		*ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF);
-		*ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4);
-	}
-
-	return 0;
-}
-
-/**
- * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,
-				    u_char *ecc_code)
-{
-	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
-						   mtd);
-	unsigned long nsectors, val1, val2, val3, val4;
-	int i;
-
-	nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
-	for (i = 0; i < nsectors; i++) {
-
-		/* Read hw-computed remainder */
-		val1 = readl(info->reg.gpmc_bch_result0[i]);
-		val2 = readl(info->reg.gpmc_bch_result1[i]);
-		val3 = readl(info->reg.gpmc_bch_result2[i]);
-		val4 = readl(info->reg.gpmc_bch_result3[i]);
-
-		/*
-		 * Add constant polynomial to remainder, in order to get an ecc
-		 * sequence of 0xFFs for a buffer filled with 0xFFs.
-		 */
-		*ecc_code++ = 0xef ^ (val4 & 0xFF);
-		*ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF);
-		*ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF);
-		*ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF);
-		*ecc_code++ = 0xed ^ (val3 & 0xFF);
-		*ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF);
-		*ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF);
-		*ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
-		*ecc_code++ = 0x97 ^ (val2 & 0xFF);
-		*ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF);
-		*ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
-		*ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF);
-		*ecc_code++ = 0xb5 ^ (val1 & 0xFF);
-	}
-
-	return 0;
-}
-
-#endif /* CONFIG_MTD_NAND_ECC_BCH */
-
-
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
-/**
- * omap3_calculate_ecc_bch - Generate bytes of ECC bytes
+ * omap_calculate_ecc_bch - Generate bytes of ECC bytes
  * @mtd:	MTD device structure
  * @dat:	The pointer to data on which ecc is computed
  * @ecc_code:	The ecc_code buffer
  *
  * Support calculating of BCH4/8 ecc vectors for the page
  */
-static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
-				    u_char *ecc_code)
+static int omap_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
+				    u_char *ecc_calc)
 {
 	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 						   mtd);
-	unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
-	int i, eccbchtsel;
+	struct nand_chip *chip = mtd->priv;
+	enum omap_ecc ecc_opt = info->ecc_opt;
+	struct gpmc_nand_regs	*gpmc_regs = &info->reg;
+	u32 eccbytes = chip->ecc.bytes;
+	u_char *ecc_ptr;
+	u32 nsectors;
+	int i, val;
 
-	nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-	/*
-	 * find BCH scheme used
-	 * 0 -> BCH4
-	 * 1 -> BCH8
-	 */
-	eccbchtsel = ((readl(info->reg.gpmc_ecc_config) >> 12) & 0x3);
+	val = readl(info->reg.gpmc_ecc_config);
+	if (((val >> 1) & 0x07) != info->gpmc_cs) {
+		pr_err("%s: invalid ECC configuration for chip-select=%d",
+				DRIVER_NAME, info->gpmc_cs);
+		return -EINVAL;
+	}
+	nsectors = ((readl(gpmc_regs->gpmc_ecc_config) >> 4) & 0x7) + 1;
 
 	for (i = 0; i < nsectors; i++) {
-
-		/* Read hw-computed remainder */
-		bch_val1 = readl(info->reg.gpmc_bch_result0[i]);
-		bch_val2 = readl(info->reg.gpmc_bch_result1[i]);
-		if (eccbchtsel) {
-			bch_val3 = readl(info->reg.gpmc_bch_result2[i]);
-			bch_val4 = readl(info->reg.gpmc_bch_result3[i]);
+		ecc_ptr = ecc_calc;
+		switch (ecc_opt) {
+		case OMAP_ECC_HAMMING_CODE_DEFAULT:
+		case OMAP_ECC_HAMMING_CODE_HW:
+		case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
+			return -EINVAL;
+		case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+		case OMAP_ECC_BCH4_CODE_HW:
+			val = readl(gpmc_regs->gpmc_bch_result1[i]);
+			*(ecc_ptr++) = ((val >> 12) & 0xFF);
+			*(ecc_ptr++) = ((val >>  4) & 0xFF);
+			*(ecc_ptr)   = ((val >>  0) << 4) & 0xF0;
+			val = readl(gpmc_regs->gpmc_bch_result0[i]);
+			*(ecc_ptr)   = ((val >> 28) & 0x0F) | *(ecc_ptr);
+			ecc_ptr++;
+			*(ecc_ptr++) = ((val >> 20) & 0xFF);
+			*(ecc_ptr++) = ((val >> 12) & 0xFF);
+			*(ecc_ptr++) = ((val >>  4) & 0xFF);
+			*(ecc_ptr++) = ((val >>  0) << 4) & 0xF0;
+			break;
+		case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+		case OMAP_ECC_BCH8_CODE_HW:
+			val = readl(gpmc_regs->gpmc_bch_result3[i]);
+			*(ecc_ptr++) = ((val >>  0) & 0xFF);
+			val = readl(gpmc_regs->gpmc_bch_result2[i]);
+			*(ecc_ptr++) = ((val >> 24) & 0xFF);
+			*(ecc_ptr++) = ((val >> 16) & 0xFF);
+			*(ecc_ptr++) = ((val >>  8) & 0xFF);
+			*(ecc_ptr++) = ((val >>  0) & 0xFF);
+			val = readl(gpmc_regs->gpmc_bch_result1[i]);
+			*(ecc_ptr++) = ((val >> 24) & 0xFF);
+			*(ecc_ptr++) = ((val >> 16) & 0xFF);
+			*(ecc_ptr++) = ((val >>  8) & 0xFF);
+			*(ecc_ptr++) = ((val >>  0) & 0xFF);
+			val = readl(gpmc_regs->gpmc_bch_result0[i]);
+			*(ecc_ptr++) = ((val >> 24) & 0xFF);
+			*(ecc_ptr++) = ((val >> 16) & 0xFF);
+			*(ecc_ptr++) = ((val >>  8) & 0xFF);
+			*(ecc_ptr++) = ((val >>  0) & 0xFF);
+			break;
+		default:
+			return -EINVAL;
 		}
-
-		if (eccbchtsel) {
-			/* BCH8 ecc scheme */
-			*ecc_code++ = (bch_val4 & 0xFF);
-			*ecc_code++ = ((bch_val3 >> 24) & 0xFF);
-			*ecc_code++ = ((bch_val3 >> 16) & 0xFF);
-			*ecc_code++ = ((bch_val3 >> 8) & 0xFF);
-			*ecc_code++ = (bch_val3 & 0xFF);
-			*ecc_code++ = ((bch_val2 >> 24) & 0xFF);
-			*ecc_code++ = ((bch_val2 >> 16) & 0xFF);
-			*ecc_code++ = ((bch_val2 >> 8) & 0xFF);
-			*ecc_code++ = (bch_val2 & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 24) & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 16) & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 8) & 0xFF);
-			*ecc_code++ = (bch_val1 & 0xFF);
-			/*
-			 * Setting 14th byte to zero to handle
-			 * erased page & maintain compatibility
-			 * with RBL
-			 */
-			*ecc_code++ = 0x0;
-		} else {
-			/* BCH4 ecc scheme */
-			*ecc_code++ = ((bch_val2 >> 12) & 0xFF);
-			*ecc_code++ = ((bch_val2 >> 4) & 0xFF);
-			*ecc_code++ = ((bch_val2 & 0xF) << 4) |
-				((bch_val1 >> 28) & 0xF);
-			*ecc_code++ = ((bch_val1 >> 20) & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 12) & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
-			*ecc_code++ = ((bch_val1 & 0xF) << 4);
-			/*
-			 * Setting 8th byte to zero to handle
-			 * erased page
-			 */
-			*ecc_code++ = 0x0;
+		/* ECC scheme specific syndrome customizations */
+		switch (ecc_opt) {
+		case OMAP_ECC_HAMMING_CODE_DEFAULT:
+		case OMAP_ECC_HAMMING_CODE_HW:
+		case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
+			return -EINVAL;
+		case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+			for (i = 0; i < eccbytes; i++)
+				*(ecc_calc + i) = *(ecc_calc + i) ^
+							bch4_polynomial[i];
+			break;
+		case OMAP_ECC_BCH4_CODE_HW:
+			*(ecc_ptr++) = 0x00;
+			break;
+		case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+			for (i = 0; i < eccbytes; i++)
+				*(ecc_calc + i) = *(ecc_calc + i) ^
+							bch8_polynomial[i];
+			break;
+		case OMAP_ECC_BCH8_CODE_HW:
+			*(ecc_ptr++) = 0x00;
+			break;
 		}
+		/* update pointer to next sector */
+		ecc_calc += eccbytes;
 	}
-
 	return 0;
 }
 
+#ifdef CONFIG_MTD_NAND_OMAP_BCH
 /**
  * omap_elm_correct_data - corrects page data area in case error reported
  * @mtd:	MTD device structure
@@ -1790,7 +1731,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.strength		= 4;
 		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
 		info->nand.ecc.correct		= nand_bch_correct_data;
-		info->nand.ecc.calculate	= omap3_calculate_ecc_bch4;
+		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
 		info->nand.ecc.priv		= nand_bch_init(mtd,
 						info->nand.ecc.size,
@@ -1813,7 +1754,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.strength		= 4;
 		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
 		info->nand.ecc.correct		= omap_elm_correct_data;
-		info->nand.ecc.calculate	= omap3_calculate_ecc_bch;
+		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		info->nand.ecc.read_page	= omap_read_page_bch;
 		info->nand.ecc.write_page	= omap_write_page_bch;
 		/* ELM H/W engine is used for locating errors */
@@ -1840,7 +1781,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.strength		= 8;
 		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
 		info->nand.ecc.correct		= nand_bch_correct_data;
-		info->nand.ecc.calculate	= omap3_calculate_ecc_bch8;
+		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
 		info->nand.ecc.priv		= nand_bch_init(mtd,
 						info->nand.ecc.size,
@@ -1863,7 +1804,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.strength		= 8;
 		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
 		info->nand.ecc.correct		= omap_elm_correct_data;
-		info->nand.ecc.calculate	= omap3_calculate_ecc_bch;
+		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		info->nand.ecc.read_page	= omap_read_page_bch;
 		info->nand.ecc.write_page	= omap_write_page_bch;
 		/* ELM H/W engine is used for locating errors */
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 2/5] mtd: nand: omap: optimize chip->ecc.calculate() for H/W ECC schemes
@ 2013-07-15 14:55   ` Pekon Gupta
  0 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, avinashphilipk, balbi, linux-mtd, Pekon Gupta,
	benoit.cousson, linux-omap

chip->ecc.calculate() is used for calculating and fetching of ECC syndrome by
processing the data passed during Read/Write accesses.

All H/W based ECC schemes supported in omap2-nand driver use GPMC controller
to calculate ECC syndrome. But each BCHx_ECC scheme implements its own function
to process and fetch ECC syndrom from GPMC controller.

This patch tries to merges the common code for different BCHx_ECC schemes into
single omap_calculate_ecc_bch(), And adds schemes specific post-possessing
after fetching ECC-syndrome. This removes redundant code and adds scalability
for future ECC-schemes. This patch:
- [un-touched]	omap_calculate_ecc():		Used for HAM1_ECC
- [merged]	omap3_calculate_ecc_bch4():	Used for BCH4_HW_DETECTION_SW
- [merged]	omap3_calculate_ecc_bch8():	Used for BCH8_HW_DETECTION_SW
- [merged]	omap3_calculate_ecc_bch():	Used for BCH4_HW and BCH8_HW
- [new]		omap_calculate_ecc_bch():	Now used for all BCHx_ECC

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/nand/omap2.c | 249 ++++++++++++++++++-----------------------------
 1 file changed, 95 insertions(+), 154 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 5321bdcc..7f7a2ee 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -151,7 +151,9 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
 	0xac, 0x6b, 0xff, 0x99, 0x7b};
 static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
 #endif
-
+static u8  bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
+static u8  bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+				0x97, 0x79, 0xe5, 0x24, 0xb5};
 /* oob info generated runtime depending on ecc algorithm and layout selected */
 static struct nand_ecclayout omap_oobinfo;
 /* Define some generic bad / good block scan pattern which are used
@@ -950,9 +952,11 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 	u32 val;
 
 	val = readl(info->reg.gpmc_ecc_config);
-	if (((val >> ECC_CONFIG_CS_SHIFT)  & ~CS_MASK) != info->gpmc_cs)
+	if (((val >> 1) & 0x07) != info->gpmc_cs) {
+		pr_err("%s: invalid ECC configuration for chip-select=%d",
+				DRIVER_NAME, info->gpmc_cs);
 		return -EINVAL;
-
+	}
 	/* read ecc result */
 	val = readl(info->reg.gpmc_ecc1_result);
 	*ecc_code++ = val;          /* P128e, ..., P1e */
@@ -1141,172 +1145,109 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
 	writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
 }
 
-#ifdef CONFIG_MTD_NAND_ECC_BCH
 /**
- * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat,
-				    u_char *ecc_code)
-{
-	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
-						   mtd);
-	unsigned long nsectors, val1, val2;
-	int i;
-
-	nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
-	for (i = 0; i < nsectors; i++) {
-
-		/* Read hw-computed remainder */
-		val1 = readl(info->reg.gpmc_bch_result0[i]);
-		val2 = readl(info->reg.gpmc_bch_result1[i]);
-
-		/*
-		 * Add constant polynomial to remainder, in order to get an ecc
-		 * sequence of 0xFFs for a buffer filled with 0xFFs; and
-		 * left-justify the resulting polynomial.
-		 */
-		*ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF);
-		*ecc_code++ = 0x13 ^ ((val2 >>  4) & 0xFF);
-		*ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
-		*ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF);
-		*ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF);
-		*ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF);
-		*ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4);
-	}
-
-	return 0;
-}
-
-/**
- * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,
-				    u_char *ecc_code)
-{
-	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
-						   mtd);
-	unsigned long nsectors, val1, val2, val3, val4;
-	int i;
-
-	nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
-	for (i = 0; i < nsectors; i++) {
-
-		/* Read hw-computed remainder */
-		val1 = readl(info->reg.gpmc_bch_result0[i]);
-		val2 = readl(info->reg.gpmc_bch_result1[i]);
-		val3 = readl(info->reg.gpmc_bch_result2[i]);
-		val4 = readl(info->reg.gpmc_bch_result3[i]);
-
-		/*
-		 * Add constant polynomial to remainder, in order to get an ecc
-		 * sequence of 0xFFs for a buffer filled with 0xFFs.
-		 */
-		*ecc_code++ = 0xef ^ (val4 & 0xFF);
-		*ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF);
-		*ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF);
-		*ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF);
-		*ecc_code++ = 0xed ^ (val3 & 0xFF);
-		*ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF);
-		*ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF);
-		*ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
-		*ecc_code++ = 0x97 ^ (val2 & 0xFF);
-		*ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF);
-		*ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
-		*ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF);
-		*ecc_code++ = 0xb5 ^ (val1 & 0xFF);
-	}
-
-	return 0;
-}
-
-#endif /* CONFIG_MTD_NAND_ECC_BCH */
-
-
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
-/**
- * omap3_calculate_ecc_bch - Generate bytes of ECC bytes
+ * omap_calculate_ecc_bch - Generate bytes of ECC bytes
  * @mtd:	MTD device structure
  * @dat:	The pointer to data on which ecc is computed
  * @ecc_code:	The ecc_code buffer
  *
  * Support calculating of BCH4/8 ecc vectors for the page
  */
-static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
-				    u_char *ecc_code)
+static int omap_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
+				    u_char *ecc_calc)
 {
 	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 						   mtd);
-	unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
-	int i, eccbchtsel;
+	struct nand_chip *chip = mtd->priv;
+	enum omap_ecc ecc_opt = info->ecc_opt;
+	struct gpmc_nand_regs	*gpmc_regs = &info->reg;
+	u32 eccbytes = chip->ecc.bytes;
+	u_char *ecc_ptr;
+	u32 nsectors;
+	int i, val;
 
-	nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-	/*
-	 * find BCH scheme used
-	 * 0 -> BCH4
-	 * 1 -> BCH8
-	 */
-	eccbchtsel = ((readl(info->reg.gpmc_ecc_config) >> 12) & 0x3);
+	val = readl(info->reg.gpmc_ecc_config);
+	if (((val >> 1) & 0x07) != info->gpmc_cs) {
+		pr_err("%s: invalid ECC configuration for chip-select=%d",
+				DRIVER_NAME, info->gpmc_cs);
+		return -EINVAL;
+	}
+	nsectors = ((readl(gpmc_regs->gpmc_ecc_config) >> 4) & 0x7) + 1;
 
 	for (i = 0; i < nsectors; i++) {
-
-		/* Read hw-computed remainder */
-		bch_val1 = readl(info->reg.gpmc_bch_result0[i]);
-		bch_val2 = readl(info->reg.gpmc_bch_result1[i]);
-		if (eccbchtsel) {
-			bch_val3 = readl(info->reg.gpmc_bch_result2[i]);
-			bch_val4 = readl(info->reg.gpmc_bch_result3[i]);
+		ecc_ptr = ecc_calc;
+		switch (ecc_opt) {
+		case OMAP_ECC_HAMMING_CODE_DEFAULT:
+		case OMAP_ECC_HAMMING_CODE_HW:
+		case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
+			return -EINVAL;
+		case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+		case OMAP_ECC_BCH4_CODE_HW:
+			val = readl(gpmc_regs->gpmc_bch_result1[i]);
+			*(ecc_ptr++) = ((val >> 12) & 0xFF);
+			*(ecc_ptr++) = ((val >>  4) & 0xFF);
+			*(ecc_ptr)   = ((val >>  0) << 4) & 0xF0;
+			val = readl(gpmc_regs->gpmc_bch_result0[i]);
+			*(ecc_ptr)   = ((val >> 28) & 0x0F) | *(ecc_ptr);
+			ecc_ptr++;
+			*(ecc_ptr++) = ((val >> 20) & 0xFF);
+			*(ecc_ptr++) = ((val >> 12) & 0xFF);
+			*(ecc_ptr++) = ((val >>  4) & 0xFF);
+			*(ecc_ptr++) = ((val >>  0) << 4) & 0xF0;
+			break;
+		case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+		case OMAP_ECC_BCH8_CODE_HW:
+			val = readl(gpmc_regs->gpmc_bch_result3[i]);
+			*(ecc_ptr++) = ((val >>  0) & 0xFF);
+			val = readl(gpmc_regs->gpmc_bch_result2[i]);
+			*(ecc_ptr++) = ((val >> 24) & 0xFF);
+			*(ecc_ptr++) = ((val >> 16) & 0xFF);
+			*(ecc_ptr++) = ((val >>  8) & 0xFF);
+			*(ecc_ptr++) = ((val >>  0) & 0xFF);
+			val = readl(gpmc_regs->gpmc_bch_result1[i]);
+			*(ecc_ptr++) = ((val >> 24) & 0xFF);
+			*(ecc_ptr++) = ((val >> 16) & 0xFF);
+			*(ecc_ptr++) = ((val >>  8) & 0xFF);
+			*(ecc_ptr++) = ((val >>  0) & 0xFF);
+			val = readl(gpmc_regs->gpmc_bch_result0[i]);
+			*(ecc_ptr++) = ((val >> 24) & 0xFF);
+			*(ecc_ptr++) = ((val >> 16) & 0xFF);
+			*(ecc_ptr++) = ((val >>  8) & 0xFF);
+			*(ecc_ptr++) = ((val >>  0) & 0xFF);
+			break;
+		default:
+			return -EINVAL;
 		}
-
-		if (eccbchtsel) {
-			/* BCH8 ecc scheme */
-			*ecc_code++ = (bch_val4 & 0xFF);
-			*ecc_code++ = ((bch_val3 >> 24) & 0xFF);
-			*ecc_code++ = ((bch_val3 >> 16) & 0xFF);
-			*ecc_code++ = ((bch_val3 >> 8) & 0xFF);
-			*ecc_code++ = (bch_val3 & 0xFF);
-			*ecc_code++ = ((bch_val2 >> 24) & 0xFF);
-			*ecc_code++ = ((bch_val2 >> 16) & 0xFF);
-			*ecc_code++ = ((bch_val2 >> 8) & 0xFF);
-			*ecc_code++ = (bch_val2 & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 24) & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 16) & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 8) & 0xFF);
-			*ecc_code++ = (bch_val1 & 0xFF);
-			/*
-			 * Setting 14th byte to zero to handle
-			 * erased page & maintain compatibility
-			 * with RBL
-			 */
-			*ecc_code++ = 0x0;
-		} else {
-			/* BCH4 ecc scheme */
-			*ecc_code++ = ((bch_val2 >> 12) & 0xFF);
-			*ecc_code++ = ((bch_val2 >> 4) & 0xFF);
-			*ecc_code++ = ((bch_val2 & 0xF) << 4) |
-				((bch_val1 >> 28) & 0xF);
-			*ecc_code++ = ((bch_val1 >> 20) & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 12) & 0xFF);
-			*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
-			*ecc_code++ = ((bch_val1 & 0xF) << 4);
-			/*
-			 * Setting 8th byte to zero to handle
-			 * erased page
-			 */
-			*ecc_code++ = 0x0;
+		/* ECC scheme specific syndrome customizations */
+		switch (ecc_opt) {
+		case OMAP_ECC_HAMMING_CODE_DEFAULT:
+		case OMAP_ECC_HAMMING_CODE_HW:
+		case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
+			return -EINVAL;
+		case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+			for (i = 0; i < eccbytes; i++)
+				*(ecc_calc + i) = *(ecc_calc + i) ^
+							bch4_polynomial[i];
+			break;
+		case OMAP_ECC_BCH4_CODE_HW:
+			*(ecc_ptr++) = 0x00;
+			break;
+		case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+			for (i = 0; i < eccbytes; i++)
+				*(ecc_calc + i) = *(ecc_calc + i) ^
+							bch8_polynomial[i];
+			break;
+		case OMAP_ECC_BCH8_CODE_HW:
+			*(ecc_ptr++) = 0x00;
+			break;
 		}
+		/* update pointer to next sector */
+		ecc_calc += eccbytes;
 	}
-
 	return 0;
 }
 
+#ifdef CONFIG_MTD_NAND_OMAP_BCH
 /**
  * omap_elm_correct_data - corrects page data area in case error reported
  * @mtd:	MTD device structure
@@ -1790,7 +1731,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.strength		= 4;
 		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
 		info->nand.ecc.correct		= nand_bch_correct_data;
-		info->nand.ecc.calculate	= omap3_calculate_ecc_bch4;
+		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
 		info->nand.ecc.priv		= nand_bch_init(mtd,
 						info->nand.ecc.size,
@@ -1813,7 +1754,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.strength		= 4;
 		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
 		info->nand.ecc.correct		= omap_elm_correct_data;
-		info->nand.ecc.calculate	= omap3_calculate_ecc_bch;
+		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		info->nand.ecc.read_page	= omap_read_page_bch;
 		info->nand.ecc.write_page	= omap_write_page_bch;
 		/* ELM H/W engine is used for locating errors */
@@ -1840,7 +1781,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.strength		= 8;
 		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
 		info->nand.ecc.correct		= nand_bch_correct_data;
-		info->nand.ecc.calculate	= omap3_calculate_ecc_bch8;
+		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
 		info->nand.ecc.priv		= nand_bch_init(mtd,
 						info->nand.ecc.size,
@@ -1863,7 +1804,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.strength		= 8;
 		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
 		info->nand.ecc.correct		= omap_elm_correct_data;
-		info->nand.ecc.calculate	= omap3_calculate_ecc_bch;
+		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		info->nand.ecc.read_page	= omap_read_page_bch;
 		info->nand.ecc.write_page	= omap_write_page_bch;
 		/* ELM H/W engine is used for locating errors */
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 3/5] mtd: nand: omap: optimize chip->ecc.hwctl() for H/W ECC schemes
  2013-07-15 14:55 ` Pekon Gupta
@ 2013-07-15 14:55   ` Pekon Gupta
  -1 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, benoit.cousson, avinashphilipk, balbi, linux-mtd,
	linux-omap, Pekon Gupta

chip->ecc.hwctl() is used for preparing the H/W controller before read/write
NAND accesses (like assigning data-buf, enabling ECC scheme configs, etc.)

Though all ECC schemes in OMAP NAND driver use GPMC controller for generating
ECC syndrome (for both Read/Write accesses). But but in current code
HAM1_ECC and BCHx_ECC schemes implement individual function to achieve this.
This patch merges the GPMC configuration code for all ECC schemes into
single omap_enable_hwecc(), thus adding scalability for future ECC schemes.

 omap_enable_hwecc() + omap3_enable_hwecc_bch() -> omap_enable_hwecc()

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/nand/omap2.c | 216 +++++++++++++++++------------------------------
 1 file changed, 79 insertions(+), 137 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 7f7a2ee..a259761 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -38,6 +38,10 @@
 #define	DRIVER_NAME	"omap2-nand"
 #define	OMAP_NAND_TIMEOUT_MS	5000
 
+#define GPMC_ECC_READ		0 /* Reset Hardware ECC for read */
+#define GPMC_ECC_WRITE		1 /* Reset Hardware ECC for write */
+#define GPMC_ECC_READSYN	2 /* Reset before syndrom is read back */
+
 #define NAND_Ecc_P1e		(1 << 0)
 #define NAND_Ecc_P2e		(1 << 1)
 #define NAND_Ecc_P4e		(1 << 2)
@@ -106,13 +110,9 @@
 #define P4o_s(a)	(TF(a & NAND_Ecc_P4o)		<< 1)
 
 #define	PREFETCH_CONFIG1_CS_SHIFT	24
-#define	ECC_CONFIG_CS_SHIFT		1
 #define	CS_MASK				0x7
 #define	ENABLE_PREFETCH			(0x1 << 7)
 #define	DMA_MPU_MODE_SHIFT		2
-#define	ECCSIZE0_SHIFT			12
-#define	ECCSIZE1_SHIFT			22
-#define	ECC1RESULTSIZE			0x1
 #define	ECCCLEAR			0x100
 #define	ECC1				0x1
 #define	PREFETCH_FIFOTHRESHOLD_MAX	0x40
@@ -123,26 +123,9 @@
 
 #define OMAP24XX_DMA_GPMC		4
 
-#define BCH8_MAX_ERROR		8	/* upto 8 bit correctable */
-#define BCH4_MAX_ERROR		4	/* upto 4 bit correctable */
-
 #define SECTOR_BYTES		512
 /* 4 bit padding to make byte aligned, 56 = 52 + 4 */
 #define BCH4_BIT_PAD		4
-#define BCH8_ECC_MAX		((SECTOR_BYTES + BCH8_ECC_OOB_BYTES) * 8)
-#define BCH4_ECC_MAX		((SECTOR_BYTES + BCH4_ECC_OOB_BYTES) * 8)
-
-/* GPMC ecc engine settings for read */
-#define BCH_WRAPMODE_1		1	/* BCH wrap mode 1 */
-#define BCH8R_ECC_SIZE0		0x1a	/* ecc_size0 = 26 */
-#define BCH8R_ECC_SIZE1		0x2	/* ecc_size1 = 2 */
-#define BCH4R_ECC_SIZE0		0xd	/* ecc_size0 = 13 */
-#define BCH4R_ECC_SIZE1		0x3	/* ecc_size1 = 3 */
-
-/* GPMC ecc engine settings for write */
-#define BCH_WRAPMODE_6		6	/* BCH wrap mode 6 */
-#define BCH_ECC_SIZE0		0x0	/* ecc_size0 = 0, no oob protection */
-#define BCH_ECC_SIZE1		0x20	/* ecc_size1 = 32 */
 
 #define BADBLOCK_MARKER_LENGTH		0x2
 
@@ -191,7 +174,6 @@ struct omap_nand_info {
 	int				buf_len;
 	struct gpmc_nand_regs		reg;
 	/* fields specific for BCHx_HW ECC scheme */
-	bool				is_elm_used;
 	struct device			*elm_dev;
 	struct device_node		*of_node;
 };
@@ -952,7 +934,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 	u32 val;
 
 	val = readl(info->reg.gpmc_ecc_config);
-	if (((val >> 1) & 0x07) != info->gpmc_cs) {
+	if (((val >> 1) & 0x7) != info->gpmc_cs) {
 		pr_err("%s: invalid ECC configuration for chip-select=%d",
 				DRIVER_NAME, info->gpmc_cs);
 		return -EINVAL;
@@ -968,47 +950,6 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 }
 
 /**
- * omap_enable_hwecc - This function enables the hardware ecc functionality
- * @mtd: MTD device structure
- * @mode: Read/Write mode
- */
-static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
-{
-	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
-							mtd);
-	struct nand_chip *chip = mtd->priv;
-	unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
-	u32 val;
-
-	/* clear ecc and enable bits */
-	val = ECCCLEAR | ECC1;
-	writel(val, info->reg.gpmc_ecc_control);
-
-	/* program ecc and result sizes */
-	val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) |
-			 ECC1RESULTSIZE);
-	writel(val, info->reg.gpmc_ecc_size_config);
-
-	switch (mode) {
-	case NAND_ECC_READ:
-	case NAND_ECC_WRITE:
-		writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
-		break;
-	case NAND_ECC_READSYN:
-		writel(ECCCLEAR, info->reg.gpmc_ecc_control);
-		break;
-	default:
-		dev_info(&info->pdev->dev,
-			"error: unrecognized Mode[%d]!\n", mode);
-		break;
-	}
-
-	/* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
-	val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
-	writel(val, info->reg.gpmc_ecc_config);
-}
-
-/**
  * omap_wait - wait until the command is done
  * @mtd: MTD device structure
  * @chip: NAND Chip structure
@@ -1065,84 +1006,87 @@ static int omap_dev_ready(struct mtd_info *mtd)
 }
 
 /**
- * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction
+ * omap_enable_hwecc - Configure OMAP GPMC to perform ECC calculation
  * @mtd: MTD device structure
  * @mode: Read/Write mode
- *
- * When using BCH, sector size is hardcoded to 512 bytes.
- * Using wrapping mode 6 both for reading and writing if ELM module not uses
- * for error correction.
- * On writing,
- * eccsize0 = 0  (no additional protected byte in spare area)
- * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
+ * Configurations for eccsize0, eccsize1, and bch_wrapmode are based on
+ * GPMC function spec:
+ * Section 4.6.3.2.3: Supported NAND page mappings and ECC schemes
  */
-static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
+static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
 {
-	int nerrors;
-	unsigned int dev_width, nsectors;
 	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 						   mtd);
 	struct nand_chip *chip = mtd->priv;
-	u32 val, wr_mode;
-	unsigned int ecc_size1, ecc_size0;
-
-	/* Using wrapping mode 6 for writing */
-	wr_mode = BCH_WRAPMODE_6;
-
-	/*
-	 * ECC engine enabled for valid ecc_size0 nibbles
-	 * and disabled for ecc_size1 nibbles.
-	 */
-	ecc_size0 = BCH_ECC_SIZE0;
-	ecc_size1 = BCH_ECC_SIZE1;
-
-	/* Perform ecc calculation on 512-byte sector */
-	nsectors = 1;
-
-	/* Update number of error correction */
-	nerrors = info->nand.ecc.strength;
-
-	/* Multi sector reading/writing for NAND flash with page size < 4096 */
-	if (info->is_elm_used && (mtd->writesize <= 4096)) {
-		if (mode == NAND_ECC_READ) {
-			/* Using wrapping mode 1 for reading */
-			wr_mode = BCH_WRAPMODE_1;
-
-			/*
-			 * ECC engine enabled for ecc_size0 nibbles
-			 * and disabled for ecc_size1 nibbles.
-			 */
-			ecc_size0 = (nerrors == 8) ?
-				BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0;
-			ecc_size1 = (nerrors == 8) ?
-				BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1;
+	unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
+	unsigned int nsectors = (mtd->writesize / SECTOR_BYTES);
+	unsigned int ecc_algo = 0;
+	unsigned int bch_type = 0;
+	unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00;
+	u32 ecc_size_config_val = 0;
+	u32 ecc_config_val = 0;
+
+	switch (info->ecc_opt) {
+	case OMAP_ECC_HAMMING_CODE_DEFAULT:
+		pr_err("%s: invalid driver configuration", DRIVER_NAME);
+		break;
+	case OMAP_ECC_HAMMING_CODE_HW:
+	case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
+		ecc_algo = 0x0;
+		bch_wrapmode = 0x00;
+		eccsize0 = (chip->ecc.size >> 1) - 1;
+		eccsize1 = 0;
+		nsectors = 0;
+		break;
+	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+	case OMAP_ECC_BCH4_CODE_HW:
+		ecc_algo = 0x1;
+		bch_type = 0x0;
+		if (mode == GPMC_ECC_READ) {
+			bch_wrapmode = 0x01;
+			eccsize0 = 13; /* ECC bits in nibbles per sector */
+			eccsize1 = 3;  /* non-ECC bits in nibbles per sector */
+		} else if (mode == GPMC_ECC_WRITE) {
+			eccsize0 = 0;  /* extra bits in nibbles per sector */
+			eccsize1 = 32; /* OOB bits in nibbles per sector */
+			bch_wrapmode = 0x06;
 		}
-
-		/* Perform ecc calculation for one page (< 4096) */
-		nsectors = info->nand.ecc.steps;
+		break;
+	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+	case OMAP_ECC_BCH8_CODE_HW:
+		ecc_algo = 0x1;
+		bch_type = 0x1;
+		if (mode == GPMC_ECC_READ) {
+			bch_wrapmode = 0x01;
+			eccsize0 = 26; /* ECC bits in nibbles per sector */
+			eccsize1 = 2;  /* non-ECC bits in nibbles per sector */
+		} else if (mode == GPMC_ECC_WRITE) {
+			bch_wrapmode = 0x01;
+			eccsize0 = 0;  /* extra bits in nibbles per sector */
+			eccsize1 = 28; /* OOB bits in nibbles per sector */
+		}
+		break;
+	default:
+		pr_err("selected ECC scheme not supported or not enabled\n");
 	}
-
-	writel(ECC1, info->reg.gpmc_ecc_control);
-
-	/* Configure ecc size for BCH */
-	val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT);
-	writel(val, info->reg.gpmc_ecc_size_config);
-
-	dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
-
-	/* BCH configuration */
-	val = ((1                        << 16) | /* enable BCH */
-	       (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
-	       (wr_mode                  <<  8) | /* wrap mode */
-	       (dev_width                <<  7) | /* bus width */
-	       (((nsectors-1) & 0x7)     <<  4) | /* number of sectors */
-	       (info->gpmc_cs            <<  1) | /* ECC CS */
-	       (0x1));                            /* enable ECC */
-
-	writel(val, info->reg.gpmc_ecc_config);
-
 	/* Clear ecc and enable bits */
 	writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
+	/* Configure ecc size for BCH */
+	ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12);
+	writel(ecc_size_config_val, info->reg.gpmc_ecc_size_config);
+	/* Configure device details for BCH engine */
+	ecc_config_val = ((ecc_algo << 16)	| /* HAM1 | BCHx */
+			(bch_type << 12)	| /* BCH4/BCH8/BCH16 */
+			(bch_wrapmode << 8)	| /* wrap mode */
+			(dev_width << 7)	| /* bus width */
+			(((nsectors-1) & 0x7) << 4) | /* number of sectors */
+			(info->gpmc_cs <<  1)	| /* ECC CS */
+			(0x0));			/* disable ECC */
+	writel(ecc_config_val, info->reg.gpmc_ecc_config);
+	/* enable ECC engine */
+	writel(ecc_config_val | 0x1, info->reg.gpmc_ecc_config);
+	/* Clear ECC and enable bits */
+	writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
 }
 
 /**
@@ -1460,7 +1404,6 @@ static int is_elm_present(struct omap_nand_info *info, enum bch_ecc bch_type)
 	int lenp;
 	struct device_node *elm_node;
 	struct platform_device *pdev;
-	info->is_elm_used = false;
 
 	/* Detect availability of ELM module */
 	parp = of_get_property(info->of_node, "elm_id", &lenp);
@@ -1472,7 +1415,6 @@ static int is_elm_present(struct omap_nand_info *info, enum bch_ecc bch_type)
 		info->elm_dev = &pdev->dev;
 		/* ELM module available, now configure it */
 		elm_config(info->elm_dev, bch_type);
-		info->is_elm_used = true;
 		return 0;
 	}
 
@@ -1729,7 +1671,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.size		= 512;
 		info->nand.ecc.bytes		= 7;
 		info->nand.ecc.strength		= 4;
-		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
+		info->nand.ecc.hwctl		= omap_enable_hwecc;
 		info->nand.ecc.correct		= nand_bch_correct_data;
 		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
@@ -1752,7 +1694,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		/* 8th bit is kept reserved for ROM-code compatibility */
 		info->nand.ecc.bytes		= 7 + 1;
 		info->nand.ecc.strength		= 4;
-		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
+		info->nand.ecc.hwctl		= omap_enable_hwecc;
 		info->nand.ecc.correct		= omap_elm_correct_data;
 		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		info->nand.ecc.read_page	= omap_read_page_bch;
@@ -1779,7 +1721,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.size		= 512;
 		info->nand.ecc.bytes		= 13;
 		info->nand.ecc.strength		= 8;
-		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
+		info->nand.ecc.hwctl		= omap_enable_hwecc;
 		info->nand.ecc.correct		= nand_bch_correct_data;
 		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
@@ -1802,7 +1744,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		/* 14th bit is kept reserved for ROM-code compatibility */
 		info->nand.ecc.bytes		= 13 + 1;
 		info->nand.ecc.strength		= 8;
-		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
+		info->nand.ecc.hwctl		= omap_enable_hwecc;
 		info->nand.ecc.correct		= omap_elm_correct_data;
 		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		info->nand.ecc.read_page	= omap_read_page_bch;
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 3/5] mtd: nand: omap: optimize chip->ecc.hwctl() for H/W ECC schemes
@ 2013-07-15 14:55   ` Pekon Gupta
  0 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, avinashphilipk, balbi, linux-mtd, Pekon Gupta,
	benoit.cousson, linux-omap

chip->ecc.hwctl() is used for preparing the H/W controller before read/write
NAND accesses (like assigning data-buf, enabling ECC scheme configs, etc.)

Though all ECC schemes in OMAP NAND driver use GPMC controller for generating
ECC syndrome (for both Read/Write accesses). But but in current code
HAM1_ECC and BCHx_ECC schemes implement individual function to achieve this.
This patch merges the GPMC configuration code for all ECC schemes into
single omap_enable_hwecc(), thus adding scalability for future ECC schemes.

 omap_enable_hwecc() + omap3_enable_hwecc_bch() -> omap_enable_hwecc()

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/nand/omap2.c | 216 +++++++++++++++++------------------------------
 1 file changed, 79 insertions(+), 137 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 7f7a2ee..a259761 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -38,6 +38,10 @@
 #define	DRIVER_NAME	"omap2-nand"
 #define	OMAP_NAND_TIMEOUT_MS	5000
 
+#define GPMC_ECC_READ		0 /* Reset Hardware ECC for read */
+#define GPMC_ECC_WRITE		1 /* Reset Hardware ECC for write */
+#define GPMC_ECC_READSYN	2 /* Reset before syndrom is read back */
+
 #define NAND_Ecc_P1e		(1 << 0)
 #define NAND_Ecc_P2e		(1 << 1)
 #define NAND_Ecc_P4e		(1 << 2)
@@ -106,13 +110,9 @@
 #define P4o_s(a)	(TF(a & NAND_Ecc_P4o)		<< 1)
 
 #define	PREFETCH_CONFIG1_CS_SHIFT	24
-#define	ECC_CONFIG_CS_SHIFT		1
 #define	CS_MASK				0x7
 #define	ENABLE_PREFETCH			(0x1 << 7)
 #define	DMA_MPU_MODE_SHIFT		2
-#define	ECCSIZE0_SHIFT			12
-#define	ECCSIZE1_SHIFT			22
-#define	ECC1RESULTSIZE			0x1
 #define	ECCCLEAR			0x100
 #define	ECC1				0x1
 #define	PREFETCH_FIFOTHRESHOLD_MAX	0x40
@@ -123,26 +123,9 @@
 
 #define OMAP24XX_DMA_GPMC		4
 
-#define BCH8_MAX_ERROR		8	/* upto 8 bit correctable */
-#define BCH4_MAX_ERROR		4	/* upto 4 bit correctable */
-
 #define SECTOR_BYTES		512
 /* 4 bit padding to make byte aligned, 56 = 52 + 4 */
 #define BCH4_BIT_PAD		4
-#define BCH8_ECC_MAX		((SECTOR_BYTES + BCH8_ECC_OOB_BYTES) * 8)
-#define BCH4_ECC_MAX		((SECTOR_BYTES + BCH4_ECC_OOB_BYTES) * 8)
-
-/* GPMC ecc engine settings for read */
-#define BCH_WRAPMODE_1		1	/* BCH wrap mode 1 */
-#define BCH8R_ECC_SIZE0		0x1a	/* ecc_size0 = 26 */
-#define BCH8R_ECC_SIZE1		0x2	/* ecc_size1 = 2 */
-#define BCH4R_ECC_SIZE0		0xd	/* ecc_size0 = 13 */
-#define BCH4R_ECC_SIZE1		0x3	/* ecc_size1 = 3 */
-
-/* GPMC ecc engine settings for write */
-#define BCH_WRAPMODE_6		6	/* BCH wrap mode 6 */
-#define BCH_ECC_SIZE0		0x0	/* ecc_size0 = 0, no oob protection */
-#define BCH_ECC_SIZE1		0x20	/* ecc_size1 = 32 */
 
 #define BADBLOCK_MARKER_LENGTH		0x2
 
@@ -191,7 +174,6 @@ struct omap_nand_info {
 	int				buf_len;
 	struct gpmc_nand_regs		reg;
 	/* fields specific for BCHx_HW ECC scheme */
-	bool				is_elm_used;
 	struct device			*elm_dev;
 	struct device_node		*of_node;
 };
@@ -952,7 +934,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 	u32 val;
 
 	val = readl(info->reg.gpmc_ecc_config);
-	if (((val >> 1) & 0x07) != info->gpmc_cs) {
+	if (((val >> 1) & 0x7) != info->gpmc_cs) {
 		pr_err("%s: invalid ECC configuration for chip-select=%d",
 				DRIVER_NAME, info->gpmc_cs);
 		return -EINVAL;
@@ -968,47 +950,6 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 }
 
 /**
- * omap_enable_hwecc - This function enables the hardware ecc functionality
- * @mtd: MTD device structure
- * @mode: Read/Write mode
- */
-static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
-{
-	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
-							mtd);
-	struct nand_chip *chip = mtd->priv;
-	unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
-	u32 val;
-
-	/* clear ecc and enable bits */
-	val = ECCCLEAR | ECC1;
-	writel(val, info->reg.gpmc_ecc_control);
-
-	/* program ecc and result sizes */
-	val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) |
-			 ECC1RESULTSIZE);
-	writel(val, info->reg.gpmc_ecc_size_config);
-
-	switch (mode) {
-	case NAND_ECC_READ:
-	case NAND_ECC_WRITE:
-		writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
-		break;
-	case NAND_ECC_READSYN:
-		writel(ECCCLEAR, info->reg.gpmc_ecc_control);
-		break;
-	default:
-		dev_info(&info->pdev->dev,
-			"error: unrecognized Mode[%d]!\n", mode);
-		break;
-	}
-
-	/* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
-	val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
-	writel(val, info->reg.gpmc_ecc_config);
-}
-
-/**
  * omap_wait - wait until the command is done
  * @mtd: MTD device structure
  * @chip: NAND Chip structure
@@ -1065,84 +1006,87 @@ static int omap_dev_ready(struct mtd_info *mtd)
 }
 
 /**
- * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction
+ * omap_enable_hwecc - Configure OMAP GPMC to perform ECC calculation
  * @mtd: MTD device structure
  * @mode: Read/Write mode
- *
- * When using BCH, sector size is hardcoded to 512 bytes.
- * Using wrapping mode 6 both for reading and writing if ELM module not uses
- * for error correction.
- * On writing,
- * eccsize0 = 0  (no additional protected byte in spare area)
- * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
+ * Configurations for eccsize0, eccsize1, and bch_wrapmode are based on
+ * GPMC function spec:
+ * Section 4.6.3.2.3: Supported NAND page mappings and ECC schemes
  */
-static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
+static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
 {
-	int nerrors;
-	unsigned int dev_width, nsectors;
 	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
 						   mtd);
 	struct nand_chip *chip = mtd->priv;
-	u32 val, wr_mode;
-	unsigned int ecc_size1, ecc_size0;
-
-	/* Using wrapping mode 6 for writing */
-	wr_mode = BCH_WRAPMODE_6;
-
-	/*
-	 * ECC engine enabled for valid ecc_size0 nibbles
-	 * and disabled for ecc_size1 nibbles.
-	 */
-	ecc_size0 = BCH_ECC_SIZE0;
-	ecc_size1 = BCH_ECC_SIZE1;
-
-	/* Perform ecc calculation on 512-byte sector */
-	nsectors = 1;
-
-	/* Update number of error correction */
-	nerrors = info->nand.ecc.strength;
-
-	/* Multi sector reading/writing for NAND flash with page size < 4096 */
-	if (info->is_elm_used && (mtd->writesize <= 4096)) {
-		if (mode == NAND_ECC_READ) {
-			/* Using wrapping mode 1 for reading */
-			wr_mode = BCH_WRAPMODE_1;
-
-			/*
-			 * ECC engine enabled for ecc_size0 nibbles
-			 * and disabled for ecc_size1 nibbles.
-			 */
-			ecc_size0 = (nerrors == 8) ?
-				BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0;
-			ecc_size1 = (nerrors == 8) ?
-				BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1;
+	unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
+	unsigned int nsectors = (mtd->writesize / SECTOR_BYTES);
+	unsigned int ecc_algo = 0;
+	unsigned int bch_type = 0;
+	unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00;
+	u32 ecc_size_config_val = 0;
+	u32 ecc_config_val = 0;
+
+	switch (info->ecc_opt) {
+	case OMAP_ECC_HAMMING_CODE_DEFAULT:
+		pr_err("%s: invalid driver configuration", DRIVER_NAME);
+		break;
+	case OMAP_ECC_HAMMING_CODE_HW:
+	case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
+		ecc_algo = 0x0;
+		bch_wrapmode = 0x00;
+		eccsize0 = (chip->ecc.size >> 1) - 1;
+		eccsize1 = 0;
+		nsectors = 0;
+		break;
+	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+	case OMAP_ECC_BCH4_CODE_HW:
+		ecc_algo = 0x1;
+		bch_type = 0x0;
+		if (mode == GPMC_ECC_READ) {
+			bch_wrapmode = 0x01;
+			eccsize0 = 13; /* ECC bits in nibbles per sector */
+			eccsize1 = 3;  /* non-ECC bits in nibbles per sector */
+		} else if (mode == GPMC_ECC_WRITE) {
+			eccsize0 = 0;  /* extra bits in nibbles per sector */
+			eccsize1 = 32; /* OOB bits in nibbles per sector */
+			bch_wrapmode = 0x06;
 		}
-
-		/* Perform ecc calculation for one page (< 4096) */
-		nsectors = info->nand.ecc.steps;
+		break;
+	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+	case OMAP_ECC_BCH8_CODE_HW:
+		ecc_algo = 0x1;
+		bch_type = 0x1;
+		if (mode == GPMC_ECC_READ) {
+			bch_wrapmode = 0x01;
+			eccsize0 = 26; /* ECC bits in nibbles per sector */
+			eccsize1 = 2;  /* non-ECC bits in nibbles per sector */
+		} else if (mode == GPMC_ECC_WRITE) {
+			bch_wrapmode = 0x01;
+			eccsize0 = 0;  /* extra bits in nibbles per sector */
+			eccsize1 = 28; /* OOB bits in nibbles per sector */
+		}
+		break;
+	default:
+		pr_err("selected ECC scheme not supported or not enabled\n");
 	}
-
-	writel(ECC1, info->reg.gpmc_ecc_control);
-
-	/* Configure ecc size for BCH */
-	val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT);
-	writel(val, info->reg.gpmc_ecc_size_config);
-
-	dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
-
-	/* BCH configuration */
-	val = ((1                        << 16) | /* enable BCH */
-	       (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
-	       (wr_mode                  <<  8) | /* wrap mode */
-	       (dev_width                <<  7) | /* bus width */
-	       (((nsectors-1) & 0x7)     <<  4) | /* number of sectors */
-	       (info->gpmc_cs            <<  1) | /* ECC CS */
-	       (0x1));                            /* enable ECC */
-
-	writel(val, info->reg.gpmc_ecc_config);
-
 	/* Clear ecc and enable bits */
 	writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
+	/* Configure ecc size for BCH */
+	ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12);
+	writel(ecc_size_config_val, info->reg.gpmc_ecc_size_config);
+	/* Configure device details for BCH engine */
+	ecc_config_val = ((ecc_algo << 16)	| /* HAM1 | BCHx */
+			(bch_type << 12)	| /* BCH4/BCH8/BCH16 */
+			(bch_wrapmode << 8)	| /* wrap mode */
+			(dev_width << 7)	| /* bus width */
+			(((nsectors-1) & 0x7) << 4) | /* number of sectors */
+			(info->gpmc_cs <<  1)	| /* ECC CS */
+			(0x0));			/* disable ECC */
+	writel(ecc_config_val, info->reg.gpmc_ecc_config);
+	/* enable ECC engine */
+	writel(ecc_config_val | 0x1, info->reg.gpmc_ecc_config);
+	/* Clear ECC and enable bits */
+	writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
 }
 
 /**
@@ -1460,7 +1404,6 @@ static int is_elm_present(struct omap_nand_info *info, enum bch_ecc bch_type)
 	int lenp;
 	struct device_node *elm_node;
 	struct platform_device *pdev;
-	info->is_elm_used = false;
 
 	/* Detect availability of ELM module */
 	parp = of_get_property(info->of_node, "elm_id", &lenp);
@@ -1472,7 +1415,6 @@ static int is_elm_present(struct omap_nand_info *info, enum bch_ecc bch_type)
 		info->elm_dev = &pdev->dev;
 		/* ELM module available, now configure it */
 		elm_config(info->elm_dev, bch_type);
-		info->is_elm_used = true;
 		return 0;
 	}
 
@@ -1729,7 +1671,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.size		= 512;
 		info->nand.ecc.bytes		= 7;
 		info->nand.ecc.strength		= 4;
-		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
+		info->nand.ecc.hwctl		= omap_enable_hwecc;
 		info->nand.ecc.correct		= nand_bch_correct_data;
 		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
@@ -1752,7 +1694,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		/* 8th bit is kept reserved for ROM-code compatibility */
 		info->nand.ecc.bytes		= 7 + 1;
 		info->nand.ecc.strength		= 4;
-		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
+		info->nand.ecc.hwctl		= omap_enable_hwecc;
 		info->nand.ecc.correct		= omap_elm_correct_data;
 		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		info->nand.ecc.read_page	= omap_read_page_bch;
@@ -1779,7 +1721,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		info->nand.ecc.size		= 512;
 		info->nand.ecc.bytes		= 13;
 		info->nand.ecc.strength		= 8;
-		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
+		info->nand.ecc.hwctl		= omap_enable_hwecc;
 		info->nand.ecc.correct		= nand_bch_correct_data;
 		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
@@ -1802,7 +1744,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 		/* 14th bit is kept reserved for ROM-code compatibility */
 		info->nand.ecc.bytes		= 13 + 1;
 		info->nand.ecc.strength		= 8;
-		info->nand.ecc.hwctl		= omap3_enable_hwecc_bch;
+		info->nand.ecc.hwctl		= omap_enable_hwecc;
 		info->nand.ecc.correct		= omap_elm_correct_data;
 		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
 		info->nand.ecc.read_page	= omap_read_page_bch;
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 4/5] mtd: devices: elm: add checks ELM H/W constrains, driver code cleanup
  2013-07-15 14:55 ` Pekon Gupta
@ 2013-07-15 14:55   ` Pekon Gupta
  -1 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, benoit.cousson, avinashphilipk, balbi, linux-mtd,
	linux-omap, Pekon Gupta

ELM H/W engine is used by BCHx_ECC schemes for detecting and locating bit-flips.
However, ELM H/W engine has some constrains like:
- ELM can decode errors in chunks of 512 data bytes only
- ELM can operate max upto 8 such buffers in parallel

This patch
- add checks for above constrains
- fixes ELM register configs based on number of info->eccsteps
- cleans-up elm_load_syndrome()

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/devices/elm.c         | 114 ++++++++++++++++++++++----------------
 drivers/mtd/nand/omap2.c          |   2 +-
 include/linux/platform_data/elm.h |   6 +-
 3 files changed, 70 insertions(+), 52 deletions(-)

diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c
index 4f683d2..fca436e 100644
--- a/drivers/mtd/devices/elm.c
+++ b/drivers/mtd/devices/elm.c
@@ -22,8 +22,11 @@
 #include <linux/of.h>
 #include <linux/sched.h>
 #include <linux/pm_runtime.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
 #include <linux/platform_data/elm.h>
 
+#define	DRIVER_NAME			"omap-elm"
 #define ELM_SYSCONFIG			0x010
 #define ELM_IRQSTATUS			0x018
 #define ELM_IRQENABLE			0x01c
@@ -82,8 +85,10 @@ struct elm_info {
 	void __iomem *elm_base;
 	struct completion elm_completion;
 	struct list_head list;
+	struct mtd_info *mtd;
 	enum bch_ecc bch_type;
 	struct elm_registers elm_regs;
+	int eccsteps;
 };
 
 static LIST_HEAD(elm_devices);
@@ -103,19 +108,36 @@ static u32 elm_read_reg(struct elm_info *info, int offset)
  * @dev:	ELM device
  * @bch_type:	Type of BCH ecc
  */
-int elm_config(struct device *dev, enum bch_ecc bch_type)
+int elm_config(struct device *dev, struct mtd_info *mtd,
+		enum bch_ecc bch_type)
 {
 	u32 reg_val;
-	struct elm_info *info = dev_get_drvdata(dev);
-
+	struct elm_info	 *info = dev_get_drvdata(dev);
+	struct nand_chip *chip = mtd->priv;
 	if (!info) {
 		dev_err(dev, "Unable to configure elm - device not probed?\n");
 		return -ENODEV;
 	}
-
+	if (!mtd) {
+		pr_err("%s: MTD device not found", DRIVER_NAME);
+		return -EINVAL;
+	}
+	/* ELM supports error correction in chunks of 512bytes of data only
+	 * where each 512bytes of data has its own ECC syndrome */
+	if (chip->ecc.size != 512) {
+		pr_err("%s: invalid ecc_size configuration", DRIVER_NAME);
+		return -EINVAL;
+	}
+	/* ELM eccsteps required to decode complete NAND page */
+	info->eccsteps = mtd->writesize / chip->ecc.size;
+	if (info->eccsteps > 8) {
+		pr_err("%s: eccsteps > 8 are not supported", DRIVER_NAME);
+		return -EINVAL;
+	}
+	info->mtd	= mtd;
+	info->bch_type	= bch_type;
 	reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
 	elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
-	info->bch_type = bch_type;
 
 	return 0;
 }
@@ -152,55 +174,51 @@ static void elm_configure_page_mode(struct elm_info *info, int index,
  * Load syndrome fragment registers with calculated ecc in reverse order.
  */
 static void elm_load_syndrome(struct elm_info *info,
-		struct elm_errorvec *err_vec, u8 *ecc)
+		struct elm_errorvec *err_vec, u8 *ecc_calc)
 {
+	struct nand_chip *chip	= info->mtd->priv;
+	unsigned int eccbytes	= chip->ecc.bytes;
+	u8 *ecc = ecc_calc;
 	int i, offset;
 	u32 val;
 
-	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
-
+	for (i = 0; i < info->eccsteps; i++) {
 		/* Check error reported */
 		if (err_vec[i].error_reported) {
 			elm_configure_page_mode(info, i, true);
-			offset = ELM_SYNDROME_FRAGMENT_0 +
-				SYNDROME_FRAGMENT_REG_SIZE * i;
-
-			/* BCH8 */
-			if (info->bch_type) {
-
-				/* syndrome fragment 0 = ecc[9-12B] */
-				val = cpu_to_be32(*(u32 *) &ecc[9]);
-				elm_write_reg(info, offset, val);
-
-				/* syndrome fragment 1 = ecc[5-8B] */
-				offset += 4;
-				val = cpu_to_be32(*(u32 *) &ecc[5]);
-				elm_write_reg(info, offset, val);
-
-				/* syndrome fragment 2 = ecc[1-4B] */
-				offset += 4;
-				val = cpu_to_be32(*(u32 *) &ecc[1]);
-				elm_write_reg(info, offset, val);
-
-				/* syndrome fragment 3 = ecc[0B] */
-				offset += 4;
-				val = ecc[0];
-				elm_write_reg(info, offset, val);
-			} else {
-				/* syndrome fragment 0 = ecc[20-52b] bits */
-				val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
-					((ecc[2] & 0xf) << 28);
-				elm_write_reg(info, offset, val);
-
-				/* syndrome fragment 1 = ecc[0-20b] bits */
-				offset += 4;
-				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
-				elm_write_reg(info, offset, val);
+			offset = SYNDROME_FRAGMENT_REG_SIZE * i;
+			ecc = ecc_calc + (i * eccbytes);
+			switch (info->bch_type) {
+			case BCH4_ECC:
+				val =	((*(ecc + 6) >>  4) & 0x0F) |
+					*(ecc +  5) <<  4 | *(ecc +  4) << 12 |
+					*(ecc +  3) << 20 | *(ecc +  2) << 28;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_0 +
+						 offset), cpu_to_le32(val));
+				val =	((*(ecc + 2) >>  4) & 0x0F) |
+					*(ecc +  1) <<  4 | *(ecc +  0) << 12;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_1 +
+						 offset), cpu_to_le32(val));
+				break;
+			case BCH8_ECC:
+				val =	*(ecc + 12) << 0  | *(ecc + 11) <<  8 |
+					*(ecc + 10) << 16 | *(ecc +  9) << 24;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_0 +
+						 offset), cpu_to_le32(val));
+				val =	*(ecc +  8) <<  0 | *(ecc +  7) <<  8 |
+					*(ecc +  6) << 16 | *(ecc +  5) << 24;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_1 +
+						 offset), cpu_to_le32(val));
+				val =	*(ecc +  4) <<  0 | *(ecc +  3) <<  8 |
+					*(ecc +  2) << 16 | *(ecc +  1) << 24;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_2 +
+						 offset), cpu_to_le32(val));
+				val =	*(ecc +  0) <<  0 & 0x000000FF;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_3 +
+						 offset), cpu_to_le32(val));
+				break;
 			}
 		}
-
-		/* Update ecc pointer with ecc byte size */
-		ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
 	}
 }
 
@@ -223,7 +241,7 @@ static void elm_start_processing(struct elm_info *info,
 	 * Set syndrome vector valid, so that ELM module
 	 * will process it for vectors error is reported
 	 */
-	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+	for (i = 0; i < info->eccsteps; i++) {
 		if (err_vec[i].error_reported) {
 			offset = ELM_SYNDROME_FRAGMENT_6 +
 				SYNDROME_FRAGMENT_REG_SIZE * i;
@@ -252,7 +270,7 @@ static void elm_error_correction(struct elm_info *info,
 	int offset;
 	u32 reg_val;
 
-	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+	for (i = 0; i < info->eccsteps; i++) {
 
 		/* Check error reported */
 		if (err_vec[i].error_reported) {
@@ -263,14 +281,12 @@ static void elm_error_correction(struct elm_info *info,
 			if (reg_val & ECC_CORRECTABLE_MASK) {
 				offset = ELM_ERROR_LOCATION_0 +
 					ERROR_LOCATION_SIZE * i;
-
 				/* Read count of correctable errors */
 				err_vec[i].error_count = reg_val &
 					ECC_NB_ERRORS_MASK;
 
 				/* Update the error locations in error vector */
 				for (j = 0; j < err_vec[i].error_count; j++) {
-
 					reg_val = elm_read_reg(info, offset);
 					err_vec[i].error_loc[j] = reg_val &
 						ECC_ERROR_LOCATION_MASK;
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index a259761..7d786d9 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -1414,7 +1414,7 @@ static int is_elm_present(struct omap_nand_info *info, enum bch_ecc bch_type)
 		pdev = of_find_device_by_node(elm_node);
 		info->elm_dev = &pdev->dev;
 		/* ELM module available, now configure it */
-		elm_config(info->elm_dev, bch_type);
+		elm_config(info->elm_dev, &info->mtd, bch_type);
 		return 0;
 	}
 
diff --git a/include/linux/platform_data/elm.h b/include/linux/platform_data/elm.h
index bf0a83b..d16465b 100644
--- a/include/linux/platform_data/elm.h
+++ b/include/linux/platform_data/elm.h
@@ -25,6 +25,7 @@ enum bch_ecc {
 
 /* ELM support 8 error syndrome process */
 #define ERROR_VECTOR_MAX		8
+#define ELM_MAX_DETECTABLE_ERRORS	16
 
 #define BCH8_ECC_OOB_BYTES		13
 #define BCH4_ECC_OOB_BYTES		7
@@ -45,10 +46,11 @@ struct elm_errorvec {
 	bool error_reported;
 	bool error_uncorrectable;
 	int error_count;
-	int error_loc[ERROR_VECTOR_MAX];
+	int error_loc[ELM_MAX_DETECTABLE_ERRORS];
 };
 
 void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
 		struct elm_errorvec *err_vec);
-int elm_config(struct device *dev, enum bch_ecc bch_type);
+int elm_config(struct device *dev, struct mtd_info *mtd,
+		enum bch_ecc bch_type);
 #endif /* __ELM_H */
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 4/5] mtd: devices: elm: add checks ELM H/W constrains, driver code cleanup
@ 2013-07-15 14:55   ` Pekon Gupta
  0 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, avinashphilipk, balbi, linux-mtd, Pekon Gupta,
	benoit.cousson, linux-omap

ELM H/W engine is used by BCHx_ECC schemes for detecting and locating bit-flips.
However, ELM H/W engine has some constrains like:
- ELM can decode errors in chunks of 512 data bytes only
- ELM can operate max upto 8 such buffers in parallel

This patch
- add checks for above constrains
- fixes ELM register configs based on number of info->eccsteps
- cleans-up elm_load_syndrome()

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/devices/elm.c         | 114 ++++++++++++++++++++++----------------
 drivers/mtd/nand/omap2.c          |   2 +-
 include/linux/platform_data/elm.h |   6 +-
 3 files changed, 70 insertions(+), 52 deletions(-)

diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c
index 4f683d2..fca436e 100644
--- a/drivers/mtd/devices/elm.c
+++ b/drivers/mtd/devices/elm.c
@@ -22,8 +22,11 @@
 #include <linux/of.h>
 #include <linux/sched.h>
 #include <linux/pm_runtime.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
 #include <linux/platform_data/elm.h>
 
+#define	DRIVER_NAME			"omap-elm"
 #define ELM_SYSCONFIG			0x010
 #define ELM_IRQSTATUS			0x018
 #define ELM_IRQENABLE			0x01c
@@ -82,8 +85,10 @@ struct elm_info {
 	void __iomem *elm_base;
 	struct completion elm_completion;
 	struct list_head list;
+	struct mtd_info *mtd;
 	enum bch_ecc bch_type;
 	struct elm_registers elm_regs;
+	int eccsteps;
 };
 
 static LIST_HEAD(elm_devices);
@@ -103,19 +108,36 @@ static u32 elm_read_reg(struct elm_info *info, int offset)
  * @dev:	ELM device
  * @bch_type:	Type of BCH ecc
  */
-int elm_config(struct device *dev, enum bch_ecc bch_type)
+int elm_config(struct device *dev, struct mtd_info *mtd,
+		enum bch_ecc bch_type)
 {
 	u32 reg_val;
-	struct elm_info *info = dev_get_drvdata(dev);
-
+	struct elm_info	 *info = dev_get_drvdata(dev);
+	struct nand_chip *chip = mtd->priv;
 	if (!info) {
 		dev_err(dev, "Unable to configure elm - device not probed?\n");
 		return -ENODEV;
 	}
-
+	if (!mtd) {
+		pr_err("%s: MTD device not found", DRIVER_NAME);
+		return -EINVAL;
+	}
+	/* ELM supports error correction in chunks of 512bytes of data only
+	 * where each 512bytes of data has its own ECC syndrome */
+	if (chip->ecc.size != 512) {
+		pr_err("%s: invalid ecc_size configuration", DRIVER_NAME);
+		return -EINVAL;
+	}
+	/* ELM eccsteps required to decode complete NAND page */
+	info->eccsteps = mtd->writesize / chip->ecc.size;
+	if (info->eccsteps > 8) {
+		pr_err("%s: eccsteps > 8 are not supported", DRIVER_NAME);
+		return -EINVAL;
+	}
+	info->mtd	= mtd;
+	info->bch_type	= bch_type;
 	reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
 	elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
-	info->bch_type = bch_type;
 
 	return 0;
 }
@@ -152,55 +174,51 @@ static void elm_configure_page_mode(struct elm_info *info, int index,
  * Load syndrome fragment registers with calculated ecc in reverse order.
  */
 static void elm_load_syndrome(struct elm_info *info,
-		struct elm_errorvec *err_vec, u8 *ecc)
+		struct elm_errorvec *err_vec, u8 *ecc_calc)
 {
+	struct nand_chip *chip	= info->mtd->priv;
+	unsigned int eccbytes	= chip->ecc.bytes;
+	u8 *ecc = ecc_calc;
 	int i, offset;
 	u32 val;
 
-	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
-
+	for (i = 0; i < info->eccsteps; i++) {
 		/* Check error reported */
 		if (err_vec[i].error_reported) {
 			elm_configure_page_mode(info, i, true);
-			offset = ELM_SYNDROME_FRAGMENT_0 +
-				SYNDROME_FRAGMENT_REG_SIZE * i;
-
-			/* BCH8 */
-			if (info->bch_type) {
-
-				/* syndrome fragment 0 = ecc[9-12B] */
-				val = cpu_to_be32(*(u32 *) &ecc[9]);
-				elm_write_reg(info, offset, val);
-
-				/* syndrome fragment 1 = ecc[5-8B] */
-				offset += 4;
-				val = cpu_to_be32(*(u32 *) &ecc[5]);
-				elm_write_reg(info, offset, val);
-
-				/* syndrome fragment 2 = ecc[1-4B] */
-				offset += 4;
-				val = cpu_to_be32(*(u32 *) &ecc[1]);
-				elm_write_reg(info, offset, val);
-
-				/* syndrome fragment 3 = ecc[0B] */
-				offset += 4;
-				val = ecc[0];
-				elm_write_reg(info, offset, val);
-			} else {
-				/* syndrome fragment 0 = ecc[20-52b] bits */
-				val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
-					((ecc[2] & 0xf) << 28);
-				elm_write_reg(info, offset, val);
-
-				/* syndrome fragment 1 = ecc[0-20b] bits */
-				offset += 4;
-				val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
-				elm_write_reg(info, offset, val);
+			offset = SYNDROME_FRAGMENT_REG_SIZE * i;
+			ecc = ecc_calc + (i * eccbytes);
+			switch (info->bch_type) {
+			case BCH4_ECC:
+				val =	((*(ecc + 6) >>  4) & 0x0F) |
+					*(ecc +  5) <<  4 | *(ecc +  4) << 12 |
+					*(ecc +  3) << 20 | *(ecc +  2) << 28;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_0 +
+						 offset), cpu_to_le32(val));
+				val =	((*(ecc + 2) >>  4) & 0x0F) |
+					*(ecc +  1) <<  4 | *(ecc +  0) << 12;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_1 +
+						 offset), cpu_to_le32(val));
+				break;
+			case BCH8_ECC:
+				val =	*(ecc + 12) << 0  | *(ecc + 11) <<  8 |
+					*(ecc + 10) << 16 | *(ecc +  9) << 24;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_0 +
+						 offset), cpu_to_le32(val));
+				val =	*(ecc +  8) <<  0 | *(ecc +  7) <<  8 |
+					*(ecc +  6) << 16 | *(ecc +  5) << 24;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_1 +
+						 offset), cpu_to_le32(val));
+				val =	*(ecc +  4) <<  0 | *(ecc +  3) <<  8 |
+					*(ecc +  2) << 16 | *(ecc +  1) << 24;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_2 +
+						 offset), cpu_to_le32(val));
+				val =	*(ecc +  0) <<  0 & 0x000000FF;
+				elm_write_reg(info, (ELM_SYNDROME_FRAGMENT_3 +
+						 offset), cpu_to_le32(val));
+				break;
 			}
 		}
-
-		/* Update ecc pointer with ecc byte size */
-		ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
 	}
 }
 
@@ -223,7 +241,7 @@ static void elm_start_processing(struct elm_info *info,
 	 * Set syndrome vector valid, so that ELM module
 	 * will process it for vectors error is reported
 	 */
-	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+	for (i = 0; i < info->eccsteps; i++) {
 		if (err_vec[i].error_reported) {
 			offset = ELM_SYNDROME_FRAGMENT_6 +
 				SYNDROME_FRAGMENT_REG_SIZE * i;
@@ -252,7 +270,7 @@ static void elm_error_correction(struct elm_info *info,
 	int offset;
 	u32 reg_val;
 
-	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+	for (i = 0; i < info->eccsteps; i++) {
 
 		/* Check error reported */
 		if (err_vec[i].error_reported) {
@@ -263,14 +281,12 @@ static void elm_error_correction(struct elm_info *info,
 			if (reg_val & ECC_CORRECTABLE_MASK) {
 				offset = ELM_ERROR_LOCATION_0 +
 					ERROR_LOCATION_SIZE * i;
-
 				/* Read count of correctable errors */
 				err_vec[i].error_count = reg_val &
 					ECC_NB_ERRORS_MASK;
 
 				/* Update the error locations in error vector */
 				for (j = 0; j < err_vec[i].error_count; j++) {
-
 					reg_val = elm_read_reg(info, offset);
 					err_vec[i].error_loc[j] = reg_val &
 						ECC_ERROR_LOCATION_MASK;
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index a259761..7d786d9 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -1414,7 +1414,7 @@ static int is_elm_present(struct omap_nand_info *info, enum bch_ecc bch_type)
 		pdev = of_find_device_by_node(elm_node);
 		info->elm_dev = &pdev->dev;
 		/* ELM module available, now configure it */
-		elm_config(info->elm_dev, bch_type);
+		elm_config(info->elm_dev, &info->mtd, bch_type);
 		return 0;
 	}
 
diff --git a/include/linux/platform_data/elm.h b/include/linux/platform_data/elm.h
index bf0a83b..d16465b 100644
--- a/include/linux/platform_data/elm.h
+++ b/include/linux/platform_data/elm.h
@@ -25,6 +25,7 @@ enum bch_ecc {
 
 /* ELM support 8 error syndrome process */
 #define ERROR_VECTOR_MAX		8
+#define ELM_MAX_DETECTABLE_ERRORS	16
 
 #define BCH8_ECC_OOB_BYTES		13
 #define BCH4_ECC_OOB_BYTES		7
@@ -45,10 +46,11 @@ struct elm_errorvec {
 	bool error_reported;
 	bool error_uncorrectable;
 	int error_count;
-	int error_loc[ERROR_VECTOR_MAX];
+	int error_loc[ELM_MAX_DETECTABLE_ERRORS];
 };
 
 void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
 		struct elm_errorvec *err_vec);
-int elm_config(struct device *dev, enum bch_ecc bch_type);
+int elm_config(struct device *dev, struct mtd_info *mtd,
+		enum bch_ecc bch_type);
 #endif /* __ELM_H */
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 5/5] mtd: nand: omap: code clean-up and white-space fixes
  2013-07-15 14:55 ` Pekon Gupta
@ 2013-07-15 14:55   ` Pekon Gupta
  -1 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, benoit.cousson, avinashphilipk, balbi, linux-mtd,
	linux-omap, Pekon Gupta

This patch just clean-up the code and fixes spaces in omap_nand_probe()
s/info->nand./chip->
s/&info->mtd/mtd

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/nand/omap2.c | 210 +++++++++++++++++++++++------------------------
 1 file changed, 102 insertions(+), 108 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 7d786d9..c2c3c6bb 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -1462,9 +1462,9 @@ static int omap_nand_probe(struct platform_device *pdev)
 	info->reg		= pdata->reg;
 	info->ecc_opt		= pdata->ecc_opt;
 
-	info->nand.options	= NAND_BUSWIDTH_AUTO;
-	info->nand.options	|= NAND_SKIP_BBTSCAN;
-	info->nand.ecc.priv	= NULL;
+	chip->options	= NAND_BUSWIDTH_AUTO;
+	chip->options	|= NAND_SKIP_BBTSCAN;
+	chip->ecc.priv	= NULL;
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
 	info->of_node		= pdata->of_node;
 #endif
@@ -1485,16 +1485,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 		goto out_free_info;
 	}
 
-	info->nand.IO_ADDR_R = ioremap(info->phys_base, info->mem_size);
-	if (!info->nand.IO_ADDR_R) {
+	chip->IO_ADDR_R = ioremap(info->phys_base, info->mem_size);
+	if (!chip->IO_ADDR_R) {
 		err = -ENOMEM;
 		goto out_release_mem_region;
 	}
 
-	info->nand.controller = &info->controller;
+	chip->controller = &info->controller;
 
-	info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
-	info->nand.cmd_ctrl  = omap_hwcontrol;
+	chip->IO_ADDR_W = chip->IO_ADDR_R;
+	chip->cmd_ctrl  = omap_hwcontrol;
 
 	/*
 	 * If RDY/BSY line is connected to OMAP then use the omap ready
@@ -1504,11 +1504,11 @@ static int omap_nand_probe(struct platform_device *pdev)
 	 * device and read status register until you get a failure or success
 	 */
 	if (pdata->dev_ready) {
-		info->nand.dev_ready = omap_dev_ready;
-		info->nand.chip_delay = 0;
+		chip->dev_ready = omap_dev_ready;
+		chip->chip_delay = 0;
 	} else {
-		info->nand.waitfunc = omap_wait;
-		info->nand.chip_delay = 50;
+		chip->waitfunc = omap_wait;
+		chip->chip_delay = 50;
 	}
 
 	/* scan NAND device conncted to controller */
@@ -1517,8 +1517,8 @@ static int omap_nand_probe(struct platform_device *pdev)
 		goto out_release_mem_region;
 	}
 	pr_info("%s: detected %s NAND flash\n", DRIVER_NAME,
-			(info->nand.options & NAND_BUSWIDTH_16) ? "x16" : "x8");
-	if ((info->nand.options & NAND_BUSWIDTH_16) !=
+			(chip->options & NAND_BUSWIDTH_16) ? "x16" : "x8");
+	if ((chip->options & NAND_BUSWIDTH_16) !=
 			(pdata->devsize & NAND_BUSWIDTH_16)) {
 		pr_err("%s: but incorrectly configured as %s", DRIVER_NAME,
 			(pdata->devsize & NAND_BUSWIDTH_16) ? "x16" : "x8");
@@ -1538,17 +1538,17 @@ static int omap_nand_probe(struct platform_device *pdev)
 	/* populate read & write API based on xfer_type selected */
 	switch (pdata->xfer_type) {
 	case NAND_OMAP_PREFETCH_POLLED:
-		info->nand.read_buf   = omap_read_buf_pref;
-		info->nand.write_buf  = omap_write_buf_pref;
+		chip->read_buf   = omap_read_buf_pref;
+		chip->write_buf  = omap_write_buf_pref;
 		break;
 
 	case NAND_OMAP_POLLED:
-		if (info->nand.options & NAND_BUSWIDTH_16) {
-			info->nand.read_buf   = omap_read_buf16;
-			info->nand.write_buf  = omap_write_buf16;
+		if (chip->options & NAND_BUSWIDTH_16) {
+			chip->read_buf   = omap_read_buf16;
+			chip->write_buf  = omap_write_buf16;
 		} else {
-			info->nand.read_buf   = omap_read_buf8;
-			info->nand.write_buf  = omap_write_buf8;
+			chip->read_buf   = omap_read_buf8;
+			chip->write_buf  = omap_write_buf8;
 		}
 		break;
 
@@ -1577,8 +1577,8 @@ static int omap_nand_probe(struct platform_device *pdev)
 					err);
 				goto out_release_mem_region;
 			}
-			info->nand.read_buf   = omap_read_buf_dma_pref;
-			info->nand.write_buf  = omap_write_buf_dma_pref;
+			chip->read_buf   = omap_read_buf_dma_pref;
+			chip->write_buf  = omap_write_buf_dma_pref;
 		}
 		break;
 
@@ -1613,8 +1613,8 @@ static int omap_nand_probe(struct platform_device *pdev)
 			goto out_release_mem_region;
 		}
 
-		info->nand.read_buf  = omap_read_buf_irq_pref;
-		info->nand.write_buf = omap_write_buf_irq_pref;
+		chip->read_buf  = omap_read_buf_irq_pref;
+		chip->write_buf = omap_write_buf_irq_pref;
 
 		break;
 
@@ -1629,34 +1629,33 @@ static int omap_nand_probe(struct platform_device *pdev)
 	switch (pdata->ecc_opt) {
 	case OMAP_ECC_HAMMING_CODE_DEFAULT:
 		pr_info("using OMAP_ECC_HAMMING_CODE_DEFAULT ECC scheme\n");
-		info->nand.ecc.mode		= NAND_ECC_SOFT;
+		chip->ecc.mode		= NAND_ECC_SOFT;
 		goto generic_ecc_layout;
 
 	case OMAP_ECC_HAMMING_CODE_HW:
 		pr_info("using OMAP_ECC_HAMMING_CODE_HW ECC scheme\n");
-		info->nand.ecc.mode             = NAND_ECC_HW;
-		info->nand.ecc.bytes            = 3;
-		info->nand.ecc.size             = 512;
-		info->nand.ecc.strength         = 1;
-		info->nand.ecc.calculate        = omap_calculate_ecc;
-		info->nand.ecc.hwctl            = omap_enable_hwecc;
-		info->nand.ecc.correct          = omap_correct_data;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.bytes		= 3;
+		chip->ecc.size		= 512;
+		chip->ecc.strength	= 1;
+		chip->ecc.calculate	= omap_calculate_ecc;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_correct_data;
 		goto generic_ecc_layout;
 
 	case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
 		pr_info("using OMAP_ECC_HAMMING_CODE_HW_ROMCODE ECC scheme\n");
-		info->nand.ecc.mode             = NAND_ECC_HW;
-		info->nand.ecc.bytes            = 3;
-		info->nand.ecc.size             = 512;
-		info->nand.ecc.strength         = 1;
-		info->nand.ecc.calculate        = omap_calculate_ecc;
-		info->nand.ecc.hwctl            = omap_enable_hwecc;
-		info->nand.ecc.correct          = omap_correct_data;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.bytes		= 3;
+		chip->ecc.size		= 512;
+		chip->ecc.strength	= 1;
+		chip->ecc.calculate	= omap_calculate_ecc;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_correct_data;
 		/* define custom ECC layout */
-		omap_oobinfo.eccbytes		= info->nand.ecc.bytes *
-							(mtd->writesize /
-							info->nand.ecc.size);
-		if (info->nand.options & NAND_BUSWIDTH_16)
+		omap_oobinfo.eccbytes	= chip->ecc.bytes *
+					(mtd->writesize / chip->ecc.size);
+		if (chip->options & NAND_BUSWIDTH_16)
 			omap_oobinfo.eccpos[0]	= BADBLOCK_MARKER_LENGTH;
 		else
 			omap_oobinfo.eccpos[0]	= 1;
@@ -1667,19 +1666,17 @@ static int omap_nand_probe(struct platform_device *pdev)
 #ifdef CONFIG_MTD_NAND_ECC_BCH
 	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
 		pr_info("using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW ECC scheme");
-		info->nand.ecc.mode		= NAND_ECC_HW;
-		info->nand.ecc.size		= 512;
-		info->nand.ecc.bytes		= 7;
-		info->nand.ecc.strength		= 4;
-		info->nand.ecc.hwctl		= omap_enable_hwecc;
-		info->nand.ecc.correct		= nand_bch_correct_data;
-		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		chip->ecc.bytes		= 7;
+		chip->ecc.strength	= 4;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= nand_bch_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
-		info->nand.ecc.priv		= nand_bch_init(mtd,
-						info->nand.ecc.size,
-						info->nand.ecc.bytes,
-						&info->nand.ecc.layout);
-		if (!info->nand.ecc.priv) {
+		chip->ecc.priv		= nand_bch_init(mtd, chip->ecc.size,
+					   chip->ecc.bytes, &chip->ecc.layout);
+		if (!chip->ecc.priv) {
 			pr_err("unable initialize S/W BCH logic\n");
 			err = -EINVAL;
 			goto out_release_mem_region;
@@ -1689,16 +1686,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
 	case OMAP_ECC_BCH4_CODE_HW:
 		pr_info("using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
-		info->nand.ecc.mode		= NAND_ECC_HW;
-		info->nand.ecc.size		= 512;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
 		/* 8th bit is kept reserved for ROM-code compatibility */
-		info->nand.ecc.bytes		= 7 + 1;
-		info->nand.ecc.strength		= 4;
-		info->nand.ecc.hwctl		= omap_enable_hwecc;
-		info->nand.ecc.correct		= omap_elm_correct_data;
-		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
-		info->nand.ecc.read_page	= omap_read_page_bch;
-		info->nand.ecc.write_page	= omap_write_page_bch;
+		chip->ecc.bytes		= 7 + 1;
+		chip->ecc.strength	= 4;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_elm_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch;
+		chip->ecc.read_page	= omap_read_page_bch;
+		chip->ecc.write_page	= omap_write_page_bch;
 		/* ELM H/W engine is used for locating errors */
 		if (is_elm_present(info, BCH4_ECC) < 0) {
 			pr_err("ELM module not detected, required for ECC\n");
@@ -1706,30 +1703,27 @@ static int omap_nand_probe(struct platform_device *pdev)
 			goto out_release_mem_region;
 		}
 		/* define custom ECC layout */
-		omap_oobinfo.eccbytes		= info->nand.ecc.bytes *
-							(mtd->writesize /
-							info->nand.ecc.size);
-		omap_oobinfo.eccpos[0]		= BADBLOCK_MARKER_LENGTH;
-		omap_oobinfo.oobfree->offset	= omap_oobinfo.eccpos[0] +
-							omap_oobinfo.eccbytes;
+		omap_oobinfo.eccbytes	= chip->ecc.bytes *
+					(mtd->writesize / chip->ecc.size);
+		omap_oobinfo.eccpos[0]	= BADBLOCK_MARKER_LENGTH;
+		omap_oobinfo.oobfree->offset = omap_oobinfo.eccpos[0] +
+						omap_oobinfo.eccbytes;
 		goto custom_ecc_layout;
 #endif
 #ifdef CONFIG_MTD_NAND_ECC_BCH
 	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
 		pr_info("using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW ECC\n");
-		info->nand.ecc.mode		= NAND_ECC_HW;
-		info->nand.ecc.size		= 512;
-		info->nand.ecc.bytes		= 13;
-		info->nand.ecc.strength		= 8;
-		info->nand.ecc.hwctl		= omap_enable_hwecc;
-		info->nand.ecc.correct		= nand_bch_correct_data;
-		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		chip->ecc.bytes		= 13;
+		chip->ecc.strength	= 8;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= nand_bch_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
-		info->nand.ecc.priv		= nand_bch_init(mtd,
-						info->nand.ecc.size,
-						info->nand.ecc.bytes,
-						&info->nand.ecc.layout);
-		if (!info->nand.ecc.priv) {
+		chip->ecc.priv		= nand_bch_init(mtd, chip->ecc.size,
+					   chip->ecc.bytes, &chip->ecc.layout);
+		if (!chip->ecc.priv) {
 			pr_err("unable initialize S/W BCH logic\n");
 			err = -EINVAL;
 			goto out_release_mem_region;
@@ -1739,16 +1733,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
 	case OMAP_ECC_BCH8_CODE_HW:
 		pr_info("using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
-		info->nand.ecc.mode		= NAND_ECC_HW;
-		info->nand.ecc.size		= 512;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
 		/* 14th bit is kept reserved for ROM-code compatibility */
-		info->nand.ecc.bytes		= 13 + 1;
-		info->nand.ecc.strength		= 8;
-		info->nand.ecc.hwctl		= omap_enable_hwecc;
-		info->nand.ecc.correct		= omap_elm_correct_data;
-		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
-		info->nand.ecc.read_page	= omap_read_page_bch;
-		info->nand.ecc.write_page	= omap_write_page_bch;
+		chip->ecc.bytes		= 13 + 1;
+		chip->ecc.strength	= 8;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_elm_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch;
+		chip->ecc.read_page	= omap_read_page_bch;
+		chip->ecc.write_page	= omap_write_page_bch;
 		/* ELM H/W engine is used for locating errors */
 		if (is_elm_present(info, BCH8_ECC) < 0) {
 			pr_err("ELM module not detected, required for ECC\n");
@@ -1756,16 +1750,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 			goto out_release_mem_region;
 		}
 		/* define custom ECC layout */
-		omap_oobinfo.eccbytes		= info->nand.ecc.bytes *
-							(mtd->writesize /
-							info->nand.ecc.size);
-		omap_oobinfo.eccpos[0]		= BADBLOCK_MARKER_LENGTH;
-		omap_oobinfo.oobfree->offset	= omap_oobinfo.eccpos[0] +
-							omap_oobinfo.eccbytes;
+		omap_oobinfo.eccbytes	= chip->ecc.bytes *
+					   (mtd->writesize / chip->ecc.size);
+		omap_oobinfo.eccpos[0]	= BADBLOCK_MARKER_LENGTH;
+		omap_oobinfo.oobfree->offset = omap_oobinfo.eccpos[0] +
+						omap_oobinfo.eccbytes;
 		goto custom_ecc_layout;
 #endif
 	default:
-		pr_err("selected ECC scheme not supported or not enabled\n");
+		pr_err("%s: selected ECC scheme not supported or not enabled",
+				DRIVER_NAME);
 		err = -EINVAL;
 		goto out_release_mem_region;
 	}
@@ -1775,8 +1769,8 @@ custom_ecc_layout:
 	pr_info("%s: using custom ecc layout\n", DRIVER_NAME);
 	omap_oobinfo.oobfree->length = mtd->oobsize - BADBLOCK_MARKER_LENGTH
 						- omap_oobinfo.eccbytes;
-	if (!(info->nand.options & NAND_BUSWIDTH_16))
-		info->nand.badblock_pattern = &bb_descrip_flashbased;
+	if (!(chip->options & NAND_BUSWIDTH_16))
+		chip->badblock_pattern = &bb_descrip_flashbased;
 	for (i = 1; i < omap_oobinfo.eccbytes; i++)
 		omap_oobinfo.eccpos[i] = omap_oobinfo.eccpos[0] + i;
 
@@ -1788,20 +1782,20 @@ custom_ecc_layout:
 		err = -EINVAL;
 		goto out_release_mem_region;
 	}
-	info->nand.ecc.layout = &omap_oobinfo;
+	chip->ecc.layout = &omap_oobinfo;
 
 generic_ecc_layout:
 	/* second phase scan */
-	if (nand_scan_tail(&info->mtd)) {
+	if (nand_scan_tail(mtd)) {
 		err = -ENXIO;
 		goto out_release_mem_region;
 	}
 
 	ppdata.of_node = pdata->of_node;
-	mtd_device_parse_register(&info->mtd, NULL, &ppdata, pdata->parts,
+	mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts,
 				  pdata->nr_parts);
 
-	platform_set_drvdata(pdev, &info->mtd);
+	platform_set_drvdata(pdev, mtd);
 
 	return 0;
 
@@ -1816,9 +1810,9 @@ out_release_mem_region:
 
 out_free_info:
 #ifdef CONFIG_MTD_NAND_ECC_BCH
-	if (info->nand.ecc.priv) {
-		nand_bch_free(info->nand.ecc.priv);
-		info->nand.ecc.priv = NULL;
+	if (chip->ecc.priv) {
+		nand_bch_free(chip->ecc.priv);
+		chip->ecc.priv = NULL;
 	}
 #endif
 	kfree(info);
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v1 5/5] mtd: nand: omap: code clean-up and white-space fixes
@ 2013-07-15 14:55   ` Pekon Gupta
  0 siblings, 0 replies; 16+ messages in thread
From: Pekon Gupta @ 2013-07-15 14:55 UTC (permalink / raw)
  To: dedekind1, dwmw2
  Cc: arnd, tony, avinashphilipk, balbi, linux-mtd, Pekon Gupta,
	benoit.cousson, linux-omap

This patch just clean-up the code and fixes spaces in omap_nand_probe()
s/info->nand./chip->
s/&info->mtd/mtd

Signed-off-by: Pekon Gupta <pekon@ti.com>
---
 drivers/mtd/nand/omap2.c | 210 +++++++++++++++++++++++------------------------
 1 file changed, 102 insertions(+), 108 deletions(-)

diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 7d786d9..c2c3c6bb 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -1462,9 +1462,9 @@ static int omap_nand_probe(struct platform_device *pdev)
 	info->reg		= pdata->reg;
 	info->ecc_opt		= pdata->ecc_opt;
 
-	info->nand.options	= NAND_BUSWIDTH_AUTO;
-	info->nand.options	|= NAND_SKIP_BBTSCAN;
-	info->nand.ecc.priv	= NULL;
+	chip->options	= NAND_BUSWIDTH_AUTO;
+	chip->options	|= NAND_SKIP_BBTSCAN;
+	chip->ecc.priv	= NULL;
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
 	info->of_node		= pdata->of_node;
 #endif
@@ -1485,16 +1485,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 		goto out_free_info;
 	}
 
-	info->nand.IO_ADDR_R = ioremap(info->phys_base, info->mem_size);
-	if (!info->nand.IO_ADDR_R) {
+	chip->IO_ADDR_R = ioremap(info->phys_base, info->mem_size);
+	if (!chip->IO_ADDR_R) {
 		err = -ENOMEM;
 		goto out_release_mem_region;
 	}
 
-	info->nand.controller = &info->controller;
+	chip->controller = &info->controller;
 
-	info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
-	info->nand.cmd_ctrl  = omap_hwcontrol;
+	chip->IO_ADDR_W = chip->IO_ADDR_R;
+	chip->cmd_ctrl  = omap_hwcontrol;
 
 	/*
 	 * If RDY/BSY line is connected to OMAP then use the omap ready
@@ -1504,11 +1504,11 @@ static int omap_nand_probe(struct platform_device *pdev)
 	 * device and read status register until you get a failure or success
 	 */
 	if (pdata->dev_ready) {
-		info->nand.dev_ready = omap_dev_ready;
-		info->nand.chip_delay = 0;
+		chip->dev_ready = omap_dev_ready;
+		chip->chip_delay = 0;
 	} else {
-		info->nand.waitfunc = omap_wait;
-		info->nand.chip_delay = 50;
+		chip->waitfunc = omap_wait;
+		chip->chip_delay = 50;
 	}
 
 	/* scan NAND device conncted to controller */
@@ -1517,8 +1517,8 @@ static int omap_nand_probe(struct platform_device *pdev)
 		goto out_release_mem_region;
 	}
 	pr_info("%s: detected %s NAND flash\n", DRIVER_NAME,
-			(info->nand.options & NAND_BUSWIDTH_16) ? "x16" : "x8");
-	if ((info->nand.options & NAND_BUSWIDTH_16) !=
+			(chip->options & NAND_BUSWIDTH_16) ? "x16" : "x8");
+	if ((chip->options & NAND_BUSWIDTH_16) !=
 			(pdata->devsize & NAND_BUSWIDTH_16)) {
 		pr_err("%s: but incorrectly configured as %s", DRIVER_NAME,
 			(pdata->devsize & NAND_BUSWIDTH_16) ? "x16" : "x8");
@@ -1538,17 +1538,17 @@ static int omap_nand_probe(struct platform_device *pdev)
 	/* populate read & write API based on xfer_type selected */
 	switch (pdata->xfer_type) {
 	case NAND_OMAP_PREFETCH_POLLED:
-		info->nand.read_buf   = omap_read_buf_pref;
-		info->nand.write_buf  = omap_write_buf_pref;
+		chip->read_buf   = omap_read_buf_pref;
+		chip->write_buf  = omap_write_buf_pref;
 		break;
 
 	case NAND_OMAP_POLLED:
-		if (info->nand.options & NAND_BUSWIDTH_16) {
-			info->nand.read_buf   = omap_read_buf16;
-			info->nand.write_buf  = omap_write_buf16;
+		if (chip->options & NAND_BUSWIDTH_16) {
+			chip->read_buf   = omap_read_buf16;
+			chip->write_buf  = omap_write_buf16;
 		} else {
-			info->nand.read_buf   = omap_read_buf8;
-			info->nand.write_buf  = omap_write_buf8;
+			chip->read_buf   = omap_read_buf8;
+			chip->write_buf  = omap_write_buf8;
 		}
 		break;
 
@@ -1577,8 +1577,8 @@ static int omap_nand_probe(struct platform_device *pdev)
 					err);
 				goto out_release_mem_region;
 			}
-			info->nand.read_buf   = omap_read_buf_dma_pref;
-			info->nand.write_buf  = omap_write_buf_dma_pref;
+			chip->read_buf   = omap_read_buf_dma_pref;
+			chip->write_buf  = omap_write_buf_dma_pref;
 		}
 		break;
 
@@ -1613,8 +1613,8 @@ static int omap_nand_probe(struct platform_device *pdev)
 			goto out_release_mem_region;
 		}
 
-		info->nand.read_buf  = omap_read_buf_irq_pref;
-		info->nand.write_buf = omap_write_buf_irq_pref;
+		chip->read_buf  = omap_read_buf_irq_pref;
+		chip->write_buf = omap_write_buf_irq_pref;
 
 		break;
 
@@ -1629,34 +1629,33 @@ static int omap_nand_probe(struct platform_device *pdev)
 	switch (pdata->ecc_opt) {
 	case OMAP_ECC_HAMMING_CODE_DEFAULT:
 		pr_info("using OMAP_ECC_HAMMING_CODE_DEFAULT ECC scheme\n");
-		info->nand.ecc.mode		= NAND_ECC_SOFT;
+		chip->ecc.mode		= NAND_ECC_SOFT;
 		goto generic_ecc_layout;
 
 	case OMAP_ECC_HAMMING_CODE_HW:
 		pr_info("using OMAP_ECC_HAMMING_CODE_HW ECC scheme\n");
-		info->nand.ecc.mode             = NAND_ECC_HW;
-		info->nand.ecc.bytes            = 3;
-		info->nand.ecc.size             = 512;
-		info->nand.ecc.strength         = 1;
-		info->nand.ecc.calculate        = omap_calculate_ecc;
-		info->nand.ecc.hwctl            = omap_enable_hwecc;
-		info->nand.ecc.correct          = omap_correct_data;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.bytes		= 3;
+		chip->ecc.size		= 512;
+		chip->ecc.strength	= 1;
+		chip->ecc.calculate	= omap_calculate_ecc;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_correct_data;
 		goto generic_ecc_layout;
 
 	case OMAP_ECC_HAMMING_CODE_HW_ROMCODE:
 		pr_info("using OMAP_ECC_HAMMING_CODE_HW_ROMCODE ECC scheme\n");
-		info->nand.ecc.mode             = NAND_ECC_HW;
-		info->nand.ecc.bytes            = 3;
-		info->nand.ecc.size             = 512;
-		info->nand.ecc.strength         = 1;
-		info->nand.ecc.calculate        = omap_calculate_ecc;
-		info->nand.ecc.hwctl            = omap_enable_hwecc;
-		info->nand.ecc.correct          = omap_correct_data;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.bytes		= 3;
+		chip->ecc.size		= 512;
+		chip->ecc.strength	= 1;
+		chip->ecc.calculate	= omap_calculate_ecc;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_correct_data;
 		/* define custom ECC layout */
-		omap_oobinfo.eccbytes		= info->nand.ecc.bytes *
-							(mtd->writesize /
-							info->nand.ecc.size);
-		if (info->nand.options & NAND_BUSWIDTH_16)
+		omap_oobinfo.eccbytes	= chip->ecc.bytes *
+					(mtd->writesize / chip->ecc.size);
+		if (chip->options & NAND_BUSWIDTH_16)
 			omap_oobinfo.eccpos[0]	= BADBLOCK_MARKER_LENGTH;
 		else
 			omap_oobinfo.eccpos[0]	= 1;
@@ -1667,19 +1666,17 @@ static int omap_nand_probe(struct platform_device *pdev)
 #ifdef CONFIG_MTD_NAND_ECC_BCH
 	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
 		pr_info("using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW ECC scheme");
-		info->nand.ecc.mode		= NAND_ECC_HW;
-		info->nand.ecc.size		= 512;
-		info->nand.ecc.bytes		= 7;
-		info->nand.ecc.strength		= 4;
-		info->nand.ecc.hwctl		= omap_enable_hwecc;
-		info->nand.ecc.correct		= nand_bch_correct_data;
-		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		chip->ecc.bytes		= 7;
+		chip->ecc.strength	= 4;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= nand_bch_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
-		info->nand.ecc.priv		= nand_bch_init(mtd,
-						info->nand.ecc.size,
-						info->nand.ecc.bytes,
-						&info->nand.ecc.layout);
-		if (!info->nand.ecc.priv) {
+		chip->ecc.priv		= nand_bch_init(mtd, chip->ecc.size,
+					   chip->ecc.bytes, &chip->ecc.layout);
+		if (!chip->ecc.priv) {
 			pr_err("unable initialize S/W BCH logic\n");
 			err = -EINVAL;
 			goto out_release_mem_region;
@@ -1689,16 +1686,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
 	case OMAP_ECC_BCH4_CODE_HW:
 		pr_info("using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
-		info->nand.ecc.mode		= NAND_ECC_HW;
-		info->nand.ecc.size		= 512;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
 		/* 8th bit is kept reserved for ROM-code compatibility */
-		info->nand.ecc.bytes		= 7 + 1;
-		info->nand.ecc.strength		= 4;
-		info->nand.ecc.hwctl		= omap_enable_hwecc;
-		info->nand.ecc.correct		= omap_elm_correct_data;
-		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
-		info->nand.ecc.read_page	= omap_read_page_bch;
-		info->nand.ecc.write_page	= omap_write_page_bch;
+		chip->ecc.bytes		= 7 + 1;
+		chip->ecc.strength	= 4;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_elm_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch;
+		chip->ecc.read_page	= omap_read_page_bch;
+		chip->ecc.write_page	= omap_write_page_bch;
 		/* ELM H/W engine is used for locating errors */
 		if (is_elm_present(info, BCH4_ECC) < 0) {
 			pr_err("ELM module not detected, required for ECC\n");
@@ -1706,30 +1703,27 @@ static int omap_nand_probe(struct platform_device *pdev)
 			goto out_release_mem_region;
 		}
 		/* define custom ECC layout */
-		omap_oobinfo.eccbytes		= info->nand.ecc.bytes *
-							(mtd->writesize /
-							info->nand.ecc.size);
-		omap_oobinfo.eccpos[0]		= BADBLOCK_MARKER_LENGTH;
-		omap_oobinfo.oobfree->offset	= omap_oobinfo.eccpos[0] +
-							omap_oobinfo.eccbytes;
+		omap_oobinfo.eccbytes	= chip->ecc.bytes *
+					(mtd->writesize / chip->ecc.size);
+		omap_oobinfo.eccpos[0]	= BADBLOCK_MARKER_LENGTH;
+		omap_oobinfo.oobfree->offset = omap_oobinfo.eccpos[0] +
+						omap_oobinfo.eccbytes;
 		goto custom_ecc_layout;
 #endif
 #ifdef CONFIG_MTD_NAND_ECC_BCH
 	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
 		pr_info("using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW ECC\n");
-		info->nand.ecc.mode		= NAND_ECC_HW;
-		info->nand.ecc.size		= 512;
-		info->nand.ecc.bytes		= 13;
-		info->nand.ecc.strength		= 8;
-		info->nand.ecc.hwctl		= omap_enable_hwecc;
-		info->nand.ecc.correct		= nand_bch_correct_data;
-		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
+		chip->ecc.bytes		= 13;
+		chip->ecc.strength	= 8;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= nand_bch_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch;
 		/* software bch library is used for locating errors */
-		info->nand.ecc.priv		= nand_bch_init(mtd,
-						info->nand.ecc.size,
-						info->nand.ecc.bytes,
-						&info->nand.ecc.layout);
-		if (!info->nand.ecc.priv) {
+		chip->ecc.priv		= nand_bch_init(mtd, chip->ecc.size,
+					   chip->ecc.bytes, &chip->ecc.layout);
+		if (!chip->ecc.priv) {
 			pr_err("unable initialize S/W BCH logic\n");
 			err = -EINVAL;
 			goto out_release_mem_region;
@@ -1739,16 +1733,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 #ifdef CONFIG_MTD_NAND_OMAP_BCH
 	case OMAP_ECC_BCH8_CODE_HW:
 		pr_info("using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
-		info->nand.ecc.mode		= NAND_ECC_HW;
-		info->nand.ecc.size		= 512;
+		chip->ecc.mode		= NAND_ECC_HW;
+		chip->ecc.size		= 512;
 		/* 14th bit is kept reserved for ROM-code compatibility */
-		info->nand.ecc.bytes		= 13 + 1;
-		info->nand.ecc.strength		= 8;
-		info->nand.ecc.hwctl		= omap_enable_hwecc;
-		info->nand.ecc.correct		= omap_elm_correct_data;
-		info->nand.ecc.calculate	= omap_calculate_ecc_bch;
-		info->nand.ecc.read_page	= omap_read_page_bch;
-		info->nand.ecc.write_page	= omap_write_page_bch;
+		chip->ecc.bytes		= 13 + 1;
+		chip->ecc.strength	= 8;
+		chip->ecc.hwctl		= omap_enable_hwecc;
+		chip->ecc.correct	= omap_elm_correct_data;
+		chip->ecc.calculate	= omap_calculate_ecc_bch;
+		chip->ecc.read_page	= omap_read_page_bch;
+		chip->ecc.write_page	= omap_write_page_bch;
 		/* ELM H/W engine is used for locating errors */
 		if (is_elm_present(info, BCH8_ECC) < 0) {
 			pr_err("ELM module not detected, required for ECC\n");
@@ -1756,16 +1750,16 @@ static int omap_nand_probe(struct platform_device *pdev)
 			goto out_release_mem_region;
 		}
 		/* define custom ECC layout */
-		omap_oobinfo.eccbytes		= info->nand.ecc.bytes *
-							(mtd->writesize /
-							info->nand.ecc.size);
-		omap_oobinfo.eccpos[0]		= BADBLOCK_MARKER_LENGTH;
-		omap_oobinfo.oobfree->offset	= omap_oobinfo.eccpos[0] +
-							omap_oobinfo.eccbytes;
+		omap_oobinfo.eccbytes	= chip->ecc.bytes *
+					   (mtd->writesize / chip->ecc.size);
+		omap_oobinfo.eccpos[0]	= BADBLOCK_MARKER_LENGTH;
+		omap_oobinfo.oobfree->offset = omap_oobinfo.eccpos[0] +
+						omap_oobinfo.eccbytes;
 		goto custom_ecc_layout;
 #endif
 	default:
-		pr_err("selected ECC scheme not supported or not enabled\n");
+		pr_err("%s: selected ECC scheme not supported or not enabled",
+				DRIVER_NAME);
 		err = -EINVAL;
 		goto out_release_mem_region;
 	}
@@ -1775,8 +1769,8 @@ custom_ecc_layout:
 	pr_info("%s: using custom ecc layout\n", DRIVER_NAME);
 	omap_oobinfo.oobfree->length = mtd->oobsize - BADBLOCK_MARKER_LENGTH
 						- omap_oobinfo.eccbytes;
-	if (!(info->nand.options & NAND_BUSWIDTH_16))
-		info->nand.badblock_pattern = &bb_descrip_flashbased;
+	if (!(chip->options & NAND_BUSWIDTH_16))
+		chip->badblock_pattern = &bb_descrip_flashbased;
 	for (i = 1; i < omap_oobinfo.eccbytes; i++)
 		omap_oobinfo.eccpos[i] = omap_oobinfo.eccpos[0] + i;
 
@@ -1788,20 +1782,20 @@ custom_ecc_layout:
 		err = -EINVAL;
 		goto out_release_mem_region;
 	}
-	info->nand.ecc.layout = &omap_oobinfo;
+	chip->ecc.layout = &omap_oobinfo;
 
 generic_ecc_layout:
 	/* second phase scan */
-	if (nand_scan_tail(&info->mtd)) {
+	if (nand_scan_tail(mtd)) {
 		err = -ENXIO;
 		goto out_release_mem_region;
 	}
 
 	ppdata.of_node = pdata->of_node;
-	mtd_device_parse_register(&info->mtd, NULL, &ppdata, pdata->parts,
+	mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts,
 				  pdata->nr_parts);
 
-	platform_set_drvdata(pdev, &info->mtd);
+	platform_set_drvdata(pdev, mtd);
 
 	return 0;
 
@@ -1816,9 +1810,9 @@ out_release_mem_region:
 
 out_free_info:
 #ifdef CONFIG_MTD_NAND_ECC_BCH
-	if (info->nand.ecc.priv) {
-		nand_bch_free(info->nand.ecc.priv);
-		info->nand.ecc.priv = NULL;
+	if (chip->ecc.priv) {
+		nand_bch_free(chip->ecc.priv);
+		chip->ecc.priv = NULL;
 	}
 #endif
 	kfree(info);
-- 
1.8.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 1/5] mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes
  2013-07-15 14:55   ` Pekon Gupta
@ 2013-07-16  6:46     ` avinash philip
  -1 siblings, 0 replies; 16+ messages in thread
From: avinash philip @ 2013-07-16  6:46 UTC (permalink / raw)
  To: Pekon Gupta
  Cc: dedekind1, dwmw2, arnd, tony, benoit.cousson, balbi, linux-mtd,
	linux-omap

Hi Pekon,

On Mon, Jul 15, 2013 at 8:25 PM, Pekon Gupta <pekon@ti.com> wrote:
> chip->ecc.correct() is used for detecting and correcting bit-flips during read
> operations. In omap2-nand driver this is done usingt following functions:
>
> - omap_correct_data(): for H/W based HAM1_ECC schemes
>         (Un-Touched in current patch)
>
> - omap_elm_correct_data(): for H/W based BCHx_ECC scheme
>         Current implementation of this function is not scalable for newer ECC
>         schemes because:
>         - It depends on a specific byte-position in OOB area (reserved as 0x00)
>           to differentiates between programmed-pages and erased-pages.
>           This reserved byte-position cannot be accomodated in all ECC schemes.
>         - Current code is not scalable for future ECC schemes due to tweaks for
>           BCH4_ECC and BCH8_ECC at multiple places.
>         - It checks for bit-flips in Erased-pages using check_erased_page().
>           This is over-work, as sanity of Erased-page can be verified by just
>           comparing them to a pre-defined ECC-syndrome for all_0xFF data.

This is correct if the erased page read back didn't have any bit flips.
If erased page result in bit flips, then this method will end up in
un-correctable
error. Can you please confirm you taken care erased page bit flips in this
modification?

I think below discussion would help.

https://lkml.org/lkml/2012/11/22/590

Thanks
Avinash

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v1 1/5] mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes
@ 2013-07-16  6:46     ` avinash philip
  0 siblings, 0 replies; 16+ messages in thread
From: avinash philip @ 2013-07-16  6:46 UTC (permalink / raw)
  To: Pekon Gupta
  Cc: arnd, dedekind1, tony, balbi, linux-mtd, benoit.cousson,
	linux-omap, dwmw2

Hi Pekon,

On Mon, Jul 15, 2013 at 8:25 PM, Pekon Gupta <pekon@ti.com> wrote:
> chip->ecc.correct() is used for detecting and correcting bit-flips during read
> operations. In omap2-nand driver this is done usingt following functions:
>
> - omap_correct_data(): for H/W based HAM1_ECC schemes
>         (Un-Touched in current patch)
>
> - omap_elm_correct_data(): for H/W based BCHx_ECC scheme
>         Current implementation of this function is not scalable for newer ECC
>         schemes because:
>         - It depends on a specific byte-position in OOB area (reserved as 0x00)
>           to differentiates between programmed-pages and erased-pages.
>           This reserved byte-position cannot be accomodated in all ECC schemes.
>         - Current code is not scalable for future ECC schemes due to tweaks for
>           BCH4_ECC and BCH8_ECC at multiple places.
>         - It checks for bit-flips in Erased-pages using check_erased_page().
>           This is over-work, as sanity of Erased-page can be verified by just
>           comparing them to a pre-defined ECC-syndrome for all_0xFF data.

This is correct if the erased page read back didn't have any bit flips.
If erased page result in bit flips, then this method will end up in
un-correctable
error. Can you please confirm you taken care erased page bit flips in this
modification?

I think below discussion would help.

https://lkml.org/lkml/2012/11/22/590

Thanks
Avinash

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PATCH v1 1/5] mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes
  2013-07-16  6:46     ` avinash philip
@ 2013-07-16  7:47       ` Gupta, Pekon
  -1 siblings, 0 replies; 16+ messages in thread
From: Gupta, Pekon @ 2013-07-16  7:47 UTC (permalink / raw)
  To: avinash philip
  Cc: dedekind1, dwmw2, arnd, tony, benoit.cousson, Balbi, Felipe,
	linux-mtd, linux-omap

> 
> Hi Pekon,
> 
> On Mon, Jul 15, 2013 at 8:25 PM, Pekon Gupta <pekon@ti.com> wrote:
> > chip->ecc.correct() is used for detecting and correcting bit-flips during read
> > operations. In omap2-nand driver this is done usingt following functions:
> >
> > - omap_correct_data(): for H/W based HAM1_ECC schemes
> >         (Un-Touched in current patch)
> >
> > - omap_elm_correct_data(): for H/W based BCHx_ECC scheme
> >         Current implementation of this function is not scalable for newer ECC
> >         schemes because:
> >         - It depends on a specific byte-position in OOB area (reserved as 0x00)
> >           to differentiates between programmed-pages and erased-pages.
> >           This reserved byte-position cannot be accomodated in all ECC
> schemes.
> >         - Current code is not scalable for future ECC schemes due to tweaks for
> >           BCH4_ECC and BCH8_ECC at multiple places.
> >         - It checks for bit-flips in Erased-pages using check_erased_page().
> >           This is over-work, as sanity of Erased-page can be verified by just
> >           comparing them to a pre-defined ECC-syndrome for all_0xFF data.
> 
> This is correct if the erased page read back didn't have any bit flips.
> If erased page result in bit flips, then this method will end up in
> un-correctable
> error. Can you please confirm you taken care erased page bit flips in this
> modification?
> 
> I think below discussion would help.
> 
> https://lkml.org/lkml/2012/11/22/590
> 

Yes, that is correct.. Please refer to latest discussion on this topic.
http://permalink.gmane.org/gmane.linux.drivers.mtd/46821

So, if a erased-page has any bit-flips, nand_read() would return it as
 EBADMSG (uncorrectable errors). I understand this is an over-cautious
 approach, but there are reasons for this:
(1) I din't wanted to burden the Read data-path with extra checks for 
finding and correcting bit-flips on erased-pages.

(2) Most File-Systems, if find un-correctable errors in erased-page 
would first torture_peb() by iteratively erasing and  checking for all(0xFF).
 So if the bit-flips were temporary they would go-away. And block would
 not be marked bad.

(3) Using erased-page with 'correctable bit-flips' for storing data makes
data on flash more vulnerable to early failures. Suppose using BCH8 
ECC scheme which has capability of correcting 8-bits per ecc.size. 
if my File-System uses an erased-page which already had 3-bit-flips 
before even it was written. So this leaves me with only 8-3 = 5 bit-flips
possibility before my data is rendered un-correctable and corrupt.
So, why not to use a erased-page which is not having any bit-flips at all ?
(And frequency of having bit-flips in erased-page would increase as
device ages)

I think from a user's point-of-view it would be better to get..
(a) more tolerance of bit-flips  + better read performance
v/s
(b) saving some blocks from getting re-erased.

Comments ?

with regards, pekon

> Thanks
> Avinash

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PATCH v1 1/5] mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes
@ 2013-07-16  7:47       ` Gupta, Pekon
  0 siblings, 0 replies; 16+ messages in thread
From: Gupta, Pekon @ 2013-07-16  7:47 UTC (permalink / raw)
  To: avinash philip
  Cc: arnd, dedekind1, tony, Balbi, Felipe, linux-mtd, benoit.cousson,
	linux-omap, dwmw2

> 
> Hi Pekon,
> 
> On Mon, Jul 15, 2013 at 8:25 PM, Pekon Gupta <pekon@ti.com> wrote:
> > chip->ecc.correct() is used for detecting and correcting bit-flips during read
> > operations. In omap2-nand driver this is done usingt following functions:
> >
> > - omap_correct_data(): for H/W based HAM1_ECC schemes
> >         (Un-Touched in current patch)
> >
> > - omap_elm_correct_data(): for H/W based BCHx_ECC scheme
> >         Current implementation of this function is not scalable for newer ECC
> >         schemes because:
> >         - It depends on a specific byte-position in OOB area (reserved as 0x00)
> >           to differentiates between programmed-pages and erased-pages.
> >           This reserved byte-position cannot be accomodated in all ECC
> schemes.
> >         - Current code is not scalable for future ECC schemes due to tweaks for
> >           BCH4_ECC and BCH8_ECC at multiple places.
> >         - It checks for bit-flips in Erased-pages using check_erased_page().
> >           This is over-work, as sanity of Erased-page can be verified by just
> >           comparing them to a pre-defined ECC-syndrome for all_0xFF data.
> 
> This is correct if the erased page read back didn't have any bit flips.
> If erased page result in bit flips, then this method will end up in
> un-correctable
> error. Can you please confirm you taken care erased page bit flips in this
> modification?
> 
> I think below discussion would help.
> 
> https://lkml.org/lkml/2012/11/22/590
> 

Yes, that is correct.. Please refer to latest discussion on this topic.
http://permalink.gmane.org/gmane.linux.drivers.mtd/46821

So, if a erased-page has any bit-flips, nand_read() would return it as
 EBADMSG (uncorrectable errors). I understand this is an over-cautious
 approach, but there are reasons for this:
(1) I din't wanted to burden the Read data-path with extra checks for 
finding and correcting bit-flips on erased-pages.

(2) Most File-Systems, if find un-correctable errors in erased-page 
would first torture_peb() by iteratively erasing and  checking for all(0xFF).
 So if the bit-flips were temporary they would go-away. And block would
 not be marked bad.

(3) Using erased-page with 'correctable bit-flips' for storing data makes
data on flash more vulnerable to early failures. Suppose using BCH8 
ECC scheme which has capability of correcting 8-bits per ecc.size. 
if my File-System uses an erased-page which already had 3-bit-flips 
before even it was written. So this leaves me with only 8-3 = 5 bit-flips
possibility before my data is rendered un-correctable and corrupt.
So, why not to use a erased-page which is not having any bit-flips at all ?
(And frequency of having bit-flips in erased-page would increase as
device ages)

I think from a user's point-of-view it would be better to get..
(a) more tolerance of bit-flips  + better read performance
v/s
(b) saving some blocks from getting re-erased.

Comments ?

with regards, pekon

> Thanks
> Avinash

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2013-07-16  7:47 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-07-15 14:55 [PATCH v1 0/5] optimize and clean-up of OMAP NAND and ELM driver Pekon Gupta
2013-07-15 14:55 ` Pekon Gupta
2013-07-15 14:55 ` [PATCH v1 1/5] mtd: nand: omap: optimized chip->ecc.correct() for H/W ECC schemes Pekon Gupta
2013-07-15 14:55   ` Pekon Gupta
2013-07-16  6:46   ` avinash philip
2013-07-16  6:46     ` avinash philip
2013-07-16  7:47     ` Gupta, Pekon
2013-07-16  7:47       ` Gupta, Pekon
2013-07-15 14:55 ` [PATCH v1 2/5] mtd: nand: omap: optimize chip->ecc.calculate() " Pekon Gupta
2013-07-15 14:55   ` Pekon Gupta
2013-07-15 14:55 ` [PATCH v1 3/5] mtd: nand: omap: optimize chip->ecc.hwctl() " Pekon Gupta
2013-07-15 14:55   ` Pekon Gupta
2013-07-15 14:55 ` [PATCH v1 4/5] mtd: devices: elm: add checks ELM H/W constrains, driver code cleanup Pekon Gupta
2013-07-15 14:55   ` Pekon Gupta
2013-07-15 14:55 ` [PATCH v1 5/5] mtd: nand: omap: code clean-up and white-space fixes Pekon Gupta
2013-07-15 14:55   ` Pekon Gupta

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.