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#include <linux/module.h>
  23#include <linux/moduleparam.h>
  24#include <linux/types.h>
  25#include <linux/errno.h>
  26#include <linux/miscdevice.h>
  27#include <linux/fs.h>
  28#include <linux/init.h>
  29#include <linux/ioport.h>
  30#include <linux/timer.h>
  31#include <linux/completion.h>
  32#include <linux/jiffies.h>
  33#include <asm/io.h>
  34#include <asm/uaccess.h>
  35
  36#include <linux/watchdog.h>
  37
  38/* adjustable parameters */
  39
  40static int verbose = 0;
  41static int port = 0x91;
  42static int ticks = 10000;
  43
  44#define PFX                     "cpu5wdt: "
  45
  46#define CPU5WDT_EXTENT          0x0A
  47
  48#define CPU5WDT_STATUS_REG      0x00
  49#define CPU5WDT_TIME_A_REG      0x02
  50#define CPU5WDT_TIME_B_REG      0x03
  51#define CPU5WDT_MODE_REG        0x04
  52#define CPU5WDT_TRIGGER_REG     0x07
  53#define CPU5WDT_ENABLE_REG      0x08
  54#define CPU5WDT_RESET_REG       0x09
  55
  56#define CPU5WDT_INTERVAL        (HZ/10+1)
  57
  58/* some device data */
  59
  60static struct {
  61        struct completion stop;
  62        volatile int running;
  63        struct timer_list timer;
  64        volatile int queue;
  65        int default_ticks;
  66        unsigned long inuse;
  67} cpu5wdt_device;
  68
  69/* generic helper functions */
  70
  71static void cpu5wdt_trigger(unsigned long unused)
  72{
  73        if ( verbose > 2 )
  74                printk(KERN_DEBUG PFX "trigger at %i ticks\n", ticks);
  75
  76        if( cpu5wdt_device.running )
  77                ticks--;
  78
  79        /* keep watchdog alive */
  80        outb(1, port + CPU5WDT_TRIGGER_REG);
  81
  82        /* requeue?? */
  83        if (cpu5wdt_device.queue && ticks)
  84                mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
  85        else {
  86                /* ticks doesn't matter anyway */
  87                complete(&cpu5wdt_device.stop);
  88        }
  89
  90}
  91
  92static void cpu5wdt_reset(void)
  93{
  94        ticks = cpu5wdt_device.default_ticks;
  95
  96        if ( verbose )
  97                printk(KERN_DEBUG PFX "reset (%i ticks)\n", (int) ticks);
  98
  99}
 100
 101static void cpu5wdt_start(void)
 102{
 103        if ( !cpu5wdt_device.queue ) {
 104                cpu5wdt_device.queue = 1;
 105                outb(0, port + CPU5WDT_TIME_A_REG);
 106                outb(0, port + CPU5WDT_TIME_B_REG);
 107                outb(1, port + CPU5WDT_MODE_REG);
 108                outb(0, port + CPU5WDT_RESET_REG);
 109                outb(0, port + CPU5WDT_ENABLE_REG);
 110                mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
 111        }
 112        /* if process dies, counter is not decremented */
 113        cpu5wdt_device.running++;
 114}
 115
 116static int cpu5wdt_stop(void)
 117{
 118        if ( cpu5wdt_device.running )
 119                cpu5wdt_device.running = 0;
 120
 121        ticks = cpu5wdt_device.default_ticks;
 122
 123        if ( verbose )
 124                printk(KERN_CRIT PFX "stop not possible\n");
 125
 126        return -EIO;
 127}
 128
 129/* filesystem operations */
 130
 131static int cpu5wdt_open(struct inode *inode, struct file *file)
 132{
 133        if ( test_and_set_bit(0, &cpu5wdt_device.inuse) )
 134                return -EBUSY;
 135
 136        return nonseekable_open(inode, file);
 137}
 138
 139static int cpu5wdt_release(struct inode *inode, struct file *file)
 140{
 141        clear_bit(0, &cpu5wdt_device.inuse);
 142        return 0;
 143}
 144
 145static int cpu5wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 146{
 147        void __user *argp = (void __user *)arg;
 148        unsigned int value;
 149        static struct watchdog_info ident =
 150        {
 151                .options = WDIOF_CARDRESET,
 152                .identity = "CPU5 WDT",
 153        };
 154
 155        switch(cmd) {
 156                case WDIOC_KEEPALIVE:
 157                        cpu5wdt_reset();
 158                        break;
 159                case WDIOC_GETSTATUS:
 160                        value = inb(port + CPU5WDT_STATUS_REG);
 161                        value = (value >> 2) & 1;
 162                        if ( copy_to_user(argp, &value, sizeof(int)) )
 163                                return -EFAULT;
 164                        break;
 165                case WDIOC_GETBOOTSTATUS:
 166                        if ( copy_to_user(argp, &value, sizeof(int)) )
 167                                return -EFAULT;
 168                        break;
 169                case WDIOC_GETSUPPORT:
 170                        if ( copy_to_user(argp, &ident, sizeof(ident)) )
 171                                return -EFAULT;
 172                        break;
 173                case WDIOC_SETOPTIONS:
 174                        if ( copy_from_user(&value, argp, sizeof(int)) )
 175                                return -EFAULT;
 176                        switch(value) {
 177                                case WDIOS_ENABLECARD:
 178                                        cpu5wdt_start();
 179                                        break;
 180                                case WDIOS_DISABLECARD:
 181                                        return cpu5wdt_stop();
 182                                default:
 183                                        return -EINVAL;
 184                        }
 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, size_t count, loff_t *ppos)
 193{
 194        if ( !count )
 195                return -EIO;
 196
 197        cpu5wdt_reset();
 198
 199        return count;
 200}
 201
 202static const struct file_operations cpu5wdt_fops = {
 203        .owner          = THIS_MODULE,
 204        .llseek         = no_llseek,
 205        .ioctl          = cpu5wdt_ioctl,
 206        .open           = cpu5wdt_open,
 207        .write          = cpu5wdt_write,
 208        .release        = cpu5wdt_release,
 209};
 210
 211static struct miscdevice cpu5wdt_misc = {
 212        .minor  = WATCHDOG_MINOR,
 213        .name   = "watchdog",
 214        .fops   = &cpu5wdt_fops,
 215};
 216
 217/* init/exit function */
 218
 219static int __devinit cpu5wdt_init(void)
 220{
 221        unsigned int val;
 222        int err;
 223
 224        if ( verbose )
 225                printk(KERN_DEBUG PFX "port=0x%x, verbose=%i\n", port, verbose);
 226
 227        if ( !request_region(port, CPU5WDT_EXTENT, PFX) ) {
 228                printk(KERN_ERR PFX "request_region failed\n");
 229                err = -EBUSY;
 230                goto no_port;
 231        }
 232
 233        if ( (err = misc_register(&cpu5wdt_misc)) < 0 ) {
 234                printk(KERN_ERR PFX "misc_register failed\n");
 235                goto no_misc;
 236        }
 237
 238        /* watchdog reboot? */
 239        val = inb(port + CPU5WDT_STATUS_REG);
 240        val = (val >> 2) & 1;
 241        if ( !val )
 242                printk(KERN_INFO PFX "sorry, was my fault\n");
 243
 244        init_completion(&cpu5wdt_device.stop);
 245        cpu5wdt_device.queue = 0;
 246
 247        clear_bit(0, &cpu5wdt_device.inuse);
 248
 249        setup_timer(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
 250
 251        cpu5wdt_device.default_ticks = ticks;
 252
 253        printk(KERN_INFO PFX "init success\n");
 254
 255        return 0;
 256
 257no_misc:
 258        release_region(port, CPU5WDT_EXTENT);
 259no_port:
 260        return err;
 261}
 262
 263static int __devinit cpu5wdt_init_module(void)
 264{
 265        return cpu5wdt_init();
 266}
 267
 268static void __devexit cpu5wdt_exit(void)
 269{
 270        if ( cpu5wdt_device.queue ) {
 271                cpu5wdt_device.queue = 0;
 272                wait_for_completion(&cpu5wdt_device.stop);
 273        }
 274
 275        misc_deregister(&cpu5wdt_misc);
 276
 277        release_region(port, CPU5WDT_EXTENT);
 278
 279}
 280
 281static void __devexit cpu5wdt_exit_module(void)
 282{
 283        cpu5wdt_exit();
 284}
 285
 286/* module entry points */
 287
 288module_init(cpu5wdt_init_module);
 289module_exit(cpu5wdt_exit_module);
 290
 291MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
 292MODULE_DESCRIPTION("sma cpu5 watchdog driver");
 293MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
 294MODULE_LICENSE("GPL");
 295MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 296
 297module_param(port, int, 0);
 298MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
 299
 300module_param(verbose, int, 0);
 301MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
 302
 303module_param(ticks, int, 0);
 304MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");
 305