[PATCH v5 1/2] i2c: imx-lpi2c: properly unwind resources on probe failure

From: carlos . song

Date: Tue Jun 30 2026 - 06:51:25 EST


From: Carlos Song <carlos.song@xxxxxxx>

When probe fails at devm_clk_rate_exclusive_get() or clk_get_rate(),
which occur before runtime PM is initialized, the clocks enabled by
clk_bulk_prepare_enable() are never disabled.

When probe fails after runtime PM is initialized, the previous error
path called pm_runtime_put_sync(), which triggers the runtime suspend
callback. However, due to different clock management strategies on
different SoCs[1] (to avoid deadlocks between the global prepare_lock
and runtime PM), the callback may only disable clocks without
unpreparing them, causing an incomplete unwind.

Introduce a new error label 'clk_disable' to explicitly invoke
clk_bulk_disable_unprepare(). Replace pm_runtime_put_sync() with the
sequence of pm_runtime_disable(), pm_runtime_set_suspended() and
pm_runtime_put_noidle() to bypass the runtime suspend callback during
error recovery. During the LPI2C driver probe phase, clock APIs are
used exclusively to manage clocks. Once probing succeeds, clock
management is handed over to the runtime PM core.

[1] https://lore.kernel.org/all/20251125084718.2156168-1-carlos.song@xxxxxxx/

Signed-off-by: Carlos Song <carlos.song@xxxxxxx>
---
drivers/i2c/busses/i2c-imx-lpi2c.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c
index e6c24a9d934d..2dc4331ed796 100644
--- a/drivers/i2c/busses/i2c-imx-lpi2c.c
+++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
@@ -1527,14 +1527,19 @@ static int lpi2c_imx_probe(struct platform_device *pdev)
* each transfer
*/
ret = devm_clk_rate_exclusive_get(&pdev->dev, lpi2c_imx->clks[0].clk);
- if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "can't lock I2C peripheral clock rate\n");
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret,
+ "can't lock I2C peripheral clock rate\n");
+ goto clk_disable;
+ }

lpi2c_imx->rate_per = clk_get_rate(lpi2c_imx->clks[0].clk);
- if (!lpi2c_imx->rate_per)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "can't get I2C peripheral clock rate\n");
+ if (!lpi2c_imx->rate_per) {
+ ret = -EINVAL;
+ dev_err_probe(&pdev->dev, ret,
+ "can't get I2C peripheral clock rate\n");
+ goto clk_disable;
+ }

if (lpi2c_imx->hwdata->need_prepare_unprepare_clk)
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_LONG_TIMEOUT_MS);
@@ -1576,8 +1581,11 @@ static int lpi2c_imx_probe(struct platform_device *pdev)

rpm_disable:
pm_runtime_dont_use_autosuspend(&pdev->dev);
- pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+clk_disable:
+ clk_bulk_disable_unprepare(lpi2c_imx->num_clks, lpi2c_imx->clks);

return ret;
}
--
2.43.0