Linux-PCI Archive on lore.kernel.org
 help / Atom feed
* [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0)
@ 2018-12-17 17:19 Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 1/6] dmaengine: Add Synopsys eDMA IP core driver Gustavo Pimentel
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Gustavo Pimentel @ 2018-12-17 17:19 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Andy Shevchenko, Russell King,
	Eugeniy Paltsev, Lorenzo Pieralisi, Bjorn Helgaas,
	Kishon Vijay Abraham I, Niklas Cassel, Joao Pinto, Jose Abreu,
	Luis Oliveira, Vitor Soares, Nelson Costa, Pedro Sousa

Add Synopsys eDMA IP driver (version 0) 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
 - PCIe EndPoint glue-logic

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       | 845 +++++++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-core.h       | 147 ++++++
 drivers/dma/dw-edma/dw-edma-pcie.c       | 258 ++++++++++
 drivers/dma/dw-edma/dw-edma-v0-core.c    | 361 +++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-core.h    |  26 +
 drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 357 +++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-debugfs.h |  24 +
 drivers/dma/dw-edma/dw-edma-v0-regs.h    | 145 ++++++
 drivers/misc/pci_endpoint_test.c         |   2 +-
 include/linux/dma/edma.h                 |  43 ++
 include/linux/pci_ids.h                  |   1 +
 16 files changed, 2243 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

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Eugeniy Paltsev <paltsev@synopsys.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: Niklas Cassel <niklas.cassel@linaro.org>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Jose Abreu <jose.abreu@synopsys.com>
Cc: Luis Oliveira <lolivei@synopsys.com>
Cc: Vitor Soares <vitor.soares@synopsys.com>
Cc: Nelson Costa <nelson.costa@synopsys.com>
Cc: Pedro Sousa <pedrom.sousa@synopsys.com>

