19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2371bb20dSDavid Riley /*
3371bb20dSDavid Riley * Toggles a GPIO pin to restart a device
4371bb20dSDavid Riley *
5371bb20dSDavid Riley * Copyright (C) 2014 Google, Inc.
6371bb20dSDavid Riley *
7371bb20dSDavid Riley * Based on the gpio-poweroff driver.
8371bb20dSDavid Riley */
9371bb20dSDavid Riley #include <linux/reboot.h>
10371bb20dSDavid Riley #include <linux/kernel.h>
11371bb20dSDavid Riley #include <linux/init.h>
12371bb20dSDavid Riley #include <linux/delay.h>
13371bb20dSDavid Riley #include <linux/platform_device.h>
14371bb20dSDavid Riley #include <linux/gpio/consumer.h>
15371bb20dSDavid Riley #include <linux/module.h>
16*2ce8284cSRob Herring #include <linux/of.h>
17371bb20dSDavid Riley
18371bb20dSDavid Riley struct gpio_restart {
19371bb20dSDavid Riley struct gpio_desc *reset_gpio;
20371bb20dSDavid Riley struct notifier_block restart_handler;
21371bb20dSDavid Riley u32 active_delay_ms;
22371bb20dSDavid Riley u32 inactive_delay_ms;
23371bb20dSDavid Riley u32 wait_delay_ms;
24371bb20dSDavid Riley };
25371bb20dSDavid Riley
gpio_restart_notify(struct notifier_block * this,unsigned long mode,void * cmd)26371bb20dSDavid Riley static int gpio_restart_notify(struct notifier_block *this,
27371bb20dSDavid Riley unsigned long mode, void *cmd)
28371bb20dSDavid Riley {
29371bb20dSDavid Riley struct gpio_restart *gpio_restart =
30371bb20dSDavid Riley container_of(this, struct gpio_restart, restart_handler);
31371bb20dSDavid Riley
32371bb20dSDavid Riley /* drive it active, also inactive->active edge */
33371bb20dSDavid Riley gpiod_direction_output(gpio_restart->reset_gpio, 1);
34371bb20dSDavid Riley mdelay(gpio_restart->active_delay_ms);
35371bb20dSDavid Riley
36371bb20dSDavid Riley /* drive inactive, also active->inactive edge */
37371bb20dSDavid Riley gpiod_set_value(gpio_restart->reset_gpio, 0);
38371bb20dSDavid Riley mdelay(gpio_restart->inactive_delay_ms);
39371bb20dSDavid Riley
40371bb20dSDavid Riley /* drive it active, also inactive->active edge */
41371bb20dSDavid Riley gpiod_set_value(gpio_restart->reset_gpio, 1);
42371bb20dSDavid Riley
43371bb20dSDavid Riley /* give it some time */
44371bb20dSDavid Riley mdelay(gpio_restart->wait_delay_ms);
45371bb20dSDavid Riley
46371bb20dSDavid Riley WARN_ON(1);
47371bb20dSDavid Riley
48371bb20dSDavid Riley return NOTIFY_DONE;
49371bb20dSDavid Riley }
50371bb20dSDavid Riley
gpio_restart_probe(struct platform_device * pdev)51371bb20dSDavid Riley static int gpio_restart_probe(struct platform_device *pdev)
52371bb20dSDavid Riley {
53371bb20dSDavid Riley struct gpio_restart *gpio_restart;
54371bb20dSDavid Riley bool open_source = false;
55371bb20dSDavid Riley u32 property;
56371bb20dSDavid Riley int ret;
57371bb20dSDavid Riley
58371bb20dSDavid Riley gpio_restart = devm_kzalloc(&pdev->dev, sizeof(*gpio_restart),
59371bb20dSDavid Riley GFP_KERNEL);
60371bb20dSDavid Riley if (!gpio_restart)
61371bb20dSDavid Riley return -ENOMEM;
62371bb20dSDavid Riley
63371bb20dSDavid Riley open_source = of_property_read_bool(pdev->dev.of_node, "open-source");
64371bb20dSDavid Riley
65371bb20dSDavid Riley gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL,
66371bb20dSDavid Riley open_source ? GPIOD_IN : GPIOD_OUT_LOW);
67b0d0d3deSChris Packham ret = PTR_ERR_OR_ZERO(gpio_restart->reset_gpio);
68b0d0d3deSChris Packham if (ret) {
69b0d0d3deSChris Packham if (ret != -EPROBE_DEFER)
700e063317SMichal Simek dev_err(&pdev->dev, "Could not get reset GPIO\n");
71b0d0d3deSChris Packham return ret;
72371bb20dSDavid Riley }
73371bb20dSDavid Riley
74371bb20dSDavid Riley gpio_restart->restart_handler.notifier_call = gpio_restart_notify;
75bcd56fe1SHeiko Stübner gpio_restart->restart_handler.priority = 129;
76371bb20dSDavid Riley gpio_restart->active_delay_ms = 100;
77371bb20dSDavid Riley gpio_restart->inactive_delay_ms = 100;
78371bb20dSDavid Riley gpio_restart->wait_delay_ms = 3000;
79371bb20dSDavid Riley
80371bb20dSDavid Riley ret = of_property_read_u32(pdev->dev.of_node, "priority", &property);
81371bb20dSDavid Riley if (!ret) {
82371bb20dSDavid Riley if (property > 255)
83371bb20dSDavid Riley dev_err(&pdev->dev, "Invalid priority property: %u\n",
84371bb20dSDavid Riley property);
85371bb20dSDavid Riley else
86371bb20dSDavid Riley gpio_restart->restart_handler.priority = property;
87371bb20dSDavid Riley }
88371bb20dSDavid Riley
89371bb20dSDavid Riley of_property_read_u32(pdev->dev.of_node, "active-delay",
90371bb20dSDavid Riley &gpio_restart->active_delay_ms);
91371bb20dSDavid Riley of_property_read_u32(pdev->dev.of_node, "inactive-delay",
92371bb20dSDavid Riley &gpio_restart->inactive_delay_ms);
93371bb20dSDavid Riley of_property_read_u32(pdev->dev.of_node, "wait-delay",
94371bb20dSDavid Riley &gpio_restart->wait_delay_ms);
95371bb20dSDavid Riley
96371bb20dSDavid Riley platform_set_drvdata(pdev, gpio_restart);
97371bb20dSDavid Riley
98371bb20dSDavid Riley ret = register_restart_handler(&gpio_restart->restart_handler);
99371bb20dSDavid Riley if (ret) {
100371bb20dSDavid Riley dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n",
101371bb20dSDavid Riley __func__, ret);
102371bb20dSDavid Riley return -ENODEV;
103371bb20dSDavid Riley }
104371bb20dSDavid Riley
105371bb20dSDavid Riley return 0;
106371bb20dSDavid Riley }
107371bb20dSDavid Riley
gpio_restart_remove(struct platform_device * pdev)108aafbf04fSUwe Kleine-König static void gpio_restart_remove(struct platform_device *pdev)
109371bb20dSDavid Riley {
110371bb20dSDavid Riley struct gpio_restart *gpio_restart = platform_get_drvdata(pdev);
111371bb20dSDavid Riley int ret;
112371bb20dSDavid Riley
113371bb20dSDavid Riley ret = unregister_restart_handler(&gpio_restart->restart_handler);
114371bb20dSDavid Riley if (ret) {
115371bb20dSDavid Riley dev_err(&pdev->dev,
116371bb20dSDavid Riley "%s: cannot unregister restart handler, %d\n",
117371bb20dSDavid Riley __func__, ret);
118371bb20dSDavid Riley }
119371bb20dSDavid Riley }
120371bb20dSDavid Riley
121371bb20dSDavid Riley static const struct of_device_id of_gpio_restart_match[] = {
122371bb20dSDavid Riley { .compatible = "gpio-restart", },
123371bb20dSDavid Riley {},
124371bb20dSDavid Riley };
125371bb20dSDavid Riley
126371bb20dSDavid Riley static struct platform_driver gpio_restart_driver = {
127371bb20dSDavid Riley .probe = gpio_restart_probe,
128aafbf04fSUwe Kleine-König .remove_new = gpio_restart_remove,
129371bb20dSDavid Riley .driver = {
130371bb20dSDavid Riley .name = "restart-gpio",
131371bb20dSDavid Riley .of_match_table = of_gpio_restart_match,
132371bb20dSDavid Riley },
133371bb20dSDavid Riley };
134371bb20dSDavid Riley
135371bb20dSDavid Riley module_platform_driver(gpio_restart_driver);
136371bb20dSDavid Riley
137371bb20dSDavid Riley MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
138371bb20dSDavid Riley MODULE_DESCRIPTION("GPIO restart driver");
139