From aaabefc79b0ff0018e8e3832169bbe359d0551fc Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Wed, 16 Sep 2026 07:06:31 +0000 Subject: [PATCH 1/4] i2c: viai2c-wmt: Fix clock leak in wmt_i2c_probe() In wmt_i2c_probe(), i2c->clk is acquired using of_clk_get(). If any subsequent operation fails or when the module is unloaded, the clock reference count is never decremented via clk_put(), leading to a resource leak. Switch to devm_clk_get() so that the clock is automatically managed and released. Fixes: 560746eb79d3 ("i2c: vt8500: Add support for I2C bus on Wondermedia SoCs") Cc: stable@vger.kernel.org Signed-off-by: Wentao Liang Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260916070631.1965713-1-vulab@iscas.ac.cn --- drivers/i2c/busses/i2c-viai2c-wmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-viai2c-wmt.c b/drivers/i2c/busses/i2c-viai2c-wmt.c index d3dda7534b0a..c10b4fd9b553 100644 --- a/drivers/i2c/busses/i2c-viai2c-wmt.c +++ b/drivers/i2c/busses/i2c-viai2c-wmt.c @@ -116,7 +116,7 @@ static int wmt_i2c_probe(struct platform_device *pdev) if (err) return err; - i2c->clk = of_clk_get(np, 0); + i2c->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(i2c->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk), "unable to request clock\n"); From 8cb5c17be3a2fadc41075d04ce1c24c831546aaf Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Mon, 28 Sep 2026 16:58:47 +0300 Subject: [PATCH 2/4] i2c: qcom-geni: Restore the source-clock selection policy Currently, the source clock lookup prefers 32 MHz whenever that rate is available, regardless of the rate configured by the platform. Before the switch to looking up the clock index, the driver used the 32 MHz timing table only if the current source clock rate was 32 MHz, and the 19.2 MHz table otherwise. On X1E80100 CRD, this results in switching the source clock from 19.2 MHz to 32 MHz, which causes intermittent failures when probing the I2C HID keyboard. These range from corrupted HID descriptors to failed resets and transfer timeouts, which also affect the touchpad on the same bus. Restore the original selection policy: use 32 MHz if already selected, otherwise request 19.2 MHz. Look up the index for the selected rate and keep the check that rejects higher-frequency multiples. This retains the dynamic clock index lookup without preferring 32 MHz on platforms that previously used 19.2 MHz. Report the requested rate if the lookup fails. Fixes: cb97bf3d4f91 ("i2c: qcom-geni: Fix hardcoded clock index in SE_GENI_CLK_SEL") Assisted-by: LLM Signed-off-by: Abel Vesa Tested-by: Hasty Heat Tested-by: Mitchell Augustin Reviewed-by: Mitchell Augustin Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20260928-b4-i2c-qcom-geni-preserve-source-clock-v1-1-9aded7552a76@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-geni.c | 37 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index ed6495ef811c..c4c6202ff15a 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -201,28 +201,31 @@ static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = { static int geni_i2c_clk_map_idx(struct geni_i2c_dev *gi2c) { const struct geni_i2c_clk_fld *itr; - unsigned long res_freq; + unsigned long req_freq, res_freq; + int ret; /* - * Frequency counter tables are calibrated for a specific source - * clock frequency and are not valid for any multiple of it - * (e.g. 64 MHz, 128 MHz). - * Use exact=true and verify res_freq matches req_freq literally - * to reject harmonics: a 64 MHz clock that divides evenly to - * 32 MHz would pass exact matching but produce double the intended - * I2C frequency with these counter values. + * Use 32 MHz only if already selected, otherwise use 19.2 MHz. + * Preferring 32 MHz merely because DFS supports it breaks I2C + * transfers on X1E80100 CRD. */ - if (!geni_se_clk_freq_match(&gi2c->se, GENI_SE_CLK_32MHZ, - &gi2c->clk_idx, &res_freq, true) && - res_freq == GENI_SE_CLK_32MHZ) { + if (clk_get_rate(gi2c->se.clk) == GENI_SE_CLK_32MHZ) { + req_freq = GENI_SE_CLK_32MHZ; itr = geni_i2c_clk_map_32mhz; - } else if (!geni_se_clk_freq_match(&gi2c->se, GENI_SE_CLK_19P2MHZ, - &gi2c->clk_idx, &res_freq, true) && - res_freq == GENI_SE_CLK_19P2MHZ) { - itr = geni_i2c_clk_map_19p2mhz; } else { - dev_err(gi2c->se.dev, - "Unsupported SE source clock: must be exactly 32 MHz or 19.2 MHz\n"); + req_freq = GENI_SE_CLK_19P2MHZ; + itr = geni_i2c_clk_map_19p2mhz; + } + + ret = geni_se_clk_freq_match(&gi2c->se, req_freq, &gi2c->clk_idx, + &res_freq, true); + if (ret) + return dev_err_probe(gi2c->se.dev, ret, + "Failed to find SE source clock %lu Hz\n", + req_freq); + if (res_freq != req_freq) { + dev_err(gi2c->se.dev, "Unsupported SE source clock %lu Hz\n", + res_freq); return -EINVAL; } From 195f3d74b5de98969dc4fad956393bb00787efd1 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Mon, 12 Oct 2026 14:54:56 +0200 Subject: [PATCH 3/4] i2c: qcom-cci: Fix HALT_ACK reset request being dropped A HALT_ACK interrupt indicates that the corresponding I2C master has entered the halted state and must be reset before further operation. The M0 and M1 HALT_ACK status bits may be asserted simultaneously in a single interrupt. However, the interrupt handler currently overwrites the reset mask when processing each HALT_ACK condition, causing the first reset request to be lost if both events are reported together. Accumulate reset requests instead of overwriting them so that both masters are properly reset when M0 and M1 HALT_ACK events occur in the same interrupt. Fixes: e517526195de4 ("i2c: Add Qualcomm CCI I2C driver") Signed-off-by: Loic Poulain Cc: # v5.8+ Reviewed-by: Konrad Dybcio Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20261012-cci-fixes-v1-1-8cd4eb1fc5f9@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-cci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 873e901a23d7..bd70c6bae5dc 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -164,12 +164,12 @@ static irqreturn_t cci_isr(int irq, void *dev) } if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK)) { - reset = CCI_RESET_CMD_M0_MASK; + reset |= CCI_RESET_CMD_M0_MASK; ret = IRQ_HANDLED; } if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK)) { - reset = CCI_RESET_CMD_M1_MASK; + reset |= CCI_RESET_CMD_M1_MASK; ret = IRQ_HANDLED; } From d33373c565af9c8a28776af02bd20ba6e5724211 Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Wed, 14 Oct 2026 16:56:55 -0700 Subject: [PATCH 4/4] i2c: xiic: restore non-managed runtime PM to fix clk WARN flood The devres conversion replaced manual pm_runtime_enable()/disable() with devm_pm_runtime_set_active_enabled() and dropped the remove-time runtime PM teardown. The managed release tears runtime PM down in the wrong order: it calls pm_runtime_dont_use_autosuspend() before pm_runtime_disable(), i.e. while runtime PM is still enabled, and devres is LIFO so the devm_clk_get_enabled() release runs afterwards. At remove(), pm_runtime_put_sync() leaves the device active with the autosuspend timer armed. Clearing use_autosuspend then makes rpm_idle() suspend immediately, and xiic_i2c_runtime_suspend() clk_disable()s the clock. The later devm_clk_get_enabled() release clk_disable_unprepare()s the already-disabled clock, so clk_core_disable() WARNs ("clkN already disabled") on every teardown. Drop the managed helper and restore the non-managed runtime PM setup and teardown, so runtime PM is enabled once in probe and disabled once in remove and the clock enable count stays balanced. Order the remove teardown so runtime PM is disabled before the usage count is dropped: pm_runtime_disable() followed by pm_runtime_put_noidle() instead of pm_runtime_put_sync(). Dropping the count first with autosuspend still enabled arms the autosuspend timer, and a preemption before pm_runtime_disable() lets it expire, suspend the device and clk_disable() the clock, which the devm release then disables again - the same WARN. Restore the non-managed IRQ request as well. The probe error paths now unwind runtime PM by hand via goto, and a devm-registered handler is only freed later during devres unwind, so it would stay live across the manual teardown (and across any future failing step added after it). Request the IRQ with request_threaded_irq() and free it explicitly in the probe error path and in remove. The probe error path also clears use_autosuspend, to match the teardown in remove(). Fixes: 50c63491ff26 ("i2c: xiic: switch to devres managed APIs") Signed-off-by: Abdurrahman Hussain Cc: # v7.1+ Signed-off-by: Andi Shyti Link: https://patch.msgid.link/20261014-i2c-xiic-restore-runtime-pm-teardown-v8-1-40668c270069@nexthop.ai --- drivers/i2c/busses/i2c-xiic.c | 49 ++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 5e397a7e63f6..ba560462d451 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -67,6 +67,7 @@ enum i2c_scl_freq { * @rx_pos: Position within current RX message * @endianness: big/little-endian byte order * @clk: Pointer to AXI4-lite input clock + * @irq: Interrupt number * @state: See STATE_ * @singlemaster: Indicates bus is single master * @dynamic: Mode of controller @@ -95,6 +96,7 @@ struct xiic_i2c { int rx_pos; enum xiic_endian endianness; struct clk *clk; + int irq; enum xilinx_i2c_state state; bool singlemaster; bool dynamic; @@ -1506,9 +1508,17 @@ static int xiic_i2c_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(dev, XIIC_PM_TIMEOUT); pm_runtime_use_autosuspend(dev); - ret = devm_pm_runtime_set_active_enabled(dev); - if (ret) + /* + * Enable runtime PM by hand: devm_pm_runtime_set_active_enabled() + * tears down in an order that races the devm-enabled clock release and + * makes clk_core_disable() WARN (see xiic_i2c_remove()). + */ + ret = pm_runtime_set_active(dev); + if (ret) { + pm_runtime_dont_use_autosuspend(dev); return ret; + } + pm_runtime_enable(dev); /* SCL frequency configuration */ i2c->input_clk = clk_get_rate(i2c->clk); @@ -1517,10 +1527,15 @@ static int xiic_i2c_probe(struct platform_device *pdev) if (ret || i2c->i2c_clk > I2C_MAX_FAST_MODE_PLUS_FREQ) i2c->i2c_clk = 0; - ret = devm_request_threaded_irq(dev, irq, NULL, xiic_process, - IRQF_ONESHOT, pdev->name, i2c); + /* + * Request the IRQ non-managed: later probe steps unwind manually via + * goto, so a devm handler could still be live after that teardown runs. + */ + i2c->irq = irq; + ret = request_threaded_irq(irq, NULL, xiic_process, IRQF_ONESHOT, + pdev->name, i2c); if (ret) - return ret; + goto err_pm_disable; i2c->singlemaster = device_property_read_bool(dev, "single-master"); @@ -1537,14 +1552,16 @@ static int xiic_i2c_probe(struct platform_device *pdev) i2c->endianness = BIG; ret = xiic_reinit(i2c); - if (ret) - return dev_err_probe(dev, ret, "Cannot xiic_reinit\n"); + if (ret) { + dev_err_probe(dev, ret, "Cannot xiic_reinit\n"); + goto err_free_irq; + } /* add i2c adapter to i2c tree */ ret = i2c_add_numbered_adapter(&i2c->adap); if (ret) { xiic_deinit(i2c); - return ret; + goto err_free_irq; } if (pdata) { @@ -1557,6 +1574,15 @@ static int xiic_i2c_probe(struct platform_device *pdev) res, irq, i2c->i2c_clk); return 0; + +err_free_irq: + free_irq(irq, i2c); +err_pm_disable: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + return ret; } static void xiic_i2c_remove(struct platform_device *pdev) @@ -1568,6 +1594,8 @@ static void xiic_i2c_remove(struct platform_device *pdev) /* remove adapter & data */ i2c_del_adapter(&i2c->adap); + free_irq(i2c->irq, i2c); + ret = pm_runtime_get_sync(dev); if (ret < 0) dev_warn(dev, "Failed to activate device for removal (%pe)\n", @@ -1575,7 +1603,10 @@ static void xiic_i2c_remove(struct platform_device *pdev) else xiic_deinit(i2c); - pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); } static const struct dev_pm_ops xiic_dev_pm_ops = {