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ádraig 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/dhg 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 || c == 0xa0) {    /* W83627EHF / W83627DHG */
  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        t |= 0x02;                /* enable the WDTO# output low pulse
 133                                    to the KBRST# pin (PIN60) */
 134        outb_p(t, WDT_EFDR);    /* Write back to CRF5 */
 135
 136        outb_p(0xF7, WDT_EFER); /* Select CRF7 */
 137        t = inb_p(WDT_EFDR);      /* read CRF7 */
 138        t &= ~0xC0;               /* disable keyboard & mouse turning off
 139                                    watchdog */
 140        outb_p(t, WDT_EFDR);    /* Write back to CRF7 */
 141
 142        w83627hf_unselect_wd_register();
 143}
 144
 145static void wdt_set_time(int timeout)
 146{
 147        spin_lock(&io_lock);
 148
 149        w83627hf_select_wd_register();
 150
 151        outb_p(0xF6, WDT_EFER);    /* Select CRF6 */
 152        outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */
 153
 154        w83627hf_unselect_wd_register();
 155
 156        spin_unlock(&io_lock);
 157}
 158
 159static int wdt_ping(void)
 160{
 161        wdt_set_time(timeout);
 162        return 0;
 163}
 164
 165static int wdt_disable(void)
 166{
 167        wdt_set_time(0);
 168        return 0;
 169}
 170
 171static int wdt_set_heartbeat(int t)
 172{
 173        if (t < 1 || t > 255)
 174                return -EINVAL;
 175        timeout = t;
 176        return 0;
 177}
 178
 179static int wdt_get_time(void)
 180{
 181        int timeleft;
 182
 183        spin_lock(&io_lock);
 184
 185        w83627hf_select_wd_register();
 186
 187        outb_p(0xF6, WDT_EFER);    /* Select CRF6 */
 188        timeleft = inb_p(WDT_EFDR); /* Read Timeout counter to CRF6 */
 189
 190        w83627hf_unselect_wd_register();
 191
 192        spin_unlock(&io_lock);
 193
 194        return timeleft;
 195}
 196
 197static ssize_t wdt_write(struct file *file, const char __user *buf,
 198                                                size_t count, loff_t *ppos)
 199{
 200        if (count) {
 201                if (!nowayout) {
 202                        size_t i;
 203
 204                        expect_close = 0;
 205
 206                        for (i = 0; i != count; i++) {
 207                                char c;
 208                                if (get_user(c, buf + i))
 209                                        return -EFAULT;
 210                                if (c == 'V')
 211                                        expect_close = 42;
 212                        }
 213                }
 214                wdt_ping();
 215        }
 216        return count;
 217}
 218
 219static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 220{
 221        void __user *argp = (void __user *)arg;
 222        int __user *p = argp;
 223        int timeval;
 224        static const struct watchdog_info ident = {
 225                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
 226                                                        WDIOF_MAGICCLOSE,
 227                .firmware_version = 1,
 228                .identity = "W83627HF WDT",
 229        };
 230
 231        switch (cmd) {
 232        case WDIOC_GETSUPPORT:
 233                if (copy_to_user(argp, &ident, sizeof(ident)))
 234                        return -EFAULT;
 235                break;
 236        case WDIOC_GETSTATUS:
 237        case WDIOC_GETBOOTSTATUS:
 238                return put_user(0, p);
 239        case WDIOC_SETOPTIONS:
 240        {
 241                int options, retval = -EINVAL;
 242
 243                if (get_user(options, p))
 244                        return -EFAULT;
 245                if (options & WDIOS_DISABLECARD) {
 246                        wdt_disable();
 247                        retval = 0;
 248                }
 249                if (options & WDIOS_ENABLECARD) {
 250                        wdt_ping();
 251                        retval = 0;
 252                }
 253                return retval;
 254        }
 255        case WDIOC_KEEPALIVE:
 256                wdt_ping();
 257                break;
 258        case WDIOC_SETTIMEOUT:
 259                if (get_user(timeval, p))
 260                        return -EFAULT;
 261                if (wdt_set_heartbeat(timeval))
 262                        return -EINVAL;
 263                wdt_ping();
 264                /* Fall */
 265        case WDIOC_GETTIMEOUT:
 266                return put_user(timeout, p);
 267        case WDIOC_GETTIMELEFT:
 268                timeval = wdt_get_time();
 269                return put_user(timeval, p);
 270        default:
 271                return -ENOTTY;
 272        }
 273        return 0;
 274}
 275
 276static int wdt_open(struct inode *inode, struct file *file)
 277{
 278        if (test_and_set_bit(0, &wdt_is_open))
 279                return -EBUSY;
 280        /*
 281         *      Activate
 282         */
 283
 284        wdt_ping();
 285        return nonseekable_open(inode, file);
 286}
 287
 288static int wdt_close(struct inode *inode, struct file *file)
 289{
 290        if (expect_close == 42)
 291                wdt_disable();
 292        else {
 293                printk(KERN_CRIT PFX
 294                        "Unexpected close, not stopping watchdog!\n");
 295                wdt_ping();
 296        }
 297        expect_close = 0;
 298        clear_bit(0, &wdt_is_open);
 299        return 0;
 300}
 301
 302/*
 303 *      Notifier for system down
 304 */
 305
 306static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
 307        void *unused)
 308{
 309        if (code == SYS_DOWN || code == SYS_HALT)
 310                wdt_disable();  /* Turn the WDT off */
 311
 312        return NOTIFY_DONE;
 313}
 314
 315/*
 316 *      Kernel Interfaces
 317 */
 318
 319static const struct file_operations wdt_fops = {
 320        .owner          = THIS_MODULE,
 321        .llseek         = no_llseek,
 322        .write          = wdt_write,
 323        .unlocked_ioctl = wdt_ioctl,
 324        .open           = wdt_open,
 325        .release        = wdt_close,
 326};
 327
 328static struct miscdevice wdt_miscdev = {
 329        .minor = WATCHDOG_MINOR,
 330        .name = "watchdog",
 331        .fops = &wdt_fops,
 332};
 333
 334/*
 335 *      The WDT needs to learn about soft shutdowns in order to
 336 *      turn the timebomb registers off.
 337 */
 338
 339static struct notifier_block wdt_notifier = {
 340        .notifier_call = wdt_notify_sys,
 341};
 342
 343static int __init wdt_init(void)
 344{
 345        int ret;
 346
 347        printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising.\n");
 348
 349        if (wdt_set_heartbeat(timeout)) {
 350                wdt_set_heartbeat(WATCHDOG_TIMEOUT);
 351                printk(KERN_INFO PFX
 352                     "timeout value must be 1 <= timeout <= 255, using %d\n",
 353                                WATCHDOG_TIMEOUT);
 354        }
 355
 356        if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
 357                printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
 358                        wdt_io);
 359                ret = -EIO;
 360                goto out;
 361        }
 362
 363        w83627hf_init();
 364
 365        ret = register_reboot_notifier(&wdt_notifier);
 366        if (ret != 0) {
 367                printk(KERN_ERR PFX
 368                        "cannot register reboot notifier (err=%d)\n", ret);
 369                goto unreg_regions;
 370        }
 371
 372        ret = misc_register(&wdt_miscdev);
 373        if (ret != 0) {
 374                printk(KERN_ERR PFX
 375                        "cannot register miscdev on minor=%d (err=%d)\n",
 376                                                        WATCHDOG_MINOR, ret);
 377                goto unreg_reboot;
 378        }
 379
 380        printk(KERN_INFO PFX
 381                        "initialized. timeout=%d sec (nowayout=%d)\n",
 382                                                        timeout, nowayout);
 383
 384out:
 385        return ret;
 386unreg_reboot:
 387        unregister_reboot_notifier(&wdt_notifier);
 388unreg_regions:
 389        release_region(wdt_io, 1);
 390        goto out;
 391}
 392
 393static void __exit wdt_exit(void)
 394{
 395        misc_deregister(&wdt_miscdev);
 396        unregister_reboot_notifier(&wdt_notifier);
 397        release_region(wdt_io, 1);
 398}
 399
 400module_init(wdt_init);
 401module_exit(wdt_exit);
 402
 403MODULE_LICENSE("GPL");
 404MODULE_AUTHOR("Pádraig  Brady <P@draigBrady.com>");
 405MODULE_DESCRIPTION("w83627hf/thf WDT driver");
 406MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 407
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.