From 9c37eac31c559b079d55890d1dbe70072fec0435 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Mon, 8 Jun 2026 07:11:23 +0000 Subject: i2c: riic: fix refcount leak in riic_i2c_resume_noirq() When riic_i2c_resume_noirq() is called, it deasserts the reset using reset_control_deassert(), which for shared resets increments a reference count. If pm_runtime_force_resume() then fails, the function returns without calling reset_control_assert() to decrement the count. This leaves the reset deasserted and the reference count unbalanced, which can prevent other users of the shared reset from properly asserting it later. Fix the leak by calling reset_control_assert() on the error handling path for a failed pm_runtime_force_resume(). Fixes: e383f0961422 ("i2c: riic: Move suspend handling to NOIRQ phase") Signed-off-by: Wentao Liang Cc: # v6.19+ Reviewed-by: Geert Uytterhoeven Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260608071123.128964-1-vulab@iscas.ac.cn --- drivers/i2c/busses/i2c-riic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index 9e3595b3623e..6d2ebf67dd62 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -725,8 +725,10 @@ static int riic_i2c_resume_noirq(struct device *dev) return ret; ret = pm_runtime_force_resume(dev); - if (ret) + if (ret) { + reset_control_assert(riic->rstc); return ret; + } ret = riic_init_hw(riic); if (ret) { -- cgit v1.2.3 From 8783fb8031799f1230997c16df8c8dce9fcd1841 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Thu, 21 May 2026 14:50:38 +0800 Subject: i2c: imx: fix clock and pinctrl state inconsistency in runtime PM In i2c_imx_runtime_suspend(), the clock is disabled before switching the pinctrl state to sleep. If pinctrl_pm_select_sleep_state() fails, the runtime suspend is aborted but the clock remains disabled, causing a system crash when the hardware is subsequently accessed. Fix this by switching the pinctrl state before disabling the clock so that a pinctrl failure leaves the clock enabled and the hardware accessible. In i2c_imx_runtime_resume(), restore the pinctrl state back to sleep if clk_enable() fails to keep the consistent. Fixes: 576eba03c994 ("i2c: imx: switch different pinctrl state in different system power status") Signed-off-by: Carlos Song Cc: # v6.14+ Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260521065038.2954998-1-carlos.song@oss.nxp.com --- drivers/i2c/busses/i2c-imx.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index a208fefd3c3b..28313d0fad37 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1892,9 +1892,15 @@ static void i2c_imx_remove(struct platform_device *pdev) static int i2c_imx_runtime_suspend(struct device *dev) { struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + return ret; clk_disable(i2c_imx->clk); - return pinctrl_pm_select_sleep_state(dev); + + return 0; } static int i2c_imx_runtime_resume(struct device *dev) @@ -1907,10 +1913,13 @@ static int i2c_imx_runtime_resume(struct device *dev) return ret; ret = clk_enable(i2c_imx->clk); - if (ret) + if (ret) { dev_err(dev, "can't enable I2C clock, ret=%d\n", ret); + pinctrl_pm_select_sleep_state(dev); + return ret; + } - return ret; + return 0; } static int i2c_imx_suspend(struct device *dev) -- cgit v1.2.3 From a124579c0763da7bc408f4cd7e8f606cadc94855 Mon Sep 17 00:00:00 2001 From: Guillermo Rodríguez Date: Tue, 26 May 2026 11:12:09 +0200 Subject: i2c: stm32f7: fix timing computation ignoring i2c-analog-filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stm32f7_i2c_compute_timing() uses i2c_dev->analog_filter to pick the analog filter delay, but i2c_dev->analog_filter is parsed from the "i2c-analog-filter" DT property only after the compute_timing loop in stm32f7_i2c_setup_timing(), so in practice the timing calculations always ignore the analog filter. On an STM32MP1 board with clock-frequency = <400000> and i2c-analog-filter set, measured SCL frequency was ~382 kHz. This also affects (widens) the computed SDADEL range. At high bus clock speeds, this can select an SDADEL value that violates tVD;DAT (data valid time). Fix by parsing "i2c-analog-filter" before the compute_timing loop. Fixes: 83c3408f7b9c ("i2c: stm32f7: support DT binding i2c-analog-filter") Signed-off-by: Guillermo Rodríguez Cc: # v5.13+ Acked-by: Alain Volmat Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260526091210.20383-1-guille.rodriguez@gmail.com --- drivers/i2c/busses/i2c-stm32f7.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 53d9df70ebe4..067af255bd22 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -694,6 +694,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter")) i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT; + i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node, + "i2c-analog-filter"); + do { ret = stm32f7_i2c_compute_timing(i2c_dev, setup, &i2c_dev->timing); @@ -715,9 +718,6 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, return ret; } - i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node, - "i2c-analog-filter"); - dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n", setup->speed_freq, setup->clock_src); dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n", -- cgit v1.2.3 From 729ac5a4b966aac42e08a94dea966f4429008548 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 16 May 2026 02:41:18 +0300 Subject: i2c: qcom-cci: Fix NULL pointer dereference in cci_remove() On all modern platforms Qualcomm CCI controller provides two I2C masters, and on particular boards only one I2C master may be initialized, and in such cases the device unbinding or driver removal causes a NULL pointer dereference, because cci_halt() is called for all two I2C masters, but a completion is initialized only for the single enabled master: % rmmod i2c-qcom-cci Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 Call trace: __wait_for_common+0x194/0x1a8 (P) wait_for_completion_timeout+0x20/0x2c cci_remove+0xc4/0x138 [i2c_qcom_cci] platform_remove+0x20/0x30 device_remove+0x4c/0x80 device_release_driver_internal+0x1c8/0x224 driver_detach+0x50/0x98 bus_remove_driver+0x6c/0xbc driver_unregister+0x30/0x60 platform_driver_unregister+0x14/0x20 qcom_cci_driver_exit+0x18/0x1008 [i2c_qcom_cci] .... Fixes: e517526195de ("i2c: Add Qualcomm CCI I2C driver") Signed-off-by: Vladimir Zapolskiy Cc: # v5.8+ Reviewed-by: Konrad Dybcio Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260515234121.1607425-2-vladimir.zapolskiy@linaro.org --- drivers/i2c/busses/i2c-qcom-cci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index f3ccfbbc4bea..01e440b6585d 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -660,8 +660,8 @@ static void cci_remove(struct platform_device *pdev) if (cci->master[i].cci) { i2c_del_adapter(&cci->master[i].adap); of_node_put(cci->master[i].adap.dev.of_node); + cci_halt(cci, i); } - cci_halt(cci, i); } disable_irq(cci->irq); -- cgit v1.2.3 From 97d0a9c6e80903d342656ab8a7743a9ec50c92e1 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Mon, 18 May 2026 17:10:12 +0530 Subject: i2c: tegra: Update Tegra410 I2C timing parameters 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 Reviewed-by: Jon Hunter Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260518114013.62065-4-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 479a1667e88d..efe93c5a7bb8 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -2115,9 +2115,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, @@ -2133,8 +2133,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, -- cgit v1.2.3 From 656646b3847ac6a21b074a813223feef2aadd6e2 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Mon, 18 May 2026 17:10:13 +0530 Subject: i2c: tegra: Fix NOIRQ suspend/resume 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") Assisted-by: Cursor:claude-4.6-opus Signed-off-by: Akhil R Cc: # v5.4+ Reviewed-by: Jon Hunter Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260518114013.62065-5-akhilrajeev@nvidia.com --- 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 efe93c5a7bb8..400c9089daf0 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -2401,29 +2401,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) @@ -2433,24 +2442,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) }; -- cgit v1.2.3 From 695fcefd4a81466ef9c529790b4e96f1ea2ba051 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Wed, 20 May 2026 17:33:23 +0800 Subject: i2c: imx-lpi2c: fix resource leaks switching to devm_dma_request_chan() The LPI2C driver requests DMA channels using dma_request_chan(), but never releases them in lpi2c_imx_remove(), resulting in DMA channel leaks every time the driver is unloaded. Additionally, when lpi2c_dma_init() successfully requests the TX DMA channel but fails to request the RX DMA channel, the probe falls back to PIO mode and completes successfully. Since probe succeeds, the devres framework will not trigger any cleanup, leaving the TX DMA channel and the memory allocated for the dma structure held for the lifetime of the device even though DMA is never used. Switch to devm_dma_request_chan() to let the device core manage DMA channel lifetime automatically. Wrap all allocations within a devres group so that devres_release_group() can release all partially acquired resources when DMA init fails and probe continues in PIO mode. Fixes: a09c8b3f9047 ("i2c: imx-lpi2c: add eDMA mode support for LPI2C") Signed-off-by: Carlos Song Cc: # v6.14+ Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260520093323.2882070-1-carlos.song@oss.nxp.com --- drivers/i2c/busses/i2c-imx-lpi2c.c | 53 +++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index a01c23696481..cd4da50c4dd9 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -1383,55 +1383,66 @@ static int lpi2c_imx_init_recovery_info(struct lpi2c_imx_struct *lpi2c_imx, return 0; } -static void dma_exit(struct device *dev, struct lpi2c_imx_dma *dma) -{ - if (dma->chan_rx) - dma_release_channel(dma->chan_rx); - - if (dma->chan_tx) - dma_release_channel(dma->chan_tx); - - devm_kfree(dev, dma); -} - static int lpi2c_dma_init(struct device *dev, dma_addr_t phy_addr) { struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev); struct lpi2c_imx_dma *dma; + void *group; int ret; - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) + /* + * Open a devres group so that all resources allocated within + * this function can be released together if DMA init fails but + * probe continues in PIO mode. + */ + group = devres_open_group(dev, NULL, GFP_KERNEL); + if (!group) return -ENOMEM; + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) { + ret = -ENOMEM; + goto release_group; + } + dma->phy_addr = phy_addr; /* Prepare for TX DMA: */ - dma->chan_tx = dma_request_chan(dev, "tx"); + dma->chan_tx = devm_dma_request_chan(dev, "tx"); if (IS_ERR(dma->chan_tx)) { ret = PTR_ERR(dma->chan_tx); if (ret != -ENODEV && ret != -EPROBE_DEFER) dev_err(dev, "can't request DMA tx channel (%d)\n", ret); - dma->chan_tx = NULL; - goto dma_exit; + goto release_group; } /* Prepare for RX DMA: */ - dma->chan_rx = dma_request_chan(dev, "rx"); + dma->chan_rx = devm_dma_request_chan(dev, "rx"); if (IS_ERR(dma->chan_rx)) { ret = PTR_ERR(dma->chan_rx); if (ret != -ENODEV && ret != -EPROBE_DEFER) dev_err(dev, "can't request DMA rx channel (%d)\n", ret); - dma->chan_rx = NULL; - goto dma_exit; + goto release_group; } + /* + * DMA init succeeded. Remove the group marker but keep all resources + * bound to the device, they will be freed at device removal. + */ + devres_remove_group(dev, group); + lpi2c_imx->can_use_dma = true; lpi2c_imx->dma = dma; return 0; -dma_exit: - dma_exit(dev, dma); +release_group: + /* + * DMA init failed. Release ALL resources allocated inside this + * group (dma memory, TX channel if already acquired, etc.) so + * that a successful PIO-mode probe does not hold unused resources + * for the entire device lifetime. + */ + devres_release_group(dev, group); return ret; } -- cgit v1.2.3 From 9bffa2a61551da9ac32171f09d556974cb96d70a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 9 Jun 2026 11:16:14 +0200 Subject: MAINTAINERS: hand over I2C to Andi Shyti After 13.5 years of maintaining I2C, it is finally time for me to move to other areas. So, I hereby transfer I2C maintainership to Andi Shyti. He has been taking care of the I2C host drivers for a while now and kindly agreed to look after the whole subsystem. Thank you, Andi! I also want to thank all contributors, reviewers, and fellow maintainers making all these years a mostly smooth ride. Happy hacking, everyone! Signed-off-by: Wolfram Sang Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260609091612.8228-4-wsa+renesas@sang-engineering.com --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index e035a3be797c..6dc7675e582f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12094,11 +12094,11 @@ F: Documentation/i2c/busses/i2c-parport.rst F: drivers/i2c/busses/i2c-parport.c I2C SUBSYSTEM -M: Wolfram Sang +M: Andi Shyti L: linux-i2c@vger.kernel.org S: Maintained Q: https://patchwork.ozlabs.org/project/linux-i2c/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux.git F: Documentation/i2c/ F: drivers/i2c/* F: include/dt-bindings/i2c/i2c.h -- cgit v1.2.3