mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 14:04:27 -04:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user