Skip to content

Commit a77541c

Browse files
committed
spi: Helper for deriving timeout values
Merge series from Miquel Raynal <[email protected]>: I recently came across an issue with the Atmel spi controller driver which would stop my transfers after a too small timeout when performing big transfers (reading a 4MiB flash in one transfer). My initial idea was to derive a the maximum amount of time a transfer would take depending on its size and use that as value to avoid erroring-out when not relevant. Mark wanted to go further by creating a core helper doing that, based on the heuristics from the sun6i driver. Here is a small series of 3 patches doing exactly that.
2 parents 01fa9ed + 6eef895 commit a77541c

File tree

3 files changed

+29
-8
lines changed

3 files changed

+29
-8
lines changed

drivers/spi/spi-atmel.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@
233233
*/
234234
#define DMA_MIN_BYTES 16
235235

236-
#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000))
236+
#define SPI_DMA_MIN_TIMEOUT (msecs_to_jiffies(1000))
237+
#define SPI_DMA_TIMEOUT_PER_10K (msecs_to_jiffies(4))
237238

238239
#define AUTOSUSPEND_TIMEOUT 2000
239240

@@ -1279,7 +1280,8 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
12791280
struct atmel_spi_device *asd;
12801281
int timeout;
12811282
int ret;
1282-
unsigned long dma_timeout;
1283+
unsigned int dma_timeout;
1284+
long ret_timeout;
12831285

12841286
as = spi_controller_get_devdata(host);
12851287

@@ -1333,11 +1335,13 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
13331335
atmel_spi_unlock(as);
13341336
}
13351337

1336-
dma_timeout = wait_for_completion_timeout(&as->xfer_completion,
1337-
SPI_DMA_TIMEOUT);
1338-
if (WARN_ON(dma_timeout == 0)) {
1339-
dev_err(&spi->dev, "spi transfer timeout\n");
1340-
as->done_status = -EIO;
1338+
dma_timeout = msecs_to_jiffies(spi_controller_xfer_timeout(host, xfer));
1339+
ret_timeout = wait_for_completion_interruptible_timeout(&as->xfer_completion,
1340+
dma_timeout);
1341+
if (ret_timeout <= 0) {
1342+
dev_err(&spi->dev, "spi transfer %s\n",
1343+
!ret_timeout ? "timeout" : "canceled");
1344+
as->done_status = ret_timeout < 0 ? ret_timeout : -EIO;
13411345
}
13421346

13431347
if (as->done_status)

drivers/spi/spi-sun6i.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
455455
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
456456
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
457457

458-
tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
458+
tx_time = spi_controller_xfer_timeout(master, tfr);
459459
start = jiffies;
460460
timeout = wait_for_completion_timeout(&sspi->done,
461461
msecs_to_jiffies(tx_time));

include/linux/spi/spi.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,23 @@ static inline bool spi_is_bpw_supported(struct spi_device *spi, u32 bpw)
12611261
return false;
12621262
}
12631263

1264+
/**
1265+
* spi_controller_xfer_timeout - Compute a suitable timeout value
1266+
* @ctlr: SPI device
1267+
* @xfer: Transfer descriptor
1268+
*
1269+
* Compute a relevant timeout value for the given transfer. We derive the time
1270+
* that it would take on a single data line and take twice this amount of time
1271+
* with a minimum of 500ms to avoid false positives on loaded systems.
1272+
*
1273+
* Returns: Transfer timeout value in milliseconds.
1274+
*/
1275+
static inline unsigned int spi_controller_xfer_timeout(struct spi_controller *ctlr,
1276+
struct spi_transfer *xfer)
1277+
{
1278+
return max(xfer->len * 8 * 2 / (xfer->speed_hz / 1000), 500U);
1279+
}
1280+
12641281
/*---------------------------------------------------------------------------*/
12651282

12661283
/* SPI transfer replacement methods which make use of spi_res */

0 commit comments

Comments
 (0)