public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] Add GPCDMA support in Tegra264
@ 2026-02-17 17:34 Akhil R
  2026-02-17 17:34 ` [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
                   ` (7 more replies)
  0 siblings, 8 replies; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

This series adds support for GPCDMA in Tegra264 with an additional
support for separate stream ID for each channel. Tegra264 GPCDMA
controller has changes in the register offset and uses 41 bit
addressing for memory. Add changes in the tegra186-gpc-dma driver
to support these.

Akhil R (8):
  dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional
  dmaengine: tegra: Make reset control optional
  dmaengine: tegra: Use struct for register offsets
  dmaengine: tegra: Support address width > 40 bits
  dmaengine: tegra: Use iommu-map for stream ID
  dmaengine: tegra: Add Tegra264 support
  arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264

 .../bindings/dma/nvidia,tegra186-gpc-dma.yaml |  27 +-
 .../arm64/boot/dts/nvidia/tegra264-p3834.dtsi |   4 +
 arch/arm64/boot/dts/nvidia/tegra264.dtsi      |  33 +-
 drivers/dma/tegra186-gpc-dma.c                | 441 +++++++++++-------
 4 files changed, 337 insertions(+), 168 deletions(-)

-- 
2.50.1


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

* [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
@ 2026-02-17 17:34 ` Akhil R
  2026-02-17 18:38   ` Rob Herring (Arm)
  2026-02-17 19:53   ` Krzysztof Kozlowski
  2026-02-17 17:34 ` [PATCH 2/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

Add iommu-map property which helps when each channel requires its own
stream ID for the transfer. Use iommu-map to specify separate stream
ID for each channel. This enables each channel to be in its own iommu
domain and keeps the memory isolated from other devices sharing the
same DMA controller.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 .../devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml  | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
index 0dabe9bbb219..542e9cb9f641 100644
--- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
@@ -14,6 +14,7 @@ description: |
 maintainers:
   - Jon Hunter <jonathanh@nvidia.com>
   - Rajesh Gumasta <rgumasta@nvidia.com>
+  - Akhil R <akhilrajeev@nvidia.com>
 
 allOf:
   - $ref: dma-controller.yaml#
@@ -51,6 +52,13 @@ properties:
   iommus:
     maxItems: 1
 
+  iommu-map:
+    description: |
+      The mapping of DMA controller channels to IOMMU stream IDs. Each entry in the map specifies the
+      relationship between a DMA channel and its corresponding IOMMU stream ID. The format is:
+      "<ch_no &smmu stream_id length>". Example: "<1 &smmu 0x801 1>"
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+
   dma-coherent: true
 
   dma-channel-mask:
-- 
2.50.1


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

* [PATCH 2/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional
  2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
  2026-02-17 17:34 ` [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
@ 2026-02-17 17:34 ` Akhil R
  2026-02-17 18:38   ` Rob Herring (Arm)
  2026-02-17 17:34 ` [PATCH 3/8] dmaengine: tegra: Make reset control optional Akhil R
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

In Tegra264 and Tegra234, GPCDMA reset control is not exposed to Linux
and is handled by BPMP. In Tegra234 BPMP supported a dummy reset which
just return success on reset without doing an actual reset. This as well
is not supported in Tegra264 BPMP. Therefore mark 'reset' and 'reset-names'
property as required only for devices prior to Tegra234.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 .../bindings/dma/nvidia,tegra186-gpc-dma.yaml | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
index 542e9cb9f641..9457d406428f 100644
--- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
@@ -16,9 +16,6 @@ maintainers:
   - Rajesh Gumasta <rgumasta@nvidia.com>
   - Akhil R <akhilrajeev@nvidia.com>
 
-allOf:
-  - $ref: dma-controller.yaml#
-
 properties:
   compatible:
     oneOf:
@@ -68,12 +65,24 @@ required:
   - compatible
   - reg
   - interrupts
-  - resets
-  - reset-names
   - "#dma-cells"
   - iommus
   - dma-channel-mask
 
+allOf:
+  - $ref: dma-controller.yaml#
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - nvidia,tegra186-gpcdma
+              - nvidia,tegra194-gpcdma
+      then:
+        required:
+          - resets
+          - reset-names
+
 additionalProperties: false
 
 examples:
-- 
2.50.1


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

* [PATCH 3/8] dmaengine: tegra: Make reset control optional
  2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
  2026-02-17 17:34 ` [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
  2026-02-17 17:34 ` [PATCH 2/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
@ 2026-02-17 17:34 ` Akhil R
  2026-02-17 18:04   ` Frank Li
  2026-02-17 17:34 ` [PATCH 4/8] dmaengine: tegra: Use struct for register offsets Akhil R
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

Tegra264 BPMP restricts access to GPCDMA reset control and the reset
is expected to be deasserted on boot by BPMP. Hence Make the reset
control optional in the driver.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 drivers/dma/tegra186-gpc-dma.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 4d6fe0efa76e..236a298c26a1 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1382,7 +1382,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
 	if (IS_ERR(tdma->base_addr))
 		return PTR_ERR(tdma->base_addr);
 
-	tdma->rst = devm_reset_control_get_exclusive(&pdev->dev, "gpcdma");
+	tdma->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, "gpcdma");
 	if (IS_ERR(tdma->rst)) {
 		return dev_err_probe(&pdev->dev, PTR_ERR(tdma->rst),
 			      "Missing controller reset\n");
-- 
2.50.1


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

* [PATCH 4/8] dmaengine: tegra: Use struct for register offsets
  2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
                   ` (2 preceding siblings ...)
  2026-02-17 17:34 ` [PATCH 3/8] dmaengine: tegra: Make reset control optional Akhil R
@ 2026-02-17 17:34 ` Akhil R
  2026-02-17 18:09   ` Frank Li
  2026-02-17 17:34 ` [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits Akhil R
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

Repurpose tegra_dma_channel_regs struct to define offsets for all the
channel registers. Previously, the struct only held the register values
for each transfer and was wrapped within tegra_dma_sg_req. Now, let
struct tegra_dma_sg_req hold the values directly and use channel_regs
for storing the register offsets. Update all register read/write to use
channel_regs struct. This is to accommodate the register offset change
in Tegra264.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 drivers/dma/tegra186-gpc-dma.c | 280 +++++++++++++++++----------------
 1 file changed, 147 insertions(+), 133 deletions(-)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 236a298c26a1..72701b543ceb 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -22,7 +22,6 @@
 #include "virt-dma.h"
 
 /* CSR register */
-#define TEGRA_GPCDMA_CHAN_CSR			0x00
 #define TEGRA_GPCDMA_CSR_ENB			BIT(31)
 #define TEGRA_GPCDMA_CSR_IE_EOC			BIT(30)
 #define TEGRA_GPCDMA_CSR_ONCE			BIT(27)
@@ -58,7 +57,6 @@
 #define TEGRA_GPCDMA_CSR_WEIGHT			GENMASK(13, 10)
 
 /* STATUS register */
-#define TEGRA_GPCDMA_CHAN_STATUS		0x004
 #define TEGRA_GPCDMA_STATUS_BUSY		BIT(31)
 #define TEGRA_GPCDMA_STATUS_ISE_EOC		BIT(30)
 #define TEGRA_GPCDMA_STATUS_PING_PONG		BIT(28)
@@ -70,22 +68,13 @@
 #define TEGRA_GPCDMA_STATUS_IRQ_STA		BIT(21)
 #define TEGRA_GPCDMA_STATUS_IRQ_TRIG_STA	BIT(20)
 
-#define TEGRA_GPCDMA_CHAN_CSRE			0x008
 #define TEGRA_GPCDMA_CHAN_CSRE_PAUSE		BIT(31)
 
-/* Source address */
-#define TEGRA_GPCDMA_CHAN_SRC_PTR		0x00C
-
-/* Destination address */
-#define TEGRA_GPCDMA_CHAN_DST_PTR		0x010
-
 /* High address pointer */
-#define TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR		0x014
 #define TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR		GENMASK(7, 0)
 #define TEGRA_GPCDMA_HIGH_ADDR_DST_PTR		GENMASK(23, 16)
 
 /* MC sequence register */
-#define TEGRA_GPCDMA_CHAN_MCSEQ			0x18
 #define TEGRA_GPCDMA_MCSEQ_DATA_SWAP		BIT(31)
 #define TEGRA_GPCDMA_MCSEQ_REQ_COUNT		GENMASK(30, 25)
 #define TEGRA_GPCDMA_MCSEQ_BURST		GENMASK(24, 23)
@@ -101,7 +90,6 @@
 #define TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK	GENMASK(6, 0)
 
 /* MMIO sequence register */
-#define TEGRA_GPCDMA_CHAN_MMIOSEQ			0x01c
 #define TEGRA_GPCDMA_MMIOSEQ_DBL_BUF		BIT(31)
 #define TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH		GENMASK(30, 28)
 #define TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_8	\
@@ -120,17 +108,7 @@
 #define TEGRA_GPCDMA_MMIOSEQ_WRAP_WORD		GENMASK(18, 16)
 #define TEGRA_GPCDMA_MMIOSEQ_MMIO_PROT		GENMASK(8, 7)
 
-/* Channel WCOUNT */
-#define TEGRA_GPCDMA_CHAN_WCOUNT		0x20
-
-/* Transfer count */
-#define TEGRA_GPCDMA_CHAN_XFER_COUNT		0x24
-
-/* DMA byte count status */
-#define TEGRA_GPCDMA_CHAN_DMA_BYTE_STATUS	0x28
-
 /* Error Status Register */
-#define TEGRA_GPCDMA_CHAN_ERR_STATUS		0x30
 #define TEGRA_GPCDMA_CHAN_ERR_TYPE_SHIFT	8
 #define TEGRA_GPCDMA_CHAN_ERR_TYPE_MASK	0xF
 #define TEGRA_GPCDMA_CHAN_ERR_TYPE(err)	(			\
@@ -143,14 +121,9 @@
 #define TEGRA_DMA_MC_SLAVE_ERR			0xB
 #define TEGRA_DMA_MMIO_SLAVE_ERR		0xA
 
-/* Fixed Pattern */
-#define TEGRA_GPCDMA_CHAN_FIXED_PATTERN		0x34
-
-#define TEGRA_GPCDMA_CHAN_TZ			0x38
 #define TEGRA_GPCDMA_CHAN_TZ_MMIO_PROT_1	BIT(0)
 #define TEGRA_GPCDMA_CHAN_TZ_MC_PROT_1		BIT(1)
 
-#define TEGRA_GPCDMA_CHAN_SPARE			0x3c
 #define TEGRA_GPCDMA_CHAN_SPARE_EN_LEGACY_FC	BIT(16)
 
 /*
@@ -181,19 +154,27 @@ struct tegra_dma_chip_data {
 	unsigned int nr_channels;
 	unsigned int channel_reg_size;
 	unsigned int max_dma_count;
+	const struct tegra_dma_channel_regs *channel_regs;
 	int (*terminate)(struct tegra_dma_channel *tdc);
 };
 
 /* DMA channel registers */
 struct tegra_dma_channel_regs {
 	u32 csr;
-	u32 src_ptr;
-	u32 dst_ptr;
-	u32 high_addr_ptr;
+	u32 status;
+	u32 csre;
+	u32 src;
+	u32 dst;
+	u32 high_addr;
 	u32 mc_seq;
 	u32 mmio_seq;
 	u32 wcount;
+	u32 wxfer;
+	u32 wstatus;
+	u32 err_status;
 	u32 fixed_pattern;
+	u32 tz;
+	u32 spare;
 };
 
 /*
@@ -205,7 +186,14 @@ struct tegra_dma_channel_regs {
  */
 struct tegra_dma_sg_req {
 	unsigned int len;
-	struct tegra_dma_channel_regs ch_regs;
+	u32 csr;
+	u32 src;
+	u32 dst;
+	u32 high_addr;
+	u32 mc_seq;
+	u32 mmio_seq;
+	u32 wcount;
+	u32 fixed_pattern;
 };
 
 /*
@@ -228,19 +216,20 @@ struct tegra_dma_desc {
  * tegra_dma_channel: Channel specific information
  */
 struct tegra_dma_channel {
-	bool config_init;
-	char name[30];
-	enum dma_transfer_direction sid_dir;
-	enum dma_status status;
-	int id;
-	int irq;
-	int slave_id;
+	const struct tegra_dma_channel_regs *regs;
 	struct tegra_dma *tdma;
 	struct virt_dma_chan vc;
 	struct tegra_dma_desc *dma_desc;
 	struct dma_slave_config dma_sconfig;
+	enum dma_transfer_direction sid_dir;
+	enum dma_status status;
 	unsigned int stream_id;
 	unsigned long chan_base_offset;
+	bool config_init;
+	char name[30];
+	int id;
+	int irq;
+	int slave_id;
 };
 
 /*
@@ -288,22 +277,25 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
 {
 	dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n",
 		tdc->id, tdc->name);
-	dev_dbg(tdc2dev(tdc), "CSR %x STA %x CSRE %x SRC %x DST %x\n",
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR),
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS),
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE),
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR),
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_DST_PTR)
+	dev_dbg(tdc2dev(tdc), "CSR %x STA %x CSRE %x\n",
+		tdc_read(tdc, tdc->regs->csr),
+		tdc_read(tdc, tdc->regs->status),
+		tdc_read(tdc, tdc->regs->csre)
 	);
-	dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x BSTA %x\n",
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ),
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_MMIOSEQ),
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_WCOUNT),
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT),
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_DMA_BYTE_STATUS)
+	dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
+		tdc_read(tdc, tdc->regs->src),
+		tdc_read(tdc, tdc->regs->dst),
+		tdc_read(tdc, tdc->regs->high_addr)
+	);
+	dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x WSTA %x\n",
+		tdc_read(tdc, tdc->regs->mc_seq),
+		tdc_read(tdc, tdc->regs->mmio_seq),
+		tdc_read(tdc, tdc->regs->wcount),
+		tdc_read(tdc, tdc->regs->wxfer),
+		tdc_read(tdc, tdc->regs->wstatus)
 	);
 	dev_dbg(tdc2dev(tdc), "DMA ERR_STA %x\n",
-		tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS));
+		tdc_read(tdc, tdc->regs->err_status));
 }
 
 static int tegra_dma_sid_reserve(struct tegra_dma_channel *tdc,
@@ -377,13 +369,13 @@ static int tegra_dma_pause(struct tegra_dma_channel *tdc)
 	int ret;
 	u32 val;
 
-	val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE);
+	val = tdc_read(tdc, tdc->regs->csre);
 	val |= TEGRA_GPCDMA_CHAN_CSRE_PAUSE;
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val);
+	tdc_write(tdc, tdc->regs->csre, val);
 
 	/* Wait until busy bit is de-asserted */
 	ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
-			tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS,
+			tdc->chan_base_offset + tdc->regs->status,
 			val,
 			!(val & TEGRA_GPCDMA_STATUS_BUSY),
 			TEGRA_GPCDMA_BURST_COMPLETE_TIME,
@@ -419,9 +411,9 @@ static void tegra_dma_resume(struct tegra_dma_channel *tdc)
 {
 	u32 val;
 
-	val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE);
+	val = tdc_read(tdc, tdc->regs->csre);
 	val &= ~TEGRA_GPCDMA_CHAN_CSRE_PAUSE;
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val);
+	tdc_write(tdc, tdc->regs->csre, val);
 
 	tdc->status = DMA_IN_PROGRESS;
 }
@@ -456,27 +448,27 @@ static void tegra_dma_disable(struct tegra_dma_channel *tdc)
 {
 	u32 csr, status;
 
-	csr = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR);
+	csr = tdc_read(tdc, tdc->regs->csr);
 
 	/* Disable interrupts */
 	csr &= ~TEGRA_GPCDMA_CSR_IE_EOC;
 
 	/* Disable DMA */
 	csr &= ~TEGRA_GPCDMA_CSR_ENB;
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, csr);
+	tdc_write(tdc, tdc->regs->csr, csr);
 
 	/* Clear interrupt status if it is there */
-	status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
+	status = tdc_read(tdc, tdc->regs->status);
 	if (status & TEGRA_GPCDMA_STATUS_ISE_EOC) {
 		dev_dbg(tdc2dev(tdc), "%s():clearing interrupt\n", __func__);
-		tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS, status);
+		tdc_write(tdc, tdc->regs->status, status);
 	}
 }
 
 static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
 {
 	struct tegra_dma_desc *dma_desc = tdc->dma_desc;
-	struct tegra_dma_channel_regs *ch_regs;
+	struct tegra_dma_sg_req *sg_req;
 	int ret;
 	u32 val;
 
@@ -488,29 +480,29 @@ static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
 
 	/* Configure next transfer immediately after DMA is busy */
 	ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
-			tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS,
+			tdc->chan_base_offset + tdc->regs->status,
 			val,
 			(val & TEGRA_GPCDMA_STATUS_BUSY), 0,
 			TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT);
 	if (ret)
 		return;
 
-	ch_regs = &dma_desc->sg_req[dma_desc->sg_idx].ch_regs;
+	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
 
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_WCOUNT, ch_regs->wcount);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR, ch_regs->src_ptr);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_DST_PTR, ch_regs->dst_ptr);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR, ch_regs->high_addr_ptr);
+	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
+	tdc_write(tdc, tdc->regs->src, sg_req->src);
+	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
+	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
 
 	/* Start DMA */
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR,
-		  ch_regs->csr | TEGRA_GPCDMA_CSR_ENB);
+	tdc_write(tdc, tdc->regs->csr,
+		  sg_req->csr | TEGRA_GPCDMA_CSR_ENB);
 }
 
 static void tegra_dma_start(struct tegra_dma_channel *tdc)
 {
 	struct tegra_dma_desc *dma_desc = tdc->dma_desc;
-	struct tegra_dma_channel_regs *ch_regs;
+	struct tegra_dma_sg_req *sg_req;
 	struct virt_dma_desc *vdesc;
 
 	if (!dma_desc) {
@@ -526,21 +518,21 @@ static void tegra_dma_start(struct tegra_dma_channel *tdc)
 		tegra_dma_resume(tdc);
 	}
 
-	ch_regs = &dma_desc->sg_req[dma_desc->sg_idx].ch_regs;
+	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
 
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_WCOUNT, ch_regs->wcount);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, 0);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR, ch_regs->src_ptr);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_DST_PTR, ch_regs->dst_ptr);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR, ch_regs->high_addr_ptr);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_FIXED_PATTERN, ch_regs->fixed_pattern);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_MMIOSEQ, ch_regs->mmio_seq);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_MCSEQ, ch_regs->mc_seq);
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, ch_regs->csr);
+	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
+	tdc_write(tdc, tdc->regs->csr, 0);
+	tdc_write(tdc, tdc->regs->src, sg_req->src);
+	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
+	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
+	tdc_write(tdc, tdc->regs->fixed_pattern, sg_req->fixed_pattern);
+	tdc_write(tdc, tdc->regs->mmio_seq, sg_req->mmio_seq);
+	tdc_write(tdc, tdc->regs->mc_seq, sg_req->mc_seq);
+	tdc_write(tdc, tdc->regs->csr, sg_req->csr);
 
 	/* Start DMA */
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR,
-		  ch_regs->csr | TEGRA_GPCDMA_CSR_ENB);
+	tdc_write(tdc, tdc->regs->csr,
+		  sg_req->csr | TEGRA_GPCDMA_CSR_ENB);
 }
 
 static void tegra_dma_xfer_complete(struct tegra_dma_channel *tdc)
@@ -601,19 +593,19 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id)
 	u32 status;
 
 	/* Check channel error status register */
-	status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS);
+	status = tdc_read(tdc, tdc->regs->err_status);
 	if (status) {
 		tegra_dma_chan_decode_error(tdc, status);
 		tegra_dma_dump_chan_regs(tdc);
-		tdc_write(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS, 0xFFFFFFFF);
+		tdc_write(tdc, tdc->regs->err_status, 0xFFFFFFFF);
 	}
 
 	spin_lock(&tdc->vc.lock);
-	status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
+	status = tdc_read(tdc, tdc->regs->status);
 	if (!(status & TEGRA_GPCDMA_STATUS_ISE_EOC))
 		goto irq_done;
 
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS,
+	tdc_write(tdc, tdc->regs->status,
 		  TEGRA_GPCDMA_STATUS_ISE_EOC);
 
 	if (!dma_desc)
@@ -673,10 +665,10 @@ static int tegra_dma_stop_client(struct tegra_dma_channel *tdc)
 	 * to stop DMA engine from starting any more bursts for
 	 * the given client and wait for in flight bursts to complete
 	 */
-	csr = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR);
+	csr = tdc_read(tdc, tdc->regs->csr);
 	csr &= ~(TEGRA_GPCDMA_CSR_REQ_SEL_MASK);
 	csr |= TEGRA_GPCDMA_CSR_REQ_SEL_UNUSED;
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, csr);
+	tdc_write(tdc, tdc->regs->csr, csr);
 
 	/* Wait for in flight data transfer to finish */
 	udelay(TEGRA_GPCDMA_BURST_COMPLETE_TIME);
@@ -687,7 +679,7 @@ static int tegra_dma_stop_client(struct tegra_dma_channel *tdc)
 
 	ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
 				tdc->chan_base_offset +
-				TEGRA_GPCDMA_CHAN_STATUS,
+				tdc->regs->status,
 				status,
 				!(status & (TEGRA_GPCDMA_STATUS_CHANNEL_TX |
 				TEGRA_GPCDMA_STATUS_CHANNEL_RX)),
@@ -739,14 +731,14 @@ static int tegra_dma_get_residual(struct tegra_dma_channel *tdc)
 	unsigned int bytes_xfer, residual;
 	u32 wcount = 0, status;
 
