aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/iTCO_wdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/iTCO_wdt.c')
-rw-r--r--drivers/watchdog/iTCO_wdt.c213
1 files changed, 48 insertions, 165 deletions
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 9c2c27c3b424..ceed39f26011 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -47,7 +47,7 @@
47 47
48/* Module and version information */ 48/* Module and version information */
49#define DRV_NAME "iTCO_wdt" 49#define DRV_NAME "iTCO_wdt"
50#define DRV_VERSION "1.07" 50#define DRV_VERSION "1.10"
51 51
52/* Includes */ 52/* Includes */
53#include <linux/module.h> /* For module specific items */ 53#include <linux/module.h> /* For module specific items */
@@ -88,8 +88,6 @@
88#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */ 88#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */
89 89
90/* internal variables */ 90/* internal variables */
91static unsigned long is_active;
92static char expect_release;
93static struct { /* this is private data for the iTCO_wdt device */ 91static struct { /* this is private data for the iTCO_wdt device */
94 /* TCO version/generation */ 92 /* TCO version/generation */
95 unsigned int iTCO_version; 93 unsigned int iTCO_version;
@@ -106,12 +104,12 @@ static struct { /* this is private data for the iTCO_wdt device */
106} iTCO_wdt_private; 104} iTCO_wdt_private;
107 105
108/* module parameters */ 106/* module parameters */
109#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ 107#define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */
110static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ 108static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */
111module_param(heartbeat, int, 0); 109module_param(heartbeat, int, 0);
112MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. " 110MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. "
113 "5..76 (TCO v1) or 3..614 (TCO v2), default=" 111 "5..76 (TCO v1) or 3..614 (TCO v2), default="
114 __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); 112 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
115 113
116static bool nowayout = WATCHDOG_NOWAYOUT; 114static bool nowayout = WATCHDOG_NOWAYOUT;
117module_param(nowayout, bool, 0); 115module_param(nowayout, bool, 0);
@@ -178,13 +176,13 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
178 return ret; /* returns: 0 = OK, -EIO = Error */ 176 return ret; /* returns: 0 = OK, -EIO = Error */
179} 177}
180 178
181static int iTCO_wdt_start(void) 179static int iTCO_wdt_start(struct watchdog_device *wd_dev)
182{ 180{
183 unsigned int val; 181 unsigned int val;
184 182
185 spin_lock(&iTCO_wdt_private.io_lock); 183 spin_lock(&iTCO_wdt_private.io_lock);
186 184
187 iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, heartbeat); 185 iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout);
188 186
189 /* disable chipset's NO_REBOOT bit */ 187 /* disable chipset's NO_REBOOT bit */
190 if (iTCO_wdt_unset_NO_REBOOT_bit()) { 188 if (iTCO_wdt_unset_NO_REBOOT_bit()) {
@@ -212,7 +210,7 @@ static int iTCO_wdt_start(void)
212 return 0; 210 return 0;
213} 211}
214 212
215static int iTCO_wdt_stop(void) 213static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
216{ 214{
217 unsigned int val; 215 unsigned int val;
218 216
@@ -236,11 +234,11 @@ static int iTCO_wdt_stop(void)
236 return 0; 234 return 0;
237} 235}
238 236
239static int iTCO_wdt_keepalive(void) 237static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
240{ 238{
241 spin_lock(&iTCO_wdt_private.io_lock); 239 spin_lock(&iTCO_wdt_private.io_lock);
242 240
243 iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, heartbeat); 241 iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout);
244 242
245 /* Reload the timer by writing to the TCO Timer Counter register */ 243 /* Reload the timer by writing to the TCO Timer Counter register */
246 if (iTCO_wdt_private.iTCO_version == 2) 244 if (iTCO_wdt_private.iTCO_version == 2)
@@ -257,7 +255,7 @@ static int iTCO_wdt_keepalive(void)
257 return 0; 255 return 0;
258} 256}
259 257
260static int iTCO_wdt_set_heartbeat(int t) 258static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
261{ 259{
262 unsigned int val16; 260 unsigned int val16;
263 unsigned char val8; 261 unsigned char val8;
@@ -304,14 +302,15 @@ static int iTCO_wdt_set_heartbeat(int t)
304 return -EINVAL; 302 return -EINVAL;
305 } 303 }
306 304
307 heartbeat = t; 305 wd_dev->timeout = t;
308 return 0; 306 return 0;
309} 307}
310 308
311static int iTCO_wdt_get_timeleft(int *time_left) 309static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
312{ 310{
313 unsigned int val16; 311 unsigned int val16;
314 unsigned char val8; 312 unsigned char val8;
313 unsigned int time_left = 0;
315 314
316 /* read the TCO Timer */ 315 /* read the TCO Timer */
317 if (iTCO_wdt_private.iTCO_version == 2) { 316 if (iTCO_wdt_private.iTCO_version == 2) {
@@ -320,7 +319,7 @@ static int iTCO_wdt_get_timeleft(int *time_left)
320 val16 &= 0x3ff; 319 val16 &= 0x3ff;
321 spin_unlock(&iTCO_wdt_private.io_lock); 320 spin_unlock(&iTCO_wdt_private.io_lock);
322 321
323 *time_left = (val16 * 6) / 10; 322 time_left = (val16 * 6) / 10;
324 } else if (iTCO_wdt_private.iTCO_version == 1) { 323 } else if (iTCO_wdt_private.iTCO_version == 1) {
325 spin_lock(&iTCO_wdt_private.io_lock); 324 spin_lock(&iTCO_wdt_private.io_lock);
326 val8 = inb(TCO_RLD); 325 val8 = inb(TCO_RLD);
@@ -329,156 +328,35 @@ static int iTCO_wdt_get_timeleft(int *time_left)
329 val8 += (inb(TCOv1_TMR) & 0x3f); 328 val8 += (inb(TCOv1_TMR) & 0x3f);
330 spin_unlock(&iTCO_wdt_private.io_lock); 329 spin_unlock(&iTCO_wdt_private.io_lock);
331 330
332 *time_left = (val8 * 6) / 10; 331 time_left = (val8 * 6) / 10;
333 } else
334 return -EINVAL;
335 return 0;
336}
337
338/*
339 * /dev/watchdog handling
340 */
341
342static int iTCO_wdt_open(struct inode *inode, struct file *file)
343{
344 /* /dev/watchdog can only be opened once */
345 if (test_and_set_bit(0, &is_active))
346 return -EBUSY;
347
348 /*
349 * Reload and activate timer
350 */
351 iTCO_wdt_start();
352 return nonseekable_open(inode, file);
353}
354
355static int iTCO_wdt_release(struct inode *inode, struct file *file)
356{
357 /*
358 * Shut off the timer.
359 */
360 if (expect_release == 42) {
361 iTCO_wdt_stop();
362 } else {
363 pr_crit("Unexpected close, not stopping watchdog!\n");
364 iTCO_wdt_keepalive();
365 }
366 clear_bit(0, &is_active);
367 expect_release = 0;
368 return 0;
369}
370
371static ssize_t iTCO_wdt_write(struct file *file, const char __user *data,
372 size_t len, loff_t *ppos)
373{
374 /* See if we got the magic character 'V' and reload the timer */
375 if (len) {
376 if (!nowayout) {
377 size_t i;
378
379 /* note: just in case someone wrote the magic
380 character five months ago... */
381 expect_release = 0;
382
383 /* scan to see whether or not we got the
384 magic character */
385 for (i = 0; i != len; i++) {
386 char c;
387 if (get_user(c, data + i))
388 return -EFAULT;
389 if (c == 'V')
390 expect_release = 42;
391 }
392 }
393
394 /* someone wrote to us, we should reload the timer */
395 iTCO_wdt_keepalive();
396 }
397 return len;
398}
399
400static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
401 unsigned long arg)
402{
403 int new_options, retval = -EINVAL;
404 int new_heartbeat;
405 void __user *argp = (void __user *)arg;
406 int __user *p = argp;
407 static const struct watchdog_info ident = {
408 .options = WDIOF_SETTIMEOUT |
409 WDIOF_KEEPALIVEPING |
410 WDIOF_MAGICCLOSE,
411 .firmware_version = 0,
412 .identity = DRV_NAME,
413 };
414
415 switch (cmd) {
416 case WDIOC_GETSUPPORT:
417 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
418 case WDIOC_GETSTATUS:
419 case WDIOC_GETBOOTSTATUS:
420 return put_user(0, p);
421
422 case WDIOC_SETOPTIONS:
423 {
424 if (get_user(new_options, p))
425 return -EFAULT;
426
427 if (new_options & WDIOS_DISABLECARD) {
428 iTCO_wdt_stop();
429 retval = 0;
430 }
431 if (new_options & WDIOS_ENABLECARD) {
432 iTCO_wdt_keepalive();
433 iTCO_wdt_start();
434 retval = 0;
435 }
436 return retval;
437 }
438 case WDIOC_KEEPALIVE:
439 iTCO_wdt_keepalive();
440 return 0;
441
442 case WDIOC_SETTIMEOUT:
443 {
444 if (get_user(new_heartbeat, p))
445 return -EFAULT;
446 if (iTCO_wdt_set_heartbeat(new_heartbeat))
447 return -EINVAL;
448 iTCO_wdt_keepalive();
449 /* Fall */
450 }
451 case WDIOC_GETTIMEOUT:
452 return put_user(heartbeat, p);
453 case WDIOC_GETTIMELEFT:
454 {
455 int time_left;
456 if (iTCO_wdt_get_timeleft(&time_left))
457 return -EINVAL;
458 return put_user(time_left, p);
459 }
460 default:
461 return -ENOTTY;
462 } 332 }
333 return time_left;
463} 334}
464 335
465/* 336/*
466 * Kernel Interfaces 337 * Kernel Interfaces
467 */ 338 */
468 339
469static const struct file_operations iTCO_wdt_fops = { 340static const struct watchdog_info ident = {
341 .options = WDIOF_SETTIMEOUT |
342 WDIOF_KEEPALIVEPING |
343 WDIOF_MAGICCLOSE,
344 .firmware_version = 0,
345 .identity = DRV_NAME,
346};
347
348static const struct watchdog_ops iTCO_wdt_ops = {
470 .owner = THIS_MODULE, 349 .owner = THIS_MODULE,
471 .llseek = no_llseek, 350 .start = iTCO_wdt_start,
472 .write = iTCO_wdt_write, 351 .stop = iTCO_wdt_stop,
473 .unlocked_ioctl = iTCO_wdt_ioctl, 352 .ping = iTCO_wdt_ping,
474 .open = iTCO_wdt_open, 353 .set_timeout = iTCO_wdt_set_timeout,
475 .release = iTCO_wdt_release, 354 .get_timeleft = iTCO_wdt_get_timeleft,
476}; 355};
477 356
478static struct miscdevice iTCO_wdt_miscdev = { 357static struct watchdog_device iTCO_wdt_watchdog_dev = {
479 .minor = WATCHDOG_MINOR, 358 .info = &ident,
480 .name = "watchdog", 359 .ops = &iTCO_wdt_ops,
481 .fops = &iTCO_wdt_fops,
482}; 360};
483 361
484/* 362/*
@@ -489,10 +367,10 @@ static void __devexit iTCO_wdt_cleanup(void)
489{ 367{
490 /* Stop the timer before we leave */ 368 /* Stop the timer before we leave */
491 if (!nowayout) 369 if (!nowayout)
492 iTCO_wdt_stop(); 370 iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
493 371
494 /* Deregister */ 372 /* Deregister */
495 misc_deregister(&iTCO_wdt_miscdev); 373 watchdog_unregister_device(&iTCO_wdt_watchdog_dev);
496 374
497 /* release resources */ 375 /* release resources */
498 release_region(iTCO_wdt_private.tco_res->start, 376 release_region(iTCO_wdt_private.tco_res->start,
@@ -605,20 +483,25 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev)
605 outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ 483 outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
606 outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ 484 outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
607 485
486 iTCO_wdt_watchdog_dev.bootstatus = 0;
487 iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT;
488 watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout);
489 iTCO_wdt_watchdog_dev.parent = dev->dev.parent;
490
608 /* Make sure the watchdog is not running */ 491 /* Make sure the watchdog is not running */
609 iTCO_wdt_stop(); 492 iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
610 493
611 /* Check that the heartbeat value is within it's range; 494 /* Check that the heartbeat value is within it's range;
612 if not reset to the default */ 495 if not reset to the default */
613 if (iTCO_wdt_set_heartbeat(heartbeat)) { 496 if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) {
614 iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT); 497 iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT);
615 pr_info("timeout value out of range, using %d\n", heartbeat); 498 pr_info("timeout value out of range, using %d\n",
499 WATCHDOG_TIMEOUT);
616 } 500 }
617 501
618 ret = misc_register(&iTCO_wdt_miscdev); 502 ret = watchdog_register_device(&iTCO_wdt_watchdog_dev);
619 if (ret != 0) { 503 if (ret != 0) {
620 pr_err("cannot register miscdev on minor=%d (err=%d)\n", 504 pr_err("cannot register watchdog device (err=%d)\n", ret);
621 WATCHDOG_MINOR, ret);
622 goto unreg_tco; 505 goto unreg_tco;
623 } 506 }
624 507
@@ -659,7 +542,7 @@ static int __devexit iTCO_wdt_remove(struct platform_device *dev)
659 542
660static void iTCO_wdt_shutdown(struct platform_device *dev) 543static void iTCO_wdt_shutdown(struct platform_device *dev)
661{ 544{
662 iTCO_wdt_stop(); 545 iTCO_wdt_stop(NULL);
663} 546}
664 547
665static struct platform_driver iTCO_wdt_driver = { 548static struct platform_driver iTCO_wdt_driver = {