diff options
Diffstat (limited to 'target/linux/layerscape/patches-4.14/818-qspi-support-layerscape.patch')
-rw-r--r-- | target/linux/layerscape/patches-4.14/818-qspi-support-layerscape.patch | 774 |
1 files changed, 774 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-4.14/818-qspi-support-layerscape.patch b/target/linux/layerscape/patches-4.14/818-qspi-support-layerscape.patch new file mode 100644 index 0000000000..945f7b244b --- /dev/null +++ b/target/linux/layerscape/patches-4.14/818-qspi-support-layerscape.patch @@ -0,0 +1,774 @@ +From 60eee49f37b77bc2d5f46c5db5a5c24d0c31bd02 Mon Sep 17 00:00:00 2001 +From: Biwen Li <biwen.li@nxp.com> +Date: Tue, 20 Nov 2018 15:36:57 +0800 +Subject: [PATCH] qspi: support layerscape + +This is an integrated patch of qspi for layerscape + +Signed-off-by: Abhimanyu Saini <abhimanyu.saini@nxp.com> +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@wedev4u.fr> +Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> +Signed-off-by: Prabhakar Kushwaha <prabhakar.kushwaha@nxp.com> +Signed-off-by: Suresh Gupta <suresh.gupta@nxp.com> +Signed-off-by: Yogesh Gaur <yogeshnarayan.gaur@nxp.com> +Signed-off-by: Biwen Li <biwen.li@nxp.com> +Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> +--- + .../devicetree/bindings/mtd/fsl-quadspi.txt | 31 ++ + drivers/mtd/spi-nor/fsl-quadspi.c | 444 +++++++++++------- + drivers/mtd/spi-nor/spi-nor.c | 5 + + 3 files changed, 320 insertions(+), 160 deletions(-) + +--- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt ++++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt +@@ -7,6 +7,7 @@ Required properties: + or + "fsl,ls2080a-qspi" followed by "fsl,ls1021a-qspi", + "fsl,ls1043a-qspi" followed by "fsl,ls1021a-qspi" ++ "fsl,ls2080a-qspi" followed by "fsl,ls1088a-qspi", + - reg : the first contains the register location and length, + the second contains the memory mapping address and length + - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory" +@@ -39,3 +40,33 @@ qspi0: quadspi@40044000 { + .... + }; + }; ++ ++qspi1: quadspi@20c0000 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x0 0x20c0000 0x0 0x10000>, ++ <0x0 0x20000000 0x0 0x10000000>; ++ reg-names = "QuadSPI", "QuadSPI-memory"; ++ interrupts = <0 25 0x4>; /* Level high type */ ++ clocks = <&clockgen 4 3>, <&clockgen 4 3>; ++ clock-names = "qspi_en", "qspi"; ++ status = "okay"; ++ ++ qflash0: s25fs512s@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ spi-max-frequency = <20000000>; ++ reg = <0>; ++ spi-rx-bus-width = <4>; ++ spi-tx-bus-width = <4>; ++ }; ++ ++ qflash1: s25fs512s@1 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ spi-max-frequency = <20000000>; ++ reg = <1>; ++ spi-rx-bus-width = <4>; ++ spi-tx-bus-width = <4>; ++ }; ++}; +--- a/drivers/mtd/spi-nor/fsl-quadspi.c ++++ b/drivers/mtd/spi-nor/fsl-quadspi.c +@@ -41,6 +41,7 @@ + #define QUADSPI_QUIRK_TKT253890 (1 << 2) + /* Controller cannot wake up from wait mode, TKT245618 */ + #define QUADSPI_QUIRK_TKT245618 (1 << 3) ++#define QUADSPI_ADDR_REMAP (1 << 4) + + /* The registers */ + #define QUADSPI_MCR 0x00 +@@ -183,7 +184,7 @@ + + /* Macros for constructing the LUT register. */ + #define LUT0(ins, pad, opr) \ +- (((opr) << OPRND0_SHIFT) | ((LUT_##pad) << PAD0_SHIFT) | \ ++ (((opr) << OPRND0_SHIFT) | ((pad) << PAD0_SHIFT) | \ + ((LUT_##ins) << INSTR0_SHIFT)) + + #define LUT1(ins, pad, opr) (LUT0(ins, pad, opr) << OPRND1_SHIFT) +@@ -193,27 +194,29 @@ + #define QUADSPI_LUT_NUM 64 + + /* SEQID -- we can have 16 seqids at most. */ +-#define SEQID_READ 0 +-#define SEQID_WREN 1 +-#define SEQID_WRDI 2 +-#define SEQID_RDSR 3 +-#define SEQID_SE 4 +-#define SEQID_CHIP_ERASE 5 +-#define SEQID_PP 6 +-#define SEQID_RDID 7 +-#define SEQID_WRSR 8 +-#define SEQID_RDCR 9 +-#define SEQID_EN4B 10 +-#define SEQID_BRWR 11 ++/* LUT0 programmed by bootloader, for run-time create entry for LUT seqid 1 */ ++#define SEQID_LUT0_BOOTLOADER 0 ++#define SEQID_LUT1_RUNTIME 1 ++#define SEQID_LUT2_AHBREAD 2 + + #define QUADSPI_MIN_IOMAP SZ_4M + ++enum fsl_qspi_ops { ++ FSL_QSPI_OPS_READ = 0, ++ FSL_QSPI_OPS_WRITE, ++ FSL_QSPI_OPS_ERASE, ++ FSL_QSPI_OPS_READ_REG, ++ FSL_QSPI_OPS_WRITE_REG, ++ FSL_QSPI_OPS_WRITE_BUF_REG, ++}; ++ + 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 { +@@ -267,6 +270,15 @@ static struct fsl_qspi_devtype_data ls10 + .driver_data = 0, + }; + ++static const struct fsl_qspi_devtype_data ls2080a_data = { ++ .devtype = FSL_QUADSPI_LS2080A, ++ .rxfifo = 128, ++ .txfifo = 64, ++ .ahb_buf_size = 1024, ++ .driver_data = QUADSPI_QUIRK_TKT253890 | QUADSPI_ADDR_REMAP, ++}; ++ ++ + #define FSL_QSPI_MAX_CHIP 4 + struct fsl_qspi { + struct spi_nor nor[FSL_QSPI_MAX_CHIP]; +@@ -310,6 +322,22 @@ static inline int needs_wakeup_wait_mode + } + + /* ++ * QSPI memory regions split into two parts: a 256MB region that is located ++ * in the least significant 4GB of the SoC address space and a 3.75GB region ++ * that is located above the least significant 4GB of the SoC address space. ++ * ++ * The 4GB QSPI address space map is shown below. ++ * ++ * SoC Address QSPI Address ++ * 0x00_2000_0000-0x00_2FFF_FFFF 0x00_0000_0000-0x00_0FFF_FFFF First 256MB ++ * 0x04_1000_0000-0x04_FFFF_FFFF 0x00_1000_0000-0x00_FFFF_FFFF Last 3.75GB ++ */ ++static inline int need_address_remap(struct fsl_qspi *q) ++{ ++ return q->devtype_data->driver_data & QUADSPI_ADDR_REMAP; ++} ++ ++/* + * R/W functions for big- or little-endian registers: + * The qSPI controller's endian is independent of the CPU core's endian. + * So far, although the CPU core is little-endian but the qSPI have two +@@ -368,137 +396,160 @@ static irqreturn_t fsl_qspi_irq_handler( + return IRQ_HANDLED; + } + +-static void fsl_qspi_init_lut(struct fsl_qspi *q) ++static inline s8 pad_count(s8 pad_val) + { ++ s8 count = -1; ++ ++ if (!pad_val) ++ return 0; ++ ++ while (pad_val) { ++ pad_val >>= 1; ++ count++; ++ } ++ return count; ++} ++ ++/* ++ * Prepare LUT entry for the input cmd. ++ * Protocol info is present in instance of struct spi_nor, using which fields ++ * like cmd, data, addrlen along with pad info etc can be parsed. ++ */ ++static void fsl_qspi_prepare_lut(struct spi_nor *nor, ++ enum fsl_qspi_ops ops, u8 cmd) ++{ ++ struct fsl_qspi *q = nor->priv; + void __iomem *base = q->iobase; + int rxfifo = q->devtype_data->rxfifo; ++ int txfifo = q->devtype_data->txfifo; + u32 lut_base; +- int i; ++ u8 cmd_pad, addr_pad, data_pad, dummy_pad; ++ enum spi_nor_protocol protocol = 0; ++ u8 addrlen = 0; ++ u8 read_dm, opcode; ++ int stop_lut; ++ ++ read_dm = opcode = cmd_pad = addr_pad = data_pad = dummy_pad = 0; ++ ++ switch (ops) { ++ case FSL_QSPI_OPS_READ_REG: ++ case FSL_QSPI_OPS_WRITE_REG: ++ case FSL_QSPI_OPS_WRITE_BUF_REG: ++ opcode = cmd; ++ protocol = nor->reg_proto; ++ break; ++ case FSL_QSPI_OPS_READ: ++ opcode = cmd; ++ read_dm = nor->read_dummy; ++ protocol = nor->read_proto; ++ break; ++ case FSL_QSPI_OPS_WRITE: ++ opcode = cmd; ++ protocol = nor->write_proto; ++ break; ++ case FSL_QSPI_OPS_ERASE: ++ opcode = cmd; ++ break; ++ default: ++ dev_err(q->dev, "Unsupported operation 0x%.2x\n", ops); ++ return; ++ } ++ ++ if (protocol) { ++ cmd_pad = spi_nor_get_protocol_inst_nbits(protocol); ++ addr_pad = spi_nor_get_protocol_addr_nbits(protocol); ++ data_pad = spi_nor_get_protocol_data_nbits(protocol); ++ } ++ ++ dummy_pad = data_pad; + +- 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; ++ dev_dbg(q->dev, "ops:%x opcode:%x pad[cmd:%d, addr:%d, data:%d]\n", ++ ops, opcode, cmd_pad, addr_pad, data_pad); + + fsl_qspi_unlock_lut(q); + +- /* Clear all the LUT table */ +- for (i = 0; i < QUADSPI_LUT_NUM; i++) +- qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4); +- +- /* Read */ +- lut_base = SEQID_READ * 4; +- +- 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)); +- +- /* Write enable */ +- lut_base = SEQID_WREN * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WREN), +- base + QUADSPI_LUT(lut_base)); +- +- /* Page Program */ +- lut_base = SEQID_PP * 4; +- +- 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)); +- +- /* Read Status */ +- lut_base = SEQID_RDSR * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDSR) | +- LUT1(FSL_READ, PAD1, 0x1), +- base + QUADSPI_LUT(lut_base)); +- +- /* Erase a sector */ +- lut_base = SEQID_SE * 4; +- +- qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) | +- LUT1(ADDR, PAD1, addrlen), +- base + QUADSPI_LUT(lut_base)); +- +- /* Erase the whole chip */ +- lut_base = SEQID_CHIP_ERASE * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE), +- base + QUADSPI_LUT(lut_base)); +- +- /* READ ID */ +- lut_base = SEQID_RDID * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDID) | +- LUT1(FSL_READ, PAD1, 0x8), +- base + QUADSPI_LUT(lut_base)); +- +- /* Write Register */ +- lut_base = SEQID_WRSR * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRSR) | +- LUT1(FSL_WRITE, PAD1, 0x2), +- base + QUADSPI_LUT(lut_base)); +- +- /* Read Configuration Register */ +- lut_base = SEQID_RDCR * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDCR) | +- LUT1(FSL_READ, PAD1, 0x1), +- base + QUADSPI_LUT(lut_base)); +- +- /* Write disable */ +- lut_base = SEQID_WRDI * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRDI), +- base + QUADSPI_LUT(lut_base)); +- +- /* Enter 4 Byte Mode (Micron) */ +- lut_base = SEQID_EN4B * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_EN4B), +- base + QUADSPI_LUT(lut_base)); +- +- /* Enter 4 Byte Mode (Spansion) */ +- lut_base = SEQID_BRWR * 4; +- qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR), +- base + QUADSPI_LUT(lut_base)); ++ /* Dynamic LUT */ ++ lut_base = SEQID_LUT1_RUNTIME * 4; ++ if (ops == FSL_QSPI_OPS_READ) ++ lut_base = SEQID_LUT2_AHBREAD * 4; ++ ++ /* default, STOP instruction to be programmed in (lut_base + 1) reg */ ++ stop_lut = 1; ++ switch (ops) { ++ case FSL_QSPI_OPS_READ_REG: ++ qspi_writel(q, LUT0(CMD, pad_count(cmd_pad), opcode) | ++ LUT1(FSL_READ, pad_count(data_pad), rxfifo), ++ base + QUADSPI_LUT(lut_base)); ++ break; ++ case FSL_QSPI_OPS_WRITE_REG: ++ qspi_writel(q, LUT0(CMD, pad_count(cmd_pad), opcode), ++ base + QUADSPI_LUT(lut_base)); ++ break; ++ case FSL_QSPI_OPS_WRITE_BUF_REG: ++ qspi_writel(q, LUT0(CMD, pad_count(cmd_pad), opcode) | ++ LUT1(FSL_WRITE, pad_count(data_pad), txfifo), ++ base + QUADSPI_LUT(lut_base)); ++ break; ++ case FSL_QSPI_OPS_READ: ++ case FSL_QSPI_OPS_WRITE: ++ case FSL_QSPI_OPS_ERASE: ++ /* Common for Read, Write and Erase ops. */ ++ ++ addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT; ++ ++ qspi_writel(q, LUT0(CMD, pad_count(cmd_pad), opcode) | ++ LUT1(ADDR, pad_count(addr_pad), addrlen), ++ base + QUADSPI_LUT(lut_base)); ++ /* ++ * For Erase ops - Data and Dummy not required. ++ * For Write ops - Dummy not required. ++ */ + +- fsl_qspi_lock_lut(q); +-} ++ if (ops == FSL_QSPI_OPS_READ) { + +-/* Get the SEQID for the command */ +-static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) +-{ +- switch (cmd) { +- case SPINOR_OP_READ_1_1_4: +- case SPINOR_OP_READ_1_1_4_4B: +- return SEQID_READ; +- case SPINOR_OP_WREN: +- return SEQID_WREN; +- case SPINOR_OP_WRDI: +- return SEQID_WRDI; +- case SPINOR_OP_RDSR: +- return SEQID_RDSR; +- case SPINOR_OP_SE: +- return SEQID_SE; +- case SPINOR_OP_CHIP_ERASE: +- return SEQID_CHIP_ERASE; +- case SPINOR_OP_PP: +- return SEQID_PP; +- case SPINOR_OP_RDID: +- return SEQID_RDID; +- case SPINOR_OP_WRSR: +- return SEQID_WRSR; +- case SPINOR_OP_RDCR: +- return SEQID_RDCR; +- case SPINOR_OP_EN4B: +- return SEQID_EN4B; +- case SPINOR_OP_BRWR: +- return SEQID_BRWR; ++ /* ++ * For cmds SPINOR_OP_READ and SPINOR_OP_READ_4B value ++ * of dummy cycles are 0. ++ */ ++ if (read_dm) ++ qspi_writel(q, ++ LUT0(DUMMY, pad_count(dummy_pad), ++ read_dm) | ++ LUT1(FSL_READ, pad_count(data_pad), ++ rxfifo), ++ base + QUADSPI_LUT(lut_base + 1)); ++ else ++ qspi_writel(q, ++ LUT0(FSL_READ, pad_count(data_pad), ++ rxfifo), ++ base + QUADSPI_LUT(lut_base + 1)); ++ ++ stop_lut = 2; ++ ++ /* TODO Add condition to check if READ is IP/AHB. */ ++ ++ /* For AHB read, add seqid in BFGENCR register. */ ++ qspi_writel(q, ++ SEQID_LUT2_AHBREAD << ++ QUADSPI_BFGENCR_SEQID_SHIFT, ++ q->iobase + QUADSPI_BFGENCR); ++ } ++ ++ if (ops == FSL_QSPI_OPS_WRITE) { ++ qspi_writel(q, LUT0(FSL_WRITE, pad_count(data_pad), 0), ++ base + QUADSPI_LUT(lut_base + 1)); ++ stop_lut = 2; ++ } ++ break; + default: +- if (cmd == q->nor[0].erase_opcode) +- return SEQID_SE; +- dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); ++ dev_err(q->dev, "Unsupported operation 0x%.2x\n", ops); + break; + } +- return -EINVAL; ++ ++ /* prepare LUT for STOP instruction. */ ++ qspi_writel(q, 0, base + QUADSPI_LUT(lut_base + stop_lut)); ++ ++ fsl_qspi_lock_lut(q); + } + + static int +@@ -508,6 +559,10 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 c + int seqid; + u32 reg, reg2; + int err; ++ u32 memmap_phyadd = q->memmap_phy; ++ ++ if (need_address_remap(q)) ++ memmap_phyadd = 0; + + init_completion(&q->c); + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len:%d, cmd:%.2x\n", +@@ -516,7 +571,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 c + /* save the reg */ + reg = qspi_readl(q, base + QUADSPI_MCR); + +- qspi_writel(q, q->memmap_phy + q->chip_base_addr + addr, ++ qspi_writel(q, memmap_phyadd + q->chip_base_addr + addr, + base + QUADSPI_SFAR); + qspi_writel(q, QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, + base + QUADSPI_RBCT); +@@ -533,7 +588,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 c + } while (1); + + /* trigger the LUT now */ +- seqid = fsl_qspi_get_seqid(q, cmd); ++ seqid = SEQID_LUT1_RUNTIME; + qspi_writel(q, (seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, + base + QUADSPI_IPCR); + +@@ -609,6 +664,7 @@ static ssize_t fsl_qspi_nor_write(struct + { + 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); +@@ -618,10 +674,18 @@ static ssize_t fsl_qspi_nor_write(struct + 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); ++ if(byts >= 4) ++ tmp = fsl_qspi_endian_xchg(q, *txbuf); ++ else { ++ memcpy(&tmp, txbuf, byts); ++ tmp = fsl_qspi_endian_xchg(q, tmp); ++ } ++ + qspi_writel(q, tmp, q->iobase + QUADSPI_TBDR); + txbuf++; ++ byts -= 4; + } + + /* fill the TXFIFO upto 16 bytes for i.MX7d */ +@@ -642,11 +706,15 @@ static void fsl_qspi_set_map_addr(struct + { + int nor_size = q->nor_size; + void __iomem *base = q->iobase; ++ u32 memmap_phyadd = q->memmap_phy; ++ ++ if (need_address_remap(q)) ++ memmap_phyadd = 0; + +- 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); ++ qspi_writel(q, nor_size + memmap_phyadd, base + QUADSPI_SFA1AD); ++ qspi_writel(q, nor_size * 2 + memmap_phyadd, base + QUADSPI_SFA2AD); ++ qspi_writel(q, nor_size * 3 + memmap_phyadd, base + QUADSPI_SFB1AD); ++ qspi_writel(q, nor_size * 4 + memmap_phyadd, base + QUADSPI_SFB2AD); + } + + /* +@@ -662,7 +730,7 @@ static void fsl_qspi_set_map_addr(struct + * causes the controller to clear the buffer, and use the sequence pointed + * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash. + */ +-static void fsl_qspi_init_abh_read(struct fsl_qspi *q) ++static void fsl_qspi_init_ahb_read(struct fsl_qspi *q) + { + void __iomem *base = q->iobase; + int seqid; +@@ -685,8 +753,8 @@ static void fsl_qspi_init_abh_read(struc + qspi_writel(q, 0, base + QUADSPI_BUF1IND); + qspi_writel(q, 0, base + QUADSPI_BUF2IND); + +- /* Set the default lut sequence for AHB Read. */ +- seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode); ++ /* Set dynamic LUT entry as lut sequence for AHB Read . */ ++ seqid = SEQID_LUT2_AHBREAD; + qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT, + q->iobase + QUADSPI_BFGENCR); + } +@@ -729,7 +797,6 @@ static int fsl_qspi_nor_setup(struct fsl + void __iomem *base = q->iobase; + u32 reg; + int ret; +- + /* disable and unprepare clock to avoid glitch pass to controller */ + fsl_qspi_clk_disable_unprep(q); + +@@ -747,9 +814,6 @@ static int fsl_qspi_nor_setup(struct fsl + base + QUADSPI_MCR); + udelay(1); + +- /* Init the LUT table. */ +- fsl_qspi_init_lut(q); +- + /* Disable the module */ + qspi_writel(q, QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK, + base + QUADSPI_MCR); +@@ -770,6 +834,9 @@ static int fsl_qspi_nor_setup(struct fsl + /* enable the interrupt */ + qspi_writel(q, QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); + ++ /* Init for AHB read */ ++ fsl_qspi_init_ahb_read(q); ++ + return 0; + } + +@@ -792,12 +859,6 @@ static int fsl_qspi_nor_setup_last(struc + if (ret) + return ret; + +- /* Init the LUT table again. */ +- fsl_qspi_init_lut(q); +- +- /* Init for AHB read */ +- fsl_qspi_init_abh_read(q); +- + return 0; + } + +@@ -807,6 +868,7 @@ static const struct of_device_id fsl_qsp + { .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 = &ls2080a_data, }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); +@@ -821,6 +883,7 @@ static int fsl_qspi_read_reg(struct spi_ + int ret; + struct fsl_qspi *q = nor->priv; + ++ fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_READ_REG, opcode); + ret = fsl_qspi_runcmd(q, opcode, 0, len); + if (ret) + return ret; +@@ -835,6 +898,8 @@ static int fsl_qspi_write_reg(struct spi + int ret; + + if (!buf) { ++ /* Prepare LUT for WRITE_REG cmd with input BUF as NULL. */ ++ fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_WRITE_REG, opcode); + ret = fsl_qspi_runcmd(q, opcode, 0, 1); + if (ret) + return ret; +@@ -843,6 +908,8 @@ static int fsl_qspi_write_reg(struct spi + fsl_qspi_invalid(q); + + } else if (len > 0) { ++ /* Prepare LUT for WRITE_REG cmd with input BUF non-NULL. */ ++ fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_WRITE_BUF_REG, opcode); + ret = fsl_qspi_nor_write(q, nor, opcode, 0, + (u32 *)buf, len); + if (ret > 0) +@@ -859,8 +926,11 @@ static ssize_t fsl_qspi_write(struct spi + size_t len, const u_char *buf) + { + struct fsl_qspi *q = nor->priv; +- ssize_t ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to, +- (u32 *)buf, len); ++ ssize_t ret; ++ ++ fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_WRITE, nor->program_opcode); ++ ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to, ++ (u32 *)buf, len); + + /* invalid the data in the AHB buffer. */ + fsl_qspi_invalid(q); +@@ -873,6 +943,8 @@ static ssize_t fsl_qspi_read(struct spi_ + struct fsl_qspi *q = nor->priv; + u8 cmd = nor->read_opcode; + ++ fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_READ, nor->read_opcode); ++ + /* if necessary,ioremap buffer before AHB read, */ + if (!q->ahb_addr) { + q->memmap_offs = q->chip_base_addr + from; +@@ -907,8 +979,9 @@ static ssize_t fsl_qspi_read(struct spi_ + len); + + /* Read out the data directly from the AHB buffer.*/ +- memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, +- len); ++ memcpy_fromio(buf, ++ q->ahb_addr + q->chip_base_addr + from - q->memmap_offs, ++ len); + + return len; + } +@@ -921,6 +994,7 @@ static int fsl_qspi_erase(struct spi_nor + dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n", + nor->mtd.erasesize / 1024, q->chip_base_addr, (u32)offs); + ++ fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_ERASE, nor->erase_opcode); + ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0); + if (ret) + return ret; +@@ -958,17 +1032,14 @@ static void fsl_qspi_unprep(struct spi_n + + static int fsl_qspi_probe(struct platform_device *pdev) + { +- const struct spi_nor_hwcaps hwcaps = { +- .mask = SNOR_HWCAPS_READ_1_1_4 | +- SNOR_HWCAPS_PP, +- }; ++ struct spi_nor_hwcaps hwcaps; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct fsl_qspi *q; + struct resource *res; + struct spi_nor *nor; + struct mtd_info *mtd; +- int ret, i = 0; ++ int ret, i = 0, value; + + q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL); + if (!q) +@@ -1041,6 +1112,10 @@ static int fsl_qspi_probe(struct platfor + + /* iterate the subnodes. */ + for_each_available_child_of_node(dev->of_node, np) { ++ /* Reset hwcaps mask to minimal caps for the slave node. */ ++ hwcaps.mask = SNOR_HWCAPS_READ | SNOR_HWCAPS_PP; ++ value = 0; ++ + /* skip the holes */ + if (!q->has_second_chip) + i *= 2; +@@ -1070,6 +1145,51 @@ static int fsl_qspi_probe(struct platfor + /* set the chip address for READID */ + fsl_qspi_set_base_addr(q, nor); + ++ /* ++ * If spi-rx-bus-width and spi-tx-bus-width not defined assign ++ * default hardware capabilities SNOR_HWCAPS_READ_1_1_4 and ++ * SNOR_HWCAPS_PP supported by the Quad-SPI controller. ++ */ ++ if (!of_property_read_u32(np, "spi-rx-bus-width", &value)) { ++ switch (value) { ++ case 1: ++ hwcaps.mask |= SNOR_HWCAPS_READ | ++ SNOR_HWCAPS_READ_FAST; ++ break; ++ case 2: ++ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2 | ++ SNOR_HWCAPS_READ_1_2_2; ++ break; ++ case 4: ++ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4 | ++ SNOR_HWCAPS_READ_1_4_4; ++ break; ++ default: ++ dev_err(dev, ++ "spi-rx-bus-width %d not supported\n", ++ value); ++ break; ++ } ++ } else ++ hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; ++ ++ if (!of_property_read_u32(np, "spi-tx-bus-width", &value)) { ++ switch (value) { ++ case 1: ++ hwcaps.mask |= SNOR_HWCAPS_PP; ++ break; ++ case 4: ++ hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4 | ++ SNOR_HWCAPS_PP_1_4_4; ++ break; ++ default: ++ dev_err(dev, ++ "spi-tx-bus-width %d not supported\n", ++ value); ++ break; ++ } ++ } ++ + ret = spi_nor_scan(nor, NULL, &hwcaps); + if (ret) + goto mutex_failed; +@@ -1098,6 +1218,8 @@ static int fsl_qspi_probe(struct platfor + if (nor->page_size > q->devtype_data->txfifo) + nor->page_size = q->devtype_data->txfifo; + ++ /*required for memory mapped AHB read*/ ++ fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_READ, nor->read_opcode); + i++; + } + +@@ -1106,6 +1228,8 @@ static int fsl_qspi_probe(struct platfor + if (ret) + goto last_init_failed; + ++ ++ + fsl_qspi_clk_disable_unprep(q); + return 0; + +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1146,6 +1146,11 @@ static const struct flash_info spi_nor_i + { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, ++ { ++ "w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ }, + { "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) }, |