diff mbox

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

Message ID 1445543450-26077-1-git-send-email-yoshitake.kobayashi@toshiba.co.jp (mailing list archive)
State New, archived
Headers show

Commit Message

KOBAYASHI Yoshitake Oct. 22, 2015, 7:50 p.m. UTC
Please consider adding the Toshiba BENAND patch for LTSI-4.1,
This patch was posted as below, but it hasn't merged yet.
  https://lkml.org/lkml/2015/7/23/859

This patch enables support for Toshiba BENAND. BENAND is a SLC NAND
solution that automatically generates ECC inside NAND chip.

Signed-off-by: KOBAYASHI Yoshitake <yoshitake.kobayashi@toshiba.co.jp>
---
 drivers/mtd/nand/Kconfig        |   21 +++++
 drivers/mtd/nand/Makefile       |    1 +
 drivers/mtd/nand/nand_base.c    |   30 +++++++-
 drivers/mtd/nand/nand_benand.c  |  154 +++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/nand.h        |    1 +
 include/linux/mtd/nand_benand.h |   48 ++++++++++++
 6 files changed, 253 insertions(+), 2 deletions(-)
 create mode 100644 drivers/mtd/nand/nand_benand.c
 create mode 100644 include/linux/mtd/nand_benand.h

Comments

Greg KH Nov. 28, 2015, 1:47 a.m. UTC | #1
On Fri, Oct 23, 2015 at 04:50:50AM +0900, KOBAYASHI Yoshitake wrote:
> Please consider adding the Toshiba BENAND patch for LTSI-4.1,
> This patch was posted as below, but it hasn't merged yet.
>   https://lkml.org/lkml/2015/7/23/859
> 
> This patch enables support for Toshiba BENAND. BENAND is a SLC NAND
> solution that automatically generates ECC inside NAND chip.
> 
> Signed-off-by: KOBAYASHI Yoshitake <yoshitake.kobayashi@toshiba.co.jp>

Applied, thanks.

greg k-h
diff mbox

Patch

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 5897d8d..443a31a 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -22,6 +22,27 @@  menuconfig MTD_NAND
 
 if MTD_NAND
 
+config MTD_NAND_BENAND
+	tristate
+	depends on MTD_NAND_BENAND_ENABLE
+	default MTD_NAND
+
+config MTD_NAND_BENAND_ENABLE
+	bool "Support for Toshiba BENAND (Built-in ECC NAND)"
+	default y
+	help
+	  This enables support for Toshiba BENAND.
+	  Toshiba BENAND is a SLC NAND solution that automatically
+	  generates ECC inside NAND chip.
+
+config MTD_NAND_BENAND_ECC_STATUS
+	bool "Enable ECC Status Read Command(0x7A)"
+	depends on MTD_NAND_BENAND_ENABLE
+	help
+	  This enables support for ECC Status Read Command(0x7A) of BENAND.
+	  When this enables, report the real number of bitflips.
+	  In other cases, report the assumud number.
+
 config MTD_NAND_BCH
 	tristate
 	select BCH
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 582bbd05..7f58bc1 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -3,6 +3,7 @@ 
 #
 
 obj-$(CONFIG_MTD_NAND)			+= nand.o
+obj-$(CONFIG_MTD_NAND_BENAND)		+= nand_benand.o
 obj-$(CONFIG_MTD_NAND_ECC)		+= nand_ecc.o
 obj-$(CONFIG_MTD_NAND_BCH)		+= nand_bch.o
 obj-$(CONFIG_MTD_NAND_IDS)		+= nand_ids.o
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index c2e1232..2abf2ea 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>
@@ -3526,8 +3527,13 @@  static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
 		if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA &&
 				nand_is_slc(chip) &&
 				(id_data[5] & 0x7) == 0x6 /* 24nm */ &&
