12bda830bSap25164 /*
22bda830bSap25164 * CDDL HEADER START
32bda830bSap25164 *
42bda830bSap25164 * The contents of this file are subject to the terms of the
52bda830bSap25164 * Common Development and Distribution License (the "License").
62bda830bSap25164 * You may not use this file except in compliance with the License.
72bda830bSap25164 *
82bda830bSap25164 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92bda830bSap25164 * or http://www.opensolaris.org/os/licensing.
102bda830bSap25164 * See the License for the specific language governing permissions
112bda830bSap25164 * and limitations under the License.
122bda830bSap25164 *
132bda830bSap25164 * When distributing Covered Code, include this CDDL HEADER in each
142bda830bSap25164 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152bda830bSap25164 * If applicable, add the following below this CDDL HEADER, with the
162bda830bSap25164 * fields enclosed by brackets "[]" replaced with your own identifying
172bda830bSap25164 * information: Portions Copyright [yyyy] [name of copyright owner]
182bda830bSap25164 *
192bda830bSap25164 * CDDL HEADER END
202bda830bSap25164 */
212bda830bSap25164
222bda830bSap25164 /*
236d0c08ffSDana Myers * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
242bda830bSap25164 * Use is subject to license terms.
252bda830bSap25164 */
262bda830bSap25164
272bda830bSap25164 /*
282bda830bSap25164 * Solaris x86 ACPI ThermalZone Monitor
292bda830bSap25164 */
302bda830bSap25164
312bda830bSap25164
322bda830bSap25164 #include <sys/errno.h>
332bda830bSap25164 #include <sys/conf.h>
342bda830bSap25164 #include <sys/modctl.h>
352bda830bSap25164 #include <sys/open.h>
362bda830bSap25164 #include <sys/stat.h>
372bda830bSap25164 #include <sys/ddi.h>
382bda830bSap25164 #include <sys/sunddi.h>
392bda830bSap25164 #include <sys/ksynch.h>
402bda830bSap25164 #include <sys/uadmin.h>
412bda830bSap25164 #include <sys/acpi/acpi.h>
422bda830bSap25164 #include <sys/acpica.h>
432bda830bSap25164 #include <sys/sdt.h>
442bda830bSap25164
452bda830bSap25164 #include "tzmon.h"
462bda830bSap25164
472bda830bSap25164
482bda830bSap25164 #define TZMON_ENUM_TRIP_POINTS 1
492bda830bSap25164 #define TZMON_ENUM_DEV_LISTS 2
502bda830bSap25164 #define TZMON_ENUM_ALL (TZMON_ENUM_TRIP_POINTS | TZMON_ENUM_DEV_LISTS)
512bda830bSap25164
52044802ffSap25164 /*
53044802ffSap25164 * TZ_TASKQ_NAME_LEN is precisely the length of the string "AcpiThermalMonitor"
54044802ffSap25164 * plus a two-digit instance number plus a NULL. If the taskq name is changed
55044802ffSap25164 * (particularly if it is lengthened), then this value needs to change.
56044802ffSap25164 */
57044802ffSap25164 #define TZ_TASKQ_NAME_LEN 21
58044802ffSap25164
59044802ffSap25164 /*
60044802ffSap25164 * Kelvin to Celsius conversion
61044802ffSap25164 * The formula for converting degrees Kelvin to degrees Celsius is
62044802ffSap25164 * C = K - 273.15 (we round to 273.2). The unit for thermal zone
63044802ffSap25164 * temperatures is tenths of a degree Kelvin. Use tenth of a degree
64044802ffSap25164 * to convert, then make a whole number out of it.
65044802ffSap25164 */
66044802ffSap25164 #define K_TO_C(temp) (((temp) - 2732) / 10)
67044802ffSap25164
682bda830bSap25164
692bda830bSap25164 /* cb_ops or dev_ops forward declarations */
702bda830bSap25164 static int tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
712bda830bSap25164 void *arg, void **result);
722bda830bSap25164 static int tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
732bda830bSap25164 static int tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
742bda830bSap25164
752bda830bSap25164 /* other forward declarations */
762bda830bSap25164 static void tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx);
772bda830bSap25164 static void tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv);
782bda830bSap25164 static thermal_zone_t *tzmon_alloc_zone();
792bda830bSap25164 static void tzmon_free_zone_list();
802bda830bSap25164 static void tzmon_discard_buffers(thermal_zone_t *tzp);
812bda830bSap25164 static void tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp,
822bda830bSap25164 int enum_flag);
832bda830bSap25164 static ACPI_STATUS tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest,
842bda830bSap25164 void *ctx, void **rv);
852bda830bSap25164 static void tzmon_find_zones(void);
862bda830bSap25164 static void tzmon_monitor(void *ctx);
879ed7afb7Sap25164 static void tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name);
889ed7afb7Sap25164 static void tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name);
892bda830bSap25164 static void tzmon_eval_zone(thermal_zone_t *tzp);
902bda830bSap25164 static void tzmon_do_shutdown(void);
912bda830bSap25164
922bda830bSap25164 extern void halt(char *);
932bda830bSap25164
942bda830bSap25164 static struct cb_ops tzmon_cb_ops = {
952bda830bSap25164 nodev, /* no open routine */
962bda830bSap25164 nodev, /* no close routine */
972bda830bSap25164 nodev, /* not a block driver */
982bda830bSap25164 nodev, /* no print routine */
992bda830bSap25164 nodev, /* no dump routine */
1002bda830bSap25164 nodev, /* no read routine */
1012bda830bSap25164 nodev, /* no write routine */
1022bda830bSap25164 nodev, /* no ioctl routine */
1032bda830bSap25164 nodev, /* no devmap routine */
1042bda830bSap25164 nodev, /* no mmap routine */
1052bda830bSap25164 nodev, /* no segmap routine */
1062bda830bSap25164 nochpoll, /* no chpoll routine */
1072bda830bSap25164 ddi_prop_op,
1082bda830bSap25164 0, /* not a STREAMS driver */
1092bda830bSap25164 D_NEW | D_MP, /* safe for multi-thread/multi-processor */
1102bda830bSap25164 };
1112bda830bSap25164
1122bda830bSap25164 static struct dev_ops tzmon_ops = {
1132bda830bSap25164 DEVO_REV, /* devo_rev */
1142bda830bSap25164 0, /* devo_refcnt */
1152bda830bSap25164 tzmon_getinfo, /* devo_getinfo */
1162bda830bSap25164 nulldev, /* devo_identify */
1172bda830bSap25164 nulldev, /* devo_probe */
1182bda830bSap25164 tzmon_attach, /* devo_attach */
1192bda830bSap25164 tzmon_detach, /* devo_detach */
1202bda830bSap25164 nodev, /* devo_reset */
1212bda830bSap25164 &tzmon_cb_ops, /* devo_cb_ops */
1222bda830bSap25164 (struct bus_ops *)0, /* devo_bus_ops */
1232bda830bSap25164 NULL, /* devo_power */
12419397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */
1252bda830bSap25164 };
1262bda830bSap25164
1272bda830bSap25164 extern struct mod_ops mod_driverops;
1282bda830bSap25164
1292bda830bSap25164 static struct modldrv modldrv = {
1302bda830bSap25164 &mod_driverops,
1319f61ace3Sap25164 "ACPI Thermal Zone Monitor",
1322bda830bSap25164 &tzmon_ops,
1332bda830bSap25164 };
1342bda830bSap25164
1352bda830bSap25164 static struct modlinkage modlinkage = {
1362bda830bSap25164 MODREV_1, /* MODREV_1 indicated by manual */
1372bda830bSap25164 (void *)&modldrv,
1382bda830bSap25164 NULL, /* termination of list of linkage structures */
1392bda830bSap25164 };
1402bda830bSap25164
1412bda830bSap25164 /* globals for this module */
1422bda830bSap25164 static dev_info_t *tzmon_dip;
1432bda830bSap25164 static thermal_zone_t *zone_list;
1442bda830bSap25164 static int zone_count;
1452bda830bSap25164 static kmutex_t zone_list_lock;
1462bda830bSap25164 static kcondvar_t zone_list_condvar;
1472bda830bSap25164
1482bda830bSap25164
1492bda830bSap25164 /*
1502bda830bSap25164 * _init, _info, and _fini support loading and unloading the driver.
1512bda830bSap25164 */
1522bda830bSap25164 int
_init(void)1532bda830bSap25164 _init(void)
1542bda830bSap25164 {
1552bda830bSap25164 return (mod_install(&modlinkage));
1562bda830bSap25164 }
1572bda830bSap25164
1582bda830bSap25164
1592bda830bSap25164 int
_info(struct modinfo * modinfop)1602bda830bSap25164 _info(struct modinfo *modinfop)
1612bda830bSap25164 {
1622bda830bSap25164 return (mod_info(&modlinkage, modinfop));
1632bda830bSap25164 }
1642bda830bSap25164
1652bda830bSap25164
1662bda830bSap25164 int
_fini(void)1672bda830bSap25164 _fini(void)
1682bda830bSap25164 {
1692bda830bSap25164 return (mod_remove(&modlinkage));
1702bda830bSap25164 }
1712bda830bSap25164
1722bda830bSap25164
1732bda830bSap25164 static int
tzmon_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1742bda830bSap25164 tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1752bda830bSap25164 {
1762bda830bSap25164 if (cmd != DDI_ATTACH)
1772bda830bSap25164 return (DDI_FAILURE);
1782bda830bSap25164
1792bda830bSap25164 if (tzmon_dip != NULL)
1802bda830bSap25164 return (DDI_FAILURE);
1812bda830bSap25164
1822bda830bSap25164 /*
1832bda830bSap25164 * Check to see if ACPI CA services are available
1842bda830bSap25164 */
1852bda830bSap25164 if (AcpiSubsystemStatus() != AE_OK)
1862bda830bSap25164 return (DDI_FAILURE);
1872bda830bSap25164
1882bda830bSap25164 mutex_init(&zone_list_lock, NULL, MUTEX_DRIVER, NULL);
1892bda830bSap25164 cv_init(&zone_list_condvar, NULL, CV_DRIVER, NULL);
1902bda830bSap25164
1912bda830bSap25164 tzmon_find_zones();
1922bda830bSap25164 mutex_enter(&zone_list_lock);
1932bda830bSap25164 if (zone_count < 1) {
1942bda830bSap25164 mutex_exit(&zone_list_lock);
1952bda830bSap25164 mutex_destroy(&zone_list_lock);
1962bda830bSap25164 cv_destroy(&zone_list_condvar);
1972bda830bSap25164 return (DDI_FAILURE);
1982bda830bSap25164 }
1992bda830bSap25164 mutex_exit(&zone_list_lock);
2002bda830bSap25164
2012bda830bSap25164 if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 0,
2022bda830bSap25164 DDI_PSEUDO, 0) == DDI_FAILURE) {
2032bda830bSap25164 tzmon_free_zone_list();
2042bda830bSap25164 mutex_destroy(&zone_list_lock);
2052bda830bSap25164 cv_destroy(&zone_list_condvar);
2062bda830bSap25164 return (DDI_FAILURE);
2072bda830bSap25164 }
2082bda830bSap25164
2092bda830bSap25164 tzmon_dip = dip;
2102bda830bSap25164
2112bda830bSap25164 ddi_report_dev(dip);
2122bda830bSap25164
2132bda830bSap25164 return (DDI_SUCCESS);
2142bda830bSap25164 }
2152bda830bSap25164
2162bda830bSap25164
2172bda830bSap25164 /*ARGSUSED*/
2182bda830bSap25164 static int
tzmon_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2192bda830bSap25164 tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2202bda830bSap25164 {
2212bda830bSap25164 int error;
2222bda830bSap25164
2232bda830bSap25164 switch (infocmd) {
2242bda830bSap25164 case DDI_INFO_DEVT2DEVINFO:
2252bda830bSap25164 *result = tzmon_dip;
2262bda830bSap25164 if (tzmon_dip == NULL)
2272bda830bSap25164 error = DDI_FAILURE;
2282bda830bSap25164 else
2292bda830bSap25164 error = DDI_SUCCESS;
2302bda830bSap25164 break;
2312bda830bSap25164 case DDI_INFO_DEVT2INSTANCE:
2322bda830bSap25164 *result = 0;
2332bda830bSap25164 error = DDI_SUCCESS;
2342bda830bSap25164 break;
2352bda830bSap25164 default:
2362bda830bSap25164 *result = NULL;
2372bda830bSap25164 error = DDI_FAILURE;
2382bda830bSap25164 }
2392bda830bSap25164
2402bda830bSap25164 return (error);
2412bda830bSap25164 }
2422bda830bSap25164
2432bda830bSap25164
2442bda830bSap25164 static int
tzmon_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2452bda830bSap25164 tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2462bda830bSap25164 {
247044802ffSap25164 thermal_zone_t *tzp = zone_list;
248044802ffSap25164
2492bda830bSap25164 if (cmd != DDI_DETACH)
2502bda830bSap25164 return (DDI_FAILURE);
2512bda830bSap25164
252044802ffSap25164 /* free allocated thermal zone name(s) */
253044802ffSap25164 while (tzp != NULL) {
254044802ffSap25164 AcpiOsFree(tzp->zone_name);
255044802ffSap25164 tzp = tzp->next;
256044802ffSap25164 }
257044802ffSap25164
2582bda830bSap25164 /* discard zone list assets */
2592bda830bSap25164 tzmon_free_zone_list();
2602bda830bSap25164
2612bda830bSap25164 ddi_remove_minor_node(dip, NULL);
2622bda830bSap25164 tzmon_dip = NULL;
2632bda830bSap25164
2642bda830bSap25164 mutex_destroy(&zone_list_lock);
2652bda830bSap25164 cv_destroy(&zone_list_condvar);
2662bda830bSap25164
2672bda830bSap25164 return (DDI_SUCCESS);
2682bda830bSap25164 }
2692bda830bSap25164
2702bda830bSap25164
2712bda830bSap25164 /*
2722bda830bSap25164 * tzmon_notify_zone
2732bda830bSap25164 * Thermal zone notification handler.
2742bda830bSap25164 */
2752bda830bSap25164 static void
tzmon_notify_zone(ACPI_HANDLE obj,UINT32 val,void * ctx)2762bda830bSap25164 tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx)
2772bda830bSap25164 {
2782bda830bSap25164 thermal_zone_t *tzp = (thermal_zone_t *)ctx;
2792bda830bSap25164
2802bda830bSap25164 switch (val) {
2812bda830bSap25164 case 0x80: /* Thermal Zone status changed */
2822bda830bSap25164 tzmon_eval_zone(tzp);
2832bda830bSap25164 break;
2842bda830bSap25164 case 0x81: /* Thermal Zone trip points changed */
2852bda830bSap25164 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_TRIP_POINTS);
2862bda830bSap25164 break;
2872bda830bSap25164 case 0x82: /* Device Lists changed */
2882bda830bSap25164 tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_DEV_LISTS);
2892bda830bSap25164 break;
2902bda830bSap25164 case 0x83: /* Thermal Relationship Table changed */
2912bda830bSap25164 /* not handling _TRT objects, so not handling this event */
2929ed7afb7Sap25164 DTRACE_PROBE1(trt__change, char *, (char *)tzp->zone_name);
2932bda830bSap25164 break;
2942bda830bSap25164 default:
2952bda830bSap25164 break;
2962bda830bSap25164 }
2972bda830bSap25164 }
2982bda830bSap25164
2992bda830bSap25164
3002bda830bSap25164 /*
3012bda830bSap25164 * tzmon_eval_int
3022bda830bSap25164 * Evaluate the object/method as an integer.
3032bda830bSap25164 */
3042bda830bSap25164 static void
tzmon_eval_int(ACPI_HANDLE obj,char * method,int * rv)3052bda830bSap25164 tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv)
3062bda830bSap25164 {
3072bda830bSap25164
3082bda830bSap25164 if (acpica_eval_int(obj, method, rv) != AE_OK)
3092bda830bSap25164 *rv = -1;
3102bda830bSap25164 }
3112bda830bSap25164
3122bda830bSap25164
3132bda830bSap25164 /*
3142bda830bSap25164 * tzmon_alloc_zone
3152bda830bSap25164 * Allocate memory for the zone structure and initialize it lock mutex.
3162bda830bSap25164 */
3172bda830bSap25164 static thermal_zone_t *
tzmon_alloc_zone()3182bda830bSap25164 tzmon_alloc_zone()
3192bda830bSap25164 {
3202bda830bSap25164 thermal_zone_t *tzp;
3212bda830bSap25164
3222bda830bSap25164 tzp = kmem_zalloc(sizeof (thermal_zone_t), KM_SLEEP);
3232bda830bSap25164 mutex_init(&tzp->lock, NULL, MUTEX_DRIVER, NULL);
3242bda830bSap25164
3252bda830bSap25164 return (tzp);
3262bda830bSap25164 }
3272bda830bSap25164
3282bda830bSap25164
3292bda830bSap25164 /*
3302bda830bSap25164 * tzmon_free_zone_list
3312bda830bSap25164 * Free the zone list, either because attach failed or detach initiated.
3322bda830bSap25164 */
3332bda830bSap25164 static void
tzmon_free_zone_list()3342bda830bSap25164 tzmon_free_zone_list()
3352bda830bSap25164 {
3362bda830bSap25164 thermal_zone_t *tzp = zone_list;
3372bda830bSap25164
3382bda830bSap25164 while (tzp != NULL) {
3392bda830bSap25164 thermal_zone_t *next;
3402bda830bSap25164
3412bda830bSap25164 mutex_enter(&tzp->lock);
3422bda830bSap25164
3432bda830bSap25164 /*
3442bda830bSap25164 * Remove the notify handler for the zone. Not much to
3452bda830bSap25164 * do if this fails (since we are on our way out), so
3462bda830bSap25164 * just ignore failure.
3472bda830bSap25164 */
3482bda830bSap25164 (void) AcpiRemoveNotifyHandler(tzp->obj, ACPI_DEVICE_NOTIFY,
3492bda830bSap25164 tzmon_notify_zone);
3502bda830bSap25164
3512bda830bSap25164 /* Shut down monitor thread, if running */
3522bda830bSap25164 if (tzp->taskq != NULL) {
3532bda830bSap25164 tzp->polling_period = 0;
3542bda830bSap25164 cv_broadcast(&zone_list_condvar);
3552bda830bSap25164
3562bda830bSap25164 /* Drop mutex to allow the thread to run */
3572bda830bSap25164 mutex_exit(&tzp->lock);
3582bda830bSap25164 ddi_taskq_destroy(tzp->taskq);
3592bda830bSap25164 mutex_enter(&tzp->lock);
3602bda830bSap25164 }
3612bda830bSap25164
3622bda830bSap25164 tzmon_discard_buffers(tzp);
3632bda830bSap25164 mutex_exit(&tzp->lock);
3642bda830bSap25164 mutex_destroy(&tzp->lock);
3652bda830bSap25164
3662bda830bSap25164 next = tzp->next;
3672bda830bSap25164 kmem_free(tzp, sizeof (thermal_zone_t));
3682bda830bSap25164 tzp = next;
3692bda830bSap25164 }
3702bda830bSap25164 }
3712bda830bSap25164
3722bda830bSap25164
3732bda830bSap25164 static void
tzmon_discard_buffers(thermal_zone_t * tzp)3742bda830bSap25164 tzmon_discard_buffers(thermal_zone_t *tzp)
3752bda830bSap25164 {
3762bda830bSap25164 int level;
3772bda830bSap25164
3782bda830bSap25164 for (level = 0; level < TZ_NUM_LEVELS; level++) {
3792bda830bSap25164 if (tzp->al[level].Pointer != NULL)
3802bda830bSap25164 AcpiOsFree(tzp->al[level].Pointer);
3812bda830bSap25164 }
3822bda830bSap25164
3832bda830bSap25164 if (tzp->psl.Pointer != NULL)
3842bda830bSap25164 AcpiOsFree(tzp->psl.Pointer);
3852bda830bSap25164 }
3862bda830bSap25164
3872bda830bSap25164
3882bda830bSap25164 /*
3892bda830bSap25164 * tzmon_enumerate_zone
3902bda830bSap25164 * Enumerates the contents of a thermal zone and updates passed-in
3912bda830bSap25164 * thermal_zone or creates a new one if tzp is NULL. Newly-created
3922bda830bSap25164 * zones are linked into the global zone_list.
3932bda830bSap25164 */
3942bda830bSap25164 static void
tzmon_enumerate_zone(ACPI_HANDLE obj,thermal_zone_t * tzp,int enum_flag)3952bda830bSap25164 tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp, int enum_flag)
3962bda830bSap25164 {
3972bda830bSap25164 ACPI_STATUS status;
398044802ffSap25164 ACPI_BUFFER zone_name;
3992bda830bSap25164 int level;
400044802ffSap25164 int instance;
4012bda830bSap25164 char abuf[5];
4022bda830bSap25164
4032bda830bSap25164 /*
4042bda830bSap25164 * Newly-created zones and existing zones both require
4052bda830bSap25164 * some individual attention.
4062bda830bSap25164 */
4072bda830bSap25164 if (tzp == NULL) {
4082bda830bSap25164 /* New zone required */
4092bda830bSap25164 tzp = tzmon_alloc_zone();
4102bda830bSap25164 mutex_enter(&zone_list_lock);
4112bda830bSap25164 tzp->next = zone_list;
4122bda830bSap25164 zone_list = tzp;
413044802ffSap25164
414044802ffSap25164 /*
415044802ffSap25164 * It is exceedingly unlikely that instance will exceed 99.
416044802ffSap25164 * However, if it does, this will cause problems when
417044802ffSap25164 * creating the taskq for this thermal zone.
418044802ffSap25164 */
419044802ffSap25164 instance = zone_count;
4202bda830bSap25164 zone_count++;
4212bda830bSap25164 mutex_exit(&zone_list_lock);
4222bda830bSap25164 mutex_enter(&tzp->lock);
4232bda830bSap25164 tzp->obj = obj;
4242bda830bSap25164
4252bda830bSap25164 /*
4262bda830bSap25164 * Set to a low level. Will get set to the actual
4272bda830bSap25164 * current power level when the thread monitor polls
4282bda830bSap25164 * the current temperature.
4292bda830bSap25164 */
4302bda830bSap25164 tzp->current_level = 0;
4312bda830bSap25164
432044802ffSap25164 /* Get the zone name in case we need to display it later */
433044802ffSap25164 zone_name.Length = ACPI_ALLOCATE_BUFFER;
434044802ffSap25164 zone_name.Pointer = NULL;
435044802ffSap25164
436044802ffSap25164 status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &zone_name);
437044802ffSap25164 ASSERT(status == AE_OK);
438044802ffSap25164
439044802ffSap25164 tzp->zone_name = zone_name.Pointer;
440044802ffSap25164
4412bda830bSap25164 status = AcpiInstallNotifyHandler(obj, ACPI_DEVICE_NOTIFY,
4422bda830bSap25164 tzmon_notify_zone, (void *)tzp);
4432bda830bSap25164 ASSERT(status == AE_OK);
4442bda830bSap25164 } else {
4452bda830bSap25164 /* Existing zone - toss out allocated items */
4462bda830bSap25164 mutex_enter(&tzp->lock);
4472bda830bSap25164 ASSERT(tzp->obj == obj);
4482bda830bSap25164
4492bda830bSap25164 if (enum_flag & TZMON_ENUM_DEV_LISTS)
4502bda830bSap25164 tzmon_discard_buffers(tzp);
4512bda830bSap25164 }
4522bda830bSap25164
4532bda830bSap25164 if (enum_flag & TZMON_ENUM_TRIP_POINTS) {
4542bda830bSap25164 for (level = 0; level < TZ_NUM_LEVELS; level++) {
4555cae022bSap25164 (void) snprintf(abuf, 5, "_AC%d", level);
4562bda830bSap25164 tzmon_eval_int(obj, abuf, &tzp->ac[level]);
4572bda830bSap25164
4582bda830bSap25164 }
4592bda830bSap25164
4602bda830bSap25164 tzmon_eval_int(obj, "_CRT", &tzp->crt);
4612bda830bSap25164 tzmon_eval_int(obj, "_HOT", &tzp->hot);
4622bda830bSap25164 tzmon_eval_int(obj, "_PSV", &tzp->psv);
4632bda830bSap25164 }
4642bda830bSap25164
4652bda830bSap25164 if (enum_flag & TZMON_ENUM_DEV_LISTS) {
4662bda830bSap25164 for (level = 0; level < TZ_NUM_LEVELS; level++) {
4672bda830bSap25164 if (tzp->ac[level] == -1) {
4682bda830bSap25164 tzp->al[level].Length = 0;
4692bda830bSap25164 tzp->al[level].Pointer = NULL;
4702bda830bSap25164 } else {
4715cae022bSap25164 (void) snprintf(abuf, 5, "_AL%d", level);
4722bda830bSap25164 tzp->al[level].Length = ACPI_ALLOCATE_BUFFER;
4732bda830bSap25164 tzp->al[level].Pointer = NULL;
4746d0c08ffSDana Myers if (AcpiEvaluateObjectTyped(obj, abuf, NULL,
4756d0c08ffSDana Myers &tzp->al[level], ACPI_TYPE_PACKAGE) !=
4766d0c08ffSDana Myers AE_OK) {
4779ed7afb7Sap25164 DTRACE_PROBE2(alx__missing, int, level,
4789ed7afb7Sap25164 char *, (char *)tzp->zone_name);
4792bda830bSap25164
4802bda830bSap25164 tzp->al[level].Length = 0;
4812bda830bSap25164 tzp->al[level].Pointer = NULL;
4822bda830bSap25164 }
4832bda830bSap25164 }
4842bda830bSap25164 }
4852bda830bSap25164
4862bda830bSap25164 tzp->psl.Length = ACPI_ALLOCATE_BUFFER;
4872bda830bSap25164 tzp->psl.Pointer = NULL;
4886d0c08ffSDana Myers (void) AcpiEvaluateObjectTyped(obj, "_PSL", NULL, &tzp->psl,
4896d0c08ffSDana Myers ACPI_TYPE_PACKAGE);
4902bda830bSap25164 }
4912bda830bSap25164
4922bda830bSap25164 tzmon_eval_int(obj, "_TC1", &tzp->tc1);
4932bda830bSap25164 tzmon_eval_int(obj, "_TC2", &tzp->tc2);
4942bda830bSap25164 tzmon_eval_int(obj, "_TSP", &tzp->tsp);
4952bda830bSap25164 tzmon_eval_int(obj, "_TZP", &tzp->tzp);
4962bda830bSap25164
4972bda830bSap25164 if (tzp->tzp == 0) {
4982bda830bSap25164 tzp->polling_period = 0;
4992bda830bSap25164 } else {
5002bda830bSap25164 if (tzp->tzp < 0)
5012bda830bSap25164 tzp->polling_period = TZ_DEFAULT_PERIOD;
5022bda830bSap25164 else
5032bda830bSap25164 tzp->polling_period = tzp->tzp/10;
5042bda830bSap25164
5052bda830bSap25164 /* start monitor thread if needed */
5062bda830bSap25164 if (tzp->taskq == NULL) {
507044802ffSap25164 char taskq_name[TZ_TASKQ_NAME_LEN];
508044802ffSap25164
509044802ffSap25164 (void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN,
510044802ffSap25164 "AcpiThermalMonitor%02d", instance);
5112bda830bSap25164 tzp->taskq = ddi_taskq_create(tzmon_dip,
512044802ffSap25164 taskq_name, 1, TASKQ_DEFAULTPRI, 0);
5132bda830bSap25164 if (tzp->taskq == NULL) {
5142bda830bSap25164 tzp->polling_period = 0;
5152bda830bSap25164 cmn_err(CE_WARN, "tzmon: could not create "
5169ed7afb7Sap25164 "monitor thread for thermal zone %s - "
5179ed7afb7Sap25164 "monitor by notify only",
5189ed7afb7Sap25164 (char *)tzp->zone_name);
5192bda830bSap25164 } else {
5205cae022bSap25164 (void) ddi_taskq_dispatch(tzp->taskq,
5215cae022bSap25164 tzmon_monitor, tzp, DDI_SLEEP);
5222bda830bSap25164 }
5232bda830bSap25164 }
5242bda830bSap25164 }
5252bda830bSap25164
5262bda830bSap25164 mutex_exit(&tzp->lock);
5272bda830bSap25164 }
5282bda830bSap25164
5292bda830bSap25164
5302bda830bSap25164 /*
5312bda830bSap25164 * tzmon_zone_callback
5322bda830bSap25164 * Enumerate the thermal zone if it has a _TMP (current thermal zone
5332bda830bSap25164 * operating temperature) method.
5342bda830bSap25164 */
5352bda830bSap25164 /*ARGSUSED*/
5362bda830bSap25164 static ACPI_STATUS
tzmon_zone_callback(ACPI_HANDLE obj,UINT32 nest,void * ctx,void ** rv)5372bda830bSap25164 tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv)
5382bda830bSap25164 {
5392bda830bSap25164 ACPI_HANDLE tmpobj;
5402bda830bSap25164
5412bda830bSap25164 /*
5422bda830bSap25164 * We get both ThermalZone() and Scope(\_TZ) objects here;
5432bda830bSap25164 * look for _TMP (without which a zone is invalid) to pick
5442bda830bSap25164 * between them (and ignore invalid zones)
5452bda830bSap25164 */
5462bda830bSap25164 if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) {
5472bda830bSap25164 tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL);
5482bda830bSap25164 }
5492bda830bSap25164
5502bda830bSap25164 return (AE_OK);
5512bda830bSap25164 }
5522bda830bSap25164
5532bda830bSap25164
5542bda830bSap25164 /*
5552bda830bSap25164 * tzmon_find_zones
5562bda830bSap25164 * Find all of the thermal zones by calling a ACPICA function that
5572bda830bSap25164 * walks the ACPI namespace and invokes a callback for each thermal
5582bda830bSap25164 * object found.
5592bda830bSap25164 */
5602bda830bSap25164 static void
tzmon_find_zones()5612bda830bSap25164 tzmon_find_zones()
5622bda830bSap25164 {
5632bda830bSap25164 ACPI_STATUS status;
5642bda830bSap25164 int retval;
5652bda830bSap25164
5662bda830bSap25164 status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
567*57190917SDana Myers 8, tzmon_zone_callback, NULL, NULL, (void **)&retval);
5682bda830bSap25164
5692bda830bSap25164 ASSERT(status == AE_OK);
5702bda830bSap25164 }
5712bda830bSap25164
5722bda830bSap25164
5732bda830bSap25164 /*
5742bda830bSap25164 * tzmon_monitor
5752bda830bSap25164 * Run as a separate thread, this wakes according to polling period and
5762bda830bSap25164 * checks particular objects in the thermal zone. One instance per
5772bda830bSap25164 * thermal zone.
5782bda830bSap25164 */
5792bda830bSap25164 static void
tzmon_monitor(void * ctx)5802bda830bSap25164 tzmon_monitor(void *ctx)
5812bda830bSap25164 {
5822bda830bSap25164 thermal_zone_t *tzp = (thermal_zone_t *)ctx;
5832bda830bSap25164 clock_t ticks;
5842bda830bSap25164
5852bda830bSap25164 do {
5862bda830bSap25164 /* Check out the zone */
5872bda830bSap25164 tzmon_eval_zone(tzp);
5882bda830bSap25164
5892bda830bSap25164 /* Go back to sleep */
5902bda830bSap25164 mutex_enter(&tzp->lock);
5912bda830bSap25164 ticks = drv_usectohz(tzp->polling_period * 1000000);
5922bda830bSap25164 if (ticks > 0)
593d3d50737SRafael Vanoni (void) cv_reltimedwait(&zone_list_condvar,
594d3d50737SRafael Vanoni &tzp->lock, ticks, TR_CLOCK_TICK);
5952bda830bSap25164 mutex_exit(&tzp->lock);
5962bda830bSap25164 } while (ticks > 0);
5972bda830bSap25164 }
5982bda830bSap25164
5992bda830bSap25164
6002bda830bSap25164 /*
6012bda830bSap25164 * tzmon_set_power_device
6022bda830bSap25164 */
6032bda830bSap25164 static void
tzmon_set_power_device(ACPI_HANDLE dev,int on_off,char * tz_name)6049ed7afb7Sap25164 tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name)
6052bda830bSap25164 {
6062bda830bSap25164 ACPI_BUFFER rb;
6072bda830bSap25164 ACPI_OBJECT *pr0;
6082bda830bSap25164 ACPI_STATUS status;
6092bda830bSap25164 int i;
6102bda830bSap25164
6112bda830bSap25164 rb.Length = ACPI_ALLOCATE_BUFFER;
6122bda830bSap25164 rb.Pointer = NULL;
6136d0c08ffSDana Myers status = AcpiEvaluateObjectTyped(dev, "_PR0", NULL, &rb,
6146d0c08ffSDana Myers ACPI_TYPE_PACKAGE);
6152bda830bSap25164 if (status != AE_OK) {
6169ed7afb7Sap25164 DTRACE_PROBE2(alx__error, int, 2, char *, tz_name);
6172bda830bSap25164 return;
6182bda830bSap25164 }
6192bda830bSap25164
6202bda830bSap25164 pr0 = ((ACPI_OBJECT *)rb.Pointer);
6212bda830bSap25164 for (i = 0; i < pr0->Package.Count; i++) {
6222bda830bSap25164 status = AcpiEvaluateObject(
6232bda830bSap25164 pr0->Package.Elements[i].Reference.Handle,
6242bda830bSap25164 on_off ? "_ON" : "_OFF", NULL, NULL);
6252bda830bSap25164 if (status != AE_OK) {
6269ed7afb7Sap25164 DTRACE_PROBE2(alx__error, int, 4, char *, tz_name);
6272bda830bSap25164 }
6282bda830bSap25164 }
6292bda830bSap25164
6302bda830bSap25164 AcpiOsFree(rb.Pointer);
6312bda830bSap25164 }
6322bda830bSap25164
6332bda830bSap25164
6342bda830bSap25164 /*
6352bda830bSap25164 * tzmon_set_power
6362bda830bSap25164 * Turn on or turn off all devices in the supplied list.
6372bda830bSap25164 */
6382bda830bSap25164 static void
tzmon_set_power(ACPI_BUFFER devlist,int on_off,char * tz_name)6399ed7afb7Sap25164 tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name)
6402bda830bSap25164 {
6412bda830bSap25164 ACPI_OBJECT *devs;
6422bda830bSap25164 int i;
6432bda830bSap25164
6442bda830bSap25164 devs = ((ACPI_OBJECT *)devlist.Pointer);
6452bda830bSap25164 if (devs->Type != ACPI_TYPE_PACKAGE) {
6469ed7afb7Sap25164 DTRACE_PROBE2(alx__error, int, 1, char *, tz_name);
6472bda830bSap25164 return;
6482bda830bSap25164 }
6492bda830bSap25164
6502bda830bSap25164 for (i = 0; i < devs->Package.Count; i++)
6512bda830bSap25164 tzmon_set_power_device(
6529ed7afb7Sap25164 devs->Package.Elements[i].Reference.Handle, on_off,
6539ed7afb7Sap25164 tz_name);
6542bda830bSap25164 }
6552bda830bSap25164
6562bda830bSap25164
6572bda830bSap25164 /*
6582bda830bSap25164 * tzmon_eval_zone
6592bda830bSap25164 * Evaluate the current conditions within the thermal zone.
6602bda830bSap25164 */
6612bda830bSap25164 static void
tzmon_eval_zone(thermal_zone_t * tzp)6622bda830bSap25164 tzmon_eval_zone(thermal_zone_t *tzp)
6632bda830bSap25164 {
6642bda830bSap25164 int tmp, new_level, level;
6652bda830bSap25164
6662bda830bSap25164 mutex_enter(&tzp->lock);
6672bda830bSap25164
6682bda830bSap25164 /* get the current temperature from ACPI */
6692bda830bSap25164 tzmon_eval_int(tzp->obj, "_TMP", &tmp);
6709ed7afb7Sap25164 DTRACE_PROBE4(tz__temp, int, tmp, int, tzp->crt, int, tzp->hot,
6719ed7afb7Sap25164 char *, (char *)tzp->zone_name);
6722bda830bSap25164
6732bda830bSap25164 /* _HOT handling */
6742bda830bSap25164 if (tzp->hot > 0 && tmp >= tzp->hot) {
675044802ffSap25164 cmn_err(CE_WARN,
676044802ffSap25164 "tzmon: Thermal zone (%s) is too hot (%d C); "
677044802ffSap25164 "initiating shutdown\n",
678044802ffSap25164 (char *)tzp->zone_name, K_TO_C(tmp));
6792bda830bSap25164
6802bda830bSap25164 tzmon_do_shutdown();
6812bda830bSap25164 }
6822bda830bSap25164
6832bda830bSap25164 /* _CRT handling */
6842bda830bSap25164 if (tzp->crt > 0 && tmp >= tzp->crt) {
685044802ffSap25164 cmn_err(CE_WARN,
686044802ffSap25164 "tzmon: Thermal zone (%s) is critically hot (%d C); "
687044802ffSap25164 "initiating rapid shutdown\n",
688044802ffSap25164 (char *)tzp->zone_name, K_TO_C(tmp));
6892bda830bSap25164
6902bda830bSap25164 /* shut down (fairly) immediately */
6912bda830bSap25164 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
6922bda830bSap25164 }
6932bda830bSap25164
6942bda830bSap25164 /*
6952bda830bSap25164 * use the temperature to determine whether the thermal zone
6962bda830bSap25164 * is at a new active cooling threshold level
6972bda830bSap25164 */
6982bda830bSap25164 for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) {
6992bda830bSap25164 if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) {
7002bda830bSap25164 new_level = level;
7012bda830bSap25164 break;
7022bda830bSap25164 }
7032bda830bSap25164 }
7042bda830bSap25164
7052bda830bSap25164 /*
7062bda830bSap25164 * if the active cooling threshold has changed, turn off the
7072bda830bSap25164 * devices associated with the old one and turn on the new one
7082bda830bSap25164 */
7092bda830bSap25164 if (tzp->current_level != new_level) {
7102bda830bSap25164 if ((tzp->current_level >= 0) &&
7112bda830bSap25164 (tzp->al[tzp->current_level].Length != 0))
7129ed7afb7Sap25164 tzmon_set_power(tzp->al[tzp->current_level], 0,
7139ed7afb7Sap25164 (char *)tzp->zone_name);
7142bda830bSap25164
7152bda830bSap25164 if ((new_level >= 0) &&
7162bda830bSap25164 (tzp->al[new_level].Length != 0))
7179ed7afb7Sap25164 tzmon_set_power(tzp->al[new_level], 1,
7189ed7afb7Sap25164 (char *)tzp->zone_name);
7192bda830bSap25164
7202bda830bSap25164 tzp->current_level = new_level;
7212bda830bSap25164 }
7222bda830bSap25164
7232bda830bSap25164 mutex_exit(&tzp->lock);
7242bda830bSap25164 }
7252bda830bSap25164
7262bda830bSap25164
7272bda830bSap25164 /*
7282bda830bSap25164 * tzmon_do_shutdown
7292bda830bSap25164 * Initiates shutdown by sending a SIGPWR signal to init.
7302bda830bSap25164 */
7312bda830bSap25164 static void
tzmon_do_shutdown(void)7322bda830bSap25164 tzmon_do_shutdown(void)
7332bda830bSap25164 {
7342bda830bSap25164 proc_t *initpp;
7352bda830bSap25164
7362bda830bSap25164 mutex_enter(&pidlock);
7372bda830bSap25164 initpp = prfind(P_INITPID);
7382bda830bSap25164 mutex_exit(&pidlock);
7392bda830bSap25164
7402bda830bSap25164 /* if we can't find init, just halt */
7412bda830bSap25164 if (initpp == NULL) {
7422bda830bSap25164 mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
7432bda830bSap25164 }
7442bda830bSap25164
7452bda830bSap25164 /* graceful shutdown with inittab and all getting involved */
7462bda830bSap25164 psignal(initpp, SIGPWR);
7472bda830bSap25164 }
748