acpi_thermal.c revision 91215
167761Smsmith/*-
278915Smsmith * Copyright (c) 2000, 2001 Michael Smith
367761Smsmith * Copyright (c) 2000 BSDi
467761Smsmith * All rights reserved.
567761Smsmith *
667761Smsmith * Redistribution and use in source and binary forms, with or without
767761Smsmith * modification, are permitted provided that the following conditions
867761Smsmith * are met:
967761Smsmith * 1. Redistributions of source code must retain the above copyright
1067761Smsmith *    notice, this list of conditions and the following disclaimer.
1167761Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1267761Smsmith *    notice, this list of conditions and the following disclaimer in the
1367761Smsmith *    documentation and/or other materials provided with the distribution.
1467761Smsmith *
1567761Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1667761Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1767761Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1867761Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1967761Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2067761Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2167761Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2267761Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2367761Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2467761Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2567761Smsmith * SUCH DAMAGE.
2667761Smsmith *
2767761Smsmith *	$FreeBSD: head/sys/dev/acpica/acpi_thermal.c 91215 2002-02-25 02:21:22Z msmith $
2867761Smsmith */
2967761Smsmith
3067761Smsmith#include "opt_acpi.h"
3167761Smsmith#include <sys/param.h>
3267761Smsmith#include <sys/kernel.h>
3391126Smsmith#include <sys/kthread.h>
3491215Smsmith#include <sys/lock.h>
3591215Smsmith#include <sys/mutex.h>
3667761Smsmith#include <sys/bus.h>
3791126Smsmith#include <sys/proc.h>
3878915Smsmith#include <sys/reboot.h>
3979283Smsmith#include <sys/sysctl.h>
4091126Smsmith#include <sys/unistd.h>
4167761Smsmith
4267761Smsmith#include "acpi.h"
4367761Smsmith
4467761Smsmith#include <dev/acpica/acpivar.h>
4567761Smsmith
4669744Smsmith/*
4769744Smsmith * Hooks for the ACPI CA debugging infrastructure
4869744Smsmith */
4978999Smsmith#define _COMPONENT	ACPI_THERMAL
5091126SmsmithACPI_MODULE_NAME("THERMAL")
5169744Smsmith
5271874Smsmith#define TZ_ZEROC	2732
5371874Smsmith#define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
5467761Smsmith
5578915Smsmith#define TZ_NOTIFY_TEMPERATURE	0x80
5678915Smsmith#define TZ_NOTIFY_DEVICES	0x81
5778915Smsmith#define TZ_NOTIFY_LEVELS	0x82
5878915Smsmith
5988420Siwasaki#define TZ_POLLRATE	30		/* every 30 seconds by default */
6078915Smsmith
6178915Smsmith#define TZ_NUMLEVELS	10		/* defined by ACPI spec */
6279375Smsmithstruct acpi_tz_zone {
6378915Smsmith    int		ac[TZ_NUMLEVELS];
6478915Smsmith    ACPI_BUFFER	al[TZ_NUMLEVELS];
6578915Smsmith    int		crt;
6678915Smsmith    int		hot;
6778915Smsmith    ACPI_BUFFER	psl;
6878915Smsmith    int		psv;
6978915Smsmith    int		tc1;
7078915Smsmith    int		tc2;
7178915Smsmith    int		tsp;
7278915Smsmith    int		tzp;
7378915Smsmith};
7478915Smsmith
7578915Smsmith
7667761Smsmithstruct acpi_tz_softc {
7779375Smsmith    device_t			tz_dev;			/* device handle */
7879375Smsmith    ACPI_HANDLE			tz_handle;		/* thermal zone handle */
7979375Smsmith    int				tz_temperature;		/* current temperature */
8079375Smsmith    int				tz_active;		/* current active cooling */
8179375Smsmith#define TZ_ACTIVE_NONE		-1
8279375Smsmith    int				tz_requested;		/* user-requested minimum active cooling */
8379375Smsmith    int				tz_thflags;		/* current temperature-related flags */
8479375Smsmith#define TZ_THFLAG_NONE		0
8579375Smsmith#define TZ_THFLAG_PSV		(1<<0)
8679375Smsmith#define TZ_THFLAG_HOT		(1<<2)
8779375Smsmith#define TZ_THFLAG_CRT		(1<<3)
8879283Smsmith    int				tz_flags;
8979375Smsmith#define TZ_FLAG_NO_SCP		(1<<0)			/* no _SCP method */
9079375Smsmith#define TZ_FLAG_GETPROFILE	(1<<1)			/* fetch powerprofile in timeout */
9185699Siwasaki    struct timespec		tz_cooling_started;	/* current cooling starting time */
9279283Smsmith
9379375Smsmith    struct sysctl_ctx_list	tz_sysctl_ctx;		/* sysctl tree */
9479283Smsmith    struct sysctl_oid		*tz_sysctl_tree;
9578915Smsmith
9679375Smsmith    struct acpi_tz_zone 	tz_zone;		/* thermal zone parameters */
9788420Siwasaki    int				tz_tmp_updating;
9867761Smsmith};
9967761Smsmith
10067761Smsmithstatic int	acpi_tz_probe(device_t dev);
10167761Smsmithstatic int	acpi_tz_attach(device_t dev);
10278915Smsmithstatic int	acpi_tz_establish(struct acpi_tz_softc *sc);
10378999Smsmithstatic void	acpi_tz_monitor(struct acpi_tz_softc *sc);
10478915Smsmithstatic void	acpi_tz_all_off(struct acpi_tz_softc *sc);
10578915Smsmithstatic void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
10678915Smsmithstatic void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
10778915Smsmithstatic void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
10879283Smsmithstatic void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
10979375Smsmithstatic int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
11071874Smsmithstatic void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
11191126Smsmithstatic void	acpi_tz_timeout(struct acpi_tz_softc *sc);
11279375Smsmithstatic void	acpi_tz_powerprofile(void *arg);
11371874Smsmith
11491126Smsmithstatic void	acpi_tz_thread(void *arg);
11591126Smsmithstatic struct proc *acpi_tz_proc;
11691126Smsmith
11767761Smsmithstatic device_method_t acpi_tz_methods[] = {
11867761Smsmith    /* Device interface */
11967761Smsmith    DEVMETHOD(device_probe,	acpi_tz_probe),
12067761Smsmith    DEVMETHOD(device_attach,	acpi_tz_attach),
12167761Smsmith
12267761Smsmith    {0, 0}
12367761Smsmith};
12467761Smsmith
12567761Smsmithstatic driver_t acpi_tz_driver = {
12667761Smsmith    "acpi_tz",
12767761Smsmith    acpi_tz_methods,
12867761Smsmith    sizeof(struct acpi_tz_softc),
12967761Smsmith};
13067761Smsmith
13189054Smsmithstatic devclass_t acpi_tz_devclass;
13267761SmsmithDRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
13367761Smsmith
13479283Smsmithstatic struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
13579283Smsmithstatic struct sysctl_oid	*acpi_tz_sysctl_tree;
13679283Smsmith
13785699Siwasakistatic int			acpi_tz_min_runtime = 0;/* minimum cooling run time */
13888420Siwasakistatic int			acpi_tz_polling_rate = TZ_POLLRATE;
13985699Siwasaki
14078915Smsmith/*
14178915Smsmith * Match an ACPI thermal zone.
14278915Smsmith */
14367761Smsmithstatic int
14467761Smsmithacpi_tz_probe(device_t dev)
14567761Smsmith{
14678999Smsmith    int		result;
14778999Smsmith
14878999Smsmith    ACPI_LOCK;
14978999Smsmith
15078915Smsmith    /* no FUNCTION_TRACE - too noisy */
15169744Smsmith
15269744Smsmith    if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
15369744Smsmith	!acpi_disabled("thermal")) {
15467761Smsmith	device_set_desc(dev, "thermal zone");
15578999Smsmith	result = -10;
15678999Smsmith    } else {
15778999Smsmith	result = ENXIO;
15867761Smsmith    }
15978999Smsmith    ACPI_UNLOCK;
16078999Smsmith    return(result);
16167761Smsmith}
16267761Smsmith
16378915Smsmith/*
16478915Smsmith * Attach to an ACPI thermal zone.
16578915Smsmith */
16667761Smsmithstatic int
16767761Smsmithacpi_tz_attach(device_t dev)
16867761Smsmith{
16967761Smsmith    struct acpi_tz_softc	*sc;
17079283Smsmith    struct acpi_softc		*acpi_sc;
17178915Smsmith    int				error;
17279283Smsmith    char			oidname[8];
17367761Smsmith
17491126Smsmith    ACPI_FUNCTION_TRACE(__func__);
17569744Smsmith
17678999Smsmith    ACPI_LOCK;
17778999Smsmith
17867761Smsmith    sc = device_get_softc(dev);
17967761Smsmith    sc->tz_dev = dev;
18067761Smsmith    sc->tz_handle = acpi_get_handle(dev);
18179375Smsmith    sc->tz_requested = TZ_ACTIVE_NONE;
18288420Siwasaki    sc->tz_tmp_updating = 0;
18367761Smsmith
18478915Smsmith    /*
18578915Smsmith     * Parse the current state of the thermal zone and build control
18678915Smsmith     * structures.
18778915Smsmith     */
18878915Smsmith    if ((error = acpi_tz_establish(sc)) != 0)
18978999Smsmith	goto out;
19078915Smsmith
19178915Smsmith    /*
19278915Smsmith     * Register for any Notify events sent to this zone.
19378915Smsmith     */
19471874Smsmith    AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
19578999Smsmith			     acpi_tz_notify_handler, sc);
19670271Stakawata
19771874Smsmith    /*
19879283Smsmith     * Create our sysctl nodes.
19979283Smsmith     *
20079283Smsmith     * XXX we need a mechanism for adding nodes under ACPI.
20179283Smsmith     */
20279283Smsmith    if (device_get_unit(dev) == 0) {
20379283Smsmith	acpi_sc = acpi_device_get_parent_softc(dev);
20479283Smsmith	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
20579283Smsmith	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
20679283Smsmith					      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
20779283Smsmith					      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
20885699Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
20985699Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
21085699Siwasaki		       OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW,
21185699Siwasaki		       &acpi_tz_min_runtime, 0, "minimum cooling run time in sec");
21288420Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
21388420Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
21488420Siwasaki		       OID_AUTO, "polling_rate", CTLFLAG_RD | CTLFLAG_RW,
21588420Siwasaki		       &acpi_tz_polling_rate, 0, "monitor polling rate");
21679283Smsmith    }
21779283Smsmith    sysctl_ctx_init(&sc->tz_sysctl_ctx);
21879283Smsmith    sprintf(oidname, "tz%d", device_get_unit(dev));
21979283Smsmith    sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
22079283Smsmith					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
22179283Smsmith					 oidname, CTLFLAG_RD, 0, "");
22279283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
22379283Smsmith		   OID_AUTO, "temperature", CTLFLAG_RD,
22479283Smsmith		   &sc->tz_temperature, 0, "current thermal zone temperature");
22579375Smsmith    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
22679375Smsmith		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
22779375Smsmith		    sc, 0, acpi_tz_active_sysctl, "I", "");
22879375Smsmith
22979283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23079375Smsmith		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
23179375Smsmith		   &sc->tz_thflags, 0, "thermal zone flags");
23279283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23379283Smsmith		   OID_AUTO, "_PSV", CTLFLAG_RD,
23479375Smsmith		   &sc->tz_zone.psv, 0, "");
23579283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23679283Smsmith		   OID_AUTO, "_HOT", CTLFLAG_RD,
23779375Smsmith		   &sc->tz_zone.hot, 0, "");
23879283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23979283Smsmith		   OID_AUTO, "_CRT", CTLFLAG_RD,
24079375Smsmith		   &sc->tz_zone.crt, 0, "");
24186399Siwasaki    SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
24286399Siwasaki		      OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac,
24386399Siwasaki		      sizeof(sc->tz_zone.ac), "I", "");
24479283Smsmith
24586399Siwasaki
24679283Smsmith    /*
24779375Smsmith     * Register our power profile event handler, and flag it for a manual
24879375Smsmith     * invocation by our timeout.  We defer it like this so that the rest
24979375Smsmith     * of the subsystem has time to come up.
25079375Smsmith     */
25179375Smsmith    EVENTHANDLER_REGISTER(powerprofile_change, acpi_tz_powerprofile, sc, 0);
25279375Smsmith    sc->tz_flags |= TZ_FLAG_GETPROFILE;
25379375Smsmith
25479375Smsmith    /*
25571874Smsmith     * Don't bother evaluating/printing the temperature at this point;
25671874Smsmith     * on many systems it'll be bogus until the EC is running.
25771874Smsmith     */
25878999Smsmith
25991126Smsmith    /*
26091126Smsmith     * Create our thread; we only need one, it will service all of the
26191126Smsmith     * thermal zones.
26291126Smsmith     */
26391126Smsmith    if (acpi_tz_proc == NULL) {
26491126Smsmith	    error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_proc,
26591126Smsmith				   RFHIGHPID, "acpi_thermal");
26691126Smsmith	    if (error != 0) {
26791126Smsmith		    device_printf(sc->tz_dev, "could not create thread - %d", error);
26891126Smsmith		    goto out;
26991126Smsmith	    }
27091126Smsmith    }
27191126Smsmith
27278999Smsmith out:
27378999Smsmith    ACPI_UNLOCK;
27479375Smsmith
27578999Smsmith    return_VALUE(error);
27667761Smsmith}
27770271Stakawata
27878915Smsmith/*
27978915Smsmith * Parse the current state of this thermal zone and set up to use it.
28078915Smsmith *
28178915Smsmith * Note that we may have previous state, which will have to be discarded.
28278915Smsmith */
28378915Smsmithstatic int
28478915Smsmithacpi_tz_establish(struct acpi_tz_softc *sc)
28578915Smsmith{
28678915Smsmith    ACPI_OBJECT	*obj;
28778915Smsmith    int		i;
28878915Smsmith    char	nbuf[8];
28978915Smsmith
29091126Smsmith    ACPI_FUNCTION_TRACE(__func__);
29178915Smsmith
29278999Smsmith    ACPI_ASSERTLOCK;
29378999Smsmith
29478915Smsmith    /*
29578915Smsmith     * Power everything off and erase any existing state.
29678915Smsmith     */
29778915Smsmith    acpi_tz_all_off(sc);
29878915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
29979375Smsmith	if (sc->tz_zone.al[i].Pointer != NULL)
30079375Smsmith	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
30179375Smsmith    if (sc->tz_zone.psl.Pointer != NULL)
30279375Smsmith	AcpiOsFree(sc->tz_zone.psl.Pointer);
30379375Smsmith    bzero(&sc->tz_zone, sizeof(sc->tz_zone));
30478915Smsmith
30578915Smsmith    /*
30678915Smsmith     * Evaluate thermal zone parameters.
30778915Smsmith     */
30878915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
30978915Smsmith	sprintf(nbuf, "_AC%d", i);
31079375Smsmith	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
31178915Smsmith	sprintf(nbuf, "_AL%d", i);
31291126Smsmith	sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
31391126Smsmith	sc->tz_zone.al[i].Pointer = NULL;
31491126Smsmith	AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
31579375Smsmith	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
31678915Smsmith	if (obj != NULL) {
31778915Smsmith	    /* should be a package containing a list of power objects */
31878915Smsmith	    if (obj->Type != ACPI_TYPE_PACKAGE) {
31978915Smsmith		device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
32078915Smsmith			      nbuf, obj->Type);
32178915Smsmith		return_VALUE(ENXIO);
32278915Smsmith	    }
32378915Smsmith	}
32478915Smsmith    }
32579375Smsmith    acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
32679375Smsmith    acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
32791126Smsmith    sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
32891126Smsmith    sc->tz_zone.psl.Pointer = NULL;
32991126Smsmith    AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
33079375Smsmith    acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
33179375Smsmith    acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
33279375Smsmith    acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
33379375Smsmith    acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
33479375Smsmith    acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
33578915Smsmith
33678915Smsmith    /*
33779283Smsmith     * Sanity-check the values we've been given.
33879283Smsmith     *
33979283Smsmith     * XXX what do we do about systems that give us the same value for
34079283Smsmith     *     more than one of these setpoints?
34179283Smsmith     */
34279375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
34379375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
34479375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
34579283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
34679375Smsmith	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
34779283Smsmith
34879283Smsmith    /*
34978915Smsmith     * Power off everything that we've just been given.
35078915Smsmith     */
35178915Smsmith    acpi_tz_all_off(sc);
35278915Smsmith
35378915Smsmith    return_VALUE(0);
35478915Smsmith}
35578915Smsmith
35685699Siwasakistatic char	*aclevel_string[] =	{
35785699Siwasaki	"NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
35885699Siwasaki	"_AC5", "_AC6", "_AC7", "_AC8", "_AC9" };
35985699Siwasaki
36085699Siwasakistatic __inline const char *
36185699Siwasakiacpi_tz_aclevel_string(int active)
36285699Siwasaki{
36385699Siwasaki	if (active < -1 || active >= TZ_NUMLEVELS) {
36485699Siwasaki		return (aclevel_string[0]);
36585699Siwasaki	}
36685699Siwasaki
36785699Siwasaki	return (aclevel_string[active+1]);
36885699Siwasaki}
36985699Siwasaki
37078915Smsmith/*
37178915Smsmith * Evaluate the condition of a thermal zone, take appropriate actions.
37278915Smsmith */
37371874Smsmithstatic void
37478915Smsmithacpi_tz_monitor(struct acpi_tz_softc *sc)
37571874Smsmith{
37679283Smsmith    int		temp;
37778915Smsmith    int		i;
37879283Smsmith    int		newactive, newflags;
37985699Siwasaki    struct	timespec curtime;
38086399Siwasaki    ACPI_STATUS	status;
38170271Stakawata
38291126Smsmith    ACPI_FUNCTION_TRACE(__func__);
38370271Stakawata
38478999Smsmith    ACPI_ASSERTLOCK;
38578999Smsmith
38688420Siwasaki    if (sc->tz_tmp_updating) {
38788420Siwasaki	goto out;
38888420Siwasaki    }
38988420Siwasaki    sc->tz_tmp_updating = 1;
39088420Siwasaki
39178915Smsmith    /*
39278915Smsmith     * Get the current temperature.
39378915Smsmith     */
39491126Smsmith    if (ACPI_FAILURE(status = acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp))) {
39586552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
39686552Siwasaki	    "error fetching current temperature -- %s\n",
39786552Siwasaki	     AcpiFormatException(status));
39878915Smsmith	/* XXX disable zone? go to max cooling? */
39988420Siwasaki	goto out;
40071874Smsmith    }
40188420Siwasaki
40282372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
40379283Smsmith    sc->tz_temperature = temp;
40478915Smsmith
40578915Smsmith    /*
40678915Smsmith     * Work out what we ought to be doing right now.
40779283Smsmith     *
40879283Smsmith     * Note that the _ACx levels sort from hot to cold.
40978915Smsmith     */
41079283Smsmith    newactive = TZ_ACTIVE_NONE;
41179375Smsmith    for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
41279375Smsmith	if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
41379283Smsmith	    newactive = i;
41482967Siwasaki	    if (sc->tz_active != newactive) {
41586552Siwasaki		ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
41686552Siwasaki		    "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
41786552Siwasaki		    TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
41885699Siwasaki		getnanotime(&sc->tz_cooling_started);
41982967Siwasaki	    }
42079375Smsmith	}
42179375Smsmith    }
42279283Smsmith
42385699Siwasaki    /*
42485699Siwasaki     * We are going to get _ACx level down (colder side), but give a guaranteed
42585699Siwasaki     * minimum cooling run time if requested.
42685699Siwasaki     */
42785699Siwasaki    if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
42885699Siwasaki	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
42985699Siwasaki	getnanotime(&curtime);
43085699Siwasaki	timespecsub(&curtime, &sc->tz_cooling_started);
43185699Siwasaki	if (curtime.tv_sec < acpi_tz_min_runtime) {
43285699Siwasaki	    newactive = sc->tz_active;
43385699Siwasaki	}
43485699Siwasaki    }
43585699Siwasaki
43679375Smsmith    /* handle user override of active mode */
43779375Smsmith    if (sc->tz_requested > newactive)
43879375Smsmith	newactive = sc->tz_requested;
43978915Smsmith
44079375Smsmith    /* update temperature-related flags */
44179375Smsmith    newflags = TZ_THFLAG_NONE;
44279375Smsmith    if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv))
44379375Smsmith	newflags |= TZ_THFLAG_PSV;
44479375Smsmith    if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot))
44579375Smsmith	newflags |= TZ_THFLAG_HOT;
44679375Smsmith    if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt))
44779375Smsmith	newflags |= TZ_THFLAG_CRT;
44879375Smsmith
44978915Smsmith    /*
45079283Smsmith     * If the active cooling state has changed, we have to switch things.
45178915Smsmith     */
45279283Smsmith    if (newactive != sc->tz_active) {
45378915Smsmith
45479283Smsmith	/* turn off the cooling devices that are on, if any are */
45579283Smsmith	if (sc->tz_active != TZ_ACTIVE_NONE)
45679375Smsmith	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
45779283Smsmith				      acpi_tz_switch_cooler_off, sc);
45878915Smsmith
45979283Smsmith	/* turn on cooling devices that are required, if any are */
46079283Smsmith	if (newactive != TZ_ACTIVE_NONE)
46179375Smsmith	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
46279283Smsmith				      acpi_tz_switch_cooler_on, sc);
46386552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
46486552Siwasaki	    "switched from %s to %s: %d.%dC\n",
46586552Siwasaki	    acpi_tz_aclevel_string(sc->tz_active),
46686552Siwasaki	    acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
46779283Smsmith	sc->tz_active = newactive;
46879283Smsmith    }
46978915Smsmith
47078915Smsmith    /*
47179283Smsmith     * XXX (de)activate any passive cooling that may be required.
47278915Smsmith     */
47378915Smsmith
47478915Smsmith    /*
47579283Smsmith     * If we have just become _HOT or _CRT, warn the user.
47679283Smsmith     *
47779283Smsmith     * We should actually shut down at this point, but it's not clear
47879283Smsmith     * that some systems don't actually map _CRT to the same value as _AC0.
47978915Smsmith     */
48079375Smsmith    if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) &&
48179375Smsmith	!(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT))) {
48279283Smsmith	device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n",
48379283Smsmith		      TZ_KELVTOC(sc->tz_temperature), sc->tz_temperature);
48479283Smsmith	/* shutdown_nice(RB_POWEROFF);*/
48578915Smsmith    }
48679375Smsmith    sc->tz_thflags = newflags;
48778915Smsmith
48888420Siwasakiout:
48988420Siwasaki    sc->tz_tmp_updating = 0;
49071874Smsmith    return_VOID;
49171874Smsmith}
49270271Stakawata
49378915Smsmith/*
49478915Smsmith * Turn off all the cooling devices.
49578915Smsmith */
49671874Smsmithstatic void
49778915Smsmithacpi_tz_all_off(struct acpi_tz_softc *sc)
49878915Smsmith{
49978915Smsmith    int		i;
50078915Smsmith
50191126Smsmith    ACPI_FUNCTION_TRACE(__func__);
50278999Smsmith
50378999Smsmith    ACPI_ASSERTLOCK;
50478915Smsmith
50578915Smsmith    /*
50679375Smsmith     * Scan all the _ALx objects, and turn them all off.
50778915Smsmith     */
50878915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
50979375Smsmith	if (sc->tz_zone.al[i].Pointer == NULL)
51078915Smsmith	    continue;
51179375Smsmith	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
51278915Smsmith				  acpi_tz_switch_cooler_off, sc);
51378915Smsmith    }
51478915Smsmith
51578915Smsmith    /*
51678915Smsmith     * XXX revert any passive-cooling options.
51778915Smsmith     */
51878915Smsmith
51979283Smsmith    sc->tz_active = TZ_ACTIVE_NONE;
52079375Smsmith    sc->tz_thflags = TZ_THFLAG_NONE;
52178915Smsmith    return_VOID;
52278915Smsmith}
52378915Smsmith
52478915Smsmith/*
52578915Smsmith * Given an object, verify that it's a reference to a device of some sort,
52678915Smsmith * and try to switch it off.
52778915Smsmith */
52878915Smsmithstatic void
52978915Smsmithacpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
53078915Smsmith{
53179375Smsmith    ACPI_HANDLE		cooler;
53278915Smsmith
53391126Smsmith    ACPI_FUNCTION_TRACE(__func__);
53478915Smsmith
53578999Smsmith    ACPI_ASSERTLOCK;
53678999Smsmith
53778915Smsmith    switch(obj->Type) {
53878915Smsmith    case ACPI_TYPE_STRING:
53982372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n", obj->String.Pointer));
54078915Smsmith
54178915Smsmith	/*
54278915Smsmith	 * Find the handle for the device and turn it off.
54378915Smsmith	 * The String object here seems to contain a fully-qualified path, so we
54478915Smsmith	 * don't have to search for it in our parents.
54578915Smsmith	 *
54678915Smsmith	 * XXX This may not always be the case.
54778915Smsmith	 */
54891126Smsmith	if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler)))
54978915Smsmith	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
55078915Smsmith	break;
55178915Smsmith
55278915Smsmith    default:
55382372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
55482372Smsmith			  obj->Type));
55578915Smsmith	break;
55678915Smsmith    }
55779375Smsmith    return_VOID;
55878915Smsmith}
55978915Smsmith
56078915Smsmith/*
56178915Smsmith * Given an object, verify that it's a reference to a device of some sort,
56278915Smsmith * and try to switch it on.
56378915Smsmith *
56478915Smsmith * XXX replication of off/on function code is bad, mmmkay?
56578915Smsmith */
56678915Smsmithstatic void
56778915Smsmithacpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
56878915Smsmith{
56978915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
57078999Smsmith    ACPI_HANDLE			cooler;
57179375Smsmith    ACPI_STATUS			status;
57279375Smsmith
57391126Smsmith    ACPI_FUNCTION_TRACE(__func__);
57478915Smsmith
57578999Smsmith    ACPI_ASSERTLOCK;
57678999Smsmith
57778915Smsmith    switch(obj->Type) {
57878915Smsmith    case ACPI_TYPE_STRING:
57982372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n", obj->String.Pointer));
58078915Smsmith
58178999Smsmith	/*
58278999Smsmith	 * Find the handle for the device and turn it off.
58378999Smsmith	 * The String object here seems to contain a fully-qualified path, so we
58478999Smsmith	 * don't have to search for it in our parents.
58578999Smsmith	 *
58678999Smsmith	 * XXX This may not always be the case.
58778999Smsmith	 */
58891126Smsmith	if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler))) {
58979375Smsmith	    if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0))) {
59086552Siwasaki		ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
59186552Siwasaki		    "failed to activate %s - %s\n",
59286552Siwasaki		    obj->String.Pointer, AcpiFormatException(status));
59379375Smsmith	    }
59479375Smsmith	} else {
59586552Siwasaki	    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
59686552Siwasaki		"couldn't find %s\n", obj->String.Pointer);
59779375Smsmith	}
59878915Smsmith	break;
59978915Smsmith
60078915Smsmith    default:
60182372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
60282372Smsmith			  obj->Type));
60378915Smsmith	break;
60478915Smsmith    }
60578915Smsmith	return_VOID;
60678915Smsmith}
60778915Smsmith
60878915Smsmith/*
60978915Smsmith * Read/debug-print a parameter, default it to -1.
61078915Smsmith */
61178915Smsmithstatic void
61278915Smsmithacpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
61378915Smsmith{
61478915Smsmith
61591126Smsmith    ACPI_FUNCTION_TRACE(__func__);
61678915Smsmith
61778999Smsmith    ACPI_ASSERTLOCK;
61878999Smsmith
61991126Smsmith    if (ACPI_FAILURE(acpi_EvaluateInteger(sc->tz_handle, node, data))) {
62078915Smsmith	*data = -1;
62178915Smsmith    } else {
62282372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n", acpi_name(sc->tz_handle),
62382372Smsmith			  node, *data));
62478915Smsmith    }
62578915Smsmith    return_VOID;
62678915Smsmith}
62779283Smsmith
62879283Smsmith/*
62979283Smsmith * Sanity-check a temperature value.  Assume that setpoints
63079283Smsmith * should be between 0C and 150C.
63179283Smsmith */
63279283Smsmithstatic void
63379283Smsmithacpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
63479283Smsmith{
63579283Smsmith    if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) {
63679283Smsmith	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
63779283Smsmith		      what, TZ_KELVTOC(*val));
63879283Smsmith	*val = -1;
63979283Smsmith    }
64079283Smsmith}
64179375Smsmith
64279375Smsmith/*
64379375Smsmith * Respond to a sysctl on the active state node.
64479375Smsmith */
64579375Smsmithstatic int
64679375Smsmithacpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
64779375Smsmith{
64879375Smsmith    struct acpi_tz_softc	*sc;
64979375Smsmith    int				active;
65079375Smsmith    int		 		error;
65179375Smsmith
65279375Smsmith    ACPI_LOCK;
65379375Smsmith
65479375Smsmith    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
65579375Smsmith    active = sc->tz_active;
65679375Smsmith    error = sysctl_handle_int(oidp, &active, 0, req);
65779375Smsmith
65879375Smsmith    /* error or no new value */
65979375Smsmith    if ((error != 0) || (req->newptr == NULL))
66079375Smsmith	goto out;
66178915Smsmith
66279375Smsmith    /* range check */
66379375Smsmith    if ((active < -1) || (active >= TZ_NUMLEVELS)) {
66479375Smsmith	error = EINVAL;
66579375Smsmith	goto out;
66679375Smsmith    }
66779375Smsmith
66879375Smsmith    /* set new preferred level and re-switch */
66979375Smsmith    sc->tz_requested = active;
67079375Smsmith    acpi_tz_monitor(sc);
67179375Smsmith
67279375Smsmith out:
67379375Smsmith    ACPI_UNLOCK;
67479375Smsmith    return(error);
67579375Smsmith}
67679375Smsmith
67778915Smsmith/*
67878915Smsmith * Respond to a Notify event sent to the zone.
67978915Smsmith */
68078915Smsmithstatic void
68171874Smsmithacpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
68271874Smsmith{
68378915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
68478915Smsmith
68591126Smsmith    ACPI_FUNCTION_TRACE(__func__);
68670271Stakawata
68778999Smsmith    ACPI_ASSERTLOCK;
68878999Smsmith
68978915Smsmith    switch(notify) {
69078915Smsmith    case TZ_NOTIFY_TEMPERATURE:
69178999Smsmith	/* temperature change occurred */
69278999Smsmith	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
69378915Smsmith	break;
69478915Smsmith    case TZ_NOTIFY_DEVICES:
69578915Smsmith    case TZ_NOTIFY_LEVELS:
69678999Smsmith	/* zone devices/setpoints changed */
69778999Smsmith	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
69878915Smsmith	break;
69978915Smsmith    default:
70086552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
70186552Siwasaki	    "unknown Notify event 0x%x\n", notify);
70278915Smsmith	break;
70371874Smsmith    }
70471874Smsmith    return_VOID;
70571874Smsmith}
70670271Stakawata
70778915Smsmith/*
70878915Smsmith * Poll the thermal zone.
70978915Smsmith */
71078915Smsmithstatic void
71191126Smsmithacpi_tz_timeout(struct acpi_tz_softc *sc)
71278915Smsmith{
71378915Smsmith
71479375Smsmith    /* do we need to get the power profile settings? */
71579375Smsmith    if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
71691126Smsmith	acpi_tz_powerprofile((void *)sc);
71779375Smsmith	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
71879375Smsmith    }
71979375Smsmith
72091126Smsmith    ACPI_ASSERTLOCK;
72178915Smsmith
72291126Smsmith    /* check the current temperature and take action based on it */
72391126Smsmith    acpi_tz_monitor(sc);
72491126Smsmith
72578915Smsmith    /* XXX passive cooling actions? */
72678915Smsmith}
72779375Smsmith
72879375Smsmith/*
72979375Smsmith * System power profile may have changed; fetch and notify the
73079375Smsmith * thermal zone accordingly.
73179375Smsmith *
73279375Smsmith * Since this can be called from an arbitrary eventhandler, it needs
73379375Smsmith * to get the ACPI lock itself.
73479375Smsmith */
73579375Smsmithstatic void
73679375Smsmithacpi_tz_powerprofile(void *arg)
73779375Smsmith{
73879375Smsmith    ACPI_OBJECT_LIST		args;
73979375Smsmith    ACPI_OBJECT			obj;
74079375Smsmith    ACPI_STATUS			status;
74179375Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
74279375Smsmith
74379375Smsmith    ACPI_LOCK;
74479375Smsmith
74579375Smsmith    /* check that we haven't decided there's no _SCP method */
74679375Smsmith    if (!(sc->tz_flags & TZ_FLAG_NO_SCP)) {
74779375Smsmith
74879375Smsmith	/* call _SCP to set the new profile */
74979375Smsmith	obj.Type = ACPI_TYPE_INTEGER;
75079375Smsmith	obj.Integer.Value = (powerprofile_get_state() == POWERPROFILE_PERFORMANCE) ? 0 : 1;
75179375Smsmith	args.Count = 1;
75279375Smsmith	args.Pointer = &obj;
75379375Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL))) {
75479385Smsmith	    if (status != AE_NOT_FOUND)
75586552Siwasaki		ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
75686552Siwasaki		    "can't evaluate %s._SCP - %s\n", acpi_name(sc->tz_handle),
75786552Siwasaki		    AcpiFormatException(status));
75879375Smsmith	    sc->tz_flags |= TZ_FLAG_NO_SCP;
75979375Smsmith	} else {
76079375Smsmith	    /* we have to re-evaluate the entire zone now */
76179375Smsmith	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
76279375Smsmith	}
76379375Smsmith    }
76479375Smsmith    ACPI_UNLOCK;
76579375Smsmith}
76679375Smsmith
76791126Smsmith/*
76891126Smsmith * Thermal zone monitor thread.
76991126Smsmith */
77091126Smsmithstatic void
77191126Smsmithacpi_tz_thread(void *arg)
77291126Smsmith{
77391126Smsmith    device_t	*devs;
77491126Smsmith    int		devcount, i;
77591126Smsmith
77691126Smsmith    ACPI_FUNCTION_TRACE(__func__);
77791126Smsmith
77891126Smsmith
77991126Smsmith    devs = NULL;
78091126Smsmith    devcount = 0;
78191126Smsmith
78291126Smsmith    for (;;) {
78391126Smsmith	tsleep(&acpi_tz_proc, PZERO, "nothing", hz * acpi_tz_polling_rate);
78491126Smsmith
78591215Smsmith	mtx_lock(&Giant);
78691215Smsmith
78791126Smsmith	if (devcount == 0)
78891126Smsmith	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
78991126Smsmith
79091126Smsmith	ACPI_LOCK;
79191126Smsmith	for (i = 0; i < devcount; i++)
79291126Smsmith	    acpi_tz_timeout(device_get_softc(devs[i]));
79391126Smsmith	ACPI_UNLOCK;
79491215Smsmith
79591215Smsmith	mtx_unlock(&Giant);
79691126Smsmith    }
79791126Smsmith}
798