[PATCH] drivers: misc: add gpio wakeup driver

From: Daniel Mack
Date: Tue Oct 01 2013 - 09:55:31 EST


This patch adds a very simple driver that enables GPIO lines as wakeup
sources. It only operates on information passed in via DT, and depends
on CONFIG_OF && CONFIG_PM_SLEEP. It can for example be used to connect
wake-on-LAN (WOL) signals or other electric wakeup networks.

The driver accepts a list of GPIO nodes and claims them along with their
interrupt line. During suspend, the interrupts will be enabled and
selected as wakeup source. The driver doesn't do anything else with the
GPIO lines, and will ignore occured interrupts silently.

Signed-off-by: Daniel Mack <zonque@xxxxxxxxx>
---
.../devicetree/bindings/misc/gpio-wakeup.txt | 16 ++
drivers/misc/Kconfig | 8 +
drivers/misc/Makefile | 1 +
drivers/misc/gpio-wakeup.c | 162 +++++++++++++++++++++
4 files changed, 187 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/gpio-wakeup.txt
create mode 100644 drivers/misc/gpio-wakeup.c

diff --git a/Documentation/devicetree/bindings/misc/gpio-wakeup.txt b/Documentation/devicetree/bindings/misc/gpio-wakeup.txt
new file mode 100644
index 0000000..6aacd0f
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/gpio-wakeup.txt
@@ -0,0 +1,16 @@
+GPIO WAKEUP DRIVER BINDINGS
+
+Required:
+
+ compatible: Must be "gpio-wakeup"
+ gpios: At list of GPIO nodes that are enabled as wakeup
+ sources.
+
+
+Example:
+
+ wake_up {
+ compatible = "gpio-wakeup";
+ gpios = <&gpio0 19 0>;
+ };
+
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 8dacd4c..c089280 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -528,6 +528,14 @@ config SRAM
the genalloc API. It is supposed to be used for small on-chip SRAM
areas found on many SoCs.

+config GPIO_WAKEUP
+ tristate "GPIO wakeup driver"
+ depends on PM_SLEEP && OF
+ help
+ Say Y to build a driver that can wake up a system from GPIO
+ lines. See Documentation/devicetree/bindings/gpio-wakeup.txt
+ for binding details.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c235d5b..ee4df84 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_INTEL_MEI) += mei/
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
obj-$(CONFIG_SRAM) += sram.o
+obj-$(CONFIG_GPIO_WAKEUP) += gpio-wakeup.o
diff --git a/drivers/misc/gpio-wakeup.c b/drivers/misc/gpio-wakeup.c
new file mode 100644
index 0000000..3c1ef88
--- /dev/null
+++ b/drivers/misc/gpio-wakeup.c
@@ -0,0 +1,162 @@
+/*
+ * Driver to select GPIO lines as wakeup sources from DT.
+ *
+ * Copyright 2013 Daniel Mack
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+struct gpio_wakeup_priv {
+ unsigned int count;
+ unsigned int irq[0];
+};
+
+static irqreturn_t gpio_wakeup_isr(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
+
+static int gpio_wakeup_probe(struct platform_device *pdev)
+{
+ int ret, count, i;
+ struct gpio_wakeup_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return -EINVAL;
+
+ count = of_gpio_count(np);
+ if (count == 0)
+ return -EINVAL;
+
+ priv = devm_kzalloc(dev, sizeof(*priv) +
+ sizeof(int) * count, GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ unsigned int gpio, irq;
+
+ priv->irq[i] = -EINVAL;
+
+ gpio = of_get_gpio(np, i);
+ if (gpio < 0) {
+ dev_warn(dev, "Unable to get gpio #%d\n", i);
+ continue;
+ }
+
+ irq = gpio_to_irq(gpio);
+ if (irq < 0) {
+ dev_warn(dev, "Can't map GPIO %d to an IRQ\n", gpio);
+ continue;
+ }
+
+ ret = devm_gpio_request_one(dev, gpio, GPIOF_IN, pdev->name);
+ if (ret < 0) {
+ dev_warn(dev, "Unable to request GPIO %d: %d\n",
+ gpio, ret);
+ continue;
+ }
+
+ ret = devm_request_irq(dev, irq, gpio_wakeup_isr,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ pdev->name, NULL);
+ if (ret < 0) {
+ dev_warn(dev, "Unable to request IRQ %d\n", irq);
+ continue;
+ }
+
+ disable_irq(irq);
+ priv->irq[i] = irq;
+
+ dev_info(dev, "Adding GPIO %d (IRQ %d) to wakeup sources\n",
+ gpio, irq);
+ }
+
+ priv->count = count;
+ device_init_wakeup(dev, 1);
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static int gpio_wakeup_suspend(struct device *dev)
+{
+ struct gpio_wakeup_priv *priv = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < priv->count; i++)
+ if (priv->irq[i] >= 0) {
+ enable_irq(priv->irq[i]);
+ enable_irq_wake(priv->irq[i]);
+ }
+
+ return 0;
+}
+
+static int gpio_wakeup_resume(struct device *dev)
+{
+ struct gpio_wakeup_priv *priv = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < priv->count; i++)
+ if (priv->irq[i] >= 0) {
+ disable_irq_wake(priv->irq[i]);
+ disable_irq(priv->irq[i]);
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(gpio_wakeup_pm_ops,
+ gpio_wakeup_suspend, gpio_wakeup_resume);
+
+static struct of_device_id gpio_wakeup_of_match[] = {
+ { .compatible = "gpio-wakeup", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, gpio_wakeup_of_match);
+
+static struct platform_driver gpio_wakeup_driver = {
+ .probe = gpio_wakeup_probe,
+ .driver = {
+ .name = "gpio-wakeup",
+ .owner = THIS_MODULE,
+ .pm = &gpio_wakeup_pm_ops,
+ .of_match_table = of_match_ptr(gpio_wakeup_of_match),
+ }
+};
+
+static int __init gpio_wakeup_init(void)
+{
+ return platform_driver_register(&gpio_wakeup_driver);
+}
+
+static void __exit gpio_wakeup_exit(void)
+{
+ platform_driver_unregister(&gpio_wakeup_driver);
+}
+
+late_initcall(gpio_wakeup_init);
+module_exit(gpio_wakeup_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Daniel Mack <zonque@xxxxxxxxx>");
+MODULE_DESCRIPTION("Driver to wake up systems from GPIOs");
+MODULE_ALIAS("platform:gpio-wakeup");
--
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/