Merge tag 'i2c-fixes-7.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux

Pull i2c fixes from Andi Shyti:
 "A set of fixes across several host controller drivers. The largest
  part addresses three issues in the i.MX driver, while the remaining
  changes fix probe ordering, power management, timeout recovery and
  error handling.

  amd-mp2:
   - unregister callback if adapter registration fails

  designware:
   - defer probe until child GPIO controllers are bound

  imx:
   - mark adapter suspended while hardware is powered down
   - fix stale slave pointer and shared IRQ registration race
   - stop slave timer before clearing slave pointer

  iproc:
   - reset controller if START_BUSY remains set after timeout

  jz4780:
   - cache clock rate to avoid clk_get_rate() deadlock

  qcom-cci:
   - rely on runtime PM helpers for system sleep

  spacemit:
   - request interrupt after clock initialization"

* tag 'i2c-fixes-7.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/andi.shyti/linux:
  i2c: qcom-cci: drop custom suspend/resume and rely on runtime PM helpers
  i2c: imx: Cancel hrtimer before clearing slave pointer
  i2c: imx: Fix slave registration race and error handling
  i2c: iproc: reset bus after timeout if START_BUSY is stuck
  i2c: imx: mark I2C adapter when hardware is powered down
  i2c: designware: defer probe if child GpioInt controllers are not bound
  i2c: jz4780: Cache host clock rate at probe to prevent CCF prepare_lock deadlock
  i2c: amd-mp2: Unregister callback on adapter add failure
  i2c: spacemit: request IRQ after controller initialization
This commit is contained in:
Linus Torvalds
2026-08-01 09:02:45 -07:00
7 changed files with 153 additions and 29 deletions

View File

@@ -316,8 +316,10 @@ static int i2c_amd_probe(struct platform_device *pdev)
amd_mp2_pm_runtime_put(mp2_dev);
if (ret < 0)
if (ret < 0) {
dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret);
amd_mp2_unregister_cb(&i2c_dev->common);
}
return ret;
}

View File

@@ -803,6 +803,17 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
}
if (!time_left && !iproc_i2c->xfer_is_done) {
/*
* The controller may fail to clear START_BUSY after a timeout,
* reset the controller to recover in that case.
*/
if (!!(iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET) &
BIT(M_CMD_START_BUSY_SHIFT))) {
bcm_iproc_i2c_enable_disable(iproc_i2c, false);
bcm_iproc_i2c_init(iproc_i2c);
bcm_iproc_i2c_enable_disable(iproc_i2c, true);
}
/* flush both TX/RX FIFOs */
val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);

View File

@@ -8,12 +8,14 @@
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
*/
#include <linux/acpi.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -130,6 +132,80 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
return 0;
}
#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
/*
* Check whether an ACPI GpioInt resource's referenced GPIO controller
* has finished probing. Resources with no named controller (resource
* source string) are skipped, since they can't be resolved to a
* struct device.
*/
static int check_gpioint_resource(struct acpi_resource *ares, void *data)
{
struct acpi_resource_gpio *agpio;
struct acpi_device *gpio_adev;
struct device *gpio_dev;
acpi_handle handle;
acpi_status status;
if (!acpi_gpio_get_irq_resource(ares, &agpio))
return 1; /* not a GpioInt resource, skip */
if (!agpio->resource_source.string_length)
return 1; /* no named controller, skip */
status = acpi_get_handle(NULL, agpio->resource_source.string_ptr, &handle);
if (ACPI_FAILURE(status))
return 1;
gpio_adev = acpi_fetch_acpi_dev(handle);
if (!gpio_adev)
return 1;
struct gpio_device *gdev __free(gpio_device_put) =
gpio_device_find_by_fwnode(acpi_fwnode_handle(gpio_adev));
if (!gdev)
return -EPROBE_DEFER; /* controller not registered yet: abort walk */
gpio_dev = gpio_device_to_device(gdev)->parent;
guard(device)(gpio_dev);
if (!device_is_bound(gpio_dev))
return -EPROBE_DEFER; /* controller not bound yet: abort walk */
return 1; /* bound, skip adding to resource list, continue walk */
}
static int check_child_gpioint(struct acpi_device *adev, void *data)
{
LIST_HEAD(res_list);
int ret;
ret = acpi_dev_get_resources(adev, &res_list, check_gpioint_resource, NULL);
if (ret < 0)
return ret;
acpi_dev_free_resource_list(&res_list);
return 0;
}
static int i2c_dw_check_gpio_dependencies(struct device *dev)
{
struct acpi_device *adev;
adev = ACPI_COMPANION(dev);
if (!adev)
return 0;
return acpi_dev_for_each_child(adev, check_child_gpioint, NULL);
}
#else
static int i2c_dw_check_gpio_dependencies(struct device *dev)
{
return 0;
}
#endif /* CONFIG_ACPI && CONFIG_GPIOLIB */
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
u32 flags = (uintptr_t)device_get_match_data(&pdev->dev);
@@ -138,6 +214,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
struct dw_i2c_dev *dev;
int irq, ret;
ret = i2c_dw_check_gpio_dependencies(device);
if (ret)
return ret;
irq = platform_get_irq_optional(pdev, 0);
if (irq == -ENXIO)
flags |= ACCESS_POLLING;

