From fe21ef44284a3aa6fd80448e4ab2e1e8a55fb926 Mon Sep 17 00:00:00 2001 From: Biwen Li Date: Wed, 17 Apr 2019 18:58:59 +0800 Subject: [PATCH] qspi: support layerscape This is an integrated patch of qspi for layerscape Signed-off-by: Abhimanyu Saini Signed-off-by: Biwen Li Signed-off-by: Chuanhua Han Signed-off-by: Cyrille Pitchen Signed-off-by: Mark Brown Signed-off-by: Neil Armstrong Signed-off-by: Prabhakar Kushwaha Signed-off-by: Suresh Gupta Signed-off-by: Yangbo Lu Signed-off-by: Yogesh Gaur --- drivers/mtd/spi-nor/fsl-quadspi.c | 444 +++++++++++++++++++----------- drivers/mtd/spi-nor/spi-nor.c | 5 + drivers/spi/spi-fsl-dspi.c | 4 +- 3 files changed, 291 insertions(+), 162 deletions(-) --- 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 @@ -1159,6 +1159,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) }, --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1024,8 +1024,8 @@ static int dspi_probe(struct platform_de goto out_clk_put; } - ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0, - pdev->name, dspi); + ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, + IRQF_SHARED, pdev->name, dspi); if (ret < 0) { dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n"); goto out_clk_put;