Patchwork mtd: nand: support BENAND (Built-in ECC NAND)

login
register
mail settings
Submitter katsuki.uwatoko@toshiba.co.jp
Date Feb. 12, 2013, 12:25 a.m.
Message ID <F3B6523425E7914AA6214ED358D820AF25D9879D@TGXML316.toshiba.local>
Download mbox | patch
Permalink /patch/219707/
State New
Headers show

Comments

katsuki.uwatoko@toshiba.co.jp - Feb. 12, 2013, 12:25 a.m.
This enables support for BENAND, which is an SLC NAND flash solution
with embedded error correction code (ECC), currently supports only
128bytes OOB type.

In the read sequence, "status read command" is executed to check the
ECC status after read data. If bitflips occur, these are
automatically corrected by BENAND and the status indicates the result.

The write sequence is the same as raw write and the ECC data are
automatically written to OOB by BENAND.

Signed-off-by: UWATOKO Katsuki <katsuki.uwatoko@toshiba.co.jp>
---
 drivers/mtd/nand/Kconfig        |   11 ++++
 drivers/mtd/nand/Makefile       |    1 +
 drivers/mtd/nand/nand_base.c    |   25 +++++++++
 drivers/mtd/nand/nand_benand.c  |  113 +++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/nand.h        |    2 +
 include/linux/mtd/nand_benand.h |   47 ++++++++++++++++
 6 files changed, 199 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/nand_benand.c
 create mode 100644 include/linux/mtd/nand_benand.h
Matthieu CASTET - Feb. 12, 2013, 9:36 a.m.
katsuki.uwatoko@toshiba.co.jp a écrit :
> This enables support for BENAND, which is an SLC NAND flash solution
> with embedded error correction code (ECC), currently supports only
> 128bytes OOB type.
> 
> In the read sequence, "status read command" is executed to check the
> ECC status after read data. If bitflips occur, these are
> automatically corrected by BENAND and the status indicates the result.
> 
> The write sequence is the same as raw write and the ECC data are
> automatically written to OOB by BENAND.
> 
> Signed-off-by: UWATOKO Katsuki <katsuki.uwatoko@toshiba.co.jp>

Are you planning to add support to other Toshiba BENAND ?


Matthieu
katsuki.uwatoko@toshiba.co.jp - Feb. 13, 2013, 6:25 a.m.
> Are you planning to add support to other Toshiba BENAND ?

Unfortunately, I am not currently, because I do not have 
any boards of the other BENANDs.

But I suppose that it is easy to support the other BENANDs 
by little modification of nand_read_page_benand() to get 
a number of bitflips.
Vikram Narayanan - Feb. 13, 2013, 5:14 p.m.
On 2/12/2013 5:55 AM, katsuki.uwatoko@toshiba.co.jp wrote:
> This enables support for BENAND, which is an SLC NAND flash solution
> with embedded error correction code (ECC), currently supports only
> 128bytes OOB type.
>
> In the read sequence, "status read command" is executed to check the
> ECC status after read data. If bitflips occur, these are
> automatically corrected by BENAND and the status indicates the result.
>
> The write sequence is the same as raw write and the ECC data are
> automatically written to OOB by BENAND.

Couple of comments. Not related to the functionality though :)

> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 1a03b7f..2b0c178 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -43,6 +43,7 @@
>   #include <linux/mtd/nand.h>
>   #include <linux/mtd/nand_ecc.h>
>   #include <linux/mtd/nand_bch.h>
> +#include <linux/mtd/nand_benand.h>
>   #include <linux/interrupt.h>
>   #include <linux/bitops.h>
>   #include <linux/leds.h>
> @@ -3076,6 +3077,11 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
>   		extid >>= 2;
>   		/* Get buswidth information */
>   		*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
> +
> +		/* check BENAND (embedded ECC) */
> +		if (id_data[0] == NAND_MFR_TOSHIBA &&
> +		    (id_data[4] & 1 << 7))

Can the (1 << 7) be made as some macro, with an understandable name?

> +			nand_benand_setupecc(mtd, &(chip->ecc.mode));
>   	}
>   }
>
> @@ -3530,6 +3536,25 @@ int nand_scan_tail(struct mtd_info *mtd)
>   			chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);
>   		break;
>
> +	case NAND_ECC_BENAND:
> +		if (!mtd_nand_has_benand()) {

Can IS_ENABLED be used here instead?

> +			pr_warn("CONFIG_MTD_BENAND not enabled\n");
> +			BUG();

Should you really BUG here?

> +		}
> +
> +		chip->ecc.read_page = nand_read_page_benand;
> +		chip->ecc.write_page = nand_write_page_raw;
> +		chip->ecc.read_page_raw = nand_read_page_raw;
> +		chip->ecc.write_page_raw = nand_write_page_raw;
> +		chip->ecc.read_oob = nand_read_oob_std;
> +		chip->ecc.write_oob = nand_write_oob_std;
> +
> +		if (nand_benand_init(mtd)) {
> +			pr_warn("BENAND initialization failed!\n");
> +			BUG();
> +		}
> +		break;
> +
>   	case NAND_ECC_NONE:
>   		pr_warn("NAND_ECC_NONE selected by board driver. "
>   			   "This is not recommended!\n");
<snip>
> diff --git a/include/linux/mtd/nand_benand.h b/include/linux/mtd/nand_benand.h
> new file mode 100644
> index 0000000..ac043e5
> --- /dev/null
> +++ b/include/linux/mtd/nand_benand.h
> @@ -0,0 +1,47 @@
> +/*
> + * (C) Copyright TOSHIBA CORPORATION 2013
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This file is the header for the NAND BENAND implementation.
> + */
> +
> +#ifndef __MTD_NAND_BENAND_H__
> +#define __MTD_NAND_BENAND_H__
> +
> +#if defined(CONFIG_MTD_NAND_BENAND)
> +
> +static inline int mtd_nand_has_benand(void) { return 1; }

