summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/ibm_acpi.c70
1 files changed, 67 insertions, 3 deletions
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c
index 56743c5..e5b8745 100644
--- a/drivers/acpi/ibm_acpi.c
+++ b/drivers/acpi/ibm_acpi.c
@@ -82,6 +82,8 @@
#include <linux/backlight.h>
#include <asm/uaccess.h>
#include <linux/dmi.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acnamesp.h>
@@ -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 <timeout> (<timeout> is 0 (off), "
+ "1-120 (seconds))\n");
if (fan_control_commands & IBMACPI_FAN_CMD_SPEED)
len += sprintf(p + len, "commands:\tspeed <speed>"
@@ -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,
},
};