Commit a77541ca authored by Mark Brown's avatar Mark Brown

spi: Helper for deriving timeout values

Merge series from Miquel Raynal <miquel.raynal@bootlin.com>:

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.
parents 01fa9edd 6eef8955
......@@ -233,7 +233,8 @@
*/
#define DMA_MIN_BYTES 16
#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000))
#define SPI_DMA_MIN_TIMEOUT (msecs_to_jiffies(1000))
#define SPI_DMA_TIMEOUT_PER_10K (msecs_to_jiffies(4))
#define AUTOSUSPEND_TIMEOUT 2000
......@@ -1279,7 +1280,8 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
struct atmel_spi_device *asd;
int timeout;
int ret;
unsigned long dma_timeout;
unsigned int dma_timeout;
long ret_timeout;
as = spi_controller_get_devdata(host);
......@@ -1333,11 +1335,13 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
atmel_spi_unlock(as);
}
dma_timeout = wait_for_completion_timeout(&as->xfer_completion,
SPI_DMA_TIMEOUT);
if (WARN_ON(dma_timeout == 0)) {
dev_err(&spi->dev, "spi transfer timeout\n");
as->done_status = -EIO;
dma_timeout = msecs_to_jiffies(spi_controller_xfer_timeout(host, xfer));
ret_timeout = wait_for_completion_interruptible_timeout(&as->xfer_completion,
dma_timeout);
if (ret_timeout <= 0) {
dev_err(&spi->dev, "spi transfer %s\n",
!ret_timeout ? "timeout" : "canceled");
as->done_status = ret_timeout < 0 ? ret_timeout : -EIO;
}
if (as->done_status)
......
......@@ -455,7 +455,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
tx_time = spi_controller_xfer_timeout(master, tfr);
start = jiffies;
timeout = wait_for_completion_timeout(&sspi->done,
msecs_to_jiffies(tx_time));
......
......@@ -1261,6 +1261,23 @@ static inline bool spi_is_bpw_supported(struct spi_device *spi, u32 bpw)
return false;
}
/**
* spi_controller_xfer_timeout - Compute a suitable timeout value
* @ctlr: SPI device
* @xfer: Transfer descriptor
*
* Compute a relevant timeout value for the given transfer. We derive the time
* that it would take on a single data line and take twice this amount of time
* with a minimum of 500ms to avoid false positives on loaded systems.
*
* Returns: Transfer timeout value in milliseconds.
*/
static inline unsigned int spi_controller_xfer_timeout(struct spi_controller *ctlr,
struct spi_transfer *xfer)
{
return max(xfer->len * 8 * 2 / (xfer->speed_hz / 1000), 500U);
}
/*---------------------------------------------------------------------------*/
/* SPI transfer replacement methods which make use of spi_res */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment