linux-old/drivers/char/advantechwdt.c
<<
>>
Prefs
   1/*
   2 *      Advantech Single Board Computer WDT driver for Linux 2.4.x
   3 *
   4 *      (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
   5 *
   6 *      Based on acquirewdt.c which is based on wdt.c.
   7 *      Original copyright messages:
   8 *
   9 *      (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
  10 *                              http://www.redhat.com
  11 *
  12 *      This program is free software; you can redistribute it and/or
  13 *      modify it under the terms of the GNU General Public License
  14 *      as published by the Free Software Foundation; either version
  15 *      2 of the License, or (at your option) any later version.
  16 *
  17 *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
  18 *      warranty for any of this software. This material is provided
  19 *      "AS-IS" and at no charge.
  20 *
  21 *      (c) Copyright 1995    Alan Cox <alan@redhat.com>
  22 *
  23 *      14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
  24 *          Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
  25 */
  26
  27#include <linux/config.h>
  28#include <linux/module.h>
  29#include <linux/types.h>
  30#include <linux/errno.h>
  31#include <linux/kernel.h>
  32#include <linux/miscdevice.h>
  33#include <linux/watchdog.h>
  34#include <linux/ioport.h>
  35#include <linux/fcntl.h>
  36#include <asm/io.h>
  37#include <asm/uaccess.h>
  38#include <asm/system.h>
  39#include <linux/notifier.h>
  40#include <linux/reboot.h>
  41#include <linux/init.h>
  42
  43static unsigned long advwdt_is_open;
  44static char adv_expect_close;
  45
  46/*
  47 *      You must set these - there is no sane way to probe for this board.
  48 *
  49 *      To enable or restart, write the timeout value in seconds (1 to 63)
  50 *      to I/O port wdt_start.  To disable, read I/O port wdt_stop.
  51 *      Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but
  52 *      check your manual (at least the PCA-6159 seems to be different -
  53 *      the manual says wdt_stop is 0x43, not 0x443).
  54 *      (0x43 is also a write-only control register for the 8254 timer!)
  55 */
  56 
  57static int wdt_stop = 0x443;
  58static int wdt_start = 0x443;
  59
  60static int wd_margin = 60; /* 60 sec default timeout */
  61
  62#ifdef CONFIG_WATCHDOG_NOWAYOUT
  63static int nowayout = 1;
  64#else
  65static int nowayout = 0;
  66#endif
  67
  68MODULE_PARM(nowayout,"i");
  69MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
  70
  71/*
  72 *      Kernel methods.
  73 */
  74
  75#ifndef MODULE
  76
  77static int __init adv_setup(char *str)
  78{
  79        int ints[4];
  80
  81        str = get_options(str, ARRAY_SIZE(ints), ints);
  82
  83        if(ints[0] > 0){
  84                wdt_stop = ints[1];
  85                if(ints[0] > 1)
  86                        wdt_start = ints[2];
  87        }
  88
  89        return 1;
  90}
  91
  92__setup("advwdt=", adv_setup);
  93
  94#endif /* !MODULE */
  95
  96MODULE_PARM(wdt_stop, "i");
  97MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)");
  98MODULE_PARM(wdt_start, "i");
  99MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)");
 100
 101static void
 102advwdt_ping(void)
 103{
 104        /* Write a watchdog value */
 105        outb_p(wd_margin, wdt_start);
 106}
 107
 108static void
 109advwdt_disable(void)
 110{
 111        inb_p(wdt_stop);
 112}
 113
 114static ssize_t
 115advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 116{
 117        /*  Can't seek (pwrite) on this device  */
 118        if (ppos != &file->f_pos)
 119                return -ESPIPE;
 120
 121        if (count) {
 122                if (!nowayout) {
 123                        size_t i;
 124
 125                        adv_expect_close = 0;
 126        
 127                        for (i = 0; i != count; i++) {
 128                                char c;
 129                                if(get_user(c, buf+i))
 130                                        return -EFAULT;
 131                                if (c == 'V')
 132                                        adv_expect_close = 42;
 133                        }
 134                }
 135                        advwdt_ping();
 136        }
 137        return count;
 138}
 139
 140static int
 141advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 142          unsigned long arg)
 143{
 144        int new_margin;
 145        static struct watchdog_info ident = {
 146                options:                WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
 147                firmware_version:       0,
 148                identity:               "Advantech WDT"
 149        };
 150
 151        switch (cmd) {
 152        case WDIOC_GETSUPPORT:
 153          if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
 154            return -EFAULT;
 155          break;
 156
 157        case WDIOC_GETSTATUS:
 158        case WDIOC_GETBOOTSTATUS:
 159          return put_user(0, (int *)arg);
 160
 161        case WDIOC_KEEPALIVE:
 162          advwdt_ping();
 163          break;
 164
 165        case WDIOC_SETTIMEOUT:
 166          if (get_user(new_margin, (int *)arg))
 167                  return -EFAULT;
 168          if ((new_margin < 1) || (new_margin > 63))
 169                  return -EINVAL;
 170          wd_margin = new_margin;
 171          advwdt_ping();
 172          /* Fall */
 173
 174        case WDIOC_GETTIMEOUT:
 175          return put_user(wd_margin, (int *)arg);
 176
 177        case WDIOC_SETOPTIONS:
 178        {
 179          int options, retval = -EINVAL;
 180
 181          if (get_user(options, (int *)arg))
 182            return -EFAULT;
 183
 184          if (options & WDIOS_DISABLECARD) {
 185            advwdt_disable();
 186            retval = 0;
 187          }
 188
 189          if (options & WDIOS_ENABLECARD) {
 190            advwdt_ping();
 191            retval = 0;
 192          }
 193
 194          return retval;
 195        }
 196
 197        default:
 198          return -ENOTTY;
 199        }
 200        return 0;
 201}
 202
 203static int
 204advwdt_open(struct inode *inode, struct file *file)
 205{
 206        if (test_and_set_bit(0, &advwdt_is_open))
 207                return -EBUSY;
 208        /*
 209         *      Activate
 210         */
 211
 212        advwdt_ping();
 213        return 0;
 214}
 215
 216static int
 217advwdt_close(struct inode *inode, struct file *file)
 218{
 219        if (adv_expect_close == 42) {
 220                advwdt_disable();
 221        } else {
 222                printk(KERN_CRIT "advancetechwdt: Unexpected close, not stopping watchdog!\n");
 223                advwdt_ping();
 224        }
 225        clear_bit(0, &advwdt_is_open);
 226        adv_expect_close = 0;
 227        return 0;
 228}
 229
 230/*
 231 *      Notifier for system down
 232 */
 233
 234static int
 235advwdt_notify_sys(struct notifier_block *this, unsigned long code,
 236        void *unused)
 237{
 238        if (code == SYS_DOWN || code == SYS_HALT) {
 239                /* Turn the WDT off */
 240                advwdt_disable();
 241        }
 242        return NOTIFY_DONE;
 243}
 244 
 245/*
 246 *      Kernel Interfaces
 247 */
 248 
 249static struct file_operations advwdt_fops = {
 250        owner:          THIS_MODULE,
 251        llseek:         no_llseek,
 252        write:          advwdt_write,
 253        ioctl:          advwdt_ioctl,
 254        open:           advwdt_open,
 255        release:        advwdt_close,
 256};
 257
 258static struct miscdevice advwdt_miscdev = {
 259        minor:          WATCHDOG_MINOR,
 260        name:           "watchdog",
 261        fops:           &advwdt_fops,
 262};
 263
 264/*
 265 *      The WDT needs to learn about soft shutdowns in order to
 266 *      turn the timebomb registers off.
 267 */
 268 
 269static struct notifier_block advwdt_notifier = {
 270        advwdt_notify_sys,
 271        NULL,
 272        0
 273};
 274
 275static int __init
 276advwdt_init(void)
 277{
 278        printk(KERN_INFO "WDT driver for Advantech single board computer initialising.\n");
 279
 280        misc_register(&advwdt_miscdev);
 281        if(wdt_stop != wdt_start)
 282                request_region(wdt_stop, 1, "Advantech WDT");
 283        request_region(wdt_start, 1, "Advantech WDT");
 284        register_reboot_notifier(&advwdt_notifier);
 285        return 0;
 286}
 287
 288static void __exit
 289advwdt_exit(void)
 290{
 291        misc_deregister(&advwdt_miscdev);
 292        unregister_reboot_notifier(&advwdt_notifier);
 293        if(wdt_stop != wdt_start)
 294                release_region(wdt_stop,1);
 295        release_region(wdt_start,1);
 296}
 297
 298module_init(advwdt_init);
 299module_exit(advwdt_exit);
 300
 301MODULE_LICENSE("GPL");
 302MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
 303MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");
 304EXPORT_NO_SYMBOLS;
 305
 306/* end of advantechwdt.c */
 307
 308
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.