diff options
author | Henrique de Moraes Holschuh <hmh@hmh.eng.br> | 2006-11-24 08:47:08 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2006-12-07 01:38:38 -0500 |
commit | 60eb0b35a9cc3400251cb4028d100e350649cf8a (patch) | |
tree | 2f655a273572e46b6c4f662071d027e57cad1bcf /drivers/acpi/ibm_acpi.c | |
parent | a26f878abcd0491906b5bbac8dd174f27019e907 (diff) |
ACPI: ibm-acpi: Implement direct-ec-access thermal reading modes for up to 16 sensors
This patch extends ibm-acpi to support reading thermal sensors directly
through ACPI EC register access. It uses a DMI match to detect ThinkPads
with a new-style embedded controller, that are known to have forward-
compatible register maps and use 0x00 to fill in non-used registers and
export thermal sensors at EC offsets 0x78-7F and 0xC0-C7.
Direct ACPI EC register access is implemented for 8-sensor and 16-sensor
new-style ThinkPad controller firmwares as an experimental feature. The
code does some limited sanity checks on the temperatures read through EC
access, and will default to the old ACPI TMP0-7 mode if anything is amiss.
Userspace ABI is not changed for 8 sensors, but /proc/acpi/ibm/thermal is
extended for 16 sensors if the firmware supports 16 sensors.
A documentation update is also provided.
The information about the ThinkPad register map was determined by studying
ibm-acpi "ecdump" output from various ThinkPad models, submitted by
subscribers of the linux-thinkpad mailinglist. Futher information was
gathered from the DSDT tables, as they describe the EC register map in
recent ThinkPads.
DSDT source shows that TMP0-7 access and direct register access are
actually the same thing on these firmwares, but unfortunately IBM never
did update their DSDT EC register map to export TMP8-TMP15 for the second
range of sensors.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Diffstat (limited to 'drivers/acpi/ibm_acpi.c')
-rw-r--r-- | drivers/acpi/ibm_acpi.c | 93 |
1 files changed, 91 insertions, 2 deletions
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index baf9492f0a79..1703c617c0e8 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c | |||
@@ -80,6 +80,7 @@ | |||
80 | #include <linux/proc_fs.h> | 80 | #include <linux/proc_fs.h> |
81 | #include <linux/backlight.h> | 81 | #include <linux/backlight.h> |
82 | #include <asm/uaccess.h> | 82 | #include <asm/uaccess.h> |
83 | #include <linux/dmi.h> | ||
83 | 84 | ||
84 | #include <acpi/acpi_drivers.h> | 85 | #include <acpi/acpi_drivers.h> |
85 | #include <acpi/acnamesp.h> | 86 | #include <acpi/acnamesp.h> |
@@ -221,13 +222,17 @@ enum thermal_access_mode { | |||
221 | IBMACPI_THERMAL_NONE = 0, /* No thermal support */ | 222 | IBMACPI_THERMAL_NONE = 0, /* No thermal support */ |
222 | IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ | 223 | IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ |
223 | IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ | 224 | IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ |
225 | IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ | ||
226 | IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ | ||
224 | }; | 227 | }; |
225 | 228 | ||
226 | #define IBMACPI_MAX_THERMAL_SENSORS 8 /* Max thermal sensors supported */ | 229 | #define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ |
227 | struct ibm_thermal_sensors_struct { | 230 | struct ibm_thermal_sensors_struct { |
228 | s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; | 231 | s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; |
229 | }; | 232 | }; |
230 | 233 | ||
234 | static int ibm_thinkpad_ec_found; | ||
235 | |||
231 | struct ibm_struct { | 236 | struct ibm_struct { |
232 | char *name; | 237 | char *name; |
233 | char param[32]; | 238 | char param[32]; |
@@ -1290,7 +1295,52 @@ static enum thermal_access_mode thermal_read_mode; | |||
1290 | 1295 | ||
1291 | static int thermal_init(void) | 1296 | static int thermal_init(void) |
1292 | { | 1297 | { |
1293 | if (acpi_evalf(ec_handle, NULL, "TMP7", "qv")) { | 1298 | u8 t, ta1, ta2; |
1299 | int i; | ||
1300 | int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); | ||
1301 | |||
1302 | if (ibm_thinkpad_ec_found && experimental) { | ||
1303 | /* | ||
1304 | * Direct EC access mode: sensors at registers | ||
1305 | * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for | ||
1306 | * non-implemented, thermal sensors return 0x80 when | ||
1307 | * not available | ||
1308 | */ | ||
1309 | |||
1310 | ta1 = ta2 = 0; | ||
1311 | for (i = 0; i < 8; i++) { | ||
1312 | if (likely(acpi_ec_read(0x78 + i, &t))) { | ||
1313 | ta1 |= t; | ||
1314 | } else { | ||
1315 | ta1 = 0; | ||
1316 | break; | ||
1317 | } | ||
1318 | if (likely(acpi_ec_read(0xC0 + i, &t))) { | ||
1319 | ta2 |= t; | ||
1320 | } else { | ||
1321 | ta1 = 0; | ||
1322 | break; | ||
1323 | } | ||
1324 | } | ||
1325 | if (ta1 == 0) { | ||
1326 | /* This is sheer paranoia, but we handle it anyway */ | ||
1327 | if (acpi_tmp7) { | ||
1328 | printk(IBM_ERR | ||
1329 | "ThinkPad ACPI EC access misbehaving, " | ||
1330 | "falling back to ACPI TMPx access mode\n"); | ||
1331 | thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; | ||
1332 | } else { | ||
1333 | printk(IBM_ERR | ||
1334 | "ThinkPad ACPI EC access misbehaving, " | ||
1335 | "disabling thermal sensors access\n"); | ||
1336 | thermal_read_mode = IBMACPI_THERMAL_NONE; | ||
1337 | } | ||
1338 | } else { | ||
1339 | thermal_read_mode = | ||
1340 | (ta2 != 0) ? | ||
1341 | IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8; | ||
1342 | } | ||
1343 | } else if (acpi_tmp7) { | ||
1294 | if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { | 1344 | if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { |
1295 | /* 600e/x, 770e, 770x */ | 1345 | /* 600e/x, 770e, 770x */ |
1296 | thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; | 1346 | thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; |
@@ -1309,12 +1359,30 @@ static int thermal_init(void) | |||
1309 | static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) | 1359 | static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) |
1310 | { | 1360 | { |
1311 | int i, t; | 1361 | int i, t; |
1362 | s8 tmp; | ||
1312 | char tmpi[] = "TMPi"; | 1363 | char tmpi[] = "TMPi"; |
1313 | 1364 | ||
1314 | if (!s) | 1365 | if (!s) |
1315 | return -EINVAL; | 1366 | return -EINVAL; |
1316 | 1367 | ||
1317 | switch (thermal_read_mode) { | 1368 | switch (thermal_read_mode) { |
1369 | #if IBMACPI_MAX_THERMAL_SENSORS >= 16 | ||
1370 | case IBMACPI_THERMAL_TPEC_16: | ||
1371 | for (i = 0; i < 8; i++) { | ||
1372 | if (!acpi_ec_read(0xC0 + i, &tmp)) | ||
1373 | return -EIO; | ||
1374 | s->temp[i + 8] = tmp * 1000; | ||
1375 | } | ||
1376 | /* fallthrough */ | ||
1377 | #endif | ||
1378 | case IBMACPI_THERMAL_TPEC_8: | ||
1379 | for (i = 0; i < 8; i++) { | ||
1380 | if (!acpi_ec_read(0x78 + i, &tmp)) | ||
1381 | return -EIO; | ||
1382 | s->temp[i] = tmp * 1000; | ||
1383 | } | ||
1384 | return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8; | ||
1385 | |||
1318 | case IBMACPI_THERMAL_ACPI_UPDT: | 1386 | case IBMACPI_THERMAL_ACPI_UPDT: |
1319 | if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) | 1387 | if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) |
1320 | return -EIO; | 1388 | return -EIO; |
@@ -2052,6 +2120,24 @@ static void acpi_ibm_exit(void) | |||
2052 | remove_proc_entry(IBM_DIR, acpi_root_dir); | 2120 | remove_proc_entry(IBM_DIR, acpi_root_dir); |
2053 | } | 2121 | } |
2054 | 2122 | ||
2123 | static int __init check_dmi_for_ec(void) | ||
2124 | { | ||
2125 | struct dmi_device *dev = NULL; | ||
2126 | |||
2127 | /* | ||
2128 | * ThinkPad T23 or newer, A31 or newer, R50e or newer, | ||
2129 | * X32 or newer, all Z series; Some models must have an | ||
2130 | * up-to-date BIOS or they will not be detected. | ||
2131 | * | ||
2132 | * See http://thinkwiki.org/wiki/List_of_DMI_IDs | ||
2133 | */ | ||
2134 | while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { | ||
2135 | if (strstr(dev->name, "IBM ThinkPad Embedded Controller")) | ||
2136 | return 1; | ||
2137 | } | ||
2138 | return 0; | ||
2139 | } | ||
2140 | |||
2055 | static int __init acpi_ibm_init(void) | 2141 | static int __init acpi_ibm_init(void) |
2056 | { | 2142 | { |
2057 | int ret, i; | 2143 | int ret, i; |
@@ -2071,6 +2157,9 @@ static int __init acpi_ibm_init(void) | |||
2071 | return -ENODEV; | 2157 | return -ENODEV; |
2072 | } | 2158 | } |
2073 | 2159 | ||
2160 | /* Models with newer firmware report the EC in DMI */ | ||
2161 | ibm_thinkpad_ec_found = check_dmi_for_ec(); | ||
2162 | |||
2074 | /* these handles are not required */ | 2163 | /* these handles are not required */ |
2075 | IBM_HANDLE_INIT(vid); | 2164 | IBM_HANDLE_INIT(vid); |
2076 | IBM_HANDLE_INIT(vid2); | 2165 | IBM_HANDLE_INIT(vid2); |