225 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Intel 21285 watchdog driver4 * Copyright (c) Phil Blundell <pb@nexus.co.uk>, 19985 *6 * based on7 *8 * SoftDog 0.05: A Software Watchdog Device9 *10 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,11 * All Rights Reserved.12 */13 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt15 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_TESTING39 40static unsigned int soft_margin = 60; /* in seconds */41static unsigned int reload;42static unsigned long timer_alive;43 44#ifdef ONLY_TESTING45/*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#endif55 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 open66 */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_AUTORELOAD82 | TIMER_CNTL_DIV256;83 84#ifdef ONLY_TESTING85 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#else91 /*92 * Setting this bit is irreversible; once enabled, there is93 * no way to disable the watchdog.94 */95 *CSR_SA110_CNTL |= 1 << 13;96 97 ret = 0;98#endif99 stream_open(inode, file);100 return ret;101}102 103/*104 * Shut off the timer.105 * Note: if we really have enabled the watchdog, there106 * is no way to turn off.107 */108static int watchdog_release(struct inode *inode, struct file *file)109{110#ifdef ONLY_TESTING111 free_irq(IRQ_TIMER4, NULL);112 clear_bit(1, &timer_alive);113#endif114 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 .write = watchdog_write,182 .unlocked_ioctl = watchdog_ioctl,183 .compat_ioctl = compat_ptr_ioctl,184 .open = watchdog_open,185 .release = watchdog_release,186};187 188static struct miscdevice watchdog_miscdev = {189 .minor = WATCHDOG_MINOR,190 .name = "watchdog",191 .fops = &watchdog_fops,192};193 194static int __init footbridge_watchdog_init(void)195{196 int retval;197 198 if (machine_is_netwinder())199 return -ENODEV;200 201 retval = misc_register(&watchdog_miscdev);202 if (retval < 0)203 return retval;204 205 pr_info("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n",206 soft_margin);207 208 return 0;209}210 211static void __exit footbridge_watchdog_exit(void)212{213 misc_deregister(&watchdog_miscdev);214}215 216MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>");217MODULE_DESCRIPTION("Footbridge watchdog driver");218MODULE_LICENSE("GPL");219 220module_param(soft_margin, int, 0);221MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");222 223module_init(footbridge_watchdog_init);224module_exit(footbridge_watchdog_exit);225