Merge pull request #307 from BU-Automation/topic/tbi/mcspi-udma-fixes

spi: spi-omap2-mcspi: Use EOW interrupt for completion when DMA enabled
This commit is contained in:
Oleg KARFICH
2025-07-10 10:35:36 +02:00
committed by GitHub Enterprise
2 changed files with 147 additions and 24 deletions
+8
View File
@@ -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>;
+139 -24
View File
@@ -132,7 +132,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;
@@ -147,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 {
@@ -459,13 +461,16 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi,
dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
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,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
xfer->tx_sg.nents, DMA_MEM_TO_DEV, flags);
if (tx) {
tx->callback = omap2_mcspi_tx_callback;
tx->callback_param = spi;
if (!mcspi->use_eow_interrupt) {
tx->callback = omap2_mcspi_tx_callback;
tx->callback_param = spi;
}
dmaengine_submit(tx);
} else {
/* FIXME: fall back to PIO? */
@@ -546,12 +551,16 @@ 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_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) {
tx->callback = omap2_mcspi_rx_callback;
tx->callback_param = spi;
if (!mcspi->use_eow_interrupt) {
tx->callback = omap2_mcspi_rx_callback;
tx->callback_param = spi;
}
dmaengine_submit(tx);
} else {
/* FIXME: fall back to PIO? */
@@ -560,7 +569,12 @@ 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);
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);
@@ -678,7 +692,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
mcspi->target_aborted = false;
reinit_completion(&mcspi_dma->dma_tx_completion);
reinit_completion(&mcspi_dma->dma_rx_completion);
reinit_completion(&mcspi->txdone);
reinit_completion(&mcspi->txrxdone);
if (tx) {
/* Enable EOW IRQ to know end of tx in target mode */
if (spi_controller_is_target(spi->controller))
@@ -702,7 +716,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
}
if (spi_controller_is_target(mcspi->ctlr)) {
ret = mcspi_wait_for_completion(mcspi, &mcspi->txdone);
ret = mcspi_wait_for_completion(mcspi, &mcspi->txrxdone);
if (ret || mcspi->target_aborted)
return 0;
}
@@ -746,6 +760,93 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
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;
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;
int wait_res;
int ret;
#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->txrxdone);
mcspi_write_reg(spi->controller, OMAP2_MCSPI_IRQENABLE, OMAP2_MCSPI_IRQSTATUS_EOW);
if (tx)
omap2_mcspi_tx_dma(spi, xfer, cfg);
if (rx)
count = omap2_mcspi_rx_dma(spi, xfer, cfg, es);
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) {
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_pio(struct spi_device *spi, struct spi_transfer *xfer)
{
@@ -1152,8 +1253,10 @@ 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);
if (!mcspi->use_eow_interrupt) {
init_completion(&mcspi_dma->dma_rx_completion);
init_completion(&mcspi_dma->dma_tx_completion);
}
no_dma:
return ret;
@@ -1250,8 +1353,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 +1364,14 @@ 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);
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;
}
@@ -1420,7 +1527,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);
@@ -1747,6 +1854,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 */
@@ -1800,7 +1915,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);