-	wcount = tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT);
+	wcount = tdc_read(tdc, tdc->regs->wxfer);
 
 	/*
 	 * Set wcount = 0 if EOC bit is set. The transfer would have
 	 * already completed and the CHAN_XFER_COUNT could have updated
 	 * for the next transfer, specifically in case of cyclic transfers.
 	 */
-	status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
+	status = tdc_read(tdc, tdc->regs->status);
 	if (status & TEGRA_GPCDMA_STATUS_ISE_EOC)
 		wcount = 0;
 
@@ -893,7 +885,7 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
 	/* Configure default priority weight for the channel */
 	csr |= FIELD_PREP(TEGRA_GPCDMA_CSR_WEIGHT, 1);
 
-	mc_seq =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
+	mc_seq =  tdc_read(tdc, tdc->regs->mc_seq);
 	/* retain stream-id and clean rest */
 	mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
 
@@ -916,16 +908,16 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
 	dma_desc->sg_count = 1;
 	sg_req = dma_desc->sg_req;
 
-	sg_req[0].ch_regs.src_ptr = 0;
-	sg_req[0].ch_regs.dst_ptr = dest;
-	sg_req[0].ch_regs.high_addr_ptr =
+	sg_req[0].src = 0;
+	sg_req[0].dst = dest;
+	sg_req[0].high_addr =
 			FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
-	sg_req[0].ch_regs.fixed_pattern = value;
+	sg_req[0].fixed_pattern = value;
 	/* Word count reg takes value as (N +1) words */
-	sg_req[0].ch_regs.wcount = ((len - 4) >> 2);
-	sg_req[0].ch_regs.csr = csr;
-	sg_req[0].ch_regs.mmio_seq = 0;
-	sg_req[0].ch_regs.mc_seq = mc_seq;
+	sg_req[0].wcount = ((len - 4) >> 2);
+	sg_req[0].csr = csr;
+	sg_req[0].mmio_seq = 0;
+	sg_req[0].mc_seq = mc_seq;
 	sg_req[0].len = len;
 
 	dma_desc->cyclic = false;
@@ -961,7 +953,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
 	/* Configure default priority weight for the channel */
 	csr |= FIELD_PREP(TEGRA_GPCDMA_CSR_WEIGHT, 1);
 
-	mc_seq =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
+	mc_seq =  tdc_read(tdc, tdc->regs->mc_seq);
 	/* retain stream-id and clean rest */
 	mc_seq &= (TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK) |
 		  (TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK);
@@ -985,17 +977,17 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
 	dma_desc->sg_count = 1;
 	sg_req = dma_desc->sg_req;
 
-	sg_req[0].ch_regs.src_ptr = src;
-	sg_req[0].ch_regs.dst_ptr = dest;
-	sg_req[0].ch_regs.high_addr_ptr =
+	sg_req[0].src = src;
+	sg_req[0].dst = dest;
+	sg_req[0].high_addr =
 		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (src >> 32));
-	sg_req[0].ch_regs.high_addr_ptr |=
+	sg_req[0].high_addr |=
 		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
 	/* Word count reg takes value as (N +1) words */
-	sg_req[0].ch_regs.wcount = ((len - 4) >> 2);
-	sg_req[0].ch_regs.csr = csr;
-	sg_req[0].ch_regs.mmio_seq = 0;
-	sg_req[0].ch_regs.mc_seq = mc_seq;
+	sg_req[0].wcount = ((len - 4) >> 2);
+	sg_req[0].csr = csr;
+	sg_req[0].mmio_seq = 0;
+	sg_req[0].mc_seq = mc_seq;
 	sg_req[0].len = len;
 
 	dma_desc->cyclic = false;
@@ -1049,7 +1041,7 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
 	if (flags & DMA_PREP_INTERRUPT)
 		csr |= TEGRA_GPCDMA_CSR_IE_EOC;
 
-	mc_seq =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
+	mc_seq =  tdc_read(tdc, tdc->regs->mc_seq);
 	/* retain stream-id and clean rest */
 	mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
 
@@ -1096,14 +1088,14 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
 		dma_desc->bytes_req += len;
 
 		if (direction == DMA_MEM_TO_DEV) {
-			sg_req[i].ch_regs.src_ptr = mem;
-			sg_req[i].ch_regs.dst_ptr = apb_ptr;
-			sg_req[i].ch_regs.high_addr_ptr =
+			sg_req[i].src = mem;
+			sg_req[i].dst = apb_ptr;
+			sg_req[i].high_addr =
 				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
 		} else if (direction == DMA_DEV_TO_MEM) {
-			sg_req[i].ch_regs.src_ptr = apb_ptr;
-			sg_req[i].ch_regs.dst_ptr = mem;
-			sg_req[i].ch_regs.high_addr_ptr =
+			sg_req[i].src = apb_ptr;
+			sg_req[i].dst = mem;
+			sg_req[i].high_addr =
 				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
 		}
 
@@ -1111,10 +1103,10 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
 		 * Word count register takes input in words. Writing a value
 		 * of N into word count register means a req of (N+1) words.
 		 */
-		sg_req[i].ch_regs.wcount = ((len - 4) >> 2);
-		sg_req[i].ch_regs.csr = csr;
-		sg_req[i].ch_regs.mmio_seq = mmio_seq;
-		sg_req[i].ch_regs.mc_seq = mc_seq;
+		sg_req[i].wcount = ((len - 4) >> 2);
+		sg_req[i].csr = csr;
+		sg_req[i].mmio_seq = mmio_seq;
+		sg_req[i].mc_seq = mc_seq;
 		sg_req[i].len = len;
 	}
 
@@ -1186,7 +1178,7 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
 
 	mmio_seq |= FIELD_PREP(TEGRA_GPCDMA_MMIOSEQ_WRAP_WORD, 1);
 
-	mc_seq =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
+	mc_seq =  tdc_read(tdc, tdc->regs->mc_seq);
 	/* retain stream-id and clean rest */
 	mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
 
@@ -1218,24 +1210,24 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
 	for (i = 0; i < period_count; i++) {
 		mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
 		if (direction == DMA_MEM_TO_DEV) {
-			sg_req[i].ch_regs.src_ptr = mem;
-			sg_req[i].ch_regs.dst_ptr = apb_ptr;
-			sg_req[i].ch_regs.high_addr_ptr =
+			sg_req[i].src = mem;
+			sg_req[i].dst = apb_ptr;
+			sg_req[i].high_addr =
 				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
 		} else if (direction == DMA_DEV_TO_MEM) {
-			sg_req[i].ch_regs.src_ptr = apb_ptr;
-			sg_req[i].ch_regs.dst_ptr = mem;
-			sg_req[i].ch_regs.high_addr_ptr =
+			sg_req[i].src = apb_ptr;
+			sg_req[i].dst = mem;
+			sg_req[i].high_addr =
 				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
 		}
 		/*
 		 * Word count register takes input in words. Writing a value
 		 * of N into word count register means a req of (N+1) words.
 		 */
-		sg_req[i].ch_regs.wcount = ((len - 4) >> 2);
-		sg_req[i].ch_regs.csr = csr;
-		sg_req[i].ch_regs.mmio_seq = mmio_seq;
-		sg_req[i].ch_regs.mc_seq = mc_seq;
+		sg_req[i].wcount = ((len - 4) >> 2);
+		sg_req[i].csr = csr;
+		sg_req[i].mmio_seq = mmio_seq;
+		sg_req[i].mc_seq = mc_seq;
 		sg_req[i].len = len;
 
 		mem += len;
@@ -1305,11 +1297,30 @@ static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec,
 	return chan;
 }
 
+static const struct tegra_dma_channel_regs tegra186_reg_offsets = {
+	.csr = 0x0,
+	.status = 0x4,
+	.csre = 0x8,
+	.src = 0xc,
+	.dst = 0x10,
+	.high_addr = 0x14,
+	.mc_seq = 0x18,
+	.mmio_seq = 0x1c,
+	.wcount = 0x20,
+	.wxfer = 0x24,
+	.wstatus = 0x28,
+	.err_status = 0x30,
+	.fixed_pattern = 0x34,
+	.tz = 0x38,
+	.spare = 0x40,
+};
+
 static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
 	.nr_channels = 32,
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = false,
+	.channel_regs = &tegra186_reg_offsets,
 	.terminate = tegra_dma_stop_client,
 };
 
@@ -1318,6 +1329,7 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = true,
+	.channel_regs = &tegra186_reg_offsets,
 	.terminate = tegra_dma_pause,
 };
 
@@ -1326,6 +1338,7 @@ static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = true,
+	.channel_regs = &tegra186_reg_offsets,
 	.terminate = tegra_dma_pause_noerr,
 };
 
@@ -1346,7 +1359,7 @@ MODULE_DEVICE_TABLE(of, tegra_dma_of_match);
 
 static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
 {
-	unsigned int reg_val =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
+	unsigned int reg_val =  tdc_read(tdc, tdc->regs->mc_seq);
 
 	reg_val &= ~(TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK);
 	reg_val &= ~(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK);
@@ -1354,7 +1367,7 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
 	reg_val |= FIELD_PREP(TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK, stream_id);
 	reg_val |= FIELD_PREP(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK, stream_id);
 
-	tdc_write(tdc, TEGRA_GPCDMA_CHAN_MCSEQ, reg_val);
+	tdc_write(tdc, tdc->regs->mc_seq, reg_val);
 	return 0;
 }
 
@@ -1420,6 +1433,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
 		tdc->chan_base_offset = TEGRA_GPCDMA_CHANNEL_BASE_ADDR_OFFSET +
 					i * cdata->channel_reg_size;
 		snprintf(tdc->name, sizeof(tdc->name), "gpcdma.%d", i);
+		tdc->regs = cdata->channel_regs;
 		tdc->tdma = tdma;
 		tdc->id = i;
 		tdc->slave_id = -1;
-- 
2.50.1


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

