#include "spi-dw.h"
#ifdef CONFIG_SPI_DW_MID_DMA
+#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/irqreturn.h>
+#include <linux/jiffies.h>
#include <linux/pci.h>
#include <linux/platform_data/dma-dw.h>
dws->master->dma_rx = dws->rxchan;
dws->master->dma_tx = dws->txchan;
+ init_completion(&dws->dma_completion);
+
return 0;
free_rxchan:
dws->master->dma_rx = dws->rxchan;
dws->master->dma_tx = dws->txchan;
+ init_completion(&dws->dma_completion);
+
return 0;
}
dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__);
dws->master->cur_msg->status = -EIO;
- spi_finalize_current_transfer(dws->master);
+ complete(&dws->dma_completion);
return IRQ_HANDLED;
}
return DMA_SLAVE_BUSWIDTH_UNDEFINED;
}
+static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer)
+{
+ unsigned long long ms;
+
+ ms = xfer->len * MSEC_PER_SEC * BITS_PER_BYTE;
+ do_div(ms, xfer->effective_speed_hz);
+ ms += ms + 200;
+
+ if (ms > UINT_MAX)
+ ms = UINT_MAX;
+
+ ms = wait_for_completion_timeout(&dws->dma_completion,
+ msecs_to_jiffies(ms));
+
+ if (ms == 0) {
+ dev_err(&dws->master->cur_msg->spi->dev,
+ "DMA transaction timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
/*
* dws->dma_chan_busy is set before the dma transfer starts, callback for tx
* channel will clear a corresponding bit.
return;
dw_writel(dws, DW_SPI_DMACR, 0);
- spi_finalize_current_transfer(dws->master);
+ complete(&dws->dma_completion);
}
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws,
return;
dw_writel(dws, DW_SPI_DMACR, 0);
- spi_finalize_current_transfer(dws->master);
+ complete(&dws->dma_completion);
}
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws,
/* Set the interrupt mask */
spi_umask_intr(dws, imr);
+ reinit_completion(&dws->dma_completion);
+
dws->transfer_handler = dma_transfer;
return 0;
static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
{
struct dma_async_tx_descriptor *txdesc, *rxdesc;
+ int ret;
/* Prepare the TX dma transfer */
txdesc = dw_spi_dma_prepare_tx(dws, xfer);
dma_async_issue_pending(dws->txchan);
}
- return 1;
+ ret = dw_spi_dma_wait(dws, xfer);
+ if (ret)
+ return ret;
+
+ return 0;
}
static void mid_spi_dma_stop(struct dw_spi *dws)