public inbox for linux-i2c@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] i2c: tegra: Improve DMA mapping, latency, and power management
@ 2026-05-05 10:59 Akhil R
  2026-05-05 10:59 ` [PATCH 1/4] i2c: tegra: use dmaengine_get_dma_device() for DMA buffer allocation Akhil R
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Akhil R @ 2026-05-05 10:59 UTC (permalink / raw)
  To: Laxman Dewangan, Dmitry Osipenko, Andi Shyti, Thierry Reding,
	Jonathan Hunter, Kartik Rajput, Wolfram Sang, linux-i2c,
	linux-tegra, linux-kernel
  Cc: mochs, Akhil R

This series addresses several aspects of the Tegra I2C driver:

- DMA mapping: Use the proper DMA device for buffer allocation to
  ensure correct DMA address translation.
- Transfer latency: Disable fair arbitration on non-MCTP buses to
  reduce transfer latency.
- Timing parameters: Update I2C timing values for Tegra410.
- Power management: Keep the controller available during noirq
  suspend/resume phases for system dependencies.

Akhil R (4):
  i2c: tegra: use dmaengine_get_dma_device() for DMA buffer allocation
  i2c: tegra: Disable fair arbitration for non-MCTP buses
  i2c: tegra: Update Tegra410 I2C timing parameters
  i2c: tegra: Fix NOIRQ suspend/resume

 drivers/i2c/busses/i2c-tegra.c | 86 +++++++++++++++++++++++-----------
 1 file changed, 58 insertions(+), 28 deletions(-)

-- 
2.50.1


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

* [PATCH 1/4] i2c: tegra: use dmaengine_get_dma_device() for DMA buffer allocation
  2026-05-05 10:59 [PATCH 0/4] i2c: tegra: Improve DMA mapping, latency, and power management Akhil R
@ 2026-05-05 10:59 ` Akhil R
  2026-05-05 10:59 ` [PATCH 2/4] i2c: tegra: Disable fair arbitration for non-MCTP buses Akhil R
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Akhil R @ 2026-05-05 10:59 UTC (permalink / raw)
  To: Laxman Dewangan, Dmitry Osipenko, Andi Shyti, Thierry Reding,
	Jonathan Hunter, Kartik Rajput, Wolfram Sang, linux-i2c,
	linux-tegra, linux-kernel
  Cc: mochs, Akhil R

Use dmaengine_get_dma_device() to obtain the correct struct device
pointer for dma_alloc_coherent() instead of directly dereferencing
chan->device->dev.

The dmaengine_get_dma_device() helper checks whether the DMA channel
has a per-channel DMA device (chan->dev->chan_dma_dev) and returns it
when available, falling back to the controller device otherwise. On
platforms where the DMA controller sits behind an IOMMU with
per-channel IOVA spaces (e.g. Tegra264 GPC DMA), the per-channel
device carries the correct DMA mapping context. Using the controller
device directly would allocate DMA buffers against the wrong IOMMU
domain, leading to SMMU faults at runtime.

On platforms without per-channel DMA devices the helper returns the
same pointer as before, so there is no change in behavior for existing
hardware.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
Assisted-by: Cursor:claude-4.6-opus
---
 drivers/i2c/busses/i2c-tegra.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 9fd5ade774a0..a21f6457d41b 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -712,7 +712,7 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev)
 		goto err_out;
 	}
 
-	i2c_dev->dma_dev = i2c_dev->dma_chan->device->dev;
+	i2c_dev->dma_dev = dmaengine_get_dma_device(i2c_dev->dma_chan);
 	i2c_dev->dma_buf_size = i2c_dev->hw->quirks->max_write_len +
 				I2C_PACKET_HEADER_SIZE;
 
-- 
2.50.1


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

* [PATCH 2/4] i2c: tegra: Disable fair arbitration for non-MCTP buses
  2026-05-05 10:59 [PATCH 0/4] i2c: tegra: Improve DMA mapping, latency, and power management Akhil R
  2026-05-05 10:59 ` [PATCH 1/4] i2c: tegra: use dmaengine_get_dma_device() for DMA buffer allocation Akhil R
