[U-Boot] [PATCH v2 0/4] Update fsl qspi driver

Changes in v2: - Remove two patches below from v1, since spi-nor-core was changed to fix them in framework. [PATCH 2/6] spi: fsl_qspi: Fix erase issue to adapt spi-mem [PATCH 6/6] spi: fsl_qspi: Fix flash write issue with small TX FIFO
- Remove a unused variable in [PATCH 1/6] spi: fsl_qspi: Fix DDR mode setting for latest iMX platforms
Ye Li (4): spi: fsl_qspi: Fix DDR mode setting for latest iMX platforms spi: fsl_qspi: Update write data size for page program LUT spi: fsl_qspi: Update to use driver data spi: fsl_qspi: Add support for QSPI on iMX7ULP
drivers/spi/fsl_qspi.c | 166 +++++++++++++++++++++++++++++++------------------ 1 file changed, 106 insertions(+), 60 deletions(-)

On latest iMX platforms like iMX7D/iMX6UL/iMX8MQ, the QSPI controller is updated to have TDH field in FLSHCR register. According to reference manual, this TDH must be set to 1 when DDR_EN is set. Otherwise, the TX DDR delay logic won't be enabled.
Another issue in DDR mode is the MCR register will be overwritten in every read/write/erase operation. This causes DDR_EN been cleared while TDH=1, then no clk2x output for TX data shift and all operations will fail.
Signed-off-by: Ye Li ye.li@nxp.com --- Changes in v2: - Remove unused variable mcr_val
drivers/spi/fsl_qspi.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 41abe19..e877d99 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -399,7 +399,7 @@ static inline void qspi_ahb_read(struct fsl_qspi_priv *priv, u8 *rxbuf, int len)
qspi_write32(priv->flags, ®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | - QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + mcr_reg);
rx_addr = (void *)(uintptr_t)(priv->cur_amba_base + priv->sf_addr); /* Read out the data directly from the AHB buffer. */ @@ -429,6 +429,14 @@ static void qspi_enable_ddr_mode(struct fsl_qspi_priv *priv) reg |= BIT(29);
qspi_write32(priv->flags, ®s->mcr, reg); + + /* Enable the TDH to 1 for some platforms like imx6ul, imx7d, etc + * These two bits are reserved on other platforms + */ + reg = qspi_read32(priv->flags, ®s->flshcr); + reg &= ~(BIT(17)); + reg |= BIT(16); + qspi_write32(priv->flags, ®s->flshcr, reg); }
/* @@ -482,7 +490,7 @@ static void qspi_op_rdbank(struct fsl_qspi_priv *priv, u8 *rxbuf, u32 len) mcr_reg = qspi_read32(priv->flags, ®s->mcr); qspi_write32(priv->flags, ®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | - QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + mcr_reg); qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS);
qspi_write32(priv->flags, ®s->sfar, priv->cur_amba_base); @@ -527,7 +535,7 @@ static void qspi_op_rdid(struct fsl_qspi_priv *priv, u32 *rxbuf, u32 len) mcr_reg = qspi_read32(priv->flags, ®s->mcr); qspi_write32(priv->flags, ®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | - QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + mcr_reg); qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS);
qspi_write32(priv->flags, ®s->sfar, priv->cur_amba_base); @@ -573,7 +581,7 @@ static void qspi_op_read(struct fsl_qspi_priv *priv, u32 *rxbuf, u32 len) mcr_reg = qspi_read32(priv->flags, ®s->mcr); qspi_write32(priv->flags, ®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | - QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + mcr_reg); qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS);
to_or_from = priv->sf_addr + priv->cur_amba_base; @@ -625,7 +633,7 @@ static void qspi_op_write(struct fsl_qspi_priv *priv, u8 *txbuf, u32 len) mcr_reg = qspi_read32(priv->flags, ®s->mcr); qspi_write32(priv->flags, ®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | - QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + mcr_reg); qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS);
status_reg = 0; @@ -700,7 +708,7 @@ static void qspi_op_rdsr(struct fsl_qspi_priv *priv, void *rxbuf, u32 len) mcr_reg = qspi_read32(priv->flags, ®s->mcr); qspi_write32(priv->flags, ®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | - QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + mcr_reg); qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS);
qspi_write32(priv->flags, ®s->sfar, priv->cur_amba_base); @@ -737,7 +745,7 @@ static void qspi_op_erase(struct fsl_qspi_priv *priv) mcr_reg = qspi_read32(priv->flags, ®s->mcr); qspi_write32(priv->flags, ®s->mcr, QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | - QSPI_MCR_RESERVED_MASK | QSPI_MCR_END_CFD_LE); + mcr_reg); qspi_write32(priv->flags, ®s->rbct, QSPI_RBCT_RXBRD_USEIPS);
to_or_from = priv->sf_addr + priv->cur_amba_base; @@ -867,7 +875,6 @@ static int fsl_qspi_child_pre_probe(struct udevice *dev)
static int fsl_qspi_probe(struct udevice *bus) { - u32 mcr_val; u32 amba_size_per_chip; struct fsl_qspi_platdata *plat = dev_get_platdata(bus); struct fsl_qspi_priv *priv = dev_get_priv(bus); @@ -900,15 +907,9 @@ static int fsl_qspi_probe(struct udevice *bus) return ret; }
- mcr_val = qspi_read32(priv->flags, &priv->regs->mcr); - - /* Set endianness to LE for i.mx */ - if (IS_ENABLED(CONFIG_MX6) || IS_ENABLED(CONFIG_MX7)) - mcr_val = QSPI_MCR_END_CFD_LE; - qspi_write32(priv->flags, &priv->regs->mcr, QSPI_MCR_RESERVED_MASK | QSPI_MCR_MDIS_MASK | - (mcr_val & QSPI_MCR_END_CFD_MASK)); + QSPI_MCR_END_CFD_LE);
qspi_cfg_smpr(priv, ~(QSPI_SMPR_FSDLY_MASK | QSPI_SMPR_DDRSMP_MASK | QSPI_SMPR_FSPHS_MASK | QSPI_SMPR_HSENA_MASK), 0);