If IS_ENABLED is used, the above would go off.

~Vikram
katsuki.uwatoko@toshiba.co.jp - Feb. 14, 2013, 8:06 a.m.
Vikram, 

Thank you for comments.

> > +
> > +		/* check BENAND (embedded ECC) */
> > +		if (id_data[0] == NAND_MFR_TOSHIBA &&
> > +		    (id_data[4] & 1 << 7))
> 
> Can the (1 << 7) be made as some macro, with an understandable name?

I will fix this.

> > +			pr_warn("CONFIG_MTD_BENAND not enabled\n");
> > +			BUG();
> 
> Should you really BUG here?

I think so, because this does not happen. 
If CONFIG_MTD_NAND_BENAND is not enabled, nobody sets NAND_ECC_BENAND.  

> If IS_ENABLED is used, the above would go off.

I will rewrite this driver by using "IS_ENABLED" and resend it.

Thanks again,

Patch

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 4883139..acf0c25 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -37,6 +37,17 @@  config MTD_NAND_ECC_BCH
 	  ECC codes. They are used with NAND devices requiring more than 1 bit
 	  of error correction.
 
+config MTD_NAND_BENAND
+	bool "Support BENAND (embedded ECC NAND)"
+	default n
+	help
+	  This enables support for BENAND, which is an SLC NAND flash
+	  solution with embedded error correction code (ECC).
+	  currently supports only 128bytes OOB type.
+	  In the read sequence, "status read command" is executed to check
+	  the ECC status after read data.
+	  The write sequence is the same as raw write.
+
 config MTD_SM_COMMON
 	tristate
 	default n
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 2cbd091..f58dee8 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -5,6 +5,7 @@ 
 obj-$(CONFIG_MTD_NAND)			+= nand.o
 obj-$(CONFIG_MTD_NAND_ECC)		+= nand_ecc.o
 obj-$(CONFIG_MTD_NAND_BCH)		+= nand_bch.o
+obj-$(CONFIG_MTD_NAND_BENAND)		+= nand_benand.o
 obj-$(CONFIG_MTD_NAND_IDS)		+= nand_ids.o
 obj-$(CONFIG_MTD_SM_COMMON) 		+= sm_common.o
 
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 1a03b7f..2b0c178 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -43,6 +43,7 @@ 
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand_ecc.h>
 #include <linux/mtd/nand_bch.h>
+#include <linux/mtd/nand_benand.h>
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/leds.h>
@@ -3076,6 +3077,11 @@  static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
 		extid >>= 2;
 		/* Get buswidth information */
 		*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+
+		/* check BENAND (embedded ECC) */
+		if (id_data[0] == NAND_MFR_TOSHIBA &&
+		    (id_data[4] & 1 << 7))
+			nand_benand_setupecc(mtd, &(chip->ecc.mode));
 	}
 }
 
@@ -3530,6 +3536,25 @@  int nand_scan_tail(struct mtd_info *mtd)
 			chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);
 		break;
 
+	case NAND_ECC_BENAND:
+		if (!mtd_nand_has_benand()) {
+			pr_warn("CONFIG_MTD_BENAND not enabled\n");
+			BUG();
+		}
+
+		chip->ecc.read_page = nand_read_page_benand;
+		chip->ecc.write_page = nand_write_page_raw;
+		chip->ecc.read_page_raw = nand_read_page_raw;
+		chip->ecc.write_page_raw = nand_write_page_raw;
+		chip->ecc.read_oob = nand_read_oob_std;
+		chip->ecc.write_oob = nand_write_oob_std;
+
+		if (nand_benand_init(mtd)) {
+			pr_warn("BENAND initialization failed!\n");
+			BUG();
+		}
+		break;
+
 	case NAND_ECC_NONE:
 		pr_warn("NAND_ECC_NONE selected by board driver. "
 			   "This is not recommended!\n");
