aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJamie Lentin <jm@lentin.co.uk>2012-11-17 03:51:04 -0500
committerJason Cooper <jason@lakedaemon.net>2012-11-23 21:56:38 -0500
commit96ff0f5c7efd4a2205c48a76a6a1fcd2731e6128 (patch)
treed928f8f738dcc2736d19a6920e5b73bc158f3ae7
parentf4a00139b7cbeff538e616a21f6b57249a9d3ed8 (diff)
power: Add simple poweroff-gpio driver
Given appropriate devicetree bindings, this driver registers a pm_power_off function to set a GPIO line high/low to power down your board. Signed-off-by: Jamie Lentin <jm@lentin.co.uk> Signed-off-by: Andrew Lunn <andrew@lunn.ch> Tested-by:Simon Baatz <gmbnomis@gmail.com> Signed-off-by: Jason Cooper <jason@lakedaemon.net>
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-poweroff.txt22
-rw-r--r--drivers/power/Kconfig3
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/reset/Kconfig15
-rw-r--r--drivers/power/reset/Makefile1
-rw-r--r--drivers/power/reset/gpio-poweroff.c129
6 files changed, 171 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt b/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt
new file mode 100644
index 000000000000..558cdf3c9abc
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt
@@ -0,0 +1,22 @@
1GPIO line that should be set high/low to power off a device
2
3Required properties:
4- compatible : should be "gpio-poweroff".
5- gpios : The GPIO to set high/low, see "gpios property" in
6 Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be
7 low to power down the board set it to "Active Low", otherwise set
8 gpio to "Active High".
9
10Optional properties:
11- input : Initially configure the GPIO line as an input. Only reconfigure
12 it to an output when the pm_power_off function is called. If this optional
13 property is not specified, the GPIO is initialized as an output in its
14 inactive state.
15
16
17Examples:
18
19gpio-poweroff {
20 compatible = "gpio-poweroff";
21 gpios = <&gpio 4 0>; /* GPIO 4 Active Low */
22};
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 49a893972318..b1d956d81f0c 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -335,6 +335,9 @@ config AB8500_BATTERY_THERM_ON_BATCTRL
335 help 335 help
336 Say Y to enable battery temperature measurements using 336 Say Y to enable battery temperature measurements using
337 thermistor connected on BATCTRL ADC. 337 thermistor connected on BATCTRL ADC.
338
339source "drivers/power/reset/Kconfig"
340
338endif # POWER_SUPPLY 341endif # POWER_SUPPLY
339 342
340source "drivers/power/avs/Kconfig" 343source "drivers/power/avs/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b949cf85590c..f1d99f4a0bc3 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -49,3 +49,4 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
49obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o 49obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
50obj-$(CONFIG_POWER_AVS) += avs/ 50obj-$(CONFIG_POWER_AVS) += avs/
51obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o 51obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
52obj-$(CONFIG_POWER_RESET) += reset/
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
new file mode 100644
index 000000000000..6461b489fb09
--- /dev/null
+++ b/drivers/power/reset/Kconfig
@@ -0,0 +1,15 @@
1menuconfig POWER_RESET
2 bool "Board level reset or power off"
3 help
4 Provides a number of drivers which either reset a complete board
5 or shut it down, by manipulating the main power supply on the board.
6
7 Say Y here to enable board reset and power off
8
9config POWER_RESET_GPIO
10 bool "GPIO power-off driver"
11 depends on OF_GPIO && POWER_RESET
12 help
13 This driver supports turning off your board via a GPIO line.
14 If your board needs a GPIO high/low to power down, say Y and
15 create a binding in your devicetree.
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
new file mode 100644
index 000000000000..751488a4a0c5
--- /dev/null
+++ b/drivers/power/reset/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c
new file mode 100644
index 000000000000..0491e5335d02
--- /dev/null
+++ b/drivers/power/reset/gpio-poweroff.c
@@ -0,0 +1,129 @@
1/*
2 * Toggles a GPIO pin to power down a device
3 *
4 * Jamie Lentin <jm@lentin.co.uk>
5 * Andrew Lunn <andrew@lunn.ch>
6 *
7 * Copyright (C) 2012 Jamie Lentin
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 *
13 */
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/delay.h>
17#include <linux/platform_device.h>
18#include <linux/gpio.h>
19#include <linux/of_platform.h>
20#include <linux/of_gpio.h>
21#include <linux/module.h>
22
23/*
24 * Hold configuration here, cannot be more than one instance of the driver
25 * since pm_power_off itself is global.
26 */
27static int gpio_num = -1;
28static int gpio_active_low;
29
30static void gpio_poweroff_do_poweroff(void)
31{
32 BUG_ON(gpio_num == -1);
33
34 /* drive it active */
35 gpio_direction_output(gpio_num, !gpio_active_low);
36 mdelay(100);
37 /* rising edge or drive inactive */
38 gpio_set_value(gpio_num, gpio_active_low);
39 mdelay(100);
40 /* falling edge */
41 gpio_set_value(gpio_num, !gpio_active_low);
42
43 /* give it some time */
44 mdelay(3000);
45
46 WARN_ON(1);
47}
48
49static int __devinit gpio_poweroff_probe(struct platform_device *pdev)
50{
51 enum of_gpio_flags flags;
52 bool input = false;
53 int ret;
54
55 /* If a pm_power_off function has already been added, leave it alone */
56 if (pm_power_off != NULL) {
57 pr_err("%s: pm_power_off function already registered",
58 __func__);
59 return -EBUSY;
60 }
61
62 gpio_num = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
63 if (gpio_num < 0) {
64 pr_err("%s: Could not get GPIO configuration: %d",
65 __func__, gpio_num);
66 return -ENODEV;
67 }
68 gpio_active_low = flags & OF_GPIO_ACTIVE_LOW;
69
70 if (of_get_property(pdev->dev.of_node, "input", NULL))
71 input = true;
72
73 ret = gpio_request(gpio_num, "poweroff-gpio");
74 if (ret) {
75 pr_err("%s: Could not get GPIO %d", __func__, gpio_num);
76 return ret;
77 }
78 if (input) {
79 if (gpio_direction_input(gpio_num)) {
80 pr_err("Could not set direction of GPIO %d to input",
81 gpio_num);
82 goto err;
83 }
84 } else {
85 if (gpio_direction_output(gpio_num, gpio_active_low)) {
86 pr_err("Could not set direction of GPIO %d", gpio_num);
87 goto err;
88 }
89 }
90
91 pm_power_off = &gpio_poweroff_do_poweroff;
92 return 0;
93
94err:
95 gpio_free(gpio_num);
96 return -ENODEV;
97}
98
99static int __devexit gpio_poweroff_remove(struct platform_device *pdev)
100{
101 if (gpio_num != -1)
102 gpio_free(gpio_num);
103 if (pm_power_off == &gpio_poweroff_do_poweroff)
104 pm_power_off = NULL;
105
106 return 0;
107}
108
109static const struct of_device_id of_gpio_poweroff_match[] = {
110 { .compatible = "gpio-poweroff", },
111 {},
112};
113
114static struct platform_driver gpio_poweroff_driver = {
115 .probe = gpio_poweroff_probe,
116 .remove = __devexit_p(gpio_poweroff_remove),
117 .driver = {
118 .name = "poweroff-gpio",
119 .owner = THIS_MODULE,
120 .of_match_table = of_gpio_poweroff_match,
121 },
122};
123
124module_platform_driver(gpio_poweroff_driver);
125
126MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>");
127MODULE_DESCRIPTION("GPIO poweroff driver");
128MODULE_LICENSE("GPL");
129MODULE_ALIAS("platform:poweroff-gpio");