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        printk(KERN_INFO "Started watchdog timer.\n");
  87
  88        return nonseekable_open(inode, file);
  89}
  90
  91static int indydog_release(struct inode *inode, struct file *file)
  92{
  93        /* Shut off the timer.
  94         * Lock it in if it's a module and we defined ...NOWAYOUT */
  95        if (!nowayout)
  96                indydog_stop();         /* Turn the WDT off */
  97        clear_bit(0, &indydog_alive);
  98        return 0;
  99}
 100
 101static ssize_t indydog_write(struct file *file, const char *data,
 102                                                size_t len, loff_t *ppos)
 103{
 104        /* Refresh the timer. */
 105        if (len)
 106                indydog_ping();
 107        return len;
 108}
 109
 110static long indydog_ioctl(struct file *file, unsigned int cmd,
 111                                                        unsigned long arg)
 112{
 113        int options, retval = -EINVAL;
 114        static const struct watchdog_info ident = {
 115                .options                = WDIOF_KEEPALIVEPING,
 116                .firmware_version       = 0,
 117                .identity               = "Hardware Watchdog for SGI IP22",
 118        };
 119
 120        switch (cmd) {
 121        case WDIOC_GETSUPPORT:
 122                if (copy_to_user((struct watchdog_info *)arg,
 123                                 &ident, sizeof(ident)))
 124                        return -EFAULT;
 125                return 0;
 126        case WDIOC_GETSTATUS:
 127        case WDIOC_GETBOOTSTATUS:
 128                return put_user(0, (int *)arg);
 129        case WDIOC_SETOPTIONS:
 130        {
 131                if (get_user(options, (int *)arg))
 132                        return -EFAULT;
 133                if (options & WDIOS_DISABLECARD) {
 134                        indydog_stop();
 135                        retval = 0;
 136                }
 137                if (options & WDIOS_ENABLECARD) {
 138                        indydog_start();
 139                        retval = 0;
 140                }
 141                return retval;
 142        }
 143        case WDIOC_KEEPALIVE:
 144                indydog_ping();
 145                return 0;
 146        case WDIOC_GETTIMEOUT:
 147                return put_user(WATCHDOG_TIMEOUT, (int *)arg);
 148        default:
 149                return -ENOTTY;
 150        }
 151}
 152
 153static int indydog_notify_sys(struct notifier_block *this,
 154                                        unsigned long code, void *unused)
 155{
 156        if (code == SYS_DOWN || code == SYS_HALT)
 157                indydog_stop();         /* Turn the WDT off */
 158
 159        return NOTIFY_DONE;
 160}
 161
 162static const struct file_operations indydog_fops = {
 163        .owner          = THIS_MODULE,
 164        .llseek         = no_llseek,
 165        .write          = indydog_write,
 166        .unlocked_ioctl = indydog_ioctl,
 167        .open           = indydog_open,
 168        .release        = indydog_release,
 169};
 170
 171static struct miscdevice indydog_miscdev = {
 172        .minor          = WATCHDOG_MINOR,
 173        .name           = "watchdog",
 174        .fops           = &indydog_fops,
 175};
 176
 177static struct notifier_block indydog_notifier = {
 178        .notifier_call = indydog_notify_sys,
 179};
 180
 181static char banner[] __initdata =
 182        KERN_INFO PFX "Hardware Watchdog Timer for SGI IP22: 0.3\n";
 183
 184static int __init watchdog_init(void)
 185{
 186        int ret;
 187
 188        spin_lock_init(&indydog_lock);
 189
 190        ret = register_reboot_notifier(&indydog_notifier);
 191        if (ret) {
 192                printk(KERN_ERR PFX
 193                        "cannot register reboot notifier (err=%d)\n", ret);
 194                return ret;
 195        }
 196
 197        ret = misc_register(&indydog_miscdev);
 198        if (ret) {
 199                printk(KERN_ERR PFX
 200                        "cannot register miscdev on minor=%d (err=%d)\n",
 201                                                        WATCHDOG_MINOR, ret);
 202                unregister_reboot_notifier(&indydog_notifier);
 203                return ret;
 204        }
 205
 206        printk(banner);
 207
 208        return 0;
 209}
 210
 211static void __exit watchdog_exit(void)
 212{
 213        misc_deregister(&indydog_miscdev);
 214        unregister_reboot_notifier(&indydog_notifier);
 215}
 216
 217module_init(watchdog_init);
 218module_exit(watchdog_exit);
 219
 220MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
 221MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
 222MODULE_LICENSE("GPL");
 223MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 224