diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-25 17:10:34 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-25 17:10:34 -0400 |
commit | 750e06992d49666a7589aac555eb3bb68e4dbb88 (patch) | |
tree | 7ee3d85adf256491dae89e5b049bb28ef1a1680c /drivers/hwmon | |
parent | d3ec4844d449cf7af9e749f73ba2052fb7b72fc2 (diff) | |
parent | 156e2d1adc03e17f18f98d297f7757fc6d93a589 (diff) |
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging:
hwmon: (lm78) Become the maintainer
hwmon: (lm78) Make ISA interface depend on CONFIG_ISA
hwmon: (lm78) Avoid forward declarations
hwmon: (sht15) Correct a comment mistake
hwmon: (max1111) Avoid extra memory allocations
hwmon: (it87) Add chassis intrusion detection support
hwmon: (via-cputemp) Add VID reporting support
hwmon-vid: Add support for VIA family 6 model D CPU
hwmon: New driver sch5636
hwmon: (sch5627) Factor out some code shared with sch5636 driver
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 21 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 2 | ||||
-rw-r--r-- | drivers/hwmon/hwmon-vid.c | 42 | ||||
-rw-r--r-- | drivers/hwmon/it87.c | 29 | ||||
-rw-r--r-- | drivers/hwmon/lm78.c | 305 | ||||
-rw-r--r-- | drivers/hwmon/max1111.c | 27 | ||||
-rw-r--r-- | drivers/hwmon/sch5627.c | 334 | ||||
-rw-r--r-- | drivers/hwmon/sch5636.c | 539 | ||||
-rw-r--r-- | drivers/hwmon/sch56xx-common.c | 340 | ||||
-rw-r--r-- | drivers/hwmon/sch56xx-common.h | 24 | ||||
-rw-r--r-- | drivers/hwmon/sht15.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/via-cputemp.c | 44 |
12 files changed, 1243 insertions, 466 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5f888f7e7dcb..0598cd22edf2 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -1041,8 +1041,13 @@ config SENSORS_SMSC47B397 | |||
1041 | This driver can also be built as a module. If so, the module | 1041 | This driver can also be built as a module. If so, the module |
1042 | will be called smsc47b397. | 1042 | will be called smsc47b397. |
1043 | 1043 | ||
1044 | config SENSORS_SCH56XX_COMMON | ||
1045 | tristate | ||
1046 | default n | ||
1047 | |||
1044 | config SENSORS_SCH5627 | 1048 | config SENSORS_SCH5627 |
1045 | tristate "SMSC SCH5627" | 1049 | tristate "SMSC SCH5627" |
1050 | select SENSORS_SCH56XX_COMMON | ||
1046 | help | 1051 | help |
1047 | If you say yes here you get support for the hardware monitoring | 1052 | If you say yes here you get support for the hardware monitoring |
1048 | features of the SMSC SCH5627 Super-I/O chip. | 1053 | features of the SMSC SCH5627 Super-I/O chip. |
@@ -1050,6 +1055,21 @@ config SENSORS_SCH5627 | |||
1050 | This driver can also be built as a module. If so, the module | 1055 | This driver can also be built as a module. If so, the module |
1051 | will be called sch5627. | 1056 | will be called sch5627. |
1052 | 1057 | ||
1058 | config SENSORS_SCH5636 | ||
1059 | tristate "SMSC SCH5636" | ||
1060 | select SENSORS_SCH56XX_COMMON | ||
1061 | help | ||
1062 | SMSC SCH5636 Super I/O chips include an embedded microcontroller for | ||
1063 | hardware monitoring solutions, allowing motherboard manufacturers to | ||
1064 | create their own custom hwmon solution based upon the SCH5636. | ||
1065 | |||
1066 | Currently this driver only supports the Fujitsu Theseus SCH5636 based | ||
1067 | hwmon solution. Say yes here if you want support for the Fujitsu | ||
1068 | Theseus' hardware monitoring features. | ||
1069 | |||
1070 | This driver can also be built as a module. If so, the module | ||
1071 | will be called sch5636. | ||
1072 | |||
1053 | config SENSORS_ADS1015 | 1073 | config SENSORS_ADS1015 |
1054 | tristate "Texas Instruments ADS1015" | 1074 | tristate "Texas Instruments ADS1015" |
1055 | depends on I2C | 1075 | depends on I2C |
@@ -1142,6 +1162,7 @@ config SENSORS_TWL4030_MADC | |||
1142 | config SENSORS_VIA_CPUTEMP | 1162 | config SENSORS_VIA_CPUTEMP |
1143 | tristate "VIA CPU temperature sensor" | 1163 | tristate "VIA CPU temperature sensor" |
1144 | depends on X86 | 1164 | depends on X86 |
1165 | select HWMON_VID | ||
1145 | help | 1166 | help |
1146 | If you say yes here you get support for the temperature | 1167 | If you say yes here you get support for the temperature |
1147 | sensor inside your CPU. Supported are all known variants of | 1168 | sensor inside your CPU. Supported are all known variants of |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 28061cfa0cdb..d7995a1d0784 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -95,7 +95,9 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o | |||
95 | obj-$(CONFIG_SENSORS_PC87427) += pc87427.o | 95 | obj-$(CONFIG_SENSORS_PC87427) += pc87427.o |
96 | obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o | 96 | obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o |
97 | obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o | 97 | obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o |
98 | obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o | ||
98 | obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o | 99 | obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o |
100 | obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o | ||
99 | obj-$(CONFIG_SENSORS_SHT15) += sht15.o | 101 | obj-$(CONFIG_SENSORS_SHT15) += sht15.o |
100 | obj-$(CONFIG_SENSORS_SHT21) += sht21.o | 102 | obj-$(CONFIG_SENSORS_SHT21) += sht21.o |
101 | obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o | 103 | obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o |
diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index c8195a077da3..932da8a5aaf4 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c | |||
@@ -140,7 +140,11 @@ int vid_from_reg(int val, u8 vrm) | |||
140 | return(val & 0x10 ? 975 - (val & 0xF) * 25 : | 140 | return(val & 0x10 ? 975 - (val & 0xF) * 25 : |
141 | 1750 - val * 50); | 141 | 1750 - val * 50); |
142 | case 13: | 142 | case 13: |
143 | case 131: | ||
143 | val &= 0x3f; | 144 | val &= 0x3f; |
145 | /* Exception for Eden ULV 500 MHz */ | ||
146 | if (vrm == 131 && val == 0x3f) | ||
147 | val++; | ||
144 | return(1708 - val * 16); | 148 | return(1708 - val * 16); |
145 | case 14: /* Intel Core */ | 149 | case 14: /* Intel Core */ |
146 | /* compute in uV, round to mV */ | 150 | /* compute in uV, round to mV */ |
@@ -205,11 +209,45 @@ static struct vrm_model vrm_models[] = { | |||
205 | {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85}, /* Nehemiah */ | 209 | {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85}, /* Nehemiah */ |
206 | {X86_VENDOR_CENTAUR, 0x6, 0x9, ANY, 17}, /* C3-M, Eden-N */ | 210 | {X86_VENDOR_CENTAUR, 0x6, 0x9, ANY, 17}, /* C3-M, Eden-N */ |
207 | {X86_VENDOR_CENTAUR, 0x6, 0xA, 0x7, 0}, /* No information */ | 211 | {X86_VENDOR_CENTAUR, 0x6, 0xA, 0x7, 0}, /* No information */ |
208 | {X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13}, /* C7, Esther */ | 212 | {X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13}, /* C7-M, C7, Eden (Esther) */ |
213 | {X86_VENDOR_CENTAUR, 0x6, 0xD, ANY, 134}, /* C7-D, C7-M, C7, Eden (Esther) */ | ||
209 | 214 | ||
210 | {X86_VENDOR_UNKNOWN, ANY, ANY, ANY, 0} /* stop here */ | 215 | {X86_VENDOR_UNKNOWN, ANY, ANY, ANY, 0} /* stop here */ |
211 | }; | 216 | }; |
212 | 217 | ||
218 | /* | ||
219 | * Special case for VIA model D: there are two different possible | ||
220 | * VID tables, so we have to figure out first, which one must be | ||
221 | * used. This resolves temporary drm value 134 to 14 (Intel Core | ||
222 | * 7-bit VID), 13 (Pentium M 6-bit VID) or 131 (Pentium M 6-bit VID | ||
223 | * + quirk for Eden ULV 500 MHz). | ||
224 | * Note: something similar might be needed for model A, I'm not sure. | ||
225 | */ | ||
226 | static u8 get_via_model_d_vrm(void) | ||
227 | { | ||
228 | unsigned int vid, brand, dummy; | ||
229 | static const char *brands[4] = { | ||
230 | "C7-M", "C7", "Eden", "C7-D" | ||
231 | }; | ||
232 | |||
233 | rdmsr(0x198, dummy, vid); | ||
234 | vid &= 0xff; | ||
235 | |||
236 | rdmsr(0x1154, brand, dummy); | ||
237 | brand = ((brand >> 4) ^ (brand >> 2)) & 0x03; | ||
238 | |||
239 | if (vid > 0x3f) { | ||
240 | pr_info("Using %d-bit VID table for VIA %s CPU\n", | ||
241 | 7, brands[brand]); | ||
242 | return 14; | ||
243 | } else { | ||
244 | pr_info("Using %d-bit VID table for VIA %s CPU\n", | ||
245 | 6, brands[brand]); | ||
246 | /* Enable quirk for Eden */ | ||
247 | return brand == 2 ? 131 : 13; | ||
248 | } | ||
249 | } | ||
250 | |||
213 | static u8 find_vrm(u8 eff_family, u8 eff_model, u8 eff_stepping, u8 vendor) | 251 | static u8 find_vrm(u8 eff_family, u8 eff_model, u8 eff_stepping, u8 vendor) |
214 | { | 252 | { |
215 | int i = 0; | 253 | int i = 0; |
@@ -247,6 +285,8 @@ u8 vid_which_vrm(void) | |||
247 | eff_model += ((eax & 0x000F0000)>>16)<<4; | 285 | eff_model += ((eax & 0x000F0000)>>16)<<4; |
248 | } | 286 | } |
249 | vrm_ret = find_vrm(eff_family, eff_model, eff_stepping, c->x86_vendor); | 287 | vrm_ret = find_vrm(eff_family, eff_model, eff_stepping, c->x86_vendor); |
288 | if (vrm_ret == 134) | ||
289 | vrm_ret = get_via_model_d_vrm(); | ||
250 | if (vrm_ret == 0) | 290 | if (vrm_ret == 0) |
251 | pr_info("Unknown VRM version of your x86 CPU\n"); | 291 | pr_info("Unknown VRM version of your x86 CPU\n"); |
252 | return vrm_ret; | 292 | return vrm_ret; |
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 5f5247750430..d912649fac50 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c | |||
@@ -1172,6 +1172,32 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, | |||
1172 | struct it87_data *data = it87_update_device(dev); | 1172 | struct it87_data *data = it87_update_device(dev); |
1173 | return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); | 1173 | return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); |
1174 | } | 1174 | } |
1175 | |||
1176 | static ssize_t clear_intrusion(struct device *dev, struct device_attribute | ||
1177 | *attr, const char *buf, size_t count) | ||
1178 | { | ||
1179 | struct it87_data *data = dev_get_drvdata(dev); | ||
1180 | long val; | ||
1181 | int config; | ||
1182 | |||
1183 | if (strict_strtol(buf, 10, &val) < 0 || val != 0) | ||
1184 | return -EINVAL; | ||
1185 | |||
1186 | mutex_lock(&data->update_lock); | ||
1187 | config = it87_read_value(data, IT87_REG_CONFIG); | ||
1188 | if (config < 0) { | ||
1189 | count = config; | ||
1190 | } else { | ||
1191 | config |= 1 << 5; | ||
1192 | it87_write_value(data, IT87_REG_CONFIG, config); | ||
1193 | /* Invalidate cache to force re-read */ | ||
1194 | data->valid = 0; | ||
1195 | } | ||
1196 | mutex_unlock(&data->update_lock); | ||
1197 | |||
1198 | return count; | ||
1199 | } | ||
1200 | |||
1175 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 8); | 1201 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 8); |
1176 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 9); | 1202 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 9); |
1177 | static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 10); | 1203 | static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 10); |
@@ -1188,6 +1214,8 @@ static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6); | |||
1188 | static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16); | 1214 | static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16); |
1189 | static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17); | 1215 | static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17); |
1190 | static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18); | 1216 | static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18); |
1217 | static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR, | ||
1218 | show_alarm, clear_intrusion, 4); | ||
1191 | 1219 | ||
1192 | static ssize_t show_beep(struct device *dev, struct device_attribute *attr, | 1220 | static ssize_t show_beep(struct device *dev, struct device_attribute *attr, |
1193 | char *buf) | 1221 | char *buf) |
@@ -1350,6 +1378,7 @@ static struct attribute *it87_attributes[] = { | |||
1350 | &sensor_dev_attr_temp3_alarm.dev_attr.attr, | 1378 | &sensor_dev_attr_temp3_alarm.dev_attr.attr, |
1351 | 1379 | ||
1352 | &dev_attr_alarms.attr, | 1380 | &dev_attr_alarms.attr, |
1381 | &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, | ||
1353 | &dev_attr_name.attr, | 1382 | &dev_attr_name.attr, |
1354 | NULL | 1383 | NULL |
1355 | }; | 1384 | }; |
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 4cb24eafe318..6df0b4681710 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c | |||
@@ -2,7 +2,7 @@ | |||
2 | lm78.c - Part of lm_sensors, Linux kernel modules for hardware | 2 | lm78.c - Part of lm_sensors, Linux kernel modules for hardware |
3 | monitoring | 3 | monitoring |
4 | Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> | 4 | Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> |
5 | Copyright (c) 2007 Jean Delvare <khali@linux-fr.org> | 5 | Copyright (c) 2007, 2011 Jean Delvare <khali@linux-fr.org> |
6 | 6 | ||
7 | This program is free software; you can redistribute it and/or modify | 7 | This program is free software; you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by | 8 | it under the terms of the GNU General Public License as published by |
@@ -26,23 +26,21 @@ | |||
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/jiffies.h> | 27 | #include <linux/jiffies.h> |
28 | #include <linux/i2c.h> | 28 | #include <linux/i2c.h> |
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/ioport.h> | ||
31 | #include <linux/hwmon.h> | 29 | #include <linux/hwmon.h> |
32 | #include <linux/hwmon-vid.h> | 30 | #include <linux/hwmon-vid.h> |
33 | #include <linux/hwmon-sysfs.h> | 31 | #include <linux/hwmon-sysfs.h> |
34 | #include <linux/err.h> | 32 | #include <linux/err.h> |
35 | #include <linux/mutex.h> | 33 | #include <linux/mutex.h> |
36 | #include <linux/io.h> | ||
37 | 34 | ||
38 | /* ISA device, if found */ | 35 | #ifdef CONFIG_ISA |
39 | static struct platform_device *pdev; | 36 | #include <linux/platform_device.h> |
37 | #include <linux/ioport.h> | ||
38 | #include <linux/io.h> | ||
39 | #endif | ||
40 | 40 | ||
41 | /* Addresses to scan */ | 41 | /* Addresses to scan */ |
42 | static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, | 42 | static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, |
43 | 0x2e, 0x2f, I2C_CLIENT_END }; | 43 | 0x2e, 0x2f, I2C_CLIENT_END }; |
44 | static unsigned short isa_address = 0x290; | ||
45 | |||
46 | enum chips { lm78, lm79 }; | 44 | enum chips { lm78, lm79 }; |
47 | 45 | ||
48 | /* Many LM78 constants specified below */ | 46 | /* Many LM78 constants specified below */ |
@@ -143,50 +141,12 @@ struct lm78_data { | |||
143 | }; | 141 | }; |
144 | 142 | ||
145 | 143 | ||
146 | static int lm78_i2c_detect(struct i2c_client *client, | ||
147 | struct i2c_board_info *info); | ||
148 | static int lm78_i2c_probe(struct i2c_client *client, | ||
149 | const struct i2c_device_id *id); | ||
150 | static int lm78_i2c_remove(struct i2c_client *client); | ||
151 | |||
152 | static int __devinit lm78_isa_probe(struct platform_device *pdev); | ||
153 | static int __devexit lm78_isa_remove(struct platform_device *pdev); | ||
154 | |||
155 | static int lm78_read_value(struct lm78_data *data, u8 reg); | 144 | static int lm78_read_value(struct lm78_data *data, u8 reg); |
156 | static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value); | 145 | static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value); |
157 | static struct lm78_data *lm78_update_device(struct device *dev); | 146 | static struct lm78_data *lm78_update_device(struct device *dev); |
158 | static void lm78_init_device(struct lm78_data *data); | 147 | static void lm78_init_device(struct lm78_data *data); |
159 | 148 | ||
160 | 149 | ||
161 | static const struct i2c_device_id lm78_i2c_id[] = { | ||
162 | { "lm78", lm78 }, | ||
163 | { "lm79", lm79 }, | ||
164 | { } | ||
165 | }; | ||
166 | MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); | ||
167 | |||
168 | static struct i2c_driver lm78_driver = { | ||
169 | .class = I2C_CLASS_HWMON, | ||
170 | .driver = { | ||
171 | .name = "lm78", | ||
172 | }, | ||
173 | .probe = lm78_i2c_probe, | ||
174 | .remove = lm78_i2c_remove, | ||
175 | .id_table = lm78_i2c_id, | ||
176 | .detect = lm78_i2c_detect, | ||
177 | .address_list = normal_i2c, | ||
178 | }; | ||
179 | |||
180 | static struct platform_driver lm78_isa_driver = { | ||
181 | .driver = { | ||
182 | .owner = THIS_MODULE, | ||
183 | .name = "lm78", | ||
184 | }, | ||
185 | .probe = lm78_isa_probe, | ||
186 | .remove = __devexit_p(lm78_isa_remove), | ||
187 | }; | ||
188 | |||
189 | |||
190 | /* 7 Voltages */ | 150 | /* 7 Voltages */ |
191 | static ssize_t show_in(struct device *dev, struct device_attribute *da, | 151 | static ssize_t show_in(struct device *dev, struct device_attribute *da, |
192 | char *buf) | 152 | char *buf) |
@@ -514,6 +474,16 @@ static const struct attribute_group lm78_group = { | |||
514 | .attrs = lm78_attributes, | 474 | .attrs = lm78_attributes, |
515 | }; | 475 | }; |
516 | 476 | ||
477 | /* | ||
478 | * ISA related code | ||
479 | */ | ||
480 | #ifdef CONFIG_ISA | ||
481 | |||
482 | /* ISA device, if found */ | ||
483 | static struct platform_device *pdev; | ||
484 | |||
485 | static unsigned short isa_address = 0x290; | ||
486 | |||
517 | /* I2C devices get this name attribute automatically, but for ISA devices | 487 | /* I2C devices get this name attribute automatically, but for ISA devices |
518 | we must create it by ourselves. */ | 488 | we must create it by ourselves. */ |
519 | static ssize_t show_name(struct device *dev, struct device_attribute | 489 | static ssize_t show_name(struct device *dev, struct device_attribute |
@@ -525,6 +495,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute | |||
525 | } | 495 | } |
526 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); | 496 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); |
527 | 497 | ||
498 | static struct lm78_data *lm78_data_if_isa(void) | ||
499 | { | ||
500 | return pdev ? platform_get_drvdata(pdev) : NULL; | ||
501 | } | ||
502 | |||
528 | /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ | 503 | /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ |
529 | static int lm78_alias_detect(struct i2c_client *client, u8 chipid) | 504 | static int lm78_alias_detect(struct i2c_client *client, u8 chipid) |
530 | { | 505 | { |
@@ -558,12 +533,24 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) | |||
558 | 533 | ||
559 | return 1; | 534 | return 1; |
560 | } | 535 | } |
536 | #else /* !CONFIG_ISA */ | ||
537 | |||
538 | static int lm78_alias_detect(struct i2c_client *client, u8 chipid) | ||
539 | { | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static struct lm78_data *lm78_data_if_isa(void) | ||
544 | { | ||
545 | return NULL; | ||
546 | } | ||
547 | #endif /* CONFIG_ISA */ | ||
561 | 548 | ||
562 | static int lm78_i2c_detect(struct i2c_client *client, | 549 | static int lm78_i2c_detect(struct i2c_client *client, |
563 | struct i2c_board_info *info) | 550 | struct i2c_board_info *info) |
564 | { | 551 | { |
565 | int i; | 552 | int i; |
566 | struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL; | 553 | struct lm78_data *isa = lm78_data_if_isa(); |
567 | const char *client_name; | 554 | const char *client_name; |
568 | struct i2c_adapter *adapter = client->adapter; | 555 | struct i2c_adapter *adapter = client->adapter; |
569 | int address = client->addr; | 556 | int address = client->addr; |
@@ -663,76 +650,24 @@ static int lm78_i2c_remove(struct i2c_client *client) | |||
663 | return 0; | 650 | return 0; |
664 | } | 651 | } |
665 | 652 | ||
666 | static int __devinit lm78_isa_probe(struct platform_device *pdev) | 653 | static const struct i2c_device_id lm78_i2c_id[] = { |
667 | { | 654 | { "lm78", lm78 }, |
668 | int err; | 655 | { "lm79", lm79 }, |
669 | struct lm78_data *data; | 656 | { } |
670 | struct resource *res; | 657 | }; |
671 | 658 | MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); | |
672 | /* Reserve the ISA region */ | ||
673 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
674 | if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { | ||
675 | err = -EBUSY; | ||
676 | goto exit; | ||
677 | } | ||
678 | |||
679 | if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { | ||
680 | err = -ENOMEM; | ||
681 | goto exit_release_region; | ||
682 | } | ||
683 | mutex_init(&data->lock); | ||
684 | data->isa_addr = res->start; | ||
685 | platform_set_drvdata(pdev, data); | ||
686 | |||
687 | if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { | ||
688 | data->type = lm79; | ||
689 | data->name = "lm79"; | ||
690 | } else { | ||
691 | data->type = lm78; | ||
692 | data->name = "lm78"; | ||
693 | } | ||
694 | |||
695 | /* Initialize the LM78 chip */ | ||
696 | lm78_init_device(data); | ||
697 | |||
698 | /* Register sysfs hooks */ | ||
699 | if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group)) | ||
700 | || (err = device_create_file(&pdev->dev, &dev_attr_name))) | ||
701 | goto exit_remove_files; | ||
702 | |||
703 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | ||
704 | if (IS_ERR(data->hwmon_dev)) { | ||
705 | err = PTR_ERR(data->hwmon_dev); | ||
706 | goto exit_remove_files; | ||
707 | } | ||
708 | |||
709 | return 0; | ||
710 | |||
711 | exit_remove_files: | ||
712 | sysfs_remove_group(&pdev->dev.kobj, &lm78_group); | ||
713 | device_remove_file(&pdev->dev, &dev_attr_name); | ||
714 | kfree(data); | ||
715 | exit_release_region: | ||
716 | release_region(res->start + LM78_ADDR_REG_OFFSET, 2); | ||
717 | exit: | ||
718 | return err; | ||
719 | } | ||
720 | |||
721 | static int __devexit lm78_isa_remove(struct platform_device *pdev) | ||
722 | { | ||
723 | struct lm78_data *data = platform_get_drvdata(pdev); | ||
724 | struct resource *res; | ||
725 | |||
726 | hwmon_device_unregister(data->hwmon_dev); | ||
727 | sysfs_remove_group(&pdev->dev.kobj, &lm78_group); | ||
728 | device_remove_file(&pdev->dev, &dev_attr_name); | ||
729 | kfree(data); | ||
730 | |||
731 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
732 | release_region(res->start + LM78_ADDR_REG_OFFSET, 2); | ||
733 | 659 | ||
734 | return 0; | 660 | static struct i2c_driver lm78_driver = { |
735 | } | 661 | .class = I2C_CLASS_HWMON, |
662 | .driver = { | ||
663 | .name = "lm78", | ||
664 | }, | ||
665 | .probe = lm78_i2c_probe, | ||
666 | .remove = lm78_i2c_remove, | ||
667 | .id_table = lm78_i2c_id, | ||
668 | .detect = lm78_i2c_detect, | ||
669 | .address_list = normal_i2c, | ||
670 | }; | ||
736 | 671 | ||
737 | /* The SMBus locks itself, but ISA access must be locked explicitly! | 672 | /* The SMBus locks itself, but ISA access must be locked explicitly! |
738 | We don't want to lock the whole ISA bus, so we lock each client | 673 | We don't want to lock the whole ISA bus, so we lock each client |
@@ -743,6 +678,7 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) | |||
743 | { | 678 | { |
744 | struct i2c_client *client = data->client; | 679 | struct i2c_client *client = data->client; |
745 | 680 | ||
681 | #ifdef CONFIG_ISA | ||
746 | if (!client) { /* ISA device */ | 682 | if (!client) { /* ISA device */ |
747 | int res; | 683 | int res; |
748 | mutex_lock(&data->lock); | 684 | mutex_lock(&data->lock); |
@@ -751,6 +687,7 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) | |||
751 | mutex_unlock(&data->lock); | 687 | mutex_unlock(&data->lock); |
752 | return res; | 688 | return res; |
753 | } else | 689 | } else |
690 | #endif | ||
754 | return i2c_smbus_read_byte_data(client, reg); | 691 | return i2c_smbus_read_byte_data(client, reg); |
755 | } | 692 | } |
756 | 693 | ||
@@ -765,6 +702,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) | |||
765 | { | 702 | { |
766 | struct i2c_client *client = data->client; | 703 | struct i2c_client *client = data->client; |
767 | 704 | ||
705 | #ifdef CONFIG_ISA | ||
768 | if (!client) { /* ISA device */ | 706 | if (!client) { /* ISA device */ |
769 | mutex_lock(&data->lock); | 707 | mutex_lock(&data->lock); |
770 | outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); | 708 | outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); |
@@ -772,6 +710,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) | |||
772 | mutex_unlock(&data->lock); | 710 | mutex_unlock(&data->lock); |
773 | return 0; | 711 | return 0; |
774 | } else | 712 | } else |
713 | #endif | ||
775 | return i2c_smbus_write_byte_data(client, reg, value); | 714 | return i2c_smbus_write_byte_data(client, reg, value); |
776 | } | 715 | } |
777 | 716 | ||
@@ -849,6 +788,88 @@ static struct lm78_data *lm78_update_device(struct device *dev) | |||
849 | return data; | 788 | return data; |
850 | } | 789 | } |
851 | 790 | ||
791 | #ifdef CONFIG_ISA | ||
792 | static int __devinit lm78_isa_probe(struct platform_device *pdev) | ||
793 | { | ||
794 | int err; | ||
795 | struct lm78_data *data; | ||
796 | struct resource *res; | ||
797 | |||
798 | /* Reserve the ISA region */ | ||
799 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
800 | if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { | ||
801 | err = -EBUSY; | ||
802 | goto exit; | ||
803 | } | ||
804 | |||
805 | data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL); | ||
806 | if (!data) { | ||
807 | err = -ENOMEM; | ||
808 | goto exit_release_region; | ||
809 | } | ||
810 | mutex_init(&data->lock); | ||
811 | data->isa_addr = res->start; | ||
812 | platform_set_drvdata(pdev, data); | ||
813 | |||
814 | if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { | ||
815 | data->type = lm79; | ||
816 | data->name = "lm79"; | ||
817 | } else { | ||
818 | data->type = lm78; | ||
819 | data->name = "lm78"; | ||
820 | } | ||
821 | |||
822 | /* Initialize the LM78 chip */ | ||
823 | lm78_init_device(data); | ||
824 | |||
825 | /* Register sysfs hooks */ | ||
826 | if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group)) | ||
827 | || (err = device_create_file(&pdev->dev, &dev_attr_name))) | ||
828 | goto exit_remove_files; | ||
829 | |||
830 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | ||
831 | if (IS_ERR(data->hwmon_dev)) { | ||
832 | err = PTR_ERR(data->hwmon_dev); | ||
833 | goto exit_remove_files; | ||
834 | } | ||
835 | |||
836 | return 0; | ||
837 | |||
838 | exit_remove_files: | ||
839 | sysfs_remove_group(&pdev->dev.kobj, &lm78_group); | ||
840 | device_remove_file(&pdev->dev, &dev_attr_name); | ||
841 | kfree(data); | ||
842 | exit_release_region: | ||
843 | release_region(res->start + LM78_ADDR_REG_OFFSET, 2); | ||
844 | exit: | ||
845 | return err; | ||
846 | } | ||
847 | |||
848 | static int __devexit lm78_isa_remove(struct platform_device *pdev) | ||
849 | { | ||
850 | struct lm78_data *data = platform_get_drvdata(pdev); | ||
851 | struct resource *res; | ||
852 | |||
853 | hwmon_device_unregister(data->hwmon_dev); | ||
854 | sysfs_remove_group(&pdev->dev.kobj, &lm78_group); | ||
855 | device_remove_file(&pdev->dev, &dev_attr_name); | ||
856 | kfree(data); | ||
857 | |||
858 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
859 | release_region(res->start + LM78_ADDR_REG_OFFSET, 2); | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static struct platform_driver lm78_isa_driver = { | ||
865 | .driver = { | ||
866 | .owner = THIS_MODULE, | ||
867 | .name = "lm78", | ||
868 | }, | ||
869 | .probe = lm78_isa_probe, | ||
870 | .remove = __devexit_p(lm78_isa_remove), | ||
871 | }; | ||
872 | |||
852 | /* return 1 if a supported chip is found, 0 otherwise */ | 873 | /* return 1 if a supported chip is found, 0 otherwise */ |
853 | static int __init lm78_isa_found(unsigned short address) | 874 | static int __init lm78_isa_found(unsigned short address) |
854 | { | 875 | { |
@@ -969,12 +990,10 @@ static int __init lm78_isa_device_add(unsigned short address) | |||
969 | return err; | 990 | return err; |
970 | } | 991 | } |
971 | 992 | ||
972 | static int __init sm_lm78_init(void) | 993 | static int __init lm78_isa_register(void) |
973 | { | 994 | { |
974 | int res; | 995 | int res; |
975 | 996 | ||
976 | /* We register the ISA device first, so that we can skip the | ||
977 | * registration of an I2C interface to the same device. */ | ||
978 | if (lm78_isa_found(isa_address)) { | 997 | if (lm78_isa_found(isa_address)) { |
979 | res = platform_driver_register(&lm78_isa_driver); | 998 | res = platform_driver_register(&lm78_isa_driver); |
980 | if (res) | 999 | if (res) |
@@ -986,32 +1005,62 @@ static int __init sm_lm78_init(void) | |||
986 | goto exit_unreg_isa_driver; | 1005 | goto exit_unreg_isa_driver; |
987 | } | 1006 | } |
988 | 1007 | ||
989 | res = i2c_add_driver(&lm78_driver); | ||
990 | if (res) | ||
991 | goto exit_unreg_isa_device; | ||
992 | |||
993 | return 0; | 1008 | return 0; |
994 | 1009 | ||
995 | exit_unreg_isa_device: | ||
996 | platform_device_unregister(pdev); | ||
997 | exit_unreg_isa_driver: | 1010 | exit_unreg_isa_driver: |
998 | platform_driver_unregister(&lm78_isa_driver); | 1011 | platform_driver_unregister(&lm78_isa_driver); |
999 | exit: | 1012 | exit: |
1000 | return res; | 1013 | return res; |
1001 | } | 1014 | } |
1002 | 1015 | ||
1003 | static void __exit sm_lm78_exit(void) | 1016 | static void lm78_isa_unregister(void) |
1004 | { | 1017 | { |
1005 | if (pdev) { | 1018 | if (pdev) { |
1006 | platform_device_unregister(pdev); | 1019 | platform_device_unregister(pdev); |
1007 | platform_driver_unregister(&lm78_isa_driver); | 1020 | platform_driver_unregister(&lm78_isa_driver); |
1008 | } | 1021 | } |
1009 | i2c_del_driver(&lm78_driver); | ||
1010 | } | 1022 | } |
1023 | #else /* !CONFIG_ISA */ | ||
1011 | 1024 | ||
1025 | static int __init lm78_isa_register(void) | ||
1026 | { | ||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | static void lm78_isa_unregister(void) | ||
1031 | { | ||
1032 | } | ||
1033 | #endif /* CONFIG_ISA */ | ||
1012 | 1034 | ||
1035 | static int __init sm_lm78_init(void) | ||
1036 | { | ||
1037 | int res; | ||
1038 | |||
1039 | /* We register the ISA device first, so that we can skip the | ||
1040 | * registration of an I2C interface to the same device. */ | ||
1041 | res = lm78_isa_register(); | ||
1042 | if (res) | ||
1043 | goto exit; | ||
1044 | |||
1045 | res = i2c_add_driver(&lm78_driver); | ||
1046 | if (res) | ||
1047 | goto exit_unreg_isa_device; | ||
1048 | |||
1049 | return 0; | ||
1050 | |||
1051 | exit_unreg_isa_device: | ||
1052 | lm78_isa_unregister(); | ||
1053 | exit: | ||
1054 | return res; | ||
1055 | } | ||
1056 | |||
1057 | static void __exit sm_lm78_exit(void) | ||
1058 | { | ||
1059 | lm78_isa_unregister(); | ||
1060 | i2c_del_driver(&lm78_driver); | ||
1061 | } | ||
1013 | 1062 | ||
1014 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); | 1063 | MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <khali@linux-fr.org>"); |
1015 | MODULE_DESCRIPTION("LM78/LM79 driver"); | 1064 | MODULE_DESCRIPTION("LM78/LM79 driver"); |
1016 | MODULE_LICENSE("GPL"); | 1065 | MODULE_LICENSE("GPL"); |
1017 | 1066 | ||
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index 14335bbc9bdc..c97b78ef9116 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c | |||
@@ -38,8 +38,8 @@ struct max1111_data { | |||
38 | struct device *hwmon_dev; | 38 | struct device *hwmon_dev; |
39 | struct spi_message msg; | 39 | struct spi_message msg; |
40 | struct spi_transfer xfer[2]; | 40 | struct spi_transfer xfer[2]; |
41 | uint8_t *tx_buf; | 41 | uint8_t tx_buf[MAX1111_TX_BUF_SIZE]; |
42 | uint8_t *rx_buf; | 42 | uint8_t rx_buf[MAX1111_RX_BUF_SIZE]; |
43 | struct mutex drvdata_lock; | 43 | struct mutex drvdata_lock; |
44 | /* protect msg, xfer and buffers from multiple access */ | 44 | /* protect msg, xfer and buffers from multiple access */ |
45 | }; | 45 | }; |
@@ -131,33 +131,23 @@ static const struct attribute_group max1111_attr_group = { | |||
131 | .attrs = max1111_attributes, | 131 | .attrs = max1111_attributes, |
132 | }; | 132 | }; |
133 | 133 | ||
134 | static int setup_transfer(struct max1111_data *data) | 134 | static int __devinit setup_transfer(struct max1111_data *data) |
135 | { | 135 | { |
136 | struct spi_message *m; | 136 | struct spi_message *m; |
137 | struct spi_transfer *x; | 137 | struct spi_transfer *x; |
138 | 138 | ||
139 | data->tx_buf = kmalloc(MAX1111_TX_BUF_SIZE, GFP_KERNEL); | ||
140 | if (!data->tx_buf) | ||
141 | return -ENOMEM; | ||
142 | |||
143 | data->rx_buf = kmalloc(MAX1111_RX_BUF_SIZE, GFP_KERNEL); | ||
144 | if (!data->rx_buf) { | ||
145 | kfree(data->tx_buf); | ||
146 | return -ENOMEM; | ||
147 | } | ||
148 | |||
149 | m = &data->msg; | 139 | m = &data->msg; |
150 | x = &data->xfer[0]; | 140 | x = &data->xfer[0]; |
151 | 141 | ||
152 | spi_message_init(m); | 142 | spi_message_init(m); |
153 | 143 | ||
154 | x->tx_buf = &data->tx_buf[0]; | 144 | x->tx_buf = &data->tx_buf[0]; |
155 | x->len = 1; | 145 | x->len = MAX1111_TX_BUF_SIZE; |
156 | spi_message_add_tail(x, m); | 146 | spi_message_add_tail(x, m); |
157 | 147 | ||
158 | x++; | 148 | x++; |
159 | x->rx_buf = &data->rx_buf[0]; | 149 | x->rx_buf = &data->rx_buf[0]; |
160 | x->len = 2; | 150 | x->len = MAX1111_RX_BUF_SIZE; |
161 | spi_message_add_tail(x, m); | 151 | spi_message_add_tail(x, m); |
162 | 152 | ||
163 | return 0; | 153 | return 0; |
@@ -192,7 +182,7 @@ static int __devinit max1111_probe(struct spi_device *spi) | |||
192 | err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group); | 182 | err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group); |
193 | if (err) { | 183 | if (err) { |
194 | dev_err(&spi->dev, "failed to create attribute group\n"); | 184 | dev_err(&spi->dev, "failed to create attribute group\n"); |
195 | goto err_free_all; | 185 | goto err_free_data; |
196 | } | 186 | } |
197 | 187 | ||
198 | data->hwmon_dev = hwmon_device_register(&spi->dev); | 188 | data->hwmon_dev = hwmon_device_register(&spi->dev); |
@@ -209,9 +199,6 @@ static int __devinit max1111_probe(struct spi_device *spi) | |||
209 | 199 | ||
210 | err_remove: | 200 | err_remove: |
211 | sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); | 201 | sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); |
212 | err_free_all: | ||
213 | kfree(data->rx_buf); | ||
214 | kfree(data->tx_buf); | ||
215 | err_free_data: | 202 | err_free_data: |
216 | kfree(data); | 203 | kfree(data); |
217 | return err; | 204 | return err; |
@@ -224,8 +211,6 @@ static int __devexit max1111_remove(struct spi_device *spi) | |||
224 | hwmon_device_unregister(data->hwmon_dev); | 211 | hwmon_device_unregister(data->hwmon_dev); |
225 | sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); | 212 | sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); |
226 | mutex_destroy(&data->drvdata_lock); | 213 | mutex_destroy(&data->drvdata_lock); |
227 | kfree(data->rx_buf); | ||
228 | kfree(data->tx_buf); | ||
229 | kfree(data); | 214 | kfree(data); |
230 | return 0; | 215 | return 0; |
231 | } | 216 | } |
diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index 3494a4cce414..e3b5c6039c25 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c | |||
@@ -28,33 +28,15 @@ | |||
28 | #include <linux/hwmon-sysfs.h> | 28 | #include <linux/hwmon-sysfs.h> |
29 | #include <linux/err.h> | 29 | #include <linux/err.h> |
30 | #include <linux/mutex.h> | 30 | #include <linux/mutex.h> |
31 | #include <linux/io.h> | 31 | #include "sch56xx-common.h" |
32 | #include <linux/acpi.h> | ||
33 | #include <linux/delay.h> | ||
34 | 32 | ||
35 | #define DRVNAME "sch5627" | 33 | #define DRVNAME "sch5627" |
36 | #define DEVNAME DRVNAME /* We only support one model */ | 34 | #define DEVNAME DRVNAME /* We only support one model */ |
37 | 35 | ||
38 | #define SIO_SCH5627_EM_LD 0x0C /* Embedded Microcontroller LD */ | ||
39 | #define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ | ||
40 | #define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ | ||
41 | |||
42 | #define SIO_REG_LDSEL 0x07 /* Logical device select */ | ||
43 | #define SIO_REG_DEVID 0x20 /* Device ID */ | ||
44 | #define SIO_REG_ENABLE 0x30 /* Logical device enable */ | ||
45 | #define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ | ||
46 | |||
47 | #define SIO_SCH5627_ID 0xC6 /* Chipset ID */ | ||
48 | |||
49 | #define REGION_LENGTH 9 | ||
50 | |||
51 | #define SCH5627_HWMON_ID 0xa5 | 36 | #define SCH5627_HWMON_ID 0xa5 |
52 | #define SCH5627_COMPANY_ID 0x5c | 37 | #define SCH5627_COMPANY_ID 0x5c |
53 | #define SCH5627_PRIMARY_ID 0xa0 | 38 | #define SCH5627_PRIMARY_ID 0xa0 |
54 | 39 | ||
55 | #define SCH5627_CMD_READ 0x02 | ||
56 | #define SCH5627_CMD_WRITE 0x03 | ||
57 | |||
58 | #define SCH5627_REG_BUILD_CODE 0x39 | 40 | #define SCH5627_REG_BUILD_CODE 0x39 |
59 | #define SCH5627_REG_BUILD_ID 0x3a | 41 | #define SCH5627_REG_BUILD_ID 0x3a |
60 | #define SCH5627_REG_HWMON_ID 0x3c | 42 | #define SCH5627_REG_HWMON_ID 0x3c |
@@ -111,182 +93,6 @@ struct sch5627_data { | |||
111 | u16 in[SCH5627_NO_IN]; | 93 | u16 in[SCH5627_NO_IN]; |
112 | }; | 94 | }; |
113 | 95 | ||
114 | static struct platform_device *sch5627_pdev; | ||
115 | |||
116 | /* Super I/O functions */ | ||
117 | static inline int superio_inb(int base, int reg) | ||
118 | { | ||
119 | outb(reg, base); | ||
120 | return inb(base + 1); | ||
121 | } | ||
122 | |||
123 | static inline int superio_enter(int base) | ||
124 | { | ||
125 | /* Don't step on other drivers' I/O space by accident */ | ||
126 | if (!request_muxed_region(base, 2, DRVNAME)) { | ||
127 | pr_err("I/O address 0x%04x already in use\n", base); | ||
128 | return -EBUSY; | ||
129 | } | ||
130 | |||
131 | outb(SIO_UNLOCK_KEY, base); | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static inline void superio_select(int base, int ld) | ||
137 | { | ||
138 | outb(SIO_REG_LDSEL, base); | ||
139 | outb(ld, base + 1); | ||
140 | } | ||
141 | |||
142 | static inline void superio_exit(int base) | ||
143 | { | ||
144 | outb(SIO_LOCK_KEY, base); | ||
145 | release_region(base, 2); | ||
146 | } | ||
147 | |||
148 | static int sch5627_send_cmd(struct sch5627_data *data, u8 cmd, u16 reg, u8 v) | ||
149 | { | ||
150 | u8 val; | ||
151 | int i; | ||
152 | /* | ||
153 | * According to SMSC for the commands we use the maximum time for | ||
154 | * the EM to respond is 15 ms, but testing shows in practice it | ||
155 | * responds within 15-32 reads, so we first busy poll, and if | ||
156 | * that fails sleep a bit and try again until we are way past | ||
157 | * the 15 ms maximum response time. | ||
158 | */ | ||
159 | const int max_busy_polls = 64; | ||
160 | const int max_lazy_polls = 32; | ||
161 | |||
162 | /* (Optional) Write-Clear the EC to Host Mailbox Register */ | ||
163 | val = inb(data->addr + 1); | ||
164 | outb(val, data->addr + 1); | ||
165 | |||
166 | /* Set Mailbox Address Pointer to first location in Region 1 */ | ||
167 | outb(0x00, data->addr + 2); | ||
168 | outb(0x80, data->addr + 3); | ||
169 | |||
170 | /* Write Request Packet Header */ | ||
171 | outb(cmd, data->addr + 4); /* VREG Access Type read:0x02 write:0x03 */ | ||
172 | outb(0x01, data->addr + 5); /* # of Entries: 1 Byte (8-bit) */ | ||
173 | outb(0x04, data->addr + 2); /* Mailbox AP to first data entry loc. */ | ||
174 | |||
175 | /* Write Value field */ | ||
176 | if (cmd == SCH5627_CMD_WRITE) | ||
177 | outb(v, data->addr + 4); | ||
178 | |||
179 | /* Write Address field */ | ||
180 | outb(reg & 0xff, data->addr + 6); | ||
181 | outb(reg >> 8, data->addr + 7); | ||
182 | |||
183 | /* Execute the Random Access Command */ | ||
184 | outb(0x01, data->addr); /* Write 01h to the Host-to-EC register */ | ||
185 | |||
186 | /* EM Interface Polling "Algorithm" */ | ||
187 | for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { | ||
188 | if (i >= max_busy_polls) | ||
189 | msleep(1); | ||
190 | /* Read Interrupt source Register */ | ||
191 | val = inb(data->addr + 8); | ||
192 | /* Write Clear the interrupt source bits */ | ||
193 | if (val) | ||
194 | outb(val, data->addr + 8); | ||
195 | /* Command Completed ? */ | ||
196 | if (val & 0x01) | ||
197 | break; | ||
198 | } | ||
199 | if (i == max_busy_polls + max_lazy_polls) { | ||
200 | pr_err("Max retries exceeded reading virtual " | ||
201 | "register 0x%04hx (%d)\n", reg, 1); | ||
202 | return -EIO; | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * According to SMSC we may need to retry this, but sofar I've always | ||
207 | * seen this succeed in 1 try. | ||
208 | */ | ||
209 | for (i = 0; i < max_busy_polls; i++) { | ||
210 | /* Read EC-to-Host Register */ | ||
211 | val = inb(data->addr + 1); | ||
212 | /* Command Completed ? */ | ||
213 | if (val == 0x01) | ||
214 | break; | ||
215 | |||
216 | if (i == 0) | ||
217 | pr_warn("EC reports: 0x%02x reading virtual register " | ||
218 | "0x%04hx\n", (unsigned int)val, reg); | ||
219 | } | ||
220 | if (i == max_busy_polls) { | ||
221 | pr_err("Max retries exceeded reading virtual " | ||
222 | "register 0x%04hx (%d)\n", reg, 2); | ||
223 | return -EIO; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * According to the SMSC app note we should now do: | ||
228 | * | ||
229 | * Set Mailbox Address Pointer to first location in Region 1 * | ||
230 | * outb(0x00, data->addr + 2); | ||
231 | * outb(0x80, data->addr + 3); | ||
232 | * | ||
233 | * But if we do that things don't work, so let's not. | ||
234 | */ | ||
235 | |||
236 | /* Read Value field */ | ||
237 | if (cmd == SCH5627_CMD_READ) | ||
238 | return inb(data->addr + 4); | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg) | ||
244 | { | ||
245 | return sch5627_send_cmd(data, SCH5627_CMD_READ, reg, 0); | ||
246 | } | ||
247 | |||
248 | static int sch5627_write_virtual_reg(struct sch5627_data *data, | ||
249 | u16 reg, u8 val) | ||
250 | { | ||
251 | return sch5627_send_cmd(data, SCH5627_CMD_WRITE, reg, val); | ||
252 | } | ||
253 | |||
254 | static int sch5627_read_virtual_reg16(struct sch5627_data *data, u16 reg) | ||
255 | { | ||
256 | int lsb, msb; | ||
257 | |||
258 | /* Read LSB first, this will cause the matching MSB to be latched */ | ||
259 | lsb = sch5627_read_virtual_reg(data, reg); | ||
260 | if (lsb < 0) | ||
261 | return lsb; | ||
262 | |||
263 | msb = sch5627_read_virtual_reg(data, reg + 1); | ||
264 | if (msb < 0) | ||
265 | return msb; | ||
266 | |||
267 | return lsb | (msb << 8); | ||
268 | } | ||
269 | |||
270 | static int sch5627_read_virtual_reg12(struct sch5627_data *data, u16 msb_reg, | ||
271 | u16 lsn_reg, int high_nibble) | ||
272 | { | ||
273 | int msb, lsn; | ||
274 | |||
275 | /* Read MSB first, this will cause the matching LSN to be latched */ | ||
276 | msb = sch5627_read_virtual_reg(data, msb_reg); | ||
277 | if (msb < 0) | ||
278 | return msb; | ||
279 | |||
280 | lsn = sch5627_read_virtual_reg(data, lsn_reg); | ||
281 | if (lsn < 0) | ||
282 | return lsn; | ||
283 | |||
284 | if (high_nibble) | ||
285 | return (msb << 4) | (lsn >> 4); | ||
286 | else | ||
287 | return (msb << 4) | (lsn & 0x0f); | ||
288 | } | ||
289 | |||
290 | static struct sch5627_data *sch5627_update_device(struct device *dev) | 96 | static struct sch5627_data *sch5627_update_device(struct device *dev) |
291 | { | 97 | { |
292 | struct sch5627_data *data = dev_get_drvdata(dev); | 98 | struct sch5627_data *data = dev_get_drvdata(dev); |
@@ -297,7 +103,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev) | |||
297 | 103 | ||
298 | /* Trigger a Vbat voltage measurement every 5 minutes */ | 104 | /* Trigger a Vbat voltage measurement every 5 minutes */ |
299 | if (time_after(jiffies, data->last_battery + 300 * HZ)) { | 105 | if (time_after(jiffies, data->last_battery + 300 * HZ)) { |
300 | sch5627_write_virtual_reg(data, SCH5627_REG_CTRL, | 106 | sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, |
301 | data->control | 0x10); | 107 | data->control | 0x10); |
302 | data->last_battery = jiffies; | 108 | data->last_battery = jiffies; |
303 | } | 109 | } |
@@ -305,7 +111,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev) | |||
305 | /* Cache the values for 1 second */ | 111 | /* Cache the values for 1 second */ |
306 | if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { | 112 | if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { |
307 | for (i = 0; i < SCH5627_NO_TEMPS; i++) { | 113 | for (i = 0; i < SCH5627_NO_TEMPS; i++) { |
308 | val = sch5627_read_virtual_reg12(data, | 114 | val = sch56xx_read_virtual_reg12(data->addr, |
309 | SCH5627_REG_TEMP_MSB[i], | 115 | SCH5627_REG_TEMP_MSB[i], |
310 | SCH5627_REG_TEMP_LSN[i], | 116 | SCH5627_REG_TEMP_LSN[i], |
311 | SCH5627_REG_TEMP_HIGH_NIBBLE[i]); | 117 | SCH5627_REG_TEMP_HIGH_NIBBLE[i]); |
@@ -317,7 +123,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev) | |||
317 | } | 123 | } |
318 | 124 | ||
319 | for (i = 0; i < SCH5627_NO_FANS; i++) { | 125 | for (i = 0; i < SCH5627_NO_FANS; i++) { |
320 | val = sch5627_read_virtual_reg16(data, | 126 | val = sch56xx_read_virtual_reg16(data->addr, |
321 | SCH5627_REG_FAN[i]); | 127 | SCH5627_REG_FAN[i]); |
322 | if (unlikely(val < 0)) { | 128 | if (unlikely(val < 0)) { |
323 | ret = ERR_PTR(val); | 129 | ret = ERR_PTR(val); |
@@ -327,7 +133,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev) | |||
327 | } | 133 | } |
328 | 134 | ||
329 | for (i = 0; i < SCH5627_NO_IN; i++) { | 135 | for (i = 0; i < SCH5627_NO_IN; i++) { |
330 | val = sch5627_read_virtual_reg12(data, | 136 | val = sch56xx_read_virtual_reg12(data->addr, |
331 | SCH5627_REG_IN_MSB[i], | 137 | SCH5627_REG_IN_MSB[i], |
332 | SCH5627_REG_IN_LSN[i], | 138 | SCH5627_REG_IN_LSN[i], |
333 | SCH5627_REG_IN_HIGH_NIBBLE[i]); | 139 | SCH5627_REG_IN_HIGH_NIBBLE[i]); |
@@ -355,18 +161,21 @@ static int __devinit sch5627_read_limits(struct sch5627_data *data) | |||
355 | * Note what SMSC calls ABS, is what lm_sensors calls max | 161 | * Note what SMSC calls ABS, is what lm_sensors calls max |
356 | * (aka high), and HIGH is what lm_sensors calls crit. | 162 | * (aka high), and HIGH is what lm_sensors calls crit. |
357 | */ | 163 | */ |
358 | val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_ABS[i]); | 164 | val = sch56xx_read_virtual_reg(data->addr, |
165 | SCH5627_REG_TEMP_ABS[i]); | ||
359 | if (val < 0) | 166 | if (val < 0) |
360 | return val; | 167 | return val; |
361 | data->temp_max[i] = val; | 168 | data->temp_max[i] = val; |
362 | 169 | ||
363 | val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_HIGH[i]); | 170 | val = sch56xx_read_virtual_reg(data->addr, |
171 | SCH5627_REG_TEMP_HIGH[i]); | ||
364 | if (val < 0) | 172 | if (val < 0) |
365 | return val; | 173 | return val; |
366 | data->temp_crit[i] = val; | 174 | data->temp_crit[i] = val; |
367 | } | 175 | } |
368 | for (i = 0; i < SCH5627_NO_FANS; i++) { | 176 | for (i = 0; i < SCH5627_NO_FANS; i++) { |
369 | val = sch5627_read_virtual_reg16(data, SCH5627_REG_FAN_MIN[i]); | 177 | val = sch56xx_read_virtual_reg16(data->addr, |
178 | SCH5627_REG_FAN_MIN[i]); | ||
370 | if (val < 0) | 179 | if (val < 0) |
371 | return val; | 180 | return val; |
372 | data->fan_min[i] = val; | 181 | data->fan_min[i] = val; |
@@ -667,7 +476,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) | |||
667 | mutex_init(&data->update_lock); | 476 | mutex_init(&data->update_lock); |
668 | platform_set_drvdata(pdev, data); | 477 | platform_set_drvdata(pdev, data); |
669 | 478 | ||
670 | val = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_ID); | 479 | val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID); |
671 | if (val < 0) { | 480 | if (val < 0) { |
672 | err = val; | 481 | err = val; |
673 | goto error; | 482 | goto error; |
@@ -679,7 +488,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) | |||
679 | goto error; | 488 | goto error; |
680 | } | 489 | } |
681 | 490 | ||
682 | val = sch5627_read_virtual_reg(data, SCH5627_REG_COMPANY_ID); | 491 | val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID); |
683 | if (val < 0) { | 492 | if (val < 0) { |
684 | err = val; | 493 | err = val; |
685 | goto error; | 494 | goto error; |
@@ -691,7 +500,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) | |||
691 | goto error; | 500 | goto error; |
692 | } | 501 | } |
693 | 502 | ||
694 | val = sch5627_read_virtual_reg(data, SCH5627_REG_PRIMARY_ID); | 503 | val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID); |
695 | if (val < 0) { | 504 | if (val < 0) { |
696 | err = val; | 505 | err = val; |
697 | goto error; | 506 | goto error; |
@@ -703,25 +512,28 @@ static int __devinit sch5627_probe(struct platform_device *pdev) | |||
703 | goto error; | 512 | goto error; |
704 | } | 513 | } |
705 | 514 | ||
706 | build_code = sch5627_read_virtual_reg(data, SCH5627_REG_BUILD_CODE); | 515 | build_code = sch56xx_read_virtual_reg(data->addr, |
516 | SCH5627_REG_BUILD_CODE); | ||
707 | if (build_code < 0) { | 517 | if (build_code < 0) { |
708 | err = build_code; | 518 | err = build_code; |
709 | goto error; | 519 | goto error; |
710 | } | 520 | } |
711 | 521 | ||
712 | build_id = sch5627_read_virtual_reg16(data, SCH5627_REG_BUILD_ID); | 522 | build_id = sch56xx_read_virtual_reg16(data->addr, |
523 | SCH5627_REG_BUILD_ID); | ||
713 | if (build_id < 0) { | 524 | if (build_id < 0) { |
714 | err = build_id; | 525 | err = build_id; |
715 | goto error; | 526 | goto error; |
716 | } | 527 | } |
717 | 528 | ||
718 | hwmon_rev = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_REV); | 529 | hwmon_rev = sch56xx_read_virtual_reg(data->addr, |
530 | SCH5627_REG_HWMON_REV); | ||
719 | if (hwmon_rev < 0) { | 531 | if (hwmon_rev < 0) { |
720 | err = hwmon_rev; | 532 | err = hwmon_rev; |
721 | goto error; | 533 | goto error; |
722 | } | 534 | } |
723 | 535 | ||
724 | val = sch5627_read_virtual_reg(data, SCH5627_REG_CTRL); | 536 | val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL); |
725 | if (val < 0) { | 537 | if (val < 0) { |
726 | err = val; | 538 | err = val; |
727 | goto error; | 539 | goto error; |
@@ -734,7 +546,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) | |||
734 | } | 546 | } |
735 | /* Trigger a Vbat voltage measurement, so that we get a valid reading | 547 | /* Trigger a Vbat voltage measurement, so that we get a valid reading |
736 | the first time we read Vbat */ | 548 | the first time we read Vbat */ |
737 | sch5627_write_virtual_reg(data, SCH5627_REG_CTRL, | 549 | sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, |
738 | data->control | 0x10); | 550 | data->control | 0x10); |
739 | data->last_battery = jiffies; | 551 | data->last_battery = jiffies; |
740 | 552 | ||
@@ -746,6 +558,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) | |||
746 | if (err) | 558 | if (err) |
747 | goto error; | 559 | goto error; |
748 | 560 | ||
561 | pr_info("found %s chip at %#hx\n", DEVNAME, data->addr); | ||
749 | pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", | 562 | pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", |
750 | build_code, build_id, hwmon_rev); | 563 | build_code, build_id, hwmon_rev); |
751 | 564 | ||
@@ -768,85 +581,6 @@ error: | |||
768 | return err; | 581 | return err; |
769 | } | 582 | } |
770 | 583 | ||
771 | static int __init sch5627_find(int sioaddr, unsigned short *address) | ||
772 | { | ||
773 | u8 devid; | ||
774 | int err = superio_enter(sioaddr); | ||
775 | if (err) | ||
776 | return err; | ||
777 | |||
778 | devid = superio_inb(sioaddr, SIO_REG_DEVID); | ||
779 | if (devid != SIO_SCH5627_ID) { | ||
780 | pr_debug("Unsupported device id: 0x%02x\n", | ||
781 | (unsigned int)devid); | ||
782 | err = -ENODEV; | ||
783 | goto exit; | ||
784 | } | ||
785 | |||
786 | superio_select(sioaddr, SIO_SCH5627_EM_LD); | ||
787 | |||
788 | if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { | ||
789 | pr_warn("Device not activated\n"); | ||
790 | err = -ENODEV; | ||
791 | goto exit; | ||
792 | } | ||
793 | |||
794 | /* | ||
795 | * Warning the order of the low / high byte is the other way around | ||
796 | * as on most other superio devices!! | ||
797 | */ | ||
798 | *address = superio_inb(sioaddr, SIO_REG_ADDR) | | ||
799 | superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; | ||
800 | if (*address == 0) { | ||
801 | pr_warn("Base address not set\n"); | ||
802 | err = -ENODEV; | ||
803 | goto exit; | ||
804 | } | ||
805 | |||
806 | pr_info("Found %s chip at %#hx\n", DEVNAME, *address); | ||
807 | exit: | ||
808 | superio_exit(sioaddr); | ||
809 | return err; | ||
810 | } | ||
811 | |||
812 | static int __init sch5627_device_add(unsigned short address) | ||
813 | { | ||
814 | struct resource res = { | ||
815 | .start = address, | ||
816 | .end = address + REGION_LENGTH - 1, | ||
817 | .flags = IORESOURCE_IO, | ||
818 | }; | ||
819 | int err; | ||
820 | |||
821 | sch5627_pdev = platform_device_alloc(DRVNAME, address); | ||
822 | if (!sch5627_pdev) | ||
823 | return -ENOMEM; | ||
824 | |||
825 | res.name = sch5627_pdev->name; | ||
826 | err = acpi_check_resource_conflict(&res); | ||
827 | if (err) | ||
828 | goto exit_device_put; | ||
829 | |||
830 | err = platform_device_add_resources(sch5627_pdev, &res, 1); | ||
831 | if (err) { | ||
832 | pr_err("Device resource addition failed\n"); | ||
833 | goto exit_device_put; | ||
834 | } | ||
835 | |||
836 | err = platform_device_add(sch5627_pdev); | ||
837 | if (err) { | ||
838 | pr_err("Device addition failed\n"); | ||
839 | goto exit_device_put; | ||
840 | } | ||
841 | |||
842 | return 0; | ||
843 | |||
844 | exit_device_put: | ||
845 | platform_device_put(sch5627_pdev); | ||
846 | |||
847 | return err; | ||
848 | } | ||
849 | |||
850 | static struct platform_driver sch5627_driver = { | 584 | static struct platform_driver sch5627_driver = { |
851 | .driver = { | 585 | .driver = { |
852 | .owner = THIS_MODULE, | 586 | .owner = THIS_MODULE, |
@@ -858,31 +592,11 @@ static struct platform_driver sch5627_driver = { | |||
858 | 592 | ||
859 | static int __init sch5627_init(void) | 593 | static int __init sch5627_init(void) |
860 | { | 594 | { |
861 | int err = -ENODEV; | 595 | return platform_driver_register(&sch5627_driver); |
862 | unsigned short address; | ||
863 | |||
864 | if (sch5627_find(0x4e, &address) && sch5627_find(0x2e, &address)) | ||
865 | goto exit; | ||
866 | |||
867 | err = platform_driver_register(&sch5627_driver); | ||
868 | if (err) | ||
869 | goto exit; | ||
870 | |||
871 | err = sch5627_device_add(address); | ||
872 | if (err) | ||
873 | goto exit_driver; | ||
874 | |||
875 | return 0; | ||
876 | |||
877 | exit_driver: | ||
878 | platform_driver_unregister(&sch5627_driver); | ||
879 | exit: | ||
880 | return err; | ||
881 | } | 596 | } |
882 | 597 | ||
883 | static void __exit sch5627_exit(void) | 598 | static void __exit sch5627_exit(void) |
884 | { | 599 | { |
885 | platform_device_unregister(sch5627_pdev); | ||
886 | platform_driver_unregister(&sch5627_driver); | 600 | platform_driver_unregister(&sch5627_driver); |
887 | } | 601 | } |
888 | 602 | ||
diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c new file mode 100644 index 000000000000..244407aa79fc --- /dev/null +++ b/drivers/hwmon/sch5636.c | |||
@@ -0,0 +1,539 @@ | |||
1 | /*************************************************************************** | ||
2 | * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com> * | ||
3 | * * | ||
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 * | ||
6 | * the Free Software Foundation; either version 2 of the License, or * | ||
7 | * (at your option) any later version. * | ||
8 | * * | ||
9 | * This program is distributed in the hope that it will be useful, * | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | ||
12 | * GNU General Public License for more details. * | ||
13 | * * | ||
14 | * You should have received a copy of the GNU General Public License * | ||
15 | * along with this program; if not, write to the * | ||
16 | * Free Software Foundation, Inc., * | ||
17 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | ||
18 | ***************************************************************************/ | ||
19 | |||
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/jiffies.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/hwmon.h> | ||
28 | #include <linux/hwmon-sysfs.h> | ||
29 | #include <linux/err.h> | ||
30 | #include <linux/mutex.h> | ||
31 | #include "sch56xx-common.h" | ||
32 | |||
33 | #define DRVNAME "sch5636" | ||
34 | #define DEVNAME "theseus" /* We only support one model for now */ | ||
35 | |||
36 | #define SCH5636_REG_FUJITSU_ID 0x780 | ||
37 | #define SCH5636_REG_FUJITSU_REV 0x783 | ||
38 | |||
39 | #define SCH5636_NO_INS 5 | ||
40 | #define SCH5636_NO_TEMPS 16 | ||
41 | #define SCH5636_NO_FANS 8 | ||
42 | |||
43 | static const u16 SCH5636_REG_IN_VAL[SCH5636_NO_INS] = { | ||
44 | 0x22, 0x23, 0x24, 0x25, 0x189 }; | ||
45 | static const u16 SCH5636_REG_IN_FACTORS[SCH5636_NO_INS] = { | ||
46 | 4400, 1500, 4000, 4400, 16000 }; | ||
47 | static const char * const SCH5636_IN_LABELS[SCH5636_NO_INS] = { | ||
48 | "3.3V", "VREF", "VBAT", "3.3AUX", "12V" }; | ||
49 | |||
50 | static const u16 SCH5636_REG_TEMP_VAL[SCH5636_NO_TEMPS] = { | ||
51 | 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181, | ||
52 | 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C }; | ||
53 | #define SCH5636_REG_TEMP_CTRL(i) (0x790 + (i)) | ||
54 | #define SCH5636_TEMP_WORKING 0x01 | ||
55 | #define SCH5636_TEMP_ALARM 0x02 | ||
56 | #define SCH5636_TEMP_DEACTIVATED 0x80 | ||
57 | |||
58 | static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = { | ||
59 | 0x2C, 0x2E, 0x30, 0x32, 0x62, 0x64, 0x66, 0x68 }; | ||
60 | #define SCH5636_REG_FAN_CTRL(i) (0x880 + (i)) | ||
61 | /* FAULT in datasheet, but acts as an alarm */ | ||
62 | #define SCH5636_FAN_ALARM 0x04 | ||
63 | #define SCH5636_FAN_NOT_PRESENT 0x08 | ||
64 | #define SCH5636_FAN_DEACTIVATED 0x80 | ||
65 | |||
66 | |||
67 | struct sch5636_data { | ||
68 | unsigned short addr; | ||
69 | struct device *hwmon_dev; | ||
70 | |||
71 | struct mutex update_lock; | ||
72 | char valid; /* !=0 if following fields are valid */ | ||
73 | unsigned long last_updated; /* In jiffies */ | ||
74 | u8 in[SCH5636_NO_INS]; | ||
75 | u8 temp_val[SCH5636_NO_TEMPS]; | ||
76 | u8 temp_ctrl[SCH5636_NO_TEMPS]; | ||
77 | u16 fan_val[SCH5636_NO_FANS]; | ||
78 | u8 fan_ctrl[SCH5636_NO_FANS]; | ||
79 | }; | ||
80 | |||
81 | static struct sch5636_data *sch5636_update_device(struct device *dev) | ||
82 | { | ||
83 | struct sch5636_data *data = dev_get_drvdata(dev); | ||
84 | struct sch5636_data *ret = data; | ||
85 | int i, val; | ||
86 | |||
87 | mutex_lock(&data->update_lock); | ||
88 | |||
89 | /* Cache the values for 1 second */ | ||
90 | if (data->valid && !time_after(jiffies, data->last_updated + HZ)) | ||
91 | goto abort; | ||
92 | |||
93 | for (i = 0; i < SCH5636_NO_INS; i++) { | ||
94 | val = sch56xx_read_virtual_reg(data->addr, | ||
95 | SCH5636_REG_IN_VAL[i]); | ||
96 | if (unlikely(val < 0)) { | ||
97 | ret = ERR_PTR(val); | ||
98 | goto abort; | ||
99 | } | ||
100 | data->in[i] = val; | ||
101 | } | ||
102 | |||
103 | for (i = 0; i < SCH5636_NO_TEMPS; i++) { | ||
104 | if (data->temp_ctrl[i] & SCH5636_TEMP_DEACTIVATED) | ||
105 | continue; | ||
106 | |||
107 | val = sch56xx_read_virtual_reg(data->addr, | ||
108 | SCH5636_REG_TEMP_VAL[i]); | ||
109 | if (unlikely(val < 0)) { | ||
110 | ret = ERR_PTR(val); | ||
111 | goto abort; | ||
112 | } | ||
113 | data->temp_val[i] = val; | ||
114 | |||
115 | val = sch56xx_read_virtual_reg(data->addr, | ||
116 | SCH5636_REG_TEMP_CTRL(i)); | ||
117 | if (unlikely(val < 0)) { | ||
118 | ret = ERR_PTR(val); | ||
119 | goto abort; | ||
120 | } | ||
121 | data->temp_ctrl[i] = val; | ||
122 | /* Alarms need to be explicitly write-cleared */ | ||
123 | if (val & SCH5636_TEMP_ALARM) { | ||
124 | sch56xx_write_virtual_reg(data->addr, | ||
125 | SCH5636_REG_TEMP_CTRL(i), val); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | for (i = 0; i < SCH5636_NO_FANS; i++) { | ||
130 | if (data->fan_ctrl[i] & SCH5636_FAN_DEACTIVATED) | ||
131 | continue; | ||
132 | |||
133 | val = sch56xx_read_virtual_reg16(data->addr, | ||
134 | SCH5636_REG_FAN_VAL[i]); | ||
135 | if (unlikely(val < 0)) { | ||
136 | ret = ERR_PTR(val); | ||
137 | goto abort; | ||
138 | } | ||
139 | data->fan_val[i] = val; | ||
140 | |||
141 | val = sch56xx_read_virtual_reg(data->addr, | ||
142 | SCH5636_REG_FAN_CTRL(i)); | ||
143 | if (unlikely(val < 0)) { | ||
144 | ret = ERR_PTR(val); | ||
145 | goto abort; | ||
146 | } | ||
147 | data->fan_ctrl[i] = val; | ||
148 | /* Alarms need to be explicitly write-cleared */ | ||
149 | if (val & SCH5636_FAN_ALARM) { | ||
150 | sch56xx_write_virtual_reg(data->addr, | ||
151 | SCH5636_REG_FAN_CTRL(i), val); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | data->last_updated = jiffies; | ||
156 | data->valid = 1; | ||
157 | abort: | ||
158 | mutex_unlock(&data->update_lock); | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | static int reg_to_rpm(u16 reg) | ||
163 | { | ||
164 | if (reg == 0) | ||
165 | return -EIO; | ||
166 | if (reg == 0xffff) | ||
167 | return 0; | ||
168 | |||
169 | return 5400540 / reg; | ||
170 | } | ||
171 | |||
172 | static ssize_t show_name(struct device *dev, struct device_attribute *devattr, | ||
173 | char *buf) | ||
174 | { | ||
175 | return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); | ||
176 | } | ||
177 | |||
178 | static ssize_t show_in_value(struct device *dev, struct device_attribute | ||
179 | *devattr, char *buf) | ||
180 | { | ||
181 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
182 | struct sch5636_data *data = sch5636_update_device(dev); | ||
183 | int val; | ||
184 | |||
185 | if (IS_ERR(data)) | ||
186 | return PTR_ERR(data); | ||
187 | |||
188 | val = DIV_ROUND_CLOSEST( | ||
189 | data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index], | ||
190 | 255); | ||
191 | return snprintf(buf, PAGE_SIZE, "%d\n", val); | ||
192 | } | ||
193 | |||
194 | static ssize_t show_in_label(struct device *dev, struct device_attribute | ||
195 | *devattr, char *buf) | ||
196 | { | ||
197 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
198 | |||
199 | return snprintf(buf, PAGE_SIZE, "%s\n", | ||
200 | SCH5636_IN_LABELS[attr->index]); | ||
201 | } | ||
202 | |||
203 | static ssize_t show_temp_value(struct device *dev, struct device_attribute | ||
204 | *devattr, char *buf) | ||
205 | { | ||
206 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
207 | struct sch5636_data *data = sch5636_update_device(dev); | ||
208 | int val; | ||
209 | |||
210 | if (IS_ERR(data)) | ||
211 | return PTR_ERR(data); | ||
212 | |||
213 | val = (data->temp_val[attr->index] - 64) * 1000; | ||
214 | return snprintf(buf, PAGE_SIZE, "%d\n", val); | ||
215 | } | ||
216 | |||
217 | static ssize_t show_temp_fault(struct device *dev, struct device_attribute | ||
218 | *devattr, char *buf) | ||
219 | { | ||
220 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
221 | struct sch5636_data *data = sch5636_update_device(dev); | ||
222 | int val; | ||
223 | |||
224 | if (IS_ERR(data)) | ||
225 | return PTR_ERR(data); | ||
226 | |||
227 | val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1; | ||
228 | return snprintf(buf, PAGE_SIZE, "%d\n", val); | ||
229 | } | ||
230 | |||
231 | static ssize_t show_temp_alarm(struct device *dev, struct device_attribute | ||
232 | *devattr, char *buf) | ||
233 | { | ||
234 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
235 | struct sch5636_data *data = sch5636_update_device(dev); | ||
236 | int val; | ||
237 | |||
238 | if (IS_ERR(data)) | ||
239 | return PTR_ERR(data); | ||
240 | |||
241 | val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0; | ||
242 | return snprintf(buf, PAGE_SIZE, "%d\n", val); | ||
243 | } | ||
244 | |||
245 | static ssize_t show_fan_value(struct device *dev, struct device_attribute | ||
246 | *devattr, char *buf) | ||
247 | { | ||
248 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
249 | struct sch5636_data *data = sch5636_update_device(dev); | ||
250 | int val; | ||
251 | |||
252 | if (IS_ERR(data)) | ||
253 | return PTR_ERR(data); | ||
254 | |||
255 | val = reg_to_rpm(data->fan_val[attr->index]); | ||
256 | if (val < 0) | ||
257 | return val; | ||
258 | |||
259 | return snprintf(buf, PAGE_SIZE, "%d\n", val); | ||
260 | } | ||
261 | |||
262 | static ssize_t show_fan_fault(struct device *dev, struct device_attribute | ||
263 | *devattr, char *buf) | ||
264 | { | ||
265 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
266 | struct sch5636_data *data = sch5636_update_device(dev); | ||
267 | int val; | ||
268 | |||
269 | if (IS_ERR(data)) | ||
270 | return PTR_ERR(data); | ||
271 | |||
272 | val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0; | ||
273 | return snprintf(buf, PAGE_SIZE, "%d\n", val); | ||
274 | } | ||
275 | |||
276 | static ssize_t show_fan_alarm(struct device *dev, struct device_attribute | ||
277 | *devattr, char *buf) | ||
278 | { | ||
279 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
280 | struct sch5636_data *data = sch5636_update_device(dev); | ||
281 | int val; | ||
282 | |||
283 | if (IS_ERR(data)) | ||
284 | return PTR_ERR(data); | ||
285 | |||
286 | val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0; | ||
287 | return snprintf(buf, PAGE_SIZE, "%d\n", val); | ||
288 | } | ||
289 | |||
290 | static struct sensor_device_attribute sch5636_attr[] = { | ||
291 | SENSOR_ATTR(name, 0444, show_name, NULL, 0), | ||
292 | SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0), | ||
293 | SENSOR_ATTR(in0_label, 0444, show_in_label, NULL, 0), | ||
294 | SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1), | ||
295 | SENSOR_ATTR(in1_label, 0444, show_in_label, NULL, 1), | ||
296 | SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2), | ||
297 | SENSOR_ATTR(in2_label, 0444, show_in_label, NULL, 2), | ||
298 | SENSOR_ATTR(in3_input, 0444, show_in_value, NULL, 3), | ||
299 | SENSOR_ATTR(in3_label, 0444, show_in_label, NULL, 3), | ||
300 | SENSOR_ATTR(in4_input, 0444, show_in_value, NULL, 4), | ||
301 | SENSOR_ATTR(in4_label, 0444, show_in_label, NULL, 4), | ||
302 | }; | ||
303 | |||
304 | static struct sensor_device_attribute sch5636_temp_attr[] = { | ||
305 | SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), | ||
306 | SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0), | ||
307 | SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0), | ||
308 | SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1), | ||
309 | SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1), | ||
310 | SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1), | ||
311 | SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2), | ||
312 | SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2), | ||
313 | SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2), | ||
314 | SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3), | ||
315 | SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3), | ||
316 | SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3), | ||
317 | SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4), | ||
318 | SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4), | ||
319 | SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4), | ||
320 | SENSOR_ATTR(temp6_input, 0444, show_temp_value, NULL, 5), | ||
321 | SENSOR_ATTR(temp6_fault, 0444, show_temp_fault, NULL, 5), | ||
322 | SENSOR_ATTR(temp6_alarm, 0444, show_temp_alarm, NULL, 5), | ||
323 | SENSOR_ATTR(temp7_input, 0444, show_temp_value, NULL, 6), | ||
324 | SENSOR_ATTR(temp7_fault, 0444, show_temp_fault, NULL, 6), | ||
325 | SENSOR_ATTR(temp7_alarm, 0444, show_temp_alarm, NULL, 6), | ||
326 | SENSOR_ATTR(temp8_input, 0444, show_temp_value, NULL, 7), | ||
327 | SENSOR_ATTR(temp8_fault, 0444, show_temp_fault, NULL, 7), | ||
328 | SENSOR_ATTR(temp8_alarm, 0444, show_temp_alarm, NULL, 7), | ||
329 | SENSOR_ATTR(temp9_input, 0444, show_temp_value, NULL, 8), | ||
330 | SENSOR_ATTR(temp9_fault, 0444, show_temp_fault, NULL, 8), | ||
331 | SENSOR_ATTR(temp9_alarm, 0444, show_temp_alarm, NULL, 8), | ||
332 | SENSOR_ATTR(temp10_input, 0444, show_temp_value, NULL, 9), | ||
333 | SENSOR_ATTR(temp10_fault, 0444, show_temp_fault, NULL, 9), | ||
334 | SENSOR_ATTR(temp10_alarm, 0444, show_temp_alarm, NULL, 9), | ||
335 | SENSOR_ATTR(temp11_input, 0444, show_temp_value, NULL, 10), | ||
336 | SENSOR_ATTR(temp11_fault, 0444, show_temp_fault, NULL, 10), | ||
337 | SENSOR_ATTR(temp11_alarm, 0444, show_temp_alarm, NULL, 10), | ||
338 | SENSOR_ATTR(temp12_input, 0444, show_temp_value, NULL, 11), | ||
339 | SENSOR_ATTR(temp12_fault, 0444, show_temp_fault, NULL, 11), | ||
340 | SENSOR_ATTR(temp12_alarm, 0444, show_temp_alarm, NULL, 11), | ||
341 | SENSOR_ATTR(temp13_input, 0444, show_temp_value, NULL, 12), | ||
342 | SENSOR_ATTR(temp13_fault, 0444, show_temp_fault, NULL, 12), | ||
343 | SENSOR_ATTR(temp13_alarm, 0444, show_temp_alarm, NULL, 12), | ||
344 | SENSOR_ATTR(temp14_input, 0444, show_temp_value, NULL, 13), | ||
345 | SENSOR_ATTR(temp14_fault, 0444, show_temp_fault, NULL, 13), | ||
346 | SENSOR_ATTR(temp14_alarm, 0444, show_temp_alarm, NULL, 13), | ||
347 | SENSOR_ATTR(temp15_input, 0444, show_temp_value, NULL, 14), | ||
348 | SENSOR_ATTR(temp15_fault, 0444, show_temp_fault, NULL, 14), | ||
349 | SENSOR_ATTR(temp15_alarm, 0444, show_temp_alarm, NULL, 14), | ||
350 | SENSOR_ATTR(temp16_input, 0444, show_temp_value, NULL, 15), | ||
351 | SENSOR_ATTR(temp16_fault, 0444, show_temp_fault, NULL, 15), | ||
352 | SENSOR_ATTR(temp16_alarm, 0444, show_temp_alarm, NULL, 15), | ||
353 | }; | ||
354 | |||
355 | static struct sensor_device_attribute sch5636_fan_attr[] = { | ||
356 | SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0), | ||
357 | SENSOR_ATTR(fan1_fault, 0444, show_fan_fault, NULL, 0), | ||
358 | SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0), | ||
359 | SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1), | ||
360 | SENSOR_ATTR(fan2_fault, 0444, show_fan_fault, NULL, 1), | ||
361 | SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1), | ||
362 | SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2), | ||
363 | SENSOR_ATTR(fan3_fault, 0444, show_fan_fault, NULL, 2), | ||
364 | SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2), | ||
365 | SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3), | ||
366 | SENSOR_ATTR(fan4_fault, 0444, show_fan_fault, NULL, 3), | ||
367 | SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3), | ||
368 | SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4), | ||
369 | SENSOR_ATTR(fan5_fault, 0444, show_fan_fault, NULL, 4), | ||
370 | SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4), | ||
371 | SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5), | ||
372 | SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5), | ||
373 | SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5), | ||
374 | SENSOR_ATTR(fan7_input, 0444, show_fan_value, NULL, 6), | ||
375 | SENSOR_ATTR(fan7_fault, 0444, show_fan_fault, NULL, 6), | ||
376 | SENSOR_ATTR(fan7_alarm, 0444, show_fan_alarm, NULL, 6), | ||
377 | SENSOR_ATTR(fan8_input, 0444, show_fan_value, NULL, 7), | ||
378 | SENSOR_ATTR(fan8_fault, 0444, show_fan_fault, NULL, 7), | ||
379 | SENSOR_ATTR(fan8_alarm, 0444, show_fan_alarm, NULL, 7), | ||
380 | }; | ||
381 | |||
382 | static int sch5636_remove(struct platform_device *pdev) | ||
383 | { | ||
384 | struct sch5636_data *data = platform_get_drvdata(pdev); | ||
385 | int i; | ||
386 | |||
387 | if (data->hwmon_dev) | ||
388 | hwmon_device_unregister(data->hwmon_dev); | ||
389 | |||
390 | for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) | ||
391 | device_remove_file(&pdev->dev, &sch5636_attr[i].dev_attr); | ||
392 | |||
393 | for (i = 0; i < SCH5636_NO_TEMPS * 3; i++) | ||
394 | device_remove_file(&pdev->dev, | ||
395 | &sch5636_temp_attr[i].dev_attr); | ||
396 | |||
397 | for (i = 0; i < SCH5636_NO_FANS * 3; i++) | ||
398 | device_remove_file(&pdev->dev, | ||
399 | &sch5636_fan_attr[i].dev_attr); | ||
400 | |||
401 | platform_set_drvdata(pdev, NULL); | ||
402 | kfree(data); | ||
403 | |||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | static int __devinit sch5636_probe(struct platform_device *pdev) | ||
408 | { | ||
409 | struct sch5636_data *data; | ||
410 | int i, err, val, revision[2]; | ||
411 | char id[4]; | ||
412 | |||
413 | data = kzalloc(sizeof(struct sch5636_data), GFP_KERNEL); | ||
414 | if (!data) | ||
415 | return -ENOMEM; | ||
416 | |||
417 | data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; | ||
418 | mutex_init(&data->update_lock); | ||
419 | platform_set_drvdata(pdev, data); | ||
420 | |||
421 | for (i = 0; i < 3; i++) { | ||
422 | val = sch56xx_read_virtual_reg(data->addr, | ||
423 | SCH5636_REG_FUJITSU_ID + i); | ||
424 | if (val < 0) { | ||
425 | pr_err("Could not read Fujitsu id byte at %#x\n", | ||
426 | SCH5636_REG_FUJITSU_ID + i); | ||
427 | err = val; | ||
428 | goto error; | ||
429 | } | ||
430 | id[i] = val; | ||
431 | } | ||
432 | id[i] = '\0'; | ||
433 | |||
434 | if (strcmp(id, "THS")) { | ||
435 | pr_err("Unknown Fujitsu id: %02x%02x%02x\n", | ||
436 | id[0], id[1], id[2]); | ||
437 | err = -ENODEV; | ||
438 | goto error; | ||
439 | } | ||
440 | |||
441 | for (i = 0; i < 2; i++) { | ||
442 | val = sch56xx_read_virtual_reg(data->addr, | ||
443 | SCH5636_REG_FUJITSU_REV + i); | ||
444 | if (val < 0) { | ||
445 | err = val; | ||
446 | goto error; | ||
447 | } | ||
448 | revision[i] = val; | ||
449 | } | ||
450 | pr_info("Found %s chip at %#hx, revison: %d.%02d\n", DEVNAME, | ||
451 | data->addr, revision[0], revision[1]); | ||
452 | |||
453 | /* Read all temp + fan ctrl registers to determine which are active */ | ||
454 | for (i = 0; i < SCH5636_NO_TEMPS; i++) { | ||
455 | val = sch56xx_read_virtual_reg(data->addr, | ||
456 | SCH5636_REG_TEMP_CTRL(i)); | ||
457 | if (unlikely(val < 0)) { | ||
458 | err = val; | ||
459 | goto error; | ||
460 | } | ||
461 | data->temp_ctrl[i] = val; | ||
462 | } | ||
463 | |||
464 | for (i = 0; i < SCH5636_NO_FANS; i++) { | ||
465 | val = sch56xx_read_virtual_reg(data->addr, | ||
466 | SCH5636_REG_FAN_CTRL(i)); | ||
467 | if (unlikely(val < 0)) { | ||
468 | err = val; | ||
469 | goto error; | ||
470 | } | ||
471 | data->fan_ctrl[i] = val; | ||
472 | } | ||
473 | |||
474 | for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) { | ||
475 | err = device_create_file(&pdev->dev, | ||
476 | &sch5636_attr[i].dev_attr); | ||
477 | if (err) | ||
478 | goto error; | ||
479 | } | ||
480 | |||
481 | for (i = 0; i < (SCH5636_NO_TEMPS * 3); i++) { | ||
482 | if (data->temp_ctrl[i/3] & SCH5636_TEMP_DEACTIVATED) | ||
483 | continue; | ||
484 | |||
485 | err = device_create_file(&pdev->dev, | ||
486 | &sch5636_temp_attr[i].dev_attr); | ||
487 | if (err) | ||
488 | goto error; | ||
489 | } | ||
490 | |||
491 | for (i = 0; i < (SCH5636_NO_FANS * 3); i++) { | ||
492 | if (data->fan_ctrl[i/3] & SCH5636_FAN_DEACTIVATED) | ||
493 | continue; | ||
494 | |||
495 | err = device_create_file(&pdev->dev, | ||
496 | &sch5636_fan_attr[i].dev_attr); | ||
497 | if (err) | ||
498 | goto error; | ||
499 | } | ||
500 | |||
501 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | ||
502 | if (IS_ERR(data->hwmon_dev)) { | ||
503 | err = PTR_ERR(data->hwmon_dev); | ||
504 | data->hwmon_dev = NULL; | ||
505 | goto error; | ||
506 | } | ||
507 | |||
508 | return 0; | ||
509 | |||
510 | error: | ||
511 | sch5636_remove(pdev); | ||
512 | return err; | ||
513 | } | ||
514 | |||
515 | static struct platform_driver sch5636_driver = { | ||
516 | .driver = { | ||
517 | .owner = THIS_MODULE, | ||
518 | .name = DRVNAME, | ||
519 | }, | ||
520 | .probe = sch5636_probe, | ||
521 | .remove = sch5636_remove, | ||
522 | }; | ||
523 | |||
524 | static int __init sch5636_init(void) | ||
525 | { | ||
526 | return platform_driver_register(&sch5636_driver); | ||
527 | } | ||
528 | |||
529 | static void __exit sch5636_exit(void) | ||
530 | { | ||
531 | platform_driver_unregister(&sch5636_driver); | ||
532 | } | ||
533 | |||
534 | MODULE_DESCRIPTION("SMSC SCH5636 Hardware Monitoring Driver"); | ||
535 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | ||
536 | MODULE_LICENSE("GPL"); | ||
537 | |||
538 | module_init(sch5636_init); | ||
539 | module_exit(sch5636_exit); | ||
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c new file mode 100644 index 000000000000..fac32ee0b10e --- /dev/null +++ b/drivers/hwmon/sch56xx-common.c | |||
@@ -0,0 +1,340 @@ | |||
1 | /*************************************************************************** | ||
2 | * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> * | ||
3 | * * | ||
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 * | ||
6 | * the Free Software Foundation; either version 2 of the License, or * | ||
7 | * (at your option) any later version. * | ||
8 | * * | ||
9 | * This program is distributed in the hope that it will be useful, * | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | ||
12 | * GNU General Public License for more details. * | ||
13 | * * | ||
14 | * You should have received a copy of the GNU General Public License * | ||
15 | * along with this program; if not, write to the * | ||
16 | * Free Software Foundation, Inc., * | ||
17 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | ||
18 | ***************************************************************************/ | ||
19 | |||
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/io.h> | ||
27 | #include <linux/acpi.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include "sch56xx-common.h" | ||
30 | |||
31 | #define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */ | ||
32 | #define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ | ||
33 | #define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ | ||
34 | |||
35 | #define SIO_REG_LDSEL 0x07 /* Logical device select */ | ||
36 | #define SIO_REG_DEVID 0x20 /* Device ID */ | ||
37 | #define SIO_REG_ENABLE 0x30 /* Logical device enable */ | ||
38 | #define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ | ||
39 | |||
40 | #define SIO_SCH5627_ID 0xC6 /* Chipset ID */ | ||
41 | #define SIO_SCH5636_ID 0xC7 /* Chipset ID */ | ||
42 | |||
43 | #define REGION_LENGTH 9 | ||
44 | |||
45 | #define SCH56XX_CMD_READ 0x02 | ||
46 | #define SCH56XX_CMD_WRITE 0x03 | ||
47 | |||
48 | static struct platform_device *sch56xx_pdev; | ||
49 | |||
50 | /* Super I/O functions */ | ||
51 | static inline int superio_inb(int base, int reg) | ||
52 | { | ||
53 | outb(reg, base); | ||
54 | return inb(base + 1); | ||
55 | } | ||
56 | |||
57 | static inline int superio_enter(int base) | ||
58 | { | ||
59 | /* Don't step on other drivers' I/O space by accident */ | ||
60 | if (!request_muxed_region(base, 2, "sch56xx")) { | ||
61 | pr_err("I/O address 0x%04x already in use\n", base); | ||
62 | return -EBUSY; | ||
63 | } | ||
64 | |||
65 | outb(SIO_UNLOCK_KEY, base); | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static inline void superio_select(int base, int ld) | ||
71 | { | ||
72 | outb(SIO_REG_LDSEL, base); | ||
73 | outb(ld, base + 1); | ||
74 | } | ||
75 | |||
76 | static inline void superio_exit(int base) | ||
77 | { | ||
78 | outb(SIO_LOCK_KEY, base); | ||
79 | release_region(base, 2); | ||
80 | } | ||
81 | |||
82 | static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) | ||
83 | { | ||
84 | u8 val; | ||
85 | int i; | ||
86 | /* | ||
87 | * According to SMSC for the commands we use the maximum time for | ||
88 | * the EM to respond is 15 ms, but testing shows in practice it | ||
89 | * responds within 15-32 reads, so we first busy poll, and if | ||
90 | * that fails sleep a bit and try again until we are way past | ||
91 | * the 15 ms maximum response time. | ||
92 | */ | ||
93 | const int max_busy_polls = 64; | ||
94 | const int max_lazy_polls = 32; | ||
95 | |||
96 | /* (Optional) Write-Clear the EC to Host Mailbox Register */ | ||
97 | val = inb(addr + 1); | ||
98 | outb(val, addr + 1); | ||
99 | |||
100 | /* Set Mailbox Address Pointer to first location in Region 1 */ | ||
101 | outb(0x00, addr + 2); | ||
102 | outb(0x80, addr + 3); | ||
103 | |||
104 | /* Write Request Packet Header */ | ||
105 | outb(cmd, addr + 4); /* VREG Access Type read:0x02 write:0x03 */ | ||
106 | outb(0x01, addr + 5); /* # of Entries: 1 Byte (8-bit) */ | ||
107 | outb(0x04, addr + 2); /* Mailbox AP to first data entry loc. */ | ||
108 | |||
109 | /* Write Value field */ | ||
110 | if (cmd == SCH56XX_CMD_WRITE) | ||
111 | outb(v, addr + 4); | ||
112 | |||
113 | /* Write Address field */ | ||
114 | outb(reg & 0xff, addr + 6); | ||
115 | outb(reg >> 8, addr + 7); | ||
116 | |||
117 | /* Execute the Random Access Command */ | ||
118 | outb(0x01, addr); /* Write 01h to the Host-to-EC register */ | ||
119 | |||
120 | /* EM Interface Polling "Algorithm" */ | ||
121 | for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { | ||
122 | if (i >= max_busy_polls) | ||
123 | msleep(1); | ||
124 | /* Read Interrupt source Register */ | ||
125 | val = inb(addr + 8); | ||
126 | /* Write Clear the interrupt source bits */ | ||
127 | if (val) | ||
128 | outb(val, addr + 8); | ||
129 | /* Command Completed ? */ | ||
130 | if (val & 0x01) | ||
131 | break; | ||
132 | } | ||
133 | if (i == max_busy_polls + max_lazy_polls) { | ||
134 | pr_err("Max retries exceeded reading virtual " | ||
135 | "register 0x%04hx (%d)\n", reg, 1); | ||
136 | return -EIO; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * According to SMSC we may need to retry this, but sofar I've always | ||
141 | * seen this succeed in 1 try. | ||
142 | */ | ||
143 | for (i = 0; i < max_busy_polls; i++) { | ||
144 | /* Read EC-to-Host Register */ | ||
145 | val = inb(addr + 1); | ||
146 | /* Command Completed ? */ | ||
147 | if (val == 0x01) | ||
148 | break; | ||
149 | |||
150 | if (i == 0) | ||
151 | pr_warn("EC reports: 0x%02x reading virtual register " | ||
152 | "0x%04hx\n", (unsigned int)val, reg); | ||
153 | } | ||
154 | if (i == max_busy_polls) { | ||
155 | pr_err("Max retries exceeded reading virtual " | ||
156 | "register 0x%04hx (%d)\n", reg, 2); | ||
157 | return -EIO; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * According to the SMSC app note we should now do: | ||
162 | * | ||
163 | * Set Mailbox Address Pointer to first location in Region 1 * | ||
164 | * outb(0x00, addr + 2); | ||
165 | * outb(0x80, addr + 3); | ||
166 | * | ||
167 | * But if we do that things don't work, so let's not. | ||
168 | */ | ||
169 | |||
170 | /* Read Value field */ | ||
171 | if (cmd == SCH56XX_CMD_READ) | ||
172 | return inb(addr + 4); | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | int sch56xx_read_virtual_reg(u16 addr, u16 reg) | ||
178 | { | ||
179 | return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, 0); | ||
180 | } | ||
181 | EXPORT_SYMBOL(sch56xx_read_virtual_reg); | ||
182 | |||
183 | int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val) | ||
184 | { | ||
185 | return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, val); | ||
186 | } | ||
187 | EXPORT_SYMBOL(sch56xx_write_virtual_reg); | ||
188 | |||
189 | int sch56xx_read_virtual_reg16(u16 addr, u16 reg) | ||
190 | { | ||
191 | int lsb, msb; | ||
192 | |||
193 | /* Read LSB first, this will cause the matching MSB to be latched */ | ||
194 | lsb = sch56xx_read_virtual_reg(addr, reg); | ||
195 | if (lsb < 0) | ||
196 | return lsb; | ||
197 | |||
198 | msb = sch56xx_read_virtual_reg(addr, reg + 1); | ||
199 | if (msb < 0) | ||
200 | return msb; | ||
201 | |||
202 | return lsb | (msb << 8); | ||
203 | } | ||
204 | EXPORT_SYMBOL(sch56xx_read_virtual_reg16); | ||
205 | |||
206 | int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, | ||
207 | int high_nibble) | ||
208 | { | ||
209 | int msb, lsn; | ||
210 | |||
211 | /* Read MSB first, this will cause the matching LSN to be latched */ | ||
212 | msb = sch56xx_read_virtual_reg(addr, msb_reg); | ||
213 | if (msb < 0) | ||
214 | return msb; | ||
215 | |||
216 | lsn = sch56xx_read_virtual_reg(addr, lsn_reg); | ||
217 | if (lsn < 0) | ||
218 | return lsn; | ||
219 | |||
220 | if (high_nibble) | ||
221 | return (msb << 4) | (lsn >> 4); | ||
222 | else | ||
223 | return (msb << 4) | (lsn & 0x0f); | ||
224 | } | ||
225 | EXPORT_SYMBOL(sch56xx_read_virtual_reg12); | ||
226 | |||
227 | static int __init sch56xx_find(int sioaddr, unsigned short *address, | ||
228 | const char **name) | ||
229 | { | ||
230 | u8 devid; | ||
231 | int err; | ||
232 | |||
233 | err = superio_enter(sioaddr); | ||
234 | if (err) | ||
235 | return err; | ||
236 | |||
237 | devid = superio_inb(sioaddr, SIO_REG_DEVID); | ||
238 | switch (devid) { | ||
239 | case SIO_SCH5627_ID: | ||
240 | *name = "sch5627"; | ||
241 | break; | ||
242 | case SIO_SCH5636_ID: | ||
243 | *name = "sch5636"; | ||
244 | break; | ||
245 | default: | ||
246 | pr_debug("Unsupported device id: 0x%02x\n", | ||
247 | (unsigned int)devid); | ||
248 | err = -ENODEV; | ||
249 | goto exit; | ||
250 | } | ||
251 | |||
252 | superio_select(sioaddr, SIO_SCH56XX_LD_EM); | ||
253 | |||
254 | if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { | ||
255 | pr_warn("Device not activated\n"); | ||
256 | err = -ENODEV; | ||
257 | goto exit; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * Warning the order of the low / high byte is the other way around | ||
262 | * as on most other superio devices!! | ||
263 | */ | ||
264 | *address = superio_inb(sioaddr, SIO_REG_ADDR) | | ||
265 | superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; | ||
266 | if (*address == 0) { | ||
267 | pr_warn("Base address not set\n"); | ||
268 | err = -ENODEV; | ||
269 | goto exit; | ||
270 | } | ||
271 | |||
272 | exit: | ||
273 | superio_exit(sioaddr); | ||
274 | return err; | ||
275 | } | ||
276 | |||
277 | static int __init sch56xx_device_add(unsigned short address, const char *name) | ||
278 | { | ||
279 | struct resource res = { | ||
280 | .start = address, | ||
281 | .end = address + REGION_LENGTH - 1, | ||
282 | .flags = IORESOURCE_IO, | ||
283 | }; | ||
284 | int err; | ||
285 | |||
286 | sch56xx_pdev = platform_device_alloc(name, address); | ||
287 | if (!sch56xx_pdev) | ||
288 | return -ENOMEM; | ||
289 | |||
290 | res.name = sch56xx_pdev->name; | ||
291 | err = acpi_check_resource_conflict(&res); | ||
292 | if (err) | ||
293 | goto exit_device_put; | ||
294 | |||
295 | err = platform_device_add_resources(sch56xx_pdev, &res, 1); | ||
296 | if (err) { | ||
297 | pr_err("Device resource addition failed\n"); | ||
298 | goto exit_device_put; | ||
299 | } | ||
300 | |||
301 | err = platform_device_add(sch56xx_pdev); | ||
302 | if (err) { | ||
303 | pr_err("Device addition failed\n"); | ||
304 | goto exit_device_put; | ||
305 | } | ||
306 | |||
307 | return 0; | ||
308 | |||
309 | exit_device_put: | ||
310 | platform_device_put(sch56xx_pdev); | ||
311 | |||
312 | return err; | ||
313 | } | ||
314 | |||
315 | static int __init sch56xx_init(void) | ||
316 | { | ||
317 | int err; | ||
318 | unsigned short address; | ||
319 | const char *name; | ||
320 | |||
321 | err = sch56xx_find(0x4e, &address, &name); | ||
322 | if (err) | ||
323 | err = sch56xx_find(0x2e, &address, &name); | ||
324 | if (err) | ||
325 | return err; | ||
326 | |||
327 | return sch56xx_device_add(address, name); | ||
328 | } | ||
329 | |||
330 | static void __exit sch56xx_exit(void) | ||
331 | { | ||
332 | platform_device_unregister(sch56xx_pdev); | ||
333 | } | ||
334 | |||
335 | MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code"); | ||
336 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | ||
337 | MODULE_LICENSE("GPL"); | ||
338 | |||
339 | module_init(sch56xx_init); | ||
340 | module_exit(sch56xx_exit); | ||
diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h new file mode 100644 index 000000000000..d5eaf3b9ebf5 --- /dev/null +++ b/drivers/hwmon/sch56xx-common.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /*************************************************************************** | ||
2 | * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> * | ||
3 | * * | ||
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 * | ||
6 | * the Free Software Foundation; either version 2 of the License, or * | ||
7 | * (at your option) any later version. * | ||
8 | * * | ||
9 | * This program is distributed in the hope that it will be useful, * | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | ||
12 | * GNU General Public License for more details. * | ||
13 | * * | ||
14 | * You should have received a copy of the GNU General Public License * | ||
15 | * along with this program; if not, write to the * | ||
16 | * Free Software Foundation, Inc., * | ||
17 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | ||
18 | ***************************************************************************/ | ||
19 | |||
20 | int sch56xx_read_virtual_reg(u16 addr, u16 reg); | ||
21 | int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val); | ||
22 | int sch56xx_read_virtual_reg16(u16 addr, u16 reg); | ||
23 | int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, | ||
24 | int high_nibble); | ||
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index cf4330b352ef..7d231cf5d2ce 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c | |||
@@ -671,7 +671,7 @@ static ssize_t sht15_show_status(struct device *dev, | |||
671 | * @buf: sysfs buffer to read the new heater state from. | 671 | * @buf: sysfs buffer to read the new heater state from. |
672 | * @count: length of the data. | 672 | * @count: length of the data. |
673 | * | 673 | * |
674 | * Will be called on read access to heater_enable sysfs attribute. | 674 | * Will be called on write access to heater_enable sysfs attribute. |
675 | * Returns number of bytes actually decoded, negative errno on error. | 675 | * Returns number of bytes actually decoded, negative errno on error. |
676 | */ | 676 | */ |
677 | static ssize_t sht15_store_heater(struct device *dev, | 677 | static ssize_t sht15_store_heater(struct device *dev, |
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index 0d18de424c66..8eac67d769fa 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/init.h> | 27 | #include <linux/init.h> |
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/hwmon.h> | 29 | #include <linux/hwmon.h> |
30 | #include <linux/hwmon-vid.h> | ||
30 | #include <linux/sysfs.h> | 31 | #include <linux/sysfs.h> |
31 | #include <linux/hwmon-sysfs.h> | 32 | #include <linux/hwmon-sysfs.h> |
32 | #include <linux/err.h> | 33 | #include <linux/err.h> |
@@ -48,8 +49,10 @@ enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME }; | |||
48 | struct via_cputemp_data { | 49 | struct via_cputemp_data { |
49 | struct device *hwmon_dev; | 50 | struct device *hwmon_dev; |
50 | const char *name; | 51 | const char *name; |
52 | u8 vrm; | ||
51 | u32 id; | 53 | u32 id; |
52 | u32 msr; | 54 | u32 msr_temp; |
55 | u32 msr_vid; | ||
53 | }; | 56 | }; |
54 | 57 | ||
55 | /* | 58 | /* |
@@ -77,13 +80,27 @@ static ssize_t show_temp(struct device *dev, | |||
77 | u32 eax, edx; | 80 | u32 eax, edx; |
78 | int err; | 81 | int err; |
79 | 82 | ||
80 | err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); | 83 | err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); |
81 | if (err) | 84 | if (err) |
82 | return -EAGAIN; | 85 | return -EAGAIN; |
83 | 86 | ||
84 | return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000); | 87 | return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000); |
85 | } | 88 | } |
86 | 89 | ||
90 | static ssize_t show_cpu_vid(struct device *dev, | ||
91 | struct device_attribute *devattr, char *buf) | ||
92 | { | ||
93 | struct via_cputemp_data *data = dev_get_drvdata(dev); | ||
94 | u32 eax, edx; | ||
95 | int err; | ||
96 | |||
97 | err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx); | ||
98 | if (err) | ||
99 | return -EAGAIN; | ||
100 | |||
101 | return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm)); | ||
102 | } | ||
103 | |||
87 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, | 104 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, |
88 | SHOW_TEMP); | 105 | SHOW_TEMP); |
89 | static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); | 106 | static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); |
@@ -100,6 +117,9 @@ static const struct attribute_group via_cputemp_group = { | |||
100 | .attrs = via_cputemp_attributes, | 117 | .attrs = via_cputemp_attributes, |
101 | }; | 118 | }; |
102 | 119 | ||
120 | /* Optional attributes */ | ||
121 | static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_cpu_vid, NULL); | ||
122 | |||
103 | static int __devinit via_cputemp_probe(struct platform_device *pdev) | 123 | static int __devinit via_cputemp_probe(struct platform_device *pdev) |
104 | { | 124 | { |
105 | struct via_cputemp_data *data; | 125 | struct via_cputemp_data *data; |
@@ -122,11 +142,12 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) | |||
122 | /* C7 A */ | 142 | /* C7 A */ |
123 | case 0xD: | 143 | case 0xD: |
124 | /* C7 D */ | 144 | /* C7 D */ |
125 | data->msr = 0x1169; | 145 | data->msr_temp = 0x1169; |
146 | data->msr_vid = 0x198; | ||
126 | break; | 147 | break; |
127 | case 0xF: | 148 | case 0xF: |
128 | /* Nano */ | 149 | /* Nano */ |
129 | data->msr = 0x1423; | 150 | data->msr_temp = 0x1423; |
130 | break; | 151 | break; |
131 | default: | 152 | default: |
132 | err = -ENODEV; | 153 | err = -ENODEV; |
@@ -134,7 +155,7 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) | |||
134 | } | 155 | } |
135 | 156 | ||
136 | /* test if we can access the TEMPERATURE MSR */ | 157 | /* test if we can access the TEMPERATURE MSR */ |
137 | err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); | 158 | err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); |
138 | if (err) { | 159 | if (err) { |
139 | dev_err(&pdev->dev, | 160 | dev_err(&pdev->dev, |
140 | "Unable to access TEMPERATURE MSR, giving up\n"); | 161 | "Unable to access TEMPERATURE MSR, giving up\n"); |
@@ -147,6 +168,15 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) | |||
147 | if (err) | 168 | if (err) |
148 | goto exit_free; | 169 | goto exit_free; |
149 | 170 | ||
171 | if (data->msr_vid) | ||
172 | data->vrm = vid_which_vrm(); | ||
173 | |||
174 | if (data->vrm) { | ||
175 | err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid); | ||
176 | if (err) | ||
177 | goto exit_remove; | ||
178 | } | ||
179 | |||
150 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | 180 | data->hwmon_dev = hwmon_device_register(&pdev->dev); |
151 | if (IS_ERR(data->hwmon_dev)) { | 181 | if (IS_ERR(data->hwmon_dev)) { |
152 | err = PTR_ERR(data->hwmon_dev); | 182 | err = PTR_ERR(data->hwmon_dev); |
@@ -158,6 +188,8 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) | |||
158 | return 0; | 188 | return 0; |
159 | 189 | ||
160 | exit_remove: | 190 | exit_remove: |
191 | if (data->vrm) | ||
192 | device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); | ||
161 | sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); | 193 | sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); |
162 | exit_free: | 194 | exit_free: |
163 | platform_set_drvdata(pdev, NULL); | 195 | platform_set_drvdata(pdev, NULL); |
@@ -171,6 +203,8 @@ static int __devexit via_cputemp_remove(struct platform_device *pdev) | |||
171 | struct via_cputemp_data *data = platform_get_drvdata(pdev); | 203 | struct via_cputemp_data *data = platform_get_drvdata(pdev); |
172 | 204 | ||
173 | hwmon_device_unregister(data->hwmon_dev); | 205 | hwmon_device_unregister(data->hwmon_dev); |
206 | if (data->vrm) | ||
207 | device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); | ||
174 | sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); | 208 | sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); |
175 | platform_set_drvdata(pdev, NULL); | 209 | platform_set_drvdata(pdev, NULL); |
176 | kfree(data); | 210 | kfree(data); |