[PATCH 6/6] dma: mmp_pdma: Add support externel DMA requests

Laurent Pinchart laurent.pinchart at ideasonboard.com
Tue Apr 15 08:13:37 PDT 2014


The MMP/PXA DMA engine supports transfer initiation by external chips
through DMA request (DREQ) signals. Support them by clearing pending DMA
requests for the associated source when starting a channel.

The request ID to DREQ index mapping depends on the hardware and is
passed through platform data or DT.

Cc: devicetree at vger.kernel.org
Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
---
 Documentation/devicetree/bindings/dma/mmp-dma.txt |  2 +
 drivers/dma/mmp_pdma.c                            | 96 ++++++++++++++++++++---
 include/linux/platform_data/mmp_dma.h             |  2 +
 3 files changed, 87 insertions(+), 13 deletions(-)

diff --git a/Documentation/devicetree/bindings/dma/mmp-dma.txt b/Documentation/devicetree/bindings/dma/mmp-dma.txt
index 7a802f6..edb9ff3 100644
--- a/Documentation/devicetree/bindings/dma/mmp-dma.txt
+++ b/Documentation/devicetree/bindings/dma/mmp-dma.txt
@@ -12,6 +12,8 @@ Required properties:
 Optional properties:
 - #dma-channels: Number of DMA channels supported by the controller (defaults
   to 32 when not specified)
+- marvell,dreq: Array of the DMA request IDs corresponding to each of the
+  external device request (DREQ) lines
 
 "marvell,pdma-1.0"
 Used platforms: pxa25x, pxa27x, pxa3xx, pxa93x, pxa168, pxa910, pxa688.
diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c
index 849bf75..4546a1c 100644
--- a/drivers/dma/mmp_pdma.c
+++ b/drivers/dma/mmp_pdma.c
@@ -27,6 +27,7 @@
 
 #define DCSR		0x0000
 #define DALGN		0x00a0
+#define DRQSR(n)	(0x00e0 + ((n) << 2))
 #define DINT		0x00f0
 #define DDADR		0x0200
 #define DSADR		0x0204
@@ -50,6 +51,9 @@
 #define DCSR_CMPST	BIT(10)	/* The Descriptor Compare Status */
 #define DCSR_EORINTR	BIT(9)	/* The end of Receive */
 
+#define DRQSR_CLR	BIT(8)	/* Clear Pending Requests */
+#define DRQSR_REQPEND	0x1f	/* Requests Pending */
+
 #define DRCMR(n)	((((n) < 64) ? 0x0100 : 0x1100) + (((n) & 0x3f) << 2))
 #define DRCMR_MAPVLD	BIT(7)	/* Map Valid (read / write) */
 #define DRCMR_CHLNUM	0x1f	/* mask for Channel Number (read / write) */
