linux/drivers/watchdog/ixp2000_wdt.c
<<
>>
Prefs
   1/*
   2 * drivers/char/watchdog/ixp2000_wdt.c
   3 *
   4 * Watchdog driver for Intel IXP2000 network processors
   5 *
   6 * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek.
   7 * The original version carries these notices:
   8 *
   9 * Author: Deepak Saxena <dsaxena@plexity.net>
  10 *
  11 * Copyright 2004 (c) MontaVista, Software, Inc.
  12 * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  13 *
  14 * This file is licensed under  the terms of the GNU General Public
  15 * License version 2. This program is licensed "as is" without any
  16 * warranty of any kind, whether express or implied.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/moduleparam.h>
  21#include <linux/types.h>
  22#include <linux/kernel.h>
  23#include <linux/fs.h>
  24#include <linux/miscdevice.h>
  25#include <linux/watchdog.h>
  26#include <linux/init.h>
  27#include <linux/bitops.h>
  28#include <linux/uaccess.h>
  29#include <mach/hardware.h>
  30
  31static int nowayout = WATCHDOG_NOWAYOUT;
  32static unsigned int heartbeat = 60;     /* (secs) Default is 1 minute */
  33static unsigned long wdt_status;
  34static spinlock_t wdt_lock;
  35
  36#define WDT_IN_USE              0
  37#define WDT_OK_TO_CLOSE         1
  38
  39static unsigned long wdt_tick_rate;
  40
  41static void wdt_enable(void)
  42{
  43        spin_lock(&wdt_lock);
  44        ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE);
  45        ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE);
  46        ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
  47        ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE);
  48        spin_unlock(&wdt_lock);
  49}
  50
  51static void wdt_disable(void)
  52{
  53        spin_lock(&wdt_lock);
  54        ixp2000_reg_write(IXP2000_T4_CTL, 0);
  55        spin_unlock(&wdt_lock);
  56}
  57
  58static void wdt_keepalive(void)
  59{
  60        spin_lock(&wdt_lock);
  61        ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
  62        spin_unlock(&wdt_lock);
  63}
  64
  65static int ixp2000_wdt_open(struct inode *inode, struct file *file)
  66{
  67        if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  68                return -EBUSY;
  69
  70        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  71
  72        wdt_enable();
  73
  74        return nonseekable_open(inode, file);
  75}
  76
  77static ssize_t ixp2000_wdt_write(struct file *file, const char *data,
  78                                                size_t len, loff_t *ppos)
  79{
  80        if (len) {
  81                if (!nowayout) {
  82                        size_t i;
  83
  84                        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  85
  86                        for (i = 0; i != len; i++) {
  87                                char c;
  88
  89                                if (get_user(c, data + i))
  90                                        return -EFAULT;
  91                                if (c == 'V')
  92                                        set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  93                        }
  94                }
  95                wdt_keepalive();
  96        }
  97
  98        return len;
  99}
 100
 101
 102static struct watchdog_info ident = {
 103        .options        = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
 104                                WDIOF_KEEPALIVEPING,
 105        .identity       = "IXP2000 Watchdog",
 106};
 107
 108static long ixp2000_wdt_ioctl(struct file *file, unsigned int cmd,
 109                                                        unsigned long arg)
 110{
 111        int ret = -ENOTTY;
 112        int time;
 113
 114        switch (cmd) {
 115        case WDIOC_GETSUPPORT:
 116                ret = copy_to_user((struct watchdog_info *)arg, &ident,
 117                                   sizeof(ident)) ? -EFAULT : 0;
 118                break;
 119
 120        case WDIOC_GETSTATUS:
 121                ret = put_user(0, (int *)arg);
 122                break;
 123
 124        case WDIOC_GETBOOTSTATUS:
 125                ret = put_user(0, (int *)arg);
 126                break;
 127
 128        case WDIOC_KEEPALIVE:
 129                wdt_enable();
 130                ret = 0;
 131                break;
 132
 133        case WDIOC_SETTIMEOUT:
 134                ret = get_user(time, (int *)arg);
 135                if (ret)
 136                        break;
 137
 138                if (time <= 0 || time > 60) {
 139                        ret = -EINVAL;
 140                        break;
 141                }
 142
 143                heartbeat = time;
 144                wdt_keepalive();
 145                /* Fall through */
 146
 147        case WDIOC_GETTIMEOUT:
 148                ret = put_user(heartbeat, (int *)arg);
 149                break;
 150        }
 151
 152        return ret;
 153}
 154
 155static int ixp2000_wdt_release(struct inode *inode, struct file *file)
 156{
 157        if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
 158                wdt_disable();
 159        else
 160                printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
 161                                        "timer will not stop\n");
 162        clear_bit(WDT_IN_USE, &wdt_status);
 163        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
 164
 165        return 0;
 166}
 167
 168
 169static const struct file_operations ixp2000_wdt_fops = {
 170        .owner          = THIS_MODULE,
 171        .llseek         = no_llseek,
 172        .write          = ixp2000_wdt_write,
 173        .unlocked_ioctl = ixp2000_wdt_ioctl,
 174        .open           = ixp2000_wdt_open,
 175        .release        = ixp2000_wdt_release,
 176};
 177
 178static struct miscdevice ixp2000_wdt_miscdev = {
 179        .minor          = WATCHDOG_MINOR,
 180        .name           = "watchdog",
 181        .fops           = &ixp2000_wdt_fops,
 182};
 183
 184static int __init ixp2000_wdt_init(void)
 185{
 186        if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) {
 187                printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n");
 188                return -EIO;
 189        }
 190        wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256;
 191        spin_lock_init(&wdt_lock);
 192        return misc_register(&ixp2000_wdt_miscdev);
 193}
 194
 195static void __exit ixp2000_wdt_exit(void)
 196{
 197        misc_deregister(&ixp2000_wdt_miscdev);
 198}
 199
 200module_init(ixp2000_wdt_init);
 201module_exit(ixp2000_wdt_exit);
 202
 203MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
 204MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog");
 205
 206module_param(heartbeat, int, 0);
 207MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
 208
 209module_param(nowayout, int, 0);
 210MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
 211
 212MODULE_LICENSE("GPL");
 213MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 214
 215