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        int __user *int_arg = (int __user *)arg;
 143        int new_margin, ret = -ENOTTY;
 144
 145        switch (cmd) {
 146        case WDIOC_GETSUPPORT:
 147                ret = 0;
 148                if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
 149                        ret = -EFAULT;
 150                break;
 151
 152        case WDIOC_GETSTATUS:
 153        case WDIOC_GETBOOTSTATUS:
 154                ret = put_user(0, int_arg);
 155                break;
 156
 157        case WDIOC_KEEPALIVE:
 158                watchdog_ping();
 159                ret = 0;
 160                break;
 161
 162        case WDIOC_SETTIMEOUT:
 163                ret = get_user(new_margin, int_arg);
 164                if (ret)
 165                        break;
 166
 167                /* Arbitrary, can't find the card's limits */
 168                if (new_margin < 0 || new_margin > 60) {
 169                        ret = -EINVAL;
 170                        break;
 171                }
 172
 173                soft_margin = new_margin;
 174                reload = soft_margin * (mem_fclk_21285 / 256);
 175                watchdog_ping();
 176                /* Fall */
 177        case WDIOC_GETTIMEOUT:
 178                ret = put_user(soft_margin, int_arg);
 179                break;
 180        }
 181        return ret;
 182}
 183
 184static const struct file_operations watchdog_fops = {
 185        .owner          = THIS_MODULE,
 186        .llseek         = no_llseek,
 187        .write          = watchdog_write,
 188        .unlocked_ioctl = watchdog_ioctl,
 189        .open           = watchdog_open,
 190        .release        = watchdog_release,
 191};
 192
 193static struct miscdevice watchdog_miscdev = {
 194        .minor          = WATCHDOG_MINOR,
 195        .name           = "watchdog",
 196        .fops           = &watchdog_fops,
 197};
 198
 199static int __init footbridge_watchdog_init(void)
 200{
 201        int retval;
 202
 203        if (machine_is_netwinder())
 204                return -ENODEV;
 205
 206        retval = misc_register(&watchdog_miscdev);
 207        if (retval < 0)
 208                return retval;
 209
 210        pr_info("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n",
 211                soft_margin);
 212
 213        if (machine_is_cats())
 214                pr_warn("Warning: Watchdog reset may not work on this machine\n");
 215        return 0;
 216}
 217
 218static void __exit footbridge_watchdog_exit(void)
 219{
 220        misc_deregister(&watchdog_miscdev);
 221}
 222
 223MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");
 224MODULE_DESCRIPTION("Footbridge watchdog driver");
 225MODULE_LICENSE("GPL");
 226
 227module_param(soft_margin, int, 0);
 228MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
 229
 230module_init(footbridge_watchdog_init);
 231module_exit(footbridge_watchdog_exit);
 232