diff mbox series

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

Message ID 20191030112726.13106-2-daniel@dd-wrt.com
State Changes Requested
Headers show
Series ipq40xx: Add new device Compex WPJ419 | expand

Commit Message

Daniel Danzberger Oct. 30, 2019, 11:27 a.m. UTC
This patch adds support for Gigadevice SPI NAND device to the mt29f stagging driver.

Signed-off-by: Daniel Danzberger <daniel@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

Comments

Robert Marko Oct. 30, 2019, 11:35 a.m. UTC | #1
On Wed, 30 Oct 2019 at 12:28, Daniel Danzberger <daniel@dd-wrt.com> wrote:
>
> This patch adds support for Gigadevice SPI NAND device to the mt29f stagging driver.
Which model of SPI NAND does this board use?
MT29F was removed in 4.21 and should not be used as upstream SPI-NAND
framework replaced it in vendor agnostic way.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/staging?h=v5.4-rc5&id=647ad49ca672b80a3fbf8396bd453ef68ba4916c
OpenWrt has backported support for all SPI-NAND drivers from 5.2
kernel, so please use SPI-NAND instead.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/mtd/nand/spi?h=v5.4-rc5
>
> Signed-off-by: Daniel Danzberger <daniel@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
Jeff Kletsky Oct. 30, 2019, 3:15 p.m. UTC | #2
On 10/30/19 4:27 AM, Daniel Danzberger wrote:

> This patch adds support for Gigadevice SPI NAND device to the mt29f stagging driver.
>
> Signed-off-by: Daniel Danzberger <daniel@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
>
>
> [...]


Is there a reason why the upstream SPI-NAND framework isn't being used?

This has been demanded of the ath79 target (see, for example
https://github.com/openwrt/openwrt/pull/1428#issuecomment-441594401)

and is available to the ipq40xx target and appears to already be in use by:

   qcom-ipq4018-rt-ac58u.dts
   qcom-ipq4018-ea6350v3.dts
   qcom-ipq4018-ap120c-ac.dts
   qcom-ipq4018-jalapeno.dts



Further, the mt29f driver has been removed from upstream Linux

commit 647ad49ca672
Author: Boris Brezillon <boris.brezillon@bootlin.com>
Date:   Mon Oct 22 22:10:59 2018 +0200

     staging: Remove the mt29f_spinand driver

     A new SPI NAND subsystem has been added in drivers/mtd/nand/spi/ and
     Micron's MT29F devices are now supported in
     drivers/mtd/nand/spi/micron.c.

     Remove the old driver.




The set of SPI-NAND chips supported by Linux 5.3 has already been
backported to OpenWrt `master`, including; GigaDevice, Macronix,
Micron, Paragon, Toshiba, and Winbond.


commit b9d58f7e06
Author: Jeff Kletsky <git-commits@allycomm.com>
Date:   Thu Oct 24 09:54:11 2019 -0700

     kernel: mtd: spinand: Backport chip definitions

     generic: Add/rename patches for upstream consistency

     ipq40xx: generic-level patch replaces same-source patches-4.19/
082-v4.20-mtd-spinand-winbond-Add-support-for-W25N01GV.patch

     The SPI-NAND framework from Linux uses common driver code that is then
     "tuned" by a tiny struct of chip-specific data that describes
     available commands, timing, and layout (data and OOB data). Several
     manufacturers and chips have been added since 4.19, several of which
     are used in devices already supported by OpenWrt (typically with no or
     "legacy" access to their NAND memory). This commit catches up the
     supported-chip definitions through Linux 5.2-rc6 and linux/next.

     The driver is only compiled for platforms with CONFIG_MTD_SPI_NAND=y.
     This presently includes ipq40xx and pistachio, with the addition of
     ath79-nand in these commits (and not ath79-generic or ath79-tiny).

     Upstream patches refreshed against 4.19.75

     Build-tested-on: ipq40xx
     Run-tested-on: ath79-nand
Daniel Danzberger Oct. 30, 2019, 3:21 p.m. UTC | #3
> Is there a reason why the upstream SPI-NAND framework isn't being used?

No, I didn't know about it. The patches I used are copied from a compex openwrt
tree and have only been modified by me to run on kernel 4.19

I am going to checkout the new API and try to make the device work with it.

On 10/30/19 4:15 PM, Jeff Kletsky wrote:
> On 10/30/19 4:27 AM, Daniel Danzberger wrote:
> 
>> This patch adds support for Gigadevice SPI NAND device to the mt29f stagging
>> driver.
>>
>> Signed-off-by: Daniel Danzberger <daniel@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
>>
>>
>>
>> [...]
> 
> 
> Is there a reason why the upstream SPI-NAND framework isn't being used?
> 
> This has been demanded of the ath79 target (see, for example
> https://github.com/openwrt/openwrt/pull/1428#issuecomment-441594401)
> 
> and is available to the ipq40xx target and appears to already be in use by:
> 
>   qcom-ipq4018-rt-ac58u.dts
>   qcom-ipq4018-ea6350v3.dts
>   qcom-ipq4018-ap120c-ac.dts
>   qcom-ipq4018-jalapeno.dts
> 
> 
> 
> Further, the mt29f driver has been removed from upstream Linux
> 
> commit 647ad49ca672
> Author: Boris Brezillon <boris.brezillon@bootlin.com>
> Date:   Mon Oct 22 22:10:59 2018 +0200
> 
>     staging: Remove the mt29f_spinand driver
> 
>     A new SPI NAND subsystem has been added in drivers/mtd/nand/spi/ and
>     Micron's MT29F devices are now supported in
>     drivers/mtd/nand/spi/micron.c.
> 
>     Remove the old driver.
> 
> 
> 
> 
> The set of SPI-NAND chips supported by Linux 5.3 has already been
> backported to OpenWrt `master`, including; GigaDevice, Macronix,
> Micron, Paragon, Toshiba, and Winbond.
> 
> 
> commit b9d58f7e06
> Author: Jeff Kletsky <git-commits@allycomm.com>
> Date:   Thu Oct 24 09:54:11 2019 -0700
> 
>     kernel: mtd: spinand: Backport chip definitions
> 
>     generic: Add/rename patches for upstream consistency
> 
>     ipq40xx: generic-level patch replaces same-source patches-4.19/
> 082-v4.20-mtd-spinand-winbond-Add-support-for-W25N01GV.patch
> 
>     The SPI-NAND framework from Linux uses common driver code that is then
>     "tuned" by a tiny struct of chip-specific data that describes
>     available commands, timing, and layout (data and OOB data). Several
>     manufacturers and chips have been added since 4.19, several of which
>     are used in devices already supported by OpenWrt (typically with no or
>     "legacy" access to their NAND memory). This commit catches up the
>     supported-chip definitions through Linux 5.2-rc6 and linux/next.
> 
>     The driver is only compiled for platforms with CONFIG_MTD_SPI_NAND=y.
>     This presently includes ipq40xx and pistachio, with the addition of
>     ath79-nand in these commits (and not ath79-generic or ath79-tiny).
> 
>     Upstream patches refreshed against 4.19.75
> 
>     Build-tested-on: ipq40xx
>     Run-tested-on: ath79-nand
> 
> 
> 
>
diff mbox series

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