1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for watchdog device controlled through GPIO-line
5 * Author: 2013, Alexander Shiyan <shc_work@mail.ru>
9 #include <linux/delay.h>
10 #include <linux/module.h>
11 #include <linux/gpio/consumer.h>
13 #include <linux/platform_device.h>
14 #include <linux/watchdog.h>
16 static bool nowayout
= WATCHDOG_NOWAYOUT
;
17 module_param(nowayout
, bool, 0);
18 MODULE_PARM_DESC(nowayout
,
19 "Watchdog cannot be stopped once started (default="
20 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
22 #define SOFT_TIMEOUT_MIN 1
23 #define SOFT_TIMEOUT_DEF 60
30 struct gpio_wdt_priv
{
31 struct gpio_desc
*gpiod
;
35 struct watchdog_device wdd
;
38 static void gpio_wdt_disable(struct gpio_wdt_priv
*priv
)
41 gpiod_set_value_cansleep(priv
->gpiod
, 1);
43 /* Put GPIO back to tristate */
44 if (priv
->hw_algo
== HW_ALGO_TOGGLE
)
45 gpiod_direction_input(priv
->gpiod
);
48 static int gpio_wdt_ping(struct watchdog_device
*wdd
)
50 struct gpio_wdt_priv
*priv
= watchdog_get_drvdata(wdd
);
52 switch (priv
->hw_algo
) {
54 /* Toggle output pin */
55 priv
->state
= !priv
->state
;
56 gpiod_set_value_cansleep(priv
->gpiod
, priv
->state
);
60 gpiod_set_value_cansleep(priv
->gpiod
, 1);
62 gpiod_set_value_cansleep(priv
->gpiod
, 0);
68 static int gpio_wdt_start(struct watchdog_device
*wdd
)
70 struct gpio_wdt_priv
*priv
= watchdog_get_drvdata(wdd
);
73 gpiod_direction_output(priv
->gpiod
, priv
->state
);
75 set_bit(WDOG_HW_RUNNING
, &wdd
->status
);
77 return gpio_wdt_ping(wdd
);
80 static int gpio_wdt_stop(struct watchdog_device
*wdd
)
82 struct gpio_wdt_priv
*priv
= watchdog_get_drvdata(wdd
);
84 if (!priv
->always_running
) {
85 gpio_wdt_disable(priv
);
87 set_bit(WDOG_HW_RUNNING
, &wdd
->status
);
93 static const struct watchdog_info gpio_wdt_ident
= {
94 .options
= WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
|
96 .identity
= "GPIO Watchdog",
99 static const struct watchdog_ops gpio_wdt_ops
= {
100 .owner
= THIS_MODULE
,
101 .start
= gpio_wdt_start
,
102 .stop
= gpio_wdt_stop
,
103 .ping
= gpio_wdt_ping
,
106 static int gpio_wdt_probe(struct platform_device
*pdev
)
108 struct device
*dev
= &pdev
->dev
;
109 struct device_node
*np
= dev
->of_node
;
110 struct gpio_wdt_priv
*priv
;
111 enum gpiod_flags gflags
;
112 unsigned int hw_margin
;
116 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
120 platform_set_drvdata(pdev
, priv
);
122 ret
= of_property_read_string(np
, "hw_algo", &algo
);
125 if (!strcmp(algo
, "toggle")) {
126 priv
->hw_algo
= HW_ALGO_TOGGLE
;
128 } else if (!strcmp(algo
, "level")) {
129 priv
->hw_algo
= HW_ALGO_LEVEL
;
130 gflags
= GPIOD_OUT_LOW
;
135 priv
->gpiod
= devm_gpiod_get(dev
, NULL
, gflags
);
136 if (IS_ERR(priv
->gpiod
))
137 return PTR_ERR(priv
->gpiod
);
139 ret
= of_property_read_u32(np
,
140 "hw_margin_ms", &hw_margin
);
143 /* Disallow values lower than 2 and higher than 65535 ms */
144 if (hw_margin
< 2 || hw_margin
> 65535)
147 priv
->always_running
= of_property_read_bool(np
,
150 watchdog_set_drvdata(&priv
->wdd
, priv
);
152 priv
->wdd
.info
= &gpio_wdt_ident
;
153 priv
->wdd
.ops
= &gpio_wdt_ops
;
154 priv
->wdd
.min_timeout
= SOFT_TIMEOUT_MIN
;
155 priv
->wdd
.max_hw_heartbeat_ms
= hw_margin
;
156 priv
->wdd
.parent
= dev
;
157 priv
->wdd
.timeout
= SOFT_TIMEOUT_DEF
;
159 watchdog_init_timeout(&priv
->wdd
, 0, dev
);
160 watchdog_set_nowayout(&priv
->wdd
, nowayout
);
162 watchdog_stop_on_reboot(&priv
->wdd
);
164 if (priv
->always_running
)
165 gpio_wdt_start(&priv
->wdd
);
167 return devm_watchdog_register_device(dev
, &priv
->wdd
);
170 static const struct of_device_id gpio_wdt_dt_ids
[] = {
171 { .compatible
= "linux,wdt-gpio", },
174 MODULE_DEVICE_TABLE(of
, gpio_wdt_dt_ids
);
176 static struct platform_driver gpio_wdt_driver
= {
179 .of_match_table
= gpio_wdt_dt_ids
,
181 .probe
= gpio_wdt_probe
,
184 #ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL
185 static int __init
gpio_wdt_init(void)
187 return platform_driver_register(&gpio_wdt_driver
);
189 arch_initcall(gpio_wdt_init
);
191 module_platform_driver(gpio_wdt_driver
);
194 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
195 MODULE_DESCRIPTION("GPIO Watchdog");
196 MODULE_LICENSE("GPL");