diff options
Diffstat (limited to 'target/linux/layerscape/patches-4.9/401-mtd-spi-nor-support-layerscape.patch')
-rw-r--r-- | target/linux/layerscape/patches-4.9/401-mtd-spi-nor-support-layerscape.patch | 1042 |
1 files changed, 1042 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-4.9/401-mtd-spi-nor-support-layerscape.patch b/target/linux/layerscape/patches-4.9/401-mtd-spi-nor-support-layerscape.patch new file mode 100644 index 0000000000..f61ae06ca2 --- /dev/null +++ b/target/linux/layerscape/patches-4.9/401-mtd-spi-nor-support-layerscape.patch @@ -0,0 +1,1042 @@ +From 120fa458ffe2250ea58578ccfc85e674005463dc Mon Sep 17 00:00:00 2001 +From: Yangbo Lu <yangbo.lu@nxp.com> +Date: Mon, 25 Sep 2017 10:53:50 +0800 +Subject: [PATCH] mtd: spi-nor: support layerscape + +This is a integrated patch for layerscape qspi support. + +Signed-off-by: Suresh Gupta <suresh.gupta@nxp.com> +Signed-off-by: Yunhui Cui <B56489@freescale.com> +Signed-off-by: mar.krzeminski <mar.krzeminski@gmail.com> +Signed-off-by: Alison Wang <b18965@freescale.com> +Signed-off-by: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.kw@hitachi.com> +Signed-off-by: LABBE Corentin <clabbe.montjoie@gmail.com> +Signed-off-by: Yuan Yao <yao.yuan@nxp.com> +Signed-off-by: Alexander Kurz <akurz@blala.de> +Signed-off-by: L. D. Pinney <ldpinney@gmail.com> +Signed-off-by: Ash Benz <ash.benz@bk.ru> +Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> +--- + drivers/mtd/mtdchar.c | 2 +- + drivers/mtd/spi-nor/fsl-quadspi.c | 356 +++++++++++++++++++++++++++++++------- + drivers/mtd/spi-nor/spi-nor.c | 136 +++++++++++++-- + include/linux/mtd/spi-nor.h | 14 +- + 4 files changed, 432 insertions(+), 76 deletions(-) + +diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c +index 2a47a3f0..4f21401d 100644 +--- a/drivers/mtd/mtdchar.c ++++ b/drivers/mtd/mtdchar.c +@@ -451,7 +451,7 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd, + * data. For our userspace tools it is important to dump areas + * with ECC errors! + * For kernel internal usage it also might return -EUCLEAN +- * to signal the caller that a bitflip has occured and has ++ * to signal the caller that a bitflip has occurred and has + * been corrected by the ECC algorithm. + * + * Note: currently the standard NAND function, nand_read_oob_std, +diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c +index 5c82e4ef..8fb75532 100644 +--- a/drivers/mtd/spi-nor/fsl-quadspi.c ++++ b/drivers/mtd/spi-nor/fsl-quadspi.c +@@ -41,6 +41,8 @@ + #define QUADSPI_QUIRK_TKT253890 (1 << 2) + /* Controller cannot wake up from wait mode, TKT245618 */ + #define QUADSPI_QUIRK_TKT245618 (1 << 3) ++/* QSPI_AMBA_BASE is internally added by SOC design */ ++#define QUADSPI_AMBA_BASE_INTERNAL (0x10000) + + /* The registers */ + #define QUADSPI_MCR 0x00 +@@ -193,7 +195,7 @@ + #define QUADSPI_LUT_NUM 64 + + /* SEQID -- we can have 16 seqids at most. */ +-#define SEQID_QUAD_READ 0 ++#define SEQID_READ 0 + #define SEQID_WREN 1 + #define SEQID_WRDI 2 + #define SEQID_RDSR 3 +@@ -205,15 +207,22 @@ + #define SEQID_RDCR 9 + #define SEQID_EN4B 10 + #define SEQID_BRWR 11 ++#define SEQID_RDAR_OR_RD_EVCR 12 ++#define SEQID_WRAR 13 ++#define SEQID_WD_EVCR 14 + + #define QUADSPI_MIN_IOMAP SZ_4M + ++#define FLASH_VENDOR_SPANSION_FS "s25fs" ++#define SPANSION_S25FS_FAMILY (1 << 1) ++ + enum fsl_qspi_devtype { + FSL_QUADSPI_VYBRID, + FSL_QUADSPI_IMX6SX, + FSL_QUADSPI_IMX7D, + FSL_QUADSPI_IMX6UL, + FSL_QUADSPI_LS1021A, ++ FSL_QUADSPI_LS2080A, + }; + + struct fsl_qspi_devtype_data { +@@ -224,7 +233,7 @@ struct fsl_qspi_devtype_data { + int driver_data; + }; + +-static struct fsl_qspi_devtype_data vybrid_data = { ++static const struct fsl_qspi_devtype_data vybrid_data = { + .devtype = FSL_QUADSPI_VYBRID, + .rxfifo = 128, + .txfifo = 64, +@@ -232,7 +241,7 @@ static struct fsl_qspi_devtype_data vybrid_data = { + .driver_data = QUADSPI_QUIRK_SWAP_ENDIAN, + }; + +-static struct fsl_qspi_devtype_data imx6sx_data = { ++static const struct fsl_qspi_devtype_data imx6sx_data = { + .devtype = FSL_QUADSPI_IMX6SX, + .rxfifo = 128, + .txfifo = 512, +@@ -241,7 +250,7 @@ static struct fsl_qspi_devtype_data imx6sx_data = { + | QUADSPI_QUIRK_TKT245618, + }; + +-static struct fsl_qspi_devtype_data imx7d_data = { ++static const struct fsl_qspi_devtype_data imx7d_data = { + .devtype = FSL_QUADSPI_IMX7D, + .rxfifo = 512, + .txfifo = 512, +@@ -250,7 +259,7 @@ static struct fsl_qspi_devtype_data imx7d_data = { + | QUADSPI_QUIRK_4X_INT_CLK, + }; + +-static struct fsl_qspi_devtype_data imx6ul_data = { ++static const struct fsl_qspi_devtype_data imx6ul_data = { + .devtype = FSL_QUADSPI_IMX6UL, + .rxfifo = 128, + .txfifo = 512, +@@ -267,6 +276,14 @@ static struct fsl_qspi_devtype_data ls1021a_data = { + .driver_data = 0, + }; + ++static struct fsl_qspi_devtype_data ls2080a_data = { ++ .devtype = FSL_QUADSPI_LS2080A, ++ .rxfifo = 128, ++ .txfifo = 64, ++ .ahb_buf_size = 1024, ++ .driver_data = QUADSPI_AMBA_BASE_INTERNAL | QUADSPI_QUIRK_TKT253890, ++}; ++ + #define FSL_QSPI_MAX_CHIP 4 + struct fsl_qspi { + struct spi_nor nor[FSL_QSPI_MAX_CHIP]; +@@ -282,6 +299,7 @@ struct fsl_qspi { + u32 nor_size; + u32 nor_num; + u32 clk_rate; ++ u32 ddr_smp; + unsigned int chip_base_addr; /* We may support two chips. */ + bool has_second_chip; + bool big_endian; +@@ -309,6 +327,23 @@ static inline int needs_wakeup_wait_mode(struct fsl_qspi *q) + return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618; + } + ++static inline int has_added_amba_base_internal(struct fsl_qspi *q) ++{ ++ return q->devtype_data->driver_data & QUADSPI_AMBA_BASE_INTERNAL; ++} ++ ++static u32 fsl_get_nor_vendor(struct spi_nor *nor) ++{ ++ u32 vendor_id; ++ ++ if (nor->vendor) { ++ if (memcmp(nor->vendor, FLASH_VENDOR_SPANSION_FS, ++ sizeof(FLASH_VENDOR_SPANSION_FS) - 1)) ++ vendor_id = SPANSION_S25FS_FAMILY; ++ } ++ return vendor_id; ++} ++ + /* + * R/W functions for big- or little-endian registers: + * The qSPI controller's endian is independent of the CPU core's endian. +@@ -331,6 +366,31 @@ static u32 qspi_readl(struct fsl_qspi *q, void __iomem *addr) + return ioread32(addr); + } + ++static inline u32 *u8tou32(u32 *dest, const u8 *src, size_t n) ++{ ++ size_t i; ++ *dest = 0; ++ ++ n = n > 4 ? 4 : n; ++ for (i = 0; i < n; i++) ++ *dest |= *src++ << i * 8; ++ ++ return dest; ++ ++} ++ ++static inline u8 *u32tou8(u8 *dest, const u32 *src, size_t n) ++{ ++ size_t i; ++ u8 *xdest = dest; ++ ++ n = n > 4 ? 4 : n; ++ for (i = 0; i < n; i++) ++ *xdest++ = *src >> i * 8; ++ ++ return dest; ++} ++ + /* + * An IC bug makes us to re-arrange the 32-bit data. + * The following chips, such as IMX6SLX, have fixed this bug. +@@ -373,8 +433,15 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + void __iomem *base = q->iobase; + int rxfifo = q->devtype_data->rxfifo; + u32 lut_base; +- u8 cmd, addrlen, dummy; + int i; ++ u32 vendor; ++ ++ struct spi_nor *nor = &q->nor[0]; ++ u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT; ++ u8 read_op = nor->read_opcode; ++ u8 read_dm = nor->read_dummy; ++ ++ vendor = fsl_get_nor_vendor(nor); + + fsl_qspi_unlock_lut(q); + +@@ -382,25 +449,51 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + for (i = 0; i < QUADSPI_LUT_NUM; i++) + qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4); + +- /* Quad Read */ +- lut_base = SEQID_QUAD_READ * 4; +- +- if (q->nor_size <= SZ_16M) { +- cmd = SPINOR_OP_READ_1_1_4; +- addrlen = ADDR24BIT; +- dummy = 8; +- } else { +- /* use the 4-byte address */ +- cmd = SPINOR_OP_READ_1_1_4; +- addrlen = ADDR32BIT; +- dummy = 8; +- } +- +- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), ++ /* Read */ ++ lut_base = SEQID_READ * 4; ++ ++ if (nor->flash_read == SPI_NOR_FAST) { ++ qspi_writel(q, LUT0(CMD, PAD1, read_op) | ++ LUT1(ADDR, PAD1, addrlen), ++ base + QUADSPI_LUT(lut_base)); ++ qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) | ++ LUT1(FSL_READ, PAD1, rxfifo), ++ base + QUADSPI_LUT(lut_base + 1)); ++ } else if (nor->flash_read == SPI_NOR_QUAD) { ++ if (q->nor_size == 0x4000000) { ++ read_op = 0xEC; ++ qspi_writel(q, ++ LUT0(CMD, PAD1, read_op) | LUT1(ADDR, PAD4, addrlen), ++ base + QUADSPI_LUT(lut_base)); ++ qspi_writel(q, ++ LUT0(MODE, PAD4, 0xff) | LUT1(DUMMY, PAD4, read_dm), ++ base + QUADSPI_LUT(lut_base + 1)); ++ qspi_writel(q, ++ LUT0(FSL_READ, PAD4, rxfifo), ++ base + QUADSPI_LUT(lut_base + 2)); ++ } else { ++ qspi_writel(q, LUT0(CMD, PAD1, read_op) | ++ LUT1(ADDR, PAD1, addrlen), ++ base + QUADSPI_LUT(lut_base)); ++ qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) | ++ LUT1(FSL_READ, PAD4, rxfifo), ++ base + QUADSPI_LUT(lut_base + 1)); ++ } ++ } else if (nor->flash_read == SPI_NOR_DDR_QUAD) { ++ /* read mode : 1-4-4, such as Spansion s25fl128s. */ ++ qspi_writel(q, LUT0(CMD, PAD1, read_op) ++ | LUT1(ADDR_DDR, PAD4, addrlen), + base + QUADSPI_LUT(lut_base)); +- qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo), ++ ++ qspi_writel(q, LUT0(MODE_DDR, PAD4, 0xff) ++ | LUT1(DUMMY, PAD1, read_dm), + base + QUADSPI_LUT(lut_base + 1)); + ++ qspi_writel(q, LUT0(FSL_READ_DDR, PAD4, rxfifo) ++ | LUT1(JMP_ON_CS, PAD1, 0), ++ base + QUADSPI_LUT(lut_base + 2)); ++ } ++ + /* Write enable */ + lut_base = SEQID_WREN * 4; + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WREN), +@@ -409,16 +502,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + /* Page Program */ + lut_base = SEQID_PP * 4; + +- if (q->nor_size <= SZ_16M) { +- cmd = SPINOR_OP_PP; +- addrlen = ADDR24BIT; +- } else { +- /* use the 4-byte address */ +- cmd = SPINOR_OP_PP; +- addrlen = ADDR32BIT; +- } +- +- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), ++ qspi_writel(q, LUT0(CMD, PAD1, nor->program_opcode) | ++ LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0), + base + QUADSPI_LUT(lut_base + 1)); +@@ -432,10 +517,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + /* Erase a sector */ + lut_base = SEQID_SE * 4; + +- cmd = q->nor[0].erase_opcode; +- addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT; +- +- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), ++ qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) | ++ LUT1(ADDR, PAD1, addrlen), + base + QUADSPI_LUT(lut_base)); + + /* Erase the whole chip */ +@@ -476,6 +559,44 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR), + base + QUADSPI_LUT(lut_base)); + ++ ++ /* ++ * Flash Micron and Spansion command confilict ++ * use the same value 0x65. But it indicates different meaning. ++ */ ++ lut_base = SEQID_RDAR_OR_RD_EVCR * 4; ++ ++ if (vendor == SPANSION_S25FS_FAMILY) { ++ /* ++ * Read any device register. ++ * Used for Spansion S25FS-S family flash only. ++ */ ++ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_SPANSION_RDAR) | ++ LUT1(ADDR, PAD1, ADDR24BIT), ++ base + QUADSPI_LUT(lut_base)); ++ qspi_writel(q, LUT0(DUMMY, PAD1, 8) | LUT1(FSL_READ, PAD1, 1), ++ base + QUADSPI_LUT(lut_base + 1)); ++ } else { ++ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR), ++ base + QUADSPI_LUT(lut_base)); ++ } ++ ++ /* ++ * Write any device register. ++ * Used for Spansion S25FS-S family flash only. ++ */ ++ lut_base = SEQID_WRAR * 4; ++ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_SPANSION_WRAR) | ++ LUT1(ADDR, PAD1, ADDR24BIT), ++ base + QUADSPI_LUT(lut_base)); ++ qspi_writel(q, LUT0(FSL_WRITE, PAD1, 1), ++ base + QUADSPI_LUT(lut_base + 1)); ++ ++ /* Write EVCR register */ ++ lut_base = SEQID_WD_EVCR * 4; ++ qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR), ++ base + QUADSPI_LUT(lut_base)); ++ + fsl_qspi_lock_lut(q); + } + +@@ -483,8 +604,24 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) + static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) + { + switch (cmd) { ++ case SPINOR_OP_READ_1_4_4_D: ++ case SPINOR_OP_READ4_1_4_4_D: ++ case SPINOR_OP_READ4_1_1_4: + case SPINOR_OP_READ_1_1_4: +- return SEQID_QUAD_READ; ++ case SPINOR_OP_READ_FAST: ++ case SPINOR_OP_READ4_FAST: ++ return SEQID_READ; ++ /* ++ * Spansion & Micron use the same command value 0x65 ++ * Spansion: SPINOR_OP_SPANSION_RDAR, read any register. ++ * Micron: SPINOR_OP_RD_EVCR, ++ * read enhanced volatile configuration register. ++ * case SPINOR_OP_RD_EVCR: ++ */ ++ case SPINOR_OP_SPANSION_RDAR: ++ return SEQID_RDAR_OR_RD_EVCR; ++ case SPINOR_OP_SPANSION_WRAR: ++ return SEQID_WRAR; + case SPINOR_OP_WREN: + return SEQID_WREN; + case SPINOR_OP_WRDI: +@@ -496,6 +633,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) + case SPINOR_OP_CHIP_ERASE: + return SEQID_CHIP_ERASE; + case SPINOR_OP_PP: ++ case SPINOR_OP_PP_4B: + return SEQID_PP; + case SPINOR_OP_RDID: + return SEQID_RDID; +@@ -507,6 +645,8 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) + return SEQID_EN4B; + case SPINOR_OP_BRWR: + return SEQID_BRWR; ++ case SPINOR_OP_WD_EVCR: ++ return SEQID_WD_EVCR; + default: + if (cmd == q->nor[0].erase_opcode) + return SEQID_SE; +@@ -531,8 +671,11 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) + /* save the reg */ + reg = qspi_readl(q, base + QUADSPI_MCR); + +- qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr, +- base + QUADSPI_SFAR); ++ if (has_added_amba_base_internal(q)) ++ qspi_writel(q, q->chip_base_addr + addr, base + QUADSPI_SFAR); ++ else ++ qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr, ++ base + QUADSPI_SFAR); + qspi_writel(q, QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, + base + QUADSPI_RBCT); + qspi_writel(q, reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR); +@@ -582,10 +725,10 @@ static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u8 *rxbuf) + q->chip_base_addr, tmp); + + if (len >= 4) { +- *((u32 *)rxbuf) = tmp; ++ u32tou8(rxbuf, &tmp, 4); + rxbuf += 4; + } else { +- memcpy(rxbuf, &tmp, len); ++ u32tou8(rxbuf, &tmp, len); + break; + } + +@@ -619,11 +762,12 @@ static inline void fsl_qspi_invalid(struct fsl_qspi *q) + } + + static ssize_t fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, +- u8 opcode, unsigned int to, u32 *txbuf, ++ u8 opcode, unsigned int to, u8 *txbuf, + unsigned count) + { + int ret, i, j; + u32 tmp; ++ u8 byts; + + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len : %d\n", + q->chip_base_addr, to, count); +@@ -633,10 +777,13 @@ static ssize_t fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor, + qspi_writel(q, tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR); + + /* fill the TX data to the FIFO */ ++ byts = count; + for (j = 0, i = ((count + 3) / 4); j < i; j++) { +- tmp = fsl_qspi_endian_xchg(q, *txbuf); ++ u8tou32(&tmp, txbuf, byts); ++ tmp = fsl_qspi_endian_xchg(q, tmp); + qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR); +- txbuf++; ++ txbuf += 4; ++ byts -= 4; + } + + /* fill the TXFIFO upto 16 bytes for i.MX7d */ +@@ -657,11 +804,43 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q) + { + int nor_size = q->nor_size; + void __iomem *base = q->iobase; ++ u32 mem_base; ++ ++ if (has_added_amba_base_internal(q)) ++ mem_base = 0x0; ++ else ++ mem_base = q->memmap_phy; ++ ++ qspi_writel(q, nor_size + mem_base, base + QUADSPI_SFA1AD); ++ qspi_writel(q, nor_size * 2 + mem_base, base + QUADSPI_SFA2AD); ++ qspi_writel(q, nor_size * 3 + mem_base, base + QUADSPI_SFB1AD); ++ qspi_writel(q, nor_size * 4 + mem_base, base + QUADSPI_SFB2AD); ++} ++ ++/* ++ * enable controller ddr quad mode to support different ++ * vender flashes ddr quad mode. ++ */ ++static void set_ddr_quad_mode(struct fsl_qspi *q) ++{ ++ u32 reg, reg2; ++ ++ reg = qspi_readl(q, q->iobase + QUADSPI_MCR); ++ ++ /* Firstly, disable the module */ ++ qspi_writel(q, reg | QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); ++ ++ /* Set the Sampling Register for DDR */ ++ reg2 = qspi_readl(q, q->iobase + QUADSPI_SMPR); ++ reg2 &= ~QUADSPI_SMPR_DDRSMP_MASK; ++ reg2 |= (((q->ddr_smp) << QUADSPI_SMPR_DDRSMP_SHIFT) & ++ QUADSPI_SMPR_DDRSMP_MASK); ++ qspi_writel(q, reg2, q->iobase + QUADSPI_SMPR); ++ ++ /* Enable the module again (enable the DDR too) */ ++ reg |= QUADSPI_MCR_DDR_EN_MASK; ++ qspi_writel(q, reg, q->iobase + QUADSPI_MCR); + +- qspi_writel(q, nor_size + q->memmap_phy, base + QUADSPI_SFA1AD); +- qspi_writel(q, nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD); +- qspi_writel(q, nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD); +- qspi_writel(q, nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD); + } + + /* +@@ -681,19 +860,36 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) + { + void __iomem *base = q->iobase; + int seqid; ++ const struct fsl_qspi_devtype_data *devtype_data = q->devtype_data; + + /* AHB configuration for access buffer 0/1/2 .*/ + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); + qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); ++ + /* +- * Set ADATSZ with the maximum AHB buffer size to improve the +- * read performance. ++ * Errata: A-009282: QuadSPI data prefetch may result in incorrect data ++ * Workaround: Keep the read data size to 64 bits (8 bytes). ++ * This disables the prefetch on the AHB buffer and ++ * prevents this issue from occurring. + */ +- qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK | +- ((q->devtype_data->ahb_buf_size / 8) +- << QUADSPI_BUF3CR_ADATSZ_SHIFT), +- base + QUADSPI_BUF3CR); ++ if (devtype_data->devtype == FSL_QUADSPI_LS2080A || ++ devtype_data->devtype == FSL_QUADSPI_LS1021A) { ++ ++ qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK | ++ (1 << QUADSPI_BUF3CR_ADATSZ_SHIFT), ++ base + QUADSPI_BUF3CR); ++ ++ } else { ++ /* ++ * Set ADATSZ with the maximum AHB buffer size to improve the ++ * read performance. ++ */ ++ qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK | ++ ((q->devtype_data->ahb_buf_size / 8) ++ << QUADSPI_BUF3CR_ADATSZ_SHIFT), ++ base + QUADSPI_BUF3CR); ++ } + + /* We only use the buffer3 */ + qspi_writel(q, 0, base + QUADSPI_BUF0IND); +@@ -704,6 +900,11 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q) + seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode); + qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT, + q->iobase + QUADSPI_BFGENCR); ++ ++ /* enable the DDR quad read */ ++ if (q->nor->flash_read == SPI_NOR_DDR_QUAD) ++ set_ddr_quad_mode(q); ++ + } + + /* This function was used to prepare and enable QSPI clock */ +@@ -822,6 +1023,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = { + { .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, }, + { .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, }, + { .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, }, ++ { .compatible = "fsl,ls2080a-qspi", .data = (void *)&ls2080a_data, }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); +@@ -835,8 +1037,12 @@ static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) + { + int ret; + struct fsl_qspi *q = nor->priv; ++ u32 to = 0; ++ ++ if (opcode == SPINOR_OP_SPANSION_RDAR) ++ u8tou32(&to, nor->cmd_buf, 4); + +- ret = fsl_qspi_runcmd(q, opcode, 0, len); ++ ret = fsl_qspi_runcmd(q, opcode, to, len); + if (ret) + return ret; + +@@ -848,9 +1054,13 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) + { + struct fsl_qspi *q = nor->priv; + int ret; ++ u32 to = 0; ++ ++ if (opcode == SPINOR_OP_SPANSION_WRAR) ++ u8tou32(&to, nor->cmd_buf, 4); + + if (!buf) { +- ret = fsl_qspi_runcmd(q, opcode, 0, 1); ++ ret = fsl_qspi_runcmd(q, opcode, to, 1); + if (ret) + return ret; + +@@ -859,7 +1069,7 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) + + } else if (len > 0) { + ret = fsl_qspi_nor_write(q, nor, opcode, 0, +- (u32 *)buf, len); ++ buf, len); + if (ret > 0) + return 0; + } else { +@@ -875,7 +1085,7 @@ static ssize_t fsl_qspi_write(struct spi_nor *nor, loff_t to, + { + struct fsl_qspi *q = nor->priv; + ssize_t ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to, +- (u32 *)buf, len); ++ (u8 *)buf, len); + + /* invalid the data in the AHB buffer. */ + fsl_qspi_invalid(q); +@@ -922,7 +1132,7 @@ static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from, + len); + + /* Read out the data directly from the AHB buffer.*/ +- memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, ++ memcpy_toio(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, + len); + + return len; +@@ -980,6 +1190,8 @@ static int fsl_qspi_probe(struct platform_device *pdev) + struct spi_nor *nor; + struct mtd_info *mtd; + int ret, i = 0; ++ int find_node; ++ enum read_mode mode = SPI_NOR_QUAD; + + q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL); + if (!q) +@@ -1027,6 +1239,12 @@ static int fsl_qspi_probe(struct platform_device *pdev) + goto clk_failed; + } + ++ /* find ddrsmp value */ ++ ret = of_property_read_u32(dev->of_node, "fsl,ddr-sampling-point", ++ &q->ddr_smp); ++ if (ret) ++ q->ddr_smp = 0; ++ + /* find the irq */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { +@@ -1050,6 +1268,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) + + mutex_init(&q->lock); + ++ find_node = 0; + /* iterate the subnodes. */ + for_each_available_child_of_node(dev->of_node, np) { + /* skip the holes */ +@@ -1076,18 +1295,25 @@ static int fsl_qspi_probe(struct platform_device *pdev) + ret = of_property_read_u32(np, "spi-max-frequency", + &q->clk_rate); + if (ret < 0) +- goto mutex_failed; ++ continue; + + /* set the chip address for READID */ + fsl_qspi_set_base_addr(q, nor); + +- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); ++ ret = of_property_read_bool(np, "m25p,fast-read"); ++ mode = (ret) ? SPI_NOR_FAST : SPI_NOR_QUAD; ++ /* Can we enable the DDR Quad Read? */ ++ ret = of_property_read_bool(np, "ddr-quad-read"); + if (ret) +- goto mutex_failed; ++ mode = SPI_NOR_DDR_QUAD; ++ ++ ret = spi_nor_scan(nor, NULL, mode); ++ if (ret) ++ continue; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) +- goto mutex_failed; ++ continue; + + /* Set the correct NOR size now. */ + if (q->nor_size == 0) { +@@ -1110,8 +1336,12 @@ static int fsl_qspi_probe(struct platform_device *pdev) + nor->page_size = q->devtype_data->txfifo; + + i++; ++ find_node++; + } + ++ if (find_node == 0) ++ goto mutex_failed; ++ + /* finish the rest init. */ + ret = fsl_qspi_nor_setup_last(q); + if (ret) +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 793d321d..190e0e45 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -40,6 +40,13 @@ + #define SPI_NOR_MAX_ID_LEN 6 + #define SPI_NOR_MAX_ADDR_WIDTH 4 + ++#define SPI_NOR_MICRON_WRITE_ENABLE 0x7f ++/* Added for S25FS-S family flash */ ++#define SPINOR_CONFIG_REG3_OFFSET 0x800004 ++#define CR3V_4KB_ERASE_UNABLE 0x8 ++#define SPINOR_S25FS_FAMILY_ID 0x81 ++ ++ + struct flash_info { + char *name; + +@@ -68,7 +75,8 @@ struct flash_info { + #define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */ + #define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */ + #define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */ +-#define USE_FSR BIT(7) /* use flag status register */ ++#define USE_FSR BIT(13) /* use flag status register */ ++#define SPI_NOR_DDR_QUAD_READ BIT(7) /* Flash supports DDR Quad Read */ + #define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */ + #define SPI_NOR_HAS_TB BIT(9) /* + * Flash SR has Top/Bottom (TB) protect +@@ -85,9 +93,11 @@ struct flash_info { + * Use dedicated 4byte address op codes + * to support memory size above 128Mib. + */ ++#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ + }; + + #define JEDEC_MFR(info) ((info)->id[0]) ++#define EXT_ID(info) ((info)->id[5]) + + static const struct flash_info *spi_nor_match_id(const char *name); + +@@ -132,7 +142,7 @@ static int read_fsr(struct spi_nor *nor) + /* + * Read configuration register, returning its value in the + * location. Return the configuration register value. +- * Returns negative if error occured. ++ * Returns negative if error occurred. + */ + static int read_cr(struct spi_nor *nor) + { +@@ -160,6 +170,8 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) + case SPI_NOR_DUAL: + case SPI_NOR_QUAD: + return 8; ++ case SPI_NOR_DDR_QUAD: ++ return 6; + case SPI_NOR_NORMAL: + return 0; + } +@@ -961,6 +973,8 @@ static const struct flash_info spi_nor_ids[] = { + + /* ESMT */ + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, ++ { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, ++ { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) }, + + /* Everspin */ + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, +@@ -1014,12 +1028,15 @@ static const struct flash_info spi_nor_ids[] = { + { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) }, ++ { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) }, ++ { "mx25u4035", INFO(0xc22533, 0, 64 * 1024, 8, SECT_4K) }, ++ { "mx25u8035", INFO(0xc22534, 0, 64 * 1024, 16, SECT_4K) }, + { "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64, 0) }, + { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, + { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, +- { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) }, ++ { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K) }, + { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) }, + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, +@@ -1033,10 +1050,11 @@ static const struct flash_info spi_nor_ids[] = { + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) }, ++ { "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, +- { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, +- { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, ++ { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, ++ { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, +@@ -1054,8 +1072,11 @@ static const struct flash_info spi_nor_ids[] = { + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, +- { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s25fs256s1", INFO6(0x010219, 0x4d0181, 64 * 1024, 512, 0)}, ++ { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ ++ | SPI_NOR_DDR_QUAD_READ) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)}, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, + { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, +@@ -1130,6 +1151,9 @@ static const struct flash_info spi_nor_ids[] = { + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, + { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, ++ { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) }, ++ { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) }, ++ { "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, + { + "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, +@@ -1192,6 +1216,53 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); + } ++/* ++ * The S25FS-S family physical sectors may be configured as a ++ * hybrid combination of eight 4-kB parameter sectors ++ * at the top or bottom of the address space with all ++ * but one of the remaining sectors being uniform size. ++ * The Parameter Sector Erase commands (20h or 21h) must ++ * be used to erase the 4-kB parameter sectors individually. ++ * The Sector (uniform sector) Erase commands (D8h or DCh) ++ * must be used to erase any of the remaining ++ * sectors, including the portion of highest or lowest address ++ * sector that is not overlaid by the parameter sectors. ++ * The uniform sector erase command has no effect on parameter sectors. ++ */ ++static int spansion_s25fs_disable_4kb_erase(struct spi_nor *nor) ++{ ++ struct fsl_qspi *q; ++ u32 cr3v_addr = SPINOR_CONFIG_REG3_OFFSET; ++ u8 cr3v = 0x0; ++ int ret = 0x0; ++ ++ q = nor->priv; ++ ++ nor->cmd_buf[2] = cr3v_addr >> 16; ++ nor->cmd_buf[1] = cr3v_addr >> 8; ++ nor->cmd_buf[0] = cr3v_addr >> 0; ++ ++ ret = nor->read_reg(nor, SPINOR_OP_SPANSION_RDAR, &cr3v, 1); ++ if (ret) ++ return ret; ++ if (cr3v & CR3V_4KB_ERASE_UNABLE) ++ return 0; ++ ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); ++ if (ret) ++ return ret; ++ cr3v = CR3V_4KB_ERASE_UNABLE; ++ nor->program_opcode = SPINOR_OP_SPANSION_WRAR; ++ nor->write(nor, cr3v_addr, 1, &cr3v); ++ ++ ret = nor->read_reg(nor, SPINOR_OP_SPANSION_RDAR, &cr3v, 1); ++ if (ret) ++ return ret; ++ if (!(cr3v & CR3V_4KB_ERASE_UNABLE)) ++ return -EPERM; ++ ++ return 0; ++} ++ + + static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +@@ -1411,7 +1482,7 @@ static int macronix_quad_enable(struct spi_nor *nor) + * Write status Register and configuration register with 2 bytes + * The first byte will be written to the status register, while the + * second byte will be written to the configuration register. +- * Return negative if error occured. ++ * Return negative if error occurred. + */ + static int write_sr_cr(struct spi_nor *nor, u16 val) + { +@@ -1459,6 +1530,24 @@ static int spansion_quad_enable(struct spi_nor *nor) + return 0; + } + ++static int set_ddr_quad_mode(struct spi_nor *nor, const struct flash_info *info) ++{ ++ int status; ++ ++ switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_SPANSION: ++ status = spansion_quad_enable(nor); ++ if (status) { ++ dev_err(nor->dev, "Spansion DDR quad-read not enabled\n"); ++ return status; ++ } ++ return status; ++ default: ++ return -EINVAL; ++ } ++} ++ ++ + static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + { + int status; +@@ -1604,9 +1693,25 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + write_sr(nor, 0); + spi_nor_wait_till_ready(nor); + } ++ if (JEDEC_MFR(info) == SNOR_MFR_MICRON) { ++ ret = read_sr(nor); ++ ret &= SPI_NOR_MICRON_WRITE_ENABLE; ++ ++ write_enable(nor); ++ write_sr(nor, ret); ++ } ++ ++ if (EXT_ID(info) == SPINOR_S25FS_FAMILY_ID) { ++ ret = spansion_s25fs_disable_4kb_erase(nor); ++ if (ret) ++ return ret; ++ } ++ + + if (!mtd->name) + mtd->name = dev_name(dev); ++ if (info->name) ++ nor->vendor = info->name; + mtd->priv = nor; + mtd->type = MTD_NORFLASH; + mtd->writesize = 1; +@@ -1639,6 +1744,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + nor->flags |= SNOR_F_USE_FSR; + if (info->flags & SPI_NOR_HAS_TB) + nor->flags |= SNOR_F_HAS_SR_TB; ++ if (info->flags & NO_CHIP_ERASE) ++ nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; + + #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + /* prefer "small sector" erase if possible */ +@@ -1676,9 +1783,15 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + /* Some devices cannot do fast-read, no matter what DT tells us */ + if (info->flags & SPI_NOR_NO_FR) + nor->flash_read = SPI_NOR_NORMAL; +- +- /* Quad/Dual-read mode takes precedence over fast/normal */ +- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { ++ /* DDR Quad/Quad/Dual-read mode takes precedence over fast/normal */ ++ if (mode == SPI_NOR_DDR_QUAD && info->flags & SPI_NOR_DDR_QUAD_READ) { ++ ret = set_ddr_quad_mode(nor, info); ++ if (ret) { ++ dev_err(dev, "DDR quad mode not supported\n"); ++ return ret; ++ } ++ nor->flash_read = SPI_NOR_DDR_QUAD; ++ } else if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { + ret = set_quad_mode(nor, info); + if (ret) { + dev_err(dev, "quad mode not supported\n"); +@@ -1691,6 +1804,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + + /* Default commands */ + switch (nor->flash_read) { ++ case SPI_NOR_DDR_QUAD: ++ nor->read_opcode = SPINOR_OP_READ4_1_4_4_D; ++ break; + case SPI_NOR_QUAD: + nor->read_opcode = SPINOR_OP_READ_1_1_4; + break; +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index f2a71803..5003ff64 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -31,10 +31,10 @@ + + /* + * Note on opcode nomenclature: some opcodes have a format like +- * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number ++ * SPINOR_OP_FUNCTION{4,}_x_y_z{_D}. The numbers x, y,and z stand for the number + * of I/O lines used for the opcode, address, and data (respectively). The + * FUNCTION has an optional suffix of '4', to represent an opcode which +- * requires a 4-byte (32-bit) address. ++ * requires a 4-byte (32-bit) address. The suffix of 'D' stands for the + */ + + /* Flash opcodes. */ +@@ -46,7 +46,9 @@ + #define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */ + #define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */ + #define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */ ++#define SPINOR_OP_READ_1_4_4_D 0xed /* Read data bytes (DDR Quad SPI) */ + #define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */ ++#define SPINOR_OP_READ4_1_4_4_D 0xee /* Read data bytes (DDR Quad SPI) */ + #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ + #define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */ + #define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */ +@@ -62,9 +64,11 @@ + /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ + #define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */ + #define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */ ++#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ + #define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */ + #define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */ + #define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */ ++#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ + #define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */ + #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ + #define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */ +@@ -94,6 +98,10 @@ + /* Used for Spansion flashes only. */ + #define SPINOR_OP_BRWR 0x17 /* Bank register write */ + ++/* Used for Spansion S25FS-S family flash only. */ ++#define SPINOR_OP_SPANSION_RDAR 0x65 /* Read any device register */ ++#define SPINOR_OP_SPANSION_WRAR 0x71 /* Write any device register */ ++ + /* Used for Micron flashes only. */ + #define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ + #define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ +@@ -124,6 +132,7 @@ enum read_mode { + SPI_NOR_FAST, + SPI_NOR_DUAL, + SPI_NOR_QUAD, ++ SPI_NOR_DDR_QUAD, + }; + + #define SPI_NOR_MAX_CMD_SIZE 8 +@@ -189,6 +198,7 @@ struct spi_nor { + bool sst_write_second; + u32 flags; + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; ++ char *vendor; + + int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); + void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); +-- +2.14.1 + |