OpenWrt – Rev 1

Subversion Repositories:
Rev:
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
@@ -1147,6 +1147,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) },