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