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