-- 
2.7.4


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC v2 1/6] dmaengine: Add Synopsys eDMA IP core driver
  2018-12-17 17:19 [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Gustavo Pimentel
@ 2018-12-17 17:19 ` Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 2/6] dmaengine: Add Synopsys eDMA IP version 0 support Gustavo Pimentel
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Gustavo Pimentel @ 2018-12-17 17:19 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Dan Williams, Eugeniy Paltsev,
	Andy Shevchenko, Russell King, Niklas Cassel, Joao Pinto,
	Jose Abreu, Luis Oliveira, Vitor Soares, Nelson Costa,
	Pedro Sousa

Add Synopsys eDMA IP core driver to kernel.

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

Also creates an abstration layer through callbacks allowing different
registers mappings in the future, organized in to versions.

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.

Changes:
RFC v1->RFC v2:
 - Replace comments // (C99 style) by /**/
 - Fix the headers of the .c and .h files according to the most recent
   convention
 - Fix errors and checks pointed out by checkpatch with --strict option
 - Replace patch small description tag from dma by dmaengine
 - Change some dev_info() into dev_dbg()
 - Remove unnecessary zero initialization after kzalloc
 - Remove direction validation on config() API, since the direction
   parameter is deprecated
 - Refactor code to replace atomic_t by u32 variable type
 - Replace start_transfer() name by dw_edma_start_transfer()
 - Add spinlock to dw_edma_device_prep_slave_sg()
 - Add spinlock to dw_edma_free_chunk()
 - Simplify switch case into if on dw_edma_device_pause(),
   dw_edma_device_resume() and dw_edma_device_terminate_all()

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Eugeniy Paltsev <paltsev@synopsys.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Niklas Cassel <niklas.cassel@linaro.org>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Jose Abreu <jose.abreu@synopsys.com>
Cc: Luis Oliveira <lolivei@synopsys.com>
Cc: Vitor Soares <vitor.soares@synopsys.com>
Cc: Nelson Costa <nelson.costa@synopsys.com>
Cc: Pedro Sousa <pedrom.sousa@synopsys.com>
---
 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 | 824 +++++++++++++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-core.h | 147 +++++++
 include/linux/dma/edma.h           |  43 ++
 7 files changed, 1030 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 de511db..40517f8 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -640,6 +640,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 7fcc4d8..3ebfab0 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..bd1d8c0
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -0,0 +1,824 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA core driver
+ */
+
+#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 "dw-edma-core.h"
+#include "../dmaengine.h"
+#include "../virt-dma.h"
+
+#define SET(reg, name, val)			\
+	reg.name = val
+
+#define SET_BOTH_CH(name, value)		\
+	do {					\
+		SET(dw->wr_edma, name, value);	\
+		SET(dw->rd_edma, name, value);	\
+	} while (0)
+
+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
+const struct dw_edma_core_ops *chan2ops(struct dw_edma_chan *chan)
+{
+	return chan->chip->dw->ops;
+}
+
+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_chan *chan = chunk->chan;
+	struct dw_edma_burst *burst;
+
+	burst = kzalloc(sizeof(*burst), GFP_NOWAIT);
+	if (unlikely(!burst))
+		return NULL;
+
+	INIT_LIST_HEAD(&burst->list);
+
+	if (chunk->burst) {
+		chunk->bursts_alloc++;
+		dev_dbg(chan2dev(chan), ": alloc new burst element (%d)\n",
+			chunk->bursts_alloc);
+		list_add_tail(&burst->list, &chunk->burst->list);
+	} else {
+		chunk->bursts_alloc = 0;
+		chunk->burst = burst;
+		dev_dbg(chan2dev(chan), ": alloc new burst head\n");
+	}
+
+	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;
+	chunk->cb = !(desc->chunks_alloc % 2);
+	chunk->p_addr = (dma_addr_t)(dw->pa_ll + chan->ll_off);
+	chunk->v_addr = (dma_addr_t)(dw->va_ll + chan->ll_off);
+
+	if (desc->chunk) {
+		desc->chunks_alloc++;
+		dev_dbg(chan2dev(chan), ": alloc new chunk element (%d)\n",
+			desc->chunks_alloc);
+		list_add_tail(&chunk->list, &desc->chunk->list);
+		dw_edma_alloc_burst(chunk);
+	} else {
+		chunk->burst = NULL;
+		desc->chunks_alloc = 0;
+		desc->chunk = chunk;
+		dev_dbg(chan2dev(chan), ": alloc new chunk head\n");
+	}
+
+	return chunk;
+}
+
+static struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan)
+{
+	struct dw_edma_desc *desc;
+
+	dev_dbg(chan2dev(chan), ": alloc new descriptor\n");
+
+	desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
+	if (unlikely(!desc))
+		return NULL;
+
+	desc->chan = chan;
+	dw_edma_alloc_chunk(desc);
+
+	return desc;
+}
+
+static void dw_edma_free_burst(struct dw_edma_chunk *chunk)
+{
+	struct dw_edma_burst *child, *_next;
+
+	if (!chunk->burst)
+		return;
+
+	/* 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_chan *chan = desc->chan;
+	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);
+		if (child->bursts_alloc)
+			dev_dbg(chan2dev(chan),	": %d bursts still allocated\n",
+				child->bursts_alloc);
+		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)
+{
+	struct dw_edma_chan *chan = desc->chan;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+
+	dw_edma_free_chunk(desc);
+	if (desc->chunks_alloc)
+		dev_dbg(chan2dev(chan), ": %d chunks still allocated\n",
+			desc->chunks_alloc);
+
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+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 virt_dma_desc *vd;
+	struct dw_edma_desc *desc;
+	struct dw_edma_chunk *child, *_next;
+	const struct dw_edma_core_ops *ops = chan2ops(chan);
+
+	vd = vchan_next_desc(&chan->vc);
+	if (!vd)
+		return;
+
+	desc = vd2dw_edma_desc(vd);
+	if (!desc)
+		return;
+
+	list_for_each_entry_safe(child, _next, &desc->chunk->list, list) {
+		ops->start(child, !desc->xfer_sz);
+		desc->xfer_sz += child->sz;
+		dev_dbg(chan2dev(chan), ": transfer of %u bytes started\n", child->sz);
+
+		dw_edma_free_burst(child);
+		if (child->bursts_alloc)
+			dev_dbg(chan2dev(chan),	": %d bursts still allocated\n",
+				child->bursts_alloc);
+		list_del(&child->list);
+		kfree(child);
+		desc->chunks_alloc--;
+
+		return;
+	}
+}
+
+static int dw_edma_device_config(struct dma_chan *dchan,
+				 struct dma_slave_config *config)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	const struct dw_edma_core_ops *ops = chan2ops(chan);
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+
+	if (!config) {
+		err = -EINVAL;
+		goto err_config;
+	}
+
+	if (chan->configured) {
+		dev_err(chan2dev(chan), ": channel already configured\n");
+		err = -EPERM;
+		goto err_config;
+	}
+
+	dev_dbg(chan2dev(chan), ": src_addr(physical) = 0x%.16x\n",
+		config->src_addr);
+	dev_dbg(chan2dev(chan), ": dst_addr(physical) = 0x%.16x\n",
+		config->dst_addr);
+
+	err = ops->device_config(dchan);
+	if (!err) {
+		chan->configured = true;
+		dev_dbg(chan2dev(chan),	": channel configured\n");
+	}
+
+err_config:
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+	return err;
+}
+
+static int dw_edma_device_pause(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+
+	if (!chan->configured) {
+		dev_err(dchan2dev(dchan), ": channel not configured\n");
+		err = -EPERM;
+		goto err_pause;
+	}
+
+	if (chan->status != EDMA_ST_BUSY) {
+		err = -EPERM;
+		goto err_pause;
+	}
+
+	if (chan->request != EDMA_REQ_NONE) {
+		err = -EPERM;
+		goto err_pause;
+	}
+
+	chan->request = EDMA_REQ_PAUSE;
+	dev_dbg(dchan2dev(dchan), ": pause requested\n");
+
+err_pause:
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+	return err;
+}
+
+static int dw_edma_device_resume(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+
+	if (!chan->configured) {
+		dev_err(dchan2dev(dchan), ": channel not configured\n");
+		err = -EPERM;
+		goto err_resume;
+	}
+
+	if (chan->status != EDMA_ST_PAUSE) {
+		err = -EPERM;
+		goto err_resume;
+	}
+
+	if (chan->request != EDMA_REQ_NONE) {
+		err = -EPERM;
+		goto err_resume;
+	}
+
+	chan->status = EDMA_ST_BUSY;
+	dev_dbg(dchan2dev(dchan), ": transfer resumed\n");
+	dw_edma_start_transfer(chan);
+
+err_resume:
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+	return err;
+}
+
+static int dw_edma_device_terminate_all(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	unsigned long flags;
+	int err = 0;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+
+	if (!chan->configured) {
+		dev_err(dchan2dev(dchan), ": channel not configured\n");
+		err = -EPERM;
+		goto err_terminate;
+	}
+
+	if (chan->status == EDMA_ST_PAUSE) {
+		dev_dbg(dchan2dev(dchan), ": channel is paused, stopping immediately\n");
+		vchan_get_all_descriptors(&chan->vc, &head);
+		vchan_dma_desc_free_list(&chan->vc, &head);
+		chan->status = EDMA_ST_IDLE;
+		goto err_terminate;
+	} else if (chan->status != EDMA_ST_BUSY) {
+		err = -EPERM;
+		goto err_terminate;
+	}
+
+	if (chan->request > EDMA_REQ_PAUSE) {
+		err = -EPERM;
+		goto err_terminate;
+	}
+
+	chan->request = EDMA_REQ_STOP;
+	dev_dbg(dchan2dev(dchan), ": termination requested\n");
+
+err_terminate:
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+	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)) {
+		dev_dbg(dchan2dev(dchan), ": transfer issued\n");
+		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);
+	const struct dw_edma_core_ops *ops = chan2ops(chan);
+	unsigned long flags;
+	enum dma_status ret;
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+
+	ret = ops->ch_status(chan);
+	if (ret == DMA_ERROR) {
+		goto ret_status;
+	} else if (ret == DMA_IN_PROGRESS) {
+		chan->status = EDMA_ST_BUSY;
+		goto ret_status;
+	} else {
+		/* DMA_COMPLETE */
+		if (chan->status == EDMA_ST_PAUSE)
+			ret = DMA_PAUSED;
+		else if (chan->status == EDMA_ST_BUSY)
+			ret = DMA_IN_PROGRESS;
+		else
+			ret = DMA_COMPLETE;
+	}
+
+ret_status:
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+	dma_set_residue(txstate, 0);
+
+	return ret;
+}
+
+static struct dma_async_tx_descriptor *
+dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
+			     unsigned int sg_len,
+			     enum dma_transfer_direction direction,
+			     unsigned long flags, void *context)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	struct dw_edma_desc *desc;
+	struct dw_edma_chunk *chunk;
+	struct dw_edma_burst *burst;
+	struct scatterlist *sg;
+	dma_addr_t dev_addr = chan->p_addr;
+	unsigned long sflags;
+	int i;
+
+	if (sg_len < 1) {
+		dev_err(chan2dev(chan), ": invalid sg length %u\n", sg_len);
+		return NULL;
+	}
+
+	if (direction == DMA_DEV_TO_MEM && chan->dir == EDMA_DIR_WRITE) {
+		dev_dbg(chan2dev(chan),	": prepare operation (WRITE)\n");
+	} else if (direction == DMA_MEM_TO_DEV && chan->dir == EDMA_DIR_READ) {
+		dev_dbg(chan2dev(chan),	": prepare operation (READ)\n");
+	} else {
+		dev_err(chan2dev(chan), ": invalid direction\n");
+		return NULL;
+	}
+
+	if (!chan->configured) {
+		dev_err(dchan2dev(dchan), ": channel not configured\n");
+		return NULL;
+	}
+	if (chan->status == EDMA_ST_BUSY) {
+		dev_err(chan2dev(chan), ": channel is busy or paused\n");
+		return NULL;
+	}
+
+	spin_lock_irqsave(&chan->vc.lock, sflags);
+
+	desc = dw_edma_alloc_desc(chan);
+	if (unlikely(!desc))
+		goto err_alloc;
+
+	chunk = dw_edma_alloc_chunk(desc);
+	if (unlikely(!chunk))
+		goto err_alloc;
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		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 (direction == DMA_MEM_TO_DEV) {
+			burst->sar = sg_dma_address(sg);
+			burst->dar = dev_addr;
+		} else {
+			burst->sar = dev_addr;
+			burst->dar = sg_dma_address(sg);
+		}
+
+		burst->sz = sg_dma_len(sg);
+		chunk->sz += burst->sz;
+		desc->alloc_sz += burst->sz;
+		dev_addr += burst->sz;
+
+		dev_dbg(chan2dev(chan), "lli %u/%u, sar=0x%.16llx, dar=0x%.16llx, size=%u bytes\n",
+			i + 1, sg_len,
+			burst->sar, burst->dar,
+			burst->sz);
+	}
+
+	spin_unlock_irqrestore(&chan->vc.lock, sflags);
+	return vchan_tx_prep(&chan->vc, &desc->vd, flags);
+
+err_alloc:
+	spin_unlock_irqrestore(&chan->vc.lock, sflags);
+	if (desc)
+		dw_edma_free_desc(desc);
+	return NULL;
+}
+
+static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
+{
+	struct dw_edma *dw = chan->chip->dw;
+	const struct dw_edma_core_ops *ops = dw->ops;
+	struct virt_dma_desc *vd;
+	struct dw_edma_desc *desc;
+	unsigned long flags;
+
+	ops->clear_done_int(chan);
+	dev_dbg(chan2dev(chan), ": clear done interrupt\n");
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+	vd = vchan_next_desc(&chan->vc);
+	switch (chan->request) {
+	case EDMA_REQ_NONE:
+		desc = vd2dw_edma_desc(vd);
+		if (desc->chunks_alloc) {
+			dev_dbg(chan2dev(chan),	": sub-transfer complete\n");
+			chan->status = EDMA_ST_BUSY;
+			dev_dbg(chan2dev(chan), ": transferred %u bytes\n",
+				desc->xfer_sz);
+			dw_edma_start_transfer(chan);
+		} else {
+			list_del(&vd->node);
+			vchan_cookie_complete(vd);
+			chan->status = EDMA_ST_IDLE;
+			dev_dbg(chan2dev(chan),	": transfer complete\n");
+		}
+		break;
+	case EDMA_REQ_STOP:
+		list_del(&vd->node);
+		vchan_cookie_complete(vd);
+		chan->request = EDMA_REQ_NONE;
+		chan->status = EDMA_ST_IDLE;
+		dev_dbg(chan2dev(chan),	": transfer stop\n");
+		break;
+	case EDMA_REQ_PAUSE:
+		chan->request = EDMA_REQ_NONE;
+		chan->status = EDMA_ST_PAUSE;
+		break;
+	default:
+		dev_err(chan2dev(chan), ": invalid status state\n");
+		break;
+	}
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static void dw_edma_abort_interrupt(struct dw_edma_chan *chan)
+{
+	struct dw_edma *dw = chan->chip->dw;
+	const struct dw_edma_core_ops *ops = dw->ops;
+	struct virt_dma_desc *vd;
+	unsigned long flags;
+
+	ops->clear_abort_int(chan);
+	dev_dbg(chan2dev(chan), ": clear abort interrupt\n");
+
+	spin_lock_irqsave(&chan->vc.lock, flags);
+	vd = vchan_next_desc(&chan->vc);
+	list_del(&vd->node);
+	vchan_cookie_complete(vd);
+	chan->request = EDMA_REQ_NONE;
+	chan->status = EDMA_ST_IDLE;
+
+	spin_unlock_irqrestore(&chan->vc.lock, flags);
+}
+
+static irqreturn_t dw_edma_interrupt(int irq, void *data)
+{
+	struct dw_edma_chip *chip = data;
+	struct dw_edma *dw = chip->dw;
+	const struct dw_edma_core_ops *ops = dw->ops;
+	struct dw_edma_chan *chan;
+	u32 i;
+
+	/* Poll, clear and process every chanel interrupt status */
+	for (i = 0; i < (dw->wr_ch_count + dw->rd_ch_count); i++) {
+		chan = &dw->chan[i];
+
+		if (ops->status_done_int(chan))
+			dw_edma_done_interrupt(chan);
+
+		if (ops->status_abort_int(chan))
+			dw_edma_abort_interrupt(chan);
+	}
+
+	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) {
+		dev_err(chan2dev(chan), ": channel is busy\n");
+		return -EBUSY;
+	}
+
+	dev_dbg(dchan2dev(dchan), ": allocated\n");
+
+	pm_runtime_get(chan->chip->dev);
+
+	return 0;
+}
+
+static void dw_edma_free_chan_resources(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	unsigned long timeout = jiffies + msecs_to_jiffies(5000);
+	int ret;
+
+	if (chan->status != EDMA_ST_IDLE)
+		dev_err(chan2dev(chan), ": channel is busy\n");
+
+	do {
+		ret = dw_edma_device_terminate_all(dchan);
+		if (!ret)
+			break;
+
+		if (time_after_eq(jiffies, timeout)) {
+			dev_err(chan2dev(chan), ": timeout\n");
+			return;
+		}
+
+		cpu_relax();
+	} while (1);
+
+	dev_dbg(dchan2dev(dchan), ": freed\n");
+
+	pm_runtime_put(chan->chip->dev);
+}
+
+int dw_edma_probe(struct dw_edma_chip *chip)
+{
+	struct dw_edma *dw = chip->dw;
+	const struct dw_edma_core_ops *ops;
+	size_t ll_chunk = dw->ll_sz;
+	int i, j, err;
+
+	raw_spin_lock_init(&dw->lock);
+
+	switch (dw->version) {
+	default:
+		dev_err(chip->dev, ": unsupported version\n");
+		return -EPERM;
+	}
+
+	pm_runtime_get_sync(chip->dev);
+
+	dw->wr_ch_count = ops->ch_count(dw, WRITE);
+	if (!dw->wr_ch_count) {
+		dev_err(chip->dev, ": invalid number of write channels(0)\n");
+		return -EINVAL;
+	}
+
+	dw->rd_ch_count = ops->ch_count(dw, READ);
+	if (!dw->rd_ch_count) {
+		dev_err(chip->dev, ": invalid number of read channels(0)\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(chip->dev, "Channels:\twrite=%d, read=%d\n",
+		dw->wr_ch_count, dw->rd_ch_count);
+
+	dw->chan = devm_kcalloc(chip->dev, dw->wr_ch_count + dw->rd_ch_count,
+				sizeof(*dw->chan), GFP_KERNEL);
+	if (!dw->chan)
+		return -ENOMEM;
+
+	ll_chunk /= roundup_pow_of_two(dw->wr_ch_count + dw->rd_ch_count);
+
+	/* Disable eDMA, only to establish the ideal initial conditions */
+	ops->off(dw);
+
+	snprintf(dw->name, sizeof(dw->name), "dw-edma-core:%d", chip->id);
+
+	err = devm_request_irq(chip->dev, chip->irq, dw_edma_interrupt,
+			       IRQF_SHARED, dw->name, chip);
+	if (err)
+		return err;
+
+	INIT_LIST_HEAD(&dw->wr_edma.channels);
+	for (i = 0; i < dw->wr_ch_count; i++) {
+		struct dw_edma_chan *chan = &dw->chan[i];
+
+		chan->chip = chip;
+		chan->id = i;
+		chan->dir = EDMA_DIR_WRITE;
+		chan->configured = false;
+		chan->request = EDMA_REQ_NONE;
+		chan->status = EDMA_ST_IDLE;
+
+		chan->ll_off = (ll_chunk * i);
+		chan->ll_max = (ll_chunk / 24) - 1;
+
+		chan->msi_done_addr = dw->msi_addr;
+		chan->msi_abort_addr = dw->msi_addr;
+		chan->msi_data = dw->msi_data;
+
+		chan->vc.desc_free = vchan_free_desc;
+		vchan_init(&chan->vc, &dw->wr_edma);
+	}
+	dma_cap_set(DMA_SLAVE, dw->wr_edma.cap_mask);
+	dw->wr_edma.directions = BIT(DMA_MEM_TO_DEV);
+	dw->wr_edma.chancnt = dw->wr_ch_count;
+
+	INIT_LIST_HEAD(&dw->rd_edma.channels);
+	for (j = 0; j < dw->rd_ch_count; j++, i++) {
+		struct dw_edma_chan *chan = &dw->chan[i];
+
+		chan->chip = chip;
+		chan->id = j;
+		chan->dir = EDMA_DIR_READ;
+		chan->request = EDMA_REQ_NONE;
+		chan->status = EDMA_ST_IDLE;
+
+		chan->ll_off = (ll_chunk * i);
+		chan->ll_max = (ll_chunk / 24) - 1;
+
+		chan->msi_done_addr = dw->msi_addr;
+		chan->msi_abort_addr = dw->msi_addr;
+		chan->msi_data = dw->msi_data;
+
+		chan->vc.desc_free = vchan_free_desc;
+		vchan_init(&chan->vc, &dw->rd_edma);
+	}
+	dma_cap_set(DMA_SLAVE, dw->rd_edma.cap_mask);
+	dw->rd_edma.directions = BIT(DMA_DEV_TO_MEM);
+	dw->rd_edma.chancnt = dw->rd_ch_count;
+
+	/* Set DMA capabilities */
+	SET_BOTH_CH(src_addr_widths, BIT(DMA_SLAVE_BUSWIDTH_4_BYTES));
+	SET_BOTH_CH(dst_addr_widths, BIT(DMA_SLAVE_BUSWIDTH_4_BYTES));
+	SET_BOTH_CH(residue_granularity, DMA_RESIDUE_GRANULARITY_DESCRIPTOR);
+
+	SET_BOTH_CH(dev, chip->dev);
+
+	SET_BOTH_CH(device_alloc_chan_resources, dw_edma_alloc_chan_resources);
+	SET_BOTH_CH(device_free_chan_resources, dw_edma_free_chan_resources);
+
+	SET_BOTH_CH(device_config, dw_edma_device_config);
+	SET_BOTH_CH(device_pause, dw_edma_device_pause);
+	SET_BOTH_CH(device_resume, dw_edma_device_resume);
+	SET_BOTH_CH(device_terminate_all, dw_edma_device_terminate_all);
+	SET_BOTH_CH(device_issue_pending, dw_edma_device_issue_pending);
+	SET_BOTH_CH(device_tx_status, dw_edma_device_tx_status);
+	SET_BOTH_CH(device_prep_slave_sg, dw_edma_device_prep_slave_sg);
+
+	/* Power management */
+	pm_runtime_enable(chip->dev);
+
+	/* Register DMA device */
+	err = dma_async_device_register(&dw->wr_edma);
+	if (err)
+		goto err_pm_disable;
+
+	err = dma_async_device_register(&dw->rd_edma);
+	if (err)
+		goto err_pm_disable;
+
+	/* Turn debugfs on */
+	err = ops->debugfs_on(chip);
+	if (err) {
+		dev_err(chip->dev, ": unable to create debugfs structure\n");
+		goto err_pm_disable;
+	}
+
+	dev_info(chip->dev, "DesignWare eDMA controller driver loaded completely\n");
+
+	return 0;
+
+err_pm_disable:
+	pm_runtime_disable(chip->dev);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(dw_edma_probe);
+
+int dw_edma_remove(struct dw_edma_chip *chip)
+{
+	struct dw_edma *dw = chip->dw;
+	const struct dw_edma_core_ops *ops = dw->ops;
+	struct dw_edma_chan *chan, *_chan;
+
+	/* Disable eDMA */
+	if (ops)
+		ops->off(dw);
+
+	/* Free irq */
+	devm_free_irq(chip->dev, chip->irq, chip);
+
+	/* Power management */
+	pm_runtime_disable(chip->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 */
+	if (ops)
+		ops->debugfs_off();
+
+	dev_info(chip->dev, ": DesignWare eDMA controller driver unloaded complete\n");
+
+	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..87d009f
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA core driver
+ */
+
+#ifndef _DW_EDMA_CORE_H
+#define _DW_EDMA_CORE_H
+
+#include <linux/dma/edma.h>
+
+#include "../virt-dma.h"
+
+#define DRV_NAME				"dw-edma"
+
+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_core_ops {
+	/* eDMA management callbacks */
+	void (*off)(struct dw_edma *dw);
+	u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir);
+	enum dma_status (*ch_status)(struct dw_edma_chan *chan);
+	void (*clear_done_int)(struct dw_edma_chan *chan);
+	void (*clear_abort_int)(struct dw_edma_chan *chan);
+	bool (*status_done_int)(struct dw_edma_chan *chan);
+	bool (*status_abort_int)(struct dw_edma_chan *chan);
+	void (*start)(struct dw_edma_chunk *chunk, bool first);
+	int (*device_config)(struct dma_chan *dchan);
+	/* eDMA debug fs callbacks */
+	int (*debugfs_on)(struct dw_edma_chip *chip);
+	void (*debugfs_off)(void);
+};
+
+struct dw_edma_burst {
+	struct list_head		list;
+	u64				sar;
+	u64				dar;
+	u32				sz;
+};
+
+struct dw_edma_chunk {
+	struct list_head		list;
+	struct dw_edma_chan		*chan;
+	struct dw_edma_burst		*burst;
+
+	u32				bursts_alloc;
+
+	u8				cb;
+	u32				sz;
+
+	dma_addr_t			p_addr;		/* Linked list */
+	dma_addr_t			v_addr;		/* 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;
+
+	u64				ll_off;
+	u32				ll_max;
+
+	u64				msi_done_addr;
+	u64				msi_abort_addr;
+	u32				msi_data;
+
+	enum dw_edma_request		request;
+	enum dw_edma_status		status;
+	u8				configured;
+
+	dma_addr_t			p_addr;		/* Data */
+};
+
+struct dw_edma {
+	char				name[20];
+
+	struct dma_device		wr_edma;
+	u16				wr_ch_count;
+	struct dma_device		rd_edma;
+	u16				rd_ch_count;
+
+	void __iomem			*regs;
+
+	void __iomem			*va_ll;
+	resource_size_t			pa_ll;
+	size_t				ll_sz;
+
+	u64				msi_addr;
+	u32				msi_data;
+
+	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 */
+};
+
+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..349e542
--- /dev/null
+++ b/include/linux/dma/edma.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+// Synopsys DesignWare eDMA core driver
+
+#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	[flat|nested] 10+ messages in thread

* [RFC v2 2/6] dmaengine: Add Synopsys eDMA IP version 0 support
  2018-12-17 17:19 [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 1/6] dmaengine: Add Synopsys eDMA IP core driver Gustavo Pimentel
@ 2018-12-17 17:19 ` Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 3/6] dmaengine: Add Synopsys eDMA IP version 0 debugfs support Gustavo Pimentel
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Gustavo Pimentel @ 2018-12-17 17:19 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Dan Williams, Eugeniy Paltsev,
	Andy Shevchenko, Russell King, Niklas Cassel, Joao Pinto,
	Jose Abreu, Luis Oliveira, Vitor Soares, Nelson Costa,
	Pedro Sousa

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.

