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