This patch adds support for Gigadevice SPI NAND device to the mt29f stagging driver.
Signed-off-by: Daniel Danzberger <dan...@dd-wrt.com> --- ...port-gigadevice-nandspi-flash-device.patch | 1778 +++++++++++++++++ 1 file changed, 1778 insertions(+) create mode 100644 target/linux/ipq40xx/patches-4.19/400-mtd-nand-support-gigadevice-nandspi-flash-device.patch diff --git a/target/linux/ipq40xx/patches-4.19/400-mtd-nand-support-gigadevice-nandspi-flash-device.patch b/target/linux/ipq40xx/patches-4.19/400-mtd-nand-support-gigadevice-nandspi-flash-device.patch new file mode 100644 index 0000000000..a3b98cd275 --- /dev/null +++ b/target/linux/ipq40xx/patches-4.19/400-mtd-nand-support-gigadevice-nandspi-flash-device.patch @@ -0,0 +1,1778 @@ +diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c +index 5423c3bb..31aac787 100644 +--- a/drivers/mtd/nand/raw/nand_ids.c ++++ b/drivers/mtd/nand/raw/nand_ids.c +@@ -185,6 +185,7 @@ static const struct nand_manufacturer nand_manufacturers[] = { + {NAND_MFR_INTEL, "Intel"}, + {NAND_MFR_ATO, "ATO"}, + {NAND_MFR_WINBOND, "Winbond"}, ++ {NAND_MFR_GIGA, "Gigadevice"}, + }; + + /** +diff --git a/drivers/staging/mt29f_spinand/Kconfig b/drivers/staging/mt29f_spinand/Kconfig +index f3f9cb3b..139c058c 100644 +--- a/drivers/staging/mt29f_spinand/Kconfig ++++ b/drivers/staging/mt29f_spinand/Kconfig +@@ -14,3 +14,13 @@ config MTD_SPINAND_ONDIEECC + help + Internal ECC. + Enables Hardware ECC support for Micron SPI NAND. ++ ++config MTD_SPINAND_GIGADEVICE ++ tristate "SPINAND Devcie Support for Gigadevice " ++ depends on MTD_SPINAND_MT29F ++ help ++ This enables support for accessing Gigadevice SPI NAND flash ++ devices. ++ If you have Gigadevice SPI NAND chip say yes. ++ ++ If unsure, say no here. +diff --git a/drivers/staging/mt29f_spinand/Makefile b/drivers/staging/mt29f_spinand/Makefile +index e47af0f7..36df11e6 100644 +--- a/drivers/staging/mt29f_spinand/Makefile ++++ b/drivers/staging/mt29f_spinand/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand.o ++obj-$(CONFIG_MTD_SPINAND_GIGADEVICE) += giga_spinand.o +diff --git a/drivers/staging/mt29f_spinand/giga_spinand.c b/drivers/staging/mt29f_spinand/giga_spinand.c +new file mode 100644 +index 00000000..a619e96d +--- /dev/null ++++ b/drivers/staging/mt29f_spinand/giga_spinand.c +@@ -0,0 +1,396 @@ ++/* Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/mtd/rawnand.h> ++#include <linux/spi/spi.h> ++#include "giga_spinand.h" ++ ++/* Only ecc un-protected fields in the spare area included */ ++static int winbond_ooblayout_64_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) { ++ if (section > 3) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 8; ++ oobregion->length = 9; ++ return 0; ++} ++ ++static int winbond_ooblayout_64_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) { ++ if (section > 3) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 2; ++ oobregion->length = 2; ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops winbond_ooblayout = { ++ .ecc = winbond_ooblayout_64_ecc, ++ .free = winbond_ooblayout_64_free, ++}; ++ ++static int ath79_ooblayout_64_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) { ++ if (section > 7) ++ return -ERANGE; ++ switch(section) { ++ case 0: ++ oobregion->offset = 64; ++ oobregion->length = 8; ++ break; ++ case 1: ++ oobregion->offset = 72; ++ oobregion->length = 8; ++ break; ++ case 2: ++ oobregion->offset = 80; ++ oobregion->length = 8; ++ break; ++ case 3: ++ oobregion->offset = 88; ++ oobregion->length = 8; ++ break; ++ case 4: ++ oobregion->offset = 96; ++ oobregion->length = 8; ++ break; ++ case 5: ++ oobregion->offset = 104; ++ oobregion->length = 8; ++ break; ++ case 6: ++ oobregion->offset = 112; ++ oobregion->length = 8; ++ break; ++ case 7: ++ oobregion->offset = 120; ++ oobregion->length = 8; ++ break; ++ } ++ return 0; ++} ++ ++static int ath79_ooblayout_64_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) { ++ if (section > 2) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16); ++ oobregion->length = 3; ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops ath79_ooblayout = { ++ .ecc = ath79_ooblayout_64_ecc, ++ .free = ath79_ooblayout_64_free, ++}; ++ ++ ++/* Only ecc un-protected fields in the spare area included */ ++/* ECC parity code stored in the additional hidden spare area */ ++static int macronix_ooblayout_64_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) { ++ return -ERANGE; ++} ++ ++static int macronix_ooblayout_64_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) { ++ if (section > 3) ++ return -ERANGE; ++ ++ oobregion->offset = (section * 16) + 2; ++ oobregion->length = 2; ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops macronix_ooblayout = { ++ .ecc = macronix_ooblayout_64_ecc, ++ .free = macronix_ooblayout_64_free, ++}; ++ ++void gigadevice_set_defaults_128mb(struct spi_device *spi_nand) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata ++ (&spi_nand->dev); ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ ++ chip->ecc.size = 0x800; ++ chip->ecc.bytes = 0x0; ++ chip->ecc.steps = 0x0; ++ ++ chip->ecc.strength = 1; ++ chip->ecc.total = 0; ++ mtd_set_ooblayout(mtd, &ath79_ooblayout); ++} ++ ++void gigadevice_set_defaults(struct spi_device *spi_nand) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata ++ (&spi_nand->dev); ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ ++ chip->ecc.size = 0x800; ++ chip->ecc.bytes = 0x0; ++ chip->ecc.steps = 0x0; ++ ++ chip->ecc.strength = 1; ++ chip->ecc.total = 0; ++} ++ ++void gigadevice_set_defaults_512mb(struct spi_device *spi_nand) ++{ ++ struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata ++ (&spi_nand->dev); ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ ++ chip->ecc.size = 0x1000; ++ chip->ecc.bytes = 0x0; ++ chip->ecc.steps = 0x0; ++ ++ chip->ecc.strength = 1; ++ chip->ecc.total = 0; ++} ++ ++void winbond_set_defaults(struct spi_device *spi_nand) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(&spi_nand->dev); ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ ++ chip->ecc.size = 0x800; ++ chip->ecc.bytes = 0x0; ++ chip->ecc.steps = 0x0; ++ ++ chip->ecc.strength = 1; ++ chip->ecc.total = 0; ++ mtd_set_ooblayout(mtd, &winbond_ooblayout); ++} ++ ++void macronix_set_defaults(struct spi_device *spi_nand) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(&spi_nand->dev); ++ struct nand_chip *chip = (struct nand_chip *)mtd->priv; ++ ++ chip->ecc.size = 0x800; ++ chip->ecc.bytes = 0x0; ++ chip->ecc.steps = 0x0; ++ ++ chip->ecc.strength = 1; ++ chip->ecc.total = 0; ++ mtd_set_ooblayout(mtd, ¯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@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel