linux/drivers/watchdog/cpu5wdt.c
<<
>>
Prefs
   1/*
   2 * sma cpu5 watchdog driver
   3 *
   4 * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19 *
  20 */
  21
  22#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  23
  24#include <linux/module.h>
  25#include <linux/moduleparam.h>
  26#include <linux/types.h>
  27#include <linux/errno.h>
  28#include <linux/miscdevice.h>
  29#include <linux/fs.h>
  30#include <linux/init.h>
  31#include <linux/ioport.h>
  32#include <linux/timer.h>
  33#include <linux/completion.h>
  34#include <linux/jiffies.h>
  35#include <linux/io.h>
  36#include <linux/uaccess.h>
  37#include <linux/watchdog.h>
  38
  39/* adjustable parameters */
  40
  41static int verbose;
  42static int port = 0x91;
  43static int ticks = 10000;
  44static DEFINE_SPINLOCK(cpu5wdt_lock);
  45
  46#define PFX                     "cpu5wdt: "
  47
  48#define CPU5WDT_EXTENT          0x0A
  49
  50#define CPU5WDT_STATUS_REG      0x00
  51#define CPU5WDT_TIME_A_REG      0x02
  52#define CPU5WDT_TIME_B_REG      0x03
  53#define CPU5WDT_MODE_REG        0x04
  54#define CPU5WDT_TRIGGER_REG     0x07
  55#define CPU5WDT_ENABLE_REG      0x08
  56#define CPU5WDT_RESET_REG       0x09
  57
  58#define CPU5WDT_INTERVAL        (HZ/10+1)
  59
  60/* some device data */
  61
  62static struct {
  63        struct completion stop;
  64        int running;
  65        struct timer_list timer;
  66        int queue;
  67        int default_ticks;
  68        unsigned long inuse;
  69} cpu5wdt_device;
  70
  71/* generic helper functions */
  72
  73static void cpu5wdt_trigger(unsigned long unused)
  74{
  75        if (verbose > 2)
  76                pr_debug("trigger at %i ticks\n", ticks);
  77
  78        if (cpu5wdt_device.running)
  79                ticks--;
  80
  81        spin_lock(&cpu5wdt_lock);
  82        /* keep watchdog alive */
  83        outb(1, port + CPU5WDT_TRIGGER_REG);
  84
  85        /* requeue?? */
  86        if (cpu5wdt_device.queue && ticks)
  87                mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
  88        else {
  89                /* ticks doesn't matter anyway */
  90                complete(&cpu5wdt_device.stop);
  91        }
  92        spin_unlock(&cpu5wdt_lock);
  93
  94}
  95
  96static void cpu5wdt_reset(void)
  97{
  98        ticks = cpu5wdt_device.default_ticks;
  99
 100        if (verbose)
 101                pr_debug("reset (%i ticks)\n", (int) ticks);
 102
 103}
 104
 105static void cpu5wdt_start(void)
 106{
 107        unsigned long flags;
 108
 109        spin_lock_irqsave(&cpu5wdt_lock, flags);
 110        if (!cpu5wdt_device.queue) {
 111                cpu5wdt_device.queue = 1;
 112                outb(0, port + CPU5WDT_TIME_A_REG);
 113                outb(0, port + CPU5WDT_TIME_B_REG);
 114                outb(1, port + CPU5WDT_MODE_REG);
 115                outb(0, port + CPU5WDT_RESET_REG);
 116                outb(0, port + CPU5WDT_ENABLE_REG);
 117                mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
 118        }
 119        /* if process dies, counter is not decremented */
 120        cpu5wdt_device.running++;
 121        spin_unlock_irqrestore(&cpu5wdt_lock, flags);
 122}
 123
 124static int cpu5wdt_stop(void)
 125{
 126        unsigned long flags;
 127
 128        spin_lock_irqsave(&cpu5wdt_lock, flags);
 129        if (cpu5wdt_device.running)
 130                cpu5wdt_device.running = 0;
 131        ticks = cpu5wdt_device.default_ticks;
 132        spin_unlock_irqrestore(&cpu5wdt_lock, flags);
 133        if (verbose)
 134                pr_crit("stop not possible\n");
 135        return -EIO;
 136}
 137
 138/* filesystem operations */
 139
 140static int cpu5wdt_open(struct inode *inode, struct file *file)
 141{
 142        if (test_and_set_bit(0, &cpu5wdt_device.inuse))
 143                return -EBUSY;
 144        return nonseekable_open(inode, file);
 145}
 146
 147static int cpu5wdt_release(struct inode *inode, struct file *file)
 148{
 149        clear_bit(0, &cpu5wdt_device.inuse);
 150        return 0;
 151}
 152
 153static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
 154                                                unsigned long arg)
 155{
 156        void __user *argp = (void __user *)arg;
 157        int __user *p = argp;
 158        unsigned int value;
 159        static const struct watchdog_info ident = {
 160                .options = WDIOF_CARDRESET,
 161                .identity = "CPU5 WDT",
 162        };
 163
 164        switch (cmd) {
 165        case WDIOC_GETSUPPORT:
 166                if (copy_to_user(argp, &ident, sizeof(ident)))
 167                        return -EFAULT;
 168                break;
 169        case WDIOC_GETSTATUS:
 170                value = inb(port + CPU5WDT_STATUS_REG);
 171                value = (value >> 2) & 1;
 172                return put_user(value, p);
 173        case WDIOC_GETBOOTSTATUS:
 174                return put_user(0, p);
 175        case WDIOC_SETOPTIONS:
 176                if (get_user(value, p))
 177                        return -EFAULT;
 178                if (value & WDIOS_ENABLECARD)
 179                        cpu5wdt_start();
 180                if (value & WDIOS_DISABLECARD)
 181                        cpu5wdt_stop();
 182                break;
 183        case WDIOC_KEEPALIVE:
 184                cpu5wdt_reset();
 185                break;
 186        default:
 187                return -ENOTTY;
 188        }
 189        return 0;
 190}
 191
 192static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
 193                                                size_t count, loff_t *ppos)
 194{
 195        if (!count)
 196                return -EIO;
 197        cpu5wdt_reset();
 198        return count;
 199}
 200
 201static const struct file_operations cpu5wdt_fops = {
 202        .owner          = THIS_MODULE,
 203        .llseek         = no_llseek,
 204        .unlocked_ioctl = cpu5wdt_ioctl,
 205        .open           = cpu5wdt_open,
 206        .write          = cpu5wdt_write,
 207        .release        = cpu5wdt_release,
 208};
 209
 210static struct miscdevice cpu5wdt_misc = {
 211        .minor  = WATCHDOG_MINOR,
 212        .name   = "watchdog",
 213        .fops   = &cpu5wdt_fops,
 214};
 215
 216/* init/exit function */
 217
 218static int cpu5wdt_init(void)
 219{
 220        unsigned int val;
 221        int err;
 222
 223        if (verbose)
 224                pr_debug("port=0x%x, verbose=%i\n", port, verbose);
 225
 226        init_completion(&cpu5wdt_device.stop);
 227        cpu5wdt_device.queue = 0;
 228        setup_timer(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
 229        cpu5wdt_device.default_ticks = ticks;
 230
 231        if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
 232                pr_err("request_region failed\n");
 233                err = -EBUSY;
 234                goto no_port;
 235        }
 236
 237        /* watchdog reboot? */
 238        val = inb(port + CPU5WDT_STATUS_REG);
 239        val = (val >> 2) & 1;
 240        if (!val)
 241                pr_info("sorry, was my fault\n");
 242
 243        err = misc_register(&cpu5wdt_misc);
 244        if (err < 0) {
 245                pr_err("misc_register failed\n");
 246                goto no_misc;
 247        }
 248
 249
 250        pr_info("init success\n");
 251        return 0;
 252
 253no_misc:
 254        release_region(port, CPU5WDT_EXTENT);
 255no_port:
 256        return err;
 257}
 258
 259static int cpu5wdt_init_module(void)
 260{
 261        return cpu5wdt_init();
 262}
 263
 264static void cpu5wdt_exit(void)
 265{
 266        if (cpu5wdt_device.queue) {
 267                cpu5wdt_device.queue = 0;
 268                wait_for_completion(&cpu5wdt_device.stop);
 269                del_timer(&cpu5wdt_device.timer);
 270        }
 271
 272        misc_deregister(&cpu5wdt_misc);
 273
 274        release_region(port, CPU5WDT_EXTENT);
 275
 276}
 277
 278static void cpu5wdt_exit_module(void)
 279{
 280        cpu5wdt_exit();
 281}
 282
 283/* module entry points */
 284
 285module_init(cpu5wdt_init_module);
 286module_exit(cpu5wdt_exit_module);
 287
 288MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
 289MODULE_DESCRIPTION("sma cpu5 watchdog driver");
 290MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
 291MODULE_LICENSE("GPL");
 292MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 293
 294module_param(port, int, 0);
 295MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
 296
 297module_param(verbose, int, 0);
 298MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
 299
 300module_param(ticks, int, 0);
 301MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");
 302