xref: /openbmc/linux/drivers/power/reset/gpio-restart.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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