* [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits
  2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
                   ` (3 preceding siblings ...)
  2026-02-17 17:34 ` [PATCH 4/8] dmaengine: tegra: Use struct for register offsets Akhil R
@ 2026-02-17 17:34 ` Akhil R
  2026-02-17 19:44   ` Frank Li
  2026-02-17 17:34 ` [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID Akhil R
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

Tegra264 supports address width of 41 bits and has a separate register
to accommodate the high address. Add a device data property to specify
the number of address bits supported on a device and use that to
program the required registers.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 drivers/dma/tegra186-gpc-dma.c | 129 +++++++++++++++++++++------------
 1 file changed, 82 insertions(+), 47 deletions(-)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 72701b543ceb..ce3b1dd52bb3 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -151,6 +151,7 @@ struct tegra_dma_channel;
  */
 struct tegra_dma_chip_data {
 	bool hw_support_pause;
+	unsigned int addr_bits;
 	unsigned int nr_channels;
 	unsigned int channel_reg_size;
 	unsigned int max_dma_count;
@@ -166,6 +167,8 @@ struct tegra_dma_channel_regs {
 	u32 src;
 	u32 dst;
 	u32 high_addr;
+	u32 src_high;
+	u32 dst_high;
 	u32 mc_seq;
 	u32 mmio_seq;
 	u32 wcount;
@@ -189,7 +192,8 @@ struct tegra_dma_sg_req {
 	u32 csr;
 	u32 src;
 	u32 dst;
-	u32 high_addr;
+	u32 src_high;
+	u32 dst_high;
 	u32 mc_seq;
 	u32 mmio_seq;
 	u32 wcount;
@@ -273,6 +277,41 @@ static inline struct device *tdc2dev(struct tegra_dma_channel *tdc)
 	return tdc->vc.chan.device->dev;
 }
 
+static void tegra_dma_program_addr(struct tegra_dma_channel *tdc,
+				   struct tegra_dma_sg_req *sg_req)
+{
+	tdc_write(tdc, tdc->regs->src, sg_req->src);
+	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
+
+	if (tdc->tdma->chip_data->addr_bits > 40) {
+		tdc_write(tdc, tdc->regs->src_high,
+			  sg_req->src_high);
+		tdc_write(tdc, tdc->regs->dst_high,
+			  sg_req->dst_high);
+	} else {
+		tdc_write(tdc, tdc->regs->high_addr,
+			  sg_req->src_high | sg_req->dst_high);
+	}
+}
+
+static void tegra_dma_configure_addr(struct tegra_dma_channel *tdc,
+				     struct tegra_dma_sg_req *sg_req,
+				phys_addr_t src, phys_addr_t dst)
+{
+	sg_req->src = lower_32_bits(src);
+	sg_req->dst = lower_32_bits(dst);
+
+	if (tdc->tdma->chip_data->addr_bits > 40) {
+		sg_req->src_high = upper_32_bits(src);
+		sg_req->dst_high = upper_32_bits(dst);
+	} else {
+		sg_req->src_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR,
+					      upper_32_bits(src));
+		sg_req->dst_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR,
+					      upper_32_bits(dst));
+	}
+}
+
 static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
 {
 	dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n",
@@ -282,11 +321,22 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
 		tdc_read(tdc, tdc->regs->status),
 		tdc_read(tdc, tdc->regs->csre)
 	);
-	dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
-		tdc_read(tdc, tdc->regs->src),
-		tdc_read(tdc, tdc->regs->dst),
-		tdc_read(tdc, tdc->regs->high_addr)
-	);
+
+	if (tdc->tdma->chip_data->addr_bits > 40) {
+		dev_dbg(tdc2dev(tdc), "SRC %x SRC HI %x DST %x DST HI %x\n",
+			tdc_read(tdc, tdc->regs->src),
+			tdc_read(tdc, tdc->regs->src_high),
+			tdc_read(tdc, tdc->regs->dst),
+			tdc_read(tdc, tdc->regs->dst_high)
+		);
+	} else {
+		dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
+			tdc_read(tdc, tdc->regs->src),
+			tdc_read(tdc, tdc->regs->dst),
+			tdc_read(tdc, tdc->regs->high_addr)
+		);
+	}
+
 	dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x WSTA %x\n",
 		tdc_read(tdc, tdc->regs->mc_seq),
 		tdc_read(tdc, tdc->regs->mmio_seq),
@@ -490,9 +540,7 @@ static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
 	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
 
 	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
-	tdc_write(tdc, tdc->regs->src, sg_req->src);
-	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
-	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
+	tegra_dma_program_addr(tdc, sg_req);
 
 	/* Start DMA */
 	tdc_write(tdc, tdc->regs->csr,
@@ -520,11 +568,9 @@ static void tegra_dma_start(struct tegra_dma_channel *tdc)
 
 	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
 
+	tegra_dma_program_addr(tdc, sg_req);
 	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
 	tdc_write(tdc, tdc->regs->csr, 0);
-	tdc_write(tdc, tdc->regs->src, sg_req->src);
-	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
-	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
 	tdc_write(tdc, tdc->regs->fixed_pattern, sg_req->fixed_pattern);
 	tdc_write(tdc, tdc->regs->mmio_seq, sg_req->mmio_seq);
 	tdc_write(tdc, tdc->regs->mc_seq, sg_req->mc_seq);
@@ -829,7 +875,7 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc,
 
 static int get_transfer_param(struct tegra_dma_channel *tdc,
 			      enum dma_transfer_direction direction,
-			      u32 *apb_addr,
+			      dma_addr_t *apb_addr,
 			      u32 *mmio_seq,
 			      u32 *csr,
 			      unsigned int *burst_size,
@@ -908,10 +954,7 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
 	dma_desc->sg_count = 1;
 	sg_req = dma_desc->sg_req;
 
-	sg_req[0].src = 0;
-	sg_req[0].dst = dest;
-	sg_req[0].high_addr =
-			FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
+	tegra_dma_configure_addr(tdc, &sg_req[0], 0, dest);
 	sg_req[0].fixed_pattern = value;
 	/* Word count reg takes value as (N +1) words */
 	sg_req[0].wcount = ((len - 4) >> 2);
@@ -977,12 +1020,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
 	dma_desc->sg_count = 1;
 	sg_req = dma_desc->sg_req;
 
-	sg_req[0].src = src;
-	sg_req[0].dst = dest;
-	sg_req[0].high_addr =
-		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (src >> 32));
-	sg_req[0].high_addr |=
-		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
+	tegra_dma_configure_addr(tdc, &sg_req[0], src, dest);
 	/* Word count reg takes value as (N +1) words */
 	sg_req[0].wcount = ((len - 4) >> 2);
 	sg_req[0].csr = csr;
@@ -1002,7 +1040,8 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
 	struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
 	unsigned int max_dma_count = tdc->tdma->chip_data->max_dma_count;
 	enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED;
-	u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0;
+	u32 csr, mc_seq, mmio_seq = 0;
+	dma_addr_t apb_ptr = 0;
 	struct tegra_dma_sg_req *sg_req;
 	struct tegra_dma_desc *dma_desc;
 	struct scatterlist *sg;
@@ -1087,17 +1126,10 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
 		mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
 		dma_desc->bytes_req += len;
 
-		if (direction == DMA_MEM_TO_DEV) {
-			sg_req[i].src = mem;
-			sg_req[i].dst = apb_ptr;
-			sg_req[i].high_addr =
-				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
-		} else if (direction == DMA_DEV_TO_MEM) {
-			sg_req[i].src = apb_ptr;
-			sg_req[i].dst = mem;
-			sg_req[i].high_addr =
-				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
-		}
+		if (direction == DMA_MEM_TO_DEV)
+			tegra_dma_configure_addr(tdc, &sg_req[i], mem, apb_ptr);
+		else if (direction == DMA_DEV_TO_MEM)
+			tegra_dma_configure_addr(tdc, &sg_req[i], apb_ptr, mem);
 
 		/*
 		 * Word count register takes input in words. Writing a value
@@ -1120,7 +1152,8 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
 			  unsigned long flags)
 {
 	enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED;
-	u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0, burst_size;
+	u32 csr, mc_seq, mmio_seq = 0, burst_size;
+	dma_addr_t apb_ptr = 0;
 	unsigned int max_dma_count, len, period_count, i;
 	struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
 	struct tegra_dma_desc *dma_desc;
@@ -1209,17 +1242,10 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
 	/* Split transfer equal to period size */
 	for (i = 0; i < period_count; i++) {
 		mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
-		if (direction == DMA_MEM_TO_DEV) {
-			sg_req[i].src = mem;
-			sg_req[i].dst = apb_ptr;
-			sg_req[i].high_addr =
-				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
-		} else if (direction == DMA_DEV_TO_MEM) {
-			sg_req[i].src = apb_ptr;
-			sg_req[i].dst = mem;
-			sg_req[i].high_addr =
-				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
-		}
+		if (direction == DMA_MEM_TO_DEV)
+			tegra_dma_configure_addr(tdc, &sg_req[i], mem, apb_ptr);
+		else if (direction == DMA_DEV_TO_MEM)
+			tegra_dma_configure_addr(tdc, &sg_req[i], apb_ptr, mem);
 		/*
 		 * Word count register takes input in words. Writing a value
 		 * of N into word count register means a req of (N+1) words.
@@ -1317,6 +1343,7 @@ static const struct tegra_dma_channel_regs tegra186_reg_offsets = {
 
 static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
 	.nr_channels = 32,
+	.addr_bits = 40,
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = false,
@@ -1326,6 +1353,7 @@ static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
 
 static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
 	.nr_channels = 32,
+	.addr_bits = 40,
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = true,
@@ -1335,6 +1363,7 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
 
 static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
 	.nr_channels = 32,
+	.addr_bits = 41,
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = true,
@@ -1446,6 +1475,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
 		tdc->stream_id = stream_id;
 	}
 
+	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to set DMA mask: %d\n", ret);
+		return ret;
+	}
+
 	dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask);
 	dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask);
 	dma_cap_set(DMA_MEMCPY, tdma->dma_dev.cap_mask);
-- 
2.50.1


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

* [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
  2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
                   ` (4 preceding siblings ...)
  2026-02-17 17:34 ` [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits Akhil R
@ 2026-02-17 17:34 ` Akhil R
  2026-02-17 19:52   ` Frank Li
  2026-02-19  8:28   ` Dan Carpenter
  2026-02-17 17:34 ` [PATCH 7/8] dmaengine: tegra: Add Tegra264 support Akhil R
  2026-02-17 17:34 ` [PATCH 8/8] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264 Akhil R
  7 siblings, 2 replies; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

Use iommu-map, when provided, to get the stream ID to be programmed
for each channel. Register each channel separately for allowing it
to use a separate IOMMU domain for the transfer.

Channels will continue to use the same global stream ID if iommu-map
property is not present in the device tree.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 drivers/dma/tegra186-gpc-dma.c | 62 +++++++++++++++++++++++++++-------
 1 file changed, 49 insertions(+), 13 deletions(-)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index ce3b1dd52bb3..b8ca269fa3ba 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_dma.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
@@ -1403,9 +1404,12 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
 static int tegra_dma_probe(struct platform_device *pdev)
 {
 	const struct tegra_dma_chip_data *cdata = NULL;
+	struct tegra_dma_channel *tdc;
+	struct tegra_dma *tdma;
+	struct dma_chan *chan;
+	bool use_iommu_map = false;
 	unsigned int i;
 	u32 stream_id;
-	struct tegra_dma *tdma;
 	int ret;
 
 	cdata = of_device_get_match_data(&pdev->dev);
@@ -1433,9 +1437,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
 
 	tdma->dma_dev.dev = &pdev->dev;
 
-	if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
-		dev_err(&pdev->dev, "Missing iommu stream-id\n");
-		return -EINVAL;
+	use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map");
+	if (!use_iommu_map) {
+		if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
+			dev_err(&pdev->dev, "Missing iommu stream-id\n");
+			return -EINVAL;
+		}
 	}
 
 	ret = device_property_read_u32(&pdev->dev, "dma-channel-mask",
@@ -1449,7 +1456,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
 
 	INIT_LIST_HEAD(&tdma->dma_dev.channels);
 	for (i = 0; i < cdata->nr_channels; i++) {
-		struct tegra_dma_channel *tdc = &tdma->channels[i];
+		tdc = &tdma->channels[i];
 
 		/* Check for channel mask */
 		if (!(tdma->chan_mask & BIT(i)))
@@ -1469,10 +1476,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
 
 		vchan_init(&tdc->vc, &tdma->dma_dev);
 		tdc->vc.desc_free = tegra_dma_desc_free;
-
-		/* program stream-id for this channel */
-		tegra_dma_program_sid(tdc, stream_id);
-		tdc->stream_id = stream_id;
 	}
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
@@ -1517,20 +1520,53 @@ static int tegra_dma_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
+		struct device *chdev = &chan->dev->device;
+
+		tdc = to_tegra_dma_chan(chan);
+		if (use_iommu_map) {
+			chdev->coherent_dma_mask = pdev->dev.coherent_dma_mask;
+			chdev->dma_mask = &chdev->coherent_dma_mask;
+			chdev->bus = pdev->dev.bus;
+
+			ret = of_dma_configure_id(chdev, pdev->dev.of_node,
+						  true, &tdc->id);
+			if (ret) {
+				dev_err(chdev, "Failed to configure IOMMU for channel %d: %d\n",
+					tdc->id, ret);
+				goto err_unregister;
+			}
+
+			if (!tegra_dev_iommu_get_stream_id(chdev, &stream_id)) {
+				dev_err(chdev, "Failed to get stream ID for channel %d\n",
+					tdc->id);
+				goto err_unregister;
+			}
+
+			chan->dev->chan_dma_dev = true;
+		}
+
+		/* program stream-id for this channel */
+		tegra_dma_program_sid(tdc, stream_id);
+		tdc->stream_id = stream_id;
+	}
+
 	ret = of_dma_controller_register(pdev->dev.of_node,
 					 tegra_dma_of_xlate, tdma);
 	if (ret < 0) {
 		dev_err_probe(&pdev->dev, ret,
 			      "GPC DMA OF registration failed\n");
-
-		dma_async_device_unregister(&tdma->dma_dev);
-		return ret;
+		goto err_unregister;
 	}
 
-	dev_info(&pdev->dev, "GPC DMA driver register %lu channels\n",
+	dev_info(&pdev->dev, "GPC DMA driver registered %lu channels\n",
 		 hweight_long(tdma->chan_mask));
 
 	return 0;
+
+err_unregister:
+	dma_async_device_unregister(&tdma->dma_dev);
+	return ret;
 }
 
 static void tegra_dma_remove(struct platform_device *pdev)
-- 
2.50.1


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

* [PATCH 7/8] dmaengine: tegra: Add Tegra264 support
  2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
                   ` (5 preceding siblings ...)
  2026-02-17 17:34 ` [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID Akhil R
@ 2026-02-17 17:34 ` Akhil R
  2026-02-17 19:53   ` Frank Li
  2026-02-17 17:34 ` [PATCH 8/8] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264 Akhil R
  7 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

Update compatible and chip data to support GPCDMA in Tegra264.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 drivers/dma/tegra186-gpc-dma.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index b8ca269fa3ba..11347c9f3215 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1342,6 +1342,25 @@ static const struct tegra_dma_channel_regs tegra186_reg_offsets = {
 	.spare = 0x40,
 };
 
+static const struct tegra_dma_channel_regs tegra264_reg_offsets = {
+	.csr = 0x0,
+	.status = 0x4,
+	.csre = 0x8,
+	.src = 0xc,
+	.dst = 0x10,
+	.src_high = 0x14,
+	.dst_high = 0x18,
+	.mc_seq = 0x1c,
+	.mmio_seq = 0x20,
+	.wcount = 0x24,
+	.wxfer = 0x28,
+	.wstatus = 0x2c,
+	.err_status = 0x34,
+	.fixed_pattern = 0x38,
+	.tz = 0x3c,
+	.spare = 0x44,
+};
+
 static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
 	.nr_channels = 32,
 	.addr_bits = 40,
@@ -1372,6 +1391,16 @@ static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
 	.terminate = tegra_dma_pause_noerr,
 };
 
+static const struct tegra_dma_chip_data tegra264_dma_chip_data = {
+	.nr_channels = 32,
+	.addr_bits = 48,
+	.channel_reg_size = SZ_64K,
+	.max_dma_count = SZ_1G,
+	.hw_support_pause = true,
+	.channel_regs = &tegra264_reg_offsets,
+	.terminate = tegra_dma_pause_noerr,
+};
+
 static const struct of_device_id tegra_dma_of_match[] = {
 	{
 		.compatible = "nvidia,tegra186-gpcdma",
@@ -1382,6 +1411,9 @@ static const struct of_device_id tegra_dma_of_match[] = {
 	}, {
 		.compatible = "nvidia,tegra234-gpcdma",
 		.data = &tegra234_dma_chip_data,
+	}, {
+		.compatible = "nvidia,tegra264-gpcdma",
+		.data = &tegra264_dma_chip_data,
 	}, {
 	},
 };
-- 
2.50.1


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

* [PATCH 8/8] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264
  2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
                   ` (6 preceding siblings ...)
  2026-02-17 17:34 ` [PATCH 7/8] dmaengine: tegra: Add Tegra264 support Akhil R
@ 2026-02-17 17:34 ` Akhil R
  2026-02-17 18:02   ` Frank Li
  7 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-17 17:34 UTC (permalink / raw)
  To: dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel, Akhil R

Add iommu-map and remove iommus in the GPCDMA controller node so
that each channel uses separate stream ID and gets its own IOMMU
domain for memory. Also enable GPCDMA for Tegra264.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 .../arm64/boot/dts/nvidia/tegra264-p3834.dtsi |  4 +++
 arch/arm64/boot/dts/nvidia/tegra264.dtsi      | 33 ++++++++++++++++++-
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
index 7e2c3e66c2ab..c8beb616964a 100644
--- a/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
@@ -16,6 +16,10 @@ serial@c4e0000 {
 		serial@c5a0000 {
 			status = "okay";
 		};
+
+		dma-controller@8400000 {
+			status = "okay";
+		};
 	};
 
 	bus@8100000000 {
diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
index 7644a41d5f72..0317418c95d3 100644
--- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
@@ -3243,7 +3243,38 @@ gpcdma: dma-controller@8400000 {
 				     <GIC_SPI 614 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 615 IRQ_TYPE_LEVEL_HIGH>;
 			#dma-cells = <1>;
-			iommus = <&smmu1 0x00000800>;
+			iommu-map =
+				<1  &smmu1 0x801 1>,
+				<2  &smmu1 0x802 1>,
+				<3  &smmu1 0x803 1>,
+				<4  &smmu1 0x804 1>,
+				<5  &smmu1 0x805 1>,
+				<6  &smmu1 0x806 1>,
+				<7  &smmu1 0x807 1>,
+				<8  &smmu1 0x808 1>,
+				<9  &smmu1 0x809 1>,
+				<10 &smmu1 0x80a 1>,
+				<11 &smmu1 0x80b 1>,
+				<12 &smmu1 0x80c 1>,
+				<13 &smmu1 0x80d 1>,
+				<14 &smmu1 0x80e 1>,
+				<15 &smmu1 0x80f 1>,
+				<16 &smmu1 0x810 1>,
+				<17 &smmu1 0x811 1>,
+				<18 &smmu1 0x812 1>,
+				<19 &smmu1 0x813 1>,
+				<20 &smmu1 0x814 1>,
+				<21 &smmu1 0x815 1>,
+				<22 &smmu1 0x816 1>,
+				<23 &smmu1 0x817 1>,
+				<24 &smmu1 0x818 1>,
+				<25 &smmu1 0x819 1>,
+				<26 &smmu1 0x81a 1>,
+				<27 &smmu1 0x81b 1>,
+				<28 &smmu1 0x81c 1>,
+				<29 &smmu1 0x81d 1>,
+				<30 &smmu1 0x81e 1>,
+				<31 &smmu1 0x81f 1>;
 			dma-coherent;
 			dma-channel-mask = <0xfffffffe>;
 			status = "disabled";
-- 
2.50.1


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

* Re: [PATCH 8/8] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264
  2026-02-17 17:34 ` [PATCH 8/8] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264 Akhil R
@ 2026-02-17 18:02   ` Frank Li
  2026-02-24  6:55     ` Akhil R
  0 siblings, 1 reply; 32+ messages in thread
From: Frank Li @ 2026-02-17 18:02 UTC (permalink / raw)
  To: Akhil R
  Cc: dmaengine, linux-tegra, devicetree, linux-kernel, vkoul, Frank.Li,
	robh, krzk+dt, conor+dt, thierry.reding, jonathanh, p.zabel

On Tue, Feb 17, 2026 at 11:04:57PM +0530, Akhil R wrote:
> Add iommu-map and remove iommus in the GPCDMA controller node so
> that each channel uses separate stream ID and gets its own IOMMU
> domain for memory. Also enable GPCDMA for Tegra264.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  .../arm64/boot/dts/nvidia/tegra264-p3834.dtsi |  4 +++
>  arch/arm64/boot/dts/nvidia/tegra264.dtsi      | 33 ++++++++++++++++++-
>  2 files changed, 36 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
> index 7e2c3e66c2ab..c8beb616964a 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
> @@ -16,6 +16,10 @@ serial@c4e0000 {
>  		serial@c5a0000 {
>  			status = "okay";
>  		};
> +
> +		dma-controller@8400000 {
> +			status = "okay";
> +		};
>  	};
>
>  	bus@8100000000 {
> diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> index 7644a41d5f72..0317418c95d3 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> @@ -3243,7 +3243,38 @@ gpcdma: dma-controller@8400000 {
>  				     <GIC_SPI 614 IRQ_TYPE_LEVEL_HIGH>,
>  				     <GIC_SPI 615 IRQ_TYPE_LEVEL_HIGH>;
>  			#dma-cells = <1>;
> -			iommus = <&smmu1 0x00000800>;
> +			iommu-map =
> +				<1  &smmu1 0x801 1>,
> +				<2  &smmu1 0x802 1>,
> +				<3  &smmu1 0x803 1>,
> +				<4  &smmu1 0x804 1>,
> +				<5  &smmu1 0x805 1>,
> +				<6  &smmu1 0x806 1>,
> +				<7  &smmu1 0x807 1>,
> +				<8  &smmu1 0x808 1>,
> +				<9  &smmu1 0x809 1>,
> +				<10 &smmu1 0x80a 1>,
> +				<11 &smmu1 0x80b 1>,
> +				<12 &smmu1 0x80c 1>,
> +				<13 &smmu1 0x80d 1>,
> +				<14 &smmu1 0x80e 1>,
> +				<15 &smmu1 0x80f 1>,
> +				<16 &smmu1 0x810 1>,
> +				<17 &smmu1 0x811 1>,
> +				<18 &smmu1 0x812 1>,
> +				<19 &smmu1 0x813 1>,
> +				<20 &smmu1 0x814 1>,
> +				<21 &smmu1 0x815 1>,
> +				<22 &smmu1 0x816 1>,
> +				<23 &smmu1 0x817 1>,
> +				<24 &smmu1 0x818 1>,
> +				<25 &smmu1 0x819 1>,
> +				<26 &smmu1 0x81a 1>,
> +				<27 &smmu1 0x81b 1>,
> +				<28 &smmu1 0x81c 1>,
> +				<29 &smmu1 0x81d 1>,
> +				<30 &smmu1 0x81e 1>,
> +				<31 &smmu1 0x81f 1>;

It is linear increase
<1 &smmu1 0x801 31>;

Frank

>  			dma-coherent;
>  			dma-channel-mask = <0xfffffffe>;
>  			status = "disabled";
> --
> 2.50.1
>

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

* Re: [PATCH 3/8] dmaengine: tegra: Make reset control optional
  2026-02-17 17:34 ` [PATCH 3/8] dmaengine: tegra: Make reset control optional Akhil R
@ 2026-02-17 18:04   ` Frank Li
  2026-02-24  5:39     ` Akhil R
  0 siblings, 1 reply; 32+ messages in thread
From: Frank Li @ 2026-02-17 18:04 UTC (permalink / raw)
  To: Akhil R
  Cc: dmaengine, linux-tegra, devicetree, linux-kernel, vkoul, Frank.Li,
	robh, krzk+dt, conor+dt, thierry.reding, jonathanh, p.zabel

On Tue, Feb 17, 2026 at 11:04:52PM +0530, Akhil R wrote:
> Tegra264 BPMP restricts access to GPCDMA reset control and the reset

what's means of BPMP?

Frank
> is expected to be deasserted on boot by BPMP. Hence Make the reset
> control optional in the driver.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  drivers/dma/tegra186-gpc-dma.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> index 4d6fe0efa76e..236a298c26a1 100644
> --- a/drivers/dma/tegra186-gpc-dma.c
> +++ b/drivers/dma/tegra186-gpc-dma.c
> @@ -1382,7 +1382,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
>  	if (IS_ERR(tdma->base_addr))
>  		return PTR_ERR(tdma->base_addr);
>
> -	tdma->rst = devm_reset_control_get_exclusive(&pdev->dev, "gpcdma");
> +	tdma->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, "gpcdma");
>  	if (IS_ERR(tdma->rst)) {
>  		return dev_err_probe(&pdev->dev, PTR_ERR(tdma->rst),
>  			      "Missing controller reset\n");
> --
> 2.50.1
>

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

* Re: [PATCH 4/8] dmaengine: tegra: Use struct for register offsets
  2026-02-17 17:34 ` [PATCH 4/8] dmaengine: tegra: Use struct for register offsets Akhil R
@ 2026-02-17 18:09   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2026-02-17 18:09 UTC (permalink / raw)
  To: Akhil R
  Cc: dmaengine, linux-tegra, devicetree, linux-kernel, vkoul, Frank.Li,
	robh, krzk+dt, conor+dt, thierry.reding, jonathanh, p.zabel

On Tue, Feb 17, 2026 at 11:04:53PM +0530, Akhil R wrote:
> Repurpose tegra_dma_channel_regs struct to define offsets for all the
> channel registers. Previously, the struct only held the register values
> for each transfer and was wrapped within tegra_dma_sg_req. Now, let
> struct tegra_dma_sg_req hold the values directly and use channel_regs
> for storing the register offsets. Update all register read/write to use
> channel_regs struct. This is to accommodate the register offset change
> in Tegra264.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>  drivers/dma/tegra186-gpc-dma.c | 280 +++++++++++++++++----------------
>  1 file changed, 147 insertions(+), 133 deletions(-)
>
> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> index 236a298c26a1..72701b543ceb 100644
> --- a/drivers/dma/tegra186-gpc-dma.c
> +++ b/drivers/dma/tegra186-gpc-dma.c
> @@ -22,7 +22,6 @@
>  #include "virt-dma.h"
>
>  /* CSR register */
> -#define TEGRA_GPCDMA_CHAN_CSR			0x00
>  #define TEGRA_GPCDMA_CSR_ENB			BIT(31)
>  #define TEGRA_GPCDMA_CSR_IE_EOC			BIT(30)
>  #define TEGRA_GPCDMA_CSR_ONCE			BIT(27)
> @@ -58,7 +57,6 @@
>  #define TEGRA_GPCDMA_CSR_WEIGHT			GENMASK(13, 10)
>
>  /* STATUS register */
> -#define TEGRA_GPCDMA_CHAN_STATUS		0x004
>  #define TEGRA_GPCDMA_STATUS_BUSY		BIT(31)
>  #define TEGRA_GPCDMA_STATUS_ISE_EOC		BIT(30)
>  #define TEGRA_GPCDMA_STATUS_PING_PONG		BIT(28)
> @@ -70,22 +68,13 @@
>  #define TEGRA_GPCDMA_STATUS_IRQ_STA		BIT(21)
>  #define TEGRA_GPCDMA_STATUS_IRQ_TRIG_STA	BIT(20)
>
> -#define TEGRA_GPCDMA_CHAN_CSRE			0x008
>  #define TEGRA_GPCDMA_CHAN_CSRE_PAUSE		BIT(31)
>
> -/* Source address */
> -#define TEGRA_GPCDMA_CHAN_SRC_PTR		0x00C
> -
> -/* Destination address */
> -#define TEGRA_GPCDMA_CHAN_DST_PTR		0x010
> -
>  /* High address pointer */
> -#define TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR		0x014
>  #define TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR		GENMASK(7, 0)
>  #define TEGRA_GPCDMA_HIGH_ADDR_DST_PTR		GENMASK(23, 16)
>
>  /* MC sequence register */
> -#define TEGRA_GPCDMA_CHAN_MCSEQ			0x18
>  #define TEGRA_GPCDMA_MCSEQ_DATA_SWAP		BIT(31)
>  #define TEGRA_GPCDMA_MCSEQ_REQ_COUNT		GENMASK(30, 25)
>  #define TEGRA_GPCDMA_MCSEQ_BURST		GENMASK(24, 23)
> @@ -101,7 +90,6 @@
>  #define TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK	GENMASK(6, 0)
>
>  /* MMIO sequence register */
> -#define TEGRA_GPCDMA_CHAN_MMIOSEQ			0x01c
>  #define TEGRA_GPCDMA_MMIOSEQ_DBL_BUF		BIT(31)
>  #define TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH		GENMASK(30, 28)
>  #define TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_8	\
> @@ -120,17 +108,7 @@
>  #define TEGRA_GPCDMA_MMIOSEQ_WRAP_WORD		GENMASK(18, 16)
>  #define TEGRA_GPCDMA_MMIOSEQ_MMIO_PROT		GENMASK(8, 7)
>
> -/* Channel WCOUNT */
> -#define TEGRA_GPCDMA_CHAN_WCOUNT		0x20
> -
> -/* Transfer count */
> -#define TEGRA_GPCDMA_CHAN_XFER_COUNT		0x24
> -
> -/* DMA byte count status */
> -#define TEGRA_GPCDMA_CHAN_DMA_BYTE_STATUS	0x28
> -
>  /* Error Status Register */
> -#define TEGRA_GPCDMA_CHAN_ERR_STATUS		0x30
>  #define TEGRA_GPCDMA_CHAN_ERR_TYPE_SHIFT	8
>  #define TEGRA_GPCDMA_CHAN_ERR_TYPE_MASK	0xF
>  #define TEGRA_GPCDMA_CHAN_ERR_TYPE(err)	(			\
> @@ -143,14 +121,9 @@
>  #define TEGRA_DMA_MC_SLAVE_ERR			0xB
>  #define TEGRA_DMA_MMIO_SLAVE_ERR		0xA
>
> -/* Fixed Pattern */
> -#define TEGRA_GPCDMA_CHAN_FIXED_PATTERN		0x34
> -
> -#define TEGRA_GPCDMA_CHAN_TZ			0x38
>  #define TEGRA_GPCDMA_CHAN_TZ_MMIO_PROT_1	BIT(0)
>  #define TEGRA_GPCDMA_CHAN_TZ_MC_PROT_1		BIT(1)
>
> -#define TEGRA_GPCDMA_CHAN_SPARE			0x3c
>  #define TEGRA_GPCDMA_CHAN_SPARE_EN_LEGACY_FC	BIT(16)
>
>  /*
> @@ -181,19 +154,27 @@ struct tegra_dma_chip_data {
>  	unsigned int nr_channels;
>  	unsigned int channel_reg_size;
>  	unsigned int max_dma_count;
> +	const struct tegra_dma_channel_regs *channel_regs;
>  	int (*terminate)(struct tegra_dma_channel *tdc);
>  };
>
>  /* DMA channel registers */
>  struct tegra_dma_channel_regs {
>  	u32 csr;
> -	u32 src_ptr;
> -	u32 dst_ptr;
> -	u32 high_addr_ptr;
> +	u32 status;
> +	u32 csre;
> +	u32 src;
> +	u32 dst;
> +	u32 high_addr;
>  	u32 mc_seq;
>  	u32 mmio_seq;
>  	u32 wcount;
> +	u32 wxfer;
> +	u32 wstatus;
> +	u32 err_status;
>  	u32 fixed_pattern;
> +	u32 tz;
> +	u32 spare;
>  };
>
>  /*
> @@ -205,7 +186,14 @@ struct tegra_dma_channel_regs {
>   */
>  struct tegra_dma_sg_req {
>  	unsigned int len;
> -	struct tegra_dma_channel_regs ch_regs;
> +	u32 csr;
> +	u32 src;
> +	u32 dst;
> +	u32 high_addr;
> +	u32 mc_seq;
> +	u32 mmio_seq;
> +	u32 wcount;
> +	u32 fixed_pattern;
>  };
>
>  /*
> @@ -228,19 +216,20 @@ struct tegra_dma_desc {
>   * tegra_dma_channel: Channel specific information
>   */
>  struct tegra_dma_channel {
> -	bool config_init;
> -	char name[30];
> -	enum dma_transfer_direction sid_dir;
> -	enum dma_status status;
> -	int id;
> -	int irq;
> -	int slave_id;
> +	const struct tegra_dma_channel_regs *regs;
>  	struct tegra_dma *tdma;
>  	struct virt_dma_chan vc;
>  	struct tegra_dma_desc *dma_desc;
>  	struct dma_slave_config dma_sconfig;
> +	enum dma_transfer_direction sid_dir;
> +	enum dma_status status;
>  	unsigned int stream_id;
>  	unsigned long chan_base_offset;
> +	bool config_init;
> +	char name[30];
> +	int id;
> +	int irq;
> +	int slave_id;
>  };
>
>  /*
> @@ -288,22 +277,25 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
>  {
>  	dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n",
>  		tdc->id, tdc->name);
> -	dev_dbg(tdc2dev(tdc), "CSR %x STA %x CSRE %x SRC %x DST %x\n",
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR),
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS),
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE),
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR),
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_DST_PTR)
> +	dev_dbg(tdc2dev(tdc), "CSR %x STA %x CSRE %x\n",
> +		tdc_read(tdc, tdc->regs->csr),
> +		tdc_read(tdc, tdc->regs->status),
> +		tdc_read(tdc, tdc->regs->csre)
>  	);
> -	dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x BSTA %x\n",
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ),
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_MMIOSEQ),
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_WCOUNT),
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT),
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_DMA_BYTE_STATUS)
> +	dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
> +		tdc_read(tdc, tdc->regs->src),
> +		tdc_read(tdc, tdc->regs->dst),
> +		tdc_read(tdc, tdc->regs->high_addr)
> +	);
> +	dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x WSTA %x\n",
> +		tdc_read(tdc, tdc->regs->mc_seq),
> +		tdc_read(tdc, tdc->regs->mmio_seq),
> +		tdc_read(tdc, tdc->regs->wcount),
> +		tdc_read(tdc, tdc->regs->wxfer),
> +		tdc_read(tdc, tdc->regs->wstatus)
>  	);
>  	dev_dbg(tdc2dev(tdc), "DMA ERR_STA %x\n",
> -		tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS));
> +		tdc_read(tdc, tdc->regs->err_status));
>  }
>
>  static int tegra_dma_sid_reserve(struct tegra_dma_channel *tdc,
> @@ -377,13 +369,13 @@ static int tegra_dma_pause(struct tegra_dma_channel *tdc)
>  	int ret;
>  	u32 val;
>
> -	val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE);
> +	val = tdc_read(tdc, tdc->regs->csre);
>  	val |= TEGRA_GPCDMA_CHAN_CSRE_PAUSE;
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val);
> +	tdc_write(tdc, tdc->regs->csre, val);
>
>  	/* Wait until busy bit is de-asserted */
>  	ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
> -			tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS,
> +			tdc->chan_base_offset + tdc->regs->status,
>  			val,
>  			!(val & TEGRA_GPCDMA_STATUS_BUSY),
>  			TEGRA_GPCDMA_BURST_COMPLETE_TIME,
> @@ -419,9 +411,9 @@ static void tegra_dma_resume(struct tegra_dma_channel *tdc)
>  {
>  	u32 val;
>
> -	val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE);
> +	val = tdc_read(tdc, tdc->regs->csre);
>  	val &= ~TEGRA_GPCDMA_CHAN_CSRE_PAUSE;
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val);
> +	tdc_write(tdc, tdc->regs->csre, val);
>
>  	tdc->status = DMA_IN_PROGRESS;
>  }
> @@ -456,27 +448,27 @@ static void tegra_dma_disable(struct tegra_dma_channel *tdc)
>  {
>  	u32 csr, status;
>
> -	csr = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR);
> +	csr = tdc_read(tdc, tdc->regs->csr);
>
>  	/* Disable interrupts */
>  	csr &= ~TEGRA_GPCDMA_CSR_IE_EOC;
>
>  	/* Disable DMA */
>  	csr &= ~TEGRA_GPCDMA_CSR_ENB;
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, csr);
> +	tdc_write(tdc, tdc->regs->csr, csr);
>
>  	/* Clear interrupt status if it is there */
> -	status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
> +	status = tdc_read(tdc, tdc->regs->status);
>  	if (status & TEGRA_GPCDMA_STATUS_ISE_EOC) {
>  		dev_dbg(tdc2dev(tdc), "%s():clearing interrupt\n", __func__);
> -		tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS, status);
> +		tdc_write(tdc, tdc->regs->status, status);
>  	}
>  }
>
>  static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
>  {
>  	struct tegra_dma_desc *dma_desc = tdc->dma_desc;
> -	struct tegra_dma_channel_regs *ch_regs;
> +	struct tegra_dma_sg_req *sg_req;
>  	int ret;
>  	u32 val;
>
> @@ -488,29 +480,29 @@ static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
>
>  	/* Configure next transfer immediately after DMA is busy */
>  	ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
> -			tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS,
> +			tdc->chan_base_offset + tdc->regs->status,
>  			val,
>  			(val & TEGRA_GPCDMA_STATUS_BUSY), 0,
>  			TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT);
>  	if (ret)
>  		return;
>
> -	ch_regs = &dma_desc->sg_req[dma_desc->sg_idx].ch_regs;
> +	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
>
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_WCOUNT, ch_regs->wcount);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR, ch_regs->src_ptr);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_DST_PTR, ch_regs->dst_ptr);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR, ch_regs->high_addr_ptr);
> +	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
> +	tdc_write(tdc, tdc->regs->src, sg_req->src);
> +	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
> +	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
>
>  	/* Start DMA */
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR,
> -		  ch_regs->csr | TEGRA_GPCDMA_CSR_ENB);
> +	tdc_write(tdc, tdc->regs->csr,
> +		  sg_req->csr | TEGRA_GPCDMA_CSR_ENB);
>  }
>
>  static void tegra_dma_start(struct tegra_dma_channel *tdc)
>  {
>  	struct tegra_dma_desc *dma_desc = tdc->dma_desc;
> -	struct tegra_dma_channel_regs *ch_regs;
> +	struct tegra_dma_sg_req *sg_req;
>  	struct virt_dma_desc *vdesc;
>
>  	if (!dma_desc) {
> @@ -526,21 +518,21 @@ static void tegra_dma_start(struct tegra_dma_channel *tdc)
>  		tegra_dma_resume(tdc);
>  	}
>
> -	ch_regs = &dma_desc->sg_req[dma_desc->sg_idx].ch_regs;
> +	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
>
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_WCOUNT, ch_regs->wcount);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, 0);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR, ch_regs->src_ptr);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_DST_PTR, ch_regs->dst_ptr);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR, ch_regs->high_addr_ptr);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_FIXED_PATTERN, ch_regs->fixed_pattern);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_MMIOSEQ, ch_regs->mmio_seq);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_MCSEQ, ch_regs->mc_seq);
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, ch_regs->csr);
> +	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
> +	tdc_write(tdc, tdc->regs->csr, 0);
> +	tdc_write(tdc, tdc->regs->src, sg_req->src);
> +	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
> +	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
> +	tdc_write(tdc, tdc->regs->fixed_pattern, sg_req->fixed_pattern);
> +	tdc_write(tdc, tdc->regs->mmio_seq, sg_req->mmio_seq);
> +	tdc_write(tdc, tdc->regs->mc_seq, sg_req->mc_seq);
> +	tdc_write(tdc, tdc->regs->csr, sg_req->csr);
>
>  	/* Start DMA */
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR,
> -		  ch_regs->csr | TEGRA_GPCDMA_CSR_ENB);
> +	tdc_write(tdc, tdc->regs->csr,
> +		  sg_req->csr | TEGRA_GPCDMA_CSR_ENB);
>  }
>
>  static void tegra_dma_xfer_complete(struct tegra_dma_channel *tdc)
> @@ -601,19 +593,19 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id)
>  	u32 status;
>
>  	/* Check channel error status register */
> -	status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS);
> +	status = tdc_read(tdc, tdc->regs->err_status);
>  	if (status) {
>  		tegra_dma_chan_decode_error(tdc, status);
>  		tegra_dma_dump_chan_regs(tdc);
> -		tdc_write(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS, 0xFFFFFFFF);
> +		tdc_write(tdc, tdc->regs->err_status, 0xFFFFFFFF);
>  	}
>
>  	spin_lock(&tdc->vc.lock);
> -	status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
> +	status = tdc_read(tdc, tdc->regs->status);
>  	if (!(status & TEGRA_GPCDMA_STATUS_ISE_EOC))
>  		goto irq_done;
>
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS,
> +	tdc_write(tdc, tdc->regs->status,
>  		  TEGRA_GPCDMA_STATUS_ISE_EOC);
>
>  	if (!dma_desc)
> @@ -673,10 +665,10 @@ static int tegra_dma_stop_client(struct tegra_dma_channel *tdc)
>  	 * to stop DMA engine from starting any more bursts for
>  	 * the given client and wait for in flight bursts to complete
>  	 */
> -	csr = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR);
> +	csr = tdc_read(tdc, tdc->regs->csr);
>  	csr &= ~(TEGRA_GPCDMA_CSR_REQ_SEL_MASK);
>  	csr |= TEGRA_GPCDMA_CSR_REQ_SEL_UNUSED;
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, csr);
> +	tdc_write(tdc, tdc->regs->csr, csr);
>
>  	/* Wait for in flight data transfer to finish */
>  	udelay(TEGRA_GPCDMA_BURST_COMPLETE_TIME);
> @@ -687,7 +679,7 @@ static int tegra_dma_stop_client(struct tegra_dma_channel *tdc)
>
>  	ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
>  				tdc->chan_base_offset +
> -				TEGRA_GPCDMA_CHAN_STATUS,
> +				tdc->regs->status,
>  				status,
>  				!(status & (TEGRA_GPCDMA_STATUS_CHANNEL_TX |
>  				TEGRA_GPCDMA_STATUS_CHANNEL_RX)),
> @@ -739,14 +731,14 @@ static int tegra_dma_get_residual(struct tegra_dma_channel *tdc)
>  	unsigned int bytes_xfer, residual;
>  	u32 wcount = 0, status;
>
> -	wcount = tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT);
> +	wcount = tdc_read(tdc, tdc->regs->wxfer);
>
>  	/*
>  	 * Set wcount = 0 if EOC bit is set. The transfer would have
>  	 * already completed and the CHAN_XFER_COUNT could have updated
>  	 * for the next transfer, specifically in case of cyclic transfers.
>  	 */
> -	status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
> +	status = tdc_read(tdc, tdc->regs->status);
>  	if (status & TEGRA_GPCDMA_STATUS_ISE_EOC)
>  		wcount = 0;
>
> @@ -893,7 +885,7 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
>  	/* Configure default priority weight for the channel */
>  	csr |= FIELD_PREP(TEGRA_GPCDMA_CSR_WEIGHT, 1);
>
> -	mc_seq =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
> +	mc_seq =  tdc_read(tdc, tdc->regs->mc_seq);
>  	/* retain stream-id and clean rest */
>  	mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
>
> @@ -916,16 +908,16 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
>  	dma_desc->sg_count = 1;
>  	sg_req = dma_desc->sg_req;
>
> -	sg_req[0].ch_regs.src_ptr = 0;
> -	sg_req[0].ch_regs.dst_ptr = dest;
> -	sg_req[0].ch_regs.high_addr_ptr =
> +	sg_req[0].src = 0;
> +	sg_req[0].dst = dest;
> +	sg_req[0].high_addr =
>  			FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
> -	sg_req[0].ch_regs.fixed_pattern = value;
> +	sg_req[0].fixed_pattern = value;
>  	/* Word count reg takes value as (N +1) words */
> -	sg_req[0].ch_regs.wcount = ((len - 4) >> 2);
> -	sg_req[0].ch_regs.csr = csr;
> -	sg_req[0].ch_regs.mmio_seq = 0;
> -	sg_req[0].ch_regs.mc_seq = mc_seq;
> +	sg_req[0].wcount = ((len - 4) >> 2);
> +	sg_req[0].csr = csr;
> +	sg_req[0].mmio_seq = 0;
> +	sg_req[0].mc_seq = mc_seq;
>  	sg_req[0].len = len;
>
>  	dma_desc->cyclic = false;
> @@ -961,7 +953,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
>  	/* Configure default priority weight for the channel */
>  	csr |= FIELD_PREP(TEGRA_GPCDMA_CSR_WEIGHT, 1);
>
> -	mc_seq =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
> +	mc_seq =  tdc_read(tdc, tdc->regs->mc_seq);
>  	/* retain stream-id and clean rest */
>  	mc_seq &= (TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK) |
>  		  (TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK);
> @@ -985,17 +977,17 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
>  	dma_desc->sg_count = 1;
>  	sg_req = dma_desc->sg_req;
>
> -	sg_req[0].ch_regs.src_ptr = src;
> -	sg_req[0].ch_regs.dst_ptr = dest;
> -	sg_req[0].ch_regs.high_addr_ptr =
> +	sg_req[0].src = src;
> +	sg_req[0].dst = dest;
> +	sg_req[0].high_addr =
>  		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (src >> 32));
> -	sg_req[0].ch_regs.high_addr_ptr |=
> +	sg_req[0].high_addr |=
>  		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
>  	/* Word count reg takes value as (N +1) words */
> -	sg_req[0].ch_regs.wcount = ((len - 4) >> 2);
> -	sg_req[0].ch_regs.csr = csr;
> -	sg_req[0].ch_regs.mmio_seq = 0;
> -	sg_req[0].ch_regs.mc_seq = mc_seq;
> +	sg_req[0].wcount = ((len - 4) >> 2);
> +	sg_req[0].csr = csr;
> +	sg_req[0].mmio_seq = 0;
> +	sg_req[0].mc_seq = mc_seq;
>  	sg_req[0].len = len;
>
>  	dma_desc->cyclic = false;
> @@ -1049,7 +1041,7 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
>  	if (flags & DMA_PREP_INTERRUPT)
>  		csr |= TEGRA_GPCDMA_CSR_IE_EOC;
>
> -	mc_seq =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
> +	mc_seq =  tdc_read(tdc, tdc->regs->mc_seq);
>  	/* retain stream-id and clean rest */
>  	mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
>
> @@ -1096,14 +1088,14 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
>  		dma_desc->bytes_req += len;
>
>  		if (direction == DMA_MEM_TO_DEV) {
> -			sg_req[i].ch_regs.src_ptr = mem;
> -			sg_req[i].ch_regs.dst_ptr = apb_ptr;
> -			sg_req[i].ch_regs.high_addr_ptr =
> +			sg_req[i].src = mem;
> +			sg_req[i].dst = apb_ptr;
> +			sg_req[i].high_addr =
>  				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
>  		} else if (direction == DMA_DEV_TO_MEM) {
> -			sg_req[i].ch_regs.src_ptr = apb_ptr;
> -			sg_req[i].ch_regs.dst_ptr = mem;
> -			sg_req[i].ch_regs.high_addr_ptr =
> +			sg_req[i].src = apb_ptr;
> +			sg_req[i].dst = mem;
> +			sg_req[i].high_addr =
>  				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
>  		}
>
> @@ -1111,10 +1103,10 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
>  		 * Word count register takes input in words. Writing a value
>  		 * of N into word count register means a req of (N+1) words.
>  		 */
> -		sg_req[i].ch_regs.wcount = ((len - 4) >> 2);
> -		sg_req[i].ch_regs.csr = csr;
> -		sg_req[i].ch_regs.mmio_seq = mmio_seq;
> -		sg_req[i].ch_regs.mc_seq = mc_seq;
> +		sg_req[i].wcount = ((len - 4) >> 2);
> +		sg_req[i].csr = csr;
> +		sg_req[i].mmio_seq = mmio_seq;
> +		sg_req[i].mc_seq = mc_seq;
>  		sg_req[i].len = len;
>  	}
>
> @@ -1186,7 +1178,7 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
>
>  	mmio_seq |= FIELD_PREP(TEGRA_GPCDMA_MMIOSEQ_WRAP_WORD, 1);
>
> -	mc_seq =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
> +	mc_seq =  tdc_read(tdc, tdc->regs->mc_seq);
>  	/* retain stream-id and clean rest */
>  	mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
>
> @@ -1218,24 +1210,24 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
>  	for (i = 0; i < period_count; i++) {
>  		mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
>  		if (direction == DMA_MEM_TO_DEV) {
> -			sg_req[i].ch_regs.src_ptr = mem;
> -			sg_req[i].ch_regs.dst_ptr = apb_ptr;
> -			sg_req[i].ch_regs.high_addr_ptr =
> +			sg_req[i].src = mem;
> +			sg_req[i].dst = apb_ptr;
> +			sg_req[i].high_addr =
>  				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
>  		} else if (direction == DMA_DEV_TO_MEM) {
> -			sg_req[i].ch_regs.src_ptr = apb_ptr;
> -			sg_req[i].ch_regs.dst_ptr = mem;
> -			sg_req[i].ch_regs.high_addr_ptr =
> +			sg_req[i].src = apb_ptr;
> +			sg_req[i].dst = mem;
> +			sg_req[i].high_addr =
>  				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
>  		}
>  		/*
>  		 * Word count register takes input in words. Writing a value
>  		 * of N into word count register means a req of (N+1) words.
>  		 */
> -		sg_req[i].ch_regs.wcount = ((len - 4) >> 2);
> -		sg_req[i].ch_regs.csr = csr;
> -		sg_req[i].ch_regs.mmio_seq = mmio_seq;
> -		sg_req[i].ch_regs.mc_seq = mc_seq;
> +		sg_req[i].wcount = ((len - 4) >> 2);
> +		sg_req[i].csr = csr;
> +		sg_req[i].mmio_seq = mmio_seq;
> +		sg_req[i].mc_seq = mc_seq;
>  		sg_req[i].len = len;
>
>  		mem += len;
> @@ -1305,11 +1297,30 @@ static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec,
>  	return chan;
>  }
>
> +static const struct tegra_dma_channel_regs tegra186_reg_offsets = {
> +	.csr = 0x0,
> +	.status = 0x4,
> +	.csre = 0x8,
> +	.src = 0xc,
> +	.dst = 0x10,
> +	.high_addr = 0x14,
> +	.mc_seq = 0x18,
> +	.mmio_seq = 0x1c,
> +	.wcount = 0x20,
> +	.wxfer = 0x24,
> +	.wstatus = 0x28,
> +	.err_status = 0x30,
> +	.fixed_pattern = 0x34,
> +	.tz = 0x38,
> +	.spare = 0x40,
> +};
> +
>  static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
>  	.nr_channels = 32,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = false,
> +	.channel_regs = &tegra186_reg_offsets,
>  	.terminate = tegra_dma_stop_client,
>  };
>
> @@ -1318,6 +1329,7 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = true,
> +	.channel_regs = &tegra186_reg_offsets,
>  	.terminate = tegra_dma_pause,
>  };
>
> @@ -1326,6 +1338,7 @@ static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = true,
> +	.channel_regs = &tegra186_reg_offsets,
>  	.terminate = tegra_dma_pause_noerr,
>  };
>
> @@ -1346,7 +1359,7 @@ MODULE_DEVICE_TABLE(of, tegra_dma_of_match);
>
>  static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
>  {
> -	unsigned int reg_val =  tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
> +	unsigned int reg_val =  tdc_read(tdc, tdc->regs->mc_seq);
>
>  	reg_val &= ~(TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK);
>  	reg_val &= ~(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK);
> @@ -1354,7 +1367,7 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
>  	reg_val |= FIELD_PREP(TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK, stream_id);
>  	reg_val |= FIELD_PREP(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK, stream_id);
>
> -	tdc_write(tdc, TEGRA_GPCDMA_CHAN_MCSEQ, reg_val);
> +	tdc_write(tdc, tdc->regs->mc_seq, reg_val);
>  	return 0;
>  }
>
> @@ -1420,6 +1433,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
>  		tdc->chan_base_offset = TEGRA_GPCDMA_CHANNEL_BASE_ADDR_OFFSET +
>  					i * cdata->channel_reg_size;
>  		snprintf(tdc->name, sizeof(tdc->name), "gpcdma.%d", i);
> +		tdc->regs = cdata->channel_regs;
>  		tdc->tdma = tdma;
>  		tdc->id = i;
>  		tdc->slave_id = -1;
> --
> 2.50.1
>

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

