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 | |
| 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
| -rw-r--r-- | Documentation/hwmon/it87 | 3 | ||||
| -rw-r--r-- | Documentation/hwmon/lm78 | 3 | ||||
| -rw-r--r-- | Documentation/hwmon/sch5636 | 31 | ||||
| -rw-r--r-- | MAINTAINERS | 7 | ||||
| -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 |
16 files changed, 1285 insertions, 468 deletions
diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 38425f0f2645..6f496a586732 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 | |||
| @@ -76,7 +76,8 @@ IT8718F, IT8720F, IT8721F, IT8726F, IT8758E and SiS950 chips. | |||
| 76 | These chips are 'Super I/O chips', supporting floppy disks, infrared ports, | 76 | These chips are 'Super I/O chips', supporting floppy disks, infrared ports, |
| 77 | joysticks and other miscellaneous stuff. For hardware monitoring, they | 77 | joysticks and other miscellaneous stuff. For hardware monitoring, they |
| 78 | include an 'environment controller' with 3 temperature sensors, 3 fan | 78 | include an 'environment controller' with 3 temperature sensors, 3 fan |
| 79 | rotation speed sensors, 8 voltage sensors, and associated alarms. | 79 | rotation speed sensors, 8 voltage sensors, associated alarms, and chassis |
| 80 | intrusion detection. | ||
| 80 | 81 | ||
| 81 | The IT8712F and IT8716F additionally feature VID inputs, used to report | 82 | The IT8712F and IT8716F additionally feature VID inputs, used to report |
| 82 | the Vcore voltage of the processor. The early IT8712F have 5 VID pins, | 83 | the Vcore voltage of the processor. The early IT8712F have 5 VID pins, |
diff --git a/Documentation/hwmon/lm78 b/Documentation/hwmon/lm78 index 60932e26abaa..2bdc881a0c12 100644 --- a/Documentation/hwmon/lm78 +++ b/Documentation/hwmon/lm78 | |||
| @@ -13,7 +13,8 @@ Supported chips: | |||
| 13 | Datasheet: Publicly available at the National Semiconductor website | 13 | Datasheet: Publicly available at the National Semiconductor website |
| 14 | http://www.national.com/ | 14 | http://www.national.com/ |
| 15 | 15 | ||
| 16 | Author: Frodo Looijaard <frodol@dds.nl> | 16 | Authors: Frodo Looijaard <frodol@dds.nl> |
| 17 | Jean Delvare <khali@linux-fr.org> | ||
| 17 | 18 | ||
| 18 | Description | 19 | Description |
| 19 | ----------- | 20 | ----------- |
diff --git a/Documentation/hwmon/sch5636 b/Documentation/hwmon/sch5636 new file mode 100644 index 000000000000..f83bd1c260f0 --- /dev/null +++ b/Documentation/hwmon/sch5636 | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | Kernel driver sch5636 | ||
| 2 | ===================== | ||
| 3 | |||
| 4 | Supported chips: | ||
| 5 | * SMSC SCH5636 | ||
| 6 | Prefix: 'sch5636' | ||
| 7 | Addresses scanned: none, address read from Super I/O config space | ||
| 8 | |||
| 9 | Author: Hans de Goede <hdegoede@redhat.com> | ||
| 10 | |||
| 11 | |||
| 12 | Description | ||
| 13 | ----------- | ||
| 14 | |||
| 15 | SMSC SCH5636 Super I/O chips include an embedded microcontroller for | ||
| 16 | hardware monitoring solutions, allowing motherboard manufacturers to create | ||
| 17 | their own custom hwmon solution based upon the SCH5636. | ||
| 18 | |||
| 19 | Currently the sch5636 driver only supports the Fujitsu Theseus SCH5636 based | ||
| 20 | hwmon solution. The sch5636 driver runs a sanity check on loading to ensure | ||
| 21 | it is dealing with a Fujitsu Theseus and not with another custom SCH5636 based | ||
| 22 | hwmon solution. | ||
| 23 | |||
| 24 | The Fujitsu Theseus can monitor up to 5 voltages, 8 fans and 16 | ||
| 25 | temperatures. Note that the driver detects how many fan headers / | ||
| 26 | temperature sensors are actually implemented on the motherboard, so you will | ||
| 27 | likely see fewer temperature and fan inputs. | ||
| 28 | |||
| 29 | An application note describing the Theseus' registers, as well as an | ||
| 30 | application note describing the protocol for communicating with the | ||
| 31 | microcontroller is available upon request. Please mail me if you want a copy. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index efe0a4b07159..91e5cc7db8c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -3960,6 +3960,13 @@ L: lm-sensors@lm-sensors.org | |||
| 3960 | S: Maintained | 3960 | S: Maintained |
| 3961 | F: drivers/hwmon/lm73.c | 3961 | F: drivers/hwmon/lm73.c |
| 3962 | 3962 | ||
| 3963 | LM78 HARDWARE MONITOR DRIVER | ||
| 3964 | M: Jean Delvare <khali@linux-fr.org> | ||
| 3965 | L: lm-sensors@lm-sensors.org | ||
| 3966 | S: Maintained | ||
| 3967 | F: Documentation/hwmon/lm78 | ||
| 3968 | F: drivers/hwmon/lm78.c | ||
| 3969 | |||
| 3963 | LM83 HARDWARE MONITOR DRIVER | 3970 | LM83 HARDWARE MONITOR DRIVER |
| 3964 | M: Jean Delvare <khali@linux-fr.org> | 3971 | M: Jean Delvare <khali@linux-fr.org> |
| 3965 | L: lm-sensors@lm-sensors.org | 3972 | L: lm-sensors@lm-sensors.org |
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); |
