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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  16
  17#include <linux/module.h>
  18#include <linux/moduleparam.h>
  19#include <linux/types.h>
  20#include <linux/kernel.h>
  21#include <linux/fs.h>
  22#include <linux/mm.h>
  23#include <linux/miscdevice.h>
  24#include <linux/watchdog.h>
  25#include <linux/notifier.h>
  26#include <linux/reboot.h>
  27#include <linux/init.h>
  28#include <linux/uaccess.h>
  29#include <asm/sgi/mc.h>
  30
  31static unsigned long indydog_alive;
  32static DEFINE_SPINLOCK(indydog_lock);
  33
  34#define WATCHDOG_TIMEOUT 30             /* 30 sec default timeout */
  35
  36static bool nowayout = WATCHDOG_NOWAYOUT;
  37module_param(nowayout, bool, 0);
  38MODULE_PARM_DESC(nowayout,
  39                "Watchdog cannot be stopped once started (default="
  40                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  41
  42static void indydog_start(void)
  43{
  44        u32 mc_ctrl0;
  45
  46        spin_lock(&indydog_lock);
  47        mc_ctrl0 = sgimc->cpuctrl0;
  48        mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG;
  49        sgimc->cpuctrl0 = mc_ctrl0;
  50        spin_unlock(&indydog_lock);
  51}
  52
  53static void indydog_stop(void)
  54{
  55        u32 mc_ctrl0;
  56
  57        spin_lock(&indydog_lock);
  58
  59        mc_ctrl0 = sgimc->cpuctrl0;
  60        mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
  61        sgimc->cpuctrl0 = mc_ctrl0;
  62        spin_unlock(&indydog_lock);
  63
  64        pr_info("Stopped watchdog timer\n");
  65}
  66
  67static void indydog_ping(void)
  68{
  69        sgimc->watchdogt = 0;
  70}
  71
  72/*
  73 *      Allow only one person to hold it open
  74 */
  75static int indydog_open(struct inode *inode, struct file *file)
  76{
  77        if (test_and_set_bit(0, &indydog_alive))
  78                return -EBUSY;
  79
  80        if (nowayout)
  81                __module_get(THIS_MODULE);
  82
  83        /* Activate timer */
  84        indydog_start();
  85        indydog_ping();
  86
  87        pr_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 const struct watchdog_info ident = {
 116                .options                = WDIOF_KEEPALIVEPING,
 117                .firmware_version       = 0,
 118                .identity               = "Hardware Watchdog for SGI IP22",
 119        };
 120
 121        switch (cmd) {
 122        case WDIOC_GETSUPPORT:
 123                if (copy_to_user((struct watchdog_info *)arg,
 124                                 &ident, sizeof(ident)))
 125                        return -EFAULT;
 126                return 0;
 127        case WDIOC_GETSTATUS:
 128        case WDIOC_GETBOOTSTATUS:
 129                return put_user(0, (int *)arg);
 130        case WDIOC_SETOPTIONS:
 131        {
 132                if (get_user(options, (int *)arg))
 133                        return -EFAULT;
 134                if (options & WDIOS_DISABLECARD) {
 135                        indydog_stop();
 136                        retval = 0;
 137                }
 138                if (options & WDIOS_ENABLECARD) {
 139                        indydog_start();
 140                        retval = 0;
 141                }
 142                return retval;
 143        }
 144        case WDIOC_KEEPALIVE:
 145                indydog_ping();
 146                return 0;
 147        case WDIOC_GETTIMEOUT:
 148                return put_user(WATCHDOG_TIMEOUT, (int *)arg);
 149        default:
 150                return -ENOTTY;
 151        }
 152}
 153
 154static int indydog_notify_sys(struct notifier_block *this,
 155                                        unsigned long code, void *unused)
 156{
 157        if (code == SYS_DOWN || code == SYS_HALT)
 158                indydog_stop();         /* Turn the WDT off */
 159
 160        return NOTIFY_DONE;
 161}
 162
 163static const struct file_operations indydog_fops = {
 164        .owner          = THIS_MODULE,
 165        .llseek         = no_llseek,
 166        .write          = indydog_write,
 167        .unlocked_ioctl = indydog_ioctl,
 168        .open           = indydog_open,
 169        .release        = indydog_release,
 170};
 171
 172static struct miscdevice indydog_miscdev = {
 173        .minor          = WATCHDOG_MINOR,
 174        .name           = "watchdog",
 175        .fops           = &indydog_fops,
 176};
 177
 178static struct notifier_block indydog_notifier = {
 179        .notifier_call = indydog_notify_sys,
 180};
 181
 182static int __init watchdog_init(void)
 183{
 184        int ret;
 185
 186        ret = register_reboot_notifier(&indydog_notifier);
 187        if (ret) {
 188                pr_err("cannot register reboot notifier (err=%d)\n", ret);
 189                return ret;
 190        }
 191
 192        ret = misc_register(&indydog_miscdev);
 193        if (ret) {
 194                pr_err("cannot register miscdev on minor=%d (err=%d)\n",
 195                       WATCHDOG_MINOR, ret);
 196                unregister_reboot_notifier(&indydog_notifier);
 197                return ret;
 198        }
 199
 200        pr_info("Hardware Watchdog Timer for SGI IP22: 0.3\n");
 201
 202        return 0;
 203}
 204
 205static void __exit watchdog_exit(void)
 206{
 207        misc_deregister(&indydog_miscdev);
 208        unregister_reboot_notifier(&indydog_notifier);
 209}
 210
 211module_init(watchdog_init);
 212module_exit(watchdog_exit);
 213
 214MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
 215MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
 216MODULE_LICENSE("GPL");
 217MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 218