Linux Tegra architecture development
 help / color / mirror / Atom feed
* [PATCH v5 07/10] dmaengine: tegra: Use managed DMA controller registration
From: Akhil R @ 2026-03-30 14:44 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
In-Reply-To: <20260330144456.13551-1-akhilrajeev@nvidia.com>

Switch to managed registration in probe. This simplifies the error
paths in the probe and also removes the requirement of the driver
remove function.

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

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 3ac43ad19ed6..9bea2ffb3b9e 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1483,37 +1483,27 @@ 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");
 		return ret;
 	}
 
-	ret = of_dma_controller_register(pdev->dev.of_node,
-					 tegra_dma_of_xlate, tdma);
+	ret = devm_of_dma_controller_register(&pdev->dev, pdev->dev.of_node,
+					      tegra_dma_of_xlate, tdma);
 	if (ret < 0) {
 		dev_err_probe(&pdev->dev, ret,
 			      "GPC DMA OF registration failed\n");
-
-		dma_async_device_unregister(&tdma->dma_dev);
 		return ret;
 	}
 
-	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;
 }
 
-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)
 {
 	struct tegra_dma *tdma = dev_get_drvdata(dev);
@@ -1564,7 +1554,6 @@ static struct platform_driver tegra_dma_driver = {
 		.of_match_table = tegra_dma_of_match,
 	},
 	.probe		= tegra_dma_probe,
-	.remove		= tegra_dma_remove,
 };
 
 module_platform_driver(tegra_dma_driver);
-- 
2.50.1


^ permalink raw reply related

* [PATCH v5 06/10] dmaengine: tegra: Support address width > 39 bits
From: Akhil R @ 2026-03-30 14:44 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
In-Reply-To: <20260330144456.13551-1-akhilrajeev@nvidia.com>

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>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/dma/tegra186-gpc-dma.c | 83 +++++++++++++++++++++-------------
 1 file changed, 52 insertions(+), 31 deletions(-)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index b213c4ae07d2..3ac43ad19ed6 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -146,6 +146,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;