-				!(id_data[4] & 0x80) /* !BENAND */) {
-			mtd->oobsize = 32 * mtd->writesize >> 9;
+				(id_data[4] & 0x80) /* BENAND */) {
+
+			if (IS_ENABLED(CONFIG_MTD_NAND_BENAND))
+				chip->ecc.mode = NAND_ECC_BENAND;
+
+		} else {
+			mtd->oobsize = 32 * mtd->writesize >> 9; /* !BENAND */
 		}
 
 	}
@@ -4075,6 +4081,26 @@  int nand_scan_tail(struct mtd_info *mtd)
 		}
 		break;
 
+	case NAND_ECC_BENAND:
+		if (!mtd_nand_has_benand()) {
+			pr_warn("CONFIG_MTD_NAND_BENAND not enabled\n");
+			BUG();
+		}
+		ecc->calculate = NULL;
+		ecc->correct = NULL;
+		ecc->read_page = nand_read_page_benand;
+		ecc->read_subpage = nand_read_subpage_benand;
+		ecc->write_page = nand_write_page_raw;
+		ecc->read_page_raw = nand_read_page_raw;
+		ecc->write_page_raw = nand_write_page_raw;
+		ecc->read_oob = nand_read_oob_std;
+		ecc->write_oob = nand_write_oob_std;
+		ecc->size = mtd->writesize;
+		ecc->strength = 8;
+
+		nand_benand_init(mtd);
+		break;
+
 	case NAND_ECC_NONE:
 		pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
 		ecc->read_page = nand_read_page_raw;
