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