linux/drivers/watchdog/wdt285.c
<<
>>
Prefs
   1/*
   2 *      Intel 21285 watchdog driver
   3 *      Copyright (c) Phil Blundell <pb@nexus.co.uk>, 1998
   4 *
   5 *      based on
   6 *
   7 *      SoftDog 0.05:   A Software Watchdog Device
   8 *
   9 *      (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
  10 *                                              All Rights Reserved.
  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 */
  18
  19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  20
  21#include <linux/module.h>
  22#include <linux/moduleparam.h>
  23#include <linux/types.h>
  24#include <linux/kernel.h>
  25#include <linux/fs.h>
  26#include <linux/mm.h>
  27#include <linux/miscdevice.h>
  28#include <linux/watchdog.h>
  29#include <linux/reboot.h>
  30#include <linux/init.h>
  31#include <linux/interrupt.h>
  32#include <linux/uaccess.h>
  33#include <linux/irq.h>
  34#include <mach/hardware.h>
  35
  36#include <asm/mach-types.h>
  37#include <asm/system_info.h>
  38#include <asm/hardware/dec21285.h>
  39
  40/*
  41 * Define this to stop the watchdog actually rebooting the machine.
  42 */
  43#undef ONLY_TESTING
  44
  45static unsigned int soft_margin = 60;           /* in seconds */
  46static unsigned int reload;
  47static unsigned long timer_alive;
  48
  49#ifdef ONLY_TESTING
  50/*
  51 *      If the timer expires..
  52 */
  53static void watchdog_fire(int irq, void *dev_id)
  54{
  55        pr_crit("Would Reboot\n");
  56        *CSR_TIMER4_CNTL = 0;
  57        *CSR_TIMER4_CLR = 0;
  58}
  59#endif
  60
  61/*
  62 *      Refresh the timer.
  63 */
  64static void watchdog_ping(void)
  65{
  66        *CSR_TIMER4_LOAD = reload;
  67}
  68
  69/*
  70 *      Allow only one person to hold it open
  71 */
  72static int watchdog_open(struct inode *inode, struct file *file)
  73{
  74        int ret;
  75
  76        if (*CSR_SA110_CNTL & (1 << 13))
  77                return -EBUSY;
  78
  79        if (test_and_set_bit(1, &timer_alive))
  80                return -EBUSY;
  81
  82        reload = soft_margin * (mem_fclk_21285 / 256);
  83
  84        *CSR_TIMER4_CLR = 0;
  85        watchdog_ping();
  86        *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD
  87                | TIMER_CNTL_DIV256;
  88
  89#ifdef ONLY_TESTING
  90        ret = request_irq(IRQ_TIMER4, watchdog_fire, 0, "watchdog", NULL);
  91        if (ret) {
  92                *CSR_TIMER4_CNTL = 0;
  93                clear_bit(1, &timer_alive);
  94        }
  95#else
  96        /*
  97         * Setting this bit is irreversible; once enabled, there is
  98         * no way to disable the watchdog.
  99         */
 100        *CSR_SA110_CNTL |= 1 << 13;
 101
 102        ret = 0;
 103#endif
 104        nonseekable_open(inode, file);
 105        return ret;
 106}
 107
 108/*
 109 *      Shut off the timer.
 110 *      Note: if we really have enabled the watchdog, there
 111 *      is no way to turn off.
 112 */
 113static int watchdog_release(struct inode *inode, struct file *file)
 114{
 115#ifdef ONLY_TESTING
 116        free_irq(IRQ_TIMER4, NULL);
 117        clear_bit(1, &timer_alive);
 118#endif
 119        return 0;
 120}
 121
 122static ssize_t watchdog_write(struct file *file, const char __user *data,
 123                              size_t len, loff_t *ppos)
 124{
 125        /*
 126         *      Refresh the timer.
 127         */
 128        if (len)
 129                watchdog_ping();
 130
 131        return len;
 132}
 133
 134static const struct watchdog_info ident = {
 135        .options        = WDIOF_SETTIMEOUT,
 136        .identity       = "Footbridge Watchdog",
 137};
 138
 139static long watchdog_ioctl(struct file *file, unsigned int cmd,
 140                           unsigned long arg)
 141{
 142        unsigned int new_margin;
 143        int __user *int_arg = (int __user *)arg;
 144        int ret = -ENOTTY;
 145
 146        switch (cmd) {
 147        case WDIOC_GETSUPPORT:
 148                ret = 0;
 149                if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
 150                        ret = -EFAULT;
 151                break;
 152
 153        case WDIOC_GETSTATUS:
 154        case WDIOC_GETBOOTSTATUS:
 155                ret = put_user(0, int_arg);
 156                break;
 157
 158        case WDIOC_KEEPALIVE:
 159                watchdog_ping();
 160                ret = 0;
 161                break;
 162
 163        case WDIOC_SETTIMEOUT:
 164                ret = get_user(new_margin, int_arg);
 165                if (ret)
 166                        break;
 167
 168                /* Arbitrary, can't find the card's limits */
 169                if (new_margin < 0 || new_margin > 60) {
 170                        ret = -EINVAL;
 171                        break;
 172                }
 173
 174                soft_margin = new_margin;
 175                reload = soft_margin * (mem_fclk_21285 / 256);
 176                watchdog_ping();
 177                /* Fall */
 178        case WDIOC_GETTIMEOUT:
 179                ret = put_user(soft_margin, int_arg);
 180                break;
 181        }
 182        return ret;
 183}
 184
 185static const struct file_operations watchdog_fops = {
 186        .owner          = THIS_MODULE,
 187        .llseek         = no_llseek,
 188        .write          = watchdog_write,
 189        .unlocked_ioctl = watchdog_ioctl,
 190        .open           = watchdog_open,
 191        .release        = watchdog_release,
 192};
 193
 194static struct miscdevice watchdog_miscdev = {
 195        .minor          = WATCHDOG_MINOR,
 196        .name           = "watchdog",
 197        .fops           = &watchdog_fops,
 198};
 199
 200static int __init footbridge_watchdog_init(void)
 201{
 202        int retval;
 203
 204        if (machine_is_netwinder())
 205                return -ENODEV;
 206
 207        retval = misc_register(&watchdog_miscdev);
 208        if (retval < 0)
 209                return retval;
 210
 211        pr_info("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n",
 212                soft_margin);
 213
 214        if (machine_is_cats())
 215                pr_warn("Warning: Watchdog reset may not work on this machine\n");
 216        return 0;
 217}
 218
 219static void __exit footbridge_watchdog_exit(void)
 220{
 221        misc_deregister(&watchdog_miscdev);
 222}
 223
 224MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");
 225MODULE_DESCRIPTION("Footbridge watchdog driver");
 226MODULE_LICENSE("GPL");
 227MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 228
 229module_param(soft_margin, int, 0);
 230MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
 231
 232module_init(footbridge_watchdog_init);
 233module_exit(footbridge_watchdog_exit);
 234