[PATCH] gpio: Add simple poweroff-gpio driver

Jamie Lentin jm at lentin.co.uk
Thu May 3 04:24:06 EDT 2012


Given appropriate devicetree bindings, this driver registers a
pm_power_off function to set a GPIO line high/low and power down
your board.

Signed-off-by: Jamie Lentin <jm at lentin.co.uk>
---
Here's an attempt at doing this. All works fine, I've not submitted a
patch to use it since GPIO patches for kirkwood aren't finalised, but
you can see it being used here:-

https://github.com/lentinj/linux.git v3.4-rc4-clk-next-orion-dnskw-gpio

The main issue is where to put it. I'm not convinced that drivers/gpio
is an appropriate home, however drivers/power and drivers/hwmon (where
drivers with similar purpose live) contain frameworks this is not part
of. drivers/mfd has some modules that can power off, but this is hardly
multifunction :) Could also give in and use drivers/misc.

Feedback appreciated.

 .../devicetree/bindings/gpio/poweroff-gpio.txt     |   15 ++++
 drivers/gpio/Kconfig                               |   10 +++
 drivers/gpio/Makefile                              |    2 +
 drivers/gpio/poweroff-gpio.c                       |   95 ++++++++++++++++++++
 4 files changed, 122 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/poweroff-gpio.txt
 create mode 100644 drivers/gpio/poweroff-gpio.c

diff --git a/Documentation/devicetree/bindings/gpio/poweroff-gpio.txt b/Documentation/devicetree/bindings/gpio/poweroff-gpio.txt
new file mode 100644
index 0000000..be52486
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/poweroff-gpio.txt
@@ -0,0 +1,15 @@
+GPIO line that should be set high/low to power off a device
+
+Required properties:
+- compatible : should be "gpio-poweroff".
+- gpios : The GPIO to set high/low, see "gpios property" in
+  Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be
+  low to power down the board set it to "Active Low", otherwise set
+  gpio to "Active High".
+
+Examples:
+
+gpio-poweroff {
+	compatible = "gpio-poweroff";
+	gpios = <&gpio 4 0>; /* GPIO 4 Active High */
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e03653d..48a531b 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -63,6 +63,16 @@ config GPIO_SYSFS
 	  Kernel drivers may also request that a particular GPIO be
 	  exported to userspace; this can be useful when debugging.
 
+config GPIO_POWEROFF
+	tristate "GPIO power-off driver"
+	depends on OF_GPIO
+	help
+	  This driver supports turning off your device via. a GPIO call.
+	  If your device needs a GPIO high/low to power down, say Y and
+	  create a binding in your devicetree.
+
+	  If unsure, say N
+
 config GPIO_GENERIC
 	tristate
 
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 007f54b..54adeb8 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -4,6 +4,8 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
 
 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o devres.o
 
+obj-$(CONFIG_GPIO_POWEROFF)	+= poweroff-gpio.o
+
 # Device drivers. Generally keep list sorted alphabetically
 obj-$(CONFIG_GPIO_GENERIC)	+= gpio-generic.o
 
diff --git a/drivers/gpio/poweroff-gpio.c b/drivers/gpio/poweroff-gpio.c
new file mode 100644
index 0000000..3d216aa
--- /dev/null
+++ b/drivers/gpio/poweroff-gpio.c
@@ -0,0 +1,95 @@
+/*
+ * Toggles a GPIO pin to power down a device
+ *
+ * Jamie Lentin <jm at lentin.co.uk>
+ * Copyright (C) 2012 Jamie Lentin
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/module.h>
+
+/*
+ * Hold configuration here, cannot be more than one instance of the driver
+ * since pm_power_off itself is global.
+ */
+static int gpio_num;
+static int gpio_active_low;
+
+static void gpio_poweroff_do_poweroff(void)
+{
+	gpio_set_value(gpio_num, !gpio_active_low);
+}
+
+static int __devinit gpio_poweroff_probe(struct platform_device *pdev)
+{
+	enum of_gpio_flags flags;
+
+	gpio_num = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
+	if (gpio_num < 0) {
+		pr_err("%s: Could not get GPIO configuration: %d",
+		       __func__, gpio_num);
+		return -ENODEV;
+	}
+	gpio_active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+	/* If a pm_power_off function has already been added, leave it alone */
+	if (pm_power_off != NULL) {
+		pr_err("%s: pm_power_off function already registered",
+		       __func__);
+		return -EBUSY;
+	}
+
+	if (gpio_request(gpio_num, "poweroff-gpio")) {
+		pr_err("%s: Could not get GPIO %d", __func__, gpio_num);
+		return -ENODEV;
+	}
+	if (gpio_direction_output(gpio_num, gpio_active_low)) {
+		pr_err("%s: Could not set direction of GPIO %d",
+		       __func__, gpio_num);
+		return -ENODEV;
+	}
+
+	pm_power_off = &gpio_poweroff_do_poweroff;
+	return 0;
+}
+
+static int __devexit gpio_poweroff_remove(struct platform_device *pdev)
+{
+	if (gpio_num)
+		gpio_free(gpio_num);
+	if (pm_power_off == &gpio_poweroff_do_poweroff)
+		pm_power_off = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id of_gpio_poweroff_match[] = {
+	{ .compatible = "gpio-poweroff", },
+	{},
+};
+
+static struct platform_driver gpio_poweroff_driver = {
+	.probe = gpio_poweroff_probe,
+	.remove = __devexit_p(gpio_poweroff_remove),
+	.driver = {
+		   .name = "poweroff-gpio",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_gpio_poweroff_match,
+		   },
+};
+
+module_platform_driver(gpio_poweroff_driver);
+
+MODULE_AUTHOR("Jamie Lentin <jm at lentin.co.uk>");
+MODULE_DESCRIPTION("GPIO poweroff driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:poweroff-gpio");
-- 
1.7.10




More information about the linux-arm-kernel mailing list