1 /* 2 * IDT Interprise 79RC32434 watchdog driver 3 * 4 * Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org> 5 * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org> 6 * 7 * based on 8 * SoftDog 0.05: A Software Watchdog Device 9 * 10 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, 11 * All Rights Reserved. 12 * 13 * This program is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU General Public License 15 * as published by the Free Software Foundation; either version 16 * 2 of the License, or (at your option) any later version. 17 * 18 */ 19 20 #include <linux/module.h> /* For module specific items */ 21 #include <linux/moduleparam.h> /* For new moduleparam's */ 22 #include <linux/types.h> /* For standard types (like size_t) */ 23 #include <linux/errno.h> /* For the -ENODEV/... values */ 24 #include <linux/kernel.h> /* For printk/panic/... */ 25 #include <linux/fs.h> /* For file operations */ 26 #include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV 27 (WATCHDOG_MINOR) */ 28 #include <linux/watchdog.h> /* For the watchdog specific items */ 29 #include <linux/init.h> /* For __init/__exit/... */ 30 #include <linux/platform_device.h> /* For platform_driver framework */ 31 #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ 32 #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ 33 34 #include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */ 35 36 #define PFX KBUILD_MODNAME ": " 37 38 #define VERSION "1.0" 39 40 static struct { 41 unsigned long inuse; 42 spinlock_t io_lock; 43 } rc32434_wdt_device; 44 45 static struct integ __iomem *wdt_reg; 46 47 static int expect_close; 48 49 /* Board internal clock speed in Hz, 50 * the watchdog timer ticks at. */ 51 extern unsigned int idt_cpu_freq; 52 53 /* translate wtcompare value to seconds and vice versa */ 54 #define WTCOMP2SEC(x) (x / idt_cpu_freq) 55 #define SEC2WTCOMP(x) (x * idt_cpu_freq) 56 57 /* Use a default timeout of 20s. This should be 58 * safe for CPU clock speeds up to 400MHz, as 59 * ((2 ^ 32) - 1) / (400MHz / 2) = 21s. */ 60 #define WATCHDOG_TIMEOUT 20 61 62 static int timeout = WATCHDOG_TIMEOUT; 63 module_param(timeout, int, 0); 64 MODULE_PARM_DESC(timeout, "Watchdog timeout value, in seconds (default=" 65 __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); 66 67 static int nowayout = WATCHDOG_NOWAYOUT; 68 module_param(nowayout, int, 0); 69 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 70 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 71 72 /* apply or and nand masks to data read from addr and write back */ 73 #define SET_BITS(addr, or, nand) \ 74 writel((readl(&addr) | or) & ~nand, &addr) 75 76 static int rc32434_wdt_set(int new_timeout) 77 { 78 int max_to = WTCOMP2SEC((u32)-1); 79 80 if (new_timeout < 0 || new_timeout > max_to) { 81 printk(KERN_ERR PFX "timeout value must be between 0 and %d", 82 max_to); 83 return -EINVAL; 84 } 85 timeout = new_timeout; 86 spin_lock(&rc32434_wdt_device.io_lock); 87 writel(SEC2WTCOMP(timeout), &wdt_reg->wtcompare); 88 spin_unlock(&rc32434_wdt_device.io_lock); 89 90 return 0; 91 } 92 93 static void rc32434_wdt_start(void) 94 { 95 u32 or, nand; 96 97 spin_lock(&rc32434_wdt_device.io_lock); 98 99 /* zero the counter before enabling */ 100 writel(0, &wdt_reg->wtcount); 101 102 /* don't generate a non-maskable interrupt, 103 * do a warm reset instead */ 104 nand = 1 << RC32434_ERR_WNE; 105 or = 1 << RC32434_ERR_WRE; 106 107 /* reset the ERRCS timeout bit in case it's set */ 108 nand |= 1 << RC32434_ERR_WTO; 109 110 SET_BITS(wdt_reg->errcs, or, nand); 111 112 /* set the timeout (either default or based on module param) */ 113 rc32434_wdt_set(timeout); 114 115 /* reset WTC timeout bit and enable WDT */ 116 nand = 1 << RC32434_WTC_TO; 117 or = 1 << RC32434_WTC_EN; 118 119 SET_BITS(wdt_reg->wtc, or, nand); 120 121 spin_unlock(&rc32434_wdt_device.io_lock); 122 printk(KERN_INFO PFX "Started watchdog timer.\n"); 123 } 124 125 static void rc32434_wdt_stop(void) 126 { 127 spin_lock(&rc32434_wdt_device.io_lock); 128 129 /* Disable WDT */ 130 SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN); 131 132 spin_unlock(&rc32434_wdt_device.io_lock); 133 printk(KERN_INFO PFX "Stopped watchdog timer.\n"); 134 } 135 136 static void rc32434_wdt_ping(void) 137 { 138 spin_lock(&rc32434_wdt_device.io_lock); 139 writel(0, &wdt_reg->wtcount); 140 spin_unlock(&rc32434_wdt_device.io_lock); 141 } 142 143 static int rc32434_wdt_open(struct inode *inode, struct file *file) 144 { 145 if (test_and_set_bit(0, &rc32434_wdt_device.inuse)) 146 return -EBUSY; 147 148 if (nowayout) 149 __module_get(THIS_MODULE); 150 151 rc32434_wdt_start(); 152 rc32434_wdt_ping(); 153 154 return nonseekable_open(inode, file); 155 } 156 157 static int rc32434_wdt_release(struct inode *inode, struct file *file) 158 { 159 if (expect_close == 42) { 160 rc32434_wdt_stop(); 161 module_put(THIS_MODULE); 162 } else { 163 printk(KERN_CRIT PFX 164 "device closed unexpectedly. WDT will not stop!\n"); 165 rc32434_wdt_ping(); 166 } 167 clear_bit(0, &rc32434_wdt_device.inuse); 168 return 0; 169 } 170 171 static ssize_t rc32434_wdt_write(struct file *file, const char *data, 172 size_t len, loff_t *ppos) 173 { 174 if (len) { 175 if (!nowayout) { 176 size_t i; 177 178 /* In case it was set long ago */ 179 expect_close = 0; 180 181 for (i = 0; i != len; i++) { 182 char c; 183 if (get_user(c, data + i)) 184 return -EFAULT; 185 if (c == 'V') 186 expect_close = 42; 187 } 188 } 189 rc32434_wdt_ping(); 190 return len; 191 } 192 return 0; 193 } 194 195 static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, 196 unsigned long arg) 197 { 198 void __user *argp = (void __user *)arg; 199 int new_timeout; 200 unsigned int value; 201 static const struct watchdog_info ident = { 202 .options = WDIOF_SETTIMEOUT | 203 WDIOF_KEEPALIVEPING | 204 WDIOF_MAGICCLOSE, 205 .identity = "RC32434_WDT Watchdog", 206 }; 207 switch (cmd) { 208 case WDIOC_GETSUPPORT: 209 if (copy_to_user(argp, &ident, sizeof(ident))) 210 return -EFAULT; 211 break; 212 case WDIOC_GETSTATUS: 213 case WDIOC_GETBOOTSTATUS: 214 value = 0; 215 if (copy_to_user(argp, &value, sizeof(int))) 216 return -EFAULT; 217 break; 218 case WDIOC_SETOPTIONS: 219 if (copy_from_user(&value, argp, sizeof(int))) 220 return -EFAULT; 221 switch (value) { 222 case WDIOS_ENABLECARD: 223 rc32434_wdt_start(); 224 break; 225 case WDIOS_DISABLECARD: 226 rc32434_wdt_stop(); 227 break; 228 default: 229 return -EINVAL; 230 } 231 break; 232 case WDIOC_KEEPALIVE: 233 rc32434_wdt_ping(); 234 break; 235 case WDIOC_SETTIMEOUT: 236 if (copy_from_user(&new_timeout, argp, sizeof(int))) 237 return -EFAULT; 238 if (rc32434_wdt_set(new_timeout)) 239 return -EINVAL; 240 /* Fall through */ 241 case WDIOC_GETTIMEOUT: 242 return copy_to_user(argp, &timeout, sizeof(int)); 243 default: 244 return -ENOTTY; 245 } 246 247 return 0; 248 } 249 250 static const struct file_operations rc32434_wdt_fops = { 251 .owner = THIS_MODULE, 252 .llseek = no_llseek, 253 .write = rc32434_wdt_write, 254 .unlocked_ioctl = rc32434_wdt_ioctl, 255 .open = rc32434_wdt_open, 256 .release = rc32434_wdt_release, 257 }; 258 259 static struct miscdevice rc32434_wdt_miscdev = { 260 .minor = WATCHDOG_MINOR, 261 .name = "watchdog", 262 .fops = &rc32434_wdt_fops, 263 }; 264 265 static char banner[] __devinitdata = KERN_INFO PFX 266 "Watchdog Timer version " VERSION ", timer margin: %d sec\n"; 267 268 static int __devinit rc32434_wdt_probe(struct platform_device *pdev) 269 { 270 int ret; 271 struct resource *r; 272 273 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res"); 274 if (!r) { 275 printk(KERN_ERR PFX "failed to retrieve resources\n"); 276 return -ENODEV; 277 } 278 279 wdt_reg = ioremap_nocache(r->start, resource_size(r)); 280 if (!wdt_reg) { 281 printk(KERN_ERR PFX "failed to remap I/O resources\n"); 282 return -ENXIO; 283 } 284 285 spin_lock_init(&rc32434_wdt_device.io_lock); 286 287 /* Make sure the watchdog is not running */ 288 rc32434_wdt_stop(); 289 290 /* Check that the heartbeat value is within it's range; 291 * if not reset to the default */ 292 if (rc32434_wdt_set(timeout)) { 293 rc32434_wdt_set(WATCHDOG_TIMEOUT); 294 printk(KERN_INFO PFX 295 "timeout value must be between 0 and %d\n", 296 WTCOMP2SEC((u32)-1)); 297 } 298 299 ret = misc_register(&rc32434_wdt_miscdev); 300 if (ret < 0) { 301 printk(KERN_ERR PFX "failed to register watchdog device\n"); 302 goto unmap; 303 } 304 305 printk(banner, timeout); 306 307 return 0; 308 309 unmap: 310 iounmap(wdt_reg); 311 return ret; 312 } 313 314 static int __devexit rc32434_wdt_remove(struct platform_device *pdev) 315 { 316 misc_deregister(&rc32434_wdt_miscdev); 317 iounmap(wdt_reg); 318 return 0; 319 } 320 321 static void rc32434_wdt_shutdown(struct platform_device *pdev) 322 { 323 rc32434_wdt_stop(); 324 } 325 326 static struct platform_driver rc32434_wdt_driver = { 327 .probe = rc32434_wdt_probe, 328 .remove = __devexit_p(rc32434_wdt_remove), 329 .shutdown = rc32434_wdt_shutdown, 330 .driver = { 331 .name = "rc32434_wdt", 332 } 333 }; 334 335 module_platform_driver(rc32434_wdt_driver); 336 337 MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>," 338 "Florian Fainelli <florian@openwrt.org>"); 339 MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog"); 340 MODULE_LICENSE("GPL"); 341 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 342