2 * Driver for watchdog device controlled through GPIO-line
4 * Author: 2013, Alexander Shiyan <shc_work@mail.ru>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
12 #include <linux/err.h>
13 #include <linux/delay.h>
14 #include <linux/module.h>
15 #include <linux/gpio/consumer.h>
17 #include <linux/platform_device.h>
18 #include <linux/watchdog.h>
20 #define SOFT_TIMEOUT_MIN 1
21 #define SOFT_TIMEOUT_DEF 60
28 struct gpio_wdt_priv
{
29 struct gpio_desc
*gpiod
;
33 struct watchdog_device wdd
;
36 static void gpio_wdt_disable(struct gpio_wdt_priv
*priv
)
39 gpiod_set_value_cansleep(priv
->gpiod
, 1);
41 /* Put GPIO back to tristate */
42 if (priv
->hw_algo
== HW_ALGO_TOGGLE
)
43 gpiod_direction_input(priv
->gpiod
);
46 static int gpio_wdt_ping(struct watchdog_device
*wdd
)
48 struct gpio_wdt_priv
*priv
= watchdog_get_drvdata(wdd
);
50 switch (priv
->hw_algo
) {
52 /* Toggle output pin */
53 priv
->state
= !priv
->state
;
54 gpiod_set_value_cansleep(priv
->gpiod
, priv
->state
);
58 gpiod_set_value_cansleep(priv
->gpiod
, 1);
60 gpiod_set_value_cansleep(priv
->gpiod
, 0);
66 static int gpio_wdt_start(struct watchdog_device
*wdd
)
68 struct gpio_wdt_priv
*priv
= watchdog_get_drvdata(wdd
);
71 gpiod_direction_output(priv
->gpiod
, priv
->state
);
73 set_bit(WDOG_HW_RUNNING
, &wdd
->status
);
75 return gpio_wdt_ping(wdd
);
78 static int gpio_wdt_stop(struct watchdog_device
*wdd
)
80 struct gpio_wdt_priv
*priv
= watchdog_get_drvdata(wdd
);
82 if (!priv
->always_running
) {
83 gpio_wdt_disable(priv
);
85 set_bit(WDOG_HW_RUNNING
, &wdd
->status
);
91 static const struct watchdog_info gpio_wdt_ident
= {
92 .options
= WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
|
94 .identity
= "GPIO Watchdog",
97 static const struct watchdog_ops gpio_wdt_ops
= {
99 .start
= gpio_wdt_start
,
100 .stop
= gpio_wdt_stop
,
101 .ping
= gpio_wdt_ping
,
104 static int gpio_wdt_probe(struct platform_device
*pdev
)
106 struct device
*dev
= &pdev
->dev
;
107 struct device_node
*np
= dev
->of_node
;
108 struct gpio_wdt_priv
*priv
;
109 enum gpiod_flags gflags
;
110 unsigned int hw_margin
;
114 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
118 platform_set_drvdata(pdev
, priv
);
120 ret
= of_property_read_string(np
, "hw_algo", &algo
);
123 if (!strcmp(algo
, "toggle")) {
124 priv
->hw_algo
= HW_ALGO_TOGGLE
;
126 } else if (!strcmp(algo
, "level")) {
127 priv
->hw_algo
= HW_ALGO_LEVEL
;
128 gflags
= GPIOD_OUT_LOW
;
133 priv
->gpiod
= devm_gpiod_get(dev
, NULL
, gflags
);
134 if (IS_ERR(priv
->gpiod
))
135 return PTR_ERR(priv
->gpiod
);
137 ret
= of_property_read_u32(np
,
138 "hw_margin_ms", &hw_margin
);
141 /* Disallow values lower than 2 and higher than 65535 ms */
142 if (hw_margin
< 2 || hw_margin
> 65535)
145 priv
->always_running
= of_property_read_bool(np
,
148 watchdog_set_drvdata(&priv
->wdd
, priv
);
150 priv
->wdd
.info
= &gpio_wdt_ident
;
151 priv
->wdd
.ops
= &gpio_wdt_ops
;
152 priv
->wdd
.min_timeout
= SOFT_TIMEOUT_MIN
;
153 priv
->wdd
.max_hw_heartbeat_ms
= hw_margin
;
154 priv
->wdd
.parent
= dev
;
156 if (watchdog_init_timeout(&priv
->wdd
, 0, dev
) < 0)
157 priv
->wdd
.timeout
= SOFT_TIMEOUT_DEF
;
159 watchdog_stop_on_reboot(&priv
->wdd
);
161 if (priv
->always_running
)
162 gpio_wdt_start(&priv
->wdd
);
164 ret
= watchdog_register_device(&priv
->wdd
);
169 static int gpio_wdt_remove(struct platform_device
*pdev
)
171 struct gpio_wdt_priv
*priv
= platform_get_drvdata(pdev
);
173 watchdog_unregister_device(&priv
->wdd
);
178 static const struct of_device_id gpio_wdt_dt_ids
[] = {
179 { .compatible
= "linux,wdt-gpio", },
182 MODULE_DEVICE_TABLE(of
, gpio_wdt_dt_ids
);
184 static struct platform_driver gpio_wdt_driver
= {
187 .of_match_table
= gpio_wdt_dt_ids
,
189 .probe
= gpio_wdt_probe
,
190 .remove
= gpio_wdt_remove
,
193 #ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL
194 static int __init
gpio_wdt_init(void)
196 return platform_driver_register(&gpio_wdt_driver
);
198 arch_initcall(gpio_wdt_init
);
200 module_platform_driver(gpio_wdt_driver
);
203 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
204 MODULE_DESCRIPTION("GPIO Watchdog");
205 MODULE_LICENSE("GPL");