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 42 static unsigned int soft_margin = 60; /* in seconds */ 43 static unsigned int reload; 44 static unsigned long timer_alive; 45 46 #ifdef ONLY_TESTING 47 /* 48 * If the timer expires.. 49 */ 50 static 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 */ 61 static void watchdog_ping(void) 62 { 63 *CSR_TIMER4_LOAD = reload; 64 } 65 66 /* 67 * Allow only one person to hold it open 68 */ 69 static 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 */ 110 static 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 119 static 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 131 static const struct watchdog_info ident = { 132 .options = WDIOF_SETTIMEOUT, 133 .identity = "Footbridge Watchdog", 134 }; 135 136 static 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 182 static 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 191 static struct miscdevice watchdog_miscdev = { 192 .minor = WATCHDOG_MINOR, 193 .name = "watchdog", 194 .fops = &watchdog_fops, 195 }; 196 197 static 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 218 static void __exit footbridge_watchdog_exit(void) 219 { 220 misc_deregister(&watchdog_miscdev); 221 } 222 223 MODULE_AUTHOR("Phil Blundell <pb@nexus.co.uk>"); 224 MODULE_DESCRIPTION("Footbridge watchdog driver"); 225 MODULE_LICENSE("GPL"); 226 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 227 228 module_param(soft_margin, int, 0); 229 MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); 230 231 module_init(footbridge_watchdog_init); 232 module_exit(footbridge_watchdog_exit); 233