Commit 32904dec authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'spi-fix-v6.6-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi fixes from Mark Brown:
 "A couple of fixes for the sun6i driver. The patch to reduce DMA RX to
  single byte width all the time is *hopefully* excessively cautious but
  it's unclear which SoCs are affected so the fix just covers everything
  for safety"

* tag 'spi-fix-v6.6-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi:
  spi: sun6i: fix race between DMA RX transfer completion and RX FIFO drain
  spi: sun6i: reduce DMA RX transfer width to single byte
parents 0c021834 1f11f420
...@@ -106,6 +106,7 @@ struct sun6i_spi { ...@@ -106,6 +106,7 @@ struct sun6i_spi {
struct reset_control *rstc; struct reset_control *rstc;
struct completion done; struct completion done;
struct completion dma_rx_done;
const u8 *tx_buf; const u8 *tx_buf;
u8 *rx_buf; u8 *rx_buf;
...@@ -200,6 +201,13 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) ...@@ -200,6 +201,13 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi)
return SUN6I_MAX_XFER_SIZE - 1; return SUN6I_MAX_XFER_SIZE - 1;
} }
static void sun6i_spi_dma_rx_cb(void *param)
{
struct sun6i_spi *sspi = param;
complete(&sspi->dma_rx_done);
}
static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi, static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi,
struct spi_transfer *tfr) struct spi_transfer *tfr)
{ {
...@@ -211,7 +219,7 @@ static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi, ...@@ -211,7 +219,7 @@ static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi,
struct dma_slave_config rxconf = { struct dma_slave_config rxconf = {
.direction = DMA_DEV_TO_MEM, .direction = DMA_DEV_TO_MEM,
.src_addr = sspi->dma_addr_rx, .src_addr = sspi->dma_addr_rx,
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.src_maxburst = 8, .src_maxburst = 8,
}; };
...@@ -224,6 +232,8 @@ static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi, ...@@ -224,6 +232,8 @@ static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi,
DMA_PREP_INTERRUPT); DMA_PREP_INTERRUPT);
if (!rxdesc) if (!rxdesc)
return -EINVAL; return -EINVAL;
rxdesc->callback_param = sspi;
rxdesc->callback = sun6i_spi_dma_rx_cb;
} }
txdesc = NULL; txdesc = NULL;
...@@ -279,6 +289,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -279,6 +289,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
return -EINVAL; return -EINVAL;
reinit_completion(&sspi->done); reinit_completion(&sspi->done);
reinit_completion(&sspi->dma_rx_done);
sspi->tx_buf = tfr->tx_buf; sspi->tx_buf = tfr->tx_buf;
sspi->rx_buf = tfr->rx_buf; sspi->rx_buf = tfr->rx_buf;
sspi->len = tfr->len; sspi->len = tfr->len;
...@@ -479,6 +490,22 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -479,6 +490,22 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
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));
if (!use_dma) {
sun6i_spi_drain_fifo(sspi);
} else {
if (timeout && rx_len) {
/*
* Even though RX on the peripheral side has finished
* RX DMA might still be in flight
*/
timeout = wait_for_completion_timeout(&sspi->dma_rx_done,
timeout);
if (!timeout)
dev_warn(&master->dev, "RX DMA timeout\n");
}
}
end = jiffies; end = jiffies;
if (!timeout) { if (!timeout) {
dev_warn(&master->dev, dev_warn(&master->dev,
...@@ -506,7 +533,6 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) ...@@ -506,7 +533,6 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
/* Transfer complete */ /* Transfer complete */
if (status & SUN6I_INT_CTL_TC) { if (status & SUN6I_INT_CTL_TC) {
sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
sun6i_spi_drain_fifo(sspi);
complete(&sspi->done); complete(&sspi->done);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -665,6 +691,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) ...@@ -665,6 +691,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
} }
init_completion(&sspi->done); init_completion(&sspi->done);
init_completion(&sspi->dma_rx_done);
sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(sspi->rstc)) { if (IS_ERR(sspi->rstc)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment