DMA Engine development
 help / color / mirror / Atom feed
* [RESEND,v2] dmaengine: sprd: Support DMA link-list mode
From: Baolin Wang @ 2018-08-28 11:09 UTC (permalink / raw)
  To: dan.j.williams, vkoul
  Cc: eric.long, broonie, baolin.wang, dmaengine, linux-kernel

From: Eric Long <eric.long@spreadtrum.com>

The Spreadtrum DMA can support the link-list transaction mode, which means
DMA controller can do transaction one by one automatically once we linked
these transaction by link-list register.

Signed-off-by: Eric Long <eric.long@spreadtrum.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
Changes since v1:
 - Remove sprd_dma_fill_chn_desc() function.
 - Remove the redundant validation of 'sglen' in sprd_dma_fill_linklist_desc().
 - Add some comments make code more clear.
 - Fix the typos.
---
 drivers/dma/sprd-dma.c       |   81 ++++++++++++++++++++++++++++++++++++++----
 include/linux/dma/sprd-dma.h |   69 +++++++++++++++++++++++++++++++++++
 2 files changed, 143 insertions(+), 7 deletions(-)

diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c
index 55df0d4..38d4e4f 100644
--- a/drivers/dma/sprd-dma.c
+++ b/drivers/dma/sprd-dma.c
@@ -68,6 +68,7 @@
 
 /* SPRD_DMA_CHN_CFG register definition */
 #define SPRD_DMA_CHN_EN			BIT(0)
+#define SPRD_DMA_LINKLIST_EN		BIT(4)
 #define SPRD_DMA_WAIT_BDONE_OFFSET	24
 #define SPRD_DMA_DONOT_WAIT_BDONE	1
 
@@ -103,7 +104,7 @@
 #define SPRD_DMA_REQ_MODE_MASK		GENMASK(1, 0)
 #define SPRD_DMA_FIX_SEL_OFFSET		21
 #define SPRD_DMA_FIX_EN_OFFSET		20
-#define SPRD_DMA_LLIST_END_OFFSET	19
+#define SPRD_DMA_LLIST_END		BIT(19)
 #define SPRD_DMA_FRG_LEN_MASK		GENMASK(16, 0)
 
 /* SPRD_DMA_CHN_BLK_LEN register definition */
@@ -164,6 +165,7 @@ struct sprd_dma_desc {
 struct sprd_dma_chn {
 	struct virt_dma_chan	vc;
 	void __iomem		*chn_base;
+	struct sprd_dma_linklist	linklist;
 	struct dma_slave_config	slave_cfg;
 	u32			chn_num;
 	u32			dev_id;
@@ -582,7 +584,8 @@ static int sprd_dma_get_step(enum dma_slave_buswidth buswidth)
 }
 
 static int sprd_dma_fill_desc(struct dma_chan *chan,
-			      struct sprd_dma_desc *sdesc,
+			      struct sprd_dma_chn_hw *hw,
+			      unsigned int sglen, int sg_index,
 			      dma_addr_t src, dma_addr_t dst, u32 len,
 			      enum dma_transfer_direction dir,
 			      unsigned long flags,
@@ -590,7 +593,6 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
 {
 	struct sprd_dma_dev *sdev = to_sprd_dma_dev(chan);
 	struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
-	struct sprd_dma_chn_hw *hw = &sdesc->chn_hw;
 	u32 req_mode = (flags >> SPRD_DMA_REQ_SHIFT) & SPRD_DMA_REQ_MODE_MASK;
 	u32 int_mode = flags & SPRD_DMA_INT_MASK;
 	int src_datawidth, dst_datawidth, src_step, dst_step;
@@ -670,12 +672,52 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
 	temp |= (src_step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_SRC_TRSF_STEP_OFFSET;
 	hw->trsf_step = temp;
 
+	/* link-list configuration */
+	if (schan->linklist.phy_addr) {
+		if (sg_index == sglen - 1)
+			hw->frg_len |= SPRD_DMA_LLIST_END;
+
+		hw->cfg |= SPRD_DMA_LINKLIST_EN;
+
+		/* link-list index */
+		temp = (sg_index + 1) % sglen;
+		/* Next link-list configuration's physical address offset */
+		temp = temp * sizeof(*hw) + SPRD_DMA_CHN_SRC_ADDR;
+		/*
+		 * Set the link-list pointer point to next link-list
+		 * configuration's physical address.
+		 */
+		hw->llist_ptr = schan->linklist.phy_addr + temp;
+	} else {
+		hw->llist_ptr = 0;
+	}
+
 	hw->frg_step = 0;
 	hw->src_blk_step = 0;
 	hw->des_blk_step = 0;
 	return 0;
 }
 
+static int sprd_dma_fill_linklist_desc(struct dma_chan *chan,
+				       unsigned int sglen, int sg_index,
+				       dma_addr_t src, dma_addr_t dst, u32 len,
+				       enum dma_transfer_direction dir,
+				       unsigned long flags,
+				       struct dma_slave_config *slave_cfg)
+{
+	struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
+	struct sprd_dma_chn_hw *hw;
+
+	if (!schan->linklist.virt_addr)
+		return -EINVAL;
+
+	hw = (struct sprd_dma_chn_hw *)(schan->linklist.virt_addr +
+					sg_index * sizeof(*hw));
+
+	return sprd_dma_fill_desc(chan, hw, sglen, sg_index, src, dst, len,
+				  dir, flags, slave_cfg);
+}
+
 static struct dma_async_tx_descriptor *
 sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
 			 size_t len, unsigned long flags)
@@ -744,10 +786,20 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
 	u32 len = 0;
 	int ret, i;
 
-	/* TODO: now we only support one sg for each DMA configuration. */
-	if (!is_slave_direction(dir) || sglen > 1)
+	if (!is_slave_direction(dir))
 		return NULL;
 
+	if (context) {
+		struct sprd_dma_linklist *ll_cfg =
+			(struct sprd_dma_linklist *)context;
+
+		schan->linklist.phy_addr = ll_cfg->phy_addr;
+		schan->linklist.virt_addr = ll_cfg->virt_addr;
+	} else {
+		schan->linklist.phy_addr = 0;
+		schan->linklist.virt_addr = 0;
+	}
+
 	sdesc = kzalloc(sizeof(*sdesc), GFP_NOWAIT);
 	if (!sdesc)
 		return NULL;
@@ -762,10 +814,25 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
 			src = slave_cfg->src_addr;
 			dst = sg_dma_address(sg);
 		}
+
+		/*
+		 * The link-list mode needs at least 2 link-list
+		 * configurations. If there is only one sg, it doesn't
+		 * need to fill the link-list configuration.
+		 */
+		if (sglen < 2)
+			break;
+
+		ret = sprd_dma_fill_linklist_desc(chan, sglen, i, src, dst, len,
+						  dir, flags, slave_cfg);
+		if (ret) {
+			kfree(sdesc);
+			return NULL;
+		}
 	}
 
-	ret = sprd_dma_fill_desc(chan, sdesc, src, dst, len, dir, flags,
-				 slave_cfg);
+	ret = sprd_dma_fill_desc(chan, &sdesc->chn_hw, 0, 0, src, dst, len,
+				 dir, flags, slave_cfg);
 	if (ret) {
 		kfree(sdesc);
 		return NULL;
diff --git a/include/linux/dma/sprd-dma.h b/include/linux/dma/sprd-dma.h
index b0115e3..b42b80e5 100644
--- a/include/linux/dma/sprd-dma.h
+++ b/include/linux/dma/sprd-dma.h
@@ -58,4 +58,73 @@ enum sprd_dma_int_type {
 	SPRD_DMA_CFGERR_INT,
 };
 
+/*
+ * struct sprd_dma_linklist - DMA link-list address structure
+ * @virt_addr: link-list virtual address to configure link-list node
+ * @phy_addr: link-list physical address to link DMA transfer
+ *
+ * The Spreadtrum DMA controller supports the link-list mode, that means slaves
+ * can supply several groups configurations (each configuration represents one
+ * DMA transfer) saved in memory, and DMA controller will link these groups
+ * configurations by writing the physical address of each configuration into the
+ * link-list register.
+ *
+ * Just as shown below, the link-list pointer register will be pointed to the
+ * physical address of 'configuration 1', and the 'configuration 1' link-list
+ * pointer will be pointed to 'configuration 2', and so on.
+ * Once trigger the DMA transfer, the DMA controller will load 'configuration
+ * 1' to its registers automatically, after 'configuration 1' transaction is
+ * done, DMA controller will load 'configuration 2' automatically, until all
+ * DMA transactions are done.
+ *
+ * Note: The last link-list pointer should point to the physical address
+ * of 'configuration 1', which can avoid DMA controller loads incorrect
+ * configuration when the last configuration transaction is done.
+ *
+ *     DMA controller                    linklist memory
+ * ======================             -----------------------
+ *|                      |           |    configuration 1    |<---
+ *|   DMA controller     |   ------->|                       |   |
+ *|                      |   |       |                       |   |
+ *|                      |   |       |                       |   |
+ *|                      |   |       |                       |   |
+ *| linklist pointer reg |----   ----|    linklist pointer   |   |
+ * ======================        |    -----------------------    |
+ *                               |                               |
+ *                               |    -----------------------    |
+ *                               |   |    configuration 2    |   |
+ *                               --->|                       |   |
+ *                                   |                       |   |
+ *                                   |                       |   |
+ *                                   |                       |   |
+ *                               ----|    linklist pointer   |   |
+ *                               |    -----------------------    |
+ *                               |                               |
+ *                               |    -----------------------    |
+ *                               |   |    configuration 3    |   |
+ *                               --->|                       |   |
+ *                                   |                       |   |
+ *                                   |           .           |   |
+ *                                               .               |
+ *                                               .               |
+ *                                               .               |
+ *                               |               .               |
+ *                               |    -----------------------    |
+ *                               |   |    configuration n    |   |
+ *                               --->|                       |   |
+ *                                   |                       |   |
+ *                                   |                       |   |
+ *                                   |                       |   |
+ *                                   |    linklist pointer   |----
+ *                                    -----------------------
+ *
+ * To support the link-list mode, DMA slaves should allocate one segment memory
+ * from always-on IRAM or dma coherent memory to store these groups of DMA
+ * configuration, and pass the virtual and physical address to DMA controller.
+ */
+struct sprd_dma_linklist {
+	unsigned long virt_addr;
+	phys_addr_t phy_addr;
+};
+
 #endif

^ permalink raw reply related

* dmaengine: Convert to using %pOFn instead of device_node.name
From: Vinod Koul @ 2018-08-28  5:16 UTC (permalink / raw)
  To: Rob Herring; +Cc: linux-kernel, Dan Williams, dmaengine

On 27-08-18, 20:52, Rob Herring wrote:
> In preparation to remove the node name pointer from struct device_node,
> convert printf users to use the %pOFn format specifier.
> 
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Vinod Koul <vkoul@kernel.org>
> Cc: dmaengine@vger.kernel.org

Applied after dropping CC list. thanks

^ permalink raw reply

* dmaengine: Convert to using %pOFn instead of device_node.name
From: Rob Herring @ 2018-08-28  1:52 UTC (permalink / raw)
  To: linux-kernel; +Cc: Dan Williams, Vinod Koul, dmaengine

In preparation to remove the node name pointer from struct device_node,
convert printf users to use the %pOFn format specifier.

Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Vinod Koul <vkoul@kernel.org>
Cc: dmaengine@vger.kernel.org
Signed-off-by: Rob Herring <robh@kernel.org>
---
 drivers/dma/nbpfaxi.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c
index 8c7b2e8703da..8d4914cd06f0 100644
--- a/drivers/dma/nbpfaxi.c
+++ b/drivers/dma/nbpfaxi.c
@@ -1095,8 +1095,8 @@ static struct dma_chan *nbpf_of_xlate(struct of_phandle_args *dma_spec,
 	if (!dchan)
 		return NULL;
 
-	dev_dbg(dchan->device->dev, "Entry %s(%s)\n", __func__,
-		dma_spec->np->name);
+	dev_dbg(dchan->device->dev, "Entry %s(%pOFn)\n", __func__,
+		dma_spec->np);
 
 	chan = nbpf_to_chan(dchan);
 

^ permalink raw reply related

* [v8,3/7] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs
From: Vinod Koul @ 2018-08-27  9:30 UTC (permalink / raw)
  To: Wen He; +Cc: dmaengine, robh+dt, leoyang.li, jiafei.pan, peng.ma

On 17-08-18, 10:54, Wen He wrote:
> NXP Queue DMA controller(qDMA) on Layerscape SoCs supports channel
> virtuallization by allowing DMA jobs to be enqueued into different

typo virtuallization

> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>

all these of_ headers, are all required?

> +static inline u64
> +qdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf)
> +{
> +	return le64_to_cpu(ccdf->data) & (U64_MAX >> 24);

so what is this *supposed* to do, data is u64 right?

> +static inline void
> +qdma_desc_addr_set64(struct fsl_qdma_format *ccdf, u64 addr)
> +{
> +	ccdf->addr_hi = upper_32_bits(addr);
> +	ccdf->addr_lo = cpu_to_le32(lower_32_bits(addr));
> +}
> +
> +static inline u64
> +qdma_ccdf_get_queue(const struct fsl_qdma_format *ccdf)
> +{
> +	return ccdf->cfg8b_w1 & U8_MAX;

why not return u8?

> +static struct fsl_qdma_queue *fsl_qdma_alloc_queue_resources(
> +					struct platform_device *pdev,
> +					unsigned int queue_num)
> +{
> +	struct fsl_qdma_queue *queue_head, *queue_temp;
> +	int ret, i;
> +	unsigned int queue_size[FSL_QDMA_QUEUE_MAX];

it would help in readability if this is preceding, inverted christmas
tree :)

> +
> +	if (queue_num > FSL_QDMA_QUEUE_MAX)
> +		queue_num = FSL_QDMA_QUEUE_MAX;
> +	queue_head = devm_kcalloc(&pdev->dev, queue_num,
> +				sizeof(*queue_head), GFP_KERNEL);
> +	if (!queue_head)
> +		return NULL;
> +
> +	ret = device_property_read_u32_array(&pdev->dev, "queue-sizes",
> +					queue_size, queue_num);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Can't get queue-sizes.\n");
> +		return NULL;
> +	}
> +
> +	for (i = 0; i < queue_num; i++) {
> +		if (queue_size[i] > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX ||
> +			    queue_size[i] < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
> +			dev_err(&pdev->dev, "Get wrong queue-sizes.\n");
> +			return NULL;
> +		}
> +		queue_temp = queue_head + i;
> +		queue_temp->cq = dma_alloc_coherent(&pdev->dev,
> +						sizeof(struct fsl_qdma_format) *
> +						queue_size[i],
> +						&queue_temp->bus_addr,
> +						GFP_KERNEL);
> +		if (!queue_temp->cq) {
> +			devm_kfree(&pdev->dev, queue_head);

why do you want to do that?

> +static struct fsl_qdma_queue *fsl_qdma_prep_status_queue(
> +						struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct fsl_qdma_queue *status_head;
> +	unsigned int status_size;
> +	int ret;
> +
> +	ret = of_property_read_u32(np, "status-sizes", &status_size);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Can't get status-sizes.\n");
> +		return NULL;
> +	}
> +	if (status_size > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX
> +			|| status_size < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
> +		dev_err(&pdev->dev, "Get wrong status_size.\n");
> +		return NULL;
> +	}
> +	status_head = devm_kzalloc(&pdev->dev, sizeof(*status_head),
> +								GFP_KERNEL);

this is bad on my eyes! How about?

        status_head = devm_kzalloc(&pdev->dev,
                                  sizeof(*status_head), GFP_KERNEL);

for here and few other format style you used. running checkpatch with
--strict option would help as well

> +static int fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma)
> +{
> +	struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
> +	struct fsl_qdma_queue *fsl_status = fsl_qdma->status;
> +	struct fsl_qdma_queue *temp_queue;
> +	struct fsl_qdma_comp *fsl_comp;
> +	struct fsl_qdma_format *status_addr;
> +	struct fsl_qdma_format *csgf_src;
> +	struct fsl_pre_status pre;
> +	void __iomem *block = fsl_qdma->block_base;
> +	u32 reg, i, count;
> +	bool duplicate;
> +
> +	memset(&pre, 0, sizeof(struct fsl_pre_status));
> +	count = FSL_QDMA_MAX_SIZE;
> +
> +	while (count--) {
> +		duplicate = 0;
> +
> +		reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQSR);
> +		if (reg & FSL_QDMA_BSQSR_QE)
> +			return 0;
> +
> +		status_addr = fsl_status->virt_head;
> +		if (qdma_ccdf_get_queue(status_addr) == pre.queue &&
> +			qdma_ccdf_addr_get64(status_addr) == pre.addr)
> +			duplicate = 1;

bad indents :(

> +
> +		i = qdma_ccdf_get_queue(status_addr);
> +		pre.queue = qdma_ccdf_get_queue(status_addr);
> +		pre.addr = qdma_ccdf_addr_get64(status_addr);
> +		temp_queue = fsl_queue + i;
> +		spin_lock(&temp_queue->queue_lock);
> +		if (list_empty(&temp_queue->comp_used)) {
> +			if (!duplicate) {
> +				spin_unlock(&temp_queue->queue_lock);
> +				return -EAGAIN;
> +			}
> +		} else {
> +			fsl_comp = list_first_entry(&temp_queue->comp_used,
> +							struct fsl_qdma_comp,
> +							list);
> +			csgf_src = fsl_comp->virt_addr + 2;
> +			if (fsl_comp->bus_addr + 16 != pre.addr) {
> +				if (duplicate) {
> +					spin_unlock(&temp_queue->queue_lock);
> +					return -EAGAIN;
> +				}
> +			}
> +		}
> +
> +			if (duplicate) {
> +				reg = qdma_readl(fsl_qdma, block +
> +						FSL_QDMA_BSQMR);

this code block indent seems complete wrong or something is really
messed up here, not aligning with else above!

> +static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id)
> +{
> +	struct fsl_qdma_engine *fsl_qdma = dev_id;
> +	unsigned int intr;
> +	void __iomem *status = fsl_qdma->status_base;
> +
> +	intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR);
> +
> +	if (intr)
> +		dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n");
> +
> +	/* Clear all error conditions and interrupts. */
> +	qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR);

clear even if intr is not set?

> +static enum dma_status fsl_qdma_tx_status(struct dma_chan *chan,
> +		dma_cookie_t cookie, struct dma_tx_state *txstate)
> +{
> +	enum dma_status ret;
> +
> +	ret = dma_cookie_status(chan, cookie, txstate);
> +	if (ret == DMA_COMPLETE || !txstate)
> +		return ret;
> +
> +	return ret;

        return dma_cookie_status()

or use dma_cookie_status as your status fn?

^ permalink raw reply

* [2/2] dmaengine: dw: Add alternative ACPI HIDs for Cherry Trail DMA controllers
From: Hans de Goede @ 2018-08-27  7:45 UTC (permalink / raw)
  To: Rafael J . Wysocki, Len Brown, Viresh Kumar, Andy Shevchenko,
	Vinod Koul
  Cc: Hans de Goede, linux-acpi, Dan Williams, dmaengine

Bay and Cherry Trail DSTDs represent a different set of devices depending
on which OS the device think it is booting. One set of decices for Windows
and another set of devices for Android which targets the Android-x86 Linux
kernel fork (which e.g. used to have its own display driver instead of
using the i915 driver).

Which set of devices we are actually going to get is out of our control,
this is controlled by the ACPI OSID variable, which gets either set through
an EFI setup option, or sometimes is autodetected. So we need to support
both.

This commit adds support for the 80862286 and 808622C0 ACPI HIDs which we
get for the first resp. second DMA controller on Cherry Trail devices when
OSID is set to Android.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/dma/dw/platform.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c
index f62dd0944908..f01b2c173fa6 100644
--- a/drivers/dma/dw/platform.c
+++ b/drivers/dma/dw/platform.c
@@ -284,6 +284,8 @@ MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
 #ifdef CONFIG_ACPI
 static const struct acpi_device_id dw_dma_acpi_id_table[] = {
 	{ "INTL9C60", 0 },
+	{ "80862286", 0 },
+	{ "808622C0", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);

^ permalink raw reply related

* [1/2] ACPI / LPSS: Add alternative ACPI HIDs for Cherry Trail DMA controllers
From: Hans de Goede @ 2018-08-27  7:45 UTC (permalink / raw)
  To: Rafael J . Wysocki, Len Brown, Viresh Kumar, Andy Shevchenko,
	Vinod Koul
  Cc: Hans de Goede, linux-acpi, Dan Williams, dmaengine

Bay and Cherry Trail DSTDs represent a different set of devices depending
on which OS the device think it is booting. One set of decices for Windows
and another set of devices for Android which targets the Android-x86 Linux
kernel fork (which e.g. used to have its own display driver instead of
using the i915 driver).

Which set of devices we are actually going to get is out of our control,
this is controlled by the ACPI OSID variable, which gets either set through
an EFI setup option, or sometimes is autodetected. So we need to support
both.

This commit adds support for the 80862286 and 808622C0 ACPI HIDs which we
get for the first resp. second DMA controller on Cherry Trail devices when
OSID is set to Android.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/acpi/acpi_lpss.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 0569972b02e1..95d9c45e13cc 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -327,9 +327,11 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
 	{ "INT33FC", },
 
 	/* Braswell LPSS devices */
+	{ "80862286", LPSS_ADDR(lpss_dma_desc) },
 	{ "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
 	{ "8086228A", LPSS_ADDR(bsw_uart_dev_desc) },
 	{ "8086228E", LPSS_ADDR(bsw_spi_dev_desc) },
+	{ "808622C0", LPSS_ADDR(lpss_dma_desc) },
 	{ "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) },
 
 	/* Broadwell LPSS devices */

^ permalink raw reply related

* [v8,2/7] dmaengine: fsldma: Adding macro FSL_DMA_IN/OUT implement for ARM platform
From: Vinod Koul @ 2018-08-27  6:35 UTC (permalink / raw)
  To: Wen He; +Cc: dmaengine, robh+dt, leoyang.li, jiafei.pan, peng.ma

On 17-08-18, 10:54, Wen He wrote:
> This patch add the macro FSL_DMA_IN/OUT implement for ARM platform.
> 
> Signed-off-by: Wen He <wen.he_1@nxp.com>
> ---
>  drivers/dma/fsldma.h | 57 ++++++++++++++++++++++++++++++++++------------------
>  1 file changed, 38 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
> index 982845b..1dc64c9 100644
> --- a/drivers/dma/fsldma.h
> +++ b/drivers/dma/fsldma.h
> @@ -196,39 +196,58 @@ struct fsldma_chan {
>  #define to_fsl_desc(lh) container_of(lh, struct fsl_desc_sw, node)
>  #define tx_to_fsl_desc(tx) container_of(tx, struct fsl_desc_sw, async_tx)
>  
> +#ifdef	CONFIG_PPC
> +#define fsl_ioread32(p)		in_le32(p)
> +#define fsl_ioread32be(p)	in_be32(p)
> +#define fsl_iowrite32(v, p)	out_le32(p, v)
> +#define fsl_iowrite32be(v, p)	out_be32(p, v)
> +
>  #ifndef __powerpc64__
> -static u64 in_be64(const u64 __iomem *addr)
> +static u64 fsl_ioread64(const u64 __iomem *addr)
>  {
> -	return ((u64)in_be32((u32 __iomem *)addr) << 32) |
> -		(in_be32((u32 __iomem *)addr + 1));
> +	return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) |
> +		(in_le32((u32 __iomem *)addr));

I do not like this very much

This is targeted for ARM (bit no dependency on ARM) which
means it can be triggered in others too...

Second, this casts seem not so great. can we do better?

        u32 addr = lower_32_bits(addr);

        return in_be32(addr) | in_be32(addr + 1) < 32;

makes a better read, unless I missed something?


/
>  }
>  
> -static void out_be64(u64 __iomem *addr, u64 val)
> +static void fsl_iowrite64(u64 val, u64 __iomem *addr)
>  {
> -	out_be32((u32 __iomem *)addr, val >> 32);
> -	out_be32((u32 __iomem *)addr + 1, (u32)val);
> +	out_le32((u32 __iomem *)addr + 1, val >> 32);
> +	out_le32((u32 __iomem *)addr, (u32)val);
>  }
>  
> -/* There is no asm instructions for 64 bits reverse loads and stores */
> -static u64 in_le64(const u64 __iomem *addr)
> +static u64 fsl_ioread64be(const u64 __iomem *addr)
>  {
> -	return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) |
> -		(in_le32((u32 __iomem *)addr));
> +	return ((u64)in_be32((u32 __iomem *)addr) << 32) |
> +		(in_be32((u32 __iomem *)addr + 1));

Improve this as well, please

^ permalink raw reply

* [v2] dma: mic_x100_dma: use devm_kzalloc to fix an issue
From: Vinod Koul @ 2018-08-27  5:46 UTC (permalink / raw)
  To: Huang Shijie; +Cc: sudeep.dutt, ashutosh.dixit, dmaengine, linux-kernel

On 22-08-18, 10:40, Huang Shijie wrote:
> The following patch introduced an issue.
>     commit f6206f00d8c5 ("dmaengine: mic_x100_dma: use the new helper to simplify the code")
> 
> This issue is :
> 
> 	kfree(mic_dma_dev)
> 	.....
> 	dma_async_device_unregister(mic_dma_dev->device);
> 
> Free the memory, and use it again.
> 
> So use devm_kzalloc to allocate mic_dma_dev to fix it.
> 
> When the Devres try to release the resources, it will call release at the
> following order:
> 
> 	dma_async_device_unregister(mic_dma_dev->device);
> 	.....
> 	kfree(mic_dma_dev)

After fixing the subsystem name, applied thanks

^ permalink raw reply

* [v4,5/7] dmaengine: xilinx_dma: autodetect whether the HW supports scatter-gather
From: Vinod Koul @ 2018-08-27  5:34 UTC (permalink / raw)
  To: Andrea Merello
  Cc: dan.j.williams, michal.simek, appana.durga.rao, dmaengine,
	v4-000linux-arm-kernel, linux-kernel, robh+dt, mark.rutland,
	devicetree, radhey.shyam.pandey

On 02-08-18, 16:10, Andrea Merello wrote:
> The AXIDMA and CDMA HW can be either direct-access or scatter-gather
> version. These are SW incompatible.
> 
> The driver can handle both versions: a DT property was used to
> tell the driver whether to assume the HW is in scatter-gather mode.
> 
> This patch makes the driver to autodetect this information. The DT
> property is not required anymore.
> 
> No changes for VDMA.
> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: devicetree@vger.kernel.org
> Cc: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> Signed-off-by: Andrea Merello <andrea.merello@gmail.com>
> Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> ---
> Changes in v2:
> 	- autodetect only in !VDMA case
> Changes in v3:
> 	- cc DT maintainers/ML
> Changes in v4:
> 	- fix typos in commit message
> ---
>  drivers/dma/xilinx/xilinx_dma.c | 14 ++++++++++----

So you are not removing this property from binding document? Or are
there variants which dont have hw mechanism?

>  1 file changed, 10 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
> index b17f24e4ec35..78d0f2f8225e 100644
> --- a/drivers/dma/xilinx/xilinx_dma.c
> +++ b/drivers/dma/xilinx/xilinx_dma.c
> @@ -86,6 +86,7 @@
>  #define XILINX_DMA_DMASR_DMA_DEC_ERR		BIT(6)
>  #define XILINX_DMA_DMASR_DMA_SLAVE_ERR		BIT(5)
>  #define XILINX_DMA_DMASR_DMA_INT_ERR		BIT(4)
> +#define XILINX_DMA_DMASR_SG_MASK		BIT(3)
>  #define XILINX_DMA_DMASR_IDLE			BIT(1)
>  #define XILINX_DMA_DMASR_HALTED		BIT(0)
>  #define XILINX_DMA_DMASR_DELAY_MASK		GENMASK(31, 24)
> @@ -407,7 +408,6 @@ struct xilinx_dma_config {
>   * @dev: Device Structure
>   * @common: DMA device structure
>   * @chan: Driver specific DMA channel
> - * @has_sg: Specifies whether Scatter-Gather is present or not
>   * @mcdma: Specifies whether Multi-Channel is present or not
>   * @flush_on_fsync: Flush on frame sync
>   * @ext_addr: Indicates 64 bit addressing is supported by dma device
> @@ -427,7 +427,6 @@ struct xilinx_dma_device {
>  	struct device *dev;
>  	struct dma_device common;
>  	struct xilinx_dma_chan *chan[XILINX_DMA_MAX_CHANS_PER_DEVICE];
> -	bool has_sg;
>  	bool mcdma;
>  	u32 flush_on_fsync;
>  	bool ext_addr;
> @@ -2400,7 +2399,6 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
>  
>  	chan->dev = xdev->dev;
>  	chan->xdev = xdev;
> -	chan->has_sg = xdev->has_sg;
>  	chan->desc_pendingcount = 0x0;
>  	chan->ext_addr = xdev->ext_addr;
>  	/* This variable ensures that descriptors are not
> @@ -2493,6 +2491,15 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
>  		chan->stop_transfer = xilinx_dma_stop_transfer;
>  	}
>  
> +	/* check if SG is enabled (only for AXIDMA and CDMA) */
> +	if (xdev->dma_config->dmatype != XDMA_TYPE_VDMA) {
> +		if (dma_ctrl_read(chan, XILINX_DMA_REG_DMASR) &
> +		    XILINX_DMA_DMASR_SG_MASK)
> +			chan->has_sg = true;
> +		dev_dbg(chan->dev, "ch %d: SG %s\n", chan->id,
> +			chan->has_sg ? "enabled" : "disabled");
> +	}
> +
>  	/* Initialize the tasklet */
>  	tasklet_init(&chan->tasklet, xilinx_dma_do_tasklet,
>  			(unsigned long)chan);
> @@ -2631,7 +2638,6 @@ static int xilinx_dma_probe(struct platform_device *pdev)
>  		return PTR_ERR(xdev->regs);
>  
>  	/* Retrieve the DMA engine properties from the device tree */
> -	xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg");
>  	xdev->max_buffer_len = GENMASK(XILINX_DMA_MAX_TRANS_LEN_MAX - 1, 0);
>  
>  	if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) {
> -- 
> 2.17.1

^ permalink raw reply

* [v4,3/7] dt-bindings: dmaengine: xilinx_dma: add optional xlnx,sg-length-width property
From: Vinod Koul @ 2018-08-27  5:31 UTC (permalink / raw)
  To: Andrea Merello
  Cc: dan.j.williams, michal.simek, appana.durga.rao, dmaengine,
	v4-000linux-arm-kernel, linux-kernel, robh+dt, mark.rutland,
	devicetree, radhey.shyam.pandey

On 02-08-18, 16:10, Andrea Merello wrote:
> The width of the "length register" cannot be autodetected, and it is now
> specified with a DT property. Add DOC for it.

Add Documentation for it...

> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: devicetree@vger.kernel.org
> Cc: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> Signed-off-by: Andrea Merello <andrea.merello@gmail.com>
> Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> ---
> Changes in v2:
> 	- change property name
> 	- property is now optional
> 	- cc DT maintainer
> Changes in v3:
> 	- reword
> 	- cc DT maintainerS and ML
> Changes in v4:
> 	- specify the unit, the valid range and the default value
> ---
>  Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> index a2b8bfaec43c..aec4a41a03ae 100644
> --- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt
> @@ -41,6 +41,10 @@ Optional properties:
>  - xlnx,include-sg: Tells configured for Scatter-mode in
>  	the hardware.
>  Optional properties for AXI DMA:
> +- xlnx,sg-length-width: Should be set to the width in bits of the length
> +  	register as configured in h/w. Takes values {8...26}. If the property
> +	is missing or invalid then the default value 23 is used. This is the
> +	maximum value that is supported by all IP versions.
>  - xlnx,mcdma: Tells whether configured for multi-channel mode in the hardware.
>  Optional properties for VDMA:
>  - xlnx,flush-fsync: Tells which channel to Flush on Frame sync.
> -- 
> 2.17.1

^ permalink raw reply

* [v4,2/7] dmaengine: xilinx_dma: in axidma slave_sg and dma_cylic mode align split descriptors
From: Vinod Koul @ 2018-08-27  5:30 UTC (permalink / raw)
  To: Andrea Merello
  Cc: dan.j.williams, michal.simek, appana.durga.rao, dmaengine,
	v4-000linux-arm-kernel, linux-kernel, robh+dt, mark.rutland,
	devicetree, radhey.shyam.pandey

On 02-08-18, 16:10, Andrea Merello wrote:

s/cylic/cyclic in patch title

> Whenever a single or cyclic transaction is prepared, the driver
> could eventually split it over several SG descriptors in order
> to deal with the HW maximum transfer length.
> 
> This could end up in DMA operations starting from a misaligned
> address. This seems fatal for the HW if DRE is not enabled.

DRE?

> 
> This patch eventually adjusts the transfer size in order to make sure
> all operations start from an aligned address.
> 
> Cc: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> Signed-off-by: Andrea Merello <andrea.merello@gmail.com>
> Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> ---
> Changes in v2:
>         - don't introduce copy_mask field, rather rely on already-esistent
>           copy_align field. Suggested by Radhey Shyam Pandey
>         - reword title
> Changes in v3:
> 	- fix bug introduced in v2: wrong copy size when DRE is enabled
> 	- use implementation suggested by Radhey Shyam Pandey
> Changes in v4:
> 	- rework on the top of 1/6
> ---
>  drivers/dma/xilinx/xilinx_dma.c | 22 ++++++++++++++++++----
>  1 file changed, 18 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
> index a3aaa0e34cc7..aaa6de8a70e4 100644
> --- a/drivers/dma/xilinx/xilinx_dma.c
> +++ b/drivers/dma/xilinx/xilinx_dma.c
> @@ -954,15 +954,28 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)
>  
>  /**
>   * xilinx_dma_calc_copysize - Calculate the amount of data to copy
> + * @chan: Driver specific DMA channel
>   * @size: Total data that needs to be copied
>   * @done: Amount of data that has been already copied
>   *
>   * Return: Amount of data that has to be copied
>   */
> -static int xilinx_dma_calc_copysize(int size, int done)
> +static int xilinx_dma_calc_copysize(struct xilinx_dma_chan *chan,
> +				    int size, int done)

please align with opening brace

>  {
> -	return min_t(size_t, size - done,
> +	size_t copy = min_t(size_t, size - done,
>  		     XILINX_DMA_MAX_TRANS_LEN);
> +
> +	if ((copy + done < size) &&
> +	    chan->xdev->common.copy_align) {
> +		/*
> +		 * If this is not the last descriptor, make sure
> +		 * the next one will be properly aligned
> +		 */
> +		copy = rounddown(copy,
> +				 (1 << chan->xdev->common.copy_align));
> +	}
> +	return copy;
>  }
>  
>  /**
> @@ -1804,7 +1817,7 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
>  			 * Calculate the maximum number of bytes to transfer,
>  			 * making sure it is less than the hw limit
>  			 */
> -			copy = xilinx_dma_calc_copysize(sg_dma_len(sg),
> +			copy = xilinx_dma_calc_copysize(chan, sg_dma_len(sg),
>  							sg_used);
>  			hw = &segment->hw;
>  
> @@ -1909,7 +1922,8 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_dma_cyclic(
>  			 * Calculate the maximum number of bytes to transfer,
>  			 * making sure it is less than the hw limit
>  			 */
> -			copy = xilinx_dma_calc_copysize(period_len, sg_used);
> +			copy = xilinx_dma_calc_copysize(chan,
> +							period_len, sg_used);
>  			hw = &segment->hw;
>  			xilinx_axidma_buf(chan, hw, buf_addr, sg_used,
>  					  period_len * i);
> -- 
> 2.17.1

^ permalink raw reply

* [v4,11/18] dmaengine: dma-jz4780: Add missing residue DTC mask
From: Vinod Koul @ 2018-08-27  5:00 UTC (permalink / raw)
  To: Paul Cercueil
  Cc: Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton, James Hogan,
	Zubair Lutfullah Kakakhel, dmaengine, devicetree, linux-kernel,
	linux-mips, Daniel Silsby

On 07-08-18, 13:42, Paul Cercueil wrote:
> From: Daniel Silsby <dansilsby@gmail.com>
> 
> The 'dtc' word in jz DMA descriptors contains two fields: The
> lowest 24 bits are the transfer count, and upper 8 bits are the DOA
> offset to next descriptor. The upper 8 bits are now correctly masked
> off when computing residue in jz4780_dma_desc_residue(). Note that
> reads of the DTCn hardware reg are automatically masked this way.
> 
> Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> Tested-by: Mathieu Malaterre <malat@debian.org>
> ---
>  drivers/dma/dma-jz4780.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
>  v2: No change
> 
>  v3: No change
> 
>  v4: Add my Signed-off-by
> 
> diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
> index 3a4d0a4b550d..a4292ac4c686 100644
> --- a/drivers/dma/dma-jz4780.c
> +++ b/drivers/dma/dma-jz4780.c
> @@ -616,7 +616,8 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan,
>  	residue = 0;
>  
>  	for (i = next_sg; i < desc->count; i++)
> -		residue += desc->desc[i].dtc << jzchan->transfer_shift;
> +		residue += (desc->desc[i].dtc & 0xffffff) <<

GENMASK(23, 0) please.

^ permalink raw reply

* [4/6] dt-bindings: dmaengine: usb-dmac: Add binding for r8a774a1
From: Vinod Koul @ 2018-08-27  4:54 UTC (permalink / raw)
  To: Fabrizio Castro
  Cc: Rob Herring, Mark Rutland, Biju Das, Greg Kroah-Hartman,
	dmaengine, devicetree, Simon Horman, Geert Uytterhoeven,
	Chris Paterson, linux-renesas-soc, linux-kernel

On 24-08-18, 08:56, Fabrizio Castro wrote:
> From: Biju Das <biju.das@bp.renesas.com>
> 
> This patch adds binding for r8a774a1 (RZ/G2M).

Acked-by: Vinod Koul <vkoul@kernel.org>

^ permalink raw reply

* [2/5] dmaengine: dmatest: Use fixed point div to calculate iops
From: kbuild test robot @ 2018-08-25  5:21 UTC (permalink / raw)
  To: Seraj Alijan
  Cc: kbuild-all, vkoul, dmaengine, dan.j.williams, james.hartley,
	sifan.naeem, ed.blake

Hi Seraj,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.18 next-20180824]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Seraj-Alijan/dmaengine-dmatest-Add-multi-chan-and-new-params/20180824-231828
config: i386-randconfig-n0-201833 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   drivers/dma/dmatest.o: In function `dmatest_persec':
>> drivers/dma/dmatest.c:457: undefined reference to `__udivdi3'
>> drivers/dma/dmatest.c:457: undefined reference to `__udivdi3'
>> drivers/dma/dmatest.c:457: undefined reference to `__udivdi3'

vim +457 drivers/dma/dmatest.c

   442	
   443	static unsigned long long dmatest_persec(s64 runtime, unsigned int val)
   444	{
   445		unsigned long long per_sec = 1000000;
   446	
   447		if (runtime <= 0)
   448			return 0;
   449	
   450		/* drop precision until runtime is 32-bits */
   451		while (runtime > UINT_MAX) {
   452			runtime >>= 1;
   453			per_sec <<= 1;
   454		}
   455	
   456		per_sec *= val;
 > 457		per_sec = FIXPT_DIV(per_sec, runtime);
   458	
   459		return per_sec;
   460	}
   461
---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply

* [5/5] dmaengine: Documentation: Add documentation for multi chan testing
From: Seraj Alijan @ 2018-08-24 12:15 UTC (permalink / raw)
  To: vkoul
  Cc: dmaengine, dan.j.williams, james.hartley, sifan.naeem, ed.blake,
	Seraj Alijan

Modify documentation to add multi channel testing support.

Signed-off-by: Seraj Alijan <seraj.alijan@sondrel.com>
---
 Documentation/driver-api/dmaengine/dmatest.rst | 27 ++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/Documentation/driver-api/dmaengine/dmatest.rst b/Documentation/driver-api/dmaengine/dmatest.rst
index 7ce5e71..0a7e4c3 100644
--- a/Documentation/driver-api/dmaengine/dmatest.rst
+++ b/Documentation/driver-api/dmaengine/dmatest.rst
@@ -26,28 +26,43 @@ Part 2 - When dmatest is built as a module
 
 Example of usage::
 
-    % modprobe dmatest channel=dma0chan0 timeout=2000 iterations=1 run=1
+    % modprobe dmatest timeout=2000 iterations=1 channel=dma0chan0 run=1
 
 ...or::
 
     % modprobe dmatest
-    % echo dma0chan0 > /sys/module/dmatest/parameters/channel
     % echo 2000 > /sys/module/dmatest/parameters/timeout
     % echo 1 > /sys/module/dmatest/parameters/iterations
+    % echo dma0chan0 > /sys/module/dmatest/parameters/channel
     % echo 1 > /sys/module/dmatest/parameters/run
 
 ...or on the kernel command line::
 
-    dmatest.channel=dma0chan0 dmatest.timeout=2000 dmatest.iterations=1 dmatest.run=1
+    dmatest.timeout=2000 dmatest.iterations=1 dmatest.channel=dma0chan0 dmatest.run=1
+
+Example of multi-channel test usage:
+    % modprobe dmatest
+    % echo 2000 > /sys/module/dmatest/parameters/timeout
+    % echo 1 > /sys/module/dmatest/parameters/iterations
+    % echo dma0chan0 > /sys/module/dmatest/parameters/channel
+    % echo dma0chan1 > /sys/module/dmatest/parameters/channel
+    % echo dma0chan2 > /sys/module/dmatest/parameters/channel
+    % echo 1 > /sys/module/dmatest/parameters/run
 
+Note: the channel parameter should always be the last parameter set prior to
+running the test (setting run=1), this is because upon setting the channel
+parameter, that specific channel is requested using the dmaengine and a thread
+is created with the existing parameters. This thread is set as pending
+and will be executed once run is set to 1. Any parameters set after the thread
+is created are not applied.
 .. hint::
   available channel list could be extracted by running the following command::
 
     % ls -1 /sys/class/dma/
 
-Once started a message like "dmatest: Started 1 threads using dma0chan0" is
-emitted. After that only test failure messages are reported until the test
-stops.
+Once started a message like " dmatest: Added 1 threads using dma0chan0" is
+emitted. A thread for that specific channel is created and is now pending, the
+pending thread is started once run is to 1.
 
 Note that running a new test will not stop any in progress test.
 

^ permalink raw reply related

* [4/5] dmaengine: dmatest: Add transfer_size parameter
From: Seraj Alijan @ 2018-08-24 12:15 UTC (permalink / raw)
  To: vkoul
  Cc: dmaengine, dan.j.williams, james.hartley, sifan.naeem, ed.blake,
	Seraj Alijan

Existing transfer size "len" is either generated randomly or set to the
size of test_buf_size. In some cases we need to explicitly specify a
transfer size that is different from the buffer size and non aligned to
test the target device's ability to handle unaligned transfers.

This patch adds optional parameter "transfer_size" to allow setting
explicit transfer size for dma transfers.

Signed-off-by: Seraj Alijan <seraj.alijan@sondrel.com>
---
 drivers/dma/dmatest.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index e3542cd..bb1f99f 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -83,6 +83,10 @@ static int alignment = -1;
 module_param(alignment, int, 0644);
 MODULE_PARM_DESC(alignment, "Custom data address alignment taken as 2^(alignment) (default: not used (-1))");
 
+static unsigned int transfer_size;
+module_param(transfer_size, uint, 0644);
+MODULE_PARM_DESC(transfer_size, "Optional custom transfer size in bytes (default: not used (0))");
+
 /**
  * struct dmatest_params - test parameters.
  * @buf_size:		size of the memcpy test buffer
@@ -108,6 +112,7 @@ struct dmatest_params {
 	bool		noverify;
 	bool		norandom;
 	int		alignment;
+	unsigned int	transfer_size;
 };
 
 /**
@@ -636,15 +641,25 @@ static int dmatest_func(void *data)
 			break;
 		}
 
-		if (params->norandom)
+		if (params->transfer_size) {
+			if (params->transfer_size >= params->buf_size) {
+				pr_err("%u-byte transfer size must be lower than %u-buffer size\n",
+				       params->transfer_size, params->buf_size);
+				break;
+			}
+			len = params->transfer_size;
+		} else if (params->norandom) {
 			len = params->buf_size;
-		else
+		} else {
 			len = dmatest_random() % params->buf_size + 1;
+		}
 
-		len = (len >> align) << align;
-		if (!len)
-			len = 1 << align;
-
+		/* Do not alter transfer size explicitly defined by user */
+		if (!params->transfer_size) {
+			len = (len >> align) << align;
+			if (!len)
+				len = 1 << align;
+		}
 		total_len += len;
 
 		if (params->norandom) {
@@ -1042,6 +1057,7 @@ static void add_threaded_test(struct dmatest_info *info)
 	params->noverify = noverify;
 	params->norandom = norandom;
 	params->alignment = alignment;
+	params->transfer_size = transfer_size;
 
 	request_channels(info, DMA_MEMCPY);
 	request_channels(info, DMA_MEMSET);

^ permalink raw reply related

* [3/5] dmaengine: dmatest: Add alignment parameter
From: Seraj Alijan @ 2018-08-24 12:15 UTC (permalink / raw)
  To: vkoul
  Cc: dmaengine, dan.j.williams, james.hartley, sifan.naeem, ed.blake,
	Seraj Alijan

Add parameter "alignment" to allow setting the address alignment
manually. Having the ability to configure address alignment from
user space adds new testing capabilities where different alignments can
be configured for testing without having to modify the dma device
alignment properties.

If configured, the alignment value will override the device alignment
property of the target device.

Signed-off-by: Seraj Alijan <seraj.alijan@sondrel.com>
---
 drivers/dma/dmatest.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index 130f343..e3542cd 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -79,6 +79,10 @@ static bool verbose;
 module_param(verbose, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)");
 
+static int alignment = -1;
+module_param(alignment, int, 0644);
+MODULE_PARM_DESC(alignment, "Custom data address alignment taken as 2^(alignment) (default: not used (-1))");
+
 /**
  * struct dmatest_params - test parameters.
  * @buf_size:		size of the memcpy test buffer
@@ -103,6 +107,7 @@ struct dmatest_params {
 	int		timeout;
 	bool		noverify;
 	bool		norandom;
+	int		alignment;
 };
 
 /**
@@ -519,22 +524,26 @@ static int dmatest_func(void *data)
 	chan = thread->chan;
 	dev = chan->device;
 	if (thread->type == DMA_MEMCPY) {
-		align = dev->copy_align;
+		align = params->alignment < 0 ? dev->copy_align :
+						params->alignment;
 		src_cnt = dst_cnt = 1;
 	} else if (thread->type == DMA_MEMSET) {
-		align = dev->fill_align;
+		align = params->alignment < 0 ? dev->fill_align :
+						params->alignment;
 		src_cnt = dst_cnt = 1;
 		is_memset = true;
 	} else if (thread->type == DMA_XOR) {
 		/* force odd to ensure dst = src */
 		src_cnt = min_odd(params->xor_sources | 1, dev->max_xor);
 		dst_cnt = 1;
-		align = dev->xor_align;
+		align = params->alignment < 0 ? dev->xor_align :
+						params->alignment;
 	} else if (thread->type == DMA_PQ) {
 		/* force odd to ensure dst = src */
 		src_cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0));
 		dst_cnt = 2;
-		align = dev->pq_align;
+		align = params->alignment < 0 ? dev->pq_align :
+						params->alignment;
 
 		pq_coefs = kmalloc(params->pq_sources + 1, GFP_KERNEL);
 		if (!pq_coefs)
@@ -1032,6 +1041,7 @@ static void add_threaded_test(struct dmatest_info *info)
 	params->timeout = timeout;
 	params->noverify = noverify;
 	params->norandom = norandom;
+	params->alignment = alignment;
 
 	request_channels(info, DMA_MEMCPY);
 	request_channels(info, DMA_MEMSET);

^ permalink raw reply related

* [2/5] dmaengine: dmatest: Use fixed point div to calculate iops
From: Seraj Alijan @ 2018-08-24 12:15 UTC (permalink / raw)
  To: vkoul
  Cc: dmaengine, dan.j.williams, james.hartley, sifan.naeem, ed.blake,
	Seraj Alijan

Use fixed point division to calculate iops to prevent reporting 0 iops
when operations last for longer than a second.

Signed-off-by: Seraj Alijan <seraj.alijan@sondrel.com>
---
 drivers/dma/dmatest.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index 8ce3b06..130f343 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -170,6 +170,14 @@ MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)");
 #define PATTERN_COUNT_MASK	0x1f
 #define PATTERN_MEMSET_IDX	0x01
 
+/* Fixed point arithmetic ops */
+#define FIXPT_SHIFT		8
+#define FIXPNT_MASK		0xFF
+#define FIXPT_TO_INT(a)	((a) >> FIXPT_SHIFT)
+#define INT_TO_FIXPT(a)	((a) << FIXPT_SHIFT)
+#define FIXPT_GET_FRAC(a)	((((a) & FIXPNT_MASK) * 100) >> FIXPT_SHIFT)
+#define FIXPT_DIV(a, b)	DIV_ROUND_CLOSEST((INT_TO_FIXPT(a)), (b))
+
 /* poor man's completion - we want to use wait_event_freezable() on it */
 struct dmatest_done {
 	bool			done;
@@ -446,13 +454,14 @@ static unsigned long long dmatest_persec(s64 runtime, unsigned int val)
 	}
 
 	per_sec *= val;
-	do_div(per_sec, runtime);
+	per_sec = FIXPT_DIV(per_sec, runtime);
+
 	return per_sec;
 }
 
 static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len)
 {
-	return dmatest_persec(runtime, len >> 10);
+	return FIXPT_TO_INT(dmatest_persec(runtime, len >> 10));
 }
 
 /*
@@ -493,6 +502,7 @@ static int dmatest_func(void *data)
 	ktime_t			comparetime = 0;
 	s64			runtime = 0;
 	unsigned long long	total_len = 0;
+	unsigned long long	iops = 0;
 	u8			align = 0;
 	bool			is_memset = false;
 	dma_addr_t		*srcs;
@@ -833,9 +843,10 @@ static int dmatest_func(void *data)
 err_srcs:
 	kfree(pq_coefs);
 err_thread_type:
-	pr_info("%s: summary %u tests, %u failures %llu iops %llu KB/s (%d)\n",
+	iops = dmatest_persec(runtime, total_tests);
+	pr_info("%s: summary %u tests, %u failures %llu.%02llu iops %llu KB/s (%d)\n",
 		current->comm, total_tests, failed_tests,
-		dmatest_persec(runtime, total_tests),
+		FIXPT_TO_INT(iops), FIXPT_GET_FRAC(iops),
 		dmatest_KBs(runtime, total_len), ret);
 
 	/* terminate all transfers on specified channels */

^ permalink raw reply related

* [1/5] dmaengine: dmatest: Add support for multi channel testing
From: Seraj Alijan @ 2018-08-24 12:15 UTC (permalink / raw)
  To: vkoul
  Cc: dmaengine, dan.j.williams, james.hartley, sifan.naeem, ed.blake,
	Seraj Alijan

Add support for running tests on multiple channels simultaneously as the
driver currently limits to 1 channel per test run. This will add support
for stress testing DMA controllers with multi channel capabilities.

This is done by adding a callback function to the "channel" parameter
that registers the requested channel prior to the "run" parameter being
set to 1. Each time the "channel" parameter is populated with a new
dma channel, a new test is appended to the thread queue. Once the "run"
parameter is set to 1, the test will kick start all pending threads.

Signed-off-by: Seraj Alijan <seraj.alijan@sondrel.com>
---
 drivers/dma/dmatest.c | 109 +++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 89 insertions(+), 20 deletions(-)

diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index aa1712b..8ce3b06 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -27,11 +27,6 @@ static unsigned int test_buf_size = 16384;
 module_param(test_buf_size, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer");
 
-static char test_channel[20];
-module_param_string(channel, test_channel, sizeof(test_channel),
-		S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)");
-
 static char test_device[32];
 module_param_string(device, test_device, sizeof(test_device),
 		S_IRUGO | S_IWUSR);
@@ -139,6 +134,21 @@ static bool dmatest_run;
 module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(run, "Run the test (default: false)");
 
+static int dmatest_chan_set(const char *val, const struct kernel_param *kp);
+static int dmatest_chan_get(char *val, const struct kernel_param *kp);
+static const struct kernel_param_ops multi_chan_ops = {
+	.set = dmatest_chan_set,
+	.get = dmatest_chan_get,
+};
+
+static char test_channel[20];
+static struct kparam_string newchan_kps = {
+	.string = test_channel,
+	.maxlen = 20,
+};
+module_param_cb(channel, &multi_chan_ops, &newchan_kps, 0644);
+MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)");
+
 /* Maximum amount of mismatched bytes in buffer to print */
 #define MAX_ERROR_COUNT		32
 
@@ -179,6 +189,7 @@ struct dmatest_thread {
 	wait_queue_head_t done_wait;
 	struct dmatest_done test_done;
 	bool			done;
+	bool			pending;
 };
 
 struct dmatest_chan {
@@ -206,6 +217,22 @@ static bool is_threaded_test_run(struct dmatest_info *info)
 	return false;
 }
 
+static bool is_threaded_test_pending(struct dmatest_info *info)
+{
+	struct dmatest_chan *dtc;
+
+	list_for_each_entry(dtc, &info->channels, node) {
+		struct dmatest_thread *thread;
+
+		list_for_each_entry(thread, &dtc->threads, node) {
+			if (thread->pending)
+				return true;
+		}
+	}
+
+	return false;
+}
+
 static int dmatest_wait_get(char *val, const struct kernel_param *kp)
 {
 	struct dmatest_info *info = &test_info;
@@ -476,6 +503,7 @@ static int dmatest_func(void *data)
 	ret = -ENOMEM;
 
 	smp_rmb();
+	thread->pending = false;
 	info = thread->info;
 	params = &info->params;
 	chan = thread->chan;
@@ -886,7 +914,7 @@ static int dmatest_add_threads(struct dmatest_info *info,
 		/* srcbuf and dstbuf are allocated by the thread itself */
 		get_task_struct(thread->task);
 		list_add_tail(&thread->node, &dtc->threads);
-		wake_up_process(thread->task);
+		thread->pending = true;
 	}
 
 	return i;
@@ -932,7 +960,7 @@ static int dmatest_add_channel(struct dmatest_info *info,
 		thread_count += cnt > 0 ? cnt : 0;
 	}
 
-	pr_info("Started %u threads using %s\n",
+	pr_info("Added %u threads using %s\n",
 		thread_count, dma_chan_name(chan));
 
 	list_add_tail(&dtc->node, &info->channels);
@@ -977,7 +1005,7 @@ static void request_channels(struct dmatest_info *info,
 	}
 }
 
-static void run_threaded_test(struct dmatest_info *info)
+static void add_threaded_test(struct dmatest_info *info)
 {
 	struct dmatest_params *params = &info->params;
 
@@ -1000,6 +1028,19 @@ static void run_threaded_test(struct dmatest_info *info)
 	request_channels(info, DMA_PQ);
 }
 
+static void run_pending_tests(struct dmatest_info *info)
+{
+	struct dmatest_chan *dtc;
+
+	list_for_each_entry(dtc, &info->channels, node) {
+		struct dmatest_thread *thread;
+
+		list_for_each_entry(thread, &dtc->threads, node) {
+			wake_up_process(thread->task);
+		}
+	}
+}
+
 static void stop_threaded_test(struct dmatest_info *info)
 {
 	struct dmatest_chan *dtc, *_dtc;
@@ -1016,7 +1057,7 @@ static void stop_threaded_test(struct dmatest_info *info)
 	info->nr_channels = 0;
 }
 
-static void restart_threaded_test(struct dmatest_info *info, bool run)
+static void start_threaded_tests(struct dmatest_info *info)
 {
 	/* we might be called early to set run=, defer running until all
 	 * parameters have been evaluated
@@ -1024,11 +1065,7 @@ static void restart_threaded_test(struct dmatest_info *info, bool run)
 	if (!info->did_init)
 		return;
 
-	/* Stop any running test first */
-	stop_threaded_test(info);
-
-	/* Run test with new parameters */
-	run_threaded_test(info);
+	run_pending_tests(info);
 }
 
 static int dmatest_run_get(char *val, const struct kernel_param *kp)
@@ -1039,7 +1076,8 @@ static int dmatest_run_get(char *val, const struct kernel_param *kp)
 	if (is_threaded_test_run(info)) {
 		dmatest_run = true;
 	} else {
-		stop_threaded_test(info);
+		if (!is_threaded_test_pending(info))
+			stop_threaded_test(info);
 		dmatest_run = false;
 	}
 	mutex_unlock(&info->lock);
@@ -1057,18 +1095,48 @@ static int dmatest_run_set(const char *val, const struct kernel_param *kp)
 	if (ret) {
 		mutex_unlock(&info->lock);
 		return ret;
+	} else if (dmatest_run && is_threaded_test_pending(info)) {
+		start_threaded_tests(info);
 	}
 
-	if (is_threaded_test_run(info))
-		ret = -EBUSY;
-	else if (dmatest_run)
-		restart_threaded_test(info, dmatest_run);
+	mutex_unlock(&info->lock);
+
+	return ret;
+}
+
+static int dmatest_chan_set(const char *val, const struct kernel_param *kp)
+{
+	struct dmatest_info *info = &test_info;
+	int ret;
 
+	mutex_lock(&info->lock);
+	ret = param_set_copystring(val, kp);
+	if (ret) {
+		mutex_unlock(&info->lock);
+		return ret;
+	}
+	if (!is_threaded_test_run(info) && !is_threaded_test_pending(info))
+		stop_threaded_test(info);
+	add_threaded_test(info);
 	mutex_unlock(&info->lock);
 
 	return ret;
 }
 
+static int dmatest_chan_get(char *val, const struct kernel_param *kp)
+{
+	struct dmatest_info *info = &test_info;
+
+	mutex_lock(&info->lock);
+	if (!is_threaded_test_run(info) && !is_threaded_test_pending(info)) {
+		stop_threaded_test(info);
+		strlcpy(test_channel, "", sizeof(test_channel));
+	}
+	mutex_unlock(&info->lock);
+
+	return param_get_string(val, kp);
+}
+
 static int __init dmatest_init(void)
 {
 	struct dmatest_info *info = &test_info;
@@ -1076,7 +1144,8 @@ static int __init dmatest_init(void)
 
 	if (dmatest_run) {
 		mutex_lock(&info->lock);
-		run_threaded_test(info);
+		add_threaded_test(info);
+		run_pending_tests(info);
 		mutex_unlock(&info->lock);
 	}
 

^ permalink raw reply related

* [v2,1/2] dmaengine: use SPDX identifier for Renesas drivers
From: Simon Horman @ 2018-08-24  9:18 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: linux-renesas-soc, Kuninori Morimoto, Dan Williams, Vinod Koul,
	dmaengine, linux-kernel

On Thu, Aug 23, 2018 at 03:34:54PM +0200, Wolfram Sang wrote:
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>

Reviewed-by: Simon Horman <horms+renesas@verge.net.au>

^ permalink raw reply

* [4/6] dt-bindings: dmaengine: usb-dmac: Add binding for r8a774a1
From: Fabrizio Castro @ 2018-08-24  7:56 UTC (permalink / raw)
  To: Vinod Koul, Rob Herring, Mark Rutland
  Cc: Biju Das, Greg Kroah-Hartman, dmaengine, devicetree, Simon Horman,
	Geert Uytterhoeven, Chris Paterson, Fabrizio Castro,
	linux-renesas-soc, linux-kernel

From: Biju Das <biju.das@bp.renesas.com>

This patch adds binding for r8a774a1 (RZ/G2M).

Signed-off-by: Biju Das <biju.das@bp.renesas.com>
Reviewed-by: Fabrizio Castro <fabrizio.castro@bp.renesas.com>
---
 Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
index 482e543..417ca90 100644
--- a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
+++ b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
@@ -5,6 +5,7 @@ Required Properties:
 	Examples with soctypes are:
 	  - "renesas,r8a7743-usb-dmac" (RZ/G1M)
 	  - "renesas,r8a7745-usb-dmac" (RZ/G1E)
+	  - "renesas,r8a774a1-usb-dmac" (RZ/G2M)
 	  - "renesas,r8a7790-usb-dmac" (R-Car H2)
 	  - "renesas,r8a7791-usb-dmac" (R-Car M2-W)
 	  - "renesas,r8a7793-usb-dmac" (R-Car M2-N)

^ permalink raw reply related

* [v2,2/2] dmaengine: uniphier-mdmac: add UniPhier MIO DMAC driver
From: Masahiro Yamada @ 2018-08-24  1:41 UTC (permalink / raw)
  To: Vinod Koul, dmaengine
  Cc: devicetree, Rob Herring, linux-kernel, Masami Hiramatsu,
	Jassi Brar, Masahiro Yamada, Dan Williams, linux-arm-kernel

The MIO DMAC (Media IO DMA Controller) is used in UniPhier LD4,
Pro4, and sLD8 SoCs.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
 - Use platform_irq_count() to get the number of channels

 MAINTAINERS                  |   1 +
 drivers/dma/Kconfig          |  11 +
 drivers/dma/Makefile         |   1 +
 drivers/dma/uniphier-mdmac.c | 481 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 494 insertions(+)
 create mode 100644 drivers/dma/uniphier-mdmac.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 994b157..83ba996 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2193,6 +2193,7 @@ F:	arch/arm/mm/cache-uniphier.c
 F:	arch/arm64/boot/dts/socionext/uniphier*
 F:	drivers/bus/uniphier-system-bus.c
 F:	drivers/clk/uniphier/
+F:	drivers/dmaengine/uniphier-mdmac.c
 F:	drivers/gpio/gpio-uniphier.c
 F:	drivers/i2c/busses/i2c-uniphier*
 F:	drivers/irqchip/irq-uniphier-aidet.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index dacf3f4..8b8c7f0 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -576,6 +576,17 @@ config TIMB_DMA
 	help
 	  Enable support for the Timberdale FPGA DMA engine.
 
+config UNIPHIER_MDMAC
+	tristate "UniPhier MIO DMAC"
+	depends on ARCH_UNIPHIER || COMPILE_TEST
+	depends on OF
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  Enable support for the MIO DMAC (Media I/O DMA controller) on the
+	  UniPhier platform.  This DMA controller is used as the external
+	  DMA engine of the SD/eMMC controllers of the LD4, Pro4, sLD8 SoCs.
+
 config XGENE_DMA
 	tristate "APM X-Gene DMA support"
 	depends on ARCH_XGENE || COMPILE_TEST
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index c91702d..973a170 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
 obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o
 obj-$(CONFIG_TEGRA210_ADMA) += tegra210-adma.o
 obj-$(CONFIG_TIMB_DMA) += timb_dma.o
+obj-$(CONFIG_UNIPHIER_MDMAC) += uniphier-mdmac.o
 obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
 obj-$(CONFIG_ZX_DMA) += zx_dma.o
 obj-$(CONFIG_ST_FDMA) += st_fdma.o
diff --git a/drivers/dma/uniphier-mdmac.c b/drivers/dma/uniphier-mdmac.c
new file mode 100644
index 0000000..e4db0a7
--- /dev/null
+++ b/drivers/dma/uniphier-mdmac.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 Socionext Inc.
+//   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "virt-dma.h"
+
+/* registers common for all channels */
+#define UNIPHIER_MDMAC_CMD		0x000	// issue DMA start/abort
+#define   UNIPHIER_MDMAC_CMD_ABORT		BIT(31) // 1: abort, 0: start
+
+/* per-channel registers */
+#define UNIPHIER_MDMAC_CH_OFFSET	0x100
+#define UNIPHIER_MDMAC_CH_STRIDE	0x040
+
+#define UNIPHIER_MDMAC_CH_IRQ_STAT	0x010	// current hw status (RO)
+#define UNIPHIER_MDMAC_CH_IRQ_REQ	0x014	// latched STAT (WOC)
+#define UNIPHIER_MDMAC_CH_IRQ_EN	0x018	// IRQ enable mask
+#define UNIPHIER_MDMAC_CH_IRQ_DET	0x01c	// REQ & EN (RO)
+#define   UNIPHIER_MDMAC_CH_IRQ__ABORT		BIT(13)
+#define   UNIPHIER_MDMAC_CH_IRQ__DONE		BIT(1)
+#define UNIPHIER_MDMAC_CH_SRC_MODE	0x020	// mode of source
+#define UNIPHIER_MDMAC_CH_DEST_MODE	0x024	// mode of destination
+#define   UNIPHIER_MDMAC_CH_MODE__ADDR_INC	(0 << 4)
+#define   UNIPHIER_MDMAC_CH_MODE__ADDR_DEC	(1 << 4)
+#define   UNIPHIER_MDMAC_CH_MODE__ADDR_FIXED	(2 << 4)
+#define UNIPHIER_MDMAC_CH_SRC_ADDR	0x028	// source address
+#define UNIPHIER_MDMAC_CH_DEST_ADDR	0x02c	// destination address
+#define UNIPHIER_MDMAC_CH_SIZE		0x030	// transfer bytes
+
+struct uniphier_mdmac_desc {
+	struct virt_dma_desc vd;
+	struct scatterlist *sgl;
+	unsigned int sg_len;
+	unsigned int sg_cur;
+	enum dma_transfer_direction dir;
+};
+
+struct uniphier_mdmac_chan {
+	struct virt_dma_chan vc;
+	struct uniphier_mdmac_device *mdev;
+	struct uniphier_mdmac_desc *md;
+	void __iomem *reg_ch_base;
+	unsigned int chan_id;
+};
+
+struct uniphier_mdmac_device {
+	struct dma_device ddev;
+	struct clk *clk;
+	void __iomem *reg_base;
+	struct uniphier_mdmac_chan channels[0];
+};
+
+static struct uniphier_mdmac_chan *to_uniphier_mdmac_chan(
+						struct virt_dma_chan *vc)
+{
+	return container_of(vc, struct uniphier_mdmac_chan, vc);
+}
+
+static struct uniphier_mdmac_desc *to_uniphier_mdmac_desc(
+						struct virt_dma_desc *vd)
+{
+	return container_of(vd, struct uniphier_mdmac_desc, vd);
+}
+
+/* mc->vc.lock must be held by caller */
+static struct uniphier_mdmac_desc *__uniphier_mdmac_next_desc(
+						struct uniphier_mdmac_chan *mc)
+{
+	struct virt_dma_desc *vd;
+
+	vd = vchan_next_desc(&mc->vc);
+	if (!vd) {
+		mc->md = NULL;
+		return NULL;
+	}
+
+	list_del(&vd->node);
+
+	mc->md = to_uniphier_mdmac_desc(vd);
+
+	return mc->md;
+}
+
+/* mc->vc.lock must be held by caller */
+static void __uniphier_mdmac_handle(struct uniphier_mdmac_chan *mc,
+				    struct uniphier_mdmac_desc *md)
+{
+	struct uniphier_mdmac_device *mdev = mc->mdev;
+	struct scatterlist *sg;
+	u32 irq_flag = UNIPHIER_MDMAC_CH_IRQ__DONE;
+	u32 src_mode, src_addr, dest_mode, dest_addr, chunk_size;
+
+	sg = &md->sgl[md->sg_cur];
+
+	if (md->dir == DMA_MEM_TO_DEV) {
+		src_mode = UNIPHIER_MDMAC_CH_MODE__ADDR_INC;
+		src_addr = sg_dma_address(sg);
+		dest_mode = UNIPHIER_MDMAC_CH_MODE__ADDR_FIXED;
+		dest_addr = 0;
+	} else {
+		src_mode = UNIPHIER_MDMAC_CH_MODE__ADDR_FIXED;
+		src_addr = 0;
+		dest_mode = UNIPHIER_MDMAC_CH_MODE__ADDR_INC;
+		dest_addr = sg_dma_address(sg);
+	}
+
+	chunk_size = sg_dma_len(sg);
+
+	writel(src_mode, mc->reg_ch_base + UNIPHIER_MDMAC_CH_SRC_MODE);
+	writel(dest_mode, mc->reg_ch_base + UNIPHIER_MDMAC_CH_DEST_MODE);
+	writel(src_addr, mc->reg_ch_base + UNIPHIER_MDMAC_CH_SRC_ADDR);
+	writel(dest_addr, mc->reg_ch_base + UNIPHIER_MDMAC_CH_DEST_ADDR);
+	writel(chunk_size, mc->reg_ch_base + UNIPHIER_MDMAC_CH_SIZE);
+
+	/* write 1 to clear */
+	writel(irq_flag, mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_REQ);
+
+	writel(irq_flag, mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_EN);
+
+	writel(BIT(mc->chan_id), mdev->reg_base + UNIPHIER_MDMAC_CMD);
+}
+
+/* mc->vc.lock must be held by caller */
+static void __uniphier_mdmac_start(struct uniphier_mdmac_chan *mc)
+{
+	struct uniphier_mdmac_desc *md;
+
+	md = __uniphier_mdmac_next_desc(mc);
+	if (md)
+		__uniphier_mdmac_handle(mc, md);
+}
+
+/* mc->vc.lock must be held by caller */
+static int __uniphier_mdmac_abort(struct uniphier_mdmac_chan *mc)
+{
+	struct uniphier_mdmac_device *mdev = mc->mdev;
+	u32 irq_flag = UNIPHIER_MDMAC_CH_IRQ__ABORT;
+	u32 val;
+
+	/* write 1 to clear */
+	writel(irq_flag, mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_REQ);
+
+	writel(UNIPHIER_MDMAC_CMD_ABORT | BIT(mc->chan_id),
+	       mdev->reg_base + UNIPHIER_MDMAC_CMD);
+
+	/*
+	 * Abort should be accepted soon. We poll the bit here instead of
+	 * waiting for the interrupt.
+	 */
+	return readl_poll_timeout(mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_REQ,
+				  val, val & irq_flag, 0, 20);
+}
+
+/* mc->vc.lock must be held by caller */
+static u32 __uniphier_mdmac_get_residue(struct uniphier_mdmac_desc *md)
+{
+	u32 residue = 0;
+	int i;
+
+	for (i = md->sg_cur; i < md->sg_len; i++)
+		residue += sg_dma_len(&md->sgl[i]);
+
+	return residue;
+}
+
+static irqreturn_t uniphier_mdmac_interrupt(int irq, void *dev_id)
+{
+	struct uniphier_mdmac_chan *mc = dev_id;
+	struct uniphier_mdmac_desc *md;
+	irqreturn_t ret = IRQ_HANDLED;
+	u32 irq_stat;
+
+	spin_lock(&mc->vc.lock);
+
+	irq_stat = readl(mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_DET);
+
+	/*
+	 * Some channels share a single interrupt line. If the IRQ status is 0,
+	 * this is probably triggered by a different channel.
+	 */
+	if (!irq_stat) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	/* write 1 to clear */
+	writel(irq_stat, mc->reg_ch_base + UNIPHIER_MDMAC_CH_IRQ_REQ);
+
+	/*
+	 * UNIPHIER_MDMAC_CH_IRQ__DONE interrupt is asserted even when the DMA
+	 * is aborted.  To distinguish the normal completion and the abort,
+	 * check mc->md.  If it is NULL, we are aborting.
+	 */
+	md = mc->md;
+	if (!md)
+		goto out;
+
+	md->sg_cur++;
+
+	if (md->sg_cur >= md->sg_len) {
+		vchan_cookie_complete(&md->vd);
+		md = __uniphier_mdmac_next_desc(mc);
+		if (!md)
+			goto out;
+	}
+
+	__uniphier_mdmac_handle(mc, md);
+
+out:
+	spin_unlock(&mc->vc.lock);
+
+	return ret;
+}
+
+static struct dma_async_tx_descriptor *uniphier_mdmac_prep_slave_sg(
+					struct dma_chan *chan,
+					struct scatterlist *sgl,
+					unsigned int sg_len,
+					enum dma_transfer_direction direction,
+					unsigned long flags, void *context)
+{
+	struct virt_dma_chan *vc = to_virt_chan(chan);
+	struct uniphier_mdmac_desc *md;
+
+	if (!is_slave_direction(direction))
+		return NULL;
+
+	md = kzalloc(sizeof(*md), GFP_KERNEL);
+	if (!md)
+		return NULL;
+
+	md->sgl = sgl;
+	md->sg_len = sg_len;
+	md->dir = direction;
+
+	return vchan_tx_prep(vc, &md->vd, flags);
+}
+
+static int uniphier_mdmac_terminate_all(struct dma_chan *chan)
+{
+	struct virt_dma_chan *vc = to_virt_chan(chan);
+	struct uniphier_mdmac_chan *mc = to_uniphier_mdmac_chan(vc);
+	unsigned long flags;
+	int ret = 0;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&vc->lock, flags);
+
+	if (mc->md) {
+		vchan_terminate_vdesc(&mc->md->vd);
+		mc->md = NULL;
+		ret = __uniphier_mdmac_abort(mc);
+	}
+	vchan_get_all_descriptors(vc, &head);
+
+	spin_unlock_irqrestore(&vc->lock, flags);
+
+	vchan_dma_desc_free_list(vc, &head);
+
+	return ret;
+}
+
+static void uniphier_mdmac_synchronize(struct dma_chan *chan)
+{
+	vchan_synchronize(to_virt_chan(chan));
+}
+
+static enum dma_status uniphier_mdmac_tx_status(struct dma_chan *chan,
+						dma_cookie_t cookie,
+						struct dma_tx_state *txstate)
+{
+	struct virt_dma_chan *vc;
+	struct virt_dma_desc *vd;
+	struct uniphier_mdmac_chan *mc;
+	struct uniphier_mdmac_desc *md = NULL;
+	enum dma_status stat;
+	unsigned long flags;
+
+	stat = dma_cookie_status(chan, cookie, txstate);
+	if (stat == DMA_COMPLETE)
+		return stat;
+
+	vc = to_virt_chan(chan);
+
+	spin_lock_irqsave(&vc->lock, flags);
+
+	mc = to_uniphier_mdmac_chan(vc);
+
+	if (mc->md && mc->md->vd.tx.cookie == cookie)
+		md = mc->md;
+
+	if (!md) {
+		vd = vchan_find_desc(vc, cookie);
+		if (vd)
+			md = to_uniphier_mdmac_desc(vd);
+	}
+
+	if (md)
+		txstate->residue = __uniphier_mdmac_get_residue(md);
+
+	spin_unlock_irqrestore(&vc->lock, flags);
+
+	return stat;
+}
+
+static void uniphier_mdmac_issue_pending(struct dma_chan *chan)
+{
+	struct virt_dma_chan *vc = to_virt_chan(chan);
+	struct uniphier_mdmac_chan *mc = to_uniphier_mdmac_chan(vc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&vc->lock, flags);
+
+	if (vchan_issue_pending(vc) && !mc->md)
+		__uniphier_mdmac_start(mc);
+
+	spin_unlock_irqrestore(&vc->lock, flags);
+}
+
+static void uniphier_mdmac_desc_free(struct virt_dma_desc *vd)
+{
+	kfree(to_uniphier_mdmac_desc(vd));
+}
+
+static int uniphier_mdmac_chan_init(struct platform_device *pdev,
+				    struct uniphier_mdmac_device *mdev,
+				    int chan_id)
+{
+	struct device *dev = &pdev->dev;
+	struct uniphier_mdmac_chan *mc = &mdev->channels[chan_id];
+	char *irq_name;
+	int irq, ret;
+
+	irq = platform_get_irq(pdev, chan_id);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to get IRQ number for ch%d\n",
+			chan_id);
+		return irq;
+	}
+
+	irq_name = devm_kasprintf(dev, GFP_KERNEL, "uniphier-mio-dmac-ch%d",
+				  chan_id);
+	if (!irq_name)
+		return -ENOMEM;
+
+	ret = devm_request_irq(dev, irq, uniphier_mdmac_interrupt,
+			       IRQF_SHARED, irq_name, mc);
+	if (ret)
+		return ret;
+
+	mc->mdev = mdev;
+	mc->reg_ch_base = mdev->reg_base + UNIPHIER_MDMAC_CH_OFFSET +
+					UNIPHIER_MDMAC_CH_STRIDE * chan_id;
+	mc->chan_id = chan_id;
+	mc->vc.desc_free = uniphier_mdmac_desc_free;
+	vchan_init(&mc->vc, &mdev->ddev);
+
+	return 0;
+}
+
+static int uniphier_mdmac_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct uniphier_mdmac_device *mdev;
+	struct dma_device *ddev;
+	struct resource *res;
+	int nr_chans, ret, i;
+
+	nr_chans = platform_irq_count(pdev);
+	if (nr_chans < 0)
+		return nr_chans;
+
+	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	mdev = devm_kzalloc(dev, struct_size(mdev, channels, nr_chans),
+			    GFP_KERNEL);
+	if (!mdev)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mdev->reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(mdev->reg_base))
+		return PTR_ERR(mdev->reg_base);
+
+	mdev->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(mdev->clk)) {
+		dev_err(dev, "failed to get clock\n");
+		return PTR_ERR(mdev->clk);
+	}
+
+	ret = clk_prepare_enable(mdev->clk);
+	if (ret)
+		return ret;
+
+	ddev = &mdev->ddev;
+	ddev->dev = dev;
+	dma_cap_set(DMA_PRIVATE, ddev->cap_mask);
+	ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED);
+	ddev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED);
+	ddev->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
+	ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+	ddev->device_prep_slave_sg = uniphier_mdmac_prep_slave_sg;
+	ddev->device_terminate_all = uniphier_mdmac_terminate_all;
+	ddev->device_synchronize = uniphier_mdmac_synchronize;
+	ddev->device_tx_status = uniphier_mdmac_tx_status;
+	ddev->device_issue_pending = uniphier_mdmac_issue_pending;
+	INIT_LIST_HEAD(&ddev->channels);
+
+	for (i = 0; i < nr_chans; i++) {
+		ret = uniphier_mdmac_chan_init(pdev, mdev, i);
+		if (ret)
+			goto disable_clk;
+	}
+
+	ret = dma_async_device_register(ddev);
+	if (ret)
+		goto disable_clk;
+
+	ret = of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id,
+					 ddev);
+	if (ret)
+		goto unregister_dmac;
+
+	platform_set_drvdata(pdev, mdev);
+
+	return 0;
+
+unregister_dmac:
+	dma_async_device_unregister(ddev);
+disable_clk:
+	clk_disable_unprepare(mdev->clk);
+
+	return ret;
+}
+
+static int uniphier_mdmac_remove(struct platform_device *pdev)
+{
+	struct uniphier_mdmac_device *mdev = platform_get_drvdata(pdev);
+
+	of_dma_controller_free(pdev->dev.of_node);
+	dma_async_device_unregister(&mdev->ddev);
+	clk_disable_unprepare(mdev->clk);
+
+	return 0;
+}
+
+static const struct of_device_id uniphier_mdmac_match[] = {
+	{ .compatible = "socionext,uniphier-mio-dmac" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, uniphier_mdmac_match);
+
+static struct platform_driver uniphier_mdmac_driver = {
+	.probe = uniphier_mdmac_probe,
+	.remove = uniphier_mdmac_remove,
+	.driver = {
+		.name = "uniphier-mio-dmac",
+		.of_match_table = uniphier_mdmac_match,
+	},
+};
+module_platform_driver(uniphier_mdmac_driver);
+
+MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
+MODULE_DESCRIPTION("UniPhier MIO DMAC driver");
+MODULE_LICENSE("GPL v2");

^ permalink raw reply related

* [v2,1/2] dt-bindings: dmaengine: add DT binding for UniPhier MIO DMAC
From: Masahiro Yamada @ 2018-08-24  1:41 UTC (permalink / raw)
  To: Vinod Koul, dmaengine
  Cc: devicetree, Rob Herring, linux-kernel, Masami Hiramatsu,
	Jassi Brar, Masahiro Yamada, Mark Rutland, linux-arm-kernel

The MIO DMAC (Media IO DMA Controller) is used in UniPhier LD4,
Pro4, and sLD8 SoCs.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
 - Rename the node "dmac" to "dma-controller"
 - Remove dma-channels property

 .../devicetree/bindings/dma/uniphier-mio-dmac.txt  | 25 ++++++++++++++++++++++
 1 file changed, 25 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/uniphier-mio-dmac.txt

diff --git a/Documentation/devicetree/bindings/dma/uniphier-mio-dmac.txt b/Documentation/devicetree/bindings/dma/uniphier-mio-dmac.txt
new file mode 100644
index 0000000..b12388d
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/uniphier-mio-dmac.txt
@@ -0,0 +1,25 @@
+UniPhier Media IO DMA controller
+
+This works as an external DMA engine for SD/eMMC controllers etc.
+found in UniPhier LD4, Pro4, sLD8 SoCs.
+
+Required properties:
+- compatible: should be "socionext,uniphier-mio-dmac".
+- reg: offset and length of the register set for the device.
+- interrupts: a list of interrupt specifiers associated with the DMA channels.
+- clocks: a single clock specifier.
+- #dma-cells: should be <1>. The single cell represents the channel index.
+
+Example:
+	dmac: dma-controller@5a000000 {
+		compatible = "socionext,uniphier-mio-dmac";
+		reg = <0x5a000000 0x1000>;
+		interrupts = <0 68 4>, <0 68 4>, <0 69 4>, <0 70 4>,
+			     <0 71 4>, <0 72 4>, <0 73 4>, <0 74 4>;
+		clocks = <&mio_clk 7>;
+		#dma-cells = <1>;
+	};
+
+Note:
+In the example above, "interrupts = <0 68 4>, <0 68 4>, ..." is not a typo.
+The first two channels share a single interrupt line.

^ permalink raw reply related

* [1/2] dt-bindings: dmaengine: add DT binding for UniPhier MIO DMAC
From: Masahiro Yamada @ 2018-08-24  1:34 UTC (permalink / raw)
  To: Jassi Brar, Rob Herring
  Cc: Vinod, open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM,
	Masami Hiramatsu, Devicetree List, linux-kernel@vger.kernel.org

Hi Rob, Jassi,


2018-08-23 23:12 GMT+09:00 Jassi Brar <jaswinder.singh@linaro.org>:
> On 23 August 2018 at 18:51, Rob Herring <robh+dt@kernel.org> wrote:
>> On Thu, Aug 23, 2018 at 12:38 AM Jassi Brar <jaswinder.singh@linaro.org> wrote:
>>> On 23 August 2018 at 10:48, Masahiro Yamada
>
>>> >
>>> > If desired, I will export of_irq_count()
>>> > and use it from my driver.
>>> >
>>> If you don't want to leave too much footprint, you could do
>>>
>>>   count = 0;
>>>   while (of_irq_parse_one(dev, count, &irq) == 0) count++
>>>
>>> of_irq_parse_one() is already exported.
>>
>> Yes, but we really don't more users and drivers shouldn't be using it.
>> Grepping DT functions and when the only users are pretty much powerpc,
>> that's a good indication not to use the function.
>>
>> And you don't want to use of_irq_count either. platform_irq_count is
>> what should be used here. It's already exported.
>>
> Thanks, platform_irq_count() is definitely better.
>
> Yamada-san, for example, gpio-tegra.c infers the number of banks from
> platform_irq_count() rather than the 'gpio-banks' property.


I did not platform_irq_count().  I will send v2 with it.

Thanks.

^ permalink raw reply

* [1/2] dt-bindings: dmaengine: add DT binding for UniPhier MIO DMAC
From: Jassi Brar @ 2018-08-23 14:12 UTC (permalink / raw)
  To: Rob Herring
  Cc: Masahiro Yamada, Vinod,
	open list:DMA GENERIC OFFLOAD ENGINE SUBSYSTEM, Masami Hiramatsu,
	Devicetree List, linux-kernel@vger.kernel.org

On 23 August 2018 at 18:51, Rob Herring <robh+dt@kernel.org> wrote:
> On Thu, Aug 23, 2018 at 12:38 AM Jassi Brar <jaswinder.singh@linaro.org> wrote:
>> On 23 August 2018 at 10:48, Masahiro Yamada

>> >
>> > If desired, I will export of_irq_count()
>> > and use it from my driver.
>> >
>> If you don't want to leave too much footprint, you could do
>>
>>   count = 0;
>>   while (of_irq_parse_one(dev, count, &irq) == 0) count++
>>
>> of_irq_parse_one() is already exported.
>
> Yes, but we really don't more users and drivers shouldn't be using it.
> Grepping DT functions and when the only users are pretty much powerpc,
> that's a good indication not to use the function.
>
> And you don't want to use of_irq_count either. platform_irq_count is
> what should be used here. It's already exported.
>
Thanks, platform_irq_count() is definitely better.

Yamada-san, for example, gpio-tegra.c infers the number of banks from
platform_irq_count() rather than the 'gpio-banks' property.

^ permalink raw reply


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