blob: ddbb7c8f994da7951261453f1f9ac4d95cdb4fc1 [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>
Len Brown0b5bfa12007-08-12 00:13:02 -040036#include <linux/dmi.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
38#include <linux/types.h>
39#include <linux/proc_fs.h>
Tim Schmielaucd354f12007-02-14 00:33:14 -080040#include <linux/jiffies.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <linux/kmod.h>
42#include <linux/seq_file.h>
Jeremy Fitzhardinge10a0a8d2007-07-17 18:37:02 -070043#include <linux/reboot.h>
Stephen Rothwellf6f5c452009-03-03 12:47:17 +110044#include <linux/device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#include <asm/uaccess.h>
Zhang Rui3f655ef2008-01-17 15:51:11 +080046#include <linux/thermal.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include <acpi/acpi_bus.h>
48#include <acpi/acpi_drivers.h>
49
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#define ACPI_THERMAL_CLASS "thermal_zone"
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone"
52#define ACPI_THERMAL_FILE_STATE "state"
53#define ACPI_THERMAL_FILE_TEMPERATURE "temperature"
54#define ACPI_THERMAL_FILE_TRIP_POINTS "trip_points"
55#define ACPI_THERMAL_FILE_COOLING_MODE "cooling_mode"
56#define ACPI_THERMAL_FILE_POLLING_FREQ "polling_frequency"
57#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80
58#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81
59#define ACPI_THERMAL_NOTIFY_DEVICES 0x82
60#define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0
61#define ACPI_THERMAL_NOTIFY_HOT 0xF1
62#define ACPI_THERMAL_MODE_ACTIVE 0x00
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
64#define ACPI_THERMAL_MAX_ACTIVE 10
65#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#define _COMPONENT ACPI_THERMAL_COMPONENT
Len Brownf52fd662007-02-12 22:42:12 -050068ACPI_MODULE_NAME("thermal");
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
Thomas Renninger1cbf4c52004-09-16 11:07:00 -040070MODULE_AUTHOR("Paul Diefenbaugh");
Len Brown7cda93e2007-02-12 23:50:02 -050071MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070072MODULE_LICENSE("GPL");
73
Len Brownf8707ec2007-08-12 00:12:54 -040074static int act;
75module_param(act, int, 0644);
Len Brown3c1d36d2007-08-14 15:12:56 -040076MODULE_PARM_DESC(act, "Disable or override all lowest active trip points.");
Len Brownf8707ec2007-08-12 00:12:54 -040077
Len Brownc52a7412007-08-14 15:49:32 -040078static int crt;
79module_param(crt, int, 0644);
80MODULE_PARM_DESC(crt, "Disable or lower all critical trip points.");
81
Linus Torvalds1da177e2005-04-16 15:20:36 -070082static int tzp;
Len Brown730ff342007-08-12 00:12:26 -040083module_param(tzp, int, 0444);
Len Brown3c1d36d2007-08-14 15:12:56 -040084MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.");
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
Len Brownf5487142007-08-12 00:12:44 -040086static int nocrt;
87module_param(nocrt, int, 0);
Len Brown8c99fdc2007-08-20 18:46:50 -040088MODULE_PARM_DESC(nocrt, "Set to take no action upon ACPI thermal zone critical trips points.");
Len Brownf5487142007-08-12 00:12:44 -040089
Len Brown72b33ef2007-08-12 00:12:17 -040090static int off;
91module_param(off, int, 0);
Len Brown3c1d36d2007-08-14 15:12:56 -040092MODULE_PARM_DESC(off, "Set to disable ACPI thermal support.");
Len Brown72b33ef2007-08-12 00:12:17 -040093
Len Browna70cdc52007-08-12 00:12:35 -040094static int psv;
95module_param(psv, int, 0644);
Len Brown3c1d36d2007-08-14 15:12:56 -040096MODULE_PARM_DESC(psv, "Disable or override all passive trip points.");
Len Browna70cdc52007-08-12 00:12:35 -040097
Len Brown4be44fc2005-08-05 00:44:28 -040098static int acpi_thermal_add(struct acpi_device *device);
99static int acpi_thermal_remove(struct acpi_device *device, int type);
Patrick Mochel5d9464a2006-12-07 20:56:27 +0800100static int acpi_thermal_resume(struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file);
102static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file);
103static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file);
Len Brown4be44fc2005-08-05 00:44:28 -0400105static ssize_t acpi_thermal_write_cooling_mode(struct file *,
106 const char __user *, size_t,
107 loff_t *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file);
Len Brown4be44fc2005-08-05 00:44:28 -0400109static ssize_t acpi_thermal_write_polling(struct file *, const char __user *,
110 size_t, loff_t *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
Thomas Renninger1ba90e32007-07-23 14:44:41 +0200112static const struct acpi_device_id thermal_device_ids[] = {
113 {ACPI_THERMAL_HID, 0},
114 {"", 0},
115};
116MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118static struct acpi_driver acpi_thermal_driver = {
Len Brownc2b67052007-02-12 23:33:40 -0500119 .name = "thermal",
Len Brown4be44fc2005-08-05 00:44:28 -0400120 .class = ACPI_THERMAL_CLASS,
Thomas Renninger1ba90e32007-07-23 14:44:41 +0200121 .ids = thermal_device_ids,
Len Brown4be44fc2005-08-05 00:44:28 -0400122 .ops = {
123 .add = acpi_thermal_add,
124 .remove = acpi_thermal_remove,
Konstantin Karasyov74ce14682006-05-08 08:32:00 -0400125 .resume = acpi_thermal_resume,
Len Brown4be44fc2005-08-05 00:44:28 -0400126 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127};
128
129struct acpi_thermal_state {
Len Brown4be44fc2005-08-05 00:44:28 -0400130 u8 critical:1;
131 u8 hot:1;
132 u8 passive:1;
133 u8 active:1;
134 u8 reserved:4;
135 int active_index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136};
137
138struct acpi_thermal_state_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400139 u8 valid:1;
140 u8 enabled:1;
141 u8 reserved:6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142};
143
144struct acpi_thermal_critical {
145 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400146 unsigned long temperature;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147};
148
149struct acpi_thermal_hot {
150 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400151 unsigned long temperature;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152};
153
154struct acpi_thermal_passive {
155 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400156 unsigned long temperature;
157 unsigned long tc1;
158 unsigned long tc2;
159 unsigned long tsp;
160 struct acpi_handle_list devices;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161};
162
163struct acpi_thermal_active {
164 struct acpi_thermal_state_flags flags;
Len Brown4be44fc2005-08-05 00:44:28 -0400165 unsigned long temperature;
166 struct acpi_handle_list devices;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167};
168
169struct acpi_thermal_trips {
170 struct acpi_thermal_critical critical;
Len Brown4be44fc2005-08-05 00:44:28 -0400171 struct acpi_thermal_hot hot;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 struct acpi_thermal_passive passive;
173 struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
174};
175
176struct acpi_thermal_flags {
Len Brown4be44fc2005-08-05 00:44:28 -0400177 u8 cooling_mode:1; /* _SCP */
178 u8 devices:1; /* _TZD */
179 u8 reserved:6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180};
181
182struct acpi_thermal {
Patrick Mochel8348e1b2006-05-19 16:54:40 -0400183 struct acpi_device * device;
Len Brown4be44fc2005-08-05 00:44:28 -0400184 acpi_bus_id name;
185 unsigned long temperature;
186 unsigned long last_temperature;
187 unsigned long polling_frequency;
Len Brown4be44fc2005-08-05 00:44:28 -0400188 volatile u8 zombie;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 struct acpi_thermal_flags flags;
190 struct acpi_thermal_state state;
191 struct acpi_thermal_trips trips;
Len Brown4be44fc2005-08-05 00:44:28 -0400192 struct acpi_handle_list devices;
Zhang Rui3f655ef2008-01-17 15:51:11 +0800193 struct thermal_zone_device *thermal_zone;
194 int tz_enabled;
Jean Delvare13614e32009-04-06 16:01:46 +0200195 int kelvin_offset;
Alexey Starikovskiy6e215782007-09-01 00:11:59 +0400196 struct mutex lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197};
198
Arjan van de Vend7508032006-07-04 13:06:00 -0400199static const struct file_operations acpi_thermal_state_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700200 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400201 .open = acpi_thermal_state_open_fs,
202 .read = seq_read,
203 .llseek = seq_lseek,
204 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205};
206
Arjan van de Vend7508032006-07-04 13:06:00 -0400207static const struct file_operations acpi_thermal_temp_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700208 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400209 .open = acpi_thermal_temp_open_fs,
210 .read = seq_read,
211 .llseek = seq_lseek,
212 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213};
214
Arjan van de Vend7508032006-07-04 13:06:00 -0400215static const struct file_operations acpi_thermal_trip_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700216 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400217 .open = acpi_thermal_trip_open_fs,
218 .read = seq_read,
Len Brown4be44fc2005-08-05 00:44:28 -0400219 .llseek = seq_lseek,
220 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221};
222
Arjan van de Vend7508032006-07-04 13:06:00 -0400223static const struct file_operations acpi_thermal_cooling_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700224 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400225 .open = acpi_thermal_cooling_open_fs,
226 .read = seq_read,
227 .write = acpi_thermal_write_cooling_mode,
228 .llseek = seq_lseek,
229 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230};
231
Arjan van de Vend7508032006-07-04 13:06:00 -0400232static const struct file_operations acpi_thermal_polling_fops = {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700233 .owner = THIS_MODULE,
Len Brown4be44fc2005-08-05 00:44:28 -0400234 .open = acpi_thermal_polling_open_fs,
235 .read = seq_read,
236 .write = acpi_thermal_write_polling,
237 .llseek = seq_lseek,
238 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239};
240
241/* --------------------------------------------------------------------------
242 Thermal Zone Management
243 -------------------------------------------------------------------------- */
244
Len Brown4be44fc2005-08-05 00:44:28 -0400245static int acpi_thermal_get_temperature(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
Len Brown4be44fc2005-08-05 00:44:28 -0400247 acpi_status status = AE_OK;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400248 unsigned long long tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
250 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400251 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
253 tz->last_temperature = tz->temperature;
254
Matthew Wilcox27663c52008-10-10 02:22:59 -0400255 status = acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400257 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
Matthew Wilcox27663c52008-10-10 02:22:59 -0400259 tz->temperature = tmp;
Len Brown4be44fc2005-08-05 00:44:28 -0400260 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n",
261 tz->temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
Patrick Mocheld550d982006-06-27 00:41:40 -0400263 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264}
265
Len Brown4be44fc2005-08-05 00:44:28 -0400266static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
Len Brown4be44fc2005-08-05 00:44:28 -0400268 acpi_status status = AE_OK;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400269 unsigned long long tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270
271 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400272 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
Matthew Wilcox27663c52008-10-10 02:22:59 -0400274 status = acpi_evaluate_integer(tz->device->handle, "_TZP", NULL, &tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400276 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Matthew Wilcox27663c52008-10-10 02:22:59 -0400278 tz->polling_frequency = tmp;
Len Brown4be44fc2005-08-05 00:44:28 -0400279 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n",
280 tz->polling_frequency));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
Patrick Mocheld550d982006-06-27 00:41:40 -0400282 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283}
284
Len Brown4be44fc2005-08-05 00:44:28 -0400285static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
288 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -0400289 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
291 tz->polling_frequency = seconds * 10; /* Convert value to deci-seconds */
292
Matthew Garrettb1569e92008-12-03 17:55:32 +0000293 tz->thermal_zone->polling_delay = seconds * 1000;
294
295 if (tz->tz_enabled)
296 thermal_zone_device_update(tz->thermal_zone);
297
Len Brown4be44fc2005-08-05 00:44:28 -0400298 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
299 "Polling frequency set to %lu seconds\n",
Sanjoy Mahajan636cedf2007-02-16 01:24:43 -0500300 tz->polling_frequency/10));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Patrick Mocheld550d982006-06-27 00:41:40 -0400302 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303}
304
Len Brown4be44fc2005-08-05 00:44:28 -0400305static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306{
Len Brown4be44fc2005-08-05 00:44:28 -0400307 acpi_status status = AE_OK;
308 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
309 struct acpi_object_list arg_list = { 1, &arg0 };
310 acpi_handle handle = NULL;
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
Patrick Mochel38ba7c92006-05-19 16:54:48 -0400316 status = acpi_get_handle(tz->device->handle, "_SCP", &handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 if (ACPI_FAILURE(status)) {
318 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400319 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 }
321
322 arg0.integer.value = mode;
323
324 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
325 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400326 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
Patrick Mocheld550d982006-06-27 00:41:40 -0400328 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
Zhang Ruice44e192008-01-17 15:51:25 +0800331#define ACPI_TRIPS_CRITICAL 0x01
332#define ACPI_TRIPS_HOT 0x02
333#define ACPI_TRIPS_PASSIVE 0x04
334#define ACPI_TRIPS_ACTIVE 0x08
335#define ACPI_TRIPS_DEVICES 0x10
336
337#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
338#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES
339
340#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \
341 ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \
342 ACPI_TRIPS_DEVICES)
343
344/*
345 * This exception is thrown out in two cases:
346 * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
347 * when re-evaluating the AML code.
348 * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
349 * We need to re-bind the cooling devices of a thermal zone when this occurs.
350 */
351#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \
352do { \
353 if (flags != ACPI_TRIPS_INIT) \
354 ACPI_EXCEPTION((AE_INFO, AE_ERROR, \
355 "ACPI thermal trip point %s changed\n" \
356 "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \
357} while (0)
358
359static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360{
Len Brown4be44fc2005-08-05 00:44:28 -0400361 acpi_status status = AE_OK;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400362 unsigned long long tmp;
Zhang Ruice44e192008-01-17 15:51:25 +0800363 struct acpi_handle_list devices;
364 int valid = 0;
365 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367 /* Critical Shutdown (required) */
Zhang Ruice44e192008-01-17 15:51:25 +0800368 if (flag & ACPI_TRIPS_CRITICAL) {
369 status = acpi_evaluate_integer(tz->device->handle,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400370 "_CRT", NULL, &tmp);
371 tz->trips.critical.temperature = tmp;
Arjan van de Vena39a2d72008-05-19 15:55:15 -0700372 /*
373 * Treat freezing temperatures as invalid as well; some
374 * BIOSes return really low values and cause reboots at startup.
Adam Buchbinderb731d7b2009-03-13 12:15:26 -0400375 * Below zero (Celsius) values clearly aren't right for sure..
Arjan van de Vena39a2d72008-05-19 15:55:15 -0700376 * ... so lets discard those as invalid.
377 */
378 if (ACPI_FAILURE(status) ||
379 tz->trips.critical.temperature <= 2732) {
Len Brownc52a7412007-08-14 15:49:32 -0400380 tz->trips.critical.flags.valid = 0;
Zhang Ruice44e192008-01-17 15:51:25 +0800381 ACPI_EXCEPTION((AE_INFO, status,
Arjan van de Vena39a2d72008-05-19 15:55:15 -0700382 "No or invalid critical threshold"));
Zhang Ruice44e192008-01-17 15:51:25 +0800383 return -ENODEV;
384 } else {
385 tz->trips.critical.flags.valid = 1;
386 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
387 "Found critical threshold [%lu]\n",
388 tz->trips.critical.temperature));
389 }
390 if (tz->trips.critical.flags.valid == 1) {
391 if (crt == -1) {
392 tz->trips.critical.flags.valid = 0;
393 } else if (crt > 0) {
394 unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
395 /*
Zhang Rui22a94d72008-10-17 02:41:20 -0400396 * Allow override critical threshold
Zhang Ruice44e192008-01-17 15:51:25 +0800397 */
Zhang Rui22a94d72008-10-17 02:41:20 -0400398 if (crt_k > tz->trips.critical.temperature)
399 printk(KERN_WARNING PREFIX
400 "Critical threshold %d C\n", crt);
401 tz->trips.critical.temperature = crt_k;
Zhang Ruice44e192008-01-17 15:51:25 +0800402 }
Len Brownc52a7412007-08-14 15:49:32 -0400403 }
404 }
405
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 /* Critical Sleep (optional) */
Zhang Ruice44e192008-01-17 15:51:25 +0800407 if (flag & ACPI_TRIPS_HOT) {
Len Browna70cdc52007-08-12 00:12:35 -0400408 status = acpi_evaluate_integer(tz->device->handle,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400409 "_HOT", NULL, &tmp);
Zhang Ruice44e192008-01-17 15:51:25 +0800410 if (ACPI_FAILURE(status)) {
411 tz->trips.hot.flags.valid = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400412 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Zhang Ruice44e192008-01-17 15:51:25 +0800413 "No hot threshold\n"));
414 } else {
Matthew Wilcox27663c52008-10-10 02:22:59 -0400415 tz->trips.hot.temperature = tmp;
Zhang Ruice44e192008-01-17 15:51:25 +0800416 tz->trips.hot.flags.valid = 1;
417 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
418 "Found hot threshold [%lu]\n",
419 tz->trips.critical.temperature));
420 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 }
422
Zhang Ruice44e192008-01-17 15:51:25 +0800423 /* Passive (optional) */
Zhang Rui0e4240d2009-01-16 12:53:42 -0500424 if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) ||
425 (flag == ACPI_TRIPS_INIT)) {
Zhang Ruice44e192008-01-17 15:51:25 +0800426 valid = tz->trips.passive.flags.valid;
427 if (psv == -1) {
428 status = AE_SUPPORT;
429 } else if (psv > 0) {
Matthew Wilcox27663c52008-10-10 02:22:59 -0400430 tmp = CELSIUS_TO_KELVIN(psv);
Zhang Ruice44e192008-01-17 15:51:25 +0800431 status = AE_OK;
432 } else {
433 status = acpi_evaluate_integer(tz->device->handle,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400434 "_PSV", NULL, &tmp);
Zhang Ruice44e192008-01-17 15:51:25 +0800435 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
Zhang Ruice44e192008-01-17 15:51:25 +0800437 if (ACPI_FAILURE(status))
438 tz->trips.passive.flags.valid = 0;
439 else {
Matthew Wilcox27663c52008-10-10 02:22:59 -0400440 tz->trips.passive.temperature = tmp;
Zhang Ruice44e192008-01-17 15:51:25 +0800441 tz->trips.passive.flags.valid = 1;
442 if (flag == ACPI_TRIPS_INIT) {
443 status = acpi_evaluate_integer(
444 tz->device->handle, "_TC1",
Matthew Wilcox27663c52008-10-10 02:22:59 -0400445 NULL, &tmp);
Zhang Ruice44e192008-01-17 15:51:25 +0800446 if (ACPI_FAILURE(status))
447 tz->trips.passive.flags.valid = 0;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400448 else
449 tz->trips.passive.tc1 = tmp;
Zhang Ruice44e192008-01-17 15:51:25 +0800450 status = acpi_evaluate_integer(
451 tz->device->handle, "_TC2",
Matthew Wilcox27663c52008-10-10 02:22:59 -0400452 NULL, &tmp);
Zhang Ruice44e192008-01-17 15:51:25 +0800453 if (ACPI_FAILURE(status))
454 tz->trips.passive.flags.valid = 0;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400455 else
456 tz->trips.passive.tc2 = tmp;
Zhang Ruice44e192008-01-17 15:51:25 +0800457 status = acpi_evaluate_integer(
458 tz->device->handle, "_TSP",
Matthew Wilcox27663c52008-10-10 02:22:59 -0400459 NULL, &tmp);
Zhang Ruice44e192008-01-17 15:51:25 +0800460 if (ACPI_FAILURE(status))
461 tz->trips.passive.flags.valid = 0;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400462 else
463 tz->trips.passive.tsp = tmp;
Zhang Ruice44e192008-01-17 15:51:25 +0800464 }
465 }
466 }
467 if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
468 memset(&devices, 0, sizeof(struct acpi_handle_list));
469 status = acpi_evaluate_reference(tz->device->handle, "_PSL",
470 NULL, &devices);
Zhang Rui0e4240d2009-01-16 12:53:42 -0500471 if (ACPI_FAILURE(status)) {
472 printk(KERN_WARNING PREFIX
473 "Invalid passive threshold\n");
Zhang Ruice44e192008-01-17 15:51:25 +0800474 tz->trips.passive.flags.valid = 0;
Zhang Rui0e4240d2009-01-16 12:53:42 -0500475 }
Zhang Ruice44e192008-01-17 15:51:25 +0800476 else
477 tz->trips.passive.flags.valid = 1;
478
479 if (memcmp(&tz->trips.passive.devices, &devices,
480 sizeof(struct acpi_handle_list))) {
481 memcpy(&tz->trips.passive.devices, &devices,
482 sizeof(struct acpi_handle_list));
483 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
484 }
485 }
486 if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
487 if (valid != tz->trips.passive.flags.valid)
488 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
489 }
490
491 /* Active (optional) */
Len Brown4be44fc2005-08-05 00:44:28 -0400492 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
Len Brown4be44fc2005-08-05 00:44:28 -0400493 char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
Zhang Ruice44e192008-01-17 15:51:25 +0800494 valid = tz->trips.active[i].flags.valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Len Brownf8707ec2007-08-12 00:12:54 -0400496 if (act == -1)
Zhang Ruice44e192008-01-17 15:51:25 +0800497 break; /* disable all active trip points */
Len Brownf8707ec2007-08-12 00:12:54 -0400498
Zhang Rui0e4240d2009-01-16 12:53:42 -0500499 if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) &&
500 tz->trips.active[i].flags.valid)) {
Zhang Ruice44e192008-01-17 15:51:25 +0800501 status = acpi_evaluate_integer(tz->device->handle,
Matthew Wilcox27663c52008-10-10 02:22:59 -0400502 name, NULL, &tmp);
Zhang Ruice44e192008-01-17 15:51:25 +0800503 if (ACPI_FAILURE(status)) {
504 tz->trips.active[i].flags.valid = 0;
505 if (i == 0)
506 break;
507 if (act <= 0)
508 break;
509 if (i == 1)
510 tz->trips.active[0].temperature =
511 CELSIUS_TO_KELVIN(act);
512 else
513 /*
514 * Don't allow override higher than
515 * the next higher trip point
516 */
517 tz->trips.active[i - 1].temperature =
518 (tz->trips.active[i - 2].temperature <
519 CELSIUS_TO_KELVIN(act) ?
520 tz->trips.active[i - 2].temperature :
521 CELSIUS_TO_KELVIN(act));
Len Brownf8707ec2007-08-12 00:12:54 -0400522 break;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400523 } else {
524 tz->trips.active[i].temperature = tmp;
Zhang Ruice44e192008-01-17 15:51:25 +0800525 tz->trips.active[i].flags.valid = 1;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400526 }
Len Brownf8707ec2007-08-12 00:12:54 -0400527 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
529 name[2] = 'L';
Zhang Ruice44e192008-01-17 15:51:25 +0800530 if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
531 memset(&devices, 0, sizeof(struct acpi_handle_list));
532 status = acpi_evaluate_reference(tz->device->handle,
533 name, NULL, &devices);
Zhang Rui0e4240d2009-01-16 12:53:42 -0500534 if (ACPI_FAILURE(status)) {
535 printk(KERN_WARNING PREFIX
536 "Invalid active%d threshold\n", i);
Zhang Ruice44e192008-01-17 15:51:25 +0800537 tz->trips.active[i].flags.valid = 0;
Zhang Rui0e4240d2009-01-16 12:53:42 -0500538 }
Zhang Ruice44e192008-01-17 15:51:25 +0800539 else
540 tz->trips.active[i].flags.valid = 1;
541
542 if (memcmp(&tz->trips.active[i].devices, &devices,
543 sizeof(struct acpi_handle_list))) {
544 memcpy(&tz->trips.active[i].devices, &devices,
545 sizeof(struct acpi_handle_list));
546 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
547 }
548 }
549 if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
550 if (valid != tz->trips.active[i].flags.valid)
551 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
552
553 if (!tz->trips.active[i].flags.valid)
554 break;
555 }
556
557 if (flag & ACPI_TRIPS_DEVICES) {
558 memset(&devices, 0, sizeof(struct acpi_handle_list));
559 status = acpi_evaluate_reference(tz->device->handle, "_TZD",
560 NULL, &devices);
561 if (memcmp(&tz->devices, &devices,
562 sizeof(struct acpi_handle_list))) {
563 memcpy(&tz->devices, &devices,
564 sizeof(struct acpi_handle_list));
565 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 }
568
Patrick Mocheld550d982006-06-27 00:41:40 -0400569 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570}
571
Zhang Ruice44e192008-01-17 15:51:25 +0800572static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
Zhang Ruice44e192008-01-17 15:51:25 +0800574 return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575}
576
Len Brown4be44fc2005-08-05 00:44:28 -0400577static void acpi_thermal_check(void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200579 struct acpi_thermal *tz = data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
Matthew Garrettb1569e92008-12-03 17:55:32 +0000581 thermal_zone_device_update(tz->thermal_zone);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582}
583
Zhang Rui3f655ef2008-01-17 15:51:11 +0800584/* sys I/F for generic thermal sysfs support */
Jean Delvare13614e32009-04-06 16:01:46 +0200585#define KELVIN_TO_MILLICELSIUS(t, off) (((t) - (off)) * 100)
Zhang, Rui5e012762008-02-28 07:51:30 +0800586
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000587static int thermal_get_temp(struct thermal_zone_device *thermal,
588 unsigned long *temp)
Zhang Rui3f655ef2008-01-17 15:51:11 +0800589{
590 struct acpi_thermal *tz = thermal->devdata;
Zhang, Rui76ecb4f2008-04-10 16:20:23 +0800591 int result;
Zhang Rui3f655ef2008-01-17 15:51:11 +0800592
593 if (!tz)
594 return -EINVAL;
595
Zhang, Rui76ecb4f2008-04-10 16:20:23 +0800596 result = acpi_thermal_get_temperature(tz);
597 if (result)
598 return result;
599
Jean Delvare13614e32009-04-06 16:01:46 +0200600 *temp = KELVIN_TO_MILLICELSIUS(tz->temperature, tz->kelvin_offset);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000601 return 0;
Zhang Rui3f655ef2008-01-17 15:51:11 +0800602}
603
604static const char enabled[] = "kernel";
605static const char disabled[] = "user";
606static int thermal_get_mode(struct thermal_zone_device *thermal,
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000607 enum thermal_device_mode *mode)
Zhang Rui3f655ef2008-01-17 15:51:11 +0800608{
609 struct acpi_thermal *tz = thermal->devdata;
610
611 if (!tz)
612 return -EINVAL;
613
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000614 *mode = tz->tz_enabled ? THERMAL_DEVICE_ENABLED :
615 THERMAL_DEVICE_DISABLED;
616
617 return 0;
Zhang Rui3f655ef2008-01-17 15:51:11 +0800618}
619
620static int thermal_set_mode(struct thermal_zone_device *thermal,
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000621 enum thermal_device_mode mode)
Zhang Rui3f655ef2008-01-17 15:51:11 +0800622{
623 struct acpi_thermal *tz = thermal->devdata;
624 int enable;
625
626 if (!tz)
627 return -EINVAL;
628
629 /*
630 * enable/disable thermal management from ACPI thermal driver
631 */
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000632 if (mode == THERMAL_DEVICE_ENABLED)
Zhang Rui3f655ef2008-01-17 15:51:11 +0800633 enable = 1;
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000634 else if (mode == THERMAL_DEVICE_DISABLED)
Zhang Rui3f655ef2008-01-17 15:51:11 +0800635 enable = 0;
636 else
637 return -EINVAL;
638
639 if (enable != tz->tz_enabled) {
640 tz->tz_enabled = enable;
641 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
642 "%s ACPI thermal control\n",
643 tz->tz_enabled ? enabled : disabled));
644 acpi_thermal_check(tz);
645 }
646 return 0;
647}
648
649static int thermal_get_trip_type(struct thermal_zone_device *thermal,
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000650 int trip, enum thermal_trip_type *type)
Zhang Rui3f655ef2008-01-17 15:51:11 +0800651{
652 struct acpi_thermal *tz = thermal->devdata;
653 int i;
654
655 if (!tz || trip < 0)
656 return -EINVAL;
657
658 if (tz->trips.critical.flags.valid) {
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000659 if (!trip) {
660 *type = THERMAL_TRIP_CRITICAL;
661 return 0;
662 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800663 trip--;
664 }
665
666 if (tz->trips.hot.flags.valid) {
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000667 if (!trip) {
668 *type = THERMAL_TRIP_HOT;
669 return 0;
670 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800671 trip--;
672 }
673
674 if (tz->trips.passive.flags.valid) {
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000675 if (!trip) {
676 *type = THERMAL_TRIP_PASSIVE;
677 return 0;
678 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800679 trip--;
680 }
681
682 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
683 tz->trips.active[i].flags.valid; i++) {
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000684 if (!trip) {
685 *type = THERMAL_TRIP_ACTIVE;
686 return 0;
687 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800688 trip--;
689 }
690
691 return -EINVAL;
692}
693
694static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000695 int trip, unsigned long *temp)
Zhang Rui3f655ef2008-01-17 15:51:11 +0800696{
697 struct acpi_thermal *tz = thermal->devdata;
698 int i;
699
700 if (!tz || trip < 0)
701 return -EINVAL;
702
703 if (tz->trips.critical.flags.valid) {
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000704 if (!trip) {
705 *temp = KELVIN_TO_MILLICELSIUS(
Jean Delvare13614e32009-04-06 16:01:46 +0200706 tz->trips.critical.temperature,
707 tz->kelvin_offset);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000708 return 0;
709 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800710 trip--;
711 }
712
713 if (tz->trips.hot.flags.valid) {
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000714 if (!trip) {
715 *temp = KELVIN_TO_MILLICELSIUS(
Jean Delvare13614e32009-04-06 16:01:46 +0200716 tz->trips.hot.temperature,
717 tz->kelvin_offset);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000718 return 0;
719 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800720 trip--;
721 }
722
723 if (tz->trips.passive.flags.valid) {
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000724 if (!trip) {
725 *temp = KELVIN_TO_MILLICELSIUS(
Jean Delvare13614e32009-04-06 16:01:46 +0200726 tz->trips.passive.temperature,
727 tz->kelvin_offset);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000728 return 0;
729 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800730 trip--;
731 }
732
733 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
734 tz->trips.active[i].flags.valid; i++) {
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000735 if (!trip) {
736 *temp = KELVIN_TO_MILLICELSIUS(
Jean Delvare13614e32009-04-06 16:01:46 +0200737 tz->trips.active[i].temperature,
738 tz->kelvin_offset);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000739 return 0;
740 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800741 trip--;
742 }
743
744 return -EINVAL;
745}
746
Zhang, Rui9ec732f2008-04-10 16:13:10 +0800747static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
748 unsigned long *temperature) {
749 struct acpi_thermal *tz = thermal->devdata;
750
751 if (tz->trips.critical.flags.valid) {
752 *temperature = KELVIN_TO_MILLICELSIUS(
Jean Delvare13614e32009-04-06 16:01:46 +0200753 tz->trips.critical.temperature,
754 tz->kelvin_offset);
Zhang, Rui9ec732f2008-04-10 16:13:10 +0800755 return 0;
756 } else
757 return -EINVAL;
758}
759
Matthew Garrettb1569e92008-12-03 17:55:32 +0000760static int thermal_notify(struct thermal_zone_device *thermal, int trip,
761 enum thermal_trip_type trip_type)
762{
763 u8 type = 0;
764 struct acpi_thermal *tz = thermal->devdata;
765
766 if (trip_type == THERMAL_TRIP_CRITICAL)
767 type = ACPI_THERMAL_NOTIFY_CRITICAL;
768 else if (trip_type == THERMAL_TRIP_HOT)
769 type = ACPI_THERMAL_NOTIFY_HOT;
770 else
771 return 0;
772
773 acpi_bus_generate_proc_event(tz->device, type, 1);
774 acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
Stephen Rothwellf6f5c452009-03-03 12:47:17 +1100775 dev_name(&tz->device->dev), type, 1);
Matthew Garrettb1569e92008-12-03 17:55:32 +0000776
777 if (trip_type == THERMAL_TRIP_CRITICAL && nocrt)
778 return 1;
779
780 return 0;
781}
782
Zhang Rui3f655ef2008-01-17 15:51:11 +0800783typedef int (*cb)(struct thermal_zone_device *, int,
784 struct thermal_cooling_device *);
785static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
786 struct thermal_cooling_device *cdev,
787 cb action)
788{
789 struct acpi_device *device = cdev->devdata;
790 struct acpi_thermal *tz = thermal->devdata;
Zhang Rui653a00c2008-01-17 15:51:18 +0800791 struct acpi_device *dev;
792 acpi_status status;
793 acpi_handle handle;
Zhang Rui3f655ef2008-01-17 15:51:11 +0800794 int i;
795 int j;
796 int trip = -1;
797 int result = 0;
798
799 if (tz->trips.critical.flags.valid)
800 trip++;
801
802 if (tz->trips.hot.flags.valid)
803 trip++;
804
805 if (tz->trips.passive.flags.valid) {
806 trip++;
807 for (i = 0; i < tz->trips.passive.devices.count;
808 i++) {
Zhang Rui653a00c2008-01-17 15:51:18 +0800809 handle = tz->trips.passive.devices.handles[i];
810 status = acpi_bus_get_device(handle, &dev);
811 if (ACPI_SUCCESS(status) && (dev == device)) {
812 result = action(thermal, trip, cdev);
813 if (result)
814 goto failed;
815 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800816 }
817 }
818
819 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
820 if (!tz->trips.active[i].flags.valid)
821 break;
822 trip++;
823 for (j = 0;
824 j < tz->trips.active[i].devices.count;
825 j++) {
Zhang Rui653a00c2008-01-17 15:51:18 +0800826 handle = tz->trips.active[i].devices.handles[j];
827 status = acpi_bus_get_device(handle, &dev);
828 if (ACPI_SUCCESS(status) && (dev == device)) {
829 result = action(thermal, trip, cdev);
830 if (result)
831 goto failed;
832 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800833 }
834 }
835
836 for (i = 0; i < tz->devices.count; i++) {
Zhang Rui653a00c2008-01-17 15:51:18 +0800837 handle = tz->devices.handles[i];
838 status = acpi_bus_get_device(handle, &dev);
839 if (ACPI_SUCCESS(status) && (dev == device)) {
840 result = action(thermal, -1, cdev);
841 if (result)
842 goto failed;
843 }
Zhang Rui3f655ef2008-01-17 15:51:11 +0800844 }
845
846failed:
847 return result;
848}
849
850static int
851acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
852 struct thermal_cooling_device *cdev)
853{
854 return acpi_thermal_cooling_device_cb(thermal, cdev,
855 thermal_zone_bind_cooling_device);
856}
857
858static int
859acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
860 struct thermal_cooling_device *cdev)
861{
862 return acpi_thermal_cooling_device_cb(thermal, cdev,
863 thermal_zone_unbind_cooling_device);
864}
865
866static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
867 .bind = acpi_thermal_bind_cooling_device,
868 .unbind = acpi_thermal_unbind_cooling_device,
869 .get_temp = thermal_get_temp,
870 .get_mode = thermal_get_mode,
871 .set_mode = thermal_set_mode,
872 .get_trip_type = thermal_get_trip_type,
873 .get_trip_temp = thermal_get_trip_temp,
Zhang, Rui9ec732f2008-04-10 16:13:10 +0800874 .get_crit_temp = thermal_get_crit_temp,
Matthew Garrettb1569e92008-12-03 17:55:32 +0000875 .notify = thermal_notify,
Zhang Rui3f655ef2008-01-17 15:51:11 +0800876};
877
878static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
879{
880 int trips = 0;
881 int result;
Zhang Rui20733932008-01-17 15:51:21 +0800882 acpi_status status;
Zhang Rui3f655ef2008-01-17 15:51:11 +0800883 int i;
884
885 if (tz->trips.critical.flags.valid)
886 trips++;
887
888 if (tz->trips.hot.flags.valid)
889 trips++;
890
891 if (tz->trips.passive.flags.valid)
892 trips++;
893
894 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
895 tz->trips.active[i].flags.valid; i++, trips++);
Matthew Garrettb1569e92008-12-03 17:55:32 +0000896
897 if (tz->trips.passive.flags.valid)
898 tz->thermal_zone =
899 thermal_zone_device_register("acpitz", trips, tz,
900 &acpi_thermal_zone_ops,
901 tz->trips.passive.tc1,
902 tz->trips.passive.tc2,
903 tz->trips.passive.tsp*100,
904 tz->polling_frequency*100);
905 else
906 tz->thermal_zone =
907 thermal_zone_device_register("acpitz", trips, tz,
908 &acpi_thermal_zone_ops,
909 0, 0, 0,
910 tz->polling_frequency);
Krzysztof Heltbb070e42008-04-08 17:41:52 -0700911 if (IS_ERR(tz->thermal_zone))
Zhang Rui3f655ef2008-01-17 15:51:11 +0800912 return -ENODEV;
913
914 result = sysfs_create_link(&tz->device->dev.kobj,
915 &tz->thermal_zone->device.kobj, "thermal_zone");
916 if (result)
917 return result;
918
919 result = sysfs_create_link(&tz->thermal_zone->device.kobj,
920 &tz->device->dev.kobj, "device");
921 if (result)
922 return result;
923
Zhang Rui20733932008-01-17 15:51:21 +0800924 status = acpi_attach_data(tz->device->handle,
925 acpi_bus_private_data_handler,
926 tz->thermal_zone);
927 if (ACPI_FAILURE(status)) {
Lin Ming55ac9a02008-09-28 14:51:56 +0800928 printk(KERN_ERR PREFIX
929 "Error attaching device data\n");
Zhang Rui20733932008-01-17 15:51:21 +0800930 return -ENODEV;
931 }
932
Zhang Rui3f655ef2008-01-17 15:51:11 +0800933 tz->tz_enabled = 1;
934
Greg Kroah-Hartmanfc3a8822008-05-02 06:02:41 +0200935 dev_info(&tz->device->dev, "registered as thermal_zone%d\n",
936 tz->thermal_zone->id);
Zhang Rui3f655ef2008-01-17 15:51:11 +0800937 return 0;
938}
939
940static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
941{
942 sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
943 sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
944 thermal_zone_device_unregister(tz->thermal_zone);
945 tz->thermal_zone = NULL;
Zhang Rui20733932008-01-17 15:51:21 +0800946 acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler);
Zhang Rui3f655ef2008-01-17 15:51:11 +0800947}
948
949
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950/* --------------------------------------------------------------------------
951 FS Interface (/proc)
952 -------------------------------------------------------------------------- */
953
Len Brown4be44fc2005-08-05 00:44:28 -0400954static struct proc_dir_entry *acpi_thermal_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
956static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset)
957{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200958 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
961 if (!tz)
962 goto end;
963
964 seq_puts(seq, "state: ");
965
Len Brown4be44fc2005-08-05 00:44:28 -0400966 if (!tz->state.critical && !tz->state.hot && !tz->state.passive
967 && !tz->state.active)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 seq_puts(seq, "ok\n");
969 else {
970 if (tz->state.critical)
971 seq_puts(seq, "critical ");
972 if (tz->state.hot)
973 seq_puts(seq, "hot ");
974 if (tz->state.passive)
975 seq_puts(seq, "passive ");
976 if (tz->state.active)
977 seq_printf(seq, "active[%d]", tz->state.active_index);
978 seq_puts(seq, "\n");
979 }
980
Len Brown4be44fc2005-08-05 00:44:28 -0400981 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400982 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983}
984
985static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file)
986{
987 return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data);
988}
989
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset)
991{
Len Brown4be44fc2005-08-05 00:44:28 -0400992 int result = 0;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200993 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
996 if (!tz)
997 goto end;
998
999 result = acpi_thermal_get_temperature(tz);
1000 if (result)
1001 goto end;
1002
Len Brown4be44fc2005-08-05 00:44:28 -04001003 seq_printf(seq, "temperature: %ld C\n",
1004 KELVIN_TO_CELSIUS(tz->temperature));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Len Brown4be44fc2005-08-05 00:44:28 -04001006 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001007 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008}
1009
1010static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file)
1011{
1012 return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data);
1013}
1014
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset)
1016{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001017 struct acpi_thermal *tz = seq->private;
Thomas Renninger68ccfaa2006-11-19 23:01:40 +01001018 struct acpi_device *device;
Thomas Renningere7c746e2007-06-18 00:40:51 -04001019 acpi_status status;
1020
Len Brown4be44fc2005-08-05 00:44:28 -04001021 int i = 0;
1022 int j = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
1025 if (!tz)
1026 goto end;
1027
1028 if (tz->trips.critical.flags.valid)
Len Brownf5487142007-08-12 00:12:44 -04001029 seq_printf(seq, "critical (S5): %ld C%s",
1030 KELVIN_TO_CELSIUS(tz->trips.critical.temperature),
1031 nocrt ? " <disabled>\n" : "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
1033 if (tz->trips.hot.flags.valid)
Len Brownf5487142007-08-12 00:12:44 -04001034 seq_printf(seq, "hot (S4): %ld C%s",
1035 KELVIN_TO_CELSIUS(tz->trips.hot.temperature),
1036 nocrt ? " <disabled>\n" : "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037
1038 if (tz->trips.passive.flags.valid) {
Len Brown4be44fc2005-08-05 00:44:28 -04001039 seq_printf(seq,
1040 "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=",
1041 KELVIN_TO_CELSIUS(tz->trips.passive.temperature),
1042 tz->trips.passive.tc1, tz->trips.passive.tc2,
1043 tz->trips.passive.tsp);
1044 for (j = 0; j < tz->trips.passive.devices.count; j++) {
Thomas Renningere7c746e2007-06-18 00:40:51 -04001045 status = acpi_bus_get_device(tz->trips.passive.devices.
1046 handles[j], &device);
1047 seq_printf(seq, "%4.4s ", status ? "" :
1048 acpi_device_bid(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 }
1050 seq_puts(seq, "\n");
1051 }
1052
1053 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
1054 if (!(tz->trips.active[i].flags.valid))
1055 break;
1056 seq_printf(seq, "active[%d]: %ld C: devices=",
Len Brown4be44fc2005-08-05 00:44:28 -04001057 i,
1058 KELVIN_TO_CELSIUS(tz->trips.active[i].temperature));
Thomas Renninger68ccfaa2006-11-19 23:01:40 +01001059 for (j = 0; j < tz->trips.active[i].devices.count; j++){
Thomas Renningere7c746e2007-06-18 00:40:51 -04001060 status = acpi_bus_get_device(tz->trips.active[i].
1061 devices.handles[j],
1062 &device);
1063 seq_printf(seq, "%4.4s ", status ? "" :
1064 acpi_device_bid(device));
Thomas Renninger68ccfaa2006-11-19 23:01:40 +01001065 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 seq_puts(seq, "\n");
1067 }
1068
Len Brown4be44fc2005-08-05 00:44:28 -04001069 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001070 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071}
1072
1073static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file)
1074{
1075 return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data);
1076}
1077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset)
1079{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001080 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
1083 if (!tz)
1084 goto end;
1085
Len Browneaca2d32007-04-30 23:27:43 -04001086 if (!tz->flags.cooling_mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 seq_puts(seq, "<setting not supported>\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 else
Len Browneaca2d32007-04-30 23:27:43 -04001089 seq_puts(seq, "0 - Active; 1 - Passive\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
Len Brown4be44fc2005-08-05 00:44:28 -04001091 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001092 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093}
1094
1095static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file)
1096{
1097 return single_open(file, acpi_thermal_cooling_seq_show,
Len Brown4be44fc2005-08-05 00:44:28 -04001098 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099}
1100
1101static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001102acpi_thermal_write_cooling_mode(struct file *file,
1103 const char __user * buffer,
1104 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001106 struct seq_file *m = file->private_data;
1107 struct acpi_thermal *tz = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001108 int result = 0;
1109 char mode_string[12] = { '\0' };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111
1112 if (!tz || (count > sizeof(mode_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -04001113 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
1115 if (!tz->flags.cooling_mode)
Patrick Mocheld550d982006-06-27 00:41:40 -04001116 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
1118 if (copy_from_user(mode_string, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001119 return -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -04001120
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 mode_string[count] = '\0';
Len Brown4be44fc2005-08-05 00:44:28 -04001122
1123 result = acpi_thermal_set_cooling_mode(tz,
1124 simple_strtoul(mode_string, NULL,
1125 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001127 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
1129 acpi_thermal_check(tz);
1130
Patrick Mocheld550d982006-06-27 00:41:40 -04001131 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132}
1133
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset)
1135{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001136 struct acpi_thermal *tz = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138
1139 if (!tz)
1140 goto end;
1141
Matthew Garrettb1569e92008-12-03 17:55:32 +00001142 if (!tz->thermal_zone->polling_delay) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 seq_puts(seq, "<polling disabled>\n");
1144 goto end;
1145 }
1146
Matthew Garrettb1569e92008-12-03 17:55:32 +00001147 seq_printf(seq, "polling frequency: %d seconds\n",
1148 (tz->thermal_zone->polling_delay / 1000));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149
Len Brown4be44fc2005-08-05 00:44:28 -04001150 end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001151 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152}
1153
1154static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file)
1155{
1156 return single_open(file, acpi_thermal_polling_seq_show,
Len Brown4be44fc2005-08-05 00:44:28 -04001157 PDE(inode)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158}
1159
1160static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -04001161acpi_thermal_write_polling(struct file *file,
1162 const char __user * buffer,
1163 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001165 struct seq_file *m = file->private_data;
1166 struct acpi_thermal *tz = m->private;
Len Brown4be44fc2005-08-05 00:44:28 -04001167 int result = 0;
1168 char polling_string[12] = { '\0' };
1169 int seconds = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171
1172 if (!tz || (count > sizeof(polling_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -04001173 return -EINVAL;
Len Brown4be44fc2005-08-05 00:44:28 -04001174
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 if (copy_from_user(polling_string, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -04001176 return -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -04001177
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 polling_string[count] = '\0';
1179
1180 seconds = simple_strtoul(polling_string, NULL, 0);
Len Brown4be44fc2005-08-05 00:44:28 -04001181
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 result = acpi_thermal_set_polling(tz, seconds);
1183 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001184 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185
1186 acpi_thermal_check(tz);
1187
Patrick Mocheld550d982006-06-27 00:41:40 -04001188 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189}
1190
Len Brown4be44fc2005-08-05 00:44:28 -04001191static int acpi_thermal_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192{
Len Brown4be44fc2005-08-05 00:44:28 -04001193 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
1196 if (!acpi_device_dir(device)) {
1197 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -04001198 acpi_thermal_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001200 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 }
1202
1203 /* 'state' [R] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001204 entry = proc_create_data(ACPI_THERMAL_FILE_STATE,
1205 S_IRUGO, acpi_device_dir(device),
1206 &acpi_thermal_state_fops,
1207 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001209 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
1211 /* 'temperature' [R] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001212 entry = proc_create_data(ACPI_THERMAL_FILE_TEMPERATURE,
1213 S_IRUGO, acpi_device_dir(device),
1214 &acpi_thermal_temp_fops,
1215 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001217 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
Pavel Machek2db9ccb2007-08-24 11:45:50 +02001219 /* 'trip_points' [R] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001220 entry = proc_create_data(ACPI_THERMAL_FILE_TRIP_POINTS,
1221 S_IRUGO,
1222 acpi_device_dir(device),
1223 &acpi_thermal_trip_fops,
1224 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001226 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
1228 /* 'cooling_mode' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001229 entry = proc_create_data(ACPI_THERMAL_FILE_COOLING_MODE,
1230 S_IFREG | S_IRUGO | S_IWUSR,
1231 acpi_device_dir(device),
1232 &acpi_thermal_cooling_fops,
1233 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001235 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
1237 /* 'polling_frequency' [R/W] */
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -07001238 entry = proc_create_data(ACPI_THERMAL_FILE_POLLING_FREQ,
1239 S_IFREG | S_IRUGO | S_IWUSR,
1240 acpi_device_dir(device),
1241 &acpi_thermal_polling_fops,
1242 acpi_driver_data(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -04001244 return -ENODEV;
Patrick Mocheld550d982006-06-27 00:41:40 -04001245 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246}
1247
Len Brown4be44fc2005-08-05 00:44:28 -04001248static int acpi_thermal_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250
1251 if (acpi_device_dir(device)) {
1252 remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ,
1253 acpi_device_dir(device));
1254 remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE,
1255 acpi_device_dir(device));
1256 remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS,
1257 acpi_device_dir(device));
1258 remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE,
1259 acpi_device_dir(device));
1260 remove_proc_entry(ACPI_THERMAL_FILE_STATE,
1261 acpi_device_dir(device));
1262 remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir);
1263 acpi_device_dir(device) = NULL;
1264 }
1265
Patrick Mocheld550d982006-06-27 00:41:40 -04001266 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267}
1268
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269/* --------------------------------------------------------------------------
1270 Driver Interface
1271 -------------------------------------------------------------------------- */
1272
Len Brown4be44fc2005-08-05 00:44:28 -04001273static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001275 struct acpi_thermal *tz = data;
Len Brown4be44fc2005-08-05 00:44:28 -04001276 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278
1279 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001280 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281
Patrick Mochel8348e1b2006-05-19 16:54:40 -04001282 device = tz->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
1284 switch (event) {
1285 case ACPI_THERMAL_NOTIFY_TEMPERATURE:
1286 acpi_thermal_check(tz);
1287 break;
1288 case ACPI_THERMAL_NOTIFY_THRESHOLDS:
Zhang Ruice44e192008-01-17 15:51:25 +08001289 acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 acpi_thermal_check(tz);
Len Brown14e04fb32007-08-23 15:20:26 -04001291 acpi_bus_generate_proc_event(device, event, 0);
Zhang Rui962ce8c2007-08-23 01:24:31 +08001292 acpi_bus_generate_netlink_event(device->pnp.device_class,
Kay Sievers07944692008-10-30 01:18:59 +01001293 dev_name(&device->dev), event, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 break;
1295 case ACPI_THERMAL_NOTIFY_DEVICES:
Zhang Ruice44e192008-01-17 15:51:25 +08001296 acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
1297 acpi_thermal_check(tz);
Len Brown14e04fb32007-08-23 15:20:26 -04001298 acpi_bus_generate_proc_event(device, event, 0);
Zhang Rui962ce8c2007-08-23 01:24:31 +08001299 acpi_bus_generate_netlink_event(device->pnp.device_class,
Kay Sievers07944692008-10-30 01:18:59 +01001300 dev_name(&device->dev), event, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 break;
1302 default:
1303 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -04001304 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 break;
1306 }
1307
Patrick Mocheld550d982006-06-27 00:41:40 -04001308 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309}
1310
Len Brown4be44fc2005-08-05 00:44:28 -04001311static int acpi_thermal_get_info(struct acpi_thermal *tz)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312{
Len Brown4be44fc2005-08-05 00:44:28 -04001313 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315
1316 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001317 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318
1319 /* Get temperature [_TMP] (required) */
1320 result = acpi_thermal_get_temperature(tz);
1321 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001322 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 /* Get trip points [_CRT, _PSV, etc.] (required) */
1325 result = acpi_thermal_get_trip_points(tz);
1326 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -04001327 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
1329 /* Set the cooling mode [_SCP] to active cooling (default) */
1330 result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
Len Brown4be44fc2005-08-05 00:44:28 -04001331 if (!result)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 tz->flags.cooling_mode = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333
1334 /* Get default polling frequency [_TZP] (optional) */
1335 if (tzp)
1336 tz->polling_frequency = tzp;
1337 else
1338 acpi_thermal_get_polling_frequency(tz);
1339
Patrick Mocheld550d982006-06-27 00:41:40 -04001340 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341}
1342
Jean Delvare13614e32009-04-06 16:01:46 +02001343/*
1344 * The exact offset between Kelvin and degree Celsius is 273.15. However ACPI
1345 * handles temperature values with a single decimal place. As a consequence,
1346 * some implementations use an offset of 273.1 and others use an offset of
1347 * 273.2. Try to find out which one is being used, to present the most
1348 * accurate and visually appealing number.
1349 *
1350 * The heuristic below should work for all ACPI thermal zones which have a
1351 * critical trip point with a value being a multiple of 0.5 degree Celsius.
1352 */
1353static void acpi_thermal_guess_offset(struct acpi_thermal *tz)
1354{
1355 if (tz->trips.critical.flags.valid &&
1356 (tz->trips.critical.temperature % 5) == 1)
1357 tz->kelvin_offset = 2731;
1358 else
1359 tz->kelvin_offset = 2732;
1360}
1361
Len Brown4be44fc2005-08-05 00:44:28 -04001362static int acpi_thermal_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363{
Len Brown4be44fc2005-08-05 00:44:28 -04001364 int result = 0;
1365 acpi_status status = AE_OK;
1366 struct acpi_thermal *tz = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
1369 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001370 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
Burman Yan36bcbec2006-12-19 12:56:11 -08001372 tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 if (!tz)
Patrick Mocheld550d982006-06-27 00:41:40 -04001374 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Patrick Mochel8348e1b2006-05-19 16:54:40 -04001376 tz->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 strcpy(tz->name, device->pnp.bus_id);
1378 strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME);
1379 strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -07001380 device->driver_data = tz;
Alexey Starikovskiy6e215782007-09-01 00:11:59 +04001381 mutex_init(&tz->lock);
Zhang Rui3f655ef2008-01-17 15:51:11 +08001382
1383
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 result = acpi_thermal_get_info(tz);
1385 if (result)
Zhang Rui3f655ef2008-01-17 15:51:11 +08001386 goto free_memory;
1387
Jean Delvare13614e32009-04-06 16:01:46 +02001388 acpi_thermal_guess_offset(tz);
1389
Zhang Rui3f655ef2008-01-17 15:51:11 +08001390 result = acpi_thermal_register_thermal_zone(tz);
1391 if (result)
1392 goto free_memory;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393
1394 result = acpi_thermal_add_fs(device);
1395 if (result)
Zhang Rui3f655ef2008-01-17 15:51:11 +08001396 goto unregister_thermal_zone;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397
Patrick Mochel38ba7c92006-05-19 16:54:48 -04001398 status = acpi_install_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001399 ACPI_DEVICE_NOTIFY,
1400 acpi_thermal_notify, tz);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 if (ACPI_FAILURE(status)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 result = -ENODEV;
Zhang Rui3f655ef2008-01-17 15:51:11 +08001403 goto remove_fs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 }
1405
1406 printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
Len Brown4be44fc2005-08-05 00:44:28 -04001407 acpi_device_name(device), acpi_device_bid(device),
1408 KELVIN_TO_CELSIUS(tz->temperature));
Zhang Rui3f655ef2008-01-17 15:51:11 +08001409 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410
Zhang Rui3f655ef2008-01-17 15:51:11 +08001411remove_fs:
1412 acpi_thermal_remove_fs(device);
1413unregister_thermal_zone:
1414 thermal_zone_device_unregister(tz->thermal_zone);
1415free_memory:
1416 kfree(tz);
1417end:
Patrick Mocheld550d982006-06-27 00:41:40 -04001418 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419}
1420
Len Brown4be44fc2005-08-05 00:44:28 -04001421static int acpi_thermal_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422{
Len Brown4be44fc2005-08-05 00:44:28 -04001423 acpi_status status = AE_OK;
1424 struct acpi_thermal *tz = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001427 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001429 tz = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430
Patrick Mochel38ba7c92006-05-19 16:54:48 -04001431 status = acpi_remove_notify_handler(device->handle,
Len Brown4be44fc2005-08-05 00:44:28 -04001432 ACPI_DEVICE_NOTIFY,
1433 acpi_thermal_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 acpi_thermal_remove_fs(device);
Zhang Rui3f655ef2008-01-17 15:51:11 +08001436 acpi_thermal_unregister_thermal_zone(tz);
Alexey Starikovskiy6e215782007-09-01 00:11:59 +04001437 mutex_destroy(&tz->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 kfree(tz);
Patrick Mocheld550d982006-06-27 00:41:40 -04001439 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440}
1441
Patrick Mochel5d9464a2006-12-07 20:56:27 +08001442static int acpi_thermal_resume(struct acpi_device *device)
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001443{
1444 struct acpi_thermal *tz = NULL;
Konstantin Karasyovb1028c52007-02-16 02:23:07 -05001445 int i, j, power_state, result;
1446
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001447
1448 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001449 return -EINVAL;
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001450
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001451 tz = acpi_driver_data(device);
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001452
Konstantin Karasyovbed936f2006-07-10 04:44:26 -07001453 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
Konstantin Karasyovb1028c52007-02-16 02:23:07 -05001454 if (!(&tz->trips.active[i]))
1455 break;
1456 if (!tz->trips.active[i].flags.valid)
1457 break;
1458 tz->trips.active[i].flags.enabled = 1;
1459 for (j = 0; j < tz->trips.active[i].devices.count; j++) {
1460 result = acpi_bus_get_power(tz->trips.active[i].devices.
1461 handles[j], &power_state);
1462 if (result || (power_state != ACPI_STATE_D0)) {
1463 tz->trips.active[i].flags.enabled = 0;
1464 break;
1465 }
Konstantin Karasyovbed936f2006-07-10 04:44:26 -07001466 }
Konstantin Karasyovb1028c52007-02-16 02:23:07 -05001467 tz->state.active |= tz->trips.active[i].flags.enabled;
Konstantin Karasyovbed936f2006-07-10 04:44:26 -07001468 }
1469
Konstantin Karasyovb1028c52007-02-16 02:23:07 -05001470 acpi_thermal_check(tz);
Konstantin Karasyov74ce14682006-05-08 08:32:00 -04001471
1472 return AE_OK;
1473}
1474
Jeff Garzik18552562007-10-03 15:15:40 -04001475static int thermal_act(const struct dmi_system_id *d) {
Len Brown0b5bfa12007-08-12 00:13:02 -04001476
1477 if (act == 0) {
1478 printk(KERN_NOTICE "ACPI: %s detected: "
1479 "disabling all active thermal trip points\n", d->ident);
1480 act = -1;
1481 }
1482 return 0;
1483}
Jeff Garzik18552562007-10-03 15:15:40 -04001484static int thermal_nocrt(const struct dmi_system_id *d) {
Len Brown8c99fdc2007-08-20 18:46:50 -04001485
1486 printk(KERN_NOTICE "ACPI: %s detected: "
1487 "disabling all critical thermal trip point actions.\n", d->ident);
1488 nocrt = 1;
1489 return 0;
1490}
Jeff Garzik18552562007-10-03 15:15:40 -04001491static int thermal_tzp(const struct dmi_system_id *d) {
Len Brown0b5bfa12007-08-12 00:13:02 -04001492
1493 if (tzp == 0) {
1494 printk(KERN_NOTICE "ACPI: %s detected: "
1495 "enabling thermal zone polling\n", d->ident);
1496 tzp = 300; /* 300 dS = 30 Seconds */
1497 }
1498 return 0;
1499}
Jeff Garzik18552562007-10-03 15:15:40 -04001500static int thermal_psv(const struct dmi_system_id *d) {
Len Brown0b5bfa12007-08-12 00:13:02 -04001501
1502 if (psv == 0) {
1503 printk(KERN_NOTICE "ACPI: %s detected: "
1504 "disabling all passive thermal trip points\n", d->ident);
1505 psv = -1;
1506 }
1507 return 0;
1508}
1509
1510static struct dmi_system_id thermal_dmi_table[] __initdata = {
1511 /*
1512 * Award BIOS on this AOpen makes thermal control almost worthless.
1513 * http://bugzilla.kernel.org/show_bug.cgi?id=8842
1514 */
1515 {
1516 .callback = thermal_act,
1517 .ident = "AOpen i915GMm-HFS",
1518 .matches = {
1519 DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
1520 DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
1521 },
1522 },
1523 {
1524 .callback = thermal_psv,
1525 .ident = "AOpen i915GMm-HFS",
1526 .matches = {
1527 DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
1528 DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
1529 },
1530 },
1531 {
1532 .callback = thermal_tzp,
1533 .ident = "AOpen i915GMm-HFS",
1534 .matches = {
1535 DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
1536 DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
1537 },
1538 },
Len Brown8c99fdc2007-08-20 18:46:50 -04001539 {
1540 .callback = thermal_nocrt,
1541 .ident = "Gigabyte GA-7ZX",
1542 .matches = {
1543 DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
1544 DMI_MATCH(DMI_BOARD_NAME, "7ZX"),
1545 },
1546 },
Len Brown0b5bfa12007-08-12 00:13:02 -04001547 {}
1548};
Len Brown0b5bfa12007-08-12 00:13:02 -04001549
Len Brown4be44fc2005-08-05 00:44:28 -04001550static int __init acpi_thermal_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551{
Len Brown4be44fc2005-08-05 00:44:28 -04001552 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Len Brown0b5bfa12007-08-12 00:13:02 -04001554 dmi_check_system(thermal_dmi_table);
1555
Len Brown72b33ef2007-08-12 00:12:17 -04001556 if (off) {
1557 printk(KERN_NOTICE "ACPI: thermal control disabled\n");
1558 return -ENODEV;
1559 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir);
1561 if (!acpi_thermal_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001562 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
1564 result = acpi_bus_register_driver(&acpi_thermal_driver);
1565 if (result < 0) {
1566 remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001567 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 }
1569
Patrick Mocheld550d982006-06-27 00:41:40 -04001570 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571}
1572
Len Brown4be44fc2005-08-05 00:44:28 -04001573static void __exit acpi_thermal_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575
1576 acpi_bus_unregister_driver(&acpi_thermal_driver);
1577
1578 remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
1579
Patrick Mocheld550d982006-06-27 00:41:40 -04001580 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581}
1582
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583module_init(acpi_thermal_init);
1584module_exit(acpi_thermal_exit);