The write data size can be overwritten by writing to the IDATSZ field of IPCR register. Since the driver always updates the IDATSZ in page program operation. Set the LUT data size to 0 to align the codes with iMX.
Signed-off-by: Ye Li ye.li@nxp.com --- Changes in v2: - None
drivers/spi/fsl_qspi.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index e877d99..5706569 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -273,19 +273,9 @@ static void qspi_set_lut(struct fsl_qspi_priv *priv) INSTR0(LUT_CMD) | OPRND1(ADDR32BIT) | PAD1(LUT_PAD1) | INSTR1(LUT_ADDR)); #endif -#if defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \ - defined(CONFIG_MX6ULL) || defined(CONFIG_MX7D) - /* - * To MX6SX, OPRND0(TX_BUFFER_SIZE) can not work correctly. - * So, Use IDATSZ in IPCR to determine the size and here set 0. - */ + /* Use IDATSZ in IPCR to determine the size and here set 0. */ qspi_write32(priv->flags, ®s->lut[lut_base + 1], OPRND0(0) | PAD0(LUT_PAD1) | INSTR0(LUT_WRITE)); -#else - qspi_write32(priv->flags, ®s->lut[lut_base + 1], - OPRND0(TX_BUFFER_SIZE) | - PAD0(LUT_PAD1) | INSTR0(LUT_WRITE)); -#endif qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0);

