Blame view
drivers/watchdog/gpio_wdt.c
4.4 KB
2874c5fd2
|
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
25134eafb
|
2 3 4 5 |
/* * Driver for watchdog device controlled through GPIO-line * * Author: 2013, Alexander Shiyan <shc_work@mail.ru> |
25134eafb
|
6 7 8 9 10 |
*/ #include <linux/err.h> #include <linux/delay.h> #include <linux/module.h> |
a2363f9d2
|
11 12 |
#include <linux/gpio/consumer.h> #include <linux/of.h> |
25134eafb
|
13 |
#include <linux/platform_device.h> |
25134eafb
|
14 |
#include <linux/watchdog.h> |
1a4aaf9f1
|
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
|
20 21 |
#define SOFT_TIMEOUT_MIN 1 #define SOFT_TIMEOUT_DEF 60 |
25134eafb
|
22 23 24 25 26 27 28 |
enum { HW_ALGO_TOGGLE, HW_ALGO_LEVEL, }; struct gpio_wdt_priv { |
a2363f9d2
|
29 |
struct gpio_desc *gpiod; |
25134eafb
|
30 |
bool state; |
ba804a951
|
31 |
bool always_running; |
25134eafb
|
32 |
unsigned int hw_algo; |
25134eafb
|
33 34 35 36 37 |
struct watchdog_device wdd; }; static void gpio_wdt_disable(struct gpio_wdt_priv *priv) { |
a2363f9d2
|
38 39 |
/* Eternal ping */ gpiod_set_value_cansleep(priv->gpiod, 1); |
25134eafb
|
40 41 42 |
/* Put GPIO back to tristate */ if (priv->hw_algo == HW_ALGO_TOGGLE) |
a2363f9d2
|
43 |
gpiod_direction_input(priv->gpiod); |
25134eafb
|
44 |
} |
03bca1583
|
45 |
static int gpio_wdt_ping(struct watchdog_device *wdd) |
4f2d0b2d1
|
46 |
{ |
4f2d0b2d1
|
47 |
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); |
4f2d0b2d1
|
48 49 50 51 |
switch (priv->hw_algo) { case HW_ALGO_TOGGLE: /* Toggle output pin */ priv->state = !priv->state; |
a2363f9d2
|
52 |
gpiod_set_value_cansleep(priv->gpiod, priv->state); |
4f2d0b2d1
|
53 54 55 |
break; case HW_ALGO_LEVEL: /* Pulse */ |
a2363f9d2
|
56 |
gpiod_set_value_cansleep(priv->gpiod, 1); |
4f2d0b2d1
|
57 |
udelay(1); |
a2363f9d2
|
58 |
gpiod_set_value_cansleep(priv->gpiod, 0); |
4f2d0b2d1
|
59 60 |
break; } |
03bca1583
|
61 |
return 0; |
ba804a951
|
62 63 64 65 66 |
} static int gpio_wdt_start(struct watchdog_device *wdd) { struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); |
a2363f9d2
|
67 68 |
priv->state = 0; gpiod_direction_output(priv->gpiod, priv->state); |
25134eafb
|
69 |
|
03bca1583
|
70 71 72 |
set_bit(WDOG_HW_RUNNING, &wdd->status); return gpio_wdt_ping(wdd); |
25134eafb
|
73 74 75 76 77 |
} static int gpio_wdt_stop(struct watchdog_device *wdd) { struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); |
ba804a951
|
78 |
if (!priv->always_running) { |
ba804a951
|
79 |
gpio_wdt_disable(priv); |
bc137dfdb
|
80 81 |
} else { set_bit(WDOG_HW_RUNNING, &wdd->status); |
ba804a951
|
82 |
} |
25134eafb
|
83 84 85 |
return 0; } |
25134eafb
|
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
|
97 98 99 100 |
}; static int gpio_wdt_probe(struct platform_device *pdev) { |
d0d0677e8
|
101 102 |
struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; |
25134eafb
|
103 |
struct gpio_wdt_priv *priv; |
a2363f9d2
|
104 |
enum gpiod_flags gflags; |
25134eafb
|
105 |
unsigned int hw_margin; |
25134eafb
|
106 107 |
const char *algo; int ret; |
d0d0677e8
|
108 |
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
25134eafb
|
109 110 |
if (!priv) return -ENOMEM; |
1ac065634
|
111 |
platform_set_drvdata(pdev, priv); |
d0d0677e8
|
112 |
ret = of_property_read_string(np, "hw_algo", &algo); |
25134eafb
|
113 114 |
if (ret) return ret; |
0a0a542f6
|
115 |
if (!strcmp(algo, "toggle")) { |
25134eafb
|
116 |
priv->hw_algo = HW_ALGO_TOGGLE; |
a2363f9d2
|
117 |
gflags = GPIOD_IN; |
0a0a542f6
|
118 |
} else if (!strcmp(algo, "level")) { |
25134eafb
|
119 |
priv->hw_algo = HW_ALGO_LEVEL; |
a2363f9d2
|
120 |
gflags = GPIOD_OUT_LOW; |
25134eafb
|
121 122 123 |
} else { return -EINVAL; } |
a2363f9d2
|
124 125 126 |
priv->gpiod = devm_gpiod_get(dev, NULL, gflags); if (IS_ERR(priv->gpiod)) return PTR_ERR(priv->gpiod); |
25134eafb
|
127 |
|
d0d0677e8
|
128 |
ret = of_property_read_u32(np, |
25134eafb
|
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
|
135 |
priv->always_running = of_property_read_bool(np, |
ba804a951
|
136 |
"always-running"); |
25134eafb
|
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
|
142 |
priv->wdd.max_hw_heartbeat_ms = hw_margin; |
d0d0677e8
|
143 |
priv->wdd.parent = dev; |
65adfa22f
|
144 |
priv->wdd.timeout = SOFT_TIMEOUT_DEF; |
25134eafb
|
145 |
|
3564fbc54
|
146 |
watchdog_init_timeout(&priv->wdd, 0, dev); |
1a4aaf9f1
|
147 |
watchdog_set_nowayout(&priv->wdd, nowayout); |
25134eafb
|
148 |
|
28e805b44
|
149 |
watchdog_stop_on_reboot(&priv->wdd); |
ba804a951
|
150 |
if (priv->always_running) |
03bca1583
|
151 |
gpio_wdt_start(&priv->wdd); |
ba804a951
|
152 |
|
3564fbc54
|
153 |
return devm_watchdog_register_device(dev, &priv->wdd); |
25134eafb
|
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
|
165 166 167 |
.of_match_table = gpio_wdt_dt_ids, }, .probe = gpio_wdt_probe, |
25134eafb
|
168 |
}; |
5e53c8ed8
|
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
|
177 |
module_platform_driver(gpio_wdt_driver); |
5e53c8ed8
|
178 |
#endif |
25134eafb
|
179 180 181 182 |
MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); MODULE_DESCRIPTION("GPIO Watchdog"); MODULE_LICENSE("GPL"); |