Unverified Commit a77541ca authored by Mark Brown's avatar Mark Brown
Browse files

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
Loading
Loading
Loading
Loading
+11 −7
Original line number Diff line number Diff line
@@ -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)
+1 −1
Original line number Diff line number Diff line
@@ -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));
+17 −0
Original line number Diff line number Diff line
@@ -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 */