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 Original line Diff line number Diff line
@@ -233,7 +233,8 @@
 */
 */
#define DMA_MIN_BYTES	16
#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
#define AUTOSUSPEND_TIMEOUT	2000


@@ -1279,7 +1280,8 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
	struct atmel_spi_device	*asd;
	struct atmel_spi_device	*asd;
	int			timeout;
	int			timeout;
	int			ret;
	int			ret;
	unsigned long		dma_timeout;
	unsigned int		dma_timeout;
	long			ret_timeout;


	as = spi_controller_get_devdata(host);
	as = spi_controller_get_devdata(host);


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


		dma_timeout = wait_for_completion_timeout(&as->xfer_completion,
		dma_timeout = msecs_to_jiffies(spi_controller_xfer_timeout(host, xfer));
							  SPI_DMA_TIMEOUT);
		ret_timeout = wait_for_completion_interruptible_timeout(&as->xfer_completion,
		if (WARN_ON(dma_timeout == 0)) {
									dma_timeout);
			dev_err(&spi->dev, "spi transfer timeout\n");
		if (ret_timeout <= 0) {
			as->done_status = -EIO;
			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)
		if (as->done_status)
+1 −1
Original line number Original line 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);
	reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
	sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
	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;
	start = jiffies;
	timeout = wait_for_completion_timeout(&sspi->done,
	timeout = wait_for_completion_timeout(&sspi->done,
					      msecs_to_jiffies(tx_time));
					      msecs_to_jiffies(tx_time));
+17 −0
Original line number Original line Diff line number Diff line
@@ -1261,6 +1261,23 @@ static inline bool spi_is_bpw_supported(struct spi_device *spi, u32 bpw)
	return false;
	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 */
/* SPI transfer replacement methods which make use of spi_res */