diff options
-rw-r--r-- | drivers/watchdog/shwdt.c | 139 |
1 files changed, 82 insertions, 57 deletions
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index 1277f7e9cc54..60f0036aaca6 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c | |||
@@ -28,9 +28,9 @@ | |||
28 | #include <linux/ioport.h> | 28 | #include <linux/ioport.h> |
29 | #include <linux/fs.h> | 29 | #include <linux/fs.h> |
30 | #include <linux/mm.h> | 30 | #include <linux/mm.h> |
31 | #include <asm/io.h> | 31 | #include <linux/io.h> |
32 | #include <asm/uaccess.h> | 32 | #include <linux/uaccess.h> |
33 | #include <asm/watchdog.h> | 33 | #include <linux/watchdog.h> |
34 | 34 | ||
35 | #define PFX "shwdt: " | 35 | #define PFX "shwdt: " |
36 | 36 | ||
@@ -72,6 +72,7 @@ static struct watchdog_info sh_wdt_info; | |||
72 | static char shwdt_expect_close; | 72 | static char shwdt_expect_close; |
73 | static DEFINE_TIMER(timer, sh_wdt_ping, 0, 0); | 73 | static DEFINE_TIMER(timer, sh_wdt_ping, 0, 0); |
74 | static unsigned long next_heartbeat; | 74 | static unsigned long next_heartbeat; |
75 | static DEFINE_SPINLOCK(shwdt_lock); | ||
75 | 76 | ||
76 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ | 77 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ |
77 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ | 78 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ |
@@ -86,6 +87,9 @@ static int nowayout = WATCHDOG_NOWAYOUT; | |||
86 | static void sh_wdt_start(void) | 87 | static void sh_wdt_start(void) |
87 | { | 88 | { |
88 | __u8 csr; | 89 | __u8 csr; |
90 | unsigned long flags; | ||
91 | |||
92 | spin_lock_irqsave(&wdt_lock, flags); | ||
89 | 93 | ||
90 | next_heartbeat = jiffies + (heartbeat * HZ); | 94 | next_heartbeat = jiffies + (heartbeat * HZ); |
91 | mod_timer(&timer, next_ping_period(clock_division_ratio)); | 95 | mod_timer(&timer, next_ping_period(clock_division_ratio)); |
@@ -123,6 +127,7 @@ static void sh_wdt_start(void) | |||
123 | csr &= ~RSTCSR_RSTS; | 127 | csr &= ~RSTCSR_RSTS; |
124 | sh_wdt_write_rstcsr(csr); | 128 | sh_wdt_write_rstcsr(csr); |
125 | #endif | 129 | #endif |
130 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
126 | } | 131 | } |
127 | 132 | ||
128 | /** | 133 | /** |
@@ -132,12 +137,16 @@ static void sh_wdt_start(void) | |||
132 | static void sh_wdt_stop(void) | 137 | static void sh_wdt_stop(void) |
133 | { | 138 | { |
134 | __u8 csr; | 139 | __u8 csr; |
140 | unsigned long flags; | ||
141 | |||
142 | spin_lock_irqsave(&wdt_lock, flags); | ||
135 | 143 | ||
136 | del_timer(&timer); | 144 | del_timer(&timer); |
137 | 145 | ||
138 | csr = sh_wdt_read_csr(); | 146 | csr = sh_wdt_read_csr(); |
139 | csr &= ~WTCSR_TME; | 147 | csr &= ~WTCSR_TME; |
140 | sh_wdt_write_csr(csr); | 148 | sh_wdt_write_csr(csr); |
149 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
141 | } | 150 | } |
142 | 151 | ||
143 | /** | 152 | /** |
@@ -146,7 +155,11 @@ static void sh_wdt_stop(void) | |||
146 | */ | 155 | */ |
147 | static inline void sh_wdt_keepalive(void) | 156 | static inline void sh_wdt_keepalive(void) |
148 | { | 157 | { |
158 | unsigned long flags; | ||
159 | |||
160 | spin_lock_irqsave(&wdt_lock, flags); | ||
149 | next_heartbeat = jiffies + (heartbeat * HZ); | 161 | next_heartbeat = jiffies + (heartbeat * HZ); |
162 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
150 | } | 163 | } |
151 | 164 | ||
152 | /** | 165 | /** |
@@ -155,10 +168,14 @@ static inline void sh_wdt_keepalive(void) | |||
155 | */ | 168 | */ |
156 | static int sh_wdt_set_heartbeat(int t) | 169 | static int sh_wdt_set_heartbeat(int t) |
157 | { | 170 | { |
158 | if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */ | 171 | unsigned long flags; |
172 | |||
173 | if (unlikely(t < 1 || t > 3600)) /* arbitrary upper limit */ | ||
159 | return -EINVAL; | 174 | return -EINVAL; |
160 | 175 | ||
176 | spin_lock_irqsave(&wdt_lock, flags); | ||
161 | heartbeat = t; | 177 | heartbeat = t; |
178 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
162 | return 0; | 179 | return 0; |
163 | } | 180 | } |
164 | 181 | ||
@@ -170,6 +187,9 @@ static int sh_wdt_set_heartbeat(int t) | |||
170 | */ | 187 | */ |
171 | static void sh_wdt_ping(unsigned long data) | 188 | static void sh_wdt_ping(unsigned long data) |
172 | { | 189 | { |
190 | unsigned long flags; | ||
191 | |||
192 | spin_lock_irqsave(&wdt_lock, flags); | ||
173 | if (time_before(jiffies, next_heartbeat)) { | 193 | if (time_before(jiffies, next_heartbeat)) { |
174 | __u8 csr; | 194 | __u8 csr; |
175 | 195 | ||
@@ -183,6 +203,7 @@ static void sh_wdt_ping(unsigned long data) | |||
183 | } else | 203 | } else |
184 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " | 204 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " |
185 | "the watchdog\n"); | 205 | "the watchdog\n"); |
206 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
186 | } | 207 | } |
187 | 208 | ||
188 | /** | 209 | /** |
@@ -310,7 +331,6 @@ static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) | |||
310 | 331 | ||
311 | /** | 332 | /** |
312 | * sh_wdt_ioctl - Query Device | 333 | * sh_wdt_ioctl - Query Device |
313 | * @inode: inode of device | ||
314 | * @file: file handle of device | 334 | * @file: file handle of device |
315 | * @cmd: watchdog command | 335 | * @cmd: watchdog command |
316 | * @arg: argument | 336 | * @arg: argument |
@@ -318,53 +338,51 @@ static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) | |||
318 | * Query basic information from the device or ping it, as outlined by the | 338 | * Query basic information from the device or ping it, as outlined by the |
319 | * watchdog API. | 339 | * watchdog API. |
320 | */ | 340 | */ |
321 | static int sh_wdt_ioctl(struct inode *inode, struct file *file, | 341 | static long sh_wdt_ioctl(struct file *file, unsigned int cmd, |
322 | unsigned int cmd, unsigned long arg) | 342 | unsigned long arg) |
323 | { | 343 | { |
324 | int new_heartbeat; | 344 | int new_heartbeat; |
325 | int options, retval = -EINVAL; | 345 | int options, retval = -EINVAL; |
326 | 346 | ||
327 | switch (cmd) { | 347 | switch (cmd) { |
328 | case WDIOC_GETSUPPORT: | 348 | case WDIOC_GETSUPPORT: |
329 | return copy_to_user((struct watchdog_info *)arg, | 349 | return copy_to_user((struct watchdog_info *)arg, |
330 | &sh_wdt_info, | 350 | &sh_wdt_info, sizeof(sh_wdt_info)) ? -EFAULT : 0; |
331 | sizeof(sh_wdt_info)) ? -EFAULT : 0; | 351 | case WDIOC_GETSTATUS: |
332 | case WDIOC_GETSTATUS: | 352 | case WDIOC_GETBOOTSTATUS: |
333 | case WDIOC_GETBOOTSTATUS: | 353 | return put_user(0, (int *)arg); |
334 | return put_user(0, (int *)arg); | 354 | case WDIOC_KEEPALIVE: |
335 | case WDIOC_KEEPALIVE: | 355 | sh_wdt_keepalive(); |
336 | sh_wdt_keepalive(); | 356 | return 0; |
337 | return 0; | 357 | case WDIOC_SETTIMEOUT: |
338 | case WDIOC_SETTIMEOUT: | 358 | if (get_user(new_heartbeat, (int *)arg)) |
339 | if (get_user(new_heartbeat, (int *)arg)) | 359 | return -EFAULT; |
340 | return -EFAULT; | ||
341 | |||
342 | if (sh_wdt_set_heartbeat(new_heartbeat)) | ||
343 | return -EINVAL; | ||
344 | |||
345 | sh_wdt_keepalive(); | ||
346 | /* Fall */ | ||
347 | case WDIOC_GETTIMEOUT: | ||
348 | return put_user(heartbeat, (int *)arg); | ||
349 | case WDIOC_SETOPTIONS: | ||
350 | if (get_user(options, (int *)arg)) | ||
351 | return -EFAULT; | ||
352 | |||
353 | if (options & WDIOS_DISABLECARD) { | ||
354 | sh_wdt_stop(); | ||
355 | retval = 0; | ||
356 | } | ||
357 | 360 | ||
358 | if (options & WDIOS_ENABLECARD) { | 361 | if (sh_wdt_set_heartbeat(new_heartbeat)) |
359 | sh_wdt_start(); | 362 | return -EINVAL; |
360 | retval = 0; | ||
361 | } | ||
362 | 363 | ||
363 | return retval; | 364 | sh_wdt_keepalive(); |
364 | default: | 365 | /* Fall */ |
365 | return -ENOTTY; | 366 | case WDIOC_GETTIMEOUT: |
366 | } | 367 | return put_user(heartbeat, (int *)arg); |
368 | case WDIOC_SETOPTIONS: | ||
369 | if (get_user(options, (int *)arg)) | ||
370 | return -EFAULT; | ||
371 | |||
372 | if (options & WDIOS_DISABLECARD) { | ||
373 | sh_wdt_stop(); | ||
374 | retval = 0; | ||
375 | } | ||
376 | |||
377 | if (options & WDIOS_ENABLECARD) { | ||
378 | sh_wdt_start(); | ||
379 | retval = 0; | ||
380 | } | ||
367 | 381 | ||
382 | return retval; | ||
383 | default: | ||
384 | return -ENOTTY; | ||
385 | } | ||
368 | return 0; | 386 | return 0; |
369 | } | 387 | } |
370 | 388 | ||
@@ -390,13 +408,13 @@ static const struct file_operations sh_wdt_fops = { | |||
390 | .owner = THIS_MODULE, | 408 | .owner = THIS_MODULE, |
391 | .llseek = no_llseek, | 409 | .llseek = no_llseek, |
392 | .write = sh_wdt_write, | 410 | .write = sh_wdt_write, |
393 | .ioctl = sh_wdt_ioctl, | 411 | .unlocked_ioctl = sh_wdt_ioctl, |
394 | .open = sh_wdt_open, | 412 | .open = sh_wdt_open, |
395 | .release = sh_wdt_close, | 413 | .release = sh_wdt_close, |
396 | .mmap = sh_wdt_mmap, | 414 | .mmap = sh_wdt_mmap, |
397 | }; | 415 | }; |
398 | 416 | ||
399 | static struct watchdog_info sh_wdt_info = { | 417 | static const struct watchdog_info sh_wdt_info = { |
400 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | | 418 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | |
401 | WDIOF_MAGICCLOSE, | 419 | WDIOF_MAGICCLOSE, |
402 | .firmware_version = 1, | 420 | .firmware_version = 1, |
@@ -422,30 +440,33 @@ static int __init sh_wdt_init(void) | |||
422 | { | 440 | { |
423 | int rc; | 441 | int rc; |
424 | 442 | ||
425 | if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { | 443 | if (clock_division_ratio < 0x5 || clock_division_ratio > 0x7) { |
426 | clock_division_ratio = WTCSR_CKS_4096; | 444 | clock_division_ratio = WTCSR_CKS_4096; |
427 | printk(KERN_INFO PFX "clock_division_ratio value must " | 445 | printk(KERN_INFO PFX |
428 | "be 0x5<=x<=0x7, using %d\n", clock_division_ratio); | 446 | "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n", |
447 | clock_division_ratio); | ||
429 | } | 448 | } |
430 | 449 | ||
431 | rc = sh_wdt_set_heartbeat(heartbeat); | 450 | rc = sh_wdt_set_heartbeat(heartbeat); |
432 | if (unlikely(rc)) { | 451 | if (unlikely(rc)) { |
433 | heartbeat = WATCHDOG_HEARTBEAT; | 452 | heartbeat = WATCHDOG_HEARTBEAT; |
434 | printk(KERN_INFO PFX "heartbeat value must " | 453 | printk(KERN_INFO PFX |
435 | "be 1<=x<=3600, using %d\n", heartbeat); | 454 | "heartbeat value must be 1<=x<=3600, using %d\n", |
455 | heartbeat); | ||
436 | } | 456 | } |
437 | 457 | ||
438 | rc = register_reboot_notifier(&sh_wdt_notifier); | 458 | rc = register_reboot_notifier(&sh_wdt_notifier); |
439 | if (unlikely(rc)) { | 459 | if (unlikely(rc)) { |
440 | printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", | 460 | printk(KERN_ERR PFX |
441 | rc); | 461 | "Can't register reboot notifier (err=%d)\n", rc); |
442 | return rc; | 462 | return rc; |
443 | } | 463 | } |
444 | 464 | ||
445 | rc = misc_register(&sh_wdt_miscdev); | 465 | rc = misc_register(&sh_wdt_miscdev); |
446 | if (unlikely(rc)) { | 466 | if (unlikely(rc)) { |
447 | printk(KERN_ERR PFX "Can't register miscdev on " | 467 | printk(KERN_ERR PFX |
448 | "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc); | 468 | "Can't register miscdev on minor=%d (err=%d)\n", |
469 | sh_wdt_miscdev.minor, rc); | ||
449 | unregister_reboot_notifier(&sh_wdt_notifier); | 470 | unregister_reboot_notifier(&sh_wdt_notifier); |
450 | return rc; | 471 | return rc; |
451 | } | 472 | } |
@@ -476,10 +497,14 @@ module_param(clock_division_ratio, int, 0); | |||
476 | 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) ")"); | 497 | 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) ")"); |
477 | 498 | ||
478 | module_param(heartbeat, int, 0); | 499 | module_param(heartbeat, int, 0); |
479 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | 500 | MODULE_PARM_DESC(heartbeat, |
501 | "Watchdog heartbeat in seconds. (1 <= heartbeat <= 3600, default=" | ||
502 | __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | ||
480 | 503 | ||
481 | module_param(nowayout, int, 0); | 504 | module_param(nowayout, int, 0); |
482 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 505 | MODULE_PARM_DESC(nowayout, |
506 | "Watchdog cannot be stopped once started (default=" | ||
507 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
483 | 508 | ||
484 | module_init(sh_wdt_init); | 509 | module_init(sh_wdt_init); |
485 | module_exit(sh_wdt_exit); | 510 | module_exit(sh_wdt_exit); |