From fd5c4cd63f179220345093f617455e3d8d7faad4 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:25 +0000 Subject: [PATCH 01/20] Revert "spi: dw: Wait for idle after TX" This reverts commit 0729ba7c2799cb2bb98bef7bdc27137f58b049ea. --- drivers/spi/spi-dw-dma.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index d9c790eda0398f..c34c202d617eb4 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -304,12 +304,6 @@ static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, return -EIO; } - if (!xfer->rx_buf) { - delay.value = dws->n_bytes * BITS_PER_BYTE; - while (dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY) - spi_delay_exec(&delay, xfer); - } - return 0; } From 97d63b8938468748254e958f401a76dd6383fb20 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:25 +0000 Subject: [PATCH 02/20] Revert "spi: dw: Let the DMAC set the transfer widths" This reverts commit 4585e9f7edb9e30134445f9bef025e6aadc0c240. --- drivers/spi/spi-dw-dma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index c34c202d617eb4..82d752e71dd5b7 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -330,6 +330,7 @@ static int dw_spi_dma_config_tx(struct dw_spi *dws) txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; txconf.dst_maxburst = dws->txburst; + txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes); txconf.device_fc = false; @@ -430,6 +431,7 @@ static int dw_spi_dma_config_rx(struct dw_spi *dws) rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; rxconf.src_maxburst = dws->rxburst; + rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes); rxconf.device_fc = false; From cd1ae3239ecf3cbe26a1bf0f1ad6e0f6f2831ec5 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:26 +0000 Subject: [PATCH 03/20] Revert "spi: dw: Clamp the minimum clock speed" This reverts commit fe59dbb5b30e6c1b04923346f29f612266da4bce. --- drivers/spi/spi-dw-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index b73aea4c253579..33a9c63ce8e23b 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -399,7 +399,7 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0); /* Note DW APB SSI clock divider doesn't support odd numbers */ - clk_div = min(DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1, 0xfffe) & 0xfffe; + clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe; speed_hz = dws->max_freq / clk_div; if (dws->current_freq != speed_hz) { From 17abe5be8d06dd65c90ba05e08ed86b58f5fd798 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:26 +0000 Subject: [PATCH 04/20] Revert "spi: dw: Fix non-DMA transmit-only transfers" This reverts commit 673012d14bd782fb8cf719f6f00e6f60724f7283. --- drivers/spi/spi-dw-core.c | 61 ++++++--------------------------------- drivers/spi/spi-dw.h | 3 -- 2 files changed, 9 insertions(+), 55 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 33a9c63ce8e23b..121ed5f9a8ae03 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -222,32 +222,6 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw) } EXPORT_SYMBOL_NS_GPL(dw_spi_check_status, "SPI_DW_CORE"); -static inline bool dw_spi_ctlr_busy(struct dw_spi *dws) -{ - return dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY; -} - -static enum hrtimer_restart dw_spi_hrtimer_handler(struct hrtimer *hr) -{ - struct dw_spi *dws = container_of(hr, struct dw_spi, hrtimer); - - if (!dw_spi_ctlr_busy(dws)) { - spi_finalize_current_transfer(dws->host); - return HRTIMER_NORESTART; - } - - if (!dws->idle_wait_retries) { - dev_err(&dws->host->dev, "controller stuck at busy\n"); - spi_finalize_current_transfer(dws->host); - return HRTIMER_NORESTART; - } - - dws->idle_wait_retries--; - hrtimer_forward_now(hr, dws->idle_wait_interval); - - return HRTIMER_RESTART; -} - static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) { u16 irq_status = dw_readl(dws, DW_SPI_ISR); @@ -274,22 +248,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) } } else if (!dws->tx_len) { dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); - if (dw_spi_ctlr_busy(dws)) { - ktime_t period = ns_to_ktime(DIV_ROUND_UP(NSEC_PER_SEC, dws->current_freq)); - - /* - * Make the initial wait an underestimate of how long the transfer - * should take, then poll rapidly to reduce the delay - */ - hrtimer_start(&dws->hrtimer, - period * (8 * dws->n_bytes - 1), - HRTIMER_MODE_REL); - dws->idle_wait_retries = 10; - dws->idle_wait_interval = period; - } else { - spi_finalize_current_transfer(dws->host); - } - return IRQ_HANDLED; + spi_finalize_current_transfer(dws->host); } /* @@ -298,13 +257,9 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) * have the TXE IRQ flood at the final stage of the transfer. */ if (irq_status & DW_SPI_INT_TXEI) { + if (!dws->tx_len) + dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); dw_writer(dws); - if (!dws->tx_len) { - if (dws->rx_len) - dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); - else - dw_writel(dws, DW_SPI_TXFTLR, 0); - } } return IRQ_HANDLED; @@ -475,7 +430,7 @@ static int dw_spi_poll_transfer(struct dw_spi *dws, ret = dw_spi_check_status(dws, true); if (ret) return ret; - } while (dws->rx_len || dws->tx_len || dw_spi_ctlr_busy(dws)); + } while (dws->rx_len); return 0; } @@ -695,6 +650,11 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) return 0; } +static inline bool dw_spi_ctlr_busy(struct dw_spi *dws) +{ + return dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY; +} + static int dw_spi_wait_mem_op_done(struct dw_spi *dws) { int retry = DW_SPI_WAIT_RETRIES; @@ -1051,8 +1011,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) } } - hrtimer_setup(&dws->hrtimer, dw_spi_hrtimer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - ret = spi_register_controller(host); if (ret) { dev_err_probe(dev, ret, "problem registering spi host\n"); @@ -1078,7 +1036,6 @@ void dw_spi_remove_host(struct dw_spi *dws) { dw_spi_debugfs_remove(dws); - hrtimer_cancel(&dws->hrtimer); spi_unregister_controller(dws->host); if (dws->dma_ops && dws->dma_ops->dma_exit) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 92e54a51fc46d8..fc267c6437ae09 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -180,9 +180,6 @@ struct dw_spi { u32 current_freq; /* frequency in hz */ u32 cur_rx_sample_dly; u32 def_rx_sample_dly_ns; - struct hrtimer hrtimer; - ktime_t idle_wait_interval; - int idle_wait_retries; /* Custom memory operations */ struct spi_controller_mem_ops mem_ops; From 869e7ab4caae1d95027943d2b5144aaee9296714 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:26 +0000 Subject: [PATCH 05/20] Revert "spi: dw: don't immediately kill DMA transfers if an error occurs" This reverts commit c917e363521a5de7487978bef5af66c346d2e6a6. --- drivers/spi/spi-dw-core.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 121ed5f9a8ae03..02701a68eff5cc 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -202,18 +202,7 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw) /* Generically handle the erroneous situation */ if (ret) { - /* - * Forcibly halting the controller can cause DMA to hang. - * Defer to dw_spi_handle_err outside of interrupt context - * and mask further interrupts for the current transfer. - */ - if (dws->dma_mapped) { - dw_spi_mask_intr(dws, 0xff); - dw_readl(dws, DW_SPI_ICR); - } else { - dw_spi_reset_chip(dws); - } - + dw_spi_reset_chip(dws); if (dws->host->cur_msg) dws->host->cur_msg->status = ret; } From 55cdf7ed54c343886876770e044f2f7004765474 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:26 +0000 Subject: [PATCH 06/20] Revert "spi: dw: Save bandwidth with the TMOD_RO feature" This reverts commit b1352f36c756a6d25543d3fdae9f3a9cf3906e71. --- drivers/spi/spi-dw-core.c | 31 +++++++--------------------- drivers/spi/spi-dw-dma.c | 43 ++++++++++++++------------------------- 2 files changed, 22 insertions(+), 52 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 02701a68eff5cc..75a872285bf20a 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -369,18 +369,18 @@ static void dw_spi_irq_setup(struct dw_spi *dws) * will be adjusted at the final stage of the IRQ-based SPI transfer * execution so not to lose the leftover of the incoming data. */ - level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len ? dws->tx_len : dws->rx_len); + level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len); dw_writel(dws, DW_SPI_TXFTLR, level); dw_writel(dws, DW_SPI_RXFTLR, level - 1); dws->transfer_handler = dw_spi_transfer_handler; - imask = DW_SPI_INT_TXEI | DW_SPI_INT_TXOI; + imask = 0; + if (dws->tx_len) + imask |= DW_SPI_INT_TXEI | DW_SPI_INT_TXOI; if (dws->rx_len) imask |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI; dw_spi_umask_intr(dws, imask); - if (!dws->tx_len) - dw_writel(dws, DW_SPI_DR, 0); } /* @@ -403,18 +403,13 @@ static int dw_spi_poll_transfer(struct dw_spi *dws, delay.unit = SPI_DELAY_UNIT_SCK; nbits = dws->n_bytes * BITS_PER_BYTE; - if (!dws->tx_len) - dw_writel(dws, DW_SPI_DR, 0); - do { - if (dws->tx_len) - dw_writer(dws); + dw_writer(dws); delay.value = nbits * (dws->rx_len - dws->tx_len); spi_delay_exec(&delay, transfer); - if (dws->rx_len) - dw_reader(dws); + dw_reader(dws); ret = dw_spi_check_status(dws, true); if (ret) @@ -434,7 +429,6 @@ static int dw_spi_transfer_one(struct spi_controller *host, .dfs = transfer->bits_per_word, .freq = transfer->speed_hz, }; - int buswidth; int ret; dws->dma_mapped = 0; @@ -449,18 +443,6 @@ static int dw_spi_transfer_one(struct spi_controller *host, cfg.tmode = DW_SPI_CTRLR0_TMOD_TO; } - if (!dws->rx) { - dws->rx_len = 0; - cfg.tmode = DW_SPI_CTRLR0_TMOD_TO; - } - if (!dws->tx) { - dws->tx_len = 0; - cfg.tmode = DW_SPI_CTRLR0_TMOD_RO; - cfg.ndf = dws->rx_len; - } - buswidth = transfer->rx_buf ? transfer->rx_nbits : - (transfer->tx_buf ? transfer->tx_nbits : 1); - /* Ensure the data above is visible for all CPUs */ smp_mb(); @@ -997,6 +979,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dev_warn(dev, "DMA init failed\n"); } else { host->can_dma = dws->dma_ops->can_dma; + host->flags |= SPI_CONTROLLER_MUST_TX; } } diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 82d752e71dd5b7..b5bed02b7e5006 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -6,7 +6,6 @@ */ #include -#include #include #include #include @@ -471,12 +470,13 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) u16 imr, dma_ctrl; int ret; + if (!xfer->tx_buf) + return -EINVAL; + /* Setup DMA channels */ - if (xfer->tx_buf) { - ret = dw_spi_dma_config_tx(dws); - if (ret) - return ret; - } + ret = dw_spi_dma_config_tx(dws); + if (ret) + return ret; if (xfer->rx_buf) { ret = dw_spi_dma_config_rx(dws); @@ -485,17 +485,13 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) } /* Set the DMA handshaking interface */ - dma_ctrl = 0; - if (xfer->tx_buf) - dma_ctrl |= DW_SPI_DMACR_TDMAE; + dma_ctrl = DW_SPI_DMACR_TDMAE; if (xfer->rx_buf) dma_ctrl |= DW_SPI_DMACR_RDMAE; dw_writel(dws, DW_SPI_DMACR, dma_ctrl); /* Set the interrupt mask */ - imr = 0; - if (xfer->tx_buf) - imr |= DW_SPI_INT_TXOI; + imr = DW_SPI_INT_TXOI; if (xfer->rx_buf) imr |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI; dw_spi_umask_intr(dws, imr); @@ -512,16 +508,15 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, { int ret; - /* Submit the DMA Tx transfer if required */ - if (xfer->tx_buf) { - ret = dw_spi_dma_submit_tx(dws, xfer->tx_sg.sgl, xfer->tx_sg.nents); - if (ret) - goto err_clear_dmac; - } + /* Submit the DMA Tx transfer */ + ret = dw_spi_dma_submit_tx(dws, xfer->tx_sg.sgl, xfer->tx_sg.nents); + if (ret) + goto err_clear_dmac; /* Submit the DMA Rx transfer if required */ if (xfer->rx_buf) { - ret = dw_spi_dma_submit_rx(dws, xfer->rx_sg.sgl, xfer->rx_sg.nents); + ret = dw_spi_dma_submit_rx(dws, xfer->rx_sg.sgl, + xfer->rx_sg.nents); if (ret) goto err_clear_dmac; @@ -529,15 +524,7 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, dma_async_issue_pending(dws->rxchan); } - if (xfer->tx_buf) { - dma_async_issue_pending(dws->txchan); - } else { - /* Pause to allow DMA channel to fetch RX descriptor */ - usleep_range(5, 10); - - /* Write something to the TX FIFO to start the transfer */ - dw_writel(dws, DW_SPI_DR, 0); - } + dma_async_issue_pending(dws->txchan); ret = dw_spi_dma_wait(dws, xfer->len, xfer->effective_speed_hz); From eb6f9afd13c40fa2a81cb1b38f1b41f80f85e0e6 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:27 +0000 Subject: [PATCH 07/20] Revert "spi: dw: Save bandwidth with the TMOD_TO feature" This reverts commit 96cbfedaf50e9a6001c2b48f65d3b47fb8256a9b. --- drivers/spi/spi-dw-core.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 75a872285bf20a..0ae104d38e0521 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -227,17 +227,12 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) * final stage of the transfer. By doing so we'll get the next IRQ * right when the leftover incoming data is received. */ - if (dws->rx_len) { - dw_reader(dws); - if (!dws->rx_len) { - dw_spi_mask_intr(dws, 0xff); - spi_finalize_current_transfer(dws->host); - } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { - dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); - } - } else if (!dws->tx_len) { - dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); + dw_reader(dws); + if (!dws->rx_len) { + dw_spi_mask_intr(dws, 0xff); spi_finalize_current_transfer(dws->host); + } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { + dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); } /* @@ -246,9 +241,12 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) * have the TXE IRQ flood at the final stage of the transfer. */ if (irq_status & DW_SPI_INT_TXEI) { - if (!dws->tx_len) - dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); dw_writer(dws); + if (!dws->tx_len) { + dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); + if (!dws->rx_len) + spi_finalize_current_transfer(dws->host); + } } return IRQ_HANDLED; @@ -438,11 +436,6 @@ static int dw_spi_transfer_one(struct spi_controller *host, dws->rx = transfer->rx_buf; dws->rx_len = dws->tx_len; - if (!dws->rx) { - dws->rx_len = 0; - cfg.tmode = DW_SPI_CTRLR0_TMOD_TO; - } - /* Ensure the data above is visible for all CPUs */ smp_mb(); From bd278320386ad6870bdbd83b02913e2ace88e453 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:27 +0000 Subject: [PATCH 08/20] Revert "spi: dw: Handle any number of gpiod CS lines" This reverts commit 1ad31fe2872c553bf6de3fa7f229a58e19bf399f. --- drivers/spi/spi-dw-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 0ae104d38e0521..2840e8dc7673fd 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -100,8 +100,7 @@ void dw_spi_set_cs(struct spi_device *spi, bool enable) * support active-high or active-low CS level. */ if (cs_high == enable) - dw_writel(dws, DW_SPI_SER, - BIT(spi_get_csgpiod(spi, 0) ? 0 : spi_get_chipselect(spi, 0))); + dw_writel(dws, DW_SPI_SER, BIT(spi_get_chipselect(spi, 0))); else dw_writel(dws, DW_SPI_SER, 0); } From a68f668a7bad618d7c777d56d871aa4fe724e015 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 11 Nov 2025 14:02:27 +0000 Subject: [PATCH 09/20] Revert "spi: dw: Handle combined tx and rx messages" This reverts commit 277985e2f8c0a37bd06ff5bebbc0507cc99cd1c4. --- drivers/spi/spi-dw-core.c | 12 +++--------- drivers/spi/spi-dw-mmio.c | 8 ++------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 2840e8dc7673fd..b3b883cb954107 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -241,11 +241,8 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) */ if (irq_status & DW_SPI_INT_TXEI) { dw_writer(dws); - if (!dws->tx_len) { + if (!dws->tx_len) dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); - if (!dws->rx_len) - spi_finalize_current_transfer(dws->host); - } } return IRQ_HANDLED; @@ -372,11 +369,8 @@ static void dw_spi_irq_setup(struct dw_spi *dws) dws->transfer_handler = dw_spi_transfer_handler; - imask = 0; - if (dws->tx_len) - imask |= DW_SPI_INT_TXEI | DW_SPI_INT_TXOI; - if (dws->rx_len) - imask |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI; + imask = DW_SPI_INT_TXEI | DW_SPI_INT_TXOI | + DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI; dw_spi_umask_intr(dws, imask); } diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 8ab9bc488b4e69..7a5197586919cd 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "spi-dw.h" @@ -342,11 +341,8 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) dws->paddr = mem->start; dws->irq = platform_get_irq(pdev, 0); - if (dws->irq < 0) { - if (dws->irq != -ENXIO) - return dws->irq; /* -ENXIO */ - dws->irq = IRQ_NOTCONNECTED; - } + if (dws->irq < 0) + return dws->irq; /* -ENXIO */ dwsmmio->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(dwsmmio->clk)) From 2bbbe19189f14a608a626536ef246ec270709354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Monin?= Date: Thu, 2 Oct 2025 14:14:37 +0200 Subject: [PATCH 10/20] spi: dw: rename the spi controller to ctlr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b926b15547d29a88932de3c24a05c12826fc1dbc upstream. Since the designware SPI controller can act as both a target and a host, rename spi_controller member of the dw_spi struct to ctlr instead of host. Similarly, rename the functions handling the controller, using controller instead of host as the suffix. No functional changes intended. Signed-off-by: Benoît Monin Link: https://patch.msgid.link/20251002-spi-dw-target-v1-1-993e91c1a712@bootlin.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-bt1.c | 4 +- drivers/spi/spi-dw-core.c | 128 +++++++++++++++++++------------------- drivers/spi/spi-dw-dma.c | 22 +++---- drivers/spi/spi-dw-mmio.c | 4 +- drivers/spi/spi-dw-pci.c | 8 +-- drivers/spi/spi-dw.h | 12 ++-- 6 files changed, 89 insertions(+), 89 deletions(-) diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c index 4a5be813efa75e..91642e05ac6077 100644 --- a/drivers/spi/spi-dw-bt1.c +++ b/drivers/spi/spi-dw-bt1.c @@ -288,7 +288,7 @@ static int dw_spi_bt1_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - ret = dw_spi_add_host(&pdev->dev, dws); + ret = dw_spi_add_controller(&pdev->dev, dws); if (ret) { pm_runtime_disable(&pdev->dev); return ret; @@ -303,7 +303,7 @@ static void dw_spi_bt1_remove(struct platform_device *pdev) { struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev); - dw_spi_remove_host(&dwsbt1->dws); + dw_spi_remove_controller(&dwsbt1->dws); pm_runtime_disable(&pdev->dev); } diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index b3b883cb954107..90dea6f9b3dab7 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -63,7 +63,7 @@ static void dw_spi_debugfs_init(struct dw_spi *dws) { char name[32]; - snprintf(name, 32, "dw_spi%d", dws->host->bus_num); + snprintf(name, 32, "dw_spi%d", dws->ctlr->bus_num); dws->debugfs = debugfs_create_dir(name, NULL); dws->regset.regs = dw_spi_dbgfs_regs; @@ -185,25 +185,25 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw) irq_status = dw_readl(dws, DW_SPI_ISR); if (irq_status & DW_SPI_INT_RXOI) { - dev_err(&dws->host->dev, "RX FIFO overflow detected\n"); + dev_err(&dws->ctlr->dev, "RX FIFO overflow detected\n"); ret = -EIO; } if (irq_status & DW_SPI_INT_RXUI) { - dev_err(&dws->host->dev, "RX FIFO underflow detected\n"); + dev_err(&dws->ctlr->dev, "RX FIFO underflow detected\n"); ret = -EIO; } if (irq_status & DW_SPI_INT_TXOI) { - dev_err(&dws->host->dev, "TX FIFO overflow detected\n"); + dev_err(&dws->ctlr->dev, "TX FIFO overflow detected\n"); ret = -EIO; } /* Generically handle the erroneous situation */ if (ret) { dw_spi_reset_chip(dws); - if (dws->host->cur_msg) - dws->host->cur_msg->status = ret; + if (dws->ctlr->cur_msg) + dws->ctlr->cur_msg->status = ret; } return ret; @@ -215,7 +215,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) u16 irq_status = dw_readl(dws, DW_SPI_ISR); if (dw_spi_check_status(dws, false)) { - spi_finalize_current_transfer(dws->host); + spi_finalize_current_transfer(dws->ctlr); return IRQ_HANDLED; } @@ -229,7 +229,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) dw_reader(dws); if (!dws->rx_len) { dw_spi_mask_intr(dws, 0xff); - spi_finalize_current_transfer(dws->host); + spi_finalize_current_transfer(dws->ctlr); } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); } @@ -250,14 +250,14 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) static irqreturn_t dw_spi_irq(int irq, void *dev_id) { - struct spi_controller *host = dev_id; - struct dw_spi *dws = spi_controller_get_devdata(host); + struct spi_controller *ctlr = dev_id; + struct dw_spi *dws = spi_controller_get_devdata(ctlr); u16 irq_status = dw_readl(dws, DW_SPI_ISR) & DW_SPI_INT_MASK; if (!irq_status) return IRQ_NONE; - if (!host->cur_msg) { + if (!ctlr->cur_msg) { dw_spi_mask_intr(dws, 0xff); return IRQ_HANDLED; } @@ -410,11 +410,11 @@ static int dw_spi_poll_transfer(struct dw_spi *dws, return 0; } -static int dw_spi_transfer_one(struct spi_controller *host, +static int dw_spi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *transfer) { - struct dw_spi *dws = spi_controller_get_devdata(host); + struct dw_spi *dws = spi_controller_get_devdata(ctlr); struct dw_spi_cfg cfg = { .tmode = DW_SPI_CTRLR0_TMOD_TR, .dfs = transfer->bits_per_word, @@ -439,7 +439,7 @@ static int dw_spi_transfer_one(struct spi_controller *host, transfer->effective_speed_hz = dws->current_freq; /* Check if current transfer is a DMA transaction */ - dws->dma_mapped = spi_xfer_is_dma_mapped(host, spi, transfer); + dws->dma_mapped = spi_xfer_is_dma_mapped(ctlr, spi, transfer); /* For poll mode just disable all interrupts */ dw_spi_mask_intr(dws, 0xff); @@ -462,10 +462,10 @@ static int dw_spi_transfer_one(struct spi_controller *host, return 1; } -static void dw_spi_handle_err(struct spi_controller *host, +static void dw_spi_handle_err(struct spi_controller *ctlr, struct spi_message *msg) { - struct dw_spi *dws = spi_controller_get_devdata(host); + struct dw_spi *dws = spi_controller_get_devdata(ctlr); if (dws->dma_mapped) dws->dma_ops->dma_stop(dws); @@ -574,7 +574,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) while (len) { entries = readl_relaxed(dws->regs + DW_SPI_TXFLR); if (!entries) { - dev_err(&dws->host->dev, "CS de-assertion on Tx\n"); + dev_err(&dws->ctlr->dev, "CS de-assertion on Tx\n"); return -EIO; } room = min(dws->fifo_len - entries, len); @@ -594,7 +594,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) if (!entries) { sts = readl_relaxed(dws->regs + DW_SPI_RISR); if (sts & DW_SPI_INT_RXOI) { - dev_err(&dws->host->dev, "FIFO overflow on Rx\n"); + dev_err(&dws->ctlr->dev, "FIFO overflow on Rx\n"); return -EIO; } continue; @@ -635,7 +635,7 @@ static int dw_spi_wait_mem_op_done(struct dw_spi *dws) spi_delay_exec(&delay, NULL); if (retry < 0) { - dev_err(&dws->host->dev, "Mem op hanged up\n"); + dev_err(&dws->ctlr->dev, "Mem op hanged up\n"); return -EIO; } @@ -898,60 +898,60 @@ static const struct spi_controller_mem_caps dw_spi_mem_caps = { .per_op_freq = true, }; -int dw_spi_add_host(struct device *dev, struct dw_spi *dws) +int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) { - struct spi_controller *host; + struct spi_controller *ctlr; int ret; if (!dws) return -EINVAL; - host = spi_alloc_host(dev, 0); - if (!host) + ctlr = spi_alloc_host(dev, 0); + if (!ctlr) return -ENOMEM; - device_set_node(&host->dev, dev_fwnode(dev)); + device_set_node(&ctlr->dev, dev_fwnode(dev)); - dws->host = host; + dws->ctlr = ctlr; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); - spi_controller_set_devdata(host, dws); + spi_controller_set_devdata(ctlr, dws); /* Basic HW init */ dw_spi_hw_init(dev, dws); ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), - host); + ctlr); if (ret < 0 && ret != -ENOTCONN) { dev_err(dev, "can not get IRQ\n"); - goto err_free_host; + goto err_free_ctlr; } dw_spi_init_mem_ops(dws); - host->use_gpio_descriptors = true; - host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + ctlr->use_gpio_descriptors = true; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; if (dws->caps & DW_SPI_CAP_DFS32) - host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); else - host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); - host->bus_num = dws->bus_num; - host->num_chipselect = dws->num_cs; - host->setup = dw_spi_setup; - host->cleanup = dw_spi_cleanup; + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + ctlr->bus_num = dws->bus_num; + ctlr->num_chipselect = dws->num_cs; + ctlr->setup = dw_spi_setup; + ctlr->cleanup = dw_spi_cleanup; if (dws->set_cs) - host->set_cs = dws->set_cs; + ctlr->set_cs = dws->set_cs; else - host->set_cs = dw_spi_set_cs; - host->transfer_one = dw_spi_transfer_one; - host->handle_err = dw_spi_handle_err; + ctlr->set_cs = dw_spi_set_cs; + ctlr->transfer_one = dw_spi_transfer_one; + ctlr->handle_err = dw_spi_handle_err; if (dws->mem_ops.exec_op) { - host->mem_ops = &dws->mem_ops; - host->mem_caps = &dw_spi_mem_caps; + ctlr->mem_ops = &dws->mem_ops; + ctlr->mem_caps = &dw_spi_mem_caps; } - host->max_speed_hz = dws->max_freq; - host->flags = SPI_CONTROLLER_GPIO_SS; - host->auto_runtime_pm = true; + ctlr->max_speed_hz = dws->max_freq; + ctlr->flags = SPI_CONTROLLER_GPIO_SS; + ctlr->auto_runtime_pm = true; /* Get default rx sample delay */ device_property_read_u32(dev, "rx-sample-delay-ns", @@ -964,14 +964,14 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) } else if (ret) { dev_warn(dev, "DMA init failed\n"); } else { - host->can_dma = dws->dma_ops->can_dma; - host->flags |= SPI_CONTROLLER_MUST_TX; + ctlr->can_dma = dws->dma_ops->can_dma; + ctlr->flags |= SPI_CONTROLLER_MUST_TX; } } - ret = spi_register_controller(host); + ret = spi_register_controller(ctlr); if (ret) { - dev_err_probe(dev, ret, "problem registering spi host\n"); + dev_err_probe(dev, ret, "problem registering spi controller\n"); goto err_dma_exit; } @@ -983,47 +983,47 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->dma_ops->dma_exit(dws); dw_spi_enable_chip(dws, 0); err_free_irq: - free_irq(dws->irq, host); -err_free_host: - spi_controller_put(host); + free_irq(dws->irq, ctlr); +err_free_ctlr: + spi_controller_put(ctlr); return ret; } -EXPORT_SYMBOL_NS_GPL(dw_spi_add_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_add_controller, "SPI_DW_CORE"); -void dw_spi_remove_host(struct dw_spi *dws) +void dw_spi_remove_controller(struct dw_spi *dws) { dw_spi_debugfs_remove(dws); - spi_unregister_controller(dws->host); + spi_unregister_controller(dws->ctlr); if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); dw_spi_shutdown_chip(dws); - free_irq(dws->irq, dws->host); + free_irq(dws->irq, dws->ctlr); } -EXPORT_SYMBOL_NS_GPL(dw_spi_remove_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_remove_controller, "SPI_DW_CORE"); -int dw_spi_suspend_host(struct dw_spi *dws) +int dw_spi_suspend_controller(struct dw_spi *dws) { int ret; - ret = spi_controller_suspend(dws->host); + ret = spi_controller_suspend(dws->ctlr); if (ret) return ret; dw_spi_shutdown_chip(dws); return 0; } -EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_controller, "SPI_DW_CORE"); -int dw_spi_resume_host(struct dw_spi *dws) +int dw_spi_resume_controller(struct dw_spi *dws) { - dw_spi_hw_init(&dws->host->dev, dws); - return spi_controller_resume(dws->host); + dw_spi_hw_init(&dws->ctlr->dev, dws); + return spi_controller_resume(dws->ctlr); } -EXPORT_SYMBOL_NS_GPL(dw_spi_resume_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_resume_controller, "SPI_DW_CORE"); MODULE_AUTHOR("Feng Tang "); MODULE_DESCRIPTION("Driver for DesignWare SPI controller core"); diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index b5bed02b7e5006..65adec7c7524b8 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -139,8 +139,8 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) if (!dws->txchan) goto free_rxchan; - dws->host->dma_rx = dws->rxchan; - dws->host->dma_tx = dws->txchan; + dws->ctlr->dma_rx = dws->rxchan; + dws->ctlr->dma_tx = dws->txchan; init_completion(&dws->dma_completion); @@ -183,8 +183,8 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) goto free_rxchan; } - dws->host->dma_rx = dws->rxchan; - dws->host->dma_tx = dws->txchan; + dws->ctlr->dma_rx = dws->rxchan; + dws->ctlr->dma_tx = dws->txchan; init_completion(&dws->dma_completion); @@ -242,10 +242,10 @@ static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes) } } -static bool dw_spi_can_dma(struct spi_controller *host, +static bool dw_spi_can_dma(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer) { - struct dw_spi *dws = spi_controller_get_devdata(host); + struct dw_spi *dws = spi_controller_get_devdata(ctlr); enum dma_slave_buswidth dma_bus_width; if (xfer->len <= dws->fifo_len) @@ -271,7 +271,7 @@ static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed) msecs_to_jiffies(ms)); if (ms == 0) { - dev_err(&dws->host->cur_msg->spi->dev, + dev_err(&dws->ctlr->cur_msg->spi->dev, "DMA transaction timed out\n"); return -ETIMEDOUT; } @@ -299,7 +299,7 @@ static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, spi_delay_exec(&delay, xfer); if (retry < 0) { - dev_err(&dws->host->dev, "Tx hanged up\n"); + dev_err(&dws->ctlr->dev, "Tx hanged up\n"); return -EIO; } @@ -400,7 +400,7 @@ static int dw_spi_dma_wait_rx_done(struct dw_spi *dws) spi_delay_exec(&delay, NULL); if (retry < 0) { - dev_err(&dws->host->dev, "Rx hanged up\n"); + dev_err(&dws->ctlr->dev, "Rx hanged up\n"); return -EIO; } @@ -656,13 +656,13 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) if (ret) return ret; - if (dws->host->cur_msg->status == -EINPROGRESS) { + if (dws->ctlr->cur_msg->status == -EINPROGRESS) { ret = dw_spi_dma_wait_tx_done(dws, xfer); if (ret) return ret; } - if (xfer->rx_buf && dws->host->cur_msg->status == -EINPROGRESS) + if (xfer->rx_buf && dws->ctlr->cur_msg->status == -EINPROGRESS) ret = dw_spi_dma_wait_rx_done(dws); return ret; diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 7a5197586919cd..a33f246560d807 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -382,7 +382,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - ret = dw_spi_add_host(&pdev->dev, dws); + ret = dw_spi_add_controller(&pdev->dev, dws); if (ret) goto out; @@ -401,7 +401,7 @@ static void dw_spi_mmio_remove(struct platform_device *pdev) { struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev); - dw_spi_remove_host(&dwsmmio->dws); + dw_spi_remove_controller(&dwsmmio->dws); pm_runtime_disable(&pdev->dev); reset_control_assert(dwsmmio->rstc); } diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index b32d6648a32ea2..72d9f5bc87f75a 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -127,7 +127,7 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en goto err_free_irq_vectors; } - ret = dw_spi_add_host(&pdev->dev, dws); + ret = dw_spi_add_controller(&pdev->dev, dws); if (ret) goto err_free_irq_vectors; @@ -156,7 +156,7 @@ static void dw_spi_pci_remove(struct pci_dev *pdev) pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); - dw_spi_remove_host(dws); + dw_spi_remove_controller(dws); pci_free_irq_vectors(pdev); } @@ -165,14 +165,14 @@ static int dw_spi_pci_suspend(struct device *dev) { struct dw_spi *dws = dev_get_drvdata(dev); - return dw_spi_suspend_host(dws); + return dw_spi_suspend_controller(dws); } static int dw_spi_pci_resume(struct device *dev) { struct dw_spi *dws = dev_get_drvdata(dev); - return dw_spi_resume_host(dws); + return dw_spi_resume_controller(dws); } #endif diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index fc267c6437ae09..9cc79c566a70c0 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -142,14 +142,14 @@ struct dw_spi_dma_ops { int (*dma_init)(struct device *dev, struct dw_spi *dws); void (*dma_exit)(struct dw_spi *dws); int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer); - bool (*can_dma)(struct spi_controller *host, struct spi_device *spi, + bool (*can_dma)(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer); int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer); void (*dma_stop)(struct dw_spi *dws); }; struct dw_spi { - struct spi_controller *host; + struct spi_controller *ctlr; u32 ip; /* Synopsys DW SSI IP-core ID */ u32 ver; /* Synopsys component version */ @@ -288,10 +288,10 @@ extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, struct dw_spi_cfg *cfg); extern int dw_spi_check_status(struct dw_spi *dws, bool raw); -extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); -extern void dw_spi_remove_host(struct dw_spi *dws); -extern int dw_spi_suspend_host(struct dw_spi *dws); -extern int dw_spi_resume_host(struct dw_spi *dws); +extern int dw_spi_add_controller(struct device *dev, struct dw_spi *dws); +extern void dw_spi_remove_controller(struct dw_spi *dws); +extern int dw_spi_suspend_controller(struct dw_spi *dws); +extern int dw_spi_resume_controller(struct dw_spi *dws); #ifdef CONFIG_SPI_DW_DMA From e332ffbb8ff4c5cb015a6249aa536fca1e3e5652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Monin?= Date: Thu, 2 Oct 2025 14:14:38 +0200 Subject: [PATCH 11/20] spi: dw: add target mode support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fe8cc44dd173cde5788ab4e3730ac61f3d316d9c upstream. Implement target mode for the DesignWare controller with the following changes: Allocate an SPI controller of the correct type based on the spi-slave property in dw_spi_add_controller() and set the controller properties depending on its type. Since they are only relevant when acting as a host controller, settings related to chip-select control and the set_cs() callback are only set in host mode, as are the loopback support, the memory operations and the maximum frequency. The target_abort() callback is set only when configured in target mode. The number of chip-select is set to 1 in dw_spi_hw_init() since the controller only has one CS input in target mode. In dw_spi_update_config(), return after setting the CTRLR0 register as the other registers are only relevant in host mode and are read-only in target mode. This function is called as part of the transfer_one() callback, which is identical in both the host and target mode. Move the code implementing the handle_err() callback to a new function named dw_spi_abort(), and use it to implement both the handle_err() and the target_abort() callbacks. Finally, drop the error path on the spi-slave property in dw_spi_mmio_probe(), as it is now a valid configuration. Signed-off-by: Benoît Monin Link: https://patch.msgid.link/20251002-spi-dw-target-v1-2-993e91c1a712@bootlin.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 82 +++++++++++++++++++++++++++------------ drivers/spi/spi-dw-mmio.c | 5 --- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 90dea6f9b3dab7..9ebf244294f89d 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -332,6 +332,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, dw_writel(dws, DW_SPI_CTRLR0, cr0); + if (spi_controller_is_target(dws->ctlr)) + return; + if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD || cfg->tmode == DW_SPI_CTRLR0_TMOD_RO) dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0); @@ -462,8 +465,7 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr, return 1; } -static void dw_spi_handle_err(struct spi_controller *ctlr, - struct spi_message *msg) +static inline void dw_spi_abort(struct spi_controller *ctlr) { struct dw_spi *dws = spi_controller_get_devdata(ctlr); @@ -473,6 +475,19 @@ static void dw_spi_handle_err(struct spi_controller *ctlr, dw_spi_reset_chip(dws); } +static void dw_spi_handle_err(struct spi_controller *ctlr, + struct spi_message *msg) +{ + dw_spi_abort(ctlr); +} + +static int dw_spi_target_abort(struct spi_controller *ctlr) +{ + dw_spi_abort(ctlr); + + return 0; +} + static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op) { if (op->data.dir == SPI_MEM_DATA_IN) @@ -834,18 +849,23 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws) DW_SPI_GET_BYTE(dws->ver, 1)); } - /* - * Try to detect the number of native chip-selects if the platform - * driver didn't set it up. There can be up to 16 lines configured. - */ - if (!dws->num_cs) { - u32 ser; + if (spi_controller_is_target(dws->ctlr)) { + /* There is only one CS input signal in target mode */ + dws->num_cs = 1; + } else { + /* + * Try to detect the number of native chip-selects if the platform + * driver didn't set it up. There can be up to 16 lines configured. + */ + if (!dws->num_cs) { + u32 ser; - dw_writel(dws, DW_SPI_SER, 0xffff); - ser = dw_readl(dws, DW_SPI_SER); - dw_writel(dws, DW_SPI_SER, 0); + dw_writel(dws, DW_SPI_SER, 0xffff); + ser = dw_readl(dws, DW_SPI_SER); + dw_writel(dws, DW_SPI_SER, 0); - dws->num_cs = hweight16(ser); + dws->num_cs = hweight16(ser); + } } /* @@ -901,12 +921,18 @@ static const struct spi_controller_mem_caps dw_spi_mem_caps = { int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) { struct spi_controller *ctlr; + bool target; int ret; if (!dws) return -EINVAL; - ctlr = spi_alloc_host(dev, 0); + target = device_property_read_bool(dev, "spi-slave"); + if (target) + ctlr = spi_alloc_target(dev, 0); + else + ctlr = spi_alloc_host(dev, 0); + if (!ctlr) return -ENOMEM; @@ -929,8 +955,7 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) dw_spi_init_mem_ops(dws); - ctlr->use_gpio_descriptors = true; - ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA; if (dws->caps & DW_SPI_CAP_DFS32) ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); else @@ -939,20 +964,27 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) ctlr->num_chipselect = dws->num_cs; ctlr->setup = dw_spi_setup; ctlr->cleanup = dw_spi_cleanup; - if (dws->set_cs) - ctlr->set_cs = dws->set_cs; - else - ctlr->set_cs = dw_spi_set_cs; ctlr->transfer_one = dw_spi_transfer_one; ctlr->handle_err = dw_spi_handle_err; - if (dws->mem_ops.exec_op) { - ctlr->mem_ops = &dws->mem_ops; - ctlr->mem_caps = &dw_spi_mem_caps; - } - ctlr->max_speed_hz = dws->max_freq; - ctlr->flags = SPI_CONTROLLER_GPIO_SS; ctlr->auto_runtime_pm = true; + if (!target) { + ctlr->use_gpio_descriptors = true; + ctlr->mode_bits |= SPI_LOOP; + if (dws->set_cs) + ctlr->set_cs = dws->set_cs; + else + ctlr->set_cs = dw_spi_set_cs; + if (dws->mem_ops.exec_op) { + ctlr->mem_ops = &dws->mem_ops; + ctlr->mem_caps = &dw_spi_mem_caps; + } + ctlr->max_speed_hz = dws->max_freq; + ctlr->flags = SPI_CONTROLLER_GPIO_SS; + } else { + ctlr->target_abort = dw_spi_target_abort; + } + /* Get default rx sample delay */ device_property_read_u32(dev, "rx-sample-delay-ns", &dws->def_rx_sample_dly_ns); diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index a33f246560d807..33239b4778cb64 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -321,11 +321,6 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) struct dw_spi *dws; int ret; - if (device_property_read_bool(&pdev->dev, "spi-slave")) { - dev_warn(&pdev->dev, "spi-slave is not yet supported\n"); - return -ENODEV; - } - dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio), GFP_KERNEL); if (!dwsmmio) From 5ca77f059c71dea0c72024497c20a4419126b316 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 29 Nov 2022 10:09:54 +0000 Subject: [PATCH 12/20] spi: dw: Handle combined tx and rx messages Signed-off-by: Phil Elwell --- drivers/spi/spi-dw-core.c | 12 +++++++++--- drivers/spi/spi-dw-mmio.c | 8 ++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 9ebf244294f89d..3cee4c2b34fe74 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -241,8 +241,11 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) */ if (irq_status & DW_SPI_INT_TXEI) { dw_writer(dws); - if (!dws->tx_len) + if (!dws->tx_len) { dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); + if (!dws->rx_len) + spi_finalize_current_transfer(dws->host); + } } return IRQ_HANDLED; @@ -372,8 +375,11 @@ static void dw_spi_irq_setup(struct dw_spi *dws) dws->transfer_handler = dw_spi_transfer_handler; - imask = DW_SPI_INT_TXEI | DW_SPI_INT_TXOI | - DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI; + imask = 0; + if (dws->tx_len) + imask |= DW_SPI_INT_TXEI | DW_SPI_INT_TXOI; + if (dws->rx_len) + imask |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI; dw_spi_umask_intr(dws, imask); } diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 33239b4778cb64..f4990dc6d25a55 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "spi-dw.h" @@ -336,8 +337,11 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) dws->paddr = mem->start; dws->irq = platform_get_irq(pdev, 0); - if (dws->irq < 0) - return dws->irq; /* -ENXIO */ + if (dws->irq < 0) { + if (dws->irq != -ENXIO) + return dws->irq; /* -ENXIO */ + dws->irq = IRQ_NOTCONNECTED; + } dwsmmio->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(dwsmmio->clk)) From 299660cc5fad388a2edc3b57d92fa1946b5eb341 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 13 May 2024 15:40:02 +0100 Subject: [PATCH 13/20] spi: dw: Handle any number of gpiod CS lines Even when configured to use only gpiod CS lines, the DW SPI controller still expects a bit to be set in the SER register, otherwise transfers stall. For the csgpiod case, nominate bit 0 for the job. See: https://github.com/raspberrypi/linux/issues/6159 Signed-off-by: Phil Elwell --- drivers/spi/spi-dw-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 3cee4c2b34fe74..2d725cbf4a4d72 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -100,7 +100,8 @@ void dw_spi_set_cs(struct spi_device *spi, bool enable) * support active-high or active-low CS level. */ if (cs_high == enable) - dw_writel(dws, DW_SPI_SER, BIT(spi_get_chipselect(spi, 0))); + dw_writel(dws, DW_SPI_SER, + BIT(spi_get_csgpiod(spi, 0) ? 0 : spi_get_chipselect(spi, 0))); else dw_writel(dws, DW_SPI_SER, 0); } From d7802637d27fd3bd0b9c8cb08a981974c8be54f1 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 1 Jul 2024 15:49:14 +0100 Subject: [PATCH 14/20] spi: dw: Save bandwidth with the TMOD_TO feature TMOD_TO is the transmit-only mode that doesn't put data into the receive FIFO. Using TMOD_TO when the user doesn't want the received data saves CPU time and memory bandwidth. Signed-off-by: Phil Elwell --- drivers/spi/spi-dw-core.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 2d725cbf4a4d72..e3f4fe10b4de02 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -227,12 +227,17 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) * final stage of the transfer. By doing so we'll get the next IRQ * right when the leftover incoming data is received. */ - dw_reader(dws); - if (!dws->rx_len) { - dw_spi_mask_intr(dws, 0xff); + if (dws->rx_len) { + dw_reader(dws); + if (!dws->rx_len) { + dw_spi_mask_intr(dws, 0xff); + spi_finalize_current_transfer(dws->ctlr); + } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { + dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); + } + } else if (!dws->tx_len) { + dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); spi_finalize_current_transfer(dws->ctlr); - } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { - dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); } /* @@ -241,12 +246,9 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) * have the TXE IRQ flood at the final stage of the transfer. */ if (irq_status & DW_SPI_INT_TXEI) { - dw_writer(dws); - if (!dws->tx_len) { + if (!dws->tx_len) dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); - if (!dws->rx_len) - spi_finalize_current_transfer(dws->host); - } + dw_writer(dws); } return IRQ_HANDLED; @@ -439,6 +441,11 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr, dws->rx = transfer->rx_buf; dws->rx_len = dws->tx_len; + if (!dws->rx) { + dws->rx_len = 0; + cfg.tmode = DW_SPI_CTRLR0_TMOD_TO; + } + /* Ensure the data above is visible for all CPUs */ smp_mb(); From 6d2bc5adb2542704b72601fb61bacb7fa165cd5b Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 1 Jul 2024 16:41:04 +0100 Subject: [PATCH 15/20] spi: dw: Save bandwidth with the TMOD_RO feature TMOD_RO is the receive-only mode that doesn't require data in the transmit FIFO in order to generate clock cycles. Using TMOD_RO when the device doesn't care about the data sent to it saves CPU time and memory bandwidth. Signed-off-by: Phil Elwell --- drivers/spi/spi-dw-core.c | 31 +++++++++++++++++++++------- drivers/spi/spi-dw-dma.c | 43 +++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index e3f4fe10b4de02..a42ef823d4f1b1 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -372,18 +372,18 @@ static void dw_spi_irq_setup(struct dw_spi *dws) * will be adjusted at the final stage of the IRQ-based SPI transfer * execution so not to lose the leftover of the incoming data. */ - level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len); + level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len ? dws->tx_len : dws->rx_len); dw_writel(dws, DW_SPI_TXFTLR, level); dw_writel(dws, DW_SPI_RXFTLR, level - 1); dws->transfer_handler = dw_spi_transfer_handler; - imask = 0; - if (dws->tx_len) - imask |= DW_SPI_INT_TXEI | DW_SPI_INT_TXOI; + imask = DW_SPI_INT_TXEI | DW_SPI_INT_TXOI; if (dws->rx_len) imask |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI; dw_spi_umask_intr(dws, imask); + if (!dws->tx_len) + dw_writel(dws, DW_SPI_DR, 0); } /* @@ -406,13 +406,18 @@ static int dw_spi_poll_transfer(struct dw_spi *dws, delay.unit = SPI_DELAY_UNIT_SCK; nbits = dws->n_bytes * BITS_PER_BYTE; + if (!dws->tx_len) + dw_writel(dws, DW_SPI_DR, 0); + do { - dw_writer(dws); + if (dws->tx_len) + dw_writer(dws); delay.value = nbits * (dws->rx_len - dws->tx_len); spi_delay_exec(&delay, transfer); - dw_reader(dws); + if (dws->rx_len) + dw_reader(dws); ret = dw_spi_check_status(dws, true); if (ret) @@ -432,6 +437,7 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr, .dfs = transfer->bits_per_word, .freq = transfer->speed_hz, }; + int buswidth; int ret; dws->dma_mapped = 0; @@ -446,6 +452,18 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr, cfg.tmode = DW_SPI_CTRLR0_TMOD_TO; } + if (!dws->rx) { + dws->rx_len = 0; + cfg.tmode = DW_SPI_CTRLR0_TMOD_TO; + } + if (!dws->tx) { + dws->tx_len = 0; + cfg.tmode = DW_SPI_CTRLR0_TMOD_RO; + cfg.ndf = dws->rx_len; + } + buswidth = transfer->rx_buf ? transfer->rx_nbits : + (transfer->tx_buf ? transfer->tx_nbits : 1); + /* Ensure the data above is visible for all CPUs */ smp_mb(); @@ -1011,7 +1029,6 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) dev_warn(dev, "DMA init failed\n"); } else { ctlr->can_dma = dws->dma_ops->can_dma; - ctlr->flags |= SPI_CONTROLLER_MUST_TX; } } diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 65adec7c7524b8..3f08cf29588b94 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -470,13 +471,12 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) u16 imr, dma_ctrl; int ret; - if (!xfer->tx_buf) - return -EINVAL; - /* Setup DMA channels */ - ret = dw_spi_dma_config_tx(dws); - if (ret) - return ret; + if (xfer->tx_buf) { + ret = dw_spi_dma_config_tx(dws); + if (ret) + return ret; + } if (xfer->rx_buf) { ret = dw_spi_dma_config_rx(dws); @@ -485,13 +485,17 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) } /* Set the DMA handshaking interface */ - dma_ctrl = DW_SPI_DMACR_TDMAE; + dma_ctrl = 0; + if (xfer->tx_buf) + dma_ctrl |= DW_SPI_DMACR_TDMAE; if (xfer->rx_buf) dma_ctrl |= DW_SPI_DMACR_RDMAE; dw_writel(dws, DW_SPI_DMACR, dma_ctrl); /* Set the interrupt mask */ - imr = DW_SPI_INT_TXOI; + imr = 0; + if (xfer->tx_buf) + imr |= DW_SPI_INT_TXOI; if (xfer->rx_buf) imr |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI; dw_spi_umask_intr(dws, imr); @@ -508,15 +512,16 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, { int ret; - /* Submit the DMA Tx transfer */ - ret = dw_spi_dma_submit_tx(dws, xfer->tx_sg.sgl, xfer->tx_sg.nents); - if (ret) - goto err_clear_dmac; + /* Submit the DMA Tx transfer if required */ + if (xfer->tx_buf) { + ret = dw_spi_dma_submit_tx(dws, xfer->tx_sg.sgl, xfer->tx_sg.nents); + if (ret) + goto err_clear_dmac; + } /* Submit the DMA Rx transfer if required */ if (xfer->rx_buf) { - ret = dw_spi_dma_submit_rx(dws, xfer->rx_sg.sgl, - xfer->rx_sg.nents); + ret = dw_spi_dma_submit_rx(dws, xfer->rx_sg.sgl, xfer->rx_sg.nents); if (ret) goto err_clear_dmac; @@ -524,7 +529,15 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, dma_async_issue_pending(dws->rxchan); } - dma_async_issue_pending(dws->txchan); + if (xfer->tx_buf) { + dma_async_issue_pending(dws->txchan); + } else { + /* Pause to allow DMA channel to fetch RX descriptor */ + usleep_range(5, 10); + + /* Write something to the TX FIFO to start the transfer */ + dw_writel(dws, DW_SPI_DR, 0); + } ret = dw_spi_dma_wait(dws, xfer->len, xfer->effective_speed_hz); From b304bda3d5ba1c9702fd3a4e1ab57c60bbbd0ac3 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Mon, 22 Jul 2024 15:27:51 +0100 Subject: [PATCH 16/20] spi: dw: don't immediately kill DMA transfers if an error occurs Disabling the peripheral resets controller state which has a dangerous side-effect of disabling the DMA handshake interface while it is active. This can cause DMA channels to hang. The error recovery pathway will wait for DMA to stop and reset the chip anyway, so mask further FIFO interrupts and let the transfer finish gracefully. Signed-off-by: Jonathan Bell --- drivers/spi/spi-dw-core.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index a42ef823d4f1b1..e12b1f34195f83 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -202,7 +202,18 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw) /* Generically handle the erroneous situation */ if (ret) { - dw_spi_reset_chip(dws); + /* + * Forcibly halting the controller can cause DMA to hang. + * Defer to dw_spi_handle_err outside of interrupt context + * and mask further interrupts for the current transfer. + */ + if (dws->dma_mapped) { + dw_spi_mask_intr(dws, 0xff); + dw_readl(dws, DW_SPI_ICR); + } else { + dw_spi_reset_chip(dws); + } + if (dws->ctlr->cur_msg) dws->ctlr->cur_msg->status = ret; } From bced23a98f2208d54fddcb7923005c35dbdd0fb8 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 29 Jul 2024 11:12:38 +0100 Subject: [PATCH 17/20] spi: dw: Fix non-DMA transmit-only transfers Ensure the transmit FIFO has emptied before ending the transfer by dropping the TX threshold to 0 when the last byte has been pushed into the FIFO. Include a similar fix for the non-IRQ paths. See: https://github.com/raspberrypi/linux/issues/6285 Fixes: 6014649de765 ("spi: dw: Save bandwidth with the TMOD_TO feature") Signed-off-by: Phil Elwell --- drivers/spi/spi-dw-core.c | 61 +++++++++++++++++++++++++++++++++------ drivers/spi/spi-dw.h | 3 ++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index e12b1f34195f83..2e53fd50911343 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -222,6 +222,32 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw) } EXPORT_SYMBOL_NS_GPL(dw_spi_check_status, "SPI_DW_CORE"); +static inline bool dw_spi_ctlr_busy(struct dw_spi *dws) +{ + return dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY; +} + +static enum hrtimer_restart dw_spi_hrtimer_handler(struct hrtimer *hr) +{ + struct dw_spi *dws = container_of(hr, struct dw_spi, hrtimer); + + if (!dw_spi_ctlr_busy(dws)) { + spi_finalize_current_transfer(dws->ctlr); + return HRTIMER_NORESTART; + } + + if (!dws->idle_wait_retries) { + dev_err(&dws->ctlr->dev, "controller stuck at busy\n"); + spi_finalize_current_transfer(dws->ctlr); + return HRTIMER_NORESTART; + } + + dws->idle_wait_retries--; + hrtimer_forward_now(hr, dws->idle_wait_interval); + + return HRTIMER_RESTART; +} + static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) { u16 irq_status = dw_readl(dws, DW_SPI_ISR); @@ -248,7 +274,22 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) } } else if (!dws->tx_len) { dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); - spi_finalize_current_transfer(dws->ctlr); + if (dw_spi_ctlr_busy(dws)) { + ktime_t period = ns_to_ktime(DIV_ROUND_UP(NSEC_PER_SEC, dws->current_freq)); + + /* + * Make the initial wait an underestimate of how long the transfer + * should take, then poll rapidly to reduce the delay + */ + hrtimer_start(&dws->hrtimer, + period * (8 * dws->n_bytes - 1), + HRTIMER_MODE_REL); + dws->idle_wait_retries = 10; + dws->idle_wait_interval = period; + } else { + spi_finalize_current_transfer(dws->ctlr); + } + return IRQ_HANDLED; } /* @@ -257,9 +298,13 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) * have the TXE IRQ flood at the final stage of the transfer. */ if (irq_status & DW_SPI_INT_TXEI) { - if (!dws->tx_len) - dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); dw_writer(dws); + if (!dws->tx_len) { + if (dws->rx_len) + dw_spi_mask_intr(dws, DW_SPI_INT_TXEI); + else + dw_writel(dws, DW_SPI_TXFTLR, 0); + } } return IRQ_HANDLED; @@ -433,7 +478,7 @@ static int dw_spi_poll_transfer(struct dw_spi *dws, ret = dw_spi_check_status(dws, true); if (ret) return ret; - } while (dws->rx_len); + } while (dws->rx_len || dws->tx_len || dw_spi_ctlr_busy(dws)); return 0; } @@ -665,11 +710,6 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) return 0; } -static inline bool dw_spi_ctlr_busy(struct dw_spi *dws) -{ - return dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY; -} - static int dw_spi_wait_mem_op_done(struct dw_spi *dws) { int retry = DW_SPI_WAIT_RETRIES; @@ -1043,6 +1083,8 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) } } + hrtimer_setup(&dws->hrtimer, dw_spi_hrtimer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + ret = spi_register_controller(ctlr); if (ret) { dev_err_probe(dev, ret, "problem registering spi controller\n"); @@ -1068,6 +1110,7 @@ void dw_spi_remove_controller(struct dw_spi *dws) { dw_spi_debugfs_remove(dws); + hrtimer_cancel(&dws->hrtimer); spi_unregister_controller(dws->ctlr); if (dws->dma_ops && dws->dma_ops->dma_exit) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 9cc79c566a70c0..d1f9aff0ec07fc 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -180,6 +180,9 @@ struct dw_spi { u32 current_freq; /* frequency in hz */ u32 cur_rx_sample_dly; u32 def_rx_sample_dly_ns; + struct hrtimer hrtimer; + ktime_t idle_wait_interval; + int idle_wait_retries; /* Custom memory operations */ struct spi_controller_mem_ops mem_ops; From 9f4fb2ac118e651e7b462413492055097bd7b3df Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 31 Jul 2024 10:55:19 +0100 Subject: [PATCH 18/20] spi: dw: Clamp the minimum clock speed The DW SPI interface has a 16-bit clock divider, where the bottom bit of the divisor must be 0. Limit how low the clock speed can go to prevent the clock divider from being truncated, as that could lead to a much higher clock rate than requested. Signed-off-by: Phil Elwell --- drivers/spi/spi-dw-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 2e53fd50911343..929ecac5b97b60 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -402,7 +402,7 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0); /* Note DW APB SSI clock divider doesn't support odd numbers */ - clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe; + clk_div = min(DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1, 0xfffe) & 0xfffe; speed_hz = dws->max_freq / clk_div; if (dws->current_freq != speed_hz) { From 42871ef00453afcfd7d37f0af1a5f92764dd1cdc Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 19 Sep 2024 18:12:12 +0100 Subject: [PATCH 19/20] spi: dw: Let the DMAC set the transfer widths SPI transfers are of defined length, unlike some UART traffic, so it is safe to let the DMA controller choose a suitable memory width. Signed-off-by: Phil Elwell --- drivers/spi/spi-dw-dma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 3f08cf29588b94..64154c5c775172 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -330,7 +330,6 @@ static int dw_spi_dma_config_tx(struct dw_spi *dws) txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; txconf.dst_maxburst = dws->txburst; - txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes); txconf.device_fc = false; @@ -431,7 +430,6 @@ static int dw_spi_dma_config_rx(struct dw_spi *dws) rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; rxconf.src_maxburst = dws->rxburst; - rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes); rxconf.device_fc = false; From 0be3771a2368ed8c93cbb37ca47b77fcb0841946 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 3 Feb 2025 17:50:20 +0000 Subject: [PATCH 20/20] spi: dw: Wait for idle after TX If this is a DMA transfer, and if there is no simultaneous RX transfer, wait for the interface to go idle before reporting that TX is done. Link: https://forums.raspberrypi.com/viewtopic.php?t=383027 Signed-off-by: Phil Elwell --- drivers/spi/spi-dw-dma.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 64154c5c775172..f5475b507ddf4a 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -304,6 +304,12 @@ static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, return -EIO; } + if (!xfer->rx_buf) { + delay.value = dws->n_bytes * BITS_PER_BYTE; + while (dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY) + spi_delay_exec(&delay, xfer); + } + return 0; }