diff --git a/drivers/mtd/nand/nand_benand.c b/drivers/mtd/nand/nand_benand.c
new file mode 100644
index 0000000..fe8008d
--- /dev/null
+++ b/drivers/mtd/nand/nand_benand.c
@@ -0,0 +1,113 @@ 
+/*
+ * This file provides functions for BENAND, which is an SLC NAND flash
+ * solution with embedded error correction code (ECC).
+ *
+ * (C) Copyright TOSHIBA CORPORATION 2013
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_benand.h>
+
+static struct nand_ecclayout benand_oob_128 = {
+	.oobfree = {
+		{.offset = 2, .length = 3},
+		{.offset = 18, .length = 3},
+		{.offset = 34, .length = 3},
+		{.offset = 50, .length = 3},
+		{.offset = 66, .length = 3},
+		{.offset = 82, .length = 3},
+		{.offset = 98, .length = 3},
+		{.offset = 114, .length = 3},
+	}
+};
+
+/**
+ * nand_read_page_raw - [Intern] read raw page data with benand.
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
+ */
+
+int nand_read_page_benand(struct mtd_info *mtd, struct nand_chip *chip,
+			  uint8_t *buf, int oob_required, int page)
+{
+	u8 status;
+	unsigned int max_bitflips = 0;
+
+	chip->ecc.read_page_raw(mtd, chip, buf, oob_required, page);
+
+	/* Check Read Status */
+
+	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+	status = chip->read_byte(mtd);
+
+	if (status & NAND_STATUS_FAIL)
+		/* uncorrectable error */
+		mtd->ecc_stats.failed++;
+	else if (status & NAND_CHIP_READ_STATUS) {
+		/* correctable error
+		   How many bits have been corrected is unknown.
+		   therefore bitflips is set to ecc.strength. */
+		mtd->ecc_stats.corrected += chip->ecc.strength;
+		max_bitflips = chip->ecc.strength;
+	}
+
+	return max_bitflips;
+}
+EXPORT_SYMBOL(nand_read_page_benand);
+
+void nand_benand_setupecc(struct mtd_info *mtd, nand_ecc_modes_t *mode)
+{
+	/* support only 128bytes OOB type */
+	if (mtd->oobsize == 128)
+		*mode = NAND_ECC_BENAND;
+}
+
+int nand_benand_init(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	chip->ecc.size = mtd->writesize;
+
+	if (mtd->oobsize == 128) {
+		chip->ecc.layout = &benand_oob_128;
+		chip->ecc.bytes = 72;      /* 9B * (4096B/512B) */
+		chip->ecc.strength = 32;   /* 4b * (4096B/512B) */
+	} else {
+		pr_warn("%d unsupported oobsize in benand_init\n",
+			mtd->oobsize);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(nand_benand_init);
+
+MODULE_AUTHOR("TOSHIBA Corporation");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("BENAND (embedded ECC NAND) support");
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 24e9159..2163ae9 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -129,6 +129,7 @@  extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 /* Status bits */
 #define NAND_STATUS_FAIL	0x01
 #define NAND_STATUS_FAIL_N1	0x02
+#define NAND_CHIP_READ_STATUS   0x08  /* for BENAND */
 #define NAND_STATUS_TRUE_READY	0x20
 #define NAND_STATUS_READY	0x40
 #define NAND_STATUS_WP		0x80
@@ -143,6 +144,7 @@  typedef enum {
 	NAND_ECC_HW_SYNDROME,
 	NAND_ECC_HW_OOB_FIRST,
 	NAND_ECC_SOFT_BCH,
+	NAND_ECC_BENAND,
 } nand_ecc_modes_t;
 
 /*
diff --git a/include/linux/mtd/nand_benand.h b/include/linux/mtd/nand_benand.h
new file mode 100644
index 0000000..ac043e5
--- /dev/null
+++ b/include/linux/mtd/nand_benand.h
@@ -0,0 +1,47 @@ 
+/*
+ * (C) Copyright TOSHIBA CORPORATION 2013
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file is the header for the NAND BENAND implementation.
+ */
+
+#ifndef __MTD_NAND_BENAND_H__
+#define __MTD_NAND_BENAND_H__
+
+#if defined(CONFIG_MTD_NAND_BENAND)
+
+static inline int mtd_nand_has_benand(void) { return 1; }
+
+void nand_benand_setupecc(struct mtd_info *mtd, nand_ecc_modes_t *mode);
+
+int nand_read_page_benand(struct mtd_info *mtd, struct nand_chip *chip,
+			  uint8_t *buf, int oob_required, int page);
+
+int nand_benand_init(struct mtd_info *mtd);
+
+#else /* !CONFIG_MTD_NAND_BENAND */
+
+static inline int mtd_nand_has_benand(void) { return 0; }
+
+static inline void nand_benand_setupecc(struct mtd_info *mtd,
+					nand_ecc_modes_t *mode) {}
+
+static inline int nand_read_page_benand(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					uint8_t *buf, int oob_required,
+					int page)
+{
+	return -1;
+}
+
+static inline int nand_benand_init(struct mtd_info *mtd)
+{
+	return -1;
+}
+
+#endif /* CONFIG_MTD_NAND_BENAND */
+#endif /* __MTD_NAND_BENAND_H__ */