xref: /onnv-gate/usr/src/uts/i86pc/io/tzmon/tzmon.c (revision 11225:eb6056029d84)
11884Sap25164 /*
21884Sap25164  * CDDL HEADER START
31884Sap25164  *
41884Sap25164  * The contents of this file are subject to the terms of the
51884Sap25164  * Common Development and Distribution License (the "License").
61884Sap25164  * You may not use this file except in compliance with the License.
71884Sap25164  *
81884Sap25164  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91884Sap25164  * or http://www.opensolaris.org/os/licensing.
101884Sap25164  * See the License for the specific language governing permissions
111884Sap25164  * and limitations under the License.
121884Sap25164  *
131884Sap25164  * When distributing Covered Code, include this CDDL HEADER in each
141884Sap25164  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151884Sap25164  * If applicable, add the following below this CDDL HEADER, with the
161884Sap25164  * fields enclosed by brackets "[]" replaced with your own identifying
171884Sap25164  * information: Portions Copyright [yyyy] [name of copyright owner]
181884Sap25164  *
191884Sap25164  * CDDL HEADER END
201884Sap25164  */
211884Sap25164 
221884Sap25164 /*
239269SDana.Myers@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241884Sap25164  * Use is subject to license terms.
251884Sap25164  */
261884Sap25164 
271884Sap25164 /*
281884Sap25164  * Solaris x86 ACPI ThermalZone Monitor
291884Sap25164  */
301884Sap25164 
311884Sap25164 
321884Sap25164 #include <sys/errno.h>
331884Sap25164 #include <sys/conf.h>
341884Sap25164 #include <sys/modctl.h>
351884Sap25164 #include <sys/open.h>
361884Sap25164 #include <sys/stat.h>
371884Sap25164 #include <sys/ddi.h>
381884Sap25164 #include <sys/sunddi.h>
391884Sap25164 #include <sys/ksynch.h>
401884Sap25164 #include <sys/uadmin.h>
411884Sap25164 #include <sys/acpi/acpi.h>
421884Sap25164 #include <sys/acpica.h>
431884Sap25164 #include <sys/sdt.h>
441884Sap25164 
451884Sap25164 #include "tzmon.h"
461884Sap25164 
471884Sap25164 
481884Sap25164 #define	TZMON_ENUM_TRIP_POINTS	1
491884Sap25164 #define	TZMON_ENUM_DEV_LISTS	2
501884Sap25164 #define	TZMON_ENUM_ALL		(TZMON_ENUM_TRIP_POINTS	| TZMON_ENUM_DEV_LISTS)
511884Sap25164 
521977Sap25164 /*
531977Sap25164  * TZ_TASKQ_NAME_LEN is precisely the length of the string "AcpiThermalMonitor"
541977Sap25164  * plus a two-digit instance number plus a NULL.  If the taskq name is changed
551977Sap25164  * (particularly if it is lengthened), then this value needs to change.
561977Sap25164  */
571977Sap25164 #define	TZ_TASKQ_NAME_LEN	21
581977Sap25164 
591977Sap25164 /*
601977Sap25164  * Kelvin to Celsius conversion
611977Sap25164  * The formula for converting degrees Kelvin to degrees Celsius is
621977Sap25164  * C = K - 273.15 (we round to 273.2).  The unit for thermal zone
631977Sap25164  * temperatures is tenths of a degree Kelvin.  Use tenth of a degree
641977Sap25164  * to convert, then make a whole number out of it.
651977Sap25164  */
661977Sap25164 #define	K_TO_C(temp)		(((temp) - 2732) / 10)
671977Sap25164 
681884Sap25164 
691884Sap25164 /* cb_ops or dev_ops forward declarations */
701884Sap25164 static	int	tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
711884Sap25164     void *arg, void **result);
721884Sap25164 static	int	tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
731884Sap25164 static	int	tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
741884Sap25164 
751884Sap25164 /* other forward declarations */
761884Sap25164 static void tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx);
771884Sap25164 static void tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv);
781884Sap25164 static thermal_zone_t *tzmon_alloc_zone();
791884Sap25164 static void tzmon_free_zone_list();
801884Sap25164 static void tzmon_discard_buffers(thermal_zone_t *tzp);
811884Sap25164 static void tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp,
821884Sap25164 	int enum_flag);
831884Sap25164 static ACPI_STATUS tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest,
841884Sap25164     void *ctx, void **rv);
851884Sap25164 static void tzmon_find_zones(void);
861884Sap25164 static void tzmon_monitor(void *ctx);
873609Sap25164 static void tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name);
883609Sap25164 static void tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name);
891884Sap25164 static void tzmon_eval_zone(thermal_zone_t *tzp);
901884Sap25164 static void tzmon_do_shutdown(void);
911884Sap25164 
921884Sap25164 extern void halt(char *);
931884Sap25164 
941884Sap25164 static struct cb_ops	tzmon_cb_ops = {
951884Sap25164 	nodev,			/* no open routine	*/
961884Sap25164 	nodev,			/* no close routine	*/
971884Sap25164 	nodev,			/* not a block driver	*/
981884Sap25164 	nodev,			/* no print routine	*/
991884Sap25164 	nodev,			/* no dump routine	*/
1001884Sap25164 	nodev,			/* no read routine	*/
1011884Sap25164 	nodev,			/* no write routine	*/
1021884Sap25164 	nodev,			/* no ioctl routine	*/
1031884Sap25164 	nodev,			/* no devmap routine	*/
1041884Sap25164 	nodev,			/* no mmap routine	*/
1051884Sap25164 	nodev,			/* no segmap routine	*/
1061884Sap25164 	nochpoll,		/* no chpoll routine	*/
1071884Sap25164 	ddi_prop_op,
1081884Sap25164 	0,			/* not a STREAMS driver	*/
1091884Sap25164 	D_NEW | D_MP,		/* safe for multi-thread/multi-processor */
1101884Sap25164 };
1111884Sap25164 
1121884Sap25164 static struct dev_ops tzmon_ops = {
1131884Sap25164 	DEVO_REV,		/* devo_rev */
1141884Sap25164 	0,			/* devo_refcnt */
1151884Sap25164 	tzmon_getinfo,		/* devo_getinfo */
1161884Sap25164 	nulldev,		/* devo_identify */
1171884Sap25164 	nulldev,		/* devo_probe */
1181884Sap25164 	tzmon_attach,		/* devo_attach */
1191884Sap25164 	tzmon_detach,		/* devo_detach */
1201884Sap25164 	nodev,			/* devo_reset */
1211884Sap25164 	&tzmon_cb_ops,		/* devo_cb_ops */
1221884Sap25164 	(struct bus_ops *)0,	/* devo_bus_ops */
1231884Sap25164 	NULL,			/* devo_power */
1247656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* devo_quiesce */
1251884Sap25164 };
1261884Sap25164 
1271884Sap25164 extern	struct	mod_ops mod_driverops;
1281884Sap25164 
1291884Sap25164 static	struct modldrv modldrv = {
1301884Sap25164 	&mod_driverops,
1315173Sap25164 	"ACPI Thermal Zone Monitor",
1321884Sap25164 	&tzmon_ops,
1331884Sap25164 };
1341884Sap25164 
1351884Sap25164 static	struct modlinkage modlinkage = {
1361884Sap25164 	MODREV_1,		/* MODREV_1 indicated by manual */
1371884Sap25164 	(void *)&modldrv,
1381884Sap25164 	NULL,			/* termination of list of linkage structures */
1391884Sap25164 };
1401884Sap25164 
1411884Sap25164 /* globals for this module */
1421884Sap25164 static dev_info_t	*tzmon_dip;
1431884Sap25164 static thermal_zone_t	*zone_list;
1441884Sap25164 static int		zone_count;
1451884Sap25164 static kmutex_t		zone_list_lock;
1461884Sap25164 static kcondvar_t	zone_list_condvar;
1471884Sap25164 
1481884Sap25164 
1491884Sap25164 /*
1501884Sap25164  * _init, _info, and _fini support loading and unloading the driver.
1511884Sap25164  */
1521884Sap25164 int
_init(void)1531884Sap25164 _init(void)
1541884Sap25164 {
1551884Sap25164 	return (mod_install(&modlinkage));
1561884Sap25164 }
1571884Sap25164 
1581884Sap25164 
1591884Sap25164 int
_info(struct modinfo * modinfop)1601884Sap25164 _info(struct modinfo *modinfop)
1611884Sap25164 {
1621884Sap25164 	return (mod_info(&modlinkage, modinfop));
1631884Sap25164 }
1641884Sap25164 
1651884Sap25164 
1661884Sap25164 int
_fini(void)1671884Sap25164 _fini(void)
1681884Sap25164 {
1691884Sap25164 	return (mod_remove(&modlinkage));
1701884Sap25164 }
1711884Sap25164 
1721884Sap25164 
1731884Sap25164 static int
tzmon_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1741884Sap25164 tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1751884Sap25164 {
1761884Sap25164 	if (cmd != DDI_ATTACH)
1771884Sap25164 		return (DDI_FAILURE);
1781884Sap25164 
1791884Sap25164 	if (tzmon_dip != NULL)
1801884Sap25164 		return (DDI_FAILURE);
1811884Sap25164 
1821884Sap25164 	/*
1831884Sap25164 	 * Check to see if ACPI CA services are available
1841884Sap25164 	 */
1851884Sap25164 	if (AcpiSubsystemStatus() != AE_OK)
1861884Sap25164 		return (DDI_FAILURE);
1871884Sap25164 
1881884Sap25164 	mutex_init(&zone_list_lock, NULL, MUTEX_DRIVER, NULL);
1891884Sap25164 	cv_init(&zone_list_condvar, NULL, CV_DRIVER, NULL);
1901884Sap25164 
1911884Sap25164 	tzmon_find_zones();
1921884Sap25164 	mutex_enter(&zone_list_lock);
1931884Sap25164 	if (zone_count < 1) {
1941884Sap25164 		mutex_exit(&zone_list_lock);
1951884Sap25164 		mutex_destroy(&zone_list_lock);
1961884Sap25164 		cv_destroy(&zone_list_condvar);
1971884Sap25164 		return (DDI_FAILURE);
1981884Sap25164 	}
1991884Sap25164 	mutex_exit(&zone_list_lock);
2001884Sap25164 
2011884Sap25164 	if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 0,
2021884Sap25164 	    DDI_PSEUDO, 0) == DDI_FAILURE) {
2031884Sap25164 		tzmon_free_zone_list();
2041884Sap25164 		mutex_destroy(&zone_list_lock);
2051884Sap25164 		cv_destroy(&zone_list_condvar);
2061884Sap25164 		return (DDI_FAILURE);
2071884Sap25164 	}
2081884Sap25164 
2091884Sap25164 	tzmon_dip = dip;
2101884Sap25164 
2111884Sap25164 	ddi_report_dev(dip);
2121884Sap25164 
2131884Sap25164 	return (DDI_SUCCESS);
2141884Sap25164 }
2151884Sap25164 
2161884Sap25164 
2171884Sap25164 /*ARGSUSED*/
2181884Sap25164 static int
tzmon_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2191884Sap25164 tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2201884Sap25164 {
2211884Sap25164 	int error;
2221884Sap25164 
2231884Sap25164 	switch (infocmd) {
2241884Sap25164 	case DDI_INFO_DEVT2DEVINFO:
2251884Sap25164 		*result = tzmon_dip;
2261884Sap25164 		if (tzmon_dip == NULL)
2271884Sap25164 			error = DDI_FAILURE;
2281884Sap25164 		else
2291884Sap25164 			error = DDI_SUCCESS;
2301884Sap25164 		break;
2311884Sap25164 	case DDI_INFO_DEVT2INSTANCE:
2321884Sap25164 		*result = 0;
2331884Sap25164 		error = DDI_SUCCESS;
2341884Sap25164 		break;
2351884Sap25164 	default:
2361884Sap25164 		*result = NULL;
2371884Sap25164 		error = DDI_FAILURE;
2381884Sap25164 	}
2391884Sap25164 
2401884Sap25164 	return (error);
2411884Sap25164 }
2421884Sap25164 
2431884Sap25164 
2441884Sap25164 static int
tzmon_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2451884Sap25164 tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2461884Sap25164 {
2471977Sap25164 	thermal_zone_t *tzp = zone_list;
2481977Sap25164 
2491884Sap25164 	if (cmd != DDI_DETACH)
2501884Sap25164 		return (DDI_FAILURE);
2511884Sap25164 
2521977Sap25164 	/* free allocated thermal zone name(s) */
2531977Sap25164 	while (tzp != NULL) {
2541977Sap25164 		AcpiOsFree(tzp->zone_name);
2551977Sap25164 		tzp = tzp->next;
2561977Sap25164 	}
2571977Sap25164 
2581884Sap25164 	/* discard zone list assets */
2591884Sap25164 	tzmon_free_zone_list();
2601884Sap25164 
2611884Sap25164 	ddi_remove_minor_node(dip, NULL);
2621884Sap25164 	tzmon_dip = NULL;
2631884Sap25164 
2641884Sap25164 	mutex_destroy(&zone_list_lock);
2651884Sap25164 	cv_destroy(&zone_list_condvar);
2661884Sap25164 
2671884Sap25164 	return (DDI_SUCCESS);
2681884Sap25164 }
2691884Sap25164 
2701884Sap25164 
2711884Sap25164 /*
2721884Sap25164  * tzmon_notify_zone
2731884Sap25164  * Thermal zone notification handler.
2741884Sap25164  */
2751884Sap25164 static void
tzmon_notify_zone(ACPI_HANDLE obj,UINT32 val,void * ctx)2761884Sap25164 tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx)
2771884Sap25164 {
2781884Sap25164 	thermal_zone_t *tzp = (thermal_zone_t *)ctx;
2791884Sap25164 
2801884Sap25164 	switch (val) {
2811884Sap25164 	case 0x80:	/* Thermal Zone status changed */
2821884Sap25164 		tzmon_eval_zone(tzp);
2831884Sap25164 		break;
2841884Sap25164 	case 0x81:	/* Thermal Zone trip points changed */
2851884Sap25164 		tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_TRIP_POINTS);
2861884Sap25164 		break;
2871884Sap25164 	case 0x82:	/* Device Lists changed */
2881884Sap25164 		tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_DEV_LISTS);
2891884Sap25164 		break;
2901884Sap25164 	case 0x83:	/* Thermal Relationship Table changed */
2911884Sap25164 		/* not handling _TRT objects, so not handling this event */
2923609Sap25164 		DTRACE_PROBE1(trt__change, char *, (char *)tzp->zone_name);
2931884Sap25164 		break;
2941884Sap25164 	default:
2951884Sap25164 		break;
2961884Sap25164 	}
2971884Sap25164 }
2981884Sap25164 
2991884Sap25164 
3001884Sap25164 /*
3011884Sap25164  * tzmon_eval_int
3021884Sap25164  * Evaluate the object/method as an integer.
3031884Sap25164  */
3041884Sap25164 static void
tzmon_eval_int(ACPI_HANDLE obj,char * method,int * rv)3051884Sap25164 tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv)
3061884Sap25164 {
3071884Sap25164 
3081884Sap25164 	if (acpica_eval_int(obj, method, rv) != AE_OK)
3091884Sap25164 		*rv = -1;
3101884Sap25164 }
3111884Sap25164 
3121884Sap25164 
3131884Sap25164 /*
3141884Sap25164  * tzmon_alloc_zone
3151884Sap25164  * Allocate memory for the zone structure and initialize it lock mutex.
3161884Sap25164  */
3171884Sap25164 static thermal_zone_t *
tzmon_alloc_zone()3181884Sap25164 tzmon_alloc_zone()
3191884Sap25164 {
3201884Sap25164 	thermal_zone_t *tzp;
3211884Sap25164 
3221884Sap25164 	tzp = kmem_zalloc(sizeof (thermal_zone_t), KM_SLEEP);
3231884Sap25164 	mutex_init(&tzp->lock, NULL, MUTEX_DRIVER, NULL);
3241884Sap25164 
3251884Sap25164 	return (tzp);
3261884Sap25164 }
3271884Sap25164 
3281884Sap25164 
3291884Sap25164 /*
3301884Sap25164  * tzmon_free_zone_list
3311884Sap25164  * Free the zone list, either because attach failed or detach initiated.
3321884Sap25164  */
3331884Sap25164 static void
tzmon_free_zone_list()3341884Sap25164 tzmon_free_zone_list()
3351884Sap25164 {
3361884Sap25164 	thermal_zone_t *tzp = zone_list;
3371884Sap25164 
3381884Sap25164 	while (tzp != NULL) {
3391884Sap25164 		thermal_zone_t *next;
3401884Sap25164 
3411884Sap25164 		mutex_enter(&tzp->lock);
3421884Sap25164 
3431884Sap25164 		/*
3441884Sap25164 		 * Remove the notify handler for the zone.  Not much to
3451884Sap25164 		 * do if this fails (since we are on our way out), so
3461884Sap25164 		 * just ignore failure.
3471884Sap25164 		 */
3481884Sap25164 		(void) AcpiRemoveNotifyHandler(tzp->obj, ACPI_DEVICE_NOTIFY,
3491884Sap25164 		    tzmon_notify_zone);
3501884Sap25164 
3511884Sap25164 		/* Shut down monitor thread, if running */
3521884Sap25164 		if (tzp->taskq != NULL) {
3531884Sap25164 			tzp->polling_period = 0;
3541884Sap25164 			cv_broadcast(&zone_list_condvar);
3551884Sap25164 
3561884Sap25164 			/* Drop mutex to allow the thread to run */
3571884Sap25164 			mutex_exit(&tzp->lock);
3581884Sap25164 			ddi_taskq_destroy(tzp->taskq);
3591884Sap25164 			mutex_enter(&tzp->lock);
3601884Sap25164 		}
3611884Sap25164 
3621884Sap25164 		tzmon_discard_buffers(tzp);
3631884Sap25164 		mutex_exit(&tzp->lock);
3641884Sap25164 		mutex_destroy(&tzp->lock);
3651884Sap25164 
3661884Sap25164 		next = tzp->next;
3671884Sap25164 		kmem_free(tzp, sizeof (thermal_zone_t));
3681884Sap25164 		tzp = next;
3691884Sap25164 	}
3701884Sap25164 }
3711884Sap25164 
3721884Sap25164 
3731884Sap25164 static void
tzmon_discard_buffers(thermal_zone_t * tzp)3741884Sap25164 tzmon_discard_buffers(thermal_zone_t *tzp)
3751884Sap25164 {
3761884Sap25164 	int level;
3771884Sap25164 
3781884Sap25164 	for (level = 0; level < TZ_NUM_LEVELS; level++) {
3791884Sap25164 		if (tzp->al[level].Pointer != NULL)
3801884Sap25164 			AcpiOsFree(tzp->al[level].Pointer);
3811884Sap25164 	}
3821884Sap25164 
3831884Sap25164 	if (tzp->psl.Pointer != NULL)
3841884Sap25164 		AcpiOsFree(tzp->psl.Pointer);
3851884Sap25164 }
3861884Sap25164 
3871884Sap25164 
3881884Sap25164 /*
3891884Sap25164  * tzmon_enumerate_zone
3901884Sap25164  * Enumerates the contents of a thermal zone and updates passed-in
3911884Sap25164  * thermal_zone or creates a new one if tzp is NULL. Newly-created
3921884Sap25164  * zones are linked into the global zone_list.
3931884Sap25164  */
3941884Sap25164 static void
tzmon_enumerate_zone(ACPI_HANDLE obj,thermal_zone_t * tzp,int enum_flag)3951884Sap25164 tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp, int enum_flag)
3961884Sap25164 {
3971884Sap25164 	ACPI_STATUS status;
3981977Sap25164 	ACPI_BUFFER zone_name;
3991884Sap25164 	int	level;
4001977Sap25164 	int	instance;
4011884Sap25164 	char	abuf[5];
4021884Sap25164 
4031884Sap25164 	/*
4041884Sap25164 	 * Newly-created zones and existing zones both require
4051884Sap25164 	 * some individual attention.
4061884Sap25164 	 */
4071884Sap25164 	if (tzp == NULL) {
4081884Sap25164 		/* New zone required */
4091884Sap25164 		tzp = tzmon_alloc_zone();
4101884Sap25164 		mutex_enter(&zone_list_lock);
4111884Sap25164 		tzp->next = zone_list;
4121884Sap25164 		zone_list = tzp;
4131977Sap25164 
4141977Sap25164 		/*
4151977Sap25164 		 * It is exceedingly unlikely that instance will exceed 99.
4161977Sap25164 		 * However, if it does, this will cause problems when
4171977Sap25164 		 * creating the taskq for this thermal zone.
4181977Sap25164 		 */
4191977Sap25164 		instance = zone_count;
4201884Sap25164 		zone_count++;
4211884Sap25164 		mutex_exit(&zone_list_lock);
4221884Sap25164 		mutex_enter(&tzp->lock);
4231884Sap25164 		tzp->obj = obj;
4241884Sap25164 
4251884Sap25164 		/*
4261884Sap25164 		 * Set to a low level.  Will get set to the actual
4271884Sap25164 		 * current power level when the thread monitor polls
4281884Sap25164 		 * the current temperature.
4291884Sap25164 		 */
4301884Sap25164 		tzp->current_level = 0;
4311884Sap25164 
4321977Sap25164 		/* Get the zone name in case we need to display it later */
4331977Sap25164 		zone_name.Length = ACPI_ALLOCATE_BUFFER;
4341977Sap25164 		zone_name.Pointer = NULL;
4351977Sap25164 
4361977Sap25164 		status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &zone_name);
4371977Sap25164 		ASSERT(status == AE_OK);
4381977Sap25164 
4391977Sap25164 		tzp->zone_name = zone_name.Pointer;
4401977Sap25164 
4411884Sap25164 		status = AcpiInstallNotifyHandler(obj, ACPI_DEVICE_NOTIFY,
4421884Sap25164 		    tzmon_notify_zone, (void *)tzp);
4431884Sap25164 		ASSERT(status == AE_OK);
4441884Sap25164 	} else {
4451884Sap25164 		/* Existing zone - toss out allocated items */
4461884Sap25164 		mutex_enter(&tzp->lock);
4471884Sap25164 		ASSERT(tzp->obj == obj);
4481884Sap25164 
4491884Sap25164 		if (enum_flag & TZMON_ENUM_DEV_LISTS)
4501884Sap25164 			tzmon_discard_buffers(tzp);
4511884Sap25164 	}
4521884Sap25164 
4531884Sap25164 	if (enum_flag & TZMON_ENUM_TRIP_POINTS) {
4541884Sap25164 		for (level = 0; level < TZ_NUM_LEVELS; level++) {
4551890Sap25164 			(void) snprintf(abuf, 5, "_AC%d", level);
4561884Sap25164 			tzmon_eval_int(obj, abuf, &tzp->ac[level]);
4571884Sap25164 
4581884Sap25164 		}
4591884Sap25164 
4601884Sap25164 		tzmon_eval_int(obj, "_CRT", &tzp->crt);
4611884Sap25164 		tzmon_eval_int(obj, "_HOT", &tzp->hot);
4621884Sap25164 		tzmon_eval_int(obj, "_PSV", &tzp->psv);
4631884Sap25164 	}
4641884Sap25164 
4651884Sap25164 	if (enum_flag & TZMON_ENUM_DEV_LISTS) {
4661884Sap25164 		for (level = 0; level < TZ_NUM_LEVELS; level++) {
4671884Sap25164 			if (tzp->ac[level] == -1) {
4681884Sap25164 				tzp->al[level].Length = 0;
4691884Sap25164 				tzp->al[level].Pointer = NULL;
4701884Sap25164 			} else {
4711890Sap25164 				(void) snprintf(abuf, 5, "_AL%d", level);
4721884Sap25164 				tzp->al[level].Length = ACPI_ALLOCATE_BUFFER;
4731884Sap25164 				tzp->al[level].Pointer = NULL;
4749269SDana.Myers@Sun.COM 				if (AcpiEvaluateObjectTyped(obj, abuf, NULL,
4759269SDana.Myers@Sun.COM 				    &tzp->al[level], ACPI_TYPE_PACKAGE) !=
4769269SDana.Myers@Sun.COM 				    AE_OK) {
4773609Sap25164 					DTRACE_PROBE2(alx__missing, int, level,
4783609Sap25164 					    char *, (char *)tzp->zone_name);
4791884Sap25164 
4801884Sap25164 					tzp->al[level].Length = 0;
4811884Sap25164 					tzp->al[level].Pointer = NULL;
4821884Sap25164 				}
4831884Sap25164 			}
4841884Sap25164 		}
4851884Sap25164 
4861884Sap25164 		tzp->psl.Length = ACPI_ALLOCATE_BUFFER;
4871884Sap25164 		tzp->psl.Pointer = NULL;
4889269SDana.Myers@Sun.COM 		(void) AcpiEvaluateObjectTyped(obj, "_PSL", NULL, &tzp->psl,
4899269SDana.Myers@Sun.COM 		    ACPI_TYPE_PACKAGE);
4901884Sap25164 	}
4911884Sap25164 
4921884Sap25164 	tzmon_eval_int(obj, "_TC1", &tzp->tc1);
4931884Sap25164 	tzmon_eval_int(obj, "_TC2", &tzp->tc2);
4941884Sap25164 	tzmon_eval_int(obj, "_TSP", &tzp->tsp);
4951884Sap25164 	tzmon_eval_int(obj, "_TZP", &tzp->tzp);
4961884Sap25164 
4971884Sap25164 	if (tzp->tzp == 0) {
4981884Sap25164 		tzp->polling_period = 0;
4991884Sap25164 	} else {
5001884Sap25164 		if (tzp->tzp < 0)
5011884Sap25164 			tzp->polling_period = TZ_DEFAULT_PERIOD;
5021884Sap25164 		else
5031884Sap25164 			tzp->polling_period = tzp->tzp/10;
5041884Sap25164 
5051884Sap25164 		/* start monitor thread if needed */
5061884Sap25164 		if (tzp->taskq == NULL) {
5071977Sap25164 			char taskq_name[TZ_TASKQ_NAME_LEN];
5081977Sap25164 
5091977Sap25164 			(void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN,
5101977Sap25164 			    "AcpiThermalMonitor%02d", instance);
5111884Sap25164 			tzp->taskq = ddi_taskq_create(tzmon_dip,
5121977Sap25164 			    taskq_name, 1, TASKQ_DEFAULTPRI, 0);
5131884Sap25164 			if (tzp->taskq == NULL) {
5141884Sap25164 				tzp->polling_period = 0;
5153609Sap25164 				cmn_err(CE_WARN, "tzmon: could not create "
5163609Sap25164 				    "monitor thread for thermal zone %s - "
5173609Sap25164 				    "monitor by notify only",
5183609Sap25164 				    (char *)tzp->zone_name);
5191884Sap25164 			} else {
5201890Sap25164 				(void) ddi_taskq_dispatch(tzp->taskq,
5211890Sap25164 				    tzmon_monitor, tzp, DDI_SLEEP);
5221884Sap25164 			}
5231884Sap25164 		}
5241884Sap25164 	}
5251884Sap25164 
5261884Sap25164 	mutex_exit(&tzp->lock);
5271884Sap25164 }
5281884Sap25164 
5291884Sap25164 
5301884Sap25164 /*
5311884Sap25164  * tzmon_zone_callback
5321884Sap25164  * Enumerate the thermal zone if it has a _TMP (current thermal zone
5331884Sap25164  * operating temperature) method.
5341884Sap25164  */
5351884Sap25164 /*ARGSUSED*/
5361884Sap25164 static ACPI_STATUS
tzmon_zone_callback(ACPI_HANDLE obj,UINT32 nest,void * ctx,void ** rv)5371884Sap25164 tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv)
5381884Sap25164 {
5391884Sap25164 	ACPI_HANDLE tmpobj;
5401884Sap25164 
5411884Sap25164 	/*
5421884Sap25164 	 * We get both ThermalZone() and Scope(\_TZ) objects here;
5431884Sap25164 	 * look for _TMP (without which a zone is invalid) to pick
5441884Sap25164 	 * between them (and ignore invalid zones)
5451884Sap25164 	 */
5461884Sap25164 	if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) {
5471884Sap25164 		tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL);
5481884Sap25164 	}
5491884Sap25164 
5501884Sap25164 	return (AE_OK);
5511884Sap25164 }
5521884Sap25164 
5531884Sap25164 
5541884Sap25164 /*
5551884Sap25164  * tzmon_find_zones
5561884Sap25164  * Find all of the thermal zones by calling a ACPICA function that
5571884Sap25164  * walks the ACPI namespace and invokes a callback for each thermal
5581884Sap25164  * object found.
5591884Sap25164  */
5601884Sap25164 static void
tzmon_find_zones()5611884Sap25164 tzmon_find_zones()
5621884Sap25164 {
5631884Sap25164 	ACPI_STATUS status;
5641884Sap25164 	int retval;
5651884Sap25164 
5661884Sap25164 	status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
567*11225SDana.Myers@Sun.COM 	    8, tzmon_zone_callback, NULL, NULL, (void **)&retval);
5681884Sap25164 
5691884Sap25164 	ASSERT(status == AE_OK);
5701884Sap25164 }
5711884Sap25164 
5721884Sap25164 
5731884Sap25164 /*
5741884Sap25164  * tzmon_monitor
5751884Sap25164  * Run as a separate thread, this wakes according to polling period and
5761884Sap25164  * checks particular objects in the thermal zone.  One instance per
5771884Sap25164  * thermal zone.
5781884Sap25164  */
5791884Sap25164 static void
tzmon_monitor(void * ctx)5801884Sap25164 tzmon_monitor(void *ctx)
5811884Sap25164 {
5821884Sap25164 	thermal_zone_t *tzp = (thermal_zone_t *)ctx;
5831884Sap25164 	clock_t ticks;
5841884Sap25164 
5851884Sap25164 	do {
5861884Sap25164 		/* Check out the zone */
5871884Sap25164 		tzmon_eval_zone(tzp);
5881884Sap25164 
5891884Sap25164 		/* Go back to sleep */
5901884Sap25164 		mutex_enter(&tzp->lock);
5911884Sap25164 		ticks = drv_usectohz(tzp->polling_period * 1000000);
5921884Sap25164 		if (ticks > 0)
59311066Srafael.vanoni@sun.com 			(void) cv_reltimedwait(&zone_list_condvar,
59411066Srafael.vanoni@sun.com 			    &tzp->lock, ticks, TR_CLOCK_TICK);
5951884Sap25164 		mutex_exit(&tzp->lock);
5961884Sap25164 	} while (ticks > 0);
5971884Sap25164 }
5981884Sap25164 
5991884Sap25164 
6001884Sap25164 /*
6011884Sap25164  * tzmon_set_power_device
6021884Sap25164  */
6031884Sap25164 static void
tzmon_set_power_device(ACPI_HANDLE dev,int on_off,char * tz_name)6043609Sap25164 tzmon_set_power_device(ACPI_HANDLE dev, int on_off, char *tz_name)
6051884Sap25164 {
6061884Sap25164 	ACPI_BUFFER rb;
6071884Sap25164 	ACPI_OBJECT *pr0;
6081884Sap25164 	ACPI_STATUS status;
6091884Sap25164 	int i;
6101884Sap25164 
6111884Sap25164 	rb.Length = ACPI_ALLOCATE_BUFFER;
6121884Sap25164 	rb.Pointer = NULL;
6139269SDana.Myers@Sun.COM 	status = AcpiEvaluateObjectTyped(dev, "_PR0", NULL, &rb,
6149269SDana.Myers@Sun.COM 	    ACPI_TYPE_PACKAGE);
6151884Sap25164 	if (status != AE_OK) {
6163609Sap25164 		DTRACE_PROBE2(alx__error, int, 2, char *, tz_name);
6171884Sap25164 		return;
6181884Sap25164 	}
6191884Sap25164 
6201884Sap25164 	pr0 = ((ACPI_OBJECT *)rb.Pointer);
6211884Sap25164 	for (i = 0; i < pr0->Package.Count; i++) {
6221884Sap25164 		status = AcpiEvaluateObject(
6231884Sap25164 		    pr0->Package.Elements[i].Reference.Handle,
6241884Sap25164 		    on_off ? "_ON" : "_OFF", NULL, NULL);
6251884Sap25164 		if (status != AE_OK) {
6263609Sap25164 			DTRACE_PROBE2(alx__error, int, 4, char *, tz_name);
6271884Sap25164 		}
6281884Sap25164 	}
6291884Sap25164 
6301884Sap25164 	AcpiOsFree(rb.Pointer);
6311884Sap25164 }
6321884Sap25164 
6331884Sap25164 
6341884Sap25164 /*
6351884Sap25164  * tzmon_set_power
6361884Sap25164  * Turn on or turn off all devices in the supplied list.
6371884Sap25164  */
6381884Sap25164 static void
tzmon_set_power(ACPI_BUFFER devlist,int on_off,char * tz_name)6393609Sap25164 tzmon_set_power(ACPI_BUFFER devlist, int on_off, char *tz_name)
6401884Sap25164 {
6411884Sap25164 	ACPI_OBJECT *devs;
6421884Sap25164 	int i;
6431884Sap25164 
6441884Sap25164 	devs = ((ACPI_OBJECT *)devlist.Pointer);
6451884Sap25164 	if (devs->Type != ACPI_TYPE_PACKAGE) {
6463609Sap25164 		DTRACE_PROBE2(alx__error, int, 1, char *, tz_name);
6471884Sap25164 		return;
6481884Sap25164 	}
6491884Sap25164 
6501884Sap25164 	for (i = 0; i < devs->Package.Count; i++)
6511884Sap25164 		tzmon_set_power_device(
6523609Sap25164 		    devs->Package.Elements[i].Reference.Handle, on_off,
6533609Sap25164 		    tz_name);
6541884Sap25164 }
6551884Sap25164 
6561884Sap25164 
6571884Sap25164 /*
6581884Sap25164  * tzmon_eval_zone
6591884Sap25164  * Evaluate the current conditions within the thermal zone.
6601884Sap25164  */
6611884Sap25164 static void
tzmon_eval_zone(thermal_zone_t * tzp)6621884Sap25164 tzmon_eval_zone(thermal_zone_t *tzp)
6631884Sap25164 {
6641884Sap25164 	int tmp, new_level, level;
6651884Sap25164 
6661884Sap25164 	mutex_enter(&tzp->lock);
6671884Sap25164 
6681884Sap25164 	/* get the current temperature from ACPI */
6691884Sap25164 	tzmon_eval_int(tzp->obj, "_TMP", &tmp);
6703609Sap25164 	DTRACE_PROBE4(tz__temp, int, tmp, int, tzp->crt, int, tzp->hot,
6713609Sap25164 	    char *, (char *)tzp->zone_name);
6721884Sap25164 
6731884Sap25164 	/* _HOT handling */
6741884Sap25164 	if (tzp->hot > 0 && tmp >= tzp->hot) {
6751977Sap25164 		cmn_err(CE_WARN,
6761977Sap25164 		    "tzmon: Thermal zone (%s) is too hot (%d C); "
6775173Sap25164 		    "initiating shutdown\n",
6781977Sap25164 		    (char *)tzp->zone_name, K_TO_C(tmp));
6791884Sap25164 
6801884Sap25164 		tzmon_do_shutdown();
6811884Sap25164 	}
6821884Sap25164 
6831884Sap25164 	/* _CRT handling */
6841884Sap25164 	if (tzp->crt > 0 && tmp >= tzp->crt) {
6851977Sap25164 		cmn_err(CE_WARN,
6861977Sap25164 		    "tzmon: Thermal zone (%s) is critically hot (%d C); "
6875173Sap25164 		    "initiating rapid shutdown\n",
6881977Sap25164 		    (char *)tzp->zone_name, K_TO_C(tmp));
6891884Sap25164 
6901884Sap25164 		/* shut down (fairly) immediately */
6911884Sap25164 		mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
6921884Sap25164 	}
6931884Sap25164 
6941884Sap25164 	/*
6951884Sap25164 	 * use the temperature to determine whether the thermal zone
6961884Sap25164 	 * is at a new active cooling threshold level
6971884Sap25164 	 */
6981884Sap25164 	for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) {
6991884Sap25164 		if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) {
7001884Sap25164 			new_level = level;
7011884Sap25164 			break;
7021884Sap25164 		}
7031884Sap25164 	}
7041884Sap25164 
7051884Sap25164 	/*
7061884Sap25164 	 * if the active cooling threshold has changed, turn off the
7071884Sap25164 	 * devices associated with the old one and turn on the new one
7081884Sap25164 	 */
7091884Sap25164 	if (tzp->current_level != new_level) {
7101884Sap25164 		if ((tzp->current_level >= 0) &&
7111884Sap25164 		    (tzp->al[tzp->current_level].Length != 0))
7123609Sap25164 			tzmon_set_power(tzp->al[tzp->current_level], 0,
7133609Sap25164 			    (char *)tzp->zone_name);
7141884Sap25164 
7151884Sap25164 		if ((new_level >= 0) &&
7161884Sap25164 		    (tzp->al[new_level].Length != 0))
7173609Sap25164 			tzmon_set_power(tzp->al[new_level], 1,
7183609Sap25164 			    (char *)tzp->zone_name);
7191884Sap25164 
7201884Sap25164 		tzp->current_level = new_level;
7211884Sap25164 	}
7221884Sap25164 
7231884Sap25164 	mutex_exit(&tzp->lock);
7241884Sap25164 }
7251884Sap25164 
7261884Sap25164 
7271884Sap25164 /*
7281884Sap25164  * tzmon_do_shutdown
7291884Sap25164  * Initiates shutdown by sending a SIGPWR signal to init.
7301884Sap25164  */
7311884Sap25164 static void
tzmon_do_shutdown(void)7321884Sap25164 tzmon_do_shutdown(void)
7331884Sap25164 {
7341884Sap25164 	proc_t *initpp;
7351884Sap25164 
7361884Sap25164 	mutex_enter(&pidlock);
7371884Sap25164 	initpp = prfind(P_INITPID);
7381884Sap25164 	mutex_exit(&pidlock);
7391884Sap25164 
7401884Sap25164 	/* if we can't find init, just halt */
7411884Sap25164 	if (initpp == NULL) {
7421884Sap25164 		mdboot(A_REBOOT, AD_HALT, NULL, B_FALSE);
7431884Sap25164 	}
7441884Sap25164 
7451884Sap25164 	/* graceful shutdown with inittab and all getting involved */
7461884Sap25164 	psignal(initpp, SIGPWR);
7471884Sap25164 }
748