@ 2026-05-05 10:59 ` Akhil R
  2026-05-05 10:59 ` [PATCH 3/4] i2c: tegra: Update Tegra410 I2C timing parameters Akhil R
  2026-05-05 10:59 ` [PATCH 4/4] i2c: tegra: Fix NOIRQ suspend/resume Akhil R
  3 siblings, 0 replies; 5+ messages in thread
From: Akhil R @ 2026-05-05 10:59 UTC (permalink / raw)
  To: Laxman Dewangan, Dmitry Osipenko, Andi Shyti, Thierry Reding,
	Jonathan Hunter, Kartik Rajput, Wolfram Sang, linux-i2c,
	linux-tegra, linux-kernel
  Cc: mochs, Akhil R

Recent Tegra I2C controllers have a fairness arbitration register, which
allows configuring the fair idle time required to support MCTP protocol
over I2C. It is enabled by default, adding a per-transfer latency overhead
that impacts non-MCTP I2C buses.

Disable the fairness arbitration register during controller init for buses
that are not MCTP controllers.

Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
Assisted-by: Cursor:claude-4.6-opus
---
 drivers/i2c/busses/i2c-tegra.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index a21f6457d41b..1d274431e209 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -164,6 +164,7 @@ struct tegra_i2c_regs {
 	unsigned int master_reset_cntrl;
 	unsigned int mst_fifo_control;
 	unsigned int mst_fifo_status;
+	unsigned int fairness_arb;
 	unsigned int sw_mutex;
 };
 
@@ -272,6 +273,7 @@ static const struct tegra_i2c_regs tegra264_i2c_regs = {
 	.master_reset_cntrl = 0x0a8,
 	.mst_fifo_control = 0x0b4,
 	.mst_fifo_status = 0x0b8,
+	.fairness_arb = 0x0e8,
 	.sw_mutex = 0x0ec,
 };
 
@@ -300,6 +302,7 @@ static const struct tegra_i2c_regs tegra410_i2c_regs = {
 	.master_reset_cntrl = 0x0ac,
 	.mst_fifo_control = 0x0b8,
 	.mst_fifo_status = 0x0bc,
+	.fairness_arb = 0x0ec,
 	.sw_mutex = 0x0f0,
 };
 
@@ -379,6 +382,7 @@ enum tegra_i2c_variant {
  *		timing settings.
  * @enable_hs_mode_support: Enable support for high speed (HS) mode transfers.
  * @has_mutex: Has mutex register for mutual exclusion with other firmwares or VMs.
+ * @has_fairarb_reg: Has fairness arbitration register for SMBUS/MCTP support.
  * @variant: This represents the I2C controller variant.
  * @regs: Register offsets for the specific SoC variant.
  */
@@ -412,6 +416,7 @@ struct tegra_i2c_hw_feature {
 	bool has_interface_timing_reg;
 	bool enable_hs_mode_support;
 	bool has_mutex;
+	bool has_fairarb_reg;
 	enum tegra_i2c_variant variant;
 	const struct tegra_i2c_regs *regs;
 };
@@ -476,6 +481,7 @@ struct tegra_i2c_dev {
 	void *dma_buf;
 
 	bool multimaster_mode;
+	bool is_mctp;
 	bool atomic_mode;
 	bool dma_mode;
 	bool msg_read;
@@ -914,6 +920,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
 	if (IS_VI(i2c_dev))
 		tegra_i2c_vi_init(i2c_dev);
 
+	/* Disable fairness arbitration if not an MCTP controller */
+	if (i2c_dev->hw->has_fairarb_reg && !i2c_dev->is_mctp)
+		i2c_writel(i2c_dev, 0, i2c_dev->hw->regs->fairness_arb);
+
 	if (i2c_dev->hw->enable_hs_mode_support)
 		max_bus_freq_hz = I2C_MAX_HIGH_SPEED_MODE_FREQ;
 	else
@@ -1779,6 +1789,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
 	.has_interface_timing_reg = false,
 	.enable_hs_mode_support = false,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra20_i2c_regs,
 };
@@ -1812,6 +1823,7 @@ static const struct tegra_i2c_hw_feature tegra20_dvc_i2c_hw = {
 	.has_interface_timing_reg = false,
 	.enable_hs_mode_support = false,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_DVC,
 	.regs = &tegra20_dvc_i2c_regs,
 };
@@ -1845,6 +1857,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
 	.has_interface_timing_reg = false,
 	.enable_hs_mode_support = false,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra20_i2c_regs,
 };
@@ -1877,6 +1890,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
 	.has_interface_timing_reg = false,
 	.enable_hs_mode_support = false,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra20_i2c_regs,
 };
@@ -1909,6 +1923,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
 	.has_interface_timing_reg = true,
 	.enable_hs_mode_support = false,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra20_i2c_regs,
 };
@@ -1941,6 +1956,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
 	.has_interface_timing_reg = true,
 	.enable_hs_mode_support = false,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra20_i2c_regs,
 };
@@ -1974,6 +1990,7 @@ static const struct tegra_i2c_hw_feature tegra210_vi_i2c_hw = {
 	.has_interface_timing_reg = true,
 	.enable_hs_mode_support = false,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_VI,
 	.regs = &tegra210_vi_i2c_regs,
 };
@@ -2007,6 +2024,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
 	.has_interface_timing_reg = true,
 	.enable_hs_mode_support = false,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra20_i2c_regs,
 };
@@ -2041,6 +2059,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
 	.has_interface_timing_reg = true,
 	.enable_hs_mode_support = true,
 	.has_mutex = false,
+	.has_fairarb_reg = false,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra20_i2c_regs,
 };
@@ -2075,6 +2094,7 @@ static const struct tegra_i2c_hw_feature tegra256_i2c_hw = {
 	.has_interface_timing_reg = true,
 	.enable_hs_mode_support = true,
 	.has_mutex = true,
+	.has_fairarb_reg = true,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra264_i2c_regs,
 };
@@ -2109,6 +2129,7 @@ static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
 	.has_interface_timing_reg = true,
 	.enable_hs_mode_support = true,
 	.has_mutex = true,
+	.has_fairarb_reg = true,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra264_i2c_regs,
 };
@@ -2143,6 +2164,7 @@ static const struct tegra_i2c_hw_feature tegra410_i2c_hw = {
 	.has_interface_timing_reg = true,
 	.enable_hs_mode_support = true,
 	.has_mutex = true,
+	.has_fairarb_reg = true,
 	.variant = TEGRA_I2C_VARIANT_DEFAULT,
 	.regs = &tegra410_i2c_regs,
 };
@@ -2175,6 +2197,7 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
 
 	multi_mode = device_property_read_bool(i2c_dev->dev, "multi-master");
 	i2c_dev->multimaster_mode = multi_mode;
+	i2c_dev->is_mctp = device_property_present(i2c_dev->dev, "mctp-controller");
 }
 
 static int tegra_i2c_init_clocks(struct tegra_i2c_dev *i2c_dev)
-- 
2.50.1


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

* [PATCH 3/4] i2c: tegra: Update Tegra410 I2C timing parameters
  2026-05-05 10:59 [PATCH 0/4] i2c: tegra: Improve DMA mapping, latency, and power management Akhil R
  2026-05-05 10:59 ` [PATCH 1/4] i2c: tegra: use dmaengine_get_dma_device() for DMA buffer allocation Akhil R
  2026-05-05 10:59 ` [PATCH 2/4] i2c: tegra: Disable fair arbitration for non-MCTP buses Akhil R
@ 2026-05-05 10:59 ` Akhil R
  2026-05-05 10:59 ` [PATCH 4/4] i2c: tegra: Fix NOIRQ suspend/resume Akhil R
  3 siblings, 0 replies; 5+ messages in thread
From: Akhil R @ 2026-05-05 10:59 UTC (permalink / raw)
  To: Laxman Dewangan, Dmitry Osipenko, Andi Shyti, Thierry Reding,
	Jonathan Hunter, Kartik Rajput, Wolfram Sang, linux-i2c,
	linux-tegra, linux-kernel
  Cc: mochs, Akhil R

Update Tegra410 I2C timing parameters based on hardware characterization
results. This adjusts the fast mode and HS mode settings to be compliant
with the I2C specification.

Fixes: 59717f260183 ("i2c: tegra: Add support for Tegra410")
Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
---
 drivers/i2c/busses/i2c-tegra.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 1d274431e209..0c26139b4721 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -2137,9 +2137,9 @@ static const struct tegra_i2c_hw_feature tegra264_i2c_hw = {
 static const struct tegra_i2c_hw_feature tegra410_i2c_hw = {
 	.has_continue_xfer_support = true,
 	.has_per_pkt_xfer_complete_irq = true,
-	.clk_divisor_hs_mode = 1,
+	.clk_divisor_hs_mode = 2,
 	.clk_divisor_std_mode = 0x3f,
-	.clk_divisor_fast_mode = 0x2c,
+	.clk_divisor_fast_mode = 0x2f,
 	.clk_divisor_fast_plus_mode = 0x11,
 	.has_config_load_reg = true,
 	.has_multi_master_mode = true,
@@ -2155,8 +2155,8 @@ static const struct tegra_i2c_hw_feature tegra410_i2c_hw = {
 	.thigh_fast_mode = 0x2,
 	.tlow_fastplus_mode = 0x2,
 	.thigh_fastplus_mode = 0x2,
-	.tlow_hs_mode = 0x8,
-	.thigh_hs_mode = 0x6,
+	.tlow_hs_mode = 0x5,
+	.thigh_hs_mode = 0x2,
 	.setup_hold_time_std_mode = 0x08080808,
 	.setup_hold_time_fast_mode = 0x02020202,
 	.setup_hold_time_fastplus_mode = 0x02020202,
-- 
2.50.1


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

* [PATCH 4/4] i2c: tegra: Fix NOIRQ suspend/resume
  2026-05-05 10:59 [PATCH 0/4] i2c: tegra: Improve DMA mapping, latency, and power management Akhil R
                   ` (2 preceding siblings ...)
  2026-05-05 10:59 ` [PATCH 3/4] i2c: tegra: Update Tegra410 I2C timing parameters Akhil R
@ 2026-05-05 10:59 ` Akhil R
  3 siblings, 0 replies; 5+ messages in thread
