[PATCH 5/8] dmaengine: tegra: Support address width > 40 bits

Akhil R posted 8 patches 1 month, 2 weeks ago
There is a newer version of this series
[PATCH 5/8] dmaengine: tegra: Support address width > 40 bits
Posted by Akhil R 1 month, 2 weeks ago
Tegra264 supports address width of 41 bits and has a separate register
to accommodate the high address. Add a device data property to specify
the number of address bits supported on a device and use that to
program the required registers.

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

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

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

So only need handle difference at one place.

Frank
> +
> +	if (tdc->tdma->chip_data->addr_bits > 40) {
> +		sg_req->src_high = upper_32_bits(src);
> +		sg_req->dst_high = upper_32_bits(dst);
> +	} else {
> +		sg_req->src_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR,
> +					      upper_32_bits(src));
> +		sg_req->dst_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR,
> +					      upper_32_bits(dst));
> +	}
> +}
> +
>  static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
>  {
>  	dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n",
> @@ -282,11 +321,22 @@ static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
>  		tdc_read(tdc, tdc->regs->status),
>  		tdc_read(tdc, tdc->regs->csre)
>  	);
> -	dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
> -		tdc_read(tdc, tdc->regs->src),
> -		tdc_read(tdc, tdc->regs->dst),
> -		tdc_read(tdc, tdc->regs->high_addr)
> -	);
> +
> +	if (tdc->tdma->chip_data->addr_bits > 40) {
> +		dev_dbg(tdc2dev(tdc), "SRC %x SRC HI %x DST %x DST HI %x\n",
> +			tdc_read(tdc, tdc->regs->src),
> +			tdc_read(tdc, tdc->regs->src_high),
> +			tdc_read(tdc, tdc->regs->dst),
> +			tdc_read(tdc, tdc->regs->dst_high)
> +		);
> +	} else {
> +		dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
> +			tdc_read(tdc, tdc->regs->src),
> +			tdc_read(tdc, tdc->regs->dst),
> +			tdc_read(tdc, tdc->regs->high_addr)
> +		);
> +	}
> +
>  	dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x WSTA %x\n",
>  		tdc_read(tdc, tdc->regs->mc_seq),
>  		tdc_read(tdc, tdc->regs->mmio_seq),
> @@ -490,9 +540,7 @@ static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
>  	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
>
>  	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
> -	tdc_write(tdc, tdc->regs->src, sg_req->src);
> -	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
> -	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
> +	tegra_dma_program_addr(tdc, sg_req);
>
>  	/* Start DMA */
>  	tdc_write(tdc, tdc->regs->csr,
> @@ -520,11 +568,9 @@ static void tegra_dma_start(struct tegra_dma_channel *tdc)
>
>  	sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
>
> +	tegra_dma_program_addr(tdc, sg_req);
>  	tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
>  	tdc_write(tdc, tdc->regs->csr, 0);
> -	tdc_write(tdc, tdc->regs->src, sg_req->src);
> -	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
> -	tdc_write(tdc, tdc->regs->high_addr, sg_req->high_addr);
>  	tdc_write(tdc, tdc->regs->fixed_pattern, sg_req->fixed_pattern);
>  	tdc_write(tdc, tdc->regs->mmio_seq, sg_req->mmio_seq);
>  	tdc_write(tdc, tdc->regs->mc_seq, sg_req->mc_seq);
> @@ -829,7 +875,7 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc,
>
>  static int get_transfer_param(struct tegra_dma_channel *tdc,
>  			      enum dma_transfer_direction direction,
> -			      u32 *apb_addr,
> +			      dma_addr_t *apb_addr,
>  			      u32 *mmio_seq,
>  			      u32 *csr,
>  			      unsigned int *burst_size,
> @@ -908,10 +954,7 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
>  	dma_desc->sg_count = 1;
>  	sg_req = dma_desc->sg_req;
>
> -	sg_req[0].src = 0;
> -	sg_req[0].dst = dest;
> -	sg_req[0].high_addr =
> -			FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
> +	tegra_dma_configure_addr(tdc, &sg_req[0], 0, dest);
>  	sg_req[0].fixed_pattern = value;
>  	/* Word count reg takes value as (N +1) words */
>  	sg_req[0].wcount = ((len - 4) >> 2);
> @@ -977,12 +1020,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
>  	dma_desc->sg_count = 1;
>  	sg_req = dma_desc->sg_req;
>
> -	sg_req[0].src = src;
> -	sg_req[0].dst = dest;
> -	sg_req[0].high_addr =
> -		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (src >> 32));
> -	sg_req[0].high_addr |=
> -		FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
> +	tegra_dma_configure_addr(tdc, &sg_req[0], src, dest);
>  	/* Word count reg takes value as (N +1) words */
>  	sg_req[0].wcount = ((len - 4) >> 2);
>  	sg_req[0].csr = csr;
> @@ -1002,7 +1040,8 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
>  	struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
>  	unsigned int max_dma_count = tdc->tdma->chip_data->max_dma_count;
>  	enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED;
> -	u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0;
> +	u32 csr, mc_seq, mmio_seq = 0;
> +	dma_addr_t apb_ptr = 0;
>  	struct tegra_dma_sg_req *sg_req;
>  	struct tegra_dma_desc *dma_desc;
>  	struct scatterlist *sg;
> @@ -1087,17 +1126,10 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
>  		mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
>  		dma_desc->bytes_req += len;
>
> -		if (direction == DMA_MEM_TO_DEV) {
> -			sg_req[i].src = mem;
> -			sg_req[i].dst = apb_ptr;
> -			sg_req[i].high_addr =
> -				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
> -		} else if (direction == DMA_DEV_TO_MEM) {
> -			sg_req[i].src = apb_ptr;
> -			sg_req[i].dst = mem;
> -			sg_req[i].high_addr =
> -				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
> -		}
> +		if (direction == DMA_MEM_TO_DEV)
> +			tegra_dma_configure_addr(tdc, &sg_req[i], mem, apb_ptr);
> +		else if (direction == DMA_DEV_TO_MEM)
> +			tegra_dma_configure_addr(tdc, &sg_req[i], apb_ptr, mem);
>
>  		/*
>  		 * Word count register takes input in words. Writing a value
> @@ -1120,7 +1152,8 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
>  			  unsigned long flags)
>  {
>  	enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED;
> -	u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0, burst_size;
> +	u32 csr, mc_seq, mmio_seq = 0, burst_size;
> +	dma_addr_t apb_ptr = 0;
>  	unsigned int max_dma_count, len, period_count, i;
>  	struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
>  	struct tegra_dma_desc *dma_desc;
> @@ -1209,17 +1242,10 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
>  	/* Split transfer equal to period size */
>  	for (i = 0; i < period_count; i++) {
>  		mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
> -		if (direction == DMA_MEM_TO_DEV) {
> -			sg_req[i].src = mem;
> -			sg_req[i].dst = apb_ptr;
> -			sg_req[i].high_addr =
> -				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
> -		} else if (direction == DMA_DEV_TO_MEM) {
> -			sg_req[i].src = apb_ptr;
> -			sg_req[i].dst = mem;
> -			sg_req[i].high_addr =
> -				FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
> -		}
> +		if (direction == DMA_MEM_TO_DEV)
> +			tegra_dma_configure_addr(tdc, &sg_req[i], mem, apb_ptr);
> +		else if (direction == DMA_DEV_TO_MEM)
> +			tegra_dma_configure_addr(tdc, &sg_req[i], apb_ptr, mem);
>  		/*
>  		 * Word count register takes input in words. Writing a value
>  		 * of N into word count register means a req of (N+1) words.
> @@ -1317,6 +1343,7 @@ static const struct tegra_dma_channel_regs tegra186_reg_offsets = {
>
>  static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
>  	.nr_channels = 32,
> +	.addr_bits = 40,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = false,
> @@ -1326,6 +1353,7 @@ static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
>
>  static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
>  	.nr_channels = 32,
> +	.addr_bits = 40,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = true,
> @@ -1335,6 +1363,7 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
>
>  static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
>  	.nr_channels = 32,
> +	.addr_bits = 41,
>  	.channel_reg_size = SZ_64K,
>  	.max_dma_count = SZ_1G,
>  	.hw_support_pause = true,
> @@ -1446,6 +1475,12 @@ static int tegra_dma_probe(struct platform_device *pdev)
>  		tdc->stream_id = stream_id;
>  	}
>
> +	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to set DMA mask: %d\n", ret);
> +		return ret;
> +	}
> +
>  	dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask);
>  	dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask);
>  	dma_cap_set(DMA_MEMCPY, tdma->dma_dev.cap_mask);
> --
> 2.50.1
>
Re: [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits
Posted by Akhil R 1 month, 1 week ago
On Tue, 17 Feb 2026 14:44:28 -0500 Frank Li wrote:
> On Tue, Feb 17, 2026 at 11:04:54PM +0530, Akhil R wrote:
>> Tegra264 supports address width of 41 bits and has a separate register
>> to accommodate the high address. Add a device data property to specify
>> the number of address bits supported on a device and use that to
>> program the required registers.
>>
>> Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
>> ---
>>  drivers/dma/tegra186-gpc-dma.c | 129 +++++++++++++++++++++------------
>>  1 file changed, 82 insertions(+), 47 deletions(-)
>>
>> diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
>> index 72701b543ceb..ce3b1dd52bb3 100644
>> --- a/drivers/dma/tegra186-gpc-dma.c
>> +++ b/drivers/dma/tegra186-gpc-dma.c
>> @@ -151,6 +151,7 @@ struct tegra_dma_channel;
>>   */
>>  struct tegra_dma_chip_data {
>>  	bool hw_support_pause;
>> +	unsigned int addr_bits;
>>  	unsigned int nr_channels;
>>  	unsigned int channel_reg_size;
>>  	unsigned int max_dma_count;
>> @@ -166,6 +167,8 @@ struct tegra_dma_channel_regs {
>>  	u32 src;
>>  	u32 dst;
>>  	u32 high_addr;
>> +	u32 src_high;
>> +	u32 dst_high;
>>  	u32 mc_seq;
>>  	u32 mmio_seq;
>>  	u32 wcount;
>> @@ -189,7 +192,8 @@ struct tegra_dma_sg_req {
>>  	u32 csr;
>>  	u32 src;
>>  	u32 dst;
>> -	u32 high_addr;
>> +	u32 src_high;
>> +	u32 dst_high;
>>  	u32 mc_seq;
>>  	u32 mmio_seq;
>>  	u32 wcount;
>> @@ -273,6 +277,41 @@ static inline struct device *tdc2dev(struct tegra_dma_channel *tdc)
>>  	return tdc->vc.chan.device->dev;
>>  }
>>
>> +static void tegra_dma_program_addr(struct tegra_dma_channel *tdc,
>> +				   struct tegra_dma_sg_req *sg_req)
>> +{
>> +	tdc_write(tdc, tdc->regs->src, sg_req->src);
>> +	tdc_write(tdc, tdc->regs->dst, sg_req->dst);
>> +
>> +	if (tdc->tdma->chip_data->addr_bits > 40) {
>> +		tdc_write(tdc, tdc->regs->src_high,
>> +			  sg_req->src_high);
>> +		tdc_write(tdc, tdc->regs->dst_high,
>> +			  sg_req->dst_high);
>> +	} else {
>> +		tdc_write(tdc, tdc->regs->high_addr,
>> +			  sg_req->src_high | sg_req->dst_high);
>> +	}
>> +}
>> +
>> +static void tegra_dma_configure_addr(struct tegra_dma_channel *tdc,
>> +				     struct tegra_dma_sg_req *sg_req,
>> +				phys_addr_t src, phys_addr_t dst)
>> +{
>> +	sg_req->src = lower_32_bits(src);
>> +	sg_req->dst = lower_32_bits(dst);
> 
> I suggest save 64bit address to sq_req.  In tegra_dma_program_addr() to
> handle difference between 40bit and 41bit.
> 
> So only need handle difference at one place.

Ack. Will update. 

Regards,
Akhil