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>; diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index caf532b70209..ee727fa7bc75 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -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);