spi: meson-spicc: move wait completion in driver to take bursts delay in account
authorNeil Armstrong <neil.armstrong@linaro.org>
Wed, 26 Oct 2022 07:58:28 +0000 (09:58 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 26 Oct 2022 13:17:46 +0000 (14:17 +0100)
Some delay occurs between each bursts, thus the default delay is wrong
and a timeout will occur with big enough transfers.

The solution is to handle the timeout management in the driver and
add some delay for each bursts in the timeout calculation.

Reported-by: Da Xue <da@libre.computer>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20221026-spicc-burst-delay-v1-0-1be5ffb7051a@linaro.org
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-meson-spicc.c

index bad201510a99261eb7d4804358e7ba0696f6cf59..52bffab183298eea46a02e2d2e0c16c2d68f0485 100644 (file)
@@ -160,6 +160,7 @@ struct meson_spicc_device {
        struct clk                      *clk;
        struct spi_message              *message;
        struct spi_transfer             *xfer;
+       struct completion               done;
        const struct meson_spicc_data   *data;
        u8                              *tx_buf;
        u8                              *rx_buf;
@@ -282,7 +283,7 @@ static irqreturn_t meson_spicc_irq(int irq, void *data)
                /* Disable all IRQs */
                writel(0, spicc->base + SPICC_INTREG);
 
-               spi_finalize_current_transfer(spicc->master);
+               complete(&spicc->done);
 
                return IRQ_HANDLED;
        }
@@ -386,6 +387,7 @@ static int meson_spicc_transfer_one(struct spi_master *master,
                                    struct spi_transfer *xfer)
 {
        struct meson_spicc_device *spicc = spi_master_get_devdata(master);
+       unsigned long timeout;
 
        /* Store current transfer */
        spicc->xfer = xfer;
@@ -410,13 +412,29 @@ static int meson_spicc_transfer_one(struct spi_master *master,
        /* Setup burst */
        meson_spicc_setup_burst(spicc);
 
+       /* Setup wait for completion */
+       reinit_completion(&spicc->done);
+
+       /* For each byte we wait for 8 cycles of the SPI clock */
+       timeout = 8LL * MSEC_PER_SEC * xfer->len;
+       do_div(timeout, xfer->speed_hz);
+
+       /* Add 10us delay between each fifo bursts */
+       timeout += ((xfer->len >> 4) * 10) / MSEC_PER_SEC;
+
+       /* Increase it twice and add 200 ms tolerance */
+       timeout += timeout + 200;
+
        /* Start burst */
        writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
 
        /* Enable interrupts */
        writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
 
-       return 1;
+       if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
+               return -ETIMEDOUT;
+
+       return 0;
 }
 
 static int meson_spicc_prepare_message(struct spi_master *master,
@@ -743,6 +761,8 @@ static int meson_spicc_probe(struct platform_device *pdev)
        spicc->pdev = pdev;
        platform_set_drvdata(pdev, spicc);
 
+       init_completion(&spicc->done);
+
        spicc->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(spicc->base)) {
                dev_err(&pdev->dev, "io resource mapping failed\n");