blob: 73b6fe7093e99b6247056fec5f8d6ddbdc08ea9f [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>
Tim Schmielaucd354f12007-02-14 00:33:14 -080039#include <linux/timer.h>
40#include <linux/jiffies.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/kmod.h>
42#include <linux/seq_file.h>
43#include <asm/uaccess.h>
44
45#include <acpi/acpi_bus.h>
46#include <acpi/acpi_drivers.h>
47
48#define ACPI_THERMAL_COMPONENT 0x04000000
49#define ACPI_THERMAL_CLASS "thermal_zone"
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone"
51#define ACPI_THERMAL_FILE_STATE "state"
52#define ACPI_THERMAL_FILE_TEMPERATURE "temperature"
53#define ACPI_THERMAL_FILE_TRIP_POINTS "trip_points"
54#define ACPI_THERMAL_FILE_COOLING_MODE "cooling_mode"
55#define ACPI_THERMAL_FILE_POLLING_FREQ "polling_frequency"
56#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80
57#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81
58#define ACPI_THERMAL_NOTIFY_DEVICES 0x82
59#define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0
60#define ACPI_THERMAL_NOTIFY_HOT 0xF1
61#define ACPI_THERMAL_MODE_ACTIVE 0x00
62#define ACPI_THERMAL_MODE_PASSIVE 0x01
63#define ACPI_THERMAL_MODE_CRITICAL 0xff
64#define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff"
65
66#define ACPI_THERMAL_MAX_ACTIVE 10
67#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
68
69#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
70#define CELSIUS_TO_KELVIN(t) ((t+273)*10)
71
72#define _COMPONENT ACPI_THERMAL_COMPONENT
Len Brownf52fd662007-02-12 22:42:12 -050073ACPI_MODULE_NAME("thermal");
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
Thomas Renninger1cbf4c52004-09-16 11:07:00 -040075MODULE_AUTHOR("Paul Diefenbaugh");
Len Brown7cda93e2007-02-12 23:50:02 -050076MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070077MODULE_LICENSE("GPL");
78
79static int tzp;
80module_param(tzp, int, 0);
81MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n");
82
Len Brown4be44fc2005-08-05 00:44:28 -040083static int acpi_thermal_add(struct acpi_device *device);
84static int acpi_thermal_remove(struct acpi_device *device, int type);
Patrick Mochel5d9464a2006-12-07 20:56:27 +080085static int acpi_thermal_resume(struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file);
87static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file);
88static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file);
Len Brown4be44fc2005-08-05 00:44:28 -040090static ssize_t acpi_thermal_write_cooling_mode(struct file *,
91 const char __user *, size_t,
92 loff_t *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file);
Len Brown4be44fc2005-08-05 00:44:28 -040094static ssize_t acpi_thermal_write_polling(struct file *, const char __user *,
95 size_t, loff_t *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
97static struct acpi_driver acpi_thermal_driver = {
Len Brownc2b67052007-02-12 23:33:40 -050098 .name = "thermal",
Len Brown4be44fc2005-08-05 00:44:28 -040099 .class = ACPI_THERMAL_CLASS,
100 .ids = ACPI_THERMAL_HID,
101 .ops = {
102 .add = acpi_thermal_add,
103 .remove = acpi_thermal_remove,
Konstantin Karasyov74ce1462006-05-08 08:32:00 -0400104 .resume = acpi_thermal_resume,
Len Brown4be44fc2005-08-05 00:44:28 -0400105 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106};
107
108struct acpi_thermal_state {
Len Brown4be44fc2005-08-05 00:44:28 -0400109 u8 critical:1;
110 u8 hot:1;
111 u8 passive:1;
112 u8 active:1;
113 u8 reserved:4;
114 int active_index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
117struct acpi_thermal_state_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400118 u8 valid:1;
119 u8 enabled:1;
120 u8 reserved:6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121};
122
123struct acpi_thermal_critical {
124 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400125 unsigned long temperature;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126};
127
128struct acpi_thermal_hot {
129 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400130 unsigned long temperature;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131};
132
133struct acpi_thermal_passive {
134 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400135 unsigned long temperature;
136 unsigned long tc1;
137 unsigned long tc2;
138 unsigned long tsp;
139 struct acpi_handle_list devices;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140};
141
142struct acpi_thermal_active {
143 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400144 unsigned long temperature;
145 struct acpi_handle_list devices;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146};
147
148struct acpi_thermal_trips {
149 struct acpi_thermal_critical critical;
Len Brown4be44fc2005-08-05 00:44:28 -0400150 struct acpi_thermal_hot hot;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 struct acpi_thermal_passive passive;
152 struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
153};
154
155struct acpi_thermal_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400156 u8 cooling_mode:1; /* _SCP */
157 u8 devices:1; /* _TZD */
158 u8 reserved:6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159};
160
161struct acpi_thermal {
Patrick Mochel8348e1b2006-05-19 16:54:40 -0400162 struct acpi_device * device;
Len Brown4be44fc2005-08-05 00:44:28 -0400163 acpi_bus_id name;
164 unsigned long temperature;
165 unsigned long last_temperature;
166 unsigned long polling_frequency;
167 u8 cooling_mode;
168 volatile u8 zombie;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 struct acpi_thermal_flags flags;
170 struct acpi_thermal_state state;
171 struct acpi_thermal_trips trips;
Len Brown4be44fc2005-08-05 00:44:28 -0400172 struct acpi_handle_list devices;
173 struct timer_list timer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174};
175
Arjan van de Vend7508032006-07-04 13:06:00 -0400176static const struct file_operations acpi_thermal_state_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400177 .open = acpi_thermal_state_open_fs,
178 .read = seq_read,
179 .llseek = seq_lseek,
180 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181};
182
Arjan van de Vend7508032006-07-04 13:06:00 -0400183static const struct file_operations acpi_thermal_temp_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400184 .open = acpi_thermal_temp_open_fs,
185 .read = seq_read,
186 .llseek = seq_lseek,
187 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188};
189
Arjan van de Vend7508032006-07-04 13:06:00 -0400190static const struct file_operations acpi_thermal_trip_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400191 .open = acpi_thermal_trip_open_fs,
192 .read = seq_read,
Len Brown4be44fc2005-08-05 00:44:28 -0400193 .llseek = seq_lseek,
194 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195};
196
Arjan van de Vend7508032006-07-04 13:06:00 -0400197static const struct file_operations acpi_thermal_cooling_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400198 .open = acpi_thermal_cooling_open_fs,
199 .read = seq_read,
200 .write = acpi_thermal_write_cooling_mode,
201 .llseek = seq_lseek,
202 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203};
204
Arjan van de Vend7508032006-07-04 13:06:00 -0400205static const struct file_operations acpi_thermal_polling_fops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400206 .open = acpi_thermal_polling_open_fs,
207 .read = seq_read,
208 .write = acpi_thermal_write_polling,
209 .llseek = seq_lseek,
210 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211};
212
213/* --------------------------------------------------------------------------
214 Thermal Zone Management
215 -------------------------------------------------------------------------- */
216
Len Brown4be44fc2005-08-05 00:44:28 -0400217static int acpi_thermal_get_temperature(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218{
Len Brown4be44fc2005-08-05 00:44:28 -0400219 acpi_status status = AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
222 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400223 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
225 tz->last_temperature = tz->temperature;
226
Len Brown4be44fc2005-08-05 00:44:28 -0400227 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400228 acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tz->temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400230 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
Len Brown4be44fc2005-08-05 00:44:28 -0400232 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n",
233 tz->temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
Patrick Mocheld550d982006-06-27 00:41:40 -0400235 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236}
237
Len Brown4be44fc2005-08-05 00:44:28 -0400238static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239{
Len Brown4be44fc2005-08-05 00:44:28 -0400240 acpi_status status = AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
243 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400244 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Len Brown4be44fc2005-08-05 00:44:28 -0400246 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400247 acpi_evaluate_integer(tz->device->handle, "_TZP", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400248 &tz->polling_frequency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400250 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Len Brown4be44fc2005-08-05 00:44:28 -0400252 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n",
253 tz->polling_frequency));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
Patrick Mocheld550d982006-06-27 00:41:40 -0400255 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256}
257
Len Brown4be44fc2005-08-05 00:44:28 -0400258static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
261 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400262 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 tz->polling_frequency = seconds * 10; /* Convert value to deci-seconds */
265
Len Brown4be44fc2005-08-05 00:44:28 -0400266 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
267 "Polling frequency set to %lu seconds\n",
Sanjoy Mahajan636cedf2007-02-16 01:24:43 -0500268 tz->polling_frequency/10));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
Patrick Mocheld550d982006-06-27 00:41:40 -0400270 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271}
272
Len Brown4be44fc2005-08-05 00:44:28 -0400273static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Len Brown4be44fc2005-08-05 00:44:28 -0400275 acpi_status status = AE_OK;
276 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
277 struct acpi_object_list arg_list = { 1, &arg0 };
278 acpi_handle handle = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
281 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400282 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400284 status = acpi_get_handle(tz->device->handle, "_SCP", &handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 if (ACPI_FAILURE(status)) {
286 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400287 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 }
289
290 arg0.integer.value = mode;
291
292 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
293 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400294 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 tz->cooling_mode = mode;
297
Len Brown4be44fc2005-08-05 00:44:28 -0400298 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling mode [%s]\n",
299 mode ? "passive" : "active"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
Patrick Mocheld550d982006-06-27 00:41:40 -0400301 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302}
303
Len Brown4be44fc2005-08-05 00:44:28 -0400304static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305{
Len Brown4be44fc2005-08-05 00:44:28 -0400306 acpi_status status = AE_OK;
307 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400311 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 /* Critical Shutdown (required) */
314
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400315 status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400316 &tz->trips.critical.temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 if (ACPI_FAILURE(status)) {
318 tz->trips.critical.flags.valid = 0;
Thomas Renningera6fc6722006-06-26 23:58:43 -0400319 ACPI_EXCEPTION((AE_INFO, status, "No critical threshold"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400320 return -ENODEV;
Len Brown4be44fc2005-08-05 00:44:28 -0400321 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 tz->trips.critical.flags.valid = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400323 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
324 "Found critical threshold [%lu]\n",
325 tz->trips.critical.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
327
328 /* Critical Sleep (optional) */
329
Len Brown4be44fc2005-08-05 00:44:28 -0400330 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400331 acpi_evaluate_integer(tz->device->handle, "_HOT", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400332 &tz->trips.hot.temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 if (ACPI_FAILURE(status)) {
334 tz->trips.hot.flags.valid = 0;
335 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n"));
Len Brown4be44fc2005-08-05 00:44:28 -0400336 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 tz->trips.hot.flags.valid = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400338 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n",
339 tz->trips.hot.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 }
341
342 /* Passive: Processors (optional) */
343
Len Brown4be44fc2005-08-05 00:44:28 -0400344 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400345 acpi_evaluate_integer(tz->device->handle, "_PSV", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400346 &tz->trips.passive.temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 if (ACPI_FAILURE(status)) {
348 tz->trips.passive.flags.valid = 0;
349 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n"));
Len Brown4be44fc2005-08-05 00:44:28 -0400350 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 tz->trips.passive.flags.valid = 1;
352
Len Brown4be44fc2005-08-05 00:44:28 -0400353 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400354 acpi_evaluate_integer(tz->device->handle, "_TC1", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400355 &tz->trips.passive.tc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 if (ACPI_FAILURE(status))
357 tz->trips.passive.flags.valid = 0;
358
Len Brown4be44fc2005-08-05 00:44:28 -0400359 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400360 acpi_evaluate_integer(tz->device->handle, "_TC2", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400361 &tz->trips.passive.tc2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 if (ACPI_FAILURE(status))
363 tz->trips.passive.flags.valid = 0;
364
Len Brown4be44fc2005-08-05 00:44:28 -0400365 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400366 acpi_evaluate_integer(tz->device->handle, "_TSP", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400367 &tz->trips.passive.tsp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 if (ACPI_FAILURE(status))
369 tz->trips.passive.flags.valid = 0;
370
Len Brown4be44fc2005-08-05 00:44:28 -0400371 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400372 acpi_evaluate_reference(tz->device->handle, "_PSL", NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400373 &tz->trips.passive.devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 if (ACPI_FAILURE(status))
375 tz->trips.passive.flags.valid = 0;
376
377 if (!tz->trips.passive.flags.valid)
Len Browncece9292006-06-26 23:04:31 -0400378 printk(KERN_WARNING PREFIX "Invalid passive threshold\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 else
Len Brown4be44fc2005-08-05 00:44:28 -0400380 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
381 "Found passive threshold [%lu]\n",
382 tz->trips.passive.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 }
384
385 /* Active: Fans, etc. (optional) */
386
Len Brown4be44fc2005-08-05 00:44:28 -0400387 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Len Brown4be44fc2005-08-05 00:44:28 -0400389 char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
Len Brown4be44fc2005-08-05 00:44:28 -0400391 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400392 acpi_evaluate_integer(tz->device->handle, name, NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400393 &tz->trips.active[i].temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 if (ACPI_FAILURE(status))
395 break;
396
397 name[2] = 'L';
Len Brown4be44fc2005-08-05 00:44:28 -0400398 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400399 acpi_evaluate_reference(tz->device->handle, name, NULL,
Len Brown4be44fc2005-08-05 00:44:28 -0400400 &tz->trips.active[i].devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 if (ACPI_SUCCESS(status)) {
402 tz->trips.active[i].flags.valid = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400403 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
404 "Found active threshold [%d]:[%lu]\n",
405 i, tz->trips.active[i].temperature));
406 } else
Thomas Renningera6fc6722006-06-26 23:58:43 -0400407 ACPI_EXCEPTION((AE_INFO, status,
408 "Invalid active threshold [%d]", i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 }
410
Patrick Mocheld550d982006-06-27 00:41:40 -0400411 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412}
413
Len Brown4be44fc2005-08-05 00:44:28 -0400414static int acpi_thermal_get_devices(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415{
Len Brown4be44fc2005-08-05 00:44:28 -0400416 acpi_status status = AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400420 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
Len Brown4be44fc2005-08-05 00:44:28 -0400422 status =
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400423 acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400425 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
Patrick Mocheld550d982006-06-27 00:41:40 -0400427 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428}
429
Len Brown4be44fc2005-08-05 00:44:28 -0400430static int acpi_thermal_call_usermode(char *path)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
Len Brown4be44fc2005-08-05 00:44:28 -0400432 char *argv[2] = { NULL, NULL };
433 char *envp[3] = { NULL, NULL, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
436 if (!path)
Patrick Mocheld550d982006-06-27 00:41:40 -0400437 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
439 argv[0] = path;
440
441 /* minimal command environment */
442 envp[0] = "HOME=/";
443 envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
Len Brown4be44fc2005-08-05 00:44:28 -0400444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 call_usermodehelper(argv[0], argv, envp, 0);
446
Patrick Mocheld550d982006-06-27 00:41:40 -0400447 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448}
449
Len Brown4be44fc2005-08-05 00:44:28 -0400450static int acpi_thermal_critical(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 if (!tz || !tz->trips.critical.flags.valid)
Patrick Mocheld550d982006-06-27 00:41:40 -0400453 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
455 if (tz->temperature >= tz->trips.critical.temperature) {
Len Browncece9292006-06-26 23:04:31 -0400456 printk(KERN_WARNING PREFIX "Critical trip point\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 tz->trips.critical.flags.enabled = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400458 } else if (tz->trips.critical.flags.enabled)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 tz->trips.critical.flags.enabled = 0;
460
Len Brown4be44fc2005-08-05 00:44:28 -0400461 printk(KERN_EMERG
462 "Critical temperature reached (%ld C), shutting down.\n",
463 KELVIN_TO_CELSIUS(tz->temperature));
Patrick Mochel8348e1b2006-05-19 16:54:40 -0400464 acpi_bus_generate_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL,
Len Brown4be44fc2005-08-05 00:44:28 -0400465 tz->trips.critical.flags.enabled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
467 acpi_thermal_call_usermode(ACPI_THERMAL_PATH_POWEROFF);
468
Patrick Mocheld550d982006-06-27 00:41:40 -0400469 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470}
471
Len Brown4be44fc2005-08-05 00:44:28 -0400472static int acpi_thermal_hot(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 if (!tz || !tz->trips.hot.flags.valid)
Patrick Mocheld550d982006-06-27 00:41:40 -0400475 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477 if (tz->temperature >= tz->trips.hot.temperature) {
Len Browncece9292006-06-26 23:04:31 -0400478 printk(KERN_WARNING PREFIX "Hot trip point\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 tz->trips.hot.flags.enabled = 1;
Len Brown4be44fc2005-08-05 00:44:28 -0400480 } else if (tz->trips.hot.flags.enabled)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 tz->trips.hot.flags.enabled = 0;
482
Patrick Mochel8348e1b2006-05-19 16:54:40 -0400483 acpi_bus_generate_event(tz->device, ACPI_THERMAL_NOTIFY_HOT,
Len Brown4be44fc2005-08-05 00:44:28 -0400484 tz->trips.hot.flags.enabled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 /* TBD: Call user-mode "sleep(S4)" function */
487
Patrick Mocheld550d982006-06-27 00:41:40 -0400488 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489}
490
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400491static void acpi_thermal_passive(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400493 int result = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 struct acpi_thermal_passive *passive = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400495 int trend = 0;
496 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
499 if (!tz || !tz->trips.passive.flags.valid)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400500 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
502 passive = &(tz->trips.passive);
503
504 /*
505 * Above Trip?
506 * -----------
507 * Calculate the thermal trend (using the passive cooling equation)
508 * and modify the performance limit for all passive cooling devices
509 * accordingly. Note that we assume symmetry.
510 */
511 if (tz->temperature >= passive->temperature) {
Len Brown4be44fc2005-08-05 00:44:28 -0400512 trend =
513 (passive->tc1 * (tz->temperature - tz->last_temperature)) +
514 (passive->tc2 * (tz->temperature - passive->temperature));
515 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
516 "trend[%d]=(tc1[%lu]*(tmp[%lu]-last[%lu]))+(tc2[%lu]*(tmp[%lu]-psv[%lu]))\n",
517 trend, passive->tc1, tz->temperature,
518 tz->last_temperature, passive->tc2,
519 tz->temperature, passive->temperature));
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400520 passive->flags.enabled = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 /* Heating up? */
522 if (trend > 0)
Len Brown4be44fc2005-08-05 00:44:28 -0400523 for (i = 0; i < passive->devices.count; i++)
524 acpi_processor_set_thermal_limit(passive->
525 devices.
526 handles[i],
527 ACPI_PROCESSOR_LIMIT_INCREMENT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 /* Cooling off? */
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400529 else if (trend < 0) {
Len Brown4be44fc2005-08-05 00:44:28 -0400530 for (i = 0; i < passive->devices.count; i++)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400531 /*
532 * assume that we are on highest
533 * freq/lowest thrott and can leave
534 * passive mode, even in error case
535 */
536 if (!acpi_processor_set_thermal_limit
537 (passive->devices.handles[i],
538 ACPI_PROCESSOR_LIMIT_DECREMENT))
539 result = 0;
540 /*
541 * Leave cooling mode, even if the temp might
542 * higher than trip point This is because some
543 * machines might have long thermal polling
544 * frequencies (tsp) defined. We will fall back
545 * into passive mode in next cycle (probably quicker)
546 */
547 if (result) {
548 passive->flags.enabled = 0;
549 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
550 "Disabling passive cooling, still above threshold,"
551 " but we are cooling down\n"));
552 }
553 }
554 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 }
556
557 /*
558 * Below Trip?
559 * -----------
560 * Implement passive cooling hysteresis to slowly increase performance
561 * and avoid thrashing around the passive trip point. Note that we
562 * assume symmetry.
563 */
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400564 if (!passive->flags.enabled)
565 return;
566 for (i = 0; i < passive->devices.count; i++)
567 if (!acpi_processor_set_thermal_limit
568 (passive->devices.handles[i],
569 ACPI_PROCESSOR_LIMIT_DECREMENT))
570 result = 0;
571 if (result) {
572 passive->flags.enabled = 0;
573 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
574 "Disabling passive cooling (zone is cool)\n"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576}
577
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400578static void acpi_thermal_active(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
Len Brown4be44fc2005-08-05 00:44:28 -0400580 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 struct acpi_thermal_active *active = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400582 int i = 0;
583 int j = 0;
584 unsigned long maxtemp = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
587 if (!tz)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400588 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
Len Brown4be44fc2005-08-05 00:44:28 -0400590 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 active = &(tz->trips.active[i]);
592 if (!active || !active->flags.valid)
593 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 if (tz->temperature >= active->temperature) {
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400595 /*
596 * Above Threshold?
597 * ----------------
598 * If not already enabled, turn ON all cooling devices
599 * associated with this active threshold.
600 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 if (active->temperature > maxtemp)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400602 tz->state.active_index = i;
603 maxtemp = active->temperature;
604 if (active->flags.enabled)
605 continue;
606 for (j = 0; j < active->devices.count; j++) {
607 result =
608 acpi_bus_set_power(active->devices.
609 handles[j],
610 ACPI_STATE_D0);
611 if (result) {
Len Browncece9292006-06-26 23:04:31 -0400612 printk(KERN_WARNING PREFIX
613 "Unable to turn cooling device [%p] 'on'\n",
Thomas Renningera6fc6722006-06-26 23:58:43 -0400614 active->devices.
Len Browncece9292006-06-26 23:04:31 -0400615 handles[j]);
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400616 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 }
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400618 active->flags.enabled = 1;
619 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
620 "Cooling device [%p] now 'on'\n",
621 active->devices.handles[j]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 }
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400623 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 }
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400625 if (!active->flags.enabled)
626 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 /*
628 * Below Threshold?
629 * ----------------
630 * Turn OFF all cooling devices associated with this
631 * threshold.
632 */
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400633 for (j = 0; j < active->devices.count; j++) {
634 result = acpi_bus_set_power(active->devices.handles[j],
635 ACPI_STATE_D3);
636 if (result) {
Len Browncece9292006-06-26 23:04:31 -0400637 printk(KERN_WARNING PREFIX
638 "Unable to turn cooling device [%p] 'off'\n",
639 active->devices.handles[j]);
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400640 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 }
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400642 active->flags.enabled = 0;
643 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
644 "Cooling device [%p] now 'off'\n",
645 active->devices.handles[j]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 }
647 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648}
649
Len Brown4be44fc2005-08-05 00:44:28 -0400650static void acpi_thermal_check(void *context);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Len Brown4be44fc2005-08-05 00:44:28 -0400652static void acpi_thermal_run(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653{
654 struct acpi_thermal *tz = (struct acpi_thermal *)data;
655 if (!tz->zombie)
Alexey Starikovskiyb8d35192006-05-05 03:23:00 -0400656 acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657}
658
Len Brown4be44fc2005-08-05 00:44:28 -0400659static void acpi_thermal_check(void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
Len Brown4be44fc2005-08-05 00:44:28 -0400661 int result = 0;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200662 struct acpi_thermal *tz = data;
Len Brown4be44fc2005-08-05 00:44:28 -0400663 unsigned long sleep_time = 0;
664 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 struct acpi_thermal_state state;
666
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
668 if (!tz) {
Len Brown64684632006-06-26 23:41:38 -0400669 printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
Patrick Mocheld550d982006-06-27 00:41:40 -0400670 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 }
672
673 state = tz->state;
674
675 result = acpi_thermal_get_temperature(tz);
676 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400677 return;
Len Brown4be44fc2005-08-05 00:44:28 -0400678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 memset(&tz->state, 0, sizeof(tz->state));
Len Brown4be44fc2005-08-05 00:44:28 -0400680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 /*
682 * Check Trip Points
683 * -----------------
684 * Compare the current temperature to the trip point values to see
685 * if we've entered one of the thermal policy states. Note that
686 * this function determines when a state is entered, but the
687 * individual policy decides when it is exited (e.g. hysteresis).
688 */
689 if (tz->trips.critical.flags.valid)
Len Brown4be44fc2005-08-05 00:44:28 -0400690 state.critical |=
691 (tz->temperature >= tz->trips.critical.temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 if (tz->trips.hot.flags.valid)
693 state.hot |= (tz->temperature >= tz->trips.hot.temperature);
694 if (tz->trips.passive.flags.valid)
Len Brown4be44fc2005-08-05 00:44:28 -0400695 state.passive |=
696 (tz->temperature >= tz->trips.passive.temperature);
697 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 if (tz->trips.active[i].flags.valid)
Len Brown4be44fc2005-08-05 00:44:28 -0400699 state.active |=
700 (tz->temperature >=
701 tz->trips.active[i].temperature);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
703 /*
704 * Invoke Policy
705 * -------------
706 * Separated from the above check to allow individual policy to
707 * determine when to exit a given state.
708 */
709 if (state.critical)
710 acpi_thermal_critical(tz);
711 if (state.hot)
712 acpi_thermal_hot(tz);
713 if (state.passive)
714 acpi_thermal_passive(tz);
715 if (state.active)
716 acpi_thermal_active(tz);
717
718 /*
719 * Calculate State
720 * ---------------
721 * Again, separated from the above two to allow independent policy
722 * decisions.
723 */
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400724 tz->state.critical = tz->trips.critical.flags.enabled;
725 tz->state.hot = tz->trips.hot.flags.enabled;
726 tz->state.passive = tz->trips.passive.flags.enabled;
727 tz->state.active = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400728 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
Thomas Renninger1cbf4c52004-09-16 11:07:00 -0400729 tz->state.active |= tz->trips.active[i].flags.enabled;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
731 /*
732 * Calculate Sleep Time
733 * --------------------
734 * If we're in the passive state, use _TSP's value. Otherwise
735 * use the default polling frequency (e.g. _TZP). If no polling
736 * frequency is specified then we'll wait forever (at least until
737 * a thermal event occurs). Note that _TSP and _TZD values are
738 * given in 1/10th seconds (we must covert to milliseconds).
739 */
740 if (tz->state.passive)
741 sleep_time = tz->trips.passive.tsp * 100;
742 else if (tz->polling_frequency > 0)
743 sleep_time = tz->polling_frequency * 100;
744
Len Brown4be44fc2005-08-05 00:44:28 -0400745 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: temperature[%lu] sleep[%lu]\n",
746 tz->name, tz->temperature, sleep_time));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
748 /*
749 * Schedule Next Poll
750 * ------------------
751 */
752 if (!sleep_time) {
753 if (timer_pending(&(tz->timer)))
754 del_timer(&(tz->timer));
Len Brown4be44fc2005-08-05 00:44:28 -0400755 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 if (timer_pending(&(tz->timer)))
Andrew Morton94e22e12007-04-23 14:41:13 -0700757 mod_timer(&(tz->timer),
758 jiffies + (HZ * sleep_time) / 1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 else {
Len Brown4be44fc2005-08-05 00:44:28 -0400760 tz->timer.data = (unsigned long)tz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 tz->timer.function = acpi_thermal_run;
762 tz->timer.expires = jiffies + (HZ * sleep_time) / 1000;
763 add_timer(&(tz->timer));
764 }
765 }
766
Patrick Mocheld550d982006-06-27 00:41:40 -0400767 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768}
769
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770/* --------------------------------------------------------------------------
771 FS Interface (/proc)
772 -------------------------------------------------------------------------- */
773
Len Brown4be44fc2005-08-05 00:44:28 -0400774static struct proc_dir_entry *acpi_thermal_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775
776static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset)
777{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200778 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
781 if (!tz)
782 goto end;
783
784 seq_puts(seq, "state: ");
785
Len Brown4be44fc2005-08-05 00:44:28 -0400786 if (!tz->state.critical && !tz->state.hot && !tz->state.passive
787 && !tz->state.active)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 seq_puts(seq, "ok\n");
789 else {
790 if (tz->state.critical)
791 seq_puts(seq, "critical ");
792 if (tz->state.hot)
793 seq_puts(seq, "hot ");
794 if (tz->state.passive)
795 seq_puts(seq, "passive ");
796 if (tz->state.active)
797 seq_printf(seq, "active[%d]", tz->state.active_index);
798 seq_puts(seq, "\n");
799 }
800
Len Brown4be44fc2005-08-05 00:44:28 -0400801 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400802 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803}
804
805static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file)
806{
807 return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data);
808}
809
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset)
811{
Len Brown4be44fc2005-08-05 00:44:28 -0400812 int result = 0;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200813 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
816 if (!tz)
817 goto end;
818
819 result = acpi_thermal_get_temperature(tz);
820 if (result)
821 goto end;
822
Len Brown4be44fc2005-08-05 00:44:28 -0400823 seq_printf(seq, "temperature: %ld C\n",
824 KELVIN_TO_CELSIUS(tz->temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Len Brown4be44fc2005-08-05 00:44:28 -0400826 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400827 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828}
829
830static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file)
831{
832 return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data);
833}
834
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset)
836{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200837 struct acpi_thermal *tz = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400838 int i = 0;
839 int j = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 if (!tz)
843 goto end;
844
845 if (tz->trips.critical.flags.valid)
846 seq_printf(seq, "critical (S5): %ld C\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400847 KELVIN_TO_CELSIUS(tz->trips.critical.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848
849 if (tz->trips.hot.flags.valid)
850 seq_printf(seq, "hot (S4): %ld C\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400851 KELVIN_TO_CELSIUS(tz->trips.hot.temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
853 if (tz->trips.passive.flags.valid) {
Len Brown4be44fc2005-08-05 00:44:28 -0400854 seq_printf(seq,
855 "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=",
856 KELVIN_TO_CELSIUS(tz->trips.passive.temperature),
857 tz->trips.passive.tc1, tz->trips.passive.tc2,
858 tz->trips.passive.tsp);
859 for (j = 0; j < tz->trips.passive.devices.count; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Len Brown4be44fc2005-08-05 00:44:28 -0400861 seq_printf(seq, "0x%p ",
862 tz->trips.passive.devices.handles[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 }
864 seq_puts(seq, "\n");
865 }
866
867 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
868 if (!(tz->trips.active[i].flags.valid))
869 break;
870 seq_printf(seq, "active[%d]: %ld C: devices=",
Len Brown4be44fc2005-08-05 00:44:28 -0400871 i,
872 KELVIN_TO_CELSIUS(tz->trips.active[i].temperature));
873 for (j = 0; j < tz->trips.active[i].devices.count; j++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 seq_printf(seq, "0x%p ",
Len Brown4be44fc2005-08-05 00:44:28 -0400875 tz->trips.active[i].devices.handles[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 seq_puts(seq, "\n");
877 }
878
Len Brown4be44fc2005-08-05 00:44:28 -0400879 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400880 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881}
882
883static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file)
884{
885 return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data);
886}
887
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset)
889{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200890 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
893 if (!tz)
894 goto end;
895
896 if (!tz->flags.cooling_mode) {
897 seq_puts(seq, "<setting not supported>\n");
898 }
899
Len Brown4be44fc2005-08-05 00:44:28 -0400900 if (tz->cooling_mode == ACPI_THERMAL_MODE_CRITICAL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 seq_printf(seq, "cooling mode: critical\n");
902 else
903 seq_printf(seq, "cooling mode: %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400904 tz->cooling_mode ? "passive" : "active");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
Len Brown4be44fc2005-08-05 00:44:28 -0400906 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400907 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908}
909
910static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file)
911{
912 return single_open(file, acpi_thermal_cooling_seq_show,
Len Brown4be44fc2005-08-05 00:44:28 -0400913 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914}
915
916static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400917acpi_thermal_write_cooling_mode(struct file *file,
918 const char __user * buffer,
919 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200921 struct seq_file *m = file->private_data;
922 struct acpi_thermal *tz = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400923 int result = 0;
924 char mode_string[12] = { '\0' };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
927 if (!tz || (count > sizeof(mode_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400928 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
930 if (!tz->flags.cooling_mode)
Patrick Mocheld550d982006-06-27 00:41:40 -0400931 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
933 if (copy_from_user(mode_string, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400934 return -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400935
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 mode_string[count] = '\0';
Len Brown4be44fc2005-08-05 00:44:28 -0400937
938 result = acpi_thermal_set_cooling_mode(tz,
939 simple_strtoul(mode_string, NULL,
940 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400942 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943
944 acpi_thermal_check(tz);
945
Patrick Mocheld550d982006-06-27 00:41:40 -0400946 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947}
948
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset)
950{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200951 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
954 if (!tz)
955 goto end;
956
957 if (!tz->polling_frequency) {
958 seq_puts(seq, "<polling disabled>\n");
959 goto end;
960 }
961
962 seq_printf(seq, "polling frequency: %lu seconds\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400963 (tz->polling_frequency / 10));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
Len Brown4be44fc2005-08-05 00:44:28 -0400965 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400966 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967}
968
969static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file)
970{
971 return single_open(file, acpi_thermal_polling_seq_show,
Len Brown4be44fc2005-08-05 00:44:28 -0400972 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973}
974
975static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400976acpi_thermal_write_polling(struct file *file,
977 const char __user * buffer,
978 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200980 struct seq_file *m = file->private_data;
981 struct acpi_thermal *tz = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400982 int result = 0;
983 char polling_string[12] = { '\0' };
984 int seconds = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
987 if (!tz || (count > sizeof(polling_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400988 return -EINVAL;
Len Brown4be44fc2005-08-05 00:44:28 -0400989
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 if (copy_from_user(polling_string, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400991 return -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400992
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 polling_string[count] = '\0';
994
995 seconds = simple_strtoul(polling_string, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -0400996
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 result = acpi_thermal_set_polling(tz, seconds);
998 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400999 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
1001 acpi_thermal_check(tz);
1002
Patrick Mocheld550d982006-06-27 00:41:40 -04001003 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004}
1005
Len Brown4be44fc2005-08-05 00:44:28 -04001006static int acpi_thermal_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007{
Len Brown4be44fc2005-08-05 00:44:28 -04001008 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
1011 if (!acpi_device_dir(device)) {
1012 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -04001013 acpi_thermal_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001015 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 acpi_device_dir(device)->owner = THIS_MODULE;
1017 }
1018
1019 /* 'state' [R] */
1020 entry = create_proc_entry(ACPI_THERMAL_FILE_STATE,
Len Brown4be44fc2005-08-05 00:44:28 -04001021 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001023 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 else {
1025 entry->proc_fops = &acpi_thermal_state_fops;
1026 entry->data = acpi_driver_data(device);
1027 entry->owner = THIS_MODULE;
1028 }
1029
1030 /* 'temperature' [R] */
1031 entry = create_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE,
Len Brown4be44fc2005-08-05 00:44:28 -04001032 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001034 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 else {
1036 entry->proc_fops = &acpi_thermal_temp_fops;
1037 entry->data = acpi_driver_data(device);
1038 entry->owner = THIS_MODULE;
1039 }
1040
1041 /* 'trip_points' [R/W] */
1042 entry = create_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS,
Len Brown4be44fc2005-08-05 00:44:28 -04001043 S_IFREG | S_IRUGO | S_IWUSR,
1044 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001046 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 else {
1048 entry->proc_fops = &acpi_thermal_trip_fops;
1049 entry->data = acpi_driver_data(device);
1050 entry->owner = THIS_MODULE;
1051 }
1052
1053 /* 'cooling_mode' [R/W] */
1054 entry = create_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE,
Len Brown4be44fc2005-08-05 00:44:28 -04001055 S_IFREG | S_IRUGO | S_IWUSR,
1056 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001058 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 else {
1060 entry->proc_fops = &acpi_thermal_cooling_fops;
1061 entry->data = acpi_driver_data(device);
1062 entry->owner = THIS_MODULE;
1063 }
1064
1065 /* 'polling_frequency' [R/W] */
1066 entry = create_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ,
Len Brown4be44fc2005-08-05 00:44:28 -04001067 S_IFREG | S_IRUGO | S_IWUSR,
1068 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001070 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 else {
1072 entry->proc_fops = &acpi_thermal_polling_fops;
1073 entry->data = acpi_driver_data(device);
1074 entry->owner = THIS_MODULE;
1075 }
1076
Patrick Mocheld550d982006-06-27 00:41:40 -04001077 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078}
1079
Len Brown4be44fc2005-08-05 00:44:28 -04001080static int acpi_thermal_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
1083 if (acpi_device_dir(device)) {
1084 remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ,
1085 acpi_device_dir(device));
1086 remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE,
1087 acpi_device_dir(device));
1088 remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS,
1089 acpi_device_dir(device));
1090 remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE,
1091 acpi_device_dir(device));
1092 remove_proc_entry(ACPI_THERMAL_FILE_STATE,
1093 acpi_device_dir(device));
1094 remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir);
1095 acpi_device_dir(device) = NULL;
1096 }
1097
Patrick Mocheld550d982006-06-27 00:41:40 -04001098 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099}
1100
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101/* --------------------------------------------------------------------------
1102 Driver Interface
1103 -------------------------------------------------------------------------- */
1104
Len Brown4be44fc2005-08-05 00:44:28 -04001105static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001107 struct acpi_thermal *tz = data;
Len Brown4be44fc2005-08-05 00:44:28 -04001108 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110
1111 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001112 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113
Patrick Mochel8348e1b2006-05-19 16:54:40 -04001114 device = tz->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115
1116 switch (event) {
1117 case ACPI_THERMAL_NOTIFY_TEMPERATURE:
1118 acpi_thermal_check(tz);
1119 break;
1120 case ACPI_THERMAL_NOTIFY_THRESHOLDS:
1121 acpi_thermal_get_trip_points(tz);
1122 acpi_thermal_check(tz);
1123 acpi_bus_generate_event(device, event, 0);
1124 break;
1125 case ACPI_THERMAL_NOTIFY_DEVICES:
1126 if (tz->flags.devices)
1127 acpi_thermal_get_devices(tz);
1128 acpi_bus_generate_event(device, event, 0);
1129 break;
1130 default:
1131 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001132 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 break;
1134 }
1135
Patrick Mocheld550d982006-06-27 00:41:40 -04001136 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137}
1138
Len Brown4be44fc2005-08-05 00:44:28 -04001139static int acpi_thermal_get_info(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140{
Len Brown4be44fc2005-08-05 00:44:28 -04001141 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143
1144 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001145 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146
1147 /* Get temperature [_TMP] (required) */
1148 result = acpi_thermal_get_temperature(tz);
1149 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001150 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151
1152 /* Get trip points [_CRT, _PSV, etc.] (required) */
1153 result = acpi_thermal_get_trip_points(tz);
1154 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001155 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156
1157 /* Set the cooling mode [_SCP] to active cooling (default) */
1158 result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
Len Brown4be44fc2005-08-05 00:44:28 -04001159 if (!result)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 tz->flags.cooling_mode = 1;
Len Brown4be44fc2005-08-05 00:44:28 -04001161 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 /* Oh,we have not _SCP method.
Len Brown4be44fc2005-08-05 00:44:28 -04001163 Generally show cooling_mode by _ACx, _PSV,spec 12.2 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 tz->flags.cooling_mode = 0;
Len Brown4be44fc2005-08-05 00:44:28 -04001165 if (tz->trips.active[0].flags.valid
1166 && tz->trips.passive.flags.valid) {
1167 if (tz->trips.passive.temperature >
1168 tz->trips.active[0].temperature)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE;
Len Brown4be44fc2005-08-05 00:44:28 -04001170 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE;
Len Brown4be44fc2005-08-05 00:44:28 -04001172 } else if (!tz->trips.active[0].flags.valid
1173 && tz->trips.passive.flags.valid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE;
Len Brown4be44fc2005-08-05 00:44:28 -04001175 } else if (tz->trips.active[0].flags.valid
1176 && !tz->trips.passive.flags.valid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE;
1178 } else {
1179 /* _ACx and _PSV are optional, but _CRT is required */
1180 tz->cooling_mode = ACPI_THERMAL_MODE_CRITICAL;
1181 }
1182 }
1183
1184 /* Get default polling frequency [_TZP] (optional) */
1185 if (tzp)
1186 tz->polling_frequency = tzp;
1187 else
1188 acpi_thermal_get_polling_frequency(tz);
1189
1190 /* Get devices in this thermal zone [_TZD] (optional) */
1191 result = acpi_thermal_get_devices(tz);
1192 if (!result)
1193 tz->flags.devices = 1;
1194
Patrick Mocheld550d982006-06-27 00:41:40 -04001195 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196}
1197
Len Brown4be44fc2005-08-05 00:44:28 -04001198static int acpi_thermal_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199{
Len Brown4be44fc2005-08-05 00:44:28 -04001200 int result = 0;
1201 acpi_status status = AE_OK;
1202 struct acpi_thermal *tz = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
1205 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001206 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207
Burman Yan36bcbec2006-12-19 12:56:11 -08001208 tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001210 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211
Patrick Mochel8348e1b2006-05-19 16:54:40 -04001212 tz->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 strcpy(tz->name, device->pnp.bus_id);
1214 strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME);
1215 strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
1216 acpi_driver_data(device) = tz;
1217
1218 result = acpi_thermal_get_info(tz);
1219 if (result)
1220 goto end;
1221
1222 result = acpi_thermal_add_fs(device);
1223 if (result)
Vasily Averin09047e72006-04-27 05:25:00 -04001224 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225
1226 init_timer(&tz->timer);
1227
1228 acpi_thermal_check(tz);
1229
Patrick Mochel38ba7c92006-05-19 16:54:48 -04001230 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001231 ACPI_DEVICE_NOTIFY,
1232 acpi_thermal_notify, tz);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 if (ACPI_FAILURE(status)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 result = -ENODEV;
1235 goto end;
1236 }
1237
1238 printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001239 acpi_device_name(device), acpi_device_bid(device),
1240 KELVIN_TO_CELSIUS(tz->temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
Len Brown4be44fc2005-08-05 00:44:28 -04001242 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 if (result) {
1244 acpi_thermal_remove_fs(device);
1245 kfree(tz);
1246 }
1247
Patrick Mocheld550d982006-06-27 00:41:40 -04001248 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249}
1250
Len Brown4be44fc2005-08-05 00:44:28 -04001251static int acpi_thermal_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252{
Len Brown4be44fc2005-08-05 00:44:28 -04001253 acpi_status status = AE_OK;
1254 struct acpi_thermal *tz = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
1257 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001258 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001260 tz = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
1262 /* avoid timer adding new defer task */
1263 tz->zombie = 1;
1264 /* wait for running timer (on other CPUs) finish */
1265 del_timer_sync(&(tz->timer));
1266 /* synchronize deferred task */
1267 acpi_os_wait_events_complete(NULL);
1268 /* deferred task may reinsert timer */
1269 del_timer_sync(&(tz->timer));
1270
Patrick Mochel38ba7c92006-05-19 16:54:48 -04001271 status = acpi_remove_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001272 ACPI_DEVICE_NOTIFY,
1273 acpi_thermal_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
1275 /* Terminate policy */
Len Brown4be44fc2005-08-05 00:44:28 -04001276 if (tz->trips.passive.flags.valid && tz->trips.passive.flags.enabled) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 tz->trips.passive.flags.enabled = 0;
1278 acpi_thermal_passive(tz);
1279 }
1280 if (tz->trips.active[0].flags.valid
Len Brown4be44fc2005-08-05 00:44:28 -04001281 && tz->trips.active[0].flags.enabled) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 tz->trips.active[0].flags.enabled = 0;
1283 acpi_thermal_active(tz);
1284 }
1285
1286 acpi_thermal_remove_fs(device);
1287
1288 kfree(tz);
Patrick Mocheld550d982006-06-27 00:41:40 -04001289 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290}
1291
Patrick Mochel5d9464a2006-12-07 20:56:27 +08001292static int acpi_thermal_resume(struct acpi_device *device)
Konstantin Karasyov74ce1462006-05-08 08:32:00 -04001293{
1294 struct acpi_thermal *tz = NULL;
Konstantin Karasyovb1028c52007-02-16 02:23:07 -05001295 int i, j, power_state, result;
1296
Konstantin Karasyov74ce1462006-05-08 08:32:00 -04001297
1298 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001299 return -EINVAL;
Konstantin Karasyov74ce1462006-05-08 08:32:00 -04001300
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001301 tz = acpi_driver_data(device);
Konstantin Karasyov74ce1462006-05-08 08:32:00 -04001302
Konstantin Karasyovbed936f2006-07-10 04:44:26 -07001303 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
Konstantin Karasyovb1028c52007-02-16 02:23:07 -05001304 if (!(&tz->trips.active[i]))
1305 break;
1306 if (!tz->trips.active[i].flags.valid)
1307 break;
1308 tz->trips.active[i].flags.enabled = 1;
1309 for (j = 0; j < tz->trips.active[i].devices.count; j++) {
1310 result = acpi_bus_get_power(tz->trips.active[i].devices.
1311 handles[j], &power_state);
1312 if (result || (power_state != ACPI_STATE_D0)) {
1313 tz->trips.active[i].flags.enabled = 0;
1314 break;
1315 }
Konstantin Karasyovbed936f2006-07-10 04:44:26 -07001316 }
Konstantin Karasyovb1028c52007-02-16 02:23:07 -05001317 tz->state.active |= tz->trips.active[i].flags.enabled;
Konstantin Karasyovbed936f2006-07-10 04:44:26 -07001318 }
1319
Konstantin Karasyovb1028c52007-02-16 02:23:07 -05001320 acpi_thermal_check(tz);
Konstantin Karasyov74ce1462006-05-08 08:32:00 -04001321
1322 return AE_OK;
1323}
1324
Len Brown4be44fc2005-08-05 00:44:28 -04001325static int __init acpi_thermal_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326{
Len Brown4be44fc2005-08-05 00:44:28 -04001327 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
1330 acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir);
1331 if (!acpi_thermal_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001332 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 acpi_thermal_dir->owner = THIS_MODULE;
1334
1335 result = acpi_bus_register_driver(&acpi_thermal_driver);
1336 if (result < 0) {
1337 remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001338 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 }
1340
Patrick Mocheld550d982006-06-27 00:41:40 -04001341 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342}
1343
Len Brown4be44fc2005-08-05 00:44:28 -04001344static void __exit acpi_thermal_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
1347 acpi_bus_unregister_driver(&acpi_thermal_driver);
1348
1349 remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
1350
Patrick Mocheld550d982006-06-27 00:41:40 -04001351 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352}
1353
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354module_init(acpi_thermal_init);
1355module_exit(acpi_thermal_exit);