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 <hmh@hmh.eng.br>
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c
index ecb5ece..4001ad1 100644
--- a/drivers/acpi/ibm_acpi.c
+++ b/drivers/acpi/ibm_acpi.c
@@ -1833,10 +1833,13 @@
 				    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 @@
 		len += sprintf(p + len, "status:\t\tnot supported\n");
 	}
 
-	if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL)
-		len += sprintf(p + len, "commands:\tlevel <level>"
-			       " (<level> is 0-7)\n");
+	if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) {
+		len += sprintf(p + len, "commands:\tlevel <level>");
+
+		switch (fan_control_access_mode) {
+		case IBMACPI_FAN_WR_ACPI_SFAN:
+			len += sprintf(p + len, " (<level> is 0-7)\n");
+			break;
+
+		default:
+			len += sprintf(p + len, " (<level> 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 @@
 			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 @@
 {
 	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)