Blame view

drivers/watchdog/gpio_wdt.c 4.4 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
2
3
4
5
  /*
   * Driver for watchdog device controlled through GPIO-line
   *
   * Author: 2013, Alexander Shiyan <shc_work@mail.ru>
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
6
7
8
9
10
   */
  
  #include <linux/err.h>
  #include <linux/delay.h>
  #include <linux/module.h>
a2363f9d2   Linus Walleij   watchdog: gpio: C...
11
12
  #include <linux/gpio/consumer.h>
  #include <linux/of.h>
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
13
  #include <linux/platform_device.h>
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
14
  #include <linux/watchdog.h>
1a4aaf9f1   Mans Rullgard   watchdog: gpio: a...
15
16
17
18
19
  static bool nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, bool, 0);
  MODULE_PARM_DESC(nowayout,
  		"Watchdog cannot be stopped once started (default="
  				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
20
21
  #define SOFT_TIMEOUT_MIN	1
  #define SOFT_TIMEOUT_DEF	60
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
22
23
24
25
26
27
28
  
  enum {
  	HW_ALGO_TOGGLE,
  	HW_ALGO_LEVEL,
  };
  
  struct gpio_wdt_priv {
a2363f9d2   Linus Walleij   watchdog: gpio: C...
29
  	struct gpio_desc	*gpiod;
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
30
  	bool			state;
ba804a951   Mike Looijmans   watchdog: gpio_wd...
31
  	bool			always_running;
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
32
  	unsigned int		hw_algo;
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
33
34
35
36
37
  	struct watchdog_device	wdd;
  };
  
  static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
  {
a2363f9d2   Linus Walleij   watchdog: gpio: C...
38
39
  	/* Eternal ping */
  	gpiod_set_value_cansleep(priv->gpiod, 1);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
40
41
42
  
  	/* Put GPIO back to tristate */
  	if (priv->hw_algo == HW_ALGO_TOGGLE)
a2363f9d2   Linus Walleij   watchdog: gpio: C...
43
  		gpiod_direction_input(priv->gpiod);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
44
  }
03bca1583   Guenter Roeck   watchdog: gpio: C...
45
  static int gpio_wdt_ping(struct watchdog_device *wdd)
4f2d0b2d1   Uwe Kleine-König   watchdog: gpio-wd...
46
  {
4f2d0b2d1   Uwe Kleine-König   watchdog: gpio-wd...
47
  	struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
4f2d0b2d1   Uwe Kleine-König   watchdog: gpio-wd...
48
49
50
51
  	switch (priv->hw_algo) {
  	case HW_ALGO_TOGGLE:
  		/* Toggle output pin */
  		priv->state = !priv->state;
a2363f9d2   Linus Walleij   watchdog: gpio: C...
52
  		gpiod_set_value_cansleep(priv->gpiod, priv->state);
4f2d0b2d1   Uwe Kleine-König   watchdog: gpio-wd...
53
54
55
  		break;
  	case HW_ALGO_LEVEL:
  		/* Pulse */
a2363f9d2   Linus Walleij   watchdog: gpio: C...
56
  		gpiod_set_value_cansleep(priv->gpiod, 1);
4f2d0b2d1   Uwe Kleine-König   watchdog: gpio-wd...
57
  		udelay(1);
a2363f9d2   Linus Walleij   watchdog: gpio: C...
58
  		gpiod_set_value_cansleep(priv->gpiod, 0);
4f2d0b2d1   Uwe Kleine-König   watchdog: gpio-wd...
59
60
  		break;
  	}
03bca1583   Guenter Roeck   watchdog: gpio: C...
61
  	return 0;
ba804a951   Mike Looijmans   watchdog: gpio_wd...
62
63
64
65
66
  }
  
  static int gpio_wdt_start(struct watchdog_device *wdd)
  {
  	struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
a2363f9d2   Linus Walleij   watchdog: gpio: C...
67
68
  	priv->state = 0;
  	gpiod_direction_output(priv->gpiod, priv->state);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
69

03bca1583   Guenter Roeck   watchdog: gpio: C...
70
71
72
  	set_bit(WDOG_HW_RUNNING, &wdd->status);
  
  	return gpio_wdt_ping(wdd);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
73
74
75
76
77
  }
  
  static int gpio_wdt_stop(struct watchdog_device *wdd)
  {
  	struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
ba804a951   Mike Looijmans   watchdog: gpio_wd...
78
  	if (!priv->always_running) {
ba804a951   Mike Looijmans   watchdog: gpio_wd...
79
  		gpio_wdt_disable(priv);
bc137dfdb   Rasmus Villemoes   watchdog: gpio_wd...
80
81
  	} else {
  		set_bit(WDOG_HW_RUNNING, &wdd->status);
ba804a951   Mike Looijmans   watchdog: gpio_wd...
82
  	}
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
83
84
85
  
  	return 0;
  }
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
86
87
88
89
90
91
92
93
94
95
96
  static const struct watchdog_info gpio_wdt_ident = {
  	.options	= WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
  			  WDIOF_SETTIMEOUT,
  	.identity	= "GPIO Watchdog",
  };
  
  static const struct watchdog_ops gpio_wdt_ops = {
  	.owner		= THIS_MODULE,
  	.start		= gpio_wdt_start,
  	.stop		= gpio_wdt_stop,
  	.ping		= gpio_wdt_ping,
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
97
98
99
100
  };
  
  static int gpio_wdt_probe(struct platform_device *pdev)
  {
d0d0677e8   Linus Walleij   watchdog: gpio: A...
101
102
  	struct device *dev = &pdev->dev;
  	struct device_node *np = dev->of_node;
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
103
  	struct gpio_wdt_priv *priv;
a2363f9d2   Linus Walleij   watchdog: gpio: C...
104
  	enum gpiod_flags gflags;
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
105
  	unsigned int hw_margin;
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
106
107
  	const char *algo;
  	int ret;
d0d0677e8   Linus Walleij   watchdog: gpio: A...
108
  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
109
110
  	if (!priv)
  		return -ENOMEM;
1ac065634   Wei Yongjun   watchdog: gpio_wd...
111
  	platform_set_drvdata(pdev, priv);
d0d0677e8   Linus Walleij   watchdog: gpio: A...
112
  	ret = of_property_read_string(np, "hw_algo", &algo);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
113
114
  	if (ret)
  		return ret;
0a0a542f6   Uwe Kleine-König   watchdog: gpio-wd...
115
  	if (!strcmp(algo, "toggle")) {
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
116
  		priv->hw_algo = HW_ALGO_TOGGLE;
a2363f9d2   Linus Walleij   watchdog: gpio: C...
117
  		gflags = GPIOD_IN;
0a0a542f6   Uwe Kleine-König   watchdog: gpio-wd...
118
  	} else if (!strcmp(algo, "level")) {
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
119
  		priv->hw_algo = HW_ALGO_LEVEL;
a2363f9d2   Linus Walleij   watchdog: gpio: C...
120
  		gflags = GPIOD_OUT_LOW;
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
121
122
123
  	} else {
  		return -EINVAL;
  	}
a2363f9d2   Linus Walleij   watchdog: gpio: C...
124
125
126
  	priv->gpiod = devm_gpiod_get(dev, NULL, gflags);
  	if (IS_ERR(priv->gpiod))
  		return PTR_ERR(priv->gpiod);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
127

d0d0677e8   Linus Walleij   watchdog: gpio: A...
128
  	ret = of_property_read_u32(np,
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
129
130
131
132
133
134
  				   "hw_margin_ms", &hw_margin);
  	if (ret)
  		return ret;
  	/* Disallow values lower than 2 and higher than 65535 ms */
  	if (hw_margin < 2 || hw_margin > 65535)
  		return -EINVAL;
d0d0677e8   Linus Walleij   watchdog: gpio: A...
135
  	priv->always_running = of_property_read_bool(np,
ba804a951   Mike Looijmans   watchdog: gpio_wd...
136
  						     "always-running");
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
137
138
139
140
141
  	watchdog_set_drvdata(&priv->wdd, priv);
  
  	priv->wdd.info		= &gpio_wdt_ident;
  	priv->wdd.ops		= &gpio_wdt_ops;
  	priv->wdd.min_timeout	= SOFT_TIMEOUT_MIN;
03bca1583   Guenter Roeck   watchdog: gpio: C...
142
  	priv->wdd.max_hw_heartbeat_ms = hw_margin;
d0d0677e8   Linus Walleij   watchdog: gpio: A...
143
  	priv->wdd.parent	= dev;
65adfa22f   Marcus Folkesson   watchdog: gpio: c...
144
  	priv->wdd.timeout	= SOFT_TIMEOUT_DEF;
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
145

3564fbc54   Guenter Roeck   watchdog: gpio_wd...
146
  	watchdog_init_timeout(&priv->wdd, 0, dev);
1a4aaf9f1   Mans Rullgard   watchdog: gpio: a...
147
  	watchdog_set_nowayout(&priv->wdd, nowayout);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
148

28e805b44   Damien Riegel   watchdog: gpio_wd...
149
  	watchdog_stop_on_reboot(&priv->wdd);
ba804a951   Mike Looijmans   watchdog: gpio_wd...
150
  	if (priv->always_running)
03bca1583   Guenter Roeck   watchdog: gpio: C...
151
  		gpio_wdt_start(&priv->wdd);
ba804a951   Mike Looijmans   watchdog: gpio_wd...
152

3564fbc54   Guenter Roeck   watchdog: gpio_wd...
153
  	return devm_watchdog_register_device(dev, &priv->wdd);
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
154
155
156
157
158
159
160
161
162
163
164
  }
  
  static const struct of_device_id gpio_wdt_dt_ids[] = {
  	{ .compatible = "linux,wdt-gpio", },
  	{ }
  };
  MODULE_DEVICE_TABLE(of, gpio_wdt_dt_ids);
  
  static struct platform_driver gpio_wdt_driver = {
  	.driver	= {
  		.name		= "gpio-wdt",
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
165
166
167
  		.of_match_table	= gpio_wdt_dt_ids,
  	},
  	.probe	= gpio_wdt_probe,
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
168
  };
5e53c8ed8   Jean-Baptiste Theou   watchdog: gpio_wd...
169
170
171
172
173
174
175
176
  
  #ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL
  static int __init gpio_wdt_init(void)
  {
  	return platform_driver_register(&gpio_wdt_driver);
  }
  arch_initcall(gpio_wdt_init);
  #else
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
177
  module_platform_driver(gpio_wdt_driver);
5e53c8ed8   Jean-Baptiste Theou   watchdog: gpio_wd...
178
  #endif
25134eafb   Alexander Shiyan   watchdog: GPIO-co...
179
180
181
182
  
  MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
  MODULE_DESCRIPTION("GPIO Watchdog");
  MODULE_LICENSE("GPL");