blob: f50a405d0134338898c28fb899c6be34895dc881 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * acpi_thermal.c - ACPI Thermal Zone Driver ($Revision: 41 $)
3 *
4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6 *
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 *
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 *
25 * This driver fully implements the ACPI thermal policy as described in the
26 * ACPI 2.0 Specification.
27 *
28 * TBD: 1. Implement passive cooling hysteresis.
29 * 2. Enhance passive cooling (CPU) states/limit interface to support
30 * concepts of 'multiple limiters', upper/lower limits, etc.
31 *
32 */
33
34#include <linux/kernel.h>
35#include <linux/module.h>
36#include <linux/init.h>
37#include <linux/types.h>
38#include <linux/proc_fs.h>
39#include <linux/sched.h>
40#include <linux/kmod.h>
41#include <linux/seq_file.h>
42#include <asm/uaccess.h>
43
44#include <acpi/acpi_bus.h>
45#include <acpi/acpi_drivers.h>
46
47#define ACPI_THERMAL_COMPONENT 0x04000000
48#define ACPI_THERMAL_CLASS "thermal_zone"
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone"
50#define ACPI_THERMAL_FILE_STATE "state"
51#define ACPI_THERMAL_FILE_TEMPERATURE "temperature"
52#define ACPI_THERMAL_FILE_TRIP_POINTS "trip_points"
53#define ACPI_THERMAL_FILE_COOLING_MODE "cooling_mode"
54#define ACPI_THERMAL_FILE_POLLING_FREQ "polling_frequency"
55#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80
56#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81
57#define ACPI_THERMAL_NOTIFY_DEVICES 0x82
58#define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0
59#define ACPI_THERMAL_NOTIFY_HOT 0xF1
60#define ACPI_THERMAL_MODE_ACTIVE 0x00
61#define ACPI_THERMAL_MODE_PASSIVE 0x01
62#define ACPI_THERMAL_MODE_CRITICAL 0xff
63#define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff"
64
65#define ACPI_THERMAL_MAX_ACTIVE 10
66#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
67
68#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
69#define CELSIUS_TO_KELVIN(t) ((t+273)*10)
70
71#define _COMPONENT ACPI_THERMAL_COMPONENT
Len Brownf52fd662007-02-12 22:42:12 -050072ACPI_MODULE_NAME("thermal");
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Thomas Renninger1cbf4c52004-09-16 11:07:00 -040074MODULE_AUTHOR("Paul Diefenbaugh");
Len Brown7cda93e2007-02-12 23:50:02 -050075MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070076MODULE_LICENSE("GPL");
77
78static int tzp;
79module_param(tzp, int, 0);
80MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n");
81
Len Brown4be44fc2005-08-05 00:44:28 -040082static int acpi_thermal_add(struct acpi_device *device);
83static int acpi_thermal_remove(struct acpi_device *device, int type);
Patrick Mochel5d9464a2006-12-07 20:56:27 +080084static int acpi_thermal_resume(struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file);
86static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file);
87static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file);
Len Brown4be44fc2005-08-05 00:44:28 -040088static ssize_t acpi_thermal_write_trip_points(struct file *,
89 const char __user *, size_t,
90 loff_t *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file);
Len Brown4be44fc2005-08-05 00:44:28 -040092static ssize_t acpi_thermal_write_cooling_mode(struct file *,
93 const char __user *, size_t,
94 loff_t *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file);
Len Brown4be44fc2005-08-05 00:44:28 -040096static ssize_t acpi_thermal_write_polling(struct file *, const char __user *,
97 size_t, loff_t *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
99static struct acpi_driver acpi_thermal_driver = {
Len Brownc2b67052007-02-12 23:33:40 -0500100 .name = "thermal",
Len Brown4be44fc2005-08-05 00:44:28 -0400101 .class = ACPI_THERMAL_CLASS,
102 .ids = ACPI_THERMAL_HID,
103 .ops = {
104 .add = acpi_thermal_add,
105 .remove = acpi_thermal_remove,
Konstantin Karasyov74ce14682006-05-08 08:32:00 -0400106 .resume = acpi_thermal_resume,
Len Brown4be44fc2005-08-05 00:44:28 -0400107 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108};
109
110struct acpi_thermal_state {
Len Brown4be44fc2005-08-05 00:44:28 -0400111 u8 critical:1;
112 u8 hot:1;
113 u8 passive:1;
114 u8 active:1;
115 u8 reserved:4;
116 int active_index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117};
118
119struct acpi_thermal_state_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400120 u8 valid:1;
121 u8 enabled:1;
122 u8 reserved:6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123};
124
125struct acpi_thermal_critical {
126 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400127 unsigned long temperature;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128};
129
130struct acpi_thermal_hot {
131 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400132 unsigned long temperature;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133};
134
135struct acpi_thermal_passive {
136 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400137 unsigned long temperature;
138 unsigned long tc1;
139 unsigned long tc2;
140 unsigned long tsp;
141 struct acpi_handle_list devices;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142};
143
144struct acpi_thermal_active {
145 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400146 unsigned long temperature;
147 struct acpi_handle_list devices;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148};
149
150struct acpi_thermal_trips {
151 struct acpi_thermal_critical critical;
Len Brown4be44fc2005-08-05 00:44:28 -0400152 struct acpi_thermal_hot hot;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 struct acpi_thermal_passive passive;
154 struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
155};
156
157struct acpi_thermal_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400158 u8 cooling_mode:1; /* _SCP */
159 u8 devices:1; /* _TZD */
160 u8 reserved:6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161};
162
163struct acpi_thermal {
Patrick Mochel8348e1b2006-05-19 16:54:40 -0400164 struct acpi_device * device;
Len Brown4be44fc2005-08-05 00:44:28 -0400165 acpi_bus_id name;
166 unsigned long temperature;
167 unsigned long last_temperature;
168 unsigned long polling_frequency;
169 u8 cooling_mode;
170 volatile u8 zombie;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 struct acpi_thermal_flags flags;
172 struct acpi_thermal_state state;
173 struct acpi_thermal_trips trips;
Len Brown4be44fc2005-08-05 00:44:28 -0400174 struct acpi_handle_list devices;
175 struct timer_list timer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176};
177
Arjan van de Vend7508032006-07-04 13:06:00 -0400178static const struct file_operations acpi_thermal_state_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400179 .open = acpi_thermal_state_open_fs,
180 .read = seq_read,
181 .llseek = seq_lseek,
182 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183};
184
Arjan van de Vend7508032006-07-04 13:06:00 -0400185static const struct file_operations acpi_thermal_temp_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400186 .open = acpi_thermal_temp_open_fs,
187 .read = seq_read,
188 .llseek = seq_lseek,
189 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190};
191
Arjan van de Vend7508032006-07-04 13:06:00 -0400192static const struct file_operations acpi_thermal_trip_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400193 .open = acpi_thermal_trip_open_fs,
194 .read = seq_read,
195 .write = acpi_thermal_write_trip_points,
196 .llseek = seq_lseek,
197 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198};
199
Arjan van de Vend7508032006-07-04 13:06:00 -0400200static const struct file_operations acpi_thermal_cooling_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400201 .open = acpi_thermal_cooling_open_fs,
202 .read = seq_read,
203 .write = acpi_thermal_write_cooling_mode,
204 .llseek = seq_lseek,
205 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206};
207
Arjan van de Vend7508032006-07-04 13:06:00 -0400208static const struct file_operations acpi_thermal_polling_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400209 .open = acpi_thermal_polling_open_fs,
210 .read = seq_read,
211 .write = acpi_thermal_write_polling,
212 .llseek = seq_lseek,
213 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214};
215
216/* --------------------------------------------------------------------------
217 Thermal Zone Management
218 -------------------------------------------------------------------------- */
219
Len Brown4be44fc2005-08-05 00:44:28 -0400220static int acpi_thermal_get_temperature(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221{
Len Brown4be44fc2005-08-05 00:44:28 -0400222 acpi_status status = AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
225 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400226 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
228 tz->last_temperature = tz->temperature;
229
Len Brown4be44fc2005-08-05 00:44:28 -0400230 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400231 acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tz->temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400233 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
Len Brown4be44fc2005-08-05 00:44:28 -0400235 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n",
236 tz->temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
Patrick Mocheld550d982006-06-27 00:41:40 -0400238 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239}
240
Len Brown4be44fc2005-08-05 00:44:28 -0400241static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
Len Brown4be44fc2005-08-05 00:44:28 -0400243 acpi_status status = AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
246 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400247 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Len Brown4be44fc2005-08-05 00:44:28 -0400249 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400250 acpi_evaluate_integer(tz->device->handle, "_TZP", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400251 &tz->polling_frequency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400253 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
Len Brown4be44fc2005-08-05 00:44:28 -0400255 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n",
256 tz->polling_frequency));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
Patrick Mocheld550d982006-06-27 00:41:40 -0400258 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259}
260
Len Brown4be44fc2005-08-05 00:44:28 -0400261static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400265 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
267 tz->polling_frequency = seconds * 10; /* Convert value to deci-seconds */
268
Len Brown4be44fc2005-08-05 00:44:28 -0400269 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
270 "Polling frequency set to %lu seconds\n",
271 tz->polling_frequency));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
Patrick Mocheld550d982006-06-27 00:41:40 -0400273 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
Len Brown4be44fc2005-08-05 00:44:28 -0400276static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Len Brown4be44fc2005-08-05 00:44:28 -0400278 acpi_status status = AE_OK;
279 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
280 struct acpi_object_list arg_list = { 1, &arg0 };
281 acpi_handle handle = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
284 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400285 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400287 status = acpi_get_handle(tz->device->handle, "_SCP", &handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 if (ACPI_FAILURE(status)) {
289 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400290 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 }
292
293 arg0.integer.value = mode;
294
295 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
296 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400297 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 tz->cooling_mode = mode;
300
Len Brown4be44fc2005-08-05 00:44:28 -0400301 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling mode [%s]\n",
302 mode ? "passive" : "active"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Patrick Mocheld550d982006-06-27 00:41:40 -0400304 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305}
306
Len Brown4be44fc2005-08-05 00:44:28 -0400307static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308{
Len Brown4be44fc2005-08-05 00:44:28 -0400309 acpi_status status = AE_OK;
310 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400314 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
316 /* Critical Shutdown (required) */
317
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400318 status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400319 &tz->trips.critical.temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 if (ACPI_FAILURE(status)) {
321 tz->trips.critical.flags.valid = 0;
Thomas Renningera6fc6722006-06-26 23:58:43 -0400322 ACPI_EXCEPTION((AE_INFO, status, "No critical threshold"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400323 return -ENODEV;
Len Brown4be44fc2005-08-05 00:44:28 -0400324 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 tz->trips.critical.flags.valid = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400326 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
327 "Found critical threshold [%lu]\n",
328 tz->trips.critical.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 }
330
331 /* Critical Sleep (optional) */
332
Len Brown4be44fc2005-08-05 00:44:28 -0400333 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400334 acpi_evaluate_integer(tz->device->handle, "_HOT", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400335 &tz->trips.hot.temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 if (ACPI_FAILURE(status)) {
337 tz->trips.hot.flags.valid = 0;
338 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n"));
Len Brown4be44fc2005-08-05 00:44:28 -0400339 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 tz->trips.hot.flags.valid = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400341 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n",
342 tz->trips.hot.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 }
344
345 /* Passive: Processors (optional) */
346
Len Brown4be44fc2005-08-05 00:44:28 -0400347 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400348 acpi_evaluate_integer(tz->device->handle, "_PSV", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400349 &tz->trips.passive.temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 if (ACPI_FAILURE(status)) {
351 tz->trips.passive.flags.valid = 0;
352 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n"));
Len Brown4be44fc2005-08-05 00:44:28 -0400353 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 tz->trips.passive.flags.valid = 1;
355
Len Brown4be44fc2005-08-05 00:44:28 -0400356 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400357 acpi_evaluate_integer(tz->device->handle, "_TC1", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400358 &tz->trips.passive.tc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 if (ACPI_FAILURE(status))
360 tz->trips.passive.flags.valid = 0;
361
Len Brown4be44fc2005-08-05 00:44:28 -0400362 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400363 acpi_evaluate_integer(tz->device->handle, "_TC2", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400364 &tz->trips.passive.tc2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 if (ACPI_FAILURE(status))
366 tz->trips.passive.flags.valid = 0;
367
Len Brown4be44fc2005-08-05 00:44:28 -0400368 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400369 acpi_evaluate_integer(tz->device->handle, "_TSP", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400370 &tz->trips.passive.tsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 if (ACPI_FAILURE(status))
372 tz->trips.passive.flags.valid = 0;
373
Len Brown4be44fc2005-08-05 00:44:28 -0400374 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400375 acpi_evaluate_reference(tz->device->handle, "_PSL", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400376 &tz->trips.passive.devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 if (ACPI_FAILURE(status))
378 tz->trips.passive.flags.valid = 0;
379
380 if (!tz->trips.passive.flags.valid)
Len Browncece9292006-06-26 23:04:31 -0400381 printk(KERN_WARNING PREFIX "Invalid passive threshold\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 else
Len Brown4be44fc2005-08-05 00:44:28 -0400383 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
384 "Found passive threshold [%lu]\n",
385 tz->trips.passive.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 }
387
388 /* Active: Fans, etc. (optional) */
389
Len Brown4be44fc2005-08-05 00:44:28 -0400390 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
Len Brown4be44fc2005-08-05 00:44:28 -0400392 char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
Len Brown4be44fc2005-08-05 00:44:28 -0400394 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400395 acpi_evaluate_integer(tz->device->handle, name, NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400396 &tz->trips.active[i].temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 if (ACPI_FAILURE(status))
398 break;
399
400 name[2] = 'L';
Len Brown4be44fc2005-08-05 00:44:28 -0400401 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400402 acpi_evaluate_reference(tz->device->handle, name, NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400403 &tz->trips.active[i].devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 if (ACPI_SUCCESS(status)) {
405 tz->trips.active[i].flags.valid = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400406 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
407 "Found active threshold [%d]:[%lu]\n",
408 i, tz->trips.active[i].temperature));
409 } else
Thomas Renningera6fc6722006-06-26 23:58:43 -0400410 ACPI_EXCEPTION((AE_INFO, status,
411 "Invalid active threshold [%d]", i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 }
413
Patrick Mocheld550d982006-06-27 00:41:40 -0400414 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415}
416
Len Brown4be44fc2005-08-05 00:44:28 -0400417static int acpi_thermal_get_devices(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418{
Len Brown4be44fc2005-08-05 00:44:28 -0400419 acpi_status status = AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
422 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400423 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
Len Brown4be44fc2005-08-05 00:44:28 -0400425 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400426 acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400428 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
Patrick Mocheld550d982006-06-27 00:41:40 -0400430 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431}
432
Len Brown4be44fc2005-08-05 00:44:28 -0400433static int acpi_thermal_call_usermode(char *path)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Len Brown4be44fc2005-08-05 00:44:28 -0400435 char *argv[2] = { NULL, NULL };
436 char *envp[3] = { NULL, NULL, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
439 if (!path)
Patrick Mocheld550d982006-06-27 00:41:40 -0400440 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 argv[0] = path;
443
444 /* minimal command environment */
445 envp[0] = "HOME=/";
446 envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
Len Brown4be44fc2005-08-05 00:44:28 -0400447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 call_usermodehelper(argv[0], argv, envp, 0);
449
Patrick Mocheld550d982006-06-27 00:41:40 -0400450 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451}
452
Len Brown4be44fc2005-08-05 00:44:28 -0400453static int acpi_thermal_critical(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 if (!tz || !tz->trips.critical.flags.valid)
Patrick Mocheld550d982006-06-27 00:41:40 -0400456 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
458 if (tz->temperature >= tz->trips.critical.temperature) {
Len Browncece9292006-06-26 23:04:31 -0400459 printk(KERN_WARNING PREFIX "Critical trip point\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 tz->trips.critical.flags.enabled = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400461 } else if (tz->trips.critical.flags.enabled)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 tz->trips.critical.flags.enabled = 0;
463
Len Brown4be44fc2005-08-05 00:44:28 -0400464 printk(KERN_EMERG
465 "Critical temperature reached (%ld C), shutting down.\n",
466 KELVIN_TO_CELSIUS(tz->temperature));
Patrick Mochel8348e1b2006-05-19 16:54:40 -0400467 acpi_bus_generate_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL,
Len Brown4be44fc2005-08-05 00:44:28 -0400468 tz->trips.critical.flags.enabled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
470 acpi_thermal_call_usermode(ACPI_THERMAL_PATH_POWEROFF);
471
Patrick Mocheld550d982006-06-27 00:41:40 -0400472 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473}
474
Len Brown4be44fc2005-08-05 00:44:28 -0400475static int acpi_thermal_hot(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 if (!tz || !tz->trips.hot.flags.valid)
Patrick Mocheld550d982006-06-27 00:41:40 -0400478 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
480 if (tz->temperature >= tz->trips.hot.temperature) {
Len Browncece9292006-06-26 23:04:31 -0400481 printk(KERN_WARNING PREFIX "Hot trip point\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 tz->trips.hot.flags.enabled = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400483 } else if (tz->trips.hot.flags.enabled)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 tz->trips.hot.flags.enabled = 0;
485
Patrick Mochel8348e1b2006-05-19 16:54:40 -0400486 acpi_bus_generate_event(tz->device, ACPI_THERMAL_NOTIFY_HOT,
Len Brown4be44fc2005-08-05 00:44:28 -0400487 tz->trips.hot.flags.enabled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
489 /* TBD: Call user-mode "sleep(S4)" function */
490
Patrick Mocheld550d982006-06-27 00:41:40 -0400491 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492}
493
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400494static void acpi_thermal_passive(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495{
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400496 int result = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 struct acpi_thermal_passive *passive = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400498 int trend = 0;
499 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
502 if (!tz || !tz->trips.passive.flags.valid)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400503 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505 passive = &(tz->trips.passive);
506
507 /*
508 * Above Trip?
509 * -----------
510 * Calculate the thermal trend (using the passive cooling equation)
511 * and modify the performance limit for all passive cooling devices
512 * accordingly. Note that we assume symmetry.
513 */
514 if (tz->temperature >= passive->temperature) {
Len Brown4be44fc2005-08-05 00:44:28 -0400515 trend =
516 (passive->tc1 * (tz->temperature - tz->last_temperature)) +
517 (passive->tc2 * (tz->temperature - passive->temperature));
518 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
519 "trend[%d]=(tc1[%lu]*(tmp[%lu]-last[%lu]))+(tc2[%lu]*(tmp[%lu]-psv[%lu]))\n",
520 trend, passive->tc1, tz->temperature,
521 tz->last_temperature, passive->tc2,
522 tz->temperature, passive->temperature));
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400523 passive->flags.enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 /* Heating up? */
525 if (trend > 0)
Len Brown4be44fc2005-08-05 00:44:28 -0400526 for (i = 0; i < passive->devices.count; i++)
527 acpi_processor_set_thermal_limit(passive->
528 devices.
529 handles[i],
530 ACPI_PROCESSOR_LIMIT_INCREMENT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 /* Cooling off? */
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400532 else if (trend < 0) {
Len Brown4be44fc2005-08-05 00:44:28 -0400533 for (i = 0; i < passive->devices.count; i++)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400534 /*
535 * assume that we are on highest
536 * freq/lowest thrott and can leave
537 * passive mode, even in error case
538 */
539 if (!acpi_processor_set_thermal_limit
540 (passive->devices.handles[i],
541 ACPI_PROCESSOR_LIMIT_DECREMENT))
542 result = 0;
543 /*
544 * Leave cooling mode, even if the temp might
545 * higher than trip point This is because some
546 * machines might have long thermal polling
547 * frequencies (tsp) defined. We will fall back
548 * into passive mode in next cycle (probably quicker)
549 */
550 if (result) {
551 passive->flags.enabled = 0;
552 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
553 "Disabling passive cooling, still above threshold,"
554 " but we are cooling down\n"));
555 }
556 }
557 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 }
559
560 /*
561 * Below Trip?
562 * -----------
563 * Implement passive cooling hysteresis to slowly increase performance
564 * and avoid thrashing around the passive trip point. Note that we
565 * assume symmetry.
566 */
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400567 if (!passive->flags.enabled)
568 return;
569 for (i = 0; i < passive->devices.count; i++)
570 if (!acpi_processor_set_thermal_limit
571 (passive->devices.handles[i],
572 ACPI_PROCESSOR_LIMIT_DECREMENT))
573 result = 0;
574 if (result) {
575 passive->flags.enabled = 0;
576 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
577 "Disabling passive cooling (zone is cool)\n"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400581static void acpi_thermal_active(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
Len Brown4be44fc2005-08-05 00:44:28 -0400583 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 struct acpi_thermal_active *active = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400585 int i = 0;
586 int j = 0;
587 unsigned long maxtemp = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 if (!tz)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400591 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
Len Brown4be44fc2005-08-05 00:44:28 -0400593 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 active = &(tz->trips.active[i]);
595 if (!active || !active->flags.valid)
596 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 if (tz->temperature >= active->temperature) {
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400598 /*
599 * Above Threshold?
600 * ----------------
601 * If not already enabled, turn ON all cooling devices
602 * associated with this active threshold.
603 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 if (active->temperature > maxtemp)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400605 tz->state.active_index = i;
606 maxtemp = active->temperature;
607 if (active->flags.enabled)
608 continue;
609 for (j = 0; j < active->devices.count; j++) {
610 result =
611 acpi_bus_set_power(active->devices.
612 handles[j],
613 ACPI_STATE_D0);
614 if (result) {
Len Browncece9292006-06-26 23:04:31 -0400615 printk(KERN_WARNING PREFIX
616 "Unable to turn cooling device [%p] 'on'\n",
Thomas Renningera6fc6722006-06-26 23:58:43 -0400617 active->devices.
Len Browncece9292006-06-26 23:04:31 -0400618 handles[j]);
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400619 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 }
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400621 active->flags.enabled = 1;
622 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
623 "Cooling device [%p] now 'on'\n",
624 active->devices.handles[j]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 }
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400626 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 }
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400628 if (!active->flags.enabled)
629 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 /*
631 * Below Threshold?
632 * ----------------
633 * Turn OFF all cooling devices associated with this
634 * threshold.
635 */
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400636 for (j = 0; j < active->devices.count; j++) {
637 result = acpi_bus_set_power(active->devices.handles[j],
638 ACPI_STATE_D3);
639 if (result) {
Len Browncece9292006-06-26 23:04:31 -0400640 printk(KERN_WARNING PREFIX
641 "Unable to turn cooling device [%p] 'off'\n",
642 active->devices.handles[j]);
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400643 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 }
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400645 active->flags.enabled = 0;
646 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
647 "Cooling device [%p] now 'off'\n",
648 active->devices.handles[j]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 }
650 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651}
652
Len Brown4be44fc2005-08-05 00:44:28 -0400653static void acpi_thermal_check(void *context);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Len Brown4be44fc2005-08-05 00:44:28 -0400655static void acpi_thermal_run(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656{
657 struct acpi_thermal *tz = (struct acpi_thermal *)data;
658 if (!tz->zombie)
Alexey Starikovskiyb8d35192006-05-05 03:23:00 -0400659 acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660}
661
Len Brown4be44fc2005-08-05 00:44:28 -0400662static void acpi_thermal_check(void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663{
Len Brown4be44fc2005-08-05 00:44:28 -0400664 int result = 0;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200665 struct acpi_thermal *tz = data;
Len Brown4be44fc2005-08-05 00:44:28 -0400666 unsigned long sleep_time = 0;
667 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 struct acpi_thermal_state state;
669
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
671 if (!tz) {
Len Brown64684632006-06-26 23:41:38 -0400672 printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
Patrick Mocheld550d982006-06-27 00:41:40 -0400673 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 }
675
676 state = tz->state;
677
678 result = acpi_thermal_get_temperature(tz);
679 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400680 return;
Len Brown4be44fc2005-08-05 00:44:28 -0400681
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 memset(&tz->state, 0, sizeof(tz->state));
Len Brown4be44fc2005-08-05 00:44:28 -0400683
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 /*
685 * Check Trip Points
686 * -----------------
687 * Compare the current temperature to the trip point values to see
688 * if we've entered one of the thermal policy states. Note that
689 * this function determines when a state is entered, but the
690 * individual policy decides when it is exited (e.g. hysteresis).
691 */
692 if (tz->trips.critical.flags.valid)
Len Brown4be44fc2005-08-05 00:44:28 -0400693 state.critical |=
694 (tz->temperature >= tz->trips.critical.temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 if (tz->trips.hot.flags.valid)
696 state.hot |= (tz->temperature >= tz->trips.hot.temperature);
697 if (tz->trips.passive.flags.valid)
Len Brown4be44fc2005-08-05 00:44:28 -0400698 state.passive |=
699 (tz->temperature >= tz->trips.passive.temperature);
700 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 if (tz->trips.active[i].flags.valid)
Len Brown4be44fc2005-08-05 00:44:28 -0400702 state.active |=
703 (tz->temperature >=
704 tz->trips.active[i].temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
706 /*
707 * Invoke Policy
708 * -------------
709 * Separated from the above check to allow individual policy to
710 * determine when to exit a given state.
711 */
712 if (state.critical)
713 acpi_thermal_critical(tz);
714 if (state.hot)
715 acpi_thermal_hot(tz);
716 if (state.passive)
717 acpi_thermal_passive(tz);
718 if (state.active)
719 acpi_thermal_active(tz);
720
721 /*
722 * Calculate State
723 * ---------------
724 * Again, separated from the above two to allow independent policy
725 * decisions.
726 */
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400727 tz->state.critical = tz->trips.critical.flags.enabled;
728 tz->state.hot = tz->trips.hot.flags.enabled;
729 tz->state.passive = tz->trips.passive.flags.enabled;
730 tz->state.active = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400731 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400732 tz->state.active |= tz->trips.active[i].flags.enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
734 /*
735 * Calculate Sleep Time
736 * --------------------
737 * If we're in the passive state, use _TSP's value. Otherwise
738 * use the default polling frequency (e.g. _TZP). If no polling
739 * frequency is specified then we'll wait forever (at least until
740 * a thermal event occurs). Note that _TSP and _TZD values are
741 * given in 1/10th seconds (we must covert to milliseconds).
742 */
743 if (tz->state.passive)
744 sleep_time = tz->trips.passive.tsp * 100;
745 else if (tz->polling_frequency > 0)
746 sleep_time = tz->polling_frequency * 100;
747
Len Brown4be44fc2005-08-05 00:44:28 -0400748 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: temperature[%lu] sleep[%lu]\n",
749 tz->name, tz->temperature, sleep_time));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 /*
752 * Schedule Next Poll
753 * ------------------
754 */
755 if (!sleep_time) {
756 if (timer_pending(&(tz->timer)))
757 del_timer(&(tz->timer));
Len Brown4be44fc2005-08-05 00:44:28 -0400758 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 if (timer_pending(&(tz->timer)))
760 mod_timer(&(tz->timer), (HZ * sleep_time) / 1000);
761 else {
Len Brown4be44fc2005-08-05 00:44:28 -0400762 tz->timer.data = (unsigned long)tz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 tz->timer.function = acpi_thermal_run;
764 tz->timer.expires = jiffies + (HZ * sleep_time) / 1000;
765 add_timer(&(tz->timer));
766 }
767 }
768
Patrick Mocheld550d982006-06-27 00:41:40 -0400769 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770}
771
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772/* --------------------------------------------------------------------------
773 FS Interface (/proc)
774 -------------------------------------------------------------------------- */
775
Len Brown4be44fc2005-08-05 00:44:28 -0400776static struct proc_dir_entry *acpi_thermal_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
778static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset)
779{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200780 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
783 if (!tz)
784 goto end;
785
786 seq_puts(seq, "state: ");
787
Len Brown4be44fc2005-08-05 00:44:28 -0400788 if (!tz->state.critical && !tz->state.hot && !tz->state.passive
789 && !tz->state.active)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 seq_puts(seq, "ok\n");
791 else {
792 if (tz->state.critical)
793 seq_puts(seq, "critical ");
794 if (tz->state.hot)
795 seq_puts(seq, "hot ");
796 if (tz->state.passive)
797 seq_puts(seq, "passive ");
798 if (tz->state.active)
799 seq_printf(seq, "active[%d]", tz->state.active_index);
800 seq_puts(seq, "\n");
801 }
802
Len Brown4be44fc2005-08-05 00:44:28 -0400803 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400804 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805}
806
807static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file)
808{
809 return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data);
810}
811
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset)
813{
Len Brown4be44fc2005-08-05 00:44:28 -0400814 int result = 0;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200815 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
818 if (!tz)
819 goto end;
820
821 result = acpi_thermal_get_temperature(tz);
822 if (result)
823 goto end;
824
Len Brown4be44fc2005-08-05 00:44:28 -0400825 seq_printf(seq, "temperature: %ld C\n",
826 KELVIN_TO_CELSIUS(tz->temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Len Brown4be44fc2005-08-05 00:44:28 -0400828 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400829 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830}
831
832static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file)
833{
834 return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data);
835}
836
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset)
838{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200839 struct acpi_thermal *tz = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400840 int i = 0;
841 int j = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
844 if (!tz)
845 goto end;
846
847 if (tz->trips.critical.flags.valid)
848 seq_printf(seq, "critical (S5): %ld C\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400849 KELVIN_TO_CELSIUS(tz->trips.critical.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850
851 if (tz->trips.hot.flags.valid)
852 seq_printf(seq, "hot (S4): %ld C\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400853 KELVIN_TO_CELSIUS(tz->trips.hot.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
855 if (tz->trips.passive.flags.valid) {
Len Brown4be44fc2005-08-05 00:44:28 -0400856 seq_printf(seq,
857 "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=",
858 KELVIN_TO_CELSIUS(tz->trips.passive.temperature),
859 tz->trips.passive.tc1, tz->trips.passive.tc2,
860 tz->trips.passive.tsp);
861 for (j = 0; j < tz->trips.passive.devices.count; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
Len Brown4be44fc2005-08-05 00:44:28 -0400863 seq_printf(seq, "0x%p ",
864 tz->trips.passive.devices.handles[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 }
866 seq_puts(seq, "\n");
867 }
868
869 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
870 if (!(tz->trips.active[i].flags.valid))
871 break;
872 seq_printf(seq, "active[%d]: %ld C: devices=",
Len Brown4be44fc2005-08-05 00:44:28 -0400873 i,
874 KELVIN_TO_CELSIUS(tz->trips.active[i].temperature));
875 for (j = 0; j < tz->trips.active[i].devices.count; j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 seq_printf(seq, "0x%p ",
Len Brown4be44fc2005-08-05 00:44:28 -0400877 tz->trips.active[i].devices.handles[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 seq_puts(seq, "\n");
879 }
880
Len Brown4be44fc2005-08-05 00:44:28 -0400881 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400882 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883}
884
885static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file)
886{
887 return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data);
888}
889
890static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400891acpi_thermal_write_trip_points(struct file *file,
892 const char __user * buffer,
893 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200895 struct seq_file *m = file->private_data;
896 struct acpi_thermal *tz = m->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Len Brown4be44fc2005-08-05 00:44:28 -0400898 char *limit_string;
899 int num, critical, hot, passive;
900 int *active;
901 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
Burman Yan36bcbec2006-12-19 12:56:11 -0800904 limit_string = kzalloc(ACPI_THERMAL_MAX_LIMIT_STR_LEN, GFP_KERNEL);
Len Brown4be44fc2005-08-05 00:44:28 -0400905 if (!limit_string)
Patrick Mocheld550d982006-06-27 00:41:40 -0400906 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Len Brown4be44fc2005-08-05 00:44:28 -0400908 active = kmalloc(ACPI_THERMAL_MAX_ACTIVE * sizeof(int), GFP_KERNEL);
Dave Jonesfdc136c2006-03-08 22:12:00 -0500909 if (!active) {
910 kfree(limit_string);
Patrick Mocheld550d982006-06-27 00:41:40 -0400911 return -ENOMEM;
Dave Jonesfdc136c2006-03-08 22:12:00 -0500912 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
914 if (!tz || (count > ACPI_THERMAL_MAX_LIMIT_STR_LEN - 1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 count = -EINVAL;
916 goto end;
917 }
Len Brown4be44fc2005-08-05 00:44:28 -0400918
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 if (copy_from_user(limit_string, buffer, count)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 count = -EFAULT;
921 goto end;
922 }
Len Brown4be44fc2005-08-05 00:44:28 -0400923
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 limit_string[count] = '\0';
925
926 num = sscanf(limit_string, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
Len Brown4be44fc2005-08-05 00:44:28 -0400927 &critical, &hot, &passive,
928 &active[0], &active[1], &active[2], &active[3], &active[4],
929 &active[5], &active[6], &active[7], &active[8],
930 &active[9]);
931 if (!(num >= 5 && num < (ACPI_THERMAL_MAX_ACTIVE + 3))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 count = -EINVAL;
933 goto end;
934 }
935
936 tz->trips.critical.temperature = CELSIUS_TO_KELVIN(critical);
937 tz->trips.hot.temperature = CELSIUS_TO_KELVIN(hot);
938 tz->trips.passive.temperature = CELSIUS_TO_KELVIN(passive);
939 for (i = 0; i < num - 3; i++) {
940 if (!(tz->trips.active[i].flags.valid))
941 break;
942 tz->trips.active[i].temperature = CELSIUS_TO_KELVIN(active[i]);
943 }
Len Brown4be44fc2005-08-05 00:44:28 -0400944
945 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 kfree(active);
947 kfree(limit_string);
Patrick Mocheld550d982006-06-27 00:41:40 -0400948 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949}
950
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset)
952{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200953 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
956 if (!tz)
957 goto end;
958
959 if (!tz->flags.cooling_mode) {
960 seq_puts(seq, "<setting not supported>\n");
961 }
962
Len Brown4be44fc2005-08-05 00:44:28 -0400963 if (tz->cooling_mode == ACPI_THERMAL_MODE_CRITICAL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 seq_printf(seq, "cooling mode: critical\n");
965 else
966 seq_printf(seq, "cooling mode: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400967 tz->cooling_mode ? "passive" : "active");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
Len Brown4be44fc2005-08-05 00:44:28 -0400969 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400970 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971}
972
973static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file)
974{
975 return single_open(file, acpi_thermal_cooling_seq_show,
Len Brown4be44fc2005-08-05 00:44:28 -0400976 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977}
978
979static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400980acpi_thermal_write_cooling_mode(struct file *file,
981 const char __user * buffer,
982 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200984 struct seq_file *m = file->private_data;
985 struct acpi_thermal *tz = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400986 int result = 0;
987 char mode_string[12] = { '\0' };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989
990 if (!tz || (count > sizeof(mode_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400991 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992
993 if (!tz->flags.cooling_mode)
Patrick Mocheld550d982006-06-27 00:41:40 -0400994 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
996 if (copy_from_user(mode_string, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400997 return -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400998
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 mode_string[count] = '\0';
Len Brown4be44fc2005-08-05 00:44:28 -04001000
1001 result = acpi_thermal_set_cooling_mode(tz,
1002 simple_strtoul(mode_string, NULL,
1003 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001005 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006
1007 acpi_thermal_check(tz);
1008
Patrick Mocheld550d982006-06-27 00:41:40 -04001009 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010}
1011
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset)
1013{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001014 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
1017 if (!tz)
1018 goto end;
1019
1020 if (!tz->polling_frequency) {
1021 seq_puts(seq, "<polling disabled>\n");
1022 goto end;
1023 }
1024
1025 seq_printf(seq, "polling frequency: %lu seconds\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001026 (tz->polling_frequency / 10));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
Len Brown4be44fc2005-08-05 00:44:28 -04001028 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001029 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030}
1031
1032static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file)
1033{
1034 return single_open(file, acpi_thermal_polling_seq_show,
Len Brown4be44fc2005-08-05 00:44:28 -04001035 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036}
1037
1038static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001039acpi_thermal_write_polling(struct file *file,
1040 const char __user * buffer,
1041 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001043 struct seq_file *m = file->private_data;
1044 struct acpi_thermal *tz = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001045 int result = 0;
1046 char polling_string[12] = { '\0' };
1047 int seconds = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
1050 if (!tz || (count > sizeof(polling_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -04001051 return -EINVAL;
Len Brown4be44fc2005-08-05 00:44:28 -04001052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 if (copy_from_user(polling_string, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001054 return -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -04001055
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 polling_string[count] = '\0';
1057
1058 seconds = simple_strtoul(polling_string, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -04001059
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 result = acpi_thermal_set_polling(tz, seconds);
1061 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001062 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
1064 acpi_thermal_check(tz);
1065
Patrick Mocheld550d982006-06-27 00:41:40 -04001066 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067}
1068
Len Brown4be44fc2005-08-05 00:44:28 -04001069static int acpi_thermal_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070{
Len Brown4be44fc2005-08-05 00:44:28 -04001071 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
1074 if (!acpi_device_dir(device)) {
1075 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -04001076 acpi_thermal_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001078 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 acpi_device_dir(device)->owner = THIS_MODULE;
1080 }
1081
1082 /* 'state' [R] */
1083 entry = create_proc_entry(ACPI_THERMAL_FILE_STATE,
Len Brown4be44fc2005-08-05 00:44:28 -04001084 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001086 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 else {
1088 entry->proc_fops = &acpi_thermal_state_fops;
1089 entry->data = acpi_driver_data(device);
1090 entry->owner = THIS_MODULE;
1091 }
1092
1093 /* 'temperature' [R] */
1094 entry = create_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE,
Len Brown4be44fc2005-08-05 00:44:28 -04001095 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001097 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 else {
1099 entry->proc_fops = &acpi_thermal_temp_fops;
1100 entry->data = acpi_driver_data(device);
1101 entry->owner = THIS_MODULE;
1102 }
1103
1104 /* 'trip_points' [R/W] */
1105 entry = create_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS,
Len Brown4be44fc2005-08-05 00:44:28 -04001106 S_IFREG | S_IRUGO | S_IWUSR,
1107 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001109 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 else {
1111 entry->proc_fops = &acpi_thermal_trip_fops;
1112 entry->data = acpi_driver_data(device);
1113 entry->owner = THIS_MODULE;
1114 }
1115
1116 /* 'cooling_mode' [R/W] */
1117 entry = create_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE,
Len Brown4be44fc2005-08-05 00:44:28 -04001118 S_IFREG | S_IRUGO | S_IWUSR,
1119 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001121 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 else {
1123 entry->proc_fops = &acpi_thermal_cooling_fops;
1124 entry->data = acpi_driver_data(device);
1125 entry->owner = THIS_MODULE;
1126 }
1127
1128 /* 'polling_frequency' [R/W] */
1129 entry = create_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ,
Len Brown4be44fc2005-08-05 00:44:28 -04001130 S_IFREG | S_IRUGO | S_IWUSR,
1131 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001133 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 else {
1135 entry->proc_fops = &acpi_thermal_polling_fops;
1136 entry->data = acpi_driver_data(device);
1137 entry->owner = THIS_MODULE;
1138 }
1139
Patrick Mocheld550d982006-06-27 00:41:40 -04001140 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141}
1142
Len Brown4be44fc2005-08-05 00:44:28 -04001143static int acpi_thermal_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
1146 if (acpi_device_dir(device)) {
1147 remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ,
1148 acpi_device_dir(device));
1149 remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE,
1150 acpi_device_dir(device));
1151 remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS,
1152 acpi_device_dir(device));
1153 remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE,
1154 acpi_device_dir(device));
1155 remove_proc_entry(ACPI_THERMAL_FILE_STATE,
1156 acpi_device_dir(device));
1157 remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir);
1158 acpi_device_dir(device) = NULL;
1159 }
1160
Patrick Mocheld550d982006-06-27 00:41:40 -04001161 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162}
1163
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164/* --------------------------------------------------------------------------
1165 Driver Interface
1166 -------------------------------------------------------------------------- */
1167
Len Brown4be44fc2005-08-05 00:44:28 -04001168static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001170 struct acpi_thermal *tz = data;
Len Brown4be44fc2005-08-05 00:44:28 -04001171 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173
1174 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001175 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176
Patrick Mochel8348e1b2006-05-19 16:54:40 -04001177 device = tz->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
1179 switch (event) {
1180 case ACPI_THERMAL_NOTIFY_TEMPERATURE:
1181 acpi_thermal_check(tz);
1182 break;
1183 case ACPI_THERMAL_NOTIFY_THRESHOLDS:
1184 acpi_thermal_get_trip_points(tz);
1185 acpi_thermal_check(tz);
1186 acpi_bus_generate_event(device, event, 0);
1187 break;
1188 case ACPI_THERMAL_NOTIFY_DEVICES:
1189 if (tz->flags.devices)
1190 acpi_thermal_get_devices(tz);
1191 acpi_bus_generate_event(device, event, 0);
1192 break;
1193 default:
1194 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001195 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 break;
1197 }
1198
Patrick Mocheld550d982006-06-27 00:41:40 -04001199 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200}
1201
Len Brown4be44fc2005-08-05 00:44:28 -04001202static int acpi_thermal_get_info(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203{
Len Brown4be44fc2005-08-05 00:44:28 -04001204 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
1207 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001208 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209
1210 /* Get temperature [_TMP] (required) */
1211 result = acpi_thermal_get_temperature(tz);
1212 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001213 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214
1215 /* Get trip points [_CRT, _PSV, etc.] (required) */
1216 result = acpi_thermal_get_trip_points(tz);
1217 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001218 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
1220 /* Set the cooling mode [_SCP] to active cooling (default) */
1221 result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
Len Brown4be44fc2005-08-05 00:44:28 -04001222 if (!result)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 tz->flags.cooling_mode = 1;
Len Brown4be44fc2005-08-05 00:44:28 -04001224 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 /* Oh,we have not _SCP method.
Len Brown4be44fc2005-08-05 00:44:28 -04001226 Generally show cooling_mode by _ACx, _PSV,spec 12.2 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 tz->flags.cooling_mode = 0;
Len Brown4be44fc2005-08-05 00:44:28 -04001228 if (tz->trips.active[0].flags.valid
1229 && tz->trips.passive.flags.valid) {
1230 if (tz->trips.passive.temperature >
1231 tz->trips.active[0].temperature)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE;
Len Brown4be44fc2005-08-05 00:44:28 -04001233 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE;
Len Brown4be44fc2005-08-05 00:44:28 -04001235 } else if (!tz->trips.active[0].flags.valid
1236 && tz->trips.passive.flags.valid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE;
Len Brown4be44fc2005-08-05 00:44:28 -04001238 } else if (tz->trips.active[0].flags.valid
1239 && !tz->trips.passive.flags.valid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE;
1241 } else {
1242 /* _ACx and _PSV are optional, but _CRT is required */
1243 tz->cooling_mode = ACPI_THERMAL_MODE_CRITICAL;
1244 }
1245 }
1246
1247 /* Get default polling frequency [_TZP] (optional) */
1248 if (tzp)
1249 tz->polling_frequency = tzp;
1250 else
1251 acpi_thermal_get_polling_frequency(tz);
1252
1253 /* Get devices in this thermal zone [_TZD] (optional) */
1254 result = acpi_thermal_get_devices(tz);
1255 if (!result)
1256 tz->flags.devices = 1;
1257
Patrick Mocheld550d982006-06-27 00:41:40 -04001258 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259}
1260
Len Brown4be44fc2005-08-05 00:44:28 -04001261static int acpi_thermal_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262{
Len Brown4be44fc2005-08-05 00:44:28 -04001263 int result = 0;
1264 acpi_status status = AE_OK;
1265 struct acpi_thermal *tz = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267
1268 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001269 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
Burman Yan36bcbec2006-12-19 12:56:11 -08001271 tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001273 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Patrick Mochel8348e1b2006-05-19 16:54:40 -04001275 tz->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 strcpy(tz->name, device->pnp.bus_id);
1277 strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME);
1278 strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
1279 acpi_driver_data(device) = tz;
1280
1281 result = acpi_thermal_get_info(tz);
1282 if (result)
1283 goto end;
1284
1285 result = acpi_thermal_add_fs(device);
1286 if (result)
Vasily Averin09047e72006-04-27 05:25:00 -04001287 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289 init_timer(&tz->timer);
1290
1291 acpi_thermal_check(tz);
1292
Patrick Mochel38ba7c92006-05-19 16:54:48 -04001293 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001294 ACPI_DEVICE_NOTIFY,
1295 acpi_thermal_notify, tz);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 if (ACPI_FAILURE(status)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 result = -ENODEV;
1298 goto end;
1299 }
1300
1301 printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001302 acpi_device_name(device), acpi_device_bid(device),
1303 KELVIN_TO_CELSIUS(tz->temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
Len Brown4be44fc2005-08-05 00:44:28 -04001305 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 if (result) {
1307 acpi_thermal_remove_fs(device);
1308 kfree(tz);
1309 }
1310
Patrick Mocheld550d982006-06-27 00:41:40 -04001311 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312}
1313
Len Brown4be44fc2005-08-05 00:44:28 -04001314static int acpi_thermal_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315{
Len Brown4be44fc2005-08-05 00:44:28 -04001316 acpi_status status = AE_OK;
1317 struct acpi_thermal *tz = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319
1320 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001321 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001323 tz = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324
1325 /* avoid timer adding new defer task */
1326 tz->zombie = 1;
1327 /* wait for running timer (on other CPUs) finish */
1328 del_timer_sync(&(tz->timer));
1329 /* synchronize deferred task */
1330 acpi_os_wait_events_complete(NULL);
1331 /* deferred task may reinsert timer */
1332 del_timer_sync(&(tz->timer));
1333
Patrick Mochel38ba7c92006-05-19 16:54:48 -04001334 status = acpi_remove_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001335 ACPI_DEVICE_NOTIFY,
1336 acpi_thermal_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
1338 /* Terminate policy */
Len Brown4be44fc2005-08-05 00:44:28 -04001339 if (tz->trips.passive.flags.valid && tz->trips.passive.flags.enabled) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 tz->trips.passive.flags.enabled = 0;
1341 acpi_thermal_passive(tz);
1342 }
1343 if (tz->trips.active[0].flags.valid
Len Brown4be44fc2005-08-05 00:44:28 -04001344 && tz->trips.active[0].flags.enabled) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 tz->trips.active[0].flags.enabled = 0;
1346 acpi_thermal_active(tz);
1347 }
1348
1349 acpi_thermal_remove_fs(device);
1350
1351 kfree(tz);
Patrick Mocheld550d982006-06-27 00:41:40 -04001352 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353}
1354
Patrick Mochel5d9464a2006-12-07 20:56:27 +08001355static int acpi_thermal_resume(struct acpi_device *device)
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001356{
1357 struct acpi_thermal *tz = NULL;
Konstantin Karasyovbed936f2006-07-10 04:44:26 -07001358 int i;
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001359
1360 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001361 return -EINVAL;
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001362
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001363 tz = acpi_driver_data(device);
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001364
Konstantin Karasyovbed936f2006-07-10 04:44:26 -07001365 acpi_thermal_get_temperature(tz);
1366
1367 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
1368 if (tz->trips.active[i].flags.valid) {
1369 tz->temperature = tz->trips.active[i].temperature;
1370 tz->trips.active[i].flags.enabled = 0;
1371
1372 acpi_thermal_active(tz);
1373
1374 tz->state.active |= tz->trips.active[i].flags.enabled;
1375 tz->state.active_index = i;
1376 }
1377 }
1378
1379 acpi_thermal_check(tz);
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001380
1381 return AE_OK;
1382}
1383
Len Brown4be44fc2005-08-05 00:44:28 -04001384static int __init acpi_thermal_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385{
Len Brown4be44fc2005-08-05 00:44:28 -04001386 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
1389 acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir);
1390 if (!acpi_thermal_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001391 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 acpi_thermal_dir->owner = THIS_MODULE;
1393
1394 result = acpi_bus_register_driver(&acpi_thermal_driver);
1395 if (result < 0) {
1396 remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001397 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 }
1399
Patrick Mocheld550d982006-06-27 00:41:40 -04001400 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401}
1402
Len Brown4be44fc2005-08-05 00:44:28 -04001403static void __exit acpi_thermal_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405
1406 acpi_bus_unregister_driver(&acpi_thermal_driver);
1407
1408 remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
1409
Patrick Mocheld550d982006-06-27 00:41:40 -04001410 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411}
1412
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413module_init(acpi_thermal_init);
1414module_exit(acpi_thermal_exit);