From: Akhil R <akhilrajeev@nvidia.com>
To: <dmaengine@vger.kernel.org>, <linux-tegra@vger.kernel.org>
Cc: <devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<vkoul@kernel.org>, <Frank.Li@kernel.org>, <robh@kernel.org>,
<krzk+dt@kernel.org>, <conor+dt@kernel.org>,
<thierry.reding@gmail.com>, <jonathanh@nvidia.com>,
<p.zabel@pengutronix.de>, Akhil R <akhilrajeev@nvidia.com>
Subject: [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits
Date: Tue, 17 Feb 2026 23:04:54 +0530 [thread overview]
Message-ID: <20260217173457.18628-6-akhilrajeev@nvidia.com> (raw)
In-Reply-To: <20260217173457.18628-1-akhilrajeev@nvidia.com>
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
next prev parent reply other threads:[~2026-02-17 17:36 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-17 17:34 [PATCH 0/8] Add GPCDMA support in Tegra264 Akhil R
2026-02-17 17:34 ` [PATCH 1/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property Akhil R
2026-02-17 18:38 ` Rob Herring (Arm)
2026-02-17 19:53 ` Krzysztof Kozlowski
2026-02-18 9:59 ` Jon Hunter
2026-02-18 15:49 ` Rob Herring
2026-02-24 6:41 ` Akhil R
2026-02-17 17:34 ` [PATCH 2/8] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional Akhil R
2026-02-17 18:38 ` Rob Herring (Arm)
2026-02-17 17:34 ` [PATCH 3/8] dmaengine: tegra: Make reset control optional Akhil R
2026-02-17 18:04 ` Frank Li
2026-02-24 5:39 ` Akhil R
2026-02-24 17:02 ` Jon Hunter
2026-02-25 10:01 ` Akhil R
2026-02-17 17:34 ` [PATCH 4/8] dmaengine: tegra: Use struct for register offsets Akhil R
2026-02-17 18:09 ` Frank Li
2026-02-17 17:34 ` Akhil R [this message]
2026-02-17 19:44 ` [PATCH 5/8] dmaengine: tegra: Support address width > 40 bits Frank Li
2026-02-24 6:03 ` Akhil R
2026-02-17 17:34 ` [PATCH 6/8] dmaengine: tegra: Use iommu-map for stream ID Akhil R
2026-02-17 19:52 ` Frank Li
2026-02-24 6:25 ` Akhil R
2026-02-24 21:59 ` Frank Li
2026-02-25 10:27 ` Akhil R
2026-02-25 11:23 ` Jon Hunter
2026-02-26 4:25 ` Akhil R
2026-02-19 8:28 ` Dan Carpenter
2026-02-17 17:34 ` [PATCH 7/8] dmaengine: tegra: Add Tegra264 support Akhil R
2026-02-17 19:53 ` Frank Li
2026-02-17 17:34 ` [PATCH 8/8] arm64: tegra: Add iommu-map and enable GPCDMA in Tegra264 Akhil R
2026-02-17 18:02 ` Frank Li
2026-02-24 6:55 ` Akhil R
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260217173457.18628-6-akhilrajeev@nvidia.com \
--to=akhilrajeev@nvidia.com \
--cc=Frank.Li@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dmaengine@vger.kernel.org \
--cc=jonathanh@nvidia.com \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=thierry.reding@gmail.com \
--cc=vkoul@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox