DMA Engine development
 help / color / mirror / Atom feed
* Re: [PATCH v3 2/7] dmaengine: sun6i: Add a quirk for additional mbus clock
From: Vinod Koul @ 2019-06-04 12:27 UTC (permalink / raw)
  To: Clément Péron
  Cc: Rob Herring, Mark Rutland, Maxime Ripard, Chen-Yu Tsai,
	Dan Williams, dmaengine, devicetree, linux-arm-kernel,
	linux-kernel, Jernej Skrabec
In-Reply-To: <20190527201459.20130-3-peron.clem@gmail.com>

On 27-05-19, 22:14, Clément Péron wrote:
> From: Jernej Skrabec <jernej.skrabec@siol.net>
> 
> H6 DMA controller needs additional mbus clock to be enabled.
> 
> Add a quirk for it and handle it accordingly.

Applied, thanks

-- 
~Vinod

^ permalink raw reply

* Re: [PATCH v3 3/7] dmaengine: sun6i: Add a quirk for setting DRQ fields
From: Vinod Koul @ 2019-06-04 12:28 UTC (permalink / raw)
  To: Clément Péron
  Cc: Rob Herring, Mark Rutland, Maxime Ripard, Chen-Yu Tsai,
	Dan Williams, dmaengine, devicetree, linux-arm-kernel,
	linux-kernel, Jernej Skrabec
In-Reply-To: <20190527201459.20130-4-peron.clem@gmail.com>

On 27-05-19, 22:14, Clément Péron wrote:
> From: Jernej Skrabec <jernej.skrabec@siol.net>
> 
> H6 DMA has more than 32 possible DRQs. That means that current maximum
> of 31 DRQs is not enough anymore.
> 
> Add a quirk which will set source and destination DRQ number.

Applied, thanks

-- 
~Vinod

^ permalink raw reply

* Re: [PATCH v3 4/7] dmaengine: sun6i: Add a quirk for setting mode fields
From: Vinod Koul @ 2019-06-04 12:28 UTC (permalink / raw)
  To: Clément Péron
  Cc: Rob Herring, Mark Rutland, Maxime Ripard, Chen-Yu Tsai,
	Dan Williams, dmaengine, devicetree, linux-arm-kernel,
	linux-kernel, Jernej Skrabec
In-Reply-To: <20190527201459.20130-5-peron.clem@gmail.com>

On 27-05-19, 22:14, Clément Péron wrote:
> From: Jernej Skrabec <jernej.skrabec@siol.net>
> 
> H6 DMA has mode fields in different position than any other currently
> supported DMA controller.
> 
> Add a quirk for that.

Applied, thanks

-- 
~Vinod

^ permalink raw reply

* Re: [PATCH v3 5/7] dmaengine: sun6i: Add support for H6 DMA
From: Vinod Koul @ 2019-06-04 12:28 UTC (permalink / raw)
  To: Clément Péron
  Cc: Rob Herring, Mark Rutland, Maxime Ripard, Chen-Yu Tsai,
	Dan Williams, dmaengine, devicetree, linux-arm-kernel,
	linux-kernel, Jernej Skrabec
In-Reply-To: <20190527201459.20130-6-peron.clem@gmail.com>

On 27-05-19, 22:14, Clément Péron wrote:
> From: Jernej Skrabec <jernej.skrabec@siol.net>
> 
> H6 DMA has more than 32 supported DRQs, which means that configuration
> register is slightly rearranged. It also needs additional clock to be
> enabled.
> 
> Add support for it.

Applied, thanks

-- 
~Vinod

^ permalink raw reply

* Re: [PATCH v3 5/8] dmaengine: fsl-edma: add drvdata for vf610
From: Vinod Koul @ 2019-06-04 12:33 UTC (permalink / raw)
  To: yibin.gong
  Cc: robh, shawnguo, s.hauer, festevam, mark.rutland, dan.j.williams,
	linux-imx, linux-arm-kernel, linux-kernel, dmaengine, devicetree,
	kernel
In-Reply-To: <20190529090848.34350-6-yibin.gong@nxp.com>

On 29-05-19, 17:08, yibin.gong@nxp.com wrote:

> @@ -205,8 +228,9 @@ static int fsl_edma_probe(struct platform_device *pdev)
>  	if (!fsl_edma)
>  		return -ENOMEM;
>  
> -	fsl_edma->version = v1;
> -	fsl_edma->dmamux_nr = DMAMUX_NR;
> +	fsl_edma->drvdata = drvdata;
> +	fsl_edma->version = drvdata->version;
> +	fsl_edma->dmamux_nr = drvdata->dmamuxs;

And can we avoid the duplication here, you have version and dmamuxs
represented in two places. But right now it looks logical so the removal
should be done after this series

-- 
~Vinod

^ permalink raw reply

* Re: [PATCH v3 7/8] dmaengine: fsl-edma: add i.mx7ulp edma2 version support
From: Vinod Koul @ 2019-06-04 12:37 UTC (permalink / raw)
  To: yibin.gong
  Cc: robh, shawnguo, s.hauer, festevam, mark.rutland, dan.j.williams,
	linux-imx, linux-arm-kernel, linux-kernel, dmaengine, devicetree,
	kernel
In-Reply-To: <20190529090848.34350-8-yibin.gong@nxp.com>

On 29-05-19, 17:08, yibin.gong@nxp.com wrote:
> From: Robin Gong <yibin.gong@nxp.com>
> 
>   Add edma2 for i.mx7ulp by version v3, since v2 has already

Why leading spaces at start of line?

