diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-06 14:54:39 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-06 14:54:39 +0100 |
| commit | cb8eaf50a764c815db5432ee3593f26f6a238762 (patch) | |
| tree | e27fb754270d07dc3957b939be1b3b139c98c63d | |
| parent | cd7878c68fccf35d47c03e0b6d580df120e983ca (diff) | |
| parent | 0d3e3376b289cdaff5b3b6c1581999926ff1000f (diff) | |
| download | linux-next-cb8eaf50a764c815db5432ee3593f26f6a238762.tar.gz linux-next-cb8eaf50a764c815db5432ee3593f26f6a238762.zip | |
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git
| -rw-r--r-- | Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml | 1 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/dma/xilinx/xlnx,axi-dma.yaml | 13 | ||||
| -rw-r--r-- | Documentation/driver-api/dmaengine/client.rst | 9 | ||||
| -rw-r--r-- | drivers/crypto/atmel-aes.c | 10 | ||||
| -rw-r--r-- | drivers/dma/dmaengine.c | 2 | ||||
| -rw-r--r-- | drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 | ||||
| -rw-r--r-- | drivers/dma/dw-edma/dw-edma-core.c | 41 | ||||
| -rw-r--r-- | drivers/dma/hisi_dma.c | 2 | ||||
| -rw-r--r-- | drivers/dma/mediatek/mtk-uart-apdma.c | 2 | ||||
| -rw-r--r-- | drivers/dma/pl330.c | 19 | ||||
| -rw-r--r-- | drivers/dma/tegra210-adma.c | 12 | ||||
| -rw-r--r-- | drivers/dma/xilinx/xilinx_dma.c | 64 | ||||
| -rw-r--r-- | drivers/dma/xilinx/zynqmp_dma.c | 6 | ||||
| -rw-r--r-- | drivers/nvme/target/pci-epf.c | 33 | ||||
| -rw-r--r-- | drivers/pci/endpoint/functions/pci-epf-mhi.c | 52 | ||||
| -rw-r--r-- | drivers/pci/endpoint/functions/pci-epf-test.c | 8 | ||||
| -rw-r--r-- | include/linux/dmaengine.h | 149 |
17 files changed, 282 insertions, 143 deletions
diff --git a/Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml b/Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml index 3708518fe7fc..bba20e88a6dc 100644 --- a/Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml +++ b/Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml @@ -40,6 +40,7 @@ properties: - items: - enum: - mediatek,mt6991-uart-dma + - mediatek,mt8189-uart-dma - mediatek,mt8196-uart-dma - const: mediatek,mt6985-uart-dma - enum: diff --git a/Documentation/devicetree/bindings/dma/xilinx/xlnx,axi-dma.yaml b/Documentation/devicetree/bindings/dma/xilinx/xlnx,axi-dma.yaml index 340ae9e91cb0..2c88e927d9d3 100644 --- a/Documentation/devicetree/bindings/dma/xilinx/xlnx,axi-dma.yaml +++ b/Documentation/devicetree/bindings/dma/xilinx/xlnx,axi-dma.yaml @@ -81,8 +81,13 @@ properties: description: Should be the number of framebuffers as configured in h/w. xlnx,flush-fsync: - type: boolean - description: Tells which channel to Flush on Frame sync. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2, 3] + description: + Tells which channel to flush on frame sync. + 1 - flush both channels + 2 - flush mm2s channel + 3 - flush s2mm channel xlnx,sg-length-width: $ref: /schemas/types.yaml#/definitions/uint32 @@ -93,7 +98,7 @@ properties: Width in bits of the length register as configured in hardware. xlnx,irq-delay: - $ref: /schemas/types.yaml#/definitions/uint32 + $ref: /schemas/types.yaml#/definitions/uint8 minimum: 0 maximum: 255 description: @@ -251,7 +256,7 @@ examples: "m_axi_s2mm_aclk", "m_axis_mm2s_aclk", "s_axis_s2mm_aclk"; xlnx,num-fstores = <8>; - xlnx,flush-fsync; + xlnx,flush-fsync = <1>; xlnx,addrwidth = <32>; dma-channel-mm2s { diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst index d491e385d61a..5ee5d4a3596d 100644 --- a/Documentation/driver-api/dmaengine/client.rst +++ b/Documentation/driver-api/dmaengine/client.rst @@ -80,6 +80,10 @@ The details of these operations are: - slave_sg: DMA a list of scatter gather buffers from/to a peripheral + - config_sg: Similar with slave_sg, just pass down dma_slave_config + struct to avoid calling dmaengine_slave_config() every time adjusting the + burst length or the FIFO address is needed. + - peripheral_dma_vec: DMA an array of scatter gather buffers from/to a peripheral. Similar to slave_sg, but uses an array of dma_vec structures instead of a scatterlist. @@ -106,6 +110,11 @@ The details of these operations are: unsigned int sg_len, enum dma_data_direction direction, unsigned long flags); + struct dma_async_tx_descriptor *dmaengine_prep_config_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long flags, struct dma_slave_config *config); + struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec( struct dma_chan *chan, const struct dma_vec *vecs, size_t nents, enum dma_data_direction direction, diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c index f79d7c456546..30acfd937d54 100644 --- a/drivers/crypto/atmel-aes.c +++ b/drivers/crypto/atmel-aes.c @@ -794,7 +794,6 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd, struct dma_slave_config config; dma_async_tx_callback callback; struct atmel_aes_dma *dma; - int err; memset(&config, 0, sizeof(config)); config.src_addr_width = addr_width; @@ -819,12 +818,9 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd, return -EINVAL; } - err = dmaengine_slave_config(dma->chan, &config); - if (err) - return err; - - desc = dmaengine_prep_slave_sg(dma->chan, dma->sg, dma->sg_len, dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + desc = dmaengine_prep_config_sg(dma->chan, dma->sg, dma->sg_len, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK, + &config); if (!desc) return -ENOMEM; diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 9049171df857..23e3bb18c166 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -1100,6 +1100,8 @@ static int __dma_async_device_channel_register(struct dma_device *device, chan->dev->device.parent = device->dev; chan->dev->chan = chan; chan->dev->dev_id = device->dev_id; + spin_lock_init(&chan->lock); + if (!name) dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id); else diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index bcefaff03b5c..eebed2474210 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -1126,7 +1126,7 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) hw_desc = &desc->hw_desc[i]; if (hw_desc->llp == llp) { axi_chan_irq_clear(chan, hw_desc->lli->status_lo); - hw_desc->lli->ctl_hi |= CH_CTL_H_LLI_VALID; + hw_desc->lli->ctl_hi |= cpu_to_le32(CH_CTL_H_LLI_VALID); desc->completed_blocks = i; if (((hw_desc->len * (i + 1)) % desc->period_len) == 0) diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index 89a4c498a17b..1fec1b52e3d4 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -267,6 +267,20 @@ static int dw_edma_device_config(struct dma_chan *dchan, return 0; } +static struct dma_slave_config * +dw_edma_device_get_config(struct dma_chan *dchan, + struct dma_slave_config *config) +{ + struct dw_edma_chan *chan; + + if (config) + return config; + + chan = dchan2dw_edma_chan(dchan); + + return &chan->config; +} + static int dw_edma_device_pause(struct dma_chan *dchan) { struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan); @@ -385,7 +399,8 @@ ret_residue: } static struct dma_async_tx_descriptor * -dw_edma_device_transfer(struct dw_edma_transfer *xfer) +dw_edma_device_transfer(struct dw_edma_transfer *xfer, + struct dma_slave_config *config) { struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan); enum dma_transfer_direction dir = xfer->direction; @@ -472,8 +487,8 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer) src_addr = xfer->xfer.il->src_start; dst_addr = xfer->xfer.il->dst_start; } else { - src_addr = chan->config.src_addr; - dst_addr = chan->config.dst_addr; + src_addr = config->src_addr; + dst_addr = config->dst_addr; } if (dir == DMA_DEV_TO_MEM) @@ -577,10 +592,11 @@ err_alloc: } static struct dma_async_tx_descriptor * -dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, - unsigned int len, - enum dma_transfer_direction direction, - unsigned long flags, void *context) +dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl, + unsigned int len, + enum dma_transfer_direction direction, + unsigned long flags, + struct dma_slave_config *config) { struct dw_edma_transfer xfer; @@ -591,7 +607,10 @@ dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, xfer.flags = flags; xfer.type = EDMA_XFER_SCATTER_GATHER; - return dw_edma_device_transfer(&xfer); + if (config && dw_edma_device_config(dchan, config)) + return NULL; + + return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, config)); } static struct dma_async_tx_descriptor * @@ -610,7 +629,7 @@ dw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr, xfer.flags = flags; xfer.type = EDMA_XFER_CYCLIC; - return dw_edma_device_transfer(&xfer); + return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL)); } static struct dma_async_tx_descriptor * @@ -626,7 +645,7 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan, xfer.flags = flags; xfer.type = EDMA_XFER_INTERLEAVED; - return dw_edma_device_transfer(&xfer); + return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL)); } static void dw_hdma_set_callback_result(struct virt_dma_desc *vd, @@ -970,7 +989,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) dma->device_terminate_all = dw_edma_device_terminate_all; dma->device_issue_pending = dw_edma_device_issue_pending; dma->device_tx_status = dw_edma_device_tx_status; - dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg; + dma->device_prep_config_sg = dw_edma_device_prep_config_sg; dma->device_prep_dma_cyclic = dw_edma_device_prep_dma_cyclic; dma->device_prep_interleaved_dma = dw_edma_device_prep_interleaved_dma; diff --git a/drivers/dma/hisi_dma.c b/drivers/dma/hisi_dma.c index 28bf818f9aa6..c751a2e49e6d 100644 --- a/drivers/dma/hisi_dma.c +++ b/drivers/dma/hisi_dma.c @@ -983,7 +983,7 @@ static int hisi_dma_probe(struct pci_dev *pdev, const struct pci_device_id *id) hdma_dev = devm_kzalloc(dev, struct_size(hdma_dev, chan, chan_num), GFP_KERNEL); if (!hdma_dev) - return -EINVAL; + return -ENOMEM; hdma_dev->base = pcim_iomap_table(pdev)[PCI_BAR_2]; hdma_dev->pdev = pdev; diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c index c269d84d7bd2..f74e9a328588 100644 --- a/drivers/dma/mediatek/mtk-uart-apdma.c +++ b/drivers/dma/mediatek/mtk-uart-apdma.c @@ -531,7 +531,7 @@ static int mtk_uart_apdma_probe(struct platform_device *pdev) for (i = 0; i < mtkd->dma_requests; i++) { c = devm_kzalloc(mtkd->ddev.dev, sizeof(*c), GFP_KERNEL); if (!c) { - rc = -ENODEV; + rc = -ENOMEM; goto err_no_dma; } diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 25ba84b18704..6214d9000db8 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -502,6 +502,7 @@ struct pl330_dmac { struct dma_pl330_chan *peripherals; /* keep at end */ int quirks; + struct dentry *dbgfs; struct reset_control *rstc; struct reset_control *rstc_ocp; }; @@ -2952,14 +2953,24 @@ DEFINE_SHOW_ATTRIBUTE(pl330_debugfs); static inline void init_pl330_debugfs(struct pl330_dmac *pl330) { - debugfs_create_file(dev_name(pl330->ddma.dev), - S_IFREG | 0444, NULL, pl330, - &pl330_debugfs_fops); + pl330->dbgfs = debugfs_create_file(dev_name(pl330->ddma.dev), + S_IFREG | 0444, NULL, pl330, + &pl330_debugfs_fops); +} + +static inline void deinit_pl330_debugfs(struct pl330_dmac *pl330) +{ + debugfs_remove(pl330->dbgfs); + pl330->dbgfs = NULL; } #else static inline void init_pl330_debugfs(struct pl330_dmac *pl330) { } + +static inline void deinit_pl330_debugfs(struct pl330_dmac *pl330) +{ +} #endif /* @@ -3204,6 +3215,8 @@ static void pl330_remove(struct amba_device *adev) struct dma_pl330_chan *pch, *_p; int i, irq; + deinit_pl330_debugfs(pl330); + pm_runtime_get_noresume(pl330->ddma.dev); if (adev->dev.of_node) diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index ceaee1e33e68..21a381d022cf 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -1087,15 +1087,9 @@ static int tegra_adma_probe(struct platform_device *pdev) } } else { /* If no 'page' property found, then reg DT binding would be legacy */ - res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res_base) { - tdma->base_addr = devm_ioremap_resource(&pdev->dev, res_base); - if (IS_ERR(tdma->base_addr)) - return PTR_ERR(tdma->base_addr); - } else { - return dev_err_probe(&pdev->dev, -ENODEV, - "failed to get memory resource\n"); - } + tdma->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(tdma->base_addr)) + return PTR_ERR(tdma->base_addr); tdma->ch_base_addr = tdma->base_addr + cdata->ch_base_offset; } diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index 404235c17353..98b41b8f8915 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -165,8 +165,10 @@ #define XILINX_DMA_FLUSH_MM2S 2 #define XILINX_DMA_FLUSH_BOTH 1 -/* Delay loop counter to prevent hardware failure */ -#define XILINX_DMA_LOOP_COUNT 1000000 +/* Timeout for polling various registers */ +#define XILINX_DMA_POLL_TIMEOUT_US 1000000 +/* Delay between polls (avoid a delay of 0 to prevent CPU stalls) */ +#define XILINX_DMA_POLL_DELAY_US 10 /* AXI DMA Specific Registers/Offsets */ #define XILINX_DMA_REG_SRCDSTADDR 0x18 @@ -1324,8 +1326,9 @@ static int xilinx_dma_stop_transfer(struct xilinx_dma_chan *chan) /* Wait for the hardware to halt */ return xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, - val & XILINX_DMA_DMASR_HALTED, 0, - XILINX_DMA_LOOP_COUNT); + val & XILINX_DMA_DMASR_HALTED, + XILINX_DMA_POLL_DELAY_US, + XILINX_DMA_POLL_TIMEOUT_US); } /** @@ -1339,8 +1342,9 @@ static int xilinx_cdma_stop_transfer(struct xilinx_dma_chan *chan) u32 val; return xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, - val & XILINX_DMA_DMASR_IDLE, 0, - XILINX_DMA_LOOP_COUNT); + val & XILINX_DMA_DMASR_IDLE, + XILINX_DMA_POLL_DELAY_US, + XILINX_DMA_POLL_TIMEOUT_US); } /** @@ -1356,8 +1360,9 @@ static void xilinx_dma_start(struct xilinx_dma_chan *chan) /* Wait for the hardware to start */ err = xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMASR, val, - !(val & XILINX_DMA_DMASR_HALTED), 0, - XILINX_DMA_LOOP_COUNT); + !(val & XILINX_DMA_DMASR_HALTED), + XILINX_DMA_POLL_DELAY_US, + XILINX_DMA_POLL_TIMEOUT_US); if (err) { dev_err(chan->dev, "Cannot start channel %p: %x\n", @@ -1580,7 +1585,14 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) return; } - if (!chan->idle) + /* + * Direct (non-SG) mode has no descriptor queue: writing the BTT + * register launches a transfer immediately, so a new transfer must + * not be programmed while one is in flight. Keep such transfers + * serialized. SG mode supports chaining onto a running transfer via + * tail-pointer extension, so it is allowed to proceed when busy. + */ + if (!chan->has_sg && !chan->idle) return; head_desc = list_first_entry(&chan->pending_list, @@ -1596,10 +1608,9 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) reg &= ~XILINX_DMA_CR_COALESCE_MAX; reg |= chan->desc_pendingcount << XILINX_DMA_CR_COALESCE_SHIFT; - dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg); } - if (chan->has_sg) + if (chan->has_sg && list_empty(&chan->active_list)) xilinx_write(chan, XILINX_DMA_REG_CURDESC, head_desc->async_tx.phys); reg &= ~XILINX_DMA_CR_DELAY_MAX; @@ -1607,7 +1618,8 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) reg |= XILINX_DMA_DMAXR_ALL_IRQ_MASK; dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg); - xilinx_dma_start(chan); + if (chan->idle) + xilinx_dma_start(chan); if (chan->err) return; @@ -1660,9 +1672,6 @@ static void xilinx_mcdma_start_transfer(struct xilinx_dma_chan *chan) if (chan->err) return; - if (!chan->idle) - return; - if (list_empty(&chan->pending_list)) return; @@ -1685,8 +1694,9 @@ static void xilinx_mcdma_start_transfer(struct xilinx_dma_chan *chan) dma_ctrl_write(chan, XILINX_MCDMA_CHAN_CR_OFFSET(chan->tdest), reg); /* Program current descriptor */ - xilinx_write(chan, XILINX_MCDMA_CHAN_CDESC_OFFSET(chan->tdest), - head_desc->async_tx.phys); + if (chan->has_sg && list_empty(&chan->active_list)) + xilinx_write(chan, XILINX_MCDMA_CHAN_CDESC_OFFSET(chan->tdest), + head_desc->async_tx.phys); /* Program channel enable register */ reg = dma_ctrl_read(chan, XILINX_MCDMA_CHEN_OFFSET); @@ -1698,7 +1708,8 @@ static void xilinx_mcdma_start_transfer(struct xilinx_dma_chan *chan) reg |= XILINX_MCDMA_CR_RUNSTOP_MASK; dma_ctrl_write(chan, XILINX_MCDMA_CHAN_CR_OFFSET(chan->tdest), reg); - xilinx_dma_start(chan); + if (chan->idle) + xilinx_dma_start(chan); if (chan->err) return; @@ -1794,8 +1805,9 @@ static int xilinx_dma_reset(struct xilinx_dma_chan *chan) /* Wait for the hardware to finish reset */ err = xilinx_dma_poll_timeout(chan, XILINX_DMA_REG_DMACR, tmp, - !(tmp & XILINX_DMA_DMACR_RESET), 0, - XILINX_DMA_LOOP_COUNT); + !(tmp & XILINX_DMA_DMACR_RESET), + XILINX_DMA_POLL_DELAY_US, + XILINX_DMA_POLL_TIMEOUT_US); if (err) { dev_err(chan->dev, "reset timeout, cr %x, sr %x\n", @@ -1893,8 +1905,10 @@ static irqreturn_t xilinx_mcdma_irq_handler(int irq, void *data) if (status & XILINX_MCDMA_IRQ_IOC_MASK) { spin_lock(&chan->lock); xilinx_dma_complete_descriptor(chan); - chan->idle = true; - chan->start_transfer(chan); + if (list_empty(&chan->active_list)) { + chan->idle = true; + chan->start_transfer(chan); + } spin_unlock(&chan->lock); } @@ -1950,8 +1964,10 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data) XILINX_DMA_DMASR_DLY_CNT_IRQ)) { spin_lock(&chan->lock); xilinx_dma_complete_descriptor(chan); - chan->idle = true; - chan->start_transfer(chan); + if (list_empty(&chan->active_list)) { + chan->idle = true; + chan->start_transfer(chan); + } spin_unlock(&chan->lock); } diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c index f6a812e49ddc..2b03584102ac 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c @@ -1159,8 +1159,6 @@ err_disable_pm: /** * zynqmp_dma_remove - Driver remove function * @pdev: Pointer to the platform_device structure - * - * Return: Always '0' */ static void zynqmp_dma_remove(struct platform_device *pdev) { @@ -1170,9 +1168,9 @@ static void zynqmp_dma_remove(struct platform_device *pdev) dma_async_device_unregister(&zdev->common); zynqmp_dma_chan_remove(zdev->chan); - if (pm_runtime_active(zdev->dev)) - zynqmp_dma_runtime_suspend(zdev->dev); pm_runtime_disable(zdev->dev); + if (!pm_runtime_status_suspended(zdev->dev)) + zynqmp_dma_runtime_suspend(zdev->dev); } static const struct of_device_id zynqmp_dma_of_match[] = { diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c index 4e9db96ebfec..b1ba2d0bea6d 100644 --- a/drivers/nvme/target/pci-epf.c +++ b/drivers/nvme/target/pci-epf.c @@ -210,9 +210,7 @@ struct nvmet_pci_epf { bool dma_enabled; struct dma_chan *dma_tx_chan; - struct mutex dma_tx_lock; struct dma_chan *dma_rx_chan; - struct mutex dma_rx_lock; struct mutex mmio_lock; @@ -295,9 +293,6 @@ static void nvmet_pci_epf_init_dma(struct nvmet_pci_epf *nvme_epf) struct dma_chan *chan; dma_cap_mask_t mask; - mutex_init(&nvme_epf->dma_rx_lock); - mutex_init(&nvme_epf->dma_tx_lock); - dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -336,8 +331,6 @@ out_dma_no_tx: nvme_epf->dma_rx_chan = NULL; out_dma_no_rx: - mutex_destroy(&nvme_epf->dma_rx_lock); - mutex_destroy(&nvme_epf->dma_tx_lock); nvme_epf->dma_enabled = false; dev_info(&epf->dev, "DMA not supported, falling back to MMIO\n"); @@ -352,8 +345,6 @@ static void nvmet_pci_epf_deinit_dma(struct nvmet_pci_epf *nvme_epf) nvme_epf->dma_tx_chan = NULL; dma_release_channel(nvme_epf->dma_rx_chan); nvme_epf->dma_rx_chan = NULL; - mutex_destroy(&nvme_epf->dma_rx_lock); - mutex_destroy(&nvme_epf->dma_tx_lock); nvme_epf->dma_enabled = false; } @@ -368,18 +359,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf, struct dma_chan *chan; dma_cookie_t cookie; dma_addr_t dma_addr; - struct mutex *lock; int ret; switch (dir) { case DMA_FROM_DEVICE: - lock = &nvme_epf->dma_rx_lock; chan = nvme_epf->dma_rx_chan; sconf.direction = DMA_DEV_TO_MEM; sconf.src_addr = seg->pci_addr; break; case DMA_TO_DEVICE: - lock = &nvme_epf->dma_tx_lock; chan = nvme_epf->dma_tx_chan; sconf.direction = DMA_MEM_TO_DEV; sconf.dst_addr = seg->pci_addr; @@ -388,22 +376,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf, return -EINVAL; } - mutex_lock(lock); - dma_dev = dmaengine_get_dma_device(chan); dma_addr = dma_map_single(dma_dev, seg->buf, seg->length, dir); ret = dma_mapping_error(dma_dev, dma_addr); if (ret) - goto unlock; - - ret = dmaengine_slave_config(chan, &sconf); - if (ret) { - dev_err(dev, "Failed to configure DMA channel\n"); - goto unmap; - } + return ret; - desc = dmaengine_prep_slave_single(chan, dma_addr, seg->length, - sconf.direction, DMA_CTRL_ACK); + desc = dmaengine_prep_config_single_safe(chan, dma_addr, seg->length, + sconf.direction, + DMA_CTRL_ACK, &sconf); if (!desc) { dev_err(dev, "Failed to prepare DMA\n"); ret = -EIO; @@ -420,16 +401,12 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf, if (dma_sync_wait(chan, cookie) != DMA_COMPLETE) { dev_err(dev, "DMA transfer failed\n"); ret = -EIO; + dmaengine_terminate_sync(chan); } - dmaengine_terminate_sync(chan); - unmap: dma_unmap_single(dma_dev, dma_addr, seg->length, dir); -unlock: - mutex_unlock(lock); - return ret; } diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index 7f5326925ed5..c3e3b58fb86c 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -328,12 +328,6 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl, config.direction = DMA_DEV_TO_MEM; config.src_addr = buf_info->host_addr; - ret = dmaengine_slave_config(chan, &config); - if (ret) { - dev_err(dev, "Failed to configure DMA channel\n"); - goto err_unlock; - } - dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size, DMA_FROM_DEVICE); ret = dma_mapping_error(dma_dev, dst_addr); @@ -342,9 +336,10 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl, goto err_unlock; } - desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size, - DMA_DEV_TO_MEM, - DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size, + DMA_DEV_TO_MEM, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT, + &config); if (!desc) { dev_err(dev, "Failed to prepare DMA\n"); ret = -EIO; @@ -401,12 +396,6 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl, config.direction = DMA_MEM_TO_DEV; config.dst_addr = buf_info->host_addr; - ret = dmaengine_slave_config(chan, &config); - if (ret) { - dev_err(dev, "Failed to configure DMA channel\n"); - goto err_unlock; - } - src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size, DMA_TO_DEVICE); ret = dma_mapping_error(dma_dev, src_addr); @@ -415,9 +404,10 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl, goto err_unlock; } - desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size, - DMA_MEM_TO_DEV, - DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + desc = dmaengine_prep_config_single(chan, src_addr, buf_info->size, + DMA_MEM_TO_DEV, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT, + &config); if (!desc) { dev_err(dev, "Failed to prepare DMA\n"); ret = -EIO; @@ -506,12 +496,6 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl, config.direction = DMA_DEV_TO_MEM; config.src_addr = buf_info->host_addr; - ret = dmaengine_slave_config(chan, &config); - if (ret) { - dev_err(dev, "Failed to configure DMA channel\n"); - goto err_unlock; - } - dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size, DMA_FROM_DEVICE); ret = dma_mapping_error(dma_dev, dst_addr); @@ -520,9 +504,10 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl, goto err_unlock; } - desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size, - DMA_DEV_TO_MEM, - DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size, + DMA_DEV_TO_MEM, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT, + &config); if (!desc) { dev_err(dev, "Failed to prepare DMA\n"); ret = -EIO; @@ -585,12 +570,6 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl, config.direction = DMA_MEM_TO_DEV; config.dst_addr = buf_info->host_addr; - ret = dmaengine_slave_config(chan, &config); - if (ret) { - dev_err(dev, "Failed to configure DMA channel\n"); - goto err_unlock; - } - src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size, DMA_TO_DEVICE); ret = dma_mapping_error(dma_dev, src_addr); @@ -599,9 +578,10 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl, goto err_unlock; } - desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size, - DMA_MEM_TO_DEV, - DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + desc = dmaengine_prep_config_single(chan, src_addr, buf_info->size, + DMA_MEM_TO_DEV, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT, + &config); if (!desc) { dev_err(dev, "Failed to prepare DMA\n"); ret = -EIO; diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 4802d4f80f78..d4905aa8e4c0 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -183,12 +183,8 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, else sconf.src_addr = dma_remote; - if (dmaengine_slave_config(chan, &sconf)) { - dev_err(dev, "DMA slave config fail\n"); - return -EIO; - } - tx = dmaengine_prep_slave_single(chan, dma_local, len, dir, - flags); + tx = dmaengine_prep_config_single(chan, dma_local, len, + dir, flags, &sconf); } else { tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len, flags); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index b3d251c9734e..6fe46c0c9452 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -322,6 +322,8 @@ struct dma_router { * @slave: ptr to the device using this channel * @cookie: last cookie value returned to client * @completed_cookie: last completed cookie for this channel + * @lock: protect between config and prepare transfer when driver have not + * implemented callback device_prep_config_sg(). * @chan_id: channel ID for sysfs * @dev: class device for sysfs * @name: backlink name for sysfs @@ -341,6 +343,12 @@ struct dma_chan { dma_cookie_t cookie; dma_cookie_t completed_cookie; + /* + * protect between config and prepare transfer because *_prep() may be + * called from complete callback, which is in GFP_NOSLEEP context. + */ + spinlock_t lock; + /* sysfs */ int chan_id; struct dma_chan_dev *dev; @@ -835,6 +843,7 @@ struct dma_filter { * where the address and size of each segment is located in one entry of * the dma_vec array. * @device_prep_slave_sg: prepares a slave dma operation + * @device_prep_config_sg: prepares a slave DMA operation with dma_slave_config * @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio. * The function takes a buffer of size buf_len. The callback function will * be called after period_len bytes have been transferred. @@ -934,6 +943,10 @@ struct dma_device { struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags, void *context); + struct dma_async_tx_descriptor *(*device_prep_config_sg)( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, struct dma_slave_config *config); struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, @@ -974,22 +987,44 @@ static inline bool is_slave_direction(enum dma_transfer_direction direction) (direction == DMA_DEV_TO_DEV); } -static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( - struct dma_chan *chan, dma_addr_t buf, size_t len, - enum dma_transfer_direction dir, unsigned long flags) +static inline struct dma_async_tx_descriptor * +dmaengine_prep_config_single(struct dma_chan *chan, dma_addr_t buf, size_t len, + enum dma_transfer_direction dir, + unsigned long flags, + struct dma_slave_config *config) { struct scatterlist sg; + + if (!chan || !chan->device) + return NULL; + sg_init_table(&sg, 1); sg_dma_address(&sg) = buf; sg_dma_len(&sg) = len; - if (!chan || !chan->device || !chan->device->device_prep_slave_sg) + if (chan->device->device_prep_config_sg) + return chan->device->device_prep_config_sg(chan, &sg, 1, dir, + flags, config); + + if (config) + if (dmaengine_slave_config(chan, config)) + return NULL; + + if (!chan->device->device_prep_slave_sg) return NULL; return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags, NULL); } +static inline struct dma_async_tx_descriptor * +dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len, + enum dma_transfer_direction dir, + unsigned long flags) +{ + return dmaengine_prep_config_single(chan, buf, len, dir, flags, NULL); +} + /** * dmaengine_prep_peripheral_dma_vec() - Prepare a DMA scatter-gather descriptor * @chan: The channel to be used for this descriptor @@ -1010,17 +1045,115 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec( dir, flags); } -static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg( - struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction dir, unsigned long flags) +static inline struct dma_async_tx_descriptor * +dmaengine_prep_config_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long flags, struct dma_slave_config *config) { - if (!chan || !chan->device || !chan->device->device_prep_slave_sg) + if (!chan || !chan->device) + return NULL; + + if (chan->device->device_prep_config_sg) + return chan->device->device_prep_config_sg(chan, sgl, sg_len, + dir, flags, config); + + if (config) + if (dmaengine_slave_config(chan, config)) + return NULL; + + if (!chan->device->device_prep_slave_sg) return NULL; return chan->device->device_prep_slave_sg(chan, sgl, sg_len, dir, flags, NULL); } +static inline struct dma_async_tx_descriptor * +dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction dir, + unsigned long flags) +{ + return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL); +} + +/** + * dmaengine_prep_config_sg_safe - prepare a scatter-gather DMA transfer + * with atomic slave configuration update + * @chan: DMA channel + * @sgl: scatterlist for the transfer + * @sg_len: number of entries in @sgl + * @dir: DMA transfer direction + * @flags: transfer preparation flags + * @config: DMA slave configuration for this transfer + * + * Prepare a DMA scatter-gather transfer together with a corresponding slave + * configuration update in a re-entrant and race-safe manner. + * + * DMA engine drivers may implement the optional + * device_prep_config_sg() callback to perform both the slave configuration + * and descriptor preparation atomically. In this case, the operation is + * fully handled by the DMA engine driver. + * + * If the DMA engine driver does not implement device_prep_config_sg(), falls + * back to calling dmaengine_slave_config() followed by dmaengine_prep_slave_sg(). + * The fallback path is protected by a per-channel spinlock to ensure that + * concurrent callers cannot interleave configuration and descriptor preparation + * on the same DMA channel. + * + * Return: Pointer to a prepared DMA async transaction descriptor on success, + * or %NULL if the transfer could not be prepared. + */ +static inline struct dma_async_tx_descriptor * +dmaengine_prep_config_sg_safe(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, + enum dma_transfer_direction dir, + unsigned long flags, + struct dma_slave_config *config) +{ + struct dma_async_tx_descriptor *tx; + unsigned long spinlock_flags; + + if (!chan || !chan->device) + return NULL; + + if (!chan->device->device_prep_config_sg) + spin_lock_irqsave(&chan->lock, spinlock_flags); + + tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config); + + if (!chan->device->device_prep_config_sg) + spin_unlock_irqrestore(&chan->lock, spinlock_flags); + + return tx; +} + +/** + * dmaengine_prep_config_single_safe - prepare a single-buffer DMA transfer + * with atomic slave configuration update + * @chan: DMA channel + * @buf: DMA buffer address + * @len: length of the transfer in bytes + * @dir: DMA transfer direction + * @flags: transfer preparation flags + * @config: DMA slave configuration for this transfer + * + * Detail see dmaengine_prep_config_sg_safe(). + */ +static inline struct dma_async_tx_descriptor * +dmaengine_prep_config_single_safe(struct dma_chan *chan, dma_addr_t buf, + size_t len, enum dma_transfer_direction dir, + unsigned long flags, + struct dma_slave_config *config) +{ + struct scatterlist sg; + + sg_init_table(&sg, 1); + sg_dma_address(&sg) = buf; + sg_dma_len(&sg) = len; + + return dmaengine_prep_config_sg_safe(chan, &sg, 1, dir, flags, config); +} + #ifdef CONFIG_RAPIDIO_DMA_ENGINE struct rio_dma_ext; static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg( |
