/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Solaris x86 ACPI ThermalZone Monitor
*/
#include "tzmon.h"
/*
* TZ_TASKQ_NAME_LEN is precisely the length of the string "AcpiThermalMonitor"
* plus a two-digit instance number plus a NULL. If the taskq name is changed
* (particularly if it is lengthened), then this value needs to change.
*/
/*
* Kelvin to Celsius conversion
* The formula for converting degrees Kelvin to degrees Celsius is
* C = K - 273.15 (we round to 273.2). The unit for thermal zone
* temperatures is tenths of a degree Kelvin. Use tenth of a degree
* to convert, then make a whole number out of it.
*/
/* cb_ops or dev_ops forward declarations */
/* other forward declarations */
static thermal_zone_t *tzmon_alloc_zone();
static void tzmon_free_zone_list();
int enum_flag);
static void tzmon_find_zones(void);
static void tzmon_monitor(void *ctx);
static void tzmon_do_shutdown(void);
extern void halt(char *);
nodev, /* no open routine */
nodev, /* no close routine */
nodev, /* not a block driver */
nodev, /* no print routine */
nodev, /* no dump routine */
nodev, /* no read routine */
nodev, /* no write routine */
nodev, /* no ioctl routine */
nodev, /* no devmap routine */
nodev, /* no mmap routine */
nodev, /* no segmap routine */
nochpoll, /* no chpoll routine */
0, /* not a STREAMS driver */
};
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
tzmon_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
tzmon_attach, /* devo_attach */
tzmon_detach, /* devo_detach */
nodev, /* devo_reset */
&tzmon_cb_ops, /* devo_cb_ops */
(struct bus_ops *)0, /* devo_bus_ops */
NULL, /* devo_power */
ddi_quiesce_not_needed, /* devo_quiesce */
};
extern struct mod_ops mod_driverops;
"ACPI Thermal Zone Monitor",
};
MODREV_1, /* MODREV_1 indicated by manual */
(void *)&modldrv,
NULL, /* termination of list of linkage structures */
};
/* globals for this module */
static int zone_count;
/*
* _init, _info, and _fini support loading and unloading the driver.
*/
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
{
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
static int
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
return (DDI_FAILURE);
/*
* Check to see if ACPI CA services are available
*/
if (AcpiSubsystemStatus() != AE_OK)
return (DDI_FAILURE);
if (zone_count < 1) {
return (DDI_FAILURE);
}
DDI_PSEUDO, 0) == DDI_FAILURE) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_FAILURE;
else
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = 0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
static int
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
/* free allocated thermal zone name(s) */
}
/* discard zone list assets */
return (DDI_SUCCESS);
}
/*
* tzmon_notify_zone
* Thermal zone notification handler.
*/
static void
{
switch (val) {
case 0x80: /* Thermal Zone status changed */
break;
case 0x81: /* Thermal Zone trip points changed */
break;
case 0x82: /* Device Lists changed */
break;
case 0x83: /* Thermal Relationship Table changed */
/* not handling _TRT objects, so not handling this event */
break;
default:
break;
}
}
/*
* tzmon_eval_int
*/
static void
{
*rv = -1;
}
/*
* tzmon_alloc_zone
* Allocate memory for the zone structure and initialize it lock mutex.
*/
static thermal_zone_t *
{
return (tzp);
}
/*
* tzmon_free_zone_list
* Free the zone list, either because attach failed or detach initiated.
*/
static void
{
/*
* Remove the notify handler for the zone. Not much to
* do if this fails (since we are on our way out), so
* just ignore failure.
*/
/* Shut down monitor thread, if running */
tzp->polling_period = 0;
/* Drop mutex to allow the thread to run */
}
}
}
static void
{
int level;
}
}
/*
* tzmon_enumerate_zone
* Enumerates the contents of a thermal zone and updates passed-in
* thermal_zone or creates a new one if tzp is NULL. Newly-created
* zones are linked into the global zone_list.
*/
static void
{
int level;
int instance;
/*
* Newly-created zones and existing zones both require
* some individual attention.
*/
/* New zone required */
tzp = tzmon_alloc_zone();
/*
* It is exceedingly unlikely that instance will exceed 99.
* However, if it does, this will cause problems when
* creating the taskq for this thermal zone.
*/
zone_count++;
/*
* Set to a low level. Will get set to the actual
* current power level when the thread monitor polls
* the current temperature.
*/
tzp->current_level = 0;
/* Get the zone name in case we need to display it later */
tzmon_notify_zone, (void *)tzp);
} else {
/* Existing zone - toss out allocated items */
if (enum_flag & TZMON_ENUM_DEV_LISTS)
}
if (enum_flag & TZMON_ENUM_TRIP_POINTS) {
}
}
if (enum_flag & TZMON_ENUM_DEV_LISTS) {
} else {
AE_OK) {
}
}
}
}
tzp->polling_period = 0;
} else {
else
/* start monitor thread if needed */
"AcpiThermalMonitor%02d", instance);
tzp->polling_period = 0;
"monitor thread for thermal zone %s - "
"monitor by notify only",
} else {
}
}
}
}
/*
* tzmon_zone_callback
* Enumerate the thermal zone if it has a _TMP (current thermal zone
* operating temperature) method.
*/
/*ARGSUSED*/
static ACPI_STATUS
{
/*
* We get both ThermalZone() and Scope(\_TZ) objects here;
* look for _TMP (without which a zone is invalid) to pick
* between them (and ignore invalid zones)
*/
}
return (AE_OK);
}
/*
* tzmon_find_zones
* Find all of the thermal zones by calling a ACPICA function that
* walks the ACPI namespace and invokes a callback for each thermal
* object found.
*/
static void
{
int retval;
}
/*
* tzmon_monitor
* Run as a separate thread, this wakes according to polling period and
* checks particular objects in the thermal zone. One instance per
* thermal zone.
*/
static void
{
do {
/* Check out the zone */
/* Go back to sleep */
if (ticks > 0)
(void) cv_reltimedwait(&zone_list_condvar,
} while (ticks > 0);
}
/*
* tzmon_set_power_device
*/
static void
{
int i;
return;
}
}
}
}
/*
* tzmon_set_power
* Turn on or turn off all devices in the supplied list.
*/
static void
{
int i;
return;
}
tz_name);
}
/*
* tzmon_eval_zone
* Evaluate the current conditions within the thermal zone.
*/
static void
{
/* get the current temperature from ACPI */
/* _HOT handling */
"tzmon: Thermal zone (%s) is too hot (%d C); "
"initiating shutdown\n",
}
/* _CRT handling */
"tzmon: Thermal zone (%s) is critically hot (%d C); "
"initiating rapid shutdown\n",
/* shut down (fairly) immediately */
}
/*
* use the temperature to determine whether the thermal zone
* is at a new active cooling threshold level
*/
break;
}
}
/*
* if the active cooling threshold has changed, turn off the
* devices associated with the old one and turn on the new one
*/
if ((tzp->current_level >= 0) &&
if ((new_level >= 0) &&
}
}
/*
* tzmon_do_shutdown
* Initiates shutdown by sending a SIGPWR signal to init.
*/
static void
tzmon_do_shutdown(void)
{
/* if we can't find init, just halt */
}
/* graceful shutdown with inittab and all getting involved */
}