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, NULL }
 138 };
 139 
 140 /* globals for this module */
 141 static dev_info_t       *tzmon_dip;
 142 static thermal_zone_t   *zone_list;
 143 static int              zone_count;
 144 static kmutex_t         zone_list_lock;
 145 static kcondvar_t       zone_list_condvar;
 146 
 147 
 148 /*
 149  * _init, _info, and _fini support loading and unloading the driver.
 150  */
 151 int
 152 _init(void)
 153 {
 154         return (mod_install(&modlinkage));
 155 }
 156 
 157 
 158 int
 159 _info(struct modinfo *modinfop)
 160 {
 161         return (mod_info(&modlinkage, modinfop));
 162 }
 163 
 164 
 165 int
 166 _fini(void)
 167 {
 168         return (mod_remove(&modlinkage));
 169 }
 170 
 171 
 172 static int
 173 tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 174 {
 175         if (cmd != DDI_ATTACH)
 176                 return (DDI_FAILURE);
 177 
 178         if (tzmon_dip != NULL)
 179                 return (DDI_FAILURE);
 180 
 181         /*
 182          * Check to see if ACPI CA services are available
 183          */
 184         if (AcpiSubsystemStatus() != AE_OK)
 185                 return (DDI_FAILURE);
 186 
 187         mutex_init(&zone_list_lock, NULL, MUTEX_DRIVER, NULL);
 188         cv_init(&zone_list_condvar, NULL, CV_DRIVER, NULL);
 189 
 190         tzmon_find_zones();
 191         mutex_enter(&zone_list_lock);
 192         if (zone_count < 1) {
 193                 mutex_exit(&zone_list_lock);
 194                 mutex_destroy(&zone_list_lock);
 195                 cv_destroy(&zone_list_condvar);
 196                 return (DDI_FAILURE);
 197         }
 198         mutex_exit(&zone_list_lock);
 199 
 200         if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 0,
 201             DDI_PSEUDO, 0) == DDI_FAILURE) {
 202                 tzmon_free_zone_list();
 203                 mutex_destroy(&zone_list_lock);
 204                 cv_destroy(&zone_list_condvar);
 205                 return (DDI_FAILURE);
 206         }
 207 
 208         tzmon_dip = dip;
 209 
 210         ddi_report_dev(dip);
 211 
 212         return (DDI_SUCCESS);
 213 }
 214 
 215 
 216 /*ARGSUSED*/
 217 static int
 218 tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 219 {
 220         int error;
 221 
 222         switch (infocmd) {
 223         case DDI_INFO_DEVT2DEVINFO:
 224                 *result = tzmon_dip;
 225                 if (tzmon_dip == NULL)
 226                         error = DDI_FAILURE;
 227                 else
 228                         error = DDI_SUCCESS;
 229                 break;
 230         case DDI_INFO_DEVT2INSTANCE:
 231                 *result = 0;
 232                 error = DDI_SUCCESS;
 233                 break;
 234         default:
 235                 *result = NULL;
 236                 error = DDI_FAILURE;
 237         }
 238 
 239         return (error);
 240 }
 241 
 242 
 243 static int
 244 tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 245 {
 246         thermal_zone_t *tzp = zone_list;
 247 
 248         if (cmd != DDI_DETACH)
 249                 return (DDI_FAILURE);
 250 
 251         /* free allocated thermal zone name(s) */
 252         while (tzp != NULL) {
 253                 AcpiOsFree(tzp->zone_name);
 254                 tzp = tzp->next;
 255         }
 256 
 257         /* discard zone list assets */
 258         tzmon_free_zone_list();
 259 
 260         ddi_remove_minor_node(dip, NULL);
 261         tzmon_dip = NULL;
 262 
 263         mutex_destroy(&zone_list_lock);
 264         cv_destroy(&zone_list_condvar);
 265 
 266         return (DDI_SUCCESS);
 267 }
 268 
 269 
 270 /*
 271  * tzmon_notify_zone
 272  * Thermal zone notification handler.
 273  */
 274 static void
 275 tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx)
 276 {
 277         thermal_zone_t *tzp = (thermal_zone_t *)ctx;
 278 
 279         switch (val) {
 280         case 0x80:      /* Thermal Zone status changed */
 281                 tzmon_eval_zone(tzp);
 282                 break;
 283         case 0x81:      /* Thermal Zone trip points changed */
 284                 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_TRIP_POINTS);
 285                 break;
 286         case 0x82:      /* Device Lists changed */
 287                 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_DEV_LISTS);
 288                 break;
 289         case 0x83:      /* Thermal Relationship Table changed */
 290                 /* not handling _TRT objects, so not handling this event */
 291                 DTRACE_PROBE1(trt__change, char *, (char *)tzp->zone_name);
 292                 break;
 293         default:
 294                 break;
 295         }
 296 }
 297 
 298 
 299 /*
 300  * tzmon_eval_int
 301  * Evaluate the object/method as an integer.
 302  */
 303 static void
 304 tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv)
 305 {
 306 
 307         if (acpica_eval_int(obj, method, rv) != AE_OK)
 308                 *rv = -1;
 309 }
 310 
 311 
 312 /*
 313  * tzmon_alloc_zone
 314  * Allocate memory for the zone structure and initialize it lock mutex.
 315  */
 316 static thermal_zone_t *
 317 tzmon_alloc_zone()
 318 {
 319         thermal_zone_t *tzp;
 320 
 321         tzp = kmem_zalloc(sizeof (thermal_zone_t), KM_SLEEP);
 322         mutex_init(&tzp->lock, NULL, MUTEX_DRIVER, NULL);
 323 
 324         return (tzp);
 325 }
 326 
 327 
 328 /*
 329  * tzmon_free_zone_list
 330  * Free the zone list, either because attach failed or detach initiated.
 331  */
 332 static void
 333 tzmon_free_zone_list()
 334 {
 335         thermal_zone_t *tzp = zone_list;
 336 
 337         while (tzp != NULL) {
 338                 thermal_zone_t *next;
 339 
 340                 mutex_enter(&tzp->lock);
 341 
 342                 /*
 343                  * Remove the notify handler for the zone.  Not much to
 344                  * do if this fails (since we are on our way out), so
 345                  * just ignore failure.
 346                  */
 347                 (void) AcpiRemoveNotifyHandler(tzp->obj, ACPI_DEVICE_NOTIFY,
 348                     tzmon_notify_zone);
 349 
 350                 /* Shut down monitor thread, if running */
 351                 if (tzp->taskq != NULL) {
 352                         tzp->polling_period = 0;
 353                         cv_broadcast(&zone_list_condvar);
 354 
 355                         /* Drop mutex to allow the thread to run */
 356                         mutex_exit(&tzp->lock);
 357                         ddi_taskq_destroy(tzp->taskq);
 358                         mutex_enter(&tzp->lock);
 359                 }
 360 
 361                 tzmon_discard_buffers(tzp);
 362                 mutex_exit(&tzp->lock);
 363                 mutex_destroy(&tzp->lock);
 364 
 365                 next = tzp->next;
 366                 kmem_free(tzp, sizeof (thermal_zone_t));
 367                 tzp = next;
 368         }
 369 }
 370 
 371 
 372 static void
 373 tzmon_discard_buffers(thermal_zone_t *tzp)
 374 {
 375         int level;
 376 
 377         for (level = 0; level < TZ_NUM_LEVELS; level++) {
 378                 if (tzp->al[level].Pointer != NULL)
 379                         AcpiOsFree(tzp->al[level].Pointer);
 380         }
 381 
 382         if (tzp->psl.Pointer != NULL)
 383                 AcpiOsFree(tzp->psl.Pointer);
 384 }
 385 
 386 
 387 /*
 388  * tzmon_enumerate_zone
 389  * Enumerates the contents of a thermal zone and updates passed-in
 390  * thermal_zone or creates a new one if tzp is NULL. Newly-created
 391  * zones are linked into the global zone_list.
 392  */
 393 static void
 394 tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp, int enum_flag)
 395 {
 396         ACPI_STATUS status;
 397         ACPI_BUFFER zone_name;
 398         int     level;
 399         int     instance;
 400         char    abuf[5];
 401 
 402         /*
 403          * Newly-created zones and existing zones both require
 404          * some individual attention.
 405          */
 406         if (tzp == NULL) {
 407                 /* New zone required */
 408                 tzp = tzmon_alloc_zone();
 409                 mutex_enter(&zone_list_lock);
 410                 tzp->next = zone_list;
 411                 zone_list = tzp;
 412 
 413                 /*
 414                  * It is exceedingly unlikely that instance will exceed 99.
 415                  * However, if it does, this will cause problems when
 416                  * creating the taskq for this thermal zone.
 417                  */
 418                 instance = zone_count;
 419                 zone_count++;
 420                 mutex_exit(&zone_list_lock);
 421                 mutex_enter(&tzp->lock);
 422                 tzp->obj = obj;
 423 
 424                 /*
 425                  * Set to a low level.  Will get set to the actual
 426                  * current power level when the thread monitor polls
 427                  * the current temperature.
 428                  */
 429                 tzp->current_level = 0;
 430 
 431                 /* Get the zone name in case we need to display it later */
 432                 zone_name.Length = ACPI_ALLOCATE_BUFFER;
 433                 zone_name.Pointer = NULL;
 434 
 435                 status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &zone_name);
 436                 ASSERT(status == AE_OK);
 437 
 438                 tzp->zone_name = zone_name.Pointer;
 439 
 440                 status = AcpiInstallNotifyHandler(obj, ACPI_DEVICE_NOTIFY,
 441                     tzmon_notify_zone, (void *)tzp);
 442                 ASSERT(status == AE_OK);
 443         } else {
 444                 /* Existing zone - toss out allocated items */
 445                 mutex_enter(&tzp->lock);
 446                 ASSERT(tzp->obj == obj);
 447 
 448                 if (enum_flag & TZMON_ENUM_DEV_LISTS)
 449                         tzmon_discard_buffers(tzp);
 450         }
 451 
 452         if (enum_flag & TZMON_ENUM_TRIP_POINTS) {
 453                 for (level = 0; level < TZ_NUM_LEVELS; level++) {
 454                         (void) snprintf(abuf, 5, "_AC%d", level);
 455                         tzmon_eval_int(obj, abuf, &tzp->ac[level]);
 456 
 457                 }
 458 
 459                 tzmon_eval_int(obj, "_CRT", &tzp->crt);
 460                 tzmon_eval_int(obj, "_HOT", &tzp->hot);
 461                 tzmon_eval_int(obj, "_PSV", &tzp->psv);
 462         }
 463 
 464         if (enum_flag & TZMON_ENUM_DEV_LISTS) {
 465                 for (level = 0; level < TZ_NUM_LEVELS; level++) {
 466                         if (tzp->ac[level] == -1) {
 467                                 tzp->al[level].Length = 0;
 468                                 tzp->al[level].Pointer = NULL;
 469                         } else {
 470                                 (void) snprintf(abuf, 5, "_AL%d", level);
 471                                 tzp->al[level].Length = ACPI_ALLOCATE_BUFFER;
 472                                 tzp->al[level].Pointer = NULL;
 473                                 if (AcpiEvaluateObjectTyped(obj, abuf, NULL,
 474                                     &tzp->al[level], ACPI_TYPE_PACKAGE) !=
 475                                     AE_OK) {
 476                                         DTRACE_PROBE2(alx__missing, int, level,
 477                                             char *, (char *)tzp->zone_name);
 478 
 479                                         tzp->al[level].Length = 0;
 480                                         tzp->al[level].Pointer = NULL;
 481                                 }
 482                         }
 483                 }
 484 
 485                 tzp->psl.Length = ACPI_ALLOCATE_BUFFER;
 486                 tzp->psl.Pointer = NULL;
 487                 (void) AcpiEvaluateObjectTyped(obj, "_PSL", NULL, &tzp->psl,
 488                     ACPI_TYPE_PACKAGE);
 489         }
 490 
 491         tzmon_eval_int(obj, "_TC1", &tzp->tc1);
 492         tzmon_eval_int(obj, "_TC2", &tzp->tc2);
 493         tzmon_eval_int(obj, "_TSP", &tzp->tsp);
 494         tzmon_eval_int(obj, "_TZP", &tzp->tzp);
 495 
 496         if (tzp->tzp == 0) {
 497                 tzp->polling_period = 0;
 498         } else {
 499                 if (tzp->tzp < 0)
 500                         tzp->polling_period = TZ_DEFAULT_PERIOD;
 501                 else
 502                         tzp->polling_period = tzp->tzp/10;
 503 
 504                 /* start monitor thread if needed */
 505                 if (tzp->taskq == NULL) {
 506                         char taskq_name[TZ_TASKQ_NAME_LEN];
 507 
 508                         (void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN,
 509                             "AcpiThermalMonitor%02d", instance);
 510                         tzp->taskq = ddi_taskq_create(tzmon_dip,
 511                             taskq_name, 1, TASKQ_DEFAULTPRI, 0);
 512                         if (tzp->taskq == NULL) {
 513                                 tzp->polling_period = 0;
 514                                 cmn_err(CE_WARN, "tzmon: could not create "
 515                                     "monitor thread for thermal zone %s - "
 516                                     "monitor by notify only",
 517                                     (char *)tzp->zone_name);
 518                         } else {
 519                                 (void) ddi_taskq_dispatch(tzp->taskq,
 520                                     tzmon_monitor, tzp, DDI_SLEEP);
 521                         }
 522                 }
 523         }
 524 
 525         mutex_exit(&tzp->lock);
 526 }
 527 
 528 
 529 /*
 530  * tzmon_zone_callback
 531  * Enumerate the thermal zone if it has a _TMP (current thermal zone
 532  * operating temperature) method.
 533  */
 534 /*ARGSUSED*/
 535 static ACPI_STATUS
 536 tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv)
 537 {
 538         ACPI_HANDLE tmpobj;
 539 
 540         /*
 541          * We get both ThermalZone() and Scope(\_TZ) objects here;
 542          * look for _TMP (without which a zone is invalid) to pick
 543          * between them (and ignore invalid zones)
 544          */
 545         if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) {
 546                 tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL);
 547         }
 548 
 549         return (AE_OK);
 550 }
 551 
 552 
 553 /*
 554  * tzmon_find_zones
 555  * Find all of the thermal zones by calling a ACPICA function that
 556  * walks the ACPI namespace and invokes a callback for each thermal
 557  * object found.
 558  */
 559 static void
 560 tzmon_find_zones()
 561 {
 562         ACPI_STATUS status;
 563         int retval;
 564 
 565         status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
 566             8, tzmon_zone_callback, NULL, NULL, (void **)&retval);
 567 
 568         ASSERT(status == AE_OK);
 569 }
 570 
 571 
 572 /*
 573  * tzmon_monitor
 574  * Run as a separate thread, this wakes according to polling period and
 575  * checks particular objects in the thermal zone.  One instance per
 576  * thermal zone.
 577  */
 578 static void
 579 tzmon_monitor(void *ctx)
 580 {
 581         thermal_zone_t *tzp = (thermal_zone_t *)ctx;
 582         clock_t ticks;
 583 
 584         do {
 585                 /* Check out the zone */
 586                 tzmon_eval_zone(tzp);
 587 
 588                 /* Go back to sleep */
 589                 mutex_enter(&tzp->lock);
 590                 ticks = drv_usectohz(tzp->polling_period * 1000000);
 591                 if (ticks > 0)
 592                         (void) cv_reltimedwait(&zone_list_condvar,
 593                             &tzp->lock, ticks, TR_CLOCK_TICK);
 594                 mutex_exit(&tzp->lock);
 595         } while (ticks > 0);
 596 }
 597 
 598 
 599 /*
 600  * tzmon_set_power_device
 601  */
 602 static void
 603 tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name)
 604 {
 605         ACPI_BUFFER rb;
 606         ACPI_OBJECT *pr0;
 607         ACPI_STATUS status;
 608         int i;
 609 
 610         rb.Length = ACPI_ALLOCATE_BUFFER;
 611         rb.Pointer = NULL;
 612         status = AcpiEvaluateObjectTyped(dev, "_PR0", NULL, &rb,
 613             ACPI_TYPE_PACKAGE);
 614         if (status != AE_OK) {
 615                 DTRACE_PROBE2(alx__error, int, 2, char *, tz_name);
 616                 return;
 617         }
 618 
 619         pr0 = ((ACPI_OBJECT *)rb.Pointer);
 620         for (i = 0; i < pr0->Package.Count; i++) {
 621                 status = AcpiEvaluateObject(
 622                     pr0->Package.Elements[i].Reference.Handle,
 623                     on_off ? "_ON" : "_OFF", NULL, NULL);
 624                 if (status != AE_OK) {
 625                         DTRACE_PROBE2(alx__error, int, 4, char *, tz_name);
 626                 }
 627         }
 628 
 629         AcpiOsFree(rb.Pointer);
 630 }
 631 
 632 
 633 /*
 634  * tzmon_set_power
 635  * Turn on or turn off all devices in the supplied list.
 636  */
 637 static void
 638 tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name)
 639 {
 640         ACPI_OBJECT *devs;
 641         int i;
 642 
 643         devs = ((ACPI_OBJECT *)devlist.Pointer);
 644         if (devs->Type != ACPI_TYPE_PACKAGE) {
 645                 DTRACE_PROBE2(alx__error, int, 1, char *, tz_name);
 646                 return;
 647         }
 648 
 649         for (i = 0; i < devs->Package.Count; i++)
 650                 tzmon_set_power_device(
 651                     devs->Package.Elements[i].Reference.Handle, on_off,
 652                     tz_name);
 653 }
 654 
 655 
 656 /*
 657  * tzmon_eval_zone
 658  * Evaluate the current conditions within the thermal zone.
 659  */
 660 static void
 661 tzmon_eval_zone(thermal_zone_t *tzp)
 662 {
 663         int tmp, new_level, level;
 664 
 665         mutex_enter(&tzp->lock);
 666 
 667         /* get the current temperature from ACPI */
 668         tzmon_eval_int(tzp->obj, "_TMP", &tmp);
 669         DTRACE_PROBE4(tz__temp, int, tmp, int, tzp->crt, int, tzp->hot,
 670             char *, (char *)tzp->zone_name);
 671 
 672         /* _HOT handling */
 673         if (tzp->hot > 0 && tmp >= tzp->hot) {
 674                 cmn_err(CE_WARN,
 675                     "tzmon: Thermal zone (%s) is too hot (%d C); "
 676                     "initiating shutdown\n",
 677                     (char *)tzp->zone_name, K_TO_C(tmp));
 678 
 679                 tzmon_do_shutdown();
 680         }
 681 
 682         /* _CRT handling */
 683         if (tzp->crt > 0 && tmp >= tzp->crt) {
 684                 cmn_err(CE_WARN,
 685                     "tzmon: Thermal zone (%s) is critically hot (%d C); "
 686                     "initiating rapid shutdown\n",
 687                     (char *)tzp->zone_name, K_TO_C(tmp));
 688 
 689                 /* shut down (fairly) immediately */
 690                 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
 691         }
 692 
 693         /*
 694          * use the temperature to determine whether the thermal zone
 695          * is at a new active cooling threshold level
 696          */
 697         for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) {
 698                 if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) {
 699                         new_level = level;
 700                         break;
 701                 }
 702         }
 703 
 704         /*
 705          * if the active cooling threshold has changed, turn off the
 706          * devices associated with the old one and turn on the new one
 707          */
 708         if (tzp->current_level != new_level) {
 709                 if ((tzp->current_level >= 0) &&
 710                     (tzp->al[tzp->current_level].Length != 0))
 711                         tzmon_set_power(tzp->al[tzp->current_level], 0,
 712                             (char *)tzp->zone_name);
 713 
 714                 if ((new_level >= 0) &&
 715                     (tzp->al[new_level].Length != 0))
 716                         tzmon_set_power(tzp->al[new_level], 1,
 717                             (char *)tzp->zone_name);
 718 
 719                 tzp->current_level = new_level;
 720         }
 721 
 722         mutex_exit(&tzp->lock);
 723 }
 724 
 725 
 726 /*
 727  * tzmon_do_shutdown
 728  * Initiates shutdown by sending a SIGPWR signal to init.
 729  */
 730 static void
 731 tzmon_do_shutdown(void)
 732 {
 733         proc_t *initpp;
 734 
 735         mutex_enter(&pidlock);
 736         initpp = prfind(P_INITPID);
 737         mutex_exit(&pidlock);
 738 
 739         /* if we can't find init, just halt */
 740         if (initpp == NULL) {
 741                 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
 742         }
 743 
 744         /* graceful shutdown with inittab and all getting involved */
 745         psignal(initpp, SIGPWR);
 746 }