Skip to content

Commit 57d0557

Browse files
Matthias Fetzerjwrdegoede
authored andcommitted
platform/x86: thinkpad_acpi: Add Thinkpad Edge E531 fan support
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 <[email protected]> Reviewed-by: Ilpo Järvinen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Hans de Goede <[email protected]> Signed-off-by: Hans de Goede <[email protected]>
1 parent 3900c6a commit 57d0557

File tree

1 file changed

+142
-1
lines changed

1 file changed

+142
-1
lines changed

drivers/platform/x86/thinkpad_acpi.c

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7749,6 +7749,28 @@ static struct ibm_struct volume_driver_data = {
77497749
* EC 0x2f (HFSP) might be available *for reading*, but do not use
77507750
* it for writing.
77517751
*
7752+
* TPACPI_FAN_RD_ACPI_FANG:
7753+
* ACPI FANG method: returns fan control register
7754+
*
7755+
* Takes one parameter which is 0x8100 plus the offset to EC memory
7756+
* address 0xf500 and returns the byte at this address.
7757+
*
7758+
* 0xf500:
7759+
* When the value is less than 9 automatic mode is enabled
7760+
* 0xf502:
7761+
* Contains the current fan speed from 0-100%
7762+
* 0xf506:
7763+
* Bit 7 has to be set in order to enable manual control by
7764+
* writing a value >= 9 to 0xf500
7765+
*
7766+
* TPACPI_FAN_WR_ACPI_FANW:
7767+
* ACPI FANW method: sets fan control registers
7768+
*
7769+
* Takes 0x8100 plus the offset to EC memory address 0xf500 and the
7770+
* value to be written there as parameters.
7771+
*
7772+
* see TPACPI_FAN_RD_ACPI_FANG
7773+
*
77527774
* TPACPI_FAN_WR_TPEC:
77537775
* ThinkPad EC register 0x2f (HFSP): fan control loop mode
77547776
* Supported on almost all ThinkPads
@@ -7882,13 +7904,15 @@ enum { /* Fan control constants */
78827904
enum fan_status_access_mode {
78837905
TPACPI_FAN_NONE = 0, /* No fan status or control */
78847906
TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
7907+
TPACPI_FAN_RD_ACPI_FANG, /* Use ACPI FANG */
78857908
TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
78867909
TPACPI_FAN_RD_TPEC_NS, /* Use non-standard ACPI EC regs (eg: L13 Yoga gen2 etc.) */
78877910
};
78887911

78897912
enum fan_control_access_mode {
78907913
TPACPI_FAN_WR_NONE = 0, /* No fan control */
78917914
TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
7915+
TPACPI_FAN_WR_ACPI_FANW, /* Use ACPI FANW */
78927916
TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
78937917
TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
78947918
};
@@ -7922,9 +7946,13 @@ TPACPI_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
79227946
TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */
79237947
"\\FSPD", /* 600e/x, 770e, 770x */
79247948
); /* all others */
7949+
TPACPI_HANDLE(fang, ec, "FANG", /* E531 */
7950+
); /* all others */
79257951
TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
79267952
"JFNS", /* 770x-JL */
79277953
); /* all others */
7954+
TPACPI_HANDLE(fanw, ec, "FANW", /* E531 */
7955+
); /* all others */
79287956

79297957
/*
79307958
* Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the
@@ -8031,6 +8059,23 @@ static int fan_get_status(u8 *status)
80318059

80328060
break;
80338061
}
8062+
case TPACPI_FAN_RD_ACPI_FANG: {
8063+
/* E531 */
8064+
int mode, speed;
8065+
8066+
if (unlikely(!acpi_evalf(fang_handle, &mode, NULL, "dd", 0x8100)))
8067+
return -EIO;
8068+
if (unlikely(!acpi_evalf(fang_handle, &speed, NULL, "dd", 0x8102)))
8069+
return -EIO;
8070+
8071+
if (likely(status)) {
8072+
*status = speed * 7 / 100;
8073+
if (mode < 9)
8074+
*status |= TP_EC_FAN_AUTO;
8075+
}
8076+
8077+
break;
8078+
}
80348079
case TPACPI_FAN_RD_TPEC:
80358080
/* all except 570, 600e/x, 770e, 770x */
80368081
if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
@@ -8145,6 +8190,17 @@ static int fan2_get_speed(unsigned int *speed)
81458190
if (speed)
81468191
*speed = lo ? FAN_RPM_CAL_CONST / lo : 0;
81478192
break;
8193+
case TPACPI_FAN_RD_ACPI_FANG: {
8194+
/* E531 */
8195+
int speed_tmp;
8196+
8197+
if (unlikely(!acpi_evalf(fang_handle, &speed_tmp, NULL, "dd", 0x8102)))
8198+
return -EIO;
8199+
8200+
if (likely(speed))
8201+
*speed = speed_tmp * 65535 / 100;
8202+
break;
8203+
}
81488204

81498205
default:
81508206
return -ENXIO;
@@ -8204,6 +8260,32 @@ static int fan_set_level(int level)
82048260
tp_features.fan_ctrl_status_undef = 0;
82058261
break;
82068262

