aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/fschmd.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2009-01-07 10:37:33 -0500
committerJean Delvare <khali@linux-fr.org>2009-01-07 10:37:33 -0500
commit97950c3d423e474ef887749b238ee67731b532fe (patch)
tree0ce2a267c13d06f1d6514c1f67a8418dc82fa753 /drivers/hwmon/fschmd.c
parent453e308d773979f6bbdf4109df27101072f6524b (diff)
hwmon: (fschmd) Add watchdog support
This patch adds support for the watchdog part found in _all_ supported FSC sensor chips. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/hwmon/fschmd.c')
-rw-r--r--drivers/hwmon/fschmd.c401
1 files changed, 383 insertions, 18 deletions
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index c188b7135025..8bb1a44a1de5 100644
--- a/drivers/hwmon/fschmd.c
+++ b/drivers/hwmon/fschmd.c
@@ -42,11 +42,20 @@
42#include <linux/mutex.h> 42#include <linux/mutex.h>
43#include <linux/sysfs.h> 43#include <linux/sysfs.h>
44#include <linux/dmi.h> 44#include <linux/dmi.h>
45#include <linux/fs.h>
46#include <linux/watchdog.h>
47#include <linux/miscdevice.h>
48#include <linux/uaccess.h>
49#include <linux/kref.h>
45 50
46/* Addresses to scan */ 51/* Addresses to scan */
47static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; 52static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
48 53
49/* Insmod parameters */ 54/* Insmod parameters */
55static int nowayout = WATCHDOG_NOWAYOUT;
56module_param(nowayout, int, 0);
57MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
58 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
50I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); 59I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd);
51 60
52/* 61/*
@@ -65,11 +74,18 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd);
65 74
66#define FSCHMD_CONTROL_ALERT_LED 0x01 75#define FSCHMD_CONTROL_ALERT_LED 0x01
67 76
68/* watchdog (support to be implemented) */ 77/* watchdog */
69#define FSCHMD_REG_WDOG_PRESET 0x28 78#define FSCHMD_REG_WDOG_PRESET 0x28
70#define FSCHMD_REG_WDOG_STATE 0x23 79#define FSCHMD_REG_WDOG_STATE 0x23
71#define FSCHMD_REG_WDOG_CONTROL 0x21 80#define FSCHMD_REG_WDOG_CONTROL 0x21
72 81
82#define FSCHMD_WDOG_CONTROL_TRIGGER 0x10
83#define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */
84#define FSCHMD_WDOG_CONTROL_STOP 0x20
85#define FSCHMD_WDOG_CONTROL_RESOLUTION 0x40
86
87#define FSCHMD_WDOG_STATE_CARDRESET 0x02
88
73/* voltages, weird order is to keep the same order as the old drivers */ 89/* voltages, weird order is to keep the same order as the old drivers */
74static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 }; 90static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 };
75 91
@@ -206,14 +222,26 @@ static struct i2c_driver fschmd_driver = {
206 */ 222 */
207 223
208struct fschmd_data { 224struct fschmd_data {
225 struct i2c_client *client;
209 struct device *hwmon_dev; 226 struct device *hwmon_dev;
210 struct mutex update_lock; 227 struct mutex update_lock;
228 struct mutex watchdog_lock;
229 struct list_head list; /* member of the watchdog_data_list */
230 struct kref kref;
231 struct miscdevice watchdog_miscdev;
211 int kind; 232 int kind;
233 unsigned long watchdog_is_open;
234 char watchdog_expect_close;
235 char watchdog_name[10]; /* must be unique to avoid sysfs conflict */
212 char valid; /* zero until following fields are valid */ 236 char valid; /* zero until following fields are valid */
213 unsigned long last_updated; /* in jiffies */ 237 unsigned long last_updated; /* in jiffies */
214 238
215 /* register values */ 239 /* register values */
240 u8 revision; /* chip revision */
216 u8 global_control; /* global control register */ 241 u8 global_control; /* global control register */
242 u8 watchdog_control; /* watchdog control register */
243 u8 watchdog_state; /* watchdog status register */
244 u8 watchdog_preset; /* watchdog counter preset on trigger val */
217 u8 volt[3]; /* 12, 5, battery voltage */ 245 u8 volt[3]; /* 12, 5, battery voltage */
218 u8 temp_act[5]; /* temperature */ 246 u8 temp_act[5]; /* temperature */
219 u8 temp_status[5]; /* status of sensor */ 247 u8 temp_status[5]; /* status of sensor */
@@ -225,11 +253,28 @@ struct fschmd_data {
225}; 253};
226 254
227/* Global variables to hold information read from special DMI tables, which are 255/* Global variables to hold information read from special DMI tables, which are
228 available on FSC machines with an fscher or later chip. */ 256 available on FSC machines with an fscher or later chip. There is no need to
257 protect these with a lock as they are only modified from our attach function
258 which always gets called with the i2c-core lock held and never accessed
259 before the attach function is done with them. */
229static int dmi_mult[3] = { 490, 200, 100 }; 260static int dmi_mult[3] = { 490, 200, 100 };
230static int dmi_offset[3] = { 0, 0, 0 }; 261static int dmi_offset[3] = { 0, 0, 0 };
231static int dmi_vref = -1; 262static int dmi_vref = -1;
232 263
264/* Somewhat ugly :( global data pointer list with all fschmd devices, so that
265 we can find our device data as when using misc_register there is no other
266 method to get to ones device data from the open fop. */
267static LIST_HEAD(watchdog_data_list);
268/* Note this lock not only protect list access, but also data.kref access */
269static DEFINE_MUTEX(watchdog_data_mutex);
270
271/* Release our data struct when we're detached from the i2c client *and* all
272 references to our watchdog device are released */
273static void fschmd_release_resources(struct kref *ref)
274{
275 struct fschmd_data *data = container_of(ref, struct fschmd_data, kref);
276 kfree(data);
277}
233 278
234/* 279/*
235 * Sysfs attr show / store functions 280 * Sysfs attr show / store functions
@@ -548,7 +593,265 @@ static struct sensor_device_attribute fschmd_fan_attr[] = {
548 593
549 594
550/* 595/*
551 * Real code 596 * Watchdog routines
597 */
598
599static int watchdog_set_timeout(struct fschmd_data *data, int timeout)
600{
601 int ret, resolution;
602 int kind = data->kind + 1; /* 0-x array index -> 1-x module param */
603
604 /* 2 second or 60 second resolution? */
605 if (timeout <= 510 || kind == fscpos || kind == fscscy)
606 resolution = 2;
607 else
608 resolution = 60;
609
610 if (timeout < resolution || timeout > (resolution * 255))
611 return -EINVAL;
612
613 mutex_lock(&data->watchdog_lock);
614 if (!data->client) {
615 ret = -ENODEV;
616 goto leave;
617 }
618
619 if (resolution == 2)
620 data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_RESOLUTION;
621 else
622 data->watchdog_control |= FSCHMD_WDOG_CONTROL_RESOLUTION;
623
624 data->watchdog_preset = DIV_ROUND_UP(timeout, resolution);
625
626 /* Write new timeout value */
627 i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_PRESET,
628 data->watchdog_preset);
629 /* Write new control register, do not trigger! */
630 i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL,
631 data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER);
632
633 ret = data->watchdog_preset * resolution;
634
635leave:
636 mutex_unlock(&data->watchdog_lock);
637 return ret;
638}
639
640static int watchdog_get_timeout(struct fschmd_data *data)
641{
642 int timeout;
643
644 mutex_lock(&data->watchdog_lock);
645 if (data->watchdog_control & FSCHMD_WDOG_CONTROL_RESOLUTION)
646 timeout = data->watchdog_preset * 60;
647 else
648 timeout = data->watchdog_preset * 2;
649 mutex_unlock(&data->watchdog_lock);
650
651 return timeout;
652}
653
654static int watchdog_trigger(struct fschmd_data *data)
655{
656 int ret = 0;
657
658 mutex_lock(&data->watchdog_lock);
659 if (!data->client) {
660 ret = -ENODEV;
661 goto leave;
662 }
663
664 data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER;
665 i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL,
666 data->watchdog_control);
667leave:
668 mutex_unlock(&data->watchdog_lock);
669 return ret;
670}
671
672static int watchdog_stop(struct fschmd_data *data)
673{
674 int ret = 0;
675
676 mutex_lock(&data->watchdog_lock);
677 if (!data->client) {
678 ret = -ENODEV;
679 goto leave;
680 }
681
682 data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED;
683 /* Don't store the stop flag in our watchdog control register copy, as
684 its a write only bit (read always returns 0) */
685 i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL,
686 data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP);
687leave:
688 mutex_unlock(&data->watchdog_lock);
689 return ret;
690}
691
692static int watchdog_open(struct inode *inode, struct file *filp)
693{
694 struct fschmd_data *pos, *data = NULL;
695
696 /* We get called from drivers/char/misc.c with misc_mtx hold, and we
697 call misc_register() from fschmd_probe() with watchdog_data_mutex
698 hold, as misc_register() takes the misc_mtx lock, this is a possible
699 deadlock, so we use mutex_trylock here. */
700 if (!mutex_trylock(&watchdog_data_mutex))
701 return -ERESTARTSYS;
702 list_for_each_entry(pos, &watchdog_data_list, list) {
703 if (pos->watchdog_miscdev.minor == iminor(inode)) {
704 data = pos;
705 break;
706 }
707 }
708 /* Note we can never not have found data, so we don't check for this */
709 kref_get(&data->kref);
710 mutex_unlock(&watchdog_data_mutex);
711
712 if (test_and_set_bit(0, &data->watchdog_is_open))
713 return -EBUSY;
714
715 /* Start the watchdog */
716 watchdog_trigger(data);
717 filp->private_data = data;
718
719 return nonseekable_open(inode, filp);
720}
721
722static int watchdog_release(struct inode *inode, struct file *filp)
723{
724 struct fschmd_data *data = filp->private_data;
725
726 if (data->watchdog_expect_close) {
727 watchdog_stop(data);
728 data->watchdog_expect_close = 0;
729 } else {
730 watchdog_trigger(data);
731 dev_crit(&data->client->dev,
732 "unexpected close, not stopping watchdog!\n");
733 }
734
735 clear_bit(0, &data->watchdog_is_open);
736
737 mutex_lock(&watchdog_data_mutex);
738 kref_put(&data->kref, fschmd_release_resources);
739 mutex_unlock(&watchdog_data_mutex);
740
741 return 0;
742}
743
744static ssize_t watchdog_write(struct file *filp, const char __user *buf,
745 size_t count, loff_t *offset)
746{
747 size_t ret;
748 struct fschmd_data *data = filp->private_data;
749
750 if (count) {
751 if (!nowayout) {
752 size_t i;
753
754 /* Clear it in case it was set with a previous write */
755 data->watchdog_expect_close = 0;
756
757 for (i = 0; i != count; i++) {
758 char c;
759 if (get_user(c, buf + i))
760 return -EFAULT;
761 if (c == 'V')
762 data->watchdog_expect_close = 1;
763 }
764 }
765 ret = watchdog_trigger(data);
766 if (ret < 0)
767 return ret;
768 }
769 return count;
770}
771
772static int watchdog_ioctl(struct inode *inode, struct file *filp,
773 unsigned int cmd, unsigned long arg)
774{
775 static struct watchdog_info ident = {
776 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
777 WDIOF_CARDRESET,
778 .identity = "FSC watchdog"
779 };
780 int i, ret = 0;
781 struct fschmd_data *data = filp->private_data;
782
783 switch (cmd) {
784 case WDIOC_GETSUPPORT:
785 ident.firmware_version = data->revision;
786 if (!nowayout)
787 ident.options |= WDIOF_MAGICCLOSE;
788 if (copy_to_user((void __user *)arg, &ident, sizeof(ident)))
789 ret = -EFAULT;
790 break;
791
792 case WDIOC_GETSTATUS:
793 ret = put_user(0, (int __user *)arg);
794 break;
795
796 case WDIOC_GETBOOTSTATUS:
797 if (data->watchdog_state & FSCHMD_WDOG_STATE_CARDRESET)
798 ret = put_user(WDIOF_CARDRESET, (int __user *)arg);
799 else
800 ret = put_user(0, (int __user *)arg);
801 break;
802
803 case WDIOC_KEEPALIVE:
804 ret = watchdog_trigger(data);
805 break;
806
807 case WDIOC_GETTIMEOUT:
808 i = watchdog_get_timeout(data);
809 ret = put_user(i, (int __user *)arg);
810 break;
811
812 case WDIOC_SETTIMEOUT:
813 if (get_user(i, (int __user *)arg)) {
814 ret = -EFAULT;
815 break;
816 }
817 ret = watchdog_set_timeout(data, i);
818 if (ret > 0)
819 ret = put_user(ret, (int __user *)arg);
820 break;
821
822 case WDIOC_SETOPTIONS:
823 if (get_user(i, (int __user *)arg)) {
824 ret = -EFAULT;
825 break;
826 }
827
828 if (i & WDIOS_DISABLECARD)
829 ret = watchdog_stop(data);
830 else if (i & WDIOS_ENABLECARD)
831 ret = watchdog_trigger(data);
832 else
833 ret = -EINVAL;
834
835 break;
836 default:
837 ret = -ENOTTY;
838 }
839
840 return ret;
841}
842
843static struct file_operations watchdog_fops = {
844 .owner = THIS_MODULE,
845 .llseek = no_llseek,
846 .open = watchdog_open,
847 .release = watchdog_release,
848 .write = watchdog_write,
849 .ioctl = watchdog_ioctl,
850};
851
852
853/*
854 * Detect, register, unregister and update device functions
552 */ 855 */
553 856
554/* DMI decode routine to read voltage scaling factors from special DMI tables, 857/* DMI decode routine to read voltage scaling factors from special DMI tables,
@@ -658,9 +961,9 @@ static int fschmd_probe(struct i2c_client *client,
658 const struct i2c_device_id *id) 961 const struct i2c_device_id *id)
659{ 962{
660 struct fschmd_data *data; 963 struct fschmd_data *data;
661 u8 revision;
662 const char * const names[5] = { "Poseidon", "Hermes", "Scylla", 964 const char * const names[5] = { "Poseidon", "Hermes", "Scylla",
663 "Heracles", "Heimdall" }; 965 "Heracles", "Heimdall" };
966 const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
664 int i, err; 967 int i, err;
665 enum chips kind = id->driver_data; 968 enum chips kind = id->driver_data;
666 969
@@ -670,6 +973,13 @@ static int fschmd_probe(struct i2c_client *client,
670 973
671 i2c_set_clientdata(client, data); 974 i2c_set_clientdata(client, data);
672 mutex_init(&data->update_lock); 975 mutex_init(&data->update_lock);
976 mutex_init(&data->watchdog_lock);
977 INIT_LIST_HEAD(&data->list);
978 kref_init(&data->kref);
979 /* Store client pointer in our data struct for watchdog usage
980 (where the client is found through a data ptr instead of the
981 otherway around) */
982 data->client = client;
673 983
674 if (kind == fscpos) { 984 if (kind == fscpos) {
675 /* The Poseidon has hardwired temp limits, fill these 985 /* The Poseidon has hardwired temp limits, fill these
@@ -690,6 +1000,17 @@ static int fschmd_probe(struct i2c_client *client,
690 } 1000 }
691 } 1001 }
692 1002
1003 /* Read in some never changing registers */
1004 data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
1005 data->global_control = i2c_smbus_read_byte_data(client,
1006 FSCHMD_REG_CONTROL);
1007 data->watchdog_control = i2c_smbus_read_byte_data(client,
1008 FSCHMD_REG_WDOG_CONTROL);
1009 data->watchdog_state = i2c_smbus_read_byte_data(client,
1010 FSCHMD_REG_WDOG_STATE);
1011 data->watchdog_preset = i2c_smbus_read_byte_data(client,
1012 FSCHMD_REG_WDOG_PRESET);
1013
693 /* i2c kind goes from 1-5, we want from 0-4 to address arrays */ 1014 /* i2c kind goes from 1-5, we want from 0-4 to address arrays */
694 data->kind = kind - 1; 1015 data->kind = kind - 1;
695 1016
@@ -732,9 +1053,43 @@ static int fschmd_probe(struct i2c_client *client,
732 goto exit_detach; 1053 goto exit_detach;
733 } 1054 }
734 1055
735 revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); 1056 /* We take the data_mutex lock early so that watchdog_open() cannot
1057 run when misc_register() has completed, but we've not yet added
1058 our data to the watchdog_data_list (and set the default timeout) */
1059 mutex_lock(&watchdog_data_mutex);
1060 for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) {
1061 /* Register our watchdog part */
1062 snprintf(data->watchdog_name, sizeof(data->watchdog_name),
1063 "watchdog%c", (i == 0) ? '\0' : ('0' + i));
1064 data->watchdog_miscdev.name = data->watchdog_name;
1065 data->watchdog_miscdev.fops = &watchdog_fops;
1066 data->watchdog_miscdev.minor = watchdog_minors[i];
1067 err = misc_register(&data->watchdog_miscdev);
1068 if (err == -EBUSY)
1069 continue;
1070 if (err) {
1071 data->watchdog_miscdev.minor = 0;
1072 dev_err(&client->dev,
1073 "Registering watchdog chardev: %d\n", err);
1074 break;
1075 }
1076
1077 list_add(&data->list, &watchdog_data_list);
1078 watchdog_set_timeout(data, 60);
1079 dev_info(&client->dev,
1080 "Registered watchdog chardev major 10, minor: %d\n",
1081 watchdog_minors[i]);
1082 break;
1083 }
1084 if (i == ARRAY_SIZE(watchdog_minors)) {
1085 data->watchdog_miscdev.minor = 0;
1086 dev_warn(&client->dev, "Couldn't register watchdog chardev "
1087 "(due to no free minor)\n");
1088 }
1089 mutex_unlock(&watchdog_data_mutex);
1090
736 dev_info(&client->dev, "Detected FSC %s chip, revision: %d\n", 1091 dev_info(&client->dev, "Detected FSC %s chip, revision: %d\n",
737 names[data->kind], (int) revision); 1092 names[data->kind], (int) data->revision);
738 1093
739 return 0; 1094 return 0;
740 1095
@@ -748,6 +1103,24 @@ static int fschmd_remove(struct i2c_client *client)
748 struct fschmd_data *data = i2c_get_clientdata(client); 1103 struct fschmd_data *data = i2c_get_clientdata(client);
749 int i; 1104 int i;
750 1105
1106 /* Unregister the watchdog (if registered) */
1107 if (data->watchdog_miscdev.minor) {
1108 misc_deregister(&data->watchdog_miscdev);
1109 if (data->watchdog_is_open) {
1110 dev_warn(&client->dev,
1111 "i2c client detached with watchdog open! "
1112 "Stopping watchdog.\n");
1113 watchdog_stop(data);
1114 }
1115 mutex_lock(&watchdog_data_mutex);
1116 list_del(&data->list);
1117 mutex_unlock(&watchdog_data_mutex);
1118 /* Tell the watchdog code the client is gone */
1119 mutex_lock(&data->watchdog_lock);
1120 data->client = NULL;
1121 mutex_unlock(&data->watchdog_lock);
1122 }
1123
751 /* Check if registered in case we're called from fschmd_detect 1124 /* Check if registered in case we're called from fschmd_detect
752 to cleanup after an error */ 1125 to cleanup after an error */
753 if (data->hwmon_dev) 1126 if (data->hwmon_dev)
@@ -762,7 +1135,10 @@ static int fschmd_remove(struct i2c_client *client)
762 device_remove_file(&client->dev, 1135 device_remove_file(&client->dev,
763 &fschmd_fan_attr[i].dev_attr); 1136 &fschmd_fan_attr[i].dev_attr);
764 1137
765 kfree(data); 1138 mutex_lock(&watchdog_data_mutex);
1139 kref_put(&data->kref, fschmd_release_resources);
1140 mutex_unlock(&watchdog_data_mutex);
1141
766 return 0; 1142 return 0;
767} 1143}
768 1144
@@ -824,17 +1200,6 @@ static struct fschmd_data *fschmd_update_device(struct device *dev)
824 data->volt[i] = i2c_smbus_read_byte_data(client, 1200 data->volt[i] = i2c_smbus_read_byte_data(client,
825 FSCHMD_REG_VOLT[i]); 1201 FSCHMD_REG_VOLT[i]);
826 1202
827 data->global_control = i2c_smbus_read_byte_data(client,
828 FSCHMD_REG_CONTROL);
829
830 /* To be implemented in the future
831 data->watchdog[0] = i2c_smbus_read_byte_data(client,
832 FSCHMD_REG_WDOG_PRESET);
833 data->watchdog[1] = i2c_smbus_read_byte_data(client,
834 FSCHMD_REG_WDOG_STATE);
835 data->watchdog[2] = i2c_smbus_read_byte_data(client,
836 FSCHMD_REG_WDOG_CONTROL); */
837
838 data->last_updated = jiffies; 1203 data->last_updated = jiffies;
839 data->valid = 1; 1204 data->valid = 1;
840 } 1205 }