ACPI: ibm-acpi: implement fan watchdog command
[powerpc.git] / drivers / acpi / ibm_acpi.c
index 56743c5..e5b8745 100644 (file)
@@ -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,
         },
 };