public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/9] Add GPCDMA support in Tegra264
@ 2026-03-02 12:32 Akhil R
  2026-03-02 12:32 ` [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
                   ` (8 more replies)
  0 siblings, 9 replies; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R

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

v1->v2:
- Fix dt_bindings_check warnings
- Drop fallback compatible "nvidia,tegra186-gpcdma" from Tegra264 DT
- Use dma_addr_t for sg_req src/dst fields and drop separate high_add
  variable and check for the addr_bits only when programming the
  registers.
- Update address width to 39 bits for Tegra234 and before since the SMMU
  supports only up to 39 bits till Tegra234.
- Add a patch to do managed DMA controller registration.
- Describe the second iteration in the probe.
- Update commit descriptions.

Akhil R (9):
  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 > 39 bits
  dmaengine: tegra: Use managed DMA controller registration
  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      |   3 +-
 drivers/dma/tegra186-gpc-dma.c                | 433 +++++++++++-------
 4 files changed, 298 insertions(+), 169 deletions(-)

-- 
2.50.1


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

* [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
@ 2026-03-02 12:32 ` Akhil R
  2026-03-03  6:39   ` Krzysztof Kozlowski
  2026-03-02 12:32 ` [PATCH v2 2/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R

Add iommu-map property to specify separate stream IDs for each DMA
channel. This enables each channel to be in its own IOMMU domain,
keeping 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     | 5 +++++
 1 file changed, 5 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..1e7b5ddd4658 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,10 @@ properties:
   iommus:
     maxItems: 1
 
+  iommu-map:
+    minItems: 1
+    maxItems: 32
+
   dma-coherent: true
 
   dma-channel-mask:
-- 
2.50.1


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

* [PATCH v2 2/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
  2026-03-02 12:32 ` [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
@ 2026-03-02 12:32 ` Akhil R
  2026-03-02 20:31   ` Frank Li
  2026-03-02 12:32 ` [PATCH v2 3/9] dmaengine: tegra: Make reset control optional Akhil R
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R

In Tegra264, GPCDMA reset control is not exposed to Linux and is handled
by the boot firmware.

Although reset was not exposed in Tegra234 as well, the firmware supported
a dummy reset which just returns success on reset without doing an actual
reset. This is also not supported in Tegra264 BPMP. Therefore mark 'reset'
and 'reset-names' properties as required only for devices prior to
Tegra264.

This also necessitates that the Tegra264 compatible to be standalone and
cannot have the fallback compatible of Tegra186. Since there is no
functional impact, we keep reset as required for Tegra234 to avoid
breaking the ABI.

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

diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
index 1e7b5ddd4658..34c9b41aecfc 100644
--- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
@@ -16,16 +16,13 @@ maintainers:
   - Rajesh Gumasta <rgumasta@nvidia.com>
   - Akhil R <akhilrajeev@nvidia.com>
 
-allOf:
-  - $ref: dma-controller.yaml#
-
 properties:
   compatible:
     oneOf:
+      - const: nvidia,tegra264-gpcdma
       - const: nvidia,tegra186-gpcdma
       - items:
           - enum:
-              - nvidia,tegra264-gpcdma
               - nvidia,tegra234-gpcdma
               - nvidia,tegra194-gpcdma
           - const: nvidia,tegra186-gpcdma
@@ -65,12 +62,25 @@ 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
+              - nvidia,tegra234-gpcdma
+    then:
+      required:
+        - resets
+        - reset-names
+
 additionalProperties: false
 
 examples:
-- 
2.50.1


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

* [PATCH v2 3/9] dmaengine: tegra: Make reset control optional
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
  2026-03-02 12:32 ` [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
  2026-03-02 12:32 ` [PATCH v2 2/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
@ 2026-03-02 12:32 ` Akhil R
  2026-03-02 20:31   ` Frank Li
  2026-03-02 12:32 ` [PATCH v2 4/9] dmaengine: tegra: Use struct for register offsets Akhil R
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R

In Tegra264, reset is not available for the driver to control as
this is handled by the boot firmware. Hence make the reset control
optional and update the error message to reflect the correct error.

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

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 4d6fe0efa76e..09a1717aa808 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1382,10 +1382,10 @@ 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");
+			      "Failed to get controller reset\n");
 	}
 	reset_control_reset(tdma->rst);
 
-- 
2.50.1


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

* [PATCH v2 4/9] dmaengine: tegra: Use struct for register offsets
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
                   ` (2 preceding siblings ...)
  2026-03-02 12:32 ` [PATCH v2 3/9] dmaengine: tegra: Make reset control optional Akhil R
@ 2026-03-02 12:32 ` Akhil R
  2026-03-02 12:32 ` [PATCH v2 5/9] dmaengine: tegra: Support address width > 39 bits Akhil R
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R, Frank Li

Repurpose the struct tegra_dma_channel_regs 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. Move the
values directly into tegra_dma_sg_req and use channel_regs for
storing the register offsets. Update all register reads/writes to use
the struct channel_regs. This prepares for 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 | 281 +++++++++++++++++----------------
 1 file changed, 146 insertions(+), 135 deletions(-)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 09a1717aa808..09ba2755c06d 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,22 @@ 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), "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), "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), "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 +366,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 +408,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 +445,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 +477,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 +515,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 +590,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 +662,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 +676,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 +728,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 +882,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 +905,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 +950,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 +974,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 +1038,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 +1085,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 +1100,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 +1175,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 +1207,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 +1294,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 +1326,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 +1335,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 +1356,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 +1364,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 +1430,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] 24+ messages in thread

* [PATCH v2 5/9] dmaengine: tegra: Support address width > 39 bits
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
                   ` (3 preceding siblings ...)
  2026-03-02 12:32 ` [PATCH v2 4/9] dmaengine: tegra: Use struct for register offsets Akhil R
