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

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
Loading
Loading
Loading
Loading
+29 −2
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ struct sun6i_spi {
	struct reset_control	*rstc;

	struct completion	done;
	struct completion	dma_rx_done;

	const u8		*tx_buf;
	u8			*rx_buf;
@@ -200,6 +201,13 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi)
	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,
				 struct spi_transfer *tfr)
{
@@ -211,7 +219,7 @@ static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi,
		struct dma_slave_config rxconf = {
			.direction = DMA_DEV_TO_MEM,
			.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,
		};

@@ -224,6 +232,8 @@ static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi,
						 DMA_PREP_INTERRUPT);
		if (!rxdesc)
			return -EINVAL;
		rxdesc->callback_param = sspi;
		rxdesc->callback = sun6i_spi_dma_rx_cb;
	}

	txdesc = NULL;
@@ -279,6 +289,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
		return -EINVAL;

	reinit_completion(&sspi->done);
	reinit_completion(&sspi->dma_rx_done);
	sspi->tx_buf = tfr->tx_buf;
	sspi->rx_buf = tfr->rx_buf;
	sspi->len = tfr->len;
@@ -479,6 +490,22 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
	start = jiffies;
	timeout = wait_for_completion_timeout(&sspi->done,
					      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;
	if (!timeout) {
		dev_warn(&master->dev,
@@ -506,7 +533,6 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
	/* Transfer complete */
	if (status & SUN6I_INT_CTL_TC) {
		sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
		sun6i_spi_drain_fifo(sspi);
		complete(&sspi->done);
		return IRQ_HANDLED;
	}
@@ -665,6 +691,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
	}

	init_completion(&sspi->done);
	init_completion(&sspi->dma_rx_done);

	sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
	if (IS_ERR(sspi->rstc)) {