1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Toggles a GPIO pin to restart a device |
4 | * |
5 | * Copyright (C) 2014 Google, Inc. |
6 | * |
7 | * Based on the gpio-poweroff driver. |
8 | */ |
9 | #include <linux/reboot.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/init.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/gpio/consumer.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | |
18 | struct gpio_restart { |
19 | struct gpio_desc *reset_gpio; |
20 | struct notifier_block restart_handler; |
21 | u32 active_delay_ms; |
22 | u32 inactive_delay_ms; |
23 | u32 wait_delay_ms; |
24 | }; |
25 | |
26 | static int gpio_restart_notify(struct notifier_block *this, |
27 | unsigned long mode, void *cmd) |
28 | { |
29 | struct gpio_restart *gpio_restart = |
30 | container_of(this, struct gpio_restart, restart_handler); |
31 | |
32 | /* drive it active, also inactive->active edge */ |
33 | gpiod_direction_output(desc: gpio_restart->reset_gpio, value: 1); |
34 | mdelay(gpio_restart->active_delay_ms); |
35 | |
36 | /* drive inactive, also active->inactive edge */ |
37 | gpiod_set_value(desc: gpio_restart->reset_gpio, value: 0); |
38 | mdelay(gpio_restart->inactive_delay_ms); |
39 | |
40 | /* drive it active, also inactive->active edge */ |
41 | gpiod_set_value(desc: gpio_restart->reset_gpio, value: 1); |
42 | |
43 | /* give it some time */ |
44 | mdelay(gpio_restart->wait_delay_ms); |
45 | |
46 | WARN_ON(1); |
47 | |
48 | return NOTIFY_DONE; |
49 | } |
50 | |
51 | static int gpio_restart_probe(struct platform_device *pdev) |
52 | { |
53 | struct gpio_restart *gpio_restart; |
54 | bool open_source = false; |
55 | u32 property; |
56 | int ret; |
57 | |
58 | gpio_restart = devm_kzalloc(dev: &pdev->dev, size: sizeof(*gpio_restart), |
59 | GFP_KERNEL); |
60 | if (!gpio_restart) |
61 | return -ENOMEM; |
62 | |
63 | open_source = of_property_read_bool(np: pdev->dev.of_node, propname: "open-source" ); |
64 | |
65 | gpio_restart->reset_gpio = devm_gpiod_get(dev: &pdev->dev, NULL, |
66 | flags: open_source ? GPIOD_IN : GPIOD_OUT_LOW); |
67 | ret = PTR_ERR_OR_ZERO(ptr: gpio_restart->reset_gpio); |
68 | if (ret) { |
69 | if (ret != -EPROBE_DEFER) |
70 | dev_err(&pdev->dev, "Could not get reset GPIO\n" ); |
71 | return ret; |
72 | } |
73 | |
74 | gpio_restart->restart_handler.notifier_call = gpio_restart_notify; |
75 | gpio_restart->restart_handler.priority = 129; |
76 | gpio_restart->active_delay_ms = 100; |
77 | gpio_restart->inactive_delay_ms = 100; |
78 | gpio_restart->wait_delay_ms = 3000; |
79 | |
80 | ret = of_property_read_u32(np: pdev->dev.of_node, propname: "priority" , out_value: &property); |
81 | if (!ret) { |
82 | if (property > 255) |
83 | dev_err(&pdev->dev, "Invalid priority property: %u\n" , |
84 | property); |
85 | else |
86 | gpio_restart->restart_handler.priority = property; |
87 | } |
88 | |
89 | of_property_read_u32(np: pdev->dev.of_node, propname: "active-delay" , |
90 | out_value: &gpio_restart->active_delay_ms); |
91 | of_property_read_u32(np: pdev->dev.of_node, propname: "inactive-delay" , |
92 | out_value: &gpio_restart->inactive_delay_ms); |
93 | of_property_read_u32(np: pdev->dev.of_node, propname: "wait-delay" , |
94 | out_value: &gpio_restart->wait_delay_ms); |
95 | |
96 | platform_set_drvdata(pdev, data: gpio_restart); |
97 | |
98 | ret = register_restart_handler(&gpio_restart->restart_handler); |
99 | if (ret) { |
100 | dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n" , |
101 | __func__, ret); |
102 | return -ENODEV; |
103 | } |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static void gpio_restart_remove(struct platform_device *pdev) |
109 | { |
110 | struct gpio_restart *gpio_restart = platform_get_drvdata(pdev); |
111 | int ret; |
112 | |
113 | ret = unregister_restart_handler(&gpio_restart->restart_handler); |
114 | if (ret) { |
115 | dev_err(&pdev->dev, |
116 | "%s: cannot unregister restart handler, %d\n" , |
117 | __func__, ret); |
118 | } |
119 | } |
120 | |
121 | static const struct of_device_id of_gpio_restart_match[] = { |
122 | { .compatible = "gpio-restart" , }, |
123 | {}, |
124 | }; |
125 | |
126 | static struct platform_driver gpio_restart_driver = { |
127 | .probe = gpio_restart_probe, |
128 | .remove_new = gpio_restart_remove, |
129 | .driver = { |
130 | .name = "restart-gpio" , |
131 | .of_match_table = of_gpio_restart_match, |
132 | }, |
133 | }; |
134 | |
135 | module_platform_driver(gpio_restart_driver); |
136 | |
137 | MODULE_AUTHOR("David Riley <davidriley@chromium.org>" ); |
138 | MODULE_DESCRIPTION("GPIO restart driver" ); |
139 | |