ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/uboot/drivers/mtd/spi-flash/spi_nand.c b/marvell/uboot/drivers/mtd/spi-flash/spi_nand.c
new file mode 100644
index 0000000..f1af3d9
--- /dev/null
+++ b/marvell/uboot/drivers/mtd/spi-flash/spi_nand.c
@@ -0,0 +1,2917 @@
+#include <common.h>
+#include <malloc.h>
+#include <linux/mtd/mtd.h>
+#include <asm-generic/errno.h>
+#include <mtd/pxa3xx_bbm.h>
+#include <linux/mtd/bbm.h>
+#include <linux/mtd/nand.h>
+#include <ubi_uboot.h>
+#include <spi_flash_chip.h>
+#include <spi_nand.h>
+
+#define OOB_REQ_ALIGN_TO_SPARE_LEN
+
+static void generic_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void mxic_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void mxic2_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void micron_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void gd_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void gd_spi_nand_ecc_status2(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void xtx_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void xtx2_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void yxsc_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void fm_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void wb_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static void wb_spi_nand_ecc_status2(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error);
+static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo);
+
+/**
+* Default OOB area specification layout
+*/
+static struct nand_ecclayout ecc_layout_64 = {
+ .eccbytes = 32,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 24, 25, 26, 27, 28, 29, 30, 21,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 56, 57, 58, 59, 60, 61, 62, 63},
+ .oobavail = 30,
+ .oobfree = {
+ {.offset = 2,
+ .length = 6},
+ {.offset = 16,
+ .length = 8},
+ {.offset = 32,
+ .length = 8},
+ {.offset = 48,
+ .length = 8}, }
+};
+
+static struct nand_ecclayout wb_ecc_layout_64 = {
+ .eccbytes = 0,
+ .oobavail = 62,
+ .oobfree = {
+ {.offset = 2,
+ .length = 62},}
+};
+
+static struct nand_ecclayout ecc_layout_128 = {
+ .eccbytes = 64,
+ .eccpos = {
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127},
+ .oobavail = 62,
+ .oobfree = {
+ {.offset = 2,
+ .length = 62}, }
+};
+
+static struct nand_ecclayout xtx_ecc_layout_128 = {
+ .eccbytes = 52,
+ .eccpos = {
+ 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63},
+ .oobavail = 74,
+ .oobfree = {
+ {.offset = 2,
+ .length = 4},
+ {.offset = 19,
+ .length = 2},
+ {.offset = 34,
+ .length = 2},
+ {.offset = 49,
+ .length = 2},
+ {.offset = 64,
+ .length = 64}, }
+};
+
+enum {
+ GET_FEATURE,
+ SET_FEATURE,
+ PAGE_READ,
+ READ_PAGE_CACHE_RDM,
+ READ_PAGE_CACHE_LAST,
+ READ_FROM_CACHE,
+ READ_FROM_CACHE_FAST,
+ READ_FROM_CACHE_X2,
+ READ_FROM_CACHE_DUAL,
+ READ_FROM_CACHE_X4,
+ READ_FROM_CACHE_QUAD,
+ READ_FROM_CACHE_DTR,
+ BLK_ERASE,
+ PROG_EXC,
+ PROG_LOAD,
+ PROG_LOAD_RDM_DATA,
+ PROG_LOAD_X4,
+ PROG_LOAD_RDM_DATA_X4,
+ WR_ENABLE,
+ WR_DISABLE,
+ READ_ID,
+ RESET,
+ READ_FROM_CACHE_QUAD_GD,
+ READ_FROM_CACHE_QUAD_GD2,
+ READ_ECCSR_MXIC,
+ MAX_CMD,
+};
+
+static struct spi_nand_info spi_nand_table[] = {
+ /* Macronix */
+ SPI_NAND_INFO("MX35UF1GE4AC", 0xC2, 0x92, 2048, 64, 64, 1024,
+ 1, 4, SPINAND_NEED_SET_BFT, 3, 0,
+ READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, mxic_spi_nand_ecc_status),
+ SPI_NAND_INFO("MX35UF2GE4AC", 0xC2, 0xA2, 2048, 64, 64, 2048,
+ 1, 4, SPINAND_NEED_SET_BFT, 3, 0,
+ READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, mxic_spi_nand_ecc_status),
+ SPI_NAND_INFO("MX35UF1GE4AD", 0xC2, 0x96, 2048, 128, 64, 1024,
+ 1, 8, SPINAND_NEED_SET_BFT, 6, 0,
+ READ_FROM_CACHE_QUAD,
+ &ecc_layout_128, mxic2_spi_nand_ecc_status),
+ SPI_NAND_INFO("MX35UF2GE4AD", 0xC2, 0xA6, 2048, 128, 64, 2048,
+ 1, 8, SPINAND_NEED_SET_BFT, 6, 0,
+ READ_FROM_CACHE_QUAD,
+ &ecc_layout_128, mxic2_spi_nand_ecc_status),
+
+ /* GigaDeivce 1.8V */
+ SPI_NAND_INFO("GD5F1GQ4RBxIG", 0xC8, 0xC1, 2048, 128, 64, 1024,
+ 1, 8, 0, 6, 0, READ_FROM_CACHE_QUAD_GD,
+ &ecc_layout_128, gd_spi_nand_ecc_status),
+ SPI_NAND_INFO("GD5F2GQ4RBxIG", 0xC8, 0xC2, 2048, 128, 64, 2048,
+ 1, 8, 0, 6, 0, READ_FROM_CACHE_QUAD_GD,
+ &ecc_layout_128, gd_spi_nand_ecc_status),
+ SPI_NAND_INFO("GD5F1GQ5RExxG", 0xC8, 0x41, 2048, 128, 64, 1024,
+ 1, 4, 0, 3, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_128, gd_spi_nand_ecc_status2),
+ SPI_NAND_INFO("GD5F2GQ5RExxG", 0xC8, 0x42, 2048, 128, 64, 2048,
+ 1, 4, 0, 3, 0, READ_FROM_CACHE_QUAD_GD2,
+ &ecc_layout_128, gd_spi_nand_ecc_status2),
+#if 1
+ SPI_NAND_INFO_DTR("GD5F1GM7RE", 0xC8, 0x81, 2048, 128, 64, 1024,
+ 1, 8, SPINAND_SUPPORT_DTR, 6, 9, 2, 2, 80,
+ READ_FROM_CACHE_QUAD, READ_FROM_CACHE_DTR,
+ &ecc_layout_128, gd_spi_nand_ecc_status),
+#else
+ SPI_NAND_INFO_TIMING("GD5F1GM7RE", 0xC8, 0x81, 2048, 128, 64, 1024,
+ 1, 8, 0, 6, 9, 2, 2, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_128, gd_spi_nand_ecc_status),
+#endif
+ SPI_NAND_INFO("GD5F2GM7RE", 0xC8, 0x82, 2048, 128, 64, 2048,
+ 1, 8, 0, 6, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_128, gd_spi_nand_ecc_status),
+ SPI_NAND_INFO_TIMING("GD5F4GQ6RExxG", 0xC8, 0x45, 2048, 128, 64, 2048,
+ 2, 4, 0, 3, 11, 2, 2, 0, READ_FROM_CACHE_QUAD_GD2,
+ &ecc_layout_128, gd_spi_nand_ecc_status2),
+ SPI_NAND_INFO("GD5F4GM8RExxG", 0xC8, 0x85, 2048, 128, 64, 4096,
+ 1, 8, 0, 6, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_128, gd_spi_nand_ecc_status),
+
+ /* GigaDeivce 3.3V */
+ SPI_NAND_INFO("GD5F1GQ4UExxG", 0xC8, 0xD1, 2048, 128, 64, 1024,
+ 1, 8, 0, 6, 0, READ_FROM_CACHE_QUAD_GD,
+ &ecc_layout_128, gd_spi_nand_ecc_status),
+
+ /* Winbond */
+ SPI_NAND_INFO("W25N512GWxxR/T", 0xEF, 0x20BA, 2048, 64, 64, 512,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("W25N01GWxxIx", 0xEF, 0x21BA, 2048, 64, 64, 1024,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("W25N01KW", 0xEF, 0x21BE, 2048, 64, 64, 1024,
+ 1, 4, 0, 3, 0, READ_FROM_CACHE_QUAD,
+ &wb_ecc_layout_64, wb_spi_nand_ecc_status),
+ SPI_NAND_INFO("W25N02KW", 0xEF, 0x22BA, 2048, 64, 64, 2048,
+ 1, 8, 0, 6, 0, READ_FROM_CACHE_QUAD,
+ &wb_ecc_layout_64, wb_spi_nand_ecc_status2),
+ SPI_NAND_INFO("W25M02GWxxIx", 0xEF, 0x21BB, 2048, 64, 64, 2048,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("W25M01JW", 0xEF, 0x21BC, 2048, 64, 64, 1024,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+
+ /* Dosilicon */
+ SPI_NAND_INFO("DS35M1GAxx", 0xE5, 0x21, 2048, 64, 64, 1024,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_X4,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("DS35M2GAxx", 0xE5, 0x22, 2048, 64, 64, 2048,
+ 1, 4, SPINAND_NEED_PLANE_SELECT, 4, 0,
+ READ_FROM_CACHE_X4,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("DS35M2GBxx", 0xE5, 0xA2, 2048, 128, 64, 2048,
+ 1, 8, SPINAND_NEED_PLANE_SELECT, 4, 83,
+ READ_FROM_CACHE_X4,
+ &ecc_layout_128, micron_spi_nand_ecc_status),
+ SPI_NAND_INFO("DS35M4GMxx", 0xE5, 0xA4, 2048, 128, 64, 4096,
+ 1, 8, SPINAND_NEED_PLANE_SELECT, 4, 83,
+ READ_FROM_CACHE_X4,
+ &ecc_layout_128, micron_spi_nand_ecc_status),
+ SPI_NAND_INFO("DS35M4GMxx", 0xE5, 0xA4, 2048, 128, 64, 4096,
+ 1, 8, SPINAND_NEED_PLANE_SELECT, 4, 83,
+ READ_FROM_CACHE_X4,
+ &ecc_layout_128, micron_spi_nand_ecc_status),
+
+ /* ZettaDevice */
+ SPI_NAND_INFO("ZD35M1GAxx", 0xBA, 0x21, 2048, 64, 64, 1024,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_X4,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+
+ /* XTX */
+ SPI_NAND_INFO("PN26Q01AWSIUG", 0xA1, 0xC1, 2048, 128, 64, 1024,
+ 1, 8, SPINAND_RDM_CMD_NEED_PAGE_READ | SPINAND_ECC_EN_ADDR_90H,
+ 8, 52, READ_FROM_CACHE_X4,
+ &xtx_ecc_layout_128, xtx_spi_nand_ecc_status),
+ SPI_NAND_INFO("XT26Q01D-BE", 0x0B, 0x51, 2048, 128, 64, 1024,
+ 1, 8, 0, 5, 0, READ_FROM_CACHE_QUAD_GD,
+ &xtx_ecc_layout_128, xtx2_spi_nand_ecc_status),
+ SPI_NAND_INFO("XT26Q02D", 0x0B, 0x52, 2048, 128, 64, 2048,
+ 1, 8, 0, 5, 0, READ_FROM_CACHE_QUAD_GD,
+ &ecc_layout_128, xtx2_spi_nand_ecc_status),
+ SPI_NAND_INFO("XT26Q02E", 0x2C, 0x25, 2048, 128, 64, 2048,
+ 1, 8, SPINAND_NEED_PLANE_SELECT, 4, 52,
+ READ_FROM_CACHE_QUAD,
+ &ecc_layout_128, micron_spi_nand_ecc_status),
+
+ /* YXSC */
+ SPI_NAND_INFO("TX25G01", 0xA1, 0xF1, 2048, 64, 64, 1024,
+ 1, 4, SPINAND_RDM_CMD_NEED_PAGE_READ | SPINAND_ECC_EN_ADDR_90H,
+ 2, 0, READ_FROM_CACHE_QUAD_GD,
+ &ecc_layout_64, yxsc_spi_nand_ecc_status),
+ /* ESMT */
+ SPI_NAND_INFO("F50D1G41LB", 0xC8, 0x11, 2048, 128, 64, 1024,
+ 1, 7, 0, 4, 52, READ_FROM_CACHE_X4,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+
+ /* Fudan Microelectronics */
+ SPI_NAND_INFO("FM25LS01", 0xA1, 0xA5, 2048, 128, 64, 1024,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_X4,
+ &ecc_layout_128, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("FM25LG02B", 0xA1, 0xB2, 2048, 128, 64, 2048,
+ 1, 8, 0, 4, 0, READ_FROM_CACHE_X4,
+ &ecc_layout_128, fm_spi_nand_ecc_status),
+ /* Micron */
+ SPI_NAND_INFO("MT29F1G01ABBFD", 0x2C, 0x15, 2048, 64, 64, 2048,
+ 1, 8, SPINAND_NEED_PLANE_SELECT, 4, 0,
+ READ_FROM_CACHE_X4,
+ &ecc_layout_128, micron_spi_nand_ecc_status),
+
+ /* SiliconGo 3.3V */
+ SPI_NAND_INFO("SGM7000I-S24W1GH", 0xEA, 0xC1, 2048, 64, 64, 1024,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+
+ SPI_NAND_INFO("SGM7000I-S25W2GH", 0xEA, 0xC2, 2048, 64, 64, 2048,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("SGM7000I-S25W4GH", 0xEA, 0xC4, 2048, 64, 64, 4096,
+ 1, 4, 0, 4, 0, READ_FROM_CACHE_QUAD,
+ &ecc_layout_64, generic_spi_nand_ecc_status),
+
+ {.name = NULL},
+};
+
+/* Standard SPI-NAND flash commands */
+static struct spi_flash_cmd_cfg cmd_table[] = {
+ /*opcode addr_bytes addr_pins mode_bits mode_pins dummy_cycles
+ dummy_pins data_pins seq_id cmd_type */
+ [GET_FEATURE] = SPI_CMD(0x0f, 1, 1, 0, 0, 0, 0, 1, 2, 1),
+ [SET_FEATURE] = SPI_CMD(0x1f, 1, 1, 0, 0, 0, 0, 1, -1, 2),
+ [PAGE_READ] = SPI_CMD(0x13, 3, 1, 0, 0, 0, 0, 0, 3, 0),
+ [READ_PAGE_CACHE_RDM] = SPI_CMD(0x30, 3, 1, 0, 0, 0, 0, 0, -1, 0),
+ [READ_PAGE_CACHE_LAST] = SPI_CMD(0x3f, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+ [READ_FROM_CACHE] = SPI_CMD(0x03, 2, 1, 0, 0, 8, 1, 1, -1, 1),
+ [READ_FROM_CACHE_FAST] = SPI_CMD(0x0b, 2, 1, 0, 0, 8, 1, 1, -1, 1),
+ [READ_FROM_CACHE_X2] = SPI_CMD(0x3b, 2, 1, 0, 0, 8, 1, 2, -1, 1),
+ [READ_FROM_CACHE_DUAL] = SPI_CMD(0xbb, 2, 2, 0, 0, 4, 2, 2, -1, 1),
+ [READ_FROM_CACHE_X4] = SPI_CMD(0x6b, 2, 1, 0, 0, 8, 1, 4, 4, 1),
+ [READ_FROM_CACHE_QUAD] = SPI_CMD(0xeb, 2, 4, 0, 0, 4, 4, 4, 5, 1),
+ [BLK_ERASE] = SPI_CMD(0xd8, 3, 1, 0, 0, 0, 0, 0, -1, 0),
+ [PROG_EXC] = SPI_CMD(0x10, 3, 1, 0, 0, 0, 0, 0, 6, 0),
+ [PROG_LOAD] = SPI_CMD(0x02, 2, 1, 0, 0, 0, 0, 1, 7, 2),
+ [PROG_LOAD_RDM_DATA] = SPI_CMD(0x84, 2, 1, 0, 0, 0, 0, 1, -1, 2),
+ [PROG_LOAD_X4] = SPI_CMD(0x32, 2, 1, 0, 0, 0, 0, 4, 8, 2),
+ [PROG_LOAD_RDM_DATA_X4] = SPI_CMD(0x34, 2, 1, 0, 0, 0, 0, 4, -1, 2),
+ [WR_ENABLE] = SPI_CMD(0x06, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+ [WR_DISABLE] = SPI_CMD(0x04, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+ [READ_ID] = SPI_CMD(0x9f, 0, 0, 0, 0, 0, 0, 1, -1, 1),
+ [RESET] = SPI_CMD(0xff, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+
+ /* Vendor Specific command */
+ [READ_FROM_CACHE_QUAD_GD] = SPI_CMD(0xeb, 2, 4, 0, 0, 2, 4, 4, 9, 1),
+ [READ_FROM_CACHE_QUAD_GD2] = SPI_CMD(0xeb, 2, 4, 0, 0, 8, 4, 4, 10, 1),
+ [READ_ECCSR_MXIC] = SPI_CMD(0x7c, 0, 0, 0, 0, 8, 1, 1, -1, 1),
+ [READ_FROM_CACHE_DTR] = SPI_CMD_DTR(0xee, 0, 4, 4, 1, 0, 0, 0, 8, 4, 4, 1, 11, 1),
+
+ /* END Mark */
+ [MAX_CMD] = SPI_CMD(0x00, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+};
+
+/**
+ * spi_nand_get_device - [GENERIC] Get chip for selected access
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
+ *
+ * Get the device and lock it for exclusive access
+ */
+static int spi_nand_get_device(struct mtd_info *mtd, int new_state)
+{
+ struct spi_flash_chip *this = mtd->priv;
+
+ this->state = new_state;
+ return 0;
+}
+
+/**
+ * spi_nand_release_device - [GENERIC] release chip
+ * @mtd: MTD device structure
+ *
+ * Deselect, release chip lock and wake up anyone waiting on the device
+ */
+static void spi_nand_release_device(struct mtd_info *mtd)
+{
+ return;
+}
+
+/**
+ * spi_nand_read_reg - send command 0Fh to read register
+ * @chip: SPI_FLASH device structure
+ * @reg; register to read
+ * @buf: buffer to store value
+ */
+static int spi_nand_read_reg(struct spi_flash_chip *chip,
+ u8 reg, u8 *buf)
+{
+ struct spi_flash_cmd cmd;
+ int ret;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + GET_FEATURE;
+ cmd.n_addr = 1;
+ cmd.addr[0] = reg;
+ cmd.n_rx = 1;
+ cmd.rx_buf = buf;
+
+ ret = chip->issue_cmd(chip, &cmd);
+ if (ret < 0)
+ printf("err: %d read register %d\n", ret, reg);
+
+ return ret;
+}
+
+/**
+ * spi_nand_write_reg - send command 1Fh to write register
+ * @chip: SPI-NAND device structure
+ * @reg; register to write
+ * @buf: buffer stored value
+ */
+static int spi_nand_write_reg(struct spi_flash_chip *chip,
+ u8 reg, u8 *buf)
+{
+ struct spi_flash_cmd cmd;
+ int ret;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + SET_FEATURE;
+ cmd.n_addr = 1;
+ cmd.addr[0] = reg;
+ cmd.n_tx = 1;
+ cmd.tx_buf = buf;
+
+ ret = chip->issue_cmd(chip, &cmd);
+ if (ret < 0)
+ printf("err: %d write register %d\n", ret, reg);
+
+ return ret;
+}
+
+/**
+ * spi_nand_read_status - get status register value
+ * @chip: SPI-NAND device structure
+ * @status: buffer to store value
+ * Description:
+ * After read, write, or erase, the Nand device is expected to set the
+ * busy status.
+ * This function is to allow reading the status of the command: read,
+ * write, and erase.
+ * Once the status turns to be ready, the other status bits also are
+ * valid status bits.
+ */
+static int spi_nand_read_status(struct spi_flash_chip *chip, u8 *status)
+{
+ return spi_nand_read_reg(chip, REG_STATUS, status);
+}
+
+/**
+ * spi_nand_get_cfg - get configuration register value
+ * @chip: SPI-NAND device structure
+ * @cfg: buffer to store value
+ * Description:
+ * Configuration register includes OTP config, Lock Tight enable/disable
+ * and Internal ECC enable/disable.
+ */
+static int spi_nand_get_cfg(struct spi_flash_chip *chip, u8 *cfg)
+{
+ return spi_nand_read_reg(chip, REG_CFG, cfg);
+}
+
+/**
+ * spi_nand_set_cfg - set value to configuration register
+ * @chip: SPI-NAND device structure
+ * @cfg: buffer stored value
+ * Description:
+ * Configuration register includes OTP config, Lock Tight enable/disable
+ * and Internal ECC enable/disable.
+ */
+static int spi_nand_set_cfg(struct spi_flash_chip *chip, u8 *cfg)
+{
+ return spi_nand_write_reg(chip, REG_CFG, cfg);
+}
+
+/**
+ * spi_nand_enable_ecc - enable internal ECC
+ * @chip: SPI-NAND device structure
+ * Description:
+ * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ * Enable chip internal ECC, set the bit to 1
+ * Disable chip internal ECC, clear the bit to 0
+ */
+static int spi_nand_enable_ecc(struct spi_flash_chip *chip)
+{
+ u8 cfg = 0;
+
+ /* For XTX spi-nand, ECC_EN locate in 0x90 feature register */
+ if (chip->options & SPINAND_ECC_EN_ADDR_90H)
+ spi_nand_read_reg(chip, 0x90, &cfg);
+ else
+ spi_nand_get_cfg(chip, &cfg);
+
+ if ((cfg & CFG_ECC_MASK) == CFG_ECC_ENABLE)
+ return 0;
+
+ cfg |= CFG_ECC_ENABLE;
+
+ if (chip->options & SPINAND_ECC_EN_ADDR_90H)
+ return spi_nand_write_reg(chip, 0x90, &cfg);
+ else
+ return spi_nand_set_cfg(chip, &cfg);
+}
+
+/**
+ * spi_nand_disable_ecc - disable internal ECC
+ * @chip: SPI-NAND device structure
+ * Description:
+ * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ * Enable chip internal ECC, set the bit to 1
+ * Disable chip internal ECC, clear the bit to 0
+ */
+static int spi_nand_disable_ecc(struct spi_flash_chip *chip)
+{
+ u8 cfg = 0;
+
+ /* For XTX spi-nand, ECC_EN locate in 0x90 feature register */
+ if (chip->options & SPINAND_ECC_EN_ADDR_90H)
+ spi_nand_read_reg(chip, 0x90, &cfg);
+ else
+ spi_nand_get_cfg(chip, &cfg);
+
+ if ((cfg & CFG_ECC_MASK) == CFG_ECC_ENABLE) {
+ cfg &= ~CFG_ECC_ENABLE;
+ if (chip->options & SPINAND_ECC_EN_ADDR_90H)
+ return spi_nand_write_reg(chip, 0x90, &cfg);
+ else
+ return spi_nand_set_cfg(chip, &cfg);
+ }
+ return 0;
+}
+
+static int spi_nand_enable_quad(struct spi_flash_chip *chip)
+{
+ u8 cfg = 0;
+
+ spi_nand_get_cfg(chip, &cfg);
+ if ((cfg & CFG_QE_MASK) == CFG_QE_ENABLE)
+ return 0;
+ cfg |= CFG_QE_ENABLE;
+ return spi_nand_set_cfg(chip, &cfg);
+}
+
+/**
+ * generic_spi_nand_ecc_status - decode status regisger to get ecc info
+ * @status: status register value to decode
+ * @corrected: bitflip count that ecc corrected
+ * @ecc_error: uncorrected bitflip happen or not
+ */
+static void generic_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+
+ *ecc_error = 0;
+ switch (ecc_status) {
+ case 0x0:
+ /* No bit error detected */
+ *corrected = 0;
+ break;
+ case 0x1:
+ /* 1~4 error bits detected and corrected */
+ *corrected = 4;
+ break;
+ case 0x2:
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ break;
+ default:
+ printf("%s: unexpected status: %d\n", __func__, ecc_status);
+ *ecc_error = 1;
+ break;
+ }
+}
+
+static int mxic_spi_nand_set_bft(struct spi_flash_chip *chip, u8 threshold)
+{
+ u8 val, bft;
+ int ret;
+
+ ret = spi_nand_read_reg(chip, 0x10, &val);
+ if (ret)
+ return ret;
+
+ bft = (val & 0xf0) >> 4;
+ printf("%s: read BFT=0x%x threshold=%d\r\n", __func__, val, threshold);
+
+ if (bft != threshold) {
+ val = threshold << 4;
+ ret = spi_nand_write_reg(chip, 0x10, &val);
+ if (ret)
+ return ret;
+
+ printf("%s: update BFT=0x%x\r\n", __func__, val);
+ }
+
+ return 0;
+}
+
+static void mxic_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+
+ *ecc_error = 0;
+ switch (ecc_status) {
+ case 0x0:
+ /* No bit error detected */
+ *corrected = 0;
+ break;
+ case 0x1:
+ /* 1~4 error bits detected and corrected */
+ *corrected = 4;
+ break;
+ case 0x2:
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ break;
+ default:
+ *corrected = 4;
+ break;
+ }
+}
+
+static int mxic_spi_nand_read_eccsr(struct spi_flash_chip *chip, u8 *eccsr)
+{
+ struct spi_flash_cmd cmd;
+ int ret;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + READ_ECCSR_MXIC;
+ cmd.n_rx = 1;
+ cmd.rx_buf = eccsr;
+
+ ret = chip->issue_cmd(chip, &cmd);
+ if (ret < 0)
+ pr_info("err: %d read eccsr register\r\n", ret);
+
+ return ret;
+}
+
+static void mxic2_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ u8 eccsr;
+
+ *ecc_error = 0;
+ switch (ecc_status) {
+ case 0x0:
+ /* No bit error detected */
+ *corrected = 0;
+ break;
+ case 0x2:
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ break;
+ case 0x1:
+ /* 1~8 error bits detected and corrected */
+ *corrected = 4;
+ default:
+ mxic_spi_nand_read_eccsr(chip, &eccsr);
+ eccsr &= 0xf;
+ if (eccsr == 0xf)
+ *ecc_error = 1;
+ else
+ *corrected = eccsr;
+ break;
+ }
+}
+
+static void micron_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & 0x70) >> SPI_NAND_ECC_SHIFT;
+
+ *ecc_error = 0;
+ switch (ecc_status) {
+ case 0x0:
+ /* No bit error detected */
+ *corrected = 0;
+ break;
+ case 0x1:
+ /* 1~3 error bits detected and corrected */
+ *corrected = 3;
+ break;
+ case 0x2:
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ break;
+ case 0x3:
+ /* 4~6 error bits detected and corrected */
+ *corrected = 6;
+ break;
+ case 0x5:
+ /* 7~8 error bits detected and corrected */
+ *corrected = 8;
+ break;
+ default:
+ printf("%s: unexpected status: %d\n", __func__, ecc_status);
+ *ecc_error = 1;
+ break;
+ }
+}
+
+static void fm_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & 0x70) >> SPI_NAND_ECC_SHIFT;
+
+ *ecc_error = 0;
+ switch (ecc_status) {
+ case 0x0:
+ /* No bit error detected */
+ *corrected = 0;
+ break;
+ case 0x1:
+ *corrected = 3;
+ break;
+ case 0x2:
+ *corrected = 4;
+ break;
+ case 0x3:
+ *corrected = 5;
+ break;
+ case 0x4:
+ *corrected = 6;
+ break;
+ case 0x5:
+ *corrected = 7;
+ break;
+ case 0x6:
+ *corrected = 8;
+ break;
+ case 0x7:
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ break;
+ default:
+ obm_printf("%s: unexpected status: %d\r\n",
+ __func__, ecc_status);
+ *ecc_error = 1;
+ break;
+ }
+}
+
+static void xtx_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+
+ *ecc_error = 0;
+ switch (ecc_status) {
+ case 0x0:
+ /* No bit error detected */
+ *corrected = 0;
+ break;
+ case 0x1:
+ /* 1~7 error bits detected and corrected */
+ *corrected = 7;
+ break;
+ case 0x2:
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ break;
+ case 0x3:
+ /* 8 error bits detected and corrected */
+ *corrected = 8;
+ break;
+ }
+}
+
+static void xtx2_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ u8 ecc_status2 = (status & 0xf0) >> SPI_NAND_ECC_SHIFT;
+
+ *ecc_error = 0;
+ switch (ecc_status) {
+ case 0x0:
+ /* No bit error detected */
+ *corrected = 0;
+ break;
+ case 0x1:
+ switch (ecc_status2) {
+ case 1:
+ *corrected = 4;
+ break;
+ case 5:
+ *corrected = 5;
+ break;
+ case 9:
+ *corrected = 6;
+ break;
+ case 0xd:
+ *corrected = 7;
+ break;
+ }
+ break;
+ case 0x2:
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ break;
+ case 0x3:
+ /* 8 error bits detected and corrected */
+ *corrected = 8;
+ break;
+ }
+}
+
+static void yxsc_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & 0x70) >> SPI_NAND_ECC_SHIFT;
+
+ *ecc_error = 0;
+ switch (ecc_status) {
+ case 0x0:
+ /* No bit error detected */
+ *corrected = 0;
+ break;
+ case 0x1:
+ /* 1~7 error bits detected and corrected */
+ *corrected = 1;
+ break;
+ case 0x2:
+ /* 2 error bits detected and corrected */
+ *ecc_error = 2;
+ break;
+ case 0x3:
+ /* 3 error bits detected and corrected */
+ *corrected = 3;
+ break;
+ case 0x4:
+ /* 4 error bits detected and corrected */
+ *corrected = 4;
+ break;
+ case 0x7:
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ break;
+ default:
+ printf("%s: unexpected status: %d\r\n",
+ __func__, ecc_status);
+ *ecc_error = 1;
+ break;
+ }
+}
+
+static void gd_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ u8 ext_status;
+ int ret;
+
+ *ecc_error = 0;
+ *corrected = 0;
+ if (ecc_status == 0x1) {
+ ret = spi_nand_read_reg(chip, 0xf0, &ext_status);
+ if (ret) {
+ printf("gd_spi_nand_ecc_status failed\n");
+ return;
+ }
+
+ ext_status = (ext_status & SPI_NAND_ECC_MASK) >>
+ SPI_NAND_ECC_SHIFT;
+ switch (ext_status) {
+ case 0x0:
+ /* 1~4 error bits detected and corrected */
+ *corrected = 4;
+ break;
+ case 0x1:
+ /* 5 error bits detected and corrected */
+ *corrected = 5;
+ break;
+ case 0x2:
+ /* 6 error bits detected and corrected */
+ *corrected = 6;
+ break;
+ case 0x3:
+ /* 7 error bits detected and corrected */
+ *corrected = 7;
+ break;
+ }
+ } else if (ecc_status == 0x2) {
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ } else if (ecc_status == 0x3) {
+ /* 8 error bits detected and corrected */
+ *corrected = 8;
+ } else if (ecc_status == 0x0) {
+ /* No bit error detected */
+ *corrected = 0;
+ }
+}
+
+static void gd_spi_nand_ecc_status2(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ u8 ext_status;
+ int ret;
+
+ *ecc_error = 0;
+ *corrected = 0;
+ if (ecc_status == 0x0) {
+ /* No bit error detected */
+ *corrected = 0;
+ } else if (ecc_status == 0x1) {
+ ret = spi_nand_read_reg(chip, 0xf0, &ext_status);
+ if (ret) {
+ printf("gd_spi_nand_ecc_status failed\n");
+ return;
+ }
+
+ ext_status = (ext_status & SPI_NAND_ECC_MASK) >>
+ SPI_NAND_ECC_SHIFT;
+
+ switch (ext_status) {
+ case 0x0:
+ *corrected = 1;
+ break;
+ case 0x1:
+ *corrected = 2;
+ break;
+ case 0x2:
+ *corrected = 3;
+ break;
+ case 0x3:
+ *corrected = 4;
+ break;
+ }
+ } else if (ecc_status == 0x2) {
+ /* Error bit > 4 detected and can not corrected */
+ *ecc_error = 1;
+ } else {
+ printf("%s: unexpected status: %d\r\n",
+ __func__, ecc_status);
+ *ecc_error = 1;
+ }
+}
+
+static void wb_spi_nand_ecc_status(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ u8 ext_status;
+ int ret;
+
+ *ecc_error = 0;
+ *corrected = 0;
+ if (ecc_status == 0x1) {
+ ret = spi_nand_read_reg(chip, 0x30, &ext_status);
+ if (ret) {
+ printf("wb_spi_nand_ecc_status failed\n");
+ return;
+ }
+
+ ext_status = (ext_status & 0x70) >> 4;
+ if (ext_status == 7)
+ *ecc_error = 1;
+ else
+ *corrected = ext_status;
+ } else if (ecc_status == 0x2) {
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ } else if (ecc_status == 0x3) {
+ /* 4 error bits detected and corrected */
+ *corrected = 4;
+ } else if (ecc_status == 0x0) {
+ /* No bit error detected */
+ *corrected = 0;
+ }
+}
+
+static void wb_spi_nand_ecc_status2(struct spi_flash_chip *chip, u8 status,
+ u32 *corrected, u32 *ecc_error)
+{
+ u8 ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ u8 ext_status;
+ int ret;
+
+ *ecc_error = 0;
+ *corrected = 0;
+ if (ecc_status == 0x1) {
+ ret = spi_nand_read_reg(chip, 0x30, &ext_status);
+ if (ret) {
+ printf("wb_spi_nand_ecc_status2 failed\n");
+ return;
+ }
+
+ ext_status = (ext_status & 0xf0) >> 4;
+ if (ext_status == 0xf)
+ *ecc_error = 1;
+ else
+ *corrected = ext_status;
+ } else if (ecc_status == 0x2) {
+ /* Error detected and can not corrected */
+ *ecc_error = 1;
+ } else if (ecc_status == 0x3) {
+ /* 4 error bits detected and corrected */
+ *corrected = 4;
+ } else if (ecc_status == 0x0) {
+ /* No bit error detected */
+ *corrected = 0;
+ }
+}
+
+/**
+ * spi_nand_write_enable - send command 06h to enable write or erase the
+ * Nand cells
+ * @chip: SPI-NAND device structure
+ * 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
+ */
+static int spi_nand_write_enable(struct spi_flash_chip *chip)
+{
+ struct spi_flash_cmd cmd;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + WR_ENABLE;
+
+ return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nand_set_ds - set value to die select register
+ * @chip: SPI-NAND device structure
+ * @cfg: buffer stored value
+ * Description:
+ * Configuration register includes OTP config, Lock Tight enable/disable
+ * and Internal ECC enable/disable.
+ */
+static int spi_nand_set_ds(struct spi_flash_chip *chip, u8 *ds)
+{
+ return spi_nand_write_reg(chip, REG_DIE_SELECT, ds);
+}
+
+/**
+ * spi_nand_lun_select - send die select command if needed
+ * @chip: SPI-NAND device structure
+ * @lun: lun need to access
+ */
+static int spi_nand_lun_select(struct spi_flash_chip *chip, u8 lun)
+{
+ u8 ds = 0;
+ int ret = 0;
+
+ if (chip->lun != lun) {
+ ds = (lun == 1) ? DIE_SELECT_DS1 : DIE_SELECT_DS0;
+ ret = spi_nand_set_ds(chip, &ds);
+ chip->lun = lun;
+ }
+
+ return ret;
+}
+
+/**
+ * spi_nand_read_page_to_cache - send command 13h to read data from Nand to cache
+ * @chip: SPI-NAND device structure
+ * @page_addr: page to read
+ */
+static int spi_nand_read_page_to_cache(struct spi_flash_chip *chip,
+ u32 page_addr)
+{
+ struct spi_flash_cmd cmd;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + PAGE_READ;
+ cmd.n_addr = 3;
+ cmd.addr[0] = (u8)(page_addr >> 16);
+ cmd.addr[1] = (u8)(page_addr >> 8);
+ cmd.addr[2] = (u8)page_addr;
+ cmd.flag = RST_AHB_DOMAIN | AHB_MAP_SIZE_PAGE;
+
+ return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nand_read_from_cache - read data out from cache register
+ * @chip: SPI-NAND device structure
+ * @page_addr: page to read
+ * @column: the location to read from the cache
+ * @len: number of bytes to read
+ * @rbuf: buffer held @len bytes
+ * Description:
+ * Command can be 03h, 0Bh, 3Bh, 6Bh, BBh, EBh
+ * The read can specify 1 to (page size + spare size) bytes of data read at
+ * the corresponding locations.
+ * No tRd delay.
+ */
+static int spi_nand_read_from_cache(struct spi_flash_chip *chip,
+ u32 page_addr, u32 column,
+ u32 len, u8 *rbuf)
+{
+ struct spi_flash_cmd cmd;
+
+ if (chip->options & SPINAND_NEED_PLANE_SELECT) {
+ column |= (((page_addr >>
+ (chip->block_shift - chip->page_shift)) & 0x1) << 12);
+ }
+
+ if (chip->xip_read && chip->memmap_read) {
+ chip->memmap_read(chip, rbuf, column, len);
+ return 0;
+ }
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + chip->read_cache_op;
+ cmd.n_addr = 2;
+ cmd.addr[0] = (u8)(column >> 8);
+ cmd.addr[1] = (u8)column;
+ cmd.n_rx = len;
+ cmd.rx_buf = rbuf;
+
+ return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nand_program_data_to_cache - write data to cache register
+ * @chip: SPI-NAND device structure
+ * @page_addr: page to write
+ * @column: the location to write to the cache
+ * @len: number of bytes to write
+ * @wrbuf: buffer held @len bytes
+ * @clr_cache: clear cache register or not
+ * Description:
+ * Command can be 02h, 32h, 84h, 34h
+ * 02h and 32h will clear the cache with 0xff value first
+ * Since it is writing the data to cache, there is no tPROG time.
+ */
+static int spi_nand_program_data_to_cache(struct spi_flash_chip *chip,
+ u32 page_addr, u32 column, u32 len,
+ const u8 *wbuf, u8 clr_cache)
+{
+ struct spi_flash_cmd cmd;
+
+ if (chip->options & SPINAND_NEED_PLANE_SELECT) {
+ column |= (((page_addr >>
+ (chip->block_shift - chip->page_shift)) & 0x1) << 12);
+ }
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ if (clr_cache)
+ cmd.cmd_cfg = chip->table + chip->write_cache_op;
+ else
+ cmd.cmd_cfg = chip->table + chip->write_cache_rdm_op;
+ cmd.n_addr = 2;
+ cmd.addr[0] = (u8)(column >> 8);
+ cmd.addr[1] = (u8)column;
+ cmd.n_tx = len;
+ cmd.tx_buf = wbuf;
+ cmd.flag = RST_AHB_DOMAIN;
+
+ return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nand_program_execute - send command 10h to write a page from
+ * cache to the Nand array
+ * @chip: SPI-NAND device structure
+ * @page_addr: the physical page location to write the page.
+ * Description:
+ * Need to wait for tPROG time to finish the transaction.
+ */
+static int spi_nand_program_execute(struct spi_flash_chip *chip, u32 page_addr)
+{
+ struct spi_flash_cmd cmd;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + PROG_EXC;
+ cmd.n_addr = 3;
+ cmd.addr[0] = (u8)(page_addr >> 16);
+ cmd.addr[1] = (u8)(page_addr >> 8);
+ cmd.addr[2] = (u8)page_addr;
+
+ return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nand_erase_block_erase - send command D8h to erase a block
+ * @chip: SPI-NAND device structure
+ * @page_addr: the page to erase.
+ * Description:
+ * Need to wait for tERS.
+ */
+static int spi_nand_erase_block(struct spi_flash_chip *chip,
+ u32 page_addr)
+{
+ struct spi_flash_cmd cmd;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + BLK_ERASE;
+ cmd.n_addr = 3;
+ cmd.addr[0] = (u8)(page_addr >> 16);
+ cmd.addr[1] = (u8)(page_addr >> 8);
+ cmd.addr[2] = (u8)page_addr;
+ cmd.flag = RST_AHB_DOMAIN | AHB_MAP_SIZE_PAGE;
+
+ return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nand_wait - wait until the command is done
+ * @chip: SPI-NAND device structure
+ * @s: buffer to store status register(can be NULL)
+ */
+static int spi_nand_wait(struct spi_flash_chip *chip, u8 *s)
+{
+ u8 status;
+ unsigned long ret = -ETIMEDOUT;
+
+ while (1) {
+ spi_nand_read_status(chip, &status);
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ ret = 0;
+ goto out;
+ }
+ }
+out:
+ if (s)
+ *s = status;
+
+ return ret;
+}
+
+/**
+ * spi_nand_read_id - send 9Fh command to get ID
+ * @chip: SPI_FLASH device structure
+ * @buf: buffer to store id
+ */
+static int spi_nand_read_id(struct spi_flash_chip *chip, u8 *buf)
+{
+ struct spi_flash_cmd cmd;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + READ_ID;
+ cmd.n_rx = 4;
+ cmd.rx_buf = buf;
+
+ return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nand_reset - send command FFh to reset chip.
+ * @chip: SPI_FLASH device structure
+ */
+static int spi_nand_reset(struct spi_flash_chip *chip)
+{
+ struct spi_flash_cmd cmd;
+
+ memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+ cmd.cmd_cfg = chip->table + RESET;
+
+ if (chip->issue_cmd(chip, &cmd))
+ printf("spi_nand reset failed!\n");
+
+ /* elapse 2ms before issuing any other command */
+ udelay(2000);
+ return 0;
+}
+
+/**
+ * spi_nand_lock_block - [Interface] write block lock register to
+ * lock/unlock device
+ * @spi: spi device structure
+ * @lock: value to set to block lock register
+ * Description:
+ * After power up, all the Nand blocks are locked. This function allows
+ * one to unlock the blocks, and so it can be written or erased.
+ *
+ * Register Bit Address
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * |---------------------------------------------------------|
+ * | BRWD2 | BP3 | BP2 | BP1 | BP0 | TB | WP | Reversed |
+ *
+ * Block Lock Register Block Protection Bits
+ *
+ * TB BP3 BP2 BP1 BP0 Protected Portion
+ * 0 0 0 0 0 All unlocked
+ * 0 0 0 0 1 Upper 1/1024 locked
+ * 0 0 0 1 0 Upper 1/512 locked
+ * 0 0 0 1 1 Upper 1/256 locked
+ * 0 0 1 0 0 Upper 1/128 locked
+ * 0 0 1 0 1 Upper 1/64 locked
+ * 0 0 1 1 0 Upper 1/32 locked
+ * 0 0 1 1 1 Upper 1/16 locked
+ * 0 1 0 0 0 Upper 1/8 locked
+ * 0 1 0 0 1 Upper 1/4 locked
+ * 0 1 0 1 0 Upper 1/2 locked
+ * 0 1 0 1 1 All Locked
+ * 0 1 1 0 0 All Locked
+ * 0 1 1 0 1 All Locked
+ * 0 1 1 1 0 All Locked
+ * 0 1 1 1 1 All Locked
+ * 1 0 0 0 0 All unlocked
+ * 1 0 0 0 1 Lower 1/1024 locked
+ * 1 0 0 1 0 Lower 1/512 locked
+ * 1 0 0 1 1 Lower 1/256 locked
+ * 1 0 1 0 0 Lower 1/128 locked
+ * 1 0 1 0 1 Lower 1/64 locked
+ * 1 0 1 1 0 Lower 1/32 locked
+ * 1 0 1 1 1 Lower 1/16 locked
+ * 1 1 0 0 0 Lower 1/8 locked
+ * 1 1 0 0 1 Lower 1/4 locked
+ * 1 1 0 1 0 Lower 1/2 locked
+ * 1 1 0 1 1 All Locked
+ * 1 1 1 0 0 All Locked
+ * 1 1 1 0 1 All Locked
+ * 1 1 1 1 0 All Locked
+ * 1 1 1 1 1 All Locked
+
+ */
+int spi_nand_lock_block(struct spi_flash_chip *chip, u8 lock)
+{
+ return spi_nand_write_reg(chip, REG_BLOCK_LOCK, &lock);
+}
+
+/**
+ * spi_nand_change_mode - switch chip to OTP/OTP protect/Normal mode
+ * @chip: SPI-NAND device structure
+ * @mode: mode to enter
+ */
+static int spi_nand_change_mode(struct spi_flash_chip *chip, u8 mode)
+{
+ u8 cfg;
+
+ spi_nand_get_cfg(chip, &cfg);
+ switch (mode) {
+ case OTP_MODE:
+ cfg = (cfg & ~CFG_OTP_MASK) | CFG_OTP_ENTER;
+ break;
+ case OTP_PROTECT_MODE:
+ cfg = (cfg & ~CFG_OTP_MASK) | CFG_OTP_PROTECT;
+ break;
+ case SNOR_READ_ENABLE_MODE:
+ cfg = (cfg & ~CFG_OTP_MASK) | CFG_SNOR_ENABLE;
+ break;
+ case NORMAL_MODE:
+ cfg = (cfg & ~CFG_OTP_MASK) | CFG_OTP_EXIT;
+ break;
+ }
+ spi_nand_set_cfg(chip, &cfg);
+
+ return 0;
+}
+
+/**
+ * spi_nand_scan_id_table - scan chip info in id table
+ * @chip: SPI-NAND device structure
+ * @id: point to manufacture id and device id
+ * Description:
+ * If found in id table, config chip with table information.
+ */
+static bool spi_nand_scan_id_table(struct spi_flash_chip *chip)
+{
+ struct spi_nand_info *type = spi_nand_table;
+ int id;
+
+ for (; type->name; type++) {
+ /* ignore high byte if not used */
+ if (!(type->dev_id >> 8))
+ id = (chip->dev_id & 0xff);
+ else
+ id = chip->dev_id;
+ if (chip->mfr_id == type->mfr_id && id == type->dev_id) {
+ //chip->name = type->name;
+ chip->size = type->page_size * type->pages_per_blk
+ * type->blks_per_lun * type->luns_per_chip;
+ chip->block_size = type->page_size
+ * type->pages_per_blk;
+ chip->page_size = type->page_size;
+ chip->oob_size = type->oob_size;
+ chip->lun_shift =
+ ilog2(chip->block_size * type->blks_per_lun);
+ chip->ecc_strength = type->ecc_strength;
+ chip->options |= type->options;
+ chip->refresh_threshold = type->bitflip_threshold;
+ chip->ecclayout = type->ecclayout;
+ chip->get_ecc_status = type->get_ecc_status;
+ chip->max_mhz = type->max_mhz;
+ chip->tclqv = type->tclqv;
+ chip->tset = type->tset;
+ chip->thold = type->thold;
+ chip->flash_info = type;
+
+ printf("SPI-NAND: %s is found.\n", type->name);
+ return true;
+ }
+ }
+
+ chip->refresh_threshold = 1;
+ return false;
+}
+
+static u16 onfi_crc16(u16 crc, u8 const *p, u32 len)
+{
+ int i;
+
+ while (len--) {
+ crc ^= *p++ << 8;
+ for (i = 0; i < 8; i++)
+ crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+ }
+
+ return crc;
+}
+
+/* Sanitize ONFI strings so we can safely print them */
+static void sanitize_string(char *s, u32 len)
+{
+ int i = len - 1;
+ int j = 0;
+
+ /* Null terminate */
+ s[i--] = 0;
+
+ /* Remove unnecessary space */
+ while (i >= 0 && (s[i] <= ' ' || s[i] > 127)) {
+ s[i--] = 0;
+ }
+ /* Remove non printable chars */
+ for (j = 0; j <= i; j++) {
+ if (s[j] < ' ' || s[j] > 127)
+ s[j] = '?';
+ }
+}
+
+/**
+ * spi_nand_detect_onfi - config chip with parameter page
+ * @chip: SPI-NAND device structure
+ * Description:
+ * This function is called when we can not get info from id table.
+ */
+static int spi_nand_detect_onfi(struct spi_flash_chip *chip)
+{
+ struct spi_nand_onfi_params *p;
+ u8 *buffer;
+ int read_cache_op;
+ int ret = true;
+ int i;
+
+ buffer = malloc(256 * 3);
+ printf("spi_nand_detect_onfi: buffer=0x%x\n", (unsigned int)buffer);
+ memset(buffer, 0x0, 256*3);
+
+ spi_nand_change_mode(chip, OTP_MODE);
+ spi_nand_read_page_to_cache(chip, 0x01);
+ spi_nand_wait(chip, NULL);
+ /*
+ * read parameter page can only ues 1-1-1 mode
+ */
+ read_cache_op = chip->read_cache_op;
+ chip->read_cache_op = READ_FROM_CACHE;
+ spi_nand_read_from_cache(chip, 0x01, 0, 256 * 4, buffer);
+ chip->read_cache_op = read_cache_op;
+ spi_nand_change_mode(chip, NORMAL_MODE);
+
+ p = (struct spi_nand_onfi_params *)buffer;
+ for (i = 0; i < 3; i++, p++) {
+ if (p->sig[0] != 'O' || p->sig[1] != 'N' ||
+ p->sig[2] != 'F' || p->sig[3] != 'I')
+ continue;
+ //if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) ==
+ // (p->crc))
+ break;
+ }
+
+ if (i == 3) {
+ printf("Could not find valid ONFI, use default settings\n");
+ ret = false;
+ goto out;
+ }
+
+ memcpy(&chip->onfi_params, p, sizeof(*p));
+ p = &chip->onfi_params;
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+
+ //chip->name = p->model;
+ chip->size = (p->byte_per_page) *
+ (p->pages_per_block) *
+ (p->blocks_per_lun) * p->lun_count;
+ chip->block_size = (p->byte_per_page) *
+ (p->pages_per_block);
+ chip->page_size = (p->byte_per_page);
+ chip->oob_size = (p->spare_bytes_per_page);
+ chip->lun_shift = ilog2(chip->block_size * (p->blocks_per_lun));
+
+// if (chip->mfr_id == SPIFLASH_MFR_MICRON) {
+// if (p->vendor.micron_sepcific.two_plane_page_read)
+// chip->options |= SPINAND_NEED_PLANE_SELECT;
+// if (p->vendor.micron_sepcific.die_selection)
+// chip->options |= SPINAND_NEED_DIE_SELECT;
+// }
+ chip->ecc_strength = p->vendor.micron_sepcific.ecc_ability;
+out:
+ free(buffer);
+ return ret;
+}
+
+#define SPI_NAND_MAX_RETRY 3
+/**
+ * spi_nand_do_read_page - read page from flash to buffer
+ * @chip: spi nand chip structure
+ * @page_addr: page address/raw address
+ * @column: column address
+ * @ecc_off: without ecc or not
+ * @corrected: how many bit error corrected
+ * @buf: data buffer
+ * @len: data length to read
+ * Description:
+ * Return -EBADMSG when internal ecc can not correct bitflips.
+ * The command sequence to transfer data from NAND array to output is
+ * follows:
+ * 13h (PAGE READ to cache register)
+ * 0Fh (GET FEATURES command to read the status)
+ * 0Bh/03h/3Bh/6Bh (Read from Cache Xn); or BBh/EBh (Read From
+ * Cache Dual/Quad IO)
+ */
+static int spi_nand_do_read_page(struct mtd_info *mtd, u32 page_addr,
+ u32 column, bool ecc_off, u32 *corrected,
+ u8 *buf, int size, bool oob_only)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ u32 ecc_error;
+ u8 status;
+ int ret;
+ int len = size;
+ int rx_len, i;
+
+ if (!buf) {
+ if (!oob_only) {
+ len = chip->page_size + chip->oob_size;
+ buf = chip->buf;
+ column = 0;
+ } else {
+ len = chip->oob_size;
+ buf = chip->oobbuf;
+ column = chip->page_size;
+ }
+ }
+
+ spi_nand_read_page_to_cache(chip, page_addr);
+ ret = spi_nand_wait(chip, &status);
+ if (ret < 0) {
+ printf("error %d waiting page 0x%x to cache\n",
+ ret, page_addr);
+ return ret;
+ }
+
+ if (!ecc_off && chip->get_ecc_status) {
+ chip->get_ecc_status(chip, status, corrected, &ecc_error);
+ if (ecc_error) {
+ printf("internal ECC error reading page 0x%x\n",
+ page_addr);
+ mtd->ecc_stats.failed++;
+ } else if (*corrected) {
+ if (*corrected < chip->refresh_threshold)
+ /*
+ * Do not report bit-flip to upper layer if
+ * less than refresh_threshold, which may
+ * make mtd->_read failure
+ */
+ *corrected = 0;
+ else
+ mtd->ecc_stats.corrected += *corrected;
+ }
+ }
+
+ rx_len = chip->rx_max_len ? chip->rx_max_len : len;
+ do {
+ int real_len;
+
+ i = 0;
+retry:
+ real_len = min(len, rx_len);
+ ret = spi_nand_read_from_cache(chip, page_addr, column,
+ real_len, buf);
+ if (ret == -EAGAIN && ++i <= SPI_NAND_MAX_RETRY) {
+ rx_len = rx_len / 2;
+ goto retry;
+ } else if (ret) {
+ return -EIO;
+ } else if (i) {
+ printf("Pass after the %dth retry\n", i);
+ }
+
+ column += real_len;
+ buf += real_len;
+ len -= real_len;
+ } while (len);
+
+ return ret;
+}
+
+/**
+ * spi_nand_transfer_oob - transfer oob to client buffer
+ * @chip: SPI-NAND device structure
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
+ */
+static void spi_nand_transfer_oob(struct spi_flash_chip *chip, u8 *oob,
+ struct mtd_oob_ops *ops, size_t len)
+{
+ switch (ops->mode) {
+
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
+ memcpy(oob, chip->oobbuf + ops->ooboffs, len);
+ return;
+
+ case MTD_OPS_AUTO_OOB: {
+ struct nand_oobfree *free = chip->ecclayout->oobfree;
+ u32 boffs = 0, roffs = ops->ooboffs;
+ size_t bytes = 0;
+
+ for (; free->length && len; free++, len -= bytes) {
+ /* Read request not from offset 0? */
+ if (unlikely(roffs)) {
+ if (roffs >= free->length) {
+ roffs -= free->length;
+ continue;
+ }
+ boffs = free->offset + roffs;
+ bytes = min_t(size_t, len,
+ (free->length - roffs));
+ roffs = 0;
+ } else {
+ bytes = min_t(size_t, len, free->length);
+ boffs = free->offset;
+ }
+ memcpy(oob, chip->oobbuf + boffs, bytes);
+ oob += bytes;
+ }
+ return;
+ }
+ default:
+ BUG();
+ }
+}
+
+/**
+ * spi_nand_fill_oob - transfer client buffer to oob
+ * @chip: SPI-NAND device structure
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
+ */
+static void spi_nand_fill_oob(struct spi_flash_chip *chip, u8 *oob,
+ size_t len, struct mtd_oob_ops *ops)
+{
+ memset(chip->oobbuf, 0xff, chip->oob_size);
+ switch (ops->mode) {
+
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
+ memcpy(chip->oobbuf + ops->ooboffs, oob, len);
+ return;
+
+ case MTD_OPS_AUTO_OOB: {
+ struct nand_oobfree *free = chip->ecclayout->oobfree;
+ u32 boffs = 0, woffs = ops->ooboffs;
+ size_t bytes = 0;
+
+ for (; free->length && len; free++, len -= bytes) {
+ /* Write request not from offset 0? */
+ if (unlikely(woffs)) {
+ if (woffs >= free->length) {
+ woffs -= free->length;
+ continue;
+ }
+ boffs = free->offset + woffs;
+ bytes = min_t(size_t, len,
+ (free->length - woffs));
+ woffs = 0;
+ } else {
+ bytes = min_t(size_t, len, free->length);
+ boffs = free->offset;
+ }
+ memcpy(chip->oobbuf + boffs, oob, bytes);
+ oob += bytes;
+ }
+ return;
+ }
+ default:
+ BUG();
+ }
+}
+
+/**
+ * spi_nand_read_pages - read data from flash to buffer
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ * @max_bitflips: maximum bitflip count
+ * Description:
+ * Normal read function, read one page to buffer before issue
+ * another.
+ */
+static int spi_nand_read_pages(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops, unsigned int *max_bitflips)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ struct mtd_ecc_stats stats;
+ int page_addr, page_offset, page_blk_shift, size, ret;
+ u32 corrected = 0;
+ int readlen = ops->len;
+ int oobreadlen = ops->ooblen;
+ bool ecc_off = ops->mode == MTD_OPS_RAW;
+#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
+ int ooblen = mtd->oobsize;
+#else
+ int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+ mtd->oobavail : mtd->oobsize;
+#endif
+ int lun_num;
+ int real_page;
+ int blk_addr = -1;
+ u8 *buf;
+
+ page_blk_shift = chip->block_shift - chip->page_shift;
+ real_page = from >> chip->page_shift;
+ page_offset = from & chip->page_mask;
+ lun_num = from >> chip->lun_shift;
+ ops->retlen = 0;
+ *max_bitflips = 0;
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+
+ stats = mtd->ecc_stats;
+ page_addr = real_page;
+ while (1) {
+ if (chip->search_bbm_table &&
+ (blk_addr != real_page >> page_blk_shift)) {
+ int addr;
+
+ blk_addr = real_page >> page_blk_shift;
+ addr = real_page << chip->page_shift;
+ addr = chip->search_bbm_table(chip, addr);
+ page_addr = addr >> chip->page_shift;
+ lun_num = addr >> chip->lun_shift;
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+ }
+
+ size = min(readlen, chip->page_size - page_offset);
+
+ if (unlikely(ops->oobbuf) || page_offset ||
+ (size < chip->page_size))
+ buf = NULL;
+ else {
+ chip->cached_page = -1;
+ buf = ops->datbuf + ops->retlen;
+ }
+
+ if (page_addr != chip->cached_page
+ || ecc_off != chip->cached_page_ecc_off) {
+ ret = spi_nand_do_read_page(mtd, page_addr, page_offset,
+ ecc_off, &corrected, buf,
+ size, false);
+ if (ret)
+ break;
+
+ if (chip->low_level_scrub) {
+ corrected = chip->low_level_scrub(chip,
+ real_page, corrected);
+ blk_addr = -1;
+ }
+
+ chip->cached_page_bitflips = corrected;
+ if (!buf && !(mtd->ecc_stats.failed - stats.failed)) {
+ chip->cached_page = page_addr;
+ chip->cached_page_ecc_off = ecc_off;
+ }
+ }
+
+ if (!buf)
+ memcpy(ops->datbuf + ops->retlen,
+ chip->buf + page_offset, size);
+
+ *max_bitflips = max(*max_bitflips, chip->cached_page_bitflips);
+
+ ops->retlen += size;
+ readlen -= size;
+ page_offset = 0;
+
+ if (unlikely(ops->oobbuf)) {
+ size = min(oobreadlen, ooblen);
+ spi_nand_transfer_oob(chip,
+ ops->oobbuf + ops->oobretlen, ops, size);
+ ops->oobretlen += size;
+ oobreadlen -= size;
+ }
+ if (!readlen)
+ break;
+
+ page_addr++;
+ real_page++;
+ /* Check, if we cross lun boundary */
+ if (!(real_page &
+ ((1 << (chip->lun_shift - chip->page_shift)) - 1))
+ && (chip->options & SPINAND_NEED_DIE_SELECT)) {
+ lun_num++;
+ spi_nand_lun_select(chip, lun_num);
+ }
+ }
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return ret;
+}
+
+/**
+ * spi_nand_do_read_ops - read data from flash to buffer
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob ops structure
+ * Description:
+ * Disable internal ECC before reading when MTD_OPS_RAW set.
+ */
+static int spi_nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ int ret;
+ unsigned int max_bitflips = 0;
+ int oobreadlen = ops->ooblen;
+ bool ecc_off = ops->mode == MTD_OPS_RAW;
+#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
+ int ooblen = mtd->oobsize;
+#else
+ int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+ mtd->oobavail : mtd->oobsize;
+#endif
+ /* Do not allow reads past end of device */
+ if (unlikely(from >= mtd->size)) {
+ pr_err("%s: attempt to read beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* for oob */
+ if (oobreadlen > 0) {
+ if (unlikely(ops->ooboffs >= ooblen)) {
+ pr_err("%s: attempt to start read outside oob\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (unlikely(ops->ooboffs + oobreadlen >
+ ((mtd->size >> chip->page_shift) - (from >> chip->page_shift))
+ * ooblen)) {
+ pr_err("%s: attempt to read beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+ ooblen -= ops->ooboffs;
+ ops->oobretlen = 0;
+ }
+
+ if (ecc_off)
+ chip->disable_ecc(chip);
+
+ ret = spi_nand_read_pages(mtd, from, ops, &max_bitflips);
+
+ if (ecc_off)
+ chip->enable_ecc(chip);
+
+ if (ret < 0)
+ return ret;
+
+ return max_bitflips;
+}
+
+static int spi_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u8 *buf)
+{
+ struct mtd_oob_ops ops;
+ int ret;
+
+ spi_nand_get_device(mtd, FL_READING);
+
+ memset(&ops, 0, sizeof(ops));
+ ops.len = len;
+ ops.datbuf = buf;
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ret = spi_nand_do_read_ops(mtd, from, &ops);
+
+ *retlen = ops.retlen;
+
+ spi_nand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * spi_nand_do_read_oob - read out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ * Description:
+ * Disable internal ECC before reading when MTD_OPS_RAW set.
+ */
+static int spi_nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ int page_addr, page_blk_shift;
+ u32 corrected = 0;
+ struct mtd_ecc_stats stats;
+ int readlen = ops->ooblen;
+ int len;
+ int ret = 0;
+ bool ecc_off = ops->mode == MTD_OPS_RAW;
+ int lun_num;
+ int real_page;
+ int blk_addr = -1;
+
+ pr_debug("%s: from = 0x%08Lx, len = %i\n",
+ __func__, (unsigned long long)from, readlen);
+
+ stats = mtd->ecc_stats;
+
+#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
+ len = mtd->oobsize;
+#else
+ len = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
+#endif
+ if (unlikely(ops->ooboffs >= len)) {
+ pr_err("%s: attempt to start read outside oob\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(from >= mtd->size ||
+ ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
+ (from >> chip->page_shift)) * len)) {
+ pr_err("%s: attempt to read beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ page_blk_shift = chip->block_shift - chip->page_shift;
+ real_page = from >> chip->page_shift;
+ lun_num = from >> chip->lun_shift;
+ len -= ops->ooboffs;
+ ops->oobretlen = 0;
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+
+ if (ecc_off)
+ chip->disable_ecc(chip);
+
+ page_addr = real_page;
+ while (1) {
+ if (chip->search_bbm_table &&
+ (blk_addr != real_page >> page_blk_shift)) {
+ int addr;
+
+ blk_addr = real_page >> page_blk_shift;
+ addr = real_page << chip->page_shift;
+ addr = chip->search_bbm_table(chip, addr);
+ page_addr = addr >> chip->page_shift;
+ lun_num = addr >> chip->lun_shift;
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+ }
+
+ /*read data from chip*/
+ ret = spi_nand_do_read_page(mtd, page_addr, 0,
+ ecc_off, &corrected, NULL,
+ 0, true);
+ if (ret)
+ goto out;
+
+ if (chip->low_level_scrub) {
+ corrected = chip->low_level_scrub(chip,
+ real_page, corrected);
+ blk_addr = -1;
+ }
+
+ len = min(len, readlen);
+ spi_nand_transfer_oob(chip, ops->oobbuf + ops->oobretlen,
+ ops, len);
+ readlen -= len;
+ ops->oobretlen += len;
+ if (!readlen)
+ break;
+ page_addr++;
+ real_page++;
+ /* Check, if we cross lun boundary */
+ if (!(real_page &
+ ((1 << (chip->lun_shift - chip->page_shift)) - 1))
+ && (chip->options & SPINAND_NEED_DIE_SELECT)) {
+ lun_num++;
+ spi_nand_lun_select(chip, lun_num);
+ }
+ }
+out:
+ if (ecc_off)
+ chip->enable_ecc(chip);
+
+ if (ret < 0)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
+/**
+ * spi_nand_read_oob - [MTD Interface] read data and/or out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
+ */
+static int spi_nand_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int ret = -ENOTSUPP;
+
+ ops->retlen = 0;
+
+ /* Do not allow reads past end of device */
+ if (ops->datbuf && (from + ops->len) > mtd->size) {
+ printf("%s: attempt to read beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ spi_nand_get_device(mtd, FL_READING);
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (!ops->datbuf)
+ ret = spi_nand_do_read_oob(mtd, from, ops);
+ else
+ ret = spi_nand_do_read_ops(mtd, from, ops);
+
+out:
+ spi_nand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * spi_nand_do_write_page - write data from buffer to flash
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @column: column address
+ * @buf: data buffer
+ * @len: data length to write
+ * @clr_cache: clear cache register with 0xFF or not
+ */
+static int spi_nand_do_write_page(struct mtd_info *mtd, u32 page_addr,
+ u32 column, const u8 *buf,
+ u32 size, u8 clr_cache, bool oob_only)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ u32 column_save, len_save;
+ u8 *buf_save;
+ u8 status;
+ int len = size, tx_len, i;
+ int ret = 0;
+ int cache_done = 0;
+
+ if (!buf) {
+ if (!oob_only) {
+ len = chip->page_size + chip->oob_size;
+ buf = chip->buf;
+ column = 0;
+ } else {
+ len = chip->oob_size;
+ buf = chip->oobbuf;
+ column = chip->page_size;
+ }
+ }
+
+ buf_save = buf;
+ column_save = column;
+ len_save = len;
+
+ tx_len = chip->tx_max_len;
+load_cache:
+ if (tx_len < len && (chip->options & SPINAND_RDM_CMD_NEED_PAGE_READ)) {
+ /*
+ * XTX spi-nand PROGRAM LOAD RANDOM DATA cmd only used for
+ * Internal Data Move, need to send 13H (PAGE READ TO CACHE)
+ * before use this command.
+ *
+ * If spi-nand from new vendor have same limitation as XTX, must
+ * set SPINAND_RDM_CMD_NEED_PAGE_READ in options of spi_nand_table.
+ */
+ spi_nand_read_page_to_cache(chip, page_addr);
+ ret = spi_nand_wait(chip, &status);
+ if (ret < 0) {
+ printf("error %d waiting page 0x%x to cache\n",
+ ret, page_addr);
+ return ret;
+ }
+
+ cache_done = 1;
+ clr_cache = 0;
+ }
+
+ spi_nand_write_enable(chip);
+ do {
+ int real_len;
+
+ i = 0;
+retry:
+ real_len = min(len, tx_len);
+ ret = spi_nand_program_data_to_cache(chip, page_addr, column,
+ real_len, buf, clr_cache);
+ if (ret == -EAGAIN && ++i <= SPI_NAND_MAX_RETRY) {
+ tx_len = tx_len / 2;
+ if (!cache_done &&
+ (chip->options & SPINAND_RDM_CMD_NEED_PAGE_READ)) {
+ buf = buf_save;
+ column = column_save;
+ len = len_save;
+ goto load_cache;
+ }
+ goto retry;
+ } else if (ret) {
+ return -EIO;
+ } else if (i) {
+ printf("Write pass after the %dth retry\n", i);
+ }
+
+ clr_cache = 0;
+ column += real_len;
+ buf += real_len;
+ len -= real_len;
+ } while (len);
+
+ spi_nand_program_execute(chip, page_addr);
+ ret = spi_nand_wait(chip, &status);
+ if (ret < 0) {
+ pr_err("error %d reading page 0x%x from cache\n",
+ ret, page_addr);
+ return ret;
+ }
+ if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
+ pr_err("program page 0x%x failed\n", page_addr);
+ ret = -EIO;
+ }
+ return ret;
+}
+
+/**
+ * spi_nand_do_write_ops - write data from buffer to flash
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ * Description:
+ * Disable internal ECC before writing when MTD_OPS_RAW set.
+ */
+static int spi_nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ int page_addr, page_offset, page_blk_shift, size;
+ int writelen = ops->len;
+ int oobwritelen = ops->ooblen;
+ int ret = 0;
+#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
+ int ooblen = mtd->oobsize;
+#else
+ int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+ mtd->oobavail : mtd->oobsize;
+#endif
+ bool ecc_off = ops->mode == MTD_OPS_RAW;
+ int lun_num;
+ int real_page;
+ int blk_addr = -1;
+ u8 *buf;
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size)) {
+ pr_err("%s: attempt to write beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ page_blk_shift = chip->block_shift - chip->page_shift;
+ real_page = to >> chip->page_shift;
+ page_offset = to & chip->page_mask;
+ lun_num = to >> chip->lun_shift;
+ ops->retlen = 0;
+
+ /* for oob */
+ if (oobwritelen > 0) {
+ /* Do not allow write past end of page */
+ if ((ops->ooboffs + oobwritelen) > ooblen) {
+ pr_err("%s: attempt to write past end of page\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (unlikely(ops->ooboffs >= ooblen)) {
+ pr_err("%s: attempt to start write outside oob\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (unlikely(ops->ooboffs + oobwritelen >
+ ((mtd->size >> chip->page_shift) - (to >> chip->page_shift))
+ * ooblen)) {
+ pr_err("%s: attempt to write beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+ ooblen -= ops->ooboffs;
+ ops->oobretlen = 0;
+ }
+
+ chip->cached_page = -1;
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+
+ if (ecc_off)
+ chip->disable_ecc(chip);
+
+ page_addr = real_page;
+ while (1) {
+ if (chip->search_bbm_table &&
+ (blk_addr != real_page >> page_blk_shift)) {
+ int addr;
+
+ blk_addr = real_page >> page_blk_shift;
+ addr = real_page << chip->page_shift;
+ addr = chip->search_bbm_table(chip, addr);
+ page_addr = addr >> chip->page_shift;
+ lun_num = addr >> chip->lun_shift;
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+ }
+
+ if (unlikely(ops->oobbuf)) {
+ size = min(oobwritelen, ooblen);
+ spi_nand_fill_oob(chip, ops->oobbuf + ops->oobretlen,
+ size, ops);
+ ops->oobretlen += size;
+ oobwritelen -= size;
+ buf = NULL;
+ } else {
+ buf = ops->datbuf + ops->retlen;
+ memset(chip->oobbuf, 0xff, chip->oob_size);
+ }
+ size = min(writelen, chip->page_size - page_offset);
+
+ if (!buf) {
+ memcpy(chip->buf + page_offset,
+ ops->datbuf + ops->retlen, size);
+ if (page_offset)
+ memset(chip->buf, 0xff, page_offset);
+ if (size < chip->page_size - page_offset)
+ memset(chip->buf + page_offset + size, 0xff,
+ chip->page_size - page_offset - size);
+ }
+
+ ret = spi_nand_do_write_page(mtd, page_addr, page_offset,
+ buf, size, 1, false);
+ if (ret) {
+ pr_err("error %d writing page 0x%x\n",
+ ret, page_addr);
+ goto out;
+ }
+
+ ops->retlen += size;
+ writelen -= size;
+ page_offset = 0;
+ if (!writelen)
+ break;
+
+ page_addr++;
+ real_page++;
+ /* Check, if we cross lun boundary */
+ if (!(real_page &
+ ((1 << (chip->lun_shift - chip->page_shift)) - 1))
+ && (chip->options & SPINAND_NEED_DIE_SELECT)) {
+ lun_num++;
+ spi_nand_lun_select(chip, lun_num);
+ }
+ }
+out:
+ if (ecc_off)
+ chip->enable_ecc(chip);
+
+ return ret;
+}
+
+static int spi_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u8 *buf)
+{
+ struct mtd_oob_ops ops;
+ int ret;
+
+ spi_nand_get_device(mtd, FL_WRITING);
+
+ memset(&ops, 0, sizeof(ops));
+ ops.len = len;
+ ops.datbuf = (u8 *)buf;
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ret = spi_nand_do_write_ops(mtd, to, &ops);
+
+ *retlen = ops.retlen;
+
+ spi_nand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * spi_nand_do_write_oob - write out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ * Description:
+ * Disable internal ECC before writing when MTD_OPS_RAW set.
+ */
+static int spi_nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int page_addr, len, ret;
+ struct spi_flash_chip *chip = mtd->priv;
+ int writelen = ops->ooblen;
+ bool ecc_off = ops->mode == MTD_OPS_RAW;
+ int lun_num;
+
+ pr_debug("%s: to = 0x%08x, len = %i\n",
+ __func__, (unsigned int)to, (int)writelen);
+
+#ifdef OOB_REQ_ALIGN_TO_SPARE_LEN
+ len = mtd->oobsize;
+#else
+ len = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
+#endif
+ /* Do not allow write past end of page */
+ if ((ops->ooboffs + writelen) > len) {
+ pr_err("%s: attempt to write past end of page\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (unlikely(ops->ooboffs >= len)) {
+ pr_err("%s: attempt to start write outside oob\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Do not allow write past end of device */
+ if (unlikely(to >= mtd->size ||
+ ops->ooboffs + writelen >
+ ((mtd->size >> chip->page_shift) -
+ (to >> chip->page_shift)) * len)) {
+ pr_err("%s: attempt to write beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (chip->search_bbm_table)
+ to = chip->search_bbm_table(chip, to);
+
+ /* Shift to get page */
+ page_addr = to >> chip->page_shift;
+ lun_num = to >> chip->lun_shift;
+
+ spi_nand_fill_oob(chip, ops->oobbuf, writelen, ops);
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+
+ if (ecc_off)
+ chip->disable_ecc(chip);
+
+ //ret = spi_nand_do_write_page(mtd, page_addr, true);
+ ret = spi_nand_do_write_page(mtd, page_addr, 0,
+ NULL, 0, 1, true);
+ if (ret) {
+ pr_err("error %d writing page 0x%x\n",
+ ret, page_addr);
+ goto out;
+ }
+ ops->oobretlen = writelen;
+
+out:
+ if (ecc_off)
+ chip->enable_ecc(chip);
+
+ return ret;
+}
+
+/**
+ * spi_nand_write_oob - [MTD Interface] write data and/or out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ */
+static int spi_nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ret = -ENOTSUPP;
+
+ ops->retlen = 0;
+
+ /* Do not allow writes past end of device */
+ if (ops->datbuf && (to + ops->len) > mtd->size) {
+ pr_err("%s: attempt to write beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ spi_nand_get_device(mtd, FL_WRITING);
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (!ops->datbuf)
+ ret = spi_nand_do_write_oob(mtd, to, ops);
+ else
+ ret = spi_nand_do_write_ops(mtd, to, ops);
+
+out:
+ spi_nand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * spi_nand_block_bad - Check if block at offset is bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ * @getchip: 0, if the chip is already selected
+ */
+static int spi_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ struct mtd_oob_ops ops = {0};
+ u32 block_addr;
+ u8 bad[2] = {0, 0};
+ u8 ret = 0;
+
+ block_addr = ofs >> chip->block_shift;
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooblen = 2;
+ ops.oobbuf = bad;
+
+ if (getchip)
+ spi_nand_get_device(mtd, FL_READING);
+ spi_nand_do_read_oob(mtd, block_addr << chip->block_shift, &ops);
+ if (getchip)
+ spi_nand_release_device(mtd);
+ if (bad[0] != 0xFF || bad[1] != 0xFF)
+ ret = 1;
+
+ return ret;
+}
+
+/**
+ * spi_nand_block_checkbad - Check if a block is marked bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @getchip: 0, if the chip is already selected
+ * @allowbbt: 1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int spi_nand_block_checkbad(struct mtd_info *mtd, loff_t ofs,
+ int getchip, int allowbbt)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+
+ if (!chip->bbt)
+ return chip->block_bad(mtd, ofs, getchip);
+
+ return 0;
+}
+
+/**
+ * spi_nand_block_isbad - [MTD Interface] Check if block at offset is bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ */
+static int spi_nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+{
+ return spi_nand_block_checkbad(mtd, offs, 1, 0);
+}
+
+/**
+ * spi_nand_is_bad_bbm - [BBT Interface] Check if block at offset is factory bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ */
+static int spi_nand_is_bad_bbm(struct mtd_info *mtd, loff_t ofs)
+{
+ return spi_nand_block_bad(mtd, ofs, 1);
+}
+
+/**
+ * spi_nand_block_markbad_lowlevel - mark a block bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * This function performs the generic bad block marking steps (i.e., bad
+ * block table(s) and/or marker(s)). We only allow the hardware driver to
+ * specify how to write bad block markers to OOB (chip->block_markbad).
+ *
+ * We try operations in the following order:
+ * (1) erase the affected block, to allow OOB marker to be written cleanly
+ * (2) write bad block marker to OOB area of affected block (unless flag
+ * NAND_BBT_NO_OOB_BBM is present)
+ * (3) update the BBT
+ * Note that we retain the first error encountered in (2) or (3), finish the
+ * procedures, and dump the error in the end.
+*/
+//static int spi_nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
+//{
+// struct spi_flash_chip *chip = mtd->priv;
+// struct mtd_oob_ops ops = {0};
+// struct erase_info einfo = {0};
+// u32 block_addr;
+// u8 buf[2] = {0, 0};
+// int res, ret = 0;
+//
+// if (!chip->bbt || !(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
+// /*erase bad block before mark bad block*/
+// einfo.mtd = mtd;
+// einfo.addr = ofs;
+// einfo.len = 1UL << chip->block_shift;
+// spi_nand_erase(mtd, &einfo);
+//
+// block_addr = ofs >> chip->block_shift;
+// ops.mode = MTD_OPS_PLACE_OOB;
+// ops.ooblen = 2;
+// ops.oobbuf = buf;
+// spi_nand_get_device(mtd, FL_WRITING);
+// ret = spi_nand_do_write_oob(mtd,
+// block_addr << chip->block_shift, &ops);
+// spi_nand_release_device(mtd);
+// }
+//
+// /* Mark block bad in BBT */
+// if (chip->bbt) {
+// res = nand_bbt_markbad(chip->bbt, ofs);
+// if (!ret)
+// ret = res;
+// }
+//
+// if (!ret)
+// mtd->ecc_stats.badblocks++;
+//
+// return ret;
+//}
+
+/**
+ * spi_nand_block_markbad - [MTD Interface] Mark block at the given offset
+ * as bad
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ */
+static int spi_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ int ret;
+
+ ret = spi_nand_block_isbad(mtd, ofs);
+ if (ret) {
+ /* If it was bad already, return success and do nothing */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ return chip->block_markbad(mtd, ofs);
+}
+
+/**
+ * __spi_nand_erase - erase block(s)
+ * @mtd: MTD device structure
+ * @einfo: erase instruction
+ * @allowbbt: allow to access bbt
+ *
+ * Erase one ore more blocks
+ */
+static int __spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo,
+ int allowbbt)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ int page_addr, pages_per_block;
+ loff_t len;
+ u8 status;
+ int ret = 0;
+ int lun_num;
+ int real_page;
+
+
+ /* check address align on block boundary */
+ if (einfo->addr & (chip->block_size - 1)) {
+ pr_err("%s: Unaligned address\n", __func__);
+ return -EINVAL;
+ }
+
+ if (einfo->len & (chip->block_size - 1)) {
+ pr_err("%s: Length not block aligned\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Do not allow erase past end of device */
+ if ((einfo->len + einfo->addr) > chip->size) {
+ pr_err("%s: Erase past end of device\n", __func__);
+ return -EINVAL;
+ }
+
+ einfo->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+
+ /* Grab the lock and see if the device is available */
+ spi_nand_get_device(mtd, FL_ERASING);
+
+ pages_per_block = 1 << (chip->block_shift - chip->page_shift);
+ real_page = einfo->addr >> chip->page_shift;
+ len = einfo->len;
+ lun_num = einfo->addr >> chip->lun_shift;
+ chip->cached_page = -1;
+
+ einfo->state = MTD_ERASING;
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+
+ while (len) {
+ if (chip->search_bbm_table) {
+ int addr;
+
+ addr = real_page << chip->page_shift;
+ addr = chip->search_bbm_table(chip, addr);
+ page_addr = addr >> chip->page_shift;
+ lun_num = addr >> chip->lun_shift;
+ if (chip->options & SPINAND_NEED_DIE_SELECT)
+ spi_nand_lun_select(chip, lun_num);
+ } else {
+ page_addr = real_page;
+ }
+
+ /* Check if we have a bad block, we do not erase bad blocks! */
+ if (spi_nand_block_checkbad(mtd, ((loff_t) page_addr) <<
+ chip->page_shift, 0, allowbbt)) {
+ pr_warn("%s: attempt to erase a bad block at 0x%012llx\n",
+ __func__, ((loff_t) page_addr) << chip->page_shift);
+ einfo->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+ spi_nand_write_enable(chip);
+ spi_nand_erase_block(chip, page_addr);
+ ret = spi_nand_wait(chip, &status);
+ if (ret < 0) {
+ pr_err("block erase command wait failed\n");
+ einfo->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+ if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
+ pr_err("erase block 0x%012llx failed\n",
+ ((loff_t) page_addr) << chip->page_shift);
+ einfo->state = MTD_ERASE_FAILED;
+ einfo->fail_addr = (loff_t)page_addr
+ << chip->page_shift;
+ goto erase_exit;
+ }
+
+ /* Increment page address and decrement length */
+ len -= (1ULL << chip->block_shift);
+ real_page += pages_per_block;
+ /* Check, if we cross lun boundary */
+ if (len && !(real_page &
+ ((1 << (chip->lun_shift - chip->page_shift)) - 1))
+ && (chip->options & SPINAND_NEED_DIE_SELECT)) {
+ lun_num++;
+ spi_nand_lun_select(chip, lun_num);
+ }
+ }
+
+ einfo->state = MTD_ERASE_DONE;
+
+erase_exit:
+
+ ret = einfo->state == MTD_ERASE_DONE ? 0 : -EIO;
+
+ spi_nand_release_device(mtd);
+
+ /* Do call back function */
+ if (!ret)
+ mtd_erase_callback(einfo);
+
+ /* Return more or less happy */
+ return ret;
+}
+
+/**
+ * spi_nand_erase - [MTD Interface] erase block(s)
+ * @mtd: MTD device structure
+ * @einfo: erase instruction
+ *
+ * Erase one ore more blocks
+ */
+static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ int ret, page, retries = 0;
+ page = (int)(einfo->addr >> chip->page_shift);
+
+retry:
+ ret = __spi_nand_erase(mtd, einfo, 0);
+ if (ret) {
+ if (chip->options & BBT_RELOCATION_IFBAD) {
+ if (retries++ < 3)
+ goto retry;
+
+ ret = chip->block_markbad(mtd,
+ (loff_t)(page << chip->page_shift));
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * spi_nand_set_rd_wr_op - Chose the best read write command
+ * @chip: SPI-NAND device structure
+ * Description:
+ * Chose the fastest r/w command according to spi controller's ability.
+ * Note:
+ * If 03h/0Bh follows SPI NAND protocol, there is no difference,
+ * while if follows SPI NOR protocol, 03h command is working under
+ * <=20Mhz@3.3V,<=5MHz@1.8V; 0Bh command is working under
+ * 133Mhz@3.3v, 83Mhz@1.8V.
+ */
+static void spi_nand_set_rd_wr_op(struct spi_flash_chip *chip)
+{
+ struct spi_nand_info *type = (struct spi_nand_info *)chip->flash_info;
+ struct spi_flash_cmd_cfg *read_cmd;
+
+ if (chip->rx_mode & SPI_OPM_RX_QUAD) {
+ if (type) {
+ if (chip->options & SPINAND_SUPPORT_DTR)
+ chip->read_cache_op = type->quad_cmd_dtr_index;
+ else
+ chip->read_cache_op = type->quad_cmd_index;
+ } else {
+ chip->read_cache_op = READ_FROM_CACHE_X4;
+ }
+ } else if (chip->rx_mode & SPI_OPM_RX_DUAL) {
+ chip->read_cache_op = READ_FROM_CACHE_DUAL;
+ } else {
+ chip->read_cache_op = READ_FROM_CACHE_FAST;
+ }
+
+ if (chip->tx_mode & SPI_OPM_TX_QUAD) {
+ chip->write_cache_op = PROG_LOAD_X4;
+ chip->write_cache_rdm_op = PROG_LOAD_RDM_DATA_X4;
+ } else {
+ chip->write_cache_op = PROG_LOAD;
+ chip->write_cache_rdm_op = PROG_LOAD_RDM_DATA;
+ }
+
+ read_cmd = chip->table + chip->read_cache_op;
+ printf("Set rx_pins: %d, tx_pins: %d, Read_CMD:0x%x\r\n",
+ chip->rx_mode, chip->tx_mode, read_cmd->opcode);
+}
+
+/**
+ * spi_nand_init - [Interface] Init SPI-NAND device driver
+ * @spi: spi device structure
+ * @chip_ptr: pointer point to spi nand device structure pointer
+ */
+struct spi_flash_chip *spi_nand_scan_ident(struct mtd_info *mtd)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ u8 id[SPINAND_MAX_ID_LEN] = {0};
+ int dev_in_tbl = 0;
+
+ chip->table = cmd_table;
+ chip->page_size = 2048;
+ chip->block_size = 64*2048;
+ chip->size = 1024*64*2048;
+
+ spi_nand_reset(chip);
+ spi_nand_read_id(chip, id);
+
+ chip->mfr_id = id[1];
+ chip->dev_id = id[2] | id[3] << 8;
+ if (spi_nand_scan_id_table(chip))
+ dev_in_tbl = 1;
+
+ printf("SPI-NAND type mfr_id: %x, dev_id: %x\n",
+ chip->mfr_id, chip->dev_id);
+
+ if (!chip->check_dtr || !chip->check_dtr(chip))
+ chip->options &= ~SPINAND_SUPPORT_DTR;
+
+ spi_nand_set_rd_wr_op(chip);
+
+ /* Not every vendor show QE bit in CFG register */
+ if (chip->mfr_id != SPIFLASH_MFR_MICRON &&
+ chip->mfr_id != SPIFLASH_MFR_WINBOND)
+ spi_nand_enable_quad(chip);
+
+ if (chip->setup_memmap_read) {
+ if (chip->setup_memmap_read(chip,
+ chip->table + chip->read_cache_op) < 0) {
+ pr_info("preinit_lookup_tbl failed, check cmd table\n");
+ return NULL;
+ }
+ }
+
+ if (chip->options & SPINAND_NEED_SET_BFT)
+ mxic_spi_nand_set_bft(chip, chip->refresh_threshold);
+
+ spi_nand_disable_ecc(chip);
+ if(!dev_in_tbl) {
+ /*
+ * Since Program Load Random Data cmd only valid for some vendor's
+ * device, set below flag for new device unknown in table for safe.
+ */
+ chip->options |= SPINAND_RDM_CMD_NEED_PAGE_READ;
+ printf("unknown device, set SPINAND_RDM_CMD_NEED_PAGE_READ\r\n");
+ if (spi_nand_detect_onfi(chip))
+ printf("nand support onfi\r\n");
+
+ if (chip->oob_size == 128)
+ chip->ecclayout = &ecc_layout_128;
+ else
+ chip->ecclayout = &ecc_layout_64;
+ chip->get_ecc_status = generic_spi_nand_ecc_status;
+ }
+
+ spi_nand_lock_block(chip, BL_ALL_UNLOCKED);
+ spi_nand_enable_ecc(chip);
+
+ chip->block_shift = ilog2(chip->block_size);
+ chip->page_shift = ilog2(chip->page_size);
+ chip->page_mask = chip->page_size - 1;
+ chip->lun = 0;
+ chip->page_mask = chip->page_size - 1;
+ if (!chip->enable_ecc)
+ chip->enable_ecc = spi_nand_enable_ecc;
+ if (!chip->disable_ecc)
+ chip->disable_ecc = spi_nand_disable_ecc;
+
+ chip->buf = kzalloc(chip->page_size + chip->oob_size, GFP_KERNEL);
+ if (!chip->buf)
+ return NULL;
+
+ chip->oobbuf = chip->buf + chip->page_size;
+ printf("block_size=0x%x page_size=0x%x bitflip_threshold=%d\n",
+ chip->block_size, chip->page_size,
+ chip->refresh_threshold);
+
+ return chip;
+}
+
+/**
+ * spi_nand_scan_tail - [SPI-NAND Interface] Scan for the SPI-NAND device
+ * @mtd: MTD device structure
+ * Description:
+ * This is the second phase of the initiazation. It fills out all the
+ * uninitialized fields of spi_flash_chip and mtd fields.
+ */
+int spi_nand_scan_tail(struct mtd_info *mtd)
+{
+ struct spi_flash_chip *chip = mtd->priv;
+ int ret;
+
+ if (chip->options & SPINAND_ECC_TYPE_HRADWARE)
+ spi_nand_disable_ecc(chip);
+
+ mtd->name = chip->name;
+ mtd->size = chip->size;
+ mtd->erasesize = chip->block_size;
+ mtd->writesize = chip->page_size;
+ mtd->owner = THIS_MODULE;
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ if (!mtd->ecc_strength)
+ mtd->ecc_strength = chip->ecc_strength ?
+ chip->ecc_strength : 1;
+
+ mtd->ecclayout = chip->ecclayout;
+ mtd->oobsize = chip->oob_size;
+ mtd->oobavail = chip->ecclayout->oobavail;
+ mtd->_erase = spi_nand_erase;
+ mtd->_point = NULL;
+ mtd->_unpoint = NULL;
+ mtd->_read = spi_nand_read;
+ mtd->_write = spi_nand_write;
+ mtd->_read_oob = spi_nand_read_oob;
+ mtd->_write_oob = spi_nand_write_oob;
+ mtd->_lock = NULL;
+ mtd->_unlock = NULL;
+ mtd->_block_isbad = spi_nand_block_isbad;
+ mtd->_block_markbad = spi_nand_block_markbad;
+
+ if (!mtd->bitflip_threshold)
+ mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
+
+ ret = spi_flash_register(chip);
+ if (ret)
+ return ret;
+
+ /* Check, if we should skip the bad block table scan */
+ if (chip->options & NAND_SKIP_BBTSCAN)
+ return 0;
+
+ /* Build bad block table */
+ return chip->scan_bbt(mtd);
+}