Changes:
RFC v1->RFC v2:
 - Replace comments // (C99 style) by /**/
 - Replace magic numbers by defines
 - Replace boolean return from ternary operation by a double negation
   operation
 - Replace QWORD_HI/QWORD_LO macros by upper_32_bits()/lower_32_bits()
 - Fix the headers of the .c and .h files according to the most recent
   convention
 - Fix errors and checks pointed out by checkpatch with --strict option
 - Replace patch small description tag from dma by dmaengine
 - Refactor code to replace atomic_t by u32 variable type

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Eugeniy Paltsev <paltsev@synopsys.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Niklas Cassel <niklas.cassel@linaro.org>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Jose Abreu <jose.abreu@synopsys.com>
Cc: Luis Oliveira <lolivei@synopsys.com>
Cc: Vitor Soares <vitor.soares@synopsys.com>
Cc: Nelson Costa <nelson.costa@synopsys.com>
Cc: Pedro Sousa <pedrom.sousa@synopsys.com>
---
 drivers/dma/dw-edma/Makefile          |   3 +-
 drivers/dma/dw-edma/dw-edma-core.c    |  21 ++
 drivers/dma/dw-edma/dw-edma-v0-core.c | 360 ++++++++++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-core.h |  26 +++
 drivers/dma/dw-edma/dw-edma-v0-regs.h | 145 ++++++++++++++
 5 files changed, 554 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 bd1d8c0..9a5201d 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -14,6 +14,7 @@
 #include <linux/dma/edma.h>
 
 #include "dw-edma-core.h"