> been used by mcf-edma.
> The big changes based on v1 are belows:
> 1. only one dmamux.
> 2. another clock dma_clk except dmamux clk.
> 3. 16 independent interrupts instead of only one interrupt for
> all channels.
> 
> Signed-off-by: Robin Gong <yibin.gong@nxp.com>
> ---
>  drivers/dma/fsl-edma-common.c | 18 +++++++++++-
>  drivers/dma/fsl-edma-common.h |  3 ++
>  drivers/dma/fsl-edma.c        | 67 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 87 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
> index 45d70d3..0d9915c 100644
> --- a/drivers/dma/fsl-edma-common.c
> +++ b/drivers/dma/fsl-edma-common.c
> @@ -90,6 +90,19 @@ static void mux_configure8(struct fsl_edma_chan *fsl_chan, void __iomem *addr,
>  	iowrite8(val8, addr + off);
>  }
>  
> +void mux_configure32(struct fsl_edma_chan *fsl_chan, void __iomem *addr,
> +		     u32 off, u32 slot, bool enable)
> +{
> +	u32 val;
> +
> +	if (enable)
> +		val = EDMAMUX_CHCFG_ENBL << 24 | slot;
> +	else
> +		val = EDMAMUX_CHCFG_DIS;
> +
> +	iowrite32(val, addr + off * 4);
> +}
> +
>  void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
>  			unsigned int slot, bool enable)
>  {
> @@ -102,7 +115,10 @@ void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
>  	muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux];
>  	slot = EDMAMUX_CHCFG_SOURCE(slot);
>  
> -	mux_configure8(fsl_chan, muxaddr, ch_off, slot, enable);
> +	if (fsl_chan->edma->version == v3)
> +		mux_configure32(fsl_chan, muxaddr, ch_off, slot, enable);
> +	else
> +		mux_configure8(fsl_chan, muxaddr, ch_off, slot, enable);
>  }
>  EXPORT_SYMBOL_GPL(fsl_edma_chan_mux);
>  
> diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h
> index 014ab74..07482d2 100644
> --- a/drivers/dma/fsl-edma-common.h
> +++ b/drivers/dma/fsl-edma-common.h
> @@ -125,6 +125,7 @@ struct fsl_edma_chan {
>  	dma_addr_t			dma_dev_addr;
>  	u32				dma_dev_size;
>  	enum dma_data_direction		dma_dir;
> +	char				chan_name[16];
>  };
>  
>  struct fsl_edma_desc {
> @@ -139,6 +140,7 @@ struct fsl_edma_desc {
>  enum edma_version {
>  	v1, /* 32ch, Vybrid, mpc57x, etc */
>  	v2, /* 64ch Coldfire */
> +	v3, /* 32ch, i.mx7ulp */
>  };
>  
>  struct fsl_edma_drvdata {
> @@ -154,6 +156,7 @@ struct fsl_edma_engine {
>  	void __iomem		*membase;
>  	void __iomem		*muxbase[DMAMUX_NR];
>  	struct clk		*muxclk[DMAMUX_NR];
> +	struct clk		*dmaclk;
>  	u32			dmamux_nr;
>  	struct mutex		fsl_edma_mutex;
>  	const struct fsl_edma_drvdata *drvdata;
> diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
> index cf18301..45b26d6 100644
> --- a/drivers/dma/fsl-edma.c
> +++ b/drivers/dma/fsl-edma.c
> @@ -165,6 +165,51 @@ fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma
>  	return 0;
>  }
>  
> +static int
> +fsl_edma2_irq_init(struct platform_device *pdev,
> +		   struct fsl_edma_engine *fsl_edma)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	int i, ret, irq;
> +	int count = 0;

Superflous initialization of count!

> +
> +	count = of_irq_count(np);
> +	dev_info(&pdev->dev, "%s Found %d interrupts\r\n", __func__, count);

Consider using debug level..

> +	if (count <= 2) {
> +		dev_err(&pdev->dev, "Interrupts in DTS not correct.\n");
> +		return -EINVAL;
> +	}
> +	/*
> +	 * 16 channel independent interrupts + 1 error interrupt on i.mx7ulp.
> +	 * 2 channel share one interrupt, for example, ch0/ch16, ch1/ch17...
> +	 * For now, just simply request irq without IRQF_SHARED flag, since 16
> +	 * channels are enough on i.mx7ulp whose M4 domain own some peripherals.
> +	 */
> +	for (i = 0; i < count; i++) {
> +		irq = platform_get_irq(pdev, i);
> +		if (irq < 0)
> +			return -ENXIO;
> +
> +		sprintf(fsl_edma->chans[i].chan_name, "eDMA2-CH%02d", i);
> +
> +		/* The last IRQ is for eDMA err */
> +		if (i == count - 1)
> +			ret = devm_request_irq(&pdev->dev, irq,
> +						fsl_edma_err_handler,
> +						0, "eDMA2-ERR", fsl_edma);
> +		else
> +

empty line is waste here

> +			ret = devm_request_irq(&pdev->dev, irq,
> +						fsl_edma_tx_handler, 0,
> +						fsl_edma->chans[i].chan_name,
> +						fsl_edma);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  static void fsl_edma_irq_exit(
>  		struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
>  {
> @@ -191,8 +236,16 @@ static struct fsl_edma_drvdata vf610_data = {
>  	.setup_irq = fsl_edma_irq_init,
>  };
>  
> +static struct fsl_edma_drvdata imx7ulp_data = {
> +	.version = v3,
> +	.dmamuxs = 1,
> +	.has_dmaclk = true,
> +	.setup_irq = fsl_edma2_irq_init,
> +};
> +
>  static const struct of_device_id fsl_edma_dt_ids[] = {
>  	{ .compatible = "fsl,vf610-edma", .data = &vf610_data},
> +	{ .compatible = "fsl,imx7ulp-edma", .data = &imx7ulp_data},
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
> @@ -242,6 +295,20 @@ static int fsl_edma_probe(struct platform_device *pdev)
>  	fsl_edma_setup_regs(fsl_edma);
>  	regs = &fsl_edma->regs;
>  
> +	if (drvdata->has_dmaclk) {
> +		fsl_edma->dmaclk = devm_clk_get(&pdev->dev, "dma");
> +		if (IS_ERR(fsl_edma->dmaclk)) {
> +			dev_err(&pdev->dev, "Missing DMA block clock.\n");
> +			return PTR_ERR(fsl_edma->dmaclk);
> +		}
> +
> +		ret = clk_prepare_enable(fsl_edma->dmaclk);
> +		if (ret) {
> +			dev_err(&pdev->dev, "DMA clk block failed.\n");
> +			return ret;
> +		}
> +	}
> +
>  	for (i = 0; i < fsl_edma->dmamux_nr; i++) {
>  		char clkname[32];
>  
> -- 
> 2.7.4

-- 
~Vinod

^ permalink raw reply

* Re: [PATCH] dmaengine: dmatest: Add support for completion polling
From: Vinod Koul @ 2019-06-04 12:45 UTC (permalink / raw)
  To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, andriy.shevchenko
In-Reply-To: <793f9f48-0609-4aa5-2688-bf30525e229c@ti.com>

On 03-06-19, 10:05, Peter Ujfalusi wrote:

> > I think the main question is how polling for completion should be
> > handled when client does not request for completion interrupt, thus we
> > will have no callback in the DMA driver when the transfer is completed.
> > 
> > If DMA_PREP_INTERRUPT is set for the tx_descriptor then the polling will
> > wait until the DMA driver internally receives the interrupt that the
> > transfer is done and sets the cookie to completed state.
> > 
> > However if DMA_PREP_INTERRUPT is not set, the DMA driver will not get
> > notification from the HW that is the transfer is done, the only way to
> > know is to check the tx_status and based on the residue (if it is 0 then
> > it is done) decide what to tell the client.
> > 
> > Should the client call dmaengine_terminate_* after the polling returned
> > unconditionally to free up the descriptor?
> 
> This is how omap-dma is handling the polled memcpy support.

Yes that is a good question. Even if the client does not set
DMA_PREP_INTERRUPT would there be no interrupt generated by controller
on txn completion? If not how will next txn be submitted to the
hardware.

I think we should view DMA_PREP_INTERRUPT from client pov, but
controller cannot get away with disabling interrupts IMO.

Assuming I had enough caffeine before I thought process, then client would
poll descriptor status using cookie and should free up once the cookie
is freed, makes sense?

-- 
~Vinod

^ permalink raw reply

* [PATCH v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0)
From: Gustavo Pimentel @ 2019-06-04 13:29 UTC (permalink / raw)
  To: linux-pci, dmaengine; +Cc: Gustavo Pimentel

Add Synopsys eDMA IP driver (version 0 and for EP side only) to Linux
kernel. This IP is generally distributed with Synopsys PCIe EndPoint IP
(depends of the use and licensing agreement), which supports:
 - legacy and unroll modes
 - 16 independent and concurrent channels (8 write + 8 read)
 - supports linked list (scatter-gather) transfer
 - each linked list descriptor can transfer from 1 byte to 4 Gbytes
 - supports cyclic transfer
 - PCIe EndPoint glue-logic

This patch series contains:
 - eDMA core + eDMA core v0 driver (implements the interface with
 DMAengine controller APIs and interfaces with eDMA HW block)
 - eDMA PCIe glue-logic reference driver (attaches to Synopsys EP and
 provides memory access to eDMA core driver)

Gustavo Pimentel (6):
  dmaengine: Add Synopsys eDMA IP core driver
  dmaengine: Add Synopsys eDMA IP version 0 support
  dmaengine: Add Synopsys eDMA IP version 0 debugfs support
  PCI: Add Synopsys endpoint EDDA Device ID
  dmaengine: Add Synopsys eDMA IP PCIe glue-logic
  MAINTAINERS: Add Synopsys eDMA IP driver maintainer

 MAINTAINERS                              |   7 +
 drivers/dma/Kconfig                      |   2 +
 drivers/dma/Makefile                     |   1 +
 drivers/dma/dw-edma/Kconfig              |  18 +
 drivers/dma/dw-edma/Makefile             |   7 +
 drivers/dma/dw-edma/dw-edma-core.c       | 937 +++++++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-core.h       | 165 ++++++
 drivers/dma/dw-edma/dw-edma-pcie.c       | 229 ++++++++
 drivers/dma/dw-edma/dw-edma-v0-core.c    | 354 ++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-core.h    |  28 +
 drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 310 ++++++++++
 drivers/dma/dw-edma/dw-edma-v0-debugfs.h |  27 +
 drivers/dma/dw-edma/dw-edma-v0-regs.h    | 158 ++++++
 drivers/misc/pci_endpoint_test.c         |   2 +-
 include/linux/dma/edma.h                 |  47 ++
 include/linux/pci_ids.h                  |   1 +
 16 files changed, 2292 insertions(+), 1 deletion(-)
 create mode 100644 drivers/dma/dw-edma/Kconfig
 create mode 100644 drivers/dma/dw-edma/Makefile
 create mode 100644 drivers/dma/dw-edma/dw-edma-core.c
 create mode 100644 drivers/dma/dw-edma/dw-edma-core.h
 create mode 100644 drivers/dma/dw-edma/dw-edma-pcie.c
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-core.c
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-core.h
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-debugfs.c
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-debugfs.h
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-regs.h
 create mode 100644 include/linux/dma/edma.h

-- 
2.7.4


^ permalink raw reply

* [PATCH v2 4/6] PCI: Add Synopsys endpoint EDDA Device ID
From: Gustavo Pimentel @ 2019-06-04 13:29 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Kishon Vijay Abraham I, Bjorn Helgaas,
	Lorenzo Pieralisi, Joao Pinto
In-Reply-To: <cover.1559654565.git.gustavo.pimentel@synopsys.com>

Create and add Synopsys Endpoint EDDA Device ID to PCI ID list, since
this ID is now being use on two different drivers (pci_endpoint_test.ko
and dw-edma-pcie.ko).

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Joao Pinto <jpinto@synopsys.com>
---
Changes:
v1 -> v2:
 - Rebased to v5.2-rc1

 drivers/misc/pci_endpoint_test.c | 2 +-
 include/linux/pci_ids.h          | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 7b015f2..1f531c1 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -804,7 +804,7 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0) },
-	{ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 0xedda) },
+	{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654),
 	  .driver_data = (kernel_ulong_t)&am654_data
 	},
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 70e8614..4aad69f 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2366,6 +2366,7 @@
 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3		0xabcd
 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI	0xabce
 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31	0xabcf
+#define PCI_DEVICE_ID_SYNOPSYS_EDDA	0xedda
 
 #define PCI_VENDOR_ID_USR		0x16ec
 
-- 
2.7.4


^ permalink raw reply related

* [PATCH v2 1/6] dmaengine: Add Synopsys eDMA IP core driver
From: Gustavo Pimentel @ 2019-06-04 13:29 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Dan Williams, Andy Shevchenko,
	Russell King, Joao Pinto
In-Reply-To: <cover.1559654565.git.gustavo.pimentel@synopsys.com>

Add Synopsys PCIe Endpoint eDMA IP core driver to kernel.

This IP is generally distributed with Synopsys PCIe Endpoint IP (depends
of the use and licensing agreement).

This core driver, initializes and configures the eDMA IP using vma-helpers
functions and dma-engine subsystem.

This driver can be compile as built-in or external module in kernel.

To enable this driver just select DW_EDMA option in kernel configuration,
however it requires and selects automatically DMA_ENGINE and
DMA_VIRTUAL_CHANNELS option too.

In order to transfer data from point A to B as fast as possible this IP
requires a dedicated memory space containing linked list of elements.

All elements of this linked list are continuous and each one describes a
data transfer (source and destination addresses, length and a control
variable).

For the sake of simplicity, lets assume a memory space for channel write
0 which allows about 42 elements.

+---------+
| Desc #0 |-+
+---------+ |
            V
       +----------+
       | Chunk #0 |-+
       |  CB = 1  | |  +----------+  +-----+  +-----------+  +-----+
       +----------+ +->| Burst #0 |->| ... |->| Burst #41 |->| llp |
            |          +----------+  +-----+  +-----------+  +-----+
            V
       +----------+
       | Chunk #1 |-+
       |  CB = 0  | |  +-----------+  +-----+  +-----------+  +-----+
       +----------+ +->| Burst #42 |->| ... |->| Burst #83 |->| llp |
            |          +-----------+  +-----+  +-----------+  +-----+
            V
       +----------+
       | Chunk #2 |-+
       |  CB = 1  | |  +-----------+  +-----+  +------------+  +-----+
       +----------+ +->| Burst #84 |->| ... |->| Burst #125 |->| llp |
            |          +-----------+  +-----+  +------------+  +-----+
            V
       +----------+
       | Chunk #3 |-+
       |  CB = 0  | |  +------------+  +-----+  +------------+  +-----+
       +----------+ +->| Burst #126 |->| ... |->| Burst #129 |->| llp |
                       +------------+  +-----+  +------------+  +-----+

Legend:
 - Linked list, also know as Chunk
 - Linked list element*, also know as Burst *CB*, also know as Change Bit,
it's a control bit (and typically is toggled) that allows to easily
identify and differentiate between the current linked list and the
previous or the next one.
 - LLP, is a special element that indicates the end of the linked list
element stream also informs that the next CB should be toggle

On every last Burst of the Chunk (Burst #41, Burst #83, Burst #125 or
even Burst #129) is set some flags on their control variable (RIE and
LIE bits) that will trigger the send of "done" interruption.

On the interruptions callback, is decided whether to recycle the linked
list memory space by writing a new set of Bursts elements (if still
exists Chunks to transfer) or is considered completed (if there is no
Chunks available to transfer).

On scatter-gather transfer mode, the client will submit a scatter-gather
list of n (on this case 130) elements, that will be divide in multiple
Chunks, each Chunk will have (on this case 42) a limited number of
Bursts and after transferring all Bursts, an interrupt will be
triggered, which will allow to recycle the all linked list dedicated
memory again with the new information relative to the next Chunk and
respective Burst associated and repeat the whole cycle again.

On cyclic transfer mode, the client will submit a buffer pointer, length
of it and number of repetitions, in this case each burst will correspond
directly to each repetition.

Each Burst can describes a data transfer from point A(source) to point
B(destination) with a length that can be from 1 byte up to 4 GB. Since
dedicated the memory space where the linked list will reside is limited,
the whole n burst elements will be organized in several Chunks, that
will be used later to recycle the dedicated memory space to initiate a
new sequence of data transfers.

The whole transfer is considered has completed when it was transferred
all bursts.

Currently this IP has a set well-known register map, which includes
support for legacy and unroll modes. Legacy mode is version of this
register map that has multiplexer register that allows to switch
registers between all write and read channels and the unroll modes
repeats all write and read channels registers with an offset between
them. This register map is called v0.

The IP team is creating a new register map more suitable to the latest
PCIe features, that very likely will change the map register, which this
version will be called v1. As soon as this new version is released by
the IP team the support for this version in be included on this driver.

According to the logic, patches 1, 2 and 3 should be squashed into 1
unique patch, but for the sake of simplicity of review, it was divided
in this 3 patches files.

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Joao Pinto <jpinto@synopsys.com>
---
Changes:
v1 -> v2:
 - Rebased to v5.2-rc1

 drivers/dma/Kconfig                |   2 +
 drivers/dma/Makefile               |   1 +
 drivers/dma/dw-edma/Kconfig        |   9 +
 drivers/dma/dw-edma/Makefile       |   4 +
 drivers/dma/dw-edma/dw-edma-core.c | 936 +++++++++++++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-core.h | 165 +++++++
 include/linux/dma/edma.h           |  47 ++
 7 files changed, 1164 insertions(+)
 create mode 100644 drivers/dma/dw-edma/Kconfig
 create mode 100644 drivers/dma/dw-edma/Makefile
 create mode 100644 drivers/dma/dw-edma/dw-edma-core.c
 create mode 100644 drivers/dma/dw-edma/dw-edma-core.h
 create mode 100644 include/linux/dma/edma.h

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index eaf78f4..76859aa 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -665,6 +665,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 6126e1c..5bddf6f 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/dw-edma/Kconfig b/drivers/dma/dw-edma/Kconfig
new file mode 100644
index 0000000..3016bed
--- /dev/null
+++ b/drivers/dma/dw-edma/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config DW_EDMA
+	tristate "Synopsys DesignWare eDMA controller driver"
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  Support the Synopsys DesignWare eDMA controller, normally
+	  implemented on endpoints SoCs.
diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
new file mode 100644
index 0000000..3224010
--- /dev/null
+++ b/drivers/dma/dw-edma/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DW_EDMA)		+= dw-edma.o
+dw-edma-objs			:= dw-edma-core.o
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
new file mode 100644
index 0000000..c9d032f
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -0,0 +1,936 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA core driver
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/dma/edma.h>
+#include <linux/pci.h>
+
+#include "dw-edma-core.h"
+#include "../dmaengine.h"
+#include "../virt-dma.h"
+
+static inline
+struct device *dchan2dev(struct dma_chan *dchan)
+{
+	return &dchan->dev->device;
+}
+
+static inline
+struct device *chan2dev(struct dw_edma_chan *chan)
+{
+	return &chan->vc.chan.dev->device;
+}
+
+static inline
+struct dw_edma_desc *vd2dw_edma_desc(struct virt_dma_desc *vd)
+{
+	return container_of(vd, struct dw_edma_desc, vd);
+}
+
+static struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk)
+{
+	struct dw_edma_burst *burst;
+
+	burst = kzalloc(sizeof(*burst), GFP_NOWAIT);
+	if (unlikely(!burst))
+		return NULL;
+
+	INIT_LIST_HEAD(&burst->list);
+	if (chunk->burst) {
+		/* Create and add new element into the linked list */
+		chunk->bursts_alloc++;
+		list_add_tail(&burst->list, &chunk->burst->list);
+	} else {
+		/* List head */
+		chunk->bursts_alloc = 0;
+		chunk->burst = burst;
+	}
+
+	return burst;
+}
+
+static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
+{
+	struct dw_edma_chan *chan = desc->chan;
+	struct dw_edma *dw = chan->chip->dw;
+	struct dw_edma_chunk *chunk;
+
+	chunk = kzalloc(sizeof(*chunk), GFP_NOWAIT);
+	if (unlikely(!chunk))
+		return NULL;
+
+	INIT_LIST_HEAD(&chunk->list);
+	chunk->chan = chan;
+	/* Toggling change bit (CB) in each chunk, this is a mechanism to
+	 * inform the eDMA HW block that this is a new linked list ready
+	 * to be consumed.
+	 *  - Odd chunks originate CB equal to 0
+	 *  - Even chunks originate CB equal to 1
+	 */
+	chunk->cb = !(desc->chunks_alloc % 2);
+	chunk->ll_region.paddr = dw->ll_region.paddr + chan->ll_off;
+	chunk->ll_region.vaddr = dw->ll_region.vaddr + chan->ll_off;
+
+	if (desc->chunk) {
+		/* Create and add new element into the linked list */
+		desc->chunks_alloc++;
+		list_add_tail(&chunk->list, &desc->chunk->list);
+		if (!dw_edma_alloc_burst(chunk)) {
+			kfree(chunk);
+			return NULL;
+		}
+	} else {
+		/* List head */
+		chunk->burst = NULL;
+		desc->chunks_alloc = 0;
+		desc->chunk = chunk;
+	}
+
+	return chunk;
+}
+
+static struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan)
+{
+	struct dw_edma_desc *desc;
+
+	desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
+	if (unlikely(!desc))
+		return NULL;
+
+	desc->chan = chan;
+	if (!dw_edma_alloc_chunk(desc)) {
+		kfree(desc);
+		return NULL;
+	}
+
+	return desc;
+}
+
+static void dw_edma_free_burst(struct dw_edma_chunk *chunk)
+{
+	struct dw_edma_burst *child, *_next;
+
+	/* Remove all the list elements */
+	list_for_each_entry_safe(child, _next, &chunk->burst->list, list) {
+		list_del(&child->list);
+		kfree(child);
+		chunk->bursts_alloc--;
+	}
+
+	/* Remove the list head */
+	kfree(child);
+	chunk->burst = NULL;
+}
+
+static void dw_edma_free_chunk(struct dw_edma_desc *desc)
+{
+	struct dw_edma_chunk *child, *_next;
+
+	if (!desc->chunk)
+		return;
+
+	/* Remove all the list elements */
+	list_for_each_entry_safe(child, _next, &desc->chunk->list, list) {
+		dw_edma_free_burst(child);
+		list_del(&child->list);
+		kfree(child);
+		desc->chunks_alloc--;
+	}
+
+	/* Remove the list head */
+	kfree(child);
+	desc->chunk = NULL;
+}
+
+static void dw_edma_free_desc(struct dw_edma_desc *desc)
+{
+	dw_edma_free_chunk(desc);
+	kfree(desc);
+}
+
+static void vchan_free_desc(struct virt_dma_desc *vdesc)
+{
+	dw_edma_free_desc(vd2dw_edma_desc(vdesc));
+}
+
+static void dw_edma_start_transfer(struct dw_edma_chan *chan)
+{
+	struct dw_edma_chunk *child;
+	struct dw_edma_desc *desc;
+	struct virt_dma_desc *vd;
+
+	vd = vchan_next_desc(&chan->vc);
+	if (!vd)
+		return;
+
+	desc = vd2dw_edma_desc(vd);
+	if (!desc)
+		return;
+
+	child = list_first_entry_or_null(&desc->chunk->list,
+					 struct dw_edma_chunk, list);
+	if (!child)
+		return;
+
+	dw_edma_v0_core_start(child, !desc->xfer_sz);
+	desc->xfer_sz += child->ll_region.sz;
+	dw_edma_free_burst(child);
+	list_del(&child->list);
+	kfree(child);
+	desc->chunks_alloc--;
+}
+
+static int dw_edma_device_config(struct dma_chan *dchan,
+				 struct dma_slave_config *config)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+
+	memcpy(&chan->config, config, sizeof(*config));
+	chan->configured = true;
+
+	return 0;
+}
+
+static int dw_edma_device_pause(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	int err = 0;
+
+	if (!chan->configured)
+		err = -EPERM;
+	else if (chan->status != EDMA_ST_BUSY)
+		err = -EPERM;
+	else if (chan->request != EDMA_REQ_NONE)
+		err = -EPERM;
+	else
+		chan->request = EDMA_REQ_PAUSE;
+
+	return err;
+}
+
+static int dw_edma_device_resume(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	int err = 0;
+
+	if (!chan->configured) {
+		err = -EPERM;
+	} else if (chan->status != EDMA_ST_PAUSE) {
+		err = -EPERM;
+	} else if (chan->request != EDMA_REQ_NONE) {
+		err = -EPERM;
+	} else {
+		chan->status = EDMA_ST_BUSY;
+		dw_edma_start_transfer(chan);
+	}
+
+	return err;
+}
+
+static int dw_edma_device_terminate_all(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	int err = 0;
+	LIST_HEAD(head);
+
+	if (!chan->configured) {
+		/* Do nothing */
+	} else if (chan->status == EDMA_ST_PAUSE) {
+		chan->status = EDMA_ST_IDLE;
+		chan->configured = false;
+	} else if (chan->status == EDMA_ST_IDLE) {
+		chan->configured = false;
+	} else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) {
+		/*
+		 * The channel is in a false BUSY state, probably didn't
+		 * receive or lost an interrupt
+		 */
+		chan->status = EDMA_ST_IDLE;
+		chan->configured = false;
+	} else if (chan->request > EDMA_REQ_PAUSE) {
+		err = -EPERM;
+	} else {
+		chan->request = EDMA_REQ_STOP;
+	}
+
+	return err;
+}
+
+static void dw_edma_device_issue_pending(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+	if (chan->configured && chan->request == EDMA_REQ_NONE &&
+	    chan->status == EDMA_ST_IDLE && vchan_issue_pending(&chan->vc)) {
+		chan->status = EDMA_ST_BUSY;
+		dw_edma_start_transfer(chan);
+	}
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static enum dma_status
+dw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
+			 struct dma_tx_state *txstate)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	struct dw_edma_desc *desc;
+	struct virt_dma_desc *vd;
+	unsigned long flags;
+	enum dma_status ret;
+	u32 residue = 0;
+
+	ret = dma_cookie_status(dchan, cookie, txstate);
+	if (ret == DMA_COMPLETE)
+		return ret;
+
+	if (ret == DMA_IN_PROGRESS && chan->status == EDMA_ST_PAUSE)
+		ret = DMA_PAUSED;
+
+	if (!txstate)
+		goto ret_residue;
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+	vd = vchan_find_desc(&chan->vc, cookie);
+	if (vd) {
+		desc = vd2dw_edma_desc(vd);
+		if (desc)
+			residue = desc->alloc_sz - desc->xfer_sz;
+	}
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+ret_residue:
+	dma_set_residue(txstate, residue);
+
+	return ret;
+}
+
+static struct dma_async_tx_descriptor *
+dw_edma_device_transfer(struct dw_edma_transfer *xfer)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
+	enum dma_transfer_direction direction = xfer->direction;
+	phys_addr_t src_addr, dst_addr;
+	struct scatterlist *sg = NULL;
+	struct dw_edma_chunk *chunk;
+	struct dw_edma_burst *burst;
+	struct dw_edma_desc *desc;
+	u32 cnt;
+	int i;
+
+	if ((direction == DMA_MEM_TO_DEV && chan->dir == EDMA_DIR_WRITE) ||
+	    (direction == DMA_DEV_TO_MEM && chan->dir == EDMA_DIR_READ))
+		return NULL;
+
+	if (xfer->cyclic) {
+		if (!xfer->xfer.cyclic.len || !xfer->xfer.cyclic.cnt)
+			return NULL;
+	} else {
+		if (xfer->xfer.sg.len < 1)
+			return NULL;
+	}
+
+	if (!chan->configured)
+		return NULL;
+
+	desc = dw_edma_alloc_desc(chan);
+	if (unlikely(!desc))
+		goto err_alloc;
+
+	chunk = dw_edma_alloc_chunk(desc);
+	if (unlikely(!chunk))
+		goto err_alloc;
+
+	src_addr = chan->config.src_addr;
+	dst_addr = chan->config.dst_addr;
+
+	if (xfer->cyclic) {
+		cnt = xfer->xfer.cyclic.cnt;
+	} else {
+		cnt = xfer->xfer.sg.len;
+		sg = xfer->xfer.sg.sgl;
+	}
+
+	for (i = 0; i < cnt; i++) {
+		if (!xfer->cyclic && !sg)
+			break;
+
+		if (chunk->bursts_alloc == chan->ll_max) {
+			chunk = dw_edma_alloc_chunk(desc);
+			if (unlikely(!chunk))
+				goto err_alloc;
+		}
+
+		burst = dw_edma_alloc_burst(chunk);
+		if (unlikely(!burst))
+			goto err_alloc;
+
+		if (xfer->cyclic)
+			burst->sz = xfer->xfer.cyclic.len;
+		else
+			burst->sz = sg_dma_len(sg);
+
+		chunk->ll_region.sz += burst->sz;
+		desc->alloc_sz += burst->sz;
+
+		if (direction == DMA_DEV_TO_MEM) {
+			burst->sar = src_addr;
+			if (xfer->cyclic) {
+				burst->dar = xfer->xfer.cyclic.paddr;
+			} else {
+				burst->dar = sg_dma_address(sg);
+				/* Unlike the typical assumption by other
+				 * drivers/IPs the peripheral memory isn't
+				 * a FIFO memory, in this case, it's a
+				 * linear memory and that why the source
+				 * and destination addresses are increased
+				 * by the same portion (data length)
+				 */
+				src_addr += sg_dma_len(sg);
+			}
+		} else {
+			burst->dar = dst_addr;
+			if (xfer->cyclic) {
+				burst->sar = xfer->xfer.cyclic.paddr;
+			} else {
+				burst->sar = sg_dma_address(sg);
+				/* Unlike the typical assumption by other
+				 * drivers/IPs the peripheral memory isn't
+				 * a FIFO memory, in this case, it's a
+				 * linear memory and that why the source
+				 * and destination addresses are increased
+				 * by the same portion (data length)
+				 */
+				dst_addr += sg_dma_len(sg);
+			}
+		}
+
+		if (!xfer->cyclic)
+			sg = sg_next(sg);
+	}
+
+	return vchan_tx_prep(&chan->vc, &desc->vd, xfer->flags);
+
+err_alloc:
+	if (desc)
+		dw_edma_free_desc(desc);
+
+	return NULL;
+}
+
+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)
+{
+	struct dw_edma_transfer xfer;
+
+	xfer.dchan = dchan;
+	xfer.direction = direction;
+	xfer.xfer.sg.sgl = sgl;
+	xfer.xfer.sg.len = len;
+	xfer.flags = flags;
+	xfer.cyclic = false;
+
+	return dw_edma_device_transfer(&xfer);
+}
+
+static struct dma_async_tx_descriptor *
+dw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr,
+			       size_t len, size_t count,
+			       enum dma_transfer_direction direction,
+			       unsigned long flags)
+{
+	struct dw_edma_transfer xfer;
+
+	xfer.dchan = dchan;
+	xfer.direction = direction;
+	xfer.xfer.cyclic.paddr = paddr;
+	xfer.xfer.cyclic.len = len;
+	xfer.xfer.cyclic.cnt = count;
+	xfer.flags = flags;
+	xfer.cyclic = true;
+
+	return dw_edma_device_transfer(&xfer);
+}
+
+static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
+{
+	struct dw_edma_desc *desc;
+	struct virt_dma_desc *vd;
+	unsigned long flags;
+
+	dw_edma_v0_core_clear_done_int(chan);
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+	vd = vchan_next_desc(&chan->vc);
+	if (vd) {
+		switch (chan->request) {
+		case EDMA_REQ_NONE:
+			desc = vd2dw_edma_desc(vd);
+			if (desc->chunks_alloc) {
+				chan->status = EDMA_ST_BUSY;
+				dw_edma_start_transfer(chan);
+			} else {
+				list_del(&vd->node);
+				vchan_cookie_complete(vd);
+				chan->status = EDMA_ST_IDLE;
+			}
+			break;
+
+		case EDMA_REQ_STOP:
+			list_del(&vd->node);
+			vchan_cookie_complete(vd);
+			chan->request = EDMA_REQ_NONE;
+			chan->status = EDMA_ST_IDLE;
+			break;
+
+		case EDMA_REQ_PAUSE:
+			chan->request = EDMA_REQ_NONE;
+			chan->status = EDMA_ST_PAUSE;
+			break;
+
+		default:
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
+{
+	struct virt_dma_desc *vd;
+	unsigned long flags;
+
+	dw_edma_v0_core_clear_abort_int(chan);
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+	vd = vchan_next_desc(&chan->vc);
+	if (vd) {
+		list_del(&vd->node);
+		vchan_cookie_complete(vd);
+	}
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+	chan->request = EDMA_REQ_NONE;
+	chan->status = EDMA_ST_IDLE;
+}
+
+static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write)
+{
+	struct dw_edma_irq *dw_irq = data;
+	struct dw_edma *dw = dw_irq->dw;
+	unsigned long total, pos, val;
+	unsigned long off;
+	u32 mask;
+
+	if (write) {
+		total = dw->wr_ch_cnt;
+		off = 0;
+		mask = dw_irq->wr_mask;
+	} else {
+		total = dw->rd_ch_cnt;
+		off = dw->wr_ch_cnt;
+		mask = dw_irq->rd_mask;
+	}
+
+	val = dw_edma_v0_core_status_done_int(dw, write ?
+							  EDMA_DIR_WRITE :
+							  EDMA_DIR_READ);
+	val &= mask;
+	for_each_set_bit(pos, &val, total) {
+		struct dw_edma_chan *chan = &dw->chan[pos + off];
+
+		dw_edma_done_interrupt(chan);
+	}
+
+	val = dw_edma_v0_core_status_abort_int(dw, write ?
+							   EDMA_DIR_WRITE :
+							   EDMA_DIR_READ);
+	val &= mask;
+	for_each_set_bit(pos, &val, total) {
+		struct dw_edma_chan *chan = &dw->chan[pos + off];
+
+		dw_edma_abort_interrupt(chan);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data)
+{
+	return dw_edma_interrupt(irq, data, true);
+}
+
+static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data)
+{
+	return dw_edma_interrupt(irq, data, false);
+}
+
+static irqreturn_t dw_edma_interrupt_common(int irq, void *data)
+{
+	dw_edma_interrupt(irq, data, true);
+	dw_edma_interrupt(irq, data, false);
+
+	return IRQ_HANDLED;
+}
+
+static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+
+	if (chan->status != EDMA_ST_IDLE)
+		return -EBUSY;
+
+	pm_runtime_get(chan->chip->dev);
+
+	return 0;
+}
+
+static void dw_edma_free_chan_resources(struct dma_chan *dchan)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(5000);
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	int ret;
+
+	while (time_before(jiffies, timeout)) {
+		ret = dw_edma_device_terminate_all(dchan);
+		if (!ret)
+			break;
+
+		if (time_after_eq(jiffies, timeout))
+			return;
+
+		cpu_relax();
+	};
+
+	pm_runtime_put(chan->chip->dev);
+}
+
+static int dw_edma_channel_setup(struct dw_edma_chip *chip, bool write,
+				 u32 wr_alloc, u32 rd_alloc)
+{
+	struct dw_edma_region *dt_region;
+	struct device *dev = chip->dev;
+	struct dw_edma *dw = chip->dw;
+	struct dw_edma_chan *chan;
+	size_t ll_chunk, dt_chunk;
+	struct dw_edma_irq *irq;
+	struct dma_device *dma;
+	u32 i, j, cnt, ch_cnt;
+	u32 alloc, off_alloc;
+	int err = 0;
+	u32 pos;
+
+	ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
+	ll_chunk = dw->ll_region.sz;
+	dt_chunk = dw->dt_region.sz;
+
+	/* Calculate linked list chunk for each channel */
+	ll_chunk /= roundup_pow_of_two(ch_cnt);
+
+	/* Calculate linked list chunk for each channel */
+	dt_chunk /= roundup_pow_of_two(ch_cnt);
+
+	if (write) {
+		i = 0;
+		cnt = dw->wr_ch_cnt;
+		dma = &dw->wr_edma;
+		alloc = wr_alloc;
+		off_alloc = 0;
+	} else {
+		i = dw->wr_ch_cnt;
+		cnt = dw->rd_ch_cnt;
+		dma = &dw->rd_edma;
+		alloc = rd_alloc;
+		off_alloc = wr_alloc;
+	}
+
+	INIT_LIST_HEAD(&dma->channels);
+	for (j = 0; (alloc || dw->nr_irqs == 1) && j < cnt; j++, i++) {
+		chan = &dw->chan[i];
+
+		dt_region = devm_kzalloc(dev, sizeof(*dt_region), GFP_KERNEL);
+		if (!dt_region)
+			return -ENOMEM;
+
+		chan->vc.chan.private = dt_region;
+
+		chan->chip = chip;
+		chan->id = j;
+		chan->dir = write ? EDMA_DIR_WRITE : EDMA_DIR_READ;
+		chan->configured = false;
+		chan->request = EDMA_REQ_NONE;
+		chan->status = EDMA_ST_IDLE;
+
+		chan->ll_off = (ll_chunk * i);
+		chan->ll_max = (ll_chunk / EDMA_LL_SZ) - 1;
+
+		chan->dt_off = (dt_chunk * i);
+
+		dev_vdbg(dev, "L. List:\tChannel %s[%u] off=0x%.8lx, max_cnt=%u\n",
+			 write ? "write" : "read", j,
+			 chan->ll_off, chan->ll_max);
+
+		if (dw->nr_irqs == 1)
+			pos = 0;
+		else
+			pos = off_alloc + (j % alloc);
+
+		irq = &dw->irq[pos];
+
+		if (write)
+			irq->wr_mask |= BIT(j);
+		else
+			irq->rd_mask |= BIT(j);
+
+		irq->dw = dw;
+		memcpy(&chan->msi, &irq->msi, sizeof(chan->msi));
+
+		dev_vdbg(dev, "MSI:\t\tChannel %s[%u] addr=0x%.8x%.8x, data=0x%.8x\n",
+			 write ? "write" : "read", j,
+			 chan->msi.address_hi, chan->msi.address_lo,
+			 chan->msi.data);
+
+		chan->vc.desc_free = vchan_free_desc;
+		vchan_init(&chan->vc, dma);
+
+		dt_region->paddr = dw->dt_region.paddr + chan->dt_off;
+		dt_region->vaddr = dw->dt_region.vaddr + chan->dt_off;
+		dt_region->sz = dt_chunk;
+
+		dev_vdbg(dev, "Data:\tChannel %s[%u] off=0x%.8lx\n",
+			 write ? "write" : "read", j, chan->dt_off);
+
+		dw_edma_v0_core_device_config(chan);
+	}
+
+	/* Set DMA channel capabilities */
+	dma_cap_zero(dma->cap_mask);
+	dma_cap_set(DMA_SLAVE, dma->cap_mask);
+	dma_cap_set(DMA_CYCLIC, dma->cap_mask);
+	dma_cap_set(DMA_PRIVATE, dma->cap_mask);
+	dma->directions = BIT(write ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV);
+	dma->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+	dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+	dma->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+	dma->chancnt = cnt;
+
+	/* Set DMA channel callbacks */
+	dma->dev = chip->dev;
+	dma->device_alloc_chan_resources = dw_edma_alloc_chan_resources;
+	dma->device_free_chan_resources = dw_edma_free_chan_resources;
+	dma->device_config = dw_edma_device_config;
+	dma->device_pause = dw_edma_device_pause;
+	dma->device_resume = dw_edma_device_resume;
+	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_dma_cyclic = dw_edma_device_prep_dma_cyclic;
+
+	dma_set_max_seg_size(dma->dev, U32_MAX);
+
+	/* Register DMA device */
+	err = dma_async_device_register(dma);
+
+	return err;
+}
+
+static inline void dw_edma_dec_irq_alloc(int *nr_irqs, u32 *alloc, u16 cnt)
+{
+	if (*nr_irqs && *alloc < cnt) {
+		(*alloc)++;
+		(*nr_irqs)--;
+	}
+}
+
+static inline void dw_edma_add_irq_mask(u32 *mask, u32 alloc, u16 cnt)
+{
+	while (*mask * alloc < cnt)
+		(*mask)++;
+}
+
+static int dw_edma_irq_request(struct dw_edma_chip *chip,
+			       u32 *wr_alloc, u32 *rd_alloc)
+{
+	struct device *dev = chip->dev;
+	struct dw_edma *dw = chip->dw;
+	u32 wr_mask = 1;
+	u32 rd_mask = 1;
+	int i, err = 0;
+	u32 ch_cnt;
+
+	ch_cnt = dw->wr_ch_cnt + dw->rd_ch_cnt;
+
+	if (dw->nr_irqs < 1)
+		return -EINVAL;
+
+	if (dw->nr_irqs == 1) {
+		/* Common IRQ shared among all channels */
+		err = request_irq(pci_irq_vector(to_pci_dev(dev), 0),
+				  dw_edma_interrupt_common,
+				  IRQF_SHARED, dw->name, &dw->irq[0]);
+		if (err) {
+			dw->nr_irqs = 0;
+			return err;
+		}
+
+		get_cached_msi_msg(pci_irq_vector(to_pci_dev(dev), 0),
+				   &dw->irq[0].msi);
+	} else {
+		/* Distribute IRQs equally among all channels */
+		int tmp = dw->nr_irqs;
+
+		while (tmp && (*wr_alloc + *rd_alloc) < ch_cnt) {
+			dw_edma_dec_irq_alloc(&tmp, wr_alloc, dw->wr_ch_cnt);
+			dw_edma_dec_irq_alloc(&tmp, rd_alloc, dw->rd_ch_cnt);
+		}
+
+		dw_edma_add_irq_mask(&wr_mask, *wr_alloc, dw->wr_ch_cnt);
+		dw_edma_add_irq_mask(&rd_mask, *rd_alloc, dw->rd_ch_cnt);
+
+		for (i = 0; i < (*wr_alloc + *rd_alloc); i++) {
+			err = request_irq(pci_irq_vector(to_pci_dev(dev), i),
+					  i < *wr_alloc ?
+						dw_edma_interrupt_write :
+						dw_edma_interrupt_read,
+					  IRQF_SHARED, dw->name,
+					  &dw->irq[i]);
+			if (err) {
+				dw->nr_irqs = i;
+				return err;
+			}
+
+			get_cached_msi_msg(pci_irq_vector(to_pci_dev(dev), i),
+					   &dw->irq[i].msi);
+		}
+
+		dw->nr_irqs = i;
+	}
+
+	return err;
+}
+
+int dw_edma_probe(struct dw_edma_chip *chip)
+{
+	struct device *dev = chip->dev;
+	struct dw_edma *dw = chip->dw;
+	u32 wr_alloc = 0;
+	u32 rd_alloc = 0;
+	int i, err;
+
+	raw_spin_lock_init(&dw->lock);
+
+	/* Find out how many write channels are supported by hardware */
+	dw->wr_ch_cnt = dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE);
+	if (!dw->wr_ch_cnt)
+		return -EINVAL;
+
+	/* Find out how many read channels are supported by hardware */
+	dw->rd_ch_cnt = dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ);
+	if (!dw->rd_ch_cnt)
+		return -EINVAL;
+
+	dev_vdbg(dev, "Channels:\twrite=%d, read=%d\n",
+		 dw->wr_ch_cnt, dw->rd_ch_cnt);
+
+	/* Allocate channels */
+	dw->chan = devm_kcalloc(dev, dw->wr_ch_cnt + dw->rd_ch_cnt,
+				sizeof(*dw->chan), GFP_KERNEL);
+	if (!dw->chan)
+		return -ENOMEM;
+
+	snprintf(dw->name, sizeof(dw->name), "dw-edma-core:%d", chip->id);
+
+	/* Disable eDMA, only to establish the ideal initial conditions */
+	dw_edma_v0_core_off(dw);
+
+	/* Request IRQs */
+	err = dw_edma_irq_request(chip, &wr_alloc, &rd_alloc);
+	if (err)
+		return err;
+
+	/* Setup write channels */
+	err = dw_edma_channel_setup(chip, true, wr_alloc, rd_alloc);
+	if (err)
+		goto err_irq_free;
+
+	/* Setup read channels */
+	err = dw_edma_channel_setup(chip, false, wr_alloc, rd_alloc);
+	if (err)
+		goto err_irq_free;
+
+	/* Power management */
+	pm_runtime_enable(dev);
+
+	/* Turn debugfs on */
+	dw_edma_v0_core_debugfs_on(chip);
+
+	return 0;
+
+err_irq_free:
+	for (i = (dw->nr_irqs - 1); i >= 0; i--)
+		free_irq(pci_irq_vector(to_pci_dev(dev), i), &dw->irq[i]);
+
+	dw->nr_irqs = 0;
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dw_edma_probe);
+
+int dw_edma_remove(struct dw_edma_chip *chip)
+{
+	struct dw_edma_chan *chan, *_chan;
+	struct device *dev = chip->dev;
+	struct dw_edma *dw = chip->dw;
+	int i;
+
+	/* Disable eDMA */
+	dw_edma_v0_core_off(dw);
+
+	/* Free irqs */
+	for (i = (dw->nr_irqs - 1); i >= 0; i--)
+		free_irq(pci_irq_vector(to_pci_dev(dev), i), &dw->irq[i]);
+
+	/* Power management */
+	pm_runtime_disable(dev);
+
+	list_for_each_entry_safe(chan, _chan, &dw->wr_edma.channels,
+				 vc.chan.device_node) {
+		list_del(&chan->vc.chan.device_node);
+		tasklet_kill(&chan->vc.task);
+	}
+
+	list_for_each_entry_safe(chan, _chan, &dw->rd_edma.channels,
+				 vc.chan.device_node) {
+		list_del(&chan->vc.chan.device_node);
+		tasklet_kill(&chan->vc.task);
+	}
+
+	/* Deregister eDMA device */
+	dma_async_device_unregister(&dw->wr_edma);
+	dma_async_device_unregister(&dw->rd_edma);
+
+	/* Turn debugfs off */
+	dw_edma_v0_core_debugfs_off();
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dw_edma_remove);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
+MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
new file mode 100644
index 0000000..b6cc90c
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA core driver
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#ifndef _DW_EDMA_CORE_H
+#define _DW_EDMA_CORE_H
+
+#include <linux/msi.h>
+#include <linux/dma/edma.h>
+
+#include "../virt-dma.h"
+
+#define EDMA_LL_SZ					24
+
+enum dw_edma_dir {
+	EDMA_DIR_WRITE = 0,
+	EDMA_DIR_READ
+};
+
+enum dw_edma_mode {
+	EDMA_MODE_LEGACY = 0,
+	EDMA_MODE_UNROLL
+};
+
+enum dw_edma_request {
+	EDMA_REQ_NONE = 0,
+	EDMA_REQ_STOP,
+	EDMA_REQ_PAUSE
+};
+
+enum dw_edma_status {
+	EDMA_ST_IDLE = 0,
+	EDMA_ST_PAUSE,
+	EDMA_ST_BUSY
+};
+
+struct dw_edma_chan;
+struct dw_edma_chunk;
+
+struct dw_edma_burst {
+	struct list_head		list;
+	u64				sar;
+	u64				dar;
+	u32				sz;
+};
+
+struct dw_edma_region {
+	phys_addr_t			paddr;
+	dma_addr_t			vaddr;
+	size_t				sz;
+};
+
+struct dw_edma_chunk {
+	struct list_head		list;
+	struct dw_edma_chan		*chan;
+	struct dw_edma_burst		*burst;
+
+	u32				bursts_alloc;
+
+	u8				cb;
+	struct dw_edma_region		ll_region;	/* Linked list */
+};
+
+struct dw_edma_desc {
+	struct virt_dma_desc		vd;
+	struct dw_edma_chan		*chan;
+	struct dw_edma_chunk		*chunk;
+
+	u32				chunks_alloc;
+
+	u32				alloc_sz;
+	u32				xfer_sz;
+};
+
+struct dw_edma_chan {
+	struct virt_dma_chan		vc;
+	struct dw_edma_chip		*chip;
+	int				id;
+	enum dw_edma_dir		dir;
+
+	off_t				ll_off;
+	u32				ll_max;
+
+	off_t				dt_off;
+
+	struct msi_msg			msi;
+
+	enum dw_edma_request		request;
+	enum dw_edma_status		status;
+	u8				configured;
+
+	struct dma_slave_config		config;
+};
+
+struct dw_edma_irq {
+	struct msi_msg                  msi;
+	u32				wr_mask;
+	u32				rd_mask;
+	struct dw_edma			*dw;
+};
+
+struct dw_edma {
+	char				name[20];
+
+	struct dma_device		wr_edma;
+	u16				wr_ch_cnt;
+
+	struct dma_device		rd_edma;
+	u16				rd_ch_cnt;
+
+	struct dw_edma_region		rg_region;	/* Registers */
+	struct dw_edma_region		ll_region;	/* Linked list */
+	struct dw_edma_region		dt_region;	/* Data */
+
+	struct dw_edma_irq		*irq;
+	int				nr_irqs;
+
+	u32				version;
+	enum dw_edma_mode		mode;
+
+	struct dw_edma_chan		*chan;
+	const struct dw_edma_core_ops	*ops;
+
+	raw_spinlock_t			lock;		/* Only for legacy */
+};
+
+struct dw_edma_sg {
+	struct scatterlist		*sgl;
+	unsigned int			len;
+};
+
+struct dw_edma_cyclic {
+	dma_addr_t			paddr;
+	size_t				len;
+	size_t				cnt;
+};
+
+struct dw_edma_transfer {
+	struct dma_chan			*dchan;
+	union dw_edma_xfer {
+		struct dw_edma_sg	sg;
+		struct dw_edma_cyclic	cyclic;
+	} xfer;
+	enum dma_transfer_direction	direction;
+	unsigned long			flags;
+	bool				cyclic;
+};
+
+static inline
+struct dw_edma_chan *vc2dw_edma_chan(struct virt_dma_chan *vc)
+{
+	return container_of(vc, struct dw_edma_chan, vc);
+}
+
+static inline
+struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
+{
+	return vc2dw_edma_chan(to_virt_chan(dchan));
+}
+
+#endif /* _DW_EDMA_CORE_H */
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
new file mode 100644
index 0000000..cab6e18
--- /dev/null
+++ b/include/linux/dma/edma.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA core driver
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#ifndef _DW_EDMA_H
+#define _DW_EDMA_H
+
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+
+struct dw_edma;
+
+/**
+ * struct dw_edma_chip - representation of DesignWare eDMA controller hardware
+ * @dev:		 struct device of the eDMA controller
+ * @id:			 instance ID
+ * @irq:		 irq line
+ * @dw:			 struct dw_edma that is filed by dw_edma_probe()
+ */
+struct dw_edma_chip {
+	struct device		*dev;
+	int			id;
+	int			irq;
+	struct dw_edma		*dw;
+};
+
+/* Export to the platform drivers */
+#if IS_ENABLED(CONFIG_DW_EDMA)
+int dw_edma_probe(struct dw_edma_chip *chip);
+int dw_edma_remove(struct dw_edma_chip *chip);
+#else
+static inline int dw_edma_probe(struct dw_edma_chip *chip)
+{
+	return -ENODEV;
+}
+
+static inline int dw_edma_remove(struct dw_edma_chip *chip)
+{
+	return 0;
+}
+#endif /* CONFIG_DW_EDMA */
+
+#endif /* _DW_EDMA_H */
-- 
2.7.4


