2 * AMD Elan SC520 processor Watchdog Timer driver
4 * Based on acquirewdt.c by Alan Cox,
5 * and sbc60xxwdt.c by Jakob Oestergaard <jakob@unthought.net>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
12 * The authors do NOT admit liability nor provide warranty for
13 * any of this software. This material is provided "AS-IS" in
14 * the hope that it may be useful for others.
16 * (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
17 * 9/27 - 2001 [Initial release]
19 * Additional fixes Alan Cox
21 * - Removed debug printks
22 * - Fixed SMP built kernel deadlock
23 * - Switched to private locks not lock_kernel
24 * - Used ioremap/writew/readw
25 * - Added NOWAYOUT support
27 * 4/12 - 2002 Changes by Rob Radez <rob@osinvestor.com>
29 * - Eliminate fop_llseek
30 * - Change CONFIG_WATCHDOG_NOWAYOUT semantics
31 * - Add KERN_* tags to printks
32 * - fix possible wdt_is_open race
33 * - Report proper capabilities in watchdog_info
34 * - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT,
35 * GETTIMEOUT, SETOPTIONS} ioctls
36 * 09/8 - 2003 Changes by Wim Van Sebroeck <wim@iguana.be>
37 * - cleanup of trailing spaces
38 * - added extra printk's for startup problems
40 * - made timeout (the emulated heartbeat) a module_param
41 * - made the keepalive ping an internal subroutine
43 * This WDT driver is different from most other Linux WDT
44 * drivers in that the driver will ping the watchdog by itself,
45 * because this particular WDT has a very short timeout (1.6
46 * seconds) and it would be insane to count on any userspace
47 * daemon always getting scheduled within that time frame.
49 * This driver uses memory mapped IO, and spinlock.
52 #include <linux/module.h>
53 #include <linux/moduleparam.h>
54 #include <linux/types.h>
55 #include <linux/timer.h>
56 #include <linux/miscdevice.h>
57 #include <linux/watchdog.h>
59 #include <linux/ioport.h>
60 #include <linux/notifier.h>
61 #include <linux/reboot.h>
62 #include <linux/init.h>
65 #include <asm/uaccess.h>
66 #include <asm/system.h>
69 * The SC520 can timeout anywhere from 492us to 32.21s.
70 * If we reset the watchdog every ~250ms we should be safe.
73 #define WDT_INTERVAL (HZ/4+1)
76 * We must not require too good response from the userspace daemon.
77 * Here we require the userspace daemon to send us a heartbeat
78 * char to /dev/watchdog every 30 seconds.
81 #define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */
82 static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */
83 module_param(timeout, int, 0);
84 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
87 * AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
89 * 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s
90 * 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s
93 #define TIMEOUT_EXPONENT ( 1 << 3 ) /* 0x08 = 2.01s */
95 /* #define MMCR_BASE_DEFAULT 0xfffef000 */
96 #define MMCR_BASE_DEFAULT ((__u16 *)0xffffe)
97 #define OFFS_WDTMRCTL ((unsigned int)0xcb0)
98 #define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */
99 #define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */
101 #define OUR_NAME "sc520_wdt"
102 #define PFX OUR_NAME ": "
104 #define WRT_DOG(data) *wdtmrctl=data
106 static __u16 *wdtmrctl;
108 static void wdt_timer_ping(unsigned long);
109 static struct timer_list timer;
110 static unsigned long next_heartbeat;
111 static unsigned long wdt_is_open;
112 static char wdt_expect_close;
113 static spinlock_t wdt_spinlock;
115 #ifdef CONFIG_WATCHDOG_NOWAYOUT
116 static int nowayout = 1;
118 static int nowayout = 0;
121 module_param(nowayout, int, 0);
122 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
128 static void wdt_timer_ping(unsigned long data)
130 /* If we got a heartbeat pulse within the WDT_US_INTERVAL
131 * we agree to ping the WDT
133 if(time_before(jiffies, next_heartbeat))
136 spin_lock(&wdt_spinlock);
137 writew(0xAAAA, wdtmrctl);
138 writew(0x5555, wdtmrctl);
139 spin_unlock(&wdt_spinlock);
141 /* Re-set the timer interval */
142 timer.expires = jiffies + WDT_INTERVAL;
145 printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n");
153 static void wdt_config(int writeval)
158 /* buy some time (ping) */
159 spin_lock_irqsave(&wdt_spinlock, flags);
160 dummy=readw(wdtmrctl); /* ensure write synchronization */
161 writew(0xAAAA, wdtmrctl);
162 writew(0x5555, wdtmrctl);
163 /* make WDT configuration register writable one time */
164 writew(0x3333, wdtmrctl);
165 writew(0xCCCC, wdtmrctl);
166 /* write WDT configuration register */
167 writew(writeval, wdtmrctl);
168 spin_unlock_irqrestore(&wdt_spinlock, flags);
171 static void wdt_startup(void)
173 next_heartbeat = jiffies + (timeout * HZ);
175 /* Start the timer */
176 timer.expires = jiffies + WDT_INTERVAL;
179 wdt_config(WDT_ENB | WDT_WRST_ENB | TIMEOUT_EXPONENT);
180 printk(KERN_INFO PFX "Watchdog timer is now enabled.\n");
183 static void wdt_turnoff(void)
189 printk(KERN_INFO PFX "Watchdog timer is now disabled...\n");
193 static void wdt_keepalive(void)
196 next_heartbeat = jiffies + (timeout * HZ);
200 * /dev/watchdog handling
203 static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
206 if(ppos != &file->f_pos)
209 /* See if we got the magic character 'V' and reload the timer */
216 /* note: just in case someone wrote the magic character
217 * five months ago... */
218 wdt_expect_close = 0;
221 for(ofs = 0; ofs != count; ofs++) {
223 if (get_user(c, buf + ofs))
226 wdt_expect_close = 42;
230 /* Well, anyhow someone wrote to us, we should return that favour */
236 static int fop_open(struct inode * inode, struct file * file)
238 /* Just in case we're already talking to someone... */
239 if(test_and_set_bit(0, &wdt_is_open))
242 __module_get(THIS_MODULE);
244 /* Good, fire up the show */
249 static int fop_close(struct inode * inode, struct file * file)
251 if(wdt_expect_close == 42)
255 printk(KERN_CRIT PFX "device file closed unexpectedly. Will not stop the WDT!\n");
257 clear_bit(0, &wdt_is_open);
258 wdt_expect_close = 0;
262 static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
265 static struct watchdog_info ident=
267 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
268 .firmware_version = 1,
276 case WDIOC_GETSUPPORT:
277 return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
278 case WDIOC_GETSTATUS:
279 case WDIOC_GETBOOTSTATUS:
280 return put_user(0, (int *)arg);
281 case WDIOC_KEEPALIVE:
284 case WDIOC_SETOPTIONS:
286 int new_options, retval = -EINVAL;
288 if(get_user(new_options, (int *)arg))
291 if(new_options & WDIOS_DISABLECARD) {
296 if(new_options & WDIOS_ENABLECARD) {
303 case WDIOC_SETTIMEOUT:
307 if(get_user(new_timeout, (int *)arg))
310 if(new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */
313 timeout = new_timeout;
317 case WDIOC_GETTIMEOUT:
318 return put_user(timeout, (int *)arg);
322 static struct file_operations wdt_fops = {
323 .owner = THIS_MODULE,
327 .release = fop_close,
331 static struct miscdevice wdt_miscdev = {
332 .minor = WATCHDOG_MINOR,
338 * Notifier for system down
341 static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
344 if(code==SYS_DOWN || code==SYS_HALT)
350 * The WDT needs to learn about soft shutdowns in order to
351 * turn the timebomb registers off.
354 static struct notifier_block wdt_notifier=
356 .notifier_call = wdt_notify_sys,
359 static void __exit sc520_wdt_unload(void)
364 misc_deregister(&wdt_miscdev);
366 unregister_reboot_notifier(&wdt_notifier);
369 static int __init sc520_wdt_init(void)
374 spin_lock_init(&wdt_spinlock);
376 if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */
378 timeout = WATCHDOG_TIMEOUT;
379 printk(KERN_INFO PFX "timeout value must be 1<=x<=3600, using %d\n",
384 timer.function = wdt_timer_ping;
387 rc = misc_register(&wdt_miscdev);
390 printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
391 wdt_miscdev.minor, rc);
392 goto err_out_region2;
395 rc = register_reboot_notifier(&wdt_notifier);
398 printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
400 goto err_out_miscdev;
403 /* get the Base Address Register */
404 cbar = inl_p(0xfffc);
405 printk(KERN_INFO PFX "CBAR: 0x%08lx\n", cbar);
406 /* check if MMCR aliasing bit is set */
407 if (cbar & 0x80000000) {
408 printk(KERN_INFO PFX "MMCR Aliasing enabled.\n");
409 wdtmrctl = (__u16 *)(cbar & 0x3fffffff);
411 printk(KERN_INFO PFX "!!! WARNING !!!\n"
412 "\t MMCR Aliasing found NOT enabled!\n"
413 "\t Using default value of: %p\n"
414 "\t This has not been tested!\n"
415 "\t Please email Scott Jennings <smj@oro.net>\n"
416 "\t and Bill Jennings <bj@oro.net> if it works!\n"
419 wdtmrctl = MMCR_BASE_DEFAULT;
422 wdtmrctl = (__u16 *)((char *)wdtmrctl + OFFS_WDTMRCTL);
423 wdtmrctl = ioremap((unsigned long)wdtmrctl, 2);
425 printk (KERN_ERR PFX "Unable to remap memory.\n");
427 goto err_out_notifier;
429 printk(KERN_INFO PFX "WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n",
435 unregister_reboot_notifier(&wdt_notifier);
437 misc_deregister(&wdt_miscdev);
442 module_init(sc520_wdt_init);
443 module_exit(sc520_wdt_unload);
445 MODULE_AUTHOR("Scott and Bill Jennings");
446 MODULE_DESCRIPTION("Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
447 MODULE_LICENSE("GPL");
448 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);