diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 7f06305e16cb..152cd6773403 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -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) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 02a3a4f2b3a0..30d541612253 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -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)); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index cfe42f8cd7a4..32c94eae8926 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -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 */