diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index f269ca1ff7718..8f70539208843 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -7749,6 +7749,28 @@ static struct ibm_struct volume_driver_data = { * EC 0x2f (HFSP) might be available *for reading*, but do not use * it for writing. * + * TPACPI_FAN_RD_ACPI_FANG: + * ACPI FANG method: returns fan control register + * + * Takes one parameter which is 0x8100 plus the offset to EC memory + * address 0xf500 and returns the byte at this address. + * + * 0xf500: + * When the value is less than 9 automatic mode is enabled + * 0xf502: + * Contains the current fan speed from 0-100% + * 0xf506: + * Bit 7 has to be set in order to enable manual control by + * writing a value >= 9 to 0xf500 + * + * TPACPI_FAN_WR_ACPI_FANW: + * ACPI FANW method: sets fan control registers + * + * Takes 0x8100 plus the offset to EC memory address 0xf500 and the + * value to be written there as parameters. + * + * see TPACPI_FAN_RD_ACPI_FANG + * * TPACPI_FAN_WR_TPEC: * ThinkPad EC register 0x2f (HFSP): fan control loop mode * Supported on almost all ThinkPads @@ -7882,6 +7904,7 @@ enum { /* Fan control constants */ enum fan_status_access_mode { TPACPI_FAN_NONE = 0, /* No fan status or control */ TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + TPACPI_FAN_RD_ACPI_FANG, /* Use ACPI FANG */ TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ TPACPI_FAN_RD_TPEC_NS, /* Use non-standard ACPI EC regs (eg: L13 Yoga gen2 etc.) */ }; @@ -7889,6 +7912,7 @@ enum fan_status_access_mode { enum fan_control_access_mode { TPACPI_FAN_WR_NONE = 0, /* No fan control */ TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + TPACPI_FAN_WR_ACPI_FANW, /* Use ACPI FANW */ TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ }; @@ -7922,9 +7946,13 @@ TPACPI_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */ "\\FSPD", /* 600e/x, 770e, 770x */ ); /* all others */ +TPACPI_HANDLE(fang, ec, "FANG", /* E531 */ + ); /* all others */ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ "JFNS", /* 770x-JL */ ); /* all others */ +TPACPI_HANDLE(fanw, ec, "FANW", /* E531 */ + ); /* all others */ /* * Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the @@ -8031,6 +8059,23 @@ static int fan_get_status(u8 *status) break; } + case TPACPI_FAN_RD_ACPI_FANG: { + /* E531 */ + int mode, speed; + + if (unlikely(!acpi_evalf(fang_handle, &mode, NULL, "dd", 0x8100))) + return -EIO; + if (unlikely(!acpi_evalf(fang_handle, &speed, NULL, "dd", 0x8102))) + return -EIO; + + if (likely(status)) { + *status = speed * 7 / 100; + if (mode < 9) + *status |= TP_EC_FAN_AUTO; + } + + break; + } case TPACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ if (unlikely(!acpi_ec_read(fan_status_offset, &s))) @@ -8145,6 +8190,17 @@ static int fan2_get_speed(unsigned int *speed) if (speed) *speed = lo ? FAN_RPM_CAL_CONST / lo : 0; break; + case TPACPI_FAN_RD_ACPI_FANG: { + /* E531 */ + int speed_tmp; + + if (unlikely(!acpi_evalf(fang_handle, &speed_tmp, NULL, "dd", 0x8102))) + return -EIO; + + if (likely(speed)) + *speed = speed_tmp * 65535 / 100; + break; + } default: return -ENXIO; @@ -8204,6 +8260,32 @@ static int fan_set_level(int level) tp_features.fan_ctrl_status_undef = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!(level & TP_EC_FAN_AUTO) && (level < 0 || level > 7)) + return -EINVAL; + if (level & TP_EC_FAN_FULLSPEED) + return -EINVAL; + + if (level & TP_EC_FAN_AUTO) { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) { + return -EIO; + } + } else { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + return -EIO; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, level * 100 / 7)) { + return -EIO; + } + } + break; + default: return -ENXIO; } @@ -8282,6 +8364,19 @@ static int fan_set_enable(void) rc = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) { + rc = -EIO; + break; + } + + rc = 0; + break; + default: rc = -ENXIO; } @@ -8324,6 +8419,22 @@ static int fan_set_disable(void) fan_control_desired_level = 0; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, 0x00)) { + rc = -EIO; + break; + } + rc = 0; + break; + default: rc = -ENXIO; } @@ -8357,6 +8468,23 @@ static int fan_set_speed(int speed) rc = -EINVAL; break; + case TPACPI_FAN_WR_ACPI_FANW: + if (speed >= 0 && speed <= 65535) { + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) { + rc = -EIO; + break; + } + if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", + 0x8102, speed * 100 / 65535)) + rc = -EIO; + } else + rc = -EINVAL; + break; + default: rc = -ENXIO; } @@ -8699,6 +8827,10 @@ static int __init fan_init(struct ibm_init_struct *iibm) TPACPI_ACPIHANDLE_INIT(gfan); TPACPI_ACPIHANDLE_INIT(sfan); } + if (tpacpi_is_lenovo()) { + TPACPI_ACPIHANDLE_INIT(fang); + TPACPI_ACPIHANDLE_INIT(fanw); + } quirks = tpacpi_check_quirks(fan_quirk_table, ARRAY_SIZE(fan_quirk_table)); @@ -8718,6 +8850,9 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; + } else if (fang_handle) { + /* E531 */ + fan_status_access_mode = TPACPI_FAN_RD_ACPI_FANG; } else { /* all other ThinkPads: note that even old-style * ThinkPad ECs supports the fan control register */ @@ -8764,6 +8899,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN; fan_control_commands |= TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE; + } else if (fanw_handle) { + /* E531 */ + fan_control_access_mode = TPACPI_FAN_WR_ACPI_FANW; + fan_control_commands |= + TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_SPEED | TPACPI_FAN_CMD_ENABLE; } else { if (!gfan_handle) { /* gfan without sfan means no fan control */ @@ -8915,6 +9055,7 @@ static int fan_read(struct seq_file *m) case TPACPI_FAN_RD_TPEC_NS: case TPACPI_FAN_RD_TPEC: + case TPACPI_FAN_RD_ACPI_FANG: /* all except 570, 600e/x, 770e, 770x */ rc = fan_get_status_safe(&status); if (rc) @@ -8935,7 +9076,7 @@ static int fan_read(struct seq_file *m) * No other levels settings available */ seq_printf(m, "level:\t\t%s\n", status & FAN_NS_CTRL ? "unknown" : "auto"); - } else { + } else if (fan_status_access_mode == TPACPI_FAN_RD_TPEC) { if (status & TP_EC_FAN_FULLSPEED) /* Disengaged mode takes precedence */ seq_printf(m, "level:\t\tdisengaged\n");