[OpenWrt-Devel] [PATCH 1/3] ipq40xx: Add gigadevice nandspi flash driver

Daniel Danzberger daniel at dd-wrt.com
Wed Oct 30 07:27:24 EDT 2019


This patch adds support for Gigadevice SPI NAND device to the mt29f stagging driver.

Signed-off-by: Daniel Danzberger <daniel at dd-wrt.com>
---
 ...port-gigadevice-nandspi-flash-device.patch | 1778 +++++++++++++++++
 1 file changed, 1778 insertions(+)
 create mode 100644 target/linux/ipq40xx/patches-4.19/400-mtd-nand-support-gigadevice-nandspi-flash-device.patch

diff --git a/target/linux/ipq40xx/patches-4.19/400-mtd-nand-support-gigadevice-nandspi-flash-device.patch b/target/linux/ipq40xx/patches-4.19/400-mtd-nand-support-gigadevice-nandspi-flash-device.patch
new file mode 100644
index 0000000000..a3b98cd275
--- /dev/null
+++ b/target/linux/ipq40xx/patches-4.19/400-mtd-nand-support-gigadevice-nandspi-flash-device.patch
@@ -0,0 +1,1778 @@
+diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c
+index 5423c3bb..31aac787 100644
+--- a/drivers/mtd/nand/raw/nand_ids.c
++++ b/drivers/mtd/nand/raw/nand_ids.c
+@@ -185,6 +185,7 @@ static const struct nand_manufacturer nand_manufacturers[] = {
+ 	{NAND_MFR_INTEL, "Intel"},
+ 	{NAND_MFR_ATO, "ATO"},
+ 	{NAND_MFR_WINBOND, "Winbond"},
++	{NAND_MFR_GIGA, "Gigadevice"},
+ };
+
+ /**
+diff --git a/drivers/staging/mt29f_spinand/Kconfig b/drivers/staging/mt29f_spinand/Kconfig
+index f3f9cb3b..139c058c 100644
+--- a/drivers/staging/mt29f_spinand/Kconfig
++++ b/drivers/staging/mt29f_spinand/Kconfig
+@@ -14,3 +14,13 @@ config MTD_SPINAND_ONDIEECC
+ 	help
+ 	  Internal ECC.
+ 	  Enables Hardware ECC support for Micron SPI NAND.
++
++config MTD_SPINAND_GIGADEVICE
++	tristate "SPINAND Devcie Support for Gigadevice "
++	depends on MTD_SPINAND_MT29F
++	help
++	   This enables support for accessing Gigadevice SPI NAND flash
++	   devices.
++	   If you have Gigadevice SPI NAND chip say yes.
++
++	   If unsure, say no here.
+diff --git a/drivers/staging/mt29f_spinand/Makefile b/drivers/staging/mt29f_spinand/Makefile
+index e47af0f7..36df11e6 100644
+--- a/drivers/staging/mt29f_spinand/Makefile
++++ b/drivers/staging/mt29f_spinand/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand.o
++obj-$(CONFIG_MTD_SPINAND_GIGADEVICE)  += giga_spinand.o
+diff --git a/drivers/staging/mt29f_spinand/giga_spinand.c b/drivers/staging/mt29f_spinand/giga_spinand.c
+new file mode 100644
+index 00000000..a619e96d
+--- /dev/null
++++ b/drivers/staging/mt29f_spinand/giga_spinand.c
+@@ -0,0 +1,396 @@
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/mtd/rawnand.h>
++#include <linux/spi/spi.h>
++#include "giga_spinand.h"
++
++/* Only ecc un-protected fields in the spare area included */
++static int winbond_ooblayout_64_ecc(struct mtd_info *mtd, int section,
++                struct mtd_oob_region *oobregion) {
++  if (section > 3)
++    return -ERANGE;
++
++  oobregion->offset = (section * 16) + 8;
++  oobregion->length = 9;
++  return 0;
++}
++
++static int winbond_ooblayout_64_free(struct mtd_info *mtd, int section,
++                 struct mtd_oob_region *oobregion) {
++  if (section > 3)
++    return -ERANGE;
++
++  oobregion->offset = (section * 16) + 2;
++  oobregion->length = 2;
++  return 0;
++}
++
++static const struct mtd_ooblayout_ops winbond_ooblayout = {
++  .ecc = winbond_ooblayout_64_ecc,
++  .free = winbond_ooblayout_64_free,
++};
++
++static int ath79_ooblayout_64_ecc(struct mtd_info *mtd, int section,
++                struct mtd_oob_region *oobregion) {
++  if (section > 7)
++    return -ERANGE;
++  switch(section) {
++    case 0:
++      oobregion->offset = 64;
++      oobregion->length = 8;
++      break;
++    case 1:
++      oobregion->offset = 72;
++      oobregion->length = 8;
++      break;
++    case 2:
++      oobregion->offset = 80;
++      oobregion->length = 8;
++      break;
++    case 3:
++      oobregion->offset = 88;
++      oobregion->length = 8;
++      break;
++    case 4:
++      oobregion->offset = 96;
++      oobregion->length = 8;
++      break;
++    case 5:
++      oobregion->offset = 104;
++      oobregion->length = 8;
++      break;
++    case 6:
++      oobregion->offset = 112;
++      oobregion->length = 8;
++      break;
++    case 7:
++      oobregion->offset = 120;
++      oobregion->length = 8;
++      break;
++  }
++  return 0;
++}
++
++static int ath79_ooblayout_64_free(struct mtd_info *mtd, int section,
++                 struct mtd_oob_region *oobregion) {
++  if (section > 2)
++    return -ERANGE;
++
++  oobregion->offset = (section * 16);
++  oobregion->length = 3;
++  return 0;
++}
++
++static const struct mtd_ooblayout_ops ath79_ooblayout = {
++  .ecc = ath79_ooblayout_64_ecc,
++  .free = ath79_ooblayout_64_free,
++};
++
++
++/* Only ecc un-protected fields in the spare area included */
++/* ECC parity code stored in the additional hidden spare area */
++static int macronix_ooblayout_64_ecc(struct mtd_info *mtd, int section,
++                struct mtd_oob_region *oobregion) {
++  return -ERANGE;
++}
++
++static int macronix_ooblayout_64_free(struct mtd_info *mtd, int section,
++                 struct mtd_oob_region *oobregion) {
++  if (section > 3)
++    return -ERANGE;
++
++  oobregion->offset = (section * 16) + 2;
++  oobregion->length = 2;
++  return 0;
++}
++
++static const struct mtd_ooblayout_ops macronix_ooblayout = {
++  .ecc = macronix_ooblayout_64_ecc,
++  .free = macronix_ooblayout_64_free,
++};
++
++void gigadevice_set_defaults_128mb(struct spi_device *spi_nand)
++{
++	struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata
++						(&spi_nand->dev);
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++
++	chip->ecc.size	= 0x800;
++	chip->ecc.bytes	= 0x0;
++	chip->ecc.steps	= 0x0;
++
++	chip->ecc.strength = 1;
++	chip->ecc.total	= 0;
++  mtd_set_ooblayout(mtd, &ath79_ooblayout);
++}
++
++void gigadevice_set_defaults(struct spi_device *spi_nand)
++{
++	struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata
++						(&spi_nand->dev);
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++
++	chip->ecc.size	= 0x800;
++	chip->ecc.bytes	= 0x0;
++	chip->ecc.steps	= 0x0;
++
++	chip->ecc.strength = 1;
++	chip->ecc.total	= 0;
++}
++
++void gigadevice_set_defaults_512mb(struct spi_device *spi_nand)
++{
++	struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata
++						(&spi_nand->dev);
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++
++	chip->ecc.size	= 0x1000;
++	chip->ecc.bytes	= 0x0;
++	chip->ecc.steps	= 0x0;
++
++	chip->ecc.strength = 1;
++	chip->ecc.total	= 0;
++}
++
++void winbond_set_defaults(struct spi_device *spi_nand)
++{
++	struct mtd_info *mtd = dev_get_drvdata(&spi_nand->dev);
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++
++	chip->ecc.size	= 0x800;
++	chip->ecc.bytes	= 0x0;
++	chip->ecc.steps	= 0x0;
++
++	chip->ecc.strength = 1;
++	chip->ecc.total	= 0;
++  mtd_set_ooblayout(mtd, &winbond_ooblayout);
++}
++
++void macronix_set_defaults(struct spi_device *spi_nand)
++{
++	struct mtd_info *mtd = dev_get_drvdata(&spi_nand->dev);
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++
++	chip->ecc.size  = 0x800;
++	chip->ecc.bytes = 0x0;
++	chip->ecc.steps = 0x0;
++
++	chip->ecc.strength = 1;
++	chip->ecc.total = 0;
++  mtd_set_ooblayout(mtd, &macronix_ooblayout);
++}
++
++void gigadevice_read_cmd(struct spinand_cmd *cmd, u32 page_id)
++{
++	cmd->addr[0] = (u8)(page_id >> 16);
++	cmd->addr[1] = (u8)(page_id >> 8);
++	cmd->addr[2] = (u8)(page_id);
++}
++
++void toshiba_read_cmd(struct spinand_cmd *cmd, u32 page_id)
++{
++	cmd->addr[0] = ((u8)(page_id >> 16) % 2);
++	cmd->addr[1] = (u8)(page_id >> 8);
++	cmd->addr[2] = (u8)(page_id);
++}
++
++void gigadevice_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = 0xff; /*dummy byte*/
++	cmd->addr[1] = (u8)(column >> 8);
++	cmd->addr[2] = (u8)(column);
++}
++
++void gigadevice_read_data_v2(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = (u8)(column >> 8);
++	cmd->addr[1] = (u8)(column);
++	cmd->addr[2] = 0xff; /*dummy byte*/
++}
++void macronix_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = ((u8)(column >> 8) & MACRONIX_NORM_RW_MASK);
++	cmd->addr[1] = (u8)(column);
++}
++
++void winbond_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = (u8)(column >> 8);
++	cmd->addr[1] = (u8)(column);
++}
++
++void toshiba_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = ((u8)(column >> 8) & TOSHIBA_NORM_RW_MASK);
++	cmd->addr[1] = (u8)(column);
++}
++
++void gigadevice_write_cmd(struct spinand_cmd *cmd, u32 page_id)
++{
++	cmd->addr[0] = (u8)(page_id >> 16);
++	cmd->addr[1] = (u8)(page_id >> 8);
++	cmd->addr[2] = (u8)(page_id);
++}
++
++void toshiba_write_cmd(struct spinand_cmd *cmd, u32 page_id)
++{
++	cmd->addr[0] = ((u8)(page_id >> 16) % 2);
++	cmd->addr[1] = (u8)(page_id >> 8);
++	cmd->addr[2] = (u8)(page_id);
++}
++
++void gigadevice_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = (u8)(column >> 8);
++	cmd->addr[1] = (u8)(column);
++}
++
++void macronix_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = ((u8)(column >> 8) & MACRONIX_NORM_RW_MASK);
++	cmd->addr[1] = (u8)(column);
++}
++
++void winbond_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = (u8)(column >> 8);
++	cmd->addr[1] = (u8)(column);
++}
++
++void toshiba_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = ((u8)(column >> 8) & TOSHIBA_NORM_RW_MASK);
++	cmd->addr[1] = (u8)(column);
++}
++
++void gigadevice_erase_blk(struct spinand_cmd *cmd, u32 page_id)
++{
++	cmd->addr[0] = (u8)(page_id >> 16);
++	cmd->addr[1] = (u8)(page_id >> 8);
++	cmd->addr[2] = (u8)(page_id);
++}
++
++void toshiba_erase_blk(struct spinand_cmd *cmd, u32 page_id)
++{
++	cmd->addr[0] = (u8)((page_id >> 16) % 2);
++	cmd->addr[1] = (u8)(page_id >> 8);
++	cmd->addr[2] = (u8)(page_id);
++}
++
++int gigadevice_verify_ecc(u8 status)
++{
++	int ecc_status = (status & STATUS_ECC_MASK_GIGA);
++
++	if (ecc_status == STATUS_ECC_ERROR_GIGA)
++		return SPINAND_ECC_ERROR;
++	else if (ecc_status >= STATUS_ECC_BF_THRESHOLD_GIGA)
++		return SPINAND_ECC_CORRECTED;
++	else
++		return 0;
++}
++
++int macronix_verify_ecc(u8 status)
++{
++	int ecc_status = (status & STATUS_ECC_MASK_MACRONIX);
++
++	if ((ecc_status == STATUS_ECC_ERROR_MACRONIX) ||
++	    (ecc_status == STATUS_ECC_MASK_MACRONIX))
++		return SPINAND_ECC_ERROR;
++	else if (ecc_status)
++		return SPINAND_ECC_CORRECTED;
++	else
++		return 0;
++}
++
++int toshiba_verify_ecc(u8 status)
++{
++	int ecc_status = (status & STATUS_ECC_MASK_TOSHIBA);
++
++	if (ecc_status == STATUS_ECC_ERROR_TOSHIBA)
++		return SPINAND_ECC_ERROR;
++	else if (ecc_status == STATUS_ECC_BF_THRESHOLD_TOSHIBA)
++		return SPINAND_ECC_CORRECTED;
++	else
++		return 0;
++}
++
++int dummy_verify_ecc(u8 status)
++{
++	return 0;
++}
++
++int gigadevice_parse_id(struct spi_device *spi_nand,
++			struct spinand_ops *ops, u8 *nand_id, u8 *id)
++{
++	if (nand_id[0] != NAND_MFR_GIGA && nand_id[0] != NAND_MFR_ATO)
++		return -EINVAL;
++
++	if (!(nand_id[0] == NAND_MFR_GIGA && nand_id[1] == ops->dev_id))
++		return -EINVAL;
++
++	id[0] = nand_id[0];
++	id[1] = nand_id[1];
++
++	return 0;
++}
++
++int gigadevice_parse_id_v2(struct spi_device *spi_nand,
++			   struct spinand_ops *ops, u8 *nand_id, u8 *id)
++{
++	if (nand_id[1] != NAND_MFR_GIGA && nand_id[1] != NAND_MFR_ATO)
++		return -EINVAL;
++
++	if (!(nand_id[1] == NAND_MFR_GIGA && nand_id[2] == ops->dev_id))
++		return -EINVAL;
++
++	id[0] = nand_id[1];
++	id[1] = nand_id[2];
++
++	return 0;
++}
++
++int macronix_parse_id(struct spi_device *spi_nand,
++		      struct spinand_ops *ops, u8 *nand_id, u8 *id)
++{
++	if (nand_id[1] != NAND_MFR_MACRONIX)
++		return -EINVAL;
++
++	return 0;
++}
++
++int winbond_parse_id(struct spi_device *spi_nand,
++		     struct spinand_ops *ops, u8 *nand_id, u8 *id)
++{
++	if (!(nand_id[1] == NAND_MFR_WINBOND && nand_id[2] == ops->dev_id))
++		return -EINVAL;
++
++	return 0;
++}
++
++int toshiba_parse_id(struct spi_device *spi_nand,
++		     struct spinand_ops *ops, u8 *nand_id, u8 *id)
++{
++	if (!(nand_id[1] == NAND_MFR_TOSHIBA && nand_id[2] == ops->dev_id))
++		return -EINVAL;
++
++	return 0;
++}
++
++MODULE_DESCRIPTION("SPI NAND driver for Gigadevice and Macronix");
+diff --git a/drivers/staging/mt29f_spinand/giga_spinand.h b/drivers/staging/mt29f_spinand/giga_spinand.h
+new file mode 100644
+index 00000000..af0f7df0
+--- /dev/null
++++ b/drivers/staging/mt29f_spinand/giga_spinand.h
+@@ -0,0 +1,94 @@
++
++/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ */
++
++#ifndef __GIGA_SPI_NAND_H
++#define __GIGA__SPI_NAND_H
++
++#include "mt29f_spinand.h"
++
++void gigadevice_set_defaults(struct spi_device *spi_nand);
++
++void gigadevice_set_defaults_128mb(struct spi_device *spi_nand);
++
++void gigadevice_set_defaults_512mb(struct spi_device *spi_nand);
++
++void winbond_set_defaults(struct spi_device *spi_nand);
++
++void macronix_set_defaults(struct spi_device *spi_nand);
++
++void gigadevice_read_cmd(struct spinand_cmd *cmd, u32 page_id);
++
++void gigadevice_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++void gigadevice_read_data_v2(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++void gigadevice_write_cmd(struct spinand_cmd *cmd, u32 column);
++
++void gigadevice_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++void gigadevice_erase_blk(struct spinand_cmd *cmd, u32 page_id);
++
++int gigadevice_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops,
++			u8 *nand_id, u8 *id);
++
++int gigadevice_parse_id_v2(struct spi_device *spi_nand, struct spinand_ops *ops,
++			   u8 *nand_id, u8 *id);
++
++int gigadevice_verify_ecc(u8 status);
++
++int dummy_verify_ecc(u8 status);
++
++void macronix_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++void macronix_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++int macronix_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops,
++		      u8 *nand_id, u8 *id);
++
++int macronix_verify_ecc(u8 status);
++
++void winbond_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++void winbond_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++int winbond_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops,
++		     u8 *nand_id, u8 *id);
++
++int winbond_die_select(struct spi_device *spi_nand,
++		       struct spinand_ops *dev_ops, u8 die_id);
++
++void toshiba_read_cmd(struct spinand_cmd *cmd, u32 page_id);
++
++void toshiba_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++void toshiba_write_cmd(struct spinand_cmd *cmd, u32 page_id);
++
++void toshiba_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id);
++
++void toshiba_erase_blk(struct spinand_cmd *cmd, u32 page_id);
++
++int toshiba_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops,
++		     u8 *nand_id, u8 *id);
++
++int toshiba_verify_ecc(u8 status);
++
++/* Macronix Specific defines */
++#define MACRONIX_NORM_RW_MASK	0x0F
++
++/* Toshiba Specific defines */
++#define TOSHIBA_NORM_RW_MASK	0x1F
++#endif /* __GIGA_SPI_NAND_H */
+diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
+index 44847845..90c21890 100644
+--- a/drivers/staging/mt29f_spinand/mt29f_spinand.c
++++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c
+@@ -20,20 +20,271 @@
+ #include <linux/mtd/partitions.h>
+ #include <linux/mtd/rawnand.h>
+ #include <linux/spi/spi.h>
++#include <linux/sizes.h>
+
+ #include "mt29f_spinand.h"
++#include "giga_spinand.h"
++
++#define BUFSIZE (10 * 64 * 4096)
++#define CACHE_BUF 4352
++
++static int spinand_disable_ecc(struct spi_device *spi_nand);
++static int spinand_lock_block(struct spi_device *spi_nand, u8 lock);
++
++struct spinand_ops spinand_dev[] = {
++#ifdef CONFIG_MTD_SPINAND_GIGADEVICE
++	{
++		NAND_MFR_GIGA,
++		1,
++		0xb1,
++		INT_MAX,
++		0x10000,
++		gigadevice_set_defaults,
++		gigadevice_read_cmd,
++		gigadevice_read_data,
++		gigadevice_write_cmd,
++		gigadevice_write_data,
++		gigadevice_erase_blk,
++		gigadevice_parse_id,
++		gigadevice_verify_ecc,
++		NULL,
++	},
++	{
++		NAND_MFR_GIGA,
++		1,
++		0xb4,
++		INT_MAX,
++		0x20000,
++		gigadevice_set_defaults_512mb,
++		gigadevice_read_cmd,
++		gigadevice_read_data,
++		gigadevice_write_cmd,
++		gigadevice_write_data,
++		gigadevice_erase_blk,
++		gigadevice_parse_id,
++		gigadevice_verify_ecc,
++		NULL,
++	},
++	{
++		NAND_MFR_GIGA,
++		1,
++		0xa1,
++		INT_MAX,
++		0x10000,
++		gigadevice_set_defaults,
++		gigadevice_read_cmd,
++		gigadevice_read_data,
++		gigadevice_write_cmd,
++		gigadevice_write_data,
++		gigadevice_erase_blk,
++		gigadevice_parse_id,
++		gigadevice_verify_ecc,
++		NULL,
++	},
++	{
++		NAND_MFR_GIGA,
++		1,
++		0xd1,
++		INT_MAX,
++		0x10000,
++		gigadevice_set_defaults_128mb,
++		gigadevice_read_cmd,
++		gigadevice_read_data_v2,
++		gigadevice_write_cmd,
++		gigadevice_write_data,
++		gigadevice_erase_blk,
++		gigadevice_parse_id_v2,
++		gigadevice_verify_ecc,
++		NULL,
++	},
++	{
++		NAND_MFR_ATO,
++		1,
++		0x12,
++		INT_MAX,
++		0x10000,
++		gigadevice_set_defaults,
++		gigadevice_read_cmd,
++		gigadevice_read_data,
++		gigadevice_write_cmd,
++		gigadevice_write_data,
++		gigadevice_erase_blk,
++		gigadevice_parse_id,
++		dummy_verify_ecc,
++		NULL,
++	},
++#endif
++	{
++		NAND_MFR_MACRONIX,
++		1,
++		0x12,
++		INT_MAX,
++		0x10000,
++		macronix_set_defaults,
++		gigadevice_read_cmd,
++		macronix_read_data,
++		gigadevice_write_cmd,
++		macronix_write_data,
++		gigadevice_erase_blk,
++		macronix_parse_id,
++		macronix_verify_ecc,
++		NULL,
++	},
++	{
++		NAND_MFR_WINBOND,
++		1,
++		0xaa,
++		INT_MAX,
++		0x10000,
++		winbond_set_defaults,
++		gigadevice_read_cmd,
++		winbond_read_data,
++		gigadevice_write_cmd,
++		winbond_write_data,
++		gigadevice_erase_blk,
++		winbond_parse_id,
++		macronix_verify_ecc,
++		NULL,
++	},
++	{
++		NAND_MFR_WINBOND,
++		2,
++		0xab,
++		INT_MAX,
++		0x10000,
++		winbond_set_defaults,
++		gigadevice_read_cmd,
++		winbond_read_data,
++		gigadevice_write_cmd,
++		winbond_write_data,
++		gigadevice_erase_blk,
++		winbond_parse_id,
++		macronix_verify_ecc,
++		winbond_die_select,
++	},
++	{
++		NAND_MFR_TOSHIBA,
++		1,
++		0xcd,
++		INT_MAX,
++		0x20000,
++		gigadevice_set_defaults_512mb,
++		toshiba_read_cmd,
++		toshiba_read_data,
++		toshiba_write_cmd,
++		toshiba_write_data,
++		toshiba_erase_blk,
++		toshiba_parse_id,
++		toshiba_verify_ecc,
++		NULL,
++	},
++	{ },
++};
++
++void mt29f_read_page_to_cache(struct spinand_cmd *cmd, u32 page_id)
++{
++	cmd->addr[1] = (u8)((page_id & 0xff00) >> 8);
++	cmd->addr[2] = (u8)(page_id & 0x00ff);
++}
++
++void mt29f_read_from_cache(struct spinand_cmd *cmd, u16 column, u32 page_id)
++{
++	cmd->addr[0] = (u8)((column & 0xff00) >> 8);
++	cmd->addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
++	cmd->addr[1] = (u8)(column & 0x00ff);
++	cmd->addr[2] = (u8)(0xff);
++}
++
++void mt29f_program_data_to_cache(struct spinand_cmd *cmd, u16 column,
++				 u32 page_id)
++{
++	cmd->addr[0] = (u8)((column & 0xff00) >> 8);
++	cmd->addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
++	cmd->addr[1] = (u8)(column & 0x00ff);
++}
++
++void mt29f_program_execute(struct spinand_cmd *cmd, u32 column)
++{
++	cmd->addr[1] = (u8)((column & 0xff00) >> 8);
++	cmd->addr[2] = (u8)(column & 0x00ff);
++}
++
++void mt29f_erase_block_erase(struct spinand_cmd *cmd, u32 page_id)
++{
++	cmd->addr[1] = (u8)((page_id & 0xff00) >> 8);
++	cmd->addr[2] = (u8)(page_id & 0x00ff);
++}
++
++int mt29f_verify_ecc(u8 status)
++{
++	int ecc_status = (status & STATUS_ECC_MASK);
++
++	if (ecc_status == STATUS_ECC_ERROR)
++		return SPINAND_ECC_ERROR;
++	else if (ecc_status == STATUS_ECC_1BIT_CORRECTED)
++		return SPINAND_ECC_CORRECTED;
++	else
++		return 0;
++}
++
++struct spinand_ops mt29f_spinand_ops = {
++		NAND_MFR_MICRON,
++		1,
++		0x0,
++		INT_MAX,
++		0x0,
++		NULL,
++		mt29f_read_page_to_cache,
++		mt29f_read_from_cache,
++		mt29f_program_execute,
++		mt29f_program_data_to_cache,
++		mt29f_erase_block_erase,
++		NULL,
++		mt29f_verify_ecc,
++		NULL,
++};
++
++static inline struct spinand_ops *get_dev_ops(struct spi_device *spi_nand)
++{
++	struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata
++							(&spi_nand->dev);
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++	struct spinand_info *info = (struct spinand_info *)chip->priv;
++	struct spinand_ops *dev_ops = info->dev_ops;
++
++	return dev_ops;
++}
++
++void spinand_parse_id(struct spi_device *spi_nand, u8 *nand_id, u8 *id)
++{
++	int tmp;
++	struct spinand_ops *tmp_ops;
++	struct mtd_info *mtd = (struct mtd_info *)
++					dev_get_drvdata(&spi_nand->dev);
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++	struct spinand_info *info = (struct spinand_info *)chip->priv;
++
++	for (tmp = 0; tmp < ARRAY_SIZE(spinand_dev) - 1; tmp++) {
++		tmp_ops = &spinand_dev[tmp];
++			if (tmp_ops->spinand_parse_id(spi_nand, tmp_ops,
++						      nand_id, id) == 0) {
++				info->dev_ops = &spinand_dev[tmp];
++				info->dev_ops->spinand_set_defaults(spi_nand);
++				return;
++			}
++	}
++	info->dev_ops = &mt29f_spinand_ops;
++}
+
+-#define BUFSIZE (10 * 64 * 2048)
+-#define CACHE_BUF 2112
+ /*
+  * OOB area specification layout:  Total 32 available free bytes.
+  */
+
+ static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd)
+ {
+-	struct nand_chip *chip = mtd_to_nand(mtd);
+-	struct spinand_info *info = nand_get_controller_data(chip);
+-	struct spinand_state *state = info->priv;
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++	struct spinand_info *info = (struct spinand_info *)chip->priv;
++	struct spinand_state *state = (struct spinand_state *)info->priv;
+
+ 	return state;
+ }
+@@ -43,7 +294,7 @@ static int enable_hw_ecc;
+ static int enable_read_hw_ecc;
+
+ static int spinand_ooblayout_64_ecc(struct mtd_info *mtd, int section,
+-				    struct mtd_oob_region *oobregion)
++					struct mtd_oob_region *oobregion)
+ {
+ 	if (section > 3)
+ 		return -ERANGE;
+@@ -55,7 +306,7 @@ static int spinand_ooblayout_64_ecc(struct mtd_info *mtd, int section,
+ }
+
+ static int spinand_ooblayout_64_free(struct mtd_info *mtd, int section,
+-				     struct mtd_oob_region *oobregion)
++                 struct mtd_oob_region *oobregion)
+ {
+ 	if (section > 3)
+ 		return -ERANGE;
+@@ -72,8 +323,8 @@ static const struct mtd_ooblayout_ops spinand_oob_64_ops = {
+ };
+ #endif
+
+-/**
+- * spinand_cmd - process a command to send to the SPI Nand
++/*
++ * spinand_cmd - to process a command to send to the SPI Nand
+  * Description:
+  *    Set up the command buffer to send to the SPI controller.
+  *    The command buffer has to initialized to 0.
+@@ -119,16 +370,72 @@ static int spinand_cmd(struct spi_device *spi, struct spinand_cmd *cmd)
+ 	return spi_sync(spi, &message);
+ }
+
+-/**
+- * spinand_read_id - Read SPI Nand ID
++static int get_die_id(struct spinand_ops *dev_ops, u32 page_id)
++{
++	u64 temp_page = (u64)page_id;
++
++	do_div(temp_page, dev_ops->pages_per_die);
++	if (temp_page > dev_ops->no_of_dies) {
++		pr_info("invalid die id : %llu\n", temp_page);
++		return -EINVAL;
++	}
++
++	return page_id;
++}
++
++/*
++ * winbond_die_select - send command 0xc2 to select die
++ * Description:
++ *   Die select function.
++ *   Die ID is given as either 0 or 1 to select die 0 or 1
++ *   respectively
++ */
++int winbond_die_select(struct spi_device *spi_nand,
++		       struct spinand_ops *dev_ops, u8 die_id)
++{
++	int retval;
++	struct spinand_cmd cmd = {0};
++
++	if (die_id < 0)
++		return -1;
++
++	if (dev_ops->prev_die_id == die_id)
++		return 0;
++
++	cmd.cmd = CMD_DIE_SELECT,
++	cmd.n_addr = 1,
++	cmd.addr[0] = die_id,
++	retval = spinand_cmd(spi_nand, &cmd);
++	if (retval < 0)
++		dev_err(&spi_nand->dev, "error %d in die select\n", retval);
++	else
++		dev_ops->prev_die_id = die_id;
++
++	return retval;
++}
++
++static inline int select_die(struct spi_device *spi_nand,
++			     struct spinand_ops *dev_ops, int die)
++{
++	if (!dev_ops->spinand_die_select)
++		return 0;
++
++	return dev_ops->spinand_die_select(spi_nand,
++		dev_ops, die);
++}
++
++/*
++ * spinand_read_id- Read SPI Nand ID
+  * Description:
+- *    read two ID bytes from the SPI Nand device
++ *    Read ID: read two ID bytes from the SPI Nand device
+  */
+ static int spinand_read_id(struct spi_device *spi_nand, u8 *id)
+ {
+ 	int retval;
++	int i;
+ 	u8 nand_id[3];
+ 	struct spinand_cmd cmd = {0};
++	struct spinand_ops *dev_ops;
+
+ 	cmd.cmd = CMD_READ_ID;
+ 	cmd.n_rx = 3;
+@@ -141,11 +448,26 @@ static int spinand_read_id(struct spi_device *spi_nand, u8 *id)
+ 	}
+ 	id[0] = nand_id[1];
+ 	id[1] = nand_id[2];
++  printk(KERN_ERR "Phi Nguyen: 1st ID %x and 2nd ID %x \n", id[0], id[1]);
++	spinand_parse_id(spi_nand, nand_id, id);
++	dev_ops = get_dev_ops(spi_nand);
++	if (dev_ops->spinand_die_select) {
++		for (i = 0; i < dev_ops->no_of_dies; i++) {
++			retval = dev_ops->spinand_die_select(spi_nand,
++						dev_ops, i);
++			if (retval < 0)
++				return retval;
++			spinand_lock_block(spi_nand, BL_ALL_UNLOCKED);
++			if (spinand_disable_ecc(spi_nand) < 0)
++				pr_info("%s: disable ecc failed!\n", __func__);
++		}
++	}
++
+ 	return retval;
+ }
+
+-/**
+- * spinand_read_status - send command 0xf to the SPI Nand status register
++/*
++ * spinand_read_status- send command 0xf to the SPI Nand status register
+  * Description:
+  *    After read, write, or erase, the Nand device is expected to set the
+  *    busy status.
+@@ -184,7 +506,7 @@ static int wait_till_ready(struct spi_device *spi_nand)
+ 		retval = spinand_read_status(spi_nand, &stat);
+ 		if (retval < 0)
+ 			return -1;
+-		if (!(stat & 0x1))
++		else if (!(stat & 0x1))
+ 			break;
+
+ 		cond_resched();
+@@ -197,7 +519,7 @@ static int wait_till_ready(struct spi_device *spi_nand)
+ }
+
+ /**
+- * spinand_get_otp - send command 0xf to read the SPI Nand OTP register
++ * spinand_get_otp- send command 0xf to read the SPI Nand OTP register
+  * Description:
+  *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+  *   Enable chip internal ECC, set the bit to 1
+@@ -221,7 +543,7 @@ static int spinand_get_otp(struct spi_device *spi_nand, u8 *otp)
+ }
+
+ /**
+- * spinand_set_otp - send command 0x1f to write the SPI Nand OTP register
++ * spinand_set_otp- send command 0x1f to write the SPI Nand OTP register
+  * Description:
+  *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+  *   Enable chip internal ECC, set the bit to 1
+@@ -232,11 +554,11 @@ static int spinand_set_otp(struct spi_device *spi_nand, u8 *otp)
+ 	int retval;
+ 	struct spinand_cmd cmd = {0};
+
+-	cmd.cmd = CMD_WRITE_REG;
+-	cmd.n_addr = 1;
+-	cmd.addr[0] = REG_OTP;
+-	cmd.n_tx = 1;
+-	cmd.tx_buf = otp;
++	cmd.cmd = CMD_WRITE_REG,
++	cmd.n_addr = 1,
++	cmd.addr[0] = REG_OTP,
++	cmd.n_tx = 1,
++	cmd.tx_buf = otp,
+
+ 	retval = spinand_cmd(spi_nand, &cmd);
+ 	if (retval < 0)
+@@ -247,7 +569,7 @@ static int spinand_set_otp(struct spi_device *spi_nand, u8 *otp)
+
+ #ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ /**
+- * spinand_enable_ecc - send command 0x1f to write the SPI Nand OTP register
++ * spinand_enable_ecc- send command 0x1f to write the SPI Nand OTP register
+  * Description:
+  *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+  *   Enable chip internal ECC, set the bit to 1
+@@ -256,19 +578,31 @@ static int spinand_set_otp(struct spi_device *spi_nand, u8 *otp)
+ static int spinand_enable_ecc(struct spi_device *spi_nand)
+ {
+ 	int retval;
++	int i;
++	struct spinand_ops *dev_ops = get_dev_ops(spi_nand);
+ 	u8 otp = 0;
+
+-	retval = spinand_get_otp(spi_nand, &otp);
+-	if (retval < 0)
+-		return retval;
++	for (i = 0; i < dev_ops->no_of_dies; i++) {
++		retval = select_die(spi_nand, dev_ops, i);
++		if (retval < 0)
++			return retval;
+
+-	if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK)
+-		return 0;
+-	otp |= OTP_ECC_MASK;
+-	retval = spinand_set_otp(spi_nand, &otp);
+-	if (retval < 0)
+-		return retval;
+-	return spinand_get_otp(spi_nand, &otp);
++		retval = spinand_get_otp(spi_nand, &otp);
++		if (retval < 0)
++			return retval;
++
++		if ((otp & OTP_ECC_MASK) != OTP_ECC_MASK) {
++			otp |= OTP_ECC_MASK;
++			retval = spinand_set_otp(spi_nand, &otp);
++			if (retval < 0)
++				return retval;
++			retval = spinand_get_otp(spi_nand, &otp);
++			if (retval < 0)
++				return retval;
++		}
++	}
++
++	return 0;
+ }
+ #endif
+
+@@ -292,58 +626,70 @@ static int spinand_disable_ecc(struct spi_device *spi_nand)
+ }
+
+ /**
+- * spinand_write_enable - send command 0x06 to enable write or erase the
+- * Nand cells
++ * spinand_write_config- send command 0x06 to enable write or erase the
++ * Nand cells or send command 0x04 to disable write or erase the Nand cells
++ *
+  * Description:
+  *   Before write and erase the Nand cells, the write enable has to be set.
+  *   After the write or erase, the write enable bit is automatically
+  *   cleared (status register bit 2)
+  *   Set the bit 2 of the status register has the same effect
++ *   After write and erase the Nand cells, the write enable has to be disabled.
+  */
+-static int spinand_write_enable(struct spi_device *spi_nand)
++static int spinand_write_config(struct spi_device *spi_nand, u8 opcode)
+ {
++	struct spinand_ops *dev_ops = get_dev_ops(spi_nand);
+ 	struct spinand_cmd cmd = {0};
++	int ret = 0;
++	int i;
+
+-	cmd.cmd = CMD_WR_ENABLE;
+-	return spinand_cmd(spi_nand, &cmd);
++	for (i = 0; i < dev_ops->no_of_dies; i++) {
++		ret = select_die(spi_nand, dev_ops, i);
++		if (ret < 0)
++			return ret;
++		cmd.cmd = opcode;
++		ret = spinand_cmd(spi_nand, &cmd);
++		if (ret < 0)
++			return ret;
++	}
++
++	return ret;
+ }
+
+ static int spinand_read_page_to_cache(struct spi_device *spi_nand, u16 page_id)
+ {
+ 	struct spinand_cmd cmd = {0};
+-	u16 row;
++	struct spinand_ops *dev_ops = get_dev_ops(spi_nand);
++
++	select_die(spi_nand, dev_ops, get_die_id(dev_ops, page_id));
+
+-	row = page_id;
+ 	cmd.cmd = CMD_READ;
+ 	cmd.n_addr = 3;
+-	cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
+-	cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+-	cmd.addr[2] = (u8)(row & 0x00ff);
++	dev_ops->spinand_read_cmd(&cmd, page_id);
+
+ 	return spinand_cmd(spi_nand, &cmd);
+ }
+
+-/**
+- * spinand_read_from_cache - send command 0x03 to read out the data from the
+- * cache register (2112 bytes max)
++/*
++ * spinand_read_from_cache- send command 0x03 to read out the data from the
++ * cache register(2112 bytes max)
++ *
+  * Description:
+  *   The read can specify 1 to 2112 bytes of data read at the corresponding
+  *   locations.
+  *   No tRd delay.
+  */
+-static int spinand_read_from_cache(struct spi_device *spi_nand, u16 page_id,
++static int spinand_read_from_cache(struct spi_device *spi_nand, u32 page_id,
+ 				   u16 byte_id, u16 len, u8 *rbuf)
+ {
+ 	struct spinand_cmd cmd = {0};
++	struct spinand_ops *dev_ops = get_dev_ops(spi_nand);
+ 	u16 column;
+
+ 	column = byte_id;
+ 	cmd.cmd = CMD_READ_RDM;
+ 	cmd.n_addr = 3;
+-	cmd.addr[0] = (u8)((column & 0xff00) >> 8);
+-	cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
+-	cmd.addr[1] = (u8)(column & 0x00ff);
+-	cmd.addr[2] = (u8)(0xff);
++	dev_ops->spinand_read_data(&cmd, column, page_id);
+ 	cmd.n_dummy = 0;
+ 	cmd.n_rx = len;
+ 	cmd.rx_buf = rbuf;
+@@ -351,22 +697,25 @@ static int spinand_read_from_cache(struct spi_device *spi_nand, u16 page_id,
+ 	return spinand_cmd(spi_nand, &cmd);
+ }
+
+-/**
+- * spinand_read_page - read a page
++/*
++ * spinand_read_page-to read a page with:
+  * @page_id: the physical page number
+  * @offset:  the location from 0 to 2111
+  * @len:     number of bytes to read
+  * @rbuf:    read buffer to hold @len bytes
+  *
+  * Description:
+- *   The read includes two commands to the Nand - 0x13 and 0x03 commands
++ *   The read includes two commands to the Nand: 0x13 and 0x03 commands
+  *   Poll to read status to wait for tRD time.
+  */
+-static int spinand_read_page(struct spi_device *spi_nand, u16 page_id,
+-			     u16 offset, u16 len, u8 *rbuf)
++static int spinand_read_page(struct spi_device *spi_nand, u32 page_id,
++			     u32 offset, u32 len, u8 *rbuf)
+ {
+-	int ret;
++	int ret, ecc_error = 0, ecc_corrected = 0;
+ 	u8 status = 0;
++	struct spinand_ops *dev_ops = get_dev_ops(spi_nand);
++	struct mtd_info *mtd = (struct mtd_info *)
++					dev_get_drvdata(&spi_nand->dev);
+
+ #ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ 	if (enable_read_hw_ecc) {
+@@ -390,10 +739,15 @@ static int spinand_read_page(struct spi_device *spi_nand, u16 page_id,
+ 		}
+
+ 		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+-			if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
++			ret = dev_ops->spinand_verify_ecc(status);
++			if (ret == SPINAND_ECC_ERROR) {
+ 				dev_err(&spi_nand->dev, "ecc error, page=%d\n",
+ 					page_id);
+-				return 0;
++				mtd->ecc_stats.failed++;
++				ecc_error = 1;
++			} else if (ret == SPINAND_ECC_CORRECTED) {
++				mtd->ecc_stats.corrected++;
++				ecc_corrected = 1;
+ 			}
+ 			break;
+ 		}
+@@ -415,14 +769,19 @@ static int spinand_read_page(struct spi_device *spi_nand, u16 page_id,
+ 		enable_read_hw_ecc = 0;
+ 	}
+ #endif
++	if (ecc_error)
++		ret = -EBADMSG;
++	else if (ecc_corrected)
++		ret = -EUCLEAN;
++
+ 	return ret;
+ }
+
+-/**
+- * spinand_program_data_to_cache - write a page to cache
++/*
++ * spinand_program_data_to_cache--to write a page to cache with:
+  * @byte_id: the location to write to the cache
+  * @len:     number of bytes to write
+- * @wbuf:    write buffer holding @len bytes
++ * @rbuf:    read buffer to hold @len bytes
+  *
+  * Description:
+  *   The write command used here is 0x84--indicating that the cache is
+@@ -430,26 +789,27 @@ static int spinand_read_page(struct spi_device *spi_nand, u16 page_id,
+  *   Since it is writing the data to cache, there is no tPROG time.
+  */
+ static int spinand_program_data_to_cache(struct spi_device *spi_nand,
+-					 u16 page_id, u16 byte_id,
++					 u32 page_id, u16 byte_id,
+ 					 u16 len, u8 *wbuf)
+ {
+ 	struct spinand_cmd cmd = {0};
+ 	u16 column;
++	struct spinand_ops *dev_ops = get_dev_ops(spi_nand);
++
++	select_die(spi_nand, dev_ops, get_die_id(dev_ops, page_id));
+
+ 	column = byte_id;
+ 	cmd.cmd = CMD_PROG_PAGE_CLRCACHE;
+ 	cmd.n_addr = 2;
+-	cmd.addr[0] = (u8)((column & 0xff00) >> 8);
+-	cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
+-	cmd.addr[1] = (u8)(column & 0x00ff);
++	dev_ops->spinand_write_data(&cmd, column, page_id);
+ 	cmd.n_tx = len;
+-	cmd.tx_buf = wbuf;
++	cmd.tx_buf = wbuf + column;
+
+ 	return spinand_cmd(spi_nand, &cmd);
+ }
+
+ /**
+- * spinand_program_execute - write a page from cache to the Nand array
++ * spinand_program_execute--to write a page from cache to the Nand array with
+  * @page_id: the physical page location to write the page.
+  *
+  * Description:
+@@ -457,27 +817,26 @@ static int spinand_program_data_to_cache(struct spi_device *spi_nand,
+  *   the Nand array.
+  *   Need to wait for tPROG time to finish the transaction.
+  */
+-static int spinand_program_execute(struct spi_device *spi_nand, u16 page_id)
++static int spinand_program_execute(struct spi_device *spi_nand, u32 page_id)
+ {
+ 	struct spinand_cmd cmd = {0};
+-	u16 row;
++	struct spinand_ops *dev_ops = get_dev_ops(spi_nand);
++
++	select_die(spi_nand, dev_ops, get_die_id(dev_ops, page_id));
+
+-	row = page_id;
+ 	cmd.cmd = CMD_PROG_PAGE_EXC;
+ 	cmd.n_addr = 3;
+-	cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
+-	cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+-	cmd.addr[2] = (u8)(row & 0x00ff);
++	dev_ops->spinand_write_cmd(&cmd, page_id);
+
+ 	return spinand_cmd(spi_nand, &cmd);
+ }
+
+ /**
+- * spinand_program_page - write a page
++ * spinand_program_page - to write a page with:
+  * @page_id: the physical page location to write the page.
+  * @offset:  the location from the cache starting from 0 to 2111
+  * @len:     the number of bytes to write
+- * @buf:     the buffer holding @len bytes
++ * @wbuf:    the buffer to hold the number of bytes
+  *
+  * Description:
+  *   The commands used here are 0x06, 0x84, and 0x10--indicating that
+@@ -486,42 +845,36 @@ static int spinand_program_execute(struct spi_device *spi_nand, u16 page_id)
+  *   Poll to wait for the tPROG time to finish the transaction.
+  */
+ static int spinand_program_page(struct spi_device *spi_nand,
+-				u16 page_id, u16 offset, u16 len, u8 *buf)
++				u32 page_id, u16 offset, u16 len, u8 *buf)
+ {
+-	int retval;
++	int retval = 0;
+ 	u8 status = 0;
+ 	u8 *wbuf;
+ #ifdef CONFIG_MTD_SPINAND_ONDIEECC
+-	unsigned int i, j;
+
+-	wbuf = devm_kzalloc(&spi_nand->dev, CACHE_BUF, GFP_KERNEL);
++	enable_read_hw_ecc = 0;
++	wbuf = kzalloc(CACHE_BUF, GFP_KERNEL);
+ 	if (!wbuf)
+ 		return -ENOMEM;
+
+-	enable_read_hw_ecc = 1;
+-	retval = spinand_read_page(spi_nand, page_id, 0, CACHE_BUF, wbuf);
+-	if (retval < 0) {
+-		dev_err(&spi_nand->dev, "ecc error on read page!!!\n");
+-		return retval;
+-	}
++	spinand_read_page(spi_nand, page_id, 0, CACHE_BUF, wbuf);
+
+-	for (i = offset, j = 0; i < len; i++, j++)
+-		wbuf[i] &= buf[j];
++	memcpy(wbuf + offset, buf, len);
+
+ 	if (enable_hw_ecc) {
+ 		retval = spinand_enable_ecc(spi_nand);
+ 		if (retval < 0) {
+ 			dev_err(&spi_nand->dev, "enable ecc failed!!\n");
+-			return retval;
++			goto exit;
+ 		}
+ 	}
+ #else
+ 	wbuf = buf;
+ #endif
+-	retval = spinand_write_enable(spi_nand);
++	retval = spinand_write_config(spi_nand, CMD_WR_ENABLE);
+ 	if (retval < 0) {
+ 		dev_err(&spi_nand->dev, "write enable failed!!\n");
+-		return retval;
++		goto exit;
+ 	}
+ 	if (wait_till_ready(spi_nand))
+ 		dev_err(&spi_nand->dev, "wait timedout!!!\n");
+@@ -529,23 +882,26 @@ static int spinand_program_page(struct spi_device *spi_nand,
+ 	retval = spinand_program_data_to_cache(spi_nand, page_id,
+ 					       offset, len, wbuf);
+ 	if (retval < 0)
+-		return retval;
++		goto exit;
++
+ 	retval = spinand_program_execute(spi_nand, page_id);
+ 	if (retval < 0)
+-		return retval;
++		goto exit;
++
+ 	while (1) {
+ 		retval = spinand_read_status(spi_nand, &status);
+ 		if (retval < 0) {
+ 			dev_err(&spi_nand->dev,
+ 				"error %d reading status register\n", retval);
+-			return retval;
++			goto exit;
+ 		}
+
+ 		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ 			if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
+ 				dev_err(&spi_nand->dev,
+ 					"program error, page %d\n", page_id);
+-				return -1;
++				retval = -1;
++				goto exit;
+ 			}
+ 			break;
+ 		}
+@@ -555,17 +911,28 @@ static int spinand_program_page(struct spi_device *spi_nand,
+ 		retval = spinand_disable_ecc(spi_nand);
+ 		if (retval < 0) {
+ 			dev_err(&spi_nand->dev, "disable ecc failed!!\n");
+-			return retval;
++			goto exit;
+ 		}
+ 		enable_hw_ecc = 0;
+ 	}
+ #endif
++	retval = spinand_write_config(spi_nand, CMD_WR_DISABLE);
++	if (retval < 0) {
++		dev_err(&spi_nand->dev, "write disable failed!!\n");
++		goto exit;
++	}
++	if (wait_till_ready(spi_nand))
++		dev_err(&spi_nand->dev, "wait timedout!!!\n");
+
+-	return 0;
++exit:
++#ifdef CONFIG_MTD_SPINAND_ONDIEECC
++	kfree(wbuf);
++#endif
++	return retval;
+ }
+
+ /**
+- * spinand_erase_block_erase - erase a page
++ * spinand_erase_block_erase--to erase a page with:
+  * @block_id: the physical block location to erase.
+  *
+  * Description:
+@@ -576,20 +943,19 @@ static int spinand_program_page(struct spi_device *spi_nand,
+ static int spinand_erase_block_erase(struct spi_device *spi_nand, u16 block_id)
+ {
+ 	struct spinand_cmd cmd = {0};
+-	u16 row;
++	struct spinand_ops *dev_ops = get_dev_ops(spi_nand);
++
++	select_die(spi_nand, dev_ops, get_die_id(dev_ops, block_id));
+
+-	row = block_id;
+ 	cmd.cmd = CMD_ERASE_BLK;
+ 	cmd.n_addr = 3;
+-	cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
+-	cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+-	cmd.addr[2] = (u8)(row & 0x00ff);
++	dev_ops->spinand_erase_blk(&cmd, block_id);
+
+ 	return spinand_cmd(spi_nand, &cmd);
+ }
+
+ /**
+- * spinand_erase_block - erase a page
++ * spinand_erase_block - erase a page with:
+  * @block_id: the physical block location to erase.
+  *
+  * Description:
+@@ -599,12 +965,16 @@ static int spinand_erase_block_erase(struct spi_device *spi_nand, u16 block_id)
+  *   and then send the 0xd8 erase command
+  *   Poll to wait for the tERS time to complete the tranaction.
+  */
+-static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id)
++static int spinand_erase_block(struct spi_device *spi_nand, u32 block_id)
+ {
+ 	int retval;
+ 	u8 status = 0;
+
+-	retval = spinand_write_enable(spi_nand);
++	retval = spinand_write_config(spi_nand, CMD_WR_ENABLE);
++	if (retval < 0) {
++		dev_err(&spi_nand->dev, "write enable failed!!\n");
++		return retval;
++	}
+ 	if (wait_till_ready(spi_nand))
+ 		dev_err(&spi_nand->dev, "wait timedout!!!\n");
+
+@@ -626,6 +996,13 @@ static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id)
+ 			break;
+ 		}
+ 	}
++	retval = spinand_write_config(spi_nand, CMD_WR_DISABLE);
++	if (retval < 0) {
++		dev_err(&spi_nand->dev, "write disable failed!!\n");
++		return retval;
++	}
++	if (wait_till_ready(spi_nand))
++		dev_err(&spi_nand->dev, "wait timedout!!!\n");
+ 	return 0;
+ }
+
+@@ -647,13 +1024,17 @@ static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ 				   u8 *buf, int oob_required, int page)
+ {
+ 	int retval;
+-	u8 status;
++	u8 status = 0;
+ 	u8 *p = buf;
+ 	int eccsize = chip->ecc.size;
+ 	int eccsteps = chip->ecc.steps;
+-	struct spinand_info *info = nand_get_controller_data(chip);
++	struct spinand_info *info = (struct spinand_info *)chip->priv;
++	struct spinand_ops *dev_ops = info->dev_ops;
++	struct spinand_state *state = (struct spinand_state *)info->priv;
+
+ 	enable_read_hw_ecc = 1;
++	spinand_read_page(info->spi, page, state->col,
++		(mtd->writesize + mtd->oobsize), state->buf);
+
+ 	nand_read_page_op(chip, page, 0, p, eccsize * eccsteps);
+ 	if (oob_required)
+@@ -668,15 +1049,32 @@ static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ 		}
+
+ 		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+-			if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
++			retval = dev_ops->spinand_verify_ecc(status);
++			if (retval == SPINAND_ECC_ERROR) {
+ 				pr_info("spinand: ECC error\n");
+ 				mtd->ecc_stats.failed++;
+-			} else if ((status & STATUS_ECC_MASK) ==
+-					STATUS_ECC_1BIT_CORRECTED)
++				retval = -EBADMSG;
++			} else if (retval == SPINAND_ECC_CORRECTED) {
+ 				mtd->ecc_stats.corrected++;
++				retval = -EUCLEAN;
++			}
+ 			break;
+ 		}
+ 	}
++	return retval;
++}
++
++static int spinand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
++			      uint8_t *buf, int oob_required, int page)
++{
++	struct spinand_info *info = (struct spinand_info *)chip->priv;
++	struct spinand_state *state = (struct spinand_state *)info->priv;
++
++	spinand_read_page(info->spi, page, state->col,
++		(mtd->writesize + mtd->oobsize), state->buf);
++	chip->read_buf(mtd, buf, mtd->writesize);
++	if (oob_required)
++		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ 	return 0;
+ }
+ #endif
+@@ -697,11 +1095,11 @@ static u8 spinand_read_byte(struct mtd_info *mtd)
+
+ static int spinand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+ {
+-	struct spinand_info *info = nand_get_controller_data(chip);
++	struct spinand_info *info = (struct spinand_info *)chip->priv;
+
+ 	unsigned long timeo = jiffies;
+ 	int retval, state = chip->state;
+-	u8 status;
++	u8 status = 0;
+
+ 	if (state == FL_ERASING)
+ 		timeo += (HZ * 400) / 1000;
+@@ -762,9 +1160,9 @@ static void spinand_reset(struct spi_device *spi_nand)
+ static void spinand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ 			    int column, int page)
+ {
+-	struct nand_chip *chip = mtd_to_nand(mtd);
+-	struct spinand_info *info = nand_get_controller_data(chip);
+-	struct spinand_state *state = info->priv;
++	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
++	struct spinand_info *info = (struct spinand_info *)chip->priv;
++	struct spinand_state *state = (struct spinand_state *)info->priv;
+
+ 	switch (command) {
+ 	/*
+@@ -772,13 +1170,15 @@ static void spinand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ 	 */
+ 	case NAND_CMD_READ1:
+ 	case NAND_CMD_READ0:
++		state->col = column;
++		state->row = page;
+ 		state->buf_ptr = 0;
+-		spinand_read_page(info->spi, page, 0x0, 0x840, state->buf);
+ 		break;
+ 	/* READOOB reads only the OOB because no ECC is performed. */
+ 	case NAND_CMD_READOOB:
+ 		state->buf_ptr = 0;
+-		spinand_read_page(info->spi, page, 0x800, 0x40, state->buf);
++		spinand_read_page(info->spi, page, (mtd->writesize + column),
++				  mtd->oobsize, state->buf);
+ 		break;
+ 	case NAND_CMD_RNDOUT:
+ 		state->buf_ptr = column;
+@@ -828,7 +1228,7 @@ static void spinand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ }
+
+ /**
+- * spinand_lock_block - send write register 0x1f command to the Nand device
++ * spinand_lock_block- send write register 0x1f command to the Nand device
+  *
+  * Description:
+  *    After power up, all the Nand blocks are locked.  This function allows
+@@ -855,12 +1255,44 @@ static int spinand_lock_block(struct spi_device *spi_nand, u8 lock)
+ 	return ret;
+ }
+
+-/**
++/* SPI NAND ID Table */
++struct nand_flash_dev spinand_flash_ids[] = {
++	{"ATO25D1GA 128MiB 3.3V",
++		{ .id = {0x9b, 0x12} }, SZ_2K, 128, SZ_128K, 0, 2, 64},
++
++	{"GD5F4GQ4UC 512MiB 3.3V",
++		{ .id = {0xc8, 0xB4} }, SZ_4K, 512, SZ_256K, 0, 2, 256},
++
++	{"GD5F1GQ1UC 128MiB 3.3V",
++		{ .id = {0xc8, 0xB1} }, SZ_2K, 128, SZ_128K, 0, 2, 128},
++
++	{"GD5F1GQ1RC 128MiB 1.8V",
++		{ .id = {0xc8, 0xA1} }, SZ_2K, 128, SZ_128K, 0, 2, 128},
++
++	{"MX35LFxGE4AB 128MiB 3.3V",
++		{ .id = {0xc2, 0x12} }, SZ_2K, 128, SZ_128K, 0, 2, 64},
++
++	{"W25N01GV 128MiB 3.3V",
++		{ .id = {0xef, 0xaa} }, SZ_2K, 128, SZ_128K, 0, 2, 64},
++
++	{"W25M02GV 256MiB 3.3V(Dual die)",
++		{ .id = {0xef, 0xab} }, SZ_2K, 256, SZ_128K, 0, 2, 64},
++
++	{"TC58CVG2S0F 4G 3.3V 8-bit",
++		{ .id = {0x98, 0xcd} }, SZ_4K, 512, SZ_256K, 0, 2, 128},
++
++	{"GD5F1GQ4UB 128MiB 3.3V",
++		{ .id = {0xc8, 0xd1} }, SZ_2K, 128, SZ_128K, 0, 2, 128},
++
++	{NULL}
++};
++
++/*
+  * spinand_probe - [spinand Interface]
+  * @spi_nand: registered device driver.
+  *
+  * Description:
+- *   Set up the device driver parameters to make the device available.
++ *   To set up the device driver parameters to make the device available.
+  */
+ static int spinand_probe(struct spi_device *spi_nand)
+ {
+@@ -868,6 +1300,7 @@ static int spinand_probe(struct spi_device *spi_nand)
+ 	struct nand_chip *chip;
+ 	struct spinand_info *info;
+ 	struct spinand_state *state;
++	int rc;
+
+ 	info  = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_info),
+ 			     GFP_KERNEL);
+@@ -903,19 +1336,19 @@ static int spinand_probe(struct spi_device *spi_nand)
+ 	chip->ecc.strength = 1;
+ 	chip->ecc.total	= chip->ecc.steps * chip->ecc.bytes;
+ 	chip->ecc.read_page = spinand_read_page_hwecc;
++	chip->ecc.read_page_raw = spinand_read_page_raw;
+ 	chip->ecc.write_page = spinand_write_page_hwecc;
+ #else
+ 	chip->ecc.mode	= NAND_ECC_SOFT;
+ 	chip->ecc.algo	= NAND_ECC_HAMMING;
+ 	if (spinand_disable_ecc(spi_nand) < 0)
+-		dev_info(&spi_nand->dev, "%s: disable ecc failed!\n",
+-			 __func__);
++		pr_info("%s: disable ecc failed!\n", __func__);
+ #endif
+
+ 	nand_set_flash_node(chip, spi_nand->dev.of_node);
+ 	nand_set_controller_data(chip, info);
+-	chip->read_buf	= spinand_read_buf;
+ 	chip->write_buf	= spinand_write_buf;
++	chip->read_buf = spinand_read_buf;
+ 	chip->read_byte	= spinand_read_byte;
+ 	chip->cmdfunc	= spinand_cmdfunc;
+ 	chip->waitfunc	= spinand_wait;
+@@ -926,22 +1359,24 @@ static int spinand_probe(struct spi_device *spi_nand)
+
+ 	mtd = nand_to_mtd(chip);
+
++#ifdef CONFIG_MTD_SPINAND_ONDIEECC
++	mtd_set_ooblayout(mtd, &spinand_oob_64_ops);
++#endif
+ 	dev_set_drvdata(&spi_nand->dev, mtd);
+
++	mtd->priv = chip;
+ 	mtd->dev.parent = &spi_nand->dev;
+ 	mtd->oobsize = 64;
+-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+-	mtd_set_ooblayout(mtd, &spinand_oob_64_ops);
+-#endif
+
+-	if (nand_scan(mtd, 1))
+-		return -ENXIO;
++	rc = nand_scan_with_ids(mtd, 1, spinand_flash_ids);
++	if (rc)
++		return rc;
+
+ 	return mtd_device_register(mtd, NULL, 0);
+ }
+
+ /**
+- * spinand_remove - remove the device driver
++ * spinand_remove - Remove the device driver
+  * @spi: the spi device.
+  *
+  * Description:
+diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.h b/drivers/staging/mt29f_spinand/mt29f_spinand.h
+index 457dc7ff..19f5a97f 100644
+--- a/drivers/staging/mt29f_spinand/mt29f_spinand.h
++++ b/drivers/staging/mt29f_spinand/mt29f_spinand.h
+@@ -36,6 +36,7 @@
+ #define CMD_RESET			0xff
+ #define CMD_READ_REG			0x0f
+ #define CMD_WRITE_REG			0x1f
++#define CMD_DIE_SELECT			0xC2
+
+ /* feature/ status reg */
+ #define REG_BLOCK_LOCK			0xa0
+@@ -58,6 +59,17 @@
+ #define STATUS_ECC_ERROR		BIT(5)
+ #define STATUS_ECC_RESERVED		(BIT(5) | BIT(4))
+
++#define STATUS_ECC_MASK_GIGA		0x70
++#define STATUS_ECC_ERROR_GIGA		0x70
++#define STATUS_ECC_BF_THRESHOLD_GIGA	0x40
++#define STATUS_ECC_MASK_MACRONIX	0x30
++#define STATUS_ECC_ERROR_MACRONIX	0x20
++#define STATUS_ECC_MASK_TOSHIBA		0x30
++#define STATUS_ECC_ERROR_TOSHIBA	0x20
++#define STATUS_ECC_BF_THRESHOLD_TOSHIBA	0x30
++#define SPINAND_ECC_ERROR		0x1
++#define SPINAND_ECC_CORRECTED		0x2
++
+ /*ECC enable defines*/
+ #define OTP_ECC_MASK			0x10
+ #define OTP_ECC_OFF			0
+@@ -77,9 +89,43 @@
+ #define BL_1_64_LOCKED     0x08
+ #define BL_ALL_UNLOCKED    0
+
++struct spinand_cmd {
++	u8		cmd;
++	u32		n_addr;		/* Number of address */
++	u8		addr[3];	/* Reg Offset */
++	u32		n_dummy;	/* Dummy use */
++	u32		n_tx;		/* Number of tx bytes */
++	u8		*tx_buf;	/* Tx buf */
++	u32		n_rx;		/* Number of rx bytes */
++	u8		*rx_buf;	/* Rx buf */
++};
++
++struct spinand_ops {
++	u8   maf_id;
++	u8   no_of_dies;
++	u16   dev_id;
++	int   prev_die_id;
++	u64   pages_per_die;
++	void (*spinand_set_defaults)(struct spi_device *spi_nand);
++	void (*spinand_read_cmd)(struct spinand_cmd *cmd, u32 page_id);
++	void (*spinand_read_data)(struct spinand_cmd *cmd, u16 column,
++				  u32 page_id);
++	void (*spinand_write_cmd)(struct spinand_cmd *cmd, u32 page_id);
++	void (*spinand_write_data)(struct spinand_cmd *cmd, u16 column,
++				   u32 page_id);
++	void (*spinand_erase_blk)(struct spinand_cmd *cmd, u32 page_id);
++	int (*spinand_parse_id)(struct spi_device *spi_nand,
++				struct spinand_ops *ops, u8 *nand_id, u8 *id);
++	int (*spinand_verify_ecc)(u8 status);
++	int (*spinand_die_select)(struct spi_device *spi_nand,
++				  struct spinand_ops *dev_ops, u8 die_id);
++};
++
+ struct spinand_info {
++	struct nand_ecclayout *ecclayout;
+ 	struct spi_device *spi;
+ 	void *priv;
++	struct spinand_ops *dev_ops;
+ };
+
+ struct spinand_state {
+@@ -89,17 +135,6 @@ struct spinand_state {
+ 	u8		*buf;
+ };
+
+-struct spinand_cmd {
+-	u8		cmd;
+-	u32		n_addr;		/* Number of address */
+-	u8		addr[3];	/* Reg Offset */
+-	u32		n_dummy;	/* Dummy use */
+-	u32		n_tx;		/* Number of tx bytes */
+-	u8		*tx_buf;	/* Tx buf */
+-	u32		n_rx;		/* Number of rx bytes */
+-	u8		*rx_buf;	/* Rx buf */
+-};
+-
+ int spinand_mtd(struct mtd_info *mtd);
+ void spinand_mtd_release(struct mtd_info *mtd);
+
+diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
+index efb23453..c7a02210 100644
+--- a/include/linux/mtd/rawnand.h
++++ b/include/linux/mtd/rawnand.h
+@@ -1438,7 +1438,7 @@ static inline void *nand_get_manufacturer_data(struct nand_chip *chip)
+ #define NAND_MFR_INTEL		0x89
+ #define NAND_MFR_ATO		0x9b
+ #define NAND_MFR_WINBOND	0xef
+-
++#define NAND_MFR_GIGA		0xc8
+
+ /*
+  * A helper for defining older NAND chips where the second ID byte fully
-- 
2.23.0


_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list