linux/drivers/watchdog/w83627hf_wdt.c
<<
>>
Prefs
   1/*
   2 *      w83627hf/thf WDT driver
   3 *
   4 *      (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
   5 *              added support for W83627THF.
   6 *
   7 *      (c) Copyright 2003,2007 P\xE1draig Brady <P@draigBrady.com>
   8 *
   9 *      Based on advantechwdt.c which is based on wdt.c.
  10 *      Original copyright messages:
  11 *
  12 *      (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
  13 *
  14 *      (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
  15 *                                              All Rights Reserved.
  16 *
  17 *      This program is free software; you can redistribute it and/or
  18 *      modify it under the terms of the GNU General Public License
  19 *      as published by the Free Software Foundation; either version
  20 *      2 of the License, or (at your option) any later version.
  21 *
  22 *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
  23 *      warranty for any of this software. This material is provided
  24 *      "AS-IS" and at no charge.
  25 *
  26 *      (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
  27 */
  28
  29#include <linux/module.h>
  30#include <linux/moduleparam.h>
  31#include <linux/types.h>
  32#include <linux/miscdevice.h>
  33#include <linux/watchdog.h>
  34#include <linux/fs.h>
  35#include <linux/ioport.h>
  36#include <linux/notifier.h>
  37#include <linux/reboot.h>
  38#include <linux/init.h>
  39#include <linux/spinlock.h>
  40#include <linux/io.h>
  41#include <linux/uaccess.h>
  42
  43#include <asm/system.h>
  44
  45#define WATCHDOG_NAME "w83627hf/thf/hg WDT"
  46#define PFX WATCHDOG_NAME ": "
  47#define WATCHDOG_TIMEOUT 60             /* 60 sec default timeout */
  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, "w83627hf/thf WDT io port (default 0x2E)");
  57
  58static int timeout = WATCHDOG_TIMEOUT;  /* in seconds */
  59module_param(timeout, int, 0);
  60MODULE_PARM_DESC(timeout,
  61                "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
  62                                __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
  63
  64static int nowayout = WATCHDOG_NOWAYOUT;
  65module_param(nowayout, int, 0);
  66MODULE_PARM_DESC(nowayout,
  67                "Watchdog cannot be stopped once started (default="
  68                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  69
  70/*
  71 *      Kernel methods.
  72 */
  73
  74#define WDT_EFER (wdt_io+0)   /* Extended Function Enable Registers */
  75#define WDT_EFIR (wdt_io+0)   /* Extended Function Index Register
  76                                                        (same as EFER) */
  77#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
  78
  79static void w83627hf_select_wd_register(void)
  80{
  81        unsigned char c;
  82        outb_p(0x87, WDT_EFER); /* Enter extended function mode */
  83        outb_p(0x87, WDT_EFER); /* Again according to manual */
  84
  85        outb(0x20, WDT_EFER);   /* check chip version   */
  86        c = inb(WDT_EFDR);
  87        if (c == 0x82) {        /* W83627THF            */
  88                outb_p(0x2b, WDT_EFER); /* select GPIO3 */
  89                c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */
  90                outb_p(0x2b, WDT_EFER);
  91                outb_p(c, WDT_EFDR);    /* set GPIO3 to WDT0 */
  92        } else if (c == 0x88) { /* W83627EHF */
  93                outb_p(0x2d, WDT_EFER); /* select GPIO5 */
  94                c = inb_p(WDT_EFDR) & ~0x01; /* PIN77 -> WDT0# */
  95                outb_p(0x2d, WDT_EFER);
  96                outb_p(c, WDT_EFDR); /* set GPIO5 to WDT0 */
  97        }
  98
  99        outb_p(0x07, WDT_EFER); /* point to logical device number reg */
 100        outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
 101        outb_p(0x30, WDT_EFER); /* select CR30 */
 102        outb_p(0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
 103}
 104
 105static void w83627hf_unselect_wd_register(void)
 106{
 107        outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
 108}
 109
 110/* tyan motherboards seem to set F5 to 0x4C ?
 111 * So explicitly init to appropriate value. */
 112
 113static void w83627hf_init(void)
 114{
 115        unsigned char t;
 116
 117        w83627hf_select_wd_register();
 118
 119        outb_p(0xF6, WDT_EFER); /* Select CRF6 */
 120        t = inb_p(WDT_EFDR);      /* read CRF6 */
 121        if (t != 0) {
 122                printk(KERN_INFO PFX
 123                     "Watchdog already running. Resetting timeout to %d sec\n",
 124                                                                timeout);
 125                outb_p(timeout, WDT_EFDR);    /* Write back to CRF6 */
 126        }
 127
 128        outb_p(0xF5, WDT_EFER); /* Select CRF5 */
 129        t = inb_p(WDT_EFDR);      /* read CRF5 */
 130        t &= ~0x0C;               /* set second mode & disable keyboard
 131                                    turning off watchdog */
 132        outb_p(t, WDT_EFDR);    /* Write back to CRF5 */
 133
 134        outb_p(0xF7, WDT_EFER); /* Select CRF7 */
 135        t = inb_p(WDT_EFDR);      /* read CRF7 */
 136        t &= ~0xC0;               /* disable keyboard & mouse turning off
 137                                    watchdog */
 138        outb_p(t, WDT_EFDR);    /* Write back to CRF7 */
 139
 140        w83627hf_unselect_wd_register();
 141}
 142
 143static void wdt_ctrl(int timeout)
 144{
 145        spin_lock(&io_lock);
 146
 147        w83627hf_select_wd_register();
 148
 149        outb_p(0xF6, WDT_EFER);    /* Select CRF6 */
 150        outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */
 151
 152        w83627hf_unselect_wd_register();
 153
 154        spin_unlock(&io_lock);
 155}
 156
 157static int wdt_ping(void)
 158{
 159        wdt_ctrl(timeout);
 160        return 0;
 161}
 162
 163static int wdt_disable(void)
 164{
 165        wdt_ctrl(0);
 166        return 0;
 167}
 168
 169static int wdt_set_heartbeat(int t)
 170{
 171        if (t < 1 || t > 255)
 172                return -EINVAL;
 173        timeout = t;
 174        return 0;
 175}
 176
 177static ssize_t wdt_write(struct file *file, const char __user *buf,
 178                                                size_t count, loff_t *ppos)
 179{
 180        if (count) {
 181                if (!nowayout) {
 182                        size_t i;
 183
 184                        expect_close = 0;
 185
 186                        for (i = 0; i != count; i++) {
 187                                char c;
 188                                if (get_user(c, buf + i))
 189                                        return -EFAULT;
 190                                if (c == 'V')
 191                                        expect_close = 42;
 192                        }
 193                }
 194                wdt_ping();
 195        }
 196        return count;
 197}
 198
 199static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 200{
 201        void __user *argp = (void __user *)arg;
 202        int __user *p = argp;
 203        int new_timeout;
 204        static const struct watchdog_info ident = {
 205                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
 206                                                        WDIOF_MAGICCLOSE,
 207                .firmware_version = 1,
 208                .identity = "W83627HF WDT",
 209        };
 210
 211        switch (cmd) {
 212        case WDIOC_GETSUPPORT:
 213                if (copy_to_user(argp, &ident, sizeof(ident)))
 214                        return -EFAULT;
 215                break;
 216        case WDIOC_GETSTATUS:
 217        case WDIOC_GETBOOTSTATUS:
 218                return put_user(0, p);
 219        case WDIOC_SETOPTIONS:
 220        {
 221                int options, retval = -EINVAL;
 222
 223                if (get_user(options, p))
 224                        return -EFAULT;
 225                if (options & WDIOS_DISABLECARD) {
 226                        wdt_disable();
 227                        retval = 0;
 228                }
 229                if (options & WDIOS_ENABLECARD) {
 230                        wdt_ping();
 231                        retval = 0;
 232                }
 233                return retval;
 234        }
 235        case WDIOC_KEEPALIVE:
 236                wdt_ping();
 237                break;
 238        case WDIOC_SETTIMEOUT:
 239                if (get_user(new_timeout, p))
 240                        return -EFAULT;
 241                if (wdt_set_heartbeat(new_timeout))
 242                        return -EINVAL;
 243                wdt_ping();
 244                /* Fall */
 245        case WDIOC_GETTIMEOUT:
 246                return put_user(timeout, p);
 247        default:
 248                return -ENOTTY;
 249        }
 250        return 0;
 251}
 252
 253static int wdt_open(struct inode *inode, struct file *file)
 254{
 255        if (test_and_set_bit(0, &wdt_is_open))
 256                return -EBUSY;
 257        /*
 258         *      Activate
 259         */
 260
 261        wdt_ping();
 262        return nonseekable_open(inode, file);
 263}
 264
 265static int wdt_close(struct inode *inode, struct file *file)
 266{
 267        if (expect_close == 42)
 268                wdt_disable();
 269        else {
 270                printk(KERN_CRIT PFX
 271                        "Unexpected close, not stopping watchdog!\n");
 272                wdt_ping();
 273        }
 274        expect_close = 0;
 275        clear_bit(0, &wdt_is_open);
 276        return 0;
 277}
 278
 279/*
 280 *      Notifier for system down
 281 */
 282
 283static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
 284        void *unused)
 285{
 286        if (code == SYS_DOWN || code == SYS_HALT)
 287                wdt_disable();  /* Turn the WDT off */
 288
 289        return NOTIFY_DONE;
 290}
 291
 292/*
 293 *      Kernel Interfaces
 294 */
 295
 296static const struct file_operations wdt_fops = {
 297        .owner          = THIS_MODULE,
 298        .llseek         = no_llseek,
 299        .write          = wdt_write,
 300        .unlocked_ioctl = wdt_ioctl,
 301        .open           = wdt_open,
 302        .release        = wdt_close,
 303};
 304
 305static struct miscdevice wdt_miscdev = {
 306        .minor = WATCHDOG_MINOR,
 307        .name = "watchdog",
 308        .fops = &wdt_fops,
 309};
 310
 311/*
 312 *      The WDT needs to learn about soft shutdowns in order to
 313 *      turn the timebomb registers off.
 314 */
 315
 316static struct notifier_block wdt_notifier = {
 317        .notifier_call = wdt_notify_sys,
 318};
 319
 320static int __init wdt_init(void)
 321{
 322        int ret;
 323
 324        printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG Super I/O chip initialising.\n");
 325
 326        if (wdt_set_heartbeat(timeout)) {
 327                wdt_set_heartbeat(WATCHDOG_TIMEOUT);
 328                printk(KERN_INFO PFX
 329                     "timeout value must be 1 <= timeout <= 255, using %d\n",
 330                                WATCHDOG_TIMEOUT);
 331        }
 332
 333        if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
 334                printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
 335                        wdt_io);
 336                ret = -EIO;
 337                goto out;
 338        }
 339
 340        w83627hf_init();
 341
 342        ret = register_reboot_notifier(&wdt_notifier);
 343        if (ret != 0) {
 344                printk(KERN_ERR PFX
 345                        "cannot register reboot notifier (err=%d)\n", ret);
 346                goto unreg_regions;
 347        }
 348
 349        ret = misc_register(&wdt_miscdev);
 350        if (ret != 0) {
 351                printk(KERN_ERR PFX
 352                        "cannot register miscdev on minor=%d (err=%d)\n",
 353                                                        WATCHDOG_MINOR, ret);
 354                goto unreg_reboot;
 355        }
 356
 357        printk(KERN_INFO PFX
 358                        "initialized. timeout=%d sec (nowayout=%d)\n",
 359                                                        timeout, nowayout);
 360
 361out:
 362        return ret;
 363unreg_reboot:
 364        unregister_reboot_notifier(&wdt_notifier);
 365unreg_regions:
 366        release_region(wdt_io, 1);
 367        goto out;
 368}
 369
 370static void __exit wdt_exit(void)
 371{
 372        misc_deregister(&wdt_miscdev);
 373        unregister_reboot_notifier(&wdt_notifier);
 374        release_region(wdt_io, 1);
 375}
 376
 377module_init(wdt_init);
 378module_exit(wdt_exit);
 379
 380MODULE_LICENSE("GPL");
 381MODULE_AUTHOR("P\xE1draig Brady <P@draigBrady.com>");
 382MODULE_DESCRIPTION("w83627hf/thf WDT driver");
 383MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 384