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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 21 22 #include <linux/module.h> /* For module specific items */ 23 #include <linux/moduleparam.h> /* For new moduleparam's */ 24 #include <linux/types.h> /* For standard types (like size_t) */ 25 #include <linux/errno.h> /* For the -ENODEV/... values */ 26 #include <linux/kernel.h> /* For printk/panic/... */ 27 #include <linux/fs.h> /* For file operations */ 28 #include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV 29 (WATCHDOG_MINOR) */ 30 #include <linux/watchdog.h> /* For the watchdog specific items */ 31 #include <linux/init.h> /* For __init/__exit/... */ 32 #include <linux/platform_device.h> /* For platform_driver framework */ 33 #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ 34 #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ 35 #include <linux/io.h> /* For devm_ioremap_nocache */ 36 37 #include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */ 38 39 #define VERSION "1.0" 40 41 static struct { 42 unsigned long inuse; 43 spinlock_t io_lock; 44 } rc32434_wdt_device; 45 46 static struct integ __iomem *wdt_reg; 47 48 static int expect_close; 49 50 /* Board internal clock speed in Hz, 51 * the watchdog timer ticks at. */ 52 extern unsigned int idt_cpu_freq; 53 54 /* translate wtcompare value to seconds and vice versa */ 55 #define WTCOMP2SEC(x) (x / idt_cpu_freq) 56 #define SEC2WTCOMP(x) (x * idt_cpu_freq) 57 58 /* Use a default timeout of 20s. This should be 59 * safe for CPU clock speeds up to 400MHz, as 60 * ((2 ^ 32) - 1) / (400MHz / 2) = 21s. */ 61 #define WATCHDOG_TIMEOUT 20 62 63 static int timeout = WATCHDOG_TIMEOUT; 64 module_param(timeout, int, 0); 65 MODULE_PARM_DESC(timeout, "Watchdog timeout value, in seconds (default=" 66 __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); 67 68 static bool nowayout = WATCHDOG_NOWAYOUT; 69 module_param(nowayout, bool, 0); 70 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 71 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 72 73 /* apply or and nand masks to data read from addr and write back */ 74 #define SET_BITS(addr, or, nand) \ 75 writel((readl(&addr) | or) & ~nand, &addr) 76 77 static int rc32434_wdt_set(int new_timeout) 78 { 79 int max_to = WTCOMP2SEC((u32)-1); 80 81 if (new_timeout < 0 || new_timeout > max_to) { 82 pr_err("timeout value must be between 0 and %d\n", 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 pr_info("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 pr_info("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 pr_crit("device closed unexpectedly. WDT will not stop!\n"); 164 rc32434_wdt_ping(); 165 } 166 clear_bit(0, &rc32434_wdt_device.inuse); 167 return 0; 168 } 169 170 static ssize_t rc32434_wdt_write(struct file *file, const char *data, 171 size_t len, loff_t *ppos) 172 { 173 if (len) { 174 if (!nowayout) { 175 size_t i; 176 177 /* In case it was set long ago */ 178 expect_close = 0; 179 180 for (i = 0; i != len; i++) { 181 char c; 182 if (get_user(c, data + i)) 183 return -EFAULT; 184 if (c == 'V') 185 expect_close = 42; 186 } 187 } 188 rc32434_wdt_ping(); 189 return len; 190 } 191 return 0; 192 } 193 194 static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, 195 unsigned long arg) 196 { 197 void __user *argp = (void __user *)arg; 198 int new_timeout; 199 unsigned int value; 200 static const struct watchdog_info ident = { 201 .options = WDIOF_SETTIMEOUT | 202 WDIOF_KEEPALIVEPING | 203 WDIOF_MAGICCLOSE, 204 .identity = "RC32434_WDT Watchdog", 205 }; 206 switch (cmd) { 207 case WDIOC_GETSUPPORT: 208 if (copy_to_user(argp, &ident, sizeof(ident))) 209 return -EFAULT; 210 break; 211 case WDIOC_GETSTATUS: 212 case WDIOC_GETBOOTSTATUS: 213 value = 0; 214 if (copy_to_user(argp, &value, sizeof(int))) 215 return -EFAULT; 216 break; 217 case WDIOC_SETOPTIONS: 218 if (copy_from_user(&value, argp, sizeof(int))) 219 return -EFAULT; 220 switch (value) { 221 case WDIOS_ENABLECARD: 222 rc32434_wdt_start(); 223 break; 224 case WDIOS_DISABLECARD: 225 rc32434_wdt_stop(); 226 break; 227 default: 228 return -EINVAL; 229 } 230 break; 231 case WDIOC_KEEPALIVE: 232 rc32434_wdt_ping(); 233 break; 234 case WDIOC_SETTIMEOUT: 235 if (copy_from_user(&new_timeout, argp, sizeof(int))) 236 return -EFAULT; 237 if (rc32434_wdt_set(new_timeout)) 238 return -EINVAL; 239 /* Fall through */ 240 case WDIOC_GETTIMEOUT: 241 return copy_to_user(argp, &timeout, sizeof(int)); 242 default: 243 return -ENOTTY; 244 } 245 246 return 0; 247 } 248 249 static const struct file_operations rc32434_wdt_fops = { 250 .owner = THIS_MODULE, 251 .llseek = no_llseek, 252 .write = rc32434_wdt_write, 253 .unlocked_ioctl = rc32434_wdt_ioctl, 254 .open = rc32434_wdt_open, 255 .release = rc32434_wdt_release, 256 }; 257 258 static struct miscdevice rc32434_wdt_miscdev = { 259 .minor = WATCHDOG_MINOR, 260 .name = "watchdog", 261 .fops = &rc32434_wdt_fops, 262 }; 263 264 static int rc32434_wdt_probe(struct platform_device *pdev) 265 { 266 int ret; 267 struct resource *r; 268 269 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res"); 270 if (!r) { 271 pr_err("failed to retrieve resources\n"); 272 return -ENODEV; 273 } 274 275 wdt_reg = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r)); 276 if (!wdt_reg) { 277 pr_err("failed to remap I/O resources\n"); 278 return -ENXIO; 279 } 280 281 spin_lock_init(&rc32434_wdt_device.io_lock); 282 283 /* Make sure the watchdog is not running */ 284 rc32434_wdt_stop(); 285 286 /* Check that the heartbeat value is within it's range; 287 * if not reset to the default */ 288 if (rc32434_wdt_set(timeout)) { 289 rc32434_wdt_set(WATCHDOG_TIMEOUT); 290 pr_info("timeout value must be between 0 and %d\n", 291 WTCOMP2SEC((u32)-1)); 292 } 293 294 ret = misc_register(&rc32434_wdt_miscdev); 295 if (ret < 0) { 296 pr_err("failed to register watchdog device\n"); 297 return ret; 298 } 299 300 pr_info("Watchdog Timer version " VERSION ", timer margin: %d sec\n", 301 timeout); 302 303 return 0; 304 } 305 306 static int rc32434_wdt_remove(struct platform_device *pdev) 307 { 308 misc_deregister(&rc32434_wdt_miscdev); 309 return 0; 310 } 311 312 static void rc32434_wdt_shutdown(struct platform_device *pdev) 313 { 314 rc32434_wdt_stop(); 315 } 316 317 static struct platform_driver rc32434_wdt_driver = { 318 .probe = rc32434_wdt_probe, 319 .remove = rc32434_wdt_remove, 320 .shutdown = rc32434_wdt_shutdown, 321 .driver = { 322 .name = "rc32434_wdt", 323 } 324 }; 325 326 module_platform_driver(rc32434_wdt_driver); 327 328 MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>," 329 "Florian Fainelli <florian@openwrt.org>"); 330 MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog"); 331 MODULE_LICENSE("GPL"); 332 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 333