linux/drivers/watchdog/alim1535_wdt.c
<<
>>
Prefs
   1/*
   2 *      Watchdog for the 7101 PMU version found in the ALi M1535 chipsets
   3 *
   4 *      This program is free software; you can redistribute it and/or
   5 *      modify it under the terms of the GNU General Public License
   6 *      as published by the Free Software Foundation; either version
   7 *      2 of the License, or (at your option) any later version.
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/moduleparam.h>
  12#include <linux/types.h>
  13#include <linux/miscdevice.h>
  14#include <linux/watchdog.h>
  15#include <linux/ioport.h>
  16#include <linux/notifier.h>
  17#include <linux/reboot.h>
  18#include <linux/init.h>
  19#include <linux/fs.h>
  20#include <linux/pci.h>
  21#include <linux/uaccess.h>
  22#include <linux/io.h>
  23
  24#define WATCHDOG_NAME "ALi_M1535"
  25#define PFX WATCHDOG_NAME ": "
  26#define WATCHDOG_TIMEOUT 60     /* 60 sec default timeout */
  27
  28/* internal variables */
  29static unsigned long ali_is_open;
  30static char ali_expect_release;
  31static struct pci_dev *ali_pci;
  32static u32 ali_timeout_bits;            /* stores the computed timeout */
  33static DEFINE_SPINLOCK(ali_lock);       /* Guards the hardware */
  34
  35/* module parameters */
  36static int timeout = WATCHDOG_TIMEOUT;
  37module_param(timeout, int, 0);
  38MODULE_PARM_DESC(timeout,
  39                "Watchdog timeout in seconds. (0 < timeout < 18000, default="
  40                                __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
  41
  42static int nowayout = WATCHDOG_NOWAYOUT;
  43module_param(nowayout, int, 0);
  44MODULE_PARM_DESC(nowayout,
  45                "Watchdog cannot be stopped once started (default="
  46                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  47
  48/*
  49 *      ali_start       -       start watchdog countdown
  50 *
  51 *      Starts the timer running providing the timer has a counter
  52 *      configuration set.
  53 */
  54
  55static void ali_start(void)
  56{
  57        u32 val;
  58
  59        spin_lock(&ali_lock);
  60
  61        pci_read_config_dword(ali_pci, 0xCC, &val);
  62        val &= ~0x3F;   /* Mask count */
  63        val |= (1 << 25) | ali_timeout_bits;
  64        pci_write_config_dword(ali_pci, 0xCC, val);
  65
  66        spin_unlock(&ali_lock);
  67}
  68
  69/*
  70 *      ali_stop        -       stop the timer countdown
  71 *
  72 *      Stop the ALi watchdog countdown
  73 */
  74
  75static void ali_stop(void)
  76{
  77        u32 val;
  78
  79        spin_lock(&ali_lock);
  80
  81        pci_read_config_dword(ali_pci, 0xCC, &val);
  82        val &= ~0x3F;           /* Mask count to zero (disabled) */
  83        val &= ~(1 << 25);      /* and for safety mask the reset enable */
  84        pci_write_config_dword(ali_pci, 0xCC, val);
  85
  86        spin_unlock(&ali_lock);
  87}
  88
  89/*
  90 *      ali_keepalive   -       send a keepalive to the watchdog
  91 *
  92 *      Send a keepalive to the timer (actually we restart the timer).
  93 */
  94
  95static void ali_keepalive(void)
  96{
  97        ali_start();
  98}
  99
 100/*
 101 *      ali_settimer    -       compute the timer reload value
 102 *      @t: time in seconds
 103 *
 104 *      Computes the timeout values needed
 105 */
 106
 107static int ali_settimer(int t)
 108{
 109        if (t < 0)
 110                return -EINVAL;
 111        else if (t < 60)
 112                ali_timeout_bits = t|(1 << 6);
 113        else if (t < 3600)
 114                ali_timeout_bits = (t / 60)|(1 << 7);
 115        else if (t < 18000)
 116                ali_timeout_bits = (t / 300)|(1 << 6)|(1 << 7);
 117        else
 118                return -EINVAL;
 119
 120        timeout = t;
 121        return 0;
 122}
 123
 124/*
 125 *      /dev/watchdog handling
 126 */
 127
 128/*
 129 *      ali_write       -       writes to ALi watchdog
 130 *      @file: file from VFS
 131 *      @data: user address of data
 132 *      @len: length of data
 133 *      @ppos: pointer to the file offset
 134 *
 135 *      Handle a write to the ALi watchdog. Writing to the file pings
 136 *      the watchdog and resets it. Writing the magic 'V' sequence allows
 137 *      the next close to turn off the watchdog.
 138 */
 139
 140static ssize_t ali_write(struct file *file, const char __user *data,
 141                                                size_t len, loff_t *ppos)
 142{
 143        /* See if we got the magic character 'V' and reload the timer */
 144        if (len) {
 145                if (!nowayout) {
 146                        size_t i;
 147
 148                        /* note: just in case someone wrote the
 149                           magic character five months ago... */
 150                        ali_expect_release = 0;
 151
 152                        /* scan to see whether or not we got
 153                           the magic character */
 154                        for (i = 0; i != len; i++) {
 155                                char c;
 156                                if (get_user(c, data + i))
 157                                        return -EFAULT;
 158                                if (c == 'V')
 159                                        ali_expect_release = 42;
 160                        }
 161                }
 162
 163                /* someone wrote to us, we should reload the timer */
 164                ali_start();
 165        }
 166        return len;
 167}
 168
 169/*
 170 *      ali_ioctl       -       handle watchdog ioctls
 171 *      @file: VFS file pointer
 172 *      @cmd: ioctl number
 173 *      @arg: arguments to the ioctl
 174 *
 175 *      Handle the watchdog ioctls supported by the ALi driver. Really
 176 *      we want an extension to enable irq ack monitoring and the like
 177 */
 178
 179static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 180{
 181        void __user *argp = (void __user *)arg;
 182        int __user *p = argp;
 183        static struct watchdog_info ident = {
 184                .options =              WDIOF_KEEPALIVEPING |
 185                                        WDIOF_SETTIMEOUT |
 186                                        WDIOF_MAGICCLOSE,
 187                .firmware_version =     0,
 188                .identity =             "ALi M1535 WatchDog Timer",
 189        };
 190
 191        switch (cmd) {
 192        case WDIOC_GETSUPPORT:
 193                return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
 194
 195        case WDIOC_GETSTATUS:
 196        case WDIOC_GETBOOTSTATUS:
 197                return put_user(0, p);
 198        case WDIOC_SETOPTIONS:
 199        {
 200                int new_options, retval = -EINVAL;
 201
 202                if (get_user(new_options, p))
 203                        return -EFAULT;
 204                if (new_options & WDIOS_DISABLECARD) {
 205                        ali_stop();
 206                        retval = 0;
 207                }
 208                if (new_options & WDIOS_ENABLECARD) {
 209                        ali_start();
 210                        retval = 0;
 211                }
 212                return retval;
 213        }
 214        case WDIOC_KEEPALIVE:
 215                ali_keepalive();
 216                return 0;
 217        case WDIOC_SETTIMEOUT:
 218        {
 219                int new_timeout;
 220                if (get_user(new_timeout, p))
 221                        return -EFAULT;
 222                if (ali_settimer(new_timeout))
 223                        return -EINVAL;
 224                ali_keepalive();
 225                /* Fall */
 226        }
 227        case WDIOC_GETTIMEOUT:
 228                return put_user(timeout, p);
 229        default:
 230                return -ENOTTY;
 231        }
 232}
 233
 234/*
 235 *      ali_open        -       handle open of ali watchdog
 236 *      @inode: inode from VFS
 237 *      @file: file from VFS
 238 *
 239 *      Open the ALi watchdog device. Ensure only one person opens it
 240 *      at a time. Also start the watchdog running.
 241 */
 242
 243static int ali_open(struct inode *inode, struct file *file)
 244{
 245        /* /dev/watchdog can only be opened once */
 246        if (test_and_set_bit(0, &ali_is_open))
 247                return -EBUSY;
 248
 249        /* Activate */
 250        ali_start();
 251        return nonseekable_open(inode, file);
 252}
 253
 254/*
 255 *      ali_release     -       close an ALi watchdog
 256 *      @inode: inode from VFS
 257 *      @file: file from VFS
 258 *
 259 *      Close the ALi watchdog device. Actual shutdown of the timer
 260 *      only occurs if the magic sequence has been set.
 261 */
 262
 263static int ali_release(struct inode *inode, struct file *file)
 264{
 265        /*
 266         *      Shut off the timer.
 267         */
 268        if (ali_expect_release == 42)
 269                ali_stop();
 270        else {
 271                printk(KERN_CRIT PFX
 272                                "Unexpected close, not stopping watchdog!\n");
 273                ali_keepalive();
 274        }
 275        clear_bit(0, &ali_is_open);
 276        ali_expect_release = 0;
 277        return 0;
 278}
 279
 280/*
 281 *      ali_notify_sys  -       System down notifier
 282 *
 283 *      Notifier for system down
 284 */
 285
 286
 287static int ali_notify_sys(struct notifier_block *this,
 288                                        unsigned long code, void *unused)
 289{
 290        if (code == SYS_DOWN || code == SYS_HALT)
 291                ali_stop();             /* Turn the WDT off */
 292        return NOTIFY_DONE;
 293}
 294
 295/*
 296 *      Data for PCI driver interface
 297 *
 298 *      This data only exists for exporting the supported
 299 *      PCI ids via MODULE_DEVICE_TABLE.  We do not actually
 300 *      register a pci_driver, because someone else might one day
 301 *      want to register another driver on the same PCI id.
 302 */
 303
 304static struct pci_device_id ali_pci_tbl[] = {
 305        { PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,},
 306        { PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,},
 307        { 0, },
 308};
 309MODULE_DEVICE_TABLE(pci, ali_pci_tbl);
 310
 311/*
 312 *      ali_find_watchdog       -       find a 1535 and 7101
 313 *
 314 *      Scans the PCI hardware for a 1535 series bridge and matching 7101
 315 *      watchdog device. This may be overtight but it is better to be safe
 316 */
 317
 318static int __init ali_find_watchdog(void)
 319{
 320        struct pci_dev *pdev;
 321        u32 wdog;
 322
 323        /* Check for a 1533/1535 series bridge */
 324        pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
 325        if (pdev == NULL)
 326                pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1533, NULL);
 327        if (pdev == NULL)
 328                return -ENODEV;
 329        pci_dev_put(pdev);
 330
 331        /* Check for the a 7101 PMU */
 332        pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
 333        if (pdev == NULL)
 334                return -ENODEV;
 335
 336        if (pci_enable_device(pdev)) {
 337                pci_dev_put(pdev);
 338                return -EIO;
 339        }
 340
 341        ali_pci = pdev;
 342
 343        /*
 344         *      Initialize the timer bits
 345         */
 346        pci_read_config_dword(pdev, 0xCC, &wdog);
 347
 348        /* Timer bits */
 349        wdog &= ~0x3F;
 350        /* Issued events */
 351        wdog &= ~((1 << 27)|(1 << 26)|(1 << 25)|(1 << 24));
 352        /* No monitor bits */
 353        wdog &= ~((1 << 16)|(1 << 13)|(1 << 12)|(1 << 11)|(1 << 10)|(1 << 9));
 354
 355        pci_write_config_dword(pdev, 0xCC, wdog);
 356
 357        return 0;
 358}
 359
 360/*
 361 *      Kernel Interfaces
 362 */
 363
 364static const struct file_operations ali_fops = {
 365        .owner          =       THIS_MODULE,
 366        .llseek         =       no_llseek,
 367        .write          =       ali_write,
 368        .unlocked_ioctl =       ali_ioctl,
 369        .open           =       ali_open,
 370        .release        =       ali_release,
 371};
 372
 373static struct miscdevice ali_miscdev = {
 374        .minor =        WATCHDOG_MINOR,
 375        .name =         "watchdog",
 376        .fops =         &ali_fops,
 377};
 378
 379static struct notifier_block ali_notifier = {
 380        .notifier_call =        ali_notify_sys,
 381};
 382
 383/*
 384 *      watchdog_init   -       module initialiser
 385 *
 386 *      Scan for a suitable watchdog and if so initialize it. Return an error
 387 *      if we cannot, the error causes the module to unload
 388 */
 389
 390static int __init watchdog_init(void)
 391{
 392        int ret;
 393
 394        /* Check whether or not the hardware watchdog is there */
 395        if (ali_find_watchdog() != 0)
 396                return -ENODEV;
 397
 398        /* Check that the timeout value is within it's range;
 399           if not reset to the default */
 400        if (timeout < 1 || timeout >= 18000) {
 401                timeout = WATCHDOG_TIMEOUT;
 402                printk(KERN_INFO PFX
 403                     "timeout value must be 0 < timeout < 18000, using %d\n",
 404                                                        timeout);
 405        }
 406
 407        /* Calculate the watchdog's timeout */
 408        ali_settimer(timeout);
 409
 410        ret = register_reboot_notifier(&ali_notifier);
 411        if (ret != 0) {
 412                printk(KERN_ERR PFX
 413                        "cannot register reboot notifier (err=%d)\n", ret);
 414                goto out;
 415        }
 416
 417        ret = misc_register(&ali_miscdev);
 418        if (ret != 0) {
 419                printk(KERN_ERR PFX
 420                        "cannot register miscdev on minor=%d (err=%d)\n",
 421                                                WATCHDOG_MINOR, ret);
 422                goto unreg_reboot;
 423        }
 424
 425        printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
 426                timeout, nowayout);
 427
 428out:
 429        return ret;
 430unreg_reboot:
 431        unregister_reboot_notifier(&ali_notifier);
 432        goto out;
 433}
 434
 435/*
 436 *      watchdog_exit   -       module de-initialiser
 437 *
 438 *      Called while unloading a successfully installed watchdog module.
 439 */
 440
 441static void __exit watchdog_exit(void)
 442{
 443        /* Stop the timer before we leave */
 444        ali_stop();
 445
 446        /* Deregister */
 447        misc_deregister(&ali_miscdev);
 448        unregister_reboot_notifier(&ali_notifier);
 449        pci_dev_put(ali_pci);
 450}
 451
 452module_init(watchdog_init);
 453module_exit(watchdog_exit);
 454
 455MODULE_AUTHOR("Alan Cox");
 456MODULE_DESCRIPTION("ALi M1535 PMU Watchdog Timer driver");
 457MODULE_LICENSE("GPL");
 458MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 459