Blame view
drivers/watchdog/acquirewdt.c
8.72 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 |
/* * Acquire Single Board Computer Watchdog Timer driver * |
143a2e54b [WATCHDOG] More c... |
4 |
* Based on wdt.c. Original copyright messages: |
1da177e4c Linux-2.6.12-rc2 |
5 |
* |
29fa0586d [PATCH] Switch al... |
6 7 |
* (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, * All Rights Reserved. |
1da177e4c Linux-2.6.12-rc2 |
8 9 10 11 12 13 14 15 16 17 |
* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide * warranty for any of this software. This material is provided * "AS-IS" and at no charge. * |
29fa0586d [PATCH] Switch al... |
18 |
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> |
1da177e4c Linux-2.6.12-rc2 |
19 |
* |
143a2e54b [WATCHDOG] More c... |
20 21 22 |
* 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT * Can't add timeout - driver doesn't allow changing value |
1da177e4c Linux-2.6.12-rc2 |
23 24 25 26 27 28 |
*/ /* * Theory of Operation: * The Watch-Dog Timer is provided to ensure that standalone * Systems can always recover from catastrophic conditions that |
25985edce Fix common misspe... |
29 |
* caused the CPU to crash. This condition may have occurred by |
1da177e4c Linux-2.6.12-rc2 |
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
* external EMI or a software bug. When the CPU stops working * correctly, hardware on the board will either perform a hardware * reset (cold boot) or a non-maskable interrupt (NMI) to bring the * system back to a known state. * * The Watch-Dog Timer is controlled by two I/O Ports. * 443 hex - Read - Enable or refresh the Watch-Dog Timer * 043 hex - Read - Disable the Watch-Dog Timer * * To enable the Watch-Dog Timer, a read from I/O port 443h must * be performed. This will enable and activate the countdown timer * which will eventually time out and either reset the CPU or cause * an NMI depending on the setting of a jumper. To ensure that this * reset condition does not occur, the Watch-Dog Timer must be * periodically refreshed by reading the same I/O port 443h. * The Watch-Dog Timer is disabled by reading I/O port 043h. * * The Watch-Dog Timer Time-Out Period is set via jumpers. * It can be 1, 2, 10, 20, 110 or 220 seconds. */ |
76c11f044 [WATCHDOG] acquir... |
50 51 52 |
/* * Includes, defines, variables, module parameters, ... */ |
1da177e4c Linux-2.6.12-rc2 |
53 |
|
76c11f044 [WATCHDOG] acquir... |
54 55 56 57 58 59 |
/* Includes */ #include <linux/module.h> /* For module specific items */ #include <linux/moduleparam.h> /* For new moduleparam's */ #include <linux/types.h> /* For standard types (like size_t) */ #include <linux/errno.h> /* For the -ENODEV/... values */ #include <linux/kernel.h> /* For printk/panic/... */ |
94da1e2ef [WATCHDOG 01/57] ... |
60 61 |
#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV (WATCHDOG_MINOR) */ |
76c11f044 [WATCHDOG] acquir... |
62 63 64 |
#include <linux/watchdog.h> /* For the watchdog specific items */ #include <linux/fs.h> /* For file operations */ #include <linux/ioport.h> /* For io-port access */ |
ad5fe3231 [WATCHDOG] acquir... |
65 |
#include <linux/platform_device.h> /* For platform_driver framework */ |
76c11f044 [WATCHDOG] acquir... |
66 |
#include <linux/init.h> /* For __init/__exit/... */ |
94da1e2ef [WATCHDOG 01/57] ... |
67 68 |
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/io.h> /* For inb/outb/... */ |
76c11f044 [WATCHDOG] acquir... |
69 70 71 72 |
/* Module information */ #define DRV_NAME "acquirewdt" #define PFX DRV_NAME ": " |
1da177e4c Linux-2.6.12-rc2 |
73 |
#define WATCHDOG_NAME "Acquire WDT" |
94da1e2ef [WATCHDOG 01/57] ... |
74 75 |
/* There is no way to see what the correct time-out period is */ #define WATCHDOG_HEARTBEAT 0 |
1da177e4c Linux-2.6.12-rc2 |
76 |
|
76c11f044 [WATCHDOG] acquir... |
77 |
/* internal variables */ |
94da1e2ef [WATCHDOG 01/57] ... |
78 79 |
/* the watchdog platform device */ static struct platform_device *acq_platform_device; |
1da177e4c Linux-2.6.12-rc2 |
80 81 |
static unsigned long acq_is_open; static char expect_close; |
76c11f044 [WATCHDOG] acquir... |
82 |
/* module parameters */ |
94da1e2ef [WATCHDOG 01/57] ... |
83 84 |
/* You must set this - there is no sane way to probe for this board. */ static int wdt_stop = 0x43; |
1da177e4c Linux-2.6.12-rc2 |
85 86 |
module_param(wdt_stop, int, 0); MODULE_PARM_DESC(wdt_stop, "Acquire WDT 'stop' io port (default 0x43)"); |
94da1e2ef [WATCHDOG 01/57] ... |
87 88 |
/* You must set this - there is no sane way to probe for this board. */ static int wdt_start = 0x443; |
1da177e4c Linux-2.6.12-rc2 |
89 90 |
module_param(wdt_start, int, 0); MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)"); |
4bfdf3783 [PATCH] consolida... |
91 |
static int nowayout = WATCHDOG_NOWAYOUT; |
1da177e4c Linux-2.6.12-rc2 |
92 |
module_param(nowayout, int, 0); |
94da1e2ef [WATCHDOG 01/57] ... |
93 94 95 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
1da177e4c Linux-2.6.12-rc2 |
96 97 |
/* |
76c11f044 [WATCHDOG] acquir... |
98 |
* Watchdog Operations |
1da177e4c Linux-2.6.12-rc2 |
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
*/ static void acq_keepalive(void) { /* Write a watchdog value */ inb_p(wdt_start); } static void acq_stop(void) { /* Turn the card off */ inb_p(wdt_stop); } /* |
76c11f044 [WATCHDOG] acquir... |
114 |
* /dev/watchdog handling |
1da177e4c Linux-2.6.12-rc2 |
115 |
*/ |
94da1e2ef [WATCHDOG 01/57] ... |
116 117 |
static ssize_t acq_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) |
1da177e4c Linux-2.6.12-rc2 |
118 119 |
{ /* See if we got the magic character 'V' and reload the timer */ |
94da1e2ef [WATCHDOG 01/57] ... |
120 |
if (count) { |
1da177e4c Linux-2.6.12-rc2 |
121 122 |
if (!nowayout) { size_t i; |
1da177e4c Linux-2.6.12-rc2 |
123 |
/* note: just in case someone wrote the magic character |
7944d3a5a [WATCHDOG] more c... |
124 |
five months ago... */ |
1da177e4c Linux-2.6.12-rc2 |
125 |
expect_close = 0; |
94da1e2ef [WATCHDOG 01/57] ... |
126 127 |
/* scan to see whether or not we got the magic character */ |
1da177e4c Linux-2.6.12-rc2 |
128 129 130 131 132 133 134 135 |
for (i = 0; i != count; i++) { char c; if (get_user(c, buf + i)) return -EFAULT; if (c == 'V') expect_close = 42; } } |
94da1e2ef [WATCHDOG 01/57] ... |
136 137 |
/* Well, anyhow someone wrote to us, we should return that favour */ |
1da177e4c Linux-2.6.12-rc2 |
138 139 140 141 |
acq_keepalive(); } return count; } |
94da1e2ef [WATCHDOG 01/57] ... |
142 |
static long acq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
1da177e4c Linux-2.6.12-rc2 |
143 144 145 146 |
{ int options, retval = -EINVAL; void __user *argp = (void __user *)arg; int __user *p = argp; |
42747d712 [WATCHDOG] watchd... |
147 |
static const struct watchdog_info ident = { |
1da177e4c Linux-2.6.12-rc2 |
148 149 |
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .firmware_version = 1, |
76c11f044 [WATCHDOG] acquir... |
150 |
.identity = WATCHDOG_NAME, |
1da177e4c Linux-2.6.12-rc2 |
151 |
}; |
94da1e2ef [WATCHDOG 01/57] ... |
152 |
switch (cmd) { |
1da177e4c Linux-2.6.12-rc2 |
153 |
case WDIOC_GETSUPPORT: |
94da1e2ef [WATCHDOG 01/57] ... |
154 |
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; |
1da177e4c Linux-2.6.12-rc2 |
155 156 157 |
case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: |
94da1e2ef [WATCHDOG 01/57] ... |
158 |
return put_user(0, p); |
1da177e4c Linux-2.6.12-rc2 |
159 |
|
1da177e4c Linux-2.6.12-rc2 |
160 161 |
case WDIOC_SETOPTIONS: { |
94da1e2ef [WATCHDOG 01/57] ... |
162 163 164 165 166 167 168 169 170 171 172 |
if (get_user(options, p)) return -EFAULT; if (options & WDIOS_DISABLECARD) { acq_stop(); retval = 0; } if (options & WDIOS_ENABLECARD) { acq_keepalive(); retval = 0; } return retval; |
1da177e4c Linux-2.6.12-rc2 |
173 |
} |
0c06090c9 [WATCHDOG] Coding... |
174 175 176 177 178 179 |
case WDIOC_KEEPALIVE: acq_keepalive(); return 0; case WDIOC_GETTIMEOUT: return put_user(WATCHDOG_HEARTBEAT, p); |
1da177e4c Linux-2.6.12-rc2 |
180 |
default: |
94da1e2ef [WATCHDOG 01/57] ... |
181 |
return -ENOTTY; |
1da177e4c Linux-2.6.12-rc2 |
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
} } static int acq_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &acq_is_open)) return -EBUSY; if (nowayout) __module_get(THIS_MODULE); /* Activate */ acq_keepalive(); return nonseekable_open(inode, file); } static int acq_close(struct inode *inode, struct file *file) { if (expect_close == 42) { acq_stop(); } else { |
94da1e2ef [WATCHDOG 01/57] ... |
203 204 205 |
printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog! "); |
1da177e4c Linux-2.6.12-rc2 |
206 207 208 209 210 211 212 213 |
acq_keepalive(); } clear_bit(0, &acq_is_open); expect_close = 0; return 0; } /* |
1da177e4c Linux-2.6.12-rc2 |
214 215 |
* Kernel Interfaces */ |
62322d255 [PATCH] make more... |
216 |
static const struct file_operations acq_fops = { |
1da177e4c Linux-2.6.12-rc2 |
217 218 219 |
.owner = THIS_MODULE, .llseek = no_llseek, .write = acq_write, |
94da1e2ef [WATCHDOG 01/57] ... |
220 |
.unlocked_ioctl = acq_ioctl, |
1da177e4c Linux-2.6.12-rc2 |
221 222 223 |
.open = acq_open, .release = acq_close, }; |
76c11f044 [WATCHDOG] acquir... |
224 225 226 227 |
static struct miscdevice acq_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &acq_fops, |
1da177e4c Linux-2.6.12-rc2 |
228 229 230 |
}; /* |
76c11f044 [WATCHDOG] acquir... |
231 232 |
* Init & exit routines */ |
ad5fe3231 [WATCHDOG] acquir... |
233 |
static int __devinit acq_probe(struct platform_device *dev) |
1da177e4c Linux-2.6.12-rc2 |
234 235 |
{ int ret; |
1da177e4c Linux-2.6.12-rc2 |
236 237 |
if (wdt_stop != wdt_start) { if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) { |
94da1e2ef [WATCHDOG 01/57] ... |
238 239 240 |
printk(KERN_ERR PFX "I/O address 0x%04x already in use ", wdt_stop); |
1da177e4c Linux-2.6.12-rc2 |
241 242 243 244 245 246 |
ret = -EIO; goto out; } } if (!request_region(wdt_start, 1, WATCHDOG_NAME)) { |
94da1e2ef [WATCHDOG 01/57] ... |
247 248 |
printk(KERN_ERR PFX "I/O address 0x%04x already in use ", |
1da177e4c Linux-2.6.12-rc2 |
249 250 251 252 |
wdt_start); ret = -EIO; goto unreg_stop; } |
1da177e4c Linux-2.6.12-rc2 |
253 254 |
ret = misc_register(&acq_miscdev); if (ret != 0) { |
94da1e2ef [WATCHDOG 01/57] ... |
255 256 257 258 |
printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d) ", WATCHDOG_MINOR, ret); |
98c08e98f [WATCHDOG] acquir... |
259 |
goto unreg_regions; |
1da177e4c Linux-2.6.12-rc2 |
260 |
} |
94da1e2ef [WATCHDOG 01/57] ... |
261 262 |
printk(KERN_INFO PFX "initialized. (nowayout=%d) ", nowayout); |
1da177e4c Linux-2.6.12-rc2 |
263 264 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
265 266 267 268 269 270 271 272 |
unreg_regions: release_region(wdt_start, 1); unreg_stop: if (wdt_stop != wdt_start) release_region(wdt_stop, 1); out: return ret; } |
ad5fe3231 [WATCHDOG] acquir... |
273 |
static int __devexit acq_remove(struct platform_device *dev) |
1da177e4c Linux-2.6.12-rc2 |
274 275 |
{ misc_deregister(&acq_miscdev); |
94da1e2ef [WATCHDOG 01/57] ... |
276 277 278 |
release_region(wdt_start, 1); if (wdt_stop != wdt_start) release_region(wdt_stop, 1); |
ad5fe3231 [WATCHDOG] acquir... |
279 280 281 |
return 0; } |
98c08e98f [WATCHDOG] acquir... |
282 283 284 285 286 |
static void acq_shutdown(struct platform_device *dev) { /* Turn the WDT off if we have a soft shutdown */ acq_stop(); } |
ad5fe3231 [WATCHDOG] acquir... |
287 288 289 |
static struct platform_driver acquirewdt_driver = { .probe = acq_probe, .remove = __devexit_p(acq_remove), |
98c08e98f [WATCHDOG] acquir... |
290 |
.shutdown = acq_shutdown, |
ad5fe3231 [WATCHDOG] acquir... |
291 292 293 294 295 296 297 298 299 |
.driver = { .owner = THIS_MODULE, .name = DRV_NAME, }, }; static int __init acq_init(void) { int err; |
94da1e2ef [WATCHDOG 01/57] ... |
300 301 302 |
printk(KERN_INFO "WDT driver for Acquire single board computer initialising. "); |
ad5fe3231 [WATCHDOG] acquir... |
303 304 305 306 |
err = platform_driver_register(&acquirewdt_driver); if (err) return err; |
94da1e2ef [WATCHDOG 01/57] ... |
307 308 |
acq_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); |
ad5fe3231 [WATCHDOG] acquir... |
309 310 311 312 |
if (IS_ERR(acq_platform_device)) { err = PTR_ERR(acq_platform_device); goto unreg_platform_driver; } |
ad5fe3231 [WATCHDOG] acquir... |
313 314 315 316 317 318 319 320 321 322 323 324 325 |
return 0; unreg_platform_driver: platform_driver_unregister(&acquirewdt_driver); return err; } static void __exit acq_exit(void) { platform_device_unregister(acq_platform_device); platform_driver_unregister(&acquirewdt_driver); printk(KERN_INFO PFX "Watchdog Module Unloaded. "); |
1da177e4c Linux-2.6.12-rc2 |
326 327 328 329 330 331 332 333 334 |
} module_init(acq_init); module_exit(acq_exit); MODULE_AUTHOR("David Woodhouse"); MODULE_DESCRIPTION("Acquire Inc. Single Board Computer Watchdog Timer driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |