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        spin_lock(&indydog_lock);
  45        sgimc->cpuctrl0 |= SGIMC_CCTRL0_WDOG;
  46        spin_unlock(&indydog_lock);
  47}
  48
  49static void indydog_stop(void)
  50{
  51        spin_lock(&indydog_lock);
  52        sgimc->cpuctrl0 &= ~SGIMC_CCTRL0_WDOG;
  53        spin_unlock(&indydog_lock);
  54
  55        pr_info("Stopped watchdog timer\n");
  56}
  57
  58static void indydog_ping(void)
  59{
  60        sgimc->watchdogt = 0;
  61}
  62
  63/*
  64 *      Allow only one person to hold it open
  65 */
  66static int indydog_open(struct inode *inode, struct file *file)
  67{
  68        if (test_and_set_bit(0, &indydog_alive))
  69                return -EBUSY;
  70
  71        if (nowayout)
  72                __module_get(THIS_MODULE);
  73
  74        /* Activate timer */
  75        indydog_start();
  76        indydog_ping();
  77
  78        pr_info("Started watchdog timer\n");
  79
  80        return nonseekable_open(inode, file);
  81}
  82
  83static int indydog_release(struct inode *inode, struct file *file)
  84{
  85        /* Shut off the timer.
  86         * Lock it in if it's a module and we defined ...NOWAYOUT */
  87        if (!nowayout)
  88                indydog_stop();         /* Turn the WDT off */
  89        clear_bit(0, &indydog_alive);
  90        return 0;
  91}
  92
  93static ssize_t indydog_write(struct file *file, const char *data,
  94                                                size_t len, loff_t *ppos)
  95{
  96        /* Refresh the timer. */
  97        if (len)
  98                indydog_ping();
  99        return len;
 100}
 101
 102static long indydog_ioctl(struct file *file, unsigned int cmd,
 103                                                        unsigned long arg)
 104{
 105        int options, retval = -EINVAL;
 106        static const struct watchdog_info ident = {
 107                .options                = WDIOF_KEEPALIVEPING,
 108                .firmware_version       = 0,
 109                .identity               = "Hardware Watchdog for SGI IP22",
 110        };
 111
 112        switch (cmd) {
 113        case WDIOC_GETSUPPORT:
 114                if (copy_to_user((struct watchdog_info *)arg,
 115                                 &ident, sizeof(ident)))
 116                        return -EFAULT;
 117                return 0;
 118        case WDIOC_GETSTATUS:
 119        case WDIOC_GETBOOTSTATUS:
 120                return put_user(0, (int *)arg);
 121        case WDIOC_SETOPTIONS:
 122        {
 123                if (get_user(options, (int *)arg))
 124                        return -EFAULT;
 125                if (options & WDIOS_DISABLECARD) {
 126                        indydog_stop();
 127                        retval = 0;
 128                }
 129                if (options & WDIOS_ENABLECARD) {
 130                        indydog_start();
 131                        retval = 0;
 132                }
 133                return retval;
 134        }
 135        case WDIOC_KEEPALIVE:
 136                indydog_ping();
 137                return 0;
 138        case WDIOC_GETTIMEOUT:
 139                return put_user(WATCHDOG_TIMEOUT, (int *)arg);
 140        default:
 141                return -ENOTTY;
 142        }
 143}
 144
 145static int indydog_notify_sys(struct notifier_block *this,
 146                                        unsigned long code, void *unused)
 147{
 148        if (code == SYS_DOWN || code == SYS_HALT)
 149                indydog_stop();         /* Turn the WDT off */
 150
 151        return NOTIFY_DONE;
 152}
 153
 154static const struct file_operations indydog_fops = {
 155        .owner          = THIS_MODULE,
 156        .llseek         = no_llseek,
 157        .write          = indydog_write,
 158        .unlocked_ioctl = indydog_ioctl,
 159        .open           = indydog_open,
 160        .release        = indydog_release,
 161};
 162
 163static struct miscdevice indydog_miscdev = {
 164        .minor          = WATCHDOG_MINOR,
 165        .name           = "watchdog",
 166        .fops           = &indydog_fops,
 167};
 168
 169static struct notifier_block indydog_notifier = {
 170        .notifier_call = indydog_notify_sys,
 171};
 172
 173static int __init watchdog_init(void)
 174{
 175        int ret;
 176
 177        ret = register_reboot_notifier(&indydog_notifier);
 178        if (ret) {
 179                pr_err("cannot register reboot notifier (err=%d)\n", ret);
 180                return ret;
 181        }
 182
 183        ret = misc_register(&indydog_miscdev);
 184        if (ret) {
 185                pr_err("cannot register miscdev on minor=%d (err=%d)\n",
 186                       WATCHDOG_MINOR, ret);
 187                unregister_reboot_notifier(&indydog_notifier);
 188                return ret;
 189        }
 190
 191        pr_info("Hardware Watchdog Timer for SGI IP22: 0.3\n");
 192
 193        return 0;
 194}
 195
 196static void __exit watchdog_exit(void)
 197{
 198        misc_deregister(&indydog_miscdev);
 199        unregister_reboot_notifier(&indydog_notifier);
 200}
 201
 202module_init(watchdog_init);
 203module_exit(watchdog_exit);
 204
 205MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
 206MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
 207MODULE_LICENSE("GPL");
 208