Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: imx: Make sure to unregister adapter on remove()

If for whatever reasons pm_runtime_resume_and_get() fails and .remove() is
exited early, the i2c adapter stays around and the irq still calls its
handler, while the driver data and the register mapping go away. So if
later the i2c adapter is accessed or the irq triggers this results in
havoc accessing freed memory and unmapped registers.

So unregister the software resources even if resume failed, and only skip
the hardware access in that case.

Fixes: 588eb93ea49f ("i2c: imx: add runtime pm support to improve the performance")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Wolfram Sang <wsa@kernel.org>

authored by

Uwe Kleine-König and committed by
Wolfram Sang
d98bdd3a 3df71d70

+11 -9
+11 -9
drivers/i2c/busses/i2c-imx.c
··· 1572 1572 struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev); 1573 1573 int irq, ret; 1574 1574 1575 - ret = pm_runtime_resume_and_get(&pdev->dev); 1576 - if (ret < 0) 1577 - return ret; 1575 + ret = pm_runtime_get_sync(&pdev->dev); 1578 1576 1579 1577 hrtimer_cancel(&i2c_imx->slave_timer); 1580 1578 ··· 1583 1585 if (i2c_imx->dma) 1584 1586 i2c_imx_dma_free(i2c_imx); 1585 1587 1586 - /* setup chip registers to defaults */ 1587 - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR); 1588 - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR); 1589 - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR); 1590 - imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR); 1588 + if (ret == 0) { 1589 + /* setup chip registers to defaults */ 1590 + imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR); 1591 + imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IFDR); 1592 + imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR); 1593 + imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR); 1594 + clk_disable(i2c_imx->clk); 1595 + } 1591 1596 1592 1597 clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb); 1593 1598 irq = platform_get_irq(pdev, 0); 1594 1599 if (irq >= 0) 1595 1600 free_irq(irq, i2c_imx); 1596 - clk_disable_unprepare(i2c_imx->clk); 1601 + 1602 + clk_unprepare(i2c_imx->clk); 1597 1603 1598 1604 pm_runtime_put_noidle(&pdev->dev); 1599 1605 pm_runtime_disable(&pdev->dev);