^ permalink raw reply related

* [PATCH v2 6/6] MAINTAINERS: Add Synopsys eDMA IP driver maintainer
From: Gustavo Pimentel @ 2019-06-04 13:29 UTC (permalink / raw)
  To: linux-pci, dmaengine; +Cc: Gustavo Pimentel, Vinod Koul, Joao Pinto
In-Reply-To: <cover.1559654565.git.gustavo.pimentel@synopsys.com>

Add Synopsys eDMA IP driver maintainer.

This driver aims to support Synopsys eDMA IP and is normally distributed
along with Synopsys PCIe EndPoint IP (depends of the use and licensing
agreement).

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Joao Pinto <jpinto@synopsys.com>
---
Changes:
v1 -> v2:
 - Rebased to v5.2-rc1

 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5cfbea4..a43f960 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4594,6 +4594,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
-- 
2.7.4


^ permalink raw reply related

* [PATCH v2 2/6] dmaengine: Add Synopsys eDMA IP version 0 support
From: Gustavo Pimentel @ 2019-06-04 13:29 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Dan Williams, Andy Shevchenko,
	Russell King, Joao Pinto
In-Reply-To: <cover.1559654565.git.gustavo.pimentel@synopsys.com>

Add support for the eDMA IP version 0 driver for both register maps (legacy
and unroll).

