ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/obm/Common/Flash/QSPI/spi_nand.c b/marvell/obm/Common/Flash/QSPI/spi_nand.c
new file mode 100644
index 0000000..b0c68fa
--- /dev/null
+++ b/marvell/obm/Common/Flash/QSPI/spi_nand.c
@@ -0,0 +1,2257 @@
+#include "qspi_host.h"
+#include "spi_nand.h"
+#include "Errors.h"
+#include "misc.h"
+#include "timer.h"
+#include "PlatformConfig.h"
+#if COPYIMAGESTOFLASH
+#include "BootLoader.h"
+#endif
+
+static void generic_spi_nand_ecc_status(struct spi_flash_chip *chip,
+ uint8_t status, uint32_t *corrected, uint32_t *ecc_error);
+static void mxic_spi_nand_ecc_status(struct spi_flash_chip *chip,
+ uint8_t status, uint32_t *corrected, uint32_t *ecc_error);
+static void mxic2_spi_nand_ecc_status(struct spi_flash_chip *chip,
+ uint8_t status, uint32_t *corrected, uint32_t *ecc_error);
+static void micron_spi_nand_ecc_status(struct spi_flash_chip *chip,
+ uint8_t status, uint32_t *corrected,
+ uint32_t *ecc_error);
+static void gd_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error);
+static void gd_spi_nand_ecc_status2(struct spi_flash_chip *chip, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error);
+static void xtx_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error);
+static void xtx2_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error);
+static void yxsc_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error);
+static void fm_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error);
+static void wb_spi_nand_ecc_status(struct spi_flash_chip *chip, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error);
+static void wb_spi_nand_ecc_status2(struct spi_flash_chip *chip, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error);
+
+enum {
+ GET_FEATURE,
+ SET_FEATURE,
+ PAGE_READ,
+ READ_PAGE_CACHE_RDM,
+ READ_PAGE_CACHE_LAST,
+ READ_FROM_CACHE,
+ READ_FROM_CACHE_GD_C,
+ 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_flash_chip nand_chip;
+
+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, 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, 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, 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, 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, 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, gd_spi_nand_ecc_status),
+ SPI_NAND_INFO("GD5F1GQ4RCxIG", 0xC8, 0xA148, 2048, 128, 64, 1024,
+ 1, 7, 0, 6, 0,
+ READ_FROM_CACHE_QUAD_GD, gd_spi_nand_ecc_status),
+ SPI_NAND_INFO("GD5F1GQ5RExxG", 0xC8, 0x41, 2048, 128, 64, 1024,
+ 1, 4, 0, 3, 0,
+ READ_FROM_CACHE_QUAD, 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, 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,
+ 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, 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, 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, gd_spi_nand_ecc_status2),
+ SPI_NAND_INFO("GD5F4GM8RExxG", 0xC8, 0x85, 2048, 128, 64, 4096,
+ 1, 8, 0, 6, 0,
+ READ_FROM_CACHE_QUAD, 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, 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, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("W25N01GWxxIx", 0xEF, 0x21BA, 2048, 64, 64, 1024,
+ 1, 4, 0, 4, 0,
+ READ_FROM_CACHE_QUAD, 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_spi_nand_ecc_status),
+ SPI_NAND_INFO("W25N02KW", 0xEF, 0x22BA, 2048, 64, 64, 2048,
+ 1, 8, 0, 6, 0,
+ READ_FROM_CACHE_QUAD, wb_spi_nand_ecc_status2),
+ SPI_NAND_INFO("W25M02GWxxIx", 0xEF, 0x21BB, 2048, 64, 64, 2048,
+ 1, 4, 0, 4, 0,
+ READ_FROM_CACHE_QUAD, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("W25M01JW", 0xEF, 0x21BC, 2048, 64, 64, 1024,
+ 1, 4, 0, 4, 0,
+ READ_FROM_CACHE_QUAD, 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, 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, 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, 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, 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, generic_spi_nand_ecc_status),
+
+ /* XTX */
+ SPI_NAND_INFO("PN26Q01AWSIUG", 0xA1, 0xC1, 2048, 64, 64, 1024,
+ 1, 4, SPINAND_RDM_CMD_NEED_PAGE_READ | SPINAND_ECC_EN_ADDR_90H,
+ 8, 52, READ_FROM_CACHE_X4, 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, 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, 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, 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, yxsc_spi_nand_ecc_status),
+
+ /* ESMT */
+ SPI_NAND_INFO("F50D1G41LB", 0xC8, 0x11, 2048, 128, 64, 1024,
+ 1, 7, 0, 4, 52,
+ READ_FROM_CACHE_QUAD, 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, generic_spi_nand_ecc_status),
+ SPI_NAND_INFO("FM25LG02B", 0xA1, 0xB2, 2048, 128, 64, 2048,
+ 1, 8, 0, 4, 0,
+ READ_FROM_CACHE_X4, 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, 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, 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, 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, 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_GD_C] = SPI_CMD(0x03, 3, 1, 0, 0, 0, 0, 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_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,
+ uint8_t reg, uint8_t *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 = qspi_start_cmd(chip, &cmd);
+ if (ret < 0)
+ obm_printf("err: %d read register %d\r\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,
+ uint8_t reg, uint8_t *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 = qspi_start_cmd(chip, &cmd);
+ if (ret < 0)
+ obm_printf("err: %d write register %d\r\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, uint8_t *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, uint8_t *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, uint8_t *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)
+{
+ uint8_t 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)
+{
+ uint8_t 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)
+{
+ uint8_t 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,
+ uint8_t status, uint32_t *corrected, uint32_t *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:
+ obm_printf("%s: unexpected status: %d\r\n",
+ __func__, ecc_status);
+ *ecc_error = 1;
+ break;
+ }
+}
+
+static int mxic_spi_nand_set_bft(struct spi_flash_chip *chip, uint8_t threshold)
+{
+ uint8_t val, bft;
+ int ret;
+
+ ret = spi_nand_read_reg(chip, 0x10, &val);
+ if (ret)
+ return ret;
+
+ bft = (val & 0xf0) >> 4;
+ obm_printf("%s: read BFT=0x%x threshold=%d\n", __func__, val, threshold);
+
+ if (bft != threshold) {
+ val = threshold << 4;
+ ret = spi_nand_write_reg(chip, 0x10, &val);
+ if (ret)
+ return ret;
+
+ obm_printf("%s: update BFT=0x%x\n", __func__, val);
+ }
+
+ return 0;
+}
+
+static void mxic_spi_nand_ecc_status(struct spi_flash_chip *chip,
+ uint8_t status, uint32_t *corrected, uint32_t *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, uint8_t *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 = qspi_start_cmd(chip, &cmd);
+ if (ret < 0)
+ obm_printf("err: %d read eccsr register\r\n", ret);
+
+ return ret;
+}
+
+static void mxic2_spi_nand_ecc_status(struct spi_flash_chip *chip,
+ uint8_t status, uint32_t *corrected, uint32_t *ecc_error)
+{
+ uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ uint8_t 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,
+ uint8_t status, uint32_t *corrected,
+ uint32_t *ecc_error)
+{
+ uint8_t 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:
+ obm_printf("%s: unexpected status: %d\r\n",
+ __func__, ecc_status);
+ *ecc_error = 1;
+ break;
+ }
+}
+
+static void fm_spi_nand_ecc_status(struct spi_flash_chip *chip,
+ uint8_t status, uint32_t *corrected,
+ uint32_t *ecc_error)
+{
+ uint8_t 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, uint8_t status,
+ uint32_t *corrected, uint32_t *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~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, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error)
+{
+ uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ uint8_t 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, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error)
+{
+ uint8_t 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:
+ obm_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, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error)
+{
+ uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ uint8_t ext_status;
+ int ret;
+
+ *ecc_error = 0;
+ *corrected = 0;
+ if (ecc_status == 0x1) {
+ ret = spi_nand_read_reg(chip, 0xf0, &ext_status);
+ if (ret) {
+ obm_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, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error)
+{
+ uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ uint8_t 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) {
+ obm_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 {
+ obm_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, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error)
+{
+ uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ uint8_t ext_status;
+ int ret;
+
+ *ecc_error = 0;
+ *corrected = 0;
+ if (ecc_status == 0x1) {
+ ret = spi_nand_read_reg(chip, 0x30, &ext_status);
+ if (ret) {
+ obm_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, uint8_t status,
+ uint32_t *corrected, uint32_t *ecc_error)
+{
+ uint8_t ecc_status = (status & SPI_NAND_ECC_MASK) >> SPI_NAND_ECC_SHIFT;
+ uint8_t ext_status;
+ int ret;
+
+ *ecc_error = 0;
+ *corrected = 0;
+ if (ecc_status == 0x1) {
+ ret = spi_nand_read_reg(chip, 0x30, &ext_status);
+ if (ret) {
+ obm_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 qspi_start_cmd(chip, &cmd);
+}
+
+/**
+ * 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,
+ uint32_t 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] = (uint8_t)(page_addr >> 16);
+ cmd.addr[1] = (uint8_t)(page_addr >> 8);
+ cmd.addr[2] = (uint8_t)page_addr;
+ cmd.flag = RST_AHB_DOMAIN | AHB_MAP_SIZE_PAGE;
+
+ return qspi_start_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,
+ uint32_t page_addr, uint32_t column,
+ uint32_t len, uint8_t *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->host->xip_read) {
+ uint32_t addr;
+
+ addr = column;
+ addr += chip->host->cs_addr[chip->cs];
+ memcpy(rbuf, (void *)addr, 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] = (uint8_t)(column >> 8);
+ cmd.addr[1] = (uint8_t)column;
+ cmd.n_rx = len;
+ cmd.rx_buf = rbuf;
+
+ return qspi_start_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,
+ uint32_t page_addr, uint32_t column, uint32_t len,
+ const uint8_t *wbuf, uint8_t 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] = (uint8_t)(column >> 8);
+ cmd.addr[1] = (uint8_t)column;
+ cmd.n_tx = len;
+ cmd.tx_buf = wbuf;
+ cmd.flag = RST_AHB_DOMAIN;
+
+ return qspi_start_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, uint32_t 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] = (uint8_t)(page_addr >> 16);
+ cmd.addr[1] = (uint8_t)(page_addr >> 8);
+ cmd.addr[2] = (uint8_t)page_addr;
+
+ return qspi_start_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,
+ uint32_t 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] = (uint8_t)(page_addr >> 16);
+ cmd.addr[1] = (uint8_t)(page_addr >> 8);
+ cmd.addr[2] = (uint8_t)page_addr;
+ cmd.flag = RST_AHB_DOMAIN | AHB_MAP_SIZE_PAGE;
+
+ return qspi_start_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, uint8_t *s)
+{
+ uint8_t 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, uint8_t *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 qspi_start_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 (qspi_start_cmd(chip, &cmd))
+ obm_printf("spi_nand reset failed!\r\n");
+
+ /* elapse 2ms before issuing any other command */
+ Delay(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, uint8_t 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, uint8_t mode)
+{
+ uint8_t 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 int 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->get_ecc_status = type->get_ecc_status;
+ chip->max_mhz = type->max_mhz;
+ chip->flash_info = type;
+
+ obm_printf("SPI-NAND: %s is found in table\r\n",
+ type->name);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static uint16_t onfi_crc16(uint16_t crc, uint8_t const *p, uint32_t 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, uint32_t 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;
+ uint8_t *buffer = NULL;
+ int read_cache_op;
+ int ret = true;
+ int i;
+
+ buffer = malloc(chip->block_size);
+ if(buffer == NULL) {
+ return HeapExhaustedError;
+ }
+
+ obm_printf("spi_nand_detect_onfi: buffer=0x%x\r\n", 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 * 3, 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, (uint8_t *)p, 254) ==
+ // (p->crc))
+ break;
+ }
+
+ if (i == 3) {
+ obm_printf("Could not find valid ONFI, use default settings\r\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 spi_flash_chip *chip, uint32_t page_addr,
+ uint32_t column, uint8_t ecc_off,
+ uint32_t *corrected, uint8_t *buf, uint32_t len)
+{
+ uint32_t ecc_error;
+ uint8_t status;
+ int ret;
+ int rx_len, i;
+
+ spi_nand_read_page_to_cache(chip, page_addr);
+ ret = spi_nand_wait(chip, &status);
+ if (ret < 0) {
+ obm_printf("error %d waiting page 0x%x to cache\r\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) {
+ obm_printf("internal ECC error reading page 0x%x"
+ " status=0x%x\r\n", page_addr, status);
+ ret = -EBADMSG;
+ return ret;
+ }
+ }
+
+ 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) {
+ obm_printf("Pass after the %dth retry\r\n", i);
+ }
+
+ column += real_len;
+ buf += real_len;
+ len -= real_len;
+ } while (len);
+
+ return ret;
+}
+
+static int spi_nand_do_read_page(struct spi_flash_chip *chip, uint32_t page_addr,
+ uint32_t column, uint8_t ecc_off,
+ uint32_t *corrected, uint8_t *buf, uint32_t len)
+{
+ uint8_t *tmp_buf = NULL;
+ int read_len;
+ int ret;
+
+ if (upload_nand_spare == TRUE)
+ len += chip->oob_size;
+
+ if (!column && len == chip->page_size)
+ return __spi_nand_do_read_page(chip, page_addr, column, ecc_off,
+ corrected, buf, len);
+
+ tmp_buf = malloc(chip->page_size + chip->oob_size);
+ if(tmp_buf == NULL)
+ return HeapExhaustedError;
+
+ read_len = chip->page_size;
+ if (column + len > chip->page_size)
+ read_len += chip->oob_size;
+
+ ret = __spi_nand_do_read_page(chip, page_addr, 0, ecc_off,
+ corrected, tmp_buf, read_len);
+ if (!ret)
+ memcpy(buf, tmp_buf + column, len);
+
+ free(tmp_buf);
+ return ret;
+}
+
+/**
+ * spi_nand_do_write_page - write data from buffer to flash
+ * @chip: spi nand chip 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
+ * Description:
+ * Page program sequence is as follows:
+ * 06h (WRITE ENABLE)
+ * 02h/32h/84h/34h (PROGRAM LOAD (RAMDOM_DATA) Xn)
+ * 10h (PROGRAM EXECUTE)
+ * 0Fh (GET FEATURE command to read the status)
+ * PROGRAM LOAD Xn instruction will reset the cache resigter with 0xFF,
+ * while PROGRAM LOAD RANDOM DATA Xn instruction will only update the
+ * data bytes that are specified by the command input sequence and the rest
+ * of data in the cache buffer will remain unchanged.
+ */
+static int spi_nand_do_write_page(struct spi_flash_chip *chip, uint32_t page_addr,
+ uint32_t column, const uint8_t *buf,
+ uint32_t len, uint8_t clr_cache)
+{
+ uint32_t column_save, len_save;
+ uint8_t *buf_save;
+ uint8_t status;
+ uint8_t p_fail = false;
+ int tx_len, i;
+ int ret = 0;
+ int cache_done = 0;
+
+ 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) {
+ obm_printf("error %d waiting page 0x%x to cache\r\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) {
+ obm_printf("Write after the %dth retry\r\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) {
+ obm_printf("error %d reading page 0x%x from cache\r\n",
+ ret, page_addr);
+ return ret;
+ }
+
+ if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
+ obm_printf("program page 0x%x failed\r\n", page_addr);
+ p_fail = true;
+ }
+
+ if (p_fail)
+ ret = -EIO;
+
+ return ret;
+}
+
+/**
+ * spi_nand_read_pages - read data from flash to buffer
+ * @chip: spi nand chip structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ * Description:
+ * Normal read function, read one page to buffer before issue
+ * another. Return -EUCLEAN when bitflip is over threshold.
+ * Return -EBADMSG when internal ecc can not correct bitflips.
+ */
+static int spi_nand_read_pages(struct spi_flash_chip *chip, uint32_t from,
+ uint32_t len, uint32_t *retlen, uint8_t *buf,
+ uint8_t ecc_off)
+{
+ int page_addr, page_offset, size;
+ int ret;
+ uint32_t corrected = 0;
+ uint32_t max_bitflip = 0;
+ int readlen = len;
+ uint32_t failed = 0;
+ uint32_t _retlen;
+
+ page_addr = from >> chip->page_shift;
+ page_offset = from & chip->page_mask;
+ _retlen = 0;
+
+ while (1) {
+ size = min(readlen, chip->page_size - page_offset);
+ ret = spi_nand_do_read_page(chip, page_addr, page_offset,
+ ecc_off, &corrected, buf + _retlen, size);
+ if (ret == -EBADMSG) {
+ failed++;
+ } else if (ret) {
+ obm_printf("error %d reading page 0x%x\r\n",
+ ret, page_addr);
+ goto out;
+ }
+ max_bitflip = max(corrected, max_bitflip);
+
+ _retlen += size;
+ if (upload_nand_spare == TRUE)
+ _retlen += chip->oob_size;
+ readlen -= size;
+ page_offset = 0;
+
+ if (!readlen)
+ break;
+ page_addr++;
+ }
+out:
+ *retlen = _retlen;
+
+#ifdef BITFLIPS_SCRUBBING
+ if (chip->refresh_threshold &&
+ max_bitflip >= chip->refresh_threshold)
+ ret = ReadDisturbError;
+#endif
+ if (failed)
+ ret = -EBADMSG;
+
+ return ret;
+}
+
+/**
+ * spi_nand_read - [Interface] SPI-NAND read
+ * @chip: spi nand device structure
+ * @from: offset to read from
+ * @len: number of bytes to read
+ * @retlen: pointer to variable to store the number of read bytes
+ * @buf: the databuffer to put data
+ */
+static int __spi_nand_read(struct spi_flash_chip *chip, uint32_t from,
+ uint32_t len, uint32_t *retlen, uint8_t *buf,
+ uint8_t ecc_off)
+{
+ int ret;
+
+ /* Do not allow reads past end of device */
+ if (from >= chip->size) {
+ obm_printf("%s: attempt to read beyond end of device\r\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (ecc_off)
+ spi_nand_disable_ecc(chip);
+
+ ret = spi_nand_read_pages(chip, from, len, retlen, buf, ecc_off);
+
+ if (ecc_off)
+ spi_nand_enable_ecc(chip);
+
+ return ret;
+}
+
+static int spi_nand_read(struct spi_flash_chip *chip,
+ int addr, int size, uint8_t *rbuf)
+{
+ uint32_t retlen;
+
+ return __spi_nand_read(chip, addr, size, &retlen, rbuf,
+ upload_disable_ecc);
+}
+
+/**
+ * spi_nand_write - [Interface] SPI-NAND write
+ * @chip: spi nand device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
+ */
+static int __spi_nand_write(struct spi_flash_chip *chip, uint32_t to,
+ uint32_t len, uint32_t *retlen, const uint8_t *buf,
+ uint8_t ecc_off)
+{
+ int page_addr, page_offset, size;
+ int writelen = len;
+ int ret = 0;
+ uint8_t clr_cache = true;
+ uint32_t _retlen;
+
+ /* Do not allow reads past end of device */
+ if (to >= chip->size) {
+ obm_printf("%s: attempt to write beyond end of device\r\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ page_addr = to >> chip->page_shift;
+ page_offset = to & chip->page_mask;
+ _retlen = 0;
+
+ if (ecc_off)
+ spi_nand_disable_ecc(chip);
+
+ while (1) {
+ size = min(writelen, chip->page_size - page_offset);
+ ret = spi_nand_do_write_page(chip, page_addr, page_offset,
+ buf + _retlen, size, clr_cache);
+ if (ret) {
+ obm_printf("error %d writing page 0x%x\r\n",
+ ret, page_addr);
+ goto out;
+ }
+
+ _retlen += size;
+ writelen -= size;
+ if (!writelen)
+ break;
+
+ page_offset = 0;
+ page_addr++;
+ }
+out:
+ *retlen = _retlen;
+ if (ecc_off)
+ spi_nand_enable_ecc(chip);
+
+ return ret;
+}
+
+static int spi_nand_write(struct spi_flash_chip *chip,
+ int addr, int size, uint8_t *wbuf)
+{
+ uint32_t retlen;
+
+ return __spi_nand_write(chip, addr, size, &retlen, wbuf, 0);
+}
+
+/**
+ * spi_nand_erase - [Interface] erase block(s)
+ * @chip: spi nand device structure
+ * @addr: address that erase start with, should be blocksize aligned
+ * @len: length that want to be erased, should be blocksize aligned
+ * Description:
+ * Erase one ore more blocks
+ * The command sequence for the BLOCK ERASE operation is as follows:
+ * 06h (WRITE ENBALE command)
+ * D8h (BLOCK ERASE command)
+ * 0Fh (GET FEATURES command to read the status register)
+ */
+int spi_nand_erase(struct spi_flash_chip *chip, uint32_t addr, uint32_t len)
+{
+ int page_addr, pages_per_block;
+ uint8_t status;
+ int ret = 0;
+
+ //obm_printf("%s: %d: addr=0x%x size=%d\r\n",
+ // __func__, __LINE__, addr, len);
+
+ /* check address align on block boundary */
+ if (addr & (chip->block_size - 1)) {
+ obm_printf("%s: Unaligned address\r\n", __func__);
+ return -EINVAL;
+ }
+
+ if (len & (chip->block_size - 1)) {
+ obm_printf("%s: Length not block aligned\r\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Do not allow erase past end of device */
+ if ((len + addr) > chip->size) {
+ obm_printf("%s: Erase past end of device len=%d"
+ " addr=0x%x size=0x%x\r\n",
+ __func__, len, addr, chip->size);
+ return -EINVAL;
+ }
+
+ pages_per_block = 1 << (chip->block_shift - chip->page_shift);
+ page_addr = addr >> chip->page_shift;
+
+ while (len) {
+ /* Check if we have a bad block, we do not erase bad blocks! */
+// if (spi_nand_block_isbad(chip, ((uint32_t) page_addr) <<
+// chip->page_shift)) {
+// obm_printf("%s: attempt to erase a bad block at 0x%x\r\n",
+// __func__, ((uint32_t) page_addr) << chip->page_shift);
+// goto erase_exit;
+// }
+ spi_nand_write_enable(chip);
+ spi_nand_erase_block(chip, page_addr);
+ ret = spi_nand_wait(chip, &status);
+ if (ret < 0) {
+ obm_printf("block erase command wait failed\r\n");
+ goto erase_exit;
+ }
+ if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
+ obm_printf("erase block 0x%x failed\r\n",
+ ((uint32_t) page_addr) << chip->page_shift);
+ ret = -EIO;
+ goto erase_exit;
+ }
+
+ /* Increment page address and decrement length */
+ len -= chip->block_size;
+ page_addr += pages_per_block;
+ }
+
+erase_exit:
+ 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,
+ uint32_t op_mode_rx, uint32_t op_mode_tx)
+{
+ struct spi_nand_info *type = (struct spi_nand_info *)chip->flash_info;
+ struct spi_flash_cmd_cfg *read_cmd;
+
+ if (op_mode_rx & 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 (op_mode_rx & SPI_OPM_RX_DUAL) {
+ chip->read_cache_op = READ_FROM_CACHE_DUAL;
+ } else {
+ if (chip->gd_ver_c)
+ chip->read_cache_op = READ_FROM_CACHE_GD_C;
+ else
+ chip->read_cache_op = READ_FROM_CACHE_FAST;
+ }
+
+ if (op_mode_tx & 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;
+ obm_printf("Set rx_pins: %d, tx_pins: %d, Read_CMD:0x%x\r\n",
+ op_mode_rx, op_mode_tx, read_cmd->opcode);
+}
+
+static int spi_nand_do_reset(FlashBootType_T fbt)
+{
+ struct spi_flash_chip *chip = &nand_chip;
+ (void) fbt;
+
+ return spi_nand_reset(chip);
+}
+
+static int spi_nand_do_read(UINT_T FlashOffset, UINT_T buffer, UINT_T size,
+ FlashBootType_T fbt)
+{
+ struct spi_flash_chip *chip = &nand_chip;
+ int ret;
+ (void) fbt;
+
+ ret = spi_nand_read(chip, FlashOffset, size, (UINT8_T *)buffer);
+ if (ret == -EINVAL) {
+ return InvalidAddressRangeError;
+ } else if (ret == -EBADMSG) {
+ return FlashReadEccError;
+ } else if (ret < 0) {
+ return ReadError;
+ }
+
+ return ret;
+}
+
+static int spi_nand_do_write(UINT_T FlashOffset, UINT_T buffer, UINT_T size,
+ FlashBootType_T fbt)
+{
+ struct spi_flash_chip *chip = &nand_chip;
+ int ret;
+ (void) fbt;
+
+ ret = spi_nand_write(chip, FlashOffset, size, (UINT8_T *)buffer);
+ if (ret == -EINVAL) {
+ return InvalidAddressRangeError;
+ } else if (ret < 0) {
+ return WriteError;
+ }
+
+ return ret;
+}
+
+static int spi_nand_do_erase(UINT_T FlashOffset, UINT_T size,
+ FlashBootType_T fbt)
+{
+ struct spi_flash_chip *chip = &nand_chip;
+ int ret;
+ (void) fbt;
+
+ ret = spi_nand_erase(chip, FlashOffset, size);
+ if (ret == -EINVAL) {
+ return InvalidAddressRangeError;
+ } else if (ret < 0) {
+ return EraseError;
+ }
+
+ return ret;
+}
+
+static int spi_nand_gen_fbbt(UINT16 *badblocklist)
+{
+ struct spi_flash_chip *chip = &nand_chip;
+ P_FlashProperties_T pFlashP = GetFlashProperties(BOOT_FLASH);
+ unsigned int i, max, bad_blks = 0;
+ uint8_t *buf = NULL;
+ uint32_t corrected = 0;
+ int page_addr, ret;
+
+ buf = malloc(chip->block_size);
+ if(buf == NULL)
+ return HeapExhaustedError;
+
+ /* Max number of relocation is 2% of device */
+ max = (pFlashP->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100;
+
+ /* Scan from block 1, block 0 is for TIM, should be good */
+ for (i = 1; i < pFlashP->NumBlocks; i++) {
+ page_addr = i << (chip->block_shift - chip->page_shift);
+ ret = spi_nand_do_read_page(chip, page_addr, chip->page_size,
+ 1, &corrected, buf, chip->oob_size);
+ if (ret) {
+ obm_printf("error %d reading page 0x%x\r\n",
+ ret, page_addr);
+ break;
+ }
+
+ if ((buf[0] & 0xFF) != 0xFF) {
+ badblocklist[bad_blks++] = i;
+ if(bad_blks >= max)
+ break;
+ }
+ }
+
+ if (bad_blks) {
+ obm_printf("Scan factory bad blocks:\r\n");
+ for (i = 0; i < bad_blks;) {
+ obm_printf("%-4d ", badblocklist[i]);
+ if (++i % 8 == 0)
+ obm_printf("\r\n");
+ }
+ obm_printf("\r\n");
+ }
+
+ free(buf);
+ return bad_blks;
+}
+
+static int spi_nand_exit()
+{
+ struct spi_flash_chip *chip = &nand_chip;
+
+#ifdef QSPI_SUPPORT_DQS
+ qspi_config_disable_dqs(chip->host);
+#endif
+ return NoError;
+}
+
+/**
+ * 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_init(struct qspi_host *host, int cs,
+ int rx_mode, int tx_mode)
+{
+ uint8_t id[SPINAND_MAX_ID_LEN] = {0};
+ struct spi_flash_chip *chip = &nand_chip;
+ int dev_in_tbl = 0;
+
+ memset(chip, 0, sizeof(struct spi_flash_chip));
+ chip->cs = cs >= QSPI_CS_MAX ? QSPI_CS_A1 : cs;
+ chip->host = host;
+ 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);
+
+ /* First check if this is a GD version-C spi-nand */
+ if (id[0] == SPIFLASH_MFR_GIGADEVICE) {
+ chip->mfr_id = id[0];
+ chip->dev_id = id[1] << 8 | id[2];
+ chip->gd_ver_c = 1;
+ obm_printf("GigaDeivce version-C spi-nand\r\n");
+ /* For debug, change to 1-bit read/write command*/
+ rx_mode = SPI_OPM_RX;
+ tx_mode = SPI_OPM_TX;
+ chip->max_mhz = 13;
+ } else {
+ chip->mfr_id = id[1];
+ chip->dev_id = id[2] | id[3] << 8;
+ chip->gd_ver_c = 0;
+ }
+
+ if (spi_nand_scan_id_table(chip))
+ dev_in_tbl = 1;
+
+ obm_printf("SPI-NAND type mfr_id: %x, dev_id: %x\r\n",
+ chip->mfr_id, chip->dev_id);
+
+ if (!chip->host->has_dtr)
+ chip->options &= ~SPINAND_SUPPORT_DTR;
+
+ spi_nand_set_rd_wr_op(chip, rx_mode, tx_mode);
+
+ /* 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);
+ /*
+ * TODO:
+ * Maybe change due to different vendor
+ */
+ qspi_enable_xip(chip, chip->table + chip->read_cache_op);
+ if (qspi_preinit_lookup_tbl(chip) < 0) {
+ obm_printf("preinit_lookup_tbl failed, check cmd table\r\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;
+ obm_printf("unknown device, set SPINAND_RDM_CMD_NEED_PAGE_READ\r\n");
+ if (spi_nand_detect_onfi(chip))
+ obm_printf("nand support onfi\r\n");
+
+ 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;
+
+ obm_printf("block_size=0x%x page_size=0x%x bitflip_threshold=%d\r\n",
+ chip->block_size, chip->page_size, chip->refresh_threshold);
+ return chip;
+}
+
+#ifdef SPI_FLASH_DQS_SCAN_WIN
+static void spi_nand_scan_dqs_window(struct spi_flash_chip *chip, int freq)
+{
+ struct qspi_host *host = chip->host;
+ uint8_t *buffer = NULL;
+ uint8_t pattern = 0x5a;
+ int flash_offset = 0x0;
+ int min, max;
+ int len = 0, avg, i;
+ int fail;
+
+ if (chip->options & SPINAND_SUPPORT_DTR) {
+ obm_printf("==> DQS not support DTR mode\r\n");
+ return;
+ }
+
+ buffer = malloc(chip->page_size);
+ memset(buffer, pattern, chip->page_size);
+
+ /* switch qspi bus to 13M to write pattern data */
+ qspi_set_func_clk(host, 13, 0, 0, 0, 0);
+
+ spi_nand_do_erase(flash_offset, chip->block_size, BOOT_FLASH);
+ spi_nand_do_write(flash_offset, buffer, chip->page_size, BOOT_FLASH);
+
+ /* switch qspi bus back to high frequency to scan valid window */
+ qspi_set_func_clk(host, freq, 0, 0, 0, 0);
+
+ min = 0;
+ len = 0;
+ do {
+ while (min <= 0xff) {
+ memset(buffer, 0x0, chip->page_size);
+ qspi_config_dqs_delay(chip->host, min);
+ spi_nand_do_read(flash_offset, buffer, chip->page_size, BOOT_FLASH);
+
+ fail = 0;
+ for (i = 0; i < chip->page_size; i++) {
+ if (buffer[i] != pattern) {
+ fail = 1;
+ break;
+ }
+ }
+
+ if (!fail)
+ break;
+
+ min++;
+ }
+
+ if (min > 0xff)
+ break;
+
+ max = min + 1;
+ while (max <= 0xff) {
+ memset(buffer, 0x0, chip->page_size);
+ qspi_config_dqs_delay(chip->host, max);
+ spi_nand_do_read(flash_offset, buffer, chip->page_size, BOOT_FLASH);
+
+ fail = 0;
+ for (i = 0; i < chip->page_size; i++) {
+ if (buffer[i] != pattern) {
+ fail = 1;
+ break;
+ }
+ }
+
+ if (fail)
+ break;
+
+ max++;
+ }
+
+ if (max - min > len) {
+ len = max - min;
+ avg = (min + max - 1) / 2;
+ }
+
+ obm_printf("pass window [%d : %d], len = %d\r\n",
+ min, max - 1, max - min);
+ min = max + 1;
+ } while (min <= 0xff);
+
+ obm_printf("max window len = %d, at %d\r\n", len, avg);
+ qspi_config_dqs_delay(chip->host, avg);
+
+ free(buffer);
+}
+#endif
+
+/***********************************************************
+* InitializeQSPINAND()
+* Initializes the SSP port on the platform and issues
+* a Read ID command to see if a device is connected
+* returns:
+* NoError - on a successful read ID
+* NotFoundError - when read ID value is bogus (0xFFFF or 0x0000)
+************************************************************/
+UINT_T InitializeQSPINAND(UINT8_T FlashNum, FlashBootType_T FlashBootType,
+ UINT8_T* P_DefaultPartitionNum)
+{
+ P_FlashProperties_T pFlashP = GetFlashProperties(FlashBootType);
+ struct spi_flash_chip *chip;
+ struct qspi_host *host;
+ struct spi_nand_info *type;
+ int tclqv, tset, thold;
+#if KSTR
+ int freq = 52;
+#else
+ int freq = 78;
+#endif
+
+ /*
+ * Use low frequency during init and increase later, since some
+ * device max may not support such large frequency
+ */
+ host = qspi_host_init(0, 13, 1);
+ chip = spi_nand_init(host, 0, SPI_OPM_RX_QUAD, SPI_OPM_TX_QUAD);
+ if (!chip)
+ return NotFoundError;
+
+ if (chip->max_mhz && freq > chip->max_mhz) {
+ obm_printf("warn: device max frequency is %d MHz!!!\r\n",
+ chip->max_mhz);
+ freq = chip->max_mhz;
+ }
+
+ /* flash_info may be inited in spi_nand_init */
+ type = (struct spi_nand_info *)chip->flash_info;
+ if (type) {
+ tclqv = type->tclqv;
+ tset = type->tset;
+ thold = type->thold;
+ } else {
+ tclqv = 0;
+ tset = 0;
+ thold = 0;
+ }
+
+ if (!PlatformIsFPGA())
+ qspi_set_func_clk(host, freq,
+ (chip->options & SPINAND_SUPPORT_DTR),
+ tclqv, tset, thold);
+
+ //define functions
+ pFlashP->ReadFromFlash = spi_nand_do_read;
+ pFlashP->WriteToFlash = spi_nand_do_write;
+ pFlashP->EraseFlash = spi_nand_do_erase;
+ pFlashP->FinalizeFlash = spi_nand_exit;
+ pFlashP->ResetFlash = spi_nand_do_reset;
+ pFlashP->GenerateFBBT = spi_nand_gen_fbbt;
+ pFlashP->FlashSettings.UseBBM = 1;
+ pFlashP->FlashSettings.UseSpareArea = 0;
+ pFlashP->PageSize = chip->page_size;
+ pFlashP->BlockSize = chip->block_size;
+ pFlashP->FlashSettings.SASize = chip->oob_size;
+ pFlashP->FlashSettings.UseHwEcc = 0;
+ pFlashP->StreamingFlash = FALSE;
+ pFlashP->FlashType = SPI_NAND;
+ pFlashP->FinalizeFlash = NULL;
+ pFlashP->TimFlashAddress = 0;
+ pFlashP->NumBlocks = chip->size >> chip->block_shift;
+
+ if (!host->en_tx_dma)
+ chip->tx_max_len = QSPI_TX_BUFF_MAX << 2;
+ else
+ chip->tx_max_len = chip->page_size;
+
+ if (chip->host->xip_read)
+ chip->rx_max_len = chip->page_size;
+ else
+ chip->rx_max_len = QSPI_RX_BUFF_MAX << 2;
+
+ *P_DefaultPartitionNum = 0;
+ obm_printf("pFlashP->NumBlocks: %d\r\n", pFlashP->NumBlocks);
+
+#ifdef SPI_FLASH_DQS_SCAN_WIN
+ spi_nand_scan_dqs_window(chip, freq);
+#endif
+ return NoError;
+}