Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Intel 21285 watchdog driver
0004  *  Copyright (c) Phil Blundell <pb@nexus.co.uk>, 1998
0005  *
0006  *  based on
0007  *
0008  *  SoftDog 0.05:   A Software Watchdog Device
0009  *
0010  *  (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
0011  *                      All Rights Reserved.
0012  */
0013 
0014 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0015 
0016 #include <linux/module.h>
0017 #include <linux/moduleparam.h>
0018 #include <linux/types.h>
0019 #include <linux/kernel.h>
0020 #include <linux/fs.h>
0021 #include <linux/mm.h>
0022 #include <linux/miscdevice.h>
0023 #include <linux/watchdog.h>
0024 #include <linux/reboot.h>
0025 #include <linux/init.h>
0026 #include <linux/interrupt.h>
0027 #include <linux/uaccess.h>
0028 #include <linux/irq.h>
0029 #include <mach/hardware.h>
0030 
0031 #include <asm/mach-types.h>
0032 #include <asm/system_info.h>
0033 #include <asm/hardware/dec21285.h>
0034 
0035 /*
0036  * Define this to stop the watchdog actually rebooting the machine.
0037  */
0038 #undef ONLY_TESTING
0039 
0040 static unsigned int soft_margin = 60;       /* in seconds */
0041 static unsigned int reload;
0042 static unsigned long timer_alive;
0043 
0044 #ifdef ONLY_TESTING
0045 /*
0046  *  If the timer expires..
0047  */
0048 static void watchdog_fire(int irq, void *dev_id)
0049 {
0050     pr_crit("Would Reboot\n");
0051     *CSR_TIMER4_CNTL = 0;
0052     *CSR_TIMER4_CLR = 0;
0053 }
0054 #endif
0055 
0056 /*
0057  *  Refresh the timer.
0058  */
0059 static void watchdog_ping(void)
0060 {
0061     *CSR_TIMER4_LOAD = reload;
0062 }
0063 
0064 /*
0065  *  Allow only one person to hold it open
0066  */
0067 static int watchdog_open(struct inode *inode, struct file *file)
0068 {
0069     int ret;
0070 
0071     if (*CSR_SA110_CNTL & (1 << 13))
0072         return -EBUSY;
0073 
0074     if (test_and_set_bit(1, &timer_alive))
0075         return -EBUSY;
0076 
0077     reload = soft_margin * (mem_fclk_21285 / 256);
0078 
0079     *CSR_TIMER4_CLR = 0;
0080     watchdog_ping();
0081     *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD
0082         | TIMER_CNTL_DIV256;
0083 
0084 #ifdef ONLY_TESTING
0085     ret = request_irq(IRQ_TIMER4, watchdog_fire, 0, "watchdog", NULL);
0086     if (ret) {
0087         *CSR_TIMER4_CNTL = 0;
0088         clear_bit(1, &timer_alive);
0089     }
0090 #else
0091     /*
0092      * Setting this bit is irreversible; once enabled, there is
0093      * no way to disable the watchdog.
0094      */
0095     *CSR_SA110_CNTL |= 1 << 13;
0096 
0097     ret = 0;
0098 #endif
0099     stream_open(inode, file);
0100     return ret;
0101 }
0102 
0103 /*
0104  *  Shut off the timer.
0105  *  Note: if we really have enabled the watchdog, there
0106  *  is no way to turn off.
0107  */
0108 static int watchdog_release(struct inode *inode, struct file *file)
0109 {
0110 #ifdef ONLY_TESTING
0111     free_irq(IRQ_TIMER4, NULL);
0112     clear_bit(1, &timer_alive);
0113 #endif
0114     return 0;
0115 }
0116 
0117 static ssize_t watchdog_write(struct file *file, const char __user *data,
0118                   size_t len, loff_t *ppos)
0119 {
0120     /*
0121      *  Refresh the timer.
0122      */
0123     if (len)
0124         watchdog_ping();
0125 
0126     return len;
0127 }
0128 
0129 static const struct watchdog_info ident = {
0130     .options    = WDIOF_SETTIMEOUT,
0131     .identity   = "Footbridge Watchdog",
0132 };
0133 
0134 static long watchdog_ioctl(struct file *file, unsigned int cmd,
0135                unsigned long arg)
0136 {
0137     int __user *int_arg = (int __user *)arg;
0138     int new_margin, ret = -ENOTTY;
0139 
0140     switch (cmd) {
0141     case WDIOC_GETSUPPORT:
0142         ret = 0;
0143         if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
0144             ret = -EFAULT;
0145         break;
0146 
0147     case WDIOC_GETSTATUS:
0148     case WDIOC_GETBOOTSTATUS:
0149         ret = put_user(0, int_arg);
0150         break;
0151 
0152     case WDIOC_KEEPALIVE:
0153         watchdog_ping();
0154         ret = 0;
0155         break;
0156 
0157     case WDIOC_SETTIMEOUT:
0158         ret = get_user(new_margin, int_arg);
0159         if (ret)
0160             break;
0161 
0162         /* Arbitrary, can't find the card's limits */
0163         if (new_margin < 0 || new_margin > 60) {
0164             ret = -EINVAL;
0165             break;
0166         }
0167 
0168         soft_margin = new_margin;
0169         reload = soft_margin * (mem_fclk_21285 / 256);
0170         watchdog_ping();
0171         fallthrough;
0172     case WDIOC_GETTIMEOUT:
0173         ret = put_user(soft_margin, int_arg);
0174         break;
0175     }
0176     return ret;
0177 }
0178 
0179 static const struct file_operations watchdog_fops = {
0180     .owner      = THIS_MODULE,
0181     .llseek     = no_llseek,
0182     .write      = watchdog_write,
0183     .unlocked_ioctl = watchdog_ioctl,
0184     .compat_ioctl   = compat_ptr_ioctl,
0185     .open       = watchdog_open,
0186     .release    = watchdog_release,
0187 };
0188 
0189 static struct miscdevice watchdog_miscdev = {
0190     .minor      = WATCHDOG_MINOR,
0191     .name       = "watchdog",
0192     .fops       = &watchdog_fops,
0193 };
0194 
0195 static int __init footbridge_watchdog_init(void)
0196 {
0197     int retval;
0198 
0199     if (machine_is_netwinder())
0200         return -ENODEV;
0201 
0202     retval = misc_register(&watchdog_miscdev);
0203     if (retval < 0)
0204         return retval;
0205 
0206     pr_info("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n",
0207         soft_margin);
0208 
0209     if (machine_is_cats())
0210         pr_warn("Warning: Watchdog reset may not work on this machine\n");
0211     return 0;
0212 }
0213 
0214 static void __exit footbridge_watchdog_exit(void)
0215 {
0216     misc_deregister(&watchdog_miscdev);
0217 }
0218 
0219 MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");
0220 MODULE_DESCRIPTION("Footbridge watchdog driver");
0221 MODULE_LICENSE("GPL");
0222 
0223 module_param(soft_margin, int, 0);
0224 MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
0225 
0226 module_init(footbridge_watchdog_init);
0227 module_exit(footbridge_watchdog_exit);