linux/drivers/watchdog/w83697hf_wdt.c
<<
>>
Prefs
   1/*
   2 *      w83697hf/hg WDT driver
   3 *
   4 *      (c) Copyright 2006 Samuel Tardieu <sam@rfc1149.net>
   5 *      (c) Copyright 2006 Marcus Junker <junker@anduras.de>
   6 *
   7 *      Based on w83627hf_wdt.c which is based on advantechwdt.c
   8 *      which is based on wdt.c.
   9 *      Original copyright messages:
  10 *
  11 *      (c) Copyright 2003 P\xC3\xA1draig Brady <P@draigBrady.com>
  12 *
  13 *      (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
  14 *
  15 *      (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
  16 *                                              All Rights Reserved.
  17 *
  18 *      This program is free software; you can redistribute it and/or
  19 *      modify it under the terms of the GNU General Public License
  20 *      as published by the Free Software Foundation; either version
  21 *      2 of the License, or (at your option) any later version.
  22 *
  23 *      Neither Marcus Junker nor ANDURAS AG admit liability nor provide
  24 *      warranty for any of this software. This material is provided
  25 *      "AS-IS" and at no charge.
  26 */
  27
  28#include <linux/module.h>
  29#include <linux/moduleparam.h>
  30#include <linux/types.h>
  31#include <linux/miscdevice.h>
  32#include <linux/watchdog.h>
  33#include <linux/fs.h>
  34#include <linux/ioport.h>
  35#include <linux/notifier.h>
  36#include <linux/reboot.h>
  37#include <linux/init.h>
  38#include <linux/spinlock.h>
  39#include <linux/io.h>
  40#include <linux/uaccess.h>
  41
  42#include <asm/system.h>
  43
  44#define WATCHDOG_NAME "w83697hf/hg WDT"
  45#define PFX WATCHDOG_NAME ": "
  46#define WATCHDOG_TIMEOUT 60             /* 60 sec default timeout */
  47#define WATCHDOG_EARLY_DISABLE 1        /* Disable until userland kicks in */
  48
  49static unsigned long wdt_is_open;
  50static char expect_close;
  51static DEFINE_SPINLOCK(io_lock);
  52
  53/* You must set this - there is no sane way to probe for this board. */
  54static int wdt_io = 0x2e;
  55module_param(wdt_io, int, 0);
  56MODULE_PARM_DESC(wdt_io,
  57                "w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)");
  58
  59static int timeout = WATCHDOG_TIMEOUT;  /* in seconds */
  60module_param(timeout, int, 0);
  61MODULE_PARM_DESC(timeout,
  62        "Watchdog timeout in seconds. 1<= timeout <=255 (default="
  63                                __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
  64
  65static int nowayout = WATCHDOG_NOWAYOUT;
  66module_param(nowayout, int, 0);
  67MODULE_PARM_DESC(nowayout,
  68        "Watchdog cannot be stopped once started (default="
  69                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  70
  71static int early_disable = WATCHDOG_EARLY_DISABLE;
  72module_param(early_disable, int, 0);
  73MODULE_PARM_DESC(early_disable,
  74        "Watchdog gets disabled at boot time (default="
  75                                __MODULE_STRING(WATCHDOG_EARLY_DISABLE) ")");
  76
  77/*
  78 *      Kernel methods.
  79 */
  80
  81#define W83697HF_EFER (wdt_io + 0)  /* Extended Function Enable Register */
  82#define W83697HF_EFIR (wdt_io + 0)  /* Extended Function Index Register
  83                                                        (same as EFER) */
  84#define W83697HF_EFDR (wdt_io + 1)  /* Extended Function Data Register */
  85
  86static inline void w83697hf_unlock(void)
  87{
  88        outb_p(0x87, W83697HF_EFER);    /* Enter extended function mode */
  89        outb_p(0x87, W83697HF_EFER);    /* Again according to manual */
  90}
  91
  92static inline void w83697hf_lock(void)
  93{
  94        outb_p(0xAA, W83697HF_EFER);    /* Leave extended function mode */
  95}
  96
  97/*
  98 *      The three functions w83697hf_get_reg(), w83697hf_set_reg() and
  99 *      w83697hf_write_timeout() must be called with the device unlocked.
 100 */
 101
 102static unsigned char w83697hf_get_reg(unsigned char reg)
 103{
 104        outb_p(reg, W83697HF_EFIR);
 105        return inb_p(W83697HF_EFDR);
 106}
 107
 108static void w83697hf_set_reg(unsigned char reg, unsigned char data)
 109{
 110        outb_p(reg, W83697HF_EFIR);
 111        outb_p(data, W83697HF_EFDR);
 112}
 113
 114static void w83697hf_write_timeout(int timeout)
 115{
 116        /* Write Timeout counter to CRF4 */
 117        w83697hf_set_reg(0xF4, timeout);
 118}
 119
 120static void w83697hf_select_wdt(void)
 121{
 122        w83697hf_unlock();
 123        w83697hf_set_reg(0x07, 0x08);   /* Switch to logic device 8 (GPIO2) */
 124}
 125
 126static inline void w83697hf_deselect_wdt(void)
 127{
 128        w83697hf_lock();
 129}
 130
 131static void w83697hf_init(void)
 132{
 133        unsigned char bbuf;
 134
 135        w83697hf_select_wdt();
 136
 137        bbuf = w83697hf_get_reg(0x29);
 138        bbuf &= ~0x60;
 139        bbuf |= 0x20;
 140
 141        /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
 142        w83697hf_set_reg(0x29, bbuf);
 143
 144        bbuf = w83697hf_get_reg(0xF3);
 145        bbuf &= ~0x04;
 146        w83697hf_set_reg(0xF3, bbuf);   /* Count mode is seconds */
 147
 148        w83697hf_deselect_wdt();
 149}
 150
 151static void wdt_ping(void)
 152{
 153        spin_lock(&io_lock);
 154        w83697hf_select_wdt();
 155
 156        w83697hf_write_timeout(timeout);
 157
 158        w83697hf_deselect_wdt();
 159        spin_unlock(&io_lock);
 160}
 161
 162static void wdt_enable(void)
 163{
 164        spin_lock(&io_lock);
 165        w83697hf_select_wdt();
 166
 167        w83697hf_write_timeout(timeout);
 168        w83697hf_set_reg(0x30, 1);      /* Enable timer */
 169
 170        w83697hf_deselect_wdt();
 171        spin_unlock(&io_lock);
 172}
 173
 174static void wdt_disable(void)
 175{
 176        spin_lock(&io_lock);
 177        w83697hf_select_wdt();
 178
 179        w83697hf_set_reg(0x30, 0);      /* Disable timer */
 180        w83697hf_write_timeout(0);
 181
 182        w83697hf_deselect_wdt();
 183        spin_unlock(&io_lock);
 184}
 185
 186static unsigned char wdt_running(void)
 187{
 188        unsigned char t;
 189
 190        spin_lock(&io_lock);
 191        w83697hf_select_wdt();
 192
 193        t = w83697hf_get_reg(0xF4);     /* Read timer */
 194
 195        w83697hf_deselect_wdt();
 196        spin_unlock(&io_lock);
 197
 198        return t;
 199}
 200
 201static int wdt_set_heartbeat(int t)
 202{
 203        if (t < 1 || t > 255)
 204                return -EINVAL;
 205
 206        timeout = t;
 207        return 0;
 208}
 209
 210static ssize_t wdt_write(struct file *file, const char __user *buf,
 211                                                size_t count, loff_t *ppos)
 212{
 213        if (count) {
 214                if (!nowayout) {
 215                        size_t i;
 216
 217                        expect_close = 0;
 218
 219                        for (i = 0; i != count; i++) {
 220                                char c;
 221                                if (get_user(c, buf + i))
 222                                        return -EFAULT;
 223                                if (c == 'V')
 224                                        expect_close = 42;
 225                        }
 226                }
 227                wdt_ping();
 228        }
 229        return count;
 230}
 231
 232static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 233{
 234        void __user *argp = (void __user *)arg;
 235        int __user *p = argp;
 236        int new_timeout;
 237        static const struct watchdog_info ident = {
 238                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
 239                                                        | WDIOF_MAGICCLOSE,
 240                .firmware_version = 1,
 241                .identity = "W83697HF WDT",
 242        };
 243
 244        switch (cmd) {
 245        case WDIOC_GETSUPPORT:
 246                if (copy_to_user(argp, &ident, sizeof(ident)))
 247                        return -EFAULT;
 248                break;
 249
 250        case WDIOC_GETSTATUS:
 251        case WDIOC_GETBOOTSTATUS:
 252                return put_user(0, p);
 253
 254        case WDIOC_SETOPTIONS:
 255        {
 256                int options, retval = -EINVAL;
 257
 258                if (get_user(options, p))
 259                        return -EFAULT;
 260
 261                if (options & WDIOS_DISABLECARD) {
 262                        wdt_disable();
 263                        retval = 0;
 264                }
 265
 266                if (options & WDIOS_ENABLECARD) {
 267                        wdt_enable();
 268                        retval = 0;
 269                }
 270
 271                return retval;
 272        }
 273
 274        case WDIOC_KEEPALIVE:
 275                wdt_ping();
 276                break;
 277
 278        case WDIOC_SETTIMEOUT:
 279                if (get_user(new_timeout, p))
 280                        return -EFAULT;
 281                if (wdt_set_heartbeat(new_timeout))
 282                        return -EINVAL;
 283                wdt_ping();
 284                /* Fall */
 285
 286        case WDIOC_GETTIMEOUT:
 287                return put_user(timeout, p);
 288
 289        default:
 290                return -ENOTTY;
 291        }
 292        return 0;
 293}
 294
 295static int wdt_open(struct inode *inode, struct file *file)
 296{
 297        if (test_and_set_bit(0, &wdt_is_open))
 298                return -EBUSY;
 299        /*
 300         *      Activate
 301         */
 302
 303        wdt_enable();
 304        return nonseekable_open(inode, file);
 305}
 306
 307static int wdt_close(struct inode *inode, struct file *file)
 308{
 309        if (expect_close == 42)
 310                wdt_disable();
 311        else {
 312                printk(KERN_CRIT PFX
 313                        "Unexpected close, not stopping watchdog!\n");
 314                wdt_ping();
 315        }
 316        expect_close = 0;
 317        clear_bit(0, &wdt_is_open);
 318        return 0;
 319}
 320
 321/*
 322 *      Notifier for system down
 323 */
 324
 325static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
 326        void *unused)
 327{
 328        if (code == SYS_DOWN || code == SYS_HALT)
 329                wdt_disable();  /* Turn the WDT off */
 330
 331        return NOTIFY_DONE;
 332}
 333
 334/*
 335 *      Kernel Interfaces
 336 */
 337
 338static const struct file_operations wdt_fops = {
 339        .owner          = THIS_MODULE,
 340        .llseek         = no_llseek,
 341        .write          = wdt_write,
 342        .unlocked_ioctl = wdt_ioctl,
 343        .open           = wdt_open,
 344        .release        = wdt_close,
 345};
 346
 347static struct miscdevice wdt_miscdev = {
 348        .minor = WATCHDOG_MINOR,
 349        .name = "watchdog",
 350        .fops = &wdt_fops,
 351};
 352
 353/*
 354 *      The WDT needs to learn about soft shutdowns in order to
 355 *      turn the timebomb registers off.
 356 */
 357
 358static struct notifier_block wdt_notifier = {
 359        .notifier_call = wdt_notify_sys,
 360};
 361
 362static int w83697hf_check_wdt(void)
 363{
 364        if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
 365                printk(KERN_ERR PFX
 366                        "I/O address 0x%x already in use\n", wdt_io);
 367                return -EIO;
 368        }
 369
 370        printk(KERN_DEBUG PFX
 371                        "Looking for watchdog at address 0x%x\n", wdt_io);
 372        w83697hf_unlock();
 373        if (w83697hf_get_reg(0x20) == 0x60) {
 374                printk(KERN_INFO PFX
 375                        "watchdog found at address 0x%x\n", wdt_io);
 376                w83697hf_lock();
 377                return 0;
 378        }
 379        /* Reprotect in case it was a compatible device */
 380        w83697hf_lock();
 381
 382        printk(KERN_INFO PFX "watchdog not found at address 0x%x\n", wdt_io);
 383        release_region(wdt_io, 2);
 384        return -EIO;
 385}
 386
 387static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 };
 388
 389static int __init wdt_init(void)
 390{
 391        int ret, i, found = 0;
 392
 393        printk(KERN_INFO PFX "WDT driver for W83697HF/HG initializing\n");
 394
 395        if (wdt_io == 0) {
 396                /* we will autodetect the W83697HF/HG watchdog */
 397                for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) {
 398                        wdt_io = w83697hf_ioports[i];
 399                        if (!w83697hf_check_wdt())
 400                                found++;
 401                }
 402        } else {
 403                if (!w83697hf_check_wdt())
 404                        found++;
 405        }
 406
 407        if (!found) {
 408                printk(KERN_ERR PFX "No W83697HF/HG could be found\n");
 409                ret = -EIO;
 410                goto out;
 411        }
 412
 413        w83697hf_init();
 414        if (early_disable) {
 415                if (wdt_running())
 416                        printk(KERN_WARNING PFX "Stopping previously enabled watchdog until userland kicks in\n");
 417                wdt_disable();
 418        }
 419
 420        if (wdt_set_heartbeat(timeout)) {
 421                wdt_set_heartbeat(WATCHDOG_TIMEOUT);
 422                printk(KERN_INFO PFX
 423                     "timeout value must be 1 <= timeout <= 255, using %d\n",
 424                                                        WATCHDOG_TIMEOUT);
 425        }
 426
 427        ret = register_reboot_notifier(&wdt_notifier);
 428        if (ret != 0) {
 429                printk(KERN_ERR PFX
 430                        "cannot register reboot notifier (err=%d)\n", ret);
 431                goto unreg_regions;
 432        }
 433
 434        ret = misc_register(&wdt_miscdev);
 435        if (ret != 0) {
 436                printk(KERN_ERR PFX
 437                        "cannot register miscdev on minor=%d (err=%d)\n",
 438                                                WATCHDOG_MINOR, ret);
 439                goto unreg_reboot;
 440        }
 441
 442        printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
 443                timeout, nowayout);
 444
 445out:
 446        return ret;
 447unreg_reboot:
 448        unregister_reboot_notifier(&wdt_notifier);
 449unreg_regions:
 450        release_region(wdt_io, 2);
 451        goto out;
 452}
 453
 454static void __exit wdt_exit(void)
 455{
 456        misc_deregister(&wdt_miscdev);
 457        unregister_reboot_notifier(&wdt_notifier);
 458        release_region(wdt_io, 2);
 459}
 460
 461module_init(wdt_init);
 462module_exit(wdt_exit);
 463
 464MODULE_LICENSE("GPL");
 465MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, Samuel Tardieu <sam@rfc1149.net>");
 466MODULE_DESCRIPTION("w83697hf/hg WDT driver");
 467MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 468