[PATCH RFC 2/3 stable-6.1] spi: spi-atmel: use dma buffers for pdc transfer
Thomas Pfaff
tpfaff at pcs.com
Wed Nov 15 06:00:11 PST 2023
From: Thomas Pfaff <tpfaff at pcs.com>
pdc transfer is broken (at least on AT91SAM9G45), because dma_map_single
will always return false on vmalloc addresses.
This can be avoided by using the buffers for dma transfers for pdc too.
Signed-off-by: Thomas Pfaff <tpfaff at pcs.com>
---
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index fc566d22aa36..eaf4f9ff2504 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -267,6 +267,7 @@ struct atmel_spi {
dma_addr_t dma_addr_tx_bbuf;
void *addr_rx_bbuf;
void *addr_tx_bbuf;
+ void *addr_rx_buf;
struct completion xfer_completion;
@@ -906,42 +907,22 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master,
}
/*
- * For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma:
- * - The buffer is either valid for CPU access, else NULL
- * - If the buffer is valid, so is its DMA address
- *
- * This driver manages the dma address unless message->is_dma_mapped.
+ * Use the internal dma buffers for pdc transfers,
*/
-static int
+static void
atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer)
{
- struct device *dev = &as->pdev->dev;
-
xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
if (xfer->tx_buf) {
- /* tx_buf is a const void* where we need a void * for the dma
- * mapping */
- void *nonconst_tx = (void *)xfer->tx_buf;
-
- xfer->tx_dma = dma_map_single(dev,
- nonconst_tx, xfer->len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, xfer->tx_dma))
- return -ENOMEM;
+ memcpy(as->addr_tx_bbuf, xfer->tx_buf, xfer->len);
+ xfer->tx_dma = as->dma_addr_tx_bbuf;
+ xfer->tx_buf = as->addr_tx_bbuf;
}
if (xfer->rx_buf) {
- xfer->rx_dma = dma_map_single(dev,
- xfer->rx_buf, xfer->len,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, xfer->rx_dma)) {
- if (xfer->tx_buf)
- dma_unmap_single(dev,
- xfer->tx_dma, xfer->len,
- DMA_TO_DEVICE);
- return -ENOMEM;
- }
+ as->addr_rx_buf = xfer->rx_buf;
+ xfer->rx_dma = as->dma_addr_rx_bbuf;
+ xfer->rx_buf = as->addr_rx_bbuf;
}
- return 0;
}
static void atmel_spi_dma_unmap_xfer(struct spi_master *master,
@@ -1125,6 +1106,11 @@ atmel_spi_pdc_interrupt(int irq, void *dev_id)
spi_writel(as, IDR, pending);
+ if (as->addr_rx_buf) {
+ as->current_transfer->rx_buf = as->addr_rx_buf;
+ memcpy(as->current_transfer->rx_buf, as->addr_rx_bbuf,
+ as->current_transfer->len);
+ }
complete(&as->xfer_completion);
}
@@ -1296,10 +1282,10 @@ static int atmel_spi_one_transfer(struct spi_master *master,
* better fault reporting.
*/
if ((!master->cur_msg->is_dma_mapped)
- && as->use_pdc) {
- if (atmel_spi_dma_map_xfer(as, xfer) < 0)
- return -ENOMEM;
- }
+ && as->use_pdc)
+ atmel_spi_dma_map_xfer(as, xfer);
+ else
+ as->addr_rx_buf = NULL;
atmel_spi_set_xfer_speed(as, spi, xfer);
@@ -1512,28 +1498,25 @@ static int atmel_spi_probe(struct platform_device *pdev)
as->use_pdc = true;
}
- if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
- as->addr_rx_bbuf = dmam_alloc_coherent(&pdev->dev,
+ as->addr_rx_bbuf = dmam_alloc_coherent(&pdev->dev,
+ SPI_MAX_DMA_XFER,
+ &as->dma_addr_rx_bbuf,
+ GFP_KERNEL | GFP_DMA);
+ if (!as->addr_rx_bbuf) {
+ as->use_dma = false;
+ as->use_pdc = false;
+ } else {
+ as->addr_tx_bbuf = dmam_alloc_coherent(&pdev->dev,
SPI_MAX_DMA_XFER,
- &as->dma_addr_rx_bbuf,
+ &as->dma_addr_tx_bbuf,
GFP_KERNEL | GFP_DMA);
- if (!as->addr_rx_bbuf) {
+ if (!as->addr_tx_bbuf) {
as->use_dma = false;
- } else {
- as->addr_tx_bbuf = dmam_alloc_coherent(&pdev->dev,
- SPI_MAX_DMA_XFER,
- &as->dma_addr_tx_bbuf,
- GFP_KERNEL | GFP_DMA);
- if (!as->addr_tx_bbuf) {
- as->use_dma = false;
- dmam_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER,
- as->addr_rx_bbuf,
- as->dma_addr_rx_bbuf);
- }
+ as->use_pdc = false;
+ dmam_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER,
+ as->addr_rx_bbuf,
+ as->dma_addr_rx_bbuf);
}
- if (!as->use_dma)
- dev_info(master->dev.parent,
- " can not allocate dma coherent memory\n");
}
if (as->caps.has_dma_support && !as->use_dma)
More information about the linux-arm-kernel
mailing list