|
@@ -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,
|
|
|
},
|
|
|
};
|