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