include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[linux-2.6-block.git] / drivers / acpi / thermal.c
CommitLineData
1da177e4
LT
1/*
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>
0b5bfa1c 36#include <linux/dmi.h>
1da177e4 37#include <linux/init.h>
5a0e3ad6 38#include <linux/slab.h>
1da177e4
LT
39#include <linux/types.h>
40#include <linux/proc_fs.h>
cd354f1a 41#include <linux/jiffies.h>
1da177e4
LT
42#include <linux/kmod.h>
43#include <linux/seq_file.h>
10a0a8d4 44#include <linux/reboot.h>
f6f5c45e 45#include <linux/device.h>
1da177e4 46#include <asm/uaccess.h>
3f655ef8 47#include <linux/thermal.h>
1da177e4
LT
48#include <acpi/acpi_bus.h>
49#include <acpi/acpi_drivers.h>
50
a192a958
LB
51#define PREFIX "ACPI: "
52
1da177e4 53#define ACPI_THERMAL_CLASS "thermal_zone"
1da177e4
LT
54#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone"
55#define ACPI_THERMAL_FILE_STATE "state"
56#define ACPI_THERMAL_FILE_TEMPERATURE "temperature"
57#define ACPI_THERMAL_FILE_TRIP_POINTS "trip_points"
58#define ACPI_THERMAL_FILE_COOLING_MODE "cooling_mode"
59#define ACPI_THERMAL_FILE_POLLING_FREQ "polling_frequency"
60#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80
61#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81
62#define ACPI_THERMAL_NOTIFY_DEVICES 0x82
63#define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0
64#define ACPI_THERMAL_NOTIFY_HOT 0xF1
65#define ACPI_THERMAL_MODE_ACTIVE 0x00
1da177e4
LT
66
67#define ACPI_THERMAL_MAX_ACTIVE 10
68#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
69
1da177e4 70#define _COMPONENT ACPI_THERMAL_COMPONENT
f52fd66d 71ACPI_MODULE_NAME("thermal");
1da177e4 72
1cbf4c56 73MODULE_AUTHOR("Paul Diefenbaugh");
7cda93e0 74MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
1da177e4
LT
75MODULE_LICENSE("GPL");
76
f8707ec9
LB
77static int act;
78module_param(act, int, 0644);
3c1d36da 79MODULE_PARM_DESC(act, "Disable or override all lowest active trip points.");
f8707ec9 80
c52a7419
LB
81static int crt;
82module_param(crt, int, 0644);
83MODULE_PARM_DESC(crt, "Disable or lower all critical trip points.");
84
1da177e4 85static int tzp;
730ff34d 86module_param(tzp, int, 0444);
3c1d36da 87MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.");
1da177e4 88
f5487145
LB
89static int nocrt;
90module_param(nocrt, int, 0);
8c99fdce 91MODULE_PARM_DESC(nocrt, "Set to take no action upon ACPI thermal zone critical trips points.");
f5487145 92
72b33ef8
LB
93static int off;
94module_param(off, int, 0);
3c1d36da 95MODULE_PARM_DESC(off, "Set to disable ACPI thermal support.");
72b33ef8 96
a70cdc52
LB
97static int psv;
98module_param(psv, int, 0644);
3c1d36da 99MODULE_PARM_DESC(psv, "Disable or override all passive trip points.");
a70cdc52 100
4be44fcd
LB
101static int acpi_thermal_add(struct acpi_device *device);
102static int acpi_thermal_remove(struct acpi_device *device, int type);
5d9464a4 103static int acpi_thermal_resume(struct acpi_device *device);
342d550d 104static void acpi_thermal_notify(struct acpi_device *device, u32 event);
1da177e4
LT
105static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file);
106static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file);
107static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file);
1da177e4 108static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file);
4be44fcd
LB
109static ssize_t acpi_thermal_write_cooling_mode(struct file *,
110 const char __user *, size_t,
111 loff_t *);
1da177e4 112static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file);
4be44fcd
LB
113static ssize_t acpi_thermal_write_polling(struct file *, const char __user *,
114 size_t, loff_t *);
1da177e4 115
1ba90e3a
TR
116static const struct acpi_device_id thermal_device_ids[] = {
117 {ACPI_THERMAL_HID, 0},
118 {"", 0},
119};
120MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
121
1da177e4 122static struct acpi_driver acpi_thermal_driver = {
c2b6705b 123 .name = "thermal",
4be44fcd 124 .class = ACPI_THERMAL_CLASS,
1ba90e3a 125 .ids = thermal_device_ids,
4be44fcd
LB
126 .ops = {
127 .add = acpi_thermal_add,
128 .remove = acpi_thermal_remove,
74ce1468 129 .resume = acpi_thermal_resume,
342d550d 130 .notify = acpi_thermal_notify,
4be44fcd 131 },
1da177e4
LT
132};
133
134struct acpi_thermal_state {
4be44fcd
LB
135 u8 critical:1;
136 u8 hot:1;
137 u8 passive:1;
138 u8 active:1;
139 u8 reserved:4;
140 int active_index;
1da177e4
LT
141};
142
143struct acpi_thermal_state_flags {
4be44fcd
LB
144 u8 valid:1;
145 u8 enabled:1;
146 u8 reserved:6;
1da177e4
LT
147};
148
149struct acpi_thermal_critical {
150 struct acpi_thermal_state_flags flags;
4be44fcd 151 unsigned long temperature;
1da177e4
LT
152};
153
154struct acpi_thermal_hot {
155 struct acpi_thermal_state_flags flags;
4be44fcd 156 unsigned long temperature;
1da177e4
LT
157};
158
159struct acpi_thermal_passive {
160 struct acpi_thermal_state_flags flags;
4be44fcd
LB
161 unsigned long temperature;
162 unsigned long tc1;
163 unsigned long tc2;
164 unsigned long tsp;
165 struct acpi_handle_list devices;
1da177e4
LT
166};
167
168struct acpi_thermal_active {
169 struct acpi_thermal_state_flags flags;
4be44fcd
LB
170 unsigned long temperature;
171 struct acpi_handle_list devices;
1da177e4
LT
172};
173
174struct acpi_thermal_trips {
175 struct acpi_thermal_critical critical;
4be44fcd 176 struct acpi_thermal_hot hot;
1da177e4
LT
177 struct acpi_thermal_passive passive;
178 struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
179};
180
181struct acpi_thermal_flags {
4be44fcd
LB
182 u8 cooling_mode:1; /* _SCP */
183 u8 devices:1; /* _TZD */
184 u8 reserved:6;
1da177e4
LT
185};
186
187struct acpi_thermal {
8348e1b1 188 struct acpi_device * device;
4be44fcd
LB
189 acpi_bus_id name;
190 unsigned long temperature;
191 unsigned long last_temperature;
192 unsigned long polling_frequency;
4be44fcd 193 volatile u8 zombie;
1da177e4
LT
194 struct acpi_thermal_flags flags;
195 struct acpi_thermal_state state;
196 struct acpi_thermal_trips trips;
4be44fcd 197 struct acpi_handle_list devices;
3f655ef8
ZR
198 struct thermal_zone_device *thermal_zone;
199 int tz_enabled;
13614e37 200 int kelvin_offset;
6e215785 201 struct mutex lock;
1da177e4
LT
202};
203
d7508032 204static const struct file_operations acpi_thermal_state_fops = {
cf7acfab 205 .owner = THIS_MODULE,
4be44fcd
LB
206 .open = acpi_thermal_state_open_fs,
207 .read = seq_read,
208 .llseek = seq_lseek,
209 .release = single_release,
1da177e4
LT
210};
211
d7508032 212static const struct file_operations acpi_thermal_temp_fops = {
cf7acfab 213 .owner = THIS_MODULE,
4be44fcd
LB
214 .open = acpi_thermal_temp_open_fs,
215 .read = seq_read,
216 .llseek = seq_lseek,
217 .release = single_release,
1da177e4
LT
218};
219
d7508032 220static const struct file_operations acpi_thermal_trip_fops = {
cf7acfab 221 .owner = THIS_MODULE,
4be44fcd
LB
222 .open = acpi_thermal_trip_open_fs,
223 .read = seq_read,
4be44fcd
LB
224 .llseek = seq_lseek,
225 .release = single_release,
1da177e4
LT
226};
227
d7508032 228static const struct file_operations acpi_thermal_cooling_fops = {
cf7acfab 229 .owner = THIS_MODULE,
4be44fcd
LB
230 .open = acpi_thermal_cooling_open_fs,
231 .read = seq_read,
232 .write = acpi_thermal_write_cooling_mode,
233 .llseek = seq_lseek,
234 .release = single_release,
1da177e4
LT
235};
236
d7508032 237static const struct file_operations acpi_thermal_polling_fops = {
cf7acfab 238 .owner = THIS_MODULE,
4be44fcd
LB
239 .open = acpi_thermal_polling_open_fs,
240 .read = seq_read,
241 .write = acpi_thermal_write_polling,
242 .llseek = seq_lseek,
243 .release = single_release,
1da177e4
LT
244};
245
246/* --------------------------------------------------------------------------
247 Thermal Zone Management
248 -------------------------------------------------------------------------- */
249
4be44fcd 250static int acpi_thermal_get_temperature(struct acpi_thermal *tz)
1da177e4 251{
4be44fcd 252 acpi_status status = AE_OK;
27663c58 253 unsigned long long tmp;
1da177e4
LT
254
255 if (!tz)
d550d98d 256 return -EINVAL;
1da177e4
LT
257
258 tz->last_temperature = tz->temperature;
259
27663c58 260 status = acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp);
1da177e4 261 if (ACPI_FAILURE(status))
d550d98d 262 return -ENODEV;
1da177e4 263
27663c58 264 tz->temperature = tmp;
4be44fcd
LB
265 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n",
266 tz->temperature));
1da177e4 267
d550d98d 268 return 0;
1da177e4
LT
269}
270
4be44fcd 271static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
1da177e4 272{
4be44fcd 273 acpi_status status = AE_OK;
27663c58 274 unsigned long long tmp;
1da177e4
LT
275
276 if (!tz)
d550d98d 277 return -EINVAL;
1da177e4 278
27663c58 279 status = acpi_evaluate_integer(tz->device->handle, "_TZP", NULL, &tmp);
1da177e4 280 if (ACPI_FAILURE(status))
d550d98d 281 return -ENODEV;
1da177e4 282
27663c58 283 tz->polling_frequency = tmp;
4be44fcd
LB
284 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n",
285 tz->polling_frequency));
1da177e4 286
d550d98d 287 return 0;
1da177e4
LT
288}
289
4be44fcd 290static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds)
1da177e4 291{
1da177e4
LT
292
293 if (!tz)
d550d98d 294 return -EINVAL;
1da177e4
LT
295
296 tz->polling_frequency = seconds * 10; /* Convert value to deci-seconds */
297
b1569e99
MG
298 tz->thermal_zone->polling_delay = seconds * 1000;
299
300 if (tz->tz_enabled)
301 thermal_zone_device_update(tz->thermal_zone);
302
4be44fcd
LB
303 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
304 "Polling frequency set to %lu seconds\n",
636cedf9 305 tz->polling_frequency/10));
1da177e4 306
d550d98d 307 return 0;
1da177e4
LT
308}
309
4be44fcd 310static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
1da177e4 311{
4be44fcd
LB
312 acpi_status status = AE_OK;
313 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
314 struct acpi_object_list arg_list = { 1, &arg0 };
315 acpi_handle handle = NULL;
1da177e4 316
1da177e4
LT
317
318 if (!tz)
d550d98d 319 return -EINVAL;
1da177e4 320
38ba7c9e 321 status = acpi_get_handle(tz->device->handle, "_SCP", &handle);
1da177e4
LT
322 if (ACPI_FAILURE(status)) {
323 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n"));
d550d98d 324 return -ENODEV;
1da177e4
LT
325 }
326
327 arg0.integer.value = mode;
328
329 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
330 if (ACPI_FAILURE(status))
d550d98d 331 return -ENODEV;
1da177e4 332
d550d98d 333 return 0;
1da177e4
LT
334}
335
ce44e197
ZR
336#define ACPI_TRIPS_CRITICAL 0x01
337#define ACPI_TRIPS_HOT 0x02
338#define ACPI_TRIPS_PASSIVE 0x04
339#define ACPI_TRIPS_ACTIVE 0x08
340#define ACPI_TRIPS_DEVICES 0x10
1da177e4 341
ce44e197
ZR
342#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
343#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES
1da177e4 344
ce44e197
ZR
345#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \
346 ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \
347 ACPI_TRIPS_DEVICES)
1da177e4 348
ce44e197
ZR
349/*
350 * This exception is thrown out in two cases:
351 * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
352 * when re-evaluating the AML code.
353 * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
354 * We need to re-bind the cooling devices of a thermal zone when this occurs.
355 */
356#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \
357do { \
358 if (flags != ACPI_TRIPS_INIT) \
359 ACPI_EXCEPTION((AE_INFO, AE_ERROR, \
360 "ACPI thermal trip point %s changed\n" \
361 "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \
362} while (0)
363
364static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
365{
366 acpi_status status = AE_OK;
27663c58 367 unsigned long long tmp;
ce44e197
ZR
368 struct acpi_handle_list devices;
369 int valid = 0;
370 int i;
1da177e4 371
fa809452 372 /* Critical Shutdown */
ce44e197
ZR
373 if (flag & ACPI_TRIPS_CRITICAL) {
374 status = acpi_evaluate_integer(tz->device->handle,
27663c58
MW
375 "_CRT", NULL, &tmp);
376 tz->trips.critical.temperature = tmp;
a39a2d7c
AV
377 /*
378 * Treat freezing temperatures as invalid as well; some
379 * BIOSes return really low values and cause reboots at startup.
b731d7b6 380 * Below zero (Celsius) values clearly aren't right for sure..
a39a2d7c
AV
381 * ... so lets discard those as invalid.
382 */
fa809452
TR
383 if (ACPI_FAILURE(status)) {
384 tz->trips.critical.flags.valid = 0;
385 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
386 "No critical threshold\n"));
387 } else if (tmp <= 2732) {
388 printk(KERN_WARNING FW_BUG "Invalid critical threshold "
389 "(%llu)\n", tmp);
c52a7419 390 tz->trips.critical.flags.valid = 0;
ce44e197
ZR
391 } else {
392 tz->trips.critical.flags.valid = 1;
393 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
fa809452
TR
394 "Found critical threshold [%lu]\n",
395 tz->trips.critical.temperature));
ce44e197
ZR
396 }
397 if (tz->trips.critical.flags.valid == 1) {
398 if (crt == -1) {
399 tz->trips.critical.flags.valid = 0;
400 } else if (crt > 0) {
401 unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
402 /*
22a94d79 403 * Allow override critical threshold
ce44e197 404 */
22a94d79
ZR
405 if (crt_k > tz->trips.critical.temperature)
406 printk(KERN_WARNING PREFIX
407 "Critical threshold %d C\n", crt);
408 tz->trips.critical.temperature = crt_k;
ce44e197 409 }
c52a7419
LB
410 }
411 }
412
1da177e4 413 /* Critical Sleep (optional) */
ce44e197 414 if (flag & ACPI_TRIPS_HOT) {
a70cdc52 415 status = acpi_evaluate_integer(tz->device->handle,
27663c58 416 "_HOT", NULL, &tmp);
ce44e197
ZR
417 if (ACPI_FAILURE(status)) {
418 tz->trips.hot.flags.valid = 0;
419 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
420 "No hot threshold\n"));
421 } else {
27663c58 422 tz->trips.hot.temperature = tmp;
ce44e197
ZR
423 tz->trips.hot.flags.valid = 1;
424 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
425 "Found hot threshold [%lu]\n",
426 tz->trips.critical.temperature));
427 }
a70cdc52
LB
428 }
429
ce44e197 430 /* Passive (optional) */
0e4240d9
ZR
431 if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) ||
432 (flag == ACPI_TRIPS_INIT)) {
ce44e197
ZR
433 valid = tz->trips.passive.flags.valid;
434 if (psv == -1) {
435 status = AE_SUPPORT;
436 } else if (psv > 0) {
27663c58 437 tmp = CELSIUS_TO_KELVIN(psv);
ce44e197
ZR
438 status = AE_OK;
439 } else {
440 status = acpi_evaluate_integer(tz->device->handle,
27663c58 441 "_PSV", NULL, &tmp);
ce44e197 442 }
1da177e4 443
1da177e4
LT
444 if (ACPI_FAILURE(status))
445 tz->trips.passive.flags.valid = 0;
ce44e197 446 else {
27663c58 447 tz->trips.passive.temperature = tmp;
ce44e197
ZR
448 tz->trips.passive.flags.valid = 1;
449 if (flag == ACPI_TRIPS_INIT) {
450 status = acpi_evaluate_integer(
451 tz->device->handle, "_TC1",
27663c58 452 NULL, &tmp);
ce44e197
ZR
453 if (ACPI_FAILURE(status))
454 tz->trips.passive.flags.valid = 0;
27663c58
MW
455 else
456 tz->trips.passive.tc1 = tmp;
ce44e197
ZR
457 status = acpi_evaluate_integer(
458 tz->device->handle, "_TC2",
27663c58 459 NULL, &tmp);
ce44e197
ZR
460 if (ACPI_FAILURE(status))
461 tz->trips.passive.flags.valid = 0;
27663c58
MW
462 else
463 tz->trips.passive.tc2 = tmp;
ce44e197
ZR
464 status = acpi_evaluate_integer(
465 tz->device->handle, "_TSP",
27663c58 466 NULL, &tmp);
ce44e197
ZR
467 if (ACPI_FAILURE(status))
468 tz->trips.passive.flags.valid = 0;
27663c58
MW
469 else
470 tz->trips.passive.tsp = tmp;
ce44e197
ZR
471 }
472 }
473 }
474 if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
475 memset(&devices, 0, sizeof(struct acpi_handle_list));
476 status = acpi_evaluate_reference(tz->device->handle, "_PSL",
477 NULL, &devices);
0e4240d9
ZR
478 if (ACPI_FAILURE(status)) {
479 printk(KERN_WARNING PREFIX
480 "Invalid passive threshold\n");
1da177e4 481 tz->trips.passive.flags.valid = 0;
0e4240d9 482 }
1da177e4 483 else
ce44e197 484 tz->trips.passive.flags.valid = 1;
1da177e4 485
ce44e197
ZR
486 if (memcmp(&tz->trips.passive.devices, &devices,
487 sizeof(struct acpi_handle_list))) {
488 memcpy(&tz->trips.passive.devices, &devices,
489 sizeof(struct acpi_handle_list));
490 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
491 }
492 }
493 if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
494 if (valid != tz->trips.passive.flags.valid)
495 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
496 }
1da177e4 497
ce44e197 498 /* Active (optional) */
4be44fcd 499 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
4be44fcd 500 char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
ce44e197 501 valid = tz->trips.active[i].flags.valid;
1da177e4 502
f8707ec9 503 if (act == -1)
ce44e197
ZR
504 break; /* disable all active trip points */
505
0e4240d9
ZR
506 if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) &&
507 tz->trips.active[i].flags.valid)) {
ce44e197 508 status = acpi_evaluate_integer(tz->device->handle,
27663c58 509 name, NULL, &tmp);
ce44e197
ZR
510 if (ACPI_FAILURE(status)) {
511 tz->trips.active[i].flags.valid = 0;
512 if (i == 0)
513 break;
514 if (act <= 0)
515 break;
516 if (i == 1)
517 tz->trips.active[0].temperature =
518 CELSIUS_TO_KELVIN(act);
519 else
520 /*
521 * Don't allow override higher than
522 * the next higher trip point
523 */
524 tz->trips.active[i - 1].temperature =
525 (tz->trips.active[i - 2].temperature <
526 CELSIUS_TO_KELVIN(act) ?
527 tz->trips.active[i - 2].temperature :
528 CELSIUS_TO_KELVIN(act));
f8707ec9 529 break;
27663c58
MW
530 } else {
531 tz->trips.active[i].temperature = tmp;
ce44e197 532 tz->trips.active[i].flags.valid = 1;
27663c58 533 }
f8707ec9 534 }
1da177e4
LT
535
536 name[2] = 'L';
ce44e197
ZR
537 if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
538 memset(&devices, 0, sizeof(struct acpi_handle_list));
539 status = acpi_evaluate_reference(tz->device->handle,
540 name, NULL, &devices);
0e4240d9
ZR
541 if (ACPI_FAILURE(status)) {
542 printk(KERN_WARNING PREFIX
543 "Invalid active%d threshold\n", i);
ce44e197 544 tz->trips.active[i].flags.valid = 0;
0e4240d9 545 }
ce44e197
ZR
546 else
547 tz->trips.active[i].flags.valid = 1;
548
549 if (memcmp(&tz->trips.active[i].devices, &devices,
550 sizeof(struct acpi_handle_list))) {
551 memcpy(&tz->trips.active[i].devices, &devices,
552 sizeof(struct acpi_handle_list));
553 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
554 }
555 }
556 if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
557 if (valid != tz->trips.active[i].flags.valid)
558 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
559
560 if (!tz->trips.active[i].flags.valid)
561 break;
562 }
563
564 if (flag & ACPI_TRIPS_DEVICES) {
565 memset(&devices, 0, sizeof(struct acpi_handle_list));
566 status = acpi_evaluate_reference(tz->device->handle, "_TZD",
567 NULL, &devices);
568 if (memcmp(&tz->devices, &devices,
569 sizeof(struct acpi_handle_list))) {
570 memcpy(&tz->devices, &devices,
571 sizeof(struct acpi_handle_list));
572 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
573 }
1da177e4
LT
574 }
575
d550d98d 576 return 0;
1da177e4
LT
577}
578
ce44e197 579static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
1da177e4 580{
8b7ef6d8
TR
581 int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
582
583 if (ret)
584 return ret;
585
586 valid = tz->trips.critical.flags.valid |
587 tz->trips.hot.flags.valid |
588 tz->trips.passive.flags.valid;
589
590 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
591 valid |= tz->trips.active[i].flags.valid;
592
593 if (!valid) {
594 printk(KERN_WARNING FW_BUG "No valid trip found\n");
595 return -ENODEV;
596 }
597 return 0;
1da177e4
LT
598}
599
4be44fcd 600static void acpi_thermal_check(void *data)
1da177e4 601{
50dd0969 602 struct acpi_thermal *tz = data;
1da177e4 603
b1569e99 604 thermal_zone_device_update(tz->thermal_zone);
1da177e4
LT
605}
606
3f655ef8 607/* sys I/F for generic thermal sysfs support */
13614e37 608#define KELVIN_TO_MILLICELSIUS(t, off) (((t) - (off)) * 100)
5e012760 609
6503e5df
MG
610static int thermal_get_temp(struct thermal_zone_device *thermal,
611 unsigned long *temp)
3f655ef8
ZR
612{
613 struct acpi_thermal *tz = thermal->devdata;
76ecb4f2 614 int result;
3f655ef8
ZR
615
616 if (!tz)
617 return -EINVAL;
618
76ecb4f2
ZR
619 result = acpi_thermal_get_temperature(tz);
620 if (result)
621 return result;
622
13614e37 623 *temp = KELVIN_TO_MILLICELSIUS(tz->temperature, tz->kelvin_offset);
6503e5df 624 return 0;
3f655ef8
ZR
625}
626
627static const char enabled[] = "kernel";
628static const char disabled[] = "user";
629static int thermal_get_mode(struct thermal_zone_device *thermal,
6503e5df 630 enum thermal_device_mode *mode)
3f655ef8
ZR
631{
632 struct acpi_thermal *tz = thermal->devdata;
633
634 if (!tz)
635 return -EINVAL;
636
6503e5df
MG
637 *mode = tz->tz_enabled ? THERMAL_DEVICE_ENABLED :
638 THERMAL_DEVICE_DISABLED;
639
640 return 0;
3f655ef8
ZR
641}
642
643static int thermal_set_mode(struct thermal_zone_device *thermal,
6503e5df 644 enum thermal_device_mode mode)
3f655ef8
ZR
645{
646 struct acpi_thermal *tz = thermal->devdata;
647 int enable;
648
649 if (!tz)
650 return -EINVAL;
651
652 /*
653 * enable/disable thermal management from ACPI thermal driver
654 */
6503e5df 655 if (mode == THERMAL_DEVICE_ENABLED)
3f655ef8 656 enable = 1;
6503e5df 657 else if (mode == THERMAL_DEVICE_DISABLED)
3f655ef8
ZR
658 enable = 0;
659 else
660 return -EINVAL;
661
662 if (enable != tz->tz_enabled) {
663 tz->tz_enabled = enable;
664 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
665 "%s ACPI thermal control\n",
666 tz->tz_enabled ? enabled : disabled));
667 acpi_thermal_check(tz);
668 }
669 return 0;
670}
671
672static int thermal_get_trip_type(struct thermal_zone_device *thermal,
6503e5df 673 int trip, enum thermal_trip_type *type)
3f655ef8
ZR
674{
675 struct acpi_thermal *tz = thermal->devdata;
676 int i;
677
678 if (!tz || trip < 0)
679 return -EINVAL;
680
681 if (tz->trips.critical.flags.valid) {
6503e5df
MG
682 if (!trip) {
683 *type = THERMAL_TRIP_CRITICAL;
684 return 0;
685 }
3f655ef8
ZR
686 trip--;
687 }
688
689 if (tz->trips.hot.flags.valid) {
6503e5df
MG
690 if (!trip) {
691 *type = THERMAL_TRIP_HOT;
692 return 0;
693 }
3f655ef8
ZR
694 trip--;
695 }
696
697 if (tz->trips.passive.flags.valid) {
6503e5df
MG
698 if (!trip) {
699 *type = THERMAL_TRIP_PASSIVE;
700 return 0;
701 }
3f655ef8
ZR
702 trip--;
703 }
704
705 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
706 tz->trips.active[i].flags.valid; i++) {
6503e5df
MG
707 if (!trip) {
708 *type = THERMAL_TRIP_ACTIVE;
709 return 0;
710 }
3f655ef8
ZR
711 trip--;
712 }
713
714 return -EINVAL;
715}
716
717static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
6503e5df 718 int trip, unsigned long *temp)
3f655ef8
ZR
719{
720 struct acpi_thermal *tz = thermal->devdata;
721 int i;
722
723 if (!tz || trip < 0)
724 return -EINVAL;
725
726 if (tz->trips.critical.flags.valid) {
6503e5df
MG
727 if (!trip) {
728 *temp = KELVIN_TO_MILLICELSIUS(
13614e37
JD
729 tz->trips.critical.temperature,
730 tz->kelvin_offset);
6503e5df
MG
731 return 0;
732 }
3f655ef8
ZR
733 trip--;
734 }
735
736 if (tz->trips.hot.flags.valid) {
6503e5df
MG
737 if (!trip) {
738 *temp = KELVIN_TO_MILLICELSIUS(
13614e37
JD
739 tz->trips.hot.temperature,
740 tz->kelvin_offset);
6503e5df
MG
741 return 0;
742 }
3f655ef8
ZR
743 trip--;
744 }
745
746 if (tz->trips.passive.flags.valid) {
6503e5df
MG
747 if (!trip) {
748 *temp = KELVIN_TO_MILLICELSIUS(
13614e37
JD
749 tz->trips.passive.temperature,
750 tz->kelvin_offset);
6503e5df
MG
751 return 0;
752 }
3f655ef8
ZR
753 trip--;
754 }
755
756 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
757 tz->trips.active[i].flags.valid; i++) {
6503e5df
MG
758 if (!trip) {
759 *temp = KELVIN_TO_MILLICELSIUS(
13614e37
JD
760 tz->trips.active[i].temperature,
761 tz->kelvin_offset);
6503e5df
MG
762 return 0;
763 }
3f655ef8
ZR
764 trip--;
765 }
766
767 return -EINVAL;
768}
769
9ec732ff
ZR
770static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
771 unsigned long *temperature) {
772 struct acpi_thermal *tz = thermal->devdata;
773
774 if (tz->trips.critical.flags.valid) {
775 *temperature = KELVIN_TO_MILLICELSIUS(
13614e37
JD
776 tz->trips.critical.temperature,
777 tz->kelvin_offset);
9ec732ff
ZR
778 return 0;
779 } else
780 return -EINVAL;
781}
782
b1569e99
MG
783static int thermal_notify(struct thermal_zone_device *thermal, int trip,
784 enum thermal_trip_type trip_type)
785{
786 u8 type = 0;
787 struct acpi_thermal *tz = thermal->devdata;
788
789 if (trip_type == THERMAL_TRIP_CRITICAL)
790 type = ACPI_THERMAL_NOTIFY_CRITICAL;
791 else if (trip_type == THERMAL_TRIP_HOT)
792 type = ACPI_THERMAL_NOTIFY_HOT;
793 else
794 return 0;
795
796 acpi_bus_generate_proc_event(tz->device, type, 1);
797 acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
f6f5c45e 798 dev_name(&tz->device->dev), type, 1);
b1569e99
MG
799
800 if (trip_type == THERMAL_TRIP_CRITICAL && nocrt)
801 return 1;
802
803 return 0;
804}
805
3f655ef8
ZR
806typedef int (*cb)(struct thermal_zone_device *, int,
807 struct thermal_cooling_device *);
808static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
809 struct thermal_cooling_device *cdev,
810 cb action)
811{
812 struct acpi_device *device = cdev->devdata;
813 struct acpi_thermal *tz = thermal->devdata;
653a00c9
ZR
814 struct acpi_device *dev;
815 acpi_status status;
816 acpi_handle handle;
3f655ef8
ZR
817 int i;
818 int j;
819 int trip = -1;
820 int result = 0;
821
822 if (tz->trips.critical.flags.valid)
823 trip++;
824
825 if (tz->trips.hot.flags.valid)
826 trip++;
827
828 if (tz->trips.passive.flags.valid) {
829 trip++;
830 for (i = 0; i < tz->trips.passive.devices.count;
831 i++) {
653a00c9
ZR
832 handle = tz->trips.passive.devices.handles[i];
833 status = acpi_bus_get_device(handle, &dev);
834 if (ACPI_SUCCESS(status) && (dev == device)) {
835 result = action(thermal, trip, cdev);
836 if (result)
837 goto failed;
838 }
3f655ef8
ZR
839 }
840 }
841
842 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
843 if (!tz->trips.active[i].flags.valid)
844 break;
845 trip++;
846 for (j = 0;
847 j < tz->trips.active[i].devices.count;
848 j++) {
653a00c9
ZR
849 handle = tz->trips.active[i].devices.handles[j];
850 status = acpi_bus_get_device(handle, &dev);
851 if (ACPI_SUCCESS(status) && (dev == device)) {
852 result = action(thermal, trip, cdev);
853 if (result)
854 goto failed;
855 }
3f655ef8
ZR
856 }
857 }
858
859 for (i = 0; i < tz->devices.count; i++) {
653a00c9
ZR
860 handle = tz->devices.handles[i];
861 status = acpi_bus_get_device(handle, &dev);
862 if (ACPI_SUCCESS(status) && (dev == device)) {
863 result = action(thermal, -1, cdev);
864 if (result)
865 goto failed;
866 }
3f655ef8
ZR
867 }
868
869failed:
870 return result;
871}
872
873static int
874acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
875 struct thermal_cooling_device *cdev)
876{
877 return acpi_thermal_cooling_device_cb(thermal, cdev,
878 thermal_zone_bind_cooling_device);
879}
880
881static int
882acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
883 struct thermal_cooling_device *cdev)
884{
885 return acpi_thermal_cooling_device_cb(thermal, cdev,
886 thermal_zone_unbind_cooling_device);
887}
888
889static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
890 .bind = acpi_thermal_bind_cooling_device,
891 .unbind = acpi_thermal_unbind_cooling_device,
892 .get_temp = thermal_get_temp,
893 .get_mode = thermal_get_mode,
894 .set_mode = thermal_set_mode,
895 .get_trip_type = thermal_get_trip_type,
896 .get_trip_temp = thermal_get_trip_temp,
9ec732ff 897 .get_crit_temp = thermal_get_crit_temp,
b1569e99 898 .notify = thermal_notify,
3f655ef8
ZR
899};
900
901static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
902{
903 int trips = 0;
904 int result;
20733939 905 acpi_status status;
3f655ef8
ZR
906 int i;
907
908 if (tz->trips.critical.flags.valid)
909 trips++;
910
911 if (tz->trips.hot.flags.valid)
912 trips++;
913
914 if (tz->trips.passive.flags.valid)
915 trips++;
916
917 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
918 tz->trips.active[i].flags.valid; i++, trips++);
b1569e99
MG
919
920 if (tz->trips.passive.flags.valid)
921 tz->thermal_zone =
922 thermal_zone_device_register("acpitz", trips, tz,
923 &acpi_thermal_zone_ops,
924 tz->trips.passive.tc1,
925 tz->trips.passive.tc2,
926 tz->trips.passive.tsp*100,
927 tz->polling_frequency*100);
928 else
929 tz->thermal_zone =
930 thermal_zone_device_register("acpitz", trips, tz,
931 &acpi_thermal_zone_ops,
932 0, 0, 0,
67405439 933 tz->polling_frequency*100);
bb070e43 934 if (IS_ERR(tz->thermal_zone))
3f655ef8
ZR
935 return -ENODEV;
936
937 result = sysfs_create_link(&tz->device->dev.kobj,
938 &tz->thermal_zone->device.kobj, "thermal_zone");
939 if (result)
940 return result;
941
942 result = sysfs_create_link(&tz->thermal_zone->device.kobj,
943 &tz->device->dev.kobj, "device");
944 if (result)
945 return result;
946
20733939
ZR
947 status = acpi_attach_data(tz->device->handle,
948 acpi_bus_private_data_handler,
949 tz->thermal_zone);
950 if (ACPI_FAILURE(status)) {
55ac9a01
LM
951 printk(KERN_ERR PREFIX
952 "Error attaching device data\n");
20733939
ZR
953 return -ENODEV;
954 }
955
3f655ef8
ZR
956 tz->tz_enabled = 1;
957
fc3a8828
GKH
958 dev_info(&tz->device->dev, "registered as thermal_zone%d\n",
959 tz->thermal_zone->id);
3f655ef8
ZR
960 return 0;
961}
962
963static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
964{
965 sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
966 sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
967 thermal_zone_device_unregister(tz->thermal_zone);
968 tz->thermal_zone = NULL;
20733939 969 acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler);
3f655ef8
ZR
970}
971
972
1da177e4
LT
973/* --------------------------------------------------------------------------
974 FS Interface (/proc)
975 -------------------------------------------------------------------------- */
976
4be44fcd 977static struct proc_dir_entry *acpi_thermal_dir;
1da177e4
LT
978
979static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset)
980{
50dd0969 981 struct acpi_thermal *tz = seq->private;
1da177e4 982
1da177e4
LT
983
984 if (!tz)
985 goto end;
986
987 seq_puts(seq, "state: ");
988
4be44fcd
LB
989 if (!tz->state.critical && !tz->state.hot && !tz->state.passive
990 && !tz->state.active)
1da177e4
LT
991 seq_puts(seq, "ok\n");
992 else {
993 if (tz->state.critical)
994 seq_puts(seq, "critical ");
995 if (tz->state.hot)
996 seq_puts(seq, "hot ");
997 if (tz->state.passive)
998 seq_puts(seq, "passive ");
999 if (tz->state.active)
1000 seq_printf(seq, "active[%d]", tz->state.active_index);
1001 seq_puts(seq, "\n");
1002 }
1003
4be44fcd 1004 end:
d550d98d 1005 return 0;
1da177e4
LT
1006}
1007
1008static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file)
1009{
1010 return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data);
1011}
1012
1da177e4
LT
1013static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset)
1014{
4be44fcd 1015 int result = 0;
50dd0969 1016 struct acpi_thermal *tz = seq->private;
1da177e4 1017
1da177e4
LT
1018
1019 if (!tz)
1020 goto end;
1021
1022 result = acpi_thermal_get_temperature(tz);
1023 if (result)
1024 goto end;
1025
4be44fcd
LB
1026 seq_printf(seq, "temperature: %ld C\n",
1027 KELVIN_TO_CELSIUS(tz->temperature));
1da177e4 1028
4be44fcd 1029 end:
d550d98d 1030 return 0;
1da177e4
LT
1031}
1032
1033static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file)
1034{
1035 return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data);
1036}
1037
1da177e4
LT
1038static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset)
1039{
50dd0969 1040 struct acpi_thermal *tz = seq->private;
68ccfaa8 1041 struct acpi_device *device;
e7c746ef
TR
1042 acpi_status status;
1043
4be44fcd
LB
1044 int i = 0;
1045 int j = 0;
1da177e4 1046
1da177e4
LT
1047
1048 if (!tz)
1049 goto end;
1050
1051 if (tz->trips.critical.flags.valid)
f5487145
LB
1052 seq_printf(seq, "critical (S5): %ld C%s",
1053 KELVIN_TO_CELSIUS(tz->trips.critical.temperature),
1054 nocrt ? " <disabled>\n" : "\n");
1da177e4
LT
1055
1056 if (tz->trips.hot.flags.valid)
f5487145
LB
1057 seq_printf(seq, "hot (S4): %ld C%s",
1058 KELVIN_TO_CELSIUS(tz->trips.hot.temperature),
1059 nocrt ? " <disabled>\n" : "\n");
1da177e4
LT
1060
1061 if (tz->trips.passive.flags.valid) {
4be44fcd
LB
1062 seq_printf(seq,
1063 "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=",
1064 KELVIN_TO_CELSIUS(tz->trips.passive.temperature),
1065 tz->trips.passive.tc1, tz->trips.passive.tc2,
1066 tz->trips.passive.tsp);
1067 for (j = 0; j < tz->trips.passive.devices.count; j++) {
e7c746ef
TR
1068 status = acpi_bus_get_device(tz->trips.passive.devices.
1069 handles[j], &device);
1070 seq_printf(seq, "%4.4s ", status ? "" :
1071 acpi_device_bid(device));
1da177e4
LT
1072 }
1073 seq_puts(seq, "\n");
7fb2616e
FP
1074 } else {
1075 seq_printf(seq, "passive (forced):");
1076 if (tz->thermal_zone->forced_passive)
1077 seq_printf(seq, " %i C\n",
1078 tz->thermal_zone->forced_passive / 1000);
1079 else
1080 seq_printf(seq, "<not set>\n");
1da177e4
LT
1081 }
1082
1083 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
1084 if (!(tz->trips.active[i].flags.valid))
1085 break;
1086 seq_printf(seq, "active[%d]: %ld C: devices=",
4be44fcd
LB
1087 i,
1088 KELVIN_TO_CELSIUS(tz->trips.active[i].temperature));
68ccfaa8 1089 for (j = 0; j < tz->trips.active[i].devices.count; j++){
e7c746ef
TR
1090 status = acpi_bus_get_device(tz->trips.active[i].
1091 devices.handles[j],
1092 &device);
1093 seq_printf(seq, "%4.4s ", status ? "" :
1094 acpi_device_bid(device));
68ccfaa8 1095 }
1da177e4
LT
1096 seq_puts(seq, "\n");
1097 }
1098
4be44fcd 1099 end:
d550d98d 1100 return 0;
1da177e4
LT
1101}
1102
1103static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file)
1104{
1105 return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data);
1106}
1107
1da177e4
LT
1108static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset)
1109{
50dd0969 1110 struct acpi_thermal *tz = seq->private;
1da177e4 1111
1da177e4
LT
1112
1113 if (!tz)
1114 goto end;
1115
eaca2d3f 1116 if (!tz->flags.cooling_mode)
1da177e4 1117 seq_puts(seq, "<setting not supported>\n");
1da177e4 1118 else
eaca2d3f 1119 seq_puts(seq, "0 - Active; 1 - Passive\n");
1da177e4 1120
4be44fcd 1121 end:
d550d98d 1122 return 0;
1da177e4
LT
1123}
1124
1125static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file)
1126{
1127 return single_open(file, acpi_thermal_cooling_seq_show,
4be44fcd 1128 PDE(inode)->data);
1da177e4
LT
1129}
1130
1131static ssize_t
4be44fcd
LB
1132acpi_thermal_write_cooling_mode(struct file *file,
1133 const char __user * buffer,
1134 size_t count, loff_t * ppos)
1da177e4 1135{
50dd0969
JE
1136 struct seq_file *m = file->private_data;
1137 struct acpi_thermal *tz = m->private;
4be44fcd
LB
1138 int result = 0;
1139 char mode_string[12] = { '\0' };
1da177e4 1140
1da177e4
LT
1141
1142 if (!tz || (count > sizeof(mode_string) - 1))
d550d98d 1143 return -EINVAL;
1da177e4
LT
1144
1145 if (!tz->flags.cooling_mode)
d550d98d 1146 return -ENODEV;
1da177e4
LT
1147
1148 if (copy_from_user(mode_string, buffer, count))
d550d98d 1149 return -EFAULT;
4be44fcd 1150
1da177e4 1151 mode_string[count] = '\0';
4be44fcd
LB
1152
1153 result = acpi_thermal_set_cooling_mode(tz,
1154 simple_strtoul(mode_string, NULL,
1155 0));
1da177e4 1156 if (result)
d550d98d 1157 return result;
1da177e4
LT
1158
1159 acpi_thermal_check(tz);
1160
d550d98d 1161 return count;
1da177e4
LT
1162}
1163
1da177e4
LT
1164static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset)
1165{
50dd0969 1166 struct acpi_thermal *tz = seq->private;
1da177e4 1167
1da177e4
LT
1168
1169 if (!tz)
1170 goto end;
1171
b1569e99 1172 if (!tz->thermal_zone->polling_delay) {
1da177e4
LT
1173 seq_puts(seq, "<polling disabled>\n");
1174 goto end;
1175 }
1176
b1569e99
MG
1177 seq_printf(seq, "polling frequency: %d seconds\n",
1178 (tz->thermal_zone->polling_delay / 1000));
1da177e4 1179
4be44fcd 1180 end:
d550d98d 1181 return 0;
1da177e4
LT
1182}
1183
1184static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file)
1185{
1186 return single_open(file, acpi_thermal_polling_seq_show,
4be44fcd 1187 PDE(inode)->data);
1da177e4
LT
1188}
1189
1190static ssize_t
4be44fcd
LB
1191acpi_thermal_write_polling(struct file *file,
1192 const char __user * buffer,
1193 size_t count, loff_t * ppos)
1da177e4 1194{
50dd0969
JE
1195 struct seq_file *m = file->private_data;
1196 struct acpi_thermal *tz = m->private;
4be44fcd
LB
1197 int result = 0;
1198 char polling_string[12] = { '\0' };
1199 int seconds = 0;
1da177e4 1200
1da177e4
LT
1201
1202 if (!tz || (count > sizeof(polling_string) - 1))
d550d98d 1203 return -EINVAL;
4be44fcd 1204
1da177e4 1205 if (copy_from_user(polling_string, buffer, count))
d550d98d 1206 return -EFAULT;
4be44fcd 1207
1da177e4
LT
1208 polling_string[count] = '\0';
1209
1210 seconds = simple_strtoul(polling_string, NULL, 0);
4be44fcd 1211
1da177e4
LT
1212 result = acpi_thermal_set_polling(tz, seconds);
1213 if (result)
d550d98d 1214 return result;
1da177e4
LT
1215
1216 acpi_thermal_check(tz);
1217
d550d98d 1218 return count;
1da177e4
LT
1219}
1220
4be44fcd 1221static int acpi_thermal_add_fs(struct acpi_device *device)
1da177e4 1222{
4be44fcd 1223 struct proc_dir_entry *entry = NULL;
1da177e4 1224
1da177e4
LT
1225
1226 if (!acpi_device_dir(device)) {
1227 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
4be44fcd 1228 acpi_thermal_dir);
1da177e4 1229 if (!acpi_device_dir(device))
d550d98d 1230 return -ENODEV;
1da177e4
LT
1231 }
1232
1233 /* 'state' [R] */
cf7acfab
DL
1234 entry = proc_create_data(ACPI_THERMAL_FILE_STATE,
1235 S_IRUGO, acpi_device_dir(device),
1236 &acpi_thermal_state_fops,
1237 acpi_driver_data(device));
1da177e4 1238 if (!entry)
d550d98d 1239 return -ENODEV;
1da177e4
LT
1240
1241 /* 'temperature' [R] */
cf7acfab
DL
1242 entry = proc_create_data(ACPI_THERMAL_FILE_TEMPERATURE,
1243 S_IRUGO, acpi_device_dir(device),
1244 &acpi_thermal_temp_fops,
1245 acpi_driver_data(device));
1da177e4 1246 if (!entry)
d550d98d 1247 return -ENODEV;
1da177e4 1248
2db9ccba 1249 /* 'trip_points' [R] */
cf7acfab
DL
1250 entry = proc_create_data(ACPI_THERMAL_FILE_TRIP_POINTS,
1251 S_IRUGO,
1252 acpi_device_dir(device),
1253 &acpi_thermal_trip_fops,
1254 acpi_driver_data(device));
1da177e4 1255 if (!entry)
d550d98d 1256 return -ENODEV;
1da177e4
LT
1257
1258 /* 'cooling_mode' [R/W] */
cf7acfab
DL
1259 entry = proc_create_data(ACPI_THERMAL_FILE_COOLING_MODE,
1260 S_IFREG | S_IRUGO | S_IWUSR,
1261 acpi_device_dir(device),
1262 &acpi_thermal_cooling_fops,
1263 acpi_driver_data(device));
1da177e4 1264 if (!entry)
d550d98d 1265 return -ENODEV;
1da177e4
LT
1266
1267 /* 'polling_frequency' [R/W] */
cf7acfab
DL
1268 entry = proc_create_data(ACPI_THERMAL_FILE_POLLING_FREQ,
1269 S_IFREG | S_IRUGO | S_IWUSR,
1270 acpi_device_dir(device),
1271 &acpi_thermal_polling_fops,
1272 acpi_driver_data(device));
1da177e4 1273 if (!entry)
d550d98d 1274 return -ENODEV;
d550d98d 1275 return 0;
1da177e4
LT
1276}
1277
4be44fcd 1278static int acpi_thermal_remove_fs(struct acpi_device *device)
1da177e4 1279{
1da177e4
LT
1280
1281 if (acpi_device_dir(device)) {
1282 remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ,
1283 acpi_device_dir(device));
1284 remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE,
1285 acpi_device_dir(device));
1286 remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS,
1287 acpi_device_dir(device));
1288 remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE,
1289 acpi_device_dir(device));
1290 remove_proc_entry(ACPI_THERMAL_FILE_STATE,
1291 acpi_device_dir(device));
1292 remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir);
1293 acpi_device_dir(device) = NULL;
1294 }
1295
d550d98d 1296 return 0;
1da177e4
LT
1297}
1298
1da177e4
LT
1299/* --------------------------------------------------------------------------
1300 Driver Interface
1301 -------------------------------------------------------------------------- */
1302
342d550d 1303static void acpi_thermal_notify(struct acpi_device *device, u32 event)
1da177e4 1304{
342d550d 1305 struct acpi_thermal *tz = acpi_driver_data(device);
1da177e4 1306
1da177e4
LT
1307
1308 if (!tz)
d550d98d 1309 return;
1da177e4 1310
1da177e4
LT
1311 switch (event) {
1312 case ACPI_THERMAL_NOTIFY_TEMPERATURE:
1313 acpi_thermal_check(tz);
1314 break;
1315 case ACPI_THERMAL_NOTIFY_THRESHOLDS:
ce44e197 1316 acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
1da177e4 1317 acpi_thermal_check(tz);
14e04fb3 1318 acpi_bus_generate_proc_event(device, event, 0);
962ce8ca 1319 acpi_bus_generate_netlink_event(device->pnp.device_class,
0794469d 1320 dev_name(&device->dev), event, 0);
1da177e4
LT
1321 break;
1322 case ACPI_THERMAL_NOTIFY_DEVICES:
ce44e197
ZR
1323 acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
1324 acpi_thermal_check(tz);
14e04fb3 1325 acpi_bus_generate_proc_event(device, event, 0);
962ce8ca 1326 acpi_bus_generate_netlink_event(device->pnp.device_class,
0794469d 1327 dev_name(&device->dev), event, 0);
1da177e4
LT
1328 break;
1329 default:
1330 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
4be44fcd 1331 "Unsupported event [0x%x]\n", event));
1da177e4
LT
1332 break;
1333 }
1da177e4
LT
1334}
1335
4be44fcd 1336static int acpi_thermal_get_info(struct acpi_thermal *tz)
1da177e4 1337{
4be44fcd 1338 int result = 0;
1da177e4 1339
1da177e4
LT
1340
1341 if (!tz)
d550d98d 1342 return -EINVAL;
1da177e4
LT
1343
1344 /* Get temperature [_TMP] (required) */
1345 result = acpi_thermal_get_temperature(tz);
1346 if (result)
d550d98d 1347 return result;
1da177e4
LT
1348
1349 /* Get trip points [_CRT, _PSV, etc.] (required) */
1350 result = acpi_thermal_get_trip_points(tz);
1351 if (result)
d550d98d 1352 return result;
1da177e4
LT
1353
1354 /* Set the cooling mode [_SCP] to active cooling (default) */
1355 result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
4be44fcd 1356 if (!result)
1da177e4 1357 tz->flags.cooling_mode = 1;
1da177e4
LT
1358
1359 /* Get default polling frequency [_TZP] (optional) */
1360 if (tzp)
1361 tz->polling_frequency = tzp;
1362 else
1363 acpi_thermal_get_polling_frequency(tz);
1364
d550d98d 1365 return 0;
1da177e4
LT
1366}
1367
13614e37
JD
1368/*
1369 * The exact offset between Kelvin and degree Celsius is 273.15. However ACPI
1370 * handles temperature values with a single decimal place. As a consequence,
1371 * some implementations use an offset of 273.1 and others use an offset of
1372 * 273.2. Try to find out which one is being used, to present the most
1373 * accurate and visually appealing number.
1374 *
1375 * The heuristic below should work for all ACPI thermal zones which have a
1376 * critical trip point with a value being a multiple of 0.5 degree Celsius.
1377 */
1378static void acpi_thermal_guess_offset(struct acpi_thermal *tz)
1379{
1380 if (tz->trips.critical.flags.valid &&
1381 (tz->trips.critical.temperature % 5) == 1)
1382 tz->kelvin_offset = 2731;
1383 else
1384 tz->kelvin_offset = 2732;
1385}
1386
4be44fcd 1387static int acpi_thermal_add(struct acpi_device *device)
1da177e4 1388{
4be44fcd 1389 int result = 0;
4be44fcd 1390 struct acpi_thermal *tz = NULL;
1da177e4 1391
1da177e4
LT
1392
1393 if (!device)
d550d98d 1394 return -EINVAL;
1da177e4 1395
36bcbec7 1396 tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
1da177e4 1397 if (!tz)
d550d98d 1398 return -ENOMEM;
1da177e4 1399
8348e1b1 1400 tz->device = device;
1da177e4
LT
1401 strcpy(tz->name, device->pnp.bus_id);
1402 strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME);
1403 strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
db89b4f0 1404 device->driver_data = tz;
6e215785 1405 mutex_init(&tz->lock);
3f655ef8
ZR
1406
1407
1da177e4
LT
1408 result = acpi_thermal_get_info(tz);
1409 if (result)
3f655ef8
ZR
1410 goto free_memory;
1411
13614e37
JD
1412 acpi_thermal_guess_offset(tz);
1413
3f655ef8
ZR
1414 result = acpi_thermal_register_thermal_zone(tz);
1415 if (result)
1416 goto free_memory;
1da177e4
LT
1417
1418 result = acpi_thermal_add_fs(device);
1419 if (result)
3f655ef8 1420 goto unregister_thermal_zone;
1da177e4 1421
1da177e4 1422 printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
4be44fcd
LB
1423 acpi_device_name(device), acpi_device_bid(device),
1424 KELVIN_TO_CELSIUS(tz->temperature));
3f655ef8 1425 goto end;
1da177e4 1426
3f655ef8
ZR
1427unregister_thermal_zone:
1428 thermal_zone_device_unregister(tz->thermal_zone);
1429free_memory:
1430 kfree(tz);
1431end:
d550d98d 1432 return result;
1da177e4
LT
1433}
1434
4be44fcd 1435static int acpi_thermal_remove(struct acpi_device *device, int type)
1da177e4 1436{
4be44fcd 1437 struct acpi_thermal *tz = NULL;
1da177e4 1438
1da177e4 1439 if (!device || !acpi_driver_data(device))
d550d98d 1440 return -EINVAL;
1da177e4 1441
50dd0969 1442 tz = acpi_driver_data(device);
1da177e4 1443
1da177e4 1444 acpi_thermal_remove_fs(device);
3f655ef8 1445 acpi_thermal_unregister_thermal_zone(tz);
6e215785 1446 mutex_destroy(&tz->lock);
1da177e4 1447 kfree(tz);
d550d98d 1448 return 0;
1da177e4
LT
1449}
1450
5d9464a4 1451static int acpi_thermal_resume(struct acpi_device *device)
74ce1468
KK
1452{
1453 struct acpi_thermal *tz = NULL;
b1028c54
KK
1454 int i, j, power_state, result;
1455
74ce1468
KK
1456
1457 if (!device || !acpi_driver_data(device))
d550d98d 1458 return -EINVAL;
74ce1468 1459
50dd0969 1460 tz = acpi_driver_data(device);
74ce1468 1461
bed936f7 1462 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
b1028c54
KK
1463 if (!(&tz->trips.active[i]))
1464 break;
1465 if (!tz->trips.active[i].flags.valid)
1466 break;
1467 tz->trips.active[i].flags.enabled = 1;
1468 for (j = 0; j < tz->trips.active[i].devices.count; j++) {
1469 result = acpi_bus_get_power(tz->trips.active[i].devices.
1470 handles[j], &power_state);
1471 if (result || (power_state != ACPI_STATE_D0)) {
1472 tz->trips.active[i].flags.enabled = 0;
1473 break;
1474 }
bed936f7 1475 }
b1028c54 1476 tz->state.active |= tz->trips.active[i].flags.enabled;
bed936f7
KK
1477 }
1478
b1028c54 1479 acpi_thermal_check(tz);
74ce1468
KK
1480
1481 return AE_OK;
1482}
1483
1855256c 1484static int thermal_act(const struct dmi_system_id *d) {
0b5bfa1c
LB
1485
1486 if (act == 0) {
1487 printk(KERN_NOTICE "ACPI: %s detected: "
1488 "disabling all active thermal trip points\n", d->ident);
1489 act = -1;
1490 }
1491 return 0;
1492}
1855256c 1493static int thermal_nocrt(const struct dmi_system_id *d) {
8c99fdce
LB
1494
1495 printk(KERN_NOTICE "ACPI: %s detected: "
1496 "disabling all critical thermal trip point actions.\n", d->ident);
1497 nocrt = 1;
1498 return 0;
1499}
1855256c 1500static int thermal_tzp(const struct dmi_system_id *d) {
0b5bfa1c
LB
1501
1502 if (tzp == 0) {
1503 printk(KERN_NOTICE "ACPI: %s detected: "
1504 "enabling thermal zone polling\n", d->ident);
1505 tzp = 300; /* 300 dS = 30 Seconds */
1506 }
1507 return 0;
1508}
1855256c 1509static int thermal_psv(const struct dmi_system_id *d) {
0b5bfa1c
LB
1510
1511 if (psv == 0) {
1512 printk(KERN_NOTICE "ACPI: %s detected: "
1513 "disabling all passive thermal trip points\n", d->ident);
1514 psv = -1;
1515 }
1516 return 0;
1517}
1518
1519static struct dmi_system_id thermal_dmi_table[] __initdata = {
1520 /*
1521 * Award BIOS on this AOpen makes thermal control almost worthless.
1522 * http://bugzilla.kernel.org/show_bug.cgi?id=8842
1523 */
1524 {
1525 .callback = thermal_act,
1526 .ident = "AOpen i915GMm-HFS",
1527 .matches = {
1528 DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
1529 DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
1530 },
1531 },
1532 {
1533 .callback = thermal_psv,
1534 .ident = "AOpen i915GMm-HFS",
1535 .matches = {
1536 DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
1537 DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
1538 },
1539 },
1540 {
1541 .callback = thermal_tzp,
1542 .ident = "AOpen i915GMm-HFS",
1543 .matches = {
1544 DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
1545 DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
1546 },
1547 },
8c99fdce
LB
1548 {
1549 .callback = thermal_nocrt,
1550 .ident = "Gigabyte GA-7ZX",
1551 .matches = {
1552 DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
1553 DMI_MATCH(DMI_BOARD_NAME, "7ZX"),
1554 },
1555 },
0b5bfa1c
LB
1556 {}
1557};
0b5bfa1c 1558
4be44fcd 1559static int __init acpi_thermal_init(void)
1da177e4 1560{
4be44fcd 1561 int result = 0;
1da177e4 1562
0b5bfa1c
LB
1563 dmi_check_system(thermal_dmi_table);
1564
72b33ef8
LB
1565 if (off) {
1566 printk(KERN_NOTICE "ACPI: thermal control disabled\n");
1567 return -ENODEV;
1568 }
1da177e4
LT
1569 acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir);
1570 if (!acpi_thermal_dir)
d550d98d 1571 return -ENODEV;
1da177e4
LT
1572
1573 result = acpi_bus_register_driver(&acpi_thermal_driver);
1574 if (result < 0) {
1575 remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
d550d98d 1576 return -ENODEV;
1da177e4
LT
1577 }
1578
d550d98d 1579 return 0;
1da177e4
LT
1580}
1581
4be44fcd 1582static void __exit acpi_thermal_exit(void)
1da177e4 1583{
1da177e4
LT
1584
1585 acpi_bus_unregister_driver(&acpi_thermal_driver);
1586
1587 remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir);
1588
d550d98d 1589 return;
1da177e4
LT
1590}
1591
1da177e4
LT
1592module_init(acpi_thermal_init);
1593module_exit(acpi_thermal_exit);