spi: meson-spicc: move wait completion in driver to take bursts delay in account
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>
This commit is contained in:
parent
195583504b
commit
04694e5002
@ -160,6 +160,7 @@ struct meson_spicc_device {
|
|||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct spi_message *message;
|
struct spi_message *message;
|
||||||
struct spi_transfer *xfer;
|
struct spi_transfer *xfer;
|
||||||
|
struct completion done;
|
||||||
const struct meson_spicc_data *data;
|
const struct meson_spicc_data *data;
|
||||||
u8 *tx_buf;
|
u8 *tx_buf;
|
||||||
u8 *rx_buf;
|
u8 *rx_buf;
|
||||||
@ -282,7 +283,7 @@ static irqreturn_t meson_spicc_irq(int irq, void *data)
|
|||||||
/* Disable all IRQs */
|
/* Disable all IRQs */
|
||||||
writel(0, spicc->base + SPICC_INTREG);
|
writel(0, spicc->base + SPICC_INTREG);
|
||||||
|
|
||||||
spi_finalize_current_transfer(spicc->master);
|
complete(&spicc->done);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
@ -386,6 +387,7 @@ static int meson_spicc_transfer_one(struct spi_master *master,
|
|||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
|
struct meson_spicc_device *spicc = spi_master_get_devdata(master);
|
||||||
|
unsigned long timeout;
|
||||||
|
|
||||||
/* Store current transfer */
|
/* Store current transfer */
|
||||||
spicc->xfer = xfer;
|
spicc->xfer = xfer;
|
||||||
@ -410,13 +412,29 @@ static int meson_spicc_transfer_one(struct spi_master *master,
|
|||||||
/* Setup burst */
|
/* Setup burst */
|
||||||
meson_spicc_setup_burst(spicc);
|
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 */
|
/* Start burst */
|
||||||
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
|
writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
|
||||||
|
|
||||||
/* Enable interrupts */
|
/* Enable interrupts */
|
||||||
writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
|
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,
|
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;
|
spicc->pdev = pdev;
|
||||||
platform_set_drvdata(pdev, spicc);
|
platform_set_drvdata(pdev, spicc);
|
||||||
|
|
||||||
|
init_completion(&spicc->done);
|
||||||
|
|
||||||
spicc->base = devm_platform_ioremap_resource(pdev, 0);
|
spicc->base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
if (IS_ERR(spicc->base)) {
|
if (IS_ERR(spicc->base)) {
|
||||||
dev_err(&pdev->dev, "io resource mapping failed\n");
|
dev_err(&pdev->dev, "io resource mapping failed\n");
|
||||||
|
Loading…
Reference in New Issue
Block a user