The legacy register mapping was the initial implementation, which consisted
in having all registers belonging to channels multiplexed, which could be
change anytime (which could led a race-condition) by view port register
(access to only one channel available each time).

This register mapping is not very effective and efficient in a multithread
environment, which has led to the development of unroll registers mapping,
which consists of having all channels registers accessible any time by
spreading all channels registers by an offset between them.

This version supports a maximum of 16 independent channels (8 write +
8 read), which can run simultaneously.

Implements a scatter-gather transfer through a linked list, where the size
of linked list depends on the allocated memory divided equally among all
channels.

Each linked list descriptor can transfer from 1 byte to 4 Gbytes and is
alignmented to DWORD.

Both SAR (Source Address Register) and DAR (Destination Address Register)
are alignmented to byte.

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Joao Pinto <jpinto@synopsys.com>
---
Changes:
v1 -> v2:
 - Rebased to v5.2-rc1

 drivers/dma/dw-edma/Makefile          |   3 +-
 drivers/dma/dw-edma/dw-edma-core.c    |   1 +
 drivers/dma/dw-edma/dw-edma-v0-core.c | 352 ++++++++++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-core.h |  28 +++
 drivers/dma/dw-edma/dw-edma-v0-regs.h | 158 +++++++++++++++
 5 files changed, 541 insertions(+), 1 deletion(-)
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-core.c
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-core.h
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-regs.h

diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
index 3224010..01c7c63 100644
--- a/drivers/dma/dw-edma/Makefile
+++ b/drivers/dma/dw-edma/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_DW_EDMA)		+= dw-edma.o
-dw-edma-objs			:= dw-edma-core.o
+dw-edma-objs			:= dw-edma-core.o \
+					dw-edma-v0-core.o
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index c9d032f..60d6a46 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -17,6 +17,7 @@
 #include <linux/pci.h>
 
 #include "dw-edma-core.h"
+#include "dw-edma-v0-core.h"
 #include "../dmaengine.h"
 #include "../virt-dma.h"
 
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
new file mode 100644
index 0000000..a38c473
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#include <linux/bitfield.h>
+
+#include "dw-edma-core.h"
+#include "dw-edma-v0-core.h"
+#include "dw-edma-v0-regs.h"
+#include "dw-edma-v0-debugfs.h"
+
+enum dw_edma_control {
+	DW_EDMA_V0_CB					= BIT(0),
+	DW_EDMA_V0_TCB					= BIT(1),
+	DW_EDMA_V0_LLP					= BIT(2),
+	DW_EDMA_V0_LIE					= BIT(3),
+	DW_EDMA_V0_RIE					= BIT(4),
+	DW_EDMA_V0_CCS					= BIT(8),
+	DW_EDMA_V0_LLE					= BIT(9),
+};
+
+static inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw)
+{
+	return (struct dw_edma_v0_regs __iomem *)dw->rg_region.vaddr;
+}
+
+#define SET(dw, name, value)				\
+	writel(value, &(__dw_regs(dw)->name))
+
+#define GET(dw, name)					\
+	readl(&(__dw_regs(dw)->name))
+
+#define SET_RW(dw, dir, name, value)			\
+	do {						\
+		if ((dir) == EDMA_DIR_WRITE)		\
+			SET(dw, wr_##name, value);	\
+		else					\
+			SET(dw, rd_##name, value);	\
+	} while (0)
+
+#define GET_RW(dw, dir, name)				\
+	((dir) == EDMA_DIR_WRITE			\
+	  ? GET(dw, wr_##name)				\
+	  : GET(dw, rd_##name))
+
+#define SET_BOTH(dw, name, value)			\
+	do {						\
+		SET(dw, wr_##name, value);		\
+		SET(dw, rd_##name, value);		\
+	} while (0)
+
+static inline struct dw_edma_v0_ch_regs __iomem *
+__dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch)
+{
+	if (dw->mode == EDMA_MODE_LEGACY)
+		return &(__dw_regs(dw)->type.legacy.ch);
+
+	if (dir == EDMA_DIR_WRITE)
+		return &__dw_regs(dw)->type.unroll.ch[ch].wr;
+
+	return &__dw_regs(dw)->type.unroll.ch[ch].rd;
+}
+
+static inline void writel_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
+			     u32 value, void __iomem *addr)
+{
+	if (dw->mode == EDMA_MODE_LEGACY) {
+		u32 viewport_sel;
+		unsigned long flags;
+
+		raw_spin_lock_irqsave(&dw->lock, flags);
+
+		viewport_sel = FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
+		if (dir == EDMA_DIR_READ)
+			viewport_sel |= BIT(31);
+
+		writel(viewport_sel,
+		       &(__dw_regs(dw)->type.legacy.viewport_sel));
+		writel(value, addr);
+
+		raw_spin_unlock_irqrestore(&dw->lock, flags);
+	} else {
+		writel(value, addr);
+	}
+}
+
+static inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
+			   const void __iomem *addr)
+{
+	u32 value;
+
+	if (dw->mode == EDMA_MODE_LEGACY) {
+		u32 viewport_sel;
+		unsigned long flags;
+
+		raw_spin_lock_irqsave(&dw->lock, flags);
+
+		viewport_sel = FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
+		if (dir == EDMA_DIR_READ)
+			viewport_sel |= BIT(31);
+
+		writel(viewport_sel,
+		       &(__dw_regs(dw)->type.legacy.viewport_sel));
+		value = readl(addr);
+
+		raw_spin_unlock_irqrestore(&dw->lock, flags);
+	} else {
+		value = readl(addr);
+	}
+
+	return value;
+}
+
+#define SET_CH(dw, dir, ch, name, value) \
+	writel_ch(dw, dir, ch, value, &(__dw_ch_regs(dw, dir, ch)->name))
+
+#define GET_CH(dw, dir, ch, name) \
+	readl_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
+
+#define SET_LL(ll, value) \
+	writel(value, ll)
+
+/* eDMA management callbacks */
+void dw_edma_v0_core_off(struct dw_edma *dw)
+{
+	SET_BOTH(dw, int_mask, EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
+	SET_BOTH(dw, int_clear, EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK);
+	SET_BOTH(dw, engine_en, 0);
+}
+
+u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir)
+{
+	u32 num_ch;
+
+	if (dir == EDMA_DIR_WRITE)
+		num_ch = FIELD_GET(EDMA_V0_WRITE_CH_COUNT_MASK, GET(dw, ctrl));
+	else
+		num_ch = FIELD_GET(EDMA_V0_READ_CH_COUNT_MASK, GET(dw, ctrl));
+
+	if (num_ch > EDMA_V0_MAX_NR_CH)
+		num_ch = EDMA_V0_MAX_NR_CH;
+
+	return (u16)num_ch;
+}
+
+enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan)
+{
+	struct dw_edma *dw = chan->chip->dw;
+	u32 tmp;
+
+	tmp = FIELD_GET(EDMA_V0_CH_STATUS_MASK,
+			GET_CH(dw, chan->dir, chan->id, ch_control1));
+
+	if (tmp == 1)
+		return DMA_IN_PROGRESS;
+	else if (tmp == 3)
+		return DMA_COMPLETE;
+	else
+		return DMA_ERROR;
+}
+
+void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan)
+{
+	struct dw_edma *dw = chan->chip->dw;
+
+	SET_RW(dw, chan->dir, int_clear,
+	       FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)));
+}
+
+void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan)
+{
+	struct dw_edma *dw = chan->chip->dw;
+
+	SET_RW(dw, chan->dir, int_clear,
+	       FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)));
+}
+
+u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir)
+{
+	return FIELD_GET(EDMA_V0_DONE_INT_MASK, GET_RW(dw, dir, int_status));
+}
+
+u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir)
+{
+	return FIELD_GET(EDMA_V0_ABORT_INT_MASK, GET_RW(dw, dir, int_status));
+}
+
+static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
+{
+	struct dw_edma_burst *child;
+	struct dw_edma_v0_lli *lli;
+	struct dw_edma_v0_llp *llp;
+	u32 control = 0, i = 0;
+	u64 sar, dar, addr;
+	int j;
+
+	lli = (struct dw_edma_v0_lli *)chunk->ll_region.vaddr;
+
+	if (chunk->cb)
+		control = DW_EDMA_V0_CB;
+
+	j = chunk->bursts_alloc;
+	list_for_each_entry(child, &chunk->burst->list, list) {
+		j--;
+		if (!j)
+			control |= (DW_EDMA_V0_LIE | DW_EDMA_V0_RIE);
+
+		/* Channel control */
+		SET_LL(&lli[i].control, control);
+		/* Transfer size */
+		SET_LL(&lli[i].transfer_size, child->sz);
+		/* SAR - low, high */
+		sar = cpu_to_le64(child->sar);
+		SET_LL(&lli[i].sar_low, lower_32_bits(sar));
+		SET_LL(&lli[i].sar_high, upper_32_bits(sar));
+		/* DAR - low, high */
+		dar = cpu_to_le64(child->dar);
+		SET_LL(&lli[i].dar_low, lower_32_bits(dar));
+		SET_LL(&lli[i].dar_high, upper_32_bits(dar));
+		i++;
+	}
+
+	llp = (struct dw_edma_v0_llp *)&lli[i];
+	control = DW_EDMA_V0_LLP | DW_EDMA_V0_TCB;
+	if (!chunk->cb)
+		control |= DW_EDMA_V0_CB;
+
+	/* Channel control */
+	SET_LL(&llp->control, control);
+	/* Linked list  - low, high */
+	addr = cpu_to_le64(chunk->ll_region.paddr);
+	SET_LL(&llp->llp_low, lower_32_bits(addr));
+	SET_LL(&llp->llp_high, upper_32_bits(addr));
+}
+
+void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+{
+	struct dw_edma_chan *chan = chunk->chan;
+	struct dw_edma *dw = chan->chip->dw;
+	u32 tmp;
+	u64 llp;
+
+	dw_edma_v0_core_write_chunk(chunk);
+
+	if (first) {
+		/* Enable engine */
+		SET_RW(dw, chan->dir, engine_en, BIT(0));
+		/* Interrupt unmask - done, abort */
+		tmp = GET_RW(dw, chan->dir, int_mask);
+		tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
+		tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
+		SET_RW(dw, chan->dir, int_mask, tmp);
+		/* Linked list error */
+		tmp = GET_RW(dw, chan->dir, linked_list_err_en);
+		tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
+		SET_RW(dw, chan->dir, linked_list_err_en, tmp);
+		/* Channel control */
+		SET_CH(dw, chan->dir, chan->id, ch_control1,
+		       (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
+		/* Linked list - low, high */
+		llp = cpu_to_le64(chunk->ll_region.paddr);
+		SET_CH(dw, chan->dir, chan->id, llp_low, lower_32_bits(llp));
+		SET_CH(dw, chan->dir, chan->id, llp_high, upper_32_bits(llp));
+	}
+	/* Doorbell */
+	SET_RW(dw, chan->dir, doorbell,
+	       FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
+}
+
+int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
+{
+	struct dw_edma *dw = chan->chip->dw;
+	u32 tmp = 0;
+
+	/* MSI done addr - low, high */
+	SET_RW(dw, chan->dir, done_imwr_low, chan->msi.address_lo);
+	SET_RW(dw, chan->dir, done_imwr_high, chan->msi.address_hi);
+	/* MSI abort addr - low, high */
+	SET_RW(dw, chan->dir, abort_imwr_low, chan->msi.address_lo);
+	SET_RW(dw, chan->dir, abort_imwr_high, chan->msi.address_hi);
+	/* MSI data - low, high */
+	switch (chan->id) {
+	case 0:
+	case 1:
+		tmp = GET_RW(dw, chan->dir, ch01_imwr_data);
+		break;
+
+	case 2:
+	case 3:
+		tmp = GET_RW(dw, chan->dir, ch23_imwr_data);
+		break;
+
+	case 4:
+	case 5:
+		tmp = GET_RW(dw, chan->dir, ch45_imwr_data);
+		break;
+
+	case 6:
+	case 7:
+		tmp = GET_RW(dw, chan->dir, ch67_imwr_data);
+		break;
+	}
+
+	if (chan->id & BIT(0)) {
+		/* Channel odd {1, 3, 5, 7} */
+		tmp &= EDMA_V0_CH_EVEN_MSI_DATA_MASK;
+		tmp |= FIELD_PREP(EDMA_V0_CH_ODD_MSI_DATA_MASK,
+				  chan->msi.data);
+	} else {
+		/* Channel even {0, 2, 4, 6} */
+		tmp &= EDMA_V0_CH_ODD_MSI_DATA_MASK;
+		tmp |= FIELD_PREP(EDMA_V0_CH_EVEN_MSI_DATA_MASK,
+				  chan->msi.data);
+	}
+
+	switch (chan->id) {
+	case 0:
+	case 1:
+		SET_RW(dw, chan->dir, ch01_imwr_data, tmp);
+		break;
+
+	case 2:
+	case 3:
+		SET_RW(dw, chan->dir, ch23_imwr_data, tmp);
+		break;
+
+	case 4:
+	case 5:
+		SET_RW(dw, chan->dir, ch45_imwr_data, tmp);
+		break;
+
+	case 6:
+	case 7:
+		SET_RW(dw, chan->dir, ch67_imwr_data, tmp);
+		break;
+	}
+
+	return 0;
+}
+
+/* eDMA debugfs callbacks */
+void dw_edma_v0_core_debugfs_on(struct dw_edma_chip *chip)
+{
+}
+
+void dw_edma_v0_core_debugfs_off(void)
+{
+}
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h
new file mode 100644
index 0000000..abae152
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#ifndef _DW_EDMA_V0_CORE_H
+#define _DW_EDMA_V0_CORE_H
+
+#include <linux/dma/edma.h>
+
+/* eDMA management callbacks */
+void dw_edma_v0_core_off(struct dw_edma *chan);
+u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir);
+enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan);
+void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan);
+void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan);
+u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir);
+u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir);
+void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first);
+int dw_edma_v0_core_device_config(struct dw_edma_chan *chan);
+/* eDMA debug fs callbacks */
+void dw_edma_v0_core_debugfs_on(struct dw_edma_chip *chip);
+void dw_edma_v0_core_debugfs_off(void);
+
+#endif /* _DW_EDMA_V0_CORE_H */
diff --git a/drivers/dma/dw-edma/dw-edma-v0-regs.h b/drivers/dma/dw-edma/dw-edma-v0-regs.h
new file mode 100644
index 0000000..cd64768
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-regs.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#ifndef _DW_EDMA_V0_REGS_H
+#define _DW_EDMA_V0_REGS_H
+
+#include <linux/dmaengine.h>
+
+#define EDMA_V0_MAX_NR_CH				8
+#define EDMA_V0_VIEWPORT_MASK				GENMASK(2, 0)
+#define EDMA_V0_DONE_INT_MASK				GENMASK(7, 0)
+#define EDMA_V0_ABORT_INT_MASK				GENMASK(23, 16)
+#define EDMA_V0_WRITE_CH_COUNT_MASK			GENMASK(3, 0)
+#define EDMA_V0_READ_CH_COUNT_MASK			GENMASK(19, 16)
+#define EDMA_V0_CH_STATUS_MASK				GENMASK(6, 5)
+#define EDMA_V0_DOORBELL_CH_MASK			GENMASK(2, 0)
+#define EDMA_V0_LINKED_LIST_ERR_MASK			GENMASK(7, 0)
+
+#define EDMA_V0_CH_ODD_MSI_DATA_MASK			GENMASK(31, 16)
+#define EDMA_V0_CH_EVEN_MSI_DATA_MASK			GENMASK(15, 0)
+
+struct dw_edma_v0_ch_regs {
+	u32 ch_control1;				/* 0x000 */
+	u32 ch_control2;				/* 0x004 */
+	u32 transfer_size;				/* 0x008 */
+	u32 sar_low;					/* 0x00c */
+	u32 sar_high;					/* 0x010 */
+	u32 dar_low;					/* 0x014 */
+	u32 dar_high;					/* 0x018 */
+	u32 llp_low;					/* 0x01c */
+	u32 llp_high;					/* 0x020 */
+};
+
+struct dw_edma_v0_ch {
+	struct dw_edma_v0_ch_regs wr;			/* 0x200 */
+	u32 padding_1[55];				/* [0x224..0x2fc] */
+	struct dw_edma_v0_ch_regs rd;			/* 0x300 */
+	u32 padding_2[55];				/* [0x224..0x2fc] */
+};
+
+struct dw_edma_v0_unroll {
+	u32 padding_1;					/* 0x0f8 */
+	u32 wr_engine_chgroup;				/* 0x100 */
+	u32 rd_engine_chgroup;				/* 0x104 */
+	u32 wr_engine_hshake_cnt_low;			/* 0x108 */
+	u32 wr_engine_hshake_cnt_high;			/* 0x10c */
+	u32 padding_2[2];				/* [0x110..0x114] */
+	u32 rd_engine_hshake_cnt_low;			/* 0x118 */
+	u32 rd_engine_hshake_cnt_high;			/* 0x11c */
+	u32 padding_3[2];				/* [0x120..0x124] */
+	u32 wr_ch0_pwr_en;				/* 0x128 */
+	u32 wr_ch1_pwr_en;				/* 0x12c */
+	u32 wr_ch2_pwr_en;				/* 0x130 */
+	u32 wr_ch3_pwr_en;				/* 0x134 */
+	u32 wr_ch4_pwr_en;				/* 0x138 */
+	u32 wr_ch5_pwr_en;				/* 0x13c */
+	u32 wr_ch6_pwr_en;				/* 0x140 */
+	u32 wr_ch7_pwr_en;				/* 0x144 */
+	u32 padding_4[8];				/* [0x148..0x164] */
+	u32 rd_ch0_pwr_en;				/* 0x168 */
+	u32 rd_ch1_pwr_en;				/* 0x16c */
+	u32 rd_ch2_pwr_en;				/* 0x170 */
+	u32 rd_ch3_pwr_en;				/* 0x174 */
+	u32 rd_ch4_pwr_en;				/* 0x178 */
+	u32 rd_ch5_pwr_en;				/* 0x18c */
+	u32 rd_ch6_pwr_en;				/* 0x180 */
+	u32 rd_ch7_pwr_en;				/* 0x184 */
+	u32 padding_5[30];				/* [0x188..0x1fc] */
+	struct dw_edma_v0_ch ch[EDMA_V0_MAX_NR_CH];	/* [0x200..0x1120] */
+};
+
+struct dw_edma_v0_legacy {
+	u32 viewport_sel;				/* 0x0f8 */
+	struct dw_edma_v0_ch_regs ch;			/* [0x100..0x120] */
+};
+
+struct dw_edma_v0_regs {
+	/* eDMA global registers */
+	u32 ctrl_data_arb_prior;			/* 0x000 */
+	u32 padding_1;					/* 0x004 */
+	u32 ctrl;					/* 0x008 */
+	u32 wr_engine_en;				/* 0x00c */
+	u32 wr_doorbell;				/* 0x010 */
+	u32 padding_2;					/* 0x014 */
+	u32 wr_ch_arb_weight_low;			/* 0x018 */
+	u32 wr_ch_arb_weight_high;			/* 0x01c */
+	u32 padding_3[3];				/* [0x020..0x028] */
+	u32 rd_engine_en;				/* 0x02c */
+	u32 rd_doorbell;				/* 0x030 */
+	u32 padding_4;					/* 0x034 */
+	u32 rd_ch_arb_weight_low;			/* 0x038 */
+	u32 rd_ch_arb_weight_high;			/* 0x03c */
+	u32 padding_5[3];				/* [0x040..0x048] */
+	/* eDMA interrupts registers */
+	u32 wr_int_status;				/* 0x04c */
+	u32 padding_6;					/* 0x050 */
+	u32 wr_int_mask;				/* 0x054 */
+	u32 wr_int_clear;				/* 0x058 */
+	u32 wr_err_status;				/* 0x05c */
+	u32 wr_done_imwr_low;				/* 0x060 */
+	u32 wr_done_imwr_high;				/* 0x064 */
+	u32 wr_abort_imwr_low;				/* 0x068 */
+	u32 wr_abort_imwr_high;				/* 0x06c */
+	u32 wr_ch01_imwr_data;				/* 0x070 */
+	u32 wr_ch23_imwr_data;				/* 0x074 */
+	u32 wr_ch45_imwr_data;				/* 0x078 */
+	u32 wr_ch67_imwr_data;				/* 0x07c */
+	u32 padding_7[4];				/* [0x080..0x08c] */
+	u32 wr_linked_list_err_en;			/* 0x090 */
+	u32 padding_8[3];				/* [0x094..0x09c] */
+	u32 rd_int_status;				/* 0x0a0 */
+	u32 padding_9;					/* 0x0a4 */
+	u32 rd_int_mask;				/* 0x0a8 */
+	u32 rd_int_clear;				/* 0x0ac */
+	u32 padding_10;					/* 0x0b0 */
+	u32 rd_err_status_low;				/* 0x0b4 */
+	u32 rd_err_status_high;				/* 0x0b8 */
+	u32 padding_11[2];				/* [0x0bc..0x0c0] */
+	u32 rd_linked_list_err_en;			/* 0x0c4 */
+	u32 padding_12;					/* 0x0c8 */
+	u32 rd_done_imwr_low;				/* 0x0cc */
+	u32 rd_done_imwr_high;				/* 0x0d0 */
+	u32 rd_abort_imwr_low;				/* 0x0d4 */
+	u32 rd_abort_imwr_high;				/* 0x0d8 */
+	u32 rd_ch01_imwr_data;				/* 0x0dc */
+	u32 rd_ch23_imwr_data;				/* 0x0e0 */
+	u32 rd_ch45_imwr_data;				/* 0x0e4 */
+	u32 rd_ch67_imwr_data;				/* 0x0e8 */
+	u32 padding_13[4];				/* [0x0ec..0x0f8] */
+	/* eDMA channel context grouping */
+	union dw_edma_v0_type {
+		struct dw_edma_v0_legacy legacy;	/* [0x0f8..0x120] */
+		struct dw_edma_v0_unroll unroll;	/* [0x0f8..0x1120] */
+	} type;
+};
+
+struct dw_edma_v0_lli {
+	u32 control;
+	u32 transfer_size;
+	u32 sar_low;
+	u32 sar_high;
+	u32 dar_low;
+	u32 dar_high;
+};
+
+struct dw_edma_v0_llp {
+	u32 control;
+	u32 reserved;
+	u32 llp_low;
+	u32 llp_high;
+};
+
+#endif /* _DW_EDMA_V0_REGS_H */
-- 
2.7.4


