linux/drivers/watchdog/ixp4xx_wdt.c
<<
>>
Prefs
   1/*
   2 * drivers/char/watchdog/ixp4xx_wdt.c
   3 *
   4 * Watchdog driver for Intel IXP4xx network processors
   5 *
   6 * Author: Deepak Saxena <dsaxena@plexity.net>
   7 *
   8 * Copyright 2004 (c) MontaVista, Software, Inc.
   9 * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  10 *
  11 * This file is licensed under  the terms of the GNU General Public
  12 * License version 2. This program is licensed "as is" without any
  13 * warranty of any kind, whether express or implied.
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/moduleparam.h>
  18#include <linux/types.h>
  19#include <linux/kernel.h>
  20#include <linux/fs.h>
  21#include <linux/miscdevice.h>
  22#include <linux/watchdog.h>
  23#include <linux/init.h>
  24#include <linux/bitops.h>
  25#include <linux/uaccess.h>
  26#include <mach/hardware.h>
  27
  28static int nowayout = WATCHDOG_NOWAYOUT;
  29static int heartbeat = 60;      /* (secs) Default is 1 minute */
  30static unsigned long wdt_status;
  31static unsigned long boot_status;
  32static DEFINE_SPINLOCK(wdt_lock);
  33
  34#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL)
  35
  36#define WDT_IN_USE              0
  37#define WDT_OK_TO_CLOSE         1
  38
  39static void wdt_enable(void)
  40{
  41        spin_lock(&wdt_lock);
  42        *IXP4XX_OSWK = IXP4XX_WDT_KEY;
  43        *IXP4XX_OSWE = 0;
  44        *IXP4XX_OSWT = WDT_TICK_RATE * heartbeat;
  45        *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE;
  46        *IXP4XX_OSWK = 0;
  47        spin_unlock(&wdt_lock);
  48}
  49
  50static void wdt_disable(void)
  51{
  52        spin_lock(&wdt_lock);
  53        *IXP4XX_OSWK = IXP4XX_WDT_KEY;
  54        *IXP4XX_OSWE = 0;
  55        *IXP4XX_OSWK = 0;
  56        spin_unlock(&wdt_lock);
  57}
  58
  59static int ixp4xx_wdt_open(struct inode *inode, struct file *file)
  60{
  61        if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  62                return -EBUSY;
  63
  64        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  65        wdt_enable();
  66        return nonseekable_open(inode, file);
  67}
  68
  69static ssize_t
  70ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
  71{
  72        if (len) {
  73                if (!nowayout) {
  74                        size_t i;
  75
  76                        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  77
  78                        for (i = 0; i != len; i++) {
  79                                char c;
  80
  81                                if (get_user(c, data + i))
  82                                        return -EFAULT;
  83                                if (c == 'V')
  84                                        set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  85                        }
  86                }
  87                wdt_enable();
  88        }
  89        return len;
  90}
  91
  92static struct watchdog_info ident = {
  93        .options        = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
  94                          WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  95        .identity       = "IXP4xx Watchdog",
  96};
  97
  98
  99static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd,
 100                                                        unsigned long arg)
 101{
 102        int ret = -ENOTTY;
 103        int time;
 104
 105        switch (cmd) {
 106        case WDIOC_GETSUPPORT:
 107                ret = copy_to_user((struct watchdog_info *)arg, &ident,
 108                                   sizeof(ident)) ? -EFAULT : 0;
 109                break;
 110
 111        case WDIOC_GETSTATUS:
 112                ret = put_user(0, (int *)arg);
 113                break;
 114
 115        case WDIOC_GETBOOTSTATUS:
 116                ret = put_user(boot_status, (int *)arg);
 117                break;
 118
 119        case WDIOC_KEEPALIVE:
 120                wdt_enable();
 121                ret = 0;
 122                break;
 123
 124        case WDIOC_SETTIMEOUT:
 125                ret = get_user(time, (int *)arg);
 126                if (ret)
 127                        break;
 128
 129                if (time <= 0 || time > 60) {
 130                        ret = -EINVAL;
 131                        break;
 132                }
 133
 134                heartbeat = time;
 135                wdt_enable();
 136                /* Fall through */
 137
 138        case WDIOC_GETTIMEOUT:
 139                ret = put_user(heartbeat, (int *)arg);
 140                break;
 141        }
 142        return ret;
 143}
 144
 145static int ixp4xx_wdt_release(struct inode *inode, struct file *file)
 146{
 147        if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
 148                wdt_disable();
 149        else
 150                printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
 151                                        "timer will not stop\n");
 152        clear_bit(WDT_IN_USE, &wdt_status);
 153        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
 154
 155        return 0;
 156}
 157
 158
 159static const struct file_operations ixp4xx_wdt_fops = {
 160        .owner          = THIS_MODULE,
 161        .llseek         = no_llseek,
 162        .write          = ixp4xx_wdt_write,
 163        .unlocked_ioctl = ixp4xx_wdt_ioctl,
 164        .open           = ixp4xx_wdt_open,
 165        .release        = ixp4xx_wdt_release,
 166};
 167
 168static struct miscdevice ixp4xx_wdt_miscdev = {
 169        .minor          = WATCHDOG_MINOR,
 170        .name           = "watchdog",
 171        .fops           = &ixp4xx_wdt_fops,
 172};
 173
 174static int __init ixp4xx_wdt_init(void)
 175{
 176        int ret;
 177
 178        if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) {
 179                printk(KERN_ERR "IXP4XXX Watchdog: Rev. A0 IXP42x CPU detected"
 180                        " - watchdog disabled\n");
 181
 182                return -ENODEV;
 183        }
 184        spin_lock_init(&wdt_lock);
 185        boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ?
 186                        WDIOF_CARDRESET : 0;
 187        ret = misc_register(&ixp4xx_wdt_miscdev);
 188        if (ret == 0)
 189                printk(KERN_INFO "IXP4xx Watchdog Timer: heartbeat %d sec\n",
 190                        heartbeat);
 191        return ret;
 192}
 193
 194static void __exit ixp4xx_wdt_exit(void)
 195{
 196        misc_deregister(&ixp4xx_wdt_miscdev);
 197}
 198
 199
 200module_init(ixp4xx_wdt_init);
 201module_exit(ixp4xx_wdt_exit);
 202
 203MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
 204MODULE_DESCRIPTION("IXP4xx 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