8263+
case TPACPI_FAN_WR_ACPI_FANW:
8264+
if (!(level & TP_EC_FAN_AUTO) && (level < 0 || level > 7))
8265+
return -EINVAL;
8266+
if (level & TP_EC_FAN_FULLSPEED)
8267+
return -EINVAL;
8268+
8269+
if (level & TP_EC_FAN_AUTO) {
8270+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) {
8271+
return -EIO;
8272+
}
8273+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) {
8274+
return -EIO;
8275+
}
8276+
} else {
8277+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) {
8278+
return -EIO;
8279+
}
8280+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) {
8281+
return -EIO;
8282+
}
8283+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, level * 100 / 7)) {
8284+
return -EIO;
8285+
}
8286+
}
8287+
break;
8288+
82078289
default:
82088290
return -ENXIO;
82098291
}
@@ -8282,6 +8364,19 @@ static int fan_set_enable(void)
82828364
rc = 0;
82838365
break;
82848366

8367+
case TPACPI_FAN_WR_ACPI_FANW:
8368+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x05)) {
8369+
rc = -EIO;
8370+
break;
8371+
}
8372+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0x00)) {
8373+
rc = -EIO;
8374+
break;
8375+
}
8376+
8377+
rc = 0;
8378+
break;
8379+
82858380
default:
82868381
rc = -ENXIO;
82878382
}
@@ -8324,6 +8419,22 @@ static int fan_set_disable(void)
83248419
fan_control_desired_level = 0;
83258420
break;
83268421

8422+
case TPACPI_FAN_WR_ACPI_FANW:
8423+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) {
8424+
rc = -EIO;
8425+
break;
8426+
}
8427+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) {
8428+
rc = -EIO;
8429+
break;
8430+
}
8431+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8102, 0x00)) {
8432+
rc = -EIO;
8433+
break;
8434+
}
8435+
rc = 0;
8436+
break;
8437+
83278438
default:
83288439
rc = -ENXIO;
83298440
}
@@ -8357,6 +8468,23 @@ static int fan_set_speed(int speed)
83578468
rc = -EINVAL;
83588469
break;
83598470

8471+
case TPACPI_FAN_WR_ACPI_FANW:
8472+
if (speed >= 0 && speed <= 65535) {
8473+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8106, 0x45)) {
8474+
rc = -EIO;
8475+
break;
8476+
}
8477+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd", 0x8100, 0xff)) {
8478+
rc = -EIO;
8479+
break;
8480+
}
8481+
if (!acpi_evalf(fanw_handle, NULL, NULL, "vdd",
8482+
0x8102, speed * 100 / 65535))
8483+
rc = -EIO;
8484+
} else
8485+
rc = -EINVAL;
8486+
break;
8487+
83608488
default:
83618489
rc = -ENXIO;
83628490
}
@@ -8699,6 +8827,10 @@ static int __init fan_init(struct ibm_init_struct *iibm)
86998827
TPACPI_ACPIHANDLE_INIT(gfan);
87008828
TPACPI_ACPIHANDLE_INIT(sfan);
87018829
}
8830+
if (tpacpi_is_lenovo()) {
8831+
TPACPI_ACPIHANDLE_INIT(fang);
8832+
TPACPI_ACPIHANDLE_INIT(fanw);
8833+
}
87028834

87038835
quirks = tpacpi_check_quirks(fan_quirk_table,
87048836
ARRAY_SIZE(fan_quirk_table));
@@ -8718,6 +8850,9 @@ static int __init fan_init(struct ibm_init_struct *iibm)
87188850
if (gfan_handle) {
87198851
/* 570, 600e/x, 770e, 770x */
87208852
fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
8853+
} else if (fang_handle) {
8854+
/* E531 */
8855+
fan_status_access_mode = TPACPI_FAN_RD_ACPI_FANG;
87218856
} else {
87228857
/* all other ThinkPads: note that even old-style
87238858
* ThinkPad ECs supports the fan control register */
@@ -8764,6 +8899,11 @@ static int __init fan_init(struct ibm_init_struct *iibm)
87648899
fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
87658900
fan_control_commands |=
87668901
TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
8902+
} else if (fanw_handle) {
8903+
/* E531 */
8904+
fan_control_access_mode = TPACPI_FAN_WR_ACPI_FANW;
8905+
fan_control_commands |=
8906+
TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_SPEED | TPACPI_FAN_CMD_ENABLE;
87678907
} else {
87688908
if (!gfan_handle) {
87698909
/* gfan without sfan means no fan control */
@@ -8915,6 +9055,7 @@ static int fan_read(struct seq_file *m)
89159055

89169056
case TPACPI_FAN_RD_TPEC_NS:
89179057
case TPACPI_FAN_RD_TPEC:
9058+
case TPACPI_FAN_RD_ACPI_FANG:
89189059
/* all except 570, 600e/x, 770e, 770x */
89199060
rc = fan_get_status_safe(&status);
89209061
if (rc)
@@ -8935,7 +9076,7 @@ static int fan_read(struct seq_file *m)
89359076
* No other levels settings available
89369077
*/
89379078
seq_printf(m, "level:\t\t%s\n", status & FAN_NS_CTRL ? "unknown" : "auto");
8938-
} else {
9079+
} else if (fan_status_access_mode == TPACPI_FAN_RD_TPEC) {
89399080
if (status & TP_EC_FAN_FULLSPEED)
89409081
/* Disengaged mode takes precedence */
89419082
seq_printf(m, "level:\t\tdisengaged\n");

0 commit comments

Comments
 (0)