From 60eb0b35a9cc3400251cb4028d100e350649cf8a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:08 -0200 Subject: 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 --- Documentation/ibm-acpi.txt | 55 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index e50595bfd8ea..30f09e7a423e 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -398,25 +398,56 @@ Temperature sensors -- /proc/acpi/ibm/thermal Most ThinkPads include six or more separate temperature sensors but only expose the CPU temperature through the standard ACPI methods. -This feature shows readings from up to eight different sensors. Some -readings may not be valid, e.g. may show large negative values. For -example, on the X40, a typical output may be: +This feature shows readings from up to eight different sensors on older +ThinkPads, and it has experimental support for up to sixteen different +sensors on newer ThinkPads. Readings from sensors that are not available +return -128. +No commands can be written to this file. + +EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the +implementation directly accesses hardware registers and may not work as +expected. USE WITH CAUTION! To use this feature, you need to supply the +experimental=1 parameter when loading the module. When EXPERIMENTAL +mode is enabled, reading the first 8 sensors on newer ThinkPads will +also use an new experimental thermal sensor access mode. + +For example, on the X40, a typical output may be: temperatures: 42 42 45 41 36 -128 33 -128 -Thomas Gruber took his R51 apart and traced all six active sensors in -his laptop (the location of sensors may vary on other models): +EXPERIMENTAL: On the T43/p, a typical output may be: +temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128 + +The mapping of thermal sensors to physical locations varies depending on +system-board model (and thus, on ThinkPad model). + +http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that +tries to track down these locations for various models. + +Most (newer?) models seem to follow this pattern: 1: CPU -2: Mini PCI Module -3: HDD +2: (depends on model) +3: (depends on model) 4: GPU -5: Battery -6: N/A -7: Battery -8: N/A +5: Main battery: main sensor +6: Bay battery: main sensor +7: Main battery: secondary sensor +8: Bay battery: secondary sensor +9-15: (depends on model) + +For the R51 (source: Thomas Gruber): +2: Mini-PCI +3: Internal HDD + +For the T43, T43/p (source: Shmidoax/Thinkwiki.org) +http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p +2: System board, left side (near PCMCIA slot), reported as HDAPS temp +3: PCMCIA slot +9: MCH (northbridge) to DRAM Bus +10: ICH (southbridge), under Mini-PCI card, under touchpad +11: Power regulator, underside of system board, below F2 key -No commands can be written to this file. EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump ------------------------------------------------------------------------ -- cgit v1.2.3 From 88679a15b3a84366e90cee2a84973abef962b727 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:09 -0200 Subject: ACPI: ibm-acpi: document thermal sensor locations for the A31 The A31 has a very atypical layout, so I separated its thermal sensors location in a separate patch. Signed-off-by: Henrique de Moraes Holschuh --- Documentation/ibm-acpi.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index 30f09e7a423e..333b8eb97f96 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -448,6 +448,17 @@ http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p 10: ICH (southbridge), under Mini-PCI card, under touchpad 11: Power regulator, underside of system board, below F2 key +The A31 has a very atypical layout for the thermal sensors +(source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31) +1: CPU +2: Main Battery: main sensor +3: Power Converter +4: Bay Battery: main sensor +5: MCH (northbridge) +6: PCMCIA/ambient +7: Main Battery: secondary sensor +8: Bay Battery: secondary sensor + EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump ------------------------------------------------------------------------ -- cgit v1.2.3 From a12095c2b50c8a7c80517e37c00d6e6c863d43c5 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:13 -0200 Subject: ACPI: ibm-acpi: fix and extend fan control functions This patch extend fan control functions, implementing enable/disable for all write access modes, implementing level control for all level-capable write access modes. The patch also updates the documentation, explaining levels auto and disengaged. ABI changes: 1. Support level 0 as an equivalent to disable 2. Add support for level auto and level disengaged when doing EC 0x2f fan control 3. Support enable/disable for all level-based write access modes 4. Add support for level command on FANS thinkpads, as per thinkwiki reports Signed-off-by: Henrique de Moraes Holschuh --- Documentation/ibm-acpi.txt | 65 ++++++++++++++++++++++++++++++++-------------- drivers/acpi/ibm_acpi.c | 39 ++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index 333b8eb97f96..cbd3a603a7ef 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -571,27 +571,57 @@ directly accesses hardware registers and may not work as expected. USE WITH CAUTION! To use this feature, you need to supply the experimental=1 parameter when loading the module. -This feature attempts to show the current fan speed. The speed is read -directly from the hardware registers of the embedded controller. This -is known to work on later R, T and X series ThinkPads but may show a -bogus value on other models. +This feature attempts to show the current fan speed, control mode and +other fan data that might be available. The speed is read directly +from the hardware registers of the embedded controller. This is known +to work on later R, T and X series ThinkPads but may show a bogus +value on other models. + +Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher +the level, the higher the fan speed, although adjacent levels often map +to the same fan speed. 7 is the highest level, where the fan reaches +the maximum recommended speed. Level "auto" means the EC changes the +fan level according to some internal algorithm, usually based on +readings from the thermal sensors. Level "disengaged" means the EC +disables the speed-locked closed-loop fan control, and drives the fan as +fast as it can go, which might exceed hardware limits, so use this level +with caution. + +The fan usually ramps up or down slowly from one speed to another, +and it is normal for the EC to take several seconds to react to fan +commands. The fan may be enabled or disabled with the following commands: echo enable >/proc/acpi/ibm/fan echo disable >/proc/acpi/ibm/fan +Placing a fan on level 0 is the same as disabling it. Enabling a fan +will try to place it in a safe level if it is too slow or disabled. + WARNING WARNING WARNING: do not leave the fan disabled unless you are -monitoring the temperature sensor readings and you are ready to enable -it if necessary to avoid overheating. +monitoring all of the temperature sensor readings and you are ready to +enable it if necessary to avoid overheating. + +An enabled fan in level "auto" may stop spinning if the EC decides the +ThinkPad is cool enough and doesn't need the extra airflow. This is +normal, and the EC will spin the fan up if the varios thermal readings +rise too much. + +On the X40, this seems to depend on the CPU and HDD temperatures. +Specifically, the fan is turned on when either the CPU temperature +climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The +fan is turned off when the CPU temperature drops to 49 degrees and the +HDD temperature drops to 41 degrees. These thresholds cannot +currently be controlled. + +The fan level can be controlled with the command: -The fan only runs if it's enabled *and* the various temperature -sensors which control it read high enough. On the X40, this seems to -depend on the CPU and HDD temperatures. Specifically, the fan is -turned on when either the CPU temperature climbs to 56 degrees or the -HDD temperature climbs to 46 degrees. The fan is turned off when the -CPU temperature drops to 49 degrees and the HDD temperature drops to -41 degrees. These thresholds cannot currently be controlled. + echo 'level ' > /proc/acpi/ibm/thermal + +Where is an integer from 0 to 7, or one of the words "auto" +or "disengaged" (without the quotes). Not all ThinkPads support the +"auto" and "disengaged" levels. On the X31 and X40 (and ONLY on those models), the fan speed can be controlled to a certain degree. Once the fan is running, it can be @@ -604,12 +634,9 @@ about 3700 to about 7350. Values outside this range either do not have any effect or the fan speed eventually settles somewhere in that range. The fan cannot be stopped or started with this command. -On the 570, temperature readings are not available through this -feature and the fan control works a little differently. The fan speed -is reported in levels from 0 (off) to 7 (max) and can be controlled -with the following command: - - echo 'level ' > /proc/acpi/ibm/thermal +The ThinkPad's ACPI DSDT code will reprogram the fan on its own when +certain conditions are met. It will override any fan programming done +through ibm-acpi. EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan --------------------------------------- diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index ecb5ece79a37..4001ad193ddc 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -1833,10 +1833,13 @@ static int fan_init(void) IBMACPI_FAN_WR_ACPI_FANS; fan_control_commands |= IBMACPI_FAN_CMD_SPEED | + IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; } else { fan_control_access_mode = IBMACPI_FAN_WR_TPEC; - fan_control_commands |= IBMACPI_FAN_CMD_ENABLE; + fan_control_commands |= + IBMACPI_FAN_CMD_LEVEL | + IBMACPI_FAN_CMD_ENABLE; } } } @@ -1948,9 +1951,20 @@ static int fan_read(char *p) len += sprintf(p + len, "status:\t\tnot supported\n"); } - if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) - len += sprintf(p + len, "commands:\tlevel " - " ( is 0-7)\n"); + if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) { + len += sprintf(p + len, "commands:\tlevel "); + + switch (fan_control_access_mode) { + case IBMACPI_FAN_WR_ACPI_SFAN: + len += sprintf(p + len, " ( is 0-7)\n"); + break; + + default: + len += sprintf(p + len, " ( is 0-7, " + "auto, disengaged)\n"); + break; + } + } if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) len += sprintf(p + len, "commands:\tenable, disable\n"); @@ -1973,6 +1987,17 @@ static int fan_set_level(int level) return -EINVAL; break; + case IBMACPI_FAN_WR_ACPI_FANS: + case IBMACPI_FAN_WR_TPEC: + if ((level != IBMACPI_FAN_EC_AUTO) && + (level != IBMACPI_FAN_EC_DISENGAGED) && + ((level < 0) || (level > 7))) + return -EINVAL; + + if (!acpi_ec_write(fan_status_offset, level)) + return -EIO; + break; + default: return -ENXIO; } @@ -2060,7 +2085,11 @@ static int fan_write_cmd_level(const char *cmd, int *rc) { int level; - if (sscanf(cmd, "level %d", &level) != 1) + if (strlencmp(cmd, "level auto") == 0) + level = IBMACPI_FAN_EC_AUTO; + else if (strlencmp(cmd, "level disengaged") == 0) + level = IBMACPI_FAN_EC_DISENGAGED; + else if (sscanf(cmd, "level %d", &level) != 1) return 0; if ((*rc = fan_set_level(level)) == -ENXIO) -- cgit v1.2.3 From 16663a87ad1df7022661bc8813b7a2e84e7f5e66 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 24 Nov 2006 11:47:14 -0200 Subject: ACPI: ibm-acpi: implement fan watchdog command This patch implements a fan control safety watchdog, by request of the authors of userspace fan control scripts. When the watchdog timer expires, the equivalent action of a "fan enable" command is executed. The watchdog timer is reset at every reception of a fan control command that could change the state of the fan itself. This command is meant to be used by userspace fan control daemons, to make sure the fan is never left set to an unsafe level because of userspace problems. Users of the X31/X40/X41 "speed" command are on their own, the current implementation of "speed" is just too incomplete to be used safely, anyway. Better to never use it, and just use the "level" command instead. The watchdog is programmed using echo "watchdog " > fan, where number is the number of seconds to wait before doing an "enable", and zero disables the watchdog. Signed-off-by: Henrique de Moraes Holschuh --- Documentation/ibm-acpi.txt | 20 +++++++++++++ drivers/acpi/ibm_acpi.c | 70 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index cbd3a603a7ef..0132d363feb5 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt @@ -670,6 +670,26 @@ example: modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable +The ibm-acpi kernel driver can be programmed to revert the fan level +to a safe setting if userspace does not issue one of the fan commands: +"enable", "disable", "level" or "watchdog" within a configurable +ammount of time. To do this, use the "watchdog" command. + + echo 'watchdog ' > /proc/acpi/ibm/fan + +Interval is the ammount of time in seconds to wait for one of the +above mentioned fan commands before reseting the fan level to a safe +one. If set to zero, the watchdog is disabled (default). When the +watchdog timer runs out, it does the exact equivalent of the "enable" +fan command. + +Note that the watchdog timer stops after it enables the fan. It will +be rearmed again automatically (using the same interval) when one of +the above mentioned fan commands is received. The fan watchdog is, +therefore, not suitable to protect against fan mode changes made +through means other than the "enable", "disable", and "level" fan +commands. + Example Configuration --------------------- diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 56743c5a0439..e5b8745b9070 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c @@ -82,6 +82,8 @@ #include #include #include +#include +#include #include #include @@ -348,7 +350,8 @@ enum fan_control_access_mode { enum fan_control_commands { IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ - IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd */ + IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, + * and also watchdog cmd */ }; enum { /* Fan control constants */ @@ -1797,12 +1800,17 @@ static enum fan_control_commands fan_control_commands; static int fan_control_status_known; static u8 fan_control_initial_status; +static void fan_watchdog_fire(void *ignored); +static int fan_watchdog_maxinterval; +static DECLARE_WORK(fan_watchdog_task, fan_watchdog_fire, NULL); + static int fan_init(void) { fan_status_access_mode = IBMACPI_FAN_NONE; fan_control_access_mode = IBMACPI_FAN_WR_NONE; fan_control_commands = 0; fan_control_status_known = 1; + fan_watchdog_maxinterval = 0; if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ @@ -1934,6 +1942,31 @@ static int fan_get_speed(unsigned int *speed) return 0; } +static void fan_exit(void) +{ + cancel_delayed_work(&fan_watchdog_task); + flush_scheduled_work(); +} + +static void fan_watchdog_reset(void) +{ + static int fan_watchdog_active = 0; + + if (fan_watchdog_active) + cancel_delayed_work(&fan_watchdog_task); + + if (fan_watchdog_maxinterval > 0) { + fan_watchdog_active = 1; + if (!schedule_delayed_work(&fan_watchdog_task, + msecs_to_jiffies(fan_watchdog_maxinterval + * 1000))) { + printk(IBM_ERR "failed to schedule the fan watchdog, " + "watchdog will not trigger\n"); + } + } else + fan_watchdog_active = 0; +} + static int fan_read(char *p) { int len = 0; @@ -2007,7 +2040,9 @@ static int fan_read(char *p) } if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) - len += sprintf(p + len, "commands:\tenable, disable\n"); + len += sprintf(p + len, "commands:\tenable, disable\n" + "commands:\twatchdog ( is 0 (off), " + "1-120 (seconds))\n"); if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) len += sprintf(p + len, "commands:\tspeed " @@ -2186,6 +2221,21 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) return 1; } +static int fan_write_cmd_watchdog(const char *cmd, int *rc) +{ + int interval; + + if (sscanf(cmd, "watchdog %d", &interval) != 1) + return 0; + + if (interval < 0 || interval > 120) + *rc = -EINVAL; + else + fan_watchdog_maxinterval = interval; + + return 1; +} + static int fan_write(char *buf) { char *cmd; @@ -2196,16 +2246,29 @@ static int fan_write(char *buf) fan_write_cmd_level(cmd, &rc)) && !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && (fan_write_cmd_enable(cmd, &rc) || - fan_write_cmd_disable(cmd, &rc))) && + fan_write_cmd_disable(cmd, &rc) || + fan_write_cmd_watchdog(cmd, &rc))) && !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && fan_write_cmd_speed(cmd, &rc)) ) rc = -EINVAL; + else if (!rc) + fan_watchdog_reset(); } return rc; } +static void fan_watchdog_fire(void *ignored) +{ + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); + if (fan_set_enable()) { + printk(IBM_ERR "fan watchdog: error while enabling fan\n"); + /* reschedule for later */ + fan_watchdog_reset(); + } +} + static struct ibm_struct ibms[] = { { .name = "driver", @@ -2317,6 +2380,7 @@ static struct ibm_struct ibms[] = { .read = fan_read, .write = fan_write, .init = fan_init, + .exit = fan_exit, .experimental = 1, }, }; -- cgit v1.2.3