diff --git a/drivers/mtd/nand/nand_benand.c b/drivers/mtd/nand/nand_benand.c
new file mode 100644
index 0000000..43f89ae
--- /dev/null
+++ b/drivers/mtd/nand/nand_benand.c
@@ -0,0 +1,154 @@ 
+/*
+ *  drivers/mtd/nand_benand.c
+ *
+ *  (C) Copyright Toshiba Corporation
+ *                Semiconductor and Storage Products Company 2015
+ *
+ *  This program supports BENAND.
+ *
+ *  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.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_benand.h>
+
+/* Recommended to rewrite for BENAND */
+#define NAND_STATUS_RECOM_REWRT	0x08
+
+/* ECC Status Read Command */
+#define NAND_CMD_ECC_STATUS	0x7A
+
+static struct nand_ecclayout benand_oob_64 = {
+	.eccbytes = 0,
+	.eccpos = {},
+	.oobfree = {
+		{.offset = 2, .length = 62}
+	}
+};
+
+static struct nand_ecclayout benand_oob_128 = {
+	.eccbytes = 0,
+	.eccpos = {},
+	.oobfree = {
+		{.offset = 2, .length = 126}
+	}
+};
+
+
+static int nand_benand_status_chk(struct mtd_info *mtd, struct nand_chip *chip)
+{
+	unsigned int max_bitflips = 0;
+	u8 status;
+
+	/* Check Read Status */
+	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+	status = chip->read_byte(mtd);
+
+	/* timeout */
+	if (!(status & NAND_STATUS_READY)) {
+		pr_debug("BENAND : Time Out!\n");
+		return -EIO;
+	}
+
+	/* uncorrectable */
+	else if (status & NAND_STATUS_FAIL)
+		mtd->ecc_stats.failed++;
+
+	/* correctable */
+	else if (status & NAND_STATUS_RECOM_REWRT) {
+		if (chip->cmd_ctrl &&
+			IS_ENABLED(CONFIG_MTD_NAND_BENAND_ECC_STATUS)) {
+
+			int i;
+			u8 ecc_status;
+			unsigned int bitflips;
+
+			/* Check Read ECC Status */
+			chip->cmd_ctrl(mtd, NAND_CMD_ECC_STATUS,
+				NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+			/* Get bitflips info per 512Byte */
+			for (i = 0; i < mtd->writesize >> 9; i++) {
+				ecc_status = chip->read_byte(mtd);
+				bitflips = ecc_status & 0x0f;
+				max_bitflips = max_t(unsigned int,
+						max_bitflips, bitflips);
+			}
+			mtd->ecc_stats.corrected += max_bitflips;
+		} else {
+			/*
+			 * If can't use chip->cmd_ctrl,
+			 * we can't get real number of bitflips.
+			 * So, we set max_bitflips mtd->bitflip_threshold.
+			 */
+			max_bitflips = mtd->bitflip_threshold;
+			mtd->ecc_stats.corrected += max_bitflips;
+		}
+	}
+
+	return max_bitflips;
+}
+
+
+void nand_benand_init(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	chip->options |= NAND_SUBPAGE_READ;
+
+	switch (mtd->oobsize) {
+	case 64:
+		chip->ecc.layout = &benand_oob_64;
+		break;
+	case 128:
+		chip->ecc.layout = &benand_oob_128;
+		break;
+	default:
+		pr_warn("No oob scheme defined for oobsize %d\n",
+				mtd->oobsize);
+		BUG();
+	}
+
+}
+EXPORT_SYMBOL(nand_benand_init);
+
+int nand_read_page_benand(struct mtd_info *mtd, struct nand_chip *chip,
+			uint8_t *buf, int oob_required, int page)
+{
+	unsigned int max_bitflips = 0;
+
+	chip->ecc.read_page_raw(mtd, chip, buf, oob_required, page);
+	max_bitflips = nand_benand_status_chk(mtd, chip);
+
+	return max_bitflips;
+}
+EXPORT_SYMBOL(nand_read_page_benand);
+
+
+int nand_read_subpage_benand(struct mtd_info *mtd, struct nand_chip *chip,
+			uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+			int page)
+{
+	uint8_t *p;
+	unsigned int max_bitflips = 0;
+
+	if (data_offs != 0)
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_offs, -1);
+
+	p = bufpoi + data_offs;
+	chip->read_buf(mtd, p, readlen);
+
+	max_bitflips = nand_benand_status_chk(mtd, chip);
+
+	return max_bitflips;
+}
+EXPORT_SYMBOL(nand_read_subpage_benand);
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Toshiba Corporation Semiconductor and Storage Products Company");
+MODULE_DESCRIPTION("BENAND (Built-in ECC NAND) support");
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 12b75f3..12678c6 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -115,6 +115,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..0aa5aec
--- /dev/null
+++ b/include/linux/mtd/nand_benand.h
@@ -0,0 +1,48 @@ 
+/*
+ *  linux/include/linux/mtd/nand_benand.h
+ *
+ *  (C) Copyright Toshiba Corporation
+ *                Semiconductor and Storage Products Company 2015
+ *
+ *  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.
+ *
+ */
+
+#ifndef __MTD_NAND_BENAND_H__
+#define __MTD_NAND_BENAND_H__
+
+#if defined(CONFIG_MTD_NAND_BENAND_ENABLE)
+
+static inline int mtd_nand_has_benand(void) { return 1; }
+
+void nand_benand_init(struct mtd_info *mtd);
+
+int nand_read_page_benand(struct mtd_info *mtd, struct nand_chip *chip,
+			uint8_t *buf, int oob_required, int page);
+
+int nand_read_subpage_benand(struct mtd_info *mtd, struct nand_chip *chip,
+			uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+			int page);
+
+#else  /* CONFIG_MTD_NAND_BENAND_ENABLE */
+static inline int mtd_nand_has_benand(void) { return 0; }
+
+void nand_benand_init(struct mtd_info *mtd) {}
+
+int nand_read_page_benand(struct mtd_info *mtd, struct nand_chip *chip,
+			uint8_t *buf, int oob_required, int page)
+{
+	return -1;
+}
+
+int nand_read_subpage_benand(struct mtd_info *mtd, struct nand_chip *chip,
+			uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+			int page)
+{
+	return -1;
+}
+
+#endif /* CONFIG_MTD_NAND_BENAND_ENABLE */
+#endif