@ 2026-03-02 12:32 ` Akhil R
  2026-03-02 21:14   ` Frank Li
  2026-03-02 12:32 ` [PATCH v2 6/9] dmaengine: tegra: Use managed DMA controller registration Akhil R
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R

Tegra264 supports address width of 41 bits. Unlike older SoCs which use
a common high_addr register for upper address bits, Tegra264 has separate
src_high and dst_high registers to accommodate this wider address space.

Add an addr_bits property to the device data structure to specify the
number of address bits supported on each device and use that to program
the appropriate registers.

Update the sg_req struct to remove the high_addr field and use
dma_addr_t for src and dst to store the complete addresses. Extract
the high address bits only when programming the registers.

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

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 09ba2755c06d..753e86d05a02 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;
@@ -186,10 +189,9 @@ struct tegra_dma_channel_regs {
  */
 struct tegra_dma_sg_req {
 	unsigned int len;
+	dma_addr_t src;
+	dma_addr_t dst;
 	u32 csr;
-	u32 src;
-	u32 dst;
-	u32 high_addr;
 	u32 mc_seq;
 	u32 mmio_seq;
 	u32 wcount;
@@ -273,6 +275,25 @@ 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, lower_32_bits(sg_req->src));
+	tdc_write(tdc, tdc->regs->dst, lower_32_bits(sg_req->dst));
+
+	if (tdc->tdma->chip_data->addr_bits > 39) {
+		tdc_write(tdc, tdc->regs->src_high, upper_32_bits(sg_req->src));
+		tdc_write(tdc, tdc->regs->dst_high, upper_32_bits(sg_req->dst));
+	} else {
+		u32 src_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR,
+					      upper_32_bits(sg_req->src));
+		u32 dst_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR,
+					      upper_32_bits(sg_req->dst));
+
+		tdc_write(tdc, tdc->regs->high_addr, src_high | dst_high);
+	}
+}
+
 static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
 {
 	dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n",
@@ -281,10 +302,20 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
 		tdc_read(tdc, tdc->regs->csr),
 		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 > 39) {
+		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),
@@ -487,9 +518,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,
@@ -517,11 +546,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);
@@ -826,7 +853,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,
@@ -904,11 +931,9 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
 	dma_desc->bytes_req = len;
 	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));
+
 	sg_req[0].fixed_pattern = value;
 	/* Word count reg takes value as (N +1) words */
 	sg_req[0].wcount = ((len - 4) >> 2);
@@ -976,10 +1001,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
 
 	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));
+
 	/* Word count reg takes value as (N +1) words */
 	sg_req[0].wcount = ((len - 4) >> 2);
 	sg_req[0].csr = csr;
@@ -999,7 +1021,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,13 +1110,9 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
 		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));
 		}
 
 		/*
@@ -1117,7 +1136,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,13 +1229,9 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
 		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));
 		}
 		/*
 		 * Word count register takes input in words. Writing a value
@@ -1314,6 +1330,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 = 39,
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = false,
@@ -1323,6 +1340,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 = 39,
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = true,
@@ -1332,6 +1350,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 = 39,
 	.channel_reg_size = SZ_64K,
 	.max_dma_count = SZ_1G,
 	.hw_support_pause = true,
@@ -1443,6 +1462,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] 24+ messages in thread

* [PATCH v2 6/9] dmaengine: tegra: Use managed DMA controller registration
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
                   ` (4 preceding siblings ...)
  2026-03-02 12:32 ` [PATCH v2 5/9] dmaengine: tegra: Support address width > 39 bits Akhil R
@ 2026-03-02 12:32 ` Akhil R
  2026-03-02 12:32 ` [PATCH v2 7/9] dmaengine: tegra: Use iommu-map for stream ID Akhil R
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R, Frank Li

Switch to dmaenginem_async_device_register() for managed
registration and to simplify the error path in the probe.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
Suggested-by: Frank Li <frank.li@nxp.com>
---
 drivers/dma/tegra186-gpc-dma.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 753e86d05a02..5997edaba28e 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1497,7 +1497,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
 	tdma->dma_dev.device_synchronize = tegra_dma_chan_synchronize;
 	tdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
 
-	ret = dma_async_device_register(&tdma->dma_dev);
+	ret = dmaenginem_async_device_register(&tdma->dma_dev);
 	if (ret < 0) {
 		dev_err_probe(&pdev->dev, ret,
 			      "GPC DMA driver registration failed\n");
@@ -1509,12 +1509,10 @@ static int tegra_dma_probe(struct platform_device *pdev)
 	if (ret < 0) {
 		dev_err_probe(&pdev->dev, ret,
 			      "GPC DMA OF registration failed\n");
-
-		dma_async_device_unregister(&tdma->dma_dev);
 		return ret;
 	}
 
-	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;
@@ -1522,10 +1520,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
 
 static void tegra_dma_remove(struct platform_device *pdev)
 {
-	struct tegra_dma *tdma = platform_get_drvdata(pdev);
-
 	of_dma_controller_free(pdev->dev.of_node);
-	dma_async_device_unregister(&tdma->dma_dev);
 }
 
 static int __maybe_unused tegra_dma_pm_suspend(struct device *dev)
-- 
2.50.1


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

* [PATCH v2 7/9] dmaengine: tegra: Use iommu-map for stream ID
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
                   ` (5 preceding siblings ...)
  2026-03-02 12:32 ` [PATCH v2 6/9] dmaengine: tegra: Use managed DMA controller registration Akhil R
@ 2026-03-02 12:32 ` Akhil R
  2026-03-02 12:32 ` [PATCH v2 8/9] dmaengine: tegra: Add Tegra264 support Akhil R
  2026-03-02 12:32 ` [PATCH v2 9/9] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264 Akhil R
  8 siblings, 0 replies; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R

Use 'iommu-map', when provided, to get the stream ID to be programmed
for each channel. Iterate over the channels registered and configure
each channel device separately using of_dma_configure_id() to allow
it to use a separate IOMMU domain for the transfer. But do this
in a second loop since the first loop populates the dma device channels
list and async_device_register() registers the channels. Both are
prerequisite for using the channel device in the next loop.

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

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

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 5997edaba28e..9af509ecf495 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>
@@ -1390,9 +1391,13 @@ 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;
+	struct device *chdev;
+	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);
@@ -1420,9 +1425,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",
@@ -1434,9 +1442,10 @@ static int tegra_dma_probe(struct platform_device *pdev)
 		tdma->chan_mask = TEGRA_GPCDMA_DEFAULT_CHANNEL_MASK;
 	}
 
