diff options
author | Hans de Goede <hdegoede@redhat.com> | 2009-01-07 10:37:33 -0500 |
---|---|---|
committer | Jean Delvare <khali@linux-fr.org> | 2009-01-07 10:37:33 -0500 |
commit | 97950c3d423e474ef887749b238ee67731b532fe (patch) | |
tree | 0ce2a267c13d06f1d6514c1f67a8418dc82fa753 /drivers/hwmon/fschmd.c | |
parent | 453e308d773979f6bbdf4109df27101072f6524b (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.c | 401 |
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 */ |
47 | static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; | 52 | static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; |
48 | 53 | ||
49 | /* Insmod parameters */ | 54 | /* Insmod parameters */ |
55 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
56 | module_param(nowayout, int, 0); | ||
57 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
58 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
50 | I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); | 59 | I2C_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 */ |
74 | static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 }; | 90 | static 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 | ||
208 | struct fschmd_data { | 224 | struct 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. */ | ||
229 | static int dmi_mult[3] = { 490, 200, 100 }; | 260 | static int dmi_mult[3] = { 490, 200, 100 }; |
230 | static int dmi_offset[3] = { 0, 0, 0 }; | 261 | static int dmi_offset[3] = { 0, 0, 0 }; |
231 | static int dmi_vref = -1; | 262 | static 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. */ | ||
267 | static LIST_HEAD(watchdog_data_list); | ||
268 | /* Note this lock not only protect list access, but also data.kref access */ | ||
269 | static 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 */ | ||
273 | static 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 | |||
599 | static 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 | |||
635 | leave: | ||
636 | mutex_unlock(&data->watchdog_lock); | ||
637 | return ret; | ||
638 | } | ||
639 | |||
640 | static 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 | |||
654 | static 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); | ||
667 | leave: | ||
668 | mutex_unlock(&data->watchdog_lock); | ||
669 | return ret; | ||
670 | } | ||
671 | |||
672 | static 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); | ||
687 | leave: | ||
688 | mutex_unlock(&data->watchdog_lock); | ||
689 | return ret; | ||
690 | } | ||
691 | |||
692 | static 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 | |||
722 | static 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 | |||
744 | static 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 | |||
772 | static 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 | |||
843 | static 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 | } |