File: /usr/src/linux/drivers/acpi/ospm/thermal/tz.c
1 /*****************************************************************************
2 *
3 * Module Name: tz.c
4 * $Revision: 40 $
5 *
6 *****************************************************************************/
7
8 /*
9 * Copyright (C) 2000, 2001 Andrew Grover
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 /*
27 * TBD: 1. Finish /proc interface (threshold values, _SCP changes, etc.)
28 * 2. Update policy for ACPI 2.0 compliance
29 * 3. Check for all required methods prior to enabling a threshold
30 * 4. Support for multiple processors in a zone (passive cooling devices)
31 */
32
33 #include <acpi.h>
34 #include <bm.h>
35 #include "tz.h"
36
37 #define _COMPONENT ACPI_THERMAL
38 MODULE_NAME ("tz")
39
40
41 /****************************************************************************
42 * Internal Functions
43 ****************************************************************************/
44
45 /****************************************************************************
46 *
47 * FUNCTION: tz_print
48 *
49 * PARAMETERS:
50 *
51 * RETURN:
52 *
53 * DESCRIPTION: Prints out information on a specific thermal zone.
54 *
55 ****************************************************************************/
56
57 void
58 tz_print (
59 TZ_CONTEXT *thermal_zone)
60 {
61 #ifdef ACPI_DEBUG
62 acpi_buffer buffer;
63 u32 i,j = 0;
64 TZ_THRESHOLD *threshold = NULL;
65
66 PROC_NAME("tz_print");
67
68 if (!thermal_zone) {
69 return;
70 }
71
72 buffer.length = 256;
73 buffer.pointer = acpi_os_callocate(buffer.length);
74 if (!buffer.pointer) {
75 return;
76 }
77
78 /*
79 * Get the full pathname for this ACPI object.
80 */
81 acpi_get_name(thermal_zone->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
82
83 /*
84 * Print out basic thermal zone information.
85 */
86 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
87 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Thermal_zone[%02x]:[%p] %s\n", thermal_zone->device_handle, thermal_zone->acpi_handle, buffer.pointer));
88 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| temperature[%d] state[%08x]\n", thermal_zone->policy.temperature, thermal_zone->policy.state));
89 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| cooling_mode[%08x] polling_freq[%d]\n", thermal_zone->policy.cooling_mode, thermal_zone->policy.polling_freq));
90
91 for (i=0; i<thermal_zone->policy.threshold_list.count; i++) {
92
93 threshold = &(thermal_zone->policy.threshold_list.thresholds[i]);
94
95 switch (threshold->type) {
96 case TZ_THRESHOLD_CRITICAL:
97 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| critical[%d]\n", threshold->temperature));
98 break;
99 case TZ_THRESHOLD_PASSIVE:
100 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| passive[%d]: tc1[%d] tc2[%d] tsp[%d]\n", threshold->temperature, thermal_zone->policy.passive.tc1, thermal_zone->policy.passive.tc2, thermal_zone->policy.passive.tsp));
101 break;
102 case TZ_THRESHOLD_ACTIVE:
103 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| active[%d]: index[%d]\n", threshold->temperature, threshold->index));
104 break;
105 default:
106 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| unknown[%d]\n", threshold->temperature));
107 break;
108 }
109
110 if (threshold->cooling_devices.count > 0) {
111 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| cooling_devices"));
112 for (j=0; (j<threshold->cooling_devices.count && j<10); j++) {
113 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "[%02x]", threshold->cooling_devices.handles[j]));
114 }
115
116 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "\n"));
117 }
118 }
119
120 ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
121
122 acpi_os_free(buffer.pointer);
123 #endif /*ACPI_DEBUG*/
124
125 return;
126 }
127
128
129 /****************************************************************************
130 *
131 * FUNCTION: tz_get_temperaturee
132 *
133 * PARAMETERS:
134 *
135 * RETURN:
136 *
137 * DESCRIPTION:
138 *
139 ****************************************************************************/
140
141 acpi_status
142 tz_get_temperature (
143 TZ_CONTEXT *thermal_zone,
144 u32 *temperature)
145 {
146 acpi_status status = AE_OK;
147
148 FUNCTION_TRACE("tz_get_temperature");
149
150 if (!thermal_zone || !temperature) {
151 return_ACPI_STATUS(AE_BAD_PARAMETER);
152 }
153
154 /*
155 * Evaluate the _TMP driver method to get the current temperature.
156 */
157 status = bm_evaluate_simple_integer(thermal_zone->acpi_handle,
158 "_TMP", temperature);
159
160 return_ACPI_STATUS(status);
161 }
162
163
164 /****************************************************************************
165 *
166 * FUNCTION: tz_set_cooling_preference
167 *
168 * PARAMETERS:
169 *
170 * RETURN:
171 *
172 * DESCRIPTION:
173 *
174 ****************************************************************************/
175
176 acpi_status
177 tz_set_cooling_preference (
178 TZ_CONTEXT *thermal_zone,
179 TZ_COOLING_MODE cooling_mode)
180 {
181 acpi_status status = AE_OK;
182 acpi_object_list arg_list;
183 acpi_object arg0;
184
185 FUNCTION_TRACE("tz_set_cooling_preference");
186
187 if (!thermal_zone || ((cooling_mode != TZ_COOLING_MODE_ACTIVE) &&
188 (cooling_mode != TZ_COOLING_MODE_PASSIVE))) {
189 return_ACPI_STATUS(AE_BAD_PARAMETER);
190 }
191
192 /*
193 * Build the argument list, which simply consists of the current
194 * cooling preference.
195 */
196 MEMSET(&arg_list, 0, sizeof(acpi_object));
197 arg_list.count = 1;
198 arg_list.pointer = &arg0;
199
200 MEMSET(&arg0, 0, sizeof(acpi_object));
201 arg0.type = ACPI_TYPE_INTEGER;
202 arg0.integer.value = cooling_mode;
203
204 /*
205 * Evaluate "_SCP" - setting the new cooling preference.
206 */
207 status = acpi_evaluate_object(thermal_zone->acpi_handle, "_SCP",
208 &arg_list, NULL);
209
210 return_ACPI_STATUS(status);
211 }
212
213
214 /***************************************************************************
215 *
216 * FUNCTION: tz_get_single_threshold
217 *
218 * PARAMETERS:
219 *
220 * RETURN:
221 *
222 * DESCRIPTION:
223 *
224 ****************************************************************************/
225
226 acpi_status
227 tz_get_single_threshold (
228 TZ_CONTEXT *thermal_zone,
229 TZ_THRESHOLD *threshold)
230 {
231 acpi_status status = AE_OK;
232
233 FUNCTION_TRACE("tz_get_single_threshold");
234
235 if (!thermal_zone || !threshold) {
236 return_ACPI_STATUS(AE_BAD_PARAMETER);
237 }
238
239 switch (threshold->type) {
240
241 /*
242 * Critical Threshold:
243 * -------------------
244 */
245 case TZ_THRESHOLD_CRITICAL:
246 threshold->index = 0;
247 threshold->cooling_devices.count = 0;
248 status = bm_evaluate_simple_integer(
249 thermal_zone->acpi_handle, "_CRT",
250 &(threshold->temperature));
251 break;
252
253 /*
254 * Passive Threshold:
255 * ------------------
256 * Evaluate _PSV to get the threshold temperature and _PSL to get
257 * references to all passive cooling devices.
258 */
259 case TZ_THRESHOLD_PASSIVE:
260 threshold->index = 0;
261 threshold->cooling_devices.count = 0;
262 status = bm_evaluate_simple_integer(
263 thermal_zone->acpi_handle, "_PSV",
264 &(threshold->temperature));
265 if (ACPI_SUCCESS(status)) {
266 status = bm_evaluate_reference_list(
267 thermal_zone->acpi_handle, "_PSL",
268 &(threshold->cooling_devices));
269 }
270
271 break;
272
273 /*
274 * Active Thresholds:
275 * ------------------
276 * Evaluate _ACx to get all threshold temperatures, and _ALx to get
277 * references to all passive cooling devices.
278 */
279 case TZ_THRESHOLD_ACTIVE:
280 {
281 char object_name[5] = {'_','A', 'C',
282 ('0'+threshold->index),'\0'};
283 status = bm_evaluate_simple_integer(
284 thermal_zone->acpi_handle, object_name,
285 &(threshold->temperature));
286 if (ACPI_SUCCESS(status)) {
287 object_name[2] = 'L';
288 status = bm_evaluate_reference_list(
289 thermal_zone->acpi_handle,
290 object_name,
291 &(threshold->cooling_devices));
292 }
293 }
294 break;
295
296 default:
297 status = AE_SUPPORT;
298 break;
299 }
300
301 return_ACPI_STATUS(status);
302 }
303
304
305 /****************************************************************************
306 *
307 * FUNCTION: tz_get_thresholds
308 *
309 * PARAMETERS: thermal_zone - Identifies the thermal zone to parse.
310 * buffer - Output buffer.
311 *
312 * RETURN: acpi_status result code.
313 *
314 * DESCRIPTION: Builds a TZ_THRESHOLD_LIST structure containing information
315 * on all thresholds for a given thermal zone.
316 *
317 * NOTES: The current design limits the number of cooling devices
318 * per theshold to the value specified by BM_MAX_HANDLES.
319 * This simplifies parsing of thresholds by allowing a maximum
320 * threshold list size to be computed (and enforced) -- which
321 * allows all thresholds to be parsed in a single pass (since
322 * memory must be contiguous when returned in the acpi_buffer).
323 *
324 ****************************************************************************/
325
326 acpi_status
327 tz_get_thresholds (
328 TZ_CONTEXT *thermal_zone,
329 TZ_THRESHOLD_LIST *threshold_list)
330 {
331 acpi_status status = AE_OK;
332 TZ_THRESHOLD *threshold = NULL;
333 u32 i = 0;
334
335 FUNCTION_TRACE("tz_get_thresholds");
336
337 if (!thermal_zone || !threshold_list) {
338 return_ACPI_STATUS(AE_BAD_PARAMETER);
339 }
340
341 threshold_list->count = 0;
342
343 /*
344 * Critical threshold:
345 * -------------------
346 * Every thermal zone must have one!
347 */
348 threshold = &(threshold_list->thresholds[threshold_list->count]);
349 threshold->type = TZ_THRESHOLD_CRITICAL;
350
351 status = tz_get_single_threshold(thermal_zone, threshold);
352 if (ACPI_SUCCESS(status)) {
353 (threshold_list->count)++;
354 }
355 else {
356 return_ACPI_STATUS(status);
357 }
358
359
360 /*
361 * Passive threshold:
362 * ------------------
363 */
364 threshold = &(threshold_list->thresholds[threshold_list->count]);
365 threshold->type = TZ_THRESHOLD_PASSIVE;
366
367 status = tz_get_single_threshold(thermal_zone, threshold);
368 if (ACPI_SUCCESS(status)) {
369 (threshold_list->count)++;
370 }
371
372 /*
373 * Active threshold:
374 * -----------------
375 * Note that active thresholds are sorted by index (e.g. _AC0,
376 * _AC1, ...), and thus from highest (_AC0) to lowest (_AC9)
377 * temperature.
378 */
379 for (i = 0; i < TZ_MAX_ACTIVE_THRESHOLDS; i++) {
380
381 threshold = &(threshold_list->thresholds[threshold_list->count]);
382 threshold->type = TZ_THRESHOLD_ACTIVE;
383 threshold->index = i;
384
385 status = tz_get_single_threshold(thermal_zone, threshold);
386 if (ACPI_SUCCESS(status)) {
387 (threshold_list->count)++;
388 }
389 else {
390 threshold->type = TZ_THRESHOLD_UNKNOWN;
391 threshold->index = 0;
392 thermal_zone->policy.active.threshold_count = i;
393 break;
394 }
395 }
396
397 return_ACPI_STATUS(AE_OK);
398 }
399
400
401 /****************************************************************************
402 *
403 * FUNCTION: tz_add_device
404 *
405 * PARAMETERS: <none>
406 *
407 * RETURN:
408 *
409 * DESCRIPTION:
410 *
411 ****************************************************************************/
412
413 acpi_status
414 tz_add_device (
415 BM_HANDLE device_handle,
416 void **context)
417 {
418 acpi_status status = AE_OK;
419 TZ_CONTEXT *thermal_zone = NULL;
420 BM_DEVICE *device = NULL;
421 acpi_handle tmp_handle = NULL;
422 static u32 zone_count = 0;
423
424 FUNCTION_TRACE("tz_add_device");
425
426 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding thermal zone [%02x].\n", device_handle));
427
428 if (!context || *context) {
429 ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Invalid context for device [%02x].\n", device_handle));
430 return_ACPI_STATUS(AE_BAD_PARAMETER);
431 }
432
433 /*
434 * Get information on this device.
435 */
436 status = bm_get_device_info(device_handle, &device);
437 if (ACPI_FAILURE(status)) {
438 return_ACPI_STATUS(status);
439 }
440
441 /*
442 * Allocate a new Thermal Zone device.
443 */
444 thermal_zone = acpi_os_callocate(sizeof(TZ_CONTEXT));
445 if (!thermal_zone) {
446 return AE_NO_MEMORY;
447 }
448
449 thermal_zone->device_handle = device->handle;
450 thermal_zone->acpi_handle = device->acpi_handle;
451
452 /* TBD: How to manage 'uid' when zones are Pn_p? */
453 sprintf(thermal_zone->uid, "%d", zone_count++);
454
455 /*
456 * _TMP?
457 * -----
458 */
459 status = acpi_get_handle(thermal_zone->acpi_handle, "_TMP",
460 &tmp_handle);
461 if (ACPI_FAILURE(status)) {
462 goto end;
463 }
464
465 /*
466 * Initialize Policy:
467 * ------------------
468 * TBD: Move all thermal zone policy to user-mode daemon...
469 */
470 status = tz_policy_add_device(thermal_zone);
471 if (ACPI_FAILURE(status)) {
472 goto end;
473 }
474
475 status = tz_osl_add_device(thermal_zone);
476 if (ACPI_FAILURE(status)) {
477 goto end;
478 }
479
480 *context = thermal_zone;
481
482 tz_print(thermal_zone);
483
484 end:
485 if (ACPI_FAILURE(status)) {
486 acpi_os_free(thermal_zone);
487 }
488
489 return_ACPI_STATUS(status);
490 }
491
492
493 /****************************************************************************
494 *
495 * FUNCTION: tz_remove_device
496 *
497 * PARAMETERS: <none>
498 *
499 * RETURN:
500 *
501 * DESCRIPTION:
502 *
503 ****************************************************************************/
504
505 acpi_status
506 tz_remove_device (
507 void **context)
508 {
509 acpi_status status = AE_OK;
510 TZ_CONTEXT *thermal_zone = NULL;
511
512 FUNCTION_TRACE("tz_remove_device");
513
514 if (!context || !*context) {
515 return_ACPI_STATUS(AE_BAD_PARAMETER);
516 }
517
518 thermal_zone = (TZ_CONTEXT*)(*context);
519
520 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing thermal zone [%02x].\n", thermal_zone->device_handle));
521
522 status = tz_osl_remove_device(thermal_zone);
523
524 /*
525 * Remove Policy:
526 * --------------
527 * TBD: Move all thermal zone policy to user-mode daemon...
528 */
529 status = tz_policy_remove_device(thermal_zone);
530 if (ACPI_FAILURE(status)) {
531 return_ACPI_STATUS(status);
532 }
533
534 acpi_os_free(thermal_zone);
535
536 return_ACPI_STATUS(status);
537 }
538
539
540 /****************************************************************************
541 * External Functions
542 ****************************************************************************/
543
544 /****************************************************************************
545 *
546 * FUNCTION: tz_initialize
547 *
548 * PARAMETERS: <none>
549 *
550 * RETURN:
551 *
552 * DESCRIPTION:
553 *
554 ****************************************************************************/
555
556 acpi_status
557 tz_initialize (void)
558 {
559 acpi_status status = AE_OK;
560 BM_DEVICE_ID criteria;
561 BM_DRIVER driver;
562
563 FUNCTION_TRACE("tz_initialize");
564
565 MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
566 MEMSET(&driver, 0, sizeof(BM_DRIVER));
567
568 /*
569 * Register driver for thermal zone devices.
570 */
571 criteria.type = BM_TYPE_THERMAL_ZONE;
572
573 driver.notify = &tz_notify;
574 driver.request = &tz_request;
575
576 status = bm_register_driver(&criteria, &driver);
577
578 return_ACPI_STATUS(status);
579 }
580
581
582 /****************************************************************************
583 *
584 * FUNCTION: tz_terminate
585 *
586 * PARAMETERS: <none>
587 *
588 * RETURN:
589 *
590 * DESCRIPTION:
591 *
592 ****************************************************************************/
593
594 acpi_status
595 tz_terminate (void)
596 {
597 acpi_status status = AE_OK;
598 BM_DEVICE_ID criteria;
599 BM_DRIVER driver;
600
601 FUNCTION_TRACE("tz_terminate");
602
603 MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
604 MEMSET(&driver, 0, sizeof(BM_DRIVER));
605
606 /*
607 * Unregister driver for thermal zone devices.
608 */
609 criteria.type = BM_TYPE_THERMAL_ZONE;
610
611 driver.notify = &tz_notify;
612 driver.request = &tz_request;
613
614 status = bm_unregister_driver(&criteria, &driver);
615
616 return_ACPI_STATUS(status);
617 }
618
619
620 /****************************************************************************
621 *
622 * FUNCTION: tz_notify
623 *
624 * PARAMETERS: <none>
625 *
626 * RETURN:
627 *
628 * DESCRIPTION:
629 *
630 ****************************************************************************/
631 acpi_status
632 tz_notify (
633 BM_NOTIFY notify_type,
634 BM_HANDLE device_handle,
635 void **context)
636 {
637 acpi_status status = AE_OK;
638 TZ_CONTEXT *thermal_zone = NULL;
639
640 FUNCTION_TRACE("tz_notify");
641
642 if (!context) {
643 return_ACPI_STATUS(AE_BAD_PARAMETER);
644 }
645
646 thermal_zone = (TZ_CONTEXT*)*context;
647
648 switch (notify_type) {
649
650 case BM_NOTIFY_DEVICE_ADDED:
651 status = tz_add_device(device_handle, context);
652 break;
653
654 case BM_NOTIFY_DEVICE_REMOVED:
655 status = tz_remove_device(context);
656 break;
657
658 case TZ_NOTIFY_TEMPERATURE_CHANGE:
659 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Temperature (_TMP) change event detected.\n"));
660 /* -------------------------------------------- */
661 /* TBD: Remove when policy moves to user-mode. */
662 tz_policy_check(*context);
663 /* -------------------------------------------- */
664 status = tz_get_temperature(thermal_zone,
665 &(thermal_zone->policy.temperature));
666 if (ACPI_SUCCESS(status)) {
667 status = tz_osl_generate_event(notify_type,
668 thermal_zone);
669 }
670 break;
671
672 case TZ_NOTIFY_THRESHOLD_CHANGE:
673 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Threshold (_SCP) change event detected.\n"));
674 /* -------------------------------------------- */
675 /* TBD: Remove when policy moves to user-mode. */
676 status = tz_policy_remove_device(thermal_zone);
677 if (ACPI_SUCCESS(status)) {
678 status = tz_policy_add_device(thermal_zone);
679 }
680 /* -------------------------------------------- */
681 status = tz_osl_generate_event(notify_type, thermal_zone);
682 break;
683
684 case TZ_NOTIFY_DEVICE_LISTS_CHANGE:
685 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Device lists (_ALx, _PSL, _TZD) change event detected.\n"));
686 /* -------------------------------------------- */
687 /* TBD: Remove when policy moves to user-mode. */
688 status = tz_policy_remove_device(thermal_zone);
689 if (ACPI_SUCCESS(status)) {
690 status = tz_policy_add_device(thermal_zone);
691 }
692 /* -------------------------------------------- */
693 status = tz_osl_generate_event(notify_type, thermal_zone);
694 break;
695
696 default:
697 status = AE_SUPPORT;
698 break;
699 }
700
701 return_ACPI_STATUS(status);
702 }
703
704
705 /****************************************************************************
706 *
707 * FUNCTION: tz_request
708 *
709 * PARAMETERS:
710 *
711 * RETURN: Exception code.
712 *
713 * DESCRIPTION:
714 *
715 ****************************************************************************/
716
717 acpi_status
718 tz_request (
719 BM_REQUEST *request,
720 void *context)
721 {
722 acpi_status status = AE_OK;
723 TZ_CONTEXT *thermal_zone = NULL;
724
725 FUNCTION_TRACE("tz_request");
726
727 /*
728 * Must have a valid request structure and context.
729 */
730 if (!request || !context) {
731 return_ACPI_STATUS(AE_BAD_PARAMETER);
732 }
733
734 thermal_zone = (TZ_CONTEXT*)context;
735
736 /*
737 * Handle request:
738 * ---------------
739 */
740 switch (request->command) {
741
742 default:
743 status = AE_SUPPORT;
744 break;
745 }
746
747 request->status = status;
748
749 return_ACPI_STATUS(status);
750 }
751