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
 -----BEGIN PGP SIGNATURE-----
 
 iHQEABYKAB0WIQScDfrjQa34uOld1VLaeAVmJtMtbgUCaivYkgAKCRDaeAVmJtMt
 bq6BAQDIi1pJu3mcfXZagsn/DWVqM55a6H8pvYHkpdIQFcchXQD4lgbiopNoKusj
 I5qIKtRs2KD/FLAhWYl0vhEoY4PkBw==
 =LfKo
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Wolfram Sang 2026-06-12 17:05:44 +02:00
commit 35ebcfcc9e
7 changed files with 87 additions and 58 deletions

View file

@ -12095,11 +12095,11 @@ F: Documentation/i2c/busses/i2c-parport.rst
F: drivers/i2c/busses/i2c-parport.c
I2C SUBSYSTEM
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
M: Andi Shyti <andi.shyti@kernel.org>
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

View file

@ -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;
}

View file

@ -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)

View file

@ -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);

View file

@ -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) {

View file

@ -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",

View file

@ -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,
@ -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)
};