summaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2006-11-24 11:47:14 -0200
committerLen Brown <len.brown@intel.com>2006-12-07 01:38:44 -0500
commit16663a87ad1df7022661bc8813b7a2e84e7f5e66 (patch)
treed78d869332bdfa8a5859134114eb145eb41b5dd3 /drivers/acpi
parent778b4d742b210b9cac31f223527f30f1fc70312b (diff)
downloadlinux-16663a87ad1df7022661bc8813b7a2e84e7f5e66.tar.bz2
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 <number>" > 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 <hmh@hmh.eng.br>
Diffstat (limited to 'drivers/acpi')
-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 56743c5a0439..e5b8745b9070 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,
},
};