Skip to content

Commit 54c208b

Browse files
pelwellpopcornmix
authored andcommitted
spi: dw: Fix non-DMA transmit-only transfers
Ensure the transmit FIFO has emptied before ending the transfer by dropping the TX threshold to 0 when the last byte has been pushed into the FIFO. Include a similar fix for the non-IRQ paths. See: #6285 Fixes: 6014649 ("spi: dw: Save bandwidth with the TMOD_TO feature") Signed-off-by: Phil Elwell <phil@raspberrypi.com>
1 parent 8e6bd5b commit 54c208b

File tree

2 files changed

+55
-9
lines changed

2 files changed

+55
-9
lines changed

drivers/spi/spi-dw-core.c

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,32 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw)
222222
}
223223
EXPORT_SYMBOL_NS_GPL(dw_spi_check_status, "SPI_DW_CORE");
224224

225+
static inline bool dw_spi_ctlr_busy(struct dw_spi *dws)
226+
{
227+
return dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY;
228+
}
229+
230+
static enum hrtimer_restart dw_spi_hrtimer_handler(struct hrtimer *hr)
231+
{
232+
struct dw_spi *dws = container_of(hr, struct dw_spi, hrtimer);
233+
234+
if (!dw_spi_ctlr_busy(dws)) {
235+
spi_finalize_current_transfer(dws->host);
236+
return HRTIMER_NORESTART;
237+
}
238+
239+
if (!dws->idle_wait_retries) {
240+
dev_err(&dws->host->dev, "controller stuck at busy\n");
241+
spi_finalize_current_transfer(dws->host);
242+
return HRTIMER_NORESTART;
243+
}
244+
245+
dws->idle_wait_retries--;
246+
hrtimer_forward_now(hr, dws->idle_wait_interval);
247+
248+
return HRTIMER_RESTART;
249+
}
250+
225251
static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
226252
{
227253
u16 irq_status = dw_readl(dws, DW_SPI_ISR);
@@ -248,7 +274,22 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
248274
}
249275
} else if (!dws->tx_len) {
250276
dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
251-
spi_finalize_current_transfer(dws->host);
277+
if (dw_spi_ctlr_busy(dws)) {
278+
ktime_t period = ns_to_ktime(DIV_ROUND_UP(NSEC_PER_SEC, dws->current_freq));
279+
280+
/*
281+
* Make the initial wait an underestimate of how long the transfer
282+
* should take, then poll rapidly to reduce the delay
283+
*/
284+
hrtimer_start(&dws->hrtimer,
285+
period * (8 * dws->n_bytes - 1),
286+
HRTIMER_MODE_REL);
287+
dws->idle_wait_retries = 10;
288+
dws->idle_wait_interval = period;
289+
} else {
290+
spi_finalize_current_transfer(dws->host);
291+
}
292+
return IRQ_HANDLED;
252293
}
253294

254295
/*
@@ -257,9 +298,13 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
257298
* have the TXE IRQ flood at the final stage of the transfer.
258299
*/
259300
if (irq_status & DW_SPI_INT_TXEI) {
260-
if (!dws->tx_len)
261-
dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
262301
dw_writer(dws);
302+
if (!dws->tx_len) {
303+
if (dws->rx_len)
304+
dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
305+
else
306+
dw_writel(dws, DW_SPI_TXFTLR, 0);
307+
}
263308
}
264309

265310
return IRQ_HANDLED;
@@ -430,7 +475,7 @@ static int dw_spi_poll_transfer(struct dw_spi *dws,
430475
ret = dw_spi_check_status(dws, true);
431476
if (ret)
432477
return ret;
433-
} while (dws->rx_len);
478+
} while (dws->rx_len || dws->tx_len || dw_spi_ctlr_busy(dws));
434479

435480
return 0;
436481
}
@@ -650,11 +695,6 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi)
650695
return 0;
651696
}
652697

653-
static inline bool dw_spi_ctlr_busy(struct dw_spi *dws)
654-
{
655-
return dw_readl(dws, DW_SPI_SR) & DW_SPI_SR_BUSY;
656-
}
657-
658698
static int dw_spi_wait_mem_op_done(struct dw_spi *dws)
659699
{
660700
int retry = DW_SPI_WAIT_RETRIES;
@@ -1011,6 +1051,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
10111051
}
10121052
}
10131053

1054+
hrtimer_setup(&dws->hrtimer, dw_spi_hrtimer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
1055+
10141056
ret = spi_register_controller(host);
10151057
if (ret) {
10161058
dev_err_probe(dev, ret, "problem registering spi host\n");
@@ -1036,6 +1078,7 @@ void dw_spi_remove_host(struct dw_spi *dws)
10361078
{
10371079
dw_spi_debugfs_remove(dws);
10381080

1081+
hrtimer_cancel(&dws->hrtimer);
10391082
spi_unregister_controller(dws->host);
10401083

10411084
if (dws->dma_ops && dws->dma_ops->dma_exit)

drivers/spi/spi-dw.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ struct dw_spi {
180180
u32 current_freq; /* frequency in hz */
181181
u32 cur_rx_sample_dly;
182182
u32 def_rx_sample_dly_ns;
183+
struct hrtimer hrtimer;
184+
ktime_t idle_wait_interval;
185+
int idle_wait_retries;
183186

184187
/* Custom memory operations */
185188
struct spi_controller_mem_ops mem_ops;

0 commit comments

Comments
 (0)