1/* 2 * Driver for watchdog device controlled through GPIO-line 3 * 4 * Author: 2013, Alexander Shiyan <shc_work@mail.ru> 5 * 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. 10 */ 11 12#include <linux/err.h> 13#include <linux/delay.h> 14#include <linux/module.h> 15#include <linux/notifier.h> 16#include <linux/of_gpio.h> 17#include <linux/platform_device.h> 18#include <linux/reboot.h> 19#include <linux/watchdog.h> 20 21#define SOFT_TIMEOUT_MIN 1 22#define SOFT_TIMEOUT_DEF 60 23#define SOFT_TIMEOUT_MAX 0xffff 24 25enum { 26 HW_ALGO_TOGGLE, 27 HW_ALGO_LEVEL, 28}; 29 30struct gpio_wdt_priv { 31 int gpio; 32 bool active_low; 33 bool state; 34 bool always_running; 35 bool armed; 36 unsigned int hw_algo; 37 unsigned int hw_margin; 38 unsigned long last_jiffies; 39 struct notifier_block notifier; 40 struct timer_list timer; 41 struct watchdog_device wdd; 42}; 43 44static void gpio_wdt_disable(struct gpio_wdt_priv *priv) 45{ 46 gpio_set_value_cansleep(priv->gpio, !priv->active_low); 47 48 /* Put GPIO back to tristate */ 49 if (priv->hw_algo == HW_ALGO_TOGGLE) 50 gpio_direction_input(priv->gpio); 51} 52 53static void gpio_wdt_hwping(unsigned long data) 54{ 55 struct watchdog_device *wdd = (struct watchdog_device *)data; 56 struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); 57 58 if (priv->armed && time_after(jiffies, priv->last_jiffies + 59 msecs_to_jiffies(wdd->timeout * 1000))) { 60 dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); 61 return; 62 } 63 64 /* Restart timer */ 65 mod_timer(&priv->timer, jiffies + priv->hw_margin); 66 67 switch (priv->hw_algo) { 68 case HW_ALGO_TOGGLE: 69 /* Toggle output pin */ 70 priv->state = !priv->state; 71 gpio_set_value_cansleep(priv->gpio, priv->state); 72 break; 73 case HW_ALGO_LEVEL: 74 /* Pulse */ 75 gpio_set_value_cansleep(priv->gpio, !priv->active_low); 76 udelay(1); 77 gpio_set_value_cansleep(priv->gpio, priv->active_low); 78 break; 79 } 80} 81 82static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv) 83{ 84 priv->state = priv->active_low; 85 gpio_direction_output(priv->gpio, priv->state); 86 priv->last_jiffies = jiffies; 87 gpio_wdt_hwping((unsigned long)&priv->wdd); 88} 89 90static int gpio_wdt_start(struct watchdog_device *wdd) 91{ 92 struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); 93 94 gpio_wdt_start_impl(priv); 95 priv->armed = true; 96 97 return 0; 98} 99 100static int gpio_wdt_stop(struct watchdog_device *wdd) 101{ 102 struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); 103 104 priv->armed = false; 105 if (!priv->always_running) { 106 mod_timer(&priv->timer, 0); 107 gpio_wdt_disable(priv); 108 } 109 110 return 0; 111} 112 113static int gpio_wdt_ping(struct watchdog_device *wdd) 114{ 115 struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); 116 117 priv->last_jiffies = jiffies; 118 119 return 0; 120} 121 122static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) 123{ 124 wdd->timeout = t; 125 126 return gpio_wdt_ping(wdd); 127} 128 129static int gpio_wdt_notify_sys(struct notifier_block *nb, unsigned long code, 130 void *unused) 131{ 132 struct gpio_wdt_priv *priv = container_of(nb, struct gpio_wdt_priv, 133 notifier); 134 135 mod_timer(&priv->timer, 0); 136 137 switch (code) { 138 case SYS_HALT: 139 case SYS_POWER_OFF: 140 gpio_wdt_disable(priv); 141 break; 142 default: 143 break; 144 } 145 146 return NOTIFY_DONE; 147} 148 149static const struct watchdog_info gpio_wdt_ident = { 150 .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | 151 WDIOF_SETTIMEOUT, 152 .identity = "GPIO Watchdog", 153}; 154 155static const struct watchdog_ops gpio_wdt_ops = { 156 .owner = THIS_MODULE, 157 .start = gpio_wdt_start, 158 .stop = gpio_wdt_stop, 159 .ping = gpio_wdt_ping, 160 .set_timeout = gpio_wdt_set_timeout, 161}; 162 163static int gpio_wdt_probe(struct platform_device *pdev) 164{ 165 struct gpio_wdt_priv *priv; 166 enum of_gpio_flags flags; 167 unsigned int hw_margin; 168 unsigned long f = 0; 169 const char *algo; 170 int ret; 171 172 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 173 if (!priv) 174 return -ENOMEM; 175 176 priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); 177 if (!gpio_is_valid(priv->gpio)) 178 return priv->gpio; 179 180 priv->active_low = flags & OF_GPIO_ACTIVE_LOW; 181 182 ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo); 183 if (ret) 184 return ret; 185 if (!strcmp(algo, "toggle")) { 186 priv->hw_algo = HW_ALGO_TOGGLE; 187 f = GPIOF_IN; 188 } else if (!strcmp(algo, "level")) { 189 priv->hw_algo = HW_ALGO_LEVEL; 190 f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; 191 } else { 192 return -EINVAL; 193 } 194 195 ret = devm_gpio_request_one(&pdev->dev, priv->gpio, f, 196 dev_name(&pdev->dev)); 197 if (ret) 198 return ret; 199 200 ret = of_property_read_u32(pdev->dev.of_node, 201 "hw_margin_ms", &hw_margin); 202 if (ret) 203 return ret; 204 /* Disallow values lower than 2 and higher than 65535 ms */ 205 if (hw_margin < 2 || hw_margin > 65535) 206 return -EINVAL; 207 208 /* Use safe value (1/2 of real timeout) */ 209 priv->hw_margin = msecs_to_jiffies(hw_margin / 2); 210 211 priv->always_running = of_property_read_bool(pdev->dev.of_node, 212 "always-running"); 213 214 watchdog_set_drvdata(&priv->wdd, priv); 215 216 priv->wdd.info = &gpio_wdt_ident; 217 priv->wdd.ops = &gpio_wdt_ops; 218 priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; 219 priv->wdd.max_timeout = SOFT_TIMEOUT_MAX; 220 priv->wdd.parent = &pdev->dev; 221 222 if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0) 223 priv->wdd.timeout = SOFT_TIMEOUT_DEF; 224 225 setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd); 226 227 ret = watchdog_register_device(&priv->wdd); 228 if (ret) 229 return ret; 230 231 priv->notifier.notifier_call = gpio_wdt_notify_sys; 232 ret = register_reboot_notifier(&priv->notifier); 233 if (ret) 234 goto error_unregister; 235 236 if (priv->always_running) 237 gpio_wdt_start_impl(priv); 238 239 return 0; 240 241error_unregister: 242 watchdog_unregister_device(&priv->wdd); 243 return ret; 244} 245 246static int gpio_wdt_remove(struct platform_device *pdev) 247{ 248 struct gpio_wdt_priv *priv = platform_get_drvdata(pdev); 249 250 del_timer_sync(&priv->timer); 251 unregister_reboot_notifier(&priv->notifier); 252 watchdog_unregister_device(&priv->wdd); 253 254 return 0; 255} 256 257static const struct of_device_id gpio_wdt_dt_ids[] = { 258 { .compatible = "linux,wdt-gpio", }, 259 { } 260}; 261MODULE_DEVICE_TABLE(of, gpio_wdt_dt_ids); 262 263static struct platform_driver gpio_wdt_driver = { 264 .driver = { 265 .name = "gpio-wdt", 266 .of_match_table = gpio_wdt_dt_ids, 267 }, 268 .probe = gpio_wdt_probe, 269 .remove = gpio_wdt_remove, 270}; 271 272#ifdef CONFIG_GPIO_WATCHDOG_ARCH_INITCALL 273static int __init gpio_wdt_init(void) 274{ 275 return platform_driver_register(&gpio_wdt_driver); 276} 277arch_initcall(gpio_wdt_init); 278#else 279module_platform_driver(gpio_wdt_driver); 280#endif 281 282MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); 283MODULE_DESCRIPTION("GPIO Watchdog"); 284MODULE_LICENSE("GPL"); 285