+	/* Initialize vchan for each channel and populate the channels list */
 	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)))
@@ -1456,10 +1465,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));
@@ -1497,6 +1502,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
 	tdma->dma_dev.device_synchronize = tegra_dma_chan_synchronize;
 	tdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
 
+	/* Register the DMA device and the channels */
 	ret = dmaenginem_async_device_register(&tdma->dma_dev);
 	if (ret < 0) {
 		dev_err_probe(&pdev->dev, ret,
@@ -1504,6 +1510,46 @@ static int tegra_dma_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	/*
+	 * Configure stream ID for each channel from the channels registered
+	 * above. This is done in a separate iteration to ensure that only
+	 * the channels available and registered for the DMA device are used.
+	 */
+	list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
+		chdev = &chan->dev->device;
+		tdc = to_tegra_dma_chan(chan);
+
+		if (use_iommu_map) {
+			chdev->bus = pdev->dev.bus;
+			ret = dma_coerce_mask_and_coherent(chdev, DMA_BIT_MASK(cdata->addr_bits));
+			if (ret) {
+				dev_err(chdev, "Failed to set DMA mask for channel %d: %d\n",
+					tdc->id, ret);
+				return ret;
+			}
+
+			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);
+				return ret;
+			}
+
+			if (!tegra_dev_iommu_get_stream_id(chdev, &stream_id)) {
+				dev_err(chdev, "Failed to get stream ID for channel %d\n",
+					tdc->id);
+				return -EINVAL;
+			}
+
+			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) {
-- 
2.50.1


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

* [PATCH v2 8/9] dmaengine: tegra: Add Tegra264 support
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
                   ` (6 preceding siblings ...)
  2026-03-02 12:32 ` [PATCH v2 7/9] dmaengine: tegra: Use iommu-map for stream ID Akhil R
@ 2026-03-02 12:32 ` Akhil R
  2026-03-02 12:32 ` [PATCH v2 9/9] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264 Akhil R
  8 siblings, 0 replies; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R, Frank Li

Add compatible and chip data to support GPCDMA in Tegra264, which has
differences in register layout and address bits compared to previous
versions.

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 9af509ecf495..d6e0dbc19e8a 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1329,6 +1329,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 = 39,
@@ -1359,6 +1378,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 = 41,
+	.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",
@@ -1369,6 +1398,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] 24+ messages in thread

* [PATCH v2 9/9] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264
  2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
                   ` (7 preceding siblings ...)
  2026-03-02 12:32 ` [PATCH v2 8/9] dmaengine: tegra: Add Tegra264 support Akhil R
@ 2026-03-02 12:32 ` Akhil R
  8 siblings, 0 replies; 24+ messages in thread
From: Akhil R @ 2026-03-02 12:32 UTC (permalink / raw)
  To: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel
  Cc: Akhil R

Add iommu-map the GPCDMA controller node so that each channel uses a
separate stream ID and gets its own IOMMU domain for memory. Enable
GCPDMA as well.

Also remove the fallback compatible string "nvidia,tegra186-gpcdma".
Tegra186 compatible cannot work on Tegra264 because of the register
offset changes and absence of reset property.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra264-p3834.dtsi | 4 ++++
 arch/arm64/boot/dts/nvidia/tegra264.dtsi       | 3 ++-
 2 files changed, 6 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..9821d085c766 100644
--- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
@@ -3208,7 +3208,7 @@ agic_page5: interrupt-controller@99b0000 {
 		};
 
 		gpcdma: dma-controller@8400000 {
-			compatible = "nvidia,tegra264-gpcdma", "nvidia,tegra186-gpcdma";
+			compatible = "nvidia,tegra264-gpcdma";
 			reg = <0x0 0x08400000 0x0 0x210000>;
 			interrupts = <GIC_SPI 584 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 585 IRQ_TYPE_LEVEL_HIGH>,
@@ -3244,6 +3244,7 @@ gpcdma: dma-controller@8400000 {
 				     <GIC_SPI 615 IRQ_TYPE_LEVEL_HIGH>;
 			#dma-cells = <1>;
 			iommus = <&smmu1 0x00000800>;
+			iommu-map = <1 &smmu1 0x801 31>;
 			dma-coherent;
 			dma-channel-mask = <0xfffffffe>;
 			status = "disabled";
-- 
2.50.1


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

* Re: [PATCH v2 2/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional
  2026-03-02 12:32 ` [PATCH v2 2/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
@ 2026-03-02 20:31   ` Frank Li
  2026-03-03 13:27     ` Jon Hunter
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-03-02 20:31 UTC (permalink / raw)
  To: Akhil R
  Cc: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel

On Mon, Mar 02, 2026 at 06:02:32PM +0530, Akhil R wrote:
> In Tegra264, GPCDMA reset control is not exposed to Linux and is handled
> by the boot firmware.
>
> Although reset was not exposed in Tegra234 as well, the firmware supported
> a dummy reset which just returns success on reset without doing an actual
> reset. This is also not supported in Tegra264 BPMP. Therefore mark 'reset'
> and 'reset-names' properties as required only for devices prior to
> Tegra264.
>
> This also necessitates that the Tegra264 compatible to be standalone and
> cannot have the fallback compatible of Tegra186. Since there is no
> functional impact, we keep reset as required for Tegra234 to avoid
> breaking the ABI.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  .../bindings/dma/nvidia,tegra186-gpc-dma.yaml | 22 ++++++++++++++-----
>  1 file changed, 16 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> index 1e7b5ddd4658..34c9b41aecfc 100644
> --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
> @@ -16,16 +16,13 @@ maintainers:
>    - Rajesh Gumasta <rgumasta@nvidia.com>
>    - Akhil R <akhilrajeev@nvidia.com>
>
> -allOf:
> -  - $ref: dma-controller.yaml#
> -
>  properties:
>    compatible:
>      oneOf:
> +      - const: nvidia,tegra264-gpcdma
>        - const: nvidia,tegra186-gpcdma

use enum

         - enum:
             - nvidia,tegra186-gpcdma
             - nvidia,tegra264-gpcdma

>        - items:
>            - enum:
> -              - nvidia,tegra264-gpcdma
>                - nvidia,tegra234-gpcdma
>                - nvidia,tegra194-gpcdma
>            - const: nvidia,tegra186-gpcdma
> @@ -65,12 +62,25 @@ 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
> +              - nvidia,tegra234-gpcdma

nvidia,tegra234-gpcdma must have fallback nvidia,tegra186-gpcdma, so
needn't nvidia,tegra234-gpcdma here.

Frank

> +    then:
> +      required:
> +        - resets
> +        - reset-names
> +
>  additionalProperties: false
>
>  examples:
> --
> 2.50.1
>

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

* Re: [PATCH v2 3/9] dmaengine: tegra: Make reset control optional
  2026-03-02 12:32 ` [PATCH v2 3/9] dmaengine: tegra: Make reset control optional Akhil R
@ 2026-03-02 20:31   ` Frank Li
  0 siblings, 0 replies; 24+ messages in thread
From: Frank Li @ 2026-03-02 20:31 UTC (permalink / raw)
  To: Akhil R
  Cc: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel

On Mon, Mar 02, 2026 at 06:02:33PM +0530, Akhil R wrote:
> In Tegra264, reset is not available for the driver to control as
> this is handled by the boot firmware. Hence make the reset control
> optional and update the error message to reflect the correct error.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>  drivers/dma/tegra186-gpc-dma.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> index 4d6fe0efa76e..09a1717aa808 100644
> --- a/drivers/dma/tegra186-gpc-dma.c
> +++ b/drivers/dma/tegra186-gpc-dma.c
> @@ -1382,10 +1382,10 @@ 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");
> +			      "Failed to get controller reset\n");
>  	}
>  	reset_control_reset(tdma->rst);
>
> --
> 2.50.1
>

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

