Skip to content

Commit bd7e984

Browse files
outman119broonie
authored andcommitted
spi: atmel: fix DMA channel and bounce buffer leaks
The original code set use_dma to false when dma_alloc_coherent() for bounce buffers failed, but DMA channels acquired earlier via atmel_spi_configure_dma() were never freed. When devm_request_irq() or clk_prepare_enable() failed later in probe, the driver also did not release DMA channels or bounce buffers already allocated. The out_free_dma error path released DMA channels but did not free the bounce buffers. Fix by moving bounce buffer allocation into atmel_spi_configure_dma() and registering the devres cleanup for DMA channels and bounce buffers. Fixes: a9889ed ("spi: atmel: Implements transfers with bounce buffer") Signed-off-by: Felix Gu <ustc.gu@gmail.com> Link: https://patch.msgid.link/20260522-atmel-v3-1-23f8c6e6aa43@gmail.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 8205695 commit bd7e984

1 file changed

Lines changed: 68 additions & 65 deletions

File tree

drivers/spi/spi-atmel.c

Lines changed: 68 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,38 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, u8 bits_per_word)
559559
return err;
560560
}
561561

562+
static void atmel_spi_release_dma(void *data)
563+
{
564+
struct spi_controller *host = data;
565+
struct atmel_spi *as = spi_controller_get_devdata(host);
566+
struct device *dev = &as->pdev->dev;
567+
568+
if (host->dma_tx) {
569+
dma_release_channel(host->dma_tx);
570+
host->dma_tx = NULL;
571+
}
572+
573+
if (host->dma_rx) {
574+
dma_release_channel(host->dma_rx);
575+
host->dma_rx = NULL;
576+
}
577+
578+
if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
579+
if (as->addr_tx_bbuf) {
580+
dma_free_coherent(dev, SPI_MAX_DMA_XFER,
581+
as->addr_tx_bbuf,
582+
as->dma_addr_tx_bbuf);
583+
as->addr_tx_bbuf = NULL;
584+
}
585+
if (as->addr_rx_bbuf) {
586+
dma_free_coherent(dev, SPI_MAX_DMA_XFER,
587+
as->addr_rx_bbuf,
588+
as->dma_addr_rx_bbuf);
589+
as->addr_rx_bbuf = NULL;
590+
}
591+
}
592+
}
593+
562594
static int atmel_spi_configure_dma(struct spi_controller *host,
563595
struct atmel_spi *as)
564596
{
@@ -569,7 +601,8 @@ static int atmel_spi_configure_dma(struct spi_controller *host,
569601
if (IS_ERR(host->dma_tx)) {
570602
err = PTR_ERR(host->dma_tx);
571603
dev_dbg(dev, "No TX DMA channel, DMA is disabled\n");
572-
goto error_clear;
604+
host->dma_tx = NULL;
605+
return err;
573606
}
574607

575608
host->dma_rx = dma_request_chan(dev, "rx");
@@ -580,26 +613,45 @@ static int atmel_spi_configure_dma(struct spi_controller *host,
580613
* requested tx channel.
581614
*/
582615
dev_dbg(dev, "No RX DMA channel, DMA is disabled\n");
583-
goto error;
616+
host->dma_rx = NULL;
617+
goto err_release_dma;
584618
}
585619

586620
err = atmel_spi_dma_slave_config(as, 8);
587621
if (err)
588-
goto error;
622+
goto err_release_dma;
623+
624+
if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
625+
as->addr_tx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER,
626+
&as->dma_addr_tx_bbuf,
627+
GFP_KERNEL | GFP_DMA);
628+
if (!as->addr_tx_bbuf) {
629+
err = -ENOMEM;
630+
goto err_release_dma;
631+
}
632+
633+
as->addr_rx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER,
634+
&as->dma_addr_rx_bbuf,
635+
GFP_KERNEL | GFP_DMA);
636+
if (!as->addr_rx_bbuf) {
637+
err = -ENOMEM;
638+
goto err_release_dma;
639+
}
640+
}
641+
642+
err = devm_add_action_or_reset(dev, atmel_spi_release_dma, host);
643+
if (err)
644+
return err;
589645