From: Akhil R @ 2026-05-05 10:59 UTC (permalink / raw)
  To: Laxman Dewangan, Dmitry Osipenko, Andi Shyti, Thierry Reding,
	Jonathan Hunter, Kartik Rajput, Wolfram Sang, linux-i2c,
	linux-tegra, linux-kernel
  Cc: mochs, Akhil R

The Tegra I2C driver relies on runtime PM to wake up the controller before
each transfer. However, runtime PM is disabled between the system suspend
and NOIRQ suspend. If an I2C device initiates a transfer during this
window, the I2C controller fails to wake up and the transfer fails. To
handle this, the controller must be kept available for this period to
allow transfers.

Rework the I2C controller's system PM callbacks such that the controller
is resumed from runtime suspend during system suspend and it stays
RPM_ACTIVE throughout the suspend-resume cycle until it is runtime
suspended back in the system resume. The clocks are disabled in NOIRQ
suspend and enabled back in NOIRQ resume by calling the controller's
runtime PM functions directly.

Fixes: 8ebf15e9c869 ("i2c: tegra: Move suspend handling to NOIRQ phase")
Signed-off-by: Akhil R <akhilrajeev@nvidia.com>
Assisted-by: Cursor:claude-4.6-opus
---
 drivers/i2c/busses/i2c-tegra.c | 53 +++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 23 deletions(-)

diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 0c26139b4721..4fee1d2d81cc 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -2425,29 +2425,38 @@ static int __maybe_unused tegra_i2c_runtime_suspend(struct device *dev)
 }
 
 static int __maybe_unused tegra_i2c_suspend(struct device *dev)
+{
+	/*
+	 * Bring the controller up and hold a usage count so it stays
+	 * available until the noirq phase.
+	 */
+	return pm_runtime_resume_and_get(dev);
+}
+
+static int __maybe_unused tegra_i2c_suspend_noirq(struct device *dev)
 {
 	struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
-	int err;
 
 	i2c_mark_adapter_suspended(&i2c_dev->adapter);
 
-	if (!pm_runtime_status_suspended(dev)) {
-		err = tegra_i2c_runtime_suspend(dev);
-		if (err)
-			return err;
-	}
-
-	return 0;
+	/*
+	 * Runtime PM is already disabled at this point, so invoke the
+	 * runtime_suspend callback directly to put the controller down.
+	 */
+	return tegra_i2c_runtime_suspend(dev);
 }
 
-static int __maybe_unused tegra_i2c_resume(struct device *dev)
+static int __maybe_unused tegra_i2c_resume_noirq(struct device *dev)
 {
 	struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
 	int err;
 
 	/*
-	 * We need to ensure that clocks are enabled so that registers can be
-	 * restored in tegra_i2c_init().
+	 * Runtime PM is still disabled at this point, so invoke the
+	 * runtime_resume callback directly to bring the controller back up
+	 * before re-initializing the hardware. The adapter is then marked
+	 * resumed so that consumers can issue transfers from their own
+	 * resume_noirq() handlers and onwards.
 	 */
 	err = tegra_i2c_runtime_resume(dev);
 	if (err)
@@ -2457,24 +2466,22 @@ static int __maybe_unused tegra_i2c_resume(struct device *dev)
 	if (err)
 		return err;
 
-	/*
-	 * In case we are runtime suspended, disable clocks again so that we
-	 * don't unbalance the clock reference counts during the next runtime
-	 * resume transition.
-	 */
-	if (pm_runtime_status_suspended(dev)) {
-		err = tegra_i2c_runtime_suspend(dev);
-		if (err)
-			return err;
-	}
-
 	i2c_mark_adapter_resumed(&i2c_dev->adapter);
 
 	return 0;
 }
 
+static int __maybe_unused tegra_i2c_resume(struct device *dev)
+{
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
 static const struct dev_pm_ops tegra_i2c_pm = {
-	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume)
+	SET_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume)
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend_noirq,
+				      tegra_i2c_resume_noirq)
 	SET_RUNTIME_PM_OPS(tegra_i2c_runtime_suspend, tegra_i2c_runtime_resume,
 			   NULL)
 };
-- 
2.50.1


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

end of thread, other threads:[~2026-05-05 11:00 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-05 10:59 [PATCH 0/4] i2c: tegra: Improve DMA mapping, latency, and power management Akhil R
2026-05-05 10:59 ` [PATCH 1/4] i2c: tegra: use dmaengine_get_dma_device() for DMA buffer allocation Akhil R
2026-05-05 10:59 ` [PATCH 2/4] i2c: tegra: Disable fair arbitration for non-MCTP buses Akhil R
2026-05-05 10:59 ` [PATCH 3/4] i2c: tegra: Update Tegra410 I2C timing parameters Akhil R
2026-05-05 10:59 ` [PATCH 4/4] i2c: tegra: Fix NOIRQ suspend/resume Akhil R

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