diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-17 09:55:43 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-17 09:55:43 -0700 |
commit | 47ebe00b684c2bc183a766bc33c8b5943bc0df85 (patch) | |
tree | c0f155acc5623f6990d20b7a623f48f5e7aa0f61 /drivers/dma | |
parent | fa121bb3fed6313b1f0af23952301e06cf6d32ed (diff) | |
parent | 5c274ca4cfb22a455e880f61536b1894fa29fd17 (diff) |
Merge tag 'dmaengine-5.3-rc1' of git://git.infradead.org/users/vkoul/slave-dma
Pull dmaengine updates from Vinod Koul:
- Add support in dmaengine core to do device node checks for DT devices
and update bunch of drivers to use that and remove open coding from
drivers
- New driver/driver support for new hardware, namely:
- MediaTek UART APDMA
- Freescale i.mx7ulp edma2
- Synopsys eDMA IP core version 0
- Allwinner H6 DMA
- Updates to axi-dma and support for interleaved cyclic transfers
- Greg's debugfs return value check removals on drivers
- Updates to stm32-dma, hsu, dw, pl330, tegra drivers
* tag 'dmaengine-5.3-rc1' of git://git.infradead.org/users/vkoul/slave-dma: (68 commits)
dmaengine: Revert "dmaengine: fsl-edma: add i.mx7ulp edma2 version support"
dmaengine: at_xdmac: check for non-empty xfers_list before invoking callback
Documentation: dmaengine: clean up description of dmatest usage
dmaengine: tegra210-adma: remove PM_CLK dependency
dmaengine: fsl-edma: add i.mx7ulp edma2 version support
dt-bindings: dma: fsl-edma: add new i.mx7ulp-edma
dmaengine: fsl-edma-common: version check for v2 instead
dmaengine: fsl-edma-common: move dmamux register to another single function
dmaengine: fsl-edma: add drvdata for fsl-edma
dmaengine: Revert "dmaengine: fsl-edma: support little endian for edma driver"
dmaengine: rcar-dmac: Reject zero-length slave DMA requests
dmaengine: dw: Enable iDMA 32-bit on Intel Elkhart Lake
dmaengine: dw-edma: fix semicolon.cocci warnings
dmaengine: sh: usb-dmac: Use [] to denote a flexible array member
dmaengine: dmatest: timeout value of -1 should specify infinite wait
dmaengine: dw: Distinguish ->remove() between DW and iDMA 32-bit
dmaengine: fsl-edma: support little endian for edma driver
dmaengine: hsu: Revert "set HSU_CH_MTSR to memory width"
dmagengine: pl330: add code to get reset property
dt-bindings: pl330: document the optional resets property
...
Diffstat (limited to 'drivers/dma')
51 files changed, 3468 insertions, 691 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 703275cc29de..03fa0c58cef3 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -103,6 +103,7 @@ config AXI_DMAC depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_SOCFPGA || COMPILE_TEST select DMA_ENGINE select DMA_VIRTUAL_CHANNELS + select REGMAP_MMIO help Enable support for the Analog Devices AXI-DMAC peripheral. This DMA controller is often used in Analog Device's reference designs for FPGA @@ -584,7 +585,7 @@ config TEGRA20_APB_DMA config TEGRA210_ADMA tristate "NVIDIA Tegra210 ADMA support" - depends on (ARCH_TEGRA_210_SOC || COMPILE_TEST) && PM_CLK + depends on (ARCH_TEGRA_210_SOC || COMPILE_TEST) select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help @@ -666,6 +667,8 @@ source "drivers/dma/qcom/Kconfig" source "drivers/dma/dw/Kconfig" +source "drivers/dma/dw-edma/Kconfig" + source "drivers/dma/hsu/Kconfig" source "drivers/dma/sh/Kconfig" diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 6126e1c3a875..5bddf6f8790f 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o obj-$(CONFIG_DW_AXI_DMAC) += dw-axi-dmac/ obj-$(CONFIG_DW_DMAC_CORE) += dw/ +obj-$(CONFIG_DW_EDMA) += dw-edma/ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 464725dcad00..9adc7a2fa3d3 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -2508,9 +2508,8 @@ DEFINE_SHOW_ATTRIBUTE(pl08x_debugfs); static void init_pl08x_debugfs(struct pl08x_driver_data *pl08x) { /* Expose a simple debugfs interface to view all clocks */ - (void) debugfs_create_file(dev_name(&pl08x->adev->dev), - S_IFREG | S_IRUGO, NULL, pl08x, - &pl08x_debugfs_fops); + debugfs_create_file(dev_name(&pl08x->adev->dev), S_IFREG | S_IRUGO, + NULL, pl08x, &pl08x_debugfs_fops); } #else diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 627ef3e5b312..b58ac720d9a1 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -1568,11 +1568,14 @@ static void at_xdmac_handle_cyclic(struct at_xdmac_chan *atchan) struct at_xdmac_desc *desc; struct dma_async_tx_descriptor *txd; - desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, xfer_node); - txd = &desc->tx_dma_desc; + if (!list_empty(&atchan->xfers_list)) { + desc = list_first_entry(&atchan->xfers_list, + struct at_xdmac_desc, xfer_node); + txd = &desc->tx_dma_desc; - if (txd->flags & DMA_PREP_INTERRUPT) - dmaengine_desc_get_callback_invoke(txd, NULL); + if (txd->flags & DMA_PREP_INTERRUPT) + dmaengine_desc_get_callback_invoke(txd, NULL); + } } static void at_xdmac_handle_error(struct at_xdmac_chan *atchan) diff --git a/drivers/dma/bcm-sba-raid.c b/drivers/dma/bcm-sba-raid.c index fa81d0177765..275e90fa829d 100644 --- a/drivers/dma/bcm-sba-raid.c +++ b/drivers/dma/bcm-sba-raid.c @@ -164,7 +164,6 @@ struct sba_device { struct list_head reqs_free_list; /* DebugFS directory entries */ struct dentry *root; - struct dentry *stats; }; /* ====== Command helper routines ===== */ @@ -1716,17 +1715,11 @@ static int sba_probe(struct platform_device *pdev) /* Create debugfs root entry */ sba->root = debugfs_create_dir(dev_name(sba->dev), NULL); - if (IS_ERR_OR_NULL(sba->root)) { - dev_err(sba->dev, "failed to create debugfs root entry\n"); - sba->root = NULL; - goto skip_debugfs; - } /* Create debugfs stats entry */ - sba->stats = debugfs_create_devm_seqfile(sba->dev, "stats", sba->root, - sba_debugfs_stats_show); - if (IS_ERR_OR_NULL(sba->stats)) - dev_err(sba->dev, "failed to create debugfs stats file\n"); + debugfs_create_devm_seqfile(sba->dev, "stats", sba->root, + sba_debugfs_stats_show); + skip_debugfs: /* Register DMA device with Linux async framework */ diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index 547786ac342b..e51d836afcc7 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1378,10 +1378,8 @@ static int __init init_coh901318_debugfs(void) dma_dentry = debugfs_create_dir("dma", NULL); - (void) debugfs_create_file("status", - S_IFREG | S_IRUGO, - dma_dentry, NULL, - &coh901318_debugfs_status_operations); + debugfs_create_file("status", S_IFREG | S_IRUGO, dma_dentry, NULL, + &coh901318_debugfs_status_operations); return 0; } diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 8a3f1043917b..a0ee404b736e 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -2,7 +2,7 @@ /* * Driver for the Analog Devices AXI-DMAC core * - * Copyright 2013-2015 Analog Devices Inc. + * Copyright 2013-2019 Analog Devices Inc. * Author: Lars-Peter Clausen <lars@metafoo.de> */ @@ -18,7 +18,9 @@ #include <linux/of.h> #include <linux/of_dma.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/slab.h> +#include <linux/fpga/adi-axi-common.h> #include <dt-bindings/dma/axi-dmac.h> @@ -62,6 +64,8 @@ #define AXI_DMAC_REG_STATUS 0x430 #define AXI_DMAC_REG_CURRENT_SRC_ADDR 0x434 #define AXI_DMAC_REG_CURRENT_DEST_ADDR 0x438 +#define AXI_DMAC_REG_PARTIAL_XFER_LEN 0x44c +#define AXI_DMAC_REG_PARTIAL_XFER_ID 0x450 #define AXI_DMAC_CTRL_ENABLE BIT(0) #define AXI_DMAC_CTRL_PAUSE BIT(1) @@ -70,6 +74,10 @@ #define AXI_DMAC_IRQ_EOT BIT(1) #define AXI_DMAC_FLAG_CYCLIC BIT(0) +#define AXI_DMAC_FLAG_LAST BIT(1) +#define AXI_DMAC_FLAG_PARTIAL_REPORT BIT(2) + +#define AXI_DMAC_FLAG_PARTIAL_XFER_DONE BIT(31) /* The maximum ID allocated by the hardware is 31 */ #define AXI_DMAC_SG_UNUSED 32U @@ -82,12 +90,14 @@ struct axi_dmac_sg { unsigned int dest_stride; unsigned int src_stride; unsigned int id; + unsigned int partial_len; bool schedule_when_free; }; struct axi_dmac_desc { struct virt_dma_desc vdesc; bool cyclic; + bool have_partial_xfer; unsigned int num_submitted; unsigned int num_completed; @@ -108,8 +118,10 @@ struct axi_dmac_chan { unsigned int dest_type; unsigned int max_length; - unsigned int align_mask; + unsigned int address_align_mask; + unsigned int length_align_mask; + bool hw_partial_xfer; bool hw_cyclic; bool hw_2d; }; @@ -167,14 +179,14 @@ static bool axi_dmac_check_len(struct axi_dmac_chan *chan, unsigned int len) { if (len == 0) return false; - if ((len & chan->align_mask) != 0) /* Not aligned */ + if ((len & chan->length_align_mask) != 0) /* Not aligned */ return false; return true; } static bool axi_dmac_check_addr(struct axi_dmac_chan *chan, dma_addr_t addr) { - if ((addr & chan->align_mask) != 0) /* Not aligned */ + if ((addr & chan->address_align_mask) != 0) /* Not aligned */ return false; return true; } @@ -210,11 +222,13 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) } desc->num_submitted++; - if (desc->num_submitted == desc->num_sgs) { + if (desc->num_submitted == desc->num_sgs || + desc->have_partial_xfer) { if (desc->cyclic) desc->num_submitted = 0; /* Start again */ else chan->next_desc = NULL; + flags |= AXI_DMAC_FLAG_LAST; } else { chan->next_desc = desc; } @@ -240,6 +254,9 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) desc->num_sgs == 1) flags |= AXI_DMAC_FLAG_CYCLIC; + if (chan->hw_partial_xfer) + flags |= AXI_DMAC_FLAG_PARTIAL_REPORT; + axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, sg->x_len - 1); axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, sg->y_len - 1); axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, flags); @@ -252,6 +269,83 @@ static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan) struct axi_dmac_desc, vdesc.node); } +static inline unsigned int axi_dmac_total_sg_bytes(struct axi_dmac_chan *chan, + struct axi_dmac_sg *sg) +{ + if (chan->hw_2d) + return sg->x_len * sg->y_len; + else + return sg->x_len; +} + +static void axi_dmac_dequeue_partial_xfers(struct axi_dmac_chan *chan) +{ + struct axi_dmac *dmac = chan_to_axi_dmac(chan); + struct axi_dmac_desc *desc; + struct axi_dmac_sg *sg; + u32 xfer_done, len, id, i; + bool found_sg; + + do { + len = axi_dmac_read(dmac, AXI_DMAC_REG_PARTIAL_XFER_LEN); + id = axi_dmac_read(dmac, AXI_DMAC_REG_PARTIAL_XFER_ID); + + found_sg = false; + list_for_each_entry(desc, &chan->active_descs, vdesc.node) { + for (i = 0; i < desc->num_sgs; i++) { + sg = &desc->sg[i]; + if (sg->id == AXI_DMAC_SG_UNUSED) + continue; + if (sg->id == id) { + desc->have_partial_xfer = true; + sg->partial_len = len; + found_sg = true; + break; + } + } + if (found_sg) + break; + } + + if (found_sg) { + dev_dbg(dmac->dma_dev.dev, + "Found partial segment id=%u, len=%u\n", + id, len); + } else { + dev_warn(dmac->dma_dev.dev, + "Not found partial segment id=%u, len=%u\n", + id, len); + } + + /* Check if we have any more partial transfers */ + xfer_done = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_DONE); + xfer_done = !(xfer_done & AXI_DMAC_FLAG_PARTIAL_XFER_DONE); + + } while (!xfer_done); +} + +static void axi_dmac_compute_residue(struct axi_dmac_chan *chan, + struct axi_dmac_desc *active) +{ + struct dmaengine_result *rslt = &active->vdesc.tx_result; + unsigned int start = active->num_completed - 1; + struct axi_dmac_sg *sg; + unsigned int i, total; + + rslt->result = DMA_TRANS_NOERROR; + rslt->residue = 0; + + /* + * We get here if the last completed segment is partial, which + * means we can compute the residue from that segment onwards + */ + for (i = start; i < active->num_sgs; i++) { + sg = &active->sg[i]; + total = axi_dmac_total_sg_bytes(chan, sg); + rslt->residue += (total - sg->partial_len); + } +} + static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, unsigned int completed_transfers) { @@ -263,6 +357,10 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, if (!active) return false; + if (chan->hw_partial_xfer && + (completed_transfers & AXI_DMAC_FLAG_PARTIAL_XFER_DONE)) + axi_dmac_dequeue_partial_xfers(chan); + do { sg = &active->sg[active->num_completed]; if (sg->id == AXI_DMAC_SG_UNUSED) /* Not yet submitted */ @@ -276,10 +374,14 @@ static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, start_next = true; } + if (sg->partial_len) + axi_dmac_compute_residue(chan, active); + if (active->cyclic) vchan_cyclic_callback(&active->vdesc); - if (active->num_completed == active->num_sgs) { + if (active->num_completed == active->num_sgs || + sg->partial_len) { if (active->cyclic) { active->num_completed = 0; /* wrap around */ } else { @@ -391,7 +493,7 @@ static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan, num_segments = DIV_ROUND_UP(period_len, chan->max_length); segment_size = DIV_ROUND_UP(period_len, num_segments); /* Take care of alignment */ - segment_size = ((segment_size - 1) | chan->align_mask) + 1; + segment_size = ((segment_size - 1) | chan->length_align_mask) + 1; for (i = 0; i < num_periods; i++) { len = period_len; @@ -561,6 +663,9 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_interleaved( desc->sg[0].y_len = 1; } + if (flags & DMA_CYCLIC) + desc->cyclic = true; + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); } @@ -574,6 +679,44 @@ static void axi_dmac_desc_free(struct virt_dma_desc *vdesc) kfree(container_of(vdesc, struct axi_dmac_desc, vdesc)); } +static bool axi_dmac_regmap_rdwr(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AXI_DMAC_REG_IRQ_MASK: + case AXI_DMAC_REG_IRQ_SOURCE: + case AXI_DMAC_REG_IRQ_PENDING: + case AXI_DMAC_REG_CTRL: + case AXI_DMAC_REG_TRANSFER_ID: + case AXI_DMAC_REG_START_TRANSFER: + case AXI_DMAC_REG_FLAGS: + case AXI_DMAC_REG_DEST_ADDRESS: + case AXI_DMAC_REG_SRC_ADDRESS: + case AXI_DMAC_REG_X_LENGTH: + case AXI_DMAC_REG_Y_LENGTH: + case AXI_DMAC_REG_DEST_STRIDE: + case AXI_DMAC_REG_SRC_STRIDE: + case AXI_DMAC_REG_TRANSFER_DONE: + case AXI_DMAC_REG_ACTIVE_TRANSFER_ID: + case AXI_DMAC_REG_STATUS: + case AXI_DMAC_REG_CURRENT_SRC_ADDR: + case AXI_DMAC_REG_CURRENT_DEST_ADDR: + case AXI_DMAC_REG_PARTIAL_XFER_LEN: + case AXI_DMAC_REG_PARTIAL_XFER_ID: + return true; + default: + return false; + } +} + +static const struct regmap_config axi_dmac_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = AXI_DMAC_REG_PARTIAL_XFER_ID, + .readable_reg = axi_dmac_regmap_rdwr, + .writeable_reg = axi_dmac_regmap_rdwr, +}; + /* * The configuration stored in the devicetree matches the configuration * parameters of the peripheral instance and allows the driver to know which @@ -617,7 +760,7 @@ static int axi_dmac_parse_chan_dt(struct device_node *of_chan, return ret; chan->dest_width = val / 8; - chan->align_mask = max(chan->dest_width, chan->src_width) - 1; + chan->address_align_mask = max(chan->dest_width, chan->src_width) - 1; if (axi_dmac_dest_is_mem(chan) && axi_dmac_src_is_mem(chan)) chan->direction = DMA_MEM_TO_MEM; @@ -631,9 +774,12 @@ static int axi_dmac_parse_chan_dt(struct device_node *of_chan, return 0; } -static void axi_dmac_detect_caps(struct axi_dmac *dmac) +static int axi_dmac_detect_caps(struct axi_dmac *dmac) { struct axi_dmac_chan *chan = &dmac->chan; + unsigned int version; + + version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION); axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, AXI_DMAC_FLAG_CYCLIC); if (axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS) == AXI_DMAC_FLAG_CYCLIC) @@ -647,6 +793,35 @@ static void axi_dmac_detect_caps(struct axi_dmac *dmac) chan->max_length = axi_dmac_read(dmac, AXI_DMAC_REG_X_LENGTH); if (chan->max_length != UINT_MAX) chan->max_length++; + + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, 0xffffffff); + if (axi_dmac_read(dmac, AXI_DMAC_REG_DEST_ADDRESS) == 0 && + chan->dest_type == AXI_DMAC_BUS_TYPE_AXI_MM) { + dev_err(dmac->dma_dev.dev, + "Destination memory-mapped interface not supported."); + return -ENODEV; + } + + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, 0xffffffff); + if (axi_dmac_read(dmac, AXI_DMAC_REG_SRC_ADDRESS) == 0 && + chan->src_type == AXI_DMAC_BUS_TYPE_AXI_MM) { + dev_err(dmac->dma_dev.dev, + "Source memory-mapped interface not supported."); + return -ENODEV; + } + + if (version >= ADI_AXI_PCORE_VER(4, 2, 'a')) + chan->hw_partial_xfer = true; + + if (version >= ADI_AXI_PCORE_VER(4, 1, 'a')) { + axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, 0x00); + chan->length_align_mask = + axi_dmac_read(dmac, AXI_DMAC_REG_X_LENGTH); + } else { + chan->length_align_mask = chan->address_align_mask; + } + + return 0; } static int axi_dmac_probe(struct platform_device *pdev) @@ -722,7 +897,11 @@ static int axi_dmac_probe(struct platform_device *pdev) if (ret < 0) return ret; - axi_dmac_detect_caps(dmac); + ret = axi_dmac_detect_caps(dmac); + if (ret) + goto err_clk_disable; + + dma_dev->copy_align = (dmac->chan.address_align_mask + 1); axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, 0x00); @@ -742,6 +921,8 @@ static int axi_dmac_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dmac); + devm_regmap_init_mmio(&pdev->dev, dmac->base, &axi_dmac_regmap_config); + return 0; err_unregister_of: diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 6b8c4c458e8a..7fe9309a876b 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -156,7 +156,6 @@ struct jz4780_dma_dev { }; struct jz4780_dma_filter_data { - struct device_node *of_node; uint32_t transfer_type; int channel; }; @@ -772,8 +771,6 @@ static bool jz4780_dma_filter_fn(struct dma_chan *chan, void *param) struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); struct jz4780_dma_filter_data *data = param; - if (jzdma->dma_device.dev->of_node != data->of_node) - return false; if (data->channel > -1) { if (data->channel != jzchan->id) @@ -797,7 +794,6 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec, if (dma_spec->args_count != 2) return NULL; - data.of_node = ofdma->of_node; data.transfer_type = dma_spec->args[0]; data.channel = dma_spec->args[1]; @@ -822,7 +818,8 @@ static struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec, return dma_get_slave_channel( &jzdma->chan[data.channel].vchan.chan); } else { - return dma_request_channel(mask, jz4780_dma_filter_fn, &data); + return __dma_request_channel(&mask, jz4780_dma_filter_fn, &data, + ofdma->of_node); } } diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 58cbf9fd5a46..03ac4b96117c 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -61,7 +61,7 @@ static long dmaengine_ref_count; /* --- sysfs implementation --- */ /** - * dev_to_dma_chan - convert a device pointer to the its sysfs container object + * dev_to_dma_chan - convert a device pointer to its sysfs container object * @dev - device node * * Must be called under dma_list_mutex @@ -629,11 +629,13 @@ EXPORT_SYMBOL_GPL(dma_get_any_slave_channel); * @mask: capabilities that the channel must satisfy * @fn: optional callback to disposition available channels * @fn_param: opaque parameter to pass to dma_filter_fn + * @np: device node to look for DMA channels * * Returns pointer to appropriate DMA channel on success or NULL. */ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, - dma_filter_fn fn, void *fn_param) + dma_filter_fn fn, void *fn_param, + struct device_node *np) { struct dma_device *device, *_d; struct dma_chan *chan = NULL; @@ -641,6 +643,10 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, /* Find a channel */ mutex_lock(&dma_list_mutex); list_for_each_entry_safe(device, _d, &dma_device_list, global_node) { + /* Finds a DMA controller with matching device node */ + if (np && device->dev->of_node && np != device->dev->of_node) + continue; + chan = find_candidate(device, mask, fn, fn_param); if (!IS_ERR(chan)) break; @@ -699,7 +705,7 @@ struct dma_chan *dma_request_chan(struct device *dev, const char *name) chan = acpi_dma_request_slave_chan_by_name(dev, name); if (chan) { - /* Valid channel found or requester need to be deferred */ + /* Valid channel found or requester needs to be deferred */ if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) return chan; } @@ -757,7 +763,7 @@ struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask) if (!mask) return ERR_PTR(-ENODEV); - chan = __dma_request_channel(mask, NULL, NULL); + chan = __dma_request_channel(mask, NULL, NULL, NULL); if (!chan) { mutex_lock(&dma_list_mutex); if (list_empty(&dma_device_list)) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index d0ad46e916a6..3d22ae8dca72 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -62,7 +62,7 @@ MODU |