View File

@@ -930,9 +930,6 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
if (i2c_imx->slave)
return -EBUSY;
i2c_imx->slave = client;
i2c_imx->last_slave_event = I2C_SLAVE_STOP;
/* Resume */
ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
if (ret < 0) {
@@ -940,6 +937,11 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
return ret;
}
scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) {
i2c_imx->slave = client;
i2c_imx->last_slave_event = I2C_SLAVE_STOP;
}
i2c_imx_slave_init(i2c_imx);
return 0;
@@ -958,6 +960,7 @@ static int i2c_imx_unreg_slave(struct i2c_client *client)
i2c_imx_reset_regs(i2c_imx);
hrtimer_cancel(&i2c_imx->slave_timer);
i2c_imx->slave = NULL;
/* Suspend */
@@ -1952,6 +1955,47 @@ static int i2c_imx_runtime_resume(struct device *dev)
return 0;
}
static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev)
{
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
int ret;
i2c_mark_adapter_suspended(&i2c_imx->adapter);
/*
* Cancel the slave timer before powering down to prevent
* i2c_imx_slave_timeout() from accessing hardware registers
* while the clock is disabled.
*/
hrtimer_cancel(&i2c_imx->slave_timer);
ret = pm_runtime_force_suspend(dev);
if (ret) {
i2c_mark_adapter_resumed(&i2c_imx->adapter);
if (i2c_imx->slave) {
hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY);
hrtimer_restart(&i2c_imx->slave_timer);
}
return ret;
}
return 0;
}
static int __maybe_unused i2c_imx_resume_noirq(struct device *dev)
{
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_force_resume(dev);
if (ret)
return ret;
i2c_mark_adapter_resumed(&i2c_imx->adapter);
return 0;
}
static int i2c_imx_suspend(struct device *dev)
{
/*
@@ -1985,8 +2029,8 @@ static int i2c_imx_resume(struct device *dev)
}
static const struct dev_pm_ops i2c_imx_pm_ops = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq,
i2c_imx_resume_noirq)
SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
};

View File

@@ -141,6 +141,7 @@ struct jz4780_i2c {
void __iomem *iomem;
int irq;
struct clk *clk;
unsigned long clk_rate_khz;
struct i2c_adapter adap;
const struct ingenic_i2c_config *cdata;
@@ -246,7 +247,7 @@ static int jz4780_i2c_set_target(struct jz4780_i2c *i2c, unsigned char address)
static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c)
{
int dev_clk_khz = clk_get_rate(i2c->clk) / 1000;
int dev_clk_khz = i2c->clk_rate_khz;
int cnt_high = 0; /* HIGH period count of the SCL clock */
int cnt_low = 0; /* LOW period count of the SCL clock */
int cnt_period = 0; /* period count of the SCL clock */
@@ -796,6 +797,8 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
if (IS_ERR(i2c->clk))
return PTR_ERR(i2c->clk);
i2c->clk_rate_khz = clk_get_rate(i2c->clk) / 1000;
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&clk_freq);
if (ret) {

View File

@@ -723,11 +723,6 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
if (i2c->irq < 0)
return dev_err_probe(dev, i2c->irq, "failed to get irq resource");
ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c);
if (ret)
return dev_err_probe(dev, ret, "failed to request irq");
clk = devm_clk_get_enabled(dev, "func");
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock");
@@ -755,6 +750,11 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
init_completion(&i2c->complete);
ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c);
if (ret)
return dev_err_probe(dev, ret, "failed to request irq");
platform_set_drvdata(pdev, i2c);
ret = i2c_add_numbered_adapter(&i2c->adapt);

View File

@@ -492,24 +492,8 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
return 0;
}
static int __maybe_unused cci_suspend(struct device *dev)
{
if (!pm_runtime_suspended(dev))
return cci_suspend_runtime(dev);
return 0;
}
static int __maybe_unused cci_resume(struct device *dev)
{
cci_resume_runtime(dev);
pm_request_autosuspend(dev);
return 0;
}
static const struct dev_pm_ops qcom_cci_pm = {
SET_SYSTEM_SLEEP_PM_OPS(cci_suspend, cci_resume)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(cci_suspend_runtime, cci_resume_runtime, NULL)
};