+#include "dw-edma-v0-core.h"
 #include "../dmaengine.h"
 #include "../virt-dma.h"
 
@@ -26,6 +27,22 @@
 		SET(dw->rd_edma, name, value);	\
 	} while (0)
 
+static const struct dw_edma_core_ops dw_edma_v0_core_ops = {
+	/* eDMA management callbacks */
+	.off = dw_edma_v0_core_off,
+	.ch_count = dw_edma_v0_core_ch_count,
+	.ch_status = dw_edma_v0_core_ch_status,
+	.clear_done_int = dw_edma_v0_core_clear_done_int,
+	.clear_abort_int = dw_edma_v0_core_clear_abort_int,
+	.status_done_int = dw_edma_v0_core_status_done_int,
+	.status_abort_int = dw_edma_v0_core_status_abort_int,
+	.start = dw_edma_v0_core_start,
+	.device_config = dw_edma_v0_core_device_config,
+	/* eDMA debug fs callbacks */
+	.debugfs_on = dw_edma_v0_core_debugfs_on,
+	.debugfs_off = dw_edma_v0_core_debugfs_off,
+};
+
 static inline
 struct device *dchan2dev(struct dma_chan *dchan)
 {
@@ -641,10 +658,14 @@ int dw_edma_probe(struct dw_edma_chip *chip)
 	raw_spin_lock_init(&dw->lock);
 
 	switch (dw->version) {
+	case 0:
+		ops = &dw_edma_v0_core_ops;
+		break;
 	default:
 		dev_err(chip->dev, ": unsupported version\n");
 		return -EPERM;
 	}
+	dw->ops = ops;
 
 	pm_runtime_get_sync(chip->dev);
 
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..f8622ce
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ */
+
+#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_CB					= BIT(0),
+	DW_EDMA_TCB					= BIT(1),
+	DW_EDMA_LLP					= BIT(2),
+	DW_EDMA_LIE					= BIT(3),
+	DW_EDMA_RIE					= BIT(4),
+	DW_EDMA_CCS					= BIT(8),
+	DW_EDMA_LLE					= BIT(9),
+};
+
+#define EDMA_VIEWPORT_SEL_MASK				0x00000007ul
+#define	EDMA_ALL_INT_MASK				0x00FF00FFul
+#define	EDMA_WRITE_CH_COUNT_MASK			0x0000000Ful
+#define	EDMA_READ_CH_COUNT_MASK				0x000F0000ul
+#define	EDMA_CH_STATUS_MASK				0x00000060ul
+#define EDMA_DOORBELL_CH_MASK				0x00000007ul
+
+static inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw)
+{
+	return dw->regs;
+}
+
+#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 = (ch & EDMA_VIEWPORT_SEL_MASK);
+		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 = (ch & EDMA_VIEWPORT_SEL_MASK);
+		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_ALL_INT_MASK);
+	SET_BOTH(dw, int_clear, EDMA_ALL_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 = GET(dw, ctrl);
+
+	if (dir == EDMA_DIR_WRITE) {
+		num_ch &= EDMA_WRITE_CH_COUNT_MASK;
+	} else {
+		num_ch &= EDMA_READ_CH_COUNT_MASK;
+		num_ch >>= 16;
+	}
+
+	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 = GET_CH(dw, chan->dir, chan->id, ch_control1);
+
+	tmp &= EDMA_CH_STATUS_MASK;
+	tmp >>= 5;
+	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, 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, BIT(chan->id + 16));
+}
+
+bool dw_edma_v0_core_status_done_int(struct dw_edma_chan *chan)
+{
+	struct dw_edma *dw = chan->chip->dw;
+	u32 tmp;
+
+	tmp = GET_RW(dw, chan->dir, int_status);
+	tmp &= BIT(chan->id);
+
+	return !!tmp;
+}
+
+bool dw_edma_v0_core_status_abort_int(struct dw_edma_chan *chan)
+{
+	struct dw_edma *dw = chan->chip->dw;
+	u32 tmp;
+
+	tmp = GET_RW(dw, chan->dir, int_status);
+	tmp &= BIT(chan->id + 16);
+
+	return !!tmp;
+}
+
+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, j;
+	u64 sar, dar, addr;
+
+	lli = (struct dw_edma_v0_lli *)chunk->v_addr;
+
+	if (chunk->cb)
+		control = DW_EDMA_CB;
+
+	j = chunk->bursts_alloc;
+	list_for_each_entry(child, &chunk->burst->list, list) {
+		j--;
+		if (!j)
+			control |= (DW_EDMA_LIE | DW_EDMA_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_LLP | DW_EDMA_TCB;
+	if (!chunk->cb)
+		control |= DW_EDMA_CB;
+
+	/* Channel control */
+	SET_LL(&llp->control, control);
+	/* Linked list  - low, high */
+	addr = cpu_to_le64(chunk->p_addr);
+	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 mask;
+	u64 llp;
+
+	dw_edma_v0_core_write_chunk(chunk);
+
+	if (first) {
+		/* Enable engine */
+		SET_RW(dw, chan->dir, engine_en, BIT(1));
+		/* Interrupt unmask - done, abort */
+		mask = GET_RW(dw, chan->dir, int_mask);
+		mask &= ~(BIT(chan->id + 16) | BIT(chan->id));
+		SET_RW(dw, chan->dir, int_mask, mask);
+		/* Linked list error */
+		SET_RW(dw, chan->dir, linked_list_err_en, BIT(chan->id));
+		/* Channel control */
+		SET_CH(dw, chan->dir, chan->id, ch_control1,
+		       DW_EDMA_CCS | DW_EDMA_LLE);
+		/* Linked list - low, high */
+		llp = cpu_to_le64(chunk->p_addr);
+		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, chan->id & EDMA_DOORBELL_CH_MASK);
+}
+
+int dw_edma_v0_core_device_config(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	struct dw_edma *dw = chan->chip->dw;
+	u32 tmp;
+
+	/* MSI done addr - low, high */
+	SET_RW(dw, chan->dir, done_imwr_low,
+	       lower_32_bits(chan->msi_done_addr));
+	SET_RW(dw, chan->dir, done_imwr_high,
+	       upper_32_bits(chan->msi_done_addr));
+	/* MSI abort addr - low, high */
+	SET_RW(dw, chan->dir, abort_imwr_low,
+	       lower_32_bits(chan->msi_abort_addr));
+	SET_RW(dw, chan->dir, abort_imwr_high,
+	       upper_32_bits(chan->msi_abort_addr));
+	/* 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 &= 0x00FFu;
+		tmp |= ((u32)chan->msi_data << 16);
+	} else {
+		/* Channel even {0, 2, 4, 6} */
+		tmp &= 0xFF00u;
+		tmp |= 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 */
+int dw_edma_v0_core_debugfs_on(struct dw_edma_chip *chip)
+{
+	return 0;
+}
+
+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..364dfbd
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ */
+
+#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);
+bool dw_edma_v0_core_status_done_int(struct dw_edma_chan *chan);
+bool dw_edma_v0_core_status_abort_int(struct dw_edma_chan *chan);
+void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first);
+int dw_edma_v0_core_device_config(struct dma_chan *dchan);
+/* eDMA debug fs callbacks */
+int 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..4af6e6d
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-regs.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ */
+
+#ifndef _DW_EDMA_V0_REGS_H
+#define _DW_EDMA_V0_REGS_H
+
+#include <linux/dmaengine.h>
+
+#define EDMA_V0_MAX_NR_CH				8
+
+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 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	[flat|nested] 10+ messages in thread

* [RFC v2 3/6] dmaengine: Add Synopsys eDMA IP version 0 debugfs support
  2018-12-17 17:19 [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 1/6] dmaengine: Add Synopsys eDMA IP core driver Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 2/6] dmaengine: Add Synopsys eDMA IP version 0 support Gustavo Pimentel
