1 /* 2 * Watchdog driver for Atmel AT91RM9200 (Thunder) 3 * 4 * Copyright (C) 2003 SAN People (Pty) Ltd 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 #include <linux/bitops.h> 13 #include <linux/errno.h> 14 #include <linux/fs.h> 15 #include <linux/init.h> 16 #include <linux/kernel.h> 17 #include <linux/miscdevice.h> 18 #include <linux/module.h> 19 #include <linux/moduleparam.h> 20 #include <linux/platform_device.h> 21 #include <linux/types.h> 22 #include <linux/watchdog.h> 23 #include <linux/uaccess.h> 24 #include <mach/at91_st.h> 25 26 #define WDT_DEFAULT_TIME 5 /* seconds */ 27 #define WDT_MAX_TIME 256 /* seconds */ 28 29 static int wdt_time = WDT_DEFAULT_TIME; 30 static int nowayout = WATCHDOG_NOWAYOUT; 31 32 module_param(wdt_time, int, 0); 33 MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" 34 __MODULE_STRING(WDT_DEFAULT_TIME) ")"); 35 36 #ifdef CONFIG_WATCHDOG_NOWAYOUT 37 module_param(nowayout, int, 0); 38 MODULE_PARM_DESC(nowayout, 39 "Watchdog cannot be stopped once started (default=" 40 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 41 #endif 42 43 44 static unsigned long at91wdt_busy; 45 46 /* ......................................................................... */ 47 48 /* 49 * Disable the watchdog. 50 */ 51 static inline void at91_wdt_stop(void) 52 { 53 at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN); 54 } 55 56 /* 57 * Enable and reset the watchdog. 58 */ 59 static inline void at91_wdt_start(void) 60 { 61 at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | 62 (((65536 * wdt_time) >> 8) & AT91_ST_WDV)); 63 at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); 64 } 65 66 /* 67 * Reload the watchdog timer. (ie, pat the watchdog) 68 */ 69 static inline void at91_wdt_reload(void) 70 { 71 at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); 72 } 73 74 /* ......................................................................... */ 75 76 /* 77 * Watchdog device is opened, and watchdog starts running. 78 */ 79 static int at91_wdt_open(struct inode *inode, struct file *file) 80 { 81 if (test_and_set_bit(0, &at91wdt_busy)) 82 return -EBUSY; 83 84 at91_wdt_start(); 85 return nonseekable_open(inode, file); 86 } 87 88 /* 89 * Close the watchdog device. 90 * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also 91 * disabled. 92 */ 93 static int at91_wdt_close(struct inode *inode, struct file *file) 94 { 95 /* Disable the watchdog when file is closed */ 96 if (!nowayout) 97 at91_wdt_stop(); 98 99 clear_bit(0, &at91wdt_busy); 100 return 0; 101 } 102 103 /* 104 * Change the watchdog time interval. 105 */ 106 static int at91_wdt_settimeout(int new_time) 107 { 108 /* 109 * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz 110 * 111 * Since WDV is a 16-bit counter, the maximum period is 112 * 65536 / 0.256 = 256 seconds. 113 */ 114 if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) 115 return -EINVAL; 116 117 /* Set new watchdog time. It will be used when 118 at91_wdt_start() is called. */ 119 wdt_time = new_time; 120 return 0; 121 } 122 123 static struct watchdog_info at91_wdt_info = { 124 .identity = "at91 watchdog", 125 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 126 }; 127 128 /* 129 * Handle commands from user-space. 130 */ 131 static long at91_wdt_ioctl(struct file *file, 132 unsigned int cmd, unsigned long arg) 133 { 134 void __user *argp = (void __user *)arg; 135 int __user *p = argp; 136 int new_value; 137 138 switch (cmd) { 139 case WDIOC_GETSUPPORT: 140 return copy_to_user(argp, &at91_wdt_info, 141 sizeof(at91_wdt_info)) ? -EFAULT : 0; 142 case WDIOC_GETSTATUS: 143 case WDIOC_GETBOOTSTATUS: 144 return put_user(0, p); 145 case WDIOC_SETOPTIONS: 146 if (get_user(new_value, p)) 147 return -EFAULT; 148 if (new_value & WDIOS_DISABLECARD) 149 at91_wdt_stop(); 150 if (new_value & WDIOS_ENABLECARD) 151 at91_wdt_start(); 152 return 0; 153 case WDIOC_KEEPALIVE: 154 at91_wdt_reload(); /* pat the watchdog */ 155 return 0; 156 case WDIOC_SETTIMEOUT: 157 if (get_user(new_value, p)) 158 return -EFAULT; 159 if (at91_wdt_settimeout(new_value)) 160 return -EINVAL; 161 /* Enable new time value */ 162 at91_wdt_start(); 163 /* Return current value */ 164 return put_user(wdt_time, p); 165 case WDIOC_GETTIMEOUT: 166 return put_user(wdt_time, p); 167 default: 168 return -ENOTTY; 169 } 170 } 171 172 /* 173 * Pat the watchdog whenever device is written to. 174 */ 175 static ssize_t at91_wdt_write(struct file *file, const char *data, 176 size_t len, loff_t *ppos) 177 { 178 at91_wdt_reload(); /* pat the watchdog */ 179 return len; 180 } 181 182 /* ......................................................................... */ 183 184 static const struct file_operations at91wdt_fops = { 185 .owner = THIS_MODULE, 186 .llseek = no_llseek, 187 .unlocked_ioctl = at91_wdt_ioctl, 188 .open = at91_wdt_open, 189 .release = at91_wdt_close, 190 .write = at91_wdt_write, 191 }; 192 193 static struct miscdevice at91wdt_miscdev = { 194 .minor = WATCHDOG_MINOR, 195 .name = "watchdog", 196 .fops = &at91wdt_fops, 197 }; 198 199 static int __init at91wdt_probe(struct platform_device *pdev) 200 { 201 int res; 202 203 if (at91wdt_miscdev.parent) 204 return -EBUSY; 205 at91wdt_miscdev.parent = &pdev->dev; 206 207 res = misc_register(&at91wdt_miscdev); 208 if (res) 209 return res; 210 211 printk(KERN_INFO "AT91 Watchdog Timer enabled (%d seconds%s)\n", 212 wdt_time, nowayout ? ", nowayout" : ""); 213 return 0; 214 } 215 216 static int __exit at91wdt_remove(struct platform_device *pdev) 217 { 218 int res; 219 220 res = misc_deregister(&at91wdt_miscdev); 221 if (!res) 222 at91wdt_miscdev.parent = NULL; 223 224 return res; 225 } 226 227 static void at91wdt_shutdown(struct platform_device *pdev) 228 { 229 at91_wdt_stop(); 230 } 231 232 #ifdef CONFIG_PM 233 234 static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) 235 { 236 at91_wdt_stop(); 237 return 0; 238 } 239 240 static int at91wdt_resume(struct platform_device *pdev) 241 { 242 if (at91wdt_busy) 243 at91_wdt_start(); 244 return 0; 245 } 246 247 #else 248 #define at91wdt_suspend NULL 249 #define at91wdt_resume NULL 250 #endif 251 252 static struct platform_driver at91wdt_driver = { 253 .probe = at91wdt_probe, 254 .remove = __exit_p(at91wdt_remove), 255 .shutdown = at91wdt_shutdown, 256 .suspend = at91wdt_suspend, 257 .resume = at91wdt_resume, 258 .driver = { 259 .name = "at91_wdt", 260 .owner = THIS_MODULE, 261 }, 262 }; 263 264 static int __init at91_wdt_init(void) 265 { 266 /* Check that the heartbeat value is within range; 267 if not reset to the default */ 268 if (at91_wdt_settimeout(wdt_time)) { 269 at91_wdt_settimeout(WDT_DEFAULT_TIME); 270 pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time); 271 } 272 273 return platform_driver_register(&at91wdt_driver); 274 } 275 276 static void __exit at91_wdt_exit(void) 277 { 278 platform_driver_unregister(&at91wdt_driver); 279 } 280 281 module_init(at91_wdt_init); 282 module_exit(at91_wdt_exit); 283 284 MODULE_AUTHOR("Andrew Victor"); 285 MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200"); 286 MODULE_LICENSE("GPL"); 287 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 288 MODULE_ALIAS("platform:at91_wdt"); 289