tzmon.c revision 044802ffd0b6258ef2221463b41121e8df53782f
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * CDDL HEADER START
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * The contents of this file are subject to the terms of the
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Common Development and Distribution License (the "License").
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * You may not use this file except in compliance with the License.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * or http://www.opensolaris.org/os/licensing.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * See the License for the specific language governing permissions
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * and limitations under the License.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * When distributing Covered Code, include this CDDL HEADER in each
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * If applicable, add the following below this CDDL HEADER, with the
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * fields enclosed by brackets "[]" replaced with your own identifying
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * information: Portions Copyright [yyyy] [name of copyright owner]
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * CDDL HEADER END
40c61268017ed628fcfb89162250c59130d14824Mike Christensen * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Use is subject to license terms.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Solaris x86 ACPI ThermalZone Monitor
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen#pragma ident "%Z%%M% %I% %E% SMI"
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen#define TZMON_ENUM_ALL (TZMON_ENUM_TRIP_POINTS | TZMON_ENUM_DEV_LISTS)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * TZ_TASKQ_NAME_LEN is precisely the length of the string "AcpiThermalMonitor"
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * plus a two-digit instance number plus a NULL. If the taskq name is changed
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * (particularly if it is lengthened), then this value needs to change.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Kelvin to Celsius conversion
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * The formula for converting degrees Kelvin to degrees Celsius is
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * C = K - 273.15 (we round to 273.2). The unit for thermal zone
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * temperatures is tenths of a degree Kelvin. Use tenth of a degree
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * to convert, then make a whole number out of it.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen/* cb_ops or dev_ops forward declarations */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic int tzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic int tzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic int tzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen/* other forward declarations */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic void tzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic void tzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic void tzmon_discard_buffers(thermal_zone_t *tzp);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic void tzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic ACPI_STATUS tzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic void tzmon_find_zones(void);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic void tzmon_set_power_device(ACPI_HANDLE dev, int on_off);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic void tzmon_set_power(ACPI_BUFFER devlist, int on_off);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenstatic void tzmon_eval_zone(thermal_zone_t *tzp);
a600f50d43405fe4fd9ab16cc92b28df19656392Mike Christensenstatic void tzmon_do_shutdown(void);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensenextern void halt(char *);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen 0, /* not a STREAMS driver */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen D_NEW | D_MP, /* safe for multi-thread/multi-processor */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen 0, /* devo_refcnt */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen "ACPI Thermal Zone Monitor %I%",
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen MODREV_1, /* MODREV_1 indicated by manual */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen NULL, /* termination of list of linkage structures */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen/* globals for this module */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * _init, _info, and _fini support loading and unloading the driver.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Check to see if ACPI CA services are available
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen mutex_init(&zone_list_lock, NULL, MUTEX_DRIVER, NULL);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen cv_init(&zone_list_condvar, NULL, CV_DRIVER, NULL);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 0,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* free allocated thermal zone name(s) */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* discard zone list assets */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_notify_zone
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Thermal zone notification handler.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_notify_zone(ACPI_HANDLE obj, UINT32 val, void *ctx)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen thermal_zone_t *tzp = (thermal_zone_t *)ctx;
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen case 0x80: /* Thermal Zone status changed */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen case 0x81: /* Thermal Zone trip points changed */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_TRIP_POINTS);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen tzmon_enumerate_zone(obj, tzp, TZMON_ENUM_DEV_LISTS);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen case 0x83: /* Thermal Relationship Table changed */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* not handling _TRT objects, so not handling this event */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen cmn_err(CE_CONT, "?tzmon: thermal relationship table changed");
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_eval_int
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Evaluate the object/method as an integer.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_eval_int(ACPI_HANDLE obj, char *method, int *rv)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen if (acpica_eval_int(obj, method, rv) != AE_OK)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_alloc_zone
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Allocate memory for the zone structure and initialize it lock mutex.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen tzp = kmem_zalloc(sizeof (thermal_zone_t), KM_SLEEP);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen mutex_init(&tzp->lock, NULL, MUTEX_DRIVER, NULL);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_free_zone_list
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Free the zone list, either because attach failed or detach initiated.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Remove the notify handler for the zone. Not much to
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * do if this fails (since we are on our way out), so
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * just ignore failure.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen (void) AcpiRemoveNotifyHandler(tzp->obj, ACPI_DEVICE_NOTIFY,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* Shut down monitor thread, if running */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* Drop mutex to allow the thread to run */
a600f50d43405fe4fd9ab16cc92b28df19656392Mike Christensen for (level = 0; level < TZ_NUM_LEVELS; level++) {
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_enumerate_zone
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Enumerates the contents of a thermal zone and updates passed-in
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * thermal_zone or creates a new one if tzp is NULL. Newly-created
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * zones are linked into the global zone_list.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_enumerate_zone(ACPI_HANDLE obj, thermal_zone_t *tzp, int enum_flag)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Newly-created zones and existing zones both require
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * some individual attention.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* New zone required */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * It is exceedingly unlikely that instance will exceed 99.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * However, if it does, this will cause problems when
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * creating the taskq for this thermal zone.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Set to a low level. Will get set to the actual
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * current power level when the thread monitor polls
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * the current temperature.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* Get the zone name in case we need to display it later */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen status = AcpiGetName(obj, ACPI_FULL_PATHNAME, &zone_name);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen status = AcpiInstallNotifyHandler(obj, ACPI_DEVICE_NOTIFY,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* Existing zone - toss out allocated items */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen for (level = 0; level < TZ_NUM_LEVELS; level++) {
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen tzmon_eval_int(obj, abuf, &tzp->ac[level]);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen for (level = 0; level < TZ_NUM_LEVELS; level++) {
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen tzp->al[level].Length = ACPI_ALLOCATE_BUFFER;
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen "evaluating _AL object");
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen (void) AcpiEvaluateObject(obj, "_PSL", NULL, &tzp->psl);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* start monitor thread if needed */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen (void) snprintf(taskq_name, TZ_TASKQ_NAME_LEN,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen " monitor thread - monitor by notify only");
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_zone_callback
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Enumerate the thermal zone if it has a _TMP (current thermal zone
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * operating temperature) method.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_zone_callback(ACPI_HANDLE obj, UINT32 nest, void *ctx, void **rv)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * We get both ThermalZone() and Scope(\_TZ) objects here;
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * look for _TMP (without which a zone is invalid) to pick
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * between them (and ignore invalid zones)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen if (AcpiGetHandle(obj, "_TMP", &tmpobj) == AE_OK) {
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen tzmon_enumerate_zone(obj, NULL, TZMON_ENUM_ALL);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_find_zones
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Find all of the thermal zones by calling a ACPICA function that
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * walks the ACPI namespace and invokes a callback for each thermal
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * object found.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen status = AcpiWalkNamespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen 8, tzmon_zone_callback, NULL, (void **)&retval);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_monitor
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Run as a separate thread, this wakes according to polling period and
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * checks particular objects in the thermal zone. One instance per
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * thermal zone.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen thermal_zone_t *tzp = (thermal_zone_t *)ctx;
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* Check out the zone */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* Go back to sleep */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen ticks = drv_usectohz(tzp->polling_period * 1000000);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen (void) cv_timedwait(&zone_list_condvar, &tzp->lock,
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen } while (ticks > 0);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_set_power_device
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_set_power_device(ACPI_HANDLE dev, int on_off)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen status = AcpiEvaluateObject(dev, "_PR0", NULL, &rb);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen cmn_err(CE_NOTE, "tzmon: can not set power");
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen cmn_err(CE_NOTE, "tzmon: can not set power");
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen cmn_err(CE_WARN, "tz_set_pwr_dev: failed to set %d\n",
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_set_power
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Turn on or turn off all devices in the supplied list.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensentzmon_set_power(ACPI_BUFFER devlist, int on_off)
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen cmn_err(CE_NOTE, "tzmon: can not set power");
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen devs->Package.Elements[i].Reference.Handle, on_off);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_eval_zone
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Evaluate the current conditions within the thermal zone.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* get the current temperature from ACPI */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* _HOT handling */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen "tzmon: Thermal zone (%s) is too hot (%d C); "
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen "initiating shutdown\n",
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* _CRT handling */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen "tzmon: Thermal zone (%s) is critically hot (%d C); "
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen "initiating rapid shutdown\n",
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* shut down (fairly) immediately */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * use the temperature to determine whether the thermal zone
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * is at a new active cooling threshold level
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen for (level = 0, new_level = -1; level < TZ_NUM_LEVELS; level++) {
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen if (tzp->ac[level] >= 0 && (tmp >= tzp->ac[level])) {
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * if the active cooling threshold has changed, turn off the
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * devices associated with the old one and turn on the new one
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen tzmon_set_power(tzp->al[tzp->current_level], 0);
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * tzmon_do_shutdown
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen * Initiates shutdown by sending a SIGPWR signal to init.
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* if we can't find init, just halt */
30588217a56ff2c9137248fb2e5065c4f0101459Mike Christensen /* graceful shutdown with inittab and all getting involved */