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