@@ -108,6 +112,7 @@ struct mmp_pdma_chan {
 	u32 dcmd;
 	u32 drcmr;
 	u32 dev_addr;
+	int drq;
 
 	/* list for desc */
 	spinlock_t desc_lock;		/* Descriptor list lock */
@@ -127,6 +132,8 @@ struct mmp_pdma_phy {
 
 struct mmp_pdma_device {
 	int				dma_channels;
+	unsigned int			num_dreq;
+	const u32			*dreq;
 	void __iomem			*base;
 	struct device			*dev;
 	struct dma_device		device;
@@ -167,6 +174,9 @@ static void enable_chan(struct mmp_pdma_phy *phy)
 		dalgn &= ~(1 << phy->idx);
 	writel(dalgn, phy->base + DALGN);
 
+	if (phy->vchan->drq != -1)
+		writel(DRQSR_CLR, phy->base + DRQSR(phy->vchan->drq));
+
 	reg = (phy->idx << 2) + DCSR;
 	writel(readl(phy->base + reg) | DCSR_RUN, phy->base + reg);
 }
@@ -685,6 +695,22 @@ fail:
 	return NULL;
 }
 
+static void mmp_pdma_set_drcmr(struct mmp_pdma_chan *chan, unsigned int drmcr)
+{
+	struct mmp_pdma_device *pdev = to_mmp_pdma_dev(cchan->chan.device);
+	unsigned int i;
+
+	chan->drcmr = drmcr;
+	chan->drq = -1;
+
+	for (i = 0; i < pdev->num_dreq; ++i) {
+		if (pdev->dreq[i] == drmcr) {
+			chan->drq = i;
+			break;
+		}
+	}
+}
+
 static int mmp_pdma_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd,
 			    unsigned long arg)
 {
@@ -745,7 +771,7 @@ static int mmp_pdma_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd,
 		 * be removed.
 		 */
 		if (cfg->slave_id)
-			chan->drcmr = cfg->slave_id;
+			mmp_pdma_set_drcmr(chan, cfg->slave_id);
 		break;
 	default:
 		return -ENOSYS;
@@ -909,16 +935,64 @@ static struct dma_chan *mmp_pdma_dma_xlate(struct of_phandle_args *dma_spec,
 	if (!chan)
 		return NULL;
 
-	to_mmp_pdma_chan(chan)->drcmr = dma_spec->args[0];
+	mmp_pdma_set_drcmr(to_mmp_pdma_chan(chan), dma_spec->args[0]);
 
 	return chan;
 }
 
+static int mmap_pdma_parse_platform_data(struct mmp_pdma_device *pdev)
+{
+	struct device_node *np = pdev->dev->of_node;
+	struct property *prop;
+
+	/* Default values: 32 channels, no external DREQ. */
+	pdev->dma_channels = 32;
+	pdev->num_dreq = 0;
+
+	if (!IS_ENABLED(CONFIG_OF) || !np) {
+		struct mmp_dma_platdata *pdata = dev_get_platdata(pdev->dev);
+
+		if (!pdata)
+			return 0;
+
+		if (pdata->dma_channels)
+			pdev->dma_channels = pdata->dma_channels;
+		if (pdata->num_dreq) {
+			pdev->num_dreq = pdata->num_dreq;
+			pdev->dreq = pdata->dreq;
+		}
+
+		return 0;
+	}
+
+	of_property_read_u32(np, "#dma-channels", &pdev->dma_channels);
+
+	prop = of_find_property(np, "marvell,dreq");
+	if (prop) {
+		unsigned int num_dreq = prop->length / sizeof(unsigned long);
+		u32 *dreq;
+
+		dreq = devm_kcalloc(pdev->dev, num_dreq, sizeof(*pdreq),
+				    GFP_KERNEL);
+		if (dreq == NULL)
+			return -ENOMEM;
+
+		ret = of_property_read_u32_array(np, "marvell,dreq", dreq,
+						 num_dreq);
+		if (ret < 0)
+			return ret;
+
+		pdev->num_dreq = num_dreq;
+		pdev->dreq = dreq;
+	}
+
+	return 0;
+}
+
 static int mmp_pdma_probe(struct platform_device *op)
 {
 	struct mmp_pdma_device *pdev;
 	const struct of_device_id *of_id;
-	struct mmp_dma_platdata *pdata = dev_get_platdata(&op->dev);
 	struct resource *iores;
 	int i, ret, irq = 0;
 	int dma_channels = 0, irq_num = 0;
@@ -936,15 +1010,11 @@ static int mmp_pdma_probe(struct platform_device *op)
 	if (IS_ERR(pdev->base))
 		return PTR_ERR(pdev->base);
 
-	of_id = of_match_device(mmp_pdma_dt_ids, pdev->dev);
-	if (of_id)
-		of_property_read_u32(pdev->dev->of_node, "#dma-channels",
-				     &dma_channels);
-	else if (pdata && pdata->dma_channels)
-		dma_channels = pdata->dma_channels;
-	else
-		dma_channels = 32;	/* default 32 channel */
-	pdev->dma_channels = dma_channels;
+	ret = mmp_pdma_parse_platform_data(pdev);
+	if (ret < 0)
+		return ret;
+
+	dma_channels = pdev->dma_channels;
 
 	for (i = 0; i < dma_channels; i++) {
 		if (platform_get_irq(op, i) > 0)
@@ -1038,7 +1108,7 @@ bool mmp_pdma_filter_fn(struct dma_chan *chan, void *param)
 	if (chan->device->dev->driver != &mmp_pdma_driver.driver)
 		return false;
 
-	c->drcmr = *(unsigned int *)param;
+	mmp_pdma_set_drcmr(c, *(unsigned int *)param);
 
 	return true;
 }
diff --git a/include/linux/platform_data/mmp_dma.h b/include/linux/platform_data/mmp_dma.h
index 2a330ec..32595b8 100644
--- a/include/linux/platform_data/mmp_dma.h
+++ b/include/linux/platform_data/mmp_dma.h
@@ -14,6 +14,8 @@
 
 struct mmp_dma_platdata {
 	int dma_channels;
+	unsigned int num_dreq;
+	const u32 *dreq;
 };
 
 #endif /* MMP_DMA_H */
-- 
1.8.3.2




More information about the linux-arm-kernel mailing list