aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/sch56xx-common.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-03-18 08:05:08 -0400
committerGuenter Roeck <guenter.roeck@ericsson.com>2012-03-20 09:42:05 -0400
commit312869ec935ab3bb67b7ba641a7d11230555aff5 (patch)
treecce1f87f557373a95c3cf44b792532f42cee728f /drivers/hwmon/sch56xx-common.c
parent840e191dba36f51ae52db8a93849191af9e89098 (diff)
hwmon: (sch56xx) Add support for the integrated watchdog (v2)
Add support for the watchdog integrated into the SMSC SCH5627 and SCH5636 superio-s. Since the watchdog is part of the hwmon logical device and thus shares ioports with it, the watchdog driver is integrated into the existing hwmon drivers for these. Note that this version of the watchdog support for sch56xx superio-s implements the watchdog chardev interface itself, rather then relying on the recently added watchdog core / watchdog_dev. This is done because currently some needed functionality is missing from watchdog_dev, as soon as this functionality is added (which is being discussed on the linux-watchdog mailinglist), I'll convert this driver over to using watchdog_dev. Signed-off-by: Hans de Goede <hdegoede@redhat.com> [guenter.roeck@ericsson.com: Added missing linux/slab.h include] Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
Diffstat (limited to 'drivers/hwmon/sch56xx-common.c')
-rw-r--r--drivers/hwmon/sch56xx-common.c519
1 files changed, 517 insertions, 2 deletions
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c
index fac32ee0b10e..ce52fc57d41d 100644
--- a/drivers/hwmon/sch56xx-common.c
+++ b/drivers/hwmon/sch56xx-common.c
@@ -1,5 +1,5 @@
1/*************************************************************************** 1/***************************************************************************
2 * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> * 2 * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com> *
3 * * 3 * *
4 * This program is free software; you can redistribute it and/or modify * 4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by * 5 * it under the terms of the GNU General Public License as published by *
@@ -26,8 +26,20 @@
26#include <linux/io.h> 26#include <linux/io.h>
27#include <linux/acpi.h> 27#include <linux/acpi.h>
28#include <linux/delay.h> 28#include <linux/delay.h>
29#include <linux/fs.h>
30#include <linux/watchdog.h>
31#include <linux/miscdevice.h>
32#include <linux/uaccess.h>
33#include <linux/kref.h>
34#include <linux/slab.h>
29#include "sch56xx-common.h" 35#include "sch56xx-common.h"
30 36
37/* Insmod parameters */
38static int nowayout = WATCHDOG_NOWAYOUT;
39module_param(nowayout, int, 0);
40MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
41 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
42
31#define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */ 43#define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */
32#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ 44#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */
33#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ 45#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
@@ -40,13 +52,45 @@
40#define SIO_SCH5627_ID 0xC6 /* Chipset ID */ 52#define SIO_SCH5627_ID 0xC6 /* Chipset ID */
41#define SIO_SCH5636_ID 0xC7 /* Chipset ID */ 53#define SIO_SCH5636_ID 0xC7 /* Chipset ID */
42 54
43#define REGION_LENGTH 9 55#define REGION_LENGTH 10
44 56
45#define SCH56XX_CMD_READ 0x02 57#define SCH56XX_CMD_READ 0x02
46#define SCH56XX_CMD_WRITE 0x03 58#define SCH56XX_CMD_WRITE 0x03
47 59
60/* Watchdog registers */
61#define SCH56XX_REG_WDOG_PRESET 0x58B
62#define SCH56XX_REG_WDOG_CONTROL 0x58C
63#define SCH56XX_WDOG_TIME_BASE_SEC 0x01
64#define SCH56XX_REG_WDOG_OUTPUT_ENABLE 0x58E
65#define SCH56XX_WDOG_OUTPUT_ENABLE 0x02
66
67struct sch56xx_watchdog_data {
68 u16 addr;
69 u32 revision;
70 struct mutex *io_lock;
71 struct mutex watchdog_lock;
72 struct list_head list; /* member of the watchdog_data_list */
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;
79 u8 watchdog_control;
80 u8 watchdog_output_enable;
81};
82
48static struct platform_device *sch56xx_pdev; 83static struct platform_device *sch56xx_pdev;
49 84
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
50/* Super I/O functions */ 94/* Super I/O functions */
51static inline int superio_inb(int base, int reg) 95static inline int superio_inb(int base, int reg)
52{ 96{
@@ -224,6 +268,477 @@ int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
224} 268}
225EXPORT_SYMBOL(sch56xx_read_virtual_reg12); 269EXPORT_SYMBOL(sch56xx_read_virtual_reg12);
226 270
271/*
272 * Watchdog routines
273 */
274
275/*
276 * Release our data struct when the platform device has been released *and*
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{
289 int ret, resolution;
290 u8 control;
291
292 /* 1 second or 60 second resolution? */
293 if (timeout <= 255)
294 resolution = 1;
295 else
296 resolution = 60;
297
298 if (timeout < resolution || timeout > (resolution * 255))
299 return -EINVAL;
300
301 mutex_lock(&data->watchdog_lock);
302 if (!data->addr) {
303 ret = -ENODEV;
304 goto leave;
305 }
306
307 if (resolution == 1)
308 control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC;
309 else
310 control = data->watchdog_control & ~SCH56XX_WDOG_TIME_BASE_SEC;
311
312 if (data->watchdog_control != control) {
313 mutex_lock(data->io_lock);
314 ret = sch56xx_write_virtual_reg(data->addr,
315 SCH56XX_REG_WDOG_CONTROL,
316 control);
317 mutex_unlock(data->io_lock);
318 if (ret)
319 goto leave;
320
321 data->watchdog_control = control;
322 }
323
324 /*
325 * Remember new timeout value, but do not write as that (re)starts
326 * the watchdog countdown.
327 */
328 data->watchdog_preset = DIV_ROUND_UP(timeout, resolution);
329
330 ret = data->watchdog_preset * resolution;
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}
349
350static int watchdog_start(struct sch56xx_watchdog_data *data)
351{
352 int ret;
353 u8 val;
354
355 mutex_lock(&data->watchdog_lock);
356 if (!data->addr) {
357 ret = -ENODEV;
358 goto leave_unlock_watchdog;
359 }
360
361 /*
362 * The sch56xx's watchdog cannot really be started / stopped
363 * it is always running, but we can avoid the timer expiring
364 * from causing a system reset by clearing the output enable bit.
365 *
366 * The sch56xx's watchdog will set the watchdog event bit, bit 0
367 * of the second interrupt source register (at base-address + 9),
368 * when the timer expires.
369 *
370 * This will only cause a system reset if the 0-1 flank happens when
371 * output enable is true. Setting output enable after the flank will
372 * not cause a reset, nor will the timer expiring a second time.
373 * This means we must clear the watchdog event bit in case it is set.
374 *
375 * The timer may still be running (after a recent watchdog_stop) and
376 * mere milliseconds away from expiring, so the timer must be reset
377 * first!
378 */
379
380 mutex_lock(data->io_lock);
381
382 /* 1. Reset the watchdog countdown counter */
383 ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET,
384 data->watchdog_preset);
385 if (ret)
386 goto leave;
387
388 /* 2. Enable output (if not already enabled) */
389 if (!(data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) {
390 val = data->watchdog_output_enable |
391 SCH56XX_WDOG_OUTPUT_ENABLE;
392 ret = sch56xx_write_virtual_reg(data->addr,
393 SCH56XX_REG_WDOG_OUTPUT_ENABLE,
394 val);
395 if (ret)
396 goto leave;
397
398 data->watchdog_output_enable = val;
399 }
400
401 /* 3. Clear the watchdog event bit if set */
402 val = inb(data->addr + 9);
403 if (val & 0x01)
404 outb(0x01, data->addr + 9);
405
406leave:
407 mutex_unlock(data->io_lock);
408leave_unlock_watchdog:
409 mutex_unlock(&data->watchdog_lock);
410 return ret;
411}
412
413static int watchdog_trigger(struct sch56xx_watchdog_data *data)
414{
415 int ret;
416
417 mutex_lock(&data->watchdog_lock);
418 if (!data->addr) {
419 ret = -ENODEV;
420 goto leave;
421 }
422
423 /* Reset the watchdog countdown counter */
424 mutex_lock(data->io_lock);
425 ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET,
426 data->watchdog_preset);
427 mutex_unlock(data->io_lock);
428leave:
429 mutex_unlock(&data->watchdog_lock);
430 return ret;
431}
432
433static int watchdog_stop_unlocked(struct sch56xx_watchdog_data *data)
434{
435 int ret = 0;
436 u8 val;
437
438 if (!data->addr)
439 return -ENODEV;
440
441 if (data->watchdog_output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) {
442 val = data->watchdog_output_enable &
443 ~SCH56XX_WDOG_OUTPUT_ENABLE;
444 mutex_lock(data->io_lock);
445 ret = sch56xx_write_virtual_reg(data->addr,
446 SCH56XX_REG_WDOG_OUTPUT_ENABLE,
447 val);
448 mutex_unlock(data->io_lock);
449 if (ret)
450 return ret;
451
452 data->watchdog_output_enable = val;
453 }
454
455 return ret;
456}
457
458static int watchdog_stop(struct sch56xx_watchdog_data *data)
459{
460 int ret;
461
462 mutex_lock(&data->watchdog_lock);
463 ret = watchdog_stop_unlocked(data);
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};
629
630struct sch56xx_watchdog_data *sch56xx_watchdog_register(
631 u16 addr, u32 revision, struct mutex *io_lock, int check_enabled)
632{
633 struct sch56xx_watchdog_data *data;
634 int i, err, control, output_enable;
635 const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
636
637 /* Cache the watchdog registers */
638 mutex_lock(io_lock);
639 control =
640 sch56xx_read_virtual_reg(addr, SCH56XX_REG_WDOG_CONTROL);
641 output_enable =
642 sch56xx_read_virtual_reg(addr, SCH56XX_REG_WDOG_OUTPUT_ENABLE);
643 mutex_unlock(io_lock);
644
645 if (control < 0)
646 return NULL;
647 if (output_enable < 0)
648 return NULL;
649 if (check_enabled && !(output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) {
650 pr_warn("Watchdog not enabled by BIOS, not registering\n");
651 return NULL;
652 }
653
654 data = kzalloc(sizeof(struct sch56xx_watchdog_data), GFP_KERNEL);
655 if (!data)
656 return NULL;
657
658 data->addr = addr;
659 data->revision = revision;
660 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
671 /*
672 * We take the data_mutex lock early so that watchdog_open() cannot
673 * run when misc_register() has completed, but we've not yet added
674 * our data to the watchdog_data_list.
675 */
676 mutex_lock(&watchdog_data_mutex);
677 for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) {
678 /* Register our watchdog part */
679 snprintf(data->watchdog_name, sizeof(data->watchdog_name),
680 "watchdog%c", (i == 0) ? '\0' : ('0' + i));
681 data->watchdog_miscdev.name = data->watchdog_name;
682 data->watchdog_miscdev.fops = &watchdog_fops;
683 data->watchdog_miscdev.minor = watchdog_minors[i];
684 err = misc_register(&data->watchdog_miscdev);
685 if (err == -EBUSY)
686 continue;
687 if (err)
688 break;
689
690 list_add(&data->list, &watchdog_data_list);
691 pr_info("Registered /dev/%s chardev major 10, minor: %d\n",
692 data->watchdog_name, watchdog_minors[i]);
693 break;
694 }
695 mutex_unlock(&watchdog_data_mutex);
696
697 if (err) {
698 pr_err("Registering watchdog chardev: %d\n", err);
699 goto error;
700 }
701 if (i == ARRAY_SIZE(watchdog_minors)) {
702 pr_warn("Couldn't register watchdog (no free minor)\n");
703 goto error;
704 }
705
706 return data;
707
708error:
709 kfree(data);
710 return NULL;
711}
712EXPORT_SYMBOL(sch56xx_watchdog_register);
713
714void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data)
715{
716 mutex_lock(&watchdog_data_mutex);
717 misc_deregister(&data->watchdog_miscdev);
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}
736EXPORT_SYMBOL(sch56xx_watchdog_unregister);
737
738/*
739 * platform dev find, add and remove functions
740 */
741
227static int __init sch56xx_find(int sioaddr, unsigned short *address, 742static int __init sch56xx_find(int sioaddr, unsigned short *address,
228 const char **name) 743 const char **name)
229{ 744{