summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica/acpi_thermal.c
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>2001-06-28 06:17:16 +0000
committermsmith <msmith@FreeBSD.org>2001-06-28 06:17:16 +0000
commitfc1d6b8218f16485b03e9c32b62fc0e837a78afc (patch)
treed6a06322db1956db15fed42753544214ee27e05a /sys/dev/acpica/acpi_thermal.c
parent741be83f389e54133ba630aba5b4f1c4a663b755 (diff)
downloadFreeBSD-src-fc1d6b8218f16485b03e9c32b62fc0e837a78afc.zip
FreeBSD-src-fc1d6b8218f16485b03e9c32b62fc0e837a78afc.tar.gz
Sync to my work in progress:
- Reorder the acpi_* functions in a sensible fashion - Add acpi_ForeachPackageObject and acpi_GetHandleInScope - Use the new debugging layer/level names - Implement most of the guts of the acpi_thermal module; passive cooling isn't there yet, but active cooling should work. - Implement power resource handling (acpi_powerres.c) This compiles and mostly works, but my test coverage is small, so feedback is welcome.
Diffstat (limited to 'sys/dev/acpica/acpi_thermal.c')
-rw-r--r--sys/dev/acpica/acpi_thermal.c378
1 files changed, 342 insertions, 36 deletions
diff --git a/sys/dev/acpica/acpi_thermal.c b/sys/dev/acpica/acpi_thermal.c
index 7b3cd4e..d381b5d 100644
--- a/sys/dev/acpica/acpi_thermal.c
+++ b/sys/dev/acpica/acpi_thermal.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000, 2001 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
*
@@ -31,6 +31,7 @@
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
+#include <sys/reboot.h>
#include "acpi.h"
@@ -45,16 +46,50 @@ MODULE_NAME("THERMAL")
#define TZ_ZEROC 2732
#define TZ_KELVTOC(x) (((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
+#define TZ_NOTIFY_TEMPERATURE 0x80
+#define TZ_NOTIFY_DEVICES 0x81
+#define TZ_NOTIFY_LEVELS 0x82
+
+#define TZ_POLLRATE (hz * 10) /* every ten seconds */
+
+#define TZ_NUMLEVELS 10 /* defined by ACPI spec */
+struct acpi_tz_state {
+ int ac[TZ_NUMLEVELS];
+ ACPI_BUFFER al[TZ_NUMLEVELS];
+ int crt;
+ int hot;
+ ACPI_BUFFER psl;
+ int psv;
+ int tc1;
+ int tc2;
+ int tsp;
+ int tzp;
+};
+
+
struct acpi_tz_softc {
- device_t tz_dev;
- ACPI_HANDLE tz_handle;
- int tz_tmp;
+ device_t tz_dev;
+ ACPI_HANDLE tz_handle;
+ struct callout_handle tz_timeout;
+ int tz_current;
+#define TZ_STATE_NONE 0
+#define TZ_STATE_PSV 1
+#define TZ_STATE_AC0 2
+#define TZ_STATE_HOT (TZ_STATE_AC0 + TZ_NUMLEVELS)
+#define TZ_STATE_CRT (TZ_STATE_AC0 + TZ_NUMLEVELS + 1)
+
+ struct acpi_tz_state tz_state;
};
static int acpi_tz_probe(device_t dev);
static int acpi_tz_attach(device_t dev);
-static void acpi_tz_check_tripping_point(void *context);
+static int acpi_tz_establish(struct acpi_tz_softc *sc);
+static void acpi_tz_all_off(struct acpi_tz_softc *sc);
+static void acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
+static void acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
+static void acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
+static void acpi_tz_timeout(void *arg);
static device_method_t acpi_tz_methods[] = {
/* Device interface */
@@ -73,25 +108,31 @@ static driver_t acpi_tz_driver = {
devclass_t acpi_tz_devclass;
DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
+/*
+ * Match an ACPI thermal zone.
+ */
static int
acpi_tz_probe(device_t dev)
{
- FUNCTION_TRACE(__func__);
+ /* no FUNCTION_TRACE - too noisy */
if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
!acpi_disabled("thermal")) {
device_set_desc(dev, "thermal zone");
- return_VALUE(0);
+ return(-10);
}
- return_VALUE(ENXIO);
+ return(ENXIO);
}
+/*
+ * Attach to an ACPI thermal zone.
+ */
static int
acpi_tz_attach(device_t dev)
{
struct acpi_tz_softc *sc;
- struct acpi_softc *acpi_sc;
+ int error;
FUNCTION_TRACE(__func__);
@@ -99,17 +140,19 @@ acpi_tz_attach(device_t dev)
sc->tz_dev = dev;
sc->tz_handle = acpi_get_handle(dev);
+ /*
+ * Parse the current state of the thermal zone and build control
+ * structures.
+ */
+ if ((error = acpi_tz_establish(sc)) != 0)
+ return_VALUE(error);
+
+ /*
+ * Register for any Notify events sent to this zone.
+ */
AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
acpi_tz_notify_handler, dev);
- if (device_get_unit(dev) == 0) {
- acpi_sc = acpi_device_get_parent_softc(dev);
- SYSCTL_ADD_UINT(&acpi_sc->acpi_sysctl_ctx,
- SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
- OID_AUTO, "temperature", CTLFLAG_RD,
- &sc->tz_tmp, 0, "");
- }
-
/*
* Don't bother evaluating/printing the temperature at this point;
* on many systems it'll be bogus until the EC is running.
@@ -117,43 +160,306 @@ acpi_tz_attach(device_t dev)
return_VALUE(0);
}
+/*
+ * Parse the current state of this thermal zone and set up to use it.
+ *
+ * Note that we may have previous state, which will have to be discarded.
+ */
+static int
+acpi_tz_establish(struct acpi_tz_softc *sc)
+{
+ ACPI_OBJECT *obj;
+ int i;
+ char nbuf[8];
+
+ FUNCTION_TRACE(__func__);
+
+ /*
+ * Power everything off and erase any existing state.
+ */
+ acpi_tz_all_off(sc);
+ for (i = 0; i < TZ_NUMLEVELS; i++)
+ if (sc->tz_state.al[i].Pointer != NULL)
+ AcpiOsFree(sc->tz_state.al[i].Pointer);
+ if (sc->tz_state.psl.Pointer != NULL)
+ AcpiOsFree(sc->tz_state.psl.Pointer);
+ bzero(&sc->tz_state, sizeof(sc->tz_state));
+
+ /* kill the timeout (harmless if not running */
+ untimeout(acpi_tz_timeout, sc, sc->tz_timeout);
+
+ /*
+ * Evaluate thermal zone parameters.
+ */
+ for (i = 0; i < TZ_NUMLEVELS; i++) {
+ sprintf(nbuf, "_AC%d", i);
+ acpi_tz_getparam(sc, nbuf, &sc->tz_state.ac[i]);
+ sprintf(nbuf, "_AL%d", i);
+ acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_state.al[i]);
+ obj = (ACPI_OBJECT *)sc->tz_state.al[i].Pointer;
+ if (obj != NULL) {
+ /* should be a package containing a list of power objects */
+ if (obj->Type != ACPI_TYPE_PACKAGE) {
+ device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
+ nbuf, obj->Type);
+ return_VALUE(ENXIO);
+ }
+ }
+ }
+ acpi_tz_getparam(sc, "_CRT", &sc->tz_state.crt);
+ acpi_tz_getparam(sc, "_HOT", &sc->tz_state.hot);
+ acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_state.psl);
+ acpi_tz_getparam(sc, "_PSV", &sc->tz_state.psv);
+ acpi_tz_getparam(sc, "_TC1", &sc->tz_state.tc1);
+ acpi_tz_getparam(sc, "_TC2", &sc->tz_state.tc2);
+ acpi_tz_getparam(sc, "_TSP", &sc->tz_state.tsp);
+ acpi_tz_getparam(sc, "_TZP", &sc->tz_state.tzp);
+
+ /*
+ * Power off everything that we've just been given.
+ */
+ acpi_tz_all_off(sc);
+
+ /*
+ * Do we need to poll the thermal zone? Ignore the suggested
+ * rate.
+ */
+ if (sc->tz_state.tzp != 0)
+ sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
+
+
+ return_VALUE(0);
+}
+
+/*
+ * Evaluate the condition of a thermal zone, take appropriate actions.
+ */
static void
-acpi_tz_check_tripping_point(void *context)
+acpi_tz_monitor(struct acpi_tz_softc *sc)
{
- struct acpi_tz_softc *sc;
- device_t dev = context;
- ACPI_STATUS status;
- int tp;
+ int temp, new;
+ int i;
FUNCTION_TRACE(__func__);
- sc = device_get_softc(dev);
- if ((status = acpi_EvaluateInteger(sc->tz_handle, "_TMP", &tp)) != AE_OK) {
- device_printf(dev, "can't evaluate _TMP method - %s\n", acpi_strerror(status));
+ /*
+ * Get the current temperature.
+ */
+ if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) {
+ device_printf(sc->tz_dev, "error fetching current temperature\n");
+ /* XXX disable zone? go to max cooling? */
+ return_VOID;
+ }
+
+ /*
+ * Work out what we ought to be doing right now.
+ */
+ new = TZ_STATE_NONE;
+ if ((sc->tz_state.psv != -1) && (temp > sc->tz_state.psv))
+ new = TZ_STATE_PSV;
+ for (i = 0; i < TZ_NUMLEVELS; i++)
+ if ((sc->tz_state.ac[i] != -1) && (temp > sc->tz_state.ac[i]))
+ new = TZ_STATE_AC0 + i;
+ if ((sc->tz_state.hot != -1) && (temp > sc->tz_state.hot))
+ new = TZ_STATE_HOT;
+ if ((sc->tz_state.crt != -1) && (temp > sc->tz_state.crt))
+ new = TZ_STATE_CRT;
+
+ /*
+ * If our state has not changed, do nothing.
+ */
+ if (new == sc->tz_current)
return_VOID;
+
+ /*
+ * XXX if we're in a passive-cooling mode, revert to full-speed operation.
+ */
+ if (sc->tz_current == TZ_STATE_PSV) {
+ /* XXX implement */
+ }
+
+ /*
+ * If we're in an active-cooling mode, turn off the current cooler(s).
+ */
+ if ((sc->tz_current >= TZ_STATE_AC0) && (sc->tz_current < (TZ_STATE_AC0 + TZ_NUMLEVELS)))
+ acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[sc->tz_current - TZ_STATE_AC0].Pointer,
+ acpi_tz_switch_cooler_off, sc);
+
+ /*
+ * XXX If the new mode is passive-cooling, make appropriate adjustments.
+ */
+
+ /*
+ * If the new mode is an active-cooling mode, turn on the new cooler(s).
+ */
+ if ((new >= TZ_STATE_AC0) && (new < (TZ_STATE_AC0 + TZ_NUMLEVELS)))
+ acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[new - TZ_STATE_AC0].Pointer,
+ acpi_tz_switch_cooler_on, sc);
+
+ /*
+ * If we're _HOT or _CRT, shut down now!
+ */
+ if ((new == TZ_STATE_HOT) || (new == TZ_STATE_CRT)) {
+ device_printf(sc->tz_dev, "WARNING - emergency thermal shutdown in progress.\n");
+ shutdown_nice(RB_POWEROFF);
}
+
+ /* gone to new state */
+ sc->tz_current = new;
+
+ return_VOID;
+}
+
+/*
+ * Turn off all the cooling devices.
+ */
+static void
+acpi_tz_all_off(struct acpi_tz_softc *sc)
+{
+ int i;
+
+ FUNCTION_TRACE(__func__);
- sc->tz_tmp = (tp - TZ_ZEROC) / 10;
- if (bootverbose) {
- device_printf(dev, "%dC\n", sc->tz_tmp);
+ /*
+ * Scan all the _AL objects, and turn them all off.
+ */
+ for (i = 0; i < TZ_NUMLEVELS; i++) {
+ if (sc->tz_state.al[i].Pointer == NULL)
+ continue;
+ acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[i].Pointer,
+ acpi_tz_switch_cooler_off, sc);
}
+
+ /*
+ * XXX revert any passive-cooling options.
+ */
+
+ sc->tz_current = TZ_STATE_NONE;
return_VOID;
}
-#define ACPI_TZ_STATUS_CHANGE 0x80
-#define ACPI_TZ_TRIPPOINT_CHANGE 0x81
+/*
+ * Given an object, verify that it's a reference to a device of some sort,
+ * and try to switch it off.
+ */
+static void
+acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
+{
+ struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg;
+ ACPI_HANDLE cooler;
+
+ FUNCTION_TRACE(__func__);
+
+ switch(obj->Type) {
+ case ACPI_TYPE_STRING:
+ DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
+
+ /*
+ * Find the handle for the device and turn it off.
+ * The String object here seems to contain a fully-qualified path, so we
+ * don't have to search for it in our parents.
+ *
+ * XXX This may not always be the case.
+ */
+ if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK)
+ acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
+ break;
+
+ default:
+ DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
+ obj->Type));
+ break;
+ }
+ return_VOID;
+}
+
+/*
+ * Given an object, verify that it's a reference to a device of some sort,
+ * and try to switch it on.
+ *
+ * XXX replication of off/on function code is bad, mmmkay?
+ */
+static void
+acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
+{
+ struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg;
+ ACPI_HANDLE cooler, parent;
+
+ FUNCTION_TRACE(__func__);
+
+ switch(obj->Type) {
+ case ACPI_TYPE_STRING:
+ DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
+
+ /* find the handle for the device and turn it off */
+ if (acpi_GetHandleInScope(sc->tz_handle, obj->String.Pointer, &cooler) == AE_OK)
+ acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
+ break;
+
+ default:
+ DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
+ obj->Type));
+ break;
+ }
+ return_VOID;
+}
+
+/*
+ * Read/debug-print a parameter, default it to -1.
+ */
+static void
+acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
+{
+
+ FUNCTION_TRACE(__func__);
+
+ if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) {
+ *data = -1;
+ } else {
+ DEBUG_PRINT(TRACE_VALUES, ("%s.%s = %d\n", acpi_name(sc->tz_handle),
+ node, *data));
+ }
+ return_VOID;
+}
+
+/*
+ * Respond to a Notify event sent to the zone.
+ */
static void
acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
{
+ struct acpi_tz_softc *sc = (struct acpi_tz_softc *)context;
+
FUNCTION_TRACE(__func__);
- switch(notify){
- case ACPI_TZ_STATUS_CHANGE:
- case ACPI_TZ_TRIPPOINT_CHANGE:
- /*Check trip point*/
- AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_tz_check_tripping_point, context);
- break;
+ switch(notify) {
+ case TZ_NOTIFY_TEMPERATURE:
+ acpi_tz_monitor(sc); /* temperature change occurred */
+ break;
+ case TZ_NOTIFY_DEVICES:
+ case TZ_NOTIFY_LEVELS:
+ acpi_tz_establish(sc); /* zone devices/setpoints changed */
+ break;
+ default:
+ device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify);
+ break;
}
return_VOID;
}
+/*
+ * Poll the thermal zone.
+ */
+static void
+acpi_tz_timeout(void *arg)
+{
+ struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg;
+
+ /* check temperature, take action */
+ acpi_tz_monitor(sc);
+
+ /* XXX passive cooling actions? */
+
+ /* re-register ourself */
+ sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
+}
OpenPOWER on IntegriCloud