i2c: allow specifying separate wakeup interrupt in device tree
[linux-drm-fsl-dcu.git] / drivers / i2c / i2c-core.c
index 98f6c75b1d18e4d685d381d6c1b43056b3c99452..5f89f1e3c2f24fc562a519eb173d33de8c280f42 100644 (file)
@@ -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;
 }