diff options
| -rw-r--r-- | Documentation/ibm-acpi.txt | 20 | ||||
| -rw-r--r-- | drivers/acpi/ibm_acpi.c | 70 |
2 files changed, 87 insertions, 3 deletions
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: | |||
| 670 | 670 | ||
| 671 | modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable | 671 | modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable |
| 672 | 672 | ||
| 673 | The ibm-acpi kernel driver can be programmed to revert the fan level | ||
| 674 | to a safe setting if userspace does not issue one of the fan commands: | ||
| 675 | "enable", "disable", "level" or "watchdog" within a configurable | ||
| 676 | ammount of time. To do this, use the "watchdog" command. | ||
| 677 | |||
| 678 | echo 'watchdog <interval>' > /proc/acpi/ibm/fan | ||
| 679 | |||
| 680 | Interval is the ammount of time in seconds to wait for one of the | ||
| 681 | above mentioned fan commands before reseting the fan level to a safe | ||
| 682 | one. If set to zero, the watchdog is disabled (default). When the | ||
| 683 | watchdog timer runs out, it does the exact equivalent of the "enable" | ||
| 684 | fan command. | ||
| 685 | |||
| 686 | Note that the watchdog timer stops after it enables the fan. It will | ||
| 687 | be rearmed again automatically (using the same interval) when one of | ||
| 688 | the above mentioned fan commands is received. The fan watchdog is, | ||
| 689 | therefore, not suitable to protect against fan mode changes made | ||
| 690 | through means other than the "enable", "disable", and "level" fan | ||
| 691 | commands. | ||
| 692 | |||
| 673 | 693 | ||
| 674 | Example Configuration | 694 | Example Configuration |
| 675 | --------------------- | 695 | --------------------- |
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 @@ | |||
| 82 | #include <linux/backlight.h> | 82 | #include <linux/backlight.h> |
| 83 | #include <asm/uaccess.h> | 83 | #include <asm/uaccess.h> |
| 84 | #include <linux/dmi.h> | 84 | #include <linux/dmi.h> |
| 85 | #include <linux/jiffies.h> | ||
| 86 | #include <linux/workqueue.h> | ||
| 85 | 87 | ||
| 86 | #include <acpi/acpi_drivers.h> | 88 | #include <acpi/acpi_drivers.h> |
| 87 | #include <acpi/acnamesp.h> | 89 | #include <acpi/acnamesp.h> |
| @@ -348,7 +350,8 @@ enum fan_control_access_mode { | |||
| 348 | enum fan_control_commands { | 350 | enum fan_control_commands { |
| 349 | IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ | 351 | IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ |
| 350 | IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ | 352 | IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ |
| 351 | IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd */ | 353 | IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, |
| 354 | * and also watchdog cmd */ | ||
| 352 | }; | 355 | }; |
| 353 | 356 | ||
| 354 | enum { /* Fan control constants */ | 357 | enum { /* Fan control constants */ |
| @@ -1797,12 +1800,17 @@ static enum fan_control_commands fan_control_commands; | |||
| 1797 | static int fan_control_status_known; | 1800 | static int fan_control_status_known; |
| 1798 | static u8 fan_control_initial_status; | 1801 | static u8 fan_control_initial_status; |
| 1799 | 1802 | ||
| 1803 | static void fan_watchdog_fire(void *ignored); | ||
| 1804 | static int fan_watchdog_maxinterval; | ||
| 1805 | static DECLARE_WORK(fan_watchdog_task, fan_watchdog_fire, NULL); | ||
| 1806 | |||
| 1800 | static int fan_init(void) | 1807 | static int fan_init(void) |
| 1801 | { | 1808 | { |
| 1802 | fan_status_access_mode = IBMACPI_FAN_NONE; | 1809 | fan_status_access_mode = IBMACPI_FAN_NONE; |
| 1803 | fan_control_access_mode = IBMACPI_FAN_WR_NONE; | 1810 | fan_control_access_mode = IBMACPI_FAN_WR_NONE; |
| 1804 | fan_control_commands = 0; | 1811 | fan_control_commands = 0; |
| 1805 | fan_control_status_known = 1; | 1812 | fan_control_status_known = 1; |
| 1813 | fan_watchdog_maxinterval = 0; | ||
| 1806 | 1814 | ||
| 1807 | if (gfan_handle) { | 1815 | if (gfan_handle) { |
| 1808 | /* 570, 600e/x, 770e, 770x */ | 1816 | /* 570, 600e/x, 770e, 770x */ |
| @@ -1934,6 +1942,31 @@ static int fan_get_speed(unsigned int *speed) | |||
| 1934 | return 0; | 1942 | return 0; |
| 1935 | } | 1943 | } |
| 1936 | 1944 | ||
| 1945 | static void fan_exit(void) | ||
| 1946 | { | ||
| 1947 | cancel_delayed_work(&fan_watchdog_task); | ||
| 1948 | flush_scheduled_work(); | ||
| 1949 | } | ||
| 1950 | |||
| 1951 | static void fan_watchdog_reset(void) | ||
| 1952 | { | ||
| 1953 | static int fan_watchdog_active = 0; | ||
| 1954 | |||
| 1955 | if (fan_watchdog_active) | ||
| 1956 | cancel_delayed_work(&fan_watchdog_task); | ||
| 1957 | |||
| 1958 | if (fan_watchdog_maxinterval > 0) { | ||
| 1959 | fan_watchdog_active = 1; | ||
| 1960 | if (!schedule_delayed_work(&fan_watchdog_task, | ||
| 1961 | msecs_to_jiffies(fan_watchdog_maxinterval | ||
| 1962 | * 1000))) { | ||
| 1963 | printk(IBM_ERR "failed to schedule the fan watchdog, " | ||
| 1964 | "watchdog will not trigger\n"); | ||
| 1965 | } | ||
| 1966 | } else | ||
| 1967 | fan_watchdog_active = 0; | ||
| 1968 | } | ||
| 1969 | |||
| 1937 | static int fan_read(char *p) | 1970 | static int fan_read(char *p) |
| 1938 | { | 1971 | { |
| 1939 | int len = 0; | 1972 | int len = 0; |
| @@ -2007,7 +2040,9 @@ static int fan_read(char *p) | |||
| 2007 | } | 2040 | } |
| 2008 | 2041 | ||
| 2009 | if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) | 2042 | if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) |
| 2010 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 2043 | len += sprintf(p + len, "commands:\tenable, disable\n" |
| 2044 | "commands:\twatchdog <timeout> (<timeout> is 0 (off), " | ||
| 2045 | "1-120 (seconds))\n"); | ||
| 2011 | 2046 | ||
| 2012 | if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) | 2047 | if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) |
| 2013 | len += sprintf(p + len, "commands:\tspeed <speed>" | 2048 | len += sprintf(p + len, "commands:\tspeed <speed>" |
| @@ -2186,6 +2221,21 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) | |||
| 2186 | return 1; | 2221 | return 1; |
| 2187 | } | 2222 | } |
| 2188 | 2223 | ||
| 2224 | static int fan_write_cmd_watchdog(const char *cmd, int *rc) | ||
| 2225 | { | ||
| 2226 | int interval; | ||
| 2227 | |||
| 2228 | if (sscanf(cmd, "watchdog %d", &interval) != 1) | ||
| 2229 | return 0; | ||
| 2230 | |||
| 2231 | if (interval < 0 || interval > 120) | ||
| 2232 | *rc = -EINVAL; | ||
| 2233 | else | ||
| 2234 | fan_watchdog_maxinterval = interval; | ||
| 2235 | |||
| 2236 | return 1; | ||
| 2237 | } | ||
| 2238 | |||
| 2189 | static int fan_write(char *buf) | 2239 | static int fan_write(char *buf) |
| 2190 | { | 2240 | { |
| 2191 | char *cmd; | 2241 | char *cmd; |
| @@ -2196,16 +2246,29 @@ static int fan_write(char *buf) | |||
| 2196 | fan_write_cmd_level(cmd, &rc)) && | 2246 | fan_write_cmd_level(cmd, &rc)) && |
| 2197 | !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && | 2247 | !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && |
| 2198 | (fan_write_cmd_enable(cmd, &rc) || | 2248 | (fan_write_cmd_enable(cmd, &rc) || |
| 2199 | fan_write_cmd_disable(cmd, &rc))) && | 2249 | fan_write_cmd_disable(cmd, &rc) || |
| 2250 | fan_write_cmd_watchdog(cmd, &rc))) && | ||
| 2200 | !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && | 2251 | !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && |
| 2201 | fan_write_cmd_speed(cmd, &rc)) | 2252 | fan_write_cmd_speed(cmd, &rc)) |
| 2202 | ) | 2253 | ) |
| 2203 | rc = -EINVAL; | 2254 | rc = -EINVAL; |
| 2255 | else if (!rc) | ||
| 2256 | fan_watchdog_reset(); | ||
| 2204 | } | 2257 | } |
| 2205 | 2258 | ||
| 2206 | return rc; | 2259 | return rc; |
| 2207 | } | 2260 | } |
| 2208 | 2261 | ||
| 2262 | static void fan_watchdog_fire(void *ignored) | ||
| 2263 | { | ||
| 2264 | printk(IBM_NOTICE "fan watchdog: enabling fan\n"); | ||
| 2265 | if (fan_set_enable()) { | ||
| 2266 | printk(IBM_ERR "fan watchdog: error while enabling fan\n"); | ||
| 2267 | /* reschedule for later */ | ||
| 2268 | fan_watchdog_reset(); | ||
| 2269 | } | ||
| 2270 | } | ||
| 2271 | |||
| 2209 | static struct ibm_struct ibms[] = { | 2272 | static struct ibm_struct ibms[] = { |
| 2210 | { | 2273 | { |
| 2211 | .name = "driver", | 2274 | .name = "driver", |
| @@ -2317,6 +2380,7 @@ static struct ibm_struct ibms[] = { | |||
| 2317 | .read = fan_read, | 2380 | .read = fan_read, |
| 2318 | .write = fan_write, | 2381 | .write = fan_write, |
| 2319 | .init = fan_init, | 2382 | .init = fan_init, |
| 2383 | .exit = fan_exit, | ||
| 2320 | .experimental = 1, | 2384 | .experimental = 1, |
| 2321 | }, | 2385 | }, |
| 2322 | }; | 2386 | }; |
