ASR_BASE

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