990-gpio_wdt.patch 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. This generic GPIO watchdog is used on Huawei E970 (brcm47xx)
  2. Signed-off-by: Mathias Adam <m.adam--openwrt@adamis.de>
  3. --- a/drivers/watchdog/Kconfig
  4. +++ b/drivers/watchdog/Kconfig
  5. @@ -1150,6 +1150,15 @@ config WDT_MTX1
  6. Hardware driver for the MTX-1 boards. This is a watchdog timer that
  7. will reboot the machine after a 100 seconds timer expired.
  8. +config GPIO_WDT
  9. + tristate "GPIO Hardware Watchdog"
  10. + help
  11. + Hardware driver for GPIO-controlled watchdogs. GPIO pin and
  12. + toggle interval settings are platform-specific. The driver
  13. + will stop toggling the GPIO (i.e. machine reboots) after a
  14. + 100 second timer expired and no process has written to
  15. + /dev/watchdog during that time.
  16. +
  17. config PNX833X_WDT
  18. tristate "PNX833x Hardware Watchdog"
  19. depends on SOC_PNX8335
  20. --- a/drivers/watchdog/Makefile
  21. +++ b/drivers/watchdog/Makefile
  22. @@ -135,6 +135,7 @@ obj-$(CONFIG_RC32434_WDT) += rc32434_wdt
  23. obj-$(CONFIG_INDYDOG) += indydog.o
  24. obj-$(CONFIG_JZ4740_WDT) += jz4740_wdt.o
  25. obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
  26. +obj-$(CONFIG_GPIO_WDT) += old_gpio_wdt.o
  27. obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
  28. obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
  29. obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
  30. --- /dev/null
  31. +++ b/drivers/watchdog/old_gpio_wdt.c
  32. @@ -0,0 +1,301 @@
  33. +/*
  34. + * Driver for GPIO-controlled Hardware Watchdogs.
  35. + *
  36. + * Copyright (C) 2013 Mathias Adam <m.adam--linux@adamis.de>
  37. + *
  38. + * Replaces mtx1_wdt (driver for the MTX-1 Watchdog):
  39. + *
  40. + * (C) Copyright 2005 4G Systems <info@4g-systems.biz>,
  41. + * All Rights Reserved.
  42. + * http://www.4g-systems.biz
  43. + *
  44. + * (C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@openwrt.org>
  45. + *
  46. + * This program is free software; you can redistribute it and/or
  47. + * modify it under the terms of the GNU General Public License
  48. + * as published by the Free Software Foundation; either version
  49. + * 2 of the License, or (at your option) any later version.
  50. + *
  51. + * Neither Michael Stickel nor 4G Systems admit liability nor provide
  52. + * warranty for any of this software. This material is provided
  53. + * "AS-IS" and at no charge.
  54. + *
  55. + * (c) Copyright 2005 4G Systems <info@4g-systems.biz>
  56. + *
  57. + * Release 0.01.
  58. + * Author: Michael Stickel michael.stickel@4g-systems.biz
  59. + *
  60. + * Release 0.02.
  61. + * Author: Florian Fainelli florian@openwrt.org
  62. + * use the Linux watchdog/timer APIs
  63. + *
  64. + * Release 0.03.
  65. + * Author: Mathias Adam <m.adam--linux@adamis.de>
  66. + * make it a generic gpio watchdog driver
  67. + *
  68. + * The Watchdog is configured to reset the MTX-1
  69. + * if it is not triggered for 100 seconds.
  70. + * It should not be triggered more often than 1.6 seconds.
  71. + *
  72. + * A timer triggers the watchdog every 5 seconds, until
  73. + * it is opened for the first time. After the first open
  74. + * it MUST be triggered every 2..95 seconds.
  75. + */
  76. +
  77. +#include <linux/module.h>
  78. +#include <linux/moduleparam.h>
  79. +#include <linux/types.h>
  80. +#include <linux/errno.h>
  81. +#include <linux/miscdevice.h>
  82. +#include <linux/fs.h>
  83. +#include <linux/init.h>
  84. +#include <linux/ioport.h>
  85. +#include <linux/timer.h>
  86. +#include <linux/completion.h>
  87. +#include <linux/jiffies.h>
  88. +#include <linux/watchdog.h>
  89. +#include <linux/platform_device.h>
  90. +#include <linux/io.h>
  91. +#include <linux/uaccess.h>
  92. +#include <linux/gpio.h>
  93. +#include <linux/old_gpio_wdt.h>
  94. +
  95. +static int ticks = 100 * HZ;
  96. +
  97. +static struct {
  98. + struct completion stop;
  99. + spinlock_t lock;
  100. + int running;
  101. + struct timer_list timer;
  102. + int queue;
  103. + int default_ticks;
  104. + unsigned long inuse;
  105. + unsigned gpio;
  106. + unsigned int gstate;
  107. + int interval;
  108. + int first_interval;
  109. +} gpio_wdt_device;
  110. +
  111. +static void gpio_wdt_trigger(unsigned long unused)
  112. +{
  113. + spin_lock(&gpio_wdt_device.lock);
  114. + if (gpio_wdt_device.running && ticks > 0)
  115. + ticks -= gpio_wdt_device.interval;
  116. +
  117. + /* toggle wdt gpio */
  118. + gpio_wdt_device.gstate = !gpio_wdt_device.gstate;
  119. + gpio_set_value(gpio_wdt_device.gpio, gpio_wdt_device.gstate);
  120. +
  121. + if (gpio_wdt_device.queue && ticks > 0)
  122. + mod_timer(&gpio_wdt_device.timer, jiffies + gpio_wdt_device.interval);
  123. + else
  124. + complete(&gpio_wdt_device.stop);
  125. + spin_unlock(&gpio_wdt_device.lock);
  126. +}
  127. +
  128. +static void gpio_wdt_reset(void)
  129. +{
  130. + ticks = gpio_wdt_device.default_ticks;
  131. +}
  132. +
  133. +
  134. +static void gpio_wdt_start(void)
  135. +{
  136. + unsigned long flags;
  137. +
  138. + spin_lock_irqsave(&gpio_wdt_device.lock, flags);
  139. + if (!gpio_wdt_device.queue) {
  140. + gpio_wdt_device.queue = 1;
  141. + gpio_wdt_device.gstate = 1;
  142. + gpio_set_value(gpio_wdt_device.gpio, 1);
  143. + mod_timer(&gpio_wdt_device.timer, jiffies + gpio_wdt_device.first_interval);
  144. + }
  145. + gpio_wdt_device.running++;
  146. + spin_unlock_irqrestore(&gpio_wdt_device.lock, flags);
  147. +}
  148. +
  149. +static int gpio_wdt_stop(void)
  150. +{
  151. + unsigned long flags;
  152. +
  153. + spin_lock_irqsave(&gpio_wdt_device.lock, flags);
  154. + if (gpio_wdt_device.queue) {
  155. + gpio_wdt_device.queue = 0;
  156. + gpio_wdt_device.gstate = 0;
  157. + gpio_set_value(gpio_wdt_device.gpio, 0);
  158. + }
  159. + ticks = gpio_wdt_device.default_ticks;
  160. + spin_unlock_irqrestore(&gpio_wdt_device.lock, flags);
  161. + return 0;
  162. +}
  163. +
  164. +/* Filesystem functions */
  165. +
  166. +static int gpio_wdt_open(struct inode *inode, struct file *file)
  167. +{
  168. + if (test_and_set_bit(0, &gpio_wdt_device.inuse))
  169. + return -EBUSY;
  170. + return nonseekable_open(inode, file);
  171. +}
  172. +
  173. +
  174. +static int gpio_wdt_release(struct inode *inode, struct file *file)
  175. +{
  176. + clear_bit(0, &gpio_wdt_device.inuse);
  177. + return 0;
  178. +}
  179. +
  180. +static long gpio_wdt_ioctl(struct file *file, unsigned int cmd,
  181. + unsigned long arg)
  182. +{
  183. + void __user *argp = (void __user *)arg;
  184. + int __user *p = (int __user *)argp;
  185. + unsigned int value;
  186. + static const struct watchdog_info ident = {
  187. + .options = WDIOF_CARDRESET,
  188. + .identity = "GPIO WDT",
  189. + };
  190. +
  191. + switch (cmd) {
  192. + case WDIOC_GETSUPPORT:
  193. + if (copy_to_user(argp, &ident, sizeof(ident)))
  194. + return -EFAULT;
  195. + break;
  196. + case WDIOC_GETSTATUS:
  197. + case WDIOC_GETBOOTSTATUS:
  198. + put_user(0, p);
  199. + break;
  200. + case WDIOC_SETOPTIONS:
  201. + if (get_user(value, p))
  202. + return -EFAULT;
  203. + if (value & WDIOS_ENABLECARD)
  204. + gpio_wdt_start();
  205. + else if (value & WDIOS_DISABLECARD)
  206. + gpio_wdt_stop();
  207. + else
  208. + return -EINVAL;
  209. + return 0;
  210. + case WDIOC_KEEPALIVE:
  211. + gpio_wdt_reset();
  212. + break;
  213. + default:
  214. + return -ENOTTY;
  215. + }
  216. + return 0;
  217. +}
  218. +
  219. +
  220. +static ssize_t gpio_wdt_write(struct file *file, const char *buf,
  221. + size_t count, loff_t *ppos)
  222. +{
  223. + if (!count)
  224. + return -EIO;
  225. + gpio_wdt_reset();
  226. + return count;
  227. +}
  228. +
  229. +static const struct file_operations gpio_wdt_fops = {
  230. + .owner = THIS_MODULE,
  231. + .llseek = no_llseek,
  232. + .unlocked_ioctl = gpio_wdt_ioctl,
  233. + .open = gpio_wdt_open,
  234. + .write = gpio_wdt_write,
  235. + .release = gpio_wdt_release,
  236. +};
  237. +
  238. +
  239. +static struct miscdevice gpio_wdt_misc = {
  240. + .minor = WATCHDOG_MINOR,
  241. + .name = "watchdog",
  242. + .fops = &gpio_wdt_fops,
  243. +};
  244. +
  245. +
  246. +static int gpio_wdt_probe(struct platform_device *pdev)
  247. +{
  248. + int ret;
  249. + struct gpio_wdt_platform_data *gpio_wdt_data = pdev->dev.platform_data;
  250. +
  251. + gpio_wdt_device.gpio = gpio_wdt_data->gpio;
  252. + gpio_wdt_device.interval = gpio_wdt_data->interval;
  253. + gpio_wdt_device.first_interval = gpio_wdt_data->first_interval;
  254. + if (gpio_wdt_device.first_interval <= 0) {
  255. + gpio_wdt_device.first_interval = gpio_wdt_device.interval;
  256. + }
  257. +
  258. + ret = gpio_request(gpio_wdt_device.gpio, "gpio-wdt");
  259. + if (ret < 0) {
  260. + dev_err(&pdev->dev, "failed to request gpio");
  261. + return ret;
  262. + }
  263. +
  264. + spin_lock_init(&gpio_wdt_device.lock);
  265. + init_completion(&gpio_wdt_device.stop);
  266. + gpio_wdt_device.queue = 0;
  267. + clear_bit(0, &gpio_wdt_device.inuse);
  268. + setup_timer(&gpio_wdt_device.timer, gpio_wdt_trigger, 0L);
  269. + gpio_wdt_device.default_ticks = ticks;
  270. +
  271. + gpio_wdt_start();
  272. + dev_info(&pdev->dev, "GPIO Hardware Watchdog driver (gpio=%i interval=%i/%i)\n",
  273. + gpio_wdt_data->gpio, gpio_wdt_data->first_interval, gpio_wdt_data->interval);
  274. + return 0;
  275. +}
  276. +
  277. +static int gpio_wdt_remove(struct platform_device *pdev)
  278. +{
  279. + /* FIXME: do we need to lock this test ? */
  280. + if (gpio_wdt_device.queue) {
  281. + gpio_wdt_device.queue = 0;
  282. + wait_for_completion(&gpio_wdt_device.stop);
  283. + }
  284. +
  285. + gpio_free(gpio_wdt_device.gpio);
  286. + misc_deregister(&gpio_wdt_misc);
  287. + return 0;
  288. +}
  289. +
  290. +static struct platform_driver gpio_wdt_driver = {
  291. + .probe = gpio_wdt_probe,
  292. + .remove = gpio_wdt_remove,
  293. + .driver.name = "gpio-wdt",
  294. + .driver.owner = THIS_MODULE,
  295. +};
  296. +
  297. +static int __init gpio_wdt_init(void)
  298. +{
  299. + return platform_driver_register(&gpio_wdt_driver);
  300. +}
  301. +arch_initcall(gpio_wdt_init);
  302. +
  303. +/*
  304. + * We do wdt initialization in two steps: arch_initcall probes the wdt
  305. + * very early to start pinging the watchdog (misc devices are not yet
  306. + * available), and later module_init() just registers the misc device.
  307. + */
  308. +static int gpio_wdt_init_late(void)
  309. +{
  310. + int ret;
  311. +
  312. + ret = misc_register(&gpio_wdt_misc);
  313. + if (ret < 0) {
  314. + pr_err("GPIO_WDT: failed to register misc device\n");
  315. + return ret;
  316. + }
  317. + return 0;
  318. +}
  319. +#ifndef MODULE
  320. +module_init(gpio_wdt_init_late);
  321. +#endif
  322. +
  323. +static void __exit gpio_wdt_exit(void)
  324. +{
  325. + platform_driver_unregister(&gpio_wdt_driver);
  326. +}
  327. +module_exit(gpio_wdt_exit);
  328. +
  329. +MODULE_AUTHOR("Michael Stickel, Florian Fainelli, Mathias Adam");
  330. +MODULE_DESCRIPTION("Driver for GPIO hardware watchdogs");
  331. +MODULE_LICENSE("GPL");
  332. +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
  333. +MODULE_ALIAS("platform:gpio-wdt");
  334. --- /dev/null
  335. +++ b/include/linux/old_gpio_wdt.h
  336. @@ -0,0 +1,21 @@
  337. +/*
  338. + * Definitions for the GPIO watchdog driver
  339. + *
  340. + * Copyright (C) 2013 Mathias Adam <m.adam--linux@adamis.de>
  341. + *
  342. + * This program is free software; you can redistribute it and/or modify
  343. + * it under the terms of the GNU General Public License version 2 as
  344. + * published by the Free Software Foundation.
  345. + *
  346. + */
  347. +
  348. +#ifndef _GPIO_WDT_H_
  349. +#define _GPIO_WDT_H_
  350. +
  351. +struct gpio_wdt_platform_data {
  352. + int gpio; /* GPIO line number */
  353. + int interval; /* watchdog reset interval in system ticks */
  354. + int first_interval; /* first wd reset interval in system ticks */
  355. +};
  356. +
  357. +#endif /* _GPIO_WDT_H_ */