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