summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorWolfram Sang <wsa+renesas@sang-engineering.com>2026-06-12 17:05:44 +0200
committerWolfram Sang <wsa+renesas@sang-engineering.com>2026-06-12 17:05:44 +0200
commit35ebcfcc9ef29afcf258db854ccf25572c75baef (patch)
treefef851d3e7f8e223cf9c6f78172384a7e850b81a /drivers
parent5cad752e1e73c326c9d784ceb34c9129285539c5 (diff)
parent9bffa2a61551da9ac32171f09d556974cb96d70a (diff)
Merge tag 'i2c-host-fixes-7.1-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux into i2c/for-current
i2c-host-fixes for v7.1-rc8 - imx: keep clock and pinctrl states consistent in runtime PM - imx-lpi2c: fix DMA resource leaks on PIO fallback - qcom-cci: fix NULL pointer dereference on remove - riic: fix reset refcount leak on resume_noirq error path - stm32f7: account for analog filter in timing computation - tegra: fix suspend/resume handling in NOIRQ phase - tegra: update Tegra410 I2C timings to match hardware specs - MAINTAINERS: hand over I2C maintainership to Andi
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/busses/i2c-imx-lpi2c.c53
-rw-r--r--drivers/i2c/busses/i2c-imx.c15
-rw-r--r--drivers/i2c/busses/i2c-qcom-cci.c2
-rw-r--r--drivers/i2c/busses/i2c-riic.c4
-rw-r--r--drivers/i2c/busses/i2c-stm32f7.c6
-rw-r--r--drivers/i2c/busses/i2c-tegra.c61
6 files changed, 85 insertions, 56 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;
}
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)
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);
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) {
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",
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 479a1667e88d..400c9089daf0 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,
@@ -2402,28 +2402,37 @@ 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)
};