* Re: [PATCH v2 5/9] dmaengine: tegra: Support address width > 39 bits
  2026-03-02 12:32 ` [PATCH v2 5/9] dmaengine: tegra: Support address width > 39 bits Akhil R
@ 2026-03-02 21:14   ` Frank Li
  0 siblings, 0 replies; 24+ messages in thread
From: Frank Li @ 2026-03-02 21:14 UTC (permalink / raw)
  To: Akhil R
  Cc: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel

On Mon, Mar 02, 2026 at 06:02:35PM +0530, Akhil R wrote:
> Tegra264 supports address width of 41 bits. Unlike older SoCs which use
> a common high_addr register for upper address bits, Tegra264 has separate
> src_high and dst_high registers to accommodate this wider address space.
>
> Add an addr_bits property to the device data structure to specify the
> number of address bits supported on each device and use that to program
> the appropriate registers.
>
> Update the sg_req struct to remove the high_addr field and use
> dma_addr_t for src and dst to store the complete addresses. Extract
> the high address bits only when programming the registers.
>
> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
> ---
>  drivers/dma/tegra186-gpc-dma.c | 87 ++++++++++++++++++++++------------
>  1 file changed, 56 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
> index 09ba2755c06d..753e86d05a02 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;
> @@ -186,10 +189,9 @@ struct tegra_dma_channel_regs {
>   */
>  struct tegra_dma_sg_req {
>  	unsigned int len;
> +	dma_addr_t src;
> +	dma_addr_t dst;
>  	u32 csr;
> -	u32 src;
> -	u32 dst;
> -	u32 high_addr;
>  	u32 mc_seq;
>  	u32 mmio_seq;
>  	u32 wcount;
> @@ -273,6 +275,25 @@ 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, lower_32_bits(sg_req->src));
> +	tdc_write(tdc, tdc->regs->dst, lower_32_bits(sg_req->dst));
> +
> +	if (tdc->tdma->chip_data->addr_bits > 39) {
> +		tdc_write(tdc, tdc->regs->src_high, upper_32_bits(sg_req->src));
> +		tdc_write(tdc, tdc->regs->dst_high, upper_32_bits(sg_req->dst));
> +	} else {
> +		u32 src_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR,
> +					      upper_32_bits(sg_req->src));
> +		u32 dst_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR,
> +					      upper_32_bits(sg_req->dst));
> +
> +		tdc_write(tdc, tdc->regs->high_addr, src_high | dst_high);
> +	}
> +}
> +
>  static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
>  {
>  	dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n",
> @@ -281,10 +302,20 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
>  		tdc_read(tdc, tdc->regs->csr),
>  		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 > 39) {
> +		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),
> @@ -487,9 +518,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,
> @@ -517,11 +546,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);
> @@ -826,7 +853,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,
> @@ -904,11 +931,9 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
>  	dma_desc->bytes_req = len;
>  	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));
> +
>  	sg_req[0].fixed_pattern = value;
>  	/* Word count reg takes value as (N +1) words */
>  	sg_req[0].wcount = ((len - 4) >> 2);
> @@ -976,10 +1001,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
>
>  	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));
> +
>  	/* Word count reg takes value as (N +1) words */
>  	sg_req[0].wcount = ((len - 4) >> 2);
>  	sg_req[0].csr = csr;
> @@ -999,7 +1021,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,13 +1110,9 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
>  		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));
>  		}
>
>  		/*
> @@ -1117,7 +1136,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,13 +1229,9 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
>  		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));
>  		}
>  		/*
>  		 * Word count register takes input in words. Writing a value
> @@ -1314,6 +1330,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 = 39,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = false,
> @@ -1323,6 +1340,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 = 39,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = true,
> @@ -1332,6 +1350,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 = 39,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = true,
> @@ -1443,6 +1462,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));

when bit mask >= 32, dma_set_mask_and_coherent() never return failure. So
needn't check return value.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
> +	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] 24+ messages in thread

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-02 12:32 ` [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
@ 2026-03-03  6:39   ` Krzysztof Kozlowski
  2026-03-03  8:40     ` Akhil R
  0 siblings, 1 reply; 24+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-03  6:39 UTC (permalink / raw)
  To: Akhil R
  Cc: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Jonathan Hunter, Laxman Dewangan,
	Philipp Zabel, dmaengine, devicetree, linux-tegra, linux-kernel

On Mon, Mar 02, 2026 at 06:02:31PM +0530, Akhil R wrote:
> Add iommu-map property to specify separate stream IDs for each DMA
> channel. This enables each channel to be in its own IOMMU domain,
> keeping 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     | 5 +++++
>  1 file changed, 5 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..1e7b5ddd4658 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,10 @@ properties:
>    iommus:
>      maxItems: 1
>  
> +  iommu-map:
> +    minItems: 1
> +    maxItems: 32

Why is this flexible? If it is, means usually items are distinctive, so
I would expect defining/listing them. If they are not distinctive,
commit msg is incorrect. If the list is as simple as 1-to-1 channel
mapping, just add it in the description how they are ordered.

> +
>    dma-coherent: true
>  
>    dma-channel-mask:
> -- 
> 2.50.1
> 

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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-03  6:39   ` Krzysztof Kozlowski
@ 2026-03-03  8:40     ` Akhil R
  2026-03-03  8:57       ` Krzysztof Kozlowski
  2026-03-03 13:09       ` Jon Hunter
  0 siblings, 2 replies; 24+ messages in thread
From: Akhil R @ 2026-03-03  8:40 UTC (permalink / raw)
  To: krzk
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, jonathanh,
	krzk+dt, ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul

On Tue, 3 Mar 2026 07:39:58 +0100 Krzysztof Kozlowski wrote:
> On Mon, Mar 02, 2026 at 06:02:31PM +0530, Akhil R wrote:
>> Add iommu-map property to specify separate stream IDs for each DMA
>> channel. This enables each channel to be in its own IOMMU domain,
>> keeping 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     | 5 +++++
>>  1 file changed, 5 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..1e7b5ddd4658 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,10 @@ properties:
>>    iommus:
>>      maxItems: 1
>>  
>> +  iommu-map:
>> +    minItems: 1
>> +    maxItems: 32
> 
> Why is this flexible? If it is, means usually items are distinctive, so
> I would expect defining/listing them. If they are not distinctive,
> commit msg is incorrect. If the list is as simple as 1-to-1 channel
> mapping, just add it in the description how they are ordered.

Yes, it is a 1-to-1 channel mapping to an IOMMU ID. The intent of making
it flexible is to allow non-consecutive IOMMU ID assignments as well.
This is particularly needed in virtualised environments where the
hypervisor may reserve certain stream IDs, and the guest VM can map only
the permitted ones. Shall I add a description here mentioning this
use-case?

> 
>> +
>>    dma-coherent: true
>>  
>>    dma-channel-mask:
>> -- 
>> 2.50.1
> 

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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-03  8:40     ` Akhil R
@ 2026-03-03  8:57       ` Krzysztof Kozlowski
  2026-03-03 13:09       ` Jon Hunter
  1 sibling, 0 replies; 24+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-03  8:57 UTC (permalink / raw)
  To: Akhil R
  Cc: Frank.Li, conor+dt, devicetree, dmaengine, jonathanh, krzk+dt,
	ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul

On 03/03/2026 09:40, Akhil R wrote:
> On Tue, 3 Mar 2026 07:39:58 +0100 Krzysztof Kozlowski wrote:
>> On Mon, Mar 02, 2026 at 06:02:31PM +0530, Akhil R wrote:
>>> Add iommu-map property to specify separate stream IDs for each DMA
>>> channel. This enables each channel to be in its own IOMMU domain,
>>> keeping 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     | 5 +++++
>>>  1 file changed, 5 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..1e7b5ddd4658 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,10 @@ properties:
>>>    iommus:
>>>      maxItems: 1
>>>  
>>> +  iommu-map:
>>> +    minItems: 1
>>> +    maxItems: 32
>>
>> Why is this flexible? If it is, means usually items are distinctive, so
>> I would expect defining/listing them. If they are not distinctive,
>> commit msg is incorrect. If the list is as simple as 1-to-1 channel
>> mapping, just add it in the description how they are ordered.
> 
> Yes, it is a 1-to-1 channel mapping to an IOMMU ID. The intent of making
> it flexible is to allow non-consecutive IOMMU ID assignments as well.

You cannot skip items if these have such meaning of mapping per channel.
The list order is the ABI - index of the list is the channel here.

> This is particularly needed in virtualised environments where the
> hypervisor may reserve certain stream IDs, and the guest VM can map only
> the permitted ones. Shall I add a description here mentioning this
> use-case?

You can also look at recent works for iris video codec from Qualcomm
solving something similar.


Best regards,
Krzysztof

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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-03  8:40     ` Akhil R
  2026-03-03  8:57       ` Krzysztof Kozlowski
@ 2026-03-03 13:09       ` Jon Hunter
  2026-03-03 17:14         ` Akhil R
  1 sibling, 1 reply; 24+ messages in thread
From: Jon Hunter @ 2026-03-03 13:09 UTC (permalink / raw)
  To: Akhil R, krzk
  Cc: Frank.Li, conor+dt, devicetree, dmaengine, krzk+dt, ldewangan,
	linux-kernel, linux-tegra, p.zabel, robh, thierry.reding, vkoul


On 03/03/2026 08:40, Akhil R wrote:

...

>> Why is this flexible? If it is, means usually items are distinctive, so
>> I would expect defining/listing them. If they are not distinctive,
>> commit msg is incorrect. If the list is as simple as 1-to-1 channel
>> mapping, just add it in the description how they are ordered.
> 
> Yes, it is a 1-to-1 channel mapping to an IOMMU ID. The intent of making
> it flexible is to allow non-consecutive IOMMU ID assignments as well.
> This is particularly needed in virtualised environments where the
> hypervisor may reserve certain stream IDs, and the guest VM can map only
> the permitted ones. Shall I add a description here mentioning this
> use-case?

Isn't this already handled by the 'dma-channel-mask' property? The 
driver will skip over any channels that are not in specified by the mask.

Jon

-- 
nvpublic


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

* Re: [PATCH v2 2/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional
  2026-03-02 20:31   ` Frank Li
@ 2026-03-03 13:27     ` Jon Hunter
  0 siblings, 0 replies; 24+ messages in thread
From: Jon Hunter @ 2026-03-03 13:27 UTC (permalink / raw)
  To: Frank Li, Akhil R
  Cc: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Thierry Reding, Laxman Dewangan, Philipp Zabel,
	dmaengine, devicetree, linux-tegra, linux-kernel


On 02/03/2026 20:31, Frank Li wrote:
> On Mon, Mar 02, 2026 at 06:02:32PM +0530, Akhil R wrote:
>> In Tegra264, GPCDMA reset control is not exposed to Linux and is handled
>> by the boot firmware.
>>
>> Although reset was not exposed in Tegra234 as well, the firmware supported
>> a dummy reset which just returns success on reset without doing an actual
>> reset. This is also not supported in Tegra264 BPMP. Therefore mark 'reset'
>> and 'reset-names' properties as required only for devices prior to
>> Tegra264.
>>
>> This also necessitates that the Tegra264 compatible to be standalone and
>> cannot have the fallback compatible of Tegra186. Since there is no
>> functional impact, we keep reset as required for Tegra234 to avoid
>> breaking the ABI.
>>
>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>> ---
>>   .../bindings/dma/nvidia,tegra186-gpc-dma.yaml | 22 ++++++++++++++-----
>>   1 file changed, 16 insertions(+), 6 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>> index 1e7b5ddd4658..34c9b41aecfc 100644
>> --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>> +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
>> @@ -16,16 +16,13 @@ maintainers:
>>     - Rajesh Gumasta <rgumasta@nvidia.com>
>>     - Akhil R <akhilrajeev@nvidia.com>
>>
>> -allOf:
>> -  - $ref: dma-controller.yaml#
>> -
>>   properties:
>>     compatible:
>>       oneOf:
>> +      - const: nvidia,tegra264-gpcdma
>>         - const: nvidia,tegra186-gpcdma
> 
> use enum
> 
>           - enum:
>               - nvidia,tegra186-gpcdma
>               - nvidia,tegra264-gpcdma
> 
>>         - items:
>>             - enum:
>> -              - nvidia,tegra264-gpcdma
>>                 - nvidia,tegra234-gpcdma
>>                 - nvidia,tegra194-gpcdma
>>             - const: nvidia,tegra186-gpcdma
>> @@ -65,12 +62,25 @@ 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
>> +              - nvidia,tegra234-gpcdma
> 
> nvidia,tegra234-gpcdma must have fallback nvidia,tegra186-gpcdma, so
> needn't nvidia,tegra234-gpcdma here.

I guess the same is true for tegra194 as well.

Jon

-- 
nvpublic


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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-03 13:09       ` Jon Hunter
@ 2026-03-03 17:14         ` Akhil R
  2026-03-03 17:34           ` Jon Hunter
  0 siblings, 1 reply; 24+ messages in thread
From: Akhil R @ 2026-03-03 17:14 UTC (permalink / raw)
  To: jonathanh
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, krzk+dt,
	krzk, ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul

On Tue, 3 Mar 2026 13:09:00 +0000, Jon Hunter wrote:
> On 03/03/2026 08:40, Akhil R wrote:
>
> ...
>
>>> Why is this flexible? If it is, means usually items are distinctive, so
>>> I would expect defining/listing them. If they are not distinctive,
>>> commit msg is incorrect. If the list is as simple as 1-to-1 channel
>>> mapping, just add it in the description how they are ordered.
>> 
>> Yes, it is a 1-to-1 channel mapping to an IOMMU ID. The intent of making
>> it flexible is to allow non-consecutive IOMMU ID assignments as well.
>> This is particularly needed in virtualised environments where the
>> hypervisor may reserve certain stream IDs, and the guest VM can map only
>> the permitted ones. Shall I add a description here mentioning this
>> use-case?
>
> Isn't this already handled by the 'dma-channel-mask' property? The 
> driver will skip over any channels that are not in specified by the mask.

dma-channel-mask would not help if a channel is exposed, and the
corresponding IOMMU ID is not exposed. For instance say channel 15 is
available for a VM, but not the stream ID 0x80f.

Regards,
Akhil

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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-03 17:14         ` Akhil R
@ 2026-03-03 17:34           ` Jon Hunter
  2026-03-04 10:37             ` Akhil R
  0 siblings, 1 reply; 24+ messages in thread
From: Jon Hunter @ 2026-03-03 17:34 UTC (permalink / raw)
  To: Akhil R
  Cc: Frank.Li, conor+dt, devicetree, dmaengine, krzk+dt, krzk,
	ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul


On 03/03/2026 17:14, Akhil R wrote:
> On Tue, 3 Mar 2026 13:09:00 +0000, Jon Hunter wrote:
>> On 03/03/2026 08:40, Akhil R wrote:
>>
>> ...
>>
>>>> Why is this flexible? If it is, means usually items are distinctive, so
>>>> I would expect defining/listing them. If they are not distinctive,
>>>> commit msg is incorrect. If the list is as simple as 1-to-1 channel
>>>> mapping, just add it in the description how they are ordered.
>>>
>>> Yes, it is a 1-to-1 channel mapping to an IOMMU ID. The intent of making
>>> it flexible is to allow non-consecutive IOMMU ID assignments as well.
>>> This is particularly needed in virtualised environments where the
>>> hypervisor may reserve certain stream IDs, and the guest VM can map only
>>> the permitted ones. Shall I add a description here mentioning this
>>> use-case?
>>
>> Isn't this already handled by the 'dma-channel-mask' property? The
>> driver will skip over any channels that are not in specified by the mask.
> 
> dma-channel-mask would not help if a channel is exposed, and the
> corresponding IOMMU ID is not exposed. For instance say channel 15 is
> available for a VM, but not the stream ID 0x80f.

Is that a valid configuration? Above we said it is a 1-to-1 mapping 
which would imply the mapping is always constant. Ie. same channels maps 
to name SID. Is that not the case?

Jon

-- 
nvpublic


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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-03 17:34           ` Jon Hunter
@ 2026-03-04 10:37             ` Akhil R
  2026-03-04 11:10               ` Jon Hunter
  0 siblings, 1 reply; 24+ messages in thread
From: Akhil R @ 2026-03-04 10:37 UTC (permalink / raw)
  To: jonathanh
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, krzk+dt,
	krzk, ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul

On Tue, 3 Mar 2026 17:34:00 +0000, Jon Hunter wrote:
> On 03/03/2026 17:14, Akhil R wrote:
>> On Tue, 3 Mar 2026 13:09:00 +0000, Jon Hunter wrote:
>>> On 03/03/2026 08:40, Akhil R wrote:
>>>
>>> ...
>>>
>>>>> Why is this flexible? If it is, means usually items are distinctive, so
>>>>> I would expect defining/listing them. If they are not distinctive,
>>>>> commit msg is incorrect. If the list is as simple as 1-to-1 channel
>>>>> mapping, just add it in the description how they are ordered.
>>>>
>>>> Yes, it is a 1-to-1 channel mapping to an IOMMU ID. The intent of making
>>>> it flexible is to allow non-consecutive IOMMU ID assignments as well.
>>>> This is particularly needed in virtualised environments where the
>>>> hypervisor may reserve certain stream IDs, and the guest VM can map only
>>>> the permitted ones. Shall I add a description here mentioning this
>>>> use-case?
>>>
>>> Isn't this already handled by the 'dma-channel-mask' property? The
>>> driver will skip over any channels that are not in specified by the mask.
>> 
>> dma-channel-mask would not help if a channel is exposed, and the
>> corresponding IOMMU ID is not exposed. For instance say channel 15 is
> available for a VM, but not the stream ID 0x80f.
>
> Is that a valid configuration? Above we said it is a 1-to-1 mapping 
> which would imply the mapping is always constant. Ie. same channels maps 
> to name SID. Is that not the case?

I think the hypervisor configuration can determinte which stream IDs
are assigned to each VM, so the mapping can vary across platforms.
By 1-to-1, I meant that each channel maps to one IOMMU ID, but the
specific IDs themselves may not be fixed. If we prefer a constant
mapping instead, we could document that only IDs in the range 0x801 to
0x81f should be allocated to a Linux VM. Happy to go either way. Let me
know your thoughts.

Regards,
Akhil

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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-04 10:37             ` Akhil R
@ 2026-03-04 11:10               ` Jon Hunter
  2026-03-10  4:44                 ` Akhil R
  0 siblings, 1 reply; 24+ messages in thread
From: Jon Hunter @ 2026-03-04 11:10 UTC (permalink / raw)
  To: Akhil R
  Cc: Frank.Li, conor+dt, devicetree, dmaengine, krzk+dt, krzk,
	ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul


On 04/03/2026 10:37, Akhil R wrote:
> On Tue, 3 Mar 2026 17:34:00 +0000, Jon Hunter wrote:
>> On 03/03/2026 17:14, Akhil R wrote:
>>> On Tue, 3 Mar 2026 13:09:00 +0000, Jon Hunter wrote:
>>>> On 03/03/2026 08:40, Akhil R wrote:
>>>>
>>>> ...
>>>>
>>>>>> Why is this flexible? If it is, means usually items are distinctive, so
>>>>>> I would expect defining/listing them. If they are not distinctive,
>>>>>> commit msg is incorrect. If the list is as simple as 1-to-1 channel
>>>>>> mapping, just add it in the description how they are ordered.
>>>>>
>>>>> Yes, it is a 1-to-1 channel mapping to an IOMMU ID. The intent of making
>>>>> it flexible is to allow non-consecutive IOMMU ID assignments as well.
>>>>> This is particularly needed in virtualised environments where the
>>>>> hypervisor may reserve certain stream IDs, and the guest VM can map only
>>>>> the permitted ones. Shall I add a description here mentioning this
>>>>> use-case?
>>>>
>>>> Isn't this already handled by the 'dma-channel-mask' property? The
>>>> driver will skip over any channels that are not in specified by the mask.
>>>
>>> dma-channel-mask would not help if a channel is exposed, and the
>>> corresponding IOMMU ID is not exposed. For instance say channel 15 is
>> available for a VM, but not the stream ID 0x80f.
>>
>> Is that a valid configuration? Above we said it is a 1-to-1 mapping
>> which would imply the mapping is always constant. Ie. same channels maps
>> to name SID. Is that not the case?
> 
> I think the hypervisor configuration can determinte which stream IDs
> are assigned to each VM, so the mapping can vary across platforms.
> By 1-to-1, I meant that each channel maps to one IOMMU ID, but the
> specific IDs themselves may not be fixed. If we prefer a constant
> mapping instead, we could document that only IDs in the range 0x801 to
> 0x81f should be allocated to a Linux VM. Happy to go either way. Let me
> know your thoughts.

I guess I don't know what flexibility we need here. But the more 
flexible, the more complex the binding and so if we need that 
flexibility then you will need to look at how Qualcomm solved this for 
their 'iris video codec' as Krzysztof mentioned.

Jon

-- 
nvpublic


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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-04 11:10               ` Jon Hunter
@ 2026-03-10  4:44                 ` Akhil R
  2026-03-10  9:47                   ` Jon Hunter
  0 siblings, 1 reply; 24+ messages in thread
From: Akhil R @ 2026-03-10  4:44 UTC (permalink / raw)
  To: jonathanh
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, krzk+dt,
	krzk, ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul

Hi Krzysztof and Jon,

On Wed, 4 Mar 2026 11:10:55 +0000, Jon Hunter wrote:
> On 04/03/2026 10:37, Akhil R wrote:
>> On Tue, 3 Mar 2026 17:34:00 +0000, Jon Hunter wrote:
>>> On 03/03/2026 17:14, Akhil R wrote:
>>>> On Tue, 3 Mar 2026 13:09:00 +0000, Jon Hunter wrote:
>>>>> On 03/03/2026 08:40, Akhil R wrote:
>>>>>
>>>>> ...
>>>>>
>>>>>>> Why is this flexible? If it is, means usually items are distinctive, so
>>>>>>> I would expect defining/listing them. If they are not distinctive,
>>>>>>> commit msg is incorrect. If the list is as simple as 1-to-1 channel
>>>>>>> mapping, just add it in the description how they are ordered.
>>>>>>
>>>>>> Yes, it is a 1-to-1 channel mapping to an IOMMU ID. The intent of making
>>>>>> it flexible is to allow non-consecutive IOMMU ID assignments as well.
>>>>>> This is particularly needed in virtualised environments where the
>>>>>> hypervisor may reserve certain stream IDs, and the guest VM can map only
>>>>>> the permitted ones. Shall I add a description here mentioning this
>>>>>> use-case?
>>>>>
>>>>> Isn't this already handled by the 'dma-channel-mask' property? The
>>>>> driver will skip over any channels that are not in specified by the mask.
>>>>
>>>> dma-channel-mask would not help if a channel is exposed, and the
>>>> corresponding IOMMU ID is not exposed. For instance say channel 15 is
>>> available for a VM, but not the stream ID 0x80f.
>>>
>>> Is that a valid configuration? Above we said it is a 1-to-1 mapping
>>> which would imply the mapping is always constant. Ie. same channels maps
>>> to name SID. Is that not the case?
>> 
>> I think the hypervisor configuration can determinte which stream IDs
>> are assigned to each VM, so the mapping can vary across platforms.
>> By 1-to-1, I meant that each channel maps to one IOMMU ID, but the
>> specific IDs themselves may not be fixed. If we prefer a constant
>> mapping instead, we could document that only IDs in the range 0x801 to
>> 0x81f should be allocated to a Linux VM. Happy to go either way. Let me
>> know your thoughts.
> 
> I guess I don't know what flexibility we need here. But the more 
> flexible, the more complex the binding and so if we need that 
> flexibility then you will need to look at how Qualcomm solved this for 
> their 'iris video codec' as Krzysztof mentioned.

Looking at the qcom,sm8750-camss.yaml, each iommus entry is describing a
functionally distinct hardware block like IPE, JPEG, etc. Here for
GPCDMA the channels and the stream IDs are identical in hardware and there
is nothing functionally unique about any individual channel to describe.

If the channels and stream IDs are consecutive, as Frank mentioned in
the previous version, we would need only one iommu-map entry for all
the channels. In a virtualized system the hypervisor may assign
non-consecutive stream IDs, or a scattered channel mask. That would
require multiple entries.

I will document this in the description. Please let me know if it sounds
good or if you have any suggestions.

Regards,
Akhil

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

* Re: [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  2026-03-10  4:44                 ` Akhil R
@ 2026-03-10  9:47                   ` Jon Hunter
  0 siblings, 0 replies; 24+ messages in thread
From: Jon Hunter @ 2026-03-10  9:47 UTC (permalink / raw)
  To: Akhil R
  Cc: Frank.Li, conor+dt, devicetree, dmaengine, krzk+dt, krzk,
	ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul


On 10/03/2026 04:44, Akhil R wrote:

...

> Looking at the qcom,sm8750-camss.yaml, each iommus entry is describing a
> functionally distinct hardware block like IPE, JPEG, etc. Here for
> GPCDMA the channels and the stream IDs are identical in hardware and there
> is nothing functionally unique about any individual channel to describe.
> 
> If the channels and stream IDs are consecutive, as Frank mentioned in
> the previous version, we would need only one iommu-map entry for all
> the channels. In a virtualized system the hypervisor may assign
> non-consecutive stream IDs, or a scattered channel mask. That would
> require multiple entries.
> 
> I will document this in the description. Please let me know if it sounds
> good or if you have any suggestions.

Even in the non-virtualised case we can set dma-channel-mask property 
and only enable specific channels. Given that the hardware allows this 
flexibility and we could potentially have various different 
permutations. So I think that we need to have a flexible number of items 
for iommu-map. And yes, let's describe that clearer in the commit message.

Jon

-- 
nvpublic


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

end of thread, other threads:[~2026-03-10  9:47 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-02 12:32 [PATCH v2 0/9] Add GPCDMA support in Tegra264 Akhil R
2026-03-02 12:32 ` [PATCH v2 1/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
2026-03-03  6:39   ` Krzysztof Kozlowski
2026-03-03  8:40     ` Akhil R
2026-03-03  8:57       ` Krzysztof Kozlowski
2026-03-03 13:09       ` Jon Hunter
2026-03-03 17:14         ` Akhil R
2026-03-03 17:34           ` Jon Hunter
2026-03-04 10:37             ` Akhil R
2026-03-04 11:10               ` Jon Hunter
2026-03-10  4:44                 ` Akhil R
2026-03-10  9:47                   ` Jon Hunter
2026-03-02 12:32 ` [PATCH v2 2/9] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
2026-03-02 20:31   ` Frank Li
2026-03-03 13:27     ` Jon Hunter
2026-03-02 12:32 ` [PATCH v2 3/9] dmaengine: tegra: Make reset control optional Akhil R
2026-03-02 20:31   ` Frank Li
2026-03-02 12:32 ` [PATCH v2 4/9] dmaengine: tegra: Use struct for register offsets Akhil R
2026-03-02 12:32 ` [PATCH v2 5/9] dmaengine: tegra: Support address width > 39 bits Akhil R
2026-03-02 21:14   ` Frank Li
2026-03-02 12:32 ` [PATCH v2 6/9] dmaengine: tegra: Use managed DMA controller registration Akhil R
2026-03-02 12:32 ` [PATCH v2 7/9] dmaengine: tegra: Use iommu-map for stream ID Akhil R
2026-03-02 12:32 ` [PATCH v2 8/9] dmaengine: tegra: Add Tegra264 support Akhil R
2026-03-02 12:32 ` [PATCH v2 9/9] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264 Akhil R

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