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:
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user