linux/drivers/watchdog/wdt.c
<<
>>
Prefs
   1/*
   2 *      Industrial Computer Source WDT501 driver
   3 *
   4 *      (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
   5 *                                              All Rights Reserved.
   6 *
   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.
  11 *
  12 *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
  13 *      warranty for any of this software. This material is provided
  14 *      "AS-IS" and at no charge.
  15 *
  16 *      (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
  17 *
  18 *      Release 0.10.
  19 *
  20 *      Fixes
  21 *              Dave Gregorich  :       Modularisation and minor bugs
  22 *              Alan Cox        :       Added the watchdog ioctl() stuff
  23 *              Alan Cox        :       Fixed the reboot problem (as noted by
  24 *                                      Matt Crocker).
  25 *              Alan Cox        :       Added wdt= boot option
  26 *              Alan Cox        :       Cleaned up copy/user stuff
  27 *              Tim Hockin      :       Added insmod parameters, comment
  28 *                                      cleanup, parameterized timeout
  29 *              Tigran Aivazian :       Restructured wdt_init() to handle
  30 *                                      failures
  31 *              Joel Becker     :       Added WDIOC_GET/SETTIMEOUT
  32 *              Matt Domsch     :       Added nowayout module option
  33 */
  34
  35#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  36
  37#include <linux/interrupt.h>
  38#include <linux/module.h>
  39#include <linux/moduleparam.h>
  40#include <linux/types.h>
  41#include <linux/miscdevice.h>
  42#include <linux/watchdog.h>
  43#include <linux/fs.h>
  44#include <linux/ioport.h>
  45#include <linux/notifier.h>
  46#include <linux/reboot.h>
  47#include <linux/init.h>
  48#include <linux/io.h>
  49#include <linux/uaccess.h>
  50
  51#include "wd501p.h"
  52
  53static unsigned long wdt_is_open;
  54static char expect_close;
  55
  56/*
  57 *      Module parameters
  58 */
  59
  60#define WD_TIMO 60                      /* Default heartbeat = 60 seconds */
  61
  62static int heartbeat = WD_TIMO;
  63static int wd_heartbeat;
  64module_param(heartbeat, int, 0);
  65MODULE_PARM_DESC(heartbeat,
  66        "Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default="
  67                                __MODULE_STRING(WD_TIMO) ")");
  68
  69static bool nowayout = WATCHDOG_NOWAYOUT;
  70module_param(nowayout, bool, 0);
  71MODULE_PARM_DESC(nowayout,
  72        "Watchdog cannot be stopped once started (default="
  73                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  74
  75/* You must set these - there is no sane way to probe for this board. */
  76static int io = 0x240;
  77static int irq = 11;
  78
  79static DEFINE_SPINLOCK(wdt_lock);
  80
  81module_param(io, int, 0);
  82MODULE_PARM_DESC(io, "WDT io port (default=0x240)");
  83module_param(irq, int, 0);
  84MODULE_PARM_DESC(irq, "WDT irq (default=11)");
  85
  86/* Support for the Fan Tachometer on the WDT501-P */
  87static int tachometer;
  88module_param(tachometer, int, 0);
  89MODULE_PARM_DESC(tachometer,
  90                "WDT501-P Fan Tachometer support (0=disable, default=0)");
  91
  92static int type = 500;
  93module_param(type, int, 0);
  94MODULE_PARM_DESC(type,
  95                "WDT501-P Card type (500 or 501, default=500)");
  96
  97/*
  98 *      Programming support
  99 */
 100
 101static void wdt_ctr_mode(int ctr, int mode)
 102{
 103        ctr <<= 6;
 104        ctr |= 0x30;
 105        ctr |= (mode << 1);
 106        outb_p(ctr, WDT_CR);
 107}
 108
 109static void wdt_ctr_load(int ctr, int val)
 110{
 111        outb_p(val&0xFF, WDT_COUNT0+ctr);
 112        outb_p(val>>8, WDT_COUNT0+ctr);
 113}
 114
 115/**
 116 *      wdt_start:
 117 *
 118 *      Start the watchdog driver.
 119 */
 120
 121static int wdt_start(void)
 122{
 123        unsigned long flags;
 124        spin_lock_irqsave(&wdt_lock, flags);
 125        inb_p(WDT_DC);                  /* Disable watchdog */
 126        wdt_ctr_mode(0, 3);             /* Program CTR0 for Mode 3:
 127                                                Square Wave Generator */
 128        wdt_ctr_mode(1, 2);             /* Program CTR1 for Mode 2:
 129                                                Rate Generator */
 130        wdt_ctr_mode(2, 0);             /* Program CTR2 for Mode 0:
 131                                                Pulse on Terminal Count */
 132        wdt_ctr_load(0, 8948);          /* Count at 100Hz */
 133        wdt_ctr_load(1, wd_heartbeat);  /* Heartbeat */
 134        wdt_ctr_load(2, 65535);         /* Length of reset pulse */
 135        outb_p(0, WDT_DC);              /* Enable watchdog */
 136        spin_unlock_irqrestore(&wdt_lock, flags);
 137        return 0;
 138}
 139
 140/**
 141 *      wdt_stop:
 142 *
 143 *      Stop the watchdog driver.
 144 */
 145
 146static int wdt_stop(void)
 147{
 148        unsigned long flags;
 149        spin_lock_irqsave(&wdt_lock, flags);
 150        /* Turn the card off */
 151        inb_p(WDT_DC);                  /* Disable watchdog */
 152        wdt_ctr_load(2, 0);             /* 0 length reset pulses now */
 153        spin_unlock_irqrestore(&wdt_lock, flags);
 154        return 0;
 155}
 156
 157/**
 158 *      wdt_ping:
 159 *
 160 *      Reload counter one with the watchdog heartbeat. We don't bother
 161 *      reloading the cascade counter.
 162 */
 163
 164static void wdt_ping(void)
 165{
 166        unsigned long flags;
 167        spin_lock_irqsave(&wdt_lock, flags);
 168        /* Write a watchdog value */
 169        inb_p(WDT_DC);                  /* Disable watchdog */
 170        wdt_ctr_mode(1, 2);             /* Re-Program CTR1 for Mode 2:
 171                                                        Rate Generator */
 172        wdt_ctr_load(1, wd_heartbeat);  /* Heartbeat */
 173        outb_p(0, WDT_DC);              /* Enable watchdog */
 174        spin_unlock_irqrestore(&wdt_lock, flags);
 175}
 176
 177/**
 178 *      wdt_set_heartbeat:
 179 *      @t:             the new heartbeat value that needs to be set.
 180 *
 181 *      Set a new heartbeat value for the watchdog device. If the heartbeat
 182 *      value is incorrect we keep the old value and return -EINVAL. If
 183 *      successful we return 0.
 184 */
 185
 186static int wdt_set_heartbeat(int t)
 187{
 188        if (t < 1 || t > 65535)
 189                return -EINVAL;
 190
 191        heartbeat = t;
 192        wd_heartbeat = t * 100;
 193        return 0;
 194}
 195
 196/**
 197 *      wdt_get_status:
 198 *
 199 *      Extract the status information from a WDT watchdog device. There are
 200 *      several board variants so we have to know which bits are valid. Some
 201 *      bits default to one and some to zero in order to be maximally painful.
 202 *
 203 *      we then map the bits onto the status ioctl flags.
 204 */
 205
 206static int wdt_get_status(void)
 207{
 208        unsigned char new_status;
 209        int status = 0;
 210        unsigned long flags;
 211
 212        spin_lock_irqsave(&wdt_lock, flags);
 213        new_status = inb_p(WDT_SR);
 214        spin_unlock_irqrestore(&wdt_lock, flags);
 215
 216        if (new_status & WDC_SR_ISOI0)
 217                status |= WDIOF_EXTERN1;
 218        if (new_status & WDC_SR_ISII1)
 219                status |= WDIOF_EXTERN2;
 220        if (type == 501) {
 221                if (!(new_status & WDC_SR_TGOOD))
 222                        status |= WDIOF_OVERHEAT;
 223                if (!(new_status & WDC_SR_PSUOVER))
 224                        status |= WDIOF_POWEROVER;
 225                if (!(new_status & WDC_SR_PSUUNDR))
 226                        status |= WDIOF_POWERUNDER;
 227                if (tachometer) {
 228                        if (!(new_status & WDC_SR_FANGOOD))
 229                                status |= WDIOF_FANFAULT;
 230                }
 231        }
 232        return status;
 233}
 234
 235/**
 236 *      wdt_get_temperature:
 237 *
 238 *      Reports the temperature in degrees Fahrenheit. The API is in
 239 *      farenheit. It was designed by an imperial measurement luddite.
 240 */
 241
 242static int wdt_get_temperature(void)
 243{
 244        unsigned short c;
 245        unsigned long flags;
 246
 247        spin_lock_irqsave(&wdt_lock, flags);
 248        c = inb_p(WDT_RT);
 249        spin_unlock_irqrestore(&wdt_lock, flags);
 250        return (c * 11 / 15) + 7;
 251}
 252
 253static void wdt_decode_501(int status)
 254{
 255        if (!(status & WDC_SR_TGOOD))
 256                pr_crit("Overheat alarm (%d)\n", inb_p(WDT_RT));
 257        if (!(status & WDC_SR_PSUOVER))
 258                pr_crit("PSU over voltage\n");
 259        if (!(status & WDC_SR_PSUUNDR))
 260                pr_crit("PSU under voltage\n");
 261}
 262
 263/**
 264 *      wdt_interrupt:
 265 *      @irq:           Interrupt number
 266 *      @dev_id:        Unused as we don't allow multiple devices.
 267 *
 268 *      Handle an interrupt from the board. These are raised when the status
 269 *      map changes in what the board considers an interesting way. That means
 270 *      a failure condition occurring.
 271 */
 272
 273static irqreturn_t wdt_interrupt(int irq, void *dev_id)
 274{
 275        /*
 276         *      Read the status register see what is up and
 277         *      then printk it.
 278         */
 279        unsigned char status;
 280
 281        spin_lock(&wdt_lock);
 282        status = inb_p(WDT_SR);
 283
 284        pr_crit("WDT status %d\n", status);
 285
 286        if (type == 501) {
 287                wdt_decode_501(status);
 288                if (tachometer) {
 289                        if (!(status & WDC_SR_FANGOOD))
 290                                pr_crit("Possible fan fault\n");
 291                }
 292        }
 293        if (!(status & WDC_SR_WCCR)) {
 294#ifdef SOFTWARE_REBOOT
 295#ifdef ONLY_TESTING
 296                pr_crit("Would Reboot\n");
 297#else
 298                pr_crit("Initiating system reboot\n");
 299                emergency_restart();
 300#endif
 301#else
 302                pr_crit("Reset in 5ms\n");
 303#endif
 304        }
 305        spin_unlock(&wdt_lock);
 306        return IRQ_HANDLED;
 307}
 308
 309
 310/**
 311 *      wdt_write:
 312 *      @file: file handle to the watchdog
 313 *      @buf: buffer to write (unused as data does not matter here
 314 *      @count: count of bytes
 315 *      @ppos: pointer to the position to write. No seeks allowed
 316 *
 317 *      A write to a watchdog device is defined as a keepalive signal. Any
 318 *      write of data will do, as we we don't define content meaning.
 319 */
 320
 321static ssize_t wdt_write(struct file *file, const char __user *buf,
 322                                                size_t count, loff_t *ppos)
 323{
 324        if (count) {
 325                if (!nowayout) {
 326                        size_t i;
 327
 328                        /* In case it was set long ago */
 329                        expect_close = 0;
 330
 331                        for (i = 0; i != count; i++) {
 332                                char c;
 333                                if (get_user(c, buf + i))
 334                                        return -EFAULT;
 335                                if (c == 'V')
 336                                        expect_close = 42;
 337                        }
 338                }
 339                wdt_ping();
 340        }
 341        return count;
 342}
 343
 344/**
 345 *      wdt_ioctl:
 346 *      @file: file handle to the device
 347 *      @cmd: watchdog command
 348 *      @arg: argument pointer
 349 *
 350 *      The watchdog API defines a common set of functions for all watchdogs
 351 *      according to their available features. We only actually usefully support
 352 *      querying capabilities and current status.
 353 */
 354
 355static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 356{
 357        void __user *argp = (void __user *)arg;
 358        int __user *p = argp;
 359        int new_heartbeat;
 360        int status;
 361
 362        struct watchdog_info ident = {
 363                .options =              WDIOF_SETTIMEOUT|
 364                                        WDIOF_MAGICCLOSE|
 365                                        WDIOF_KEEPALIVEPING,
 366                .firmware_version =     1,
 367                .identity =             "WDT500/501",
 368        };
 369
 370        /* Add options according to the card we have */
 371        ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
 372        if (type == 501) {
 373                ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
 374                                                        WDIOF_POWEROVER);
 375                if (tachometer)
 376                        ident.options |= WDIOF_FANFAULT;
 377        }
 378
 379        switch (cmd) {
 380        case WDIOC_GETSUPPORT:
 381                return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
 382        case WDIOC_GETSTATUS:
 383                status = wdt_get_status();
 384                return put_user(status, p);
 385        case WDIOC_GETBOOTSTATUS:
 386                return put_user(0, p);
 387        case WDIOC_KEEPALIVE:
 388                wdt_ping();
 389                return 0;
 390        case WDIOC_SETTIMEOUT:
 391                if (get_user(new_heartbeat, p))
 392                        return -EFAULT;
 393                if (wdt_set_heartbeat(new_heartbeat))
 394                        return -EINVAL;
 395                wdt_ping();
 396                /* Fall */
 397        case WDIOC_GETTIMEOUT:
 398                return put_user(heartbeat, p);
 399        default:
 400                return -ENOTTY;
 401        }
 402}
 403
 404/**
 405 *      wdt_open:
 406 *      @inode: inode of device
 407 *      @file: file handle to device
 408 *
 409 *      The watchdog device has been opened. The watchdog device is single
 410 *      open and on opening we load the counters. Counter zero is a 100Hz
 411 *      cascade, into counter 1 which downcounts to reboot. When the counter
 412 *      triggers counter 2 downcounts the length of the reset pulse which
 413 *      set set to be as long as possible.
 414 */
 415
 416static int wdt_open(struct inode *inode, struct file *file)
 417{
 418        if (test_and_set_bit(0, &wdt_is_open))
 419                return -EBUSY;
 420        /*
 421         *      Activate
 422         */
 423        wdt_start();
 424        return nonseekable_open(inode, file);
 425}
 426
 427/**
 428 *      wdt_release:
 429 *      @inode: inode to board
 430 *      @file: file handle to board
 431 *
 432 *      The watchdog has a configurable API. There is a religious dispute
 433 *      between people who want their watchdog to be able to shut down and
 434 *      those who want to be sure if the watchdog manager dies the machine
 435 *      reboots. In the former case we disable the counters, in the latter
 436 *      case you have to open it again very soon.
 437 */
 438
 439static int wdt_release(struct inode *inode, struct file *file)
 440{
 441        if (expect_close == 42) {
 442                wdt_stop();
 443                clear_bit(0, &wdt_is_open);
 444        } else {
 445                pr_crit("WDT device closed unexpectedly.  WDT will not stop!\n");
 446                wdt_ping();
 447        }
 448        expect_close = 0;
 449        return 0;
 450}
 451
 452/**
 453 *      wdt_temp_read:
 454 *      @file: file handle to the watchdog board
 455 *      @buf: buffer to write 1 byte into
 456 *      @count: length of buffer
 457 *      @ptr: offset (no seek allowed)
 458 *
 459 *      Temp_read reports the temperature in degrees Fahrenheit. The API is in
 460 *      farenheit. It was designed by an imperial measurement luddite.
 461 */
 462
 463static ssize_t wdt_temp_read(struct file *file, char __user *buf,
 464                                                size_t count, loff_t *ptr)
 465{
 466        int temperature = wdt_get_temperature();
 467
 468        if (copy_to_user(buf, &temperature, 1))
 469                return -EFAULT;
 470
 471        return 1;
 472}
 473
 474/**
 475 *      wdt_temp_open:
 476 *      @inode: inode of device
 477 *      @file: file handle to device
 478 *
 479 *      The temperature device has been opened.
 480 */
 481
 482static int wdt_temp_open(struct inode *inode, struct file *file)
 483{
 484        return nonseekable_open(inode, file);
 485}
 486
 487/**
 488 *      wdt_temp_release:
 489 *      @inode: inode to board
 490 *      @file: file handle to board
 491 *
 492 *      The temperature device has been closed.
 493 */
 494
 495static int wdt_temp_release(struct inode *inode, struct file *file)
 496{
 497        return 0;
 498}
 499
 500/**
 501 *      notify_sys:
 502 *      @this: our notifier block
 503 *      @code: the event being reported
 504 *      @unused: unused
 505 *
 506 *      Our notifier is called on system shutdowns. We want to turn the card
 507 *      off at reboot otherwise the machine will reboot again during memory
 508 *      test or worse yet during the following fsck. This would suck, in fact
 509 *      trust me - if it happens it does suck.
 510 */
 511
 512static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
 513        void *unused)
 514{
 515        if (code == SYS_DOWN || code == SYS_HALT)
 516                wdt_stop();
 517        return NOTIFY_DONE;
 518}
 519
 520/*
 521 *      Kernel Interfaces
 522 */
 523
 524
 525static const struct file_operations wdt_fops = {
 526        .owner          = THIS_MODULE,
 527        .llseek         = no_llseek,
 528        .write          = wdt_write,
 529        .unlocked_ioctl = wdt_ioctl,
 530        .open           = wdt_open,
 531        .release        = wdt_release,
 532};
 533
 534static struct miscdevice wdt_miscdev = {
 535        .minor  = WATCHDOG_MINOR,
 536        .name   = "watchdog",
 537        .fops   = &wdt_fops,
 538};
 539
 540static const struct file_operations wdt_temp_fops = {
 541        .owner          = THIS_MODULE,
 542        .llseek         = no_llseek,
 543        .read           = wdt_temp_read,
 544        .open           = wdt_temp_open,
 545        .release        = wdt_temp_release,
 546};
 547
 548static struct miscdevice temp_miscdev = {
 549        .minor  = TEMP_MINOR,
 550        .name   = "temperature",
 551        .fops   = &wdt_temp_fops,
 552};
 553
 554/*
 555 *      The WDT card needs to learn about soft shutdowns in order to
 556 *      turn the timebomb registers off.
 557 */
 558
 559static struct notifier_block wdt_notifier = {
 560        .notifier_call = wdt_notify_sys,
 561};
 562
 563/**
 564 *      cleanup_module:
 565 *
 566 *      Unload the watchdog. You cannot do this with any file handles open.
 567 *      If your watchdog is set to continue ticking on close and you unload
 568 *      it, well it keeps ticking. We won't get the interrupt but the board
 569 *      will not touch PC memory so all is fine. You just have to load a new
 570 *      module in 60 seconds or reboot.
 571 */
 572
 573static void __exit wdt_exit(void)
 574{
 575        misc_deregister(&wdt_miscdev);
 576        if (type == 501)
 577                misc_deregister(&temp_miscdev);
 578        unregister_reboot_notifier(&wdt_notifier);
 579        free_irq(irq, NULL);
 580        release_region(io, 8);
 581}
 582
 583/**
 584 *      wdt_init:
 585 *
 586 *      Set up the WDT watchdog board. All we have to do is grab the
 587 *      resources we require and bitch if anyone beat us to them.
 588 *      The open() function will actually kick the board off.
 589 */
 590
 591static int __init wdt_init(void)
 592{
 593        int ret;
 594
 595        if (type != 500 && type != 501) {
 596                pr_err("unknown card type '%d'\n", type);
 597                return -ENODEV;
 598        }
 599
 600        /* Check that the heartbeat value is within it's range;
 601           if not reset to the default */
 602        if (wdt_set_heartbeat(heartbeat)) {
 603                wdt_set_heartbeat(WD_TIMO);
 604                pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n",
 605                        WD_TIMO);
 606        }
 607
 608        if (!request_region(io, 8, "wdt501p")) {
 609                pr_err("I/O address 0x%04x already in use\n", io);
 610                ret = -EBUSY;
 611                goto out;
 612        }
 613
 614        ret = request_irq(irq, wdt_interrupt, 0, "wdt501p", NULL);
 615        if (ret) {
 616                pr_err("IRQ %d is not free\n", irq);
 617                goto outreg;
 618        }
 619
 620        ret = register_reboot_notifier(&wdt_notifier);
 621        if (ret) {
 622                pr_err("cannot register reboot notifier (err=%d)\n", ret);
 623                goto outirq;
 624        }
 625
 626        if (type == 501) {
 627                ret = misc_register(&temp_miscdev);
 628                if (ret) {
 629                        pr_err("cannot register miscdev on minor=%d (err=%d)\n",
 630                               TEMP_MINOR, ret);
 631                        goto outrbt;
 632                }
 633        }
 634
 635        ret = misc_register(&wdt_miscdev);
 636        if (ret) {
 637                pr_err("cannot register miscdev on minor=%d (err=%d)\n",
 638                       WATCHDOG_MINOR, ret);
 639                goto outmisc;
 640        }
 641
 642        pr_info("WDT500/501-P driver 0.10 at 0x%04x (Interrupt %d). heartbeat=%d sec (nowayout=%d)\n",
 643                io, irq, heartbeat, nowayout);
 644        if (type == 501)
 645                pr_info("Fan Tachometer is %s\n",
 646                        tachometer ? "Enabled" : "Disabled");
 647        return 0;
 648
 649outmisc:
 650        if (type == 501)
 651                misc_deregister(&temp_miscdev);
 652outrbt:
 653        unregister_reboot_notifier(&wdt_notifier);
 654outirq:
 655        free_irq(irq, NULL);
 656outreg:
 657        release_region(io, 8);
 658out:
 659        return ret;
 660}
 661
 662module_init(wdt_init);
 663module_exit(wdt_exit);
 664
 665MODULE_AUTHOR("Alan Cox");
 666MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");
 667MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 668MODULE_ALIAS_MISCDEV(TEMP_MINOR);
 669MODULE_LICENSE("GPL");
 670
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.