diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/acpi/thermal.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/acpi/thermal.c')
-rw-r--r-- | drivers/acpi/thermal.c | 1445 |
1 files changed, 1445 insertions, 0 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c new file mode 100644 index 000000000000..79c3a686bc44 --- /dev/null +++ b/drivers/acpi/thermal.c | |||
@@ -0,0 +1,1445 @@ | |||
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> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/types.h> | ||
38 | #include <linux/proc_fs.h> | ||
39 | #include <linux/sched.h> | ||
40 | #include <linux/kmod.h> | ||
41 | #include <linux/seq_file.h> | ||
42 | #include <asm/uaccess.h> | ||
43 | |||
44 | #include <acpi/acpi_bus.h> | ||
45 | #include <acpi/acpi_drivers.h> | ||
46 | |||
47 | #define ACPI_THERMAL_COMPONENT 0x04000000 | ||
48 | #define ACPI_THERMAL_CLASS "thermal_zone" | ||
49 | #define ACPI_THERMAL_DRIVER_NAME "ACPI Thermal Zone Driver" | ||
50 | #define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" | ||
51 | #define ACPI_THERMAL_FILE_STATE "state" | ||
52 | #define ACPI_THERMAL_FILE_TEMPERATURE "temperature" | ||
53 | #define ACPI_THERMAL_FILE_TRIP_POINTS "trip_points" | ||
54 | #define ACPI_THERMAL_FILE_COOLING_MODE "cooling_mode" | ||
55 | #define ACPI_THERMAL_FILE_POLLING_FREQ "polling_frequency" | ||
56 | #define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80 | ||
57 | #define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81 | ||
58 | #define ACPI_THERMAL_NOTIFY_DEVICES 0x82 | ||
59 | #define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 | ||
60 | #define ACPI_THERMAL_NOTIFY_HOT 0xF1 | ||
61 | #define ACPI_THERMAL_MODE_ACTIVE 0x00 | ||
62 | #define ACPI_THERMAL_MODE_PASSIVE 0x01 | ||
63 | #define ACPI_THERMAL_MODE_CRITICAL 0xff | ||
64 | #define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff" | ||
65 | |||
66 | #define ACPI_THERMAL_MAX_ACTIVE 10 | ||
67 | #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 | ||
68 | |||
69 | #define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10) | ||
70 | #define CELSIUS_TO_KELVIN(t) ((t+273)*10) | ||
71 | |||
72 | #define _COMPONENT ACPI_THERMAL_COMPONENT | ||
73 | ACPI_MODULE_NAME ("acpi_thermal") | ||
74 | |||
75 | MODULE_AUTHOR("Paul Diefenbaugh"); | ||
76 | MODULE_DESCRIPTION(ACPI_THERMAL_DRIVER_NAME); | ||
77 | MODULE_LICENSE("GPL"); | ||
78 | |||
79 | static int tzp; | ||
80 | module_param(tzp, int, 0); | ||
81 | MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n"); | ||
82 | |||
83 | |||
84 | static int acpi_thermal_add (struct acpi_device *device); | ||
85 | static int acpi_thermal_remove (struct acpi_device *device, int type); | ||
86 | static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file); | ||
87 | static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file); | ||
88 | static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file); | ||
89 | static ssize_t acpi_thermal_write_trip_points (struct file*,const char __user *,size_t,loff_t *); | ||
90 | static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file); | ||
91 | static ssize_t acpi_thermal_write_cooling_mode (struct file*,const char __user *,size_t,loff_t *); | ||
92 | static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file); | ||
93 | static ssize_t acpi_thermal_write_polling(struct file*,const char __user *,size_t,loff_t *); | ||
94 | |||
95 | static struct acpi_driver acpi_thermal_driver = { | ||
96 | .name = ACPI_THERMAL_DRIVER_NAME, | ||
97 | .class = ACPI_THERMAL_CLASS, | ||
98 | .ids = ACPI_THERMAL_HID, | ||
99 | .ops = { | ||
100 | .add = acpi_thermal_add, | ||
101 | .remove = acpi_thermal_remove, | ||
102 | }, | ||
103 | }; | ||
104 | |||
105 | struct acpi_thermal_state { | ||
106 | u8 critical:1; | ||
107 | u8 hot:1; | ||
108 | u8 passive:1; | ||
109 | u8 active:1; | ||
110 | u8 reserved:4; | ||
111 | int active_index; | ||
112 | }; | ||
113 | |||
114 | struct acpi_thermal_state_flags { | ||
115 | u8 valid:1; | ||
116 | u8 enabled:1; | ||
117 | u8 reserved:6; | ||
118 | }; | ||
119 | |||
120 | struct acpi_thermal_critical { | ||
121 | struct acpi_thermal_state_flags flags; | ||
122 | unsigned long temperature; | ||
123 | }; | ||
124 | |||
125 | struct acpi_thermal_hot { | ||
126 | struct acpi_thermal_state_flags flags; | ||
127 | unsigned long temperature; | ||
128 | }; | ||
129 | |||
130 | struct acpi_thermal_passive { | ||
131 | struct acpi_thermal_state_flags flags; | ||
132 | unsigned long temperature; | ||
133 | unsigned long tc1; | ||
134 | unsigned long tc2; | ||
135 | unsigned long tsp; | ||
136 | struct acpi_handle_list devices; | ||
137 | }; | ||
138 | |||
139 | struct acpi_thermal_active { | ||
140 | struct acpi_thermal_state_flags flags; | ||
141 | unsigned long temperature; | ||
142 | struct acpi_handle_list devices; | ||
143 | }; | ||
144 | |||
145 | struct acpi_thermal_trips { | ||
146 | struct acpi_thermal_critical critical; | ||
147 | struct acpi_thermal_hot hot; | ||
148 | struct acpi_thermal_passive passive; | ||
149 | struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE]; | ||
150 | }; | ||
151 | |||
152 | struct acpi_thermal_flags { | ||
153 | u8 cooling_mode:1; /* _SCP */ | ||
154 | u8 devices:1; /* _TZD */ | ||
155 | u8 reserved:6; | ||
156 | }; | ||
157 | |||
158 | struct acpi_thermal { | ||
159 | acpi_handle handle; | ||
160 | acpi_bus_id name; | ||
161 | unsigned long temperature; | ||
162 | unsigned long last_temperature; | ||
163 | unsigned long polling_frequency; | ||
164 | u8 cooling_mode; | ||
165 | volatile u8 zombie; | ||
166 | struct acpi_thermal_flags flags; | ||
167 | struct acpi_thermal_state state; | ||
168 | struct acpi_thermal_trips trips; | ||
169 | struct acpi_handle_list devices; | ||
170 | struct timer_list timer; | ||
171 | }; | ||
172 | |||
173 | static struct file_operations acpi_thermal_state_fops = { | ||
174 | .open = acpi_thermal_state_open_fs, | ||
175 | .read = seq_read, | ||
176 | .llseek = seq_lseek, | ||
177 | .release = single_release, | ||
178 | }; | ||
179 | |||
180 | static struct file_operations acpi_thermal_temp_fops = { | ||
181 | .open = acpi_thermal_temp_open_fs, | ||
182 | .read = seq_read, | ||
183 | .llseek = seq_lseek, | ||
184 | .release = single_release, | ||
185 | }; | ||
186 | |||
187 | static struct file_operations acpi_thermal_trip_fops = { | ||
188 | .open = acpi_thermal_trip_open_fs, | ||
189 | .read = seq_read, | ||
190 | .write = acpi_thermal_write_trip_points, | ||
191 | .llseek = seq_lseek, | ||
192 | .release = single_release, | ||
193 | }; | ||
194 | |||
195 | static struct file_operations acpi_thermal_cooling_fops = { | ||
196 | .open = acpi_thermal_cooling_open_fs, | ||
197 | .read = seq_read, | ||
198 | .write = acpi_thermal_write_cooling_mode, | ||
199 | .llseek = seq_lseek, | ||
200 | .release = single_release, | ||
201 | }; | ||
202 | |||
203 | static struct file_operations acpi_thermal_polling_fops = { | ||
204 | .open = acpi_thermal_polling_open_fs, | ||
205 | .read = seq_read, | ||
206 | .write = acpi_thermal_write_polling, | ||
207 | .llseek = seq_lseek, | ||
208 | .release = single_release, | ||
209 | }; | ||
210 | |||
211 | /* -------------------------------------------------------------------------- | ||
212 | Thermal Zone Management | ||
213 | -------------------------------------------------------------------------- */ | ||
214 | |||
215 | static int | ||
216 | acpi_thermal_get_temperature ( | ||
217 | struct acpi_thermal *tz) | ||
218 | { | ||
219 | acpi_status status = AE_OK; | ||
220 | |||
221 | ACPI_FUNCTION_TRACE("acpi_thermal_get_temperature"); | ||
222 | |||
223 | if (!tz) | ||
224 | return_VALUE(-EINVAL); | ||
225 | |||
226 | tz->last_temperature = tz->temperature; | ||
227 | |||
228 | status = acpi_evaluate_integer(tz->handle, "_TMP", NULL, &tz->temperature); | ||
229 | if (ACPI_FAILURE(status)) | ||
230 | return_VALUE(-ENODEV); | ||
231 | |||
232 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n", tz->temperature)); | ||
233 | |||
234 | return_VALUE(0); | ||
235 | } | ||
236 | |||
237 | |||
238 | static int | ||
239 | acpi_thermal_get_polling_frequency ( | ||
240 | struct acpi_thermal *tz) | ||
241 | { | ||
242 | acpi_status status = AE_OK; | ||
243 | |||
244 | ACPI_FUNCTION_TRACE("acpi_thermal_get_polling_frequency"); | ||
245 | |||
246 | if (!tz) | ||
247 | return_VALUE(-EINVAL); | ||
248 | |||
249 | status = acpi_evaluate_integer(tz->handle, "_TZP", NULL, &tz->polling_frequency); | ||
250 | if (ACPI_FAILURE(status)) | ||
251 | return_VALUE(-ENODEV); | ||
252 | |||
253 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n", tz->polling_frequency)); | ||
254 | |||
255 | return_VALUE(0); | ||
256 | } | ||
257 | |||
258 | |||
259 | static int | ||
260 | acpi_thermal_set_polling ( | ||
261 | struct acpi_thermal *tz, | ||
262 | int seconds) | ||
263 | { | ||
264 | ACPI_FUNCTION_TRACE("acpi_thermal_set_polling"); | ||
265 | |||
266 | if (!tz) | ||
267 | return_VALUE(-EINVAL); | ||
268 | |||
269 | tz->polling_frequency = seconds * 10; /* Convert value to deci-seconds */ | ||
270 | |||
271 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency set to %lu seconds\n", tz->polling_frequency)); | ||
272 | |||
273 | return_VALUE(0); | ||
274 | } | ||
275 | |||
276 | |||
277 | static int | ||
278 | acpi_thermal_set_cooling_mode ( | ||
279 | struct acpi_thermal *tz, | ||
280 | int mode) | ||
281 | { | ||
282 | acpi_status status = AE_OK; | ||
283 | union acpi_object arg0 = {ACPI_TYPE_INTEGER}; | ||
284 | struct acpi_object_list arg_list = {1, &arg0}; | ||
285 | acpi_handle handle = NULL; | ||
286 | |||
287 | ACPI_FUNCTION_TRACE("acpi_thermal_set_cooling_mode"); | ||
288 | |||
289 | if (!tz) | ||
290 | return_VALUE(-EINVAL); | ||
291 | |||
292 | status = acpi_get_handle(tz->handle, "_SCP", &handle); | ||
293 | if (ACPI_FAILURE(status)) { | ||
294 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n")); | ||
295 | return_VALUE(-ENODEV); | ||
296 | } | ||
297 | |||
298 | arg0.integer.value = mode; | ||
299 | |||
300 | status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); | ||
301 | if (ACPI_FAILURE(status)) | ||
302 | return_VALUE(-ENODEV); | ||
303 | |||
304 | tz->cooling_mode = mode; | ||
305 | |||
306 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling mode [%s]\n", | ||
307 | mode?"passive":"active")); | ||
308 | |||
309 | return_VALUE(0); | ||
310 | } | ||
311 | |||
312 | |||
313 | static int | ||
314 | acpi_thermal_get_trip_points ( | ||
315 | struct acpi_thermal *tz) | ||
316 | { | ||
317 | acpi_status status = AE_OK; | ||
318 | int i = 0; | ||
319 | |||
320 | ACPI_FUNCTION_TRACE("acpi_thermal_get_trip_points"); | ||
321 | |||
322 | if (!tz) | ||
323 | return_VALUE(-EINVAL); | ||
324 | |||
325 | /* Critical Shutdown (required) */ | ||
326 | |||
327 | status = acpi_evaluate_integer(tz->handle, "_CRT", NULL, | ||
328 | &tz->trips.critical.temperature); | ||
329 | if (ACPI_FAILURE(status)) { | ||
330 | tz->trips.critical.flags.valid = 0; | ||
331 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No critical threshold\n")); | ||
332 | return_VALUE(-ENODEV); | ||
333 | } | ||
334 | else { | ||
335 | tz->trips.critical.flags.valid = 1; | ||
336 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found critical threshold [%lu]\n", tz->trips.critical.temperature)); | ||
337 | } | ||
338 | |||
339 | /* Critical Sleep (optional) */ | ||
340 | |||
341 | status = acpi_evaluate_integer(tz->handle, "_HOT", NULL, &tz->trips.hot.temperature); | ||
342 | if (ACPI_FAILURE(status)) { | ||
343 | tz->trips.hot.flags.valid = 0; | ||
344 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n")); | ||
345 | } | ||
346 | else { | ||
347 | tz->trips.hot.flags.valid = 1; | ||
348 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n", tz->trips.hot.temperature)); | ||
349 | } | ||
350 | |||
351 | /* Passive: Processors (optional) */ | ||
352 | |||
353 | status = acpi_evaluate_integer(tz->handle, "_PSV", NULL, &tz->trips.passive.temperature); | ||
354 | if (ACPI_FAILURE(status)) { | ||
355 | tz->trips.passive.flags.valid = 0; | ||
356 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n")); | ||
357 | } | ||
358 | else { | ||
359 | tz->trips.passive.flags.valid = 1; | ||
360 | |||
361 | status = acpi_evaluate_integer(tz->handle, "_TC1", NULL, &tz->trips.passive.tc1); | ||
362 | if (ACPI_FAILURE(status)) | ||
363 | tz->trips.passive.flags.valid = 0; | ||
364 | |||
365 | status = acpi_evaluate_integer(tz->handle, "_TC2", NULL, &tz->trips.passive.tc2); | ||
366 | if (ACPI_FAILURE(status)) | ||
367 | tz->trips.passive.flags.valid = 0; | ||
368 | |||
369 | status = acpi_evaluate_integer(tz->handle, "_TSP", NULL, &tz->trips.passive.tsp); | ||
370 | if (ACPI_FAILURE(status)) | ||
371 | tz->trips.passive.flags.valid = 0; | ||
372 | |||
373 | status = acpi_evaluate_reference(tz->handle, "_PSL", NULL, &tz->trips.passive.devices); | ||
374 | if (ACPI_FAILURE(status)) | ||
375 | tz->trips.passive.flags.valid = 0; | ||
376 | |||
377 | if (!tz->trips.passive.flags.valid) | ||
378 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid passive threshold\n")); | ||
379 | else | ||
380 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found passive threshold [%lu]\n", tz->trips.passive.temperature)); | ||
381 | } | ||
382 | |||
383 | /* Active: Fans, etc. (optional) */ | ||
384 | |||
385 | for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) { | ||
386 | |||
387 | char name[5] = {'_','A','C',('0'+i),'\0'}; | ||
388 | |||
389 | status = acpi_evaluate_integer(tz->handle, name, NULL, &tz->trips.active[i].temperature); | ||
390 | if (ACPI_FAILURE(status)) | ||
391 | break; | ||
392 | |||
393 | name[2] = 'L'; | ||
394 | status = acpi_evaluate_reference(tz->handle, name, NULL, &tz->trips.active[i].devices); | ||
395 | if (ACPI_SUCCESS(status)) { | ||
396 | tz->trips.active[i].flags.valid = 1; | ||
397 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found active threshold [%d]:[%lu]\n", i, tz->trips.active[i].temperature)); | ||
398 | } | ||
399 | else | ||
400 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid active threshold [%d]\n", i)); | ||
401 | } | ||
402 | |||
403 | return_VALUE(0); | ||
404 | } | ||
405 | |||
406 | |||
407 | static int | ||
408 | acpi_thermal_get_devices ( | ||
409 | struct acpi_thermal *tz) | ||
410 | { | ||
411 | acpi_status status = AE_OK; | ||
412 | |||
413 | ACPI_FUNCTION_TRACE("acpi_thermal_get_devices"); | ||
414 | |||
415 | if (!tz) | ||
416 | return_VALUE(-EINVAL); | ||
417 | |||
418 | status = acpi_evaluate_reference(tz->handle, "_TZD", NULL, &tz->devices); | ||
419 | if (ACPI_FAILURE(status)) | ||
420 | return_VALUE(-ENODEV); | ||
421 | |||
422 | return_VALUE(0); | ||
423 | } | ||
424 | |||
425 | |||
426 | static int | ||
427 | acpi_thermal_call_usermode ( | ||
428 | char *path) | ||
429 | { | ||
430 | char *argv[2] = {NULL, NULL}; | ||
431 | char *envp[3] = {NULL, NULL, NULL}; | ||
432 | |||
433 | ACPI_FUNCTION_TRACE("acpi_thermal_call_usermode"); | ||
434 | |||
435 | if (!path) | ||
436 | return_VALUE(-EINVAL); | ||
437 | |||
438 | argv[0] = path; | ||
439 | |||
440 | /* minimal command environment */ | ||
441 | envp[0] = "HOME=/"; | ||
442 | envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | ||
443 | |||
444 | call_usermodehelper(argv[0], argv, envp, 0); | ||
445 | |||
446 | return_VALUE(0); | ||
447 | } | ||
448 | |||
449 | |||
450 | static int | ||
451 | acpi_thermal_critical ( | ||
452 | struct acpi_thermal *tz) | ||
453 | { | ||
454 | int result = 0; | ||
455 | struct acpi_device *device = NULL; | ||
456 | |||
457 | ACPI_FUNCTION_TRACE("acpi_thermal_critical"); | ||
458 | |||
459 | if (!tz || !tz->trips.critical.flags.valid) | ||
460 | return_VALUE(-EINVAL); | ||
461 | |||
462 | if (tz->temperature >= tz->trips.critical.temperature) { | ||
463 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Critical trip point\n")); | ||
464 | tz->trips.critical.flags.enabled = 1; | ||
465 | } | ||
466 | else if (tz->trips.critical.flags.enabled) | ||
467 | tz->trips.critical.flags.enabled = 0; | ||
468 | |||
469 | result = acpi_bus_get_device(tz->handle, &device); | ||
470 | if (result) | ||
471 | return_VALUE(result); | ||
472 | |||
473 | printk(KERN_EMERG "Critical temperature reached (%ld C), shutting down.\n", KELVIN_TO_CELSIUS(tz->temperature)); | ||
474 | acpi_bus_generate_event(device, ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); | ||
475 | |||
476 | acpi_thermal_call_usermode(ACPI_THERMAL_PATH_POWEROFF); | ||
477 | |||
478 | return_VALUE(0); | ||
479 | } | ||
480 | |||
481 | |||
482 | static int | ||
483 | acpi_thermal_hot ( | ||
484 | struct acpi_thermal *tz) | ||
485 | { | ||
486 | int result = 0; | ||
487 | struct acpi_device *device = NULL; | ||
488 | |||
489 | ACPI_FUNCTION_TRACE("acpi_thermal_hot"); | ||
490 | |||
491 | if (!tz || !tz->trips.hot.flags.valid) | ||
492 | return_VALUE(-EINVAL); | ||
493 | |||
494 | if (tz->temperature >= tz->trips.hot.temperature) { | ||
495 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Hot trip point\n")); | ||
496 | tz->trips.hot.flags.enabled = 1; | ||
497 | } | ||
498 | else if (tz->trips.hot.flags.enabled) | ||
499 | tz->trips.hot.flags.enabled = 0; | ||
500 | |||
501 | result = acpi_bus_get_device(tz->handle, &device); | ||
502 | if (result) | ||
503 | return_VALUE(result); | ||
504 | |||
505 | acpi_bus_generate_event(device, ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled); | ||
506 | |||
507 | /* TBD: Call user-mode "sleep(S4)" function */ | ||
508 | |||
509 | return_VALUE(0); | ||
510 | } | ||
511 | |||
512 | |||
513 | static int | ||
514 | acpi_thermal_passive ( | ||
515 | struct acpi_thermal *tz) | ||
516 | { | ||
517 | int result = 0; | ||
518 | struct acpi_thermal_passive *passive = NULL; | ||
519 | int trend = 0; | ||
520 | int i = 0; | ||
521 | |||
522 | ACPI_FUNCTION_TRACE("acpi_thermal_passive"); | ||
523 | |||
524 | if (!tz || !tz->trips.passive.flags.valid) | ||
525 | return_VALUE(-EINVAL); | ||
526 | |||
527 | passive = &(tz->trips.passive); | ||
528 | |||
529 | /* | ||
530 | * Above Trip? | ||
531 | * ----------- | ||
532 | * Calculate the thermal trend (using the passive cooling equation) | ||
533 | * and modify the performance limit for all passive cooling devices | ||
534 | * accordingly. Note that we assume symmetry. | ||
535 | */ | ||
536 | if (tz->temperature >= passive->temperature) { | ||
537 | trend = (passive->tc1 * (tz->temperature - tz->last_temperature)) + (passive->tc2 * (tz->temperature - passive->temperature)); | ||
538 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
539 | "trend[%d]=(tc1[%lu]*(tmp[%lu]-last[%lu]))+(tc2[%lu]*(tmp[%lu]-psv[%lu]))\n", | ||
540 | trend, passive->tc1, tz->temperature, | ||
541 | tz->last_temperature, passive->tc2, | ||
542 | tz->temperature, passive->temperature)); | ||
543 | tz->trips.passive.flags.enabled = 1; | ||
544 | /* Heating up? */ | ||
545 | if (trend > 0) | ||
546 | for (i=0; i<passive->devices.count; i++) | ||
547 | acpi_processor_set_thermal_limit( | ||
548 | passive->devices.handles[i], | ||
549 | ACPI_PROCESSOR_LIMIT_INCREMENT); | ||
550 | /* Cooling off? */ | ||
551 | else if (trend < 0) | ||
552 | for (i=0; i<passive->devices.count; i++) | ||
553 | acpi_processor_set_thermal_limit( | ||
554 | passive->devices.handles[i], | ||
555 | ACPI_PROCESSOR_LIMIT_DECREMENT); | ||
556 | } | ||
557 | |||
558 | /* | ||
559 | * Below Trip? | ||
560 | * ----------- | ||
561 | * Implement passive cooling hysteresis to slowly increase performance | ||
562 | * and avoid thrashing around the passive trip point. Note that we | ||
563 | * assume symmetry. | ||
564 | */ | ||
565 | else if (tz->trips.passive.flags.enabled) { | ||
566 | for (i=0; i<passive->devices.count; i++) | ||
567 | result = acpi_processor_set_thermal_limit( | ||
568 | passive->devices.handles[i], | ||
569 | ACPI_PROCESSOR_LIMIT_DECREMENT); | ||
570 | if (result == 1) { | ||
571 | tz->trips.passive.flags.enabled = 0; | ||
572 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
573 | "Disabling passive cooling (zone is cool)\n")); | ||
574 | } | ||
575 | } | ||
576 | |||
577 | return_VALUE(0); | ||
578 | } | ||
579 | |||
580 | |||
581 | static int | ||
582 | acpi_thermal_active ( | ||
583 | struct acpi_thermal *tz) | ||
584 | { | ||
585 | int result = 0; | ||
586 | struct acpi_thermal_active *active = NULL; | ||
587 | int i = 0; | ||
588 | int j = 0; | ||
589 | unsigned long maxtemp = 0; | ||
590 | |||
591 | ACPI_FUNCTION_TRACE("acpi_thermal_active"); | ||
592 | |||
593 | if (!tz) | ||
594 | return_VALUE(-EINVAL); | ||
595 | |||
596 | for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) { | ||
597 | |||
598 | active = &(tz->trips.active[i]); | ||
599 | if (!active || !active->flags.valid) | ||
600 | break; | ||
601 | |||
602 | /* | ||
603 | * Above Threshold? | ||
604 | * ---------------- | ||
605 | * If not already enabled, turn ON all cooling devices | ||
606 | * associated with this active threshold. | ||
607 | */ | ||
608 | if (tz->temperature >= active->temperature) { | ||
609 | if (active->temperature > maxtemp) | ||
610 | tz->state.active_index = i, maxtemp = active->temperature; | ||
611 | if (!active->flags.enabled) { | ||
612 | for (j = 0; j < active->devices.count; j++) { | ||
613 | result = acpi_bus_set_power(active->devices.handles[j], ACPI_STATE_D0); | ||
614 | if (result) { | ||
615 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to turn cooling device [%p] 'on'\n", active->devices.handles[j])); | ||
616 | continue; | ||
617 | } | ||
618 | active->flags.enabled = 1; | ||
619 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling device [%p] now 'on'\n", active->devices.handles[j])); | ||
620 | } | ||
621 | } | ||
622 | } | ||
623 | /* | ||
624 | * Below Threshold? | ||
625 | * ---------------- | ||
626 | * Turn OFF all cooling devices associated with this | ||
627 | * threshold. | ||
628 | */ | ||
629 | else if (active->flags.enabled) { | ||
630 | for (j = 0; j < active->devices.count; j++) { | ||
631 | result = acpi_bus_set_power(active->devices.handles[j], ACPI_STATE_D3); | ||
632 | if (result) { | ||
633 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to turn cooling device [%p] 'off'\n", active->devices.handles[j])); | ||
634 | continue; | ||
635 | } | ||
636 | active->flags.enabled = 0; | ||
637 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling device [%p] now 'off'\n", active->devices.handles[j])); | ||
638 | } | ||
639 | } | ||
640 | } | ||
641 | |||
642 | return_VALUE(0); | ||
643 | } | ||
644 | |||
645 | |||
646 | static void acpi_thermal_check (void *context); | ||
647 | |||
648 | static void | ||
649 | acpi_thermal_run ( | ||
650 | unsigned long data) | ||
651 | { | ||
652 | struct acpi_thermal *tz = (struct acpi_thermal *)data; | ||
653 | if (!tz->zombie) | ||
654 | acpi_os_queue_for_execution(OSD_PRIORITY_GPE, | ||
655 | acpi_thermal_check, (void *) data); | ||
656 | } | ||
657 | |||
658 | |||
659 | static void | ||
660 | acpi_thermal_check ( | ||
661 | void *data) | ||
662 | { | ||
663 | int result = 0; | ||
664 | struct acpi_thermal *tz = (struct acpi_thermal *) data; | ||
665 | unsigned long sleep_time = 0; | ||
666 | int i = 0; | ||
667 | struct acpi_thermal_state state; | ||
668 | |||
669 | ACPI_FUNCTION_TRACE("acpi_thermal_check"); | ||
670 | |||
671 | if (!tz) { | ||
672 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid (NULL) context.\n")); | ||
673 | return_VOID; | ||
674 | } | ||
675 | |||
676 | state = tz->state; | ||
677 | |||
678 | result = acpi_thermal_get_temperature(tz); | ||
679 | if (result) | ||
680 | return_VOID; | ||
681 | |||
682 | memset(&tz->state, 0, sizeof(tz->state)); | ||
683 | |||
684 | /* | ||
685 | * Check Trip Points | ||
686 | * ----------------- | ||
687 | * Compare the current temperature to the trip point values to see | ||
688 | * if we've entered one of the thermal policy states. Note that | ||
689 | * this function determines when a state is entered, but the | ||
690 | * individual policy decides when it is exited (e.g. hysteresis). | ||
691 | */ | ||
692 | if (tz->trips.critical.flags.valid) | ||
693 | state.critical |= (tz->temperature >= tz->trips.critical.temperature); | ||
694 | if (tz->trips.hot.flags.valid) | ||
695 | state.hot |= (tz->temperature >= tz->trips.hot.temperature); | ||
696 | if (tz->trips.passive.flags.valid) | ||
697 | state.passive |= (tz->temperature >= tz->trips.passive.temperature); | ||
698 | for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) | ||
699 | if (tz->trips.active[i].flags.valid) | ||
700 | state.active |= (tz->temperature >= tz->trips.active[i].temperature); | ||
701 | |||
702 | /* | ||
703 | * Invoke Policy | ||
704 | * ------------- | ||
705 | * Separated from the above check to allow individual policy to | ||
706 | * determine when to exit a given state. | ||
707 | */ | ||
708 | if (state.critical) | ||
709 | acpi_thermal_critical(tz); | ||
710 | if (state.hot) | ||
711 | acpi_thermal_hot(tz); | ||
712 | if (state.passive) | ||
713 | acpi_thermal_passive(tz); | ||
714 | if (state.active) | ||
715 | acpi_thermal_active(tz); | ||
716 | |||
717 | /* | ||
718 | * Calculate State | ||
719 | * --------------- | ||
720 | * Again, separated from the above two to allow independent policy | ||
721 | * decisions. | ||
722 | */ | ||
723 | if (tz->trips.critical.flags.enabled) | ||
724 | tz->state.critical = 1; | ||
725 | if (tz->trips.hot.flags.enabled) | ||
726 | tz->state.hot = 1; | ||
727 | if (tz->trips.passive.flags.enabled) | ||
728 | tz->state.passive = 1; | ||
729 | for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) | ||
730 | if (tz->trips.active[i].flags.enabled) | ||
731 | tz->state.active = 1; | ||
732 | |||
733 | /* | ||
734 | * Calculate Sleep Time | ||
735 | * -------------------- | ||
736 | * If we're in the passive state, use _TSP's value. Otherwise | ||
737 | * use the default polling frequency (e.g. _TZP). If no polling | ||
738 | * frequency is specified then we'll wait forever (at least until | ||
739 | * a thermal event occurs). Note that _TSP and _TZD values are | ||
740 | * given in 1/10th seconds (we must covert to milliseconds). | ||
741 | */ | ||
742 | if (tz->state.passive) | ||
743 | sleep_time = tz->trips.passive.tsp * 100; | ||
744 | else if (tz->polling_frequency > 0) | ||
745 | sleep_time = tz->polling_frequency * 100; | ||
746 | |||
747 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: temperature[%lu] sleep[%lu]\n", | ||
748 | tz->name, tz->temperature, sleep_time)); | ||
749 | |||
750 | /* | ||
751 | * Schedule Next Poll | ||
752 | * ------------------ | ||
753 | */ | ||
754 | if (!sleep_time) { | ||
755 | if (timer_pending(&(tz->timer))) | ||
756 | del_timer(&(tz->timer)); | ||
757 | } | ||
758 | else { | ||
759 | if (timer_pending(&(tz->timer))) | ||
760 | mod_timer(&(tz->timer), (HZ * sleep_time) / 1000); | ||
761 | else { | ||
762 | tz->timer.data = (unsigned long) tz; | ||
763 | tz->timer.function = acpi_thermal_run; | ||
764 | tz->timer.expires = jiffies + (HZ * sleep_time) / 1000; | ||
765 | add_timer(&(tz->timer)); | ||
766 | } | ||
767 | } | ||
768 | |||
769 | return_VOID; | ||
770 | } | ||
771 | |||
772 | |||
773 | /* -------------------------------------------------------------------------- | ||
774 | FS Interface (/proc) | ||
775 | -------------------------------------------------------------------------- */ | ||
776 | |||
777 | static struct proc_dir_entry *acpi_thermal_dir; | ||
778 | |||
779 | static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset) | ||
780 | { | ||
781 | struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; | ||
782 | |||
783 | ACPI_FUNCTION_TRACE("acpi_thermal_state_seq_show"); | ||
784 | |||
785 | if (!tz) | ||
786 | goto end; | ||
787 | |||
788 | seq_puts(seq, "state: "); | ||
789 | |||
790 | if (!tz->state.critical && !tz->state.hot && !tz->state.passive && !tz->state.active) | ||
791 | seq_puts(seq, "ok\n"); | ||
792 | else { | ||
793 | if (tz->state.critical) | ||
794 | seq_puts(seq, "critical "); | ||
795 | if (tz->state.hot) | ||
796 | seq_puts(seq, "hot "); | ||
797 | if (tz->state.passive) | ||
798 | seq_puts(seq, "passive "); | ||
799 | if (tz->state.active) | ||
800 | seq_printf(seq, "active[%d]", tz->state.active_index); | ||
801 | seq_puts(seq, "\n"); | ||
802 | } | ||
803 | |||
804 | end: | ||
805 | return_VALUE(0); | ||
806 | } | ||
807 | |||
808 | static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file) | ||
809 | { | ||
810 | return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data); | ||
811 | } | ||
812 | |||
813 | |||
814 | static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset) | ||
815 | { | ||
816 | int result = 0; | ||
817 | struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; | ||
818 | |||
819 | ACPI_FUNCTION_TRACE("acpi_thermal_temp_seq_show"); | ||
820 | |||
821 | if (!tz) | ||
822 | goto end; | ||
823 | |||
824 | result = acpi_thermal_get_temperature(tz); | ||
825 | if (result) | ||
826 | goto end; | ||
827 | |||
828 | seq_printf(seq, "temperature: %ld C\n", | ||
829 | KELVIN_TO_CELSIUS(tz->temperature)); | ||
830 | |||
831 | end: | ||
832 | return_VALUE(0); | ||
833 | } | ||
834 | |||
835 | static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file) | ||
836 | { | ||
837 | return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data); | ||
838 | } | ||
839 | |||
840 | |||
841 | static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset) | ||
842 | { | ||
843 | struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; | ||
844 | int i = 0; | ||
845 | int j = 0; | ||
846 | |||
847 | ACPI_FUNCTION_TRACE("acpi_thermal_trip_seq_show"); | ||
848 | |||
849 | if (!tz) | ||
850 | goto end; | ||
851 | |||
852 | if (tz->trips.critical.flags.valid) | ||
853 | seq_printf(seq, "critical (S5): %ld C\n", | ||
854 | KELVIN_TO_CELSIUS(tz->trips.critical.temperature)); | ||
855 | |||
856 | if (tz->trips.hot.flags.valid) | ||
857 | seq_printf(seq, "hot (S4): %ld C\n", | ||
858 | KELVIN_TO_CELSIUS(tz->trips.hot.temperature)); | ||
859 | |||
860 | if (tz->trips.passive.flags.valid) { | ||
861 | seq_printf(seq, "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=", | ||
862 | KELVIN_TO_CELSIUS(tz->trips.passive.temperature), | ||
863 | tz->trips.passive.tc1, | ||
864 | tz->trips.passive.tc2, | ||
865 | tz->trips.passive.tsp); | ||
866 | for (j=0; j<tz->trips.passive.devices.count; j++) { | ||
867 | |||
868 | seq_printf(seq, "0x%p ", tz->trips.passive.devices.handles[j]); | ||
869 | } | ||
870 | seq_puts(seq, "\n"); | ||
871 | } | ||
872 | |||
873 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { | ||
874 | if (!(tz->trips.active[i].flags.valid)) | ||
875 | break; | ||
876 | seq_printf(seq, "active[%d]: %ld C: devices=", | ||
877 | i, KELVIN_TO_CELSIUS(tz->trips.active[i].temperature)); | ||
878 | for (j = 0; j < tz->trips.active[i].devices.count; j++) | ||
879 | seq_printf(seq, "0x%p ", | ||
880 | tz->trips.active[i].devices.handles[j]); | ||
881 | seq_puts(seq, "\n"); | ||
882 | } | ||
883 | |||
884 | end: | ||
885 | return_VALUE(0); | ||
886 | } | ||
887 | |||
888 | static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file) | ||
889 | { | ||
890 | return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data); | ||
891 | } | ||
892 | |||
893 | static ssize_t | ||
894 | acpi_thermal_write_trip_points ( | ||
895 | struct file *file, | ||
896 | const char __user *buffer, | ||
897 | size_t count, | ||
898 | loff_t *ppos) | ||
899 | { | ||
900 | struct seq_file *m = (struct seq_file *)file->private_data; | ||
901 | struct acpi_thermal *tz = (struct acpi_thermal *)m->private; | ||
902 | |||
903 | char *limit_string; | ||
904 | int num, critical, hot, passive; | ||
905 | int *active; | ||
906 | int i = 0; | ||
907 | |||
908 | ACPI_FUNCTION_TRACE("acpi_thermal_write_trip_points"); | ||
909 | |||
910 | limit_string = kmalloc(ACPI_THERMAL_MAX_LIMIT_STR_LEN, GFP_KERNEL); | ||
911 | if(!limit_string) | ||
912 | return_VALUE(-ENOMEM); | ||
913 | |||
914 | memset(limit_string, 0, ACPI_THERMAL_MAX_LIMIT_STR_LEN); | ||
915 | |||
916 | active = kmalloc(ACPI_THERMAL_MAX_ACTIVE *sizeof(int), GFP_KERNEL); | ||
917 | if(!active) | ||
918 | return_VALUE(-ENOMEM); | ||
919 | |||
920 | if (!tz || (count > ACPI_THERMAL_MAX_LIMIT_STR_LEN - 1)) { | ||
921 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument\n")); | ||
922 | count = -EINVAL; | ||
923 | goto end; | ||
924 | } | ||
925 | |||
926 | if (copy_from_user(limit_string, buffer, count)) { | ||
927 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n")); | ||
928 | count = -EFAULT; | ||
929 | goto end; | ||
930 | } | ||
931 | |||
932 | limit_string[count] = '\0'; | ||
933 | |||
934 | num = sscanf(limit_string, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", | ||
935 | &critical, &hot, &passive, | ||
936 | &active[0], &active[1], &active[2], &active[3], &active[4], | ||
937 | &active[5], &active[6], &active[7], &active[8], &active[9]); | ||
938 | if(!(num >=5 && num < (ACPI_THERMAL_MAX_ACTIVE + 3))) { | ||
939 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n")); | ||
940 | count = -EINVAL; | ||
941 | goto end; | ||
942 | } | ||
943 | |||
944 | tz->trips.critical.temperature = CELSIUS_TO_KELVIN(critical); | ||
945 | tz->trips.hot.temperature = CELSIUS_TO_KELVIN(hot); | ||
946 | tz->trips.passive.temperature = CELSIUS_TO_KELVIN(passive); | ||
947 | for (i = 0; i < num - 3; i++) { | ||
948 | if (!(tz->trips.active[i].flags.valid)) | ||
949 | break; | ||
950 | tz->trips.active[i].temperature = CELSIUS_TO_KELVIN(active[i]); | ||
951 | } | ||
952 | |||
953 | end: | ||
954 | kfree(active); | ||
955 | kfree(limit_string); | ||
956 | return_VALUE(count); | ||
957 | } | ||
958 | |||
959 | |||
960 | static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset) | ||
961 | { | ||
962 | struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; | ||
963 | |||
964 | ACPI_FUNCTION_TRACE("acpi_thermal_cooling_seq_show"); | ||
965 | |||
966 | if (!tz) | ||
967 | goto end; | ||
968 | |||
969 | if (!tz->flags.cooling_mode) { | ||
970 | seq_puts(seq, "<setting not supported>\n"); | ||
971 | } | ||
972 | |||
973 | if ( tz->cooling_mode == ACPI_THERMAL_MODE_CRITICAL ) | ||
974 | seq_printf(seq, "cooling mode: critical\n"); | ||
975 | else | ||
976 | seq_printf(seq, "cooling mode: %s\n", | ||
977 | tz->cooling_mode?"passive":"active"); | ||
978 | |||
979 | end: | ||
980 | return_VALUE(0); | ||
981 | } | ||
982 | |||
983 | static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file) | ||
984 | { | ||
985 | return single_open(file, acpi_thermal_cooling_seq_show, | ||
986 | PDE(inode)->data); | ||
987 | } | ||
988 | |||
989 | static ssize_t | ||
990 | acpi_thermal_write_cooling_mode ( | ||
991 | struct file *file, | ||
992 | const char __user *buffer, | ||
993 | size_t count, | ||
994 | loff_t *ppos) | ||
995 | { | ||
996 | struct seq_file *m = (struct seq_file *)file->private_data; | ||
997 | struct acpi_thermal *tz = (struct acpi_thermal *)m->private; | ||
998 | int result = 0; | ||
999 | char mode_string[12] = {'\0'}; | ||
1000 | |||
1001 | ACPI_FUNCTION_TRACE("acpi_thermal_write_cooling_mode"); | ||
1002 | |||
1003 | if (!tz || (count > sizeof(mode_string) - 1)) | ||
1004 | return_VALUE(-EINVAL); | ||
1005 | |||
1006 | if (!tz->flags.cooling_mode) | ||
1007 | return_VALUE(-ENODEV); | ||
1008 | |||
1009 | if (copy_from_user(mode_string, buffer, count)) | ||
1010 | return_VALUE(-EFAULT); | ||
1011 | |||
1012 | mode_string[count] = '\0'; | ||
1013 | |||
1014 | result = acpi_thermal_set_cooling_mode(tz, | ||
1015 | simple_strtoul(mode_string, NULL, 0)); | ||
1016 | if (result) | ||
1017 | return_VALUE(result); | ||
1018 | |||
1019 | acpi_thermal_check(tz); | ||
1020 | |||
1021 | return_VALUE(count); | ||
1022 | } | ||
1023 | |||
1024 | |||
1025 | static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset) | ||
1026 | { | ||
1027 | struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; | ||
1028 | |||
1029 | ACPI_FUNCTION_TRACE("acpi_thermal_polling_seq_show"); | ||
1030 | |||
1031 | if (!tz) | ||
1032 | goto end; | ||
1033 | |||
1034 | if (!tz->polling_frequency) { | ||
1035 | seq_puts(seq, "<polling disabled>\n"); | ||
1036 | goto end; | ||
1037 | } | ||
1038 | |||
1039 | seq_printf(seq, "polling frequency: %lu seconds\n", | ||
1040 | (tz->polling_frequency / 10)); | ||
1041 | |||
1042 | end: | ||
1043 | return_VALUE(0); | ||
1044 | } | ||
1045 | |||
1046 | static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file) | ||
1047 | { | ||
1048 | return single_open(file, acpi_thermal_polling_seq_show, | ||
1049 | PDE(inode)->data); | ||
1050 | } | ||
1051 | |||
1052 | static ssize_t | ||
1053 | acpi_thermal_write_polling ( | ||
1054 | struct file *file, | ||
1055 | const char __user *buffer, | ||
1056 | size_t count, | ||
1057 | loff_t *ppos) | ||
1058 | { | ||
1059 | struct seq_file *m = (struct seq_file *)file->private_data; | ||
1060 | struct acpi_thermal *tz = (struct acpi_thermal *)m->private; | ||
1061 | int result = 0; | ||
1062 | char polling_string[12] = {'\0'}; | ||
1063 | int seconds = 0; | ||
1064 | |||
1065 | ACPI_FUNCTION_TRACE("acpi_thermal_write_polling"); | ||
1066 | |||
1067 | if (!tz || (count > sizeof(polling_string) - 1)) | ||
1068 | return_VALUE(-EINVAL); | ||
1069 | |||
1070 | if (copy_from_user(polling_string, buffer, count)) | ||
1071 | return_VALUE(-EFAULT); | ||
1072 | |||
1073 | polling_string[count] = '\0'; | ||
1074 | |||
1075 | seconds = simple_strtoul(polling_string, NULL, 0); | ||
1076 | |||
1077 | result = acpi_thermal_set_polling(tz, seconds); | ||
1078 | if (result) | ||
1079 | return_VALUE(result); | ||
1080 | |||
1081 | acpi_thermal_check(tz); | ||
1082 | |||
1083 | return_VALUE(count); | ||
1084 | } | ||
1085 | |||
1086 | |||
1087 | static int | ||
1088 | acpi_thermal_add_fs ( | ||
1089 | struct acpi_device *device) | ||
1090 | { | ||
1091 | struct proc_dir_entry *entry = NULL; | ||
1092 | |||
1093 | ACPI_FUNCTION_TRACE("acpi_thermal_add_fs"); | ||
1094 | |||
1095 | if (!acpi_device_dir(device)) { | ||
1096 | acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), | ||
1097 | acpi_thermal_dir); | ||
1098 | if (!acpi_device_dir(device)) | ||
1099 | return_VALUE(-ENODEV); | ||
1100 | acpi_device_dir(device)->owner = THIS_MODULE; | ||
1101 | } | ||
1102 | |||
1103 | /* 'state' [R] */ | ||
1104 | entry = create_proc_entry(ACPI_THERMAL_FILE_STATE, | ||
1105 | S_IRUGO, acpi_device_dir(device)); | ||
1106 | if (!entry) | ||
1107 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1108 | "Unable to create '%s' fs entry\n", | ||
1109 | ACPI_THERMAL_FILE_STATE)); | ||
1110 | else { | ||
1111 | entry->proc_fops = &acpi_thermal_state_fops; | ||
1112 | entry->data = acpi_driver_data(device); | ||
1113 | entry->owner = THIS_MODULE; | ||
1114 | } | ||
1115 | |||
1116 | /* 'temperature' [R] */ | ||
1117 | entry = create_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE, | ||
1118 | S_IRUGO, acpi_device_dir(device)); | ||
1119 | if (!entry) | ||
1120 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1121 | "Unable to create '%s' fs entry\n", | ||
1122 | ACPI_THERMAL_FILE_TEMPERATURE)); | ||
1123 | else { | ||
1124 | entry->proc_fops = &acpi_thermal_temp_fops; | ||
1125 | entry->data = acpi_driver_data(device); | ||
1126 | entry->owner = THIS_MODULE; | ||
1127 | } | ||
1128 | |||
1129 | /* 'trip_points' [R/W] */ | ||
1130 | entry = create_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS, | ||
1131 | S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); | ||
1132 | if (!entry) | ||
1133 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1134 | "Unable to create '%s' fs entry\n", | ||
1135 | ACPI_THERMAL_FILE_TRIP_POINTS)); | ||
1136 | else { | ||
1137 | entry->proc_fops = &acpi_thermal_trip_fops; | ||
1138 | entry->data = acpi_driver_data(device); | ||
1139 | entry->owner = THIS_MODULE; | ||
1140 | } | ||
1141 | |||
1142 | /* 'cooling_mode' [R/W] */ | ||
1143 | entry = create_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE, | ||
1144 | S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); | ||
1145 | if (!entry) | ||
1146 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1147 | "Unable to create '%s' fs entry\n", | ||
1148 | ACPI_THERMAL_FILE_COOLING_MODE)); | ||
1149 | else { | ||
1150 | entry->proc_fops = &acpi_thermal_cooling_fops; | ||
1151 | entry->data = acpi_driver_data(device); | ||
1152 | entry->owner = THIS_MODULE; | ||
1153 | } | ||
1154 | |||
1155 | /* 'polling_frequency' [R/W] */ | ||
1156 | entry = create_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ, | ||
1157 | S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); | ||
1158 | if (!entry) | ||
1159 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1160 | "Unable to create '%s' fs entry\n", | ||
1161 | ACPI_THERMAL_FILE_POLLING_FREQ)); | ||
1162 | else { | ||
1163 | entry->proc_fops = &acpi_thermal_polling_fops; | ||
1164 | entry->data = acpi_driver_data(device); | ||
1165 | entry->owner = THIS_MODULE; | ||
1166 | } | ||
1167 | |||
1168 | return_VALUE(0); | ||
1169 | } | ||
1170 | |||
1171 | |||
1172 | static int | ||
1173 | acpi_thermal_remove_fs ( | ||
1174 | struct acpi_device *device) | ||
1175 | { | ||
1176 | ACPI_FUNCTION_TRACE("acpi_thermal_remove_fs"); | ||
1177 | |||
1178 | if (acpi_device_dir(device)) { | ||
1179 | remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ, | ||
1180 | acpi_device_dir(device)); | ||
1181 | remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE, | ||
1182 | acpi_device_dir(device)); | ||
1183 | remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS, | ||
1184 | acpi_device_dir(device)); | ||
1185 | remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE, | ||
1186 | acpi_device_dir(device)); | ||
1187 | remove_proc_entry(ACPI_THERMAL_FILE_STATE, | ||
1188 | acpi_device_dir(device)); | ||
1189 | remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir); | ||
1190 | acpi_device_dir(device) = NULL; | ||
1191 | } | ||
1192 | |||
1193 | return_VALUE(0); | ||
1194 | } | ||
1195 | |||
1196 | |||
1197 | /* -------------------------------------------------------------------------- | ||
1198 | Driver Interface | ||
1199 | -------------------------------------------------------------------------- */ | ||
1200 | |||
1201 | static void | ||
1202 | acpi_thermal_notify ( | ||
1203 | acpi_handle handle, | ||
1204 | u32 event, | ||
1205 | void *data) | ||
1206 | { | ||
1207 | struct acpi_thermal *tz = (struct acpi_thermal *) data; | ||
1208 | struct acpi_device *device = NULL; | ||
1209 | |||
1210 | ACPI_FUNCTION_TRACE("acpi_thermal_notify"); | ||
1211 | |||
1212 | if (!tz) | ||
1213 | return_VOID; | ||
1214 | |||
1215 | if (acpi_bus_get_device(tz->handle, &device)) | ||
1216 | return_VOID; | ||
1217 | |||
1218 | switch (event) { | ||
1219 | case ACPI_THERMAL_NOTIFY_TEMPERATURE: | ||
1220 | acpi_thermal_check(tz); | ||
1221 | break; | ||
1222 | case ACPI_THERMAL_NOTIFY_THRESHOLDS: | ||
1223 | acpi_thermal_get_trip_points(tz); | ||
1224 | acpi_thermal_check(tz); | ||
1225 | acpi_bus_generate_event(device, event, 0); | ||
1226 | break; | ||
1227 | case ACPI_THERMAL_NOTIFY_DEVICES: | ||
1228 | if (tz->flags.devices) | ||
1229 | acpi_thermal_get_devices(tz); | ||
1230 | acpi_bus_generate_event(device, event, 0); | ||
1231 | break; | ||
1232 | default: | ||
1233 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
1234 | "Unsupported event [0x%x]\n", event)); | ||
1235 | break; | ||
1236 | } | ||
1237 | |||
1238 | return_VOID; | ||
1239 | } | ||
1240 | |||
1241 | |||
1242 | static int | ||
1243 | acpi_thermal_get_info ( | ||
1244 | struct acpi_thermal *tz) | ||
1245 | { | ||
1246 | int result = 0; | ||
1247 | |||
1248 | ACPI_FUNCTION_TRACE("acpi_thermal_get_info"); | ||
1249 | |||
1250 | if (!tz) | ||
1251 | return_VALUE(-EINVAL); | ||
1252 | |||
1253 | /* Get temperature [_TMP] (required) */ | ||
1254 | result = acpi_thermal_get_temperature(tz); | ||
1255 | if (result) | ||
1256 | return_VALUE(result); | ||
1257 | |||
1258 | /* Get trip points [_CRT, _PSV, etc.] (required) */ | ||
1259 | result = acpi_thermal_get_trip_points(tz); | ||
1260 | if (result) | ||
1261 | return_VALUE(result); | ||
1262 | |||
1263 | /* Set the cooling mode [_SCP] to active cooling (default) */ | ||
1264 | result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE); | ||
1265 | if (!result) | ||
1266 | tz->flags.cooling_mode = 1; | ||
1267 | else { | ||
1268 | /* Oh,we have not _SCP method. | ||
1269 | Generally show cooling_mode by _ACx, _PSV,spec 12.2*/ | ||
1270 | tz->flags.cooling_mode = 0; | ||
1271 | if ( tz->trips.active[0].flags.valid && tz->trips.passive.flags.valid ) { | ||
1272 | if ( tz->trips.passive.temperature > tz->trips.active[0].temperature ) | ||
1273 | tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE; | ||
1274 | else | ||
1275 | tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE; | ||
1276 | } else if ( !tz->trips.active[0].flags.valid && tz->trips.passive.flags.valid ) { | ||
1277 | tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE; | ||
1278 | } else if ( tz->trips.active[0].flags.valid && !tz->trips.passive.flags.valid ) { | ||
1279 | tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE; | ||
1280 | } else { | ||
1281 | /* _ACx and _PSV are optional, but _CRT is required */ | ||
1282 | tz->cooling_mode = ACPI_THERMAL_MODE_CRITICAL; | ||
1283 | } | ||
1284 | } | ||
1285 | |||
1286 | /* Get default polling frequency [_TZP] (optional) */ | ||
1287 | if (tzp) | ||
1288 | tz->polling_frequency = tzp; | ||
1289 | else | ||
1290 | acpi_thermal_get_polling_frequency(tz); | ||
1291 | |||
1292 | /* Get devices in this thermal zone [_TZD] (optional) */ | ||
1293 | result = acpi_thermal_get_devices(tz); | ||
1294 | if (!result) | ||
1295 | tz->flags.devices = 1; | ||
1296 | |||
1297 | return_VALUE(0); | ||
1298 | } | ||
1299 | |||
1300 | |||
1301 | static int | ||
1302 | acpi_thermal_add ( | ||
1303 | struct acpi_device *device) | ||
1304 | { | ||
1305 | int result = 0; | ||
1306 | acpi_status status = AE_OK; | ||
1307 | struct acpi_thermal *tz = NULL; | ||
1308 | |||
1309 | ACPI_FUNCTION_TRACE("acpi_thermal_add"); | ||
1310 | |||
1311 | if (!device) | ||
1312 | return_VALUE(-EINVAL); | ||
1313 | |||
1314 | tz = kmalloc(sizeof(struct acpi_thermal), GFP_KERNEL); | ||
1315 | if (!tz) | ||
1316 | return_VALUE(-ENOMEM); | ||
1317 | memset(tz, 0, sizeof(struct acpi_thermal)); | ||
1318 | |||
1319 | tz->handle = device->handle; | ||
1320 | strcpy(tz->name, device->pnp.bus_id); | ||
1321 | strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME); | ||
1322 | strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); | ||
1323 | acpi_driver_data(device) = tz; | ||
1324 | |||
1325 | result = acpi_thermal_get_info(tz); | ||
1326 | if (result) | ||
1327 | goto end; | ||
1328 | |||
1329 | result = acpi_thermal_add_fs(device); | ||
1330 | if (result) | ||
1331 | return_VALUE(result); | ||
1332 | |||
1333 | init_timer(&tz->timer); | ||
1334 | |||
1335 | acpi_thermal_check(tz); | ||
1336 | |||
1337 | status = acpi_install_notify_handler(tz->handle, | ||
1338 | ACPI_DEVICE_NOTIFY, acpi_thermal_notify, tz); | ||
1339 | if (ACPI_FAILURE(status)) { | ||
1340 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1341 | "Error installing notify handler\n")); | ||
1342 | result = -ENODEV; | ||
1343 | goto end; | ||
1344 | } | ||
1345 | |||
1346 | printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", | ||
1347 | acpi_device_name(device), acpi_device_bid(device), | ||
1348 | KELVIN_TO_CELSIUS(tz->temperature)); | ||
1349 | |||
1350 | end: | ||
1351 | if (result) { | ||
1352 | acpi_thermal_remove_fs(device); | ||
1353 | kfree(tz); | ||
1354 | } | ||
1355 | |||
1356 | return_VALUE(result); | ||
1357 | } | ||
1358 | |||
1359 | |||
1360 | static int | ||
1361 | acpi_thermal_remove ( | ||
1362 | struct acpi_device *device, | ||
1363 | int type) | ||
1364 | { | ||
1365 | acpi_status status = AE_OK; | ||
1366 | struct acpi_thermal *tz = NULL; | ||
1367 | |||
1368 | ACPI_FUNCTION_TRACE("acpi_thermal_remove"); | ||
1369 | |||
1370 | if (!device || !acpi_driver_data(device)) | ||
1371 | return_VALUE(-EINVAL); | ||
1372 | |||
1373 | tz = (struct acpi_thermal *) acpi_driver_data(device); | ||
1374 | |||
1375 | /* avoid timer adding new defer task */ | ||
1376 | tz->zombie = 1; | ||
1377 | /* wait for running timer (on other CPUs) finish */ | ||
1378 | del_timer_sync(&(tz->timer)); | ||
1379 | /* synchronize deferred task */ | ||
1380 | acpi_os_wait_events_complete(NULL); | ||
1381 | /* deferred task may reinsert timer */ | ||
1382 | del_timer_sync(&(tz->timer)); | ||
1383 | |||
1384 | status = acpi_remove_notify_handler(tz->handle, | ||
1385 | ACPI_DEVICE_NOTIFY, acpi_thermal_notify); | ||
1386 | if (ACPI_FAILURE(status)) | ||
1387 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1388 | "Error removing notify handler\n")); | ||
1389 | |||
1390 | /* Terminate policy */ | ||
1391 | if (tz->trips.passive.flags.valid | ||
1392 | && tz->trips.passive.flags.enabled) { | ||
1393 | tz->trips.passive.flags.enabled = 0; | ||
1394 | acpi_thermal_passive(tz); | ||
1395 | } | ||
1396 | if (tz->trips.active[0].flags.valid | ||
1397 | && tz->trips.active[0].flags.enabled) { | ||
1398 | tz->trips.active[0].flags.enabled = 0; | ||
1399 | acpi_thermal_active(tz); | ||
1400 | } | ||
1401 | |||
1402 | acpi_thermal_remove_fs(device); | ||
1403 | |||
1404 | kfree(tz); | ||
1405 | return_VALUE(0); | ||
1406 | } | ||
1407 | |||
1408 | |||
1409 | static int __init | ||
1410 | acpi_thermal_init (void) | ||
1411 | { | ||
1412 | int result = 0; | ||
1413 | |||
1414 | ACPI_FUNCTION_TRACE("acpi_thermal_init"); | ||
1415 | |||
1416 | acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); | ||
1417 | if (!acpi_thermal_dir) | ||
1418 | return_VALUE(-ENODEV); | ||
1419 | acpi_thermal_dir->owner = THIS_MODULE; | ||
1420 | |||
1421 | result = acpi_bus_register_driver(&acpi_thermal_driver); | ||
1422 | if (result < 0) { | ||
1423 | remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir); | ||
1424 | return_VALUE(-ENODEV); | ||
1425 | } | ||
1426 | |||
1427 | return_VALUE(0); | ||
1428 | } | ||
1429 | |||
1430 | |||
1431 | static void __exit | ||
1432 | acpi_thermal_exit (void) | ||
1433 | { | ||
1434 | ACPI_FUNCTION_TRACE("acpi_thermal_exit"); | ||
1435 | |||
1436 | acpi_bus_unregister_driver(&acpi_thermal_driver); | ||
1437 | |||
1438 | remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir); | ||
1439 | |||
1440 | return_VOID; | ||
1441 | } | ||
1442 | |||
1443 | |||
1444 | module_init(acpi_thermal_init); | ||
1445 | module_exit(acpi_thermal_exit); | ||