From 9d0cd08a05f1d8e903ee3316c39576a4bebd23b5 Mon Sep 17 00:00:00 2001 From: Vaishnav Achath Date: Mon, 22 Aug 2022 14:45:31 +0530 Subject: [PATCH 1/4] spi: spi-omap2-mcspi: Use EOW interrupt for completion when DMA enabled In MCSPI controller EOW interrupt is triggered when the channel has transmitted the set number of bytes in MCSPI_XFERLEVEL[31-16] WCNT, this can be used to signal the completion of a TX/RX when the internal FIFO is enabled, when DMA is enabled the internal FIFO is always enabled. Waiting for the DMA completion adds unpredictable delays due to the non-realtime completion calculation mechanism in k3-udma driver. This commit removes the dma_tx_completion and dma_rx_completion and relies on the MCSPI controller EOW interrupt to signal transaction completion.This fixes the real-time performance issues in master and slave mode when DMA was enabled which resulted from the DMA completion calculation delays. Since the MCSPI driver now uses internal mechanism to identify a transfer completion we disable the TX and RX DMA completion callback and remove DMA_PREP_INTERRUPT. Signed-off-by: Vaishnav Achath Reported-by: kernel test robot --- drivers/spi/spi-omap2-mcspi.c | 150 ++++++++-------------------------- 1 file changed, 35 insertions(+), 115 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index caf532b70209..f3e66d3222bd 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -105,10 +105,6 @@ static int kbusdelay = 0; /* KBUS Inter-Frame-Delay in nanoseconds */ struct omap2_mcspi_dma { struct dma_chan *dma_tx; struct dma_chan *dma_rx; - - struct completion dma_tx_completion; - struct completion dma_rx_completion; - char dma_rx_ch_name[14]; char dma_tx_ch_name[14]; }; @@ -132,7 +128,7 @@ struct omap2_mcspi_regs { }; struct omap2_mcspi { - struct completion txdone; + struct completion txrxdone; struct spi_controller *ctlr; /* Virtual base address of the controller */ void __iomem *base; @@ -412,40 +408,6 @@ static int mcspi_wait_for_completion(struct omap2_mcspi *mcspi, return 0; } -static void omap2_mcspi_rx_callback(void *data) -{ - struct spi_device *spi = data; - struct omap2_mcspi *mcspi = spi_controller_get_devdata(spi->controller); - struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)]; - - /* We must disable the DMA RX request */ - omap2_mcspi_set_dma_req(spi, 1, 0); - -#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION - if (spi->dev.driver == &kbus_driver.driver) - trace_pxc_kbus(__func__, "DMA: RX completed!"); -#endif - - complete(&mcspi_dma->dma_rx_completion); -} - -static void omap2_mcspi_tx_callback(void *data) -{ - struct spi_device *spi = data; - struct omap2_mcspi *mcspi = spi_controller_get_devdata(spi->controller); - struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)]; - - /* We must disable the DMA TX request */ - omap2_mcspi_set_dma_req(spi, 0, 0); - -#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION - if (spi->dev.driver == &kbus_driver.driver) - trace_pxc_kbus(__func__, "DMA: TX completed!"); -#endif - - complete(&mcspi_dma->dma_tx_completion); -} - static void omap2_mcspi_tx_dma(struct spi_device *spi, struct spi_transfer *xfer, struct dma_slave_config cfg) @@ -460,12 +422,9 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, - xfer->tx_sg.nents, - DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_CTRL_ACK); + if (tx) { - tx->callback = omap2_mcspi_tx_callback; - tx->callback_param = spi; dmaengine_submit(tx); } else { /* FIXME: fall back to PIO? */ @@ -547,11 +506,9 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, } tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, sg_out[0], - out_mapped_nents[0], DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + out_mapped_nents[0], DMA_DEV_TO_MEM, DMA_CTRL_ACK); + if (tx) { - tx->callback = omap2_mcspi_rx_callback; - tx->callback_param = spi; dmaengine_submit(tx); } else { /* FIXME: fall back to PIO? */ @@ -560,10 +517,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, dma_async_issue_pending(mcspi_dma->dma_rx); omap2_mcspi_set_dma_req(spi, 1, 1); - ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_rx_completion); + ret = mcspi_wait_for_completion(mcspi, &mcspi->txrxdone); + omap2_mcspi_set_dma_req(spi, 1, 0); if (ret || mcspi->target_aborted) { dmaengine_terminate_sync(mcspi_dma->dma_rx); - omap2_mcspi_set_dma_req(spi, 1, 0); return 0; } @@ -639,8 +596,8 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) enum dma_slave_buswidth width; unsigned es; void __iomem *chstat_reg; - void __iomem *irqstat_reg; int wait_res; + int ret; #ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION trace_pxc_kbus(__func__, "enter"); @@ -676,67 +633,34 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) tx = xfer->tx_buf; mcspi->target_aborted = false; - reinit_completion(&mcspi_dma->dma_tx_completion); - reinit_completion(&mcspi_dma->dma_rx_completion); - reinit_completion(&mcspi->txdone); - if (tx) { - /* Enable EOW IRQ to know end of tx in target mode */ - if (spi_controller_is_target(spi->controller)) - mcspi_write_reg(spi->controller, - OMAP2_MCSPI_IRQENABLE, - OMAP2_MCSPI_IRQSTATUS_EOW); + reinit_completion(&mcspi->txrxdone); + mcspi_write_reg(spi->controller, OMAP2_MCSPI_IRQENABLE, OMAP2_MCSPI_IRQSTATUS_EOW); + if (tx) omap2_mcspi_tx_dma(spi, xfer, cfg); - } - if (rx != NULL) + if (rx) count = omap2_mcspi_rx_dma(spi, xfer, cfg, es); - if (tx != NULL) { - int ret; - - ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_tx_completion); - if (ret || mcspi->target_aborted) { - dmaengine_terminate_sync(mcspi_dma->dma_tx); - omap2_mcspi_set_dma_req(spi, 0, 0); - return 0; - } - - if (spi_controller_is_target(mcspi->ctlr)) { - ret = mcspi_wait_for_completion(mcspi, &mcspi->txdone); - if (ret || mcspi->target_aborted) - return 0; - } + ret = mcspi_wait_for_completion(mcspi, &mcspi->txrxdone); + omap2_mcspi_set_dma_req(spi, 0, 0); + if (ret || mcspi->target_aborted) + return 0; + /* for TX_ONLY mode, be sure all words have shifted out */ + if (tx && !rx) { + chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; if (mcspi->fifo_depth > 0) { - irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; - - if (mcspi_wait_for_reg_bit(irqstat_reg, - OMAP2_MCSPI_IRQSTATUS_EOW) < 0) - dev_err(&spi->dev, "EOW timed out\n"); - - mcspi_write_reg(mcspi->ctlr, OMAP2_MCSPI_IRQSTATUS, - OMAP2_MCSPI_IRQSTATUS_EOW); - } - - /* for TX_ONLY mode, be sure all words have shifted out */ - if (rx == NULL) { - chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; - if (mcspi->fifo_depth > 0) { - wait_res = mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_TXFFE); - if (wait_res < 0) - dev_err(&spi->dev, "TXFFE timed out\n"); - } else { - wait_res = mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_TXS); - if (wait_res < 0) - dev_err(&spi->dev, "TXS timed out\n"); - } - if (wait_res >= 0 && - (mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_EOT) < 0)) - dev_err(&spi->dev, "EOT timed out\n"); + wait_res = mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXFFE); + if (wait_res < 0) + dev_err(&spi->dev, "TXFFE timed out\n"); + } else { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS); + if (wait_res < 0) + dev_err(&spi->dev, "TXS timed out\n"); } + if (wait_res >= 0 && (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0)) + dev_err(&spi->dev, "EOT timed out\n"); } #ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION @@ -1152,9 +1076,6 @@ static int omap2_mcspi_request_dma(struct omap2_mcspi *mcspi, mcspi_dma->dma_rx = NULL; } - init_completion(&mcspi_dma->dma_rx_completion); - init_completion(&mcspi_dma->dma_tx_completion); - no_dma: return ret; } @@ -1250,8 +1171,10 @@ static irqreturn_t omap2_mcspi_irq_handler(int irq, void *data) /* Disable IRQ and wakeup target xfer task */ mcspi_write_reg(mcspi->ctlr, OMAP2_MCSPI_IRQENABLE, 0); - if (irqstat & OMAP2_MCSPI_IRQSTATUS_EOW) - complete(&mcspi->txdone); + if (irqstat & OMAP2_MCSPI_IRQSTATUS_EOW) { + complete_all(&mcspi->txrxdone); + mcspi_write_reg(mcspi->ctlr, OMAP2_MCSPI_IRQSTATUS, OMAP2_MCSPI_IRQSTATUS_EOW); + } return IRQ_HANDLED; } @@ -1259,12 +1182,9 @@ static irqreturn_t omap2_mcspi_irq_handler(int irq, void *data) static int omap2_mcspi_target_abort(struct spi_controller *ctlr) { struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr); - struct omap2_mcspi_dma *mcspi_dma = mcspi->dma_channels; mcspi->target_aborted = true; - complete(&mcspi_dma->dma_rx_completion); - complete(&mcspi_dma->dma_tx_completion); - complete(&mcspi->txdone); + complete_all(&mcspi->txrxdone); return 0; } @@ -1800,7 +1720,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) status = platform_get_irq(pdev, 0); if (status < 0) goto free_ctlr; - init_completion(&mcspi->txdone); + init_completion(&mcspi->txrxdone); status = devm_request_irq(&pdev->dev, status, omap2_mcspi_irq_handler, 0, pdev->name, mcspi); From d5a09e12b2d6d30fc58ab95d198aea7d48b07eee Mon Sep 17 00:00:00 2001 From: Tobias Biehl Date: Fri, 4 Jul 2025 15:09:21 +0200 Subject: [PATCH 2/4] drivers: spi: spi-omap2-mcspi: add dts configuration for EOW interrupt When the dts property use-eow-interrupt is set for McSPI node, then the McSPI EOW interrupt will be used to signal when a DMA Transfer is done. Signed-off-by: Tobias Biehl --- drivers/spi/spi-omap2-mcspi.c | 217 ++++++++++++++++++++++++++++++++-- 1 file changed, 210 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index f3e66d3222bd..424a20b7576e 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -105,6 +105,10 @@ static int kbusdelay = 0; /* KBUS Inter-Frame-Delay in nanoseconds */ struct omap2_mcspi_dma { struct dma_chan *dma_tx; struct dma_chan *dma_rx; + + struct completion dma_tx_completion; + struct completion dma_rx_completion; + char dma_rx_ch_name[14]; char dma_tx_ch_name[14]; }; @@ -143,6 +147,8 @@ struct omap2_mcspi { unsigned int pin_dir:1; size_t max_xfer_len; u32 ref_clk_hz; + bool use_eow_interrupt; + unsigned (*txrx_dma_transfer)(struct spi_device *spi, struct spi_transfer *xfer); }; struct omap2_mcspi_cs { @@ -408,6 +414,40 @@ static int mcspi_wait_for_completion(struct omap2_mcspi *mcspi, return 0; } +static void omap2_mcspi_rx_callback(void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi = spi_controller_get_devdata(spi->controller); + struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)]; + + /* We must disable the DMA RX request */ + omap2_mcspi_set_dma_req(spi, 1, 0); + +#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION + if (spi->dev.driver == &kbus_driver.driver) + trace_pxc_kbus(__func__, "DMA: RX completed!"); +#endif + + complete(&mcspi_dma->dma_rx_completion); +} + +static void omap2_mcspi_tx_callback(void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi = spi_controller_get_devdata(spi->controller); + struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)]; + + /* We must disable the DMA TX request */ + omap2_mcspi_set_dma_req(spi, 0, 0); + +#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION + if (spi->dev.driver == &kbus_driver.driver) + trace_pxc_kbus(__func__, "DMA: TX completed!"); +#endif + + complete(&mcspi_dma->dma_tx_completion); +} + static void omap2_mcspi_tx_dma(struct spi_device *spi, struct spi_transfer *xfer, struct dma_slave_config cfg) @@ -421,10 +461,20 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, - xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_CTRL_ACK); + if (mcspi->use_eow_interrupt) + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, DMA_MEM_TO_DEV, + DMA_CTRL_ACK); + else + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { + if (!mcspi->use_eow_interrupt) { + tx->callback = omap2_mcspi_tx_callback; + tx->callback_param = spi; + } dmaengine_submit(tx); } else { /* FIXME: fall back to PIO? */ @@ -505,10 +555,20 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, return 0; } - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, sg_out[0], - out_mapped_nents[0], DMA_DEV_TO_MEM, DMA_CTRL_ACK); + if (mcspi->use_eow_interrupt) + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, sg_out[0], + out_mapped_nents[0], DMA_DEV_TO_MEM, + DMA_CTRL_ACK); + else + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, sg_out[0], + out_mapped_nents[0], DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { + if (!mcspi->use_eow_interrupt) { + tx->callback = omap2_mcspi_rx_callback; + tx->callback_param = spi; + } dmaengine_submit(tx); } else { /* FIXME: fall back to PIO? */ @@ -517,10 +577,15 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, dma_async_issue_pending(mcspi_dma->dma_rx); omap2_mcspi_set_dma_req(spi, 1, 1); - ret = mcspi_wait_for_completion(mcspi, &mcspi->txrxdone); - omap2_mcspi_set_dma_req(spi, 1, 0); + if (mcspi->use_eow_interrupt) { + ret = mcspi_wait_for_completion(mcspi, &mcspi->txrxdone); + omap2_mcspi_set_dma_req(spi, 1, 0); + } else { + ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_rx_completion); + } if (ret || mcspi->target_aborted) { dmaengine_terminate_sync(mcspi_dma->dma_rx); + omap2_mcspi_set_dma_req(spi, 1, 0); return 0; } @@ -585,6 +650,126 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, static unsigned omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct omap2_mcspi *mcspi; + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi_dma *mcspi_dma; + unsigned int count; + u8 *rx; + const u8 *tx; + struct dma_slave_config cfg; + enum dma_slave_buswidth width; + unsigned es; + void __iomem *chstat_reg; + void __iomem *irqstat_reg; + int wait_res; + +#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION + trace_pxc_kbus(__func__, "enter"); + + kbus_boost_dma_task(1); +#endif + + mcspi = spi_controller_get_devdata(spi->controller); + mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)]; + + if (cs->word_len <= 8) { + width = DMA_SLAVE_BUSWIDTH_1_BYTE; + es = 1; + } else if (cs->word_len <= 16) { + width = DMA_SLAVE_BUSWIDTH_2_BYTES; + es = 2; + } else { + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + es = 4; + } + + count = xfer->len; + + memset(&cfg, 0, sizeof(cfg)); + cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; + cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; + cfg.src_addr_width = width; + cfg.dst_addr_width = width; + cfg.src_maxburst = 1; + cfg.dst_maxburst = 1; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + + mcspi->target_aborted = false; + reinit_completion(&mcspi_dma->dma_tx_completion); + reinit_completion(&mcspi_dma->dma_rx_completion); + reinit_completion(&mcspi->txrxdone); + if (tx) { + /* Enable EOW IRQ to know end of tx in target mode */ + if (spi_controller_is_target(spi->controller)) + mcspi_write_reg(spi->controller, + OMAP2_MCSPI_IRQENABLE, + OMAP2_MCSPI_IRQSTATUS_EOW); + omap2_mcspi_tx_dma(spi, xfer, cfg); + } + + if (rx != NULL) + count = omap2_mcspi_rx_dma(spi, xfer, cfg, es); + + if (tx != NULL) { + int ret; + + ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_tx_completion); + if (ret || mcspi->target_aborted) { + dmaengine_terminate_sync(mcspi_dma->dma_tx); + omap2_mcspi_set_dma_req(spi, 0, 0); + return 0; + } + + if (spi_controller_is_target(mcspi->ctlr)) { + ret = mcspi_wait_for_completion(mcspi, &mcspi->txrxdone); + if (ret || mcspi->target_aborted) + return 0; + } + + if (mcspi->fifo_depth > 0) { + irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; + + if (mcspi_wait_for_reg_bit(irqstat_reg, + OMAP2_MCSPI_IRQSTATUS_EOW) < 0) + dev_err(&spi->dev, "EOW timed out\n"); + + mcspi_write_reg(mcspi->ctlr, OMAP2_MCSPI_IRQSTATUS, + OMAP2_MCSPI_IRQSTATUS_EOW); + } + + /* for TX_ONLY mode, be sure all words have shifted out */ + if (rx == NULL) { + chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; + if (mcspi->fifo_depth > 0) { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXFFE); + if (wait_res < 0) + dev_err(&spi->dev, "TXFFE timed out\n"); + } else { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS); + if (wait_res < 0) + dev_err(&spi->dev, "TXS timed out\n"); + } + if (wait_res >= 0 && + (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0)) + dev_err(&spi->dev, "EOT timed out\n"); + } + } + +#ifdef CONFIG_SPI_KBUS_OMAP_EXTENSION + trace_pxc_kbus(__func__, "leave"); +#endif + + return count; +} + +static unsigned +omap2_mcspi_txrx_dma_eow(struct spi_device *spi, struct spi_transfer *xfer) { struct omap2_mcspi *mcspi; struct omap2_mcspi_cs *cs = spi->controller_state; @@ -1076,6 +1261,11 @@ static int omap2_mcspi_request_dma(struct omap2_mcspi *mcspi, mcspi_dma->dma_rx = NULL; } + if (!mcspi->use_eow_interrupt) { + init_completion(&mcspi_dma->dma_rx_completion); + init_completion(&mcspi_dma->dma_tx_completion); + } + no_dma: return ret; } @@ -1184,6 +1374,11 @@ static int omap2_mcspi_target_abort(struct spi_controller *ctlr) struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr); mcspi->target_aborted = true; + if (!mcspi->use_eow_interrupt) { + struct omap2_mcspi_dma *mcspi_dma = mcspi->dma_channels; + complete(&mcspi_dma->dma_rx_completion); + complete(&mcspi_dma->dma_tx_completion); + } complete_all(&mcspi->txrxdone); return 0; @@ -1340,7 +1535,7 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr, #ifndef CONFIG_SPI_KBUS_OMAP_EXTENSION ctlr->can_dma(ctlr, spi, t)) { #endif - count = omap2_mcspi_txrx_dma(spi, t); + count = mcspi->txrx_dma_transfer(spi, t); } else { count = omap2_mcspi_txrx_pio(spi, t); @@ -1667,6 +1862,14 @@ static int omap2_mcspi_probe(struct platform_device *pdev) mcspi = spi_controller_get_devdata(ctlr); mcspi->ctlr = ctlr; + if (of_property_read_bool(node, "use-eow-interrupt")) { + mcspi->use_eow_interrupt = true; + mcspi->txrx_dma_transfer = omap2_mcspi_txrx_dma_eow; + } else { + mcspi->use_eow_interrupt = false; + mcspi->txrx_dma_transfer = omap2_mcspi_txrx_dma; + } + match = of_match_device(omap_mcspi_of_match, &pdev->dev); if (match) { u32 num_cs = 1; /* default number of chipselect */ From 43dc753691a284c592e513370b6827b80a79391d Mon Sep 17 00:00:00 2001 From: Tobias Biehl Date: Fri, 4 Jul 2025 15:10:09 +0200 Subject: [PATCH 3/4] arm64: dts: k3-am623-pfc: activate McSPI EOW interrupt handling Signed-off-by: Tobias Biehl --- arch/arm64/boot/dts/ti/k3-am62x-pfc.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/ti/k3-am62x-pfc.dtsi b/arch/arm64/boot/dts/ti/k3-am62x-pfc.dtsi index 17233bdcb213..e81e78ce25fa 100644 --- a/arch/arm64/boot/dts/ti/k3-am62x-pfc.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am62x-pfc.dtsi @@ -727,6 +727,14 @@ dmas = <&main_pktdma 0xc300 0>, <&main_pktdma 0x4300 0>; dma-names = "tx0", "rx0"; + /* + * When this option is set, the McSPI EOW Interrupt will be used + * to signal when a DMA Transfer is done. This is needed for this + * platform to prevent unpredictable delays resulting from TX/RX + * completions within the UDMA driver. + */ + use-eow-interrupt; + kbus@0 { pinctrl-names = "default"; pinctrl-0 = <&kbus_pins_default>; From 6c780f1d0100c0d711266b17f59f3ce782faefa9 Mon Sep 17 00:00:00 2001 From: Tobias Biehl Date: Wed, 9 Jul 2025 11:42:27 +0200 Subject: [PATCH 4/4] drivers: spi: spi-omap2-mcspi: rework flags for dma preparation Co-authored-by: Oleg Karfich Signed-off-by: Tobias Biehl --- drivers/spi/spi-omap2-mcspi.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 424a20b7576e..ee727fa7bc75 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -461,14 +461,10 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); - if (mcspi->use_eow_interrupt) - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, - xfer->tx_sg.nents, DMA_MEM_TO_DEV, - DMA_CTRL_ACK); - else - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, - xfer->tx_sg.nents, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + unsigned long flags = mcspi->use_eow_interrupt ? DMA_CTRL_ACK + : (DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, DMA_MEM_TO_DEV, flags); if (tx) { if (!mcspi->use_eow_interrupt) { @@ -555,14 +551,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, return 0; } - if (mcspi->use_eow_interrupt) - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, sg_out[0], - out_mapped_nents[0], DMA_DEV_TO_MEM, - DMA_CTRL_ACK); - else - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, sg_out[0], - out_mapped_nents[0], DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + unsigned long flags = mcspi->use_eow_interrupt ? DMA_CTRL_ACK + : (DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, DMA_MEM_TO_DEV, flags); if (tx) { if (!mcspi->use_eow_interrupt) {