Skip to content

Commit bdbdf0f

Browse files
fancerbroonie
authored andcommitted
spi: dw: Locally wait for the DMA transfers completion
In general each DMA-based SPI transfer can be split up into two stages: DMA data transmission/reception and SPI-bus transmission/reception. DMA asynchronous transactions completion can be tracked by means of the DMA async Tx-descriptor completion callback. But that callback being called indicates that the DMA transfer has been finished, it doesn't mean that SPI data transmission is also done. Moreover in fact it isn't for at least Tx-only SPI transfers. Upon DMA transfer completion some data is left in the Tx FIFO and being pushed out by the SPI controller. So in order to make sure that an SPI transfer is completely pushed to the SPI-bus, the driver has to wait for both DMA transaction and the SPI-bus transmission/reception are finished. Note if there is a way to asynchronously track the former event by means of the DMA async Tx callback, there isn't easy one for the later (IRQ-based solution won't work since SPI controller doesn't notify about Rx FIFO being empty). The DMA transfer completion callback isn't suitable to wait for the SPI controller activity finish either. The callback might (in case of DW DMAC it will) be called in the tasklet context. Waiting for the SPI controller to complete the transfer might take a considerable amount of time since SPI-bus might be pretty slow. In this case delaying the execution in the tasklet atomic context might cause significant system performance drop. So to speak the best option we've got to solve the problem is to consequently wait for both stages being finished in the locally implemented SPI transfer execution procedure even if it costs us of the local wait-function re-implementation. In this case we don't need to use the SPI-core transfer-wait functionality, but we'll make sure that all DMA and SPI-bus transactions are completely finished before the SPI-core transfer_one callback returns. In this commit we provide an implementation of the DMA-transfers completion wait functionality. The DW APB SSI DMA-specific SPI transfer_one function waits for both Tx and Rx DMA transfers being finished, and only then exits with zero returned signalling to the SPI core that the SPI transfer is finished. This implementation is fully equivalent to the currently used DMA-execution-SPI-core-wait algorithm. The SPI-bus transmission/reception wait methods will be added in the follow-up commits. Signed-off-by: Serge Semin <[email protected]> Cc: Georgy Vlasov <[email protected]> Cc: Ramil Zaripov <[email protected]> Cc: Alexey Malahov <[email protected]> Cc: Thomas Bogendoerfer <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Feng Tang <[email protected]> Cc: Andy Shevchenko <[email protected]> Cc: Rob Herring <[email protected]> Cc: [email protected] Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent f0410bb commit bdbdf0f

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

drivers/spi/spi-dw-mid.c

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
#include "spi-dw.h"
1212

1313
#ifdef CONFIG_SPI_DW_MID_DMA
14+
#include <linux/completion.h>
1415
#include <linux/dma-mapping.h>
1516
#include <linux/dmaengine.h>
1617
#include <linux/irqreturn.h>
18+
#include <linux/jiffies.h>
1719
#include <linux/pci.h>
1820
#include <linux/platform_data/dma-dw.h>
1921

@@ -66,6 +68,8 @@ static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws)
6668
dws->master->dma_rx = dws->rxchan;
6769
dws->master->dma_tx = dws->txchan;
6870

71+
init_completion(&dws->dma_completion);
72+
6973
return 0;
7074

7175
free_rxchan:
@@ -91,6 +95,8 @@ static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws)
9195
dws->master->dma_rx = dws->rxchan;
9296
dws->master->dma_tx = dws->txchan;
9397

98+
init_completion(&dws->dma_completion);
99+
94100
return 0;
95101
}
96102

@@ -121,7 +127,7 @@ static irqreturn_t dma_transfer(struct dw_spi *dws)
121127

122128
dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__);
123129
dws->master->cur_msg->status = -EIO;
124-
spi_finalize_current_transfer(dws->master);
130+
complete(&dws->dma_completion);
125131
return IRQ_HANDLED;
126132
}
127133

@@ -142,6 +148,29 @@ static enum dma_slave_buswidth convert_dma_width(u8 n_bytes) {
142148
return DMA_SLAVE_BUSWIDTH_UNDEFINED;
143149
}
144150

151+
static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer)
152+
{
153+
unsigned long long ms;
154+
155+
ms = xfer->len * MSEC_PER_SEC * BITS_PER_BYTE;
156+
do_div(ms, xfer->effective_speed_hz);
157+
ms += ms + 200;
158+
159+
if (ms > UINT_MAX)
160+
ms = UINT_MAX;
161+
162+
ms = wait_for_completion_timeout(&dws->dma_completion,
163+
msecs_to_jiffies(ms));
164+
165+
if (ms == 0) {
166+
dev_err(&dws->master->cur_msg->spi->dev,
167+
"DMA transaction timed out\n");
168+
return -ETIMEDOUT;
169+
}
170+
171+
return 0;
172+
}
173+
145174
/*
146175
* dws->dma_chan_busy is set before the dma transfer starts, callback for tx
147176
* channel will clear a corresponding bit.
@@ -155,7 +184,7 @@ static void dw_spi_dma_tx_done(void *arg)
155184
return;
156185

157186
dw_writel(dws, DW_SPI_DMACR, 0);
158-
spi_finalize_current_transfer(dws->master);
187+
complete(&dws->dma_completion);
159188
}
160189

161190
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws,
@@ -204,7 +233,7 @@ static void dw_spi_dma_rx_done(void *arg)
204233
return;
205234

206235
dw_writel(dws, DW_SPI_DMACR, 0);
207-
spi_finalize_current_transfer(dws->master);
236+
complete(&dws->dma_completion);
208237
}
209238

210239
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws,
@@ -260,6 +289,8 @@ static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer)
260289
/* Set the interrupt mask */
261290
spi_umask_intr(dws, imr);
262291

292+
reinit_completion(&dws->dma_completion);
293+
263294
dws->transfer_handler = dma_transfer;
264295

265296
return 0;
@@ -268,6 +299,7 @@ static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer)
268299
static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
269300
{
270301
struct dma_async_tx_descriptor *txdesc, *rxdesc;
302+
int ret;
271303

272304
/* Prepare the TX dma transfer */
273305
txdesc = dw_spi_dma_prepare_tx(dws, xfer);
@@ -288,7 +320,11 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
288320
dma_async_issue_pending(dws->txchan);
289321
}
290322

291-
return 1;
323+
ret = dw_spi_dma_wait(dws, xfer);
324+
if (ret)
325+
return ret;
326+
327+
return 0;
292328
}
293329

294330
static void mid_spi_dma_stop(struct dw_spi *dws)

drivers/spi/spi-dw.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#ifndef DW_SPI_HEADER_H
33
#define DW_SPI_HEADER_H
44

5+
#include <linux/completion.h>
56
#include <linux/irqreturn.h>
67
#include <linux/io.h>
78
#include <linux/scatterlist.h>
@@ -145,6 +146,7 @@ struct dw_spi {
145146
unsigned long dma_chan_busy;
146147
dma_addr_t dma_addr; /* phy address of the Data register */
147148
const struct dw_spi_dma_ops *dma_ops;
149+
struct completion dma_completion;
148150

149151
#ifdef CONFIG_DEBUG_FS
150152
struct dentry *debugfs;

0 commit comments

Comments
 (0)