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