590646
dev_info(&as->pdev->dev,
591-
"Using %s (tx) and %s (rx) for DMA transfers\n",
592-
dma_chan_name(host->dma_tx),
593-
dma_chan_name(host->dma_rx));
647+
"Using %s (tx) and %s (rx) for DMA transfers\n",
648+
dma_chan_name(host->dma_tx), dma_chan_name(host->dma_rx));
594649

595650
return 0;
596-
error:
597-
if (!IS_ERR(host->dma_rx))
598-
dma_release_channel(host->dma_rx);
599-
if (!IS_ERR(host->dma_tx))
600-
dma_release_channel(host->dma_tx);
601-
error_clear:
602-
host->dma_tx = host->dma_rx = NULL;
651+
652+
err_release_dma:
653+
atmel_spi_release_dma(host);
654+
603655
return err;
604656
}
605657

@@ -611,18 +663,6 @@ static void atmel_spi_stop_dma(struct spi_controller *host)
611663
dmaengine_terminate_all(host->dma_tx);
612664
}
613665

614-
static void atmel_spi_release_dma(struct spi_controller *host)
615-
{
616-
if (host->dma_rx) {
617-
dma_release_channel(host->dma_rx);
618-
host->dma_rx = NULL;
619-
}
620-
if (host->dma_tx) {
621-
dma_release_channel(host->dma_tx);
622-
host->dma_tx = NULL;
623-
}
624-
}
625-
626666
/* This function is called by the DMA driver from tasklet context */
627667
static void dma_callback(void *data)
628668
{
@@ -1581,30 +1621,6 @@ static int atmel_spi_probe(struct platform_device *pdev)
15811621
as->use_pdc = true;
15821622
}
15831623

1584-
if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
1585-
as->addr_rx_bbuf = dma_alloc_coherent(&pdev->dev,
1586-
SPI_MAX_DMA_XFER,
1587-
&as->dma_addr_rx_bbuf,
1588-
GFP_KERNEL | GFP_DMA);
1589-
if (!as->addr_rx_bbuf) {
1590-
as->use_dma = false;
1591-
} else {
1592-
as->addr_tx_bbuf = dma_alloc_coherent(&pdev->dev,
1593-
SPI_MAX_DMA_XFER,
1594-
&as->dma_addr_tx_bbuf,
1595-
GFP_KERNEL | GFP_DMA);
1596-
if (!as->addr_tx_bbuf) {
1597-
as->use_dma = false;
1598-
dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER,
1599-
as->addr_rx_bbuf,
1600-
as->dma_addr_rx_bbuf);
1601-
}
1602-
}
1603-
if (!as->use_dma)
1604-
dev_info(host->dev.parent,
1605-
" can not allocate dma coherent memory\n");
1606-
}
1607-
16081624
if (as->caps.has_dma_support && !as->use_dma)
16091625
dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n");
16101626

@@ -1664,13 +1680,10 @@ static int atmel_spi_probe(struct platform_device *pdev)
16641680
out_free_dma:
16651681
pm_runtime_disable(&pdev->dev);
16661682
pm_runtime_set_suspended(&pdev->dev);
1667-
1668-
if (as->use_dma)
1669-
atmel_spi_release_dma(host);
1670-
16711683
spi_writel(as, CR, SPI_BIT(SWRST));
16721684
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
1673-
clk_disable_unprepare(as->gclk);
1685+
if (as->gclk)
1686+
clk_disable_unprepare(as->gclk);
16741687
out_disable_clk:
16751688
clk_disable_unprepare(clk);
16761689

@@ -1687,18 +1700,8 @@ static void atmel_spi_remove(struct platform_device *pdev)
16871700
spi_unregister_controller(host);
16881701

16891702
/* reset the hardware and block queue progress */
1690-
if (as->use_dma) {
1703+
if (as->use_dma)
16911704
atmel_spi_stop_dma(host);
1692-
atmel_spi_release_dma(host);
1693-
if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) {
1694-
dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER,
1695-
as->addr_tx_bbuf,
1696-
as->dma_addr_tx_bbuf);
1697-
dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER,
1698-
as->addr_rx_bbuf,
1699-
as->dma_addr_rx_bbuf);
1700-
}
1701-
}
17021705

17031706
spin_lock_irq(&as->lock);
17041707
spi_writel(as, CR, SPI_BIT(SWRST));

0 commit comments

Comments
 (0)