Skip to content

Commit

Permalink
platform/x86: thinkpad_acpi: Add Thinkpad Edge E531 fan support
Browse files Browse the repository at this point in the history
Fan control on the E531 is done using the ACPI methods FANG and
FANW. The correct parameters and register values were found by
analyzing EC firmware as well as DSDT. This has been tested on
my Thinkpad Edge E531 (6885CTO, BIOS HEET52WW 1.33).

Signed-off-by: Matthias Fetzer <kontakt@matthias-fetzer.de>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20240816141228.134529-1-kontakt@matthias-fetzer.de
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
  • Loading branch information
Matthias Fetzer authored and Hans de Goede committed Aug 19, 2024
1 parent 3900c6a commit 57d0557
Showing 1 changed file with 142 additions and 1 deletion.
143 changes: 142 additions & 1 deletion drivers/platform/x86/thinkpad_acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -7882,13 +7904,15 @@ 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.) */
};

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 */
};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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));
Expand All @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -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");
Expand Down

0 comments on commit 57d0557

Please sign in to comment.