@@ -161,6 +162,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;
@@ -179,10 +182,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;
@@ -266,6 +268,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",
@@ -274,10 +295,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),
@@ -480,9 +511,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,
@@ -510,11 +539,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);
@@ -819,7 +846,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,
@@ -897,11 +924,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);
@@ -969,10 +994,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;
@@ -992,7 +1014,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;
@@ -1080,13 +1103,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));
 		}
 
 		/*
@@ -1110,7 +1129,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;
@@ -1201,13 +1221,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
@@ -1304,6 +1320,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,
@@ -1313,6 +1330,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,
@@ -1322,6 +1340,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,
@@ -1433,6 +1452,8 @@ static int tegra_dma_probe(struct platform_device *pdev)
 		tdc->stream_id = stream_id;
 	}
 
+	dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
+
 	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

* [PATCH v5 05/10] dmaengine: tegra: Use struct for register offsets
From: Akhil R @ 2026-03-30 14:44 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
In-Reply-To: <20260330144456.13551-1-akhilrajeev@nvidia.com>

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 | 282 +++++++++++++++++----------------
 1 file changed, 142 insertions(+), 140 deletions(-)

diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index a0522a992ebc..b213c4ae07d2 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,16 +121,6 @@
 #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)
-
 /*
  * If any burst is in flight and DMA paused then this is the time to complete
  * on-flight burst and update DMA status register.
@@ -181,18 +149,24 @@ 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;
 };
 
@@ -205,7 +179,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 +209,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 +270,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 +359,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 +401,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 +438,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 +470,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 +508,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 +583,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 +655,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 +669,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 +721,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 +875,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 +898,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 +943,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 +967,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 +1031,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 +1078,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 +1093,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 +1168,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;
 
@@ -1217,24 +1199,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;
@@ -1304,11 +1286,28 @@ 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,
+};
+
 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,
 };
 
@@ -1317,6 +1316,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,
 };
 
@@ -1325,6 +1325,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,
 };
 
@@ -1345,7 +1346,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);
@@ -1353,7 +1354,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;
 }
 
@@ -1419,6 +1420,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

* [PATCH v5 04/10] dmaengine: tegra: Make reset control optional
From: Akhil R @ 2026-03-30 14:44 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
In-Reply-To: <20260330144456.13551-1-akhilrajeev@nvidia.com>

On 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 5948fbf32c21..a0522a992ebc 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -1381,10 +1381,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

* [PATCH v5 03/10] dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
From: Akhil R @ 2026-03-30 14:44 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-Reply-To: <20260330144456.13551-1-akhilrajeev@nvidia.com>

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.

Define the constraints such that if the channel and stream IDs are
contiguous, a single entry can map all the channels, but if the
channels or stream IDs are non-contiguous support multiple entries.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
---
 .../devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
index 64f1e9d9896d..bc093c783d98 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>
 
 properties:
   compatible:
@@ -49,6 +50,14 @@ properties:
   iommus:
     maxItems: 1
 
+  iommu-map:
+    description:
+      Maps DMA channel numbers to IOMMU stream IDs. A single entry can map all
+      channels when stream IDs are contiguous. In systems where the channels or
+      stream IDs are not contiguous, multiple entries may be needed.
+    minItems: 1
+    maxItems: 32
+
   dma-coherent: true
 
   dma-channel-mask:
-- 
2.50.1


^ permalink raw reply related

* [PATCH v5 02/10] arm64: tegra: Remove fallback compatible for GPCDMA
From: Akhil R @ 2026-03-30 14:44 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-Reply-To: <20260330144456.13551-1-akhilrajeev@nvidia.com>

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

Fixes: 65ef237e4810 ("arm64: tegra: Add Tegra264 support")
Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra264.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
index 24cc2c51a272..af077420d7d9 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>,
-- 
2.50.1


^ permalink raw reply related

* [PATCH v5 01/10] dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional
From: Akhil R @ 2026-03-30 14:44 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-Reply-To: <20260330144456.13551-1-akhilrajeev@nvidia.com>

On 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 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.

Fixes: bb8c97571db5 ("dt-bindings: dma: Add Tegra264 compatible string")
Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
---
 .../bindings/dma/nvidia,tegra186-gpc-dma.yaml | 23 +++++++++++++------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
index 0dabe9bbb219..64f1e9d9896d 100644
--- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml
@@ -15,16 +15,14 @@ maintainers:
   - Jon Hunter <jonathanh@nvidia.com>
   - Rajesh Gumasta <rgumasta@nvidia.com>
 
-allOf:
-  - $ref: dma-controller.yaml#
-
 properties:
   compatible:
     oneOf:
-      - const: nvidia,tegra186-gpcdma
+      - enum:
+          - nvidia,tegra264-gpcdma
+          - nvidia,tegra186-gpcdma
       - items:
           - enum:
-              - nvidia,tegra264-gpcdma
               - nvidia,tegra234-gpcdma
               - nvidia,tegra194-gpcdma
           - const: nvidia,tegra186-gpcdma
@@ -60,12 +58,23 @@ 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
+    then:
+      required:
+        - resets
+        - reset-names
+
 additionalProperties: false
 
 examples:
-- 
2.50.1


^ permalink raw reply related

* [PATCH v5 00/10] Add GPCDMA support in Tegra264
From: Akhil R @ 2026-03-30 14:44 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.

v4->v5:
- Use dev_err_probe() when returning error from the probe function.
- Remove tegra194 and tegra234 compatible from the reset 'if' condition
  in the bindings as suggested in v2 (which I missed).
v3->v4:
- Split device tree changes to two patches.
- Reordered patches to have fixes first.
- Added fixes tag to dt-bindings and device tree changes.
v2->v3:
- Add description for iommu-map property and update commit descriptions.
- Use enum for compatible string instead of const.
- Remove unused registers from struct tegra_dma_channel_regs.
- Use devm_of_dma_controller_register() to register the DMA controller.
- Remove return value check for mask setting in the driver as the bitmask
  value is always greater than 32.
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 (10):
  dt-bindings: dma: nvidia,tegra186-gpc-dma: Make reset optional
  arm64: tegra: Remove fallback compatible for GPCDMA
  dt-bindings: dma: nvidia,tegra186-gpc-dma: Add iommu-map property
  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: Enable GPCDMA in Tegra264 and add iommu-map

 .../bindings/dma/nvidia,tegra186-gpc-dma.yaml |  32 +-
 .../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, 288 insertions(+), 184 deletions(-)

-- 
2.50.1


^ permalink raw reply

* Re: [PATCH v2 2/7] pwm: tegra: Avoid hard-coded max clock frequency
From: kernel test robot @ 2026-03-30 14:36 UTC (permalink / raw)
  To: Mikko Perttunen, Thierry Reding, Uwe Kleine-König,
	Jonathan Hunter, Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: oe-kbuild-all, linux-pwm, linux-tegra, linux-kernel, devicetree,
	Yi-Wei Wang, Mikko Perttunen
In-Reply-To: <20260325-t264-pwm-v2-2-998d885984b3@nvidia.com>

Hi Mikko,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 11439c4635edd669ae435eec308f4ab8a0804808]

url:    https://github.com/intel-lab-lkp/linux/commits/Mikko-Perttunen/dt-bindings-pwm-Document-Tegra194-and-Tegra264-controllers/20260329-233356
base:   11439c4635edd669ae435eec308f4ab8a0804808
patch link:    https://lore.kernel.org/r/20260325-t264-pwm-v2-2-998d885984b3%40nvidia.com
patch subject: [PATCH v2 2/7] pwm: tegra: Avoid hard-coded max clock frequency
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260330/202603302259.NdAkuCVx-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260330/202603302259.NdAkuCVx-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603302259.NdAkuCVx-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from include/linux/kernel.h:17,
                    from include/linux/clk.h:13,
                    from drivers/pwm/pwm-tegra.c:39:
   drivers/pwm/pwm-tegra.c: In function 'tegra_pwm_probe':
>> include/linux/limits.h:26:25: warning: unsigned conversion from 'long long int' to 'long unsigned int' changes value from '9223372036854775807' to '4294967295' [-Woverflow]
      26 | #define S64_MAX         ((s64)(U64_MAX >> 1))
         |                         ^~~~~~~~~~~~~~~~~~~~~
   drivers/pwm/pwm-tegra.c:303:47: note: in expansion of macro 'S64_MAX'
     303 |         ret = dev_pm_opp_set_rate(&pdev->dev, S64_MAX);
         |                                               ^~~~~~~


vim +26 include/linux/limits.h

3c9d017cc283df Andy Shevchenko 2023-08-04  14  
54d50897d544c8 Masahiro Yamada 2019-03-07  15  #define U8_MAX		((u8)~0U)
54d50897d544c8 Masahiro Yamada 2019-03-07  16  #define S8_MAX		((s8)(U8_MAX >> 1))
54d50897d544c8 Masahiro Yamada 2019-03-07  17  #define S8_MIN		((s8)(-S8_MAX - 1))
54d50897d544c8 Masahiro Yamada 2019-03-07  18  #define U16_MAX		((u16)~0U)
54d50897d544c8 Masahiro Yamada 2019-03-07  19  #define S16_MAX		((s16)(U16_MAX >> 1))
54d50897d544c8 Masahiro Yamada 2019-03-07  20  #define S16_MIN		((s16)(-S16_MAX - 1))
54d50897d544c8 Masahiro Yamada 2019-03-07  21  #define U32_MAX		((u32)~0U)
3f50f132d8400e John Fastabend  2020-03-30  22  #define U32_MIN		((u32)0)
54d50897d544c8 Masahiro Yamada 2019-03-07  23  #define S32_MAX		((s32)(U32_MAX >> 1))
54d50897d544c8 Masahiro Yamada 2019-03-07  24  #define S32_MIN		((s32)(-S32_MAX - 1))
54d50897d544c8 Masahiro Yamada 2019-03-07  25  #define U64_MAX		((u64)~0ULL)
54d50897d544c8 Masahiro Yamada 2019-03-07 @26  #define S64_MAX		((s64)(U64_MAX >> 1))
54d50897d544c8 Masahiro Yamada 2019-03-07  27  #define S64_MIN		((s64)(-S64_MAX - 1))
54d50897d544c8 Masahiro Yamada 2019-03-07  28  

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

^ permalink raw reply

* Re: [PATCH v3] staging: nvec: fix block comment style in nvec.c
From: Greg KH @ 2026-03-30 14:29 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Oskar Ray-Frayssinet, marvin24, linux-staging, linux-tegra,
	linux-kernel
In-Reply-To: <accc-kOYHllCEnxi@orome>

On Sat, Mar 28, 2026 at 01:21:40AM +0100, Thierry Reding wrote:
> On Wed, Mar 18, 2026 at 03:59:43PM +0100, Greg KH wrote:
> > On Mon, Mar 09, 2026 at 11:07:18PM +0100, Oskar Ray-Frayssinet wrote:
> > > Fix block comment formatting to use * on subsequent lines
> > > and */ on a separate line as required by kernel coding style.
> > > 
> > > Signed-off-by: Oskar Ray-Frayssinet <rayfraytech@gmail.com>
> > > ---
> > >  drivers/staging/nvec/nvec.c | 2 +-
> > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > > 
> > > diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c
> > > index e70fafc095f2..0e655f79ea4a 100644
> > > --- a/drivers/staging/nvec/nvec.c
> > > +++ b/drivers/staging/nvec/nvec.c
> > > @@ -659,7 +659,7 @@ static irqreturn_t nvec_interrupt(int irq, void *dev)
> > >  			nvec_tx_set(nvec);
> > >  			to_send = nvec->tx->data[0];
> > >  			nvec->tx->pos = 1;
> > > -			/* Delay ACK due to AP20 HW Bug
> > > +			/* delay ACK due to AP20 HW Bug
> > >  			 * do not replace by usleep_range
> > >  			 */
> > >  			udelay(33);
> > > -- 
> > > 2.43.0
> > > 
> > > 
> > 
> > This change is not what you documented is changing :(
> 
> Hm... this is the 8th version of this patch that I've seen.
> 
> I don't know why there was a flurry of these. The checkpatch warning
> certainly isn't new, so maybe this was a new wave of janitors or
> something? Or maybe people using AI agents to get into kernel
> development. Not that it matters much, but it's not a pattern that I've
> seen before.
> 
> Also, the fact that 7 out of the 8 versions came in after the first had
> already landed in linux-next:
> 
>   29e79c66b3cc ("staging: nvec: fix block comment style in nvec_interrupt()")
> 
> suggests that people aren't using linux-next as their baseline. Do we
> need to be stricter in this regard? Seems a bit wasteful for you to have
> to spend so much time looking at duplicates, even though it seems like
> your automation did a lot of the work.

It's easy for me to reject things that obviously do not apply :)

thanks,

greg k-h

^ permalink raw reply

* Re: [PATCH v6 phy-next 03/28] usb: add missing headers transitively included by <linux/phy/phy.h>
From: Greg Kroah-Hartman @ 2026-03-30 13:19 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: linux-phy, Vinod Koul, Neil Armstrong, dri-devel, freedreno,
	linux-arm-kernel, linux-arm-msm, linux-can, linux-gpio, linux-ide,
	linux-kernel, linux-media, linux-pci, linux-renesas-soc,
	linux-riscv, linux-rockchip, linux-samsung-soc, linux-scsi,
	linux-sunxi, linux-tegra, linux-usb, netdev, spacemit,
	UNGLinuxDriver, Thinh Nguyen, Peter Chen, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam
In-Reply-To: <20260327184706.1600329-4-vladimir.oltean@nxp.com>

On Fri, Mar 27, 2026 at 08:46:41PM +0200, Vladimir Oltean wrote:
> The chipidea ci_hdrc_imx driver uses regulator consumer API like
> regulator_enable() but does not include <linux/regulator/consumer.h>.
> 
> The core USB HCD driver calls invalidate_kernel_vmap_range() and
> flush_kernel_vmap_range(), but does not include <linux/highmem.h>.
> 
> The DWC3 gadget driver calls:
> - device_property_present()
> - device_property_count_u8()
> - device_property_read_u8_array()
> but does not include <linux/property.h>
> 
> The dwc3-generic-plat driver uses of_device_get_match_data() but does
> not include <linux/of.h>.
> 
> In all these cases, the necessary includes were still provided somehow,
> directly or indirectly, through <linux/phy/phy.h>. The latter header
> wants to drop those includes, so fill in the required headers to avoid
> any breakage.
> 
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com> # dwc3
> ---
> Cc: Peter Chen <peter.chen@kernel.org>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Frank Li <Frank.Li@nxp.com>
> Cc: Sascha Hauer <s.hauer@pengutronix.de>
> Cc: Pengutronix Kernel Team <kernel@pengutronix.de>
> Cc: Fabio Estevam <festevam@gmail.com>
> Cc: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
> 
> v2->v6: none
> v1->v2: collect tag
> ---
>  drivers/usb/chipidea/ci_hdrc_imx.c   | 1 +
>  drivers/usb/core/hcd.c               | 1 +
>  drivers/usb/dwc3/dwc3-generic-plat.c | 1 +
>  drivers/usb/dwc3/gadget.c            | 1 +
>  4 files changed, 4 insertions(+)

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply

* [PATCH v3] staging: nvec: validate battery response length before memcpy
From: Sebastian Josue Alba Vives @ 2026-03-30 12:52 UTC (permalink / raw)
  To: gregkh
  Cc: marvin24, linux-staging, linux-tegra, linux-kernel, stable,
	Sebastián Alba Vives, kernel test robot

From: Sebastián Alba Vives <sebasjosue84@gmail.com>

In nvec_power_notifier(), the response length from the embedded
controller is used directly as the size argument to memcpy() when
copying battery manufacturer, model, and type strings. The
destination buffers (bat_manu, bat_model, bat_type) are fixed at
30 bytes, but res->length is a u8 that can be up to 255, allowing
a heap buffer overflow.

Additionally, if res->length is less than 2, the subtraction
res->length - 2 wraps around as an unsigned value, resulting in a
large copy that corrupts kernel heap memory.

Introduce NVEC_BAT_STRING_SIZE to replace the hardcoded buffer
size, store res->length - 2 in a local copy_len variable for
clarity, and add bounds checks before each memcpy to ensure the
copy length does not exceed the destination buffer and that
res->length is at least 2 to prevent unsigned integer underflow.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202603301722.axpoITcy-lkp@intel.com/
Tested-by: Marc Dietrich <marvin24@gmx.de>
Cc: stable@vger.kernel.org
Signed-off-by: Sebastián Alba Vives <sebasjosue84@gmail.com>
---
v3:
  - Fix build error: add missing closing brace for TYPE case
    compound statement (kernel test robot)
v2:
  - Introduce NVEC_BAT_STRING_SIZE constant (Marc Dietrich)
  - Store res->length - 2 in local copy_len variable (Marc Dietrich)
  - Use NVEC_BAT_STRING_SIZE in strncmp call for consistency
 drivers/staging/nvec/nvec_power.c | 42 +++++++++++++++++++++----------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/drivers/staging/nvec/nvec_power.c b/drivers/staging/nvec/nvec_power.c
index 2faab9fde..30719e142 100644
--- a/drivers/staging/nvec/nvec_power.c
+++ b/drivers/staging/nvec/nvec_power.c
@@ -19,6 +19,7 @@
 #include "nvec.h"
 
 #define GET_SYSTEM_STATUS 0x00
+#define NVEC_BAT_STRING_SIZE 30
 
 struct nvec_power {
 	struct notifier_block notifier;
@@ -38,9 +39,9 @@ struct nvec_power {
 	int bat_temperature;
 	int bat_cap;
 	int bat_type_enum;
-	char bat_manu[30];
-	char bat_model[30];
-	char bat_type[30];
+	char bat_manu[NVEC_BAT_STRING_SIZE];
+	char bat_model[NVEC_BAT_STRING_SIZE];
+	char bat_type[NVEC_BAT_STRING_SIZE];
 };
 
 enum {
@@ -192,26 +193,41 @@ static int nvec_power_bat_notifier(struct notifier_block *nb,
 	case TEMPERATURE:
 		power->bat_temperature = res->plu - 2732;
 		break;
-	case MANUFACTURER:
-		memcpy(power->bat_manu, &res->plc, res->length - 2);
-		power->bat_manu[res->length - 2] = '\0';
+	case MANUFACTURER: {
+		size_t copy_len = res->length - 2;
+
+		if (res->length < 2 || copy_len > NVEC_BAT_STRING_SIZE - 1)
+			break;
+		memcpy(power->bat_manu, &res->plc, copy_len);
+		power->bat_manu[copy_len] = '\0';
 		break;
-	case MODEL:
-		memcpy(power->bat_model, &res->plc, res->length - 2);
-		power->bat_model[res->length - 2] = '\0';
+	}
+	case MODEL: {
+		size_t copy_len = res->length - 2;
+
+		if (res->length < 2 || copy_len > NVEC_BAT_STRING_SIZE - 1)
+			break;
+		memcpy(power->bat_model, &res->plc, copy_len);
+		power->bat_model[copy_len] = '\0';
 		break;
-	case TYPE:
-		memcpy(power->bat_type, &res->plc, res->length - 2);
-		power->bat_type[res->length - 2] = '\0';
+	}
+	case TYPE: {
+		size_t copy_len = res->length - 2;
+
+		if (res->length < 2 || copy_len > NVEC_BAT_STRING_SIZE - 1)
+			break;
+		memcpy(power->bat_type, &res->plc, copy_len);
+		power->bat_type[copy_len] = '\0';
 		/*
 		 * This differs a little from the spec fill in more if you find
 		 * some.
 		 */
-		if (!strncmp(power->bat_type, "Li", 30))
+		if (!strncmp(power->bat_type, "Li", NVEC_BAT_STRING_SIZE))
 			power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_LION;
 		else
 			power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
 		break;
+	}
 	default:
 		return NOTIFY_STOP;
 	}
-- 
2.43.0


^ permalink raw reply related

* Re: [GIT PULL 4/7] ARM: tegra: Device tree changes for v7.1-rc1
From: Krzysztof Kozlowski @ 2026-03-30 11:46 UTC (permalink / raw)
  To: Thierry Reding, arm, soc
  Cc: Thierry Reding, Jon Hunter, linux-tegra, linux-arm-kernel
In-Reply-To: <20260329151045.1443133-4-thierry.reding@kernel.org>

On 29/03/2026 17:10, Thierry Reding wrote:
> ----------------------------------------------------------------
> ARM: tegra: Device tree changes for v7.1-rc1
> 
> Various improvements for Tegra114 boards, as well as some legacy cleanup
> for PAZ00 and Transformers devices.
> 
> ----------------------------------------------------------------
> Dmitry Torokhov (1):
>       ARM: tegra: paz00: Configure WiFi rfkill switch through device tree
> 
> Svyatoslav Ryhel (8):
>       ARM: tegra: Add SOCTHERM support on Tegra114
>       ARM: tn7: Adjust panel node
>       ARM: tegra: lg-x3: Add panel and bridge nodes
>       ARM: tegra: lg-x3: Add USB and power related nodes
>       ARM: tegra: lg-x3: Add node for capacitive buttons
>       ARM: tegra: Add ACTMON node to Tegra114 device tree
>       ARM: tegra: Add External Memory Controller node on Tegra114
>       ARM: tegra: transformers: Add connector node
> 
>  arch/arm/boot/dts/nvidia/tegra114-tn7.dts        |  13 +-
>  arch/arm/boot/dts/nvidia/tegra114.dtsi           | 221 +++++++++++++++++++++++
>  arch/arm/boot/dts/nvidia/tegra20-paz00.dts       |   8 +
>  arch/arm/boot/dts/nvidia/tegra30-asus-tf600t.dts |  21 ++-
>  arch/arm/boot/dts/nvidia/tegra30-lg-p880.dts     |  23 +++
>  arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts     |  33 ++++
>  arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi      | 174 +++++++++++++++++-
>  arch/arm/mach-tegra/Makefile                     |   2 -
>  arch/arm/mach-tegra/board-paz00.c                |  56 ------
>  arch/arm/mach-tegra/board.h                      |   2 -
>  arch/arm/mach-tegra/tegra.c                      |   4 -

Why does the DTS branch has mach code? Tag message mentions legacy
cleanup only and such cleanup should not cause mixing independent
hardware description (DTS) with drivers.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [GIT PULL 6/7] arm64: tegra: Device tree changes for v7.1-rc1
From: Krzysztof Kozlowski @ 2026-03-30 11:45 UTC (permalink / raw)
  To: Thierry Reding, arm, soc
  Cc: Thierry Reding, Jon Hunter, linux-tegra, linux-arm-kernel
In-Reply-To: <20260329151045.1443133-6-thierry.reding@kernel.org>

On 29/03/2026 17:10, Thierry Reding wrote:
> From: Thierry Reding <thierry.reding@gmail.com>
> 
> Hi ARM SoC maintainers,
> 
> The following changes since commit 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f:
> 
>   Linux 7.0-rc1 (2026-02-22 13:18:59 -0800)
> 
> are available in the Git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git tags/tegra-for-7.1-arm64-dt
> 
> for you to fetch changes up to c70e6bc11d2008fbb19695394b69fd941ab39030:
> 
>   arm64: tegra: Add Tegra264 GPIO controllers (2026-03-28 01:36:46 +0100)
> 
> Thanks,
> Thierry
> 
> ----------------------------------------------------------------
> arm64: tegra: Device tree changes for v7.1-rc1
> 
> Various fixes and new additions across a number of devices. GPIO and PCI
> are enabled on Tegra264 and the Jetson AGX Thor Developer Kit, allowing
> it to boot via network and mass storage.
> 
> ----------------------------------------------------------------
> Diogo Ivo (1):
>       arm64: tegra: smaug: Enable SPI-NOR flash
> 
> Jon Hunter (1):
>       arm64: tegra: Fix RTC aliases
> 
> Prathamesh Shete (1):
>       arm64: tegra: Add Tegra264 GPIO controllers
> 
> Thierry Reding (6):
>       dt-bindings: pci: Document the NVIDIA Tegra264 PCIe controller


This is unreviewed/unacked binding where PCI maintainers had 1 day to
react to your v3. Maybe they had more time for previous versions, but
nevertheless it is also part of other patchset, so it will get into the
kernel other tree and nothing on v3 posting:
https://lore.kernel.org/all/20260326135855.2795149-4-thierry.reding@kernel.org/
gives hints that there will be cross tree merge.

So no, that branch cannot be taken.

Please get your code first reviewed with subsystem maintainers.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [GIT PULL 1/7] dt-bindings: Changes for v7.1-rc1
From: Krzysztof Kozlowski @ 2026-03-30 11:40 UTC (permalink / raw)
  To: Thierry Reding, arm, soc
  Cc: Thierry Reding, Jon Hunter, linux-tegra, linux-arm-kernel
In-Reply-To: <406ca5ed-4a3e-48ba-94ad-d88c53b09299@kernel.org>

On 30/03/2026 13:39, Krzysztof Kozlowski wrote:
> On 29/03/2026 17:10, Thierry Reding wrote:
>> From: Thierry Reding <thierry.reding@gmail.com>
>>
>> Hi ARM SoC maintainers,
>>
>> The following changes since commit 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f:
>>
>>   Linux 7.0-rc1 (2026-02-22 13:18:59 -0800)
>>
>> are available in the Git repository at:
>>
>>   git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git tags/tegra-for-7.1-dt-bindings
>>
>> for you to fetch changes up to bed2f5b4de6c6fd8f8928f6373ad92e8795c370f:
>>
>>   dt-bindings: arm: tegra: Document Jetson AGX Thor DevKit (2026-03-28 01:05:24 +0100)
>>
>> Thanks,
>> Thierry
>>
>> ----------------------------------------------------------------
>> dt-bindings: Changes for v7.1-rc1
>>
>> This contains a few conversions to DT schema along with various
>> additions and fixes to reduce the amount of validation warnings.
>>
>> Included are also a new binding for the PCIe controller found on
>> Tegra264 as well as compatible strings for the Jetson AGX Thor
>> Developer Kit.
>>
>> ----------------------------------------------------------------
>> Sumit Gupta (1):
>>       dt-bindings: arm: tegra: Add Tegra238 CBB compatible strings
>>
>> Svyatoslav Ryhel (1):
>>       dt-bindings: display: tegra: Document Tegra20 HDMI port
>>
>> Thierry Reding (9):
>>       dt-bindings: pci: Document the NVIDIA Tegra264 PCIe controller
> 
> Why are you taking subsystem patches? This was posted on 26th of March
> and was not acked by PCI maintainers.
> 
> How the bindings should go is already documented, so there is no
> question here.
> 
> The question was whether you can take them if subsystem maintainer is
> non-responsive and yes, you can. You gave PCI maintainers one day before
> applying it.
> 
> 
>>       dt-bindings: phy: tegra-xusb: Document Type C support
> 
> No acks, but that is waiting for one month so it is fine.

Ha, no, you did not even send it to maintainers, so how they could ack it?

Nothing here was sent to the right people.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [GIT PULL 1/7] dt-bindings: Changes for v7.1-rc1
From: Krzysztof Kozlowski @ 2026-03-30 11:39 UTC (permalink / raw)
  To: Thierry Reding, arm, soc
  Cc: Thierry Reding, Jon Hunter, linux-tegra, linux-arm-kernel
In-Reply-To: <20260329151045.1443133-1-thierry.reding@kernel.org>

On 29/03/2026 17:10, Thierry Reding wrote:
> From: Thierry Reding <thierry.reding@gmail.com>
> 
> Hi ARM SoC maintainers,
> 
> The following changes since commit 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f:
> 
>   Linux 7.0-rc1 (2026-02-22 13:18:59 -0800)
> 
> are available in the Git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git tags/tegra-for-7.1-dt-bindings
> 
> for you to fetch changes up to bed2f5b4de6c6fd8f8928f6373ad92e8795c370f:
> 
>   dt-bindings: arm: tegra: Document Jetson AGX Thor DevKit (2026-03-28 01:05:24 +0100)
> 
> Thanks,
> Thierry
> 
> ----------------------------------------------------------------
> dt-bindings: Changes for v7.1-rc1
> 
> This contains a few conversions to DT schema along with various
> additions and fixes to reduce the amount of validation warnings.
> 
> Included are also a new binding for the PCIe controller found on
> Tegra264 as well as compatible strings for the Jetson AGX Thor
> Developer Kit.
> 
> ----------------------------------------------------------------
> Sumit Gupta (1):
>       dt-bindings: arm: tegra: Add Tegra238 CBB compatible strings
> 
> Svyatoslav Ryhel (1):
>       dt-bindings: display: tegra: Document Tegra20 HDMI port
> 
> Thierry Reding (9):
>       dt-bindings: pci: Document the NVIDIA Tegra264 PCIe controller

Why are you taking subsystem patches? This was posted on 26th of March
and was not acked by PCI maintainers.

How the bindings should go is already documented, so there is no
question here.

The question was whether you can take them if subsystem maintainer is
non-responsive and yes, you can. You gave PCI maintainers one day before
applying it.


>       dt-bindings: phy: tegra-xusb: Document Type C support

No acks, but that is waiting for one month so it is fine.

>       dt-bindings: clock: tegra124-dfll: Convert to json-schema
>       dt-bindings: interrupt-controller: tegra: Fix reg entries
>       dt-bindings: arm: tegra: Add missing compatible strings
>       dt-bindings: phy: tegra: Document Tegra210 USB PHY
>       dt-bindings: memory: Add Tegra210 memory controller bindings
>       dt-bindings: memory: tegra210: Mark EMC as cooling device

That's even my subsystem and I did not ack it. You did not even sent it
to me as requested by MAINTAINERS file (+dt is ignore alias), so
obviously I did not even had a chance to ack it.

And we even had few days ago talk were I explained you how these
bindings must go. Seeing pull request completely ignoring that
discussion is just huge surprise.

No, it cannot go in. Send patches to proper maintainers first.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v2] staging: nvec: validate battery response length before memcpy
From: kernel test robot @ 2026-03-30  9:36 UTC (permalink / raw)
  To: Sebastian Josue Alba Vives, marvin24
  Cc: oe-kbuild-all, ac100, linux-tegra, linux-kernel, stable,
	Sebastián Alba Vives
In-Reply-To: <20260329210800.597697-1-sebasjosue84@gmail.com>

Hi Sebastian,

kernel test robot noticed the following build errors:

[auto build test ERROR on staging/staging-testing]

url:    https://github.com/intel-lab-lkp/linux/commits/Sebastian-Josue-Alba-Vives/staging-nvec-validate-battery-response-length-before-memcpy/20260330-083704
base:   staging/staging-testing
patch link:    https://lore.kernel.org/r/20260329210800.597697-1-sebasjosue84%40gmail.com
patch subject: [PATCH v2] staging: nvec: validate battery response length before memcpy
config: arm64-randconfig-003-20260330 (https://download.01.org/0day-ci/archive/20260330/202603301722.axpoITcy-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260330/202603301722.axpoITcy-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603301722.axpoITcy-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/staging/nvec/nvec_power.c: In function 'nvec_power_bat_notifier':
>> drivers/staging/nvec/nvec_power.c:237:12: error: invalid storage class for function 'nvec_power_get_property'
    static int nvec_power_get_property(struct power_supply *psy,
               ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/staging/nvec/nvec_power.c:253:12: error: invalid storage class for function 'nvec_battery_get_property'
    static int nvec_battery_get_property(struct power_supply *psy,
               ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/staging/nvec/nvec_power.c:344:18: error: initializer element is not constant
     .get_property = nvec_battery_get_property,
                     ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/staging/nvec/nvec_power.c:344:18: note: (near initialization for 'nvec_bat_psy_desc.get_property')
   drivers/staging/nvec/nvec_power.c:352:18: error: initializer element is not constant
     .get_property = nvec_power_get_property,
                     ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/staging/nvec/nvec_power.c:352:18: note: (near initialization for 'nvec_psy_desc.get_property')
>> drivers/staging/nvec/nvec_power.c:363:13: error: invalid storage class for function 'nvec_power_poll'
    static void nvec_power_poll(struct work_struct *work)
                ^~~~~~~~~~~~~~~
>> drivers/staging/nvec/nvec_power.c:387:12: error: invalid storage class for function 'nvec_power_probe'
    static int nvec_power_probe(struct platform_device *pdev)
               ^~~~~~~~~~~~~~~~
>> drivers/staging/nvec/nvec_power.c:434:13: error: invalid storage class for function 'nvec_power_remove'
    static void nvec_power_remove(struct platform_device *pdev)
                ^~~~~~~~~~~~~~~~~
   drivers/staging/nvec/nvec_power.c:450:11: error: initializer element is not constant
     .probe = nvec_power_probe,
              ^~~~~~~~~~~~~~~~
   drivers/staging/nvec/nvec_power.c:450:11: note: (near initialization for 'nvec_power_driver.probe')
   drivers/staging/nvec/nvec_power.c:451:12: error: initializer element is not constant
     .remove = nvec_power_remove,
               ^~~~~~~~~~~~~~~~~
   drivers/staging/nvec/nvec_power.c:451:12: note: (near initialization for 'nvec_power_driver.remove')
   In file included from include/linux/device.h:32,
                    from include/linux/platform_device.h:13,
                    from drivers/staging/nvec/nvec_power.c:12:
>> drivers/staging/nvec/nvec_power.c:457:24: error: invalid storage class for function 'nvec_power_driver_init'
    module_platform_driver(nvec_power_driver);
                           ^~~~~~~~~~~~~~~~~
   include/linux/device/driver.h:267:19: note: in definition of macro 'module_driver'
    static int __init __driver##_init(void) \
                      ^~~~~~~~
   drivers/staging/nvec/nvec_power.c:457:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(nvec_power_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/build_bug.h:5,
                    from include/linux/container_of.h:5,
                    from include/linux/list.h:5,
                    from include/linux/module.h:12,
                    from drivers/staging/nvec/nvec_power.c:11:
   include/linux/compiler.h:276:44: error: initializer element is not constant
     __UNIQUE_ID(__PASTE(addressable_, sym)) = (void *)(uintptr_t)&sym;
                                               ^
   include/linux/compiler.h:279:2: note: in expansion of macro '___ADDRESSABLE'
     ___ADDRESSABLE(sym, __section(".discard.addressable"))
     ^~~~~~~~~~~~~~
   include/linux/init.h:251:2: note: in expansion of macro '__ADDRESSABLE'
     __ADDRESSABLE(fn)
     ^~~~~~~~~~~~~
   include/linux/init.h:256:2: note: in expansion of macro '__define_initcall_stub'
     __define_initcall_stub(__stub, fn)   \
     ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/init.h:269:2: note: in expansion of macro '____define_initcall'
     ____define_initcall(fn,     \
     ^~~~~~~~~~~~~~~~~~~
   include/linux/init.h:275:2: note: in expansion of macro '__unique_initcall'
     __unique_initcall(fn, id, __sec, __initcall_id(fn))
     ^~~~~~~~~~~~~~~~~
   include/linux/init.h:277:35: note: in expansion of macro '___define_initcall'
    #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
                                      ^~~~~~~~~~~~~~~~~~
   include/linux/init.h:306:30: note: in expansion of macro '__define_initcall'
    #define device_initcall(fn)  __define_initcall(fn, 6)
                                 ^~~~~~~~~~~~~~~~~
   include/linux/init.h:311:24: note: in expansion of macro 'device_initcall'
    #define __initcall(fn) device_initcall(fn)
                           ^~~~~~~~~~~~~~~
   include/linux/module.h:89:24: note: in expansion of macro '__initcall'
    #define module_init(x) __initcall(x);
                           ^~~~~~~~~~
   include/linux/device/driver.h:271:1: note: in expansion of macro 'module_init'
    module_init(__driver##_init); \
    ^~~~~~~~~~~
   include/linux/platform_device.h:295:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
   drivers/staging/nvec/nvec_power.c:457:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(nvec_power_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/device.h:32,
                    from include/linux/platform_device.h:13,
                    from drivers/staging/nvec/nvec_power.c:12:
>> drivers/staging/nvec/nvec_power.c:457:24: error: invalid storage class for function 'nvec_power_driver_exit'
    module_platform_driver(nvec_power_driver);
                           ^~~~~~~~~~~~~~~~~
   include/linux/device/driver.h:272:20: note: in definition of macro 'module_driver'
    static void __exit __driver##_exit(void) \
                       ^~~~~~~~
   drivers/staging/nvec/nvec_power.c:457:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(nvec_power_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/arm64/include/asm/alternative.h:9,
                    from arch/arm64/include/asm/lse.h:12,
                    from arch/arm64/include/asm/cmpxchg.h:14,
                    from arch/arm64/include/asm/atomic.h:16,
                    from include/linux/atomic.h:7,
                    from include/asm-generic/bitops/atomic.h:5,
                    from arch/arm64/include/asm/bitops.h:25,
                    from include/linux/bitops.h:67,
                    from arch/arm64/include/asm/cache.h:40,
                    from include/vdso/cache.h:5,
                    from include/linux/cache.h:6,
                    from include/linux/time.h:5,
                    from arch/arm64/include/asm/stat.h:12,
                    from include/linux/stat.h:6,
                    from include/linux/module.h:13,
                    from drivers/staging/nvec/nvec_power.c:11:
   drivers/staging/nvec/nvec_power.c:457:24: error: initializer element is not constant
    module_platform_driver(nvec_power_driver);
                           ^~~~~~~~~~~~~~~~~
   include/linux/init.h:314:50: note: in definition of macro '__exitcall'
     static exitcall_t __exitcall_##fn __exit_call = fn
                                                     ^~
   include/linux/device/driver.h:276:1: note: in expansion of macro 'module_exit'
    module_exit(__driver##_exit);
    ^~~~~~~~~~~
   include/linux/platform_device.h:295:2: note: in expansion of macro 'module_driver'
     module_driver(__platform_driver, platform_driver_register, \
     ^~~~~~~~~~~~~
   drivers/staging/nvec/nvec_power.c:457:1: note: in expansion of macro 'module_platform_driver'
    module_platform_driver(nvec_power_driver);
    ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/staging/nvec/nvec_power.c:462:1: error: expected declaration or statement at end of input
    MODULE_ALIAS("platform:nvec-power");
    ^~~~~~~~~~~~


vim +/nvec_power_get_property +237 drivers/staging/nvec/nvec_power.c

32890b98308613 Marc Dietrich       2011-05-19  236  
32890b98308613 Marc Dietrich       2011-05-19 @237  static int nvec_power_get_property(struct power_supply *psy,
32890b98308613 Marc Dietrich       2011-05-19  238  				   enum power_supply_property psp,
32890b98308613 Marc Dietrich       2011-05-19  239  				   union power_supply_propval *val)
32890b98308613 Marc Dietrich       2011-05-19  240  {
297d716f6260cc Krzysztof Kozlowski 2015-03-12  241  	struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
fa799617865037 Pawel Lebioda       2014-07-03  242  
32890b98308613 Marc Dietrich       2011-05-19  243  	switch (psp) {
32890b98308613 Marc Dietrich       2011-05-19  244  	case POWER_SUPPLY_PROP_ONLINE:
32890b98308613 Marc Dietrich       2011-05-19  245  		val->intval = power->on;
32890b98308613 Marc Dietrich       2011-05-19  246  		break;
32890b98308613 Marc Dietrich       2011-05-19  247  	default:
32890b98308613 Marc Dietrich       2011-05-19  248  		return -EINVAL;
32890b98308613 Marc Dietrich       2011-05-19  249  	}
32890b98308613 Marc Dietrich       2011-05-19  250  	return 0;
32890b98308613 Marc Dietrich       2011-05-19  251  }
32890b98308613 Marc Dietrich       2011-05-19  252  
32890b98308613 Marc Dietrich       2011-05-19 @253  static int nvec_battery_get_property(struct power_supply *psy,
32890b98308613 Marc Dietrich       2011-05-19  254  				     enum power_supply_property psp,
32890b98308613 Marc Dietrich       2011-05-19  255  				     union power_supply_propval *val)
32890b98308613 Marc Dietrich       2011-05-19  256  {
297d716f6260cc Krzysztof Kozlowski 2015-03-12  257  	struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
32890b98308613 Marc Dietrich       2011-05-19  258  
162c7d8c4be2d5 Marc Dietrich       2011-09-27  259  	switch (psp) {
32890b98308613 Marc Dietrich       2011-05-19  260  	case POWER_SUPPLY_PROP_STATUS:
32890b98308613 Marc Dietrich       2011-05-19  261  		val->intval = power->bat_status;
32890b98308613 Marc Dietrich       2011-05-19  262  		break;
32890b98308613 Marc Dietrich       2011-05-19  263  	case POWER_SUPPLY_PROP_CAPACITY:
32890b98308613 Marc Dietrich       2011-05-19  264  		val->intval = power->bat_cap;
32890b98308613 Marc Dietrich       2011-05-19  265  		break;
32890b98308613 Marc Dietrich       2011-05-19  266  	case POWER_SUPPLY_PROP_PRESENT:
32890b98308613 Marc Dietrich       2011-05-19  267  		val->intval = power->bat_present;
32890b98308613 Marc Dietrich       2011-05-19  268  		break;
32890b98308613 Marc Dietrich       2011-05-19  269  	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
32890b98308613 Marc Dietrich       2011-05-19  270  		val->intval = power->bat_voltage_now;
32890b98308613 Marc Dietrich       2011-05-19  271  		break;
32890b98308613 Marc Dietrich       2011-05-19  272  	case POWER_SUPPLY_PROP_CURRENT_NOW:
32890b98308613 Marc Dietrich       2011-05-19  273  		val->intval = power->bat_current_now;
32890b98308613 Marc Dietrich       2011-05-19  274  		break;
32890b98308613 Marc Dietrich       2011-05-19  275  	case POWER_SUPPLY_PROP_CURRENT_AVG:
32890b98308613 Marc Dietrich       2011-05-19  276  		val->intval = power->bat_current_avg;
32890b98308613 Marc Dietrich       2011-05-19  277  		break;
32890b98308613 Marc Dietrich       2011-05-19  278  	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
32890b98308613 Marc Dietrich       2011-05-19  279  		val->intval = power->time_remain;
32890b98308613 Marc Dietrich       2011-05-19  280  		break;
32890b98308613 Marc Dietrich       2011-05-19  281  	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
32890b98308613 Marc Dietrich       2011-05-19  282  		val->intval = power->charge_full_design;
32890b98308613 Marc Dietrich       2011-05-19  283  		break;
32890b98308613 Marc Dietrich       2011-05-19  284  	case POWER_SUPPLY_PROP_CHARGE_FULL:
32890b98308613 Marc Dietrich       2011-05-19  285  		val->intval = power->charge_last_full;
32890b98308613 Marc Dietrich       2011-05-19  286  		break;
32890b98308613 Marc Dietrich       2011-05-19  287  	case POWER_SUPPLY_PROP_CHARGE_EMPTY:
32890b98308613 Marc Dietrich       2011-05-19  288  		val->intval = power->critical_capacity;
32890b98308613 Marc Dietrich       2011-05-19  289  		break;
32890b98308613 Marc Dietrich       2011-05-19  290  	case POWER_SUPPLY_PROP_CHARGE_NOW:
32890b98308613 Marc Dietrich       2011-05-19  291  		val->intval = power->capacity_remain;
32890b98308613 Marc Dietrich       2011-05-19  292  		break;
32890b98308613 Marc Dietrich       2011-05-19  293  	case POWER_SUPPLY_PROP_TEMP:
32890b98308613 Marc Dietrich       2011-05-19  294  		val->intval = power->bat_temperature;
32890b98308613 Marc Dietrich       2011-05-19  295  		break;
32890b98308613 Marc Dietrich       2011-05-19  296  	case POWER_SUPPLY_PROP_MANUFACTURER:
32890b98308613 Marc Dietrich       2011-05-19  297  		val->strval = power->bat_manu;
32890b98308613 Marc Dietrich       2011-05-19  298  		break;
32890b98308613 Marc Dietrich       2011-05-19  299  	case POWER_SUPPLY_PROP_MODEL_NAME:
32890b98308613 Marc Dietrich       2011-05-19  300  		val->strval = power->bat_model;
32890b98308613 Marc Dietrich       2011-05-19  301  		break;
32890b98308613 Marc Dietrich       2011-05-19  302  	case POWER_SUPPLY_PROP_TECHNOLOGY:
32890b98308613 Marc Dietrich       2011-05-19  303  		val->intval = power->bat_type_enum;
32890b98308613 Marc Dietrich       2011-05-19  304  		break;
32890b98308613 Marc Dietrich       2011-05-19  305  	default:
32890b98308613 Marc Dietrich       2011-05-19  306  		return -EINVAL;
32890b98308613 Marc Dietrich       2011-05-19  307  	}
32890b98308613 Marc Dietrich       2011-05-19  308  	return 0;
32890b98308613 Marc Dietrich       2011-05-19  309  }
32890b98308613 Marc Dietrich       2011-05-19  310  
32890b98308613 Marc Dietrich       2011-05-19  311  static enum power_supply_property nvec_power_props[] = {
32890b98308613 Marc Dietrich       2011-05-19  312  	POWER_SUPPLY_PROP_ONLINE,
32890b98308613 Marc Dietrich       2011-05-19  313  };
32890b98308613 Marc Dietrich       2011-05-19  314  
32890b98308613 Marc Dietrich       2011-05-19  315  static enum power_supply_property nvec_battery_props[] = {
32890b98308613 Marc Dietrich       2011-05-19  316  	POWER_SUPPLY_PROP_STATUS,
32890b98308613 Marc Dietrich       2011-05-19  317  	POWER_SUPPLY_PROP_PRESENT,
32890b98308613 Marc Dietrich       2011-05-19  318  	POWER_SUPPLY_PROP_CAPACITY,
32890b98308613 Marc Dietrich       2011-05-19  319  	POWER_SUPPLY_PROP_VOLTAGE_NOW,
32890b98308613 Marc Dietrich       2011-05-19  320  	POWER_SUPPLY_PROP_CURRENT_NOW,
32890b98308613 Marc Dietrich       2011-05-19  321  #ifdef EC_FULL_DIAG
32890b98308613 Marc Dietrich       2011-05-19  322  	POWER_SUPPLY_PROP_CURRENT_AVG,
32890b98308613 Marc Dietrich       2011-05-19  323  	POWER_SUPPLY_PROP_TEMP,
32890b98308613 Marc Dietrich       2011-05-19  324  	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
32890b98308613 Marc Dietrich       2011-05-19  325  #endif
32890b98308613 Marc Dietrich       2011-05-19  326  	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
32890b98308613 Marc Dietrich       2011-05-19  327  	POWER_SUPPLY_PROP_CHARGE_FULL,
32890b98308613 Marc Dietrich       2011-05-19  328  	POWER_SUPPLY_PROP_CHARGE_EMPTY,
32890b98308613 Marc Dietrich       2011-05-19  329  	POWER_SUPPLY_PROP_CHARGE_NOW,
32890b98308613 Marc Dietrich       2011-05-19  330  	POWER_SUPPLY_PROP_MANUFACTURER,
32890b98308613 Marc Dietrich       2011-05-19  331  	POWER_SUPPLY_PROP_MODEL_NAME,
32890b98308613 Marc Dietrich       2011-05-19  332  	POWER_SUPPLY_PROP_TECHNOLOGY,
32890b98308613 Marc Dietrich       2011-05-19  333  };
32890b98308613 Marc Dietrich       2011-05-19  334  
32890b98308613 Marc Dietrich       2011-05-19  335  static char *nvec_power_supplied_to[] = {
32890b98308613 Marc Dietrich       2011-05-19  336  	"battery",
32890b98308613 Marc Dietrich       2011-05-19  337  };
32890b98308613 Marc Dietrich       2011-05-19  338  
297d716f6260cc Krzysztof Kozlowski 2015-03-12  339  static const struct power_supply_desc nvec_bat_psy_desc = {
32890b98308613 Marc Dietrich       2011-05-19  340  	.name = "battery",
32890b98308613 Marc Dietrich       2011-05-19  341  	.type = POWER_SUPPLY_TYPE_BATTERY,
32890b98308613 Marc Dietrich       2011-05-19  342  	.properties = nvec_battery_props,
32890b98308613 Marc Dietrich       2011-05-19  343  	.num_properties = ARRAY_SIZE(nvec_battery_props),
32890b98308613 Marc Dietrich       2011-05-19 @344  	.get_property = nvec_battery_get_property,
32890b98308613 Marc Dietrich       2011-05-19  345  };
32890b98308613 Marc Dietrich       2011-05-19  346  
297d716f6260cc Krzysztof Kozlowski 2015-03-12  347  static const struct power_supply_desc nvec_psy_desc = {
32890b98308613 Marc Dietrich       2011-05-19  348  	.name = "ac",
32890b98308613 Marc Dietrich       2011-05-19  349  	.type = POWER_SUPPLY_TYPE_MAINS,
32890b98308613 Marc Dietrich       2011-05-19  350  	.properties = nvec_power_props,
32890b98308613 Marc Dietrich       2011-05-19  351  	.num_properties = ARRAY_SIZE(nvec_power_props),
32890b98308613 Marc Dietrich       2011-05-19  352  	.get_property = nvec_power_get_property,
32890b98308613 Marc Dietrich       2011-05-19  353  };
32890b98308613 Marc Dietrich       2011-05-19  354  
162c7d8c4be2d5 Marc Dietrich       2011-09-27  355  static int counter;
dc31fc6ce69e03 Fatih Yildirim      2021-02-12  356  static const int bat_iter[] = {
32890b98308613 Marc Dietrich       2011-05-19  357  	SLOT_STATUS, VOLTAGE, CURRENT, CAPACITY_REMAINING,
32890b98308613 Marc Dietrich       2011-05-19  358  #ifdef EC_FULL_DIAG
32890b98308613 Marc Dietrich       2011-05-19  359  	AVERAGE_CURRENT, TEMPERATURE, TIME_REMAINING,
32890b98308613 Marc Dietrich       2011-05-19  360  #endif
32890b98308613 Marc Dietrich       2011-05-19  361  };
32890b98308613 Marc Dietrich       2011-05-19  362  
32890b98308613 Marc Dietrich       2011-05-19 @363  static void nvec_power_poll(struct work_struct *work)
32890b98308613 Marc Dietrich       2011-05-19  364  {
93eff83ff1640b Marc Dietrich       2013-01-27  365  	char buf[] = { NVEC_SYS, GET_SYSTEM_STATUS };
32890b98308613 Marc Dietrich       2011-05-19  366  	struct nvec_power *power = container_of(work, struct nvec_power,
32890b98308613 Marc Dietrich       2011-05-19  367  						poller.work);
32890b98308613 Marc Dietrich       2011-05-19  368  
32890b98308613 Marc Dietrich       2011-05-19  369  	if (counter >= ARRAY_SIZE(bat_iter))
32890b98308613 Marc Dietrich       2011-05-19  370  		counter = 0;
32890b98308613 Marc Dietrich       2011-05-19  371  
32890b98308613 Marc Dietrich       2011-05-19  372  	/* AC status via sys req */
32890b98308613 Marc Dietrich       2011-05-19  373  	nvec_write_async(power->nvec, buf, 2);
32890b98308613 Marc Dietrich       2011-05-19  374  	msleep(100);
32890b98308613 Marc Dietrich       2011-05-19  375  
66ad85d13f56b4 Simon Guinot        2015-12-09  376  	/*
66ad85d13f56b4 Simon Guinot        2015-12-09  377  	 * Select a battery request function via round robin doing it all at
66ad85d13f56b4 Simon Guinot        2015-12-09  378  	 * once seems to overload the power supply.
66ad85d13f56b4 Simon Guinot        2015-12-09  379  	 */
93eff83ff1640b Marc Dietrich       2013-01-27  380  	buf[0] = NVEC_BAT;
32890b98308613 Marc Dietrich       2011-05-19  381  	buf[1] = bat_iter[counter++];
32890b98308613 Marc Dietrich       2011-05-19  382  	nvec_write_async(power->nvec, buf, 2);
32890b98308613 Marc Dietrich       2011-05-19  383  
32890b98308613 Marc Dietrich       2011-05-19  384  	schedule_delayed_work(to_delayed_work(work), msecs_to_jiffies(5000));
32890b98308613 Marc Dietrich       2011-05-19  385  };
32890b98308613 Marc Dietrich       2011-05-19  386  
46620803c309d2 Bill Pemberton      2012-11-19 @387  static int nvec_power_probe(struct platform_device *pdev)
32890b98308613 Marc Dietrich       2011-05-19  388  {
297d716f6260cc Krzysztof Kozlowski 2015-03-12  389  	struct power_supply **psy;
297d716f6260cc Krzysztof Kozlowski 2015-03-12  390  	const struct power_supply_desc *psy_desc;
f5e3352e5185ef Marc Dietrich       2012-06-24  391  	struct nvec_power *power;
32890b98308613 Marc Dietrich       2011-05-19  392  	struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
2dc9215d7c94f7 Krzysztof Kozlowski 2015-03-12  393  	struct power_supply_config psy_cfg = {};
32890b98308613 Marc Dietrich       2011-05-19  394  
f5e3352e5185ef Marc Dietrich       2012-06-24  395  	power = devm_kzalloc(&pdev->dev, sizeof(struct nvec_power), GFP_NOWAIT);
4c42d979816a0b Somya Anand         2015-03-16  396  	if (!power)
f5e3352e5185ef Marc Dietrich       2012-06-24  397  		return -ENOMEM;
f5e3352e5185ef Marc Dietrich       2012-06-24  398  
32890b98308613 Marc Dietrich       2011-05-19  399  	dev_set_drvdata(&pdev->dev, power);
32890b98308613 Marc Dietrich       2011-05-19  400  	power->nvec = nvec;
32890b98308613 Marc Dietrich       2011-05-19  401  
32890b98308613 Marc Dietrich       2011-05-19  402  	switch (pdev->id) {
32890b98308613 Marc Dietrich       2011-05-19  403  	case AC:
32890b98308613 Marc Dietrich       2011-05-19  404  		psy = &nvec_psy;
297d716f6260cc Krzysztof Kozlowski 2015-03-12  405  		psy_desc = &nvec_psy_desc;
2dc9215d7c94f7 Krzysztof Kozlowski 2015-03-12  406  		psy_cfg.supplied_to = nvec_power_supplied_to;
2dc9215d7c94f7 Krzysztof Kozlowski 2015-03-12  407  		psy_cfg.num_supplicants = ARRAY_SIZE(nvec_power_supplied_to);
32890b98308613 Marc Dietrich       2011-05-19  408  
32890b98308613 Marc Dietrich       2011-05-19  409  		power->notifier.notifier_call = nvec_power_notifier;
32890b98308613 Marc Dietrich       2011-05-19  410  
32890b98308613 Marc Dietrich       2011-05-19  411  		INIT_DELAYED_WORK(&power->poller, nvec_power_poll);
32890b98308613 Marc Dietrich       2011-05-19  412  		schedule_delayed_work(&power->poller, msecs_to_jiffies(5000));
32890b98308613 Marc Dietrich       2011-05-19  413  		break;
32890b98308613 Marc Dietrich       2011-05-19  414  	case BAT:
32890b98308613 Marc Dietrich       2011-05-19  415  		psy = &nvec_bat_psy;
297d716f6260cc Krzysztof Kozlowski 2015-03-12  416  		psy_desc = &nvec_bat_psy_desc;
32890b98308613 Marc Dietrich       2011-05-19  417  
32890b98308613 Marc Dietrich       2011-05-19  418  		power->notifier.notifier_call = nvec_power_bat_notifier;
32890b98308613 Marc Dietrich       2011-05-19  419  		break;
32890b98308613 Marc Dietrich       2011-05-19  420  	default:
32890b98308613 Marc Dietrich       2011-05-19  421  		return -ENODEV;
32890b98308613 Marc Dietrich       2011-05-19  422  	}
32890b98308613 Marc Dietrich       2011-05-19  423  
32890b98308613 Marc Dietrich       2011-05-19  424  	nvec_register_notifier(nvec, &power->notifier, NVEC_SYS);
32890b98308613 Marc Dietrich       2011-05-19  425  
32890b98308613 Marc Dietrich       2011-05-19  426  	if (pdev->id == BAT)
32890b98308613 Marc Dietrich       2011-05-19  427  		get_bat_mfg_data(power);
32890b98308613 Marc Dietrich       2011-05-19  428  
297d716f6260cc Krzysztof Kozlowski 2015-03-12  429  	*psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
297d716f6260cc Krzysztof Kozlowski 2015-03-12  430  
297d716f6260cc Krzysztof Kozlowski 2015-03-12  431  	return PTR_ERR_OR_ZERO(*psy);
32890b98308613 Marc Dietrich       2011-05-19  432  }
32890b98308613 Marc Dietrich       2011-05-19  433  
f1e870c45be5b6 Uwe Kleine-König    2023-04-03 @434  static void nvec_power_remove(struct platform_device *pdev)
3cdde3a3d55e64 Marc Dietrich       2012-06-24  435  {
3cdde3a3d55e64 Marc Dietrich       2012-06-24  436  	struct nvec_power *power = platform_get_drvdata(pdev);
3cdde3a3d55e64 Marc Dietrich       2012-06-24  437  
3cdde3a3d55e64 Marc Dietrich       2012-06-24  438  	cancel_delayed_work_sync(&power->poller);
c2b62f60f67e03 Marc Dietrich       2013-04-29  439  	nvec_unregister_notifier(power->nvec, &power->notifier);
3cdde3a3d55e64 Marc Dietrich       2012-06-24  440  	switch (pdev->id) {
3cdde3a3d55e64 Marc Dietrich       2012-06-24  441  	case AC:
297d716f6260cc Krzysztof Kozlowski 2015-03-12  442  		power_supply_unregister(nvec_psy);
3cdde3a3d55e64 Marc Dietrich       2012-06-24  443  		break;
3cdde3a3d55e64 Marc Dietrich       2012-06-24  444  	case BAT:
297d716f6260cc Krzysztof Kozlowski 2015-03-12  445  		power_supply_unregister(nvec_bat_psy);
3cdde3a3d55e64 Marc Dietrich       2012-06-24  446  	}
3cdde3a3d55e64 Marc Dietrich       2012-06-24  447  }
3cdde3a3d55e64 Marc Dietrich       2012-06-24  448  

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

^ permalink raw reply

* Re: [PATCH v4 08/10] dmaengine: tegra: Use iommu-map for stream ID
From: Akhil R @ 2026-03-30  9:03 UTC (permalink / raw)
  To: frank.li
  Cc: Frank.Li, akhilrajeev, conor+dt, devicetree, dmaengine, jonathanh,
	krzk+dt, ldewangan, linux-kernel, linux-tegra, p.zabel, robh,
	thierry.reding, vkoul
In-Reply-To: <acWWW6r5gZ2nGerQ@lizhi-Precision-Tower-5810>

On Thu, 26 Mar 2026 16:26:03 -0400, Frank Li wrote:
> On Thu, Mar 26, 2026 at 04:39:45PM +0530, Akhil R wrote:
>> 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
>> prerequisites 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>
>> ---
> ...
>> @@ -1490,6 +1496,41 @@ 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;
>> +			dma_coerce_mask_and_coherent(chdev, DMA_BIT_MASK(cdata->addr_bits));
>> +
>> +			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;
> 
> This is in probe funciton
> 
> 	return dev_err_probe();

Ack. I will update.

Best Regards,
Akhil

^ permalink raw reply

* [PATCH v3 7/7] arm64: tegra: Add PWM controllers on Tegra264
From: Mikko Perttunen @ 2026-03-30  8:53 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König, Jonathan Hunter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-pwm, linux-tegra, linux-kernel, devicetree, Thierry Reding,
	Mikko Perttunen
In-Reply-To: <20260330-t264-pwm-v3-0-5714427d5976@nvidia.com>

From: Thierry Reding <treding@nvidia.com>

Tegra264 has a number of PWM controllers that are similar but
incompatible with those found on earlier chips.

Signed-off-by: Thierry Reding <treding@nvidia.com>
[mperttunen: Adjust commit message]
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra264.dtsi | 72 ++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
index 7644a41d5f72..13fd04068016 100644
--- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
@@ -3336,6 +3336,18 @@ i2c3: i2c@c610000 {
 			status = "disabled";
 		};
 
+		pwm4: pwm@c6a0000 {
+			compatible = "nvidia,tegra264-pwm";
+			reg = <0x0 0xc6a0000 0x0 0x10000>;
+			status = "disabled";
+
+			clocks = <&bpmp TEGRA264_CLK_PWM4>;
+			resets = <&bpmp TEGRA264_RESET_PWM4>;
+			reset-names = "pwm";
+
+			#pwm-cells = <2>;
+		};
+
 		pmc: pmc@c800000 {
 			compatible = "nvidia,tegra264-pmc";
 			reg = <0x0 0x0c800000 0x0 0x100000>,
@@ -3538,6 +3550,66 @@ i2c16: i2c@c430000 {
 			status = "disabled";
 		};
 
+		pwm2: pwm@c5e0000 {
+			compatible = "nvidia,tegra264-pwm";
+			reg = <0x0 0xc5e0000 0x0 0x10000>;
+			status = "disabled";
+
+			clocks = <&bpmp TEGRA264_CLK_PWM2>;
+			resets = <&bpmp TEGRA264_RESET_PWM2>;
+			reset-names = "pwm";
+
+			#pwm-cells = <2>;
+		};
+
+		pwm3: pwm@c5f0000 {
+			compatible = "nvidia,tegra264-pwm";
+			reg = <0x0 0xc5f0000 0x0 0x10000>;
+			status = "disabled";
+
+			clocks = <&bpmp TEGRA264_CLK_PWM3>;
+			resets = <&bpmp TEGRA264_RESET_PWM3>;
+			reset-names = "pwm";
+
+			#pwm-cells = <2>;
+		};
+
+		pwm5: pwm@c600000 {
+			compatible = "nvidia,tegra264-pwm";
+			reg = <0x0 0xc600000 0x0 0x10000>;
+			status = "disabled";
+
+			clocks = <&bpmp TEGRA264_CLK_PWM5>;
+			resets = <&bpmp TEGRA264_RESET_PWM5>;
+			reset-names = "pwm";
+
+			#pwm-cells = <2>;
+		};
+
+		pwm9: pwm@c610000 {
+			compatible = "nvidia,tegra264-pwm";
+			reg = <0x0 0xc610000 0x0 0x10000>;
+			status = "disabled";
+
+			clocks = <&bpmp TEGRA264_CLK_PWM9>;
+			resets = <&bpmp TEGRA264_RESET_PWM9>;
+			reset-names = "pwm";
+
+			#pwm-cells = <2>;
+		};
+
+		pwm10: pwm@c620000 {
+			compatible = "nvidia,tegra264-pwm";
+			reg = <0x0 0xc620000 0x0 0x10000>;
+			status = "disabled";
+
+			clocks = <&bpmp TEGRA264_CLK_PWM10>;
+			resets = <&bpmp TEGRA264_RESET_PWM10>;
+			reset-names = "pwm";
+
+			#pwm-cells = <2>;
+		};
+
 		i2c0: i2c@c630000 {
 			compatible = "nvidia,tegra264-i2c";
 			reg = <0x00 0x0c630000 0x0 0x10000>;

-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 6/7] pwm: tegra: Add support for Tegra264
From: Mikko Perttunen @ 2026-03-30  8:53 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König, Jonathan Hunter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-pwm, linux-tegra, linux-kernel, devicetree, Yi-Wei Wang,
	Mikko Perttunen
In-Reply-To: <20260330-t264-pwm-v3-0-5714427d5976@nvidia.com>

Tegra264 changes the register layout to accommodate wider fields
for duty and scale, and adds configurable depth which will be
supported in a later patch.

Add SoC data and update top comment to describe register layout
in more detail.

Co-developed-by: Yi-Wei Wang <yiweiw@nvidia.com>
Signed-off-by: Yi-Wei Wang <yiweiw@nvidia.com>
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
 drivers/pwm/pwm-tegra.c | 75 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 61 insertions(+), 14 deletions(-)

diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 857301baad51..c1e8a804d783 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -7,22 +7,60 @@
  * Copyright (c) 2010-2020, NVIDIA Corporation.
  * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de>
  *
- * Overview of Tegra Pulse Width Modulator Register:
- * 1. 13-bit: Frequency division (SCALE)
- * 2. 8-bit : Pulse division (DUTY)
- * 3. 1-bit : Enable bit
+ * Overview of Tegra Pulse Width Modulator Register
+ * CSR_0 of Tegra20, Tegra186, and Tegra194:
+ * +-------+-------+-----------------------------------------------------------+
+ * | Bit   | Field | Description                                               |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 31    | ENB   | Enable Pulse width modulator.                             |
+ * |       |       | 0 = DISABLE, 1 = ENABLE.                                  |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 30:16 | PWM_0 | Pulse width that needs to be programmed.                  |
+ * |       |       | 0 = Always low.                                           |
+ * |       |       | 1 = 1 / 256 pulse high.                                   |
+ * |       |       | 2 = 2 / 256 pulse high.                                   |
+ * |       |       | N = N / 256 pulse high.                                   |
+ * |       |       | Only 8 bits are usable [23:16].                           |
+ * |       |       | Bit[24] can be programmed to 1 to achieve 100% duty       |
+ * |       |       | cycle. In this case the other bits [23:16] are set to     |
+ * |       |       | don’t care.                                               |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 12:0  | PFM_0 | Frequency divider that needs to be programmed, also known |
+ * |       |       | as SCALE. Division by (1 + PFM_0).                        |
+ * +-------+-------+-----------------------------------------------------------+
  *
- * The PWM clock frequency is divided by 256 before subdividing it based
- * on the programmable frequency division value to generate the required
- * frequency for PWM output. The maximum output frequency that can be
- * achieved is (max rate of source clock) / 256.
- * e.g. if source clock rate is 408 MHz, maximum output frequency can be:
- * 408 MHz/256 = 1.6 MHz.
- * This 1.6 MHz frequency can further be divided using SCALE value in PWM.
+ * CSR_0 of Tegra264:
+ * +-------+-------+-----------------------------------------------------------+
+ * | Bit   | Field | Description                                               |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 31:16 | PWM_0 | Pulse width that needs to be programmed.                  |
+ * |       |       | 0 = Always low.                                           |
+ * |       |       | 1 = 1 / (1 + CSR_1.DEPTH) pulse high.                     |
+ * |       |       | 2 = 2 / (1 + CSR_1.DEPTH) pulse high.                     |
+ * |       |       | N = N / (1 + CSR_1.DEPTH) pulse high.                     |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 15:0  | PFM_0 | Frequency divider that needs to be programmed, also known |
+ * |       |       | as SCALE. Division by (1 + PFM_0).                        |
+ * +-------+-------+-----------------------------------------------------------+
+ *
+ * CSR_1 of Tegra264:
+ * +-------+-------+-----------------------------------------------------------+
+ * | Bit   | Field | Description                                               |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 31    | ENB   | Enable Pulse width modulator.                             |
+ * |       |       | 0 = DISABLE, 1 = ENABLE.                                  |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 30:15 | DEPTH | Depth for pulse width modulator. This controls the pulse  |
+ * |       |       | time generated. Division by (1 + CSR_1.DEPTH).            |
+ * +-------+-------+-----------------------------------------------------------+
  *
- * PWM pulse width: 8 bits are usable [23:16] for varying pulse width.
- * To achieve 100% duty cycle, program Bit [24] of this register to
- * 1’b1. In which case the other bits [23:16] are set to don't care.
+ * The PWM clock frequency is divided by DEPTH = (1 + CSR_1.DEPTH) before subdividing it
+ * based on the programmable frequency division value to generate the required frequency
+ * for PWM output. DEPTH is fixed to 256 before Tegra264. The maximum output frequency
+ * that can be achieved is (max rate of source clock) / DEPTH.
+ * e.g. if source clock rate is 408 MHz, and DEPTH = 256, maximum output frequency can be:
+ * 408 MHz / 256 ~= 1.6 MHz.
+ * This 1.6 MHz frequency can further be divided using SCALE value in PWM.
  *
  * Limitations:
  * -	When PWM is disabled, the output is driven to inactive.
@@ -56,6 +94,7 @@
 #define PWM_SCALE_SHIFT	0
 
 #define PWM_CSR_0	0
+#define PWM_CSR_1	4
 
 #define PWM_DEPTH	256
 
@@ -418,10 +457,18 @@ static const struct tegra_pwm_soc tegra186_pwm_soc = {
 	.scale_width = 13,
 };
 
+static const struct tegra_pwm_soc tegra264_pwm_soc = {
+	.num_channels = 1,
+	.enable_reg = PWM_CSR_1,
+	.duty_width = 16,
+	.scale_width = 16,
+};
+
 static const struct of_device_id tegra_pwm_of_match[] = {
 	{ .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc },
 	{ .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc },
 	{ .compatible = "nvidia,tegra194-pwm", .data = &tegra186_pwm_soc },
+	{ .compatible = "nvidia,tegra264-pwm", .data = &tegra264_pwm_soc },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);

-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 5/7] pwm: tegra: Parametrize duty and scale field widths
From: Mikko Perttunen @ 2026-03-30  8:53 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König, Jonathan Hunter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-pwm, linux-tegra, linux-kernel, devicetree, Yi-Wei Wang,
	Thierry Reding, Mikko Perttunen
In-Reply-To: <20260330-t264-pwm-v3-0-5714427d5976@nvidia.com>

Tegra264 has wider fields for the duty and scale register fields.
Parameterize the driver in preparation. The depth value also
becomes disconnected from the width of the duty field, so define
it separately.

Co-developed-by: Yi-Wei Wang <yiweiw@nvidia.com>
Signed-off-by: Yi-Wei Wang <yiweiw@nvidia.com>
Reviewed-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
 drivers/pwm/pwm-tegra.c | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 22d709986e8c..857301baad51 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -52,16 +52,19 @@
 #include <soc/tegra/common.h>
 
 #define PWM_ENABLE	(1 << 31)
-#define PWM_DUTY_WIDTH	8
 #define PWM_DUTY_SHIFT	16
-#define PWM_SCALE_WIDTH	13
 #define PWM_SCALE_SHIFT	0
 
 #define PWM_CSR_0	0
 
+#define PWM_DEPTH	256
+
 struct tegra_pwm_soc {
 	unsigned int num_channels;
 	unsigned int enable_reg;
+
+	unsigned int duty_width;
+	unsigned int scale_width;
 };
 
 struct tegra_pwm_chip {
@@ -106,22 +109,22 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	/*
 	 * Convert from duty_ns / period_ns to a fixed number of duty ticks
-	 * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the
+	 * per PWM_DEPTH cycles and make sure to round to the
 	 * nearest integer during division.
 	 */
-	c *= (1 << PWM_DUTY_WIDTH);
+	c *= PWM_DEPTH;
 	c = DIV_ROUND_CLOSEST_ULL(c, period_ns);
 
 	val = (u32)c << PWM_DUTY_SHIFT;
 
 	/*
-	 *  min period = max clock limit >> PWM_DUTY_WIDTH
+	 *  min period = max clock limit / PWM_DEPTH
 	 */
 	if (period_ns < pc->min_period_ns)
 		return -EINVAL;
 
 	/*
-	 * Compute the prescaler value for which (1 << PWM_DUTY_WIDTH)
+	 * Compute the prescaler value for which PWM_DEPTH
 	 * cycles at the PWM clock rate will take period_ns nanoseconds.
 	 *
 	 * num_channels: If single instance of PWM controller has multiple
@@ -135,7 +138,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	 */
 	if (pc->soc->num_channels == 1) {
 		/*
-		 * Rate is multiplied with 2^PWM_DUTY_WIDTH so that it matches
+		 * Rate is multiplied with PWM_DEPTH so that it matches
 		 * with the maximum possible rate that the controller can
 		 * provide. Any further lower value can be derived by setting
 		 * PFM bits[0:12].
@@ -145,7 +148,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 		 * source clock rate as required_clk_rate, PWM controller will
 		 * be able to configure the requested period.
 		 */
-		required_clk_rate = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC << PWM_DUTY_WIDTH,
+		required_clk_rate = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * PWM_DEPTH,
 						     period_ns);
 
 		if (required_clk_rate > clk_round_rate(pc->clk, required_clk_rate))
@@ -169,7 +172,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	/* Consider precision in PWM_SCALE_WIDTH rate calculation */
 	rate = mul_u64_u64_div_u64(pc->clk_rate, period_ns,
-				   (u64)NSEC_PER_SEC << PWM_DUTY_WIDTH);
+				   (u64)NSEC_PER_SEC * PWM_DEPTH);
 
 	/*
 	 * Since the actual PWM divider is the register's frequency divider
@@ -185,7 +188,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	 * Make sure that the rate will fit in the register's frequency
 	 * divider field.
 	 */
-	if (rate >> PWM_SCALE_WIDTH)
+	if (rate >> pc->soc->scale_width)
 		return -EINVAL;
 
 	val |= rate << PWM_SCALE_SHIFT;
@@ -324,7 +327,7 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 
 	/* Set minimum limit of PWM period for the IP */
 	pc->min_period_ns =
-	    (NSEC_PER_SEC / (pc->clk_rate >> PWM_DUTY_WIDTH)) + 1;
+	    (NSEC_PER_SEC / (pc->clk_rate / PWM_DEPTH)) + 1;
 
 	pc->rst = devm_reset_control_get_exclusive(&pdev->dev, "pwm");
 	if (IS_ERR(pc->rst)) {
@@ -404,11 +407,15 @@ static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev)
 static const struct tegra_pwm_soc tegra20_pwm_soc = {
 	.num_channels = 4,
 	.enable_reg = PWM_CSR_0,
+	.duty_width = 8,
+	.scale_width = 13,
 };
 
 static const struct tegra_pwm_soc tegra186_pwm_soc = {
 	.num_channels = 1,
 	.enable_reg = PWM_CSR_0,
+	.duty_width = 8,
+	.scale_width = 13,
 };
 
 static const struct of_device_id tegra_pwm_of_match[] = {

-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 4/7] pwm: tegra: Parametrize enable register offset
From: Mikko Perttunen @ 2026-03-30  8:53 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König, Jonathan Hunter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-pwm, linux-tegra, linux-kernel, devicetree, Yi-Wei Wang,
	Mikko Perttunen
In-Reply-To: <20260330-t264-pwm-v3-0-5714427d5976@nvidia.com>

On Tegra264, the PWM enablement bit is not located at the base address
of the PWM controller. Hence, introduce an enablement offset field in
the tegra_pwm_soc structure to describe the offset of the register.

Co-developed-by: Yi-Wei Wang <yiweiw@nvidia.com>
Signed-off-by: Yi-Wei Wang <yiweiw@nvidia.com>
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
 drivers/pwm/pwm-tegra.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index cf54f75d92a5..22d709986e8c 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -61,6 +61,7 @@
 
 struct tegra_pwm_soc {
 	unsigned int num_channels;
+	unsigned int enable_reg;
 };
 
 struct tegra_pwm_chip {
@@ -197,8 +198,9 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 		err = pm_runtime_resume_and_get(pwmchip_parent(chip));
 		if (err)
 			return err;
-	} else
+	} else if (pc->soc->enable_reg == PWM_CSR_0) {
 		val |= PWM_ENABLE;
+	}
 
 	pwm_writel(pwm, PWM_CSR_0, val);
 
@@ -213,6 +215,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 
 static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
 	int rc = 0;
 	u32 val;
 
@@ -220,20 +223,22 @@ static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 	if (rc)
 		return rc;
 
-	val = pwm_readl(pwm, PWM_CSR_0);
+
+	val = pwm_readl(pwm, pc->soc->enable_reg);
 	val |= PWM_ENABLE;
-	pwm_writel(pwm, PWM_CSR_0, val);
+	pwm_writel(pwm, pc->soc->enable_reg, val);
 
 	return 0;
 }
 
 static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
+	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
 	u32 val;
 
-	val = pwm_readl(pwm, PWM_CSR_0);
+	val = pwm_readl(pwm, pc->soc->enable_reg);
 	val &= ~PWM_ENABLE;
-	pwm_writel(pwm, PWM_CSR_0, val);
+	pwm_writel(pwm, pc->soc->enable_reg, val);
 
 	pm_runtime_put_sync(pwmchip_parent(chip));
 }
@@ -398,10 +403,12 @@ static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev)
 
 static const struct tegra_pwm_soc tegra20_pwm_soc = {
 	.num_channels = 4,
+	.enable_reg = PWM_CSR_0,
 };
 
 static const struct tegra_pwm_soc tegra186_pwm_soc = {
 	.num_channels = 1,
+	.enable_reg = PWM_CSR_0,
 };
 
 static const struct of_device_id tegra_pwm_of_match[] = {

-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 3/7] pwm: tegra: Modify read/write accessors for multi-register channel
From: Mikko Perttunen @ 2026-03-30  8:53 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König, Jonathan Hunter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-pwm, linux-tegra, linux-kernel, devicetree, Thierry Reding,
	Mikko Perttunen
In-Reply-To: <20260330-t264-pwm-v3-0-5714427d5976@nvidia.com>

On Tegra264, each PWM instance has two registers (per channel, of which
there is one). Update the pwm_readl/pwm_writel helper functions to
take channel (as struct pwm_device *) and offset separately.

Reviewed-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
 drivers/pwm/pwm-tegra.c | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 759b98b97b6e..cf54f75d92a5 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -57,6 +57,8 @@
 #define PWM_SCALE_WIDTH	13
 #define PWM_SCALE_SHIFT	0
 
+#define PWM_CSR_0	0
+
 struct tegra_pwm_soc {
 	unsigned int num_channels;
 };
@@ -78,14 +80,18 @@ static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip)
 	return pwmchip_get_drvdata(chip);
 }
 
-static inline u32 pwm_readl(struct tegra_pwm_chip *pc, unsigned int offset)
+static inline u32 pwm_readl(struct pwm_device *dev, unsigned int offset)
 {
-	return readl(pc->regs + (offset << 4));
+	struct tegra_pwm_chip *chip = to_tegra_pwm_chip(dev->chip);
+
+	return readl(chip->regs + (dev->hwpwm * 16) + offset);
 }
 
-static inline void pwm_writel(struct tegra_pwm_chip *pc, unsigned int offset, u32 value)
+static inline void pwm_writel(struct pwm_device *dev, unsigned int offset, u32 value)
 {
-	writel(value, pc->regs + (offset << 4));
+	struct tegra_pwm_chip *chip = to_tegra_pwm_chip(dev->chip);
+
+	writel(value, chip->regs + (dev->hwpwm * 16) + offset);
 }
 
 static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -194,7 +200,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	} else
 		val |= PWM_ENABLE;
 
-	pwm_writel(pc, pwm->hwpwm, val);
+	pwm_writel(pwm, PWM_CSR_0, val);
 
 	/*
 	 * If the PWM is not enabled, turn the clock off again to save power.
@@ -207,7 +213,6 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 
 static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
 	int rc = 0;
 	u32 val;
 
@@ -215,21 +220,20 @@ static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 	if (rc)
 		return rc;
 
-	val = pwm_readl(pc, pwm->hwpwm);
+	val = pwm_readl(pwm, PWM_CSR_0);
 	val |= PWM_ENABLE;
-	pwm_writel(pc, pwm->hwpwm, val);
+	pwm_writel(pwm, PWM_CSR_0, val);
 
 	return 0;
 }
 
 static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
 	u32 val;
 
-	val = pwm_readl(pc, pwm->hwpwm);
+	val = pwm_readl(pwm, PWM_CSR_0);
 	val &= ~PWM_ENABLE;
-	pwm_writel(pc, pwm->hwpwm, val);
+	pwm_writel(pwm, PWM_CSR_0, val);
 
 	pm_runtime_put_sync(pwmchip_parent(chip));
 }

-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 2/7] pwm: tegra: Avoid hard-coded max clock frequency
From: Mikko Perttunen @ 2026-03-30  8:53 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König, Jonathan Hunter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-pwm, linux-tegra, linux-kernel, devicetree, Yi-Wei Wang,
	Thierry Reding, Mikko Perttunen
In-Reply-To: <20260330-t264-pwm-v3-0-5714427d5976@nvidia.com>

From: Yi-Wei Wang <yiweiw@nvidia.com>

The clock driving the Tegra PWM IP can be sourced from different parent
clocks. Hence, let dev_pm_opp_set_rate() set the max clock rate based
upon the current parent clock that can be specified via device-tree.

After this, the Tegra194 SoC data becomes redundant, so get rid of it.

Signed-off-by: Yi-Wei Wang <yiweiw@nvidia.com>
Reviewed-by: Thierry Reding <treding@nvidia.com>
Co-developed-by: Mikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
 drivers/pwm/pwm-tegra.c | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 172063b51d44..759b98b97b6e 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -59,9 +59,6 @@
 
 struct tegra_pwm_soc {
 	unsigned int num_channels;
-
-	/* Maximum IP frequency for given SoCs */
-	unsigned long max_frequency;
 };
 
 struct tegra_pwm_chip {
@@ -303,7 +300,7 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 		return ret;
 
 	/* Set maximum frequency of the IP */
-	ret = dev_pm_opp_set_rate(&pdev->dev, pc->soc->max_frequency);
+	ret = dev_pm_opp_set_rate(&pdev->dev, S64_MAX);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret);
 		goto put_pm;
@@ -318,7 +315,7 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 
 	/* Set minimum limit of PWM period for the IP */
 	pc->min_period_ns =
-	    (NSEC_PER_SEC / (pc->soc->max_frequency >> PWM_DUTY_WIDTH)) + 1;
+	    (NSEC_PER_SEC / (pc->clk_rate >> PWM_DUTY_WIDTH)) + 1;
 
 	pc->rst = devm_reset_control_get_exclusive(&pdev->dev, "pwm");
 	if (IS_ERR(pc->rst)) {
@@ -397,23 +394,16 @@ static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev)
 
 static const struct tegra_pwm_soc tegra20_pwm_soc = {
 	.num_channels = 4,
-	.max_frequency = 48000000UL,
 };
 
 static const struct tegra_pwm_soc tegra186_pwm_soc = {
 	.num_channels = 1,
-	.max_frequency = 102000000UL,
-};
-
-static const struct tegra_pwm_soc tegra194_pwm_soc = {
-	.num_channels = 1,
-	.max_frequency = 408000000UL,
 };
 
 static const struct of_device_id tegra_pwm_of_match[] = {
 	{ .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc },
 	{ .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc },
-	{ .compatible = "nvidia,tegra194-pwm", .data = &tegra194_pwm_soc },
+	{ .compatible = "nvidia,tegra194-pwm", .data = &tegra186_pwm_soc },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);

-- 
2.53.0


^ permalink raw reply related

* [PATCH v3 1/7] dt-bindings: pwm: Document Tegra264 controller
From: Mikko Perttunen @ 2026-03-30  8:53 UTC (permalink / raw)
  To: Thierry Reding, Uwe Kleine-König, Jonathan Hunter,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-pwm, linux-tegra, linux-kernel, devicetree, Thierry Reding,
	Mikko Perttunen
In-Reply-To: <20260330-t264-pwm-v3-0-5714427d5976@nvidia.com>

From: Thierry Reding <treding@nvidia.com>

Add a new compatible string for the PWM controller found on Tegra264.
The controller is similar to earlier generations but not compatible
with them.

Signed-off-by: Thierry Reding <treding@nvidia.com>
[mperttunen: Drop extra Tegra194 compatible string]
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
 Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.yaml b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.yaml
index 41cea4979132..cb2f36e7b5d6 100644
--- a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.yaml
+++ b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.yaml
@@ -16,6 +16,7 @@ properties:
       - enum:
           - nvidia,tegra20-pwm
           - nvidia,tegra186-pwm
+          - nvidia,tegra264-pwm
 
       - items:
           - enum:

-- 
2.53.0


^ permalink raw reply related


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