]>
Commit | Line | Data |
---|---|---|
371bb20d DR |
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; | |
bcd56fe1 | 81 | gpio_restart->restart_handler.priority = 129; |
371bb20d DR |
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", | |
371bb20d DR |
140 | .of_match_table = of_gpio_restart_match, |
141 | }, | |
142 | }; | |
143 | ||
144 | module_platform_driver(gpio_restart_driver); | |
145 | ||
146 | MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); | |
147 | MODULE_DESCRIPTION("GPIO restart driver"); | |
148 | MODULE_LICENSE("GPL"); |