diff options
-rw-r--r-- | Documentation/devicetree/bindings/gpio/gpio-restart.txt | 54 | ||||
-rw-r--r-- | drivers/power/reset/Kconfig | 8 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/reset/gpio-restart.c | 149 |
4 files changed, 212 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/gpio/gpio-restart.txt b/Documentation/devicetree/bindings/gpio/gpio-restart.txt new file mode 100644 index 000000000000..af3701bc15c4 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-restart.txt | |||
@@ -0,0 +1,54 @@ | |||
1 | Drive a GPIO line that can be used to restart the system from a restart | ||
2 | handler. | ||
3 | |||
4 | This binding supports level and edge triggered reset. At driver load | ||
5 | time, the driver will request the given gpio line and install a restart | ||
6 | handler. If the optional properties 'open-source' is not found, the GPIO line | ||
7 | will be driven in the inactive state. Otherwise its not driven until | ||
8 | the restart is initiated. | ||
9 | |||
10 | When the system is restarted, the restart handler will be invoked in | ||
11 | priority order. The gpio is configured as an output, and driven active, | ||
12 | triggering a level triggered reset condition. This will also cause an | ||
13 | inactive->active edge condition, triggering positive edge triggered | ||
14 | reset. After a delay specified by active-delay, the GPIO is set to | ||
15 | inactive, thus causing an active->inactive edge, triggering negative edge | ||
16 | triggered reset. After a delay specified by inactive-delay, the GPIO | ||
17 | is driven active again. After a delay specified by wait-delay, the | ||
18 | restart handler completes allowing other restart handlers to be attempted. | ||
19 | |||
20 | Required properties: | ||
21 | - compatible : should be "gpio-restart". | ||
22 | - gpios : The GPIO to set high/low, see "gpios property" in | ||
23 | Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be | ||
24 | low to reset the board set it to "Active Low", otherwise set | ||
25 | gpio to "Active High". | ||
26 | |||
27 | Optional properties: | ||
28 | - open-source : Treat the GPIO as being open source and defer driving | ||
29 | it to when the restart is initiated. If this optional property is not | ||
30 | specified, the GPIO is initialized as an output in its inactive state. | ||
31 | - priority : A priority ranging from 0 to 255 (default 128) according to | ||
32 | the following guidelines: | ||
33 | 0: Restart handler of last resort, with limited restart | ||
34 | capabilities | ||
35 | 128: Default restart handler; use if no other restart handler is | ||
36 | expected to be available, and/or if restart functionality is | ||
37 | sufficient to restart the entire system | ||
38 | 255: Highest priority restart handler, will preempt all other | ||
39 | restart handlers | ||
40 | - active-delay: Delay (default 100) to wait after driving gpio active [ms] | ||
41 | - inactive-delay: Delay (default 100) to wait after driving gpio inactive [ms] | ||
42 | - wait-delay: Delay (default 3000) to wait after completing restart | ||
43 | sequence [ms] | ||
44 | |||
45 | Examples: | ||
46 | |||
47 | gpio-restart { | ||
48 | compatible = "gpio-restart"; | ||
49 | gpios = <&gpio 4 0>; | ||
50 | priority = <128>; | ||
51 | active-delay = <100>; | ||
52 | inactive-delay = <100>; | ||
53 | wait-delay = <3000>; | ||
54 | }; | ||
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 88603b6990ed..a178e9c50a20 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig | |||
@@ -39,6 +39,14 @@ config POWER_RESET_GPIO | |||
39 | If your board needs a GPIO high/low to power down, say Y and | 39 | If your board needs a GPIO high/low to power down, say Y and |
40 | create a binding in your devicetree. | 40 | create a binding in your devicetree. |
41 | 41 | ||
42 | config POWER_RESET_GPIO_RESTART | ||
43 | bool "GPIO restart driver" | ||
44 | depends on OF_GPIO && POWER_RESET | ||
45 | help | ||
46 | This driver supports restarting your board via a GPIO line. | ||
47 | If your board needs a GPIO high/low to restart, say Y and | ||
48 | create a binding in your devicetree. | ||
49 | |||
42 | config POWER_RESET_HISI | 50 | config POWER_RESET_HISI |
43 | bool "Hisilicon power-off driver" | 51 | bool "Hisilicon power-off driver" |
44 | depends on POWER_RESET && ARCH_HISI | 52 | depends on POWER_RESET && ARCH_HISI |
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 06f106b6cfc8..ab8cd348a099 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile | |||
@@ -2,6 +2,7 @@ obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o | |||
2 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o | 2 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o |
3 | obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o | 3 | obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o |
4 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o | 4 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o |
5 | obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o | ||
5 | obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o | 6 | obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o |
6 | obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o | 7 | obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o |
7 | obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o | 8 | obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o |
diff --git a/drivers/power/reset/gpio-restart.c b/drivers/power/reset/gpio-restart.c new file mode 100644 index 000000000000..a76829b3f1cd --- /dev/null +++ b/drivers/power/reset/gpio-restart.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Toggles a GPIO pin to restart a device | ||
3 | * | ||
4 | * Copyright (C) 2014 Google, Inc. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Based on the gpio-poweroff driver. | ||
16 | */ | ||
17 | #include <linux/reboot.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/gpio/consumer.h> | ||
23 | #include <linux/of_platform.h> | ||
24 | #include <linux/module.h> | ||
25 | |||
26 | struct gpio_restart { | ||
27 | struct gpio_desc *reset_gpio; | ||
28 | struct notifier_block restart_handler; | ||
29 | u32 active_delay_ms; | ||
30 | u32 inactive_delay_ms; | ||
31 | u32 wait_delay_ms; | ||
32 | }; | ||
33 | |||
34 | static int gpio_restart_notify(struct notifier_block *this, | ||
35 | unsigned long mode, void *cmd) | ||
36 | { | ||
37 | struct gpio_restart *gpio_restart = | ||
38 | container_of(this, struct gpio_restart, restart_handler); | ||
39 | |||
40 | /* drive it active, also inactive->active edge */ | ||
41 | gpiod_direction_output(gpio_restart->reset_gpio, 1); | ||
42 | mdelay(gpio_restart->active_delay_ms); | ||
43 | |||
44 | /* drive inactive, also active->inactive edge */ | ||
45 | gpiod_set_value(gpio_restart->reset_gpio, 0); | ||
46 | mdelay(gpio_restart->inactive_delay_ms); | ||
47 | |||
48 | /* drive it active, also inactive->active edge */ | ||
49 | gpiod_set_value(gpio_restart->reset_gpio, 1); | ||
50 | |||
51 | /* give it some time */ | ||
52 | mdelay(gpio_restart->wait_delay_ms); | ||
53 | |||
54 | WARN_ON(1); | ||
55 | |||
56 | return NOTIFY_DONE; | ||
57 | } | ||
58 | |||
59 | static int gpio_restart_probe(struct platform_device *pdev) | ||
60 | { | ||
61 | struct gpio_restart *gpio_restart; | ||
62 | bool open_source = false; | ||
63 | u32 property; | ||
64 | int ret; | ||
65 | |||
66 | gpio_restart = devm_kzalloc(&pdev->dev, sizeof(*gpio_restart), | ||
67 | GFP_KERNEL); | ||
68 | if (!gpio_restart) | ||
69 | return -ENOMEM; | ||
70 | |||
71 | open_source = of_property_read_bool(pdev->dev.of_node, "open-source"); | ||
72 | |||
73 | gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, | ||
74 | open_source ? GPIOD_IN : GPIOD_OUT_LOW); | ||
75 | if (IS_ERR(gpio_restart->reset_gpio)) { | ||
76 | dev_err(&pdev->dev, "Could net get reset GPIO\n"); | ||
77 | return PTR_ERR(gpio_restart->reset_gpio); | ||
78 | } | ||
79 | |||
80 | gpio_restart->restart_handler.notifier_call = gpio_restart_notify; | ||
81 | gpio_restart->restart_handler.priority = 128; | ||
82 | gpio_restart->active_delay_ms = 100; | ||
83 | gpio_restart->inactive_delay_ms = 100; | ||
84 | gpio_restart->wait_delay_ms = 3000; | ||
85 | |||
86 | ret = of_property_read_u32(pdev->dev.of_node, "priority", &property); | ||
87 | if (!ret) { | ||
88 | if (property > 255) | ||
89 | dev_err(&pdev->dev, "Invalid priority property: %u\n", | ||
90 | property); | ||
91 | else | ||
92 | gpio_restart->restart_handler.priority = property; | ||
93 | } | ||
94 | |||
95 | of_property_read_u32(pdev->dev.of_node, "active-delay", | ||
96 | &gpio_restart->active_delay_ms); | ||
97 | of_property_read_u32(pdev->dev.of_node, "inactive-delay", | ||
98 | &gpio_restart->inactive_delay_ms); | ||
99 | of_property_read_u32(pdev->dev.of_node, "wait-delay", | ||
100 | &gpio_restart->wait_delay_ms); | ||
101 | |||
102 | platform_set_drvdata(pdev, gpio_restart); | ||
103 | |||
104 | ret = register_restart_handler(&gpio_restart->restart_handler); | ||
105 | if (ret) { | ||
106 | dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n", | ||
107 | __func__, ret); | ||
108 | return -ENODEV; | ||
109 | } | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int gpio_restart_remove(struct platform_device *pdev) | ||
115 | { | ||
116 | struct gpio_restart *gpio_restart = platform_get_drvdata(pdev); | ||
117 | int ret; | ||
118 | |||
119 | ret = unregister_restart_handler(&gpio_restart->restart_handler); | ||
120 | if (ret) { | ||
121 | dev_err(&pdev->dev, | ||
122 | "%s: cannot unregister restart handler, %d\n", | ||
123 | __func__, ret); | ||
124 | return -ENODEV; | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static const struct of_device_id of_gpio_restart_match[] = { | ||
131 | { .compatible = "gpio-restart", }, | ||
132 | {}, | ||
133 | }; | ||
134 | |||
135 | static struct platform_driver gpio_restart_driver = { | ||
136 | .probe = gpio_restart_probe, | ||
137 | .remove = gpio_restart_remove, | ||
138 | .driver = { | ||
139 | .name = "restart-gpio", | ||
140 | .owner = THIS_MODULE, | ||
141 | .of_match_table = of_gpio_restart_match, | ||
142 | }, | ||
143 | }; | ||
144 | |||
145 | module_platform_driver(gpio_restart_driver); | ||
146 | |||
147 | MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); | ||
148 | MODULE_DESCRIPTION("GPIO restart driver"); | ||
149 | MODULE_LICENSE("GPL"); | ||