aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/hwmon/Kconfig5
-rw-r--r--drivers/hwmon/fschmd.c401
2 files changed, 386 insertions, 20 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index aba01b4ceca4..91975148f4b9 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -329,10 +329,11 @@ config SENSORS_FSCHMD
329 depends on X86 && I2C && EXPERIMENTAL 329 depends on X86 && I2C && EXPERIMENTAL
330 help 330 help
331 If you say yes here you get support for various Fujitsu Siemens 331 If you say yes here you get support for various Fujitsu Siemens
332 Computers sensor chips. 332 Computers sensor chips, including support for the integrated
333 watchdog.
333 334
334 This is a new merged driver for FSC sensor chips which is intended 335 This is a new merged driver for FSC sensor chips which is intended
335 as a replacment for the fscpos, fscscy and fscher drivers and adds 336 as a replacement for the fscpos, fscscy and fscher drivers and adds
336 support for several other FCS sensor chips. 337 support for several other FCS sensor chips.
337 338
338 This driver can also be built as a module. If so, the module 339 This driver can also be built as a module. If so, the module
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 }