@ 2018-12-17 17:19 ` Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 4/6] PCI: Add Synopsys endpoint EDDA Device id Gustavo Pimentel
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Gustavo Pimentel @ 2018-12-17 17:19 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Dan Williams, Eugeniy Paltsev,
	Andy Shevchenko, Russell King, Niklas Cassel, Joao Pinto,
	Jose Abreu, Luis Oliveira, Vitor Soares, Nelson Costa,
	Pedro Sousa

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/
dw-edma/
├── mode
├── wr_ch_count
├── rd_ch_count
└── 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

Changes:
RFC v1->RFC v2:
 - Replace comments // (C99 style) by /**/
 - Fix the headers of the .c and .h files according to the most recent
   convention
 - Fix errors and checks pointed out by checkpatch with --strict
   option
 - Replace patch small description tag from dma by dmaengine

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Eugeniy Paltsev <paltsev@synopsys.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Niklas Cassel <niklas.cassel@linaro.org>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Jose Abreu <jose.abreu@synopsys.com>
Cc: Luis Oliveira <lolivei@synopsys.com>
Cc: Vitor Soares <vitor.soares@synopsys.com>
Cc: Nelson Costa <nelson.costa@synopsys.com>
Cc: Pedro Sousa <pedrom.sousa@synopsys.com>
---
 drivers/dma/dw-edma/Makefile             |   3 +-
 drivers/dma/dw-edma/dw-edma-v0-core.c    |   3 +-
 drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 357 +++++++++++++++++++++++++++++++
 drivers/dma/dw-edma/dw-edma-v0-debugfs.h |  24 +++
 4 files changed, 385 insertions(+), 2 deletions(-)
 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 f8622ce..93d69b5 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -352,9 +352,10 @@ int dw_edma_v0_core_device_config(struct dma_chan *dchan)
 /* eDMA debugfs callbacks */
 int dw_edma_v0_core_debugfs_on(struct dw_edma_chip *chip)
 {
-	return 0;
+	return 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..a6ab0ad
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ */
+
+#include <linux/debugfs.h>
+
+#include "dw-edma-v0-debugfs.h"
+#include "dw-edma-v0-regs.h"
+#include "dw-edma-core.h"
+
+#define RD_PERM					0444
+
+#define REGS_ADDR(name) \
+	(&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];
+	void __iomem				*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_count; 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_count; 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 |= (ch & 0x00000007ul);
+
+		raw_spin_lock_irqsave(&dw->lock, flags);
+		*val = readl((u32 *)ptr);
+		raw_spin_unlock_irqrestore(&dw->lock, flags);
+	} else {
+		*val = readl((u32 *)data);
+	}
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_edma_debugfs_u32_get, NULL, "0x%08llx\n");
+
+static int dw_edma_debugfs_create_x32(const struct debugfs_entries entries[],
+				      int nr_entries, struct dentry *dir)
+{
+	struct dentry *entry;
+	int i;
+
+	for (i = 0; i < nr_entries; i++) {
+		entry = debugfs_create_file_unsafe(entries[i].name, RD_PERM,
+						   dir, entries[i].reg,
+						   &fops_x32);
+		if (!entry)
+			return -EPERM;
+	}
+
+	return 0;
+}
+
+static int 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 = sizeof(debugfs_regs) / sizeof(struct debugfs_entries);
+	return dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, dir);
+}
+
+static int dw_edma_debugfs_regs_wr(struct dentry *dir)
+{
+	struct dentry *regs_dir, *ch_dir;
+	int nr_entries, i, err;
+	char name[16];
+	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),
+	};
+
+	regs_dir = debugfs_create_dir(WRITE_STR, dir);
+	if (!regs_dir)
+		return -EPERM;
+
+	nr_entries = sizeof(debugfs_regs) / sizeof(struct debugfs_entries);
+	err = dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+	if (err)
+		return err;
+
+	if (dw->mode == EDMA_MODE_UNROLL) {
+		nr_entries = sizeof(debugfs_unroll_regs) /
+			     sizeof(struct debugfs_entries);
+		err = dw_edma_debugfs_create_x32(debugfs_unroll_regs,
+						 nr_entries, regs_dir);
+		if (err)
+			return err;
+	}
+
+	for (i = 0; i < dw->wr_ch_count; i++) {
+		snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);
+
+		ch_dir = debugfs_create_dir(name, regs_dir);
+		if (!ch_dir)
+			return -EPERM;
+
+		err = dw_edma_debugfs_regs_ch(&regs->type.unroll.ch[i].wr,
+					      ch_dir);
+		if (err)
+			return err;
+
+		lim[0][i].start = &regs->type.unroll.ch[i].wr;
+		lim[0][i].end = &regs->type.unroll.ch[i].padding_1[0];
+	}
+
+	return 0;
+}
+
+static int dw_edma_debugfs_regs_rd(struct dentry *dir)
+{
+	struct dentry *regs_dir, *ch_dir;
+	int nr_entries, i, err;
+	char name[16];
+	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),
+	};
+
+	regs_dir = debugfs_create_dir(READ_STR, dir);
+	if (!regs_dir)
+		return -EPERM;
+
+	nr_entries = sizeof(debugfs_regs) / sizeof(struct debugfs_entries);
+	err = dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+	if (err)
+		return err;
+
+	if (dw->mode == EDMA_MODE_UNROLL) {
+		nr_entries = sizeof(debugfs_unroll_regs) /
+			     sizeof(struct debugfs_entries);
+		err = dw_edma_debugfs_create_x32(debugfs_unroll_regs,
+						 nr_entries, regs_dir);
+		if (err)
+			return err;
+	}
+
+	for (i = 0; i < dw->rd_ch_count; i++) {
+		snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i);
+
+		ch_dir = debugfs_create_dir(name, regs_dir);
+		if (!ch_dir)
+			return -EPERM;
+
+		err = dw_edma_debugfs_regs_ch(&regs->type.unroll.ch[i].rd,
+					      ch_dir);
+		if (err)
+			return err;
+
+		lim[1][i].start = &regs->type.unroll.ch[i].rd;
+		lim[1][i].end = &regs->type.unroll.ch[i].padding_2[0];
+	}
+
+	return 0;
+}
+
+static int dw_edma_debugfs_regs(void)
+{
+	struct dentry *regs_dir;
+	int nr_entries, err;
+	const struct debugfs_entries debugfs_regs[] = {
+		REGISTER(ctrl_data_arb_prior),
+		REGISTER(ctrl),
+	};
+
+	regs_dir = debugfs_create_dir(REGISTERS_STR, base_dir);
+	if (!regs_dir)
+		return -EPERM;
+
+	nr_entries = sizeof(debugfs_regs) / sizeof(struct debugfs_entries);
+	err = dw_edma_debugfs_create_x32(debugfs_regs, nr_entries, regs_dir);
+	if (err)
+		return err;
+
+	err = dw_edma_debugfs_regs_wr(regs_dir);
+	if (err)
+		return err;
+
+	err = dw_edma_debugfs_regs_rd(regs_dir);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+int dw_edma_v0_debugfs_on(struct dw_edma_chip *chip)
+{
+	struct dentry *entry;
+	int err;
+
+	dw = chip->dw;
+	if (!dw)
+		return -EPERM;
+
+	regs = (struct dw_edma_v0_regs *)dw->regs;
+	if (!regs)
+		return -EPERM;
+
+	base_dir = debugfs_create_dir(DRV_NAME, 0);
+	if (!base_dir)
+		return -EPERM;
+
+	entry = debugfs_create_u32("version", RD_PERM, base_dir, &dw->version);
+	if (!entry)
+		return -EPERM;
+
+	entry = debugfs_create_u32("mode", RD_PERM, base_dir, &dw->mode);
+	if (!entry)
+		return -EPERM;
+
+	entry = debugfs_create_u16("wr_ch_count", RD_PERM, base_dir,
+				   &dw->wr_ch_count);
+	if (!entry)
+		return -EPERM;
+
+	entry = debugfs_create_u16("rd_ch_count", RD_PERM, base_dir,
+				   &dw->rd_ch_count);
+	if (!entry)
+		return -EPERM;
+
+	err = dw_edma_debugfs_regs();
+	return err;
+}
+
+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..175f646
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA v0 core
+ */
+
+#ifndef _DW_EDMA_V0_DEBUG_FS_H
+#define _DW_EDMA_V0_DEBUG_FS_H
+
+#include <linux/dma/edma.h>
+
+#ifdef CONFIG_DEBUG_FS
+int dw_edma_v0_debugfs_on(struct dw_edma_chip *chip);
+void dw_edma_v0_debugfs_off(void);
+#else
+static inline int dw_edma_v0_debugfs_on(struct dw_edma_chip *chip);
+{
+	return 0;
+}
+
+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	[flat|nested] 10+ messages in thread

* [RFC v2 4/6] PCI: Add Synopsys endpoint EDDA Device id
  2018-12-17 17:19 [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Gustavo Pimentel
                   ` (2 preceding siblings ...)
  2018-12-17 17:19 ` [RFC v2 3/6] dmaengine: Add Synopsys eDMA IP version 0 debugfs support Gustavo Pimentel
@ 2018-12-17 17:19 ` Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 5/6] dmaengine: Add Synopsys eDMA IP PCIe glue-logic Gustavo Pimentel
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Gustavo Pimentel @ 2018-12-17 17:19 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Kishon Vijay Abraham I, Bjorn Helgaas,
	Lorenzo Pieralisi, Niklas Cassel, Joao Pinto, Jose Abreu,
	Luis Oliveira, Vitor Soares, Nelson Costa, Pedro Sousa

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).

Changes:
RFC v1->RFC v2:
 - Reword subject line patch
 - Reorder patch order on the series

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: Niklas Cassel <niklas.cassel@linaro.org>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Jose Abreu <jose.abreu@synopsys.com>
Cc: Luis Oliveira <lolivei@synopsys.com>
Cc: Vitor Soares <vitor.soares@synopsys.com>
Cc: Nelson Costa <nelson.costa@synopsys.com>
Cc: Pedro Sousa <pedrom.sousa@synopsys.com>
---
 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 896e2df..d27efe838 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -788,7 +788,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
 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_SYNOPSYS, 0xedda) },
