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
  ...
This commit is contained in:
Linus Torvalds
2019-07-17 09:55:43 -07:00
66 changed files with 3667 additions and 794 deletions
@@ -1,33 +0,0 @@
* Mediatek UART APDMA Controller
Required properties:
- compatible should contain:
* "mediatek,mt2712-uart-dma" for MT2712 compatible APDMA
* "mediatek,mt6577-uart-dma" for MT6577 and all of the above
- reg: The base address of the APDMA register bank.
- interrupts: A single interrupt specifier.
- clocks : Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: The APDMA clock for register accesses
Examples:
apdma: dma-controller@11000380 {
compatible = "mediatek,mt2712-uart-dma";
reg = <0 0x11000380 0 0x400>;
interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 65 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 66 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 68 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 69 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 70 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_AP_DMA>;
clock-names = "apdma";
#dma-cells = <1>;
};
@@ -16,6 +16,9 @@ Optional properties:
- dma-channels: contains the total number of DMA channels supported by the DMAC
- dma-requests: contains the total number of DMA requests supported by the DMAC
- arm,pl330-broken-no-flushp: quirk for avoiding to execute DMAFLUSHP
- resets: contains an entry for each entry in reset-names.
See ../reset/reset.txt for details.
- reset-names: must contain at least "dma", and optional is "dma-ocp".
Example:
@@ -9,15 +9,16 @@ group, DMAMUX0 or DMAMUX1, but not both.
Required properties:
- compatible :
- "fsl,vf610-edma" for eDMA used similar to that on Vybrid vf610 SoC
- "fsl,imx7ulp-edma" for eDMA2 used similar to that on i.mx7ulp
- reg : Specifies base physical address(s) and size of the eDMA registers.
The 1st region is eDMA control register's address and size.
The 2nd and the 3rd regions are programmable channel multiplexing
control register's address and size.
- interrupts : A list of interrupt-specifiers, one for each entry in
interrupt-names.
- interrupt-names : Should contain:
"edma-tx" - the transmission interrupt
"edma-err" - the error interrupt
interrupt-names on vf610 similar SoC. But for i.mx7ulp per channel
per transmission interrupt, total 16 channel interrupt and 1
error interrupt(located in the last), no interrupt-names list on
i.mx7ulp for clean on dts.
- #dma-cells : Must be <2>.
The 1st cell specifies the DMAMUX(0 for DMAMUX0 and 1 for DMAMUX1).
Specific request source can only be multiplexed by specific channels
@@ -28,6 +29,7 @@ Required properties:
- clock-names : A list of channel group clock names. Should contain:
"dmamux0" - clock name of mux0 group
"dmamux1" - clock name of mux1 group
Note: No dmamux0 on i.mx7ulp, but another 'dma' clk added on i.mx7ulp.
- clocks : A list of phandle and clock-specifier pairs, one for each entry in
clock-names.
@@ -35,6 +37,10 @@ Optional properties:
- big-endian: If present registers and hardware scatter/gather descriptors
of the eDMA are implemented in big endian mode, otherwise in little
mode.
- interrupt-names : Should contain the below on vf610 similar SoC but not used
on i.mx7ulp similar SoC:
"edma-tx" - the transmission interrupt
"edma-err" - the error interrupt
Examples:
@@ -52,8 +58,36 @@ edma0: dma-controller@40018000 {
clock-names = "dmamux0", "dmamux1";
clocks = <&clks VF610_CLK_DMAMUX0>,
<&clks VF610_CLK_DMAMUX1>;
};
}; /* vf610 */
edma1: dma-controller@40080000 {
#dma-cells = <2>;
compatible = "fsl,imx7ulp-edma";
reg = <0x40080000 0x2000>,
<0x40210000 0x1000>;
dma-channels = <32>;
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
/* last is eDMA2-ERR interrupt */
<GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "dma", "dmamux0";
clocks = <&pcc2 IMX7ULP_CLK_DMA1>,
<&pcc2 IMX7ULP_CLK_DMA_MUX1>;
}; /* i.mx7ulp */
* DMA clients
DMA client drivers that uses the DMA function must use the format described
@@ -0,0 +1,54 @@
* Mediatek UART APDMA Controller
Required properties:
- compatible should contain:
* "mediatek,mt2712-uart-dma" for MT2712 compatible APDMA
* "mediatek,mt6577-uart-dma" for MT6577 and all of the above
- reg: The base address of the APDMA register bank.
- interrupts: A single interrupt specifier.
One interrupt per dma-requests, or 8 if no dma-requests property is present
- dma-requests: The number of DMA channels
- clocks : Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: The APDMA clock for register accesses
- mediatek,dma-33bits: Present if the DMA requires support
Examples:
apdma: dma-controller@11000400 {
compatible = "mediatek,mt2712-uart-dma";
reg = <0 0x11000400 0 0x80>,
<0 0x11000480 0 0x80>,
<0 0x11000500 0 0x80>,
<0 0x11000580 0 0x80>,
<0 0x11000600 0 0x80>,
<0 0x11000680 0 0x80>,
<0 0x11000700 0 0x80>,
<0 0x11000780 0 0x80>,
<0 0x11000800 0 0x80>,
<0 0x11000880 0 0x80>,
<0 0x11000900 0 0x80>,
<0 0x11000980 0 0x80>;
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 105 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 106 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 107 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 108 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 109 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 110 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 111 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 112 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 113 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 114 IRQ_TYPE_LEVEL_LOW>;
dma-requests = <12>;
clocks = <&pericfg CLK_PERI_AP_DMA>;
clock-names = "apdma";
mediatek,dma-33bits;
#dma-cells = <1>;
};
@@ -28,12 +28,17 @@ Example:
};
------------------------------------------------------------------------------
For A64 DMA controller:
For A64 and H6 DMA controller:
Required properties:
- compatible: "allwinner,sun50i-a64-dma"
- compatible: Must be one of
"allwinner,sun50i-a64-dma"
"allwinner,sun50i-h6-dma"
- dma-channels: Number of DMA channels supported by the controller.
Refer to Documentation/devicetree/bindings/dma/dma.txt
- clocks: In addition to parent AHB clock, it should also contain mbus
clock (H6 only)
- clock-names: Should contain "bus" and "mbus" (H6 only)
- all properties above, i.e. reg, interrupts, clocks, resets and #dma-cells
Optional properties:
+13 -8
View File
@@ -44,7 +44,8 @@ Example of usage::
dmatest.timeout=2000 dmatest.iterations=1 dmatest.channel=dma0chan0 dmatest.run=1
Example of multi-channel test usage:
Example of multi-channel test usage (new in the 5.0 kernel)::
% modprobe dmatest
% echo 2000 > /sys/module/dmatest/parameters/timeout
% echo 1 > /sys/module/dmatest/parameters/iterations
@@ -53,15 +54,18 @@ Example of multi-channel test usage:
% echo dma0chan2 > /sys/module/dmatest/parameters/channel
% echo 1 > /sys/module/dmatest/parameters/run
Note: the channel parameter should always be the last parameter set prior to
running the test (setting run=1), this is because upon setting the channel
parameter, that specific channel is requested using the dmaengine and a thread
is created with the existing parameters. This thread is set as pending
and will be executed once run is set to 1. Any parameters set after the thread
is created are not applied.
.. note::
For all tests, starting in the 5.0 kernel, either single- or multi-channel,
the channel parameter(s) must be set after all other parameters. It is at
that time that the existing parameter values are acquired for use by the
thread(s). All other parameters are shared. Therefore, if changes are made
to any of the other parameters, and an additional channel specified, the
(shared) parameters used for all threads will use the new values.
After the channels are specified, each thread is set as pending. All threads
begin execution when the run parameter is set to 1.
.. hint::
available channel list could be extracted by running the following command::
A list of available channels can be found by running the following command::
% ls -1 /sys/class/dma/
@@ -204,6 +208,7 @@ Releasing Channels
Channels can be freed by setting run to 0.
Example::
% echo dma0chan1 > /sys/module/dmatest/parameters/channel
dmatest: Added 1 threads using dma0chan1
% cat /sys/class/dma/dma0chan1/in_use
+7
View File
@@ -4683,6 +4683,13 @@ L: linux-mtd@lists.infradead.org
S: Supported
F: drivers/mtd/nand/raw/denali*
DESIGNWARE EDMA CORE IP DRIVER
M: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
L: dmaengine@vger.kernel.org
S: Maintained
F: drivers/dma/dw-edma/
F: include/linux/dma/edma.h
DESIGNWARE USB2 DRD IP DRIVER
M: Minas Harutyunyan <hminas@synopsys.com>
L: linux-usb@vger.kernel.org
+4 -1
View File
@@ -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"
+1
View File
@@ -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
+2 -3
View File
@@ -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
+7 -4
View File
@@ -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)
+3 -10
View File
@@ -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 */
+2 -4
View File
@@ -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;
}
+191 -10
View File
@@ -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:
+2 -5
View File
@@ -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);
}
}
+10 -4
View File
@@ -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))
+3 -3
View File
@@ -62,7 +62,7 @@ MODULE_PARM_DESC(pq_sources,
static int timeout = 3000;
module_param(timeout, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
"Pass -1 for infinite timeout");
"Pass 0xFFFFFFFF (4294967295) for maximum timeout");
static bool noverify;
module_param(noverify, bool, S_IRUGO | S_IWUSR);
@@ -94,7 +94,7 @@ MODULE_PARM_DESC(transfer_size, "Optional custom transfer size in bytes (default
* @iterations: iterations before stopping test
* @xor_sources: number of xor source buffers
* @pq_sources: number of p+q source buffers
* @timeout: transfer timeout in msec, -1 for infinite timeout
* @timeout: transfer timeout in msec, 0 - 0xFFFFFFFF (4294967295)
*/
struct dmatest_params {
unsigned int buf_size;
@@ -105,7 +105,7 @@ struct dmatest_params {
unsigned int iterations;
unsigned int xor_sources;
unsigned int pq_sources;
int timeout;
unsigned int timeout;
bool noverify;
bool norandom;
int alignment;
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0
config DW_EDMA
tristate "Synopsys DesignWare eDMA controller driver"
depends on PCI && PCI_MSI
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Support the Synopsys DesignWare eDMA controller, normally
implemented on endpoints SoCs.
config DW_EDMA_PCIE
tristate "Synopsys DesignWare eDMA PCIe driver"
depends on PCI && PCI_MSI
select DW_EDMA
help
Provides a glue-logic between the Synopsys DesignWare
eDMA controller and an endpoint PCIe device. This also serves
as a reference design to whom desires to use this IP.
+7
View File
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DW_EDMA) += dw-edma.o
dw-edma-$(CONFIG_DEBUG_FS) := dw-edma-v0-debugfs.o
dw-edma-objs := dw-edma-core.o \
dw-edma-v0-core.o $(dw-edma-y)
obj-$(CONFIG_DW_EDMA_PCIE) += dw-edma-pcie.o
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More