Blame view
drivers/watchdog/sc1200wdt.c
11.3 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>, * All Rights Reserved. * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively. * * 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. * * The author(s) of this software shall not be held liable for damages * of any nature resulting due to the use of this software. This * software is provided AS-IS with no warranties. * * Changelog: * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware. |
103a1d5c5 sc1200 watchdog d... |
18 19 |
* 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik * and Alan Cox. |
1da177e4c Linux-2.6.12-rc2 |
20 21 22 |
* 20020222 Zwane Mwaikambo Added probing. * 20020225 Zwane Mwaikambo Added ISAPNP support. * 20020412 Rob Radez Broke out start/stop functions |
103a1d5c5 sc1200 watchdog d... |
23 24 25 26 |
* <rob@osinvestor.com> Return proper status instead of * temperature warning * Add WDIOC_GETBOOTSTATUS and * WDIOC_SETOPTIONS ioctls |
1da177e4c Linux-2.6.12-rc2 |
27 |
* Fix CONFIG_WATCHDOG_NOWAYOUT |
103a1d5c5 sc1200 watchdog d... |
28 29 |
* 20020530 Joel Becker Add Matt Domsch's nowayout module * option |
1da177e4c Linux-2.6.12-rc2 |
30 31 32 |
* 20030116 Adam Belay Updated to the latest pnp code * */ |
1da177e4c Linux-2.6.12-rc2 |
33 34 35 36 37 38 39 40 41 42 43 |
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/ioport.h> #include <linux/spinlock.h> #include <linux/notifier.h> #include <linux/reboot.h> #include <linux/init.h> #include <linux/pnp.h> #include <linux/fs.h> |
6188e10d3 Convert asm/semap... |
44 |
#include <linux/semaphore.h> |
103a1d5c5 sc1200 watchdog d... |
45 46 |
#include <linux/io.h> #include <linux/uaccess.h> |
1da177e4c Linux-2.6.12-rc2 |
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
#define SC1200_MODULE_VER "build 20020303" #define SC1200_MODULE_NAME "sc1200wdt" #define PFX SC1200_MODULE_NAME ": " #define MAX_TIMEOUT 255 /* 255 minutes */ #define PMIR (io) /* Power Management Index Register */ #define PMDR (io+1) /* Power Management Data Register */ /* Data Register indexes */ #define FER1 0x00 /* Function enable register 1 */ #define FER2 0x01 /* Function enable register 2 */ #define PMC1 0x02 /* Power Management Ctrl 1 */ #define PMC2 0x03 /* Power Management Ctrl 2 */ #define PMC3 0x04 /* Power Management Ctrl 3 */ #define WDTO 0x05 /* Watchdog timeout register */ #define WDCF 0x06 /* Watchdog config register */ #define WDST 0x07 /* Watchdog status register */ /* WDCF bitfields - which devices assert WDO */ #define KBC_IRQ 0x01 /* Keyboard Controller */ #define MSE_IRQ 0x02 /* Mouse */ #define UART1_IRQ 0x03 /* Serial0 */ #define UART2_IRQ 0x04 /* Serial1 */ /* 5 -7 are reserved */ |
143a2e54b [WATCHDOG] More c... |
72 |
static char banner[] __initdata = PFX SC1200_MODULE_VER; |
1da177e4c Linux-2.6.12-rc2 |
73 74 75 |
static int timeout = 1; static int io = -1; static int io_len = 2; /* for non plug and play */ |
103a1d5c5 sc1200 watchdog d... |
76 |
static unsigned long open_flag; |
1da177e4c Linux-2.6.12-rc2 |
77 |
static char expect_close; |
c7dfd0cca [WATCHDOG] spin_l... |
78 |
static DEFINE_SPINLOCK(sc1200wdt_lock); /* io port access serialisation */ |
1da177e4c Linux-2.6.12-rc2 |
79 80 81 82 83 84 |
#if defined CONFIG_PNP static int isapnp = 1; static struct pnp_dev *wdt_dev; module_param(isapnp, int, 0); |
103a1d5c5 sc1200 watchdog d... |
85 86 |
MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled"); |
1da177e4c Linux-2.6.12-rc2 |
87 88 89 90 91 92 |
#endif module_param(io, int, 0); MODULE_PARM_DESC(io, "io port"); module_param(timeout, int, 0); MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1"); |
4bfdf3783 [PATCH] consolida... |
93 |
static int nowayout = WATCHDOG_NOWAYOUT; |
1da177e4c Linux-2.6.12-rc2 |
94 |
module_param(nowayout, int, 0); |
103a1d5c5 sc1200 watchdog d... |
95 96 97 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
1da177e4c Linux-2.6.12-rc2 |
98 99 100 101 |
/* Read from Data Register */ |
103a1d5c5 sc1200 watchdog d... |
102 103 |
static inline void __sc1200wdt_read_data(unsigned char index, unsigned char *data) |
1da177e4c Linux-2.6.12-rc2 |
104 |
{ |
1da177e4c Linux-2.6.12-rc2 |
105 106 |
outb_p(index, PMIR); *data = inb(PMDR); |
1da177e4c Linux-2.6.12-rc2 |
107 |
} |
103a1d5c5 sc1200 watchdog d... |
108 109 110 111 112 113 |
static void sc1200wdt_read_data(unsigned char index, unsigned char *data) { spin_lock(&sc1200wdt_lock); __sc1200wdt_read_data(index, data); spin_unlock(&sc1200wdt_lock); } |
1da177e4c Linux-2.6.12-rc2 |
114 115 |
/* Write to Data Register */ |
103a1d5c5 sc1200 watchdog d... |
116 117 |
static inline void __sc1200wdt_write_data(unsigned char index, unsigned char data) |
1da177e4c Linux-2.6.12-rc2 |
118 |
{ |
1da177e4c Linux-2.6.12-rc2 |
119 120 |
outb_p(index, PMIR); outb(data, PMDR); |
103a1d5c5 sc1200 watchdog d... |
121 122 123 124 125 126 127 |
} static inline void sc1200wdt_write_data(unsigned char index, unsigned char data) { spin_lock(&sc1200wdt_lock); __sc1200wdt_write_data(index, data); |
1da177e4c Linux-2.6.12-rc2 |
128 129 130 131 132 133 134 |
spin_unlock(&sc1200wdt_lock); } static void sc1200wdt_start(void) { unsigned char reg; |
103a1d5c5 sc1200 watchdog d... |
135 |
spin_lock(&sc1200wdt_lock); |
1da177e4c Linux-2.6.12-rc2 |
136 |
|
103a1d5c5 sc1200 watchdog d... |
137 |
__sc1200wdt_read_data(WDCF, ®); |
1da177e4c Linux-2.6.12-rc2 |
138 139 |
/* assert WDO when any of the following interrupts are triggered too */ reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ); |
103a1d5c5 sc1200 watchdog d... |
140 |
__sc1200wdt_write_data(WDCF, reg); |
1da177e4c Linux-2.6.12-rc2 |
141 |
/* set the timeout and get the ball rolling */ |
103a1d5c5 sc1200 watchdog d... |
142 |
__sc1200wdt_write_data(WDTO, timeout); |
1da177e4c Linux-2.6.12-rc2 |
143 |
|
103a1d5c5 sc1200 watchdog d... |
144 145 |
spin_unlock(&sc1200wdt_lock); } |
1da177e4c Linux-2.6.12-rc2 |
146 147 148 149 150 |
static void sc1200wdt_stop(void) { sc1200wdt_write_data(WDTO, 0); } |
1da177e4c Linux-2.6.12-rc2 |
151 152 153 154 155 156 157 158 159 160 |
/* This returns the status of the WDO signal, inactive high. */ static inline int sc1200wdt_status(void) { unsigned char ret; sc1200wdt_read_data(WDST, &ret); /* If the bit is inactive, the watchdog is enabled, so return * KEEPALIVEPING which is a bit of a kludge because there's nothing * else for enabled/disabled status */ |
103a1d5c5 sc1200 watchdog d... |
161 |
return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING; |
1da177e4c Linux-2.6.12-rc2 |
162 |
} |
1da177e4c Linux-2.6.12-rc2 |
163 164 |
static int sc1200wdt_open(struct inode *inode, struct file *file) { |
1da177e4c Linux-2.6.12-rc2 |
165 |
/* allow one at a time */ |
103a1d5c5 sc1200 watchdog d... |
166 |
if (test_and_set_bit(0, &open_flag)) |
1da177e4c Linux-2.6.12-rc2 |
167 168 169 170 171 172 173 |
return -EBUSY; if (timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT; sc1200wdt_start(); printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout); |
6abe78bf1 [WATCHDOG] Return... |
174 |
return nonseekable_open(inode, file); |
1da177e4c Linux-2.6.12-rc2 |
175 |
} |
103a1d5c5 sc1200 watchdog d... |
176 177 |
static long sc1200wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
1da177e4c Linux-2.6.12-rc2 |
178 179 180 181 |
{ int new_timeout; void __user *argp = (void __user *)arg; int __user *p = argp; |
103a1d5c5 sc1200 watchdog d... |
182 183 184 |
static const struct watchdog_info ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, |
1da177e4c Linux-2.6.12-rc2 |
185 186 187 188 189 |
.firmware_version = 0, .identity = "PC87307/PC97307", }; switch (cmd) { |
103a1d5c5 sc1200 watchdog d... |
190 |
case WDIOC_GETSUPPORT: |
e04ab9587 [WATCHDOG] sizeof... |
191 |
if (copy_to_user(argp, &ident, sizeof(ident))) |
103a1d5c5 sc1200 watchdog d... |
192 193 |
return -EFAULT; return 0; |
1da177e4c Linux-2.6.12-rc2 |
194 |
|
103a1d5c5 sc1200 watchdog d... |
195 196 |
case WDIOC_GETSTATUS: return put_user(sc1200wdt_status(), p); |
1da177e4c Linux-2.6.12-rc2 |
197 |
|
103a1d5c5 sc1200 watchdog d... |
198 199 |
case WDIOC_GETBOOTSTATUS: return put_user(0, p); |
1da177e4c Linux-2.6.12-rc2 |
200 |
|
103a1d5c5 sc1200 watchdog d... |
201 202 203 |
case WDIOC_SETOPTIONS: { int options, retval = -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
204 |
|
103a1d5c5 sc1200 watchdog d... |
205 206 |
if (get_user(options, p)) return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
207 |
|
103a1d5c5 sc1200 watchdog d... |
208 209 210 211 |
if (options & WDIOS_DISABLECARD) { sc1200wdt_stop(); retval = 0; } |
1da177e4c Linux-2.6.12-rc2 |
212 |
|
103a1d5c5 sc1200 watchdog d... |
213 214 215 |
if (options & WDIOS_ENABLECARD) { sc1200wdt_start(); retval = 0; |
1da177e4c Linux-2.6.12-rc2 |
216 |
} |
103a1d5c5 sc1200 watchdog d... |
217 218 219 |
return retval; } |
0c06090c9 [WATCHDOG] Coding... |
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
case WDIOC_KEEPALIVE: sc1200wdt_write_data(WDTO, timeout); return 0; case WDIOC_SETTIMEOUT: if (get_user(new_timeout, p)) return -EFAULT; /* the API states this is given in secs */ new_timeout /= 60; if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) return -EINVAL; timeout = new_timeout; sc1200wdt_write_data(WDTO, timeout); /* fall through and return the new timeout */ case WDIOC_GETTIMEOUT: return put_user(timeout * 60, p); |
103a1d5c5 sc1200 watchdog d... |
237 238 |
default: return -ENOTTY; |
1da177e4c Linux-2.6.12-rc2 |
239 240 241 242 243 244 245 246 247 248 249 250 |
} } static int sc1200wdt_release(struct inode *inode, struct file *file) { if (expect_close == 42) { sc1200wdt_stop(); printk(KERN_INFO PFX "Watchdog disabled "); } else { sc1200wdt_write_data(WDTO, timeout); |
103a1d5c5 sc1200 watchdog d... |
251 252 253 |
printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s) ", timeout); |
1da177e4c Linux-2.6.12-rc2 |
254 |
} |
103a1d5c5 sc1200 watchdog d... |
255 |
clear_bit(0, &open_flag); |
1da177e4c Linux-2.6.12-rc2 |
256 257 258 259 |
expect_close = 0; return 0; } |
103a1d5c5 sc1200 watchdog d... |
260 261 |
static ssize_t sc1200wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) |
1da177e4c Linux-2.6.12-rc2 |
262 263 264 265 266 267 268 269 270 |
{ if (len) { if (!nowayout) { size_t i; expect_close = 0; for (i = 0; i != len; i++) { char c; |
7944d3a5a [WATCHDOG] more c... |
271 |
if (get_user(c, data + i)) |
1da177e4c Linux-2.6.12-rc2 |
272 273 274 275 276 277 278 279 280 281 282 283 |
return -EFAULT; if (c == 'V') expect_close = 42; } } sc1200wdt_write_data(WDTO, timeout); return len; } return 0; } |
103a1d5c5 sc1200 watchdog d... |
284 285 |
static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) |
1da177e4c Linux-2.6.12-rc2 |
286 287 288 289 290 291 |
{ if (code == SYS_DOWN || code == SYS_HALT) sc1200wdt_stop(); return NOTIFY_DONE; } |
103a1d5c5 sc1200 watchdog d... |
292 |
static struct notifier_block sc1200wdt_notifier = { |
1da177e4c Linux-2.6.12-rc2 |
293 294 |
.notifier_call = sc1200wdt_notify_sys, }; |
103a1d5c5 sc1200 watchdog d... |
295 |
static const struct file_operations sc1200wdt_fops = { |
1da177e4c Linux-2.6.12-rc2 |
296 297 298 |
.owner = THIS_MODULE, .llseek = no_llseek, .write = sc1200wdt_write, |
103a1d5c5 sc1200 watchdog d... |
299 |
.unlocked_ioctl = sc1200wdt_ioctl, |
1da177e4c Linux-2.6.12-rc2 |
300 301 302 |
.open = sc1200wdt_open, .release = sc1200wdt_release, }; |
103a1d5c5 sc1200 watchdog d... |
303 |
static struct miscdevice sc1200wdt_miscdev = { |
1da177e4c Linux-2.6.12-rc2 |
304 305 306 307 308 309 310 311 312 313 314 |
.minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &sc1200wdt_fops, }; static int __init sc1200wdt_probe(void) { /* The probe works by reading the PMC3 register's default value of 0x0e * there is one caveat, if the device disables the parallel port or any * of the UARTs we won't be able to detect it. |
103a1d5c5 sc1200 watchdog d... |
315 316 |
* NB. This could be done with accuracy by reading the SID registers, * but we don't have access to those io regions. |
1da177e4c Linux-2.6.12-rc2 |
317 318 319 320 321 |
*/ unsigned char reg; sc1200wdt_read_data(PMC3, ®); |
103a1d5c5 sc1200 watchdog d... |
322 |
reg &= 0x0f; /* we don't want the UART busy bits */ |
1da177e4c Linux-2.6.12-rc2 |
323 324 325 326 327 328 329 330 331 332 333 |
return (reg == 0x0e) ? 0 : -ENODEV; } #if defined CONFIG_PNP static struct pnp_device_id scl200wdt_pnp_devices[] = { /* National Semiconductor PC87307/PC97307 watchdog component */ {.id = "NSC0800", .driver_data = 0}, {.id = ""}, }; |
103a1d5c5 sc1200 watchdog d... |
334 335 |
static int scl200wdt_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) |
1da177e4c Linux-2.6.12-rc2 |
336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
{ /* this driver only supports one card at a time */ if (wdt_dev || !isapnp) return -EBUSY; wdt_dev = dev; io = pnp_port_start(wdt_dev, 0); io_len = pnp_port_len(wdt_dev, 0); if (!request_region(io, io_len, SC1200_MODULE_NAME)) { printk(KERN_ERR PFX "Unable to register IO port %#x ", io); return -EBUSY; } |
103a1d5c5 sc1200 watchdog d... |
350 351 352 |
printk(KERN_INFO "scl200wdt: PnP device found at io port %#x/%d ", io, io_len); |
1da177e4c Linux-2.6.12-rc2 |
353 354 |
return 0; } |
103a1d5c5 sc1200 watchdog d... |
355 |
static void scl200wdt_pnp_remove(struct pnp_dev *dev) |
1da177e4c Linux-2.6.12-rc2 |
356 |
{ |
103a1d5c5 sc1200 watchdog d... |
357 |
if (wdt_dev) { |
1da177e4c Linux-2.6.12-rc2 |
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
release_region(io, io_len); wdt_dev = NULL; } } static struct pnp_driver scl200wdt_pnp_driver = { .name = "scl200wdt", .id_table = scl200wdt_pnp_devices, .probe = scl200wdt_pnp_probe, .remove = scl200wdt_pnp_remove, }; #endif /* CONFIG_PNP */ static int __init sc1200wdt_init(void) { int ret; |
143a2e54b [WATCHDOG] More c... |
376 377 |
printk(KERN_INFO "%s ", banner); |
1da177e4c Linux-2.6.12-rc2 |
378 |
|
1da177e4c Linux-2.6.12-rc2 |
379 380 381 382 383 384 385 386 387 388 389 390 |
#if defined CONFIG_PNP if (isapnp) { ret = pnp_register_driver(&scl200wdt_pnp_driver); if (ret) goto out_clean; } #endif if (io == -1) { printk(KERN_ERR PFX "io parameter must be specified "); ret = -EINVAL; |
150ed8ed6 [WATCHDOG] sc1200... |
391 |
goto out_pnp; |
1da177e4c Linux-2.6.12-rc2 |
392 393 394 395 396 397 398 399 400 401 402 403 404 |
} #if defined CONFIG_PNP /* now that the user has specified an IO port and we haven't detected * any devices, disable pnp support */ isapnp = 0; pnp_unregister_driver(&scl200wdt_pnp_driver); #endif if (!request_region(io, io_len, SC1200_MODULE_NAME)) { printk(KERN_ERR PFX "Unable to register IO port %#x ", io); ret = -EBUSY; |
150ed8ed6 [WATCHDOG] sc1200... |
405 |
goto out_pnp; |
1da177e4c Linux-2.6.12-rc2 |
406 407 408 409 410 411 412 413 |
} ret = sc1200wdt_probe(); if (ret) goto out_io; ret = register_reboot_notifier(&sc1200wdt_notifier); if (ret) { |
103a1d5c5 sc1200 watchdog d... |
414 415 416 |
printk(KERN_ERR PFX "Unable to register reboot notifier err = %d ", ret); |
1da177e4c Linux-2.6.12-rc2 |
417 418 419 420 421 |
goto out_io; } ret = misc_register(&sc1200wdt_miscdev); if (ret) { |
103a1d5c5 sc1200 watchdog d... |
422 423 424 425 |
printk(KERN_ERR PFX "Unable to register miscdev on minor %d ", WATCHDOG_MINOR); |
1da177e4c Linux-2.6.12-rc2 |
426 427 428 429 430 431 432 433 434 435 436 437 438 |
goto out_rbt; } /* ret = 0 */ out_clean: return ret; out_rbt: unregister_reboot_notifier(&sc1200wdt_notifier); out_io: release_region(io, io_len); |
150ed8ed6 [WATCHDOG] sc1200... |
439 440 441 442 443 |
out_pnp: #if defined CONFIG_PNP if (isapnp) pnp_unregister_driver(&scl200wdt_pnp_driver); #endif |
1da177e4c Linux-2.6.12-rc2 |
444 445 446 447 448 449 450 451 452 453 |
goto out_clean; } static void __exit sc1200wdt_exit(void) { misc_deregister(&sc1200wdt_miscdev); unregister_reboot_notifier(&sc1200wdt_notifier); #if defined CONFIG_PNP |
103a1d5c5 sc1200 watchdog d... |
454 |
if (isapnp) |
1da177e4c Linux-2.6.12-rc2 |
455 456 457 458 459 460 461 462 463 464 |
pnp_unregister_driver(&scl200wdt_pnp_driver); else #endif release_region(io, io_len); } module_init(sc1200wdt_init); module_exit(sc1200wdt_exit); MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>"); |
143a2e54b [WATCHDOG] More c... |
465 466 |
MODULE_DESCRIPTION( "Driver for National Semiconductor PC87307/PC97307 watchdog component"); |
1da177e4c Linux-2.6.12-rc2 |
467 468 |
MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |