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 devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html