Add the driver data for each compatible string. So we can remove the SOC config and use driver data instead.
Signed-off-by: Ye Li ye.li@nxp.com --- Changes in v2: - None
drivers/spi/fsl_qspi.c | 113 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 34 deletions(-)
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 5706569..d759e98 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -19,14 +19,6 @@
DECLARE_GLOBAL_DATA_PTR;
-#define RX_BUFFER_SIZE 0x80 -#if defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \ - defined(CONFIG_MX6ULL) || defined(CONFIG_MX7D) -#define TX_BUFFER_SIZE 0x200 -#else -#define TX_BUFFER_SIZE 0x40 -#endif - #define OFFSET_BITS_MASK GENMASK(23, 0)
#define FLASH_STATUS_WEL 0x02 @@ -85,6 +77,23 @@ DECLARE_GLOBAL_DATA_PTR; /* QSPI max chipselect signals number */ #define FSL_QSPI_MAX_CHIPSELECT_NUM 4
+/* Controller needs driver to swap endian */ +#define QUADSPI_QUIRK_SWAP_ENDIAN BIT(0) + +enum fsl_qspi_devtype { + FSL_QUADSPI_VYBRID, + FSL_QUADSPI_IMX6SX, + FSL_QUADSPI_IMX6UL_7D, +}; + +struct fsl_qspi_devtype_data { + enum fsl_qspi_devtype devtype; + u32 rxfifo; + u32 txfifo; + u32 ahb_buf_size; + u32 driver_data; +}; + /** * struct fsl_qspi_platdata - platform data for Freescale QSPI * @@ -133,8 +142,32 @@ struct fsl_qspi_priv { u32 flash_num; u32 num_chipselect; struct fsl_qspi_regs *regs; + struct fsl_qspi_devtype_data *devtype_data; };
+static const struct fsl_qspi_devtype_data vybrid_data = { + .devtype = FSL_QUADSPI_VYBRID, + .rxfifo = 128, + .txfifo = 64, + .ahb_buf_size = 1024, + .driver_data = QUADSPI_QUIRK_SWAP_ENDIAN, +}; + +static const struct fsl_qspi_devtype_data imx6sx_data = { + .devtype = FSL_QUADSPI_IMX6SX, + .rxfifo = 128, + .txfifo = 512, + .ahb_buf_size = 1024, + .driver_data = 0, +}; + +static const struct fsl_qspi_devtype_data imx6ul_7d_data = { + .devtype = FSL_QUADSPI_IMX6UL_7D, + .rxfifo = 128, + .txfifo = 512, + .ahb_buf_size = 1024, + .driver_data = 0, +};
static u32 qspi_read32(u32 flags, u32 *addr) { @@ -162,13 +195,12 @@ static inline int is_controller_busy(const struct fsl_qspi_priv *priv)
/* QSPI support swapping the flash read/write data * in hardware for LS102xA, but not for VF610 */ -static inline u32 qspi_endian_xchg(u32 data) +static inline u32 qspi_endian_xchg(struct fsl_qspi_priv *priv, u32 data) { -#ifdef CONFIG_VF610 - return swab32(data); -#else - return data; -#endif + if (priv->devtype_data->driver_data & QUADSPI_QUIRK_SWAP_ENDIAN) + return swab32(data); + else + return data; }
static void qspi_set_lut(struct fsl_qspi_priv *priv) @@ -210,7 +242,7 @@ static void qspi_set_lut(struct fsl_qspi_priv *priv) #endif qspi_write32(priv->flags, ®s->lut[lut_base + 1], OPRND0(8) | PAD0(LUT_PAD1) | INSTR0(LUT_DUMMY) | - OPRND1(RX_BUFFER_SIZE) | PAD1(LUT_PAD1) | + OPRND1(priv->devtype_data->rxfifo) | PAD1(LUT_PAD1) | INSTR1(LUT_READ)); qspi_write32(priv->flags, ®s->lut[lut_base + 2], 0); qspi_write32(priv->flags, ®s->lut[lut_base + 3], 0); @@ -417,7 +449,6 @@ static void qspi_enable_ddr_mode(struct fsl_qspi_priv *priv) reg |= QSPI_MCR_DDR_EN_MASK; /* Enable bit 29 for imx6sx */ reg |= BIT(29); - qspi_write32(priv->flags, ®s->mcr, reg);
/* Enable the TDH to 1 for some platforms like imx6ul, imx7d, etc @@ -451,7 +482,7 @@ static void qspi_init_ahb_read(struct fsl_qspi_priv *priv) qspi_write32(priv->flags, ®s->buf1cr, QSPI_BUFXCR_INVALID_MSTRID); qspi_write32(priv->flags, ®s->buf2cr, QSPI_BUFXCR_INVALID_MSTRID); qspi_write32(priv->flags, ®s->buf3cr, QSPI_BUF3CR_ALLMST_MASK | - (0x80 << QSPI_BUF3CR_ADATSZ_SHIFT)); + ((priv->devtype_data->ahb_buf_size >> 3) << QSPI_BUF3CR_ADATSZ_SHIFT));
/* We only use the buffer3 */ qspi_write32(priv->flags, ®s->buf0ind, 0); @@ -503,7 +534,7 @@ static void qspi_op_rdbank(struct fsl_qspi_priv *priv, u8 *rxbuf, u32 len) reg = qspi_read32(priv->flags, ®s->rbsr); if (reg & QSPI_RBSR_RDBFL_MASK) { data = qspi_read32(priv->flags, ®s->rbdr[0]); - data = qspi_endian_xchg(data); + data = qspi_endian_xchg(priv, data); memcpy(rxbuf, &data, len); qspi_write32(priv->flags, ®s->mcr, qspi_read32(priv->flags, ®s->mcr) | @@ -536,13 +567,13 @@ static void qspi_op_rdid(struct fsl_qspi_priv *priv, u32 *rxbuf, u32 len) ;
i = 0; - while ((RX_BUFFER_SIZE >= len) && (len > 0)) { + while ((priv->devtype_data->rxfifo >= len) && (len > 0)) { WATCHDOG_RESET();
rbsr_reg = qspi_read32(priv->flags, ®s->rbsr); if (rbsr_reg & QSPI_RBSR_RDBFL_MASK) { data = qspi_read32(priv->flags, ®s->rbdr[i]); - data = qspi_endian_xchg(data); + data = qspi_endian_xchg(priv, data); size = (len < 4) ? len : 4; memcpy(rxbuf, &data, size); len -= size; @@ -581,8 +612,8 @@ static void qspi_op_read(struct fsl_qspi_priv *priv, u32 *rxbuf, u32 len)
qspi_write32(priv->flags, ®s->sfar, to_or_from);
- size = (len > RX_BUFFER_SIZE) ? - RX_BUFFER_SIZE : len; + size = (len > priv->devtype_data->rxfifo) ? + priv->devtype_data->rxfifo : len;
qspi_write32(priv->flags, ®s->ipcr, (seqid << QSPI_IPCR_SEQID_SHIFT) | @@ -594,9 +625,9 @@ static void qspi_op_read(struct fsl_qspi_priv *priv, u32 *rxbuf, u32 len) len -= size;
i = 0; - while ((RX_BUFFER_SIZE >= size) && (size > 0)) { + while ((priv->devtype_data->rxfifo >= size) && (size > 0)) { data = qspi_read32(priv->flags, ®s->rbdr[i]); - data = qspi_endian_xchg(data); + data = qspi_endian_xchg(priv, data); if (size < 4) memcpy(rxbuf, &data, size); else @@ -643,7 +674,7 @@ static void qspi_op_write(struct fsl_qspi_priv *priv, u8 *txbuf, u32 len) reg = qspi_read32(priv->flags, ®s->rbsr); if (reg & QSPI_RBSR_RDBFL_MASK) { status_reg = qspi_read32(priv->flags, ®s->rbdr[0]); - status_reg = qspi_endian_xchg(status_reg); + status_reg = qspi_endian_xchg(priv, status_reg); } qspi_write32(priv->flags, ®s->mcr, qspi_read32(priv->flags, ®s->mcr) | @@ -665,8 +696,8 @@ static void qspi_op_write(struct fsl_qspi_priv *priv, u8 *txbuf, u32 len)
qspi_write32(priv->flags, ®s->sfar, to_or_from);
- tx_size = (len > TX_BUFFER_SIZE) ? - TX_BUFFER_SIZE : len; + tx_size = (len > priv->devtype_data->txfifo) ? + priv->devtype_data->txfifo : len;
size = tx_size / 16; /* @@ -677,7 +708,7 @@ static void qspi_op_write(struct fsl_qspi_priv *priv, u8 *txbuf, u32 len) size++; for (i = 0; i < size * 4; i++) { memcpy(&data, txbuf, 4); - data = qspi_endian_xchg(data); + data = qspi_endian_xchg(priv, data); qspi_write32(priv->flags, ®s->tbdr, data); txbuf += 4; } @@ -714,7 +745,7 @@ static void qspi_op_rdsr(struct fsl_qspi_priv *priv, void *rxbuf, u32 len) reg = qspi_read32(priv->flags, ®s->rbsr); if (reg & QSPI_RBSR_RDBFL_MASK) { data = qspi_read32(priv->flags, ®s->rbdr[0]); - data = qspi_endian_xchg(data); + data = qspi_endian_xchg(priv, data); memcpy(rxbuf, &data, len); qspi_write32(priv->flags, ®s->mcr, qspi_read32(priv->flags, ®s->mcr) | @@ -857,8 +888,9 @@ void qspi_cfg_smpr(struct fsl_qspi_priv *priv, u32 clear_bits, u32 set_bits) static int fsl_qspi_child_pre_probe(struct udevice *dev) { struct spi_slave *slave = dev_get_parent_priv(dev); + struct fsl_qspi_priv *priv = dev_get_priv(dev_get_parent(dev));
- slave->max_write_size = TX_BUFFER_SIZE; + slave->max_write_size = priv->devtype_data->txfifo;
return 0; } @@ -889,6 +921,19 @@ static int fsl_qspi_probe(struct udevice *bus) priv->flash_num = plat->flash_num; priv->num_chipselect = plat->num_chipselect;
+ priv->devtype_data = (struct fsl_qspi_devtype_data *)dev_get_driver_data(bus); + if (!priv->devtype_data) { + printf("ERROR : No devtype_data found\n"); + return -ENODEV; + } + + debug("devtype=%d, txfifo=%d, rxfifo=%d, ahb=%d, data=0x%x\n", + priv->devtype_data->devtype, + priv->devtype_data->txfifo, + priv->devtype_data->rxfifo, + priv->devtype_data->ahb_buf_size, + priv->devtype_data->driver_data); + /* make sure controller is not busy anywhere */ ret = is_controller_busy(priv);
@@ -1095,10 +1140,10 @@ static const struct dm_spi_ops fsl_qspi_ops = { };
static const struct udevice_id fsl_qspi_ids[] = { - { .compatible = "fsl,vf610-qspi" }, - { .compatible = "fsl,imx6sx-qspi" }, - { .compatible = "fsl,imx6ul-qspi" }, - { .compatible = "fsl,imx7d-qspi" }, + { .compatible = "fsl,vf610-qspi", .data = (ulong)&vybrid_data }, + { .compatible = "fsl,imx6sx-qspi", .data = (ulong)&imx6sx_data }, + { .compatible = "fsl,imx6ul-qspi", .data = (ulong)&imx6ul_7d_data }, + { .compatible = "fsl,imx7d-qspi", .data = (ulong)&imx6ul_7d_data }, { } };

Add the compatible string and driver data for iMX7ULP platform
Signed-off-by: Ye Li ye.li@nxp.com --- Changes in v2: - None
drivers/spi/fsl_qspi.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index d759e98..8e2a09d 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -84,6 +84,7 @@ enum fsl_qspi_devtype { FSL_QUADSPI_VYBRID, FSL_QUADSPI_IMX6SX, FSL_QUADSPI_IMX6UL_7D, + FSL_QUADSPI_IMX7ULP, };
struct fsl_qspi_devtype_data { @@ -169,6 +170,14 @@ static const struct fsl_qspi_devtype_data imx6ul_7d_data = { .driver_data = 0, };
+static const struct fsl_qspi_devtype_data imx7ulp_data = { + .devtype = FSL_QUADSPI_IMX7ULP, + .rxfifo = 64, + .txfifo = 64, + .ahb_buf_size = 128, + .driver_data = 0, +}; + static u32 qspi_read32(u32 flags, u32 *addr) { return flags & QSPI_FLAG_REGMAP_ENDIAN_BIG ? @@ -1144,6 +1153,7 @@ static const struct udevice_id fsl_qspi_ids[] = { { .compatible = "fsl,imx6sx-qspi", .data = (ulong)&imx6sx_data }, { .compatible = "fsl,imx6ul-qspi", .data = (ulong)&imx6ul_7d_data }, { .compatible = "fsl,imx7d-qspi", .data = (ulong)&imx6ul_7d_data }, + { .compatible = "fsl,imx7ulp-qspi", .data = (ulong)&imx7ulp_data }, { } };
participants (1)
-
Ye Li