diff options
author | Wim Van Sebroeck <wim@iguana.be> | 2006-09-02 14:53:19 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2006-10-04 16:36:42 -0400 |
commit | aa1fd4d7c3b131026bf156da40fdf94bcbd705aa (patch) | |
tree | 10159853c6652287e3f2e80c536867b659b05595 | |
parent | 8386c8cfb2131b2a9caae3db6bf94292bbbe1caf (diff) |
[WATCHDOG] Winbond SMsC37B787 watchdog fixes
* Added io spinlocking
* Deleted WATCHDOG_MINOR (it's in the miscdevice include
* Changed timer_enabled to use set_bit functions
* WDIOC_GETSUPPORT should return -EFAULT or 0
* timeout should be correct before we initialize the watchdog
* we should initialize the watchdog before we give access
to userspace
* Third parameter of module_param is not the default or
initial value
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r-- | drivers/char/watchdog/smsc37b787_wdt.c | 47 |
1 files changed, 30 insertions, 17 deletions
diff --git a/drivers/char/watchdog/smsc37b787_wdt.c b/drivers/char/watchdog/smsc37b787_wdt.c index 1d01b3074db3..9f56913b484f 100644 --- a/drivers/char/watchdog/smsc37b787_wdt.c +++ b/drivers/char/watchdog/smsc37b787_wdt.c | |||
@@ -54,6 +54,7 @@ | |||
54 | #include <linux/notifier.h> | 54 | #include <linux/notifier.h> |
55 | #include <linux/reboot.h> | 55 | #include <linux/reboot.h> |
56 | #include <linux/init.h> | 56 | #include <linux/init.h> |
57 | #include <linux/spinlock.h> | ||
57 | 58 | ||
58 | #include <asm/io.h> | 59 | #include <asm/io.h> |
59 | #include <asm/uaccess.h> | 60 | #include <asm/uaccess.h> |
@@ -72,18 +73,18 @@ | |||
72 | #define MODNAME "smsc37b787_wdt: " | 73 | #define MODNAME "smsc37b787_wdt: " |
73 | #define VERSION "1.1" | 74 | #define VERSION "1.1" |
74 | 75 | ||
75 | #define WATCHDOG_MINOR 130 | ||
76 | |||
77 | #define IOPORT 0x3F0 | 76 | #define IOPORT 0x3F0 |
78 | #define IOPORT_SIZE 2 | 77 | #define IOPORT_SIZE 2 |
79 | #define IODEV_NO 8 | 78 | #define IODEV_NO 8 |
80 | 79 | ||
81 | static int unit = UNIT_SECOND; /* timer's unit */ | 80 | static int unit = UNIT_SECOND; /* timer's unit */ |
82 | static int timeout = 60; /* timeout value: default is 60 "units" */ | 81 | static int timeout = 60; /* timeout value: default is 60 "units" */ |
83 | static int timer_enabled = 0; /* is the timer enabled? */ | 82 | static unsigned long timer_enabled = 0; /* is the timer enabled? */ |
84 | 83 | ||
85 | static char expect_close; /* is the close expected? */ | 84 | static char expect_close; /* is the close expected? */ |
86 | 85 | ||
86 | static spinlock_t io_lock; /* to guard the watchdog from io races */ | ||
87 | |||
87 | static int nowayout = WATCHDOG_NOWAYOUT; | 88 | static int nowayout = WATCHDOG_NOWAYOUT; |
88 | 89 | ||
89 | /* -- Low level function ----------------------------------------*/ | 90 | /* -- Low level function ----------------------------------------*/ |
@@ -210,6 +211,7 @@ static void wb_smsc_wdt_initialize(void) | |||
210 | { | 211 | { |
211 | unsigned char old; | 212 | unsigned char old; |
212 | 213 | ||
214 | spin_lock(&io_lock); | ||
213 | open_io_config(); | 215 | open_io_config(); |
214 | select_io_device(IODEV_NO); | 216 | select_io_device(IODEV_NO); |
215 | 217 | ||
@@ -234,12 +236,14 @@ static void wb_smsc_wdt_initialize(void) | |||
234 | wdt_timer_units(old); | 236 | wdt_timer_units(old); |
235 | 237 | ||
236 | close_io_config(); | 238 | close_io_config(); |
239 | spin_unlock(&io_lock); | ||
237 | } | 240 | } |
238 | 241 | ||
239 | /* shutdown the watchdog */ | 242 | /* shutdown the watchdog */ |
240 | 243 | ||
241 | static void wb_smsc_wdt_shutdown(void) | 244 | static void wb_smsc_wdt_shutdown(void) |
242 | { | 245 | { |
246 | spin_lock(&io_lock); | ||
243 | open_io_config(); | 247 | open_io_config(); |
244 | select_io_device(IODEV_NO); | 248 | select_io_device(IODEV_NO); |
245 | 249 | ||
@@ -257,12 +261,14 @@ static void wb_smsc_wdt_shutdown(void) | |||
257 | wdt_timeout_value(0x00); | 261 | wdt_timeout_value(0x00); |
258 | 262 | ||
259 | close_io_config(); | 263 | close_io_config(); |
264 | spin_unlock(&io_lock); | ||
260 | } | 265 | } |
261 | 266 | ||
262 | /* set timeout => enable watchdog */ | 267 | /* set timeout => enable watchdog */ |
263 | 268 | ||
264 | static void wb_smsc_wdt_set_timeout(unsigned char new_timeout) | 269 | static void wb_smsc_wdt_set_timeout(unsigned char new_timeout) |
265 | { | 270 | { |
271 | spin_lock(&io_lock); | ||
266 | open_io_config(); | 272 | open_io_config(); |
267 | select_io_device(IODEV_NO); | 273 | select_io_device(IODEV_NO); |
268 | 274 | ||
@@ -273,6 +279,7 @@ static void wb_smsc_wdt_set_timeout(unsigned char new_timeout) | |||
273 | wdt_timeout_value(new_timeout); | 279 | wdt_timeout_value(new_timeout); |
274 | 280 | ||
275 | close_io_config(); | 281 | close_io_config(); |
282 | spin_unlock(&io_lock); | ||
276 | } | 283 | } |
277 | 284 | ||
278 | /* get timeout */ | 285 | /* get timeout */ |
@@ -281,10 +288,12 @@ static unsigned char wb_smsc_wdt_get_timeout(void) | |||
281 | { | 288 | { |
282 | unsigned char set_timeout; | 289 | unsigned char set_timeout; |
283 | 290 | ||
291 | spin_lock(&io_lock); | ||
284 | open_io_config(); | 292 | open_io_config(); |
285 | select_io_device(IODEV_NO); | 293 | select_io_device(IODEV_NO); |
286 | set_timeout = read_io_cr(0xF2); | 294 | set_timeout = read_io_cr(0xF2); |
287 | close_io_config(); | 295 | close_io_config(); |
296 | spin_unlock(&io_lock); | ||
288 | 297 | ||
289 | return set_timeout; | 298 | return set_timeout; |
290 | } | 299 | } |
@@ -309,6 +318,7 @@ static void wb_smsc_wdt_enable(void) | |||
309 | 318 | ||
310 | static void wb_smsc_wdt_reset_timer(void) | 319 | static void wb_smsc_wdt_reset_timer(void) |
311 | { | 320 | { |
321 | spin_lock(&io_lock); | ||
312 | open_io_config(); | 322 | open_io_config(); |
313 | select_io_device(IODEV_NO); | 323 | select_io_device(IODEV_NO); |
314 | 324 | ||
@@ -317,6 +327,7 @@ static void wb_smsc_wdt_reset_timer(void) | |||
317 | wdt_timer_conf(0x08); | 327 | wdt_timer_conf(0x08); |
318 | 328 | ||
319 | close_io_config(); | 329 | close_io_config(); |
330 | spin_unlock(&io_lock); | ||
320 | } | 331 | } |
321 | 332 | ||
322 | /* return, if the watchdog is enabled (timeout is set...) */ | 333 | /* return, if the watchdog is enabled (timeout is set...) */ |
@@ -335,14 +346,13 @@ static int wb_smsc_wdt_open(struct inode *inode, struct file *file) | |||
335 | { | 346 | { |
336 | /* /dev/watchdog can only be opened once */ | 347 | /* /dev/watchdog can only be opened once */ |
337 | 348 | ||
338 | if (timer_enabled) | 349 | if (test_and_set_bit(0, &timer_enabled)) |
339 | return -EBUSY; | 350 | return -EBUSY; |
340 | 351 | ||
341 | if (nowayout) | 352 | if (nowayout) |
342 | __module_get(THIS_MODULE); | 353 | __module_get(THIS_MODULE); |
343 | 354 | ||
344 | /* Reload and activate timer */ | 355 | /* Reload and activate timer */ |
345 | timer_enabled = 1; | ||
346 | wb_smsc_wdt_enable(); | 356 | wb_smsc_wdt_enable(); |
347 | 357 | ||
348 | printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); | 358 | printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); |
@@ -364,7 +374,7 @@ static int wb_smsc_wdt_release(struct inode *inode, struct file *file) | |||
364 | wb_smsc_wdt_reset_timer(); | 374 | wb_smsc_wdt_reset_timer(); |
365 | } | 375 | } |
366 | 376 | ||
367 | timer_enabled = 0; | 377 | clear_bit(0, &timer_enabled); |
368 | expect_close = 0; | 378 | expect_close = 0; |
369 | return 0; | 379 | return 0; |
370 | } | 380 | } |
@@ -425,7 +435,8 @@ static int wb_smsc_wdt_ioctl(struct inode *inode, struct file *file, | |||
425 | return -ENOTTY; | 435 | return -ENOTTY; |
426 | 436 | ||
427 | case WDIOC_GETSUPPORT: | 437 | case WDIOC_GETSUPPORT: |
428 | return copy_to_user(uarg.ident, &ident, sizeof(ident)); | 438 | return copy_to_user(uarg.ident, &ident, |
439 | sizeof(ident)) ? -EFAULT : 0; | ||
429 | 440 | ||
430 | case WDIOC_GETSTATUS: | 441 | case WDIOC_GETSTATUS: |
431 | return put_user(wb_smsc_wdt_status(), uarg.i); | 442 | return put_user(wb_smsc_wdt_status(), uarg.i); |
@@ -506,12 +517,12 @@ static struct file_operations wb_smsc_wdt_fops = | |||
506 | .write = wb_smsc_wdt_write, | 517 | .write = wb_smsc_wdt_write, |
507 | .ioctl = wb_smsc_wdt_ioctl, | 518 | .ioctl = wb_smsc_wdt_ioctl, |
508 | .open = wb_smsc_wdt_open, | 519 | .open = wb_smsc_wdt_open, |
509 | .release = wb_smsc_wdt_release | 520 | .release = wb_smsc_wdt_release, |
510 | }; | 521 | }; |
511 | 522 | ||
512 | static struct notifier_block wb_smsc_wdt_notifier = | 523 | static struct notifier_block wb_smsc_wdt_notifier = |
513 | { | 524 | { |
514 | .notifier_call = wb_smsc_wdt_notify_sys | 525 | .notifier_call = wb_smsc_wdt_notify_sys, |
515 | }; | 526 | }; |
516 | 527 | ||
517 | static struct miscdevice wb_smsc_wdt_miscdev = | 528 | static struct miscdevice wb_smsc_wdt_miscdev = |
@@ -529,6 +540,8 @@ static int __init wb_smsc_wdt_init(void) | |||
529 | { | 540 | { |
530 | int ret; | 541 | int ret; |
531 | 542 | ||
543 | spin_lock_init(&io_lock); | ||
544 | |||
532 | printk("SMsC 37B787 watchdog component driver " VERSION " initialising...\n"); | 545 | printk("SMsC 37B787 watchdog component driver " VERSION " initialising...\n"); |
533 | 546 | ||
534 | if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) { | 547 | if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) { |
@@ -537,6 +550,13 @@ static int __init wb_smsc_wdt_init(void) | |||
537 | goto out_pnp; | 550 | goto out_pnp; |
538 | } | 551 | } |
539 | 552 | ||
553 | // set new maximum, if it's too big | ||
554 | if (timeout > MAX_TIMEOUT) | ||
555 | timeout = MAX_TIMEOUT; | ||
556 | |||
557 | // init the watchdog timer | ||
558 | wb_smsc_wdt_initialize(); | ||
559 | |||
540 | ret = register_reboot_notifier(&wb_smsc_wdt_notifier); | 560 | ret = register_reboot_notifier(&wb_smsc_wdt_notifier); |
541 | if (ret) { | 561 | if (ret) { |
542 | printk(KERN_ERR MODNAME "Unable to register reboot notifier err = %d\n", ret); | 562 | printk(KERN_ERR MODNAME "Unable to register reboot notifier err = %d\n", ret); |
@@ -549,13 +569,6 @@ static int __init wb_smsc_wdt_init(void) | |||
549 | goto out_rbt; | 569 | goto out_rbt; |
550 | } | 570 | } |
551 | 571 | ||
552 | // init the watchdog timer | ||
553 | wb_smsc_wdt_initialize(); | ||
554 | |||
555 | // set new maximum, if it's too big | ||
556 | if (timeout > MAX_TIMEOUT) | ||
557 | timeout = MAX_TIMEOUT; | ||
558 | |||
559 | // output info | 572 | // output info |
560 | printk(KERN_INFO MODNAME "Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); | 573 | printk(KERN_INFO MODNAME "Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)"); |
561 | printk(KERN_INFO MODNAME "Watchdog initialized and sleeping (nowayout=%d)...\n", nowayout); | 574 | printk(KERN_INFO MODNAME "Watchdog initialized and sleeping (nowayout=%d)...\n", nowayout); |
@@ -607,7 +620,7 @@ module_param(unit, int, 0); | |||
607 | MODULE_PARM_DESC(unit, "set unit to use, 0=seconds or 1=minutes, default is 0"); | 620 | MODULE_PARM_DESC(unit, "set unit to use, 0=seconds or 1=minutes, default is 0"); |
608 | #endif | 621 | #endif |
609 | 622 | ||
610 | module_param(timeout, int, 60); | 623 | module_param(timeout, int, 0); |
611 | MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60"); | 624 | MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60"); |
612 | 625 | ||
613 | module_param(nowayout, int, 0); | 626 | module_param(nowayout, int, 0); |