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

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

Reply via email to