+	{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 69f0abe..57f17dd 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2358,6 +2358,7 @@
 #define PCI_DEVICE_ID_CENATEK_IDE	0x0001
 
 #define PCI_VENDOR_ID_SYNOPSYS		0x16c3
+#define PCI_DEVICE_ID_SYNOPSYS_EDDA	0xedda
 
 #define PCI_VENDOR_ID_VITESSE		0x1725
 #define PCI_DEVICE_ID_VITESSE_VSC7174	0x7174
-- 
2.7.4


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC v2 5/6] dmaengine: Add Synopsys eDMA IP PCIe glue-logic
  2018-12-17 17:19 [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Gustavo Pimentel
                   ` (3 preceding siblings ...)
  2018-12-17 17:19 ` [RFC v2 4/6] PCI: Add Synopsys endpoint EDDA Device id Gustavo Pimentel
@ 2018-12-17 17:19 ` Gustavo Pimentel
  2018-12-17 17:19 ` [RFC v2 6/6] MAINTAINERS: Add Synopsys eDMA IP driver maintainer Gustavo Pimentel
  2018-12-17 22:21 ` [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Niklas Cassel
  6 siblings, 0 replies; 10+ messages in thread
From: Gustavo Pimentel @ 2018-12-17 17:19 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Dan Williams, Eugeniy Paltsev,
	Andy Shevchenko, Russell King, Niklas Cassel, Lorenzo Pieralisi,
	Joao Pinto, Jose Abreu, Luis Oliveira, Vitor Soares,
	Nelson Costa, Pedro Sousa

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 linked list BAR
 - eDMA linked list offset
 - eDMA linked list size
 - eDMA version
 - eDMA mode

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 = 0x1000 (4 Kbytes)
 - eDMA linked list BAR = 2
 - eDMA linked list offset = 0x0 (0 Kbytes)
 - eDMA linked list size = 0x20000 (128 Kbytes)
 - eDMA version = 0
 - eDMA mode = EDMA_MODE_UNROLL

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.

Changes:
RFC v1->RFC v2:
 - Replace comments // (C99 style) by /**/
 - Merge two pcim_iomap_regions() calls into just one call
 - Remove pci_try_set_mwi() call
 - Replace some dev_info() by dev_dbg() to reduce *noise*
 - Remove pci_name(pdev) call after being call dw_edma_remove()
 - Remove all power management support
 - Fix the headers of the .c and .h files according to the most recent
   convention
 - Fix errors and checks pointed out by checkpatch with --strict option
 - Replace patch small description tag from dma by dmaengine

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Eugeniy Paltsev <paltsev@synopsys.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Niklas Cassel <niklas.cassel@linaro.org>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Jose Abreu <jose.abreu@synopsys.com>
Cc: Luis Oliveira <lolivei@synopsys.com>
Cc: Vitor Soares <vitor.soares@synopsys.com>
Cc: Nelson Costa <nelson.costa@synopsys.com>
Cc: Pedro Sousa <pedrom.sousa@synopsys.com>
---
 drivers/dma/dw-edma/Kconfig        |   9 ++
 drivers/dma/dw-edma/Makefile       |   1 +
 drivers/dma/dw-edma/dw-edma-pcie.c | 258 +++++++++++++++++++++++++++++++++++++
 3 files changed, 268 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..6305768
--- /dev/null
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare eDMA PCIe driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/dma/edma.h>
+
+#include "dw-edma-core.h"
+
+enum dw_edma_pcie_bar {
+	BAR_0,
+	BAR_1,
+	BAR_2,
+	BAR_3,
+	BAR_4,
+	BAR_5
+};
+
+struct dw_edma_pcie_data {
+	enum dw_edma_pcie_bar		regs_bar;
+	u64				regs_off;
+	enum dw_edma_pcie_bar		ll_bar;
+	u64				ll_off;
+	size_t				ll_sz;
+	u32				version;
+	enum dw_edma_mode		mode;
+};
+
+static const struct dw_edma_pcie_data snps_edda_data = {
+	/* eDMA registers location */
+	.regs_bar			= BAR_0,
+	.regs_off			= 0x1000,	/*   4 KBytes */
+	/* eDMA memory linked list location */
+	.ll_bar				= BAR_2,
+	.ll_off				= 0,		/*   0 KBytes */
+	.ll_sz				= 0x20000,	/* 128 KBytes */
+	/* Other */
+	.version			= 0,
+	.mode				= EDMA_MODE_UNROLL,
+};
+
+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;
+	struct dw_edma *dw;
+	void __iomem *reg;
+	int err, irq = -1;
+	u32 addr_hi, addr_lo;
+	u16 flags;
+	u8 cap_off;
+
+	if (!pdata) {
+		dev_err(dev, "%s missing data struture\n",
+			pci_name(pdev));
+		return -EFAULT;
+	}
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(dev, "%s enabling device failed\n",
+			pci_name(pdev));
+		return err;
+	}
+
+	err = pcim_iomap_regions(pdev,
+				 BIT(pdata->regs_bar) | BIT(pdata->ll_bar),
+				 pci_name(pdev));
+	if (err) {
+		dev_err(dev, "%s eDMA BAR I/O remapping failed\n",
+			pci_name(pdev));
+		return err;
+	}
+
+	pci_set_master(pdev);
+
+	err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (err) {
+		dev_err(dev, "%s DMA mask set failed\n",
+			pci_name(pdev));
+		return err;
+	}
+
+	err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (err) {
+		dev_err(dev, "%s consistent DMA mask set failed\n",
+			pci_name(pdev));
+		return err;
+	}
+
+	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
+	if (!dw)
+		return -ENOMEM;
+
+	irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX);
+	if (irq < 0) {
+		dev_err(dev, "%s failed to alloc IRQ vector\n",
+			pci_name(pdev));
+		return -EPERM;
+	}
+
+	chip->dw = dw;
+	chip->dev = dev;
+	chip->id = pdev->devfn;
+	chip->irq = pdev->irq;
+
+	dw->regs = pcim_iomap_table(pdev)[pdata->regs_bar];
+	dw->regs += pdata->regs_off;
+
+	dw->va_ll = pcim_iomap_table(pdev)[pdata->ll_bar];
+	dw->va_ll += pdata->ll_off;
+	dw->pa_ll = pdev->resource[pdata->ll_bar].start;
+	dw->pa_ll += pdata->ll_off;
+	dw->ll_sz = pdata->ll_sz;
+
+	dw->msi_addr = 0;
+	dw->msi_data = 0;
+
+	dw->version = pdata->version;
+	dw->mode = pdata->mode;
+
+	dev_dbg(dev, "Version:\t%u\n", dw->version);
+
+	dev_dbg(dev, "Mode:\t%s\n",
+		dw->mode == EDMA_MODE_LEGACY ? "Legacy" : "Unroll");
+
+	dev_dbg(dev, "Registers:\tBAR=%u, off=0x%.16llx B, addr=0x%.8lx\n",
+		pdata->regs_bar, pdata->regs_off,
+		(unsigned long)dw->regs);
+
+	dev_dbg(dev, "L. List:\tBAR=%u, off=0x%.16llx B, sz=0x%.8x B, vaddr=0x%.8lx, paddr=0x%.8lx",
+		pdata->ll_bar, pdata->ll_off, pdata->ll_sz,
+		(unsigned long)dw->va_ll,
+		(unsigned long)dw->pa_ll);
+
+	if (pdev->msi_cap && pdev->msi_enabled) {
+		cap_off = pdev->msi_cap + PCI_MSI_FLAGS;
+		pci_read_config_word(pdev, cap_off, &flags);
+		if (flags & PCI_MSI_FLAGS_ENABLE) {
+			cap_off = pdev->msi_cap + PCI_MSI_ADDRESS_LO;
+			pci_read_config_dword(pdev, cap_off, &addr_lo);
+
+			if (flags & PCI_MSI_FLAGS_64BIT) {
+				cap_off = pdev->msi_cap + PCI_MSI_ADDRESS_HI;
+				pci_read_config_dword(pdev, cap_off, &addr_hi);
+				cap_off = pdev->msi_cap + PCI_MSI_DATA_64;
+			} else {
+				addr_hi = 0;
+				cap_off = pdev->msi_cap + PCI_MSI_DATA_32;
+			}
+
+			dw->msi_addr = addr_hi;
+			dw->msi_addr <<= 32;
+			dw->msi_addr |= addr_lo;
+
+			pci_read_config_dword(pdev, cap_off, &dw->msi_data);
+			dw->msi_data &= 0xffff;
+
+			dev_dbg(dev, "MSI:\t\taddr=0x%.16llx, data=0x%.8x, nr=%d\n",
+				dw->msi_addr, dw->msi_data, pdev->irq);
+		}
+	}
+
+	if (pdev->msix_cap && pdev->msix_enabled) {
+		u32 offset;
+		u8 bir;
+
+		cap_off = pdev->msix_cap + PCI_MSIX_FLAGS;
+		pci_read_config_word(pdev, cap_off, &flags);
+
+		if (flags & PCI_MSIX_FLAGS_ENABLE) {
+			cap_off = pdev->msix_cap + PCI_MSIX_TABLE;
+			pci_read_config_dword(pdev, cap_off, &offset);
+
+			bir = offset & PCI_MSIX_TABLE_BIR;
+			offset &= PCI_MSIX_TABLE_OFFSET;
+
+			reg = pcim_iomap_table(pdev)[bir];
+			reg += offset;
+
+			addr_lo = readl(reg + PCI_MSIX_ENTRY_LOWER_ADDR);
+			addr_hi = readl(reg + PCI_MSIX_ENTRY_UPPER_ADDR);
+			dw->msi_addr = addr_hi;
+			dw->msi_addr <<= 32;
+			dw->msi_addr |= addr_lo;
+
+			dw->msi_data = readl(reg + PCI_MSIX_ENTRY_DATA);
+
+			dev_dbg(dev,
+				"MSI-X:\taddr=0x%.16llx, data=0x%.8x, nr=%d\n",
+				dw->msi_addr, dw->msi_data, pdev->irq);
+		}
+	}
+
+	if (!pdev->msi_enabled && !pdev->msix_enabled) {
+		dev_err(dev, "%s enable interrupt failed\n",
+			pci_name(pdev));
+		return -EPERM;
+	}
+
+	err = dw_edma_probe(chip);
+	if (err) {
+		dev_err(dev, "%s eDMA probe failed\n",
+			pci_name(pdev));
+		return err;
+	}
+
+	pci_set_drvdata(pdev, chip);
+
+	dev_info(dev, "DesignWare eDMA PCIe driver loaded completely\n");
+
+	return 0;
+}
+
+static void dw_edma_pcie_remove(struct pci_dev *pdev)
+{
+	struct dw_edma_chip *chip = pci_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+	int err;
+
+	err = dw_edma_remove(chip);
+	if (err)
+		dev_warn(dev, "can't remove device properly: %d\n", err);
+
+	pci_free_irq_vectors(pdev);
+
+	dev_info(dev, "DesignWare eDMA PCIe driver unloaded completely\n");
+}
+
+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	[flat|nested] 10+ messages in thread

* [RFC v2 6/6] MAINTAINERS: Add Synopsys eDMA IP driver maintainer
  2018-12-17 17:19 [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Gustavo Pimentel
                   ` (4 preceding siblings ...)
  2018-12-17 17:19 ` [RFC v2 5/6] dmaengine: Add Synopsys eDMA IP PCIe glue-logic Gustavo Pimentel
@ 2018-12-17 17:19 ` Gustavo Pimentel
  2018-12-17 22:21 ` [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Niklas Cassel
  6 siblings, 0 replies; 10+ messages in thread
From: Gustavo Pimentel @ 2018-12-17 17:19 UTC (permalink / raw)
  To: linux-pci, dmaengine
  Cc: Gustavo Pimentel, Vinod Koul, Eugeniy Paltsev, Joao Pinto,
	Jose Abreu, Luis Oliveira, Vitor Soares, Nelson Costa,
	Pedro Sousa

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).

Changes:
RFC v1->RFC v2:
 - No changes

Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: Eugeniy Paltsev <paltsev@synopsys.com>
Cc: Joao Pinto <jpinto@synopsys.com>
Cc: Jose Abreu <jose.abreu@synopsys.com>
Cc: Luis Oliveira <lolivei@synopsys.com>
Cc: Vitor Soares <vitor.soares@synopsys.com>
Cc: Nelson Costa <nelson.costa@synopsys.com>
Cc: Pedro Sousa <pedrom.sousa@synopsys.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index f485597..bdbfc14 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4256,6 +4256,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	[flat|nested] 10+ messages in thread

* Re: [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0)
  2018-12-17 17:19 [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Gustavo Pimentel
                   ` (5 preceding siblings ...)
  2018-12-17 17:19 ` [RFC v2 6/6] MAINTAINERS: Add Synopsys eDMA IP driver maintainer Gustavo Pimentel
@ 2018-12-17 22:21 ` Niklas Cassel
  2018-12-18 10:36   ` Gustavo Pimentel
  6 siblings, 1 reply; 10+ messages in thread
From: Niklas Cassel @ 2018-12-17 22:21 UTC (permalink / raw)
  To: Gustavo Pimentel
  Cc: linux-pci, dmaengine, Vinod Koul, Andy Shevchenko, Russell King,
	Eugeniy Paltsev, Lorenzo Pieralisi, Bjorn Helgaas,
	Kishon Vijay Abraham I, Joao Pinto, Jose Abreu, Luis Oliveira,
	Vitor Soares, Nelson Costa, Pedro Sousa

On Mon, Dec 17, 2018 at 06:19:32PM +0100, Gustavo Pimentel wrote:
> Add Synopsys eDMA IP driver (version 0) 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
>  - PCIe EndPoint glue-logic
> 
> 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

Hello Gustavo,

Nice to see support for the embedded DMA controller used in the DWC PCIe
controller.

Some kind of documentation in Documentation/ would be nice.


The eDMA can be used both while the PCIe controller is in EP and RC mode,
right?
I guess that this support is only to use the eDMA for RC mode?

Do you have any benchmarks to share? (Perhaps with different buffer sizes).
Not sure what we will see, lower CPU utilization but more IRQs?
Have you thought about doing IRQ coalescing?

Usually we need dmas and dma-names in device tree, however, maybe that
isn't needed here, since it is an embedded DMA controller.
Don't we even need/want a DT property in the PCIe node, that says if we
want to enable the eDMA or not?

Usually a PCIe EP driver will call pci_ioremap_bar() and then use
readl()/writel() directly.
But since we now need to setup a DMA transfer, and wait for an IRQ,
how does this work?

I have a board that has a CC_DMA_ENABLE set, so if you give me some
detailed instructions how to test, I might be able to provide you with a
Tested-by.


Kind regards,
Niklas


> 
>  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       | 845 +++++++++++++++++++++++++++++++
>  drivers/dma/dw-edma/dw-edma-core.h       | 147 ++++++
>  drivers/dma/dw-edma/dw-edma-pcie.c       | 258 ++++++++++
>  drivers/dma/dw-edma/dw-edma-v0-core.c    | 361 +++++++++++++
>  drivers/dma/dw-edma/dw-edma-v0-core.h    |  26 +
>  drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 357 +++++++++++++
>  drivers/dma/dw-edma/dw-edma-v0-debugfs.h |  24 +
>  drivers/dma/dw-edma/dw-edma-v0-regs.h    | 145 ++++++
>  drivers/misc/pci_endpoint_test.c         |   2 +-
>  include/linux/dma/edma.h                 |  43 ++
>  include/linux/pci_ids.h                  |   1 +
>  16 files changed, 2243 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
> 
> Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
> Cc: Vinod Koul <vkoul@kernel.org>
> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Cc: Russell King <rmk+kernel@armlinux.org.uk>
> Cc: Eugeniy Paltsev <paltsev@synopsys.com>
> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: Kishon Vijay Abraham I <kishon@ti.com>
> Cc: Niklas Cassel <niklas.cassel@linaro.org>
> Cc: Joao Pinto <jpinto@synopsys.com>
> Cc: Jose Abreu <jose.abreu@synopsys.com>
> Cc: Luis Oliveira <lolivei@synopsys.com>
> Cc: Vitor Soares <vitor.soares@synopsys.com>
> Cc: Nelson Costa <nelson.costa@synopsys.com>
> Cc: Pedro Sousa <pedrom.sousa@synopsys.com>
> 
> -- 
> 2.7.4
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0)
  2018-12-17 22:21 ` [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Niklas Cassel
@ 2018-12-18 10:36   ` Gustavo Pimentel
  2019-01-07 21:51     ` Niklas Cassel
  0 siblings, 1 reply; 10+ messages in thread
From: Gustavo Pimentel @ 2018-12-18 10:36 UTC (permalink / raw)
  To: Niklas Cassel, Gustavo Pimentel
  Cc: linux-pci, dmaengine, Vinod Koul, Andy Shevchenko, Russell King,
	Eugeniy Paltsev, Lorenzo Pieralisi, Bjorn Helgaas,
	Kishon Vijay Abraham I, Joao Pinto, Jose Abreu, Luis Oliveira,
	Vitor Soares, Nelson Costa, Pedro Sousa

Hi,

On 17/12/2018 22:21, Niklas Cassel wrote:
> On Mon, Dec 17, 2018 at 06:19:32PM +0100, Gustavo Pimentel wrote:
>> Add Synopsys eDMA IP driver (version 0) 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
>>  - PCIe EndPoint glue-logic
>>
>> 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
> 
> Hello Gustavo,
> 
> Nice to see support for the embedded DMA controller used in the DWC PCIe
> controller.

Yes, I included you because you were from the beginning one of those who
requested this feature. :)

> 
> Some kind of documentation in Documentation/ would be nice.

Yes, I thought so too, but under of DOCUMENTATION folder doesn't have anything
related with DMA drivers implementations either on topic of DMA or PCI.

I thought maybe putting on the dw-edma folder but that doesn't follow the
standard documentation flow.

> 
> 
> The eDMA can be used both while the PCIe controller is in EP and RC mode,
> right?

For now I can not give this certainty. My setup only have the eDMA on the EP
side and has the eDMA mapped on PCI BAR, which allows me to configure it through
PCI.

> I guess that this support is only to use the eDMA for RC mode?

For now this driver runs on the RC side (host/CPU) and configures the eDMA EP.
I've some plans to support eDMA configuration but on the EP side. I'm guessing
that also typical use case.

> 
> Do you have any benchmarks to share? (Perhaps with different buffer sizes).

I'm currently working on the dw-edma-test driver (similar to dmatest driver)
that supports DEV_TO_MEM and MEM_TO_DEV directions instead of MEM_TO_MEM
(dmatest), and adapted to WRITE and READ channels with multiple threads. After
finishing this driver I'll able to do some benchmarks and comparisons. :)

Given your enthusiasm, when I'm finished we could do some brainstorm and define
some test cases / benchmarks, what do you think?

> Not sure what we will see, lower CPU utilization but more IRQs?

Probably yes. Let me explain you

With this implementation, you can transfer several chunks (a chunk is linked
list of burst elements).

Each burst basically defines the source, destination addresses and size to
transfer (from 1 Byte to 4 GB).

This linked list will be written on EP's RAM (mapped on a PCI BAR) which the HW
block will consume it. The linked list size is dependent of this EP's RAM size,
i.e. 8 KB will allow 340 elements (bursts).

On my currently setup I'm testing the driver implementation like this:
 - 4 chunks
 - each chunk is composed by 340 elements (bursts)
 - each burst have a transfer size of 100 bytes (can be up to 4GB and the size
can be also different for each burst)

Chunk #0
       |--> Burst #0 --> ... --> Burst #339 -> IRQ (Reload new linked list)
Chunk #1
       |--> Burst #0 --> ... --> Burst #339 -> IRQ (Reload new linked list)
Chunk #2
       |--> Burst #0 --> ... --> Burst #339 -> IRQ (Reload new linked list)
Chunk #3
       |--> Burst #0 --> ... --> Burst #34 -> IRQ (End transfer)

In total I'm writing ((4 * 340) + 35) * 100 = 139500 Bytes <=> 136 KB

> Have you thought about doing IRQ coalescing?

No. I'm only focus to implement the driver for now. After the implementation and
the benchmarks, maybe we could think about it. :)

