aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/sch56xx-common.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-05-22 05:40:24 -0400
committerWim Van Sebroeck <wim@iguana.be>2012-05-30 01:55:38 -0400
commitfb551405c0f8e15d6fc7ae6e16a5e15382f8b8ac (patch)
tree79b8e6dc8ee5180e74a368748f2fac94f525f4e8 /drivers/hwmon/sch56xx-common.c
parente907df32725204d6d2cb79b872529911c8eadcdf (diff)
watchdog: sch56xx: Use watchdog core
Convert sch56xx drivers to the generic watchdog core. Note this patch depends on the "watchdog: Add multiple device support" patch from Alan Cox. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/hwmon/sch56xx-common.c')
-rw-r--r--drivers/hwmon/sch56xx-common.c371
1 files changed, 58 insertions, 313 deletions
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c
index ce52fc57d41d..419a8e8f5191 100644
--- a/drivers/hwmon/sch56xx-common.c
+++ b/drivers/hwmon/sch56xx-common.c
@@ -66,15 +66,9 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
66 66
67struct sch56xx_watchdog_data { 67struct sch56xx_watchdog_data {
68 u16 addr; 68 u16 addr;
69 u32 revision;
70 struct mutex *io_lock; 69 struct mutex *io_lock;
71 struct mutex watchdog_lock; 70 struct watchdog_info wdinfo;
72 struct list_head list; /* member of the watchdog_data_list */ 71 struct watchdog_device wddev;
73 struct kref kref;
74 struct miscdevice watchdog_miscdev;
75 unsigned long watchdog_is_open;
76 char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
77 char watchdog_expect_close;
78 u8 watchdog_preset; 72 u8 watchdog_preset;
79 u8 watchdog_control; 73 u8 watchdog_control;
80 u8 watchdog_output_enable; 74 u8 watchdog_output_enable;
@@ -82,15 +76,6 @@ struct sch56xx_watchdog_data {
82 76
83static struct platform_device *sch56xx_pdev; 77static struct platform_device *sch56xx_pdev;
84 78
85/*
86 * Somewhat ugly :( global data pointer list with all sch56xx devices, so that
87 * we can find our device data as when using misc_register there is no other
88 * method to get to ones device data from the open fop.
89 */
90static LIST_HEAD(watchdog_data_list);
91/* Note this lock not only protect list access, but also data.kref access */
92static DEFINE_MUTEX(watchdog_data_mutex);
93
94/* Super I/O functions */ 79/* Super I/O functions */
95static inline int superio_inb(int base, int reg) 80static inline int superio_inb(int base, int reg)
96{ 81{
@@ -272,22 +257,13 @@ EXPORT_SYMBOL(sch56xx_read_virtual_reg12);
272 * Watchdog routines 257 * Watchdog routines
273 */ 258 */
274 259
275/* 260static int watchdog_set_timeout(struct watchdog_device *wddev,
276 * Release our data struct when the platform device has been released *and* 261 unsigned int timeout)
277 * all references to our watchdog device are released.
278 */
279static void sch56xx_watchdog_release_resources(struct kref *r)
280{
281 struct sch56xx_watchdog_data *data =
282 container_of(r, struct sch56xx_watchdog_data, kref);
283 kfree(data);
284}
285
286static int watchdog_set_timeout(struct sch56xx_watchdog_data *data,
287 int timeout)
288{ 262{
289 int ret, resolution; 263 struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
264 unsigned int resolution;
290 u8 control; 265 u8 control;
266 int ret;
291 267
292 /* 1 second or 60 second resolution? */ 268 /* 1 second or 60 second resolution? */
293 if (timeout <= 255) 269 if (timeout <= 255)
@@ -298,12 +274,6 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data,
298 if (timeout < resolution || timeout > (resolution * 255)) 274 if (timeout < resolution || timeout > (resolution * 255))
299 return -EINVAL; 275 return -EINVAL;
300 276
301 mutex_lock(&data->watchdog_lock);
302 if (!data->addr) {
303 ret = -ENODEV;
304 goto leave;
305 }
306
307 if (resolution == 1) 277 if (resolution == 1)
308 control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC; 278 control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC;
309 else 279 else
@@ -316,7 +286,7 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data,
316 control); 286 control);
317 mutex_unlock(data->io_lock); 287 mutex_unlock(data->io_lock);
318 if (ret) 288 if (ret)
319 goto leave; 289 return ret;
320 290
321 data->watchdog_control = control; 291 data->watchdog_control = control;
322 } 292 }
@@ -326,38 +296,17 @@ static int watchdog_set_timeout(struct sch56xx_watchdog_data *data,
326 * the watchdog countdown. 296 * the watchdog countdown.
327 */ 297 */
328 data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); 298 data->watchdog_preset = DIV_ROUND_UP(timeout, resolution);
299 wddev->timeout = data->watchdog_preset * resolution;
329 300
330 ret = data->watchdog_preset * resolution; 301 return 0;
331leave:
332 mutex_unlock(&data->watchdog_lock);
333 return ret;
334}
335
336static int watchdog_get_timeout(struct sch56xx_watchdog_data *data)
337{
338 int timeout;
339
340 mutex_lock(&data->watchdog_lock);
341 if (data->watchdog_control & SCH56XX_WDOG_TIME_BASE_SEC)
342 timeout = data->watchdog_preset;
343 else
344 timeout = data->watchdog_preset * 60;
345 mutex_unlock(&data->watchdog_lock);
346
347 return timeout;
348} 302}
349 303
350static int watchdog_start(struct sch56xx_watchdog_data *data) 304static int watchdog_start(struct watchdog_device *wddev)
351{ 305{
306 struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
352 int ret; 307 int ret;
353 u8 val; 308 u8 val;
354 309
355 mutex_lock(&data->watchdog_lock);
356 if (!data->addr) {
357 ret = -ENODEV;
358 goto leave_unlock_watchdog;
359 }
360
361 /* 310 /*
362 * The sch56xx's watchdog cannot really be started / stopped 311 * The sch56xx's watchdog cannot really be started / stopped
363 * it is always running, but we can avoid the timer expiring 312 * it is always running, but we can avoid the timer expiring
@@ -405,39 +354,29 @@ static int watchdog_start(struct sch56xx_watchdog_data *data)
405 354
406leave: 355leave:
407 mutex_unlock(data->io_lock); 356 mutex_unlock(data->io_lock);
408leave_unlock_watchdog:
409 mutex_unlock(&data->watchdog_lock);
410 return ret; 357 return ret;
411} 358}
412 359
413static int watchdog_trigger(struct sch56xx_watchdog_data *data) 360static int watchdog_trigger(struct watchdog_device *wddev)
414{ 361{
362 struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
415 int ret; 363 int ret;
416 364
417 mutex_lock(&data->watchdog_lock);
418 if (!data->addr) {
419 ret = -ENODEV;
420 goto leave;
421 }
422
423 /* Reset the watchdog countdown counter */ 365 /* Reset the watchdog countdown counter */
424 mutex_lock(data->io_lock); 366 mutex_lock(data->io_lock);
425 ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, 367 ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET,
426 data->watchdog_preset); 368 data->watchdog_preset);
427 mutex_unlock(data->io_lock); 369 mutex_unlock(data->io_lock);
428leave: 370
429 mutex_unlock(&data->watchdog_lock);
430 return ret; 371 return ret;
431} 372}
432 373
433static int watchdog_stop_unlocked(struct sch56xx_watchdog_data *data) 374static int watchdog_stop(struct watchdog_device *wddev)
434{ 375{
376 struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev);
435 int ret = 0; 377 int ret = 0;
436 u8 val; 378 u8 val;
437 379
438 if (!data->addr)
439 return -ENODEV;
440
441 if (data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) { 380 if (data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) {
442 val = data->watchdog_output_enable & 381 val = data->watchdog_output_enable &
443 ~SCH56XX_WDOG_OUTPUT_ENABLE; 382 ~SCH56XX_WDOG_OUTPUT_ENABLE;
@@ -455,184 +394,19 @@ static int watchdog_stop_unlocked(struct sch56xx_watchdog_data *data)
455 return ret; 394 return ret;
456} 395}
457 396
458static int watchdog_stop(struct sch56xx_watchdog_data *data) 397static const struct watchdog_ops watchdog_ops = {
459{ 398 .owner = THIS_MODULE,
460 int ret; 399 .start = watchdog_start,
461 400 .stop = watchdog_stop,
462 mutex_lock(&data->watchdog_lock); 401 .ping = watchdog_trigger,
463 ret = watchdog_stop_unlocked(data); 402 .set_timeout = watchdog_set_timeout,
464 mutex_unlock(&data->watchdog_lock);
465
466 return ret;
467}
468
469static int watchdog_release(struct inode *inode, struct file *filp)
470{
471 struct sch56xx_watchdog_data *data = filp->private_data;
472
473 if (data->watchdog_expect_close) {
474 watchdog_stop(data);
475 data->watchdog_expect_close = 0;
476 } else {
477 watchdog_trigger(data);
478 pr_crit("unexpected close, not stopping watchdog!\n");
479 }
480
481 clear_bit(0, &data->watchdog_is_open);
482
483 mutex_lock(&watchdog_data_mutex);
484 kref_put(&data->kref, sch56xx_watchdog_release_resources);
485 mutex_unlock(&watchdog_data_mutex);
486
487 return 0;
488}
489
490static int watchdog_open(struct inode *inode, struct file *filp)
491{
492 struct sch56xx_watchdog_data *pos, *data = NULL;
493 int ret, watchdog_is_open;
494
495 /*
496 * We get called from drivers/char/misc.c with misc_mtx hold, and we
497 * call misc_register() from sch56xx_watchdog_probe() with
498 * watchdog_data_mutex hold, as misc_register() takes the misc_mtx
499 * lock, this is a possible deadlock, so we use mutex_trylock here.
500 */
501 if (!mutex_trylock(&watchdog_data_mutex))
502 return -ERESTARTSYS;
503 list_for_each_entry(pos, &watchdog_data_list, list) {
504 if (pos->watchdog_miscdev.minor == iminor(inode)) {
505 data = pos;
506 break;
507 }
508 }
509 /* Note we can never not have found data, so we don't check for this */
510 watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open);
511 if (!watchdog_is_open)
512 kref_get(&data->kref);
513 mutex_unlock(&watchdog_data_mutex);
514
515 if (watchdog_is_open)
516 return -EBUSY;
517
518 filp->private_data = data;
519
520 /* Start the watchdog */
521 ret = watchdog_start(data);
522 if (ret) {
523 watchdog_release(inode, filp);
524 return ret;
525 }
526
527 return nonseekable_open(inode, filp);
528}
529
530static ssize_t watchdog_write(struct file *filp, const char __user *buf,
531 size_t count, loff_t *offset)
532{
533 int ret;
534 struct sch56xx_watchdog_data *data = filp->private_data;
535
536 if (count) {
537 if (!nowayout) {
538 size_t i;
539
540 /* Clear it in case it was set with a previous write */
541 data->watchdog_expect_close = 0;
542
543 for (i = 0; i != count; i++) {
544 char c;
545 if (get_user(c, buf + i))
546 return -EFAULT;
547 if (c == 'V')
548 data->watchdog_expect_close = 1;
549 }
550 }
551 ret = watchdog_trigger(data);
552 if (ret)
553 return ret;
554 }
555 return count;
556}
557
558static long watchdog_ioctl(struct file *filp, unsigned int cmd,
559 unsigned long arg)
560{
561 struct watchdog_info ident = {
562 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
563 .identity = "sch56xx watchdog"
564 };
565 int i, ret = 0;
566 struct sch56xx_watchdog_data *data = filp->private_data;
567
568 switch (cmd) {
569 case WDIOC_GETSUPPORT:
570 ident.firmware_version = data->revision;
571 if (!nowayout)
572 ident.options |= WDIOF_MAGICCLOSE;
573 if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
574 ret = -EFAULT;
575 break;
576
577 case WDIOC_GETSTATUS:
578 case WDIOC_GETBOOTSTATUS:
579 ret = put_user(0, (int __user *)arg);
580 break;
581
582 case WDIOC_KEEPALIVE:
583 ret = watchdog_trigger(data);
584 break;
585
586 case WDIOC_GETTIMEOUT:
587 i = watchdog_get_timeout(data);
588 ret = put_user(i, (int __user *)arg);
589 break;
590
591 case WDIOC_SETTIMEOUT:
592 if (get_user(i, (int __user *)arg)) {
593 ret = -EFAULT;
594 break;
595 }
596 ret = watchdog_set_timeout(data, i);
597 if (ret >= 0)
598 ret = put_user(ret, (int __user *)arg);
599 break;
600
601 case WDIOC_SETOPTIONS:
602 if (get_user(i, (int __user *)arg)) {
603 ret = -EFAULT;
604 break;
605 }
606
607 if (i & WDIOS_DISABLECARD)
608 ret = watchdog_stop(data);
609 else if (i & WDIOS_ENABLECARD)
610 ret = watchdog_trigger(data);
611 else
612 ret = -EINVAL;
613 break;
614
615 default:
616 ret = -ENOTTY;
617 }
618 return ret;
619}
620
621static const struct file_operations watchdog_fops = {
622 .owner = THIS_MODULE,
623 .llseek = no_llseek,
624 .open = watchdog_open,
625 .release = watchdog_release,
626 .write = watchdog_write,
627 .unlocked_ioctl = watchdog_ioctl,
628}; 403};
629 404
630struct sch56xx_watchdog_data *sch56xx_watchdog_register( 405struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent,
631 u16 addr, u32 revision, struct mutex *io_lock, int check_enabled) 406 u16 addr, u32 revision, struct mutex *io_lock, int check_enabled)
632{ 407{
633 struct sch56xx_watchdog_data *data; 408 struct sch56xx_watchdog_data *data;
634 int i, err, control, output_enable; 409 int err, control, output_enable;
635 const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
636 410
637 /* Cache the watchdog registers */ 411 /* Cache the watchdog registers */
638 mutex_lock(io_lock); 412 mutex_lock(io_lock);
@@ -656,82 +430,53 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(
656 return NULL; 430 return NULL;
657 431
658 data->addr = addr; 432 data->addr = addr;
659 data->revision = revision;
660 data->io_lock = io_lock; 433 data->io_lock = io_lock;
661 data->watchdog_control = control;
662 data->watchdog_output_enable = output_enable;
663 mutex_init(&data->watchdog_lock);
664 INIT_LIST_HEAD(&data->list);
665 kref_init(&data->kref);
666
667 err = watchdog_set_timeout(data, 60);
668 if (err < 0)
669 goto error;
670 434
671 /* 435 strlcpy(data->wdinfo.identity, "sch56xx watchdog",
672 * We take the data_mutex lock early so that watchdog_open() cannot 436 sizeof(data->wdinfo.identity));
673 * run when misc_register() has completed, but we've not yet added 437 data->wdinfo.firmware_version = revision;
674 * our data to the watchdog_data_list. 438 data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT;
675 */ 439 if (!nowayout)
676 mutex_lock(&watchdog_data_mutex); 440 data->wdinfo.options |= WDIOF_MAGICCLOSE;
677 for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { 441
678 /* Register our watchdog part */ 442 data->wddev.info = &data->wdinfo;
679 snprintf(data->watchdog_name, sizeof(data->watchdog_name), 443 data->wddev.ops = &watchdog_ops;
680 "watchdog%c", (i == 0) ? '\0' : ('0' + i)); 444 data->wddev.parent = parent;
681 data->watchdog_miscdev.name = data->watchdog_name; 445 data->wddev.timeout = 60;
682 data->watchdog_miscdev.fops = &watchdog_fops; 446 data->wddev.min_timeout = 1;
683 data->watchdog_miscdev.minor = watchdog_minors[i]; 447 data->wddev.max_timeout = 255 * 60;
684 err = misc_register(&data->watchdog_miscdev); 448 if (nowayout)
685 if (err == -EBUSY) 449 data->wddev.status |= WDOG_NO_WAY_OUT;
686 continue; 450 if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)
687 if (err) 451 data->wddev.status |= WDOG_ACTIVE;
688 break; 452
453 /* Since the watchdog uses a downcounter there is no register to read
454 the BIOS set timeout from (if any was set at all) ->
455 Choose a preset which will give us a 1 minute timeout */
456 if (control & SCH56XX_WDOG_TIME_BASE_SEC)
457 data->watchdog_preset = 60; /* seconds */
458 else
459 data->watchdog_preset = 1; /* minute */
689 460
690 list_add(&data->list, &watchdog_data_list); 461 data->watchdog_control = control;
691 pr_info("Registered /dev/%s chardev major 10, minor: %d\n", 462 data->watchdog_output_enable = output_enable;
692 data->watchdog_name, watchdog_minors[i]);
693 break;
694 }
695 mutex_unlock(&watchdog_data_mutex);
696 463
464 watchdog_set_drvdata(&data->wddev, data);
465 err = watchdog_register_device(&data->wddev);
697 if (err) { 466 if (err) {
698 pr_err("Registering watchdog chardev: %d\n", err); 467 pr_err("Registering watchdog chardev: %d\n", err);
699 goto error; 468 kfree(data);
700 } 469 return NULL;
701 if (i == ARRAY_SIZE(watchdog_minors)) {
702 pr_warn("Couldn't register watchdog (no free minor)\n");
703 goto error;
704 } 470 }
705 471
706 return data; 472 return data;
707
708error:
709 kfree(data);
710 return NULL;
711} 473}
712EXPORT_SYMBOL(sch56xx_watchdog_register); 474EXPORT_SYMBOL(sch56xx_watchdog_register);
713 475
714void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) 476void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data)
715{ 477{
716 mutex_lock(&watchdog_data_mutex); 478 watchdog_unregister_device(&data->wddev);
717 misc_deregister(&data->watchdog_miscdev); 479 kfree(data);
718 list_del(&data->list);
719 mutex_unlock(&watchdog_data_mutex);
720
721 mutex_lock(&data->watchdog_lock);
722 if (data->watchdog_is_open) {
723 pr_warn("platform device unregistered with watchdog "
724 "open! Stopping watchdog.\n");
725 watchdog_stop_unlocked(data);
726 }
727 /* Tell the wdog start/stop/trigger functions our dev is gone */
728 data->addr = 0;
729 data->io_lock = NULL;
730 mutex_unlock(&data->watchdog_lock);
731
732 mutex_lock(&watchdog_data_mutex);
733 kref_put(&data->kref, sch56xx_watchdog_release_resources);
734 mutex_unlock(&watchdog_data_mutex);
735} 480}
736EXPORT_SYMBOL(sch56xx_watchdog_unregister); 481EXPORT_SYMBOL(sch56xx_watchdog_unregister);
737 482