1 From 633194396e40f919974dd8b81e97ddfea463b733 Mon Sep 17 00:00:00 2001
2 From: popcornmix <popcornmix@gmail.com>
3 Date: Wed, 1 May 2013 19:54:32 +0100
4 Subject: [PATCH 005/114] bcm2708 watchdog driver
6 Signed-off-by: popcornmix <popcornmix@gmail.com>
8 drivers/watchdog/Kconfig | 6 +
9 drivers/watchdog/Makefile | 1 +
10 drivers/watchdog/bcm2708_wdog.c | 382 ++++++++++++++++++++++++++++++++++++++++
11 3 files changed, 389 insertions(+)
12 create mode 100644 drivers/watchdog/bcm2708_wdog.c
14 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
15 index d0107d4..ff56894 100644
16 --- a/drivers/watchdog/Kconfig
17 +++ b/drivers/watchdog/Kconfig
18 @@ -452,6 +452,12 @@ config RETU_WATCHDOG
19 To compile this driver as a module, choose M here: the
20 module will be called retu_wdt.
23 + tristate "BCM2708 Watchdog"
24 + depends on ARCH_BCM2708
26 + Enables BCM2708 watchdog support.
29 tristate "MOXART watchdog"
30 depends on ARCH_MOXART
31 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
32 index c569ec8..10e0665 100644
33 --- a/drivers/watchdog/Makefile
34 +++ b/drivers/watchdog/Makefile
35 @@ -56,6 +56,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
36 obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
37 obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
38 obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
39 +obj-$(CONFIG_BCM2708_WDT) += bcm2708_wdog.o
40 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
41 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
42 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
43 diff --git a/drivers/watchdog/bcm2708_wdog.c b/drivers/watchdog/bcm2708_wdog.c
45 index 0000000..8a27d68
47 +++ b/drivers/watchdog/bcm2708_wdog.c
50 + * Broadcom BCM2708 watchdog driver.
52 + * (c) Copyright 2010 Broadcom Europe Ltd
54 + * This program is free software; you can redistribute it and/or
55 + * modify it under the terms of the GNU General Public License
56 + * as published by the Free Software Foundation; either version
57 + * 2 of the License, or (at your option) any later version.
59 + * BCM2708 watchdog driver. Loosely based on wdt driver.
62 +#include <linux/interrupt.h>
63 +#include <linux/module.h>
64 +#include <linux/moduleparam.h>
65 +#include <linux/types.h>
66 +#include <linux/miscdevice.h>
67 +#include <linux/watchdog.h>
68 +#include <linux/fs.h>
69 +#include <linux/ioport.h>
70 +#include <linux/notifier.h>
71 +#include <linux/reboot.h>
72 +#include <linux/init.h>
73 +#include <linux/io.h>
74 +#include <linux/uaccess.h>
75 +#include <mach/platform.h>
77 +#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
78 +#define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
80 +static unsigned long wdog_is_open;
81 +static uint32_t wdog_ticks; /* Ticks to load into wdog timer */
82 +static char expect_close;
88 +#define WD_TIMO 10 /* Default heartbeat = 60 seconds */
89 +static int heartbeat = WD_TIMO; /* Heartbeat in seconds */
91 +module_param(heartbeat, int, 0);
92 +MODULE_PARM_DESC(heartbeat,
93 + "Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
94 + __MODULE_STRING(WD_TIMO) ")");
96 +static int nowayout = WATCHDOG_NOWAYOUT;
97 +module_param(nowayout, int, 0);
98 +MODULE_PARM_DESC(nowayout,
99 + "Watchdog cannot be stopped once started (default="
100 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
102 +static DEFINE_SPINLOCK(wdog_lock);
105 + * Start the watchdog driver.
108 +static int wdog_start(unsigned long timeout)
111 + unsigned long flags;
112 + spin_lock_irqsave(&wdog_lock, flags);
114 + /* enable the watchdog */
115 + iowrite32(PM_PASSWORD | (timeout & PM_WDOG_TIME_SET),
116 + __io_address(PM_WDOG));
117 + cur = ioread32(__io_address(PM_RSTC));
118 + iowrite32(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) |
119 + PM_RSTC_WRCFG_FULL_RESET, __io_address(PM_RSTC));
121 + spin_unlock_irqrestore(&wdog_lock, flags);
126 + * Stop the watchdog driver.
129 +static int wdog_stop(void)
131 + iowrite32(PM_PASSWORD | PM_RSTC_RESET, __io_address(PM_RSTC));
132 + printk(KERN_INFO "watchdog stopped\n");
137 + * Reload counter one with the watchdog heartbeat. We don't bother
138 + * reloading the cascade counter.
141 +static void wdog_ping(void)
143 + wdog_start(wdog_ticks);
147 + * @t: the new heartbeat value that needs to be set.
149 + * Set a new heartbeat value for the watchdog device. If the heartbeat
150 + * value is incorrect we keep the old value and return -EINVAL. If
151 + * successful we return 0.
154 +static int wdog_set_heartbeat(int t)
156 + if (t < 1 || t > WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET))
160 + wdog_ticks = SECS_TO_WDOG_TICKS(t);
165 + * @file: file handle to the watchdog
166 + * @buf: buffer to write (unused as data does not matter here
167 + * @count: count of bytes
168 + * @ppos: pointer to the position to write. No seeks allowed
170 + * A write to a watchdog device is defined as a keepalive signal.
172 + * if 'nowayout' is set then normally a close() is ignored. But
173 + * if you write 'V' first then the close() will stop the timer.
176 +static ssize_t wdog_write(struct file *file, const char __user *buf,
177 + size_t count, loff_t *ppos)
183 + /* In case it was set long ago */
186 + for (i = 0; i != count; i++) {
188 + if (get_user(c, buf + i))
199 +static int wdog_get_status(void)
201 + unsigned long flags;
203 + spin_lock_irqsave(&wdog_lock, flags);
204 + /* FIXME: readback reset reason */
205 + spin_unlock_irqrestore(&wdog_lock, flags);
209 +static uint32_t wdog_get_remaining(void)
211 + uint32_t ret = ioread32(__io_address(PM_WDOG));
212 + return ret & PM_WDOG_TIME_SET;
216 + * @file: file handle to the device
217 + * @cmd: watchdog command
218 + * @arg: argument pointer
220 + * The watchdog API defines a common set of functions for all watchdogs
221 + * according to their available features. We only actually usefully support
222 + * querying capabilities and current status.
225 +static long wdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
227 + void __user *argp = (void __user *)arg;
228 + int __user *p = argp;
232 + uint32_t remaining;
234 + struct watchdog_info ident = {
235 + .options = WDIOF_SETTIMEOUT|
237 + WDIOF_KEEPALIVEPING,
238 + .firmware_version = 1,
239 + .identity = "BCM2708",
243 + case WDIOC_GETSUPPORT:
244 + return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
245 + case WDIOC_GETSTATUS:
246 + status = wdog_get_status();
247 + return put_user(status, p);
248 + case WDIOC_GETBOOTSTATUS:
249 + return put_user(0, p);
250 + case WDIOC_KEEPALIVE:
253 + case WDIOC_SETTIMEOUT:
254 + if (get_user(new_heartbeat, p))
256 + if (wdog_set_heartbeat(new_heartbeat))
260 + case WDIOC_GETTIMEOUT:
261 + return put_user(heartbeat, p);
262 + case WDIOC_GETTIMELEFT:
263 + remaining = WDOG_TICKS_TO_SECS(wdog_get_remaining());
264 + return put_user(remaining, p);
265 + case WDIOC_SETOPTIONS:
266 + if (get_user(options, p))
268 + if (options & WDIOS_DISABLECARD)
270 + if (options & WDIOS_ENABLECARD)
271 + wdog_start(wdog_ticks);
279 + * @inode: inode of device
280 + * @file: file handle to device
282 + * The watchdog device has been opened. The watchdog device is single
283 + * open and on opening we load the counters.
286 +static int wdog_open(struct inode *inode, struct file *file)
288 + if (test_and_set_bit(0, &wdog_is_open))
293 + wdog_start(wdog_ticks);
294 + return nonseekable_open(inode, file);
298 + * @inode: inode to board
299 + * @file: file handle to board
301 + * The watchdog has a configurable API. There is a religious dispute
302 + * between people who want their watchdog to be able to shut down and
303 + * those who want to be sure if the watchdog manager dies the machine
304 + * reboots. In the former case we disable the counters, in the latter
305 + * case you have to open it again very soon.
308 +static int wdog_release(struct inode *inode, struct file *file)
310 + if (expect_close == 42) {
314 + "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
317 + clear_bit(0, &wdog_is_open);
323 + * @this: our notifier block
324 + * @code: the event being reported
327 + * Our notifier is called on system shutdowns. Turn the watchdog
328 + * off so that it does not fire during the next reboot.
331 +static int wdog_notify_sys(struct notifier_block *this, unsigned long code,
334 + if (code == SYS_DOWN || code == SYS_HALT)
336 + return NOTIFY_DONE;
340 + * Kernel Interfaces
344 +static const struct file_operations wdog_fops = {
345 + .owner = THIS_MODULE,
346 + .llseek = no_llseek,
347 + .write = wdog_write,
348 + .unlocked_ioctl = wdog_ioctl,
350 + .release = wdog_release,
353 +static struct miscdevice wdog_miscdev = {
354 + .minor = WATCHDOG_MINOR,
355 + .name = "watchdog",
356 + .fops = &wdog_fops,
360 + * The WDT card needs to learn about soft shutdowns in order to
361 + * turn the timebomb registers off.
364 +static struct notifier_block wdog_notifier = {
365 + .notifier_call = wdog_notify_sys,
371 + * Unload the watchdog. You cannot do this with any file handles open.
372 + * If your watchdog is set to continue ticking on close and you unload
373 + * it, well it keeps ticking. We won't get the interrupt but the board
374 + * will not touch PC memory so all is fine. You just have to load a new
375 + * module in 60 seconds or reboot.
378 +static void __exit wdog_exit(void)
380 + misc_deregister(&wdog_miscdev);
381 + unregister_reboot_notifier(&wdog_notifier);
384 +static int __init wdog_init(void)
388 + /* Check that the heartbeat value is within it's range;
389 + if not reset to the default */
390 + if (wdog_set_heartbeat(heartbeat)) {
391 + wdog_set_heartbeat(WD_TIMO);
392 + printk(KERN_INFO "bcm2708_wdog: heartbeat value must be "
393 + "0 < heartbeat < %d, using %d\n",
394 + WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
398 + ret = register_reboot_notifier(&wdog_notifier);
401 + "wdt: cannot register reboot notifier (err=%d)\n", ret);
405 + ret = misc_register(&wdog_miscdev);
408 + "wdt: cannot register miscdev on minor=%d (err=%d)\n",
409 + WATCHDOG_MINOR, ret);
413 + printk(KERN_INFO "bcm2708 watchdog, heartbeat=%d sec (nowayout=%d)\n",
414 + heartbeat, nowayout);
418 + unregister_reboot_notifier(&wdog_notifier);
423 +module_init(wdog_init);
424 +module_exit(wdog_exit);
426 +MODULE_AUTHOR("Luke Diamand");
427 +MODULE_DESCRIPTION("Driver for BCM2708 watchdog");
428 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
429 +MODULE_ALIAS_MISCDEV(TEMP_MINOR);
430 +MODULE_LICENSE("GPL");