> 
> Usually we need dmas and dma-names in device tree, however, maybe that
> isn't needed here, since it is an embedded DMA controller.
> Don't we even need/want a DT property in the PCIe node, that says if we
> want to enable the eDMA or not?

That will be necessary in the future when we have eDMA in RC or when we
implement the driver to control the eDMA on the EP side.

> 
> Usually a PCIe EP driver will call pci_ioremap_bar() and then use
> readl()/writel() directly.
> But since we now need to setup a DMA transfer, and wait for an IRQ,
> how does this work?

I gave a brief explanation in a previous comment in this email (see info about
the chunks and bursts)

I'm dividing this patch series like this:

 - eDMA core + eDMA core v0 driver (implementing the interface with DMAengine
controller APIs and the interface with the eDMA HW block)

 - eDMA PCIe glue-logic driver (attaches to EP and provides memory access to
eDMA core driver), for now is attach to Synopsys Vendor and Device ID and has a
platform data specific to our EP.

 - eDMA Test driver (is a sample driver which aims to test the eDMA, but also
provides a example how an customer driver would use the eDMA driver, through the
DMAengine client APIs) (under developing, I hope to make it available before
xmas :))

Sounds logic?

> 
> I have a board that has a CC_DMA_ENABLE set, so if you give me some
> detailed instructions how to test, I might be able to provide you with a
> Tested-by.

