1 /* 2 * Watchdog driver for Atmel AT91SAM9x processors. 3 * 4 * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12 /* 13 * The Watchdog Timer Mode Register can be only written to once. If the 14 * timeout need to be set from Linux, be sure that the bootstrap or the 15 * bootloader doesn't write to this register. 16 */ 17 18 #include <linux/errno.h> 19 #include <linux/fs.h> 20 #include <linux/init.h> 21 #include <linux/kernel.h> 22 #include <linux/miscdevice.h> 23 #include <linux/module.h> 24 #include <linux/moduleparam.h> 25 #include <linux/platform_device.h> 26 #include <linux/types.h> 27 #include <linux/watchdog.h> 28 #include <linux/jiffies.h> 29 #include <linux/timer.h> 30 #include <linux/bitops.h> 31 #include <linux/uaccess.h> 32 33 #include <asm/arch/at91_wdt.h> 34 35 #define DRV_NAME "AT91SAM9 Watchdog" 36 37 /* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, 38 * use this to convert a watchdog 39 * value from/to milliseconds. 40 */ 41 #define ms_to_ticks(t) (((t << 8) / 1000) - 1) 42 #define ticks_to_ms(t) (((t + 1) * 1000) >> 8) 43 44 /* Hardware timeout in seconds */ 45 #define WDT_HW_TIMEOUT 2 46 47 /* Timer heartbeat (500ms) */ 48 #define WDT_TIMEOUT (HZ/2) 49 50 /* User land timeout */ 51 #define WDT_HEARTBEAT 15 52 static int heartbeat = WDT_HEARTBEAT; 53 module_param(heartbeat, int, 0); 54 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " 55 "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); 56 57 static int nowayout = WATCHDOG_NOWAYOUT; 58 module_param(nowayout, int, 0); 59 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 60 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 61 62 static void at91_ping(unsigned long data); 63 64 static struct { 65 unsigned long next_heartbeat; /* the next_heartbeat for the timer */ 66 unsigned long open; 67 char expect_close; 68 struct timer_list timer; /* The timer that pings the watchdog */ 69 } at91wdt_private; 70 71 /* ......................................................................... */ 72 73 74 /* 75 * Reload the watchdog timer. (ie, pat the watchdog) 76 */ 77 static inline void at91_wdt_reset(void) 78 { 79 at91_sys_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); 80 } 81 82 /* 83 * Timer tick 84 */ 85 static void at91_ping(unsigned long data) 86 { 87 if (time_before(jiffies, at91wdt_private.next_heartbeat) || 88 (!nowayout && !at91wdt_private.open)) { 89 at91_wdt_reset(); 90 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); 91 } else 92 printk(KERN_CRIT DRV_NAME": I will reset your machine !\n"); 93 } 94 95 /* 96 * Watchdog device is opened, and watchdog starts running. 97 */ 98 static int at91_wdt_open(struct inode *inode, struct file *file) 99 { 100 if (test_and_set_bit(0, &at91wdt_private.open)) 101 return -EBUSY; 102 103 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 104 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); 105 106 return nonseekable_open(inode, file); 107 } 108 109 /* 110 * Close the watchdog device. 111 */ 112 static int at91_wdt_close(struct inode *inode, struct file *file) 113 { 114 clear_bit(0, &at91wdt_private.open); 115 116 /* stop internal ping */ 117 if (!at91wdt_private.expect_close) 118 del_timer(&at91wdt_private.timer); 119 120 at91wdt_private.expect_close = 0; 121 return 0; 122 } 123 124 /* 125 * Set the watchdog time interval in 1/256Hz (write-once) 126 * Counter is 12 bit. 127 */ 128 static int at91_wdt_settimeout(unsigned int timeout) 129 { 130 unsigned int reg; 131 unsigned int mr; 132 133 /* Check if disabled */ 134 mr = at91_sys_read(AT91_WDT_MR); 135 if (mr & AT91_WDT_WDDIS) { 136 printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n"); 137 return -EIO; 138 } 139 140 /* 141 * All counting occurs at SLOW_CLOCK / 128 = 256 Hz 142 * 143 * Since WDV is a 12-bit counter, the maximum period is 144 * 4096 / 256 = 16 seconds. 145 */ 146 reg = AT91_WDT_WDRSTEN /* causes watchdog reset */ 147 /* | AT91_WDT_WDRPROC causes processor reset only */ 148 | AT91_WDT_WDDBGHLT /* disabled in debug mode */ 149 | AT91_WDT_WDD /* restart at any time */ 150 | (timeout & AT91_WDT_WDV); /* timer value */ 151 at91_sys_write(AT91_WDT_MR, reg); 152 153 return 0; 154 } 155 156 static const struct watchdog_info at91_wdt_info = { 157 .identity = DRV_NAME, 158 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 159 }; 160 161 /* 162 * Handle commands from user-space. 163 */ 164 static long at91_wdt_ioctl(struct file *file, 165 unsigned int cmd, unsigned long arg) 166 { 167 void __user *argp = (void __user *)arg; 168 int __user *p = argp; 169 int new_value; 170 171 switch (cmd) { 172 case WDIOC_GETSUPPORT: 173 return copy_to_user(argp, &at91_wdt_info, 174 sizeof(at91_wdt_info)) ? -EFAULT : 0; 175 176 case WDIOC_GETSTATUS: 177 case WDIOC_GETBOOTSTATUS: 178 return put_user(0, p); 179 180 case WDIOC_KEEPALIVE: 181 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 182 return 0; 183 184 case WDIOC_SETTIMEOUT: 185 if (get_user(new_value, p)) 186 return -EFAULT; 187 188 heartbeat = new_value; 189 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 190 191 return put_user(new_value, p); /* return current value */ 192 193 case WDIOC_GETTIMEOUT: 194 return put_user(heartbeat, p); 195 } 196 return -ENOTTY; 197 } 198 199 /* 200 * Pat the watchdog whenever device is written to. 201 */ 202 static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, 203 loff_t *ppos) 204 { 205 if (!len) 206 return 0; 207 208 /* Scan for magic character */ 209 if (!nowayout) { 210 size_t i; 211 212 at91wdt_private.expect_close = 0; 213 214 for (i = 0; i < len; i++) { 215 char c; 216 if (get_user(c, data + i)) 217 return -EFAULT; 218 if (c == 'V') { 219 at91wdt_private.expect_close = 42; 220 break; 221 } 222 } 223 } 224 225 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 226 227 return len; 228 } 229 230 /* ......................................................................... */ 231 232 static const struct file_operations at91wdt_fops = { 233 .owner = THIS_MODULE, 234 .llseek = no_llseek, 235 .unlocked_ioctl = at91_wdt_ioctl, 236 .open = at91_wdt_open, 237 .release = at91_wdt_close, 238 .write = at91_wdt_write, 239 }; 240 241 static struct miscdevice at91wdt_miscdev = { 242 .minor = WATCHDOG_MINOR, 243 .name = "watchdog", 244 .fops = &at91wdt_fops, 245 }; 246 247 static int __init at91wdt_probe(struct platform_device *pdev) 248 { 249 int res; 250 251 if (at91wdt_miscdev.parent) 252 return -EBUSY; 253 at91wdt_miscdev.parent = &pdev->dev; 254 255 /* Set watchdog */ 256 res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000)); 257 if (res) 258 return res; 259 260 res = misc_register(&at91wdt_miscdev); 261 if (res) 262 return res; 263 264 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 265 setup_timer(&at91wdt_private.timer, at91_ping, 0); 266 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); 267 268 printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n", 269 heartbeat, nowayout); 270 271 return 0; 272 } 273 274 static int __exit at91wdt_remove(struct platform_device *pdev) 275 { 276 int res; 277 278 res = misc_deregister(&at91wdt_miscdev); 279 if (!res) 280 at91wdt_miscdev.parent = NULL; 281 282 return res; 283 } 284 285 #ifdef CONFIG_PM 286 287 static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) 288 { 289 return 0; 290 } 291 292 static int at91wdt_resume(struct platform_device *pdev) 293 { 294 return 0; 295 } 296 297 #else 298 #define at91wdt_suspend NULL 299 #define at91wdt_resume NULL 300 #endif 301 302 static struct platform_driver at91wdt_driver = { 303 .remove = __exit_p(at91wdt_remove), 304 .suspend = at91wdt_suspend, 305 .resume = at91wdt_resume, 306 .driver = { 307 .name = "at91_wdt", 308 .owner = THIS_MODULE, 309 }, 310 }; 311 312 static int __init at91sam_wdt_init(void) 313 { 314 return platform_driver_probe(&at91wdt_driver, at91wdt_probe); 315 } 316 317 static void __exit at91sam_wdt_exit(void) 318 { 319 platform_driver_unregister(&at91wdt_driver); 320 } 321 322 module_init(at91sam_wdt_init); 323 module_exit(at91sam_wdt_exit); 324 325 MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>"); 326 MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors"); 327 MODULE_LICENSE("GPL"); 328 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 329