diff options
Diffstat (limited to 'drivers/i2c/i2c-core.c')
-rw-r--r-- | drivers/i2c/i2c-core.c | 51 |
1 files changed, 42 insertions, 9 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 98f6c75b1d18..5f89f1e3c2f2 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -48,6 +48,7 @@ #include <linux/rwsem.h> #include <linux/pm_runtime.h> #include <linux/pm_domain.h> +#include <linux/pm_wakeirq.h> #include <linux/acpi.h> #include <linux/jump_label.h> #include <asm/uaccess.h> @@ -645,11 +646,13 @@ static int i2c_device_probe(struct device *dev) if (!client->irq) { int irq = -ENOENT; - if (dev->of_node) - irq = of_irq_get(dev->of_node, 0); - else if (ACPI_COMPANION(dev)) + if (dev->of_node) { + irq = of_irq_get_byname(dev->of_node, "irq"); + if (irq == -EINVAL || irq == -ENODATA) + irq = of_irq_get(dev->of_node, 0); + } else if (ACPI_COMPANION(dev)) { irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0); - + } if (irq == -EPROBE_DEFER) return irq; if (irq < 0) @@ -662,23 +665,49 @@ static int i2c_device_probe(struct device *dev) if (!driver->probe || !driver->id_table) return -ENODEV; - if (!device_can_wakeup(&client->dev)) - device_init_wakeup(&client->dev, - client->flags & I2C_CLIENT_WAKE); + if (client->flags & I2C_CLIENT_WAKE) { + int wakeirq = -ENOENT; + + if (dev->of_node) { + wakeirq = of_irq_get_byname(dev->of_node, "wakeup"); + if (wakeirq == -EPROBE_DEFER) + return wakeirq; + } + + device_init_wakeup(&client->dev, true); + + if (wakeirq > 0 && wakeirq != client->irq) + status = dev_pm_set_dedicated_wake_irq(dev, wakeirq); + else if (client->irq > 0) + status = dev_pm_set_wake_irq(dev, wakeirq); + else + status = 0; + + if (status) + dev_warn(&client->dev, "failed to set up wakeup irq"); + } + dev_dbg(dev, "probe\n"); status = of_clk_set_defaults(dev->of_node, false); if (status < 0) - return status; + goto err_clear_wakeup_irq; status = dev_pm_domain_attach(&client->dev, true); if (status != -EPROBE_DEFER) { status = driver->probe(client, i2c_match_id(driver->id_table, client)); if (status) - dev_pm_domain_detach(&client->dev, true); + goto err_detach_pm_domain; } + return 0; + +err_detach_pm_domain: + dev_pm_domain_detach(&client->dev, true); +err_clear_wakeup_irq: + dev_pm_clear_wake_irq(&client->dev); + device_init_wakeup(&client->dev, false); return status; } @@ -698,6 +727,10 @@ static int i2c_device_remove(struct device *dev) } dev_pm_domain_detach(&client->dev, true); + + dev_pm_clear_wake_irq(&client->dev); + device_init_wakeup(&client->dev, false); + return status; } |