Add linux v2.6.20-rc2, as most patches were merged in
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / releases / upstream / 2.6.20-rc2 / 0018-ACPI-ibm-acpi-implement-fan-watchdog-command.txt
blob103d7a8decfb40d92225fc190002ccba63d1cc3f
1 From 16663a87ad1df7022661bc8813b7a2e84e7f5e66 Mon Sep 17 00:00:00 2001
2 From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
3 Date: Fri, 24 Nov 2006 11:47:14 -0200
4 Subject: [PATCH 18/28] ACPI: ibm-acpi: implement fan watchdog command
6 This patch implements a fan control safety watchdog, by request of the
7 authors of userspace fan control scripts.
9 When the watchdog timer expires, the equivalent action of a "fan enable"
10 command is executed.  The watchdog timer is reset at every reception of a
11 fan control command that could change the state of the fan itself.
13 This command is meant to be used by userspace fan control daemons, to make
14 sure the fan is never left set to an unsafe level because of userspace
15 problems.
17 Users of the X31/X40/X41 "speed" command are on their own, the current
18 implementation of "speed" is just too incomplete to be used safely,
19 anyway.  Better to never use it, and just use the "level" command instead.
21 The watchdog is programmed using echo "watchdog <number>" > fan, where
22 number is the number of seconds to wait before doing an "enable", and zero
23 disables the watchdog.
25 Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
26 ---
27  Documentation/ibm-acpi.txt |   20 ++++++++++++
28  drivers/acpi/ibm_acpi.c    |   70 ++++++++++++++++++++++++++++++++++++++++++--
29  2 files changed, 87 insertions(+), 3 deletions(-)
31 diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt
32 index cbd3a60..0132d36 100644
33 --- a/Documentation/ibm-acpi.txt
34 +++ b/Documentation/ibm-acpi.txt
35 @@ -670,6 +670,26 @@ example:
37         modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable
39 +The ibm-acpi kernel driver can be programmed to revert the fan level
40 +to a safe setting if userspace does not issue one of the fan commands:
41 +"enable", "disable", "level" or "watchdog" within a configurable
42 +ammount of time.  To do this, use the "watchdog" command.
44 +       echo 'watchdog <interval>' > /proc/acpi/ibm/fan
46 +Interval is the ammount of time in seconds to wait for one of the
47 +above mentioned fan commands before reseting the fan level to a safe
48 +one.  If set to zero, the watchdog is disabled (default).  When the
49 +watchdog timer runs out, it does the exact equivalent of the "enable"
50 +fan command.
52 +Note that the watchdog timer stops after it enables the fan.  It will
53 +be rearmed again automatically (using the same interval) when one of
54 +the above mentioned fan commands is received.  The fan watchdog is,
55 +therefore, not suitable to protect against fan mode changes made
56 +through means other than the "enable", "disable", and "level" fan
57 +commands.
60  Example Configuration
61  ---------------------
62 diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c
63 index 56743c5..e5b8745 100644
64 --- a/drivers/acpi/ibm_acpi.c
65 +++ b/drivers/acpi/ibm_acpi.c
66 @@ -82,6 +82,8 @@
67  #include <linux/backlight.h>
68  #include <asm/uaccess.h>
69  #include <linux/dmi.h>
70 +#include <linux/jiffies.h>
71 +#include <linux/workqueue.h>
73  #include <acpi/acpi_drivers.h>
74  #include <acpi/acnamesp.h>
75 @@ -348,7 +350,8 @@ enum fan_control_access_mode {
76  enum fan_control_commands {
77         IBMACPI_FAN_CMD_SPEED   = 0x0001,       /* speed command */
78         IBMACPI_FAN_CMD_LEVEL   = 0x0002,       /* level command  */
79 -       IBMACPI_FAN_CMD_ENABLE  = 0x0004,       /* enable/disable cmd */
80 +       IBMACPI_FAN_CMD_ENABLE  = 0x0004,       /* enable/disable cmd,
81 +                                                * and also watchdog cmd */
82  };
84  enum {                                 /* Fan control constants */
85 @@ -1797,12 +1800,17 @@ static enum fan_control_commands fan_control_commands;
86  static int fan_control_status_known;
87  static u8 fan_control_initial_status;
89 +static void fan_watchdog_fire(void *ignored);
90 +static int fan_watchdog_maxinterval;
91 +static DECLARE_WORK(fan_watchdog_task, fan_watchdog_fire, NULL);
93  static int fan_init(void)
94  {
95         fan_status_access_mode = IBMACPI_FAN_NONE;
96         fan_control_access_mode = IBMACPI_FAN_WR_NONE;
97         fan_control_commands = 0;
98         fan_control_status_known = 1;
99 +       fan_watchdog_maxinterval = 0;
101         if (gfan_handle) {
102                 /* 570, 600e/x, 770e, 770x */
103 @@ -1934,6 +1942,31 @@ static int fan_get_speed(unsigned int *speed)
104         return 0;
107 +static void fan_exit(void)
109 +       cancel_delayed_work(&fan_watchdog_task);
110 +       flush_scheduled_work();
113 +static void fan_watchdog_reset(void)
115 +       static int fan_watchdog_active = 0;
117 +       if (fan_watchdog_active)
118 +               cancel_delayed_work(&fan_watchdog_task);
120 +       if (fan_watchdog_maxinterval > 0) {
121 +               fan_watchdog_active = 1;
122 +               if (!schedule_delayed_work(&fan_watchdog_task,
123 +                               msecs_to_jiffies(fan_watchdog_maxinterval
124 +                                                * 1000))) {
125 +                       printk(IBM_ERR "failed to schedule the fan watchdog, "
126 +                              "watchdog will not trigger\n");
127 +               }
128 +       } else
129 +               fan_watchdog_active = 0;
132  static int fan_read(char *p)
134         int len = 0;
135 @@ -2007,7 +2040,9 @@ static int fan_read(char *p)
136         }
138         if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE)
139 -               len += sprintf(p + len, "commands:\tenable, disable\n");
140 +               len += sprintf(p + len, "commands:\tenable, disable\n"
141 +                              "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
142 +                              "1-120 (seconds))\n");
144         if (fan_control_commands & IBMACPI_FAN_CMD_SPEED)
145                 len += sprintf(p + len, "commands:\tspeed <speed>"
146 @@ -2186,6 +2221,21 @@ static int fan_write_cmd_speed(const char *cmd, int *rc)
147         return 1;
150 +static int fan_write_cmd_watchdog(const char *cmd, int *rc)
152 +       int interval;
154 +       if (sscanf(cmd, "watchdog %d", &interval) != 1)
155 +               return 0;
157 +       if (interval < 0 || interval > 120)
158 +               *rc = -EINVAL;
159 +       else
160 +               fan_watchdog_maxinterval = interval;
162 +       return 1;
165  static int fan_write(char *buf)
167         char *cmd;
168 @@ -2196,16 +2246,29 @@ static int fan_write(char *buf)
169                       fan_write_cmd_level(cmd, &rc)) &&
170                     !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) &&
171                       (fan_write_cmd_enable(cmd, &rc) ||
172 -                      fan_write_cmd_disable(cmd, &rc))) &&
173 +                      fan_write_cmd_disable(cmd, &rc) ||
174 +                      fan_write_cmd_watchdog(cmd, &rc))) &&
175                     !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) &&
176                       fan_write_cmd_speed(cmd, &rc))
177                     )
178                         rc = -EINVAL;
179 +               else if (!rc)
180 +                       fan_watchdog_reset();
181         }
183         return rc;
186 +static void fan_watchdog_fire(void *ignored)
188 +       printk(IBM_NOTICE "fan watchdog: enabling fan\n");
189 +       if (fan_set_enable()) {
190 +               printk(IBM_ERR "fan watchdog: error while enabling fan\n");
191 +               /* reschedule for later */
192 +               fan_watchdog_reset();
193 +       }
196  static struct ibm_struct ibms[] = {
197         {
198          .name = "driver",
199 @@ -2317,6 +2380,7 @@ static struct ibm_struct ibms[] = {
200          .read = fan_read,
201          .write = fan_write,
202          .init = fan_init,
203 +        .exit = fan_exit,
204          .experimental = 1,
205          },
206  };
207 -- 
208 1.4.4.2