^ permalink raw reply related

* [PATCH v2 5/6] dmaengine: Add Synopsys eDMA IP PCIe glue-logic
From: Gustavo Pimentel @ 2019-06-04 13:29 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Dan Williams, Russell King,
	Lorenzo Pieralisi, Joao Pinto
In-Reply-To: <cover.1559654565.git.gustavo.pimentel@synopsys.com>

Synopsys eDMA IP is normally distributed along with Synopsys PCIe
EndPoint IP (depends of the use and licensing agreement).

This IP requires some basic configurations, such as:
 - eDMA registers BAR
 - eDMA registers offset
 - eDMA registers size
 - eDMA linked list memory BAR
 - eDMA linked list memory offset
 - eDMA linked list memory size
 - eDMA data memory BAR
 - eDMA data memory offset
 - eDMA data memory size
 - eDMA version
 - eDMA mode
 - IRQs available for eDMA

As a working example, PCIe glue-logic will attach to a Synopsys PCIe
EndPoint IP prototype kit (Vendor ID = 0x16c3, Device ID = 0xedda),
which has built-in an eDMA IP with this default configuration:
 - eDMA registers BAR = 0
 - eDMA registers offset = 0x00001000 (4 Kbytes)
 - eDMA registers size = 0x00002000 (8 Kbytes)
 - eDMA linked list memory BAR = 2
 - eDMA linked list memory offset = 0x00000000 (0 Kbytes)
 - eDMA linked list memory size = 0x00800000 (8 Mbytes)
 - eDMA data memory BAR = 2
 - eDMA data memory offset = 0x00800000 (8 Mbytes)
 - eDMA data memory size = 0x03800000 (56 Mbytes)
 - eDMA version = 0
 - eDMA mode = EDMA_MODE_UNROLL
 - IRQs = 1

This driver can be compile as built-in or external module in kernel.

To enable this driver just select DW_EDMA_PCIE option in kernel
configuration, however it requires and selects automatically DW_EDMA
option too.

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Joao Pinto <jpinto@synopsys.com>
Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
---
Changes:
v1 -> v2:
 - Rebased to v5.2-rc1

 drivers/dma/dw-edma/Kconfig        |   9 ++
 drivers/dma/dw-edma/Makefile       |   1 +
 drivers/dma/dw-edma/dw-edma-pcie.c | 229 +++++++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 drivers/dma/dw-edma/dw-edma-pcie.c

diff --git a/drivers/dma/dw-edma/Kconfig b/drivers/dma/dw-edma/Kconfig
index 3016bed..c0838ce 100644
--- a/drivers/dma/dw-edma/Kconfig
+++ b/drivers/dma/dw-edma/Kconfig
@@ -7,3 +7,12 @@ config DW_EDMA
 	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.
diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
index 0c53033..8d45c0d 100644
--- a/drivers/dma/dw-edma/Makefile
+++ b/drivers/dma/dw-edma/Makefile
@@ -4,3 +4,4 @@ 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
diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
new file mode 100644
index 0000000..4c96e1c
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA PCIe driver
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/dma/edma.h>
+#include <linux/pci-epf.h>
+#include <linux/msi.h>
+
+#include "dw-edma-core.h"
+
+struct dw_edma_pcie_data {
+	/* eDMA registers location */
+	enum pci_barno			rg_bar;
+	off_t				rg_off;
+	size_t				rg_sz;
+	/* eDMA memory linked list location */
+	enum pci_barno			ll_bar;
+	off_t				ll_off;
+	size_t				ll_sz;
+	/* eDMA memory data location */
+	enum pci_barno			dt_bar;
+	off_t				dt_off;
+	size_t				dt_sz;
+	/* Other */
+	u32				version;
+	enum dw_edma_mode		mode;
+	u8				irqs;
+};
+
+static const struct dw_edma_pcie_data snps_edda_data = {
+	/* eDMA registers location */
+	.rg_bar				= BAR_0,
+	.rg_off				= 0x00001000,	/*  4 Kbytes */
+	.rg_sz				= 0x00002000,	/*  8 Kbytes */
+	/* eDMA memory linked list location */
+	.ll_bar				= BAR_2,
+	.ll_off				= 0x00000000,	/*  0 Kbytes */
+	.ll_sz				= 0x00800000,	/*  8 Mbytes */
+	/* eDMA memory data location */
+	.dt_bar				= BAR_2,
+	.dt_off				= 0x00800000,	/*  8 Mbytes */
+	.dt_sz				= 0x03800000,	/* 56 Mbytes */
+	/* Other */
+	.version			= 0,
+	.mode				= EDMA_MODE_UNROLL,
+	.irqs				= 1,
+};
+
+static int dw_edma_pcie_probe(struct pci_dev *pdev,
+			      const struct pci_device_id *pid)
+{
+	const struct dw_edma_pcie_data *pdata = (void *)pid->driver_data;
+	struct device *dev = &pdev->dev;
+	struct dw_edma_chip *chip;
+	int err, nr_irqs;
+	struct dw_edma *dw;
+
+	/* Enable PCI device */
+	err = pcim_enable_device(pdev);
+	if (err) {
+		pci_err(pdev, "enabling device failed\n");
+		return err;
+	}
+
+	/* Mapping PCI BAR regions */
+	err = pcim_iomap_regions(pdev, BIT(pdata->rg_bar) |
+				       BIT(pdata->ll_bar) |
+				       BIT(pdata->dt_bar),
+				 pci_name(pdev));
+	if (err) {
+		pci_err(pdev, "eDMA BAR I/O remapping failed\n");
+		return err;
+	}
+
+	pci_set_master(pdev);
+
+	/* DMA configuration */
+	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+	if (!err) {
+		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+		if (err) {
+			pci_err(pdev, "consistent DMA mask 64 set failed\n");
+			return err;
+		}
+	} else {
+		pci_err(pdev, "DMA mask 64 set failed\n");
+
+		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+		if (err) {
+			pci_err(pdev, "DMA mask 32 set failed\n");
+			return err;
+		}
+
+		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+		if (err) {
+			pci_err(pdev, "consistent DMA mask 32 set failed\n");
+			return err;
+		}
+	}
+
+	/* Data structure allocation */
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL);
+	if (!dw)
+		return -ENOMEM;
+
+	/* IRQs allocation */
+	nr_irqs = pci_alloc_irq_vectors(pdev, 1, pdata->irqs,
+					PCI_IRQ_MSI | PCI_IRQ_MSIX);
+	if (nr_irqs < 1) {
+		pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n",
+			nr_irqs);
+		return -EPERM;
+	}
+
+	/* Data structure initialization */
+	chip->dw = dw;
+	chip->dev = dev;
+	chip->id = pdev->devfn;
+	chip->irq = pdev->irq;
+
+	dw->rg_region.vaddr = (dma_addr_t)pcim_iomap_table(pdev)[pdata->rg_bar];
+	dw->rg_region.vaddr += pdata->rg_off;
+	dw->rg_region.paddr = pdev->resource[pdata->rg_bar].start;
+	dw->rg_region.paddr += pdata->rg_off;
+	dw->rg_region.sz = pdata->rg_sz;
+
+	dw->ll_region.vaddr = (dma_addr_t)pcim_iomap_table(pdev)[pdata->ll_bar];
+	dw->ll_region.vaddr += pdata->ll_off;
+	dw->ll_region.paddr = pdev->resource[pdata->ll_bar].start;
+	dw->ll_region.paddr += pdata->ll_off;
+	dw->ll_region.sz = pdata->ll_sz;
+
+	dw->dt_region.vaddr = (dma_addr_t)pcim_iomap_table(pdev)[pdata->dt_bar];
+	dw->dt_region.vaddr += pdata->dt_off;
+	dw->dt_region.paddr = pdev->resource[pdata->dt_bar].start;
+	dw->dt_region.paddr += pdata->dt_off;
+	dw->dt_region.sz = pdata->dt_sz;
+
+	dw->version = pdata->version;
+	dw->mode = pdata->mode;
+	dw->nr_irqs = nr_irqs;
+
+	/* Debug info */
+	pci_dbg(pdev, "Version:\t%u\n", dw->version);
+
+	pci_dbg(pdev, "Mode:\t%s\n",
+		dw->mode == EDMA_MODE_LEGACY ? "Legacy" : "Unroll");
+
+	pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%pa, p=%pa)\n",
+		pdata->rg_bar, pdata->rg_off, pdata->rg_sz,
+		&dw->rg_region.vaddr, &dw->rg_region.paddr);
+
+	pci_dbg(pdev, "L. List:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%pa, p=%pa)\n",
+		pdata->ll_bar, pdata->ll_off, pdata->ll_sz,
+		&dw->ll_region.vaddr, &dw->ll_region.paddr);
+
+	pci_dbg(pdev, "Data:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%pa, p=%pa)\n",
+		pdata->dt_bar, pdata->dt_off, pdata->dt_sz,
+		&dw->dt_region.vaddr, &dw->dt_region.paddr);
+
+	pci_dbg(pdev, "Nr. IRQs:\t%u\n", dw->nr_irqs);
+
+	/* Validating if PCI interrupts were enabled */
+	if (!pci_dev_msi_enabled(pdev)) {
+		pci_err(pdev, "enable interrupt failed\n");
+		return -EPERM;
+	}
+
+	dw->irq = devm_kcalloc(dev, nr_irqs, sizeof(*dw->irq), GFP_KERNEL);
+	if (!dw->irq)
+		return -ENOMEM;
+
+	/* Starting eDMA driver */
+	err = dw_edma_probe(chip);
+	if (err) {
+		pci_err(pdev, "eDMA probe failed\n");
+		return err;
+	}
+
+	/* Saving data structure reference */
+	pci_set_drvdata(pdev, chip);
+
+	return 0;
+}
+
+static void dw_edma_pcie_remove(struct pci_dev *pdev)
+{
+	struct dw_edma_chip *chip = pci_get_drvdata(pdev);
+	int err;
+
+	/* Stopping eDMA driver */
+	err = dw_edma_remove(chip);
+	if (err)
+		pci_warn(pdev, "can't remove device properly: %d\n", err);
+
+	/* Freeing IRQs */
+	pci_free_irq_vectors(pdev);
+}
+
+static const struct pci_device_id dw_edma_pcie_id_table[] = {
+	{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
+
+static struct pci_driver dw_edma_pcie_driver = {
+	.name		= "dw-edma-pcie",
+	.id_table	= dw_edma_pcie_id_table,
+	.probe		= dw_edma_pcie_probe,
+	.remove		= dw_edma_pcie_remove,
+};
+
+module_pci_driver(dw_edma_pcie_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Synopsys DesignWare eDMA PCIe driver");
+MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
-- 
2.7.4


^ permalink raw reply related

* [PATCH v2 3/6] dmaengine: Add Synopsys eDMA IP version 0 debugfs support
From: Gustavo Pimentel @ 2019-06-04 13:29 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Dan Williams, Andy Shevchenko,
	Russell King, Joao Pinto
In-Reply-To: <cover.1559654565.git.gustavo.pimentel@synopsys.com>

Add Synopsys eDMA IP version 0 debugfs support to assist any debug
in the future.

Creates a file system structure composed by folders and files that mimic
the IP register map (this files are read only) to ease any debug.

To enable this feature is necessary to select DEBUG_FS option on kernel
configuration.

Small output example:

(eDMA IP version 0, unroll, 1 write + 1 read channels)

% mount -t debugfs none /sys/kernel/debug/
% tree /sys/kernel/debug/dw-edma-core:0/
dw-edma/
├── version
├── mode
├── wr_ch_cnt
├── rd_ch_cnt
└── registers
    ├── ctrl_data_arb_prior
    ├── ctrl
    ├── write
    │   ├── engine_en
    │   ├── doorbell
    │   ├── ch_arb_weight_low
    │   ├── ch_arb_weight_high
    │   ├── int_status
    │   ├── int_mask
    │   ├── int_clear
    │   ├── err_status
    │   ├── done_imwr_low
    │   ├── done_imwr_high
    │   ├── abort_imwr_low
    │   ├── abort_imwr_high
    │   ├── ch01_imwr_data
    │   ├── ch23_imwr_data
    │   ├── ch45_imwr_data
    │   ├── ch67_imwr_data
    │   ├── linked_list_err_en
    │   ├── engine_chgroup
    │   ├── engine_hshake_cnt_low
    │   ├── engine_hshake_cnt_high
    │   ├── ch0_pwr_en
    │   ├── ch1_pwr_en
    │   ├── ch2_pwr_en
    │   ├── ch3_pwr_en
    │   ├── ch4_pwr_en
    │   ├── ch5_pwr_en
    │   ├── ch6_pwr_en
    │   ├── ch7_pwr_en
    │   └── channel:0
    │       ├── ch_control1
    │       ├── ch_control2
    │       ├── transfer_size
    │       ├── sar_low
    │       ├── sar_high
    │       ├── dar_high
    │       ├── llp_low
    │       └── llp_high
    └── read
        ├── engine_en
        ├── doorbell
        ├── ch_arb_weight_low
        ├── ch_arb_weight_high
        ├── int_status
        ├── int_mask
        ├── int_clear
        ├── err_status_low
        ├── err_status_high
        ├── done_imwr_low
        ├── done_imwr_high
        ├── abort_imwr_low
        ├── abort_imwr_high
        ├── ch01_imwr_data
        ├── ch23_imwr_data
        ├── ch45_imwr_data
        ├── ch67_imwr_data
        ├── linked_list_err_en
        ├── engine_chgroup
        ├── engine_hshake_cnt_low
        ├── engine_hshake_cnt_high
        ├── ch0_pwr_en
        ├── ch1_pwr_en
        ├── ch2_pwr_en
        ├── ch3_pwr_en
        ├── ch4_pwr_en
        ├── ch5_pwr_en
        ├── ch6_pwr_en
        ├── ch7_pwr_en
        └── channel:0
            ├── ch_control1
            ├── ch_control2
            ├── transfer_size
            ├── sar_low
            ├── sar_high
            ├── dar_high
            ├── llp_low
            └── llp_high

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Joao Pinto <jpinto@synopsys.com>
---
Changes:
v1 -> v2:
 - Rebased to v5.2-rc1
 
 drivers/dma/dw-edma/Makefile             |   3 +-
 drivers/dma/dw-edma/dw-edma-v0-core.c    |   2 +
 drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 310 +++++++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-debugfs.h |  27 +++
 4 files changed, 341 insertions(+), 1 deletion(-)
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-debugfs.c
 create mode 100644 drivers/dma/dw-edma/dw-edma-v0-debugfs.h

diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile
index 01c7c63..0c53033 100644
--- a/drivers/dma/dw-edma/Makefile
+++ b/drivers/dma/dw-edma/Makefile
@@ -1,5 +1,6 @@
 # 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-v0-core.o $(dw-edma-y)
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index a38c473..8a3180e 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -345,8 +345,10 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan)
 /* eDMA debugfs callbacks */
 void dw_edma_v0_core_debugfs_on(struct dw_edma_chip *chip)
 {
+	dw_edma_v0_debugfs_on(chip);
 }
 
 void dw_edma_v0_core_debugfs_off(void)
 {
+	dw_edma_v0_debugfs_off();
 }
diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
new file mode 100644
index 0000000..3226f52
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/bitfield.h>
+
+#include "dw-edma-v0-debugfs.h"
+#include "dw-edma-v0-regs.h"
+#include "dw-edma-core.h"
+
+#define REGS_ADDR(name) \
+	((dma_addr_t *)&regs->name)
+#define REGISTER(name) \
+	{ #name, REGS_ADDR(name) }
+
+#define WR_REGISTER(name) \
+	{ #name, REGS_ADDR(wr_##name) }
+#define RD_REGISTER(name) \
+	{ #name, REGS_ADDR(rd_##name) }
+
+#define WR_REGISTER_LEGACY(name) \
+	{ #name, REGS_ADDR(type.legacy.wr_##name) }
+#define RD_REGISTER_LEGACY(name) \
+	{ #name, REGS_ADDR(type.legacy.rd_##name) }
+
+#define WR_REGISTER_UNROLL(name) \
+	{ #name, REGS_ADDR(type.unroll.wr_##name) }
+#define RD_REGISTER_UNROLL(name) \
+	{ #name, REGS_ADDR(type.unroll.rd_##name) }
+
+#define WRITE_STR				"write"
+#define READ_STR				"read"
+#define CHANNEL_STR				"channel"
+#define REGISTERS_STR				"registers"
+
+static struct dentry				*base_dir;
+static struct dw_edma				*dw;
+static struct dw_edma_v0_regs			*regs;
+
+static struct {
+	void					*start;
+	void					*end;
+} lim[2][EDMA_V0_MAX_NR_CH];
+
+struct debugfs_entries {
+	char					name[24];
+	dma_addr_t				*reg;
+};
+
+static int dw_edma_debugfs_u32_get(void *data, u64 *val)
+{
+	if (dw->mode == EDMA_MODE_LEGACY &&
+	    data >= (void *)&regs->type.legacy.ch) {
+		void *ptr = (void *)&regs->type.legacy.ch;
+		u32 viewport_sel = 0;
+		unsigned long flags;
+		u16 ch;
+
+		for (ch = 0; ch < dw->wr_ch_cnt; ch++)
+			if (lim[0][ch].start >= data && data < lim[0][ch].end) {
+				ptr += (data - lim[0][ch].start);
+				goto legacy_sel_wr;
+			}
+
+		for (ch = 0; ch < dw->rd_ch_cnt; ch++)
+			if (lim[1][ch].start >= data && data < lim[1][ch].end) {
+				ptr += (data - lim[1][ch].start);
+				goto legacy_sel_rd;
+			}
+
+		return 0;
+legacy_sel_rd:
+		viewport_sel = BIT(31);
+legacy_sel_wr:
+		viewport_sel |= FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
+
+		raw_spin_lock_irqsave(&dw->lock, flags);
+
+		writel(viewport_sel, &regs->type.legacy.viewport_sel);
+		*val = readl(ptr);
+
+		raw_spin_unlock_irqrestore(&dw->lock, flags);
+	} else {
+		*val = readl(data);
+	}
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_edma_debugfs_u32_get, NULL, "0x%08llx\n");
+
+static void dw_edma_debugfs_create_x32(const struct debugfs_entries entries[],
+				       int nr_entries, struct dentry *dir)
+{
+	int i;
+
+	for (i = 0; i < nr_entries; i++) {
+		if (!debugfs_create_file_unsafe(entries[i].name, 0444, dir,
+						entries[i].reg,	&fops_x32))
+			break;
+	}
+}
+
+static void dw_edma_debugfs_regs_ch(struct dw_edma_v0_ch_regs *regs,
+				    struct dentry *dir)
+{
+	int nr_entries;
+	const struct debugfs_entries debugfs_regs[] = {
+		REGISTER(ch_control1),
+		REGISTER(ch_control2),
+		REGISTER(transfer_size),
+		REGISTER(sar_low),
+		REGISTER(sar_high),
+		REGISTER(dar_low),
+		REGISTER(dar_high),
+		REGISTER(llp_low),
+		REGISTER(llp_high),
+	};
+
+	nr_entries = ARRAY_SIZE(debugfs_regs);
+	dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, dir);
+}
+
+static void dw_edma_debugfs_regs_wr(struct dentry *dir)
+{
+	const struct debugfs_entries debugfs_regs[] = {
+		/* eDMA global registers */
+		WR_REGISTER(engine_en),
+		WR_REGISTER(doorbell),
+		WR_REGISTER(ch_arb_weight_low),
+		WR_REGISTER(ch_arb_weight_high),
+		/* eDMA interrupts registers */
+		WR_REGISTER(int_status),
+		WR_REGISTER(int_mask),
+		WR_REGISTER(int_clear),
+		WR_REGISTER(err_status),
+		WR_REGISTER(done_imwr_low),
+		WR_REGISTER(done_imwr_high),
+		WR_REGISTER(abort_imwr_low),
+		WR_REGISTER(abort_imwr_high),
+		WR_REGISTER(ch01_imwr_data),
+		WR_REGISTER(ch23_imwr_data),
+		WR_REGISTER(ch45_imwr_data),
+		WR_REGISTER(ch67_imwr_data),
+		WR_REGISTER(linked_list_err_en),
+	};
+	const struct debugfs_entries debugfs_unroll_regs[] = {
+		/* eDMA channel context grouping */
+		WR_REGISTER_UNROLL(engine_chgroup),
+		WR_REGISTER_UNROLL(engine_hshake_cnt_low),
+		WR_REGISTER_UNROLL(engine_hshake_cnt_high),
+		WR_REGISTER_UNROLL(ch0_pwr_en),
+		WR_REGISTER_UNROLL(ch1_pwr_en),
+		WR_REGISTER_UNROLL(ch2_pwr_en),
+		WR_REGISTER_UNROLL(ch3_pwr_en),
+		WR_REGISTER_UNROLL(ch4_pwr_en),
+		WR_REGISTER_UNROLL(ch5_pwr_en),
+		WR_REGISTER_UNROLL(ch6_pwr_en),
+		WR_REGISTER_UNROLL(ch7_pwr_en),
+	};
+	struct dentry *regs_dir, *ch_dir;
+	int nr_entries, i;
+	char name[16];
+
+	regs_dir = debugfs_create_dir(WRITE_STR, dir);
+	if (!regs_dir)
+		return;
+
+	nr_entries = ARRAY_SIZE(debugfs_regs);
+	dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+
+	if (dw->mode == EDMA_MODE_UNROLL) {
+		nr_entries = ARRAY_SIZE(debugfs_unroll_regs);
+		dw_edma_debugfs_create_x32(debugfs_unroll_regs, nr_entries,
+					   regs_dir);
+	}
+
+	for (i = 0; i < dw->wr_ch_cnt; i++) {
+		snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);
+
+		ch_dir = debugfs_create_dir(name, regs_dir);
+		if (!ch_dir)
+			return;
+
+		dw_edma_debugfs_regs_ch(&regs->type.unroll.ch[i].wr, ch_dir);
+
+		lim[0][i].start = &regs->type.unroll.ch[i].wr;
+		lim[0][i].end = &regs->type.unroll.ch[i].padding_1[0];
+	}
+}
+
+static void dw_edma_debugfs_regs_rd(struct dentry *dir)
+{
+	const struct debugfs_entries debugfs_regs[] = {
+		/* eDMA global registers */
+		RD_REGISTER(engine_en),
+		RD_REGISTER(doorbell),
+		RD_REGISTER(ch_arb_weight_low),
+		RD_REGISTER(ch_arb_weight_high),
+		/* eDMA interrupts registers */
+		RD_REGISTER(int_status),
+		RD_REGISTER(int_mask),
+		RD_REGISTER(int_clear),
+		RD_REGISTER(err_status_low),
+		RD_REGISTER(err_status_high),
+		RD_REGISTER(linked_list_err_en),
+		RD_REGISTER(done_imwr_low),
+		RD_REGISTER(done_imwr_high),
+		RD_REGISTER(abort_imwr_low),
+		RD_REGISTER(abort_imwr_high),
+		RD_REGISTER(ch01_imwr_data),
+		RD_REGISTER(ch23_imwr_data),
+		RD_REGISTER(ch45_imwr_data),
+		RD_REGISTER(ch67_imwr_data),
+	};
+	const struct debugfs_entries debugfs_unroll_regs[] = {
+		/* eDMA channel context grouping */
+		RD_REGISTER_UNROLL(engine_chgroup),
+		RD_REGISTER_UNROLL(engine_hshake_cnt_low),
+		RD_REGISTER_UNROLL(engine_hshake_cnt_high),
+		RD_REGISTER_UNROLL(ch0_pwr_en),
+		RD_REGISTER_UNROLL(ch1_pwr_en),
+		RD_REGISTER_UNROLL(ch2_pwr_en),
+		RD_REGISTER_UNROLL(ch3_pwr_en),
+		RD_REGISTER_UNROLL(ch4_pwr_en),
+		RD_REGISTER_UNROLL(ch5_pwr_en),
+		RD_REGISTER_UNROLL(ch6_pwr_en),
+		RD_REGISTER_UNROLL(ch7_pwr_en),
+	};
+	struct dentry *regs_dir, *ch_dir;
+	int nr_entries, i;
+	char name[16];
+
+	regs_dir = debugfs_create_dir(READ_STR, dir);
+	if (!regs_dir)
+		return;
+
+	nr_entries = ARRAY_SIZE(debugfs_regs);
+	dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+
+	if (dw->mode == EDMA_MODE_UNROLL) {
+		nr_entries = ARRAY_SIZE(debugfs_unroll_regs);
+		dw_edma_debugfs_create_x32(debugfs_unroll_regs, nr_entries,
+					   regs_dir);
+	}
+
+	for (i = 0; i < dw->rd_ch_cnt; i++) {
+		snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);
+
+		ch_dir = debugfs_create_dir(name, regs_dir);
+		if (!ch_dir)
+			return;
+
+		dw_edma_debugfs_regs_ch(&regs->type.unroll.ch[i].rd, ch_dir);
+
+		lim[1][i].start = &regs->type.unroll.ch[i].rd;
+		lim[1][i].end = &regs->type.unroll.ch[i].padding_2[0];
+	}
+}
+
+static void dw_edma_debugfs_regs(void)
+{
+	const struct debugfs_entries debugfs_regs[] = {
+		REGISTER(ctrl_data_arb_prior),
+		REGISTER(ctrl),
+	};
+	struct dentry *regs_dir;
+	int nr_entries;
+
+	regs_dir = debugfs_create_dir(REGISTERS_STR, base_dir);
+	if (!regs_dir)
+		return;
+
+	nr_entries = ARRAY_SIZE(debugfs_regs);
+	dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+
+	dw_edma_debugfs_regs_wr(regs_dir);
+	dw_edma_debugfs_regs_rd(regs_dir);
+}
+
+void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
+{
+	dw = chip->dw;
+	if (!dw)
+		return;
+
+	regs = (struct dw_edma_v0_regs *)dw->rg_region.vaddr;
+	if (!regs)
+		return;
+
+	base_dir = debugfs_create_dir(dw->name, 0);
+	if (!base_dir)
+		return;
+
+	debugfs_create_u32("version", 0444, base_dir, &dw->version);
+	debugfs_create_u32("mode", 0444, base_dir, &dw->mode);
+	debugfs_create_u16("wr_ch_cnt", 0444, base_dir, &dw->wr_ch_cnt);
+	debugfs_create_u16("rd_ch_cnt", 0444, base_dir, &dw->rd_ch_cnt);
+
+	dw_edma_debugfs_regs();
+}
+
+void dw_edma_v0_debugfs_off(void)
+{
+	debugfs_remove_recursive(base_dir);
+}
diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.h b/drivers/dma/dw-edma/dw-edma-v0-debugfs.h
new file mode 100644
index 0000000..5450a0a
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ *
+ * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+ */
+
+#ifndef _DW_EDMA_V0_DEBUG_FS_H
+#define _DW_EDMA_V0_DEBUG_FS_H
+
+#include <linux/dma/edma.h>
+
+#ifdef CONFIG_DEBUG_FS
+void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip);
+void dw_edma_v0_debugfs_off(void);
+#else
+static inline void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
+{
+}
+
+static inline void dw_edma_v0_debugfs_off(void)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+#endif /* _DW_EDMA_V0_DEBUG_FS_H */
-- 
2.7.4


^ permalink raw reply related

* Re: [PATCH] dmaengine: dmatest: Add support for completion polling
From: Peter Ujfalusi @ 2019-06-04 13:35 UTC (permalink / raw)
  To: Vinod Koul; +Cc: dan.j.williams, dmaengine, andriy.shevchenko
In-Reply-To: <20190604124527.GG15118@vkoul-mobl>



On 04/06/2019 15.45, Vinod Koul wrote:
> On 03-06-19, 10:05, Peter Ujfalusi wrote:
> 
>>> I think the main question is how polling for completion should be
>>> handled when client does not request for completion interrupt, thus we
>>> will have no callback in the DMA driver when the transfer is completed.
>>>
>>> If DMA_PREP_INTERRUPT is set for the tx_descriptor then the polling will
>>> wait until the DMA driver internally receives the interrupt that the
>>> transfer is done and sets the cookie to completed state.
>>>
>>> However if DMA_PREP_INTERRUPT is not set, the DMA driver will not get
>>> notification from the HW that is the transfer is done, the only way to
>>> know is to check the tx_status and based on the residue (if it is 0 then
>>> it is done) decide what to tell the client.
>>>
>>> Should the client call dmaengine_terminate_* after the polling returned
>>> unconditionally to free up the descriptor?
>>
>> This is how omap-dma is handling the polled memcpy support.
> 
> Yes that is a good question. Even if the client does not set
> DMA_PREP_INTERRUPT would there be no interrupt generated by controller
> on txn completion? If not how will next txn be submitted to the
> hardware.
> 
> I think we should view DMA_PREP_INTERRUPT from client pov, but
> controller cannot get away with disabling interrupts IMO.

What happens if client is issuing a DMA memcpy (short one) while
interrupts are disabled?

The user for this is:
drivers/gpu/drm/omapdrm/omap_dmm_tiler.c

commit: f5b9930b85dc6319fd6bcc259e447eff62fc691c

The interrupt based completion is not going to work in some cases, the
DMA driver should obey that the missing DMA_PREP_INTERRUPT really
implies that interrupts can not be used.

> Assuming I had enough caffeine before I thought process, then client would
> poll descriptor status using cookie and should free up once the cookie
> is freed, makes sense?

OK, so clients are expected to call dmaengine_terminate_*
unconditionally after the transfer is completed, right?

If we use interrupts then the handler would anyway free up the
descriptor, so terminating should not do any harm, if we can not have
interrupts then terminate will clear up the completed descriptor
proactively.

In any case I have updated the EDMA patch to do the same thing in case
of polling w/o interrupts as it would do in the completion irq handler,
and similar approach prepared for omap-dma as well.

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* Re: [PATCH 2/2] dmagengine: pl330: add code to get reset property
From: Dinh Nguyen @ 2019-06-04 14:21 UTC (permalink / raw)
  To: Vinod Koul; +Cc: dmaengine, robh+dt, mark.rutland, devicetree
In-Reply-To: <20190604121424.GW15118@vkoul-mobl>

Hi Vinod,

On 6/4/19 7:14 AM, Vinod Koul wrote:
> On 23-05-19, 19:28, Dinh Nguyen wrote:
>> The DMA controller on some SoCs can be held in reset, and thus requires
>> the reset signal(s) to deasserted. Most SoCs will have just one reset
>> signal, but there are others, i.e. Arria10/Stratix10 will have an
>> additional reset signal, referred to as the OCP.
>>
>> Add code to get the reset property from the device tree for deassert and
>> assert.
>>
>> Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
>> ---
>>  drivers/dma/pl330.c | 38 ++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 38 insertions(+)
>>
>> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
>> index 6e6837214210..6018c43e785d 100644
>> --- a/drivers/dma/pl330.c
>> +++ b/drivers/dma/pl330.c
>> @@ -29,6 +29,7 @@
>>  #include <linux/err.h>
>>  #include <linux/pm_runtime.h>
>>  #include <linux/bug.h>
>> +#include <linux/reset.h>
>>  
>>  #include "dmaengine.h"
>>  #define PL330_MAX_CHAN		8
>> @@ -500,6 +501,9 @@ struct pl330_dmac {
>>  	unsigned int num_peripherals;
>>  	struct dma_pl330_chan *peripherals; /* keep at end */
>>  	int quirks;
>> +
>> +	struct reset_control	*rstc;
>> +	struct reset_control	*rstc_ocp;
>>  };
>>  
>>  static struct pl330_of_quirks {
>> @@ -3028,6 +3032,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
>>  
>>  	amba_set_drvdata(adev, pl330);
>>  
>> +	pl330->rstc = devm_reset_control_get_optional(&adev->dev, "dma");
>> +	if (IS_ERR(pl330->rstc)) {
>> +		dev_err(&adev->dev, "No reset controller specified.\n");
> 
> Wasnt this optional??

Yes, this is optional. The call devm_reset_control_get_optional() will
just return NULL if the reset property is not there, but an error
pointer if something really went wrong. Thus, I'm using IS_ERR() for the
error checking.

Dinh

^ permalink raw reply

* Re: [PATCH 2/2] dmagengine: pl330: add code to get reset property
From: Geert Uytterhoeven @ 2019-06-04 16:31 UTC (permalink / raw)
  To: Dinh Nguyen
  Cc: Vinod Koul, dmaengine, Rob Herring, Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
In-Reply-To: <1dd97825-f6a2-7a1b-33ef-e28e00cc8506@kernel.org>

Hi Dinh,

On Tue, Jun 4, 2019 at 4:21 PM Dinh Nguyen <dinguyen@kernel.org> wrote:
> On 6/4/19 7:14 AM, Vinod Koul wrote:
> > On 23-05-19, 19:28, Dinh Nguyen wrote:
> >> The DMA controller on some SoCs can be held in reset, and thus requires
> >> the reset signal(s) to deasserted. Most SoCs will have just one reset
> >> signal, but there are others, i.e. Arria10/Stratix10 will have an
> >> additional reset signal, referred to as the OCP.
> >>
> >> Add code to get the reset property from the device tree for deassert and
> >> assert.
> >>
> >> Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
> >> ---
> >>  drivers/dma/pl330.c | 38 ++++++++++++++++++++++++++++++++++++++
> >>  1 file changed, 38 insertions(+)
> >>
> >> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
> >> index 6e6837214210..6018c43e785d 100644
> >> --- a/drivers/dma/pl330.c
> >> +++ b/drivers/dma/pl330.c
> >> @@ -29,6 +29,7 @@
> >>  #include <linux/err.h>
> >>  #include <linux/pm_runtime.h>
> >>  #include <linux/bug.h>
> >> +#include <linux/reset.h>
> >>
> >>  #include "dmaengine.h"
> >>  #define PL330_MAX_CHAN              8
> >> @@ -500,6 +501,9 @@ struct pl330_dmac {
> >>      unsigned int num_peripherals;
> >>      struct dma_pl330_chan *peripherals; /* keep at end */
> >>      int quirks;
> >> +
> >> +    struct reset_control    *rstc;
> >> +    struct reset_control    *rstc_ocp;
> >>  };
> >>
> >>  static struct pl330_of_quirks {
> >> @@ -3028,6 +3032,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
> >>
> >>      amba_set_drvdata(adev, pl330);
> >>
> >> +    pl330->rstc = devm_reset_control_get_optional(&adev->dev, "dma");
> >> +    if (IS_ERR(pl330->rstc)) {
> >> +            dev_err(&adev->dev, "No reset controller specified.\n");
> >
> > Wasnt this optional??
>
> Yes, this is optional. The call devm_reset_control_get_optional() will
> just return NULL if the reset property is not there, but an error
> pointer if something really went wrong. Thus, I'm using IS_ERR() for the
> error checking.

So the error message is incorrect, as this is a real error condition?

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0)
From: Bjorn Helgaas @ 2019-06-04 19:40 UTC (permalink / raw)
  To: Gustavo Pimentel; +Cc: linux-pci, dmaengine
In-Reply-To: <cover.1559654565.git.gustavo.pimentel@synopsys.com>

On Tue, Jun 04, 2019 at 03:29:21PM +0200, Gustavo Pimentel wrote:
> Add Synopsys eDMA IP driver (version 0 and for EP side only) to Linux
> kernel. This IP is generally distributed with Synopsys PCIe EndPoint IP
> (depends of the use and licensing agreement), which supports:
>  - legacy and unroll modes
>  - 16 independent and concurrent channels (8 write + 8 read)
>  - supports linked list (scatter-gather) transfer
>  - each linked list descriptor can transfer from 1 byte to 4 Gbytes
>  - supports cyclic transfer
>  - PCIe EndPoint glue-logic
> 
> This patch series contains:
>  - eDMA core + eDMA core v0 driver (implements the interface with
>  DMAengine controller APIs and interfaces with eDMA HW block)
>  - eDMA PCIe glue-logic reference driver (attaches to Synopsys EP and
>  provides memory access to eDMA core driver)

What changed from v1?  (Just curious; I expect somebody else to merge
this since I've already acked the PCI parts.)

Bjorn

^ permalink raw reply

* Re: [PATCH v3 5/8] dmaengine: fsl-edma: add drvdata for vf610
From: Robin Gong @ 2019-06-05  2:25 UTC (permalink / raw)
  To: vkoul@kernel.org
  Cc: dl-linux-imx, linux-kernel@vger.kernel.org,
	devicetree@vger.kernel.org, festevam@gmail.com,
	dan.j.williams@intel.com, mark.rutland@arm.com,
	dmaengine@vger.kernel.org, robh@kernel.org, shawnguo@kernel.org,
	linux-arm-kernel@lists.infradead.org, kernel@pengutronix.de,
	s.hauer@pengutronix.de
In-Reply-To: <20190604123331.GE15118@vkoul-mobl>

On 二, 2019-06-04 at 18:03 +0530, Vinod Koul wrote:
> On 29-05-19, 17:08, yibin.gong@nxp.com wrote:
> 
> > 
> > @@ -205,8 +228,9 @@ static int fsl_edma_probe(struct
> > platform_device *pdev)
> >  	if (!fsl_edma)
> >  		return -ENOMEM;
> >  
> > -	fsl_edma->version = v1;
> > -	fsl_edma->dmamux_nr = DMAMUX_NR;
> > +	fsl_edma->drvdata = drvdata;
> > +	fsl_edma->version = drvdata->version;
> > +	fsl_edma->dmamux_nr = drvdata->dmamuxs;
> And can we avoid the duplication here, you have version and dmamuxs
> represented in two places. But right now it looks logical so the
> removal
> should be done after this series
To avoid more code changes in other edma driver such as mcf-edma.c and
fsl-edma-common.c(replace all version/dmamux_nr with new
'drvdata'),meanwhile, no board to test mcf-edma so I keep
'version'/'dmamux' here in the last minute. But if you stick, I would
try to refine it in next version. 
> 

^ permalink raw reply

* Re: [PATCH v3 7/8] dmaengine: fsl-edma: add i.mx7ulp edma2 version support
From: Robin Gong @ 2019-06-05  2:29 UTC (permalink / raw)
  To: vkoul@kernel.org
  Cc: dl-linux-imx, linux-kernel@vger.kernel.org,
	devicetree@vger.kernel.org, festevam@gmail.com,
	dan.j.williams@intel.com, mark.rutland@arm.com,
	dmaengine@vger.kernel.org, robh@kernel.org, shawnguo@kernel.org,
	linux-arm-kernel@lists.infradead.org, kernel@pengutronix.de,
	s.hauer@pengutronix.de
In-Reply-To: <20190604123722.GF15118@vkoul-mobl>

On 2019-06-04 at 12:37 +0000, Vinod Koul wrote:
> On 29-05-19, 17:08, yibin.gong@nxp.com wrote:
> > 
> > From: Robin Gong <yibin.gong@nxp.com>
> > 
> >   Add edma2 for i.mx7ulp by version v3, since v2 has already
> Why leading spaces at start of line?
Sorry for the typo, will fix in next version
> 
> > 
> > been used by mcf-edma.
> > The big changes based on v1 are belows:
> > 1. only one dmamux.
> > 2. another clock dma_clk except dmamux clk.
> > 3. 16 independent interrupts instead of only one interrupt for
> > all channels.
> > 
> > Signed-off-by: Robin Gong <yibin.gong@nxp.com>
> > ---
> >  drivers/dma/fsl-edma-common.c | 18 +++++++++++-
> >  drivers/dma/fsl-edma-common.h |  3 ++
> >  drivers/dma/fsl-edma.c        | 67
> > +++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 87 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-
> > common.c
> > index 45d70d3..0d9915c 100644
> > --- a/drivers/dma/fsl-edma-common.c
> > +++ b/drivers/dma/fsl-edma-common.c
> > @@ -90,6 +90,19 @@ static void mux_configure8(struct fsl_edma_chan
> > *fsl_chan, void __iomem *addr,
> >  	iowrite8(val8, addr + off);
> >  }
> >  
> > +void mux_configure32(struct fsl_edma_chan *fsl_chan, void __iomem
> > *addr,
> > +		     u32 off, u32 slot, bool enable)
> > +{
> > +	u32 val;
> > +
> > +	if (enable)
> > +		val = EDMAMUX_CHCFG_ENBL << 24 | slot;
> > +	else
> > +		val = EDMAMUX_CHCFG_DIS;
> > +
> > +	iowrite32(val, addr + off * 4);
> > +}
> > +
> >  void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
> >  			unsigned int slot, bool enable)
> >  {
> > @@ -102,7 +115,10 @@ void fsl_edma_chan_mux(struct fsl_edma_chan
> > *fsl_chan,
> >  	muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux];
> >  	slot = EDMAMUX_CHCFG_SOURCE(slot);
> >  
> > -	mux_configure8(fsl_chan, muxaddr, ch_off, slot, enable);
> > +	if (fsl_chan->edma->version == v3)
> > +		mux_configure32(fsl_chan, muxaddr, ch_off, slot,
> > enable);
> > +	else
> > +		mux_configure8(fsl_chan, muxaddr, ch_off, slot,
> > enable);
> >  }
> >  EXPORT_SYMBOL_GPL(fsl_edma_chan_mux);
> >  
> > diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-
> > common.h
> > index 014ab74..07482d2 100644
> > --- a/drivers/dma/fsl-edma-common.h
> > +++ b/drivers/dma/fsl-edma-common.h
> > @@ -125,6 +125,7 @@ struct fsl_edma_chan {
> >  	dma_addr_t			dma_dev_addr;
> >  	u32				dma_dev_size;
> >  	enum dma_data_direction		dma_dir;
> > +	char				chan_name[16];
> >  };
> >  
> >  struct fsl_edma_desc {
> > @@ -139,6 +140,7 @@ struct fsl_edma_desc {
> >  enum edma_version {
> >  	v1, /* 32ch, Vybrid, mpc57x, etc */
> >  	v2, /* 64ch Coldfire */
> > +	v3, /* 32ch, i.mx7ulp */
> >  };
> >  
> >  struct fsl_edma_drvdata {
> > @@ -154,6 +156,7 @@ struct fsl_edma_engine {
> >  	void __iomem		*membase;
> >  	void __iomem		*muxbase[DMAMUX_NR];
> >  	struct clk		*muxclk[DMAMUX_NR];
> > +	struct clk		*dmaclk;
> >  	u32			dmamux_nr;
> >  	struct mutex		fsl_edma_mutex;
> >  	const struct fsl_edma_drvdata *drvdata;
> > diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
> > index cf18301..45b26d6 100644
> > --- a/drivers/dma/fsl-edma.c
> > +++ b/drivers/dma/fsl-edma.c
> > @@ -165,6 +165,51 @@ fsl_edma_irq_init(struct platform_device
> > *pdev, struct fsl_edma_engine *fsl_edma
> >  	return 0;
> >  }
> >  
> > +static int
> > +fsl_edma2_irq_init(struct platform_device *pdev,
> > +		   struct fsl_edma_engine *fsl_edma)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	int i, ret, irq;
> > +	int count = 0;
> Superflous initialization of count!
Would fix it in v4.
> 
> > 
> > +
> > +	count = of_irq_count(np);
> > +	dev_info(&pdev->dev, "%s Found %d interrupts\r\n",
> > __func__, count);
> Consider using debug level..
Would downgrade print level in v4.
> 
> > 
> > +	if (count <= 2) {
> > +		dev_err(&pdev->dev, "Interrupts in DTS not
> > correct.\n");
> > +		return -EINVAL;
> > +	}
> > +	/*
> > +	 * 16 channel independent interrupts + 1 error interrupt
> > on i.mx7ulp.
> > +	 * 2 channel share one interrupt, for example, ch0/ch16,
> > ch1/ch17...
> > +	 * For now, just simply request irq without IRQF_SHARED
> > flag, since 16
> > +	 * channels are enough on i.mx7ulp whose M4 domain own
> > some peripherals.
> > +	 */
> > +	for (i = 0; i < count; i++) {
> > +		irq = platform_get_irq(pdev, i);
> > +		if (irq < 0)
> > +			return -ENXIO;
> > +
> > +		sprintf(fsl_edma->chans[i].chan_name, "eDMA2-
> > CH%02d", i);
> > +
> > +		/* The last IRQ is for eDMA err */
> > +		if (i == count - 1)
> > +			ret = devm_request_irq(&pdev->dev, irq,
> > +						fsl_edma_err_handl
> > er,
> > +						0, "eDMA2-ERR",
> > fsl_edma);
> > +		else
> > +
> empty line is waste here
> 
> > 
> > +			ret = devm_request_irq(&pdev->dev, irq,
> > +						fsl_edma_tx_handle
> > r, 0,
> > +						fsl_edma-
> > >chans[i].chan_name,
> > +						fsl_edma);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  static void fsl_edma_irq_exit(
> >  		struct platform_device *pdev, struct
> > fsl_edma_engine *fsl_edma)
> >  {
> > @@ -191,8 +236,16 @@ static struct fsl_edma_drvdata vf610_data = {
> >  	.setup_irq = fsl_edma_irq_init,
> >  };
> >  
> > +static struct fsl_edma_drvdata imx7ulp_data = {
> > +	.version = v3,
> > +	.dmamuxs = 1,
> > +	.has_dmaclk = true,
> > +	.setup_irq = fsl_edma2_irq_init,
> > +};
> > +
> >  static const struct of_device_id fsl_edma_dt_ids[] = {
> >  	{ .compatible = "fsl,vf610-edma", .data = &vf610_data},
> > +	{ .compatible = "fsl,imx7ulp-edma", .data =
> > &imx7ulp_data},
> >  	{ /* sentinel */ }
> >  };
> >  MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
> > @@ -242,6 +295,20 @@ static int fsl_edma_probe(struct
> > platform_device *pdev)
> >  	fsl_edma_setup_regs(fsl_edma);
> >  	regs = &fsl_edma->regs;
> >  
> > +	if (drvdata->has_dmaclk) {
> > +		fsl_edma->dmaclk = devm_clk_get(&pdev->dev,
> > "dma");
> > +		if (IS_ERR(fsl_edma->dmaclk)) {
> > +			dev_err(&pdev->dev, "Missing DMA block
> > clock.\n");
> > +			return PTR_ERR(fsl_edma->dmaclk);
> > +		}
> > +
> > +		ret = clk_prepare_enable(fsl_edma->dmaclk);
> > +		if (ret) {
> > +			dev_err(&pdev->dev, "DMA clk block
> > failed.\n");
> > +			return ret;
> > +		}
> > +	}
> > +
> >  	for (i = 0; i < fsl_edma->dmamux_nr; i++) {
> >  		char clkname[32];
> >  
> > -- 
> > 2.7.4

^ permalink raw reply

* RE: [PATCH v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0)
From: Gustavo Pimentel @ 2019-06-05  8:14 UTC (permalink / raw)
  To: Bjorn Helgaas, Gustavo Pimentel, Vinod Koul
  Cc: linux-pci@vger.kernel.org, dmaengine@vger.kernel.org
In-Reply-To: <20190604194008.GA84290@google.com>

++ Vinod

Hi Bjorn,

On Tue, Jun 4, 2019 at 20:40:8, Bjorn Helgaas <helgaas@kernel.org> wrote:

> On Tue, Jun 04, 2019 at 03:29:21PM +0200, Gustavo Pimentel wrote:
> > Add Synopsys eDMA IP driver (version 0 and for EP side only) to Linux
> > kernel. This IP is generally distributed with Synopsys PCIe EndPoint IP
> > (depends of the use and licensing agreement), which supports:
> >  - legacy and unroll modes
> >  - 16 independent and concurrent channels (8 write + 8 read)
> >  - supports linked list (scatter-gather) transfer
> >  - each linked list descriptor can transfer from 1 byte to 4 Gbytes
> >  - supports cyclic transfer
> >  - PCIe EndPoint glue-logic
> > 
> > This patch series contains:
> >  - eDMA core + eDMA core v0 driver (implements the interface with
> >  DMAengine controller APIs and interfaces with eDMA HW block)
> >  - eDMA PCIe glue-logic reference driver (attaches to Synopsys EP and
> >  provides memory access to eDMA core driver)
> 
> What changed from v1?  (Just curious; I expect somebody else to merge
> this since I've already acked the PCI parts.)
> 
> Bjorn

This version 2 is basically, patch series rebased to v5.2-rc1, there were 
some conflicts on the pci-endpoint-test driver.
I'm hoping Vinod can merge this patches series into the next release 
v5.3.

Gustavo

^ permalink raw reply

* Re: [PATCH 2/2] dmagengine: pl330: add code to get reset property
From: Dinh Nguyen @ 2019-06-05 14:41 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Vinod Koul, dmaengine, Rob Herring, Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
In-Reply-To: <CAMuHMdV+_DzS+LD720BeAn05RzYGO9rS51-ucicP=8D0wz9Psg@mail.gmail.com>

Hi Geert,

On 6/4/19 11:31 AM, Geert Uytterhoeven wrote:
> Hi Dinh,
> 
> On Tue, Jun 4, 2019 at 4:21 PM Dinh Nguyen <dinguyen@kernel.org> wrote:
>> On 6/4/19 7:14 AM, Vinod Koul wrote:
>>> On 23-05-19, 19:28, Dinh Nguyen wrote:
>>>> The DMA controller on some SoCs can be held in reset, and thus requires
>>>> the reset signal(s) to deasserted. Most SoCs will have just one reset
>>>> signal, but there are others, i.e. Arria10/Stratix10 will have an
>>>> additional reset signal, referred to as the OCP.
>>>>
>>>> Add code to get the reset property from the device tree for deassert and
>>>> assert.
>>>>
>>>> Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
>>>> ---
>>>>  drivers/dma/pl330.c | 38 ++++++++++++++++++++++++++++++++++++++
>>>>  1 file changed, 38 insertions(+)
>>>>
>>>> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
>>>> index 6e6837214210..6018c43e785d 100644
>>>> --- a/drivers/dma/pl330.c
>>>> +++ b/drivers/dma/pl330.c
>>>> @@ -29,6 +29,7 @@
>>>>  #include <linux/err.h>
>>>>  #include <linux/pm_runtime.h>
>>>>  #include <linux/bug.h>
>>>> +#include <linux/reset.h>
>>>>
>>>>  #include "dmaengine.h"
>>>>  #define PL330_MAX_CHAN              8
>>>> @@ -500,6 +501,9 @@ struct pl330_dmac {
>>>>      unsigned int num_peripherals;
>>>>      struct dma_pl330_chan *peripherals; /* keep at end */
>>>>      int quirks;
>>>> +
>>>> +    struct reset_control    *rstc;
>>>> +    struct reset_control    *rstc_ocp;
>>>>  };
>>>>
>>>>  static struct pl330_of_quirks {
>>>> @@ -3028,6 +3032,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
>>>>
>>>>      amba_set_drvdata(adev, pl330);
>>>>
>>>> +    pl330->rstc = devm_reset_control_get_optional(&adev->dev, "dma");
>>>> +    if (IS_ERR(pl330->rstc)) {
>>>> +            dev_err(&adev->dev, "No reset controller specified.\n");
>>>
>>> Wasnt this optional??
>>
>> Yes, this is optional. The call devm_reset_control_get_optional() will
>> just return NULL if the reset property is not there, but an error
>> pointer if something really went wrong. Thus, I'm using IS_ERR() for the
>> error checking.
> 
> So the error message is incorrect, as this is a real error condition?
> 

Yes, you're right! Will correct in V2.

Dinh

^ permalink raw reply

* Re: [PATCH 2/2] dmagengine: pl330: add code to get reset property
From: Dinh Nguyen @ 2019-06-05 15:31 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Vinod Koul, dmaengine, Rob Herring, Mark Rutland,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
In-Reply-To: <00841780-ad68-ba8d-bdf0-d3f78fa42c98@kernel.org>

Hi Geert,

On 6/5/19 9:41 AM, Dinh Nguyen wrote:
> Hi Geert,
> 
> On 6/4/19 11:31 AM, Geert Uytterhoeven wrote:
>> Hi Dinh,
>>
>> On Tue, Jun 4, 2019 at 4:21 PM Dinh Nguyen <dinguyen@kernel.org> wrote:
>>> On 6/4/19 7:14 AM, Vinod Koul wrote:
>>>> On 23-05-19, 19:28, Dinh Nguyen wrote:
>>>>> The DMA controller on some SoCs can be held in reset, and thus requires
>>>>> the reset signal(s) to deasserted. Most SoCs will have just one reset
>>>>> signal, but there are others, i.e. Arria10/Stratix10 will have an
>>>>> additional reset signal, referred to as the OCP.
>>>>>
>>>>> Add code to get the reset property from the device tree for deassert and
>>>>> assert.
>>>>>
>>>>> Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
>>>>> ---
>>>>>  drivers/dma/pl330.c | 38 ++++++++++++++++++++++++++++++++++++++
>>>>>  1 file changed, 38 insertions(+)
>>>>>
>>>>> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
>>>>> index 6e6837214210..6018c43e785d 100644
>>>>> --- a/drivers/dma/pl330.c
>>>>> +++ b/drivers/dma/pl330.c
>>>>> @@ -29,6 +29,7 @@
>>>>>  #include <linux/err.h>
>>>>>  #include <linux/pm_runtime.h>
>>>>>  #include <linux/bug.h>
>>>>> +#include <linux/reset.h>
>>>>>
>>>>>  #include "dmaengine.h"
>>>>>  #define PL330_MAX_CHAN              8
>>>>> @@ -500,6 +501,9 @@ struct pl330_dmac {
>>>>>      unsigned int num_peripherals;
>>>>>      struct dma_pl330_chan *peripherals; /* keep at end */
>>>>>      int quirks;
>>>>> +
>>>>> +    struct reset_control    *rstc;
>>>>> +    struct reset_control    *rstc_ocp;
>>>>>  };
>>>>>
>>>>>  static struct pl330_of_quirks {
>>>>> @@ -3028,6 +3032,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
>>>>>
>>>>>      amba_set_drvdata(adev, pl330);
>>>>>
>>>>> +    pl330->rstc = devm_reset_control_get_optional(&adev->dev, "dma");
>>>>> +    if (IS_ERR(pl330->rstc)) {
>>>>> +            dev_err(&adev->dev, "No reset controller specified.\n");
>>>>
>>>> Wasnt this optional??
>>>
>>> Yes, this is optional. The call devm_reset_control_get_optional() will
>>> just return NULL if the reset property is not there, but an error
>>> pointer if something really went wrong. Thus, I'm using IS_ERR() for the
>>> error checking.
>>
>> So the error message is incorrect, as this is a real error condition?
>>
> 
> Yes, you're right! Will correct in V2.

Looking at this again, I think the error message is correct. The
optional call will return NULL if the resets property is not specified,
and will return an error pointer if the reset propert is specified, but
the pointer to the reset controller is not found.

So I think the error message is correct.

Dinh

^ permalink raw reply

* Re: [PATCH] [RFC] dmaengine: add fifo_size member
From: Sameer Pujar @ 2019-06-06  3:49 UTC (permalink / raw)
  To: Vinod Koul
  Cc: dan.j.williams, tiwai, jonathanh, dmaengine, linux-kernel,
	sharadg, rlokhande, dramesh, mkumard
In-Reply-To: <20190506155046.GH3845@vkoul-mobl.Dlink>

Sorry for late reply.
[Resending the reply since delivery failed for few recipients]

On 5/6/2019 9:20 PM, Vinod Koul wrote:
> On 06-05-19, 18:34, Sameer Pujar wrote:
>> On 5/4/2019 3:53 PM, Vinod Koul wrote:
>>> On 02-05-19, 18:59, Sameer Pujar wrote:
>>>> On 5/2/2019 5:55 PM, Vinod Koul wrote:
>>>>> On 02-05-19, 16:23, Sameer Pujar wrote:
>>>>>> On 5/2/2019 11:34 AM, Vinod Koul wrote:
>>>>>>> On 30-04-19, 17:00, Sameer Pujar wrote:
>>>>>>>> During the DMA transfers from memory to I/O, it was observed that transfers
>>>>>>>> were inconsistent and resulted in glitches for audio playback. It happened
>>>>>>>> because fifo size on DMA did not match with slave channel configuration.
>>>>>>>>
>>>>>>>> currently 'dma_slave_config' structure does not have a field for fifo size.
>>>>>>>> Hence the platform pcm driver cannot pass the fifo size as a slave_config.
>>>>>>>> Note that 'snd_dmaengine_dai_dma_data' structure has fifo_size field which
>>>>>>>> cannot be used to pass the size info. This patch introduces fifo_size field
>>>>>>>> and the same can be populated on slave side. Users can set required size
>>>>>>>> for slave peripheral (multiple channels can be independently running with
>>>>>>>> different fifo sizes) and the corresponding sizes are programmed through
>>>>>>>> dma_slave_config on DMA side.
>>>>>>> FIFO size is a hardware property not sure why you would want an
>>>>>>> interface to program that?
>>>>>>>
>>>>>>> On mismatch, I guess you need to take care of src/dst_maxburst..
>>>>>> Yes, FIFO size is a HW property. But it is SW configurable(atleast in my
>>>>>> case) on
>>>>>> slave side and can be set to different sizes. The src/dst_maxburst is
>>>>> Are you sure, have you talked to HW folks on that? IIUC you are
>>>>> programming the data to be used in FIFO not the FIFO length!
>>>> Yes, I mentioned about FIFO length.
>>>>
>>>> 1. MAX FIFO size is fixed in HW. But there is a way to limit the usage per
>>>> channel
>>>>      in multiples of 64 bytes.
>>>> 2. Having a separate member would give independent control over MAX BURST
>>>> SIZE and
>>>>      FIFO SIZE.
>>>>>> programmed
>>>>>> for specific values, I think this depends on few factors related to
>>>>>> bandwidth
>>>>>> needs of client, DMA needs of the system etc.,
>>>>> Precisely
>>>>>
>>>>>> In such cases how does DMA know the actual FIFO depth of slave peripheral?
>>>>> Why should DMA know? Its job is to push/pull data as configured by
>>>>> peripheral driver. The peripheral driver knows and configures DMA
>>>>> accordingly.
>>>> I am not sure if there is any HW logic that mandates DMA to know the size
>>>> of configured FIFO depth on slave side. I will speak to HW folks and
>>>> would update here.
>>> I still do not comprehend why dma would care about slave side
>>> configuration. In the absence of patch which uses this I am not sure
>>> what you are trying to do...
>> I am using DMA HW in cyclic mode for data transfers to Audio sub-system.
>> In such cases flow control on DMA transfers is essential, since I/O is
> right and people use burst size for precisely that!
>
>> consuming/producing the data at slower rate. The DMA tranfer is enabled/
>> disabled during start/stop of audio playback/capture sessions through ALSA
>> callbacks and DMA runs in cyclic mode. Hence DMA is the one which is doing
>> flow control and it is necessary for it to know the peripheral FIFO depth
>> to avoid overruns/underruns.
> not really, knowing that doesnt help anyway you have described! DMA
> pushes/pulls data and that is controlled by burst configured by slave
> (so it know what to expect and porgrams things accordingly)
>
> you are really going other way around about the whole picture. FWIW that
> is how *other* folks do audio with dmaengine!
I discussed this internally with HW folks and below is the reason why 
DMA needs
to know FIFO size.

- FIFOs reside in peripheral device(ADMAIF), which is the ADMA interface 
to the audio sub-system.
- ADMAIF has multiple channels and share FIFO buffer for individual 
operations. There is a provision
   to allocate specific fifo size for each individual ADMAIF channel 
from the shared buffer.
- Tegra Audio DMA(ADMA) architecture is different from the usual DMA 
engines, which you described earlier.
- The flow control logic is placed inside ADMA. Slave peripheral 
device(ADMAIF) signals ADMA whenever a
   read or write happens on the FIFO(per WORD basis). Please note that 
the signaling is per channel. There is
   no other signaling present from ADMAIF to ADMA.
- ADMA keeps a counter related to above signaling. Whenever a sufficient 
space is available, it initiates a transfer.
   But the question is, how does it know when to transfer. This is the 
reason, why ADMA has to be aware of FIFO
   depth of ADMAIF channel. Depending on the counters and FIFO depth, it 
knows exactly when a free space is available
   in the context of a specific channel. On ADMA, FIFO_SIZE is just a 
value which should match to actual FIFO_DEPTH/SIZE
   of ADMAIF channel.
- Now consider two cases based on above logic,
   * Case 1: when DMA_FIFO_SIZE > SLAVE_FIFO_SIZE
     In this case, ADMA thinks that there is enough space available for 
transfer, when actually the FIFO data
     on slave is not consumed yet. It would result in OVERRUN.
   * Case 2: when DMA_FIFO_SIZE < SLAVE_FIFO_SIZE
     This is case where ADMA won’t transfer, even though sufficient 
space is available, resulting in UNDERRUN.
- The guideline is to program, DMA_FIFO_SIZE(on ADMA side) = 
SLAVE_FIFO_SIZE(on ADMAIF side) and hence we need a
   way to communicate fifo size info to ADMA.

Thanks,
Sameer.
>> Also please note that, peripheral device has multiple channels and share
>> a fixed MAX FIFO buffer. But SW can program different FIFO sizes for
>> individual channels.
> yeah peripheral driver, yes. DMA driver nope!
>

^ permalink raw reply

* Re: [PATCH 01/16] firmware: ti_sci: Add resource management APIs for ringacc, psi-l and udma
From: Lokesh Vutla @ 2019-06-06  6:00 UTC (permalink / raw)
  To: Peter Ujfalusi, vkoul, robh+dt, nm, ssantosh
  Cc: dan.j.williams, dmaengine, linux-arm-kernel, devicetree,
	linux-kernel, grygorii.strashko, t-kristo, tony
In-Reply-To: <20190506123456.6777-2-peter.ujfalusi@ti.com>

Hi Peter,

On 06/05/19 6:04 PM, Peter Ujfalusi wrote:
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>

Patch has the following checkpatch warnings and checks which can be fixed:

WARNING: Missing commit description - Add an appropriate one

CHECK: Lines should not end with a '('
#262: FILE: drivers/firmware/ti_sci.c:2286:
+static int ti_sci_cmd_rm_udmap_tx_ch_cfg(

CHECK: Lines should not end with a '('
#323: FILE: drivers/firmware/ti_sci.c:2347:
+static int ti_sci_cmd_rm_udmap_rx_ch_cfg(

CHECK: Lines should not end with a '('
#383: FILE: drivers/firmware/ti_sci.c:2407:
+static int ti_sci_cmd_rm_udmap_rx_flow_cfg1(

CHECK: Lines should not end with a '('
#1414: FILE: include/linux/soc/ti/ti_sci_protocol.h:455:
+	int (*rx_flow_cfg)(

total: 0 errors, 2 warnings, 4 checks, 1399 lines checked



> ---
>  drivers/firmware/ti_sci.c              | 439 +++++++++++++++
>  drivers/firmware/ti_sci.h              | 704 +++++++++++++++++++++++++
>  include/linux/soc/ti/ti_sci_protocol.h | 216 ++++++++
>  3 files changed, 1359 insertions(+)
> 
> diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
> index 64d895b80bc3..af3ebcdeab18 100644
> --- a/drivers/firmware/ti_sci.c
> +++ b/drivers/firmware/ti_sci.c

[..snip.]

> +}
> +
> +static int ti_sci_cmd_rm_psil_pair(const struct ti_sci_handle *handle,
> +				   u32 nav_id, u32 src_thread, u32 dst_thread)
> +{

All the psil ops doesn't have the  kernel-doc function comments. Just be
consistent with other functions :)


> +	struct ti_sci_msg_hdr *resp;
> +	struct ti_sci_msg_psil_pair *req;
> +	struct ti_sci_xfer *xfer;
> +	struct ti_sci_info *info;
> +	struct device *dev;
> +	int ret = 0;
> +
> +	if (IS_ERR(handle))
> +		return PTR_ERR(handle);
> +	if (!handle)
> +		return -EINVAL;
> +
> +	info = handle_to_ti_sci_info(handle);
> +	dev = info->dev;
> +
> +	xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_RM_PSIL_PAIR,
> +				   TI_SCI_FLAG_REQ_ACK_ON_PROCESSED,
> +				   sizeof(*req), sizeof(*resp));
> +	if (IS_ERR(xfer)) {
> +		ret = PTR_ERR(xfer);
> +		dev_err(dev, "RM_PSIL:Message reconfig failed(%d)\n", ret);
> +		return ret;
> +	}
> +	req = (struct ti_sci_msg_psil_pair *)xfer->xfer_buf;
> +	req->nav_id = nav_id;
> +	req->src_thread = src_thread;
> +	req->dst_thread = dst_thread;
> +
> +	ret = ti_sci_do_xfer(info, xfer);
> +	if (ret) {
> +		dev_err(dev, "RM_PSIL:Mbox send fail %d\n", ret);
> +		goto fail;
> +	}
> +
> +	resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf;
> +	ret = ti_sci_is_response_ack(resp) ? 0 : -EINVAL;
> +
> +fail:
> +	ti_sci_put_one_xfer(&info->minfo, xfer);
> +
> +	return ret;
> +}
> +

[..snip..]

> + */
> +struct ti_sci_msg_rm_ring_cfg_req {
> +	struct ti_sci_msg_hdr hdr;
> +	u32 valid_params;
> +	u16 nav_id;
> +	u16 index;
> +	u32 addr_lo;
> +	u32 addr_hi;
> +	u32 count;
> +	u8 mode;
> +	u8 size;
> +	u8 order_id;
> +} __packed;
> +
> +/**
> + * struct ti_sci_msg_rm_ring_cfg_resp - Response to configuring a ring.
> + *
> + * @hdr:	Generic Header
> + */
> +struct ti_sci_msg_rm_ring_cfg_resp {
> +	struct ti_sci_msg_hdr hdr;
> +} __packed;

If it is a generic ACK, NACK response, just use the header directly.

[..snip..]

> + */
> +struct ti_sci_msg_rm_udmap_rx_ch_cfg_req {
> +	struct ti_sci_msg_hdr hdr;
> +	u32 valid_params;
> +	u16 nav_id;
> +	u16 index;
> +	u16 rx_fetch_size;
> +	u16 rxcq_qnum;
> +	u8 rx_priority;
> +	u8 rx_qos;
> +	u8 rx_orderid;
> +	u8 rx_sched_priority;
> +	u16 flowid_start;
> +	u16 flowid_cnt;
> +	u8 rx_pause_on_err;
> +	u8 rx_atype;
> +	u8 rx_chan_type;
> +	u8 rx_ignore_short;
> +	u8 rx_ignore_long;
> +	u8 rx_burst_size;
> +

extra line?

> +} __packed;
> +
> +/**


Thanks and regards,
Lokesh

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox