1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Solaris x86 ACPI ThermalZone Monitor
29 */
30
31
32 #include <sys/errno.h>
33 #include <sys/conf.h>
34 #include <sys/modctl.h>
35 #include <sys/open.h>
36 #include <sys/stat.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/ksynch.h>
40 #include <sys/uadmin.h>
41 #include <sys/acpi/acpi.h>
42 #include <sys/acpica.h>
43 #include <sys/sdt.h>
44
45 #include "tzmon.h"
46
47
48 #define TZMON_ENUM_TRIP_POINTS 1
49 #define TZMON_ENUM_DEV_LISTS 2
50 #define TZMON_ENUM_ALL (TZMON_ENUM_TRIP_POINTS | TZMON_ENUM_DEV_LISTS)
51
52 /*
53 * TZ_TASKQ_NAME_LEN is precisely the length of the string "AcpiThermalMonitor"
54 * plus a two-digit instance number plus a NULL. If the taskq name is changed
55 * (particularly if it is lengthened), then this value needs to change.
56 */
57 #define TZ_TASKQ_NAME_LEN 21
58
59 /*
60 * Kelvin to Celsius conversion
61 * The formula for converting degrees Kelvin to degrees Celsius is
62 * C = K - 273.15 (we round to 273.2). The unit for thermal zone
63 * temperatures is tenths of a degree Kelvin. Use tenth of a degree
64 * to convert, then make a whole number out of it.
65 */
66 #define K_TO_C(temp) (((temp) - 2732) / 10)
67
68
69 /* cb_ops or dev_ops forward declarations */
70 static int tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
71 void *arg, void **result);
72 static int tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
73 static int tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
74
75 /* other forward declarations */
76 static void tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx);
77 static void tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv);
78 static thermal_zone_t *tzmon_alloc_zone();
79 static void tzmon_free_zone_list();
80 static void tzmon_discard_buffers(thermal_zone_t *tzp);
81 static void tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp,
82 int enum_flag);
83 static ACPI_STATUS tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest,
84 void *ctx, void **rv);
85 static void tzmon_find_zones(void);
86 static void tzmon_monitor(void *ctx);
87 static void tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name);
88 static void tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name);
89 static void tzmon_eval_zone(thermal_zone_t *tzp);
90 static void tzmon_do_shutdown(void);
91
92 extern void halt(char *);
93
94 static struct cb_ops tzmon_cb_ops = {
95 nodev, /* no open routine */
96 nodev, /* no close routine */
97 nodev, /* not a block driver */
98 nodev, /* no print routine */
99 nodev, /* no dump routine */
100 nodev, /* no read routine */
101 nodev, /* no write routine */
102 nodev, /* no ioctl routine */
103 nodev, /* no devmap routine */
104 nodev, /* no mmap routine */
105 nodev, /* no segmap routine */
106 nochpoll, /* no chpoll routine */
107 ddi_prop_op,
108 0, /* not a STREAMS driver */
109 D_NEW | D_MP, /* safe for multi-thread/multi-processor */
110 };
111
112 static struct dev_ops tzmon_ops = {
113 DEVO_REV, /* devo_rev */
114 0, /* devo_refcnt */
115 tzmon_getinfo, /* devo_getinfo */
116 nulldev, /* devo_identify */
117 nulldev, /* devo_probe */
118 tzmon_attach, /* devo_attach */
119 tzmon_detach, /* devo_detach */
120 nodev, /* devo_reset */
121 &tzmon_cb_ops, /* devo_cb_ops */
122 (struct bus_ops *)0, /* devo_bus_ops */
123 NULL, /* devo_power */
124 ddi_quiesce_not_needed, /* devo_quiesce */
125 };
126
127 extern struct mod_ops mod_driverops;
128
129 static struct modldrv modldrv = {
130 &mod_driverops,
131 "ACPI Thermal Zone Monitor",
132 &tzmon_ops,
133 };
134
135 static struct modlinkage modlinkage = {
136 MODREV_1, /* MODREV_1 indicated by manual */
137 (void *)&modldrv,
138 NULL, /* termination of list of linkage structures */
139 };
140
141 /* globals for this module */
142 static dev_info_t *tzmon_dip;
143 static thermal_zone_t *zone_list;
144 static int zone_count;
145 static kmutex_t zone_list_lock;
146 static kcondvar_t zone_list_condvar;
147
148
149 /*
150 * _init, _info, and _fini support loading and unloading the driver.
151 */
152 int
_init(void)153 _init(void)
154 {
155 return (mod_install(&modlinkage));
156 }
157
158
159 int
_info(struct modinfo * modinfop)160 _info(struct modinfo *modinfop)
161 {
162 return (mod_info(&modlinkage, modinfop));
163 }
164
165
166 int
_fini(void)167 _fini(void)
168 {
169 return (mod_remove(&modlinkage));
170 }
171
172
173 static int
tzmon_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)174 tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
175 {
176 if (cmd != DDI_ATTACH)
177 return (DDI_FAILURE);
178
179 if (tzmon_dip != NULL)
180 return (DDI_FAILURE);
181
182 /*
183 * Check to see if ACPI CA services are available
184 */
185 if (AcpiSubsystemStatus() != AE_OK)
186 return (DDI_FAILURE);
187
188 mutex_init(&zone_list_lock, NULL, MUTEX_DRIVER, NULL);
189 cv_init(&zone_list_condvar, NULL, CV_DRIVER, NULL);
190
191 tzmon_find_zones();
192 mutex_enter(&zone_list_lock);
193 if (zone_count < 1) {
194 mutex_exit(&zone_list_lock);
195 mutex_destroy(&zone_list_lock);
196 cv_destroy(&zone_list_condvar);
197 return (DDI_FAILURE);
198 }
199 mutex_exit(&zone_list_lock);
200
201 if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 0,
202 DDI_PSEUDO, 0) == DDI_FAILURE) {
203 tzmon_free_zone_list();
204 mutex_destroy(&zone_list_lock);
205 cv_destroy(&zone_list_condvar);
206 return (DDI_FAILURE);
207 }
208
209 tzmon_dip = dip;
210
211 ddi_report_dev(dip);
212
213 return (DDI_SUCCESS);
214 }
215
216
217 /*ARGSUSED*/
218 static int
tzmon_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)219 tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
220 {
221 int error;
222
223 switch (infocmd) {
224 case DDI_INFO_DEVT2DEVINFO:
225 *result = tzmon_dip;
226 if (tzmon_dip == NULL)
227 error = DDI_FAILURE;
228 else
229 error = DDI_SUCCESS;
230 break;
231 case DDI_INFO_DEVT2INSTANCE:
232 *result = 0;
233 error = DDI_SUCCESS;
234 break;
235 default:
236 *result = NULL;
237 error = DDI_FAILURE;
238 }
239
240 return (error);
241 }
242
243
244 static int
tzmon_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)245 tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
246 {
247 thermal_zone_t *tzp = zone_list;
248
249 if (cmd != DDI_DETACH)
250 return (DDI_FAILURE);
251
252 /* free allocated thermal zone name(s) */
253 while (tzp != NULL) {
254 AcpiOsFree(tzp->zone_name);
255 tzp = tzp->next;
256 }
257
258 /* discard zone list assets */
259 tzmon_free_zone_list();
260
261 ddi_remove_minor_node(dip, NULL);
262 tzmon_dip = NULL;
263
264 mutex_destroy(&zone_list_lock);
265 cv_destroy(&zone_list_condvar);
266
267 return (DDI_SUCCESS);
268 }
269
270
271 /*
272 * tzmon_notify_zone
273 * Thermal zone notification handler.
274 */
275 static void
tzmon_notify_zone(ACPI_HANDLE obj,UINT32 val,void * ctx)276 tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx)
277 {
278 thermal_zone_t *tzp = (thermal_zone_t *)ctx;
279
280 switch (val) {
281 case 0x80: /* Thermal Zone status changed */
282 tzmon_eval_zone(tzp);
283 break;
284 case 0x81: /* Thermal Zone trip points changed */
285 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_TRIP_POINTS);
286 break;
287 case 0x82: /* Device Lists changed */
288 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_DEV_LISTS);
289 break;
290 case 0x83: /* Thermal Relationship Table changed */
291 /* not handling _TRT objects, so not handling this event */
292 DTRACE_PROBE1(trt__change, char *, (char *)tzp->zone_name);
293 break;
294 default:
295 break;
296 }
297 }
298
299
300 /*
301 * tzmon_eval_int
302 * Evaluate the object/method as an integer.
303 */
304 static void
tzmon_eval_int(ACPI_HANDLE obj,char * method,int * rv)305 tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv)
306 {
307
308 if (acpica_eval_int(obj, method, rv) != AE_OK)
309 *rv = -1;
310 }
311
312
313 /*
314 * tzmon_alloc_zone
315 * Allocate memory for the zone structure and initialize it lock mutex.
316 */
317 static thermal_zone_t *
tzmon_alloc_zone()318 tzmon_alloc_zone()
319 {
320 thermal_zone_t *tzp;
321
322 tzp = kmem_zalloc(sizeof (thermal_zone_t), KM_SLEEP);
323 mutex_init(&tzp->lock, NULL, MUTEX_DRIVER, NULL);
324
325 return (tzp);
326 }
327
328
329 /*
330 * tzmon_free_zone_list
331 * Free the zone list, either because attach failed or detach initiated.
332 */
333 static void
tzmon_free_zone_list()334 tzmon_free_zone_list()
335 {
336 thermal_zone_t *tzp = zone_list;
337
338 while (tzp != NULL) {
339 thermal_zone_t *next;
340
341 mutex_enter(&tzp->lock);
342
343 /*
344 * Remove the notify handler for the zone. Not much to
345 * do if this fails (since we are on our way out), so
346 * just ignore failure.
347 */
348 (void) AcpiRemoveNotifyHandler(tzp->obj, ACPI_DEVICE_NOTIFY,
349 tzmon_notify_zone);
350
351 /* Shut down monitor thread, if running */
352 if (tzp->taskq != NULL) {
353 tzp->polling_period = 0;
354 cv_broadcast(&zone_list_condvar);
355
356 /* Drop mutex to allow the thread to run */
357 mutex_exit(&tzp->lock);
358 ddi_taskq_destroy(tzp->taskq);
359 mutex_enter(&tzp->lock);
360 }
361
362 tzmon_discard_buffers(tzp);
363 mutex_exit(&tzp->lock);
364 mutex_destroy(&tzp->lock);
365
366 next = tzp->next;
367 kmem_free(tzp, sizeof (thermal_zone_t));
368 tzp = next;
369 }
370 }
371
372
373 static void
tzmon_discard_buffers(thermal_zone_t * tzp)374 tzmon_discard_buffers(thermal_zone_t *tzp)
375 {
376 int level;
377
378 for (level = 0; level < TZ_NUM_LEVELS; level++) {
379 if (tzp->al[level].Pointer != NULL)
380 AcpiOsFree(tzp->al[level].Pointer);
381 }
382
383 if (tzp->psl.Pointer != NULL)
384 AcpiOsFree(tzp->psl.Pointer);
385 }
386
387
388 /*
389 * tzmon_enumerate_zone
390 * Enumerates the contents of a thermal zone and updates passed-in
391 * thermal_zone or creates a new one if tzp is NULL. Newly-created
392 * zones are linked into the global zone_list.
393 */
394 static void
tzmon_enumerate_zone(ACPI_HANDLE obj,thermal_zone_t * tzp,int enum_flag)395 tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp, int enum_flag)
396 {
397 ACPI_STATUS status;
398 ACPI_BUFFER zone_name;
399 int level;
400 int instance;
401 char abuf[5];
402
403 /*
404 * Newly-created zones and existing zones both require
405 * some individual attention.
406 */
407 if (tzp == NULL) {
408 /* New zone required */
409 tzp = tzmon_alloc_zone();
410 mutex_enter(&zone_list_lock);
411 tzp->next = zone_list;
412 zone_list = tzp;
413
414 /*
415 * It is exceedingly unlikely that instance will exceed 99.
416 * However, if it does, this will cause problems when
417 * creating the taskq for this thermal zone.
418 */
419 instance = zone_count;
420 zone_count++;
421 mutex_exit(&zone_list_lock);
422 mutex_enter(&tzp->lock);
423 tzp->obj = obj;
424
425 /*
426 * Set to a low level. Will get set to the actual
427 * current power level when the thread monitor polls
428 * the current temperature.
429 */
430 tzp->current_level = 0;
431
432 /* Get the zone name in case we need to display it later */
433 zone_name.Length = ACPI_ALLOCATE_BUFFER;
434 zone_name.Pointer = NULL;
435
436 status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &zone_name);
437 ASSERT(status == AE_OK);
438
439 tzp->zone_name = zone_name.Pointer;
440
441 status = AcpiInstallNotifyHandler(obj, ACPI_DEVICE_NOTIFY,
442 tzmon_notify_zone, (void *)tzp);
443 ASSERT(status == AE_OK);
444 } else {
445 /* Existing zone - toss out allocated items */
446 mutex_enter(&tzp->lock);
447 ASSERT(tzp->obj == obj);
448
449 if (enum_flag & TZMON_ENUM_DEV_LISTS)
450 tzmon_discard_buffers(tzp);
451 }
452
453 if (enum_flag & TZMON_ENUM_TRIP_POINTS) {
454 for (level = 0; level < TZ_NUM_LEVELS; level++) {
455 (void) snprintf(abuf, 5, "_AC%d", level);
456 tzmon_eval_int(obj, abuf, &tzp->ac[level]);
457
458 }
459
460 tzmon_eval_int(obj, "_CRT", &tzp->crt);
461 tzmon_eval_int(obj, "_HOT", &tzp->hot);
462 tzmon_eval_int(obj, "_PSV", &tzp->psv);
463 }
464
465 if (enum_flag & TZMON_ENUM_DEV_LISTS) {
466 for (level = 0; level < TZ_NUM_LEVELS; level++) {
467 if (tzp->ac[level] == -1) {
468 tzp->al[level].Length = 0;
469 tzp->al[level].Pointer = NULL;
470 } else {
471 (void) snprintf(abuf, 5, "_AL%d", level);
472 tzp->al[level].Length = ACPI_ALLOCATE_BUFFER;
473 tzp->al[level].Pointer = NULL;
474 if (AcpiEvaluateObjectTyped(obj, abuf, NULL,
475 &tzp->al[level], ACPI_TYPE_PACKAGE) !=
476 AE_OK) {
477 DTRACE_PROBE2(alx__missing, int, level,
478 char *, (char *)tzp->zone_name);
479
480 tzp->al[level].Length = 0;
481 tzp->al[level].Pointer = NULL;
482 }
483 }
484 }
485
486 tzp->psl.Length = ACPI_ALLOCATE_BUFFER;
487 tzp->psl.Pointer = NULL;
488 (void) AcpiEvaluateObjectTyped(obj, "_PSL", NULL, &tzp->psl,
489 ACPI_TYPE_PACKAGE);
490 }
491
492 tzmon_eval_int(obj, "_TC1", &tzp->tc1);
493 tzmon_eval_int(obj, "_TC2", &tzp->tc2);
494 tzmon_eval_int(obj, "_TSP", &tzp->tsp);
495 tzmon_eval_int(obj, "_TZP", &tzp->tzp);
496
497 if (tzp->tzp == 0) {
498 tzp->polling_period = 0;
499 } else {
500 if (tzp->tzp < 0)
501 tzp->polling_period = TZ_DEFAULT_PERIOD;
502 else
503 tzp->polling_period = tzp->tzp/10;
504
505 /* start monitor thread if needed */
506 if (tzp->taskq == NULL) {
507 char taskq_name[TZ_TASKQ_NAME_LEN];
508
509 (void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN,
510 "AcpiThermalMonitor%02d", instance);
511 tzp->taskq = ddi_taskq_create(tzmon_dip,
512 taskq_name, 1, TASKQ_DEFAULTPRI, 0);
513 if (tzp->taskq == NULL) {
514 tzp->polling_period = 0;
515 cmn_err(CE_WARN, "tzmon: could not create "
516 "monitor thread for thermal zone %s - "
517 "monitor by notify only",
518 (char *)tzp->zone_name);
519 } else {
520 (void) ddi_taskq_dispatch(tzp->taskq,
521 tzmon_monitor, tzp, DDI_SLEEP);
522 }
523 }
524 }
525
526 mutex_exit(&tzp->lock);
527 }
528
529
530 /*
531 * tzmon_zone_callback
532 * Enumerate the thermal zone if it has a _TMP (current thermal zone
533 * operating temperature) method.
534 */
535 /*ARGSUSED*/
536 static ACPI_STATUS
tzmon_zone_callback(ACPI_HANDLE obj,UINT32 nest,void * ctx,void ** rv)537 tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv)
538 {
539 ACPI_HANDLE tmpobj;
540
541 /*
542 * We get both ThermalZone() and Scope(\_TZ) objects here;
543 * look for _TMP (without which a zone is invalid) to pick
544 * between them (and ignore invalid zones)
545 */
546 if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) {
547 tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL);
548 }
549
550 return (AE_OK);
551 }
552
553
554 /*
555 * tzmon_find_zones
556 * Find all of the thermal zones by calling a ACPICA function that
557 * walks the ACPI namespace and invokes a callback for each thermal
558 * object found.
559 */
560 static void
tzmon_find_zones()561 tzmon_find_zones()
562 {
563 ACPI_STATUS status;
564 int retval;
565
566 status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
567 8, tzmon_zone_callback, NULL, NULL, (void **)&retval);
568
569 ASSERT(status == AE_OK);
570 }
571
572
573 /*
574 * tzmon_monitor
575 * Run as a separate thread, this wakes according to polling period and
576 * checks particular objects in the thermal zone. One instance per
577 * thermal zone.
578 */
579 static void
tzmon_monitor(void * ctx)580 tzmon_monitor(void *ctx)
581 {
582 thermal_zone_t *tzp = (thermal_zone_t *)ctx;
583 clock_t ticks;
584
585 do {
586 /* Check out the zone */
587 tzmon_eval_zone(tzp);
588
589 /* Go back to sleep */
590 mutex_enter(&tzp->lock);
591 ticks = drv_usectohz(tzp->polling_period * 1000000);
592 if (ticks > 0)
593 (void) cv_reltimedwait(&zone_list_condvar,
594 &tzp->lock, ticks, TR_CLOCK_TICK);
595 mutex_exit(&tzp->lock);
596 } while (ticks > 0);
597 }
598
599
600 /*
601 * tzmon_set_power_device
602 */
603 static void
tzmon_set_power_device(ACPI_HANDLE dev,int on_off,char * tz_name)604 tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name)
605 {
606 ACPI_BUFFER rb;
607 ACPI_OBJECT *pr0;
608 ACPI_STATUS status;
609 int i;
610
611 rb.Length = ACPI_ALLOCATE_BUFFER;
612 rb.Pointer = NULL;
613 status = AcpiEvaluateObjectTyped(dev, "_PR0", NULL, &rb,
614 ACPI_TYPE_PACKAGE);
615 if (status != AE_OK) {
616 DTRACE_PROBE2(alx__error, int, 2, char *, tz_name);
617 return;
618 }
619
620 pr0 = ((ACPI_OBJECT *)rb.Pointer);
621 for (i = 0; i < pr0->Package.Count; i++) {
622 status = AcpiEvaluateObject(
623 pr0->Package.Elements[i].Reference.Handle,
624 on_off ? "_ON" : "_OFF", NULL, NULL);
625 if (status != AE_OK) {
626 DTRACE_PROBE2(alx__error, int, 4, char *, tz_name);
627 }
628 }
629
630 AcpiOsFree(rb.Pointer);
631 }
632
633
634 /*
635 * tzmon_set_power
636 * Turn on or turn off all devices in the supplied list.
637 */
638 static void
tzmon_set_power(ACPI_BUFFER devlist,int on_off,char * tz_name)639 tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name)
640 {
641 ACPI_OBJECT *devs;
642 int i;
643
644 devs = ((ACPI_OBJECT *)devlist.Pointer);
645 if (devs->Type != ACPI_TYPE_PACKAGE) {
646 DTRACE_PROBE2(alx__error, int, 1, char *, tz_name);
647 return;
648 }
649
650 for (i = 0; i < devs->Package.Count; i++)
651 tzmon_set_power_device(
652 devs->Package.Elements[i].Reference.Handle, on_off,
653 tz_name);
654 }
655
656
657 /*
658 * tzmon_eval_zone
659 * Evaluate the current conditions within the thermal zone.
660 */
661 static void
tzmon_eval_zone(thermal_zone_t * tzp)662 tzmon_eval_zone(thermal_zone_t *tzp)
663 {
664 int tmp, new_level, level;
665
666 mutex_enter(&tzp->lock);
667
668 /* get the current temperature from ACPI */
669 tzmon_eval_int(tzp->obj, "_TMP", &tmp);
670 DTRACE_PROBE4(tz__temp, int, tmp, int, tzp->crt, int, tzp->hot,
671 char *, (char *)tzp->zone_name);
672
673 /* _HOT handling */
674 if (tzp->hot > 0 && tmp >= tzp->hot) {
675 cmn_err(CE_WARN,
676 "tzmon: Thermal zone (%s) is too hot (%d C); "
677 "initiating shutdown\n",
678 (char *)tzp->zone_name, K_TO_C(tmp));
679
680 tzmon_do_shutdown();
681 }
682
683 /* _CRT handling */
684 if (tzp->crt > 0 && tmp >= tzp->crt) {
685 cmn_err(CE_WARN,
686 "tzmon: Thermal zone (%s) is critically hot (%d C); "
687 "initiating rapid shutdown\n",
688 (char *)tzp->zone_name, K_TO_C(tmp));
689
690 /* shut down (fairly) immediately */
691 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
692 }
693
694 /*
695 * use the temperature to determine whether the thermal zone
696 * is at a new active cooling threshold level
697 */
698 for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) {
699 if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) {
700 new_level = level;
701 break;
702 }
703 }
704
705 /*
706 * if the active cooling threshold has changed, turn off the
707 * devices associated with the old one and turn on the new one
708 */
709 if (tzp->current_level != new_level) {
710 if ((tzp->current_level >= 0) &&
711 (tzp->al[tzp->current_level].Length != 0))
712 tzmon_set_power(tzp->al[tzp->current_level], 0,
713 (char *)tzp->zone_name);
714
715 if ((new_level >= 0) &&
716 (tzp->al[new_level].Length != 0))
717 tzmon_set_power(tzp->al[new_level], 1,
718 (char *)tzp->zone_name);
719
720 tzp->current_level = new_level;
721 }
722
723 mutex_exit(&tzp->lock);
724 }
725
726
727 /*
728 * tzmon_do_shutdown
729 * Initiates shutdown by sending a SIGPWR signal to init.
730 */
731 static void
tzmon_do_shutdown(void)732 tzmon_do_shutdown(void)
733 {
734 proc_t *initpp;
735
736 mutex_enter(&pidlock);
737 initpp = prfind(P_INITPID);
738 mutex_exit(&pidlock);
739
740 /* if we can't find init, just halt */
741 if (initpp == NULL) {
742 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
743 }
744
745 /* graceful shutdown with inittab and all getting involved */
746 psignal(initpp, SIGPWR);
747 }
748