Ok, let me just finish the eDMA test and append it to the patch series.
I'm happy to help you with this. :)

Thanks!

Gustavo

> 
> 
> Kind regards,
> Niklas
> 
> 
>>
>>  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       | 845 +++++++++++++++++++++++++++++++
>>  drivers/dma/dw-edma/dw-edma-core.h       | 147 ++++++
>>  drivers/dma/dw-edma/dw-edma-pcie.c       | 258 ++++++++++
>>  drivers/dma/dw-edma/dw-edma-v0-core.c    | 361 +++++++++++++
>>  drivers/dma/dw-edma/dw-edma-v0-core.h    |  26 +
>>  drivers/dma/dw-edma/dw-edma-v0-debugfs.c | 357 +++++++++++++
>>  drivers/dma/dw-edma/dw-edma-v0-debugfs.h |  24 +
>>  drivers/dma/dw-edma/dw-edma-v0-regs.h    | 145 ++++++
>>  drivers/misc/pci_endpoint_test.c         |   2 +-
>>  include/linux/dma/edma.h                 |  43 ++
>>  include/linux/pci_ids.h                  |   1 +
>>  16 files changed, 2243 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
>>
>> Signed-off-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
>> Cc: Vinod Koul <vkoul@kernel.org>
>> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Cc: Russell King <rmk+kernel@armlinux.org.uk>
>> Cc: Eugeniy Paltsev <paltsev@synopsys.com>
>> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
>> Cc: Bjorn Helgaas <bhelgaas@google.com>
>> Cc: Kishon Vijay Abraham I <kishon@ti.com>
>> Cc: Niklas Cassel <niklas.cassel@linaro.org>
>> Cc: Joao Pinto <jpinto@synopsys.com>
>> Cc: Jose Abreu <jose.abreu@synopsys.com>
>> Cc: Luis Oliveira <lolivei@synopsys.com>
>> Cc: Vitor Soares <vitor.soares@synopsys.com>
>> Cc: Nelson Costa <nelson.costa@synopsys.com>
>> Cc: Pedro Sousa <pedrom.sousa@synopsys.com>
>>
>> -- 
>> 2.7.4
>>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0)
  2018-12-18 10:36   ` Gustavo Pimentel
@ 2019-01-07 21:51     ` Niklas Cassel
  0 siblings, 0 replies; 10+ messages in thread
From: Niklas Cassel @ 2019-01-07 21:51 UTC (permalink / raw)
  To: Gustavo Pimentel
  Cc: linux-pci, dmaengine, Vinod Koul, Andy Shevchenko, Russell King,
	Eugeniy Paltsev, Lorenzo Pieralisi, Bjorn Helgaas,
	Kishon Vijay Abraham I, Joao Pinto, Jose Abreu, Luis Oliveira,
	Vitor Soares, Nelson Costa, Pedro Sousa

On Tue, Dec 18, 2018 at 10:36:58AM +0000, Gustavo Pimentel wrote:
> Hi,
> 
> On 17/12/2018 22:21, Niklas Cassel wrote:
> > On Mon, Dec 17, 2018 at 06:19:32PM +0100, Gustavo Pimentel wrote:
> >> Add Synopsys eDMA IP driver (version 0) 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
> >>  - PCIe EndPoint glue-logic
> >>
> >> 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
> > 
> > Hello Gustavo,
> > 
> > Nice to see support for the embedded DMA controller used in the DWC PCIe
> > controller.
> 
> Yes, I included you because you were from the beginning one of those who
> requested this feature. :)

Hello Gustavo,

sorry for the delayed response, I took an extra long christmas vacation :)

Unfortunately, it was my previous employer (Axis) who was interested in this.
Right now I am simply looking at this patch series out of my own curiosity :)

(snip)

> > The eDMA can be used both while the PCIe controller is in EP and RC mode,
> > right?
> 
> For now I can not give this certainty. My setup only have the eDMA on the EP
> side and has the eDMA mapped on PCI BAR, which allows me to configure it through
> PCI.

Ok, I looked at the databook, and it does mention support for both modes.

Perhaps the cover letter could mention more clearly that this is just for eDMA
on the EP side. (Since the IP seems to support eDMA for both RC and EP side.)

(snip)

> > Do you have any benchmarks to share? (Perhaps with different buffer sizes).
> 
> I'm currently working on the dw-edma-test driver (similar to dmatest driver)
> that supports DEV_TO_MEM and MEM_TO_DEV directions instead of MEM_TO_MEM
> (dmatest), and adapted to WRITE and READ channels with multiple threads. After
> finishing this driver I'll able to do some benchmarks and comparisons. :)
> 
> Given your enthusiasm, when I'm finished we could do some brainstorm and define
> some test cases / benchmarks, what do you think?

I might have the enthusiasm, but I don't have the expertise to come up with
some relevant use-cases :p

> 
> > Not sure what we will see, lower CPU utilization but more IRQs?
> 
> Probably yes. Let me explain you
> 
> With this implementation, you can transfer several chunks (a chunk is linked
> list of burst elements).
> 
> Each burst basically defines the source, destination addresses and size to
> transfer (from 1 Byte to 4 GB).
> 
> This linked list will be written on EP's RAM (mapped on a PCI BAR) which the HW
> block will consume it. The linked list size is dependent of this EP's RAM size,
> i.e. 8 KB will allow 340 elements (bursts).
> 
> On my currently setup I'm testing the driver implementation like this:
>  - 4 chunks
>  - each chunk is composed by 340 elements (bursts)
>  - each burst have a transfer size of 100 bytes (can be up to 4GB and the size
> can be also different for each burst)
> 
> Chunk #0
>        |--> Burst #0 --> ... --> Burst #339 -> IRQ (Reload new linked list)
> Chunk #1
>        |--> Burst #0 --> ... --> Burst #339 -> IRQ (Reload new linked list)
> Chunk #2
>        |--> Burst #0 --> ... --> Burst #339 -> IRQ (Reload new linked list)
> Chunk #3
>        |--> Burst #0 --> ... --> Burst #34 -> IRQ (End transfer)
> 
> In total I'm writing ((4 * 340) + 35) * 100 = 139500 Bytes <=> 136 KB

This is a nice explaination, and it probably belongs in the cover letter,
and/or commit message, and/or somewhere in Documentation/, and/or as a
comment in the source code.

(snip)

> I'm dividing this patch series like this:
> 
>  - eDMA core + eDMA core v0 driver (implementing the interface with DMAengine
> controller APIs and the interface with the eDMA HW block)
> 
>  - eDMA PCIe glue-logic driver (attaches to EP and provides memory access to
> eDMA core driver), for now is attach to Synopsys Vendor and Device ID and has a
> platform data specific to our EP.
> 
>  - eDMA Test driver (is a sample driver which aims to test the eDMA, but also
> provides a example how an customer driver would use the eDMA driver, through the
> DMAengine client APIs) (under developing, I hope to make it available before
> xmas :))
> 
> Sounds logic?

This definitely belongs in the cover letter :)

> 
> > 
> > I have a board that has a CC_DMA_ENABLE set, so if you give me some
> > detailed instructions how to test, I might be able to provide you with a
> > Tested-by.
> 
> Ok, let me just finish the eDMA test and append it to the patch series.
> I'm happy to help you with this. :)

The hardware I currently have access to (Qualcomm hardware), hasn't
implemented the software support for running the PCIe controller in endpoint
mode yet.
This means that I won't be able to help you with testing this series.
Sorry for that. (I wanted to help out here, promise :p).

Hopefully there is someone else who has this IP configured with
CC_DMA_ENABLE + has EP support implemented, who can help you out :)


Kind regards,
Niklas

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, back to index

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-17 17:19 [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Gustavo Pimentel
2018-12-17 17:19 ` [RFC v2 1/6] dmaengine: Add Synopsys eDMA IP core driver Gustavo Pimentel
2018-12-17 17:19 ` [RFC v2 2/6] dmaengine: Add Synopsys eDMA IP version 0 support Gustavo Pimentel
2018-12-17 17:19 ` [RFC v2 3/6] dmaengine: Add Synopsys eDMA IP version 0 debugfs support Gustavo Pimentel
2018-12-17 17:19 ` [RFC v2 4/6] PCI: Add Synopsys endpoint EDDA Device id Gustavo Pimentel
2018-12-17 17:19 ` [RFC v2 5/6] dmaengine: Add Synopsys eDMA IP PCIe glue-logic Gustavo Pimentel
2018-12-17 17:19 ` [RFC v2 6/6] MAINTAINERS: Add Synopsys eDMA IP driver maintainer Gustavo Pimentel
2018-12-17 22:21 ` [RFC v2 0/6] dmaengine: Add Synopsys eDMA IP driver (version 0) Niklas Cassel
2018-12-18 10:36   ` Gustavo Pimentel
2019-01-07 21:51     ` Niklas Cassel

Linux-PCI Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-pci/0 linux-pci/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-pci linux-pci/ https://lore.kernel.org/linux-pci \
		linux-pci@vger.kernel.org linux-pci@archiver.kernel.org
	public-inbox-index linux-pci


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-pci


AGPL code for this site: git clone https://public-inbox.org/ public-inbox