Re: [PATCH v2 2/8] spi: imx: replace fixed timeout with calculated one
From: Robin Gong
Date: Wed Sep 30 2015 - 04:48:13 EST
On Fri, Sep 25, 2015 at 07:57:09PM +0200, Anton Bondarenko wrote:
> Fixed timeout value can fire while transaction is ongoing. This may happen
> because there are no strict requirements on SPI transaction duration.
> Dynamic timeout value is generated based on SCLK and transaction size.
>
> There is also 4 * SCLK delay between TX bursts related to CS change.
>
> Signed-off-by: Anton Bondarenko <anton_bondarenko@xxxxxxxxxx>
> ---
> drivers/spi/spi-imx.c | 49 +++++++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 41 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
> index 165bc2c..6c98eda 100644
> --- a/drivers/spi/spi-imx.c
> +++ b/drivers/spi/spi-imx.c
> @@ -59,7 +59,6 @@
>
> /* The maximum bytes that a sdma BD can transfer.*/
> #define MAX_SDMA_BD_BYTES (1 << 15)
> -#define IMX_DMA_TIMEOUT (msecs_to_jiffies(3000))
> struct spi_imx_config {
> unsigned int speed_hz;
> unsigned int bpw;
> @@ -95,6 +94,7 @@ struct spi_imx_data {
> struct clk *clk_per;
> struct clk *clk_ipg;
> unsigned long spi_clk;
> + unsigned int spi_bus_clk;
>
> unsigned int count;
> void (*tx)(struct spi_imx_data *);
> @@ -317,8 +317,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
> {
> u32 ctrl = MX51_ECSPI_CTRL_ENABLE, dma = 0;
> u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG);
> -
> - u32 clk = config->speed_hz, delay;
> + u32 delay;
>
> /*
> * The hardware seems to have a race condition when changing modes. The
> @@ -330,7 +329,9 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
> ctrl |= MX51_ECSPI_CTRL_MODE_MASK;
>
> /* set clock speed */
> - ctrl |= mx51_ecspi_clkdiv(spi_imx->spi_clk, config->speed_hz, &clk);
> + spi_imx->spi_bus_clk = config->speed_hz;
> + ctrl |= mx51_ecspi_clkdiv(spi_imx->spi_clk, config->speed_hz,
> + &spi_imx->spi_bus_clk);
>
> /* set chip select to use */
> ctrl |= MX51_ECSPI_CTRL_CS(config->cs);
> @@ -363,7 +364,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
> * the SPI communication as the device on the other end would consider
> * the change of SCLK polarity as a clock tick already.
> */
> - delay = (2 * 1000000) / clk;
> + delay = (2 * USEC_PER_SEC) / spi_imx->spi_bus_clk;
> if (likely(delay < 10)) /* SCLK is faster than 100 kHz */
> udelay(delay);
> else /* SCLK is _very_ slow */
> @@ -889,12 +890,40 @@ static void spi_imx_dma_tx_callback(void *cookie)
> complete(&spi_imx->dma_tx_completion);
> }
>
> +static int spi_imx_calculate_timeout(struct spi_imx_data *spi_imx, int size)
> +{
> + unsigned long coef1 = 1;
> + unsigned long coef2 = MSEC_PER_SEC;
> + unsigned long timeout = 0;
> +
> + /* Swap coeficients to avoid div by 0 */
> + if (spi_imx->spi_bus_clk < MSEC_PER_SEC) {
> + coef1 = MSEC_PER_SEC;
> + coef2 = 1;
> + }
> +
> + /* Time with actual data transfer */
> + timeout += DIV_ROUND_UP(8 * size * coef1,
> + spi_imx->spi_bus_clk / coef2);
> +
> + /* Take CS change delay related to HW */
> + timeout += DIV_ROUND_UP((size - 1) * 4 * coef1,
> + spi_imx->spi_bus_clk / coef2);
> +
> + /* Add extra second for scheduler related activities */
> + timeout += MSEC_PER_SEC;
> +
> + /* Double calculated timeout */
> + return msecs_to_jiffies(2 * timeout);
> +}
> +
> static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
> struct spi_transfer *transfer)
> {
> struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
> int ret;
> unsigned long timeout;
> + unsigned long transfer_timeout;
> const int left = transfer->len % spi_imx->wml;
> struct spi_master *master = spi_imx->bitbang.master;
> struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg;
> @@ -947,9 +976,11 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
> dma_async_issue_pending(master->dma_tx);
> spi_imx->devtype_data->trigger(spi_imx);
>
> + transfer_timeout = spi_imx_calculate_timeout(spi_imx, transfer->len);
> +
> /* Wait SDMA to finish the data transfer.*/
> timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
> - IMX_DMA_TIMEOUT);
> + transfer_timeout);
> if (!timeout) {
> pr_warn("%s %s: I/O Error in DMA TX\n",
> dev_driver_string(&master->dev),
> @@ -957,8 +988,10 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
> dmaengine_terminate_all(master->dma_tx);
> dmaengine_terminate_all(master->dma_rx);
> } else {
> + transfer_timeout = spi_imx_calculate_timeout(spi_imx,
> + spi_imx->wml * 2);
*2 again here? Although spi_imx_calculate_timeout has double it.
> timeout = wait_for_completion_timeout(
> - &spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT);
> + &spi_imx->dma_rx_completion, transfer_timeout);
> if (!timeout) {
> pr_warn("%s %s: I/O Error in DMA RX\n",
> dev_driver_string(&master->dev),
> @@ -980,7 +1013,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
> spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TCEN);
>
> timeout = wait_for_completion_timeout(
> - &spi_imx->xfer_done, IMX_DMA_TIMEOUT);
> + &spi_imx->xfer_done, transfer_timeout);
> if (!timeout) {
> pr_warn("%s %s: I/O Error in RX tail\n",
> dev_driver_string(&master->dev),
> --
> 2.5.2
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/