diff options
| author | Mark Brown <broonie@kernel.org> | 2025-12-15 04:09:43 +0300 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2025-12-15 04:09:43 +0300 |
| commit | 403a4f3c27736f7b2e8e28aa1ab64708b0bd6bcb (patch) | |
| tree | aca8f1a1d5fd5020a26736a8f66f323e02db8c75 | |
| parent | b884e34994ca41f7b7819f3c41b78ff494787b27 (diff) | |
| parent | fee876b2ec75dcc18fdea154eae1f5bf14d82659 (diff) | |
| download | linux-403a4f3c27736f7b2e8e28aa1ab64708b0bd6bcb.tar.xz | |
spi: stm32: Update for OSPI and QSPI drivers
Merge series from Patrice Chotard <patrice.chotard@foss.st.com>:
This serie applies the following updates on the spi-stm32-ospi and
spi-stm32-qspi dirvers :
_ Update FIFO accesses using u16 and u32 when possible instead of u8
only to optimize throughput.
_ Replace Transmit Complete and Transmit Error interrupt management by
usage of read_poll_timeout_atomic() to optimize throughtput.
_ Simplify Status Match interrupt check.
_ Set DMA burst configuration dynamically.
Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
Changes in v2:
- Fix compilation error in stm32_ospi_wait_cmd()
- Link to v1: https://lore.kernel.org/r/20251205-upstream_qspi_ospi_updates-v1-0-7e6c8b9f5141@foss.st.com
---
Patrice Chotard (8):
spi: stm32-ospi: Set DMA maxburst dynamically
spi: stm32-ospi: Optimize FIFO accesses using u16 or u32
spi: stm32-ospi: Remove CR_TCIE and CR_TEIE irq usage
spi: stm32-ospi: Simplify SMIE interrupt test
spi: stm32-qspi: Set DMA maxburst dynamically
spi: stm32-qspi: Optimize FIFO accesses using u16 or u32
spi: stm32-qspi: Remove CR_TCIE and CR_TEIE irq usage
spi: stm32-qspi: Simplify SMIE interrupt test
drivers/spi/spi-stm32-ospi.c | 107 +++++++++++++++++++++++++----------------
drivers/spi/spi-stm32-qspi.c | 111 +++++++++++++++++++++++++------------------
2 files changed, 132 insertions(+), 86 deletions(-)
---
base-commit: 7d0a66e4bb9081d75c82ec4957c50034cb0ea449
change-id: 20251205-upstream_qspi_ospi_updates-4faf7a3b098c
Best regards,
--
Patrice Chotard <patrice.chotard@foss.st.com>
| -rw-r--r-- | drivers/spi/spi-stm32-ospi.c | 107 | ||||
| -rw-r--r-- | drivers/spi/spi-stm32-qspi.c | 111 |
2 files changed, 132 insertions, 86 deletions
diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index f36fd36da269..c36df8d3f5cb 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -34,8 +34,6 @@ #define CR_ABORT BIT(1) #define CR_DMAEN BIT(2) #define CR_FTHRES_SHIFT 8 -#define CR_TEIE BIT(16) -#define CR_TCIE BIT(17) #define CR_SMIE BIT(19) #define CR_APMS BIT(22) #define CR_CSSEL BIT(24) @@ -106,7 +104,7 @@ #define STM32_ABT_TIMEOUT_US 100000 #define STM32_COMP_TIMEOUT_MS 5000 #define STM32_BUSY_TIMEOUT_US 100000 - +#define STM32_WAIT_CMD_TIMEOUT_US 5000 #define STM32_AUTOSUSPEND_DELAY -1 @@ -116,7 +114,6 @@ struct stm32_ospi { struct clk *clk; struct reset_control *rstc; - struct completion data_completion; struct completion match_completion; struct dma_chan *dma_chtx; @@ -142,14 +139,32 @@ struct stm32_ospi { struct mutex lock; }; -static void stm32_ospi_read_fifo(u8 *val, void __iomem *addr) +static void stm32_ospi_read_fifo(void *val, void __iomem *addr, u8 len) { - *val = readb_relaxed(addr); + switch (len) { + case sizeof(u32): + *((u32 *)val) = readl_relaxed(addr); + break; + case sizeof(u16): + *((u16 *)val) = readw_relaxed(addr); + break; + case sizeof(u8): + *((u8 *)val) = readb_relaxed(addr); + }; } -static void stm32_ospi_write_fifo(u8 *val, void __iomem *addr) +static void stm32_ospi_write_fifo(void *val, void __iomem *addr, u8 len) { - writeb_relaxed(*val, addr); + switch (len) { + case sizeof(u32): + writel_relaxed(*((u32 *)val), addr); + break; + case sizeof(u16): + writew_relaxed(*((u16 *)val), addr); + break; + case sizeof(u8): + writeb_relaxed(*((u8 *)val), addr); + }; } static int stm32_ospi_abort(struct stm32_ospi *ospi) @@ -172,19 +187,20 @@ static int stm32_ospi_abort(struct stm32_ospi *ospi) return timeout; } -static int stm32_ospi_poll(struct stm32_ospi *ospi, u8 *buf, u32 len, bool read) +static int stm32_ospi_poll(struct stm32_ospi *ospi, void *buf, u32 len, bool read) { void __iomem *regs_base = ospi->regs_base; - void (*fifo)(u8 *val, void __iomem *addr); + void (*fifo)(void *val, void __iomem *addr, u8 len); u32 sr; int ret; + u8 step; if (read) fifo = stm32_ospi_read_fifo; else fifo = stm32_ospi_write_fifo; - while (len--) { + while (len) { ret = readl_relaxed_poll_timeout_atomic(regs_base + OSPI_SR, sr, sr & SR_FTF, 1, STM32_FIFO_TIMEOUT_US); @@ -193,7 +209,17 @@ static int stm32_ospi_poll(struct stm32_ospi *ospi, u8 *buf, u32 len, bool read) len, sr); return ret; } - fifo(buf++, regs_base + OSPI_DR); + + if (len >= sizeof(u32)) + step = sizeof(u32); + else if (len >= sizeof(u16)) + step = sizeof(u16); + else + step = sizeof(u8); + + fifo(buf, regs_base + OSPI_DR, step); + len -= step; + buf += step; } return 0; @@ -211,22 +237,16 @@ static int stm32_ospi_wait_nobusy(struct stm32_ospi *ospi) static int stm32_ospi_wait_cmd(struct stm32_ospi *ospi) { void __iomem *regs_base = ospi->regs_base; - u32 cr, sr; + u32 sr; int err = 0; - if ((readl_relaxed(regs_base + OSPI_SR) & SR_TCF) || - ospi->fmode == CR_FMODE_APM) + if (ospi->fmode == CR_FMODE_APM) goto out; - reinit_completion(&ospi->data_completion); - cr = readl_relaxed(regs_base + OSPI_CR); - writel_relaxed(cr | CR_TCIE | CR_TEIE, regs_base + OSPI_CR); + err = readl_relaxed_poll_timeout_atomic(ospi->regs_base + OSPI_SR, sr, + (sr & (SR_TEF | SR_TCF)), 1, + STM32_WAIT_CMD_TIMEOUT_US); - if (!wait_for_completion_timeout(&ospi->data_completion, - msecs_to_jiffies(STM32_COMP_TIMEOUT_MS))) - err = -ETIMEDOUT; - - sr = readl_relaxed(regs_base + OSPI_SR); if (sr & SR_TCF) /* avoid false timeout */ err = 0; @@ -259,29 +279,29 @@ static irqreturn_t stm32_ospi_irq(int irq, void *dev_id) cr = readl_relaxed(regs_base + OSPI_CR); sr = readl_relaxed(regs_base + OSPI_SR); - if (cr & CR_SMIE && sr & SR_SMF) { + if (sr & SR_SMF) { /* disable irq */ cr &= ~CR_SMIE; writel_relaxed(cr, regs_base + OSPI_CR); complete(&ospi->match_completion); - - return IRQ_HANDLED; - } - - if (sr & (SR_TEF | SR_TCF)) { - /* disable irq */ - cr &= ~CR_TCIE & ~CR_TEIE; - writel_relaxed(cr, regs_base + OSPI_CR); - complete(&ospi->data_completion); } return IRQ_HANDLED; } -static void stm32_ospi_dma_setup(struct stm32_ospi *ospi, - struct dma_slave_config *dma_cfg) +static int stm32_ospi_dma_setup(struct stm32_ospi *ospi, + struct dma_slave_config *dma_cfg) { + struct dma_slave_caps caps; + int ret = 0; + if (dma_cfg && ospi->dma_chrx) { + ret = dma_get_slave_caps(ospi->dma_chrx, &caps); + if (ret) + return ret; + + dma_cfg->src_maxburst = caps.max_burst / dma_cfg->src_addr_width; + if (dmaengine_slave_config(ospi->dma_chrx, dma_cfg)) { dev_err(ospi->dev, "dma rx config failed\n"); dma_release_channel(ospi->dma_chrx); @@ -290,6 +310,12 @@ static void stm32_ospi_dma_setup(struct stm32_ospi *ospi, } if (dma_cfg && ospi->dma_chtx) { + ret = dma_get_slave_caps(ospi->dma_chtx, &caps); + if (ret) + return ret; + + dma_cfg->dst_maxburst = caps.max_burst / dma_cfg->dst_addr_width; + if (dmaengine_slave_config(ospi->dma_chtx, dma_cfg)) { dev_err(ospi->dev, "dma tx config failed\n"); dma_release_channel(ospi->dma_chtx); @@ -298,6 +324,8 @@ static void stm32_ospi_dma_setup(struct stm32_ospi *ospi, } init_completion(&ospi->dma_completion); + + return ret; } static int stm32_ospi_tx_mm(struct stm32_ospi *ospi, @@ -391,7 +419,7 @@ static int stm32_ospi_xfer(struct stm32_ospi *ospi, const struct spi_mem_op *op) if (op->data.dir == SPI_MEM_DATA_IN) buf = op->data.buf.in; else - buf = (u8 *)op->data.buf.out; + buf = (void *)op->data.buf.out; return stm32_ospi_poll(ospi, buf, op->data.nbytes, op->data.dir == SPI_MEM_DATA_IN); @@ -838,7 +866,6 @@ static int stm32_ospi_get_resources(struct platform_device *pdev) dev_info(dev, "No memory-map region found\n"); } - init_completion(&ospi->data_completion); init_completion(&ospi->match_completion); return 0; @@ -899,9 +926,9 @@ static int stm32_ospi_probe(struct platform_device *pdev) dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_cfg.src_addr = ospi->regs_phys_base + OSPI_DR; dma_cfg.dst_addr = ospi->regs_phys_base + OSPI_DR; - dma_cfg.src_maxburst = 4; - dma_cfg.dst_maxburst = 4; - stm32_ospi_dma_setup(ospi, &dma_cfg); + ret = stm32_ospi_dma_setup(ospi, &dma_cfg); + if (ret) + return ret; mutex_init(&ospi->lock); diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index f2d19f1c5ab1..2a0ee96786fa 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -31,8 +31,6 @@ #define CR_DFM BIT(6) #define CR_FSEL BIT(7) #define CR_FTHRES_SHIFT 8 -#define CR_TEIE BIT(16) -#define CR_TCIE BIT(17) #define CR_FTIE BIT(18) #define CR_SMIE BIT(19) #define CR_TOIE BIT(20) @@ -86,11 +84,12 @@ #define STM32_QSPI_MAX_MMAP_SZ SZ_256M #define STM32_QSPI_MAX_NORCHIP 2 -#define STM32_FIFO_TIMEOUT_US 30000 -#define STM32_BUSY_TIMEOUT_US 100000 -#define STM32_ABT_TIMEOUT_US 100000 -#define STM32_COMP_TIMEOUT_MS 1000 -#define STM32_AUTOSUSPEND_DELAY -1 +#define STM32_FIFO_TIMEOUT_US 30000 +#define STM32_BUSY_TIMEOUT_US 100000 +#define STM32_ABT_TIMEOUT_US 100000 +#define STM32_WAIT_CMD_TIMEOUT_US 5000 +#define STM32_COMP_TIMEOUT_MS 1000 +#define STM32_AUTOSUSPEND_DELAY -1 struct stm32_qspi_flash { u32 cs; @@ -107,7 +106,6 @@ struct stm32_qspi { struct clk *clk; u32 clk_rate; struct stm32_qspi_flash flash[STM32_QSPI_MAX_NORCHIP]; - struct completion data_completion; struct completion match_completion; u32 fmode; @@ -134,53 +132,63 @@ static irqreturn_t stm32_qspi_irq(int irq, void *dev_id) cr = readl_relaxed(qspi->io_base + QSPI_CR); sr = readl_relaxed(qspi->io_base + QSPI_SR); - if (cr & CR_SMIE && sr & SR_SMF) { + if (sr & SR_SMF) { /* disable irq */ cr &= ~CR_SMIE; writel_relaxed(cr, qspi->io_base + QSPI_CR); complete(&qspi->match_completion); - - return IRQ_HANDLED; - } - - if (sr & (SR_TEF | SR_TCF)) { - /* disable irq */ - cr &= ~CR_TCIE & ~CR_TEIE; - writel_relaxed(cr, qspi->io_base + QSPI_CR); - complete(&qspi->data_completion); } return IRQ_HANDLED; } -static void stm32_qspi_read_fifo(u8 *val, void __iomem *addr) +static void stm32_qspi_read_fifo(void *val, void __iomem *addr, u8 len) { - *val = readb_relaxed(addr); + switch (len) { + case sizeof(u32): + *((u32 *)val) = readl_relaxed(addr); + break; + case sizeof(u16): + *((u16 *)val) = readw_relaxed(addr); + break; + case sizeof(u8): + *((u8 *)val) = readb_relaxed(addr); + }; } -static void stm32_qspi_write_fifo(u8 *val, void __iomem *addr) +static void stm32_qspi_write_fifo(void *val, void __iomem *addr, u8 len) { - writeb_relaxed(*val, addr); + switch (len) { + case sizeof(u32): + writel_relaxed(*((u32 *)val), addr); + break; + case sizeof(u16): + writew_relaxed(*((u16 *)val), addr); + break; + case sizeof(u8): + writeb_relaxed(*((u8 *)val), addr); + }; } static int stm32_qspi_tx_poll(struct stm32_qspi *qspi, const struct spi_mem_op *op) { - void (*tx_fifo)(u8 *val, void __iomem *addr); + void (*fifo)(void *val, void __iomem *addr, u8 len); u32 len = op->data.nbytes, sr; - u8 *buf; + void *buf; int ret; + u8 step; if (op->data.dir == SPI_MEM_DATA_IN) { - tx_fifo = stm32_qspi_read_fifo; + fifo = stm32_qspi_read_fifo; buf = op->data.buf.in; } else { - tx_fifo = stm32_qspi_write_fifo; - buf = (u8 *)op->data.buf.out; + fifo = stm32_qspi_write_fifo; + buf = (void *)op->data.buf.out; } - while (len--) { + while (len) { ret = readl_relaxed_poll_timeout_atomic(qspi->io_base + QSPI_SR, sr, (sr & SR_FTF), 1, STM32_FIFO_TIMEOUT_US); @@ -189,7 +197,17 @@ static int stm32_qspi_tx_poll(struct stm32_qspi *qspi, len, sr); return ret; } - tx_fifo(buf++, qspi->io_base + QSPI_DR); + + if (len >= sizeof(u32)) + step = sizeof(u32); + else if (len >= sizeof(u16)) + step = sizeof(u16); + else + step = sizeof(u8); + + fifo(buf, qspi->io_base + QSPI_DR, step); + len -= step; + buf += step; } return 0; @@ -301,25 +319,18 @@ static int stm32_qspi_wait_nobusy(struct stm32_qspi *qspi) static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi) { - u32 cr, sr; + u32 sr; int err = 0; - if ((readl_relaxed(qspi->io_base + QSPI_SR) & SR_TCF) || - qspi->fmode == CCR_FMODE_APM) + if (qspi->fmode == CCR_FMODE_APM) goto out; - reinit_completion(&qspi->data_completion); - cr = readl_relaxed(qspi->io_base + QSPI_CR); - writel_relaxed(cr | CR_TCIE | CR_TEIE, qspi->io_base + QSPI_CR); + err = readl_relaxed_poll_timeout_atomic(qspi->io_base + QSPI_SR, sr, + (sr & (SR_TEF | SR_TCF)), 1, + STM32_WAIT_CMD_TIMEOUT_US); - if (!wait_for_completion_timeout(&qspi->data_completion, - msecs_to_jiffies(STM32_COMP_TIMEOUT_MS))) { - err = -ETIMEDOUT; - } else { - sr = readl_relaxed(qspi->io_base + QSPI_SR); - if (sr & SR_TEF) - err = -EIO; - } + if (sr & SR_TEF) + err = -EIO; out: /* clear flags */ @@ -689,6 +700,7 @@ static int stm32_qspi_dma_setup(struct stm32_qspi *qspi) { struct dma_slave_config dma_cfg; struct device *dev = qspi->dev; + struct dma_slave_caps caps; int ret = 0; memset(&dma_cfg, 0, sizeof(dma_cfg)); @@ -697,8 +709,6 @@ static int stm32_qspi_dma_setup(struct stm32_qspi *qspi) dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_cfg.src_addr = qspi->phys_base + QSPI_DR; dma_cfg.dst_addr = qspi->phys_base + QSPI_DR; - dma_cfg.src_maxburst = 4; - dma_cfg.dst_maxburst = 4; qspi->dma_chrx = dma_request_chan(dev, "rx"); if (IS_ERR(qspi->dma_chrx)) { @@ -707,6 +717,11 @@ static int stm32_qspi_dma_setup(struct stm32_qspi *qspi) if (ret == -EPROBE_DEFER) goto out; } else { + ret = dma_get_slave_caps(qspi->dma_chrx, &caps); + if (ret) + return ret; + + dma_cfg.src_maxburst = caps.max_burst / dma_cfg.src_addr_width; if (dmaengine_slave_config(qspi->dma_chrx, &dma_cfg)) { dev_err(dev, "dma rx config failed\n"); dma_release_channel(qspi->dma_chrx); @@ -719,6 +734,11 @@ static int stm32_qspi_dma_setup(struct stm32_qspi *qspi) ret = PTR_ERR(qspi->dma_chtx); qspi->dma_chtx = NULL; } else { + ret = dma_get_slave_caps(qspi->dma_chtx, &caps); + if (ret) + return ret; + + dma_cfg.dst_maxburst = caps.max_burst / dma_cfg.dst_addr_width; if (dmaengine_slave_config(qspi->dma_chtx, &dma_cfg)) { dev_err(dev, "dma tx config failed\n"); dma_release_channel(qspi->dma_chtx); @@ -797,7 +817,6 @@ static int stm32_qspi_probe(struct platform_device *pdev) return ret; } - init_completion(&qspi->data_completion); init_completion(&qspi->match_completion); qspi->clk = devm_clk_get(dev, NULL); |
