linux/drivers/watchdog/indydog.c
<<
>>
Prefs
   1/*
   2 *      IndyDog 0.3     A Hardware Watchdog Device for SGI IP22
   3 *
   4 *      (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>,
   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 *      based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/moduleparam.h>
  17#include <linux/types.h>
  18#include <linux/kernel.h>
  19#include <linux/fs.h>
  20#include <linux/mm.h>
  21#include <linux/miscdevice.h>
  22#include <linux/watchdog.h>
  23#include <linux/notifier.h>
  24#include <linux/reboot.h>
  25#include <linux/init.h>
  26#include <linux/uaccess.h>
  27#include <asm/sgi/mc.h>
  28
  29#define PFX "indydog: "
  30static unsigned long indydog_alive;
  31static spinlock_t indydog_lock;
  32
  33#define WATCHDOG_TIMEOUT 30             /* 30 sec default timeout */
  34
  35static int nowayout = WATCHDOG_NOWAYOUT;
  36module_param(nowayout, int, 0);
  37MODULE_PARM_DESC(nowayout,
  38                "Watchdog cannot be stopped once started (default="
  39                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  40
  41static void indydog_start(void)
  42{
  43        u32 mc_ctrl0;
  44
  45        spin_lock(&indydog_lock);
  46        mc_ctrl0 = sgimc->cpuctrl0;
  47        mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG;
  48        sgimc->cpuctrl0 = mc_ctrl0;
  49        spin_unlock(&indydog_lock);
  50}
  51
  52static void indydog_stop(void)
  53{
  54        u32 mc_ctrl0;
  55
  56        spin_lock(&indydog_lock);
  57
  58        mc_ctrl0 = sgimc->cpuctrl0;
  59        mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
  60        sgimc->cpuctrl0 = mc_ctrl0;
  61        spin_unlock(&indydog_lock);
  62
  63        printk(KERN_INFO PFX "Stopped watchdog timer.\n");
  64}
  65
  66static void indydog_ping(void)
  67{
  68        sgimc->watchdogt = 0;
  69}
  70
  71/*
  72 *      Allow only one person to hold it open
  73 */
  74static int indydog_open(struct inode *inode, struct file *file)
  75{
  76        if (test_and_set_bit(0, &indydog_alive))
  77                return -EBUSY;
  78
  79        if (nowayout)
  80                __module_get(THIS_MODULE);
  81
  82        /* Activate timer */
  83        indydog_start();
  84        indydog_ping();
  85
  86        indydog_alive = 1;
  87        printk(KERN_INFO "Started watchdog timer.\n");
  88
  89        return nonseekable_open(inode, file);
  90}
  91
  92static int indydog_release(struct inode *inode, struct file *file)
  93{
  94        /* Shut off the timer.
  95         * Lock it in if it's a module and we defined ...NOWAYOUT */
  96        if (!nowayout)
  97                indydog_stop();         /* Turn the WDT off */
  98        clear_bit(0, &indydog_alive);
  99        return 0;
 100}
 101
 102static ssize_t indydog_write(struct file *file, const char *data,
 103                                                size_t len, loff_t *ppos)
 104{
 105        /* Refresh the timer. */
 106        if (len)
 107                indydog_ping();
 108        return len;
 109}
 110
 111static long indydog_ioctl(struct file *file, unsigned int cmd,
 112                                                        unsigned long arg)
 113{
 114        int options, retval = -EINVAL;
 115        static struct watchdog_info ident = {
 116                .options                = WDIOF_KEEPALIVEPING |
 117                                          WDIOF_MAGICCLOSE,
 118                .firmware_version       = 0,
 119                .identity               = "Hardware Watchdog for SGI IP22",
 120        };
 121
 122        switch (cmd) {
 123        case WDIOC_GETSUPPORT:
 124                if (copy_to_user((struct watchdog_info *)arg,
 125                                 &ident, sizeof(ident)))
 126                        return -EFAULT;
 127                return 0;
 128        case WDIOC_GETSTATUS:
 129        case WDIOC_GETBOOTSTATUS:
 130                return put_user(0, (int *)arg);
 131        case WDIOC_SETOPTIONS:
 132        {
 133                if (get_user(options, (int *)arg))
 134                        return -EFAULT;
 135                if (options & WDIOS_DISABLECARD) {
 136                        indydog_stop();
 137                        retval = 0;
 138                }
 139                if (options & WDIOS_ENABLECARD) {
 140                        indydog_start();
 141                        retval = 0;
 142                }
 143                return retval;
 144        }
 145        case WDIOC_KEEPALIVE:
 146                indydog_ping();
 147                return 0;
 148        case WDIOC_GETTIMEOUT:
 149                return put_user(WATCHDOG_TIMEOUT, (int *)arg);
 150        default:
 151                return -ENOTTY;
 152        }
 153}
 154
 155static int indydog_notify_sys(struct notifier_block *this,
 156                                        unsigned long code, void *unused)
 157{
 158        if (code == SYS_DOWN || code == SYS_HALT)
 159                indydog_stop();         /* Turn the WDT off */
 160
 161        return NOTIFY_DONE;
 162}
 163
 164static const struct file_operations indydog_fops = {
 165        .owner          = THIS_MODULE,
 166        .llseek         = no_llseek,
 167        .write          = indydog_write,
 168        .unlocked_ioctl = indydog_ioctl,
 169        .open           = indydog_open,
 170        .release        = indydog_release,
 171};
 172
 173static struct miscdevice indydog_miscdev = {
 174        .minor          = WATCHDOG_MINOR,
 175        .name           = "watchdog",
 176        .fops           = &indydog_fops,
 177};
 178
 179static struct notifier_block indydog_notifier = {
 180        .notifier_call = indydog_notify_sys,
 181};
 182
 183static char banner[] __initdata =
 184        KERN_INFO PFX "Hardware Watchdog Timer for SGI IP22: 0.3\n";
 185
 186static int __init watchdog_init(void)
 187{
 188        int ret;
 189
 190        spin_lock_init(&indydog_lock);
 191
 192        ret = register_reboot_notifier(&indydog_notifier);
 193        if (ret) {
 194                printk(KERN_ERR PFX
 195                        "cannot register reboot notifier (err=%d)\n", ret);
 196                return ret;
 197        }
 198
 199        ret = misc_register(&indydog_miscdev);
 200        if (ret) {
 201                printk(KERN_ERR PFX
 202                        "cannot register miscdev on minor=%d (err=%d)\n",
 203                                                        WATCHDOG_MINOR, ret);
 204                unregister_reboot_notifier(&indydog_notifier);
 205                return ret;
 206        }
 207
 208        printk(banner);
 209
 210        return 0;
 211}
 212
 213static void __exit watchdog_exit(void)
 214{
 215        misc_deregister(&indydog_miscdev);
 216        unregister_reboot_notifier(&indydog_notifier);
 217}
 218
 219module_init(watchdog_init);
 220module_exit(watchdog_exit);
 221
 222MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
 223MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
 224MODULE_LICENSE("GPL");
 225MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 226