diff options
| -rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 10 | ||||
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 122 |
2 files changed, 130 insertions, 2 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index f2ff638cce8d..0d5e91379ae8 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
| @@ -1269,7 +1269,7 @@ Fan control and monitoring: fan speed, fan enable/disable | |||
| 1269 | 1269 | ||
| 1270 | procfs: /proc/acpi/ibm/fan | 1270 | procfs: /proc/acpi/ibm/fan |
| 1271 | sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1, | 1271 | sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1, |
| 1272 | pwm1_enable | 1272 | pwm1_enable, fan2_input |
| 1273 | sysfs hwmon driver attributes: fan_watchdog | 1273 | sysfs hwmon driver attributes: fan_watchdog |
| 1274 | 1274 | ||
| 1275 | NOTE NOTE NOTE: fan control operations are disabled by default for | 1275 | NOTE NOTE NOTE: fan control operations are disabled by default for |
| @@ -1282,6 +1282,9 @@ from the hardware registers of the embedded controller. This is known | |||
| 1282 | to work on later R, T, X and Z series ThinkPads but may show a bogus | 1282 | to work on later R, T, X and Z series ThinkPads but may show a bogus |
| 1283 | value on other models. | 1283 | value on other models. |
| 1284 | 1284 | ||
| 1285 | Some Lenovo ThinkPads support a secondary fan. This fan cannot be | ||
| 1286 | controlled separately, it shares the main fan control. | ||
| 1287 | |||
| 1285 | Fan levels: | 1288 | Fan levels: |
| 1286 | 1289 | ||
| 1287 | Most ThinkPad fans work in "levels" at the firmware interface. Level 0 | 1290 | Most ThinkPad fans work in "levels" at the firmware interface. Level 0 |
| @@ -1412,6 +1415,11 @@ hwmon device attribute fan1_input: | |||
| 1412 | which can take up to two minutes. May return rubbish on older | 1415 | which can take up to two minutes. May return rubbish on older |
| 1413 | ThinkPads. | 1416 | ThinkPads. |
| 1414 | 1417 | ||
| 1418 | hwmon device attribute fan2_input: | ||
| 1419 | Fan tachometer reading, in RPM, for the secondary fan. | ||
| 1420 | Available only on some ThinkPads. If the secondary fan is | ||
| 1421 | not installed, will always read 0. | ||
| 1422 | |||
| 1415 | hwmon driver attribute fan_watchdog: | 1423 | hwmon driver attribute fan_watchdog: |
| 1416 | Fan safety watchdog timer interval, in seconds. Minimum is | 1424 | Fan safety watchdog timer interval, in seconds. Minimum is |
| 1417 | 1 second, maximum is 120 seconds. 0 disables the watchdog. | 1425 | 1 second, maximum is 120 seconds. 0 disables the watchdog. |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index c8d74dbacbbd..27ca676a7092 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
| @@ -264,6 +264,7 @@ static struct { | |||
| 264 | u32 wan:1; | 264 | u32 wan:1; |
| 265 | u32 uwb:1; | 265 | u32 uwb:1; |
| 266 | u32 fan_ctrl_status_undef:1; | 266 | u32 fan_ctrl_status_undef:1; |
| 267 | u32 second_fan:1; | ||
| 267 | u32 beep_needs_two_args:1; | 268 | u32 beep_needs_two_args:1; |
| 268 | u32 input_device_registered:1; | 269 | u32 input_device_registered:1; |
| 269 | u32 platform_drv_registered:1; | 270 | u32 platform_drv_registered:1; |
| @@ -6298,6 +6299,21 @@ static struct ibm_struct volume_driver_data = { | |||
| 6298 | * For firmware bugs, refer to: | 6299 | * For firmware bugs, refer to: |
| 6299 | * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues | 6300 | * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues |
| 6300 | * | 6301 | * |
| 6302 | * ---- | ||
| 6303 | * | ||
| 6304 | * ThinkPad EC register 0x31 bit 0 (only on select models) | ||
| 6305 | * | ||
| 6306 | * When bit 0 of EC register 0x31 is zero, the tachometer registers | ||
| 6307 | * show the speed of the main fan. When bit 0 of EC register 0x31 | ||
| 6308 | * is one, the tachometer registers show the speed of the auxiliary | ||
| 6309 | * fan. | ||
| 6310 | * | ||
| 6311 | * Fan control seems to affect both fans, regardless of the state | ||
| 6312 | * of this bit. | ||
| 6313 | * | ||
| 6314 | * So far, only the firmware for the X60/X61 non-tablet versions | ||
| 6315 | * seem to support this (firmware TP-7M). | ||
| 6316 | * | ||
| 6301 | * TPACPI_FAN_WR_ACPI_FANS: | 6317 | * TPACPI_FAN_WR_ACPI_FANS: |
| 6302 | * ThinkPad X31, X40, X41. Not available in the X60. | 6318 | * ThinkPad X31, X40, X41. Not available in the X60. |
| 6303 | * | 6319 | * |
| @@ -6324,6 +6340,8 @@ enum { /* Fan control constants */ | |||
| 6324 | fan_status_offset = 0x2f, /* EC register 0x2f */ | 6340 | fan_status_offset = 0x2f, /* EC register 0x2f */ |
| 6325 | fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) | 6341 | fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) |
| 6326 | * 0x84 must be read before 0x85 */ | 6342 | * 0x84 must be read before 0x85 */ |
| 6343 | fan_select_offset = 0x31, /* EC register 0x31 (Firmware 7M) | ||
| 6344 | bit 0 selects which fan is active */ | ||
| 6327 | 6345 | ||
| 6328 | TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ | 6346 | TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ |
| 6329 | TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ | 6347 | TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ |
| @@ -6417,6 +6435,38 @@ static void fan_quirk1_handle(u8 *fan_status) | |||
| 6417 | } | 6435 | } |
| 6418 | } | 6436 | } |
| 6419 | 6437 | ||
| 6438 | /* Select main fan on X60/X61, NOOP on others */ | ||
| 6439 | static bool fan_select_fan1(void) | ||
| 6440 | { | ||
| 6441 | if (tp_features.second_fan) { | ||
| 6442 | u8 val; | ||
| 6443 | |||
| 6444 | if (ec_read(fan_select_offset, &val) < 0) | ||
| 6445 | return false; | ||
| 6446 | val &= 0xFEU; | ||
| 6447 | if (ec_write(fan_select_offset, val) < 0) | ||
| 6448 | return false; | ||
| 6449 | } | ||
| 6450 | return true; | ||
| 6451 | } | ||
| 6452 | |||
| 6453 | /* Select secondary fan on X60/X61 */ | ||
| 6454 | static bool fan_select_fan2(void) | ||
| 6455 | { | ||
| 6456 | u8 val; | ||
| 6457 | |||
| 6458 | if (!tp_features.second_fan) | ||
| 6459 | return false; | ||
| 6460 | |||
| 6461 | if (ec_read(fan_select_offset, &val) < 0) | ||
| 6462 | return false; | ||
| 6463 | val |= 0x01U; | ||
| 6464 | if (ec_write(fan_select_offset, val) < 0) | ||
| 6465 | return false; | ||
| 6466 | |||
| 6467 | return true; | ||
| 6468 | } | ||
| 6469 | |||
| 6420 | /* | 6470 | /* |
| 6421 | * Call with fan_mutex held | 6471 | * Call with fan_mutex held |
| 6422 | */ | 6472 | */ |
| @@ -6494,6 +6544,8 @@ static int fan_get_speed(unsigned int *speed) | |||
| 6494 | switch (fan_status_access_mode) { | 6544 | switch (fan_status_access_mode) { |
| 6495 | case TPACPI_FAN_RD_TPEC: | 6545 | case TPACPI_FAN_RD_TPEC: |
| 6496 | /* all except 570, 600e/x, 770e, 770x */ | 6546 | /* all except 570, 600e/x, 770e, 770x */ |
| 6547 | if (unlikely(!fan_select_fan1())) | ||
| 6548 | return -EIO; | ||
| 6497 | if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || | 6549 | if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || |
| 6498 | !acpi_ec_read(fan_rpm_offset + 1, &hi))) | 6550 | !acpi_ec_read(fan_rpm_offset + 1, &hi))) |
| 6499 | return -EIO; | 6551 | return -EIO; |
| @@ -6510,6 +6562,34 @@ static int fan_get_speed(unsigned int *speed) | |||
| 6510 | return 0; | 6562 | return 0; |
| 6511 | } | 6563 | } |
| 6512 | 6564 | ||
| 6565 | static int fan2_get_speed(unsigned int *speed) | ||
| 6566 | { | ||
| 6567 | u8 hi, lo; | ||
| 6568 | bool rc; | ||
| 6569 | |||
| 6570 | switch (fan_status_access_mode) { | ||
| 6571 | case TPACPI_FAN_RD_TPEC: | ||
| 6572 | /* all except 570, 600e/x, 770e, 770x */ | ||
| 6573 | if (unlikely(!fan_select_fan2())) | ||
| 6574 | return -EIO; | ||
| 6575 | rc = !acpi_ec_read(fan_rpm_offset, &lo) || | ||
| 6576 | !acpi_ec_read(fan_rpm_offset + 1, &hi); | ||
| 6577 | fan_select_fan1(); /* play it safe */ | ||
| 6578 | if (rc) | ||
| 6579 | return -EIO; | ||
| 6580 | |||
| 6581 | if (likely(speed)) | ||
| 6582 | *speed = (hi << 8) | lo; | ||
| 6583 | |||
| 6584 | break; | ||
| 6585 | |||
| 6586 | default: | ||
| 6587 | return -ENXIO; | ||
| 6588 | } | ||
| 6589 | |||
| 6590 | return 0; | ||
| 6591 | } | ||
| 6592 | |||
| 6513 | static int fan_set_level(int level) | 6593 | static int fan_set_level(int level) |
| 6514 | { | 6594 | { |
| 6515 | if (!fan_control_allowed) | 6595 | if (!fan_control_allowed) |
| @@ -6915,6 +6995,25 @@ static struct device_attribute dev_attr_fan_fan1_input = | |||
| 6915 | __ATTR(fan1_input, S_IRUGO, | 6995 | __ATTR(fan1_input, S_IRUGO, |
| 6916 | fan_fan1_input_show, NULL); | 6996 | fan_fan1_input_show, NULL); |
| 6917 | 6997 | ||
| 6998 | /* sysfs fan fan2_input ------------------------------------------------ */ | ||
| 6999 | static ssize_t fan_fan2_input_show(struct device *dev, | ||
| 7000 | struct device_attribute *attr, | ||
| 7001 | char *buf) | ||
| 7002 | { | ||
| 7003 | int res; | ||
| 7004 | unsigned int speed; | ||
| 7005 | |||
| 7006 | res = fan2_get_speed(&speed); | ||
| 7007 | if (res < 0) | ||
| 7008 | return res; | ||
| 7009 | |||
| 7010 | return snprintf(buf, PAGE_SIZE, "%u\n", speed); | ||
| 7011 | } | ||
| 7012 | |||
| 7013 | static struct device_attribute dev_attr_fan_fan2_input = | ||
| 7014 | __ATTR(fan2_input, S_IRUGO, | ||
| 7015 | fan_fan2_input_show, NULL); | ||
| 7016 | |||
| 6918 | /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ | 7017 | /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ |
| 6919 | static ssize_t fan_fan_watchdog_show(struct device_driver *drv, | 7018 | static ssize_t fan_fan_watchdog_show(struct device_driver *drv, |
| 6920 | char *buf) | 7019 | char *buf) |
| @@ -6948,6 +7047,7 @@ static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, | |||
| 6948 | static struct attribute *fan_attributes[] = { | 7047 | static struct attribute *fan_attributes[] = { |
| 6949 | &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, | 7048 | &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, |
| 6950 | &dev_attr_fan_fan1_input.attr, | 7049 | &dev_attr_fan_fan1_input.attr, |
| 7050 | NULL, /* for fan2_input */ | ||
| 6951 | NULL | 7051 | NULL |
| 6952 | }; | 7052 | }; |
| 6953 | 7053 | ||
| @@ -6955,7 +7055,8 @@ static const struct attribute_group fan_attr_group = { | |||
| 6955 | .attrs = fan_attributes, | 7055 | .attrs = fan_attributes, |
| 6956 | }; | 7056 | }; |
| 6957 | 7057 | ||
| 6958 | #define TPACPI_FAN_Q1 0x0001 | 7058 | #define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */ |
| 7059 | #define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */ | ||
| 6959 | 7060 | ||
| 6960 | #define TPACPI_FAN_QI(__id1, __id2, __quirks) \ | 7061 | #define TPACPI_FAN_QI(__id1, __id2, __quirks) \ |
| 6961 | { .vendor = PCI_VENDOR_ID_IBM, \ | 7062 | { .vendor = PCI_VENDOR_ID_IBM, \ |
| @@ -6963,13 +7064,21 @@ static const struct attribute_group fan_attr_group = { | |||
| 6963 | .ec = TPID(__id1, __id2), \ | 7064 | .ec = TPID(__id1, __id2), \ |
| 6964 | .quirks = __quirks } | 7065 | .quirks = __quirks } |
| 6965 | 7066 | ||
| 7067 | #define TPACPI_FAN_QL(__id1, __id2, __quirks) \ | ||
| 7068 | { .vendor = PCI_VENDOR_ID_LENOVO, \ | ||
| 7069 | .bios = TPACPI_MATCH_ANY, \ | ||
| 7070 | .ec = TPID(__id1, __id2), \ | ||
| 7071 | .quirks = __quirks } | ||
| 7072 | |||
| 6966 | static const struct tpacpi_quirk fan_quirk_table[] __initconst = { | 7073 | static const struct tpacpi_quirk fan_quirk_table[] __initconst = { |
| 6967 | TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1), | 7074 | TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1), |
| 6968 | TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1), | 7075 | TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1), |
| 6969 | TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1), | 7076 | TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1), |
| 6970 | TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1), | 7077 | TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1), |
| 7078 | TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN), | ||
| 6971 | }; | 7079 | }; |
| 6972 | 7080 | ||
| 7081 | #undef TPACPI_FAN_QL | ||
| 6973 | #undef TPACPI_FAN_QI | 7082 | #undef TPACPI_FAN_QI |
| 6974 | 7083 | ||
| 6975 | static int __init fan_init(struct ibm_init_struct *iibm) | 7084 | static int __init fan_init(struct ibm_init_struct *iibm) |
| @@ -6986,6 +7095,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
| 6986 | fan_control_commands = 0; | 7095 | fan_control_commands = 0; |
| 6987 | fan_watchdog_maxinterval = 0; | 7096 | fan_watchdog_maxinterval = 0; |
| 6988 | tp_features.fan_ctrl_status_undef = 0; | 7097 | tp_features.fan_ctrl_status_undef = 0; |
| 7098 | tp_features.second_fan = 0; | ||
| 6989 | fan_control_desired_level = 7; | 7099 | fan_control_desired_level = 7; |
| 6990 | 7100 | ||
| 6991 | TPACPI_ACPIHANDLE_INIT(fans); | 7101 | TPACPI_ACPIHANDLE_INIT(fans); |
| @@ -7006,6 +7116,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
| 7006 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; | 7116 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; |
| 7007 | if (quirks & TPACPI_FAN_Q1) | 7117 | if (quirks & TPACPI_FAN_Q1) |
| 7008 | fan_quirk1_setup(); | 7118 | fan_quirk1_setup(); |
| 7119 | if (quirks & TPACPI_FAN_2FAN) { | ||
| 7120 | tp_features.second_fan = 1; | ||
| 7121 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, | ||
| 7122 | "secondary fan support enabled\n"); | ||
| 7123 | } | ||
| 7009 | } else { | 7124 | } else { |
| 7010 | printk(TPACPI_ERR | 7125 | printk(TPACPI_ERR |
| 7011 | "ThinkPad ACPI EC access misbehaving, " | 7126 | "ThinkPad ACPI EC access misbehaving, " |
| @@ -7061,6 +7176,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
| 7061 | 7176 | ||
| 7062 | if (fan_status_access_mode != TPACPI_FAN_NONE || | 7177 | if (fan_status_access_mode != TPACPI_FAN_NONE || |
| 7063 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { | 7178 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { |
| 7179 | if (tp_features.second_fan) { | ||
| 7180 | /* attach second fan tachometer */ | ||
| 7181 | fan_attributes[ARRAY_SIZE(fan_attributes)-2] = | ||
| 7182 | &dev_attr_fan_fan2_input.attr; | ||
| 7183 | } | ||
| 7064 | rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, | 7184 | rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, |
| 7065 | &fan_attr_group); | 7185 | &fan_attr_group); |
| 7066 | if (rc < 0) | 7186 | if (rc < 0) |
