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 1532bda830bSap25164 _init(void) 1542bda830bSap25164 { 1552bda830bSap25164 return (mod_install(&modlinkage)); 1562bda830bSap25164 } 1572bda830bSap25164 1582bda830bSap25164 1592bda830bSap25164 int 1602bda830bSap25164 _info(struct modinfo *modinfop) 1612bda830bSap25164 { 1622bda830bSap25164 return (mod_info(&modlinkage, modinfop)); 1632bda830bSap25164 } 1642bda830bSap25164 1652bda830bSap25164 1662bda830bSap25164 int 1672bda830bSap25164 _fini(void) 1682bda830bSap25164 { 1692bda830bSap25164 return (mod_remove(&modlinkage)); 1702bda830bSap25164 } 1712bda830bSap25164 1722bda830bSap25164 1732bda830bSap25164 static int 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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