* Re: [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-02-17 17:34 ` [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
@ 2026-02-17 18:38   ` Rob Herring (Arm)
  2026-02-17 19:53   ` Krzysztof Kozlowski
  1 sibling, 0 replies; 32+ messages in thread
From: Rob Herring (Arm) @ 2026-02-17 18:38 UTC (permalink / raw)
  To: Akhil R
  Cc: vkoul, dmaengine, krzk+dt, devicetree, linux-tegra, Frank.Li,
	thierry.reding, conor+dt, jonathanh, linux-kernel, p.zabel


On Tue, 17 Feb 2026 23:04:50 +0530, Akhil R wrote:
> Add iommu-map property which helps when each channel requires its own
> stream ID for the transfer. Use iommu-map to specify separate stream
> ID for each channel. This enables each channel to be in its own iommu
> domain and keeps the memory isolated from other devices sharing the
> same DMA controller.
> 
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  .../devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml  | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/virtio/pci-iommu.example.dtb: pcie@40000000: iommu-map:0: [0, 1, 0, 8, 9, 1, 9, 65527] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/iommu/riscv,iommu.example.dtb: pcie@30000000: iommu-map:0: [0, 4, 0, 8, 9, 4, 9, 65527] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.example.dtb: pcie@1c00000 (qcom,pcie-sc8180x): iommu-map:0: [0, 4294967295, 7552, 1, 256, 4294967295, 7553, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sc8180x.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.example.dtb: pcie@1c00000 (qcom,pcie-sc8180x): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'dma-coherent', 'interconnect-names', 'interconnects', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'num-lanes', 'phy-names', 'phys', 'power-domains', 'ranges' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sc8180x.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.example.dtb: pcie@1c00000 (qcom,pcie-sc8180x): iommu-map:0: [0, 4294967295, 7552, 1, 256, 4294967295, 7553, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.example.dtb: pcie@1c00000 (qcom,pcie-sm8150): iommu-map:0: [0, 4294967295, 7552, 1, 256, 4294967295, 7553, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8150.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.example.dtb: pcie@1c00000 (qcom,pcie-sm8150): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'num-lanes', 'perst-gpios', 'phy-names', 'phys', 'power-domains', 'ranges', 'wake-gpios' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8150.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.example.dtb: pcie@1c00000 (qcom,pcie-sm8150): iommu-map:0: [0, 4294967295, 7552, 1, 256, 4294967295, 7553, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.example.dtb: pcie@1c08000 (qcom,pcie-sc7280): iommu-map:0: [0, 4294967295, 7296, 1, 256, 4294967295, 7297, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sc7280.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.example.dtb: pcie@1c08000 (qcom,pcie-sc7280): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'dma-coherent', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'num-lanes', 'pcie@0', 'power-domains', 'ranges', 'vddpe-3v3-supply' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sc7280.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.example.dtb: pcie@1c08000 (qcom,pcie-sc7280): iommu-map:0: [0, 4294967295, 7296, 1, 256, 4294967295, 7297, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.example.dtb: pcie@1c08000 (qcom,pcie-x1e80100): iommu-map:0: [0, 4294967295, 5120, 1, 256, 4294967295, 5121, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-x1e80100.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.example.dtb: pcie@1c08000 (qcom,pcie-x1e80100): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'dma-coherent', 'interconnect-names', 'interconnects', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'num-lanes', 'perst-gpios', 'phy-names', 'phys', 'power-domains', 'ranges', 'wake-gpios' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-x1e80100.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.example.dtb: pcie@1c08000 (qcom,pcie-x1e80100): iommu-map:0: [0, 4294967295, 5120, 1, 256, 4294967295, 5121, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.example.dtb: pcie@1c00000 (qcom,pcie-sa8775p): iommu-map:0: [0, 4294967295, 0, 1, 256, 4294967295, 1, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sa8775p.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.example.dtb: pcie@1c00000 (qcom,pcie-sa8775p): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'dma-coherent', 'interconnect-names', 'interconnects', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'num-lanes', 'perst-gpios', 'phy-names', 'phys', 'power-domains', 'ranges', 'wake-gpios' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sa8775p.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.example.dtb: pcie@1c00000 (qcom,pcie-sa8775p): iommu-map:0: [0, 4294967295, 0, 1, 256, 4294967295, 1, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8350.example.dtb: pcie@1c00000 (qcom,pcie-sm8350): iommu-map:0: [0, 4294967295, 7168, 1, 256, 4294967295, 7169, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8350.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8350.example.dtb: pcie@1c00000 (qcom,pcie-sm8350): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'num-lanes', 'perst-gpios', 'phy-names', 'phys', 'power-domains', 'ranges', 'wake-gpios' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8350.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8350.example.dtb: pcie@1c00000 (qcom,pcie-sm8350): iommu-map:0: [0, 4294967295, 7168, 1, 256, 4294967295, 7169, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.example.dtb: pcie@1c00000 (qcom,pcie-sm8450-pcie0): iommu-map:0: [0, 4294967295, 7168, 1, 256, 4294967295, 7169, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8450.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.example.dtb: pcie@1c00000 (qcom,pcie-sm8450-pcie0): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'max-link-speed', 'msi-map', 'msi-map-mask', 'num-lanes', 'perst-gpios', 'phy-names', 'phys', 'power-domains', 'ranges', 'wake-gpios' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8450.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.example.dtb: pcie@1c00000 (qcom,pcie-sm8450-pcie0): iommu-map:0: [0, 4294967295, 7168, 1, 256, 4294967295, 7169, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/apple,pcie.example.dtb: pcie@690000000 (apple,t8103-pcie): iommu-map:0: [256, 4294967295, 1, 1, 512, 4294967295, 1, 1, 768, 4294967295, 1, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/apple,pcie.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/apple,pcie.example.dtb: pcie@690000000 (apple,t8103-pcie): Unevaluated properties are not allowed ('#address-cells', '#size-cells', 'bus-range', 'device_type', 'pci@0,0', 'pci@1,0', 'pci@2,0' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/apple,pcie.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/apple,pcie.example.dtb: pcie@690000000 (apple,t8103-pcie): iommu-map:0: [256, 4294967295, 1, 1, 512, 4294967295, 1, 1, 768, 4294967295, 1, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.example.dtb: pcie@1c00000 (qcom,pcie-sm8550): iommu-map:0: [0, 4294967295, 5120, 1, 256, 4294967295, 5121, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8550.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.example.dtb: pcie@1c00000 (qcom,pcie-sm8550): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'dma-coherent', 'interconnect-names', 'interconnects', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'num-lanes', 'perst-gpios', 'phy-names', 'phys', 'power-domains', 'ranges', 'wake-gpios' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8550.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.example.dtb: pcie@1c00000 (qcom,pcie-sm8550): iommu-map:0: [0, 4294967295, 5120, 1, 256, 4294967295, 5121, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8250.example.dtb: pcie@1c00000 (qcom,pcie-sm8250): iommu-map:0: [0, 4294967295, 7168, 1, 256, 4294967295, 7169, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8250.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8250.example.dtb: pcie@1c00000 (qcom,pcie-sm8250): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'dma-coherent', 'interrupt-map', 'interrupt-map-mask', 'iommu-map', 'linux,pci-domain', 'num-lanes', 'perst-gpios', 'phy-names', 'phys', 'power-domains', 'ranges', 'wake-gpios' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sm8250.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sm8250.example.dtb: pcie@1c00000 (qcom,pcie-sm8250): iommu-map:0: [0, 4294967295, 7168, 1, 256, 4294967295, 7169, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.example.dtb: pci@1c00000 (qcom,pcie-sa8255p): iommu-map:0: [0, 4294967295, 0, 1, 256, 4294967295, 1, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sa8255p.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.example.dtb: pci@1c00000 (qcom,pcie-sa8255p): Unevaluated properties are not allowed ('#address-cells', '#interrupt-cells', '#size-cells', 'bus-range', 'device_type', 'interrupt-map', 'interrupt-map-mask', 'linux,pci-domain', 'pcie@0' were unexpected)
	from schema $id: http://devicetree.org/schemas/pci/qcom,pcie-sa8255p.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.example.dtb: pci@1c00000 (qcom,pcie-sa8255p): iommu-map:0: [0, 4294967295, 0, 1, 256, 4294967295, 1, 1] is too long
	from schema $id: http://devicetree.org/schemas/pci/pci-iommu.yaml

doc reference errors (make refcheckdocs):

See https://patchwork.kernel.org/project/devicetree/patch/20260217173457.18628-2-akhilrajeev@nvidia.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


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

* Re: [PATCH 2/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional
  2026-02-17 17:34 ` [PATCH 2/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
@ 2026-02-17 18:38   ` Rob Herring (Arm)
  0 siblings, 0 replies; 32+ messages in thread
From: Rob Herring (Arm) @ 2026-02-17 18:38 UTC (permalink / raw)
  To: Akhil R
  Cc: linux-tegra, thierry.reding, vkoul, p.zabel, dmaengine, conor+dt,
	devicetree, jonathanh, Frank.Li, krzk+dt, linux-kernel


On Tue, 17 Feb 2026 23:04:51 +0530, Akhil R wrote:
> In Tegra264 and Tegra234, GPCDMA reset control is not exposed to Linux
> and is handled by BPMP. In Tegra234 BPMP supported a dummy reset which
> just return success on reset without doing an actual reset. This as well
> is not supported in Tegra264 BPMP. Therefore mark 'reset' and 'reset-names'
> property as required only for devices prior to Tegra234.
> 
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  .../bindings/dma/nvidia,tegra186-gpc-dma.yaml | 19 ++++++++++++++-----
>  1 file changed, 14 insertions(+), 5 deletions(-)
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml: allOf:1: 'then' is a dependency of 'if'
	hint: Keywords must be a subset of known json-schema keywords
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml: allOf:1:if: 'if' is a dependency of 'then'
	hint: Keywords must be a subset of known json-schema keywords
	from schema $id: http://devicetree.org/meta-schemas/keywords.yaml

doc reference errors (make refcheckdocs):

See https://patchwork.kernel.org/project/devicetree/patch/20260217173457.18628-3-akhilrajeev@nvidia.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


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

* Re: [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits
  2026-02-17 17:34 ` [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits Akhil R
@ 2026-02-17 19:44   ` Frank Li
  2026-02-24  6:03     ` Akhil R
  0 siblings, 1 reply; 32+ messages in thread
From: Frank Li @ 2026-02-17 19:44 UTC (permalink / raw)
  To: Akhil R
  Cc: dmaengine, linux-tegra, devicetree, linux-kernel, vkoul, Frank.Li,
	robh, krzk+dt, conor+dt, thierry.reding, jonathanh, p.zabel

On Tue, Feb 17, 2026 at 11:04:54PM +0530, Akhil R wrote:
> Tegra264 supports address width of 41 bits and has a separate register
> to accommodate the high address. Add a device data property to specify
> the number of address bits supported on a device and use that to
> program the required registers.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  drivers/dma/tegra186-gpc-dma.c | 129 +++++++++++++++++++++------------
>  1 file changed, 82 insertions(+), 47 deletions(-)
>
> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> index 72701b543ceb..ce3b1dd52bb3 100644
> --- a/drivers/dma/tegra186-gpc-dma.c
> +++ b/drivers/dma/tegra186-gpc-dma.c
> @@ -151,6 +151,7 @@ struct tegra_dma_channel;
>   */
>  struct tegra_dma_chip_data {
>  	bool hw_support_pause;
> +	unsigned int addr_bits;
>  	unsigned int nr_channels;
>  	unsigned int channel_reg_size;
>  	unsigned int max_dma_count;
> @@ -166,6 +167,8 @@ struct tegra_dma_channel_regs {
>  	u32 src;
>  	u32 dst;
>  	u32 high_addr;
> +	u32 src_high;
> +	u32 dst_high;
>  	u32 mc_seq;
>  	u32 mmio_seq;
>  	u32 wcount;
> @@ -189,7 +192,8 @@ struct tegra_dma_sg_req {
>  	u32 csr;
>  	u32 src;
>  	u32 dst;
> -	u32 high_addr;
> +	u32 src_high;
> +	u32 dst_high;
>  	u32 mc_seq;
>  	u32 mmio_seq;
>  	u32 wcount;
> @@ -273,6 +277,41 @@ static inline struct device *tdc2dev(struct tegra_dma_channel *tdc)
>  	return tdc->vc.chan.device->dev;
>  }
>
> +static void tegra_dma_program_addr(struct tegra_dma_channel *tdc,
> +				   struct tegra_dma_sg_req *sg_req)
> +{
> +	tdc_write(tdc, tdc->regs->src, sg_req->src);
> +	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
> +
> +	if (tdc->tdma->chip_data->addr_bits > 40) {
> +		tdc_write(tdc, tdc->regs->src_high,
> +			  sg_req->src_high);
> +		tdc_write(tdc, tdc->regs->dst_high,
> +			  sg_req->dst_high);
> +	} else {
> +		tdc_write(tdc, tdc->regs->high_addr,
> +			  sg_req->src_high | sg_req->dst_high);
> +	}
> +}
> +
> +static void tegra_dma_configure_addr(struct tegra_dma_channel *tdc,
> +				     struct tegra_dma_sg_req *sg_req,
> +				phys_addr_t src, phys_addr_t dst)
> +{
> +	sg_req->src = lower_32_bits(src);
> +	sg_req->dst = lower_32_bits(dst);

I suggest save 64bit address to sq_req.  In tegra_dma_program_addr() to
handle difference between 40bit and 41bit.

So only need handle difference at one place.

Frank
> +
> +	if (tdc->tdma->chip_data->addr_bits > 40) {
> +		sg_req->src_high = upper_32_bits(src);
> +		sg_req->dst_high = upper_32_bits(dst);
> +	} else {
> +		sg_req->src_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR,
> +					      upper_32_bits(src));
> +		sg_req->dst_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR,
> +					      upper_32_bits(dst));
> +	}
> +}
> +
>  static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
>  {
>  	dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n",
> @@ -282,11 +321,22 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
>  		tdc_read(tdc, tdc->regs->status),
>  		tdc_read(tdc, tdc->regs->csre)
>  	);
> -	dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
> -		tdc_read(tdc, tdc->regs->src),
> -		tdc_read(tdc, tdc->regs->dst),
> -		tdc_read(tdc, tdc->regs->high_addr)
> -	);
> +
> +	if (tdc->tdma->chip_data->addr_bits > 40) {
> +		dev_dbg(tdc2dev(tdc), "SRC %x SRC HI %x DST %x DST HI %x\n",
> +			tdc_read(tdc, tdc->regs->src),
> +			tdc_read(tdc, tdc->regs->src_high),
> +			tdc_read(tdc, tdc->regs->dst),
> +			tdc_read(tdc, tdc->regs->dst_high)
> +		);
> +	} else {
> +		dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
> +			tdc_read(tdc, tdc->regs->src),
> +			tdc_read(tdc, tdc->regs->dst),
> +			tdc_read(tdc, tdc->regs->high_addr)
> +		);
> +	}
> +
>  	dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x WSTA %x\n",
>  		tdc_read(tdc, tdc->regs->mc_seq),
>  		tdc_read(tdc, tdc->regs->mmio_seq),
> @@ -490,9 +540,7 @@ static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
>  	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
>
>  	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
> -	tdc_write(tdc, tdc->regs->src, sg_req->src);
> -	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
> -	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
> +	tegra_dma_program_addr(tdc, sg_req);
>
>  	/* Start DMA */
>  	tdc_write(tdc, tdc->regs->csr,
> @@ -520,11 +568,9 @@ static void tegra_dma_start(struct tegra_dma_channel *tdc)
>
>  	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
>
> +	tegra_dma_program_addr(tdc, sg_req);
>  	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
>  	tdc_write(tdc, tdc->regs->csr, 0);
> -	tdc_write(tdc, tdc->regs->src, sg_req->src);
> -	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
> -	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
>  	tdc_write(tdc, tdc->regs->fixed_pattern, sg_req->fixed_pattern);
>  	tdc_write(tdc, tdc->regs->mmio_seq, sg_req->mmio_seq);
>  	tdc_write(tdc, tdc->regs->mc_seq, sg_req->mc_seq);
> @@ -829,7 +875,7 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc,
>
>  static int get_transfer_param(struct tegra_dma_channel *tdc,
>  			      enum dma_transfer_direction direction,
> -			      u32 *apb_addr,
> +			      dma_addr_t *apb_addr,
>  			      u32 *mmio_seq,
>  			      u32 *csr,
>  			      unsigned int *burst_size,
> @@ -908,10 +954,7 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
>  	dma_desc->sg_count = 1;
>  	sg_req = dma_desc->sg_req;
>
> -	sg_req[0].src = 0;
> -	sg_req[0].dst = dest;
> -	sg_req[0].high_addr =
> -			FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
> +	tegra_dma_configure_addr(tdc, &sg_req[0], 0, dest);
>  	sg_req[0].fixed_pattern = value;
>  	/* Word count reg takes value as (N +1) words */
>  	sg_req[0].wcount = ((len - 4) >> 2);
> @@ -977,12 +1020,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
>  	dma_desc->sg_count = 1;
>  	sg_req = dma_desc->sg_req;
>
> -	sg_req[0].src = src;
> -	sg_req[0].dst = dest;
> -	sg_req[0].high_addr =
> -		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (src >> 32));
> -	sg_req[0].high_addr |=
> -		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
> +	tegra_dma_configure_addr(tdc, &sg_req[0], src, dest);
>  	/* Word count reg takes value as (N +1) words */
>  	sg_req[0].wcount = ((len - 4) >> 2);
>  	sg_req[0].csr = csr;
> @@ -1002,7 +1040,8 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
>  	struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
>  	unsigned int max_dma_count = tdc->tdma->chip_data->max_dma_count;
>  	enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED;
> -	u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0;
> +	u32 csr, mc_seq, mmio_seq = 0;
> +	dma_addr_t apb_ptr = 0;
>  	struct tegra_dma_sg_req *sg_req;
>  	struct tegra_dma_desc *dma_desc;
>  	struct scatterlist *sg;
> @@ -1087,17 +1126,10 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
>  		mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
>  		dma_desc->bytes_req += len;
>
> -		if (direction == DMA_MEM_TO_DEV) {
> -			sg_req[i].src = mem;
> -			sg_req[i].dst = apb_ptr;
> -			sg_req[i].high_addr =
> -				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
> -		} else if (direction == DMA_DEV_TO_MEM) {
> -			sg_req[i].src = apb_ptr;
> -			sg_req[i].dst = mem;
> -			sg_req[i].high_addr =
> -				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
> -		}
> +		if (direction == DMA_MEM_TO_DEV)
> +			tegra_dma_configure_addr(tdc, &sg_req[i], mem, apb_ptr);
> +		else if (direction == DMA_DEV_TO_MEM)
> +			tegra_dma_configure_addr(tdc, &sg_req[i], apb_ptr, mem);
>
>  		/*
>  		 * Word count register takes input in words. Writing a value
> @@ -1120,7 +1152,8 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
>  			  unsigned long flags)
>  {
>  	enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED;
> -	u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0, burst_size;
> +	u32 csr, mc_seq, mmio_seq = 0, burst_size;
> +	dma_addr_t apb_ptr = 0;
>  	unsigned int max_dma_count, len, period_count, i;
>  	struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
>  	struct tegra_dma_desc *dma_desc;
> @@ -1209,17 +1242,10 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
>  	/* Split transfer equal to period size */
>  	for (i = 0; i < period_count; i++) {
>  		mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
> -		if (direction == DMA_MEM_TO_DEV) {
> -			sg_req[i].src = mem;
> -			sg_req[i].dst = apb_ptr;
> -			sg_req[i].high_addr =
> -				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
> -		} else if (direction == DMA_DEV_TO_MEM) {
> -			sg_req[i].src = apb_ptr;
> -			sg_req[i].dst = mem;
> -			sg_req[i].high_addr =
> -				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
> -		}
> +		if (direction == DMA_MEM_TO_DEV)
> +			tegra_dma_configure_addr(tdc, &sg_req[i], mem, apb_ptr);
> +		else if (direction == DMA_DEV_TO_MEM)
> +			tegra_dma_configure_addr(tdc, &sg_req[i], apb_ptr, mem);
>  		/*
>  		 * Word count register takes input in words. Writing a value
>  		 * of N into word count register means a req of (N+1) words.
> @@ -1317,6 +1343,7 @@ static const struct tegra_dma_channel_regs tegra186_reg_offsets = {
>
>  static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
>  	.nr_channels = 32,
> +	.addr_bits = 40,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = false,
> @@ -1326,6 +1353,7 @@ static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
>
>  static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
>  	.nr_channels = 32,
> +	.addr_bits = 40,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = true,
> @@ -1335,6 +1363,7 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
>
>  static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
>  	.nr_channels = 32,
> +	.addr_bits = 41,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = true,
> @@ -1446,6 +1475,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
>  		tdc->stream_id = stream_id;
>  	}
>
> +	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to set DMA mask: %d\n", ret);
> +		return ret;
> +	}
> +
>  	dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask);
>  	dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask);
>  	dma_cap_set(DMA_MEMCPY, tdma->dma_dev.cap_mask);
> --
> 2.50.1
>

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

* Re: [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
  2026-02-17 17:34 ` [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID Akhil R
@ 2026-02-17 19:52   ` Frank Li
  2026-02-24  6:25     ` Akhil R
  2026-02-19  8:28   ` Dan Carpenter
  1 sibling, 1 reply; 32+ messages in thread
From: Frank Li @ 2026-02-17 19:52 UTC (permalink / raw)
  To: Akhil R
  Cc: dmaengine, linux-tegra, devicetree, linux-kernel, vkoul, Frank.Li,
	robh, krzk+dt, conor+dt, thierry.reding, jonathanh, p.zabel

On Tue, Feb 17, 2026 at 11:04:55PM +0530, Akhil R wrote:
> Use iommu-map, when provided, to get the stream ID to be programmed
> for each channel. Register each channel separately for allowing it
> to use a separate IOMMU domain for the transfer.
>
> Channels will continue to use the same global stream ID if iommu-map
> property is not present in the device tree.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  drivers/dma/tegra186-gpc-dma.c | 62 +++++++++++++++++++++++++++-------
>  1 file changed, 49 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> index ce3b1dd52bb3..b8ca269fa3ba 100644
> --- a/drivers/dma/tegra186-gpc-dma.c
> +++ b/drivers/dma/tegra186-gpc-dma.c
> @@ -15,6 +15,7 @@
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_dma.h>
> +#include <linux/of_device.h>
>  #include <linux/platform_device.h>
>  #include <linux/reset.h>
>  #include <linux/slab.h>
> @@ -1403,9 +1404,12 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
>  static int tegra_dma_probe(struct platform_device *pdev)
>  {
>  	const struct tegra_dma_chip_data *cdata = NULL;
> +	struct tegra_dma_channel *tdc;
> +	struct tegra_dma *tdma;
> +	struct dma_chan *chan;
> +	bool use_iommu_map = false;
>  	unsigned int i;
>  	u32 stream_id;
> -	struct tegra_dma *tdma;
>  	int ret;
>
>  	cdata = of_device_get_match_data(&pdev->dev);
> @@ -1433,9 +1437,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
>
>  	tdma->dma_dev.dev = &pdev->dev;
>
> -	if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
> -		dev_err(&pdev->dev, "Missing iommu stream-id\n");
> -		return -EINVAL;
> +	use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map");
> +	if (!use_iommu_map) {
> +		if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
> +			dev_err(&pdev->dev, "Missing iommu stream-id\n");
> +			return -EINVAL;
> +		}
>  	}
>
>  	ret = device_property_read_u32(&pdev->dev, "dma-channel-mask",
> @@ -1449,7 +1456,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
>
>  	INIT_LIST_HEAD(&tdma->dma_dev.channels);
>  	for (i = 0; i < cdata->nr_channels; i++) {
> -		struct tegra_dma_channel *tdc = &tdma->channels[i];
> +		tdc = &tdma->channels[i];
>
>  		/* Check for channel mask */
>  		if (!(tdma->chan_mask & BIT(i)))
> @@ -1469,10 +1476,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
>
>  		vchan_init(&tdc->vc, &tdma->dma_dev);
>  		tdc->vc.desc_free = tegra_dma_desc_free;
> -
> -		/* program stream-id for this channel */
> -		tegra_dma_program_sid(tdc, stream_id);
> -		tdc->stream_id = stream_id;
>  	}
>
>  	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
> @@ -1517,20 +1520,53 @@ static int tegra_dma_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>
> +	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
> +		struct device *chdev = &chan->dev->device;

why no use
	for (i = 0; i < cdata->nr_channels; i++) {
		struct tegra_dma_channel *tdc = &tdma->channels[i];
> +
> +		tdc = to_tegra_dma_chan(chan);
> +		if (use_iommu_map) {
> +			chdev->coherent_dma_mask = pdev->dev.coherent_dma_mask;
> +			chdev->dma_mask = &chdev->coherent_dma_mask;
> +			chdev->bus = pdev->dev.bus;
> +
> +			ret = of_dma_configure_id(chdev, pdev->dev.of_node,
> +						  true, &tdc->id);
> +			if (ret) {
> +				dev_err(chdev, "Failed to configure IOMMU for channel %d: %d\n",
> +					tdc->id, ret);
> +				goto err_unregister;
> +			}
> +
> +			if (!tegra_dev_iommu_get_stream_id(chdev, &stream_id)) {
> +				dev_err(chdev, "Failed to get stream ID for channel %d\n",
> +					tdc->id);
> +				goto err_unregister;
> +			}
> +
> +			chan->dev->chan_dma_dev = true;
> +		}
> +
> +		/* program stream-id for this channel */
> +		tegra_dma_program_sid(tdc, stream_id);
> +		tdc->stream_id = stream_id;
> +	}
> +
>  	ret = of_dma_controller_register(pdev->dev.of_node,
>  					 tegra_dma_of_xlate, tdma);
>  	if (ret < 0) {
>  		dev_err_probe(&pdev->dev, ret,
>  			      "GPC DMA OF registration failed\n");
> -
> -		dma_async_device_unregister(&tdma->dma_dev);
> -		return ret;
> +		goto err_unregister;
>  	}
>
> -	dev_info(&pdev->dev, "GPC DMA driver register %lu channels\n",
> +	dev_info(&pdev->dev, "GPC DMA driver registered %lu channels\n",
>  		 hweight_long(tdma->chan_mask));
>
>  	return 0;
> +
> +err_unregister:
> +	dma_async_device_unregister(&tdma->dma_dev);

Can you use dmaenginem_async_device_register() to simple error path?

Frank
> +	return ret;
>  }
>
>  static void tegra_dma_remove(struct platform_device *pdev)
> --
> 2.50.1
>

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

* Re: [PATCH 7/8] dmaengine: tegra: Add Tegra264 support
  2026-02-17 17:34 ` [PATCH 7/8] dmaengine: tegra: Add Tegra264 support Akhil R
@ 2026-02-17 19:53   ` Frank Li
  0 siblings, 0 replies; 32+ messages in thread
From: Frank Li @ 2026-02-17 19:53 UTC (permalink / raw)
  To: Akhil R
  Cc: dmaengine, linux-tegra, devicetree, linux-kernel, vkoul, Frank.Li,
	robh, krzk+dt, conor+dt, thierry.reding, jonathanh, p.zabel

On Tue, Feb 17, 2026 at 11:04:56PM +0530, Akhil R wrote:
> Update compatible and chip data to support GPCDMA in Tegra264.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/dma/tegra186-gpc-dma.c | 32 ++++++++++++++++++++++++++++++++
>  1 file changed, 32 insertions(+)
>
> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> index b8ca269fa3ba..11347c9f3215 100644
> --- a/drivers/dma/tegra186-gpc-dma.c
> +++ b/drivers/dma/tegra186-gpc-dma.c
> @@ -1342,6 +1342,25 @@ static const struct tegra_dma_channel_regs tegra186_reg_offsets = {
>  	.spare = 0x40,
>  };
>
> +static const struct tegra_dma_channel_regs tegra264_reg_offsets = {
> +	.csr = 0x0,
> +	.status = 0x4,
> +	.csre = 0x8,
> +	.src = 0xc,
> +	.dst = 0x10,
> +	.src_high = 0x14,
> +	.dst_high = 0x18,
> +	.mc_seq = 0x1c,
> +	.mmio_seq = 0x20,
> +	.wcount = 0x24,
> +	.wxfer = 0x28,
> +	.wstatus = 0x2c,
> +	.err_status = 0x34,
> +	.fixed_pattern = 0x38,
> +	.tz = 0x3c,
> +	.spare = 0x44,
> +};
> +
>  static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
>  	.nr_channels = 32,
>  	.addr_bits = 40,
> @@ -1372,6 +1391,16 @@ static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
>  	.terminate = tegra_dma_pause_noerr,
>  };
>
> +static const struct tegra_dma_chip_data tegra264_dma_chip_data = {
> +	.nr_channels = 32,
> +	.addr_bits = 48,
> +	.channel_reg_size = SZ_64K,
> +	.max_dma_count = SZ_1G,
> +	.hw_support_pause = true,
> +	.channel_regs = &tegra264_reg_offsets,
> +	.terminate = tegra_dma_pause_noerr,
> +};
> +
>  static const struct of_device_id tegra_dma_of_match[] = {
>  	{
>  		.compatible = "nvidia,tegra186-gpcdma",
> @@ -1382,6 +1411,9 @@ static const struct of_device_id tegra_dma_of_match[] = {
>  	}, {
>  		.compatible = "nvidia,tegra234-gpcdma",
>  		.data = &tegra234_dma_chip_data,
> +	}, {
> +		.compatible = "nvidia,tegra264-gpcdma",
> +		.data = &tegra264_dma_chip_data,
>  	}, {
>  	},
>  };
> --
> 2.50.1
>

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

* Re: [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-02-17 17:34 ` [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
  2026-02-17 18:38   ` Rob Herring (Arm)
@ 2026-02-17 19:53   ` Krzysztof Kozlowski
  2026-02-18  9:59     ` Jon Hunter
  1 sibling, 1 reply; 32+ messages in thread
From: Krzysztof Kozlowski @ 2026-02-17 19:53 UTC (permalink / raw)
  To: Akhil R, dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, jonathanh, p.zabel

On 17/02/2026 18:34, Akhil R wrote:
> Add iommu-map property which helps when each channel requires its own
> stream ID for the transfer. Use iommu-map to specify separate stream
> ID for each channel. This enables each channel to be in its own iommu
> domain and keeps the memory isolated from other devices sharing the
> same DMA controller.
> 
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  .../devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml  | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> index 0dabe9bbb219..542e9cb9f641 100644
> --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> @@ -14,6 +14,7 @@ description: |
>  maintainers:
>    - Jon Hunter <jonathanh@nvidia.com>
>    - Rajesh Gumasta <rgumasta@nvidia.com>
> +  - Akhil R <akhilrajeev@nvidia.com>

With 4.5 trillion USD capitalization of Nvidia one could assume they can
spare few resources to test the patch before sending it... instead of
relying on Rob's and my machines to do that for them.

Expect grumpy review because you do not care about our time.

>  
>  allOf:
>    - $ref: dma-controller.yaml#
> @@ -51,6 +52,13 @@ properties:
>    iommus:
>      maxItems: 1
>  
> +  iommu-map:
> +    description: |
> +      The mapping of DMA controller channels to IOMMU stream IDs. Each entry in the map specifies the

Please read Linux coding style.

> +      relationship between a DMA channel and its corresponding IOMMU stream ID. The format is:
> +      "<ch_no &smmu stream_id length>". Example: "<1 &smmu 0x801 1>"

Missing constraints.

Anyway, do not redefine or explain standard properties. See dtschema.

> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +
>    dma-coherent: true
>  
>    dma-channel-mask:


Best regards,
Krzysztof

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

* Re: [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-02-17 19:53   ` Krzysztof Kozlowski
@ 2026-02-18  9:59     ` Jon Hunter
  2026-02-18 15:49       ` Rob Herring
  0 siblings, 1 reply; 32+ messages in thread
From: Jon Hunter @ 2026-02-18  9:59 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Akhil R, dmaengine, linux-tegra
  Cc: devicetree, linux-kernel, vkoul, Frank.Li, robh, krzk+dt,
	conor+dt, thierry.reding, p.zabel


On 17/02/2026 19:53, Krzysztof Kozlowski wrote:
> On 17/02/2026 18:34, Akhil R wrote:
>> Add iommu-map property which helps when each channel requires its own
>> stream ID for the transfer. Use iommu-map to specify separate stream
>> ID for each channel. This enables each channel to be in its own iommu
>> domain and keeps the memory isolated from other devices sharing the
>> same DMA controller.
>>
>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>> ---
>>   .../devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml  | 8 ++++++++
>>   1 file changed, 8 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>> index 0dabe9bbb219..542e9cb9f641 100644
>> --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>> +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>> @@ -14,6 +14,7 @@ description: |
>>   maintainers:
>>     - Jon Hunter <jonathanh@nvidia.com>
>>     - Rajesh Gumasta <rgumasta@nvidia.com>
>> +  - Akhil R <akhilrajeev@nvidia.com>
> 
> With 4.5 trillion USD capitalization of Nvidia one could assume they can
> spare few resources to test the patch before sending it... instead of
> relying on Rob's and my machines to do that for them.
> 
> Expect grumpy review because you do not care about our time.


ACK! We need to do a better job here. I will work with Akhil to improve 
this.

Jon

-- 
nvpublic


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

* Re: [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-02-18  9:59     ` Jon Hunter
@ 2026-02-18 15:49       ` Rob Herring
  2026-02-24  6:41         ` Akhil R
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Herring @ 2026-02-18 15:49 UTC (permalink / raw)
  To: Jon Hunter
  Cc: Krzysztof Kozlowski, Akhil R, dmaengine, linux-tegra, devicetree,
	linux-kernel, vkoul, Frank.Li, krzk+dt, conor+dt, thierry.reding,
	p.zabel

On Wed, Feb 18, 2026 at 3:59 AM Jon Hunter <jonathanh@nvidia.com> wrote:
>
>
> On 17/02/2026 19:53, Krzysztof Kozlowski wrote:
> > On 17/02/2026 18:34, Akhil R wrote:
> >> Add iommu-map property which helps when each channel requires its own
> >> stream ID for the transfer. Use iommu-map to specify separate stream
> >> ID for each channel. This enables each channel to be in its own iommu
> >> domain and keeps the memory isolated from other devices sharing the
> >> same DMA controller.
> >>
> >> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> >> ---
> >>   .../devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml  | 8 ++++++++
> >>   1 file changed, 8 insertions(+)
> >>
> >> diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> >> index 0dabe9bbb219..542e9cb9f641 100644
> >> --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> >> +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> >> @@ -14,6 +14,7 @@ description: |
> >>   maintainers:
> >>     - Jon Hunter <jonathanh@nvidia.com>
> >>     - Rajesh Gumasta <rgumasta@nvidia.com>
> >> +  - Akhil R <akhilrajeev@nvidia.com>
> >
> > With 4.5 trillion USD capitalization of Nvidia one could assume they can
> > spare few resources to test the patch before sending it... instead of
> > relying on Rob's and my machines to do that for them.
> >
> > Expect grumpy review because you do not care about our time.
>
>
> ACK! We need to do a better job here. I will work with Akhil to improve
> this.

Anyone that wants to run a gitlab-runner (and docker) on one of their
machines to add to the test capacity would be more than welcomed. It's
pretty trivial to set up. The only requirement is something faster
than gitlab shared runners. My 2 machines are M1 and M3 MBPs.

Rob

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

* Re: [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
  2026-02-17 17:34 ` [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID Akhil R
  2026-02-17 19:52   ` Frank Li
@ 2026-02-19  8:28   ` Dan Carpenter
  1 sibling, 0 replies; 32+ messages in thread
From: Dan Carpenter @ 2026-02-19  8:28 UTC (permalink / raw)
  To: oe-kbuild, Akhil R, dmaengine, linux-tegra
  Cc: lkp, oe-kbuild-all, devicetree, linux-kernel, vkoul, Frank.Li,
	robh, krzk+dt, conor+dt, thierry.reding, jonathanh, p.zabel,
	Akhil R

Hi Akhil,

kernel test robot noticed the following build warnings:

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Akhil-R/dt-bindings-dma-nvidia-tegra186-gpc-dma-Add-iommu-map-property/20260218-014114
base:   https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git next
patch link:    https://lore.kernel.org/r/20260217173457.18628-7-akhilrajeev%40nvidia.com
patch subject: [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
config: sparc64-randconfig-r072-20260218 (https://download.01.org/0day-ci/archive/20260218/202602181757.Amx49qCP-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 10.5.0
smatch version: v0.5.0-8994-gd50c5a4c

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202602181757.Amx49qCP-lkp@intel.com/

smatch warnings:
drivers/dma/tegra186-gpc-dma.c:1543 tegra_dma_probe() warn: missing error code 'ret'

vim +/ret +1543 drivers/dma/tegra186-gpc-dma.c

ee17028009d49f Akhil R         2022-02-25  1514  	tdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
ee17028009d49f Akhil R         2022-02-25  1515  
ee17028009d49f Akhil R         2022-02-25  1516  	ret = dma_async_device_register(&tdma->dma_dev);
ee17028009d49f Akhil R         2022-02-25  1517  	if (ret < 0) {
ee17028009d49f Akhil R         2022-02-25  1518  		dev_err_probe(&pdev->dev, ret,
ee17028009d49f Akhil R         2022-02-25  1519  			      "GPC DMA driver registration failed\n");
ee17028009d49f Akhil R         2022-02-25  1520  		return ret;
ee17028009d49f Akhil R         2022-02-25  1521  	}
ee17028009d49f Akhil R         2022-02-25  1522  
43f59d3fa0deca Akhil R         2026-02-17  1523  	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
43f59d3fa0deca Akhil R         2026-02-17  1524  		struct device *chdev = &chan->dev->device;
43f59d3fa0deca Akhil R         2026-02-17  1525  
43f59d3fa0deca Akhil R         2026-02-17  1526  		tdc = to_tegra_dma_chan(chan);
43f59d3fa0deca Akhil R         2026-02-17  1527  		if (use_iommu_map) {
43f59d3fa0deca Akhil R         2026-02-17  1528  			chdev->coherent_dma_mask = pdev->dev.coherent_dma_mask;
43f59d3fa0deca Akhil R         2026-02-17  1529  			chdev->dma_mask = &chdev->coherent_dma_mask;
43f59d3fa0deca Akhil R         2026-02-17  1530  			chdev->bus = pdev->dev.bus;
43f59d3fa0deca Akhil R         2026-02-17  1531  
43f59d3fa0deca Akhil R         2026-02-17  1532  			ret = of_dma_configure_id(chdev, pdev->dev.of_node,
43f59d3fa0deca Akhil R         2026-02-17  1533  						  true, &tdc->id);
43f59d3fa0deca Akhil R         2026-02-17  1534  			if (ret) {
43f59d3fa0deca Akhil R         2026-02-17  1535  				dev_err(chdev, "Failed to configure IOMMU for channel %d: %d\n",
43f59d3fa0deca Akhil R         2026-02-17  1536  					tdc->id, ret);
43f59d3fa0deca Akhil R         2026-02-17  1537  				goto err_unregister;
43f59d3fa0deca Akhil R         2026-02-17  1538  			}
43f59d3fa0deca Akhil R         2026-02-17  1539  
43f59d3fa0deca Akhil R         2026-02-17  1540  			if (!tegra_dev_iommu_get_stream_id(chdev, &stream_id)) {
43f59d3fa0deca Akhil R         2026-02-17  1541  				dev_err(chdev, "Failed to get stream ID for channel %d\n",
43f59d3fa0deca Akhil R         2026-02-17  1542  					tdc->id);
43f59d3fa0deca Akhil R         2026-02-17 @1543  				goto err_unregister;

ret = -EINVAL;

43f59d3fa0deca Akhil R         2026-02-17  1544  			}
43f59d3fa0deca Akhil R         2026-02-17  1545  
43f59d3fa0deca Akhil R         2026-02-17  1546  			chan->dev->chan_dma_dev = true;
43f59d3fa0deca Akhil R         2026-02-17  1547  		}
43f59d3fa0deca Akhil R         2026-02-17  1548  
43f59d3fa0deca Akhil R         2026-02-17  1549  		/* program stream-id for this channel */
43f59d3fa0deca Akhil R         2026-02-17  1550  		tegra_dma_program_sid(tdc, stream_id);
43f59d3fa0deca Akhil R         2026-02-17  1551  		tdc->stream_id = stream_id;
43f59d3fa0deca Akhil R         2026-02-17  1552  	}
43f59d3fa0deca Akhil R         2026-02-17  1553  
ee17028009d49f Akhil R         2022-02-25  1554  	ret = of_dma_controller_register(pdev->dev.of_node,
ee17028009d49f Akhil R         2022-02-25  1555  					 tegra_dma_of_xlate, tdma);
ee17028009d49f Akhil R         2022-02-25  1556  	if (ret < 0) {
ee17028009d49f Akhil R         2022-02-25  1557  		dev_err_probe(&pdev->dev, ret,
ee17028009d49f Akhil R         2022-02-25  1558  			      "GPC DMA OF registration failed\n");
43f59d3fa0deca Akhil R         2026-02-17  1559  		goto err_unregister;
ee17028009d49f Akhil R         2022-02-25  1560  	}
ee17028009d49f Akhil R         2022-02-25  1561  
43f59d3fa0deca Akhil R         2026-02-17  1562  	dev_info(&pdev->dev, "GPC DMA driver registered %lu channels\n",
3a0c95b61385f5 Akhil R         2022-11-10  1563  		 hweight_long(tdma->chan_mask));
ee17028009d49f Akhil R         2022-02-25  1564  
ee17028009d49f Akhil R         2022-02-25  1565  	return 0;
43f59d3fa0deca Akhil R         2026-02-17  1566  
43f59d3fa0deca Akhil R         2026-02-17  1567  err_unregister:
43f59d3fa0deca Akhil R         2026-02-17  1568  	dma_async_device_unregister(&tdma->dma_dev);
43f59d3fa0deca Akhil R         2026-02-17  1569  	return ret;
ee17028009d49f Akhil R         2022-02-25  1570  }

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


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

* Re: [PATCH 3/8] dmaengine: tegra: Make reset control optional
  2026-02-17 18:04   ` Frank Li
@ 2026-02-24  5:39     ` Akhil R
  2026-02-24 17:02       ` Jon Hunter
  0 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-24  5:39 UTC (permalink / raw)
  To: frank.li
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, jonathanh,
	krzk+dt, linux-kernel, linux-tegra, p.zabel, robh, thierry.reding,
	vkoul

Hi Frank,

On Tue, 17 Feb 2026 13:04:57 -0500, Frank Li wrote:
> On Tue, Feb 17, 2026 at 11:04:52PM +0530, Akhil R wrote:
>> Tegra264 BPMP restricts access to GPCDMA reset control and the reset
> 
> what's means of BPMP?

BPMP is Boot and Power Management Processor which is a co-processor
in Tegra and runs a dedicated firmware. It manages the boot, clock,
reset etc. I will put the expansion in the commit message in the next
version. Do you suggest adding more details?

There is a documentation for this in Linux -
https://www.kernel.org/doc/Documentation/devicetree/bindings/firmware/nvidia%2Ctegra186-bpmp.txt

> 
> Frank
>> is expected to be deasserted on boot by BPMP. Hence Make the reset
>> control optional in the driver.
>>
>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>> ---
>>  drivers/dma/tegra186-gpc-dma.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
>> index 4d6fe0efa76e..236a298c26a1 100644
>> --- a/drivers/dma/tegra186-gpc-dma.c
>> +++ b/drivers/dma/tegra186-gpc-dma.c
>> @@ -1382,7 +1382,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>  	if (IS_ERR(tdma->base_addr))
>>  		return PTR_ERR(tdma->base_addr);
>>
>> -	tdma->rst = devm_reset_control_get_exclusive(&pdev->dev, "gpcdma");
>> +	tdma->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, "gpcdma");
>>  	if (IS_ERR(tdma->rst)) {
>>  		return dev_err_probe(&pdev->dev, PTR_ERR(tdma->rst),
>>  			      "Missing controller reset\n");

Thanks for the review.

Regards,
Akhil

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

* Re: [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits
  2026-02-17 19:44   ` Frank Li
@ 2026-02-24  6:03     ` Akhil R
  0 siblings, 0 replies; 32+ messages in thread
From: Akhil R @ 2026-02-24  6:03 UTC (permalink / raw)
  To: frank.li
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, jonathanh,
	krzk+dt, linux-kernel, linux-tegra, p.zabel, robh, thierry.reding,
	vkoul

On Tue, 17 Feb 2026 14:44:28 -0500 Frank Li wrote:
> On Tue, Feb 17, 2026 at 11:04:54PM +0530, Akhil R wrote:
>> Tegra264 supports address width of 41 bits and has a separate register
>> to accommodate the high address. Add a device data property to specify
>> the number of address bits supported on a device and use that to
>> program the required registers.
>>
>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>> ---
>>  drivers/dma/tegra186-gpc-dma.c | 129 +++++++++++++++++++++------------
>>  1 file changed, 82 insertions(+), 47 deletions(-)
>>
>> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
>> index 72701b543ceb..ce3b1dd52bb3 100644
>> --- a/drivers/dma/tegra186-gpc-dma.c
>> +++ b/drivers/dma/tegra186-gpc-dma.c
>> @@ -151,6 +151,7 @@ struct tegra_dma_channel;
>>   */
>>  struct tegra_dma_chip_data {
>>  	bool hw_support_pause;
>> +	unsigned int addr_bits;
>>  	unsigned int nr_channels;
>>  	unsigned int channel_reg_size;
>>  	unsigned int max_dma_count;
>> @@ -166,6 +167,8 @@ struct tegra_dma_channel_regs {
>>  	u32 src;
>>  	u32 dst;
>>  	u32 high_addr;
>> +	u32 src_high;
>> +	u32 dst_high;
>>  	u32 mc_seq;
>>  	u32 mmio_seq;
>>  	u32 wcount;
>> @@ -189,7 +192,8 @@ struct tegra_dma_sg_req {
>>  	u32 csr;
>>  	u32 src;
>>  	u32 dst;
>> -	u32 high_addr;
>> +	u32 src_high;
>> +	u32 dst_high;
>>  	u32 mc_seq;
>>  	u32 mmio_seq;
>>  	u32 wcount;
>> @@ -273,6 +277,41 @@ static inline struct device *tdc2dev(struct tegra_dma_channel *tdc)
>>  	return tdc->vc.chan.device->dev;
>>  }
>>
>> +static void tegra_dma_program_addr(struct tegra_dma_channel *tdc,
>> +				   struct tegra_dma_sg_req *sg_req)
>> +{
>> +	tdc_write(tdc, tdc->regs->src, sg_req->src);
>> +	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
>> +
>> +	if (tdc->tdma->chip_data->addr_bits > 40) {
>> +		tdc_write(tdc, tdc->regs->src_high,
>> +			  sg_req->src_high);
>> +		tdc_write(tdc, tdc->regs->dst_high,
>> +			  sg_req->dst_high);
>> +	} else {
>> +		tdc_write(tdc, tdc->regs->high_addr,
>> +			  sg_req->src_high | sg_req->dst_high);
>> +	}
>> +}
>> +
>> +static void tegra_dma_configure_addr(struct tegra_dma_channel *tdc,
>> +				     struct tegra_dma_sg_req *sg_req,
>> +				phys_addr_t src, phys_addr_t dst)
>> +{
>> +	sg_req->src = lower_32_bits(src);
>> +	sg_req->dst = lower_32_bits(dst);
> 
> I suggest save 64bit address to sq_req.  In tegra_dma_program_addr() to
> handle difference between 40bit and 41bit.
> 
> So only need handle difference at one place.

Ack. Will update. 

Regards,
Akhil

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

* Re: [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
  2026-02-17 19:52   ` Frank Li
@ 2026-02-24  6:25     ` Akhil R
  2026-02-24 21:59       ` Frank Li
  0 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-24  6:25 UTC (permalink / raw)
  To: frank.li
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, jonathanh,
	krzk+dt, linux-kernel, linux-tegra, p.zabel, robh, thierry.reding,
	vkoul

On Tue, 17 Feb 2026 14:52:17 -0500, Frank Li wrote:
> On Tue, Feb 17, 2026 at 11:04:55PM +0530, Akhil R wrote:
>> Use iommu-map, when provided, to get the stream ID to be programmed
>> for each channel. Register each channel separately for allowing it
>> to use a separate IOMMU domain for the transfer.
>>
>> Channels will continue to use the same global stream ID if iommu-map
>> property is not present in the device tree.
>>
>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>> ---
>>  drivers/dma/tegra186-gpc-dma.c | 62 +++++++++++++++++++++++++++-------
>>  1 file changed, 49 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
>> index ce3b1dd52bb3..b8ca269fa3ba 100644
>> --- a/drivers/dma/tegra186-gpc-dma.c
>> +++ b/drivers/dma/tegra186-gpc-dma.c
>> @@ -15,6 +15,7 @@
>>  #include <linux/module.h>
>>  #include <linux/of.h>
>>  #include <linux/of_dma.h>
>> +#include <linux/of_device.h>
>>  #include <linux/platform_device.h>
>>  #include <linux/reset.h>
>>  #include <linux/slab.h>
>> @@ -1403,9 +1404,12 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
>>  static int tegra_dma_probe(struct platform_device *pdev)
>>  {
>>  	const struct tegra_dma_chip_data *cdata = NULL;
>> +	struct tegra_dma_channel *tdc;
>> +	struct tegra_dma *tdma;
>> +	struct dma_chan *chan;
>> +	bool use_iommu_map = false;
>>  	unsigned int i;
>>  	u32 stream_id;
>> -	struct tegra_dma *tdma;
>>  	int ret;
>>
>>  	cdata = of_device_get_match_data(&pdev->dev);
>> @@ -1433,9 +1437,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>
>>  	tdma->dma_dev.dev = &pdev->dev;
>>
>> -	if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
>> -		dev_err(&pdev->dev, "Missing iommu stream-id\n");
>> -		return -EINVAL;
>> +	use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map");
>> +	if (!use_iommu_map) {
>> +		if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
>> +			dev_err(&pdev->dev, "Missing iommu stream-id\n");
>> +			return -EINVAL;
>> +		}
>>  	}
>>
>>  	ret = device_property_read_u32(&pdev->dev, "dma-channel-mask",
>> @@ -1449,7 +1456,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>
>>  	INIT_LIST_HEAD(&tdma->dma_dev.channels);
>>  	for (i = 0; i < cdata->nr_channels; i++) {
>> -		struct tegra_dma_channel *tdc = &tdma->channels[i];
>> +		tdc = &tdma->channels[i];
>>
>>  		/* Check for channel mask */
>>  		if (!(tdma->chan_mask & BIT(i)))
>> @@ -1469,10 +1476,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>
>>  		vchan_init(&tdc->vc, &tdma->dma_dev);
>>  		tdc->vc.desc_free = tegra_dma_desc_free;
>> -
>> -		/* program stream-id for this channel */
>> -		tegra_dma_program_sid(tdc, stream_id);
>> -		tdc->stream_id = stream_id;
>>  	}
>>
>>  	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
>> @@ -1517,20 +1520,53 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>  		return ret;
>>  	}
>>
>> +	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
>> +		struct device *chdev = &chan->dev->device;
>>
> why no use
> 	for (i = 0; i < cdata->nr_channels; i++) {
> 		struct tegra_dma_channel *tdc = &tdma->channels[i];

I thought this would ensure that we try to configure only the channels
where the chan_dev and vchan are initialized. I understand that it is
not probable in the current implementation that we will have channels
which are uninitialized, but I felt this a better approach.
Do you see any disadvantage in using the channels list?

>> +
>> +		tdc = to_tegra_dma_chan(chan);
>> +		if (use_iommu_map) {
>> +			chdev->coherent_dma_mask = pdev->dev.coherent_dma_mask;
>> +			chdev->dma_mask = &chdev->coherent_dma_mask;
>> +			chdev->bus = pdev->dev.bus;
>> +
>> +			ret = of_dma_configure_id(chdev, pdev->dev.of_node,
>> +						  true, &tdc->id);
>> +			if (ret) {
>> +				dev_err(chdev, "Failed to configure IOMMU for channel %d: %d\n",
>> +					tdc->id, ret);
>> +				goto err_unregister;
>> +			}
>> +
>> +			if (!tegra_dev_iommu_get_stream_id(chdev, &stream_id)) {
>> +				dev_err(chdev, "Failed to get stream ID for channel %d\n",
>> +					tdc->id);
>> +				goto err_unregister;
>> +			}
>> +
>> +			chan->dev->chan_dma_dev = true;
>> +		}
>> +
>> +		/* program stream-id for this channel */
>> +		tegra_dma_program_sid(tdc, stream_id);
>> +		tdc->stream_id = stream_id;
>> +	}
>> +
>>  	ret = of_dma_controller_register(pdev->dev.of_node,
>>  					 tegra_dma_of_xlate, tdma);
>>  	if (ret < 0) {
>>  		dev_err_probe(&pdev->dev, ret,
>>  			      "GPC DMA OF registration failed\n");
>> -
>> -		dma_async_device_unregister(&tdma->dma_dev);
>> -		return ret;
>> +		goto err_unregister;
>>  	}
>>
>> -	dev_info(&pdev->dev, "GPC DMA driver register %lu channels\n",
>> +	dev_info(&pdev->dev, "GPC DMA driver registered %lu channels\n",
>>  		 hweight_long(tdma->chan_mask));
>>
>>  	return 0;
>> +
>> +err_unregister:
>> +	dma_async_device_unregister(&tdma->dma_dev);
>
> Can you use dmaenginem_async_device_register() to simple error path?
>
> Frank

Agree. I will update it to use this function instead.

>> +	return ret;
>>  }
>>
>>  static void tegra_dma_remove(struct platform_device *pdev)
>> --

Regards,
Akhil

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

* Re: [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-02-18 15:49       ` Rob Herring
@ 2026-02-24  6:41         ` Akhil R
  0 siblings, 0 replies; 32+ messages in thread
From: Akhil R @ 2026-02-24  6:41 UTC (permalink / raw)
  To: robh
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, jonathanh,
	krzk+dt, krzk, linux-kernel, linux-tegra, p.zabel, thierry.reding,
	vkoul

On Wed, 18 Feb 2026 09:49:28 -0600, Rob Herring wrote:
> On Wed, Feb 18, 2026 at 3:59 AM Jon Hunter <jonathanh@nvidia.com> wrote:
>>
>>
>> On 17/02/2026 19:53, Krzysztof Kozlowski wrote:
>>> On 17/02/2026 18:34, Akhil R wrote:
>>>> Add iommu-map property which helps when each channel requires its own
>>>> stream ID for the transfer. Use iommu-map to specify separate stream
>>>> ID for each channel. This enables each channel to be in its own iommu
>>>> domain and keeps the memory isolated from other devices sharing the
>>>> same DMA controller.
>>>>
>>>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>>>> ---
>>>>   .../devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml  | 8 ++++++++
>>>>   1 file changed, 8 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>>>> index 0dabe9bbb219..542e9cb9f641 100644
>>>> --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>>>> +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>>>> @@ -14,6 +14,7 @@ description: |
>>>>   maintainers:
>>>>     - Jon Hunter <jonathanh@nvidia.com>
>>>>     - Rajesh Gumasta <rgumasta@nvidia.com>
>>>> +  - Akhil R <akhilrajeev@nvidia.com>
>>>
>>> With 4.5 trillion USD capitalization of Nvidia one could assume they can
>>> spare few resources to test the patch before sending it... instead of
>>> relying on Rob's and my machines to do that for them.
>>>
>>> Expect grumpy review because you do not care about our time.
>>
>>
>> ACK! We need to do a better job here. I will work with Akhil to improve
>> this.
>
> Anyone that wants to run a gitlab-runner (and docker) on one of their
> machines to add to the test capacity would be more than welcomed. It's
> pretty trivial to set up. The only requirement is something faster
> than gitlab shared runners. My 2 machines are M1 and M3 MBPs.

I did not realise that the system I used had a very old version of dtschema
where these errors did not show up. I do see them now once I upgraded the
dtschema. Will fix these and test them in the next revision. 

Regards,
Akhil

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

* Re: [PATCH 8/8] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264
  2026-02-17 18:02   ` Frank Li
@ 2026-02-24  6:55     ` Akhil R
  0 siblings, 0 replies; 32+ messages in thread
From: Akhil R @ 2026-02-24  6:55 UTC (permalink / raw)
  To: frank.li
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, jonathanh,
	krzk+dt, linux-kernel, linux-tegra, p.zabel, robh, thierry.reding,
	vkoul

On Tue, 17 Feb 2026 13:02:54 -0500, Frank Li wrote:
> On Tue, Feb 17, 2026 at 11:04:57PM +0530, Akhil R wrote:
>> Add iommu-map and remove iommus in the GPCDMA controller node so
>> that each channel uses separate stream ID and gets its own IOMMU
>> domain for memory. Also enable GPCDMA for Tegra264.
>>
>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>> ---
>>  .../arm64/boot/dts/nvidia/tegra264-p3834.dtsi |  4 +++
>>  arch/arm64/boot/dts/nvidia/tegra264.dtsi      | 33 ++++++++++++++++++-
>>  2 files changed, 36 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi b/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
>> index 7e2c3e66c2ab..c8beb616964a 100644
>> --- a/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
>> +++ b/arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi
>> @@ -16,6 +16,10 @@ serial@c4e0000 {
>>  		serial@c5a0000 {
>>  			status = "okay";
>>  		};
>> +
>> +		dma-controller@8400000 {
>> +			status = "okay";
>> +		};
>>  	};
>>
>>  	bus@8100000000 {
>> diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
>> index 7644a41d5f72..0317418c95d3 100644
>> --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
>> +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
>> @@ -3243,7 +3243,38 @@ gpcdma: dma-controller@8400000 {
>> 				     <GIC_SPI 614 IRQ_TYPE_LEVEL_HIGH>,
>> 				     <GIC_SPI 615 IRQ_TYPE_LEVEL_HIGH>;
>> 			#dma-cells = <1>;
>> -			iommus = <&smmu1 0x00000800>;
>> +			iommu-map =
>> +				<1  &smmu1 0x801 1>,
>> +				<2  &smmu1 0x802 1>,
>> +				<3  &smmu1 0x803 1>,
>> +				<4  &smmu1 0x804 1>,
>> +				<5  &smmu1 0x805 1>,
>> +				<6  &smmu1 0x806 1>,
>> +				<7  &smmu1 0x807 1>,
>> +				<8  &smmu1 0x808 1>,
>> +				<9  &smmu1 0x809 1>,
>> +				<10 &smmu1 0x80a 1>,
>> +				<11 &smmu1 0x80b 1>,
>> +				<12 &smmu1 0x80c 1>,
>> +				<13 &smmu1 0x80d 1>,
>> +				<14 &smmu1 0x80e 1>,
>> +				<15 &smmu1 0x80f 1>,
>> +				<16 &smmu1 0x810 1>,
>> +				<17 &smmu1 0x811 1>,
>> +				<18 &smmu1 0x812 1>,
>> +				<19 &smmu1 0x813 1>,
>> +				<20 &smmu1 0x814 1>,
>> +				<21 &smmu1 0x815 1>,
>> +				<22 &smmu1 0x816 1>,
>> +				<23 &smmu1 0x817 1>,
>> +				<24 &smmu1 0x818 1>,
>> +				<25 &smmu1 0x819 1>,
>> +				<26 &smmu1 0x81a 1>,
>> +				<27 &smmu1 0x81b 1>,
>> +				<28 &smmu1 0x81c 1>,
>> +				<29 &smmu1 0x81d 1>,
>> +				<30 &smmu1 0x81e 1>,
>> +				<31 &smmu1 0x81f 1>;
> 
> It is linear increase
> <1 &smmu1 0x801 31>;

Ack. I will update.

> 
> Frank
> 
>>  			dma-coherent;
>>  			dma-channel-mask = <0xfffffffe>;
>>  			status = "disabled";
>> --

Regards,
Akhil

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

* Re: [PATCH 3/8] dmaengine: tegra: Make reset control optional
  2026-02-24  5:39     ` Akhil R
@ 2026-02-24 17:02       ` Jon Hunter
  2026-02-25 10:01         ` Akhil R
  0 siblings, 1 reply; 32+ messages in thread
From: Jon Hunter @ 2026-02-24 17:02 UTC (permalink / raw)
  To: Akhil R, frank.li
  Cc: Frank.Li, conor+dt, devicetree, dmaengine, krzk+dt, linux-kernel,
	linux-tegra, p.zabel, robh, thierry.reding, vkoul


On 24/02/2026 05:39, Akhil R wrote:
> Hi Frank,
> 
> On Tue, 17 Feb 2026 13:04:57 -0500, Frank Li wrote:
>> On Tue, Feb 17, 2026 at 11:04:52PM +0530, Akhil R wrote:
>>> Tegra264 BPMP restricts access to GPCDMA reset control and the reset
>>
>> what's means of BPMP?
> 
> BPMP is Boot and Power Management Processor which is a co-processor
> in Tegra and runs a dedicated firmware. It manages the boot, clock,
> reset etc. I will put the expansion in the commit message in the next
> version. Do you suggest adding more details?

Technically you don't even need to mention BPMP here if it confuses 
matters. We can just say that for "Tegra264 there is no reset available 
for the driver to control and this is handled by boot firmware".

Jon

-- 
nvpublic


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

* Re: [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
  2026-02-24  6:25     ` Akhil R
@ 2026-02-24 21:59       ` Frank Li
  2026-02-25 10:27         ` Akhil R
  0 siblings, 1 reply; 32+ messages in thread
From: Frank Li @ 2026-02-24 21:59 UTC (permalink / raw)
  To: Akhil R
  Cc: Frank.Li, conor+dt, devicetree, dmaengine, jonathanh, krzk+dt,
	linux-kernel, linux-tegra, p.zabel, robh, thierry.reding, vkoul

On Tue, Feb 24, 2026 at 11:55:43AM +0530, Akhil R wrote:
> On Tue, 17 Feb 2026 14:52:17 -0500, Frank Li wrote:
> > On Tue, Feb 17, 2026 at 11:04:55PM +0530, Akhil R wrote:
> >> Use iommu-map, when provided, to get the stream ID to be programmed
> >> for each channel. Register each channel separately for allowing it
> >> to use a separate IOMMU domain for the transfer.
> >>
> >> Channels will continue to use the same global stream ID if iommu-map
> >> property is not present in the device tree.
> >>
> >> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> >> ---
> >>  drivers/dma/tegra186-gpc-dma.c | 62 +++++++++++++++++++++++++++-------
> >>  1 file changed, 49 insertions(+), 13 deletions(-)
> >>
> >> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> >> index ce3b1dd52bb3..b8ca269fa3ba 100644
> >> --- a/drivers/dma/tegra186-gpc-dma.c
> >> +++ b/drivers/dma/tegra186-gpc-dma.c
> >> @@ -15,6 +15,7 @@
> >>  #include <linux/module.h>
> >>  #include <linux/of.h>
> >>  #include <linux/of_dma.h>
> >> +#include <linux/of_device.h>
> >>  #include <linux/platform_device.h>
> >>  #include <linux/reset.h>
> >>  #include <linux/slab.h>
> >> @@ -1403,9 +1404,12 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
> >>  static int tegra_dma_probe(struct platform_device *pdev)
> >>  {
> >>  	const struct tegra_dma_chip_data *cdata = NULL;
> >> +	struct tegra_dma_channel *tdc;
> >> +	struct tegra_dma *tdma;
> >> +	struct dma_chan *chan;
> >> +	bool use_iommu_map = false;
> >>  	unsigned int i;
> >>  	u32 stream_id;
> >> -	struct tegra_dma *tdma;
> >>  	int ret;
> >>
> >>  	cdata = of_device_get_match_data(&pdev->dev);
> >> @@ -1433,9 +1437,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
> >>
> >>  	tdma->dma_dev.dev = &pdev->dev;
> >>
> >> -	if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
> >> -		dev_err(&pdev->dev, "Missing iommu stream-id\n");
> >> -		return -EINVAL;
> >> +	use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map");
> >> +	if (!use_iommu_map) {
> >> +		if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
> >> +			dev_err(&pdev->dev, "Missing iommu stream-id\n");
> >> +			return -EINVAL;
> >> +		}
> >>  	}
> >>
> >>  	ret = device_property_read_u32(&pdev->dev, "dma-channel-mask",
> >> @@ -1449,7 +1456,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
> >>
> >>  	INIT_LIST_HEAD(&tdma->dma_dev.channels);
> >>  	for (i = 0; i < cdata->nr_channels; i++) {
> >> -		struct tegra_dma_channel *tdc = &tdma->channels[i];
> >> +		tdc = &tdma->channels[i];
> >>
> >>  		/* Check for channel mask */
> >>  		if (!(tdma->chan_mask & BIT(i)))
> >> @@ -1469,10 +1476,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
> >>
> >>  		vchan_init(&tdc->vc, &tdma->dma_dev);
> >>  		tdc->vc.desc_free = tegra_dma_desc_free;
> >> -
> >> -		/* program stream-id for this channel */
> >> -		tegra_dma_program_sid(tdc, stream_id);
> >> -		tdc->stream_id = stream_id;
> >>  	}
> >>
> >>  	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
> >> @@ -1517,20 +1520,53 @@ static int tegra_dma_probe(struct platform_device *pdev)
> >>  		return ret;
> >>  	}
> >>
> >> +	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
> >> +		struct device *chdev = &chan->dev->device;
> >>
> > why no use
> > 	for (i = 0; i < cdata->nr_channels; i++) {
> > 		struct tegra_dma_channel *tdc = &tdma->channels[i];
>
> I thought this would ensure that we try to configure only the channels
> where the chan_dev and vchan are initialized. I understand that it is
> not probable in the current implementation that we will have channels
> which are uninitialized, but I felt this a better approach.
> Do you see any disadvantage in using the channels list?

not big issue, just strange, previous code use
for (i = 0; i < cdata->nr_channels; i++) {
}

why need enumerate it again and use difference method.

Frank
>
> >> +
> >> +		tdc = to_tegra_dma_chan(chan);
> >> +		if (use_iommu_map) {
> >> +			chdev->coherent_dma_mask = pdev->dev.coherent_dma_mask;
> >> +			chdev->dma_mask = &chdev->coherent_dma_mask;
> >> +			chdev->bus = pdev->dev.bus;
> >> +
> >> +			ret = of_dma_configure_id(chdev, pdev->dev.of_node,
> >> +						  true, &tdc->id);
> >> +			if (ret) {
> >> +				dev_err(chdev, "Failed to configure IOMMU for channel %d: %d\n",
> >> +					tdc->id, ret);
> >> +				goto err_unregister;
> >> +			}
> >> +
> >> +			if (!tegra_dev_iommu_get_stream_id(chdev, &stream_id)) {
> >> +				dev_err(chdev, "Failed to get stream ID for channel %d\n",
> >> +					tdc->id);
> >> +				goto err_unregister;
> >> +			}
> >> +
> >> +			chan->dev->chan_dma_dev = true;
> >> +		}
> >> +
> >> +		/* program stream-id for this channel */
> >> +		tegra_dma_program_sid(tdc, stream_id);
> >> +		tdc->stream_id = stream_id;
> >> +	}
> >> +
> >>  	ret = of_dma_controller_register(pdev->dev.of_node,
> >>  					 tegra_dma_of_xlate, tdma);
> >>  	if (ret < 0) {
> >>  		dev_err_probe(&pdev->dev, ret,
> >>  			      "GPC DMA OF registration failed\n");
> >> -
> >> -		dma_async_device_unregister(&tdma->dma_dev);
> >> -		return ret;
> >> +		goto err_unregister;
> >>  	}
> >>
> >> -	dev_info(&pdev->dev, "GPC DMA driver register %lu channels\n",
> >> +	dev_info(&pdev->dev, "GPC DMA driver registered %lu channels\n",
> >>  		 hweight_long(tdma->chan_mask));
> >>
> >>  	return 0;
> >> +
> >> +err_unregister:
> >> +	dma_async_device_unregister(&tdma->dma_dev);
> >
> > Can you use dmaenginem_async_device_register() to simple error path?
> >
> > Frank
>
> Agree. I will update it to use this function instead.
>
> >> +	return ret;
> >>  }
> >>
> >>  static void tegra_dma_remove(struct platform_device *pdev)
> >> --
>
> Regards,
> Akhil

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

* Re: [PATCH 3/8] dmaengine: tegra: Make reset control optional
  2026-02-24 17:02       ` Jon Hunter
@ 2026-02-25 10:01         ` Akhil R
  0 siblings, 0 replies; 32+ messages in thread
From: Akhil R @ 2026-02-25 10:01 UTC (permalink / raw)
  To: jonathanh
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, frank.li,
	krzk+dt, linux-kernel, linux-tegra, p.zabel, robh, thierry.reding,
	vkoul

On Tue, 24 Feb 2026 17:02:24 +0000 Jon Hunter wrote:
> On 24/02/2026 05:39, Akhil R wrote:
>> Hi Frank,
>> 
>> On Tue, 17 Feb 2026 13:04:57 -0500, Frank Li wrote:
>>> On Tue, Feb 17, 2026 at 11:04:52PM +0530, Akhil R wrote:
>>>> Tegra264 BPMP restricts access to GPCDMA reset control and the reset
>>>
>>> what's means of BPMP?
>> 
>> BPMP is Boot and Power Management Processor which is a co-processor
>> in Tegra and runs a dedicated firmware. It manages the boot, clock,
>> reset etc. I will put the expansion in the commit message in the next
>> version. Do you suggest adding more details?
> 
> Technically you don't even need to mention BPMP here if it confuses 
> matters. We can just say that for "Tegra264 there is no reset available 
> for the driver to control and this is handled by boot firmware".

Ack. I will update the commit message.

Regards,
Akhil

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

* Re: [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
  2026-02-24 21:59       ` Frank Li
@ 2026-02-25 10:27         ` Akhil R
  2026-02-25 11:23           ` Jon Hunter
  0 siblings, 1 reply; 32+ messages in thread
From: Akhil R @ 2026-02-25 10:27 UTC (permalink / raw)
  To: frank.li
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, jonathanh,
	krzk+dt, linux-kernel, linux-tegra, p.zabel, robh, thierry.reding,
	vkoul

On Tue, 24 Feb 2026 16:59:44 -0500 Frank Li wrote:
> On Tue, Feb 24, 2026 at 11:55:43AM +0530, Akhil R wrote:
>> On Tue, 17 Feb 2026 14:52:17 -0500, Frank Li wrote:
>>> On Tue, Feb 17, 2026 at 11:04:55PM +0530, Akhil R wrote:
>>>> Use iommu-map, when provided, to get the stream ID to be programmed
>>>> for each channel. Register each channel separately for allowing it
>>>> to use a separate IOMMU domain for the transfer.
>>>>
>>>> Channels will continue to use the same global stream ID if iommu-map
>>>> property is not present in the device tree.
>>>>
>>>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>>>> ---
>>>>  drivers/dma/tegra186-gpc-dma.c | 62 +++++++++++++++++++++++++++-------
>>>>  1 file changed, 49 insertions(+), 13 deletions(-)
>>>>
>>>> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
>>>> index ce3b1dd52bb3..b8ca269fa3ba 100644
>>>> --- a/drivers/dma/tegra186-gpc-dma.c
>>>> +++ b/drivers/dma/tegra186-gpc-dma.c
>>>> @@ -15,6 +15,7 @@
>>>>  #include <linux/module.h>
>>>>  #include <linux/of.h>
>>>>  #include <linux/of_dma.h>
>>>> +#include <linux/of_device.h>
>>>>  #include <linux/platform_device.h>
>>>>  #include <linux/reset.h>
>>>>  #include <linux/slab.h>
>>>> @@ -1403,9 +1404,12 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
>>>>  static int tegra_dma_probe(struct platform_device *pdev)
>>>>  {
>>>>  	const struct tegra_dma_chip_data *cdata = NULL;
>>>> +	struct tegra_dma_channel *tdc;
>>>> +	struct tegra_dma *tdma;
>>>> +	struct dma_chan *chan;
>>>> +	bool use_iommu_map = false;
>>>>  	unsigned int i;
>>>>  	u32 stream_id;
>>>> -	struct tegra_dma *tdma;
>>>>  	int ret;
>>>>
>>>>  	cdata = of_device_get_match_data(&pdev->dev);
>>>> @@ -1433,9 +1437,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>
>>>>  	tdma->dma_dev.dev = &pdev->dev;
>>>>
>>>> -	if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
>>>> -		dev_err(&pdev->dev, "Missing iommu stream-id\n");
>>>> -		return -EINVAL;
>>>> +	use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map");
>>>> +	if (!use_iommu_map) {
>>>> +		if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
>>>> +			dev_err(&pdev->dev, "Missing iommu stream-id\n");
>>>> +			return -EINVAL;
>>>> +		}
>>>>  	}
>>>>
>>>>  	ret = device_property_read_u32(&pdev->dev, "dma-channel-mask",
>>>> @@ -1449,7 +1456,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>
>>>>  	INIT_LIST_HEAD(&tdma->dma_dev.channels);
>>>>  	for (i = 0; i < cdata->nr_channels; i++) {
>>>> -		struct tegra_dma_channel *tdc = &tdma->channels[i];
>>>> +		tdc = &tdma->channels[i];
>>>>
>>>>  		/* Check for channel mask */
>>>>  		if (!(tdma->chan_mask & BIT(i)))
>>>> @@ -1469,10 +1476,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>
>>>>  		vchan_init(&tdc->vc, &tdma->dma_dev);
>>>>  		tdc->vc.desc_free = tegra_dma_desc_free;
>>>> -
>>>> -		/* program stream-id for this channel */
>>>> -		tegra_dma_program_sid(tdc, stream_id);
>>>> -		tdc->stream_id = stream_id;
>>>>  	}
>>>>
>>>>  	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
>>>> @@ -1517,20 +1520,53 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>  		return ret;
>>>>  	}
>>>>
>>>> +	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
>>>> +		struct device *chdev = &chan->dev->device;
>>>>
>>> why no use
>>> 	for (i = 0; i < cdata->nr_channels; i++) {
>>> 		struct tegra_dma_channel *tdc = &tdma->channels[i];
>>
>> I thought this would ensure that we try to configure only the channels
>> where the chan_dev and vchan are initialized. I understand that it is
>> not probable in the current implementation that we will have channels
>> which are uninitialized, but I felt this a better approach.
>> Do you see any disadvantage in using the channels list?
> 
> not big issue, just strange, previous code use
> for (i = 0; i < cdata->nr_channels; i++) {
> }
> 
> why need enumerate it again and use difference method.

I think we will not get to use chan->dev->device before
async_device_register() and thats why I added a separate loop to
configure the channels.

I can add a comment on why we need this loop. Do you suggest
changing it to a for-loop itself? Let me know your thoughts.

Regards,
Akhil

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

* Re: [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
  2026-02-25 10:27         ` Akhil R
@ 2026-02-25 11:23           ` Jon Hunter
  2026-02-26  4:25             ` Akhil R
  0 siblings, 1 reply; 32+ messages in thread
From: Jon Hunter @ 2026-02-25 11:23 UTC (permalink / raw)
  To: Akhil R, frank.li
  Cc: Frank.Li, conor+dt, devicetree, dmaengine, krzk+dt, linux-kernel,
	linux-tegra, p.zabel, robh, thierry.reding, vkoul



On 25/02/2026 10:27, Akhil R wrote:
> On Tue, 24 Feb 2026 16:59:44 -0500 Frank Li wrote:
>> On Tue, Feb 24, 2026 at 11:55:43AM +0530, Akhil R wrote:
>>> On Tue, 17 Feb 2026 14:52:17 -0500, Frank Li wrote:
>>>> On Tue, Feb 17, 2026 at 11:04:55PM +0530, Akhil R wrote:
>>>>> Use iommu-map, when provided, to get the stream ID to be programmed
>>>>> for each channel. Register each channel separately for allowing it
>>>>> to use a separate IOMMU domain for the transfer.
>>>>>
>>>>> Channels will continue to use the same global stream ID if iommu-map
>>>>> property is not present in the device tree.
>>>>>
>>>>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>>>>> ---
>>>>>   drivers/dma/tegra186-gpc-dma.c | 62 +++++++++++++++++++++++++++-------
>>>>>   1 file changed, 49 insertions(+), 13 deletions(-)
>>>>>
>>>>> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
>>>>> index ce3b1dd52bb3..b8ca269fa3ba 100644
>>>>> --- a/drivers/dma/tegra186-gpc-dma.c
>>>>> +++ b/drivers/dma/tegra186-gpc-dma.c
>>>>> @@ -15,6 +15,7 @@
>>>>>   #include <linux/module.h>
>>>>>   #include <linux/of.h>
>>>>>   #include <linux/of_dma.h>
>>>>> +#include <linux/of_device.h>
>>>>>   #include <linux/platform_device.h>
>>>>>   #include <linux/reset.h>
>>>>>   #include <linux/slab.h>
>>>>> @@ -1403,9 +1404,12 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
>>>>>   static int tegra_dma_probe(struct platform_device *pdev)
>>>>>   {
>>>>>   	const struct tegra_dma_chip_data *cdata = NULL;
>>>>> +	struct tegra_dma_channel *tdc;
>>>>> +	struct tegra_dma *tdma;
>>>>> +	struct dma_chan *chan;
>>>>> +	bool use_iommu_map = false;
>>>>>   	unsigned int i;
>>>>>   	u32 stream_id;
>>>>> -	struct tegra_dma *tdma;
>>>>>   	int ret;
>>>>>
>>>>>   	cdata = of_device_get_match_data(&pdev->dev);
>>>>> @@ -1433,9 +1437,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>>
>>>>>   	tdma->dma_dev.dev = &pdev->dev;
>>>>>
>>>>> -	if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
>>>>> -		dev_err(&pdev->dev, "Missing iommu stream-id\n");
>>>>> -		return -EINVAL;
>>>>> +	use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map");
>>>>> +	if (!use_iommu_map) {
>>>>> +		if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
>>>>> +			dev_err(&pdev->dev, "Missing iommu stream-id\n");
>>>>> +			return -EINVAL;
>>>>> +		}
>>>>>   	}
>>>>>
>>>>>   	ret = device_property_read_u32(&pdev->dev, "dma-channel-mask",
>>>>> @@ -1449,7 +1456,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>>
>>>>>   	INIT_LIST_HEAD(&tdma->dma_dev.channels);
>>>>>   	for (i = 0; i < cdata->nr_channels; i++) {
>>>>> -		struct tegra_dma_channel *tdc = &tdma->channels[i];
>>>>> +		tdc = &tdma->channels[i];
>>>>>
>>>>>   		/* Check for channel mask */
>>>>>   		if (!(tdma->chan_mask & BIT(i)))
>>>>> @@ -1469,10 +1476,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>>
>>>>>   		vchan_init(&tdc->vc, &tdma->dma_dev);
>>>>>   		tdc->vc.desc_free = tegra_dma_desc_free;
>>>>> -
>>>>> -		/* program stream-id for this channel */
>>>>> -		tegra_dma_program_sid(tdc, stream_id);
>>>>> -		tdc->stream_id = stream_id;
>>>>>   	}
>>>>>
>>>>>   	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
>>>>> @@ -1517,20 +1520,53 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>>   		return ret;
>>>>>   	}
>>>>>
>>>>> +	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
>>>>> +		struct device *chdev = &chan->dev->device;
>>>>>
>>>> why no use
>>>> 	for (i = 0; i < cdata->nr_channels; i++) {
>>>> 		struct tegra_dma_channel *tdc = &tdma->channels[i];
>>>
>>> I thought this would ensure that we try to configure only the channels
>>> where the chan_dev and vchan are initialized. I understand that it is
>>> not probable in the current implementation that we will have channels
>>> which are uninitialized, but I felt this a better approach.
>>> Do you see any disadvantage in using the channels list?
>>
>> not big issue, just strange, previous code use
>> for (i = 0; i < cdata->nr_channels; i++) {
>> }
>>
>> why need enumerate it again and use difference method.
> 
> I think we will not get to use chan->dev->device before
> async_device_register() and thats why I added a separate loop to
> configure the channels.

I assume that this needs to be done after the async_device_register()? 
If so we should note that in the commit message to explain that we need 
a 2nd loop. Unless we can move the 1st loop after the 
async_device_register() and just have one loop?

> I can add a comment on why we need this loop. Do you suggest
> changing it to a for-loop itself? Let me know your thoughts.

If using the list avoids the following check, then probably good to keep 
as is, but yes explain why we do it this way.

  /* Check for channel mask */
  if (!(tdma->chan_mask & BIT(i)))
          continue;

Jon

-- 
nvpublic


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

* Re: [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID
  2026-02-25 11:23           ` Jon Hunter
@ 2026-02-26  4:25             ` Akhil R
  0 siblings, 0 replies; 32+ messages in thread
From: Akhil R @ 2026-02-26  4:25 UTC (permalink / raw)
  To: jonathanh
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, frank.li,
	krzk+dt, linux-kernel, linux-tegra, p.zabel, robh, thierry.reding,
	vkoul

On Wed, 25 Feb 2026 11:23:08 +0000 Jon Hunter wrote:
> On 25/02/2026 10:27, Akhil R wrote:
>> On Tue, 24 Feb 2026 16:59:44 -0500 Frank Li wrote:
>>> On Tue, Feb 24, 2026 at 11:55:43AM +0530, Akhil R wrote:
>>>> On Tue, 17 Feb 2026 14:52:17 -0500, Frank Li wrote:
>>>>> On Tue, Feb 17, 2026 at 11:04:55PM +0530, Akhil R wrote:
>>>>>> Use iommu-map, when provided, to get the stream ID to be programmed
>>>>>> for each channel. Register each channel separately for allowing it
>>>>>> to use a separate IOMMU domain for the transfer.
>>>>>>
>>>>>> Channels will continue to use the same global stream ID if iommu-map
>>>>>> property is not present in the device tree.
>>>>>>
>>>>>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>>>>>> ---
>>>>>>   drivers/dma/tegra186-gpc-dma.c | 62 +++++++++++++++++++++++++++-------
>>>>>>   1 file changed, 49 insertions(+), 13 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
>>>>>> index ce3b1dd52bb3..b8ca269fa3ba 100644
>>>>>> --- a/drivers/dma/tegra186-gpc-dma.c
>>>>>> +++ b/drivers/dma/tegra186-gpc-dma.c
>>>>>> @@ -15,6 +15,7 @@
>>>>>>   #include <linux/module.h>
>>>>>>   #include <linux/of.h>
>>>>>>   #include <linux/of_dma.h>
>>>>>> +#include <linux/of_device.h>
>>>>>>   #include <linux/platform_device.h>
>>>>>>   #include <linux/reset.h>
>>>>>>   #include <linux/slab.h>
>>>>>> @@ -1403,9 +1404,12 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
>>>>>>   static int tegra_dma_probe(struct platform_device *pdev)
>>>>>>   {
>>>>>>   	const struct tegra_dma_chip_data *cdata = NULL;
>>>>>> +	struct tegra_dma_channel *tdc;
>>>>>> +	struct tegra_dma *tdma;
>>>>>> +	struct dma_chan *chan;
>>>>>> +	bool use_iommu_map = false;
>>>>>>   	unsigned int i;
>>>>>>   	u32 stream_id;
>>>>>> -	struct tegra_dma *tdma;
>>>>>>   	int ret;
>>>>>>
>>>>>>   	cdata = of_device_get_match_data(&pdev->dev);
>>>>>> @@ -1433,9 +1437,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>>>
>>>>>>   	tdma->dma_dev.dev = &pdev->dev;
>>>>>>
>>>>>> -	if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
>>>>>> -		dev_err(&pdev->dev, "Missing iommu stream-id\n");
>>>>>> -		return -EINVAL;
>>>>>> +	use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map");
>>>>>> +	if (!use_iommu_map) {
>>>>>> +		if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
>>>>>> +			dev_err(&pdev->dev, "Missing iommu stream-id\n");
>>>>>> +			return -EINVAL;
>>>>>> +		}
>>>>>>   	}
>>>>>>
>>>>>>   	ret = device_property_read_u32(&pdev->dev, "dma-channel-mask",
>>>>>> @@ -1449,7 +1456,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>>>
>>>>>>   	INIT_LIST_HEAD(&tdma->dma_dev.channels);
>>>>>>   	for (i = 0; i < cdata->nr_channels; i++) {
>>>>>> -		struct tegra_dma_channel *tdc = &tdma->channels[i];
>>>>>> +		tdc = &tdma->channels[i];
>>>>>>
>>>>>>   		/* Check for channel mask */
>>>>>>   		if (!(tdma->chan_mask & BIT(i)))
>>>>>> @@ -1469,10 +1476,6 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>>>
>>>>>>   		vchan_init(&tdc->vc, &tdma->dma_dev);
>>>>>>   		tdc->vc.desc_free = tegra_dma_desc_free;
>>>>>> -
>>>>>> -		/* program stream-id for this channel */
>>>>>> -		tegra_dma_program_sid(tdc, stream_id);
>>>>>> -		tdc->stream_id = stream_id;
>>>>>>   	}
>>>>>>
>>>>>>   	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
>>>>>> @@ -1517,20 +1520,53 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>>>>>   		return ret;
>>>>>>   	}
>>>>>>
>>>>>> +	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
>>>>>> +		struct device *chdev = &chan->dev->device;
>>>>>>
>>>>> why no use
>>>>> 	for (i = 0; i < cdata->nr_channels; i++) {
>>>>> 		struct tegra_dma_channel *tdc = &tdma->channels[i];
>>>>
>>>> I thought this would ensure that we try to configure only the channels
>>>> where the chan_dev and vchan are initialized. I understand that it is
>>>> not probable in the current implementation that we will have channels
>>>> which are uninitialized, but I felt this a better approach.
>>>> Do you see any disadvantage in using the channels list?
>>>
>>> not big issue, just strange, previous code use
>>> for (i = 0; i < cdata->nr_channels; i++) {
>>> }
>>>
>>> why need enumerate it again and use difference method.
>> 
>> I think we will not get to use chan->dev->device before
>> async_device_register() and thats why I added a separate loop to
>> configure the channels.
> 
> I assume that this needs to be done after the async_device_register()? 
> If so we should note that in the commit message to explain that we need 
> a 2nd loop. Unless we can move the 1st loop after the 
> async_device_register() and just have one loop?

dma_dev.channels is populated by vchan_init(). So, the first loop must
exist before async_device_register(). If we have to restrict to one loop,
we would need to use dma_async_device_channel_register() to register the
channels within this driver directly. I feel having a second loop is
better than trying to restrict it to one.

> 
>> I can add a comment on why we need this loop. Do you suggest
>> changing it to a for-loop itself? Let me know your thoughts.
> 
> If using the list avoids the following check, then probably good to keep 
> as is, but yes explain why we do it this way.
> 
>   /* Check for channel mask */
>   if (!(tdma->chan_mask & BIT(i)))
>           continue;

Yes. Using dma_dev.channels avoids the above check. I will describe this
better in the commit message and as comments.

Regards,
Akhil

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

end of thread, other threads:[~2026-02-26  4:26 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
2026-02-17 17:34 ` [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
2026-02-17 18:38   ` Rob Herring (Arm)
2026-02-17 19:53   ` Krzysztof Kozlowski
2026-02-18  9:59     ` Jon Hunter
2026-02-18 15:49       ` Rob Herring
2026-02-24  6:41         ` Akhil R
2026-02-17 17:34 ` [PATCH 2/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
2026-02-17 18:38   ` Rob Herring (Arm)
2026-02-17 17:34 ` [PATCH 3/8] dmaengine: tegra: Make reset control optional Akhil R
2026-02-17 18:04   ` Frank Li
2026-02-24  5:39     ` Akhil R
2026-02-24 17:02       ` Jon Hunter
2026-02-25 10:01         ` Akhil R
2026-02-17 17:34 ` [PATCH 4/8] dmaengine: tegra: Use struct for register offsets Akhil R
2026-02-17 18:09   ` Frank Li
2026-02-17 17:34 ` [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits Akhil R
2026-02-17 19:44   ` Frank Li
2026-02-24  6:03     ` Akhil R
2026-02-17 17:34 ` [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID Akhil R
2026-02-17 19:52   ` Frank Li
2026-02-24  6:25     ` Akhil R
2026-02-24 21:59       ` Frank Li
2026-02-25 10:27         ` Akhil R
2026-02-25 11:23           ` Jon Hunter
2026-02-26  4:25             ` Akhil R
2026-02-19  8:28   ` Dan Carpenter
2026-02-17 17:34 ` [PATCH 7/8] dmaengine: tegra: Add Tegra264 support Akhil R
2026-02-17 19:53   ` Frank Li
2026-02-17 17:34 ` [PATCH 8/8] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264 Akhil R
2026-02-17 18:02   ` Frank Li
2026-02-24  6:55     ` Akhil R

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