ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/uboot/drivers/mtd/spi-flash/spi_nor.c b/marvell/uboot/drivers/mtd/spi-flash/spi_nor.c
new file mode 100644
index 0000000..1eb6595
--- /dev/null
+++ b/marvell/uboot/drivers/mtd/spi-flash/spi_nor.c
@@ -0,0 +1,1287 @@
+#include <common.h>
+#include <malloc.h>
+#include <linux/mtd/mtd.h>
+#include <asm-generic/errno.h>
+#ifdef CONFIG_BBM
+#include <mtd/pxa3xx_bbm.h>
+#include <linux/mtd/bbm.h>
+#include <linux/mtd/nand.h>
+#include <ubi_uboot.h>
+#endif
+#include <spi_flash_chip.h>
+#include <spi_nor.h>
+
+enum normal_cmd {
+	ENABLE_4BYTE,
+	DISABLE_4BYTE,
+	READ_STATUS1,
+	READ_STATUS2,
+	READ_STATUS3,
+	READ_STATUS2_MXIC,
+	WRITE_STATUS,
+	WRITE_STATUS2,
+	READ_SLOW,
+	READ_FAST,
+	READ_FAST_X2,
+	READ_FAST_X4,
+	READ_FAST_DUAL,
+	READ_FAST_QUAD,
+	SECTOR_ERASE_4K,
+	BLK_ERASE_32K,
+	BLK_ERASE_64K,
+	CHIP_ERASE,
+	PROG,
+	PROG_X4,
+	PROG_X4_MXIC,
+	WR_ENABLE,
+	WR_DISABLE,
+	READ_ID,
+	ENABLE_QPI,
+	ENABLE_QPI_MXIC,
+	PGM_ERS_SUSPEND,
+	PGM_ERS_RESUME,
+	PGM_ERS_SUSPEND_MXIC,
+	PGM_ERS_RESUME_MXIC,
+	EN_RST,
+	RESET,
+	MAX_CMD,
+};
+
+enum qspi_cmd {
+	ENABLE_4BYTE_QPI,
+	DISABLE_4BYTE_QPI,
+	READ_STATUS1_QPI,
+	READ_STATUS2_QPI,
+	READ_STATUS3_QPI,
+	READ_STATUS2_MXIC_QPI,
+	WRITE_STATUS_QPI,
+	WRITE_STATUS2_QPI,
+	READ_FAST_QPI,
+	READ_FAST_QUAD_QPI,
+	SECTOR_ERASE_4K_QPI,
+	BLK_ERASE_32K_QPI,
+	BLK_ERASE_64K_QPI,
+	CHIP_ERASE_QPI,
+	PROG_QPI,
+	WR_ENABLE_QPI,
+	WR_DISABLE_QPI,
+	READ_ID_QPI,
+	READ_QPIID,
+	DISABLE_QPI,
+	DISABLE_QPI_MXIC,
+	SET_READ_PARA_QPI,
+	PGM_ERS_SUSPEND_QPI,
+	PGM_ERS_RESUME_QPI,
+	PGM_ERS_SUSPEND_MXIC_QPI,
+	PGM_ERS_RESUME_MXIC_QPI,
+	EN_RST_QPI,
+	RESET_QPI,
+	MAX_CMD_QPI,
+};
+
+static struct spi_nor_info spi_nor_table[] = {
+	/* Macronix */
+	SPI_NOR_INFO("MX25U25643G", 0xC2, 0x2539, 256, 256, 32*1024*1024,
+		SPINOR_QE_USE_BIT6, 0,
+		READ_FAST_X4),
+	SPI_NOR_INFO("MX25U12835", 0xC2, 0x2538, 256, 256, 16*1024*1024,
+		SPINOR_QE_USE_BIT6, 0,
+		READ_FAST_X4),
+	SPI_NOR_INFO("MX25U3273", 0xC2, 0x2536, 256, 256, 4*1024*1024,
+		SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
+		READ_FAST_X4),
+
+	/* GigaDeivce */
+	SPI_NOR_INFO("GD25LQ256C", 0xC8, 0x6019, 256, 256, 32*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("GD25LQ128D", 0xC8, 0x6018, 256, 256, 16*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("GD25LQ64C", 0xC8, 0x6017, 256, 256, 8*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("GD25LE32E", 0xC8, 0x6016, 256, 256, 4*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+
+	/* Winbond */
+	SPI_NOR_INFO("W25Q256JW", 0xEF, 0x6019, 256, 256, 32*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("W25Q256JW-IM*", 0xEF, 0x8019, 256, 256, 32*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("W25Q128JW", 0xEF, 0x6018, 256, 256, 16*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("W25Q64FW", 0xEF, 0x6017, 256, 256, 8*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+
+	/* Dosilicon */
+	SPI_NOR_INFO("FM25M4AA", 0xF8, 0x4218, 256, 256, 16*1024*1024, 0, 0,
+		READ_FAST_X4),
+
+	/* DouQi */
+	SPI_NOR_INFO("DQ25Q128AL", 0x54, 0x6018, 256, 256, 16*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2, 0,
+		READ_FAST_X4),
+
+	/* Puya */
+	SPI_NOR_INFO("P25Q128L", 0x85, 0x6018, 256, 256, 16*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2, 78,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("P25Q64LE", 0x85, 0x6017, 256, 256, 8*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2, 0,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("P25Q32SL", 0x85, 0x6016, 256, 256, 4*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2, 0,
+		READ_FAST_QUAD),
+	SPI_NOR_INFO("P25Q128LA", 0x85, 0x6518, 256, 256, 16*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2 | SPINOR_WRITE_STATUS1_1BYTE, 0,
+		READ_FAST_QUAD),
+
+	/* ZettaDevice */
+	SPI_NOR_INFO("ZD25Q128", 0xBA, 0x4218, 256, 256, 16*1024*1024, 0, 0,
+		READ_FAST_QUAD),
+
+	/* Wuhan XinXin */
+	SPI_NOR_INFO("XM25QU64B", 0x20, 0x5017, 256, 256, 8*1024*1024,
+		SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
+		READ_FAST_X4),
+	SPI_NOR_INFO("XM25QU64C", 0x20, 0x4117, 256, 256, 8*1024*1024,
+		0, 0,
+		READ_FAST_X4),
+	SPI_NOR_INFO("XM25QU128B", 0x20, 0x5018, 256, 256, 16*1024*1024,
+		SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
+		READ_FAST_X4),
+	SPI_NOR_INFO("XM25QU128C", 0x20, 0x4118, 256, 256, 16*1024*1024,
+		0, 0,
+		READ_FAST_X4),
+	SPI_NOR_INFO("XM25QU256B", 0x20, 0x7019, 256, 256, 32*1024*1024,
+		SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
+		READ_FAST_X4),
+	SPI_NOR_INFO("XM25LU32C", 0x20, 0x5016, 256, 256, 4*1024*1024,
+		0, 0,
+		READ_FAST_X4),
+
+	/* Fudan Microelectronics */
+	SPI_NOR_INFO("FM25W128", 0xA1, 0x2818, 256, 256, 16*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2, 52,
+		READ_FAST_X4),
+
+	/* Zbit Semiconductor */
+	SPI_NOR_INFO("ZB25LQ64A", 0x5E, 0x7017, 256, 256, 8*1024*1024,
+		0, 0, READ_FAST_QUAD),
+	SPI_NOR_INFO("ZB25LQ128", 0x5E, 0x5018, 256, 256, 16*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2, 0,
+		READ_FAST_QUAD),
+
+	/* XTX */
+	SPI_NOR_INFO("XT25Q128D", 0x0B, 0x6018, 256, 256, 16*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2 | SPINOR_WRITE_STATUS1_1BYTE, 0,
+		READ_FAST_QUAD),
+
+	/* Elite */
+	SPI_NOR_INFO("EN25SX128A", 0x1c, 0x7818, 256, 256, 16*1024*1024,
+		SPINOR_HAVE_WRITE_STATUS2 | SPINOR_WRITE_STATUS1_1BYTE, 0,
+		READ_FAST_X4),
+
+	/* Silicon Kaiser */
+	SPI_NOR_INFO("SK25LP128", 0x17, 0x7018, 256, 256, 16*1024*1024,
+		SPINOR_QE_USE_BIT6 | SPINOR_WRITE_STATUS1_1BYTE, 0,
+		READ_FAST_X4),
+	{.name = NULL},
+};
+
+/**
+ * spi_nor_scan_id_table - scan chip info in id table
+ * @chip: SPI-NOR device structure
+ * Description:
+ *   If found in id table, config chip with table information.
+ */
+static int spi_nor_scan_id_table(struct spi_flash_chip *chip)
+{
+	struct spi_nor_info *type = spi_nor_table;
+
+	for (; type->name; type++) {
+		if (chip->mfr_id == type->mfr_id && chip->dev_id == type->dev_id) {
+			chip->name = type->name;
+			chip->size = type->total_size;
+			chip->block_size = type->page_size
+					* type->pages_per_blk;
+			chip->page_size = type->page_size;
+			chip->options = type->options;
+			chip->max_mhz = type->max_mhz;
+			chip->flash_info = type;
+			pr_info("SPI-NOR: %s is found in table\r\n",
+					type->name);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/* Standard SPI-NOR flash normal 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 */
+	[ENABLE_4BYTE]		= SPI_CMD(0xb7, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[DISABLE_4BYTE]		= SPI_CMD(0xe9, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[READ_STATUS1]		= SPI_CMD(0x05, 0, 0, 0, 0, 0, 0, 1,  2, 1),
+	[READ_STATUS2]		= SPI_CMD(0x35, 0, 0, 0, 0, 0, 0, 1, -1, 1),
+	[READ_STATUS3]		= SPI_CMD(0x15, 0, 0, 0, 0, 0, 0, 1, -1, 1),
+	[WRITE_STATUS]		= SPI_CMD(0x01, 0, 0, 0, 0, 0, 0, 1, -1, 2),
+	[WRITE_STATUS2]		= SPI_CMD(0x31, 0, 0, 0, 0, 0, 0, 1, -1, 2),
+	[READ_SLOW]		= SPI_CMD(0x03, 3, 1, 0, 0, 0, 0, 1, -1, 1),
+	[READ_FAST]		= SPI_CMD(0x0b, 3, 1, 0, 0, 8, 1, 1,  3, 1),
+	[READ_FAST_X2]		= SPI_CMD(0x3b, 3, 1, 0, 0, 8, 1, 2, -1, 1),
+	[READ_FAST_X4]		= SPI_CMD(0x6b, 3, 1, 0, 0, 8, 1, 4,  4, 1),
+	[READ_FAST_DUAL]	= SPI_CMD(0xbb, 3, 2, 8, 2, 0, 0, 2, -1, 1),
+	[READ_FAST_QUAD]	= SPI_CMD(0xeb, 3, 4, 8, 4, 4, 4, 4,  5, 1),
+	[SECTOR_ERASE_4K]	= SPI_CMD(0x20, 3, 1, 0, 0, 0, 0, 0, -1, 0),
+	[BLK_ERASE_32K]		= SPI_CMD(0x52, 3, 1, 0, 0, 0, 0, 0, -1, 0),
+	[BLK_ERASE_64K]		= SPI_CMD(0xd8, 3, 1, 0, 0, 0, 0, 0, -1, 0),
+	[CHIP_ERASE]		= SPI_CMD(0xc7, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PROG]			= SPI_CMD(0x02, 3, 1, 0, 0, 0, 0, 1,  6, 2),
+	[PROG_X4]		= SPI_CMD(0x32, 3, 1, 0, 0, 0, 0, 4,  7, 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),
+	[ENABLE_QPI]		= SPI_CMD(0x38, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PGM_ERS_SUSPEND]	= SPI_CMD(0x75, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PGM_ERS_RESUME]	= SPI_CMD(0x7a, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[EN_RST]		= SPI_CMD(0x66, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[RESET]			= SPI_CMD(0x99, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+
+	/* Vendor specific command */
+	[READ_STATUS2_MXIC]	= SPI_CMD(0x15, 0, 0, 0, 0, 0, 0, 1, -1, 1),
+	[PROG_X4_MXIC]		= SPI_CMD(0x38, 3, 4, 0, 0, 0, 0, 4,  8, 2),
+	[ENABLE_QPI_MXIC]	= SPI_CMD(0x35, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PGM_ERS_SUSPEND_MXIC]	= SPI_CMD(0xb0, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PGM_ERS_RESUME_MXIC]	= SPI_CMD(0x30, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+
+	/* END Mark */
+	[MAX_CMD]		= SPI_CMD(0x00, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+};
+
+#ifdef QSPINOR_ENABLE_QPI_MODE
+/* Standard SPI-NOR flash QPI commands */
+static struct spi_flash_cmd_cfg cmd_table_qpi[] = {
+	/*opcode  addr_bytes  addr_pins mode_bits  mode_pins  dummy_cycles
+		dummy_pins   data_pins  seq_id  cmd_type */
+	[ENABLE_4BYTE_QPI]	= SPI_CMD(0xb7, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[DISABLE_4BYTE_QPI]	= SPI_CMD(0xe9, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[READ_STATUS1_QPI]	= SPI_CMD(0x05, 0, 0, 0, 0, 0, 0, 4,  2, 1),
+	[READ_STATUS2_QPI]	= SPI_CMD(0x35, 0, 0, 0, 0, 0, 0, 4, -1, 1),
+	[READ_STATUS3_QPI]	= SPI_CMD(0x11, 0, 0, 0, 0, 0, 0, 4, -1, 1),
+	[WRITE_STATUS_QPI]	= SPI_CMD(0x01, 0, 0, 0, 0, 0, 0, 4, -1, 2),
+	[WRITE_STATUS2_QPI]	= SPI_CMD(0x31, 0, 0, 0, 0, 0, 0, 4, -1, 2),
+	[READ_FAST_QPI]		= SPI_CMD(0x0b, 3, 4, 0, 0, 8, 4, 4,  3, 1),
+	[READ_FAST_QUAD_QPI]	= SPI_CMD(0xeb, 3, 4, 0, 0, 8, 4, 4,  4, 1),
+	[SECTOR_ERASE_4K_QPI]	= SPI_CMD(0x20, 3, 4, 0, 0, 0, 0, 0, -1, 0),
+	[BLK_ERASE_32K_QPI]	= SPI_CMD(0x52, 3, 4, 0, 0, 0, 0, 0, -1, 0),
+	[BLK_ERASE_64K_QPI]	= SPI_CMD(0xd8, 3, 4, 0, 0, 0, 0, 0, -1, 0),
+	[CHIP_ERASE_QPI]	= SPI_CMD(0xc7, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PROG_QPI] 		= SPI_CMD(0x02, 3, 4, 0, 0, 0, 0, 4,  5, 2),
+	[WR_ENABLE_QPI]		= SPI_CMD(0x06, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[WR_DISABLE_QPI]	= SPI_CMD(0x04, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[READ_ID_QPI]		= SPI_CMD(0x9f, 0, 0, 0, 0, 0, 0, 4, -1, 1),
+	[READ_QPIID]		= SPI_CMD(0xaf, 0, 0, 0, 0, 0, 0, 4, -1, 1),
+	[DISABLE_QPI]		= SPI_CMD(0xff, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[SET_READ_PARA_QPI]	= SPI_CMD(0xc0, 0, 0, 0, 0, 0, 0, 4, -1, 2),
+	[PGM_ERS_SUSPEND_QPI]	= SPI_CMD(0x75, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PGM_ERS_RESUME_QPI]	= SPI_CMD(0x7a, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[EN_RST_QPI]		= SPI_CMD(0x66, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[RESET_QPI]		= SPI_CMD(0x99, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+
+	/* Vendor specific command */
+	[READ_STATUS2_MXIC_QPI]	= SPI_CMD(0x15, 0, 0, 0, 0, 0, 0, 4, -1, 1),
+	[DISABLE_QPI_MXIC]	= SPI_CMD(0xf5, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PGM_ERS_SUSPEND_MXIC_QPI]= SPI_CMD(0xb0, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+	[PGM_ERS_RESUME_MXIC_QPI]= SPI_CMD(0x30, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+
+	/* END Mark */
+	[MAX_CMD_QPI]		= SPI_CMD(0x00, 0, 0, 0, 0, 0, 0, 0, -1, 0),
+};
+#endif
+
+/**
+ * spi_nor_read_id - send 9Fh command to get ID
+ * @chip: SPI_FLASH device structure
+ * @buf: buffer to store id
+ */
+static int spi_nor_read_id(struct spi_flash_chip *chip, u8 *buf)
+{
+	struct spi_flash_cmd cmd;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + READ_ID_QPI;
+	else
+		cmd.cmd_cfg = chip->table + READ_ID;
+	cmd.n_rx = 3;
+	cmd.rx_buf = buf;
+
+	return chip->issue_cmd(chip, &cmd);
+}
+
+static int spi_nor_read_qpiid(struct spi_flash_chip *chip, u8 *buf)
+{
+	struct spi_flash_cmd cmd;
+
+	if (!chip->qpi_enabled) {
+		pr_info("err: not in qpi mode\n");
+		return -1;		
+	}
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	cmd.cmd_cfg = chip->table + READ_QPIID;
+	cmd.n_rx = 3;
+	cmd.rx_buf = buf;
+
+	return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nor_reset - send command to reset chip.
+ * @chip: SPI_FLASH device structure
+ */
+static int spi_nor_reset(struct spi_flash_chip *chip)
+{
+	struct spi_flash_cmd cmd;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + EN_RST_QPI;
+	else
+		cmd.cmd_cfg = chip->table + EN_RST;
+	if (chip->issue_cmd(chip, &cmd) < 0)
+		pr_info("spi_nor enable reset failed!\n");
+
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + RESET_QPI;
+	else
+		cmd.cmd_cfg = chip->table + RESET;
+	if (chip->issue_cmd(chip, &cmd) < 0)
+		pr_info("spi_nor reset failed!\n");
+
+	/* elapse 2ms before issuing any other command */
+	udelay(2000);
+	return 0;
+}
+
+/**
+ * spi_nor_write_enable - send command 06h to enable write or erase the
+ * Nand cells
+ * @chip: SPI_FLASH 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_nor_write_enable(struct spi_flash_chip *chip)
+{
+	struct spi_flash_cmd cmd;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + WR_ENABLE_QPI;
+	else
+		cmd.cmd_cfg = chip->table + WR_ENABLE;
+
+	return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nor_read_status - get status register value
+ * @chip: SPI_FLASH 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_nor_read_status(struct spi_flash_chip *chip, u8 index,
+				u8 *status)
+{
+	struct spi_flash_cmd cmd;
+	int ret;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	cmd.cmd_cfg = chip->table + index;
+	cmd.n_rx = 1;
+	cmd.rx_buf = status;
+
+	ret = chip->issue_cmd(chip, &cmd);
+	if (ret < 0)
+		pr_info("err: read register %d\n", ret);
+
+	return ret;
+}
+
+static int spi_nor_read_status1(struct spi_flash_chip *chip, u8 *status)
+{
+	u8 cmd_index;
+
+	if (chip->qpi_enabled)
+		cmd_index = READ_STATUS1_QPI;
+	else
+		cmd_index = READ_STATUS1;
+
+	return spi_nor_read_status(chip, cmd_index, status);
+}
+
+static int spi_nor_read_status2(struct spi_flash_chip *chip, u8 *status)
+{
+	u8 cmd_index;
+
+	if (chip->mfr_id == SPIFLASH_MFR_MXIC) {
+		/* Not support SPINOR_CMD_READ_STATUS2 for 32MB spi-nor */
+		if (chip->dev_id == 0x2536)
+			return 0;
+
+		if (chip->qpi_enabled)
+			cmd_index = READ_STATUS2_MXIC_QPI;
+		else
+			cmd_index = READ_STATUS2_MXIC;
+	} else {
+		if (chip->qpi_enabled)
+			cmd_index = READ_STATUS2_QPI;
+		else
+			cmd_index = READ_STATUS2;
+	}
+
+	return spi_nor_read_status(chip, cmd_index, status);
+}
+
+static int spi_nor_read_status3(struct spi_flash_chip *chip, u8 *status)
+{
+	u8 cmd_index;
+
+	if (chip->qpi_enabled)
+		cmd_index = READ_STATUS3_QPI;
+	else
+		cmd_index = READ_STATUS3;
+
+	return spi_nor_read_status(chip, cmd_index, status);
+}
+
+/**
+ * spi_nor_write_reg - send command 1Fh to write register
+ * @chip: SPI_FLASH device structure
+ * @reg; register to write
+ * @buf: buffer stored value
+ */
+/**
+ * spi_nor_write_reg - send command 1Fh to write register
+ * @chip: SPI_FLASH device structure
+ * @reg; register to write
+ * @buf: buffer stored value
+ */
+static int spi_nor_write_status1(struct spi_flash_chip *chip, u8 *buf, u8 count)
+{
+	struct spi_flash_cmd cmd;
+	int ret;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + WRITE_STATUS_QPI;
+	else
+		cmd.cmd_cfg = chip->table + WRITE_STATUS;
+
+	cmd.n_tx = count;
+	cmd.tx_buf = buf;
+
+	ret = chip->issue_cmd(chip, &cmd);
+	if (ret < 0)
+		pr_info("err: %d write status\n", ret);
+
+	return ret;
+}
+
+static int spi_nor_write_status2(struct spi_flash_chip *chip, u8 *buf)
+{
+	struct spi_flash_cmd cmd;
+	int ret;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + WRITE_STATUS2_QPI;
+	else
+		cmd.cmd_cfg = chip->table + WRITE_STATUS2;
+
+	cmd.n_tx = 1;
+	cmd.tx_buf = buf;
+
+	ret = chip->issue_cmd(chip, &cmd);
+	if (ret < 0)
+		pr_info("err: %d write status2\n", ret);
+
+	return ret;
+}
+
+/**
+ * spi_nor_wait - wait until the command is done
+ * @chip: SPI_FLASH device structure
+ * @s: buffer to store status register(can be NULL)
+ */
+static int spi_nor_wait(struct spi_flash_chip *chip, u8 *s)
+{
+	u8 status;
+	unsigned long ret = -ETIMEDOUT;
+
+	while (1) {
+		spi_nor_read_status1(chip, &status);
+		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+			ret = 0;
+			goto out;
+		}
+	}
+out:
+	if (s)
+		*s = status;
+	return ret;
+}
+
+/**
+ * spi_nor_erase_block_erase - send command D8h to erase a block
+ * @chip: SPI_FLASH device structure
+ * @addr: the flash addr to erase.
+ * Description:
+ *   Need to wait for tERS.
+ */
+static int spi_nor_erase_sector(struct spi_flash_chip *chip,
+				u32 addr)
+{
+	struct spi_flash_cmd cmd;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + SECTOR_ERASE_4K_QPI;
+	else
+		cmd.cmd_cfg = chip->table + SECTOR_ERASE_4K;
+
+	if (chip->en_addr_4byte) {
+		cmd.n_addr = 4;
+		cmd.addr[0] = (u8)(addr >> 24);
+		cmd.addr[1] = (u8)(addr >> 16);
+		cmd.addr[2] = (u8)(addr >> 8);
+		cmd.addr[3] = (u8)addr;
+	} else {
+		cmd.n_addr = 3;
+		cmd.addr[0] = (u8)(addr >> 16);
+		cmd.addr[1] = (u8)(addr >> 8);
+		cmd.addr[2] = (u8)addr;
+	}
+	cmd.flag = RST_AHB_DOMAIN;
+
+	return chip->issue_cmd(chip, &cmd);
+}
+
+/**
+ * spi_nor_erase_block_erase - send command D8h to erase a block
+ * @chip: SPI_FLASH device structure
+ * @addr: the flash addr to erase.
+ * Description:
+ *   Need to wait for tERS.
+ */
+static int spi_nor_erase_block(struct spi_flash_chip *chip, u32 addr, u8 opcode)
+{
+	struct spi_flash_cmd cmd;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	cmd.cmd_cfg = chip->table + opcode;
+	if (chip->en_addr_4byte) {
+		cmd.n_addr = 4;
+		cmd.addr[0] = (u8)(addr >> 24);
+		cmd.addr[1] = (u8)(addr >> 16);
+		cmd.addr[2] = (u8)(addr >> 8);
+		cmd.addr[3] = (u8)addr;
+	} else {
+		cmd.n_addr = 3;
+		cmd.addr[0] = (u8)(addr >> 16);
+		cmd.addr[1] = (u8)(addr >> 8);
+		cmd.addr[2] = (u8)addr;
+	}
+	cmd.flag = RST_AHB_DOMAIN;
+
+	return chip->issue_cmd(chip, &cmd);
+}
+
+static int spi_nor_erase_32k_blk(struct spi_flash_chip *chip, u32 addr)
+{
+	u8 opcode;
+
+	if (chip->qpi_enabled)
+		opcode = BLK_ERASE_32K_QPI;
+	else
+		opcode = BLK_ERASE_32K;
+
+	return spi_nor_erase_block(chip, addr, opcode);
+}
+
+static int spi_nor_erase_64k_blk(struct spi_flash_chip *chip, u32 addr)
+{
+	u8 opcode;
+
+	if (chip->qpi_enabled)
+		opcode = BLK_ERASE_64K_QPI;
+	else
+		opcode = BLK_ERASE_64K;
+
+	return spi_nor_erase_block(chip, addr, opcode);
+}
+
+static int spi_nor_erase_chip(struct spi_flash_chip *chip)
+{
+	struct spi_flash_cmd cmd;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + CHIP_ERASE_QPI;
+	else
+		cmd.cmd_cfg = chip->table + CHIP_ERASE;
+	cmd.flag = RST_AHB_DOMAIN;
+
+	return chip->issue_cmd(chip, &cmd);
+}
+
+static int spi_nor_erase_all(struct spi_flash_chip *chip)
+{
+	u8 status;
+	int ret = 0;
+
+	spi_nor_write_enable(chip);
+	spi_nor_erase_chip(chip);
+	ret = spi_nor_wait(chip, &status);
+	if (ret < 0) {
+		pr_info("chip erase command wait failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int spi_nor_enable_4byte_mode(struct spi_flash_chip *chip)
+{
+	struct spi_flash_cmd cmd;
+	u8 data[3];
+	u8 en4b_shift;
+	u32 sdata;
+	int ret;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + ENABLE_4BYTE_QPI;
+	else
+		cmd.cmd_cfg = chip->table + ENABLE_4BYTE;
+
+	ret = chip->issue_cmd(chip, &cmd);
+	if (ret < 0) {
+		pr_info("enable_4byte mode failed\n");
+		return ret;
+	}
+
+	chip->en_addr_4byte = 1;
+	/*
+	 * Macronix use status register bit S13 for EN4B
+	 * Gigadevice use status register bit S11
+	 * WinBond use status register bit S17
+	 */
+	if (chip->mfr_id == SPIFLASH_MFR_MXIC)
+		en4b_shift = 13;
+	else if (chip->mfr_id == SPIFLASH_MFR_GIGADEVICE)
+		en4b_shift = 11;
+	else if (chip->mfr_id == SPIFLASH_MFR_WINBOND)
+		en4b_shift = 16;
+	else {
+		pr_info("EN4B: lack vendor status bit info\n");
+		return 0;
+	}
+
+	if (en4b_shift >= 16)
+		spi_nor_read_status3(chip, &data[2]);
+	else if (en4b_shift >= 8)
+		spi_nor_read_status2(chip, &data[1]);
+	else
+		spi_nor_read_status1(chip, &data[0]);
+
+	sdata = data[2] << 16 | data[1] << 8 | data[0];
+	if (!(sdata & (1 << en4b_shift))) {
+		pr_info("Failed to enable 4byte mode: bit%d\n", en4b_shift);
+		chip->en_addr_4byte = 0;
+		return -1;
+	}
+
+	pr_info("Enter 4byte address mode, bit%d\n", en4b_shift);
+	return 0;
+}
+
+static int spi_nor_disable_4byte_mode(struct spi_flash_chip *chip)
+{
+	struct spi_flash_cmd cmd;
+	int ret;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->qpi_enabled)
+		cmd.cmd_cfg = chip->table + DISABLE_4BYTE_QPI;
+	else
+		cmd.cmd_cfg = chip->table + DISABLE_4BYTE;
+
+	ret = chip->issue_cmd(chip, &cmd);
+	if (ret < 0) {
+		pr_info("Failed to disable 4byte_mode\n");
+		return ret;
+	}
+
+	chip->en_addr_4byte = 0;
+	return 0;
+}
+
+/**
+ * spi_nor_erase - [Interface] erase block(s)
+ * @chip: spi flash 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)
+ */
+static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	struct spi_flash_chip *chip = mtd->priv;
+	int addr = instr->addr;
+	int len = instr->len;
+	u8 status;
+	int erase_len;
+	int ret = 0;
+
+	//pr_info("%s: address = 0x%x, len = %d\n", __func__, addr, len);
+
+	/* check address align on 4K boundary */
+	if (addr & (4*1024 - 1)) {
+		pr_info("%s: Unaligned address\n", __func__);
+		return -EINVAL;
+	}
+
+	if (len & (4*1024 - 1)) {
+		pr_info("%s: Length not 4K-Sector aligned\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Do not allow erase past end of device */
+	if ((len + addr) > chip->size) {
+		pr_info("%s: Erase past end of device\n", __func__);
+		return -EINVAL;
+	}
+
+	while (len > 0) {
+		spi_nor_write_enable(chip);
+		if (len >= 0x10000 && !(addr & (0x10000 - 1))) {
+			erase_len = 0x10000;
+			spi_nor_erase_64k_blk(chip, addr);
+		} else if (len >= 0x8000 && !(addr & (0x8000 - 1))) {
+			erase_len = 0x8000;
+			spi_nor_erase_32k_blk(chip, addr);
+		} else {
+			erase_len = 0x1000;
+			spi_nor_erase_sector(chip, addr);
+		}
+		ret = spi_nor_wait(chip, &status);
+		if (ret < 0) {
+			pr_info("erase command wait failed, erase_len=0x%x\n",
+				erase_len);
+			break;
+		}
+
+		/* Increment page address and decrement length */
+		len -= erase_len;
+		addr += erase_len;
+		if (addr >= chip->size)
+			break;
+	}
+
+	if (!ret) {
+		instr->state = MTD_ERASE_DONE;
+		mtd_erase_callback(instr);
+	}
+
+	return ret;
+}
+
+static int __spi_nor_read(struct spi_flash_chip *chip, int addr,
+			int size, u8 *rbuf)
+{
+	struct spi_flash_cmd cmd;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	cmd.cmd_cfg = chip->table + chip->read_op;
+	if (chip->en_addr_4byte) {
+		cmd.n_addr = 4;
+		cmd.addr[0] = (u8)((addr >> 24) & 0xff);
+		cmd.addr[1] = (u8)((addr >> 16) & 0xff);
+		cmd.addr[2] = (u8)((addr >> 8) & 0xff);
+		cmd.addr[3] = (u8)(addr & 0xff);
+	} else {
+		cmd.n_addr = 3;
+		cmd.addr[0] = (u8)((addr >> 16) & 0xff);
+		cmd.addr[1] = (u8)((addr >> 8) & 0xff);
+		cmd.addr[2] = (u8)(addr & 0xff);
+	}
+
+	cmd.n_rx = size;
+	cmd.rx_buf = rbuf;
+	cmd.mode = 0xff;
+
+	return chip->issue_cmd(chip, &cmd);
+}
+
+#define SPI_NOR_MAX_RETRY	3
+static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
+			size_t *retlen, u8 *buf)
+{
+	struct spi_flash_chip *chip = mtd->priv;
+	int rx_len, i;
+	int size = len;
+	int ret;
+
+	/* Do not allow reads past end of device */
+	if ((from + size) > chip->size) {
+		pr_err("%s: attempt to read beyond end of device\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if (chip->xip_read && chip->memmap_read) {
+		chip->memmap_read(chip, buf, from, size);
+		*retlen = len;
+		return 0;
+	}
+
+	rx_len = chip->rx_max_len ? chip->rx_max_len : len;
+	do {
+		int real_len;
+
+		i = 0;
+retry:
+		real_len = min(size, rx_len);
+		ret = __spi_nor_read(chip, from, real_len, buf);
+		if (ret == -EAGAIN && ++i <= SPI_NOR_MAX_RETRY) {
+			rx_len = rx_len / 2;
+			goto retry;
+		} else if (ret) {
+			return -EIO;
+		} else if (i) {
+			pr_info("Pass after the %dth retry\n", i);
+		}
+
+		from += real_len;
+		buf += real_len;
+		len -= real_len;
+	} while (size);
+
+	*retlen = len;
+	return ret;
+}
+
+/**
+ * spi_nor_program_data_to_cache - write data to cache register
+ * @chip: SPI_FLASH 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_nor_program_data(struct spi_flash_chip *chip,
+				u32 addr, int len, const u8 *wbuf)
+{
+	struct spi_flash_cmd cmd;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	cmd.cmd_cfg = chip->table + chip->write_op;
+	if (chip->en_addr_4byte) {
+		cmd.n_addr = 4;
+		cmd.addr[0] = (u8)((addr >> 24) & 0xff);
+		cmd.addr[1] = (u8)((addr >> 16) & 0xff);;
+		cmd.addr[2] = (u8)((addr >> 8) & 0xff);;
+		cmd.addr[3] = (u8)(addr & 0xff);;
+	} else {
+		cmd.n_addr = 3;
+		cmd.addr[0] = (u8)((addr >> 16) & 0xff);
+		cmd.addr[1] = (u8)((addr >> 8) & 0xff);;
+		cmd.addr[2] = (u8)(addr & 0xff);;
+	}
+	cmd.n_tx = len;
+	cmd.tx_buf = wbuf;
+	cmd.flag = RST_AHB_DOMAIN;
+
+	return chip->issue_cmd(chip, &cmd);
+}
+
+static int spi_nor_write_page(struct spi_flash_chip *chip, int addr,
+				int size, u8 *buf)
+{
+	u8 status;
+	int ret = 0;
+
+	spi_nor_write_enable(chip);
+	spi_nor_program_data(chip, addr, size, buf);
+	ret = spi_nor_wait(chip, &status);
+	if (ret < 0) {
+		pr_info("error %d reading page 0x%x from cache\n",
+			ret, addr);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int __spi_nor_write(struct spi_flash_chip *chip,
+			    int addr, int size, const u8 *buf)
+{
+	int max_tx_len = chip->tx_max_len;
+	int len, ret = 0;
+
+	while (size) {
+		len = chip->page_size - (addr & chip->page_mask);
+		len = min(min(len, size), max_tx_len);
+
+		ret = spi_nor_write_page(chip, addr, len, buf);
+		if (ret < 0) {
+			pr_info("page program failed\n");
+			break;
+		}
+
+		/* Increment page address and decrement length */
+		size -= len;
+		buf += len;
+		addr += len;
+	}
+
+	return ret;
+}
+
+static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
+			 size_t *retlen, const u8 *buf)
+{
+	struct spi_flash_chip *chip = mtd->priv;
+	int ret;
+
+	ret = __spi_nor_write(chip, to, len, buf);
+	*retlen = len;
+
+	return ret;
+}
+
+static void spi_nor_set_rd_wr_op(struct spi_flash_chip *chip)
+{
+	struct spi_nor_info *type = (struct spi_nor_info *)chip->flash_info;
+	struct spi_flash_cmd_cfg *read_cmd;
+
+	if (chip->rx_mode & SPI_OPM_RX_QUAD) {
+		if (chip->qpi_enabled) {
+			chip->read_op = READ_FAST_QPI;
+		} else {
+			if (type)
+				chip->read_op = type->quad_cmd_index;
+			else
+				chip->read_op = READ_FAST_X4;
+		}
+	} else if (chip->rx_mode & SPI_OPM_RX_DUAL) {
+		chip->read_op = READ_FAST_DUAL;
+	} else {
+		chip->read_op = READ_FAST;
+	}
+
+	if (chip->tx_mode & SPI_OPM_TX_QUAD) {
+		if (chip->qpi_enabled) {
+			chip->write_op = PROG_QPI;
+		} else {
+			if (chip->mfr_id == SPIFLASH_MFR_MXIC)
+				chip->write_op = PROG_X4_MXIC;
+			else
+				chip->write_op = PROG_X4;
+		}
+	} else {
+		chip->write_op = PROG;
+	}
+
+	read_cmd = chip->table + chip->read_op;
+	pr_info("Set rx_pins: %d, tx_pins: %d, Read_CMD:0x%x\r\n",
+		chip->rx_mode, chip->tx_mode, read_cmd->opcode);
+}
+
+static int spi_set_read_parameters(struct spi_flash_chip *chip, u8 *buf)
+{
+	struct spi_flash_cmd cmd;
+	int ret = 0;
+
+	if (!chip->qpi_enabled)
+		return 0;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	cmd.cmd_cfg = chip->table + SET_READ_PARA_QPI;
+	cmd.n_tx = 1;
+	cmd.tx_buf = buf;
+
+	ret = chip->issue_cmd(chip, &cmd);
+	if (ret < 0)
+		pr_info("err: %d set_read_parameters\n", ret);
+
+	return ret;
+}
+
+static int spi_nor_set_quad(struct spi_flash_chip *chip, int enable)
+{
+	u8 data[2];
+	u8 status, qe_shift;
+	u16 sdata;
+	int ret;
+
+	enable = !!enable;
+	/*
+	 * Macronix use status register bit s6 for QE enable
+	 * Winbond/Gigadevice use status register bit S9
+	 */
+	if (chip->options & SPINOR_QE_USE_BIT6)
+		qe_shift = 6;
+	else
+		qe_shift = 9;
+
+	spi_nor_read_status1(chip, &data[0]);
+	spi_nor_read_status2(chip, &data[1]);
+
+	sdata = data[1] << 8 | data[0];
+	if (((sdata >> qe_shift) & 0x1) == enable) {
+		pr_info("QE(bit%d) already set to %d\r\n", qe_shift, enable);
+		return 0;
+	}
+	if (enable)
+		sdata |= 1 << qe_shift;
+	else
+		sdata &= ~(1 << qe_shift);
+	data[0] = sdata & 0xff;
+	data[1] = sdata >> 8;
+
+	spi_nor_write_enable(chip);
+	if (qe_shift > 7) {
+		if (chip->options & SPINOR_HAVE_WRITE_STATUS2)
+			spi_nor_write_status2(chip, &data[1]);
+		else
+			spi_nor_write_status1(chip, data, 2);
+	} else {
+		if (chip->options & SPINOR_WRITE_STATUS1_1BYTE)
+			spi_nor_write_status1(chip, data, 1);
+		else
+			spi_nor_write_status1(chip, data, 2);
+	}
+
+	ret = spi_nor_wait(chip, &status);
+	if (ret < 0) {
+		pr_info("error %d write status register\n", ret);
+		return ret;
+	}
+
+	spi_nor_read_status1(chip, &data[0]);
+	spi_nor_read_status2(chip, &data[1]);
+
+	sdata = data[1] << 8 | data[0];
+	if (((sdata >> qe_shift) & 0x1) != enable) {
+		pr_info("err: failed to %s QE\r\n",
+			enable ? "enable" : "disable");
+		return -1;
+	}
+
+	pr_info("==>%s QE succeed\r\n", enable ? "enable" : "disable");
+	return 0;
+}
+
+static int spi_nor_enable_quad(struct spi_flash_chip *chip)
+{
+	return spi_nor_set_quad(chip, 1);
+}
+
+static int spi_nor_disable_quad(struct spi_flash_chip *chip)
+{
+	return spi_nor_set_quad(chip, 0);
+}
+
+#ifdef QSPINOR_ENABLE_QPI_MODE
+static int spi_nor_disable_qpi(struct spi_flash_chip *chip)
+{
+	struct spi_flash_cmd cmd;
+	int ret = 0;
+
+	if (!chip->qpi_enabled)
+		return 0;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->mfr_id == SPIFLASH_MFR_MXIC)
+		cmd.cmd_cfg = chip->table + DISABLE_QPI_MXIC;
+	else
+		cmd.cmd_cfg = chip->table + DISABLE_QPI;
+
+	ret = chip->issue_cmd(chip, &cmd);
+	if (ret < 0)
+		return ret;
+
+	chip->qpi_enabled = 0;
+	chip->table = cmd_table;
+
+	pr_info("QPI Disabled\n");
+	return 0;
+}
+
+/*
+ * QPI dummy clock depend on bus frequency
+ *
+ * Giga: {4, 6, 8, 8}, Winbond: {2, 4, 6, 8}
+ * Macronix use a different way to set dummy clock
+ */
+static u8 qpi_dummy_wb[4][2] = { {2, 26}, {4, 50}, {6, 80}, {8, 104} };
+static u8 qpi_dummy_gd[4][2] = { {4, 80}, {4, 80}, {6, 100}, {8, 133} };
+
+static int spi_nor_enable_qpi(struct spi_flash_chip *chip)
+{
+	struct spi_flash_cmd cmd;
+	int ret = 0, i;
+	u8 cfg;
+	u8 (*qpi_dummy)[2];
+
+	if (chip->qpi_enabled)
+		return 0;
+
+	memset(&cmd, 0, sizeof(struct spi_flash_cmd));
+	if (chip->mfr_id == SPIFLASH_MFR_MXIC)
+		cmd.cmd_cfg = chip->table + ENABLE_QPI_MXIC;
+	else
+		cmd.cmd_cfg = chip->table + ENABLE_QPI;
+
+	ret = chip->issue_cmd(chip, &cmd);
+	if (ret < 0)
+		return ret;
+
+	chip->qpi_enabled = 1;
+	chip->table = cmd_table_qpi;
+
+	if(chip->mfr_id == SPIFLASH_MFR_MXIC) {
+		chip->qpi_dummy = 6;
+	} else {
+		if (chip->mfr_id == SPIFLASH_MFR_GIGADEVICE)
+			qpi_dummy = qpi_dummy_gd;
+		else
+			qpi_dummy = qpi_dummy_wb;
+
+		for (i = 0; i < 4; i++) {
+			if(chip->bus_clk <= qpi_dummy[i][1]) {
+				cfg = i << 4;
+				chip->qpi_dummy = qpi_dummy[i][0];
+				break;
+			}
+		}
+
+		ret = spi_set_read_parameters(chip, &cfg);
+		if (ret < 0)
+			return ret;
+	}
+
+	chip->rx_mode = SPI_OPM_RX_QUAD;
+	chip->tx_mode = SPI_OPM_TX_QUAD;
+	spi_nor_set_rd_wr_op(chip);
+	pr_info("QPI Enabled, P5-4:%d dummy_cycles:%d\n", i, chip->qpi_dummy);
+	return 0;
+}
+#endif
+
+/**
+ * spi_nor_init - [Interface] Init SPI_FLASH device driver
+ * @spi: spi device structure
+ * @chip_ptr: pointer point to spi nand device structure pointer
+ */
+struct spi_flash_chip *spi_nor_scan_ident(struct mtd_info *mtd)
+{
+	struct spi_flash_chip *chip = mtd->priv;
+	u8 id[SPINOR_MAX_ID_LEN] = {0};
+
+#ifdef QSPINOR_ENABLE_QPI_MODE
+	if (chip->qpi_enabled)
+		spi_nor_disable_qpi(chip);
+#endif
+	chip->table = cmd_table;
+	chip->size = 16*1024*1024;
+	chip->block_size = 1 << 16;
+	chip->page_size = 1 << 8;
+
+	spi_nor_reset(chip);
+	spi_nor_read_id(chip, id);
+	chip->mfr_id = id[0];
+	chip->dev_id = id[1] << 8 | id[2];
+	spi_nor_scan_id_table(chip);
+	chip->block_shift = ilog2(chip->block_size);
+	chip->page_shift = ilog2(chip->page_size);
+	chip->page_mask = chip->page_size - 1;
+	pr_info("SPI-NOR: mfr_id: 0x%x, dev_id: 0x%x\n",
+			chip->mfr_id, chip->dev_id);
+
+	/*
+	 * The Enter 4-Byte Address Mode instruction will allow 32-bit address
+	 * (A31-A0) to be used to access the memory array beyond 128Mb
+	 */
+	if (chip->size > 16*1024*1024)
+		spi_nor_enable_4byte_mode(chip);
+
+	spi_nor_enable_quad(chip);
+#ifdef QSPINOR_ENABLE_QPI_MODE
+	spi_nor_enable_qpi(chip);
+#endif
+	spi_nor_set_rd_wr_op(chip);
+
+	if (chip->setup_memmap_read) {
+		if (chip->setup_memmap_read(chip,
+			chip->table + chip->read_op) < 0) {
+			pr_info("preinit_lookup_tbl failed, check cmd table\n");
+			return NULL;
+		}
+	}
+
+	return chip;
+}
+
+/**
+ * spi_nor_scan_tail - [SPI-NAND Interface] Scan for the SPI-NAND device
+ * @mtd: MTD device structure
+ * Description:
+ *   This is the second phase of the initiazation. It fills out all the
+ *   uninitialized fields of spi_flash_chip and mtd fields.
+ */
+int spi_nor_scan_tail(struct mtd_info *mtd)
+{
+	struct spi_flash_chip *chip = mtd->priv;
+	int ret;
+
+	mtd->name = chip->name;
+	mtd->size = chip->size;
+	mtd->erasesize = 4096u;
+	mtd->writesize = chip->page_size;
+	mtd->owner = 0;
+	mtd->type = MTD_NORFLASH;
+	mtd->flags = MTD_CAP_NORFLASH;
+	mtd->ecc_strength = 0;
+	mtd->_erase = spi_nor_erase;
+	mtd->_point = NULL;
+	mtd->_unpoint = NULL;
+	mtd->_read = spi_nor_read;
+	mtd->_write = spi_nor_write;
+	mtd->_lock = NULL;
+	mtd->_unlock = NULL;
+
+	ret = spi_flash_register(chip);
+	return ret;
+}