aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/watchdog/shwdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/watchdog/shwdt.c')
-rw-r--r--drivers/char/watchdog/shwdt.c110
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 */
131static void sh_wdt_stop(void) 130static 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 */
147static void sh_wdt_keepalive(void) 145static 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 */
157static int sh_wdt_set_heartbeat(int t) 154static 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 */
276static 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,
337static int sh_wdt_notify_sys(struct notifier_block *this, 378static 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
356static struct watchdog_info sh_wdt_info = { 397static 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");
434MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 475MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
435 476
436module_param(clock_division_ratio, int, 0); 477module_param(clock_division_ratio, int, 0);
437MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7."); 478MODULE_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
439module_param(heartbeat, int, 0); 480module_param(heartbeat, int, 0);
440MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); 481MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
441 482
442module_param(nowayout, int, 0); 483module_param(nowayout, int, 0);
443MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); 484MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
444 485
445module_init(sh_wdt_init); 486module_init(sh_wdt_init);
446module_exit(sh_wdt_exit); 487module_exit(sh_wdt_exit);
447