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

Signed-off-by: Daniel Danzberger <dan...@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@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to