[ltp] [patch 11/12][2.6.18] ibm-acpi: workaround for EC 0x2f initialization bug

Henrique de Moraes Holschuh linux-thinkpad@linux-thinkpad.org
Mon, 23 Oct 2006 12:17:56 -0300


A few ThinkPads fail to initialize EC register 0x2f both in the EC
firmware and ACPI DSDT.  If the BIOS and the ACPI DSDT also do not
initialize it, then the initial status of that register does not
correspond to reality.

On all reported buggy machines, EC 0x2f will read 0x07 (fan level 7) upon
cold boot, when the EC is actually in mode 0x80 (auto mode).  Since
returning a text string ("unknown") would break a number of userspace
programs, instead we correct the reading for the most probably correct
answer, and return it is in auto mode.

The workaround flags the status and level as unknown on module load/kernel
boot, until we are certain at least one fan control command was issued,
either by us, or by something else.

We don't work around the bug by doing a "fan enable" at module
load/startup (which would initialize the EC register) because it is not
known if these ThinkPad ACPI DSDT might have set the fan to level 7
instead of "auto" (we don't know if they can do this or not) due to a
thermal condition, and we don't want to override that, should they be
capable of it.

We should be setting the workaround flag to "status known" upon resume, as
both reports and a exaustive search on the DSDT tables at acpi.sf.net show
that the DSDTs always enable the fan on resume, thus working around the
bug.  But since we don't have suspend/resume handlers in ibm-acpi yet and
the "EC register 0x2f was modified" logic is likely to catch the change
anyway, we don't.

Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: borislav@users.sourceforge.net
---
 drivers/acpi/ibm_acpi.c |   51 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 48 insertions(+), 3 deletions(-)

Index: 2.6.18/drivers/acpi/ibm_acpi.c
===================================================================
--- 2.6.18.orig/drivers/acpi/ibm_acpi.c
+++ 2.6.18/drivers/acpi/ibm_acpi.c
@@ -1740,13 +1740,15 @@ static enum fan_status_access_mode fan_s
 static enum fan_control_access_mode fan_control_access_mode;
 static enum fan_control_commands fan_control_commands;
 
+static int fan_control_status_known;
+static u8 fan_control_initial_status;
+
 static int fan_init(void)
 {
-	u8 status;
-
 	fan_status_access_mode = IBMACPI_FAN_NONE;
 	fan_control_access_mode = IBMACPI_FAN_WR_NONE;
 	fan_control_commands = 0;
+	fan_control_status_known = 1;
 
 	if (gfan_handle) {
 		/* 570, 600e/x, 770e, 770x */
@@ -1754,8 +1756,33 @@ static int fan_init(void)
 	} else {
 		/* all other ThinkPads: note that even old-style
 		 * ThinkPad ECs supports the fan control register */
-		if (likely(acpi_ec_read(fan_status_offset, &status))) {
+		if (likely(acpi_ec_read(fan_status_offset,
+					&fan_control_initial_status))) {
 			fan_status_access_mode = IBMACPI_FAN_RD_TPEC;
+
+			/* In some ThinkPads, neither the EC nor the ACPI
+			 * DSDT initialize the fan status, and it ends up
+			 * being set to 0x07 when it *could* be either
+			 * 0x07 or 0x80.
+			 *
+			 * Enable for TP-1Y (T43), TP-78 (R51e),
+			 * TP-76 (R52), TP-60 (T43, R52), which are known
+			 * to be buggy. */
+			if (fan_control_initial_status == 0x07 &&
+					ibm_thinkpad_ec_found &&
+				 	((ibm_thinkpad_ec_found[0] == '1' &&
+					  ibm_thinkpad_ec_found[1] == 'Y') ||
+					 (ibm_thinkpad_ec_found[0] == '7' &&
+					  (ibm_thinkpad_ec_found[1] == '6' ||
+					   ibm_thinkpad_ec_found[1] == '8' ||
+					   ibm_thinkpad_ec_found[1] == '0'))
+					)) {
+				printk(IBM_NOTICE
+					"fan_init: initial fan status is "
+					"unknown, assuming it is in auto "
+					"mode\n");
+				fan_control_status_known = 0;
+			}
 		} else {
 			printk(IBM_ERR
 				"ThinkPad ACPI EC access misbehaving, "
@@ -1816,9 +1843,21 @@ static int fan_read(char *p)
 		if (unlikely(!acpi_ec_read(fan_status_offset, &status)))
 			return -EIO;
 
+		if (unlikely(!fan_control_status_known)) {
+			if (status != fan_control_initial_status)
+				fan_control_status_known = 1;
+			else
+				/* Return most likely status. In fact, it
+				 * might be the only possible status */
+				status = IBMACPI_FAN_EC_AUTO;
+		}
+
 		len += sprintf(p + len, "status:\t\t%s\n",
 				(status != 0) ? "enabled" : "disabled" );
 
+		/* No ThinkPad boots on disengaged mode, we can safely
+		 * assume the tachometer is online if fan control status
+		 * was unknown */
 		if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
 			  !acpi_ec_read(fan_rpm_offset + 1, &hi)))
 			return -EIO;
@@ -1888,6 +1927,8 @@ static int fan_write_level(int level)
 
 		if (!acpi_ec_write(fan_status_offset, level))
 			return -EIO;
+		else
+			fan_control_status_known = 1;
 		break;
 
 	default:
@@ -1905,6 +1946,8 @@ static int fan_write_enable( void )
 	case IBMACPI_FAN_WR_TPEC:
 		if (!acpi_ec_write(fan_status_offset, IBMACPI_FAN_EC_AUTO))
 			return -EIO;
+		else
+			fan_control_status_known = 1;
 		break;
 
 	case IBMACPI_FAN_WR_ACPI_SFAN:
@@ -1928,6 +1971,8 @@ static int fan_write_disable( void )
 	case IBMACPI_FAN_WR_TPEC:
 		if (!acpi_ec_write(fan_status_offset, 0x00))
 			return -EIO;
+		else
+			fan_control_status_known = 1;
 		break;
 
 	case IBMACPI_FAN_WR_ACPI_SFAN:

--
  "One disk to rule them all, One disk to find them. One disk to bring
  them all and in the darkness grind them. In the Land of Redmond
  where the shadows lie." -- The Silicon Valley Tarot
  Henrique Holschuh