diff options
Diffstat (limited to 'drivers/char/watchdog/shwdt.c')
-rw-r--r-- | drivers/char/watchdog/shwdt.c | 110 |
1 files changed, 75 insertions, 35 deletions
diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c index 1355038f1044..e5b8c64f1d65 100644 --- a/drivers/char/watchdog/shwdt.c +++ b/drivers/char/watchdog/shwdt.c | |||
@@ -27,7 +27,7 @@ | |||
27 | #include <linux/notifier.h> | 27 | #include <linux/notifier.h> |
28 | #include <linux/ioport.h> | 28 | #include <linux/ioport.h> |
29 | #include <linux/fs.h> | 29 | #include <linux/fs.h> |
30 | 30 | #include <linux/mm.h> | |
31 | #include <asm/io.h> | 31 | #include <asm/io.h> |
32 | #include <asm/uaccess.h> | 32 | #include <asm/uaccess.h> |
33 | #include <asm/watchdog.h> | 33 | #include <asm/watchdog.h> |
@@ -125,7 +125,6 @@ static void sh_wdt_start(void) | |||
125 | 125 | ||
126 | /** | 126 | /** |
127 | * sh_wdt_stop - Stop the Watchdog | 127 | * sh_wdt_stop - Stop the Watchdog |
128 | * | ||
129 | * Stops the watchdog. | 128 | * Stops the watchdog. |
130 | */ | 129 | */ |
131 | static void sh_wdt_stop(void) | 130 | static void sh_wdt_stop(void) |
@@ -141,22 +140,20 @@ static void sh_wdt_stop(void) | |||
141 | 140 | ||
142 | /** | 141 | /** |
143 | * sh_wdt_keepalive - Keep the Userspace Watchdog Alive | 142 | * sh_wdt_keepalive - Keep the Userspace Watchdog Alive |
144 | * | ||
145 | * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. | 143 | * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. |
146 | */ | 144 | */ |
147 | static void sh_wdt_keepalive(void) | 145 | static inline void sh_wdt_keepalive(void) |
148 | { | 146 | { |
149 | next_heartbeat = jiffies + (heartbeat * HZ); | 147 | next_heartbeat = jiffies + (heartbeat * HZ); |
150 | } | 148 | } |
151 | 149 | ||
152 | /** | 150 | /** |
153 | * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat | 151 | * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat |
154 | * | ||
155 | * Set the Userspace Watchdog heartbeat | 152 | * Set the Userspace Watchdog heartbeat |
156 | */ | 153 | */ |
157 | static int sh_wdt_set_heartbeat(int t) | 154 | static int sh_wdt_set_heartbeat(int t) |
158 | { | 155 | { |
159 | if ((t < 1) || (t > 3600)) /* arbitrary upper limit */ | 156 | if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */ |
160 | return -EINVAL; | 157 | return -EINVAL; |
161 | 158 | ||
162 | heartbeat = t; | 159 | heartbeat = t; |
@@ -165,7 +162,6 @@ static int sh_wdt_set_heartbeat(int t) | |||
165 | 162 | ||
166 | /** | 163 | /** |
167 | * sh_wdt_ping - Ping the Watchdog | 164 | * sh_wdt_ping - Ping the Watchdog |
168 | * | ||
169 | * @data: Unused | 165 | * @data: Unused |
170 | * | 166 | * |
171 | * Clears overflow bit, resets timer counter. | 167 | * Clears overflow bit, resets timer counter. |
@@ -182,14 +178,13 @@ static void sh_wdt_ping(unsigned long data) | |||
182 | sh_wdt_write_cnt(0); | 178 | sh_wdt_write_cnt(0); |
183 | 179 | ||
184 | mod_timer(&timer, next_ping_period(clock_division_ratio)); | 180 | mod_timer(&timer, next_ping_period(clock_division_ratio)); |
185 | } else { | 181 | } else |
186 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); | 182 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " |
187 | } | 183 | "the watchdog\n"); |
188 | } | 184 | } |
189 | 185 | ||
190 | /** | 186 | /** |
191 | * sh_wdt_open - Open the Device | 187 | * sh_wdt_open - Open the Device |
192 | * | ||
193 | * @inode: inode of device | 188 | * @inode: inode of device |
194 | * @file: file handle of device | 189 | * @file: file handle of device |
195 | * | 190 | * |
@@ -209,7 +204,6 @@ static int sh_wdt_open(struct inode *inode, struct file *file) | |||
209 | 204 | ||
210 | /** | 205 | /** |
211 | * sh_wdt_close - Close the Device | 206 | * sh_wdt_close - Close the Device |
212 | * | ||
213 | * @inode: inode of device | 207 | * @inode: inode of device |
214 | * @file: file handle of device | 208 | * @file: file handle of device |
215 | * | 209 | * |
@@ -220,7 +214,8 @@ static int sh_wdt_close(struct inode *inode, struct file *file) | |||
220 | if (shwdt_expect_close == 42) { | 214 | if (shwdt_expect_close == 42) { |
221 | sh_wdt_stop(); | 215 | sh_wdt_stop(); |
222 | } else { | 216 | } else { |
223 | printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); | 217 | printk(KERN_CRIT PFX "Unexpected close, not " |
218 | "stopping watchdog!\n"); | ||
224 | sh_wdt_keepalive(); | 219 | sh_wdt_keepalive(); |
225 | } | 220 | } |
226 | 221 | ||
@@ -232,7 +227,6 @@ static int sh_wdt_close(struct inode *inode, struct file *file) | |||
232 | 227 | ||
233 | /** | 228 | /** |
234 | * sh_wdt_write - Write to Device | 229 | * sh_wdt_write - Write to Device |
235 | * | ||
236 | * @file: file handle of device | 230 | * @file: file handle of device |
237 | * @buf: buffer to write | 231 | * @buf: buffer to write |
238 | * @count: length of buffer | 232 | * @count: length of buffer |
@@ -264,8 +258,56 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf, | |||
264 | } | 258 | } |
265 | 259 | ||
266 | /** | 260 | /** |
267 | * sh_wdt_ioctl - Query Device | 261 | * sh_wdt_mmap - map WDT/CPG registers into userspace |
262 | * @file: file structure for the device | ||
263 | * @vma: VMA to map the registers into | ||
264 | * | ||
265 | * A simple mmap() implementation for the corner cases where the counter | ||
266 | * needs to be mapped in userspace directly. Due to the relatively small | ||
267 | * size of the area, neighbouring registers not necessarily tied to the | ||
268 | * CPG will also be accessible through the register page, so this remains | ||
269 | * configurable for users that really know what they're doing. | ||
268 | * | 270 | * |
271 | * Additionaly, the register page maps in the CPG register base relative | ||
272 | * to the nearest page-aligned boundary, which requires that userspace do | ||
273 | * the appropriate CPU subtype math for calculating the page offset for | ||
274 | * the counter value. | ||
275 | */ | ||
276 | static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) | ||
277 | { | ||
278 | int ret = -ENOSYS; | ||
279 | |||
280 | #ifdef CONFIG_SH_WDT_MMAP | ||
281 | unsigned long addr; | ||
282 | |||
283 | /* Only support the simple cases where we map in a register page. */ | ||
284 | if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) | ||
285 | return -EINVAL; | ||
286 | |||
287 | /* | ||
288 | * Pick WTCNT as the start, it's usually the first register after the | ||
289 | * FRQCR, and neither one are generally page-aligned out of the box. | ||
290 | */ | ||
291 | addr = WTCNT & ~(PAGE_SIZE - 1); | ||
292 | |||
293 | vma->vm_flags |= VM_IO; | ||
294 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
295 | |||
296 | if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, | ||
297 | PAGE_SIZE, vma->vm_page_prot)) { | ||
298 | printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n", | ||
299 | __FUNCTION__); | ||
300 | return -EAGAIN; | ||
301 | } | ||
302 | |||
303 | ret = 0; | ||
304 | #endif | ||
305 | |||
306 | return ret; | ||
307 | } | ||
308 | |||
309 | /** | ||
310 | * sh_wdt_ioctl - Query Device | ||
269 | * @inode: inode of device | 311 | * @inode: inode of device |
270 | * @file: file handle of device | 312 | * @file: file handle of device |
271 | * @cmd: watchdog command | 313 | * @cmd: watchdog command |
@@ -326,7 +368,6 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, | |||
326 | 368 | ||
327 | /** | 369 | /** |
328 | * sh_wdt_notify_sys - Notifier Handler | 370 | * sh_wdt_notify_sys - Notifier Handler |
329 | * | ||
330 | * @this: notifier block | 371 | * @this: notifier block |
331 | * @code: notifier event | 372 | * @code: notifier event |
332 | * @unused: unused | 373 | * @unused: unused |
@@ -337,9 +378,8 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, | |||
337 | static int sh_wdt_notify_sys(struct notifier_block *this, | 378 | static int sh_wdt_notify_sys(struct notifier_block *this, |
338 | unsigned long code, void *unused) | 379 | unsigned long code, void *unused) |
339 | { | 380 | { |
340 | if (code == SYS_DOWN || code == SYS_HALT) { | 381 | if (code == SYS_DOWN || code == SYS_HALT) |
341 | sh_wdt_stop(); | 382 | sh_wdt_stop(); |
342 | } | ||
343 | 383 | ||
344 | return NOTIFY_DONE; | 384 | return NOTIFY_DONE; |
345 | } | 385 | } |
@@ -351,10 +391,12 @@ static const struct file_operations sh_wdt_fops = { | |||
351 | .ioctl = sh_wdt_ioctl, | 391 | .ioctl = sh_wdt_ioctl, |
352 | .open = sh_wdt_open, | 392 | .open = sh_wdt_open, |
353 | .release = sh_wdt_close, | 393 | .release = sh_wdt_close, |
394 | .mmap = sh_wdt_mmap, | ||
354 | }; | 395 | }; |
355 | 396 | ||
356 | static struct watchdog_info sh_wdt_info = { | 397 | static struct watchdog_info sh_wdt_info = { |
357 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, | 398 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | |
399 | WDIOF_MAGICCLOSE, | ||
358 | .firmware_version = 1, | 400 | .firmware_version = 1, |
359 | .identity = "SH WDT", | 401 | .identity = "SH WDT", |
360 | }; | 402 | }; |
@@ -371,7 +413,6 @@ static struct miscdevice sh_wdt_miscdev = { | |||
371 | 413 | ||
372 | /** | 414 | /** |
373 | * sh_wdt_init - Initialize module | 415 | * sh_wdt_init - Initialize module |
374 | * | ||
375 | * Registers the device and notifier handler. Actual device | 416 | * Registers the device and notifier handler. Actual device |
376 | * initialization is handled by sh_wdt_open(). | 417 | * initialization is handled by sh_wdt_open(). |
377 | */ | 418 | */ |
@@ -381,15 +422,15 @@ static int __init sh_wdt_init(void) | |||
381 | 422 | ||
382 | if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { | 423 | if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { |
383 | clock_division_ratio = WTCSR_CKS_4096; | 424 | clock_division_ratio = WTCSR_CKS_4096; |
384 | printk(KERN_INFO PFX "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n", | 425 | printk(KERN_INFO PFX "clock_division_ratio value must " |
385 | clock_division_ratio); | 426 | "be 0x5<=x<=0x7, using %d\n", clock_division_ratio); |
386 | } | 427 | } |
387 | 428 | ||
388 | if (sh_wdt_set_heartbeat(heartbeat)) | 429 | rc = sh_wdt_set_heartbeat(heartbeat); |
389 | { | 430 | if (unlikely(rc)) { |
390 | heartbeat = WATCHDOG_HEARTBEAT; | 431 | heartbeat = WATCHDOG_HEARTBEAT; |
391 | printk(KERN_INFO PFX "heartbeat value must be 1<=x<=3600, using %d\n", | 432 | printk(KERN_INFO PFX "heartbeat value must " |
392 | heartbeat); | 433 | "be 1<=x<=3600, using %d\n", heartbeat); |
393 | } | 434 | } |
394 | 435 | ||
395 | init_timer(&timer); | 436 | init_timer(&timer); |
@@ -397,15 +438,16 @@ static int __init sh_wdt_init(void) | |||
397 | timer.data = 0; | 438 | timer.data = 0; |
398 | 439 | ||
399 | rc = register_reboot_notifier(&sh_wdt_notifier); | 440 | rc = register_reboot_notifier(&sh_wdt_notifier); |
400 | if (rc) { | 441 | if (unlikely(rc)) { |
401 | printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", rc); | 442 | printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", |
443 | rc); | ||
402 | return rc; | 444 | return rc; |
403 | } | 445 | } |
404 | 446 | ||
405 | rc = misc_register(&sh_wdt_miscdev); | 447 | rc = misc_register(&sh_wdt_miscdev); |
406 | if (rc) { | 448 | if (unlikely(rc)) { |
407 | printk(KERN_ERR PFX "Can't register miscdev on minor=%d (err=%d)\n", | 449 | printk(KERN_ERR PFX "Can't register miscdev on " |
408 | sh_wdt_miscdev.minor, rc); | 450 | "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc); |
409 | unregister_reboot_notifier(&sh_wdt_notifier); | 451 | unregister_reboot_notifier(&sh_wdt_notifier); |
410 | return rc; | 452 | return rc; |
411 | } | 453 | } |
@@ -418,7 +460,6 @@ static int __init sh_wdt_init(void) | |||
418 | 460 | ||
419 | /** | 461 | /** |
420 | * sh_wdt_exit - Deinitialize module | 462 | * sh_wdt_exit - Deinitialize module |
421 | * | ||
422 | * Unregisters the device and notifier handler. Actual device | 463 | * Unregisters the device and notifier handler. Actual device |
423 | * deinitialization is handled by sh_wdt_close(). | 464 | * deinitialization is handled by sh_wdt_close(). |
424 | */ | 465 | */ |
@@ -434,14 +475,13 @@ MODULE_LICENSE("GPL"); | |||
434 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 475 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
435 | 476 | ||
436 | module_param(clock_division_ratio, int, 0); | 477 | module_param(clock_division_ratio, int, 0); |
437 | MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7."); | 478 | MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")"); |
438 | 479 | ||
439 | module_param(heartbeat, int, 0); | 480 | module_param(heartbeat, int, 0); |
440 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | 481 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); |
441 | 482 | ||
442 | module_param(nowayout, int, 0); | 483 | module_param(nowayout, int, 0); |
443 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); | 484 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
444 | 485 | ||
445 | module_init(sh_wdt_init); | 486 | module_init(sh_wdt_init); |
446 | module_exit(sh_wdt_exit); | 487 | module_exit(sh_wdt_exit); |
447 | |||