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;
+}