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