acpi_thermal.c revision 121493
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
28119418Sobrien#include <sys/cdefs.h>
29119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_thermal.c 121493 2003-10-25 05:03:25Z njl $");
30119418Sobrien
3167761Smsmith#include "opt_acpi.h"
3267761Smsmith#include <sys/param.h>
3367761Smsmith#include <sys/kernel.h>
3491126Smsmith#include <sys/kthread.h>
3567761Smsmith#include <sys/bus.h>
3691126Smsmith#include <sys/proc.h>
3778915Smsmith#include <sys/reboot.h>
3879283Smsmith#include <sys/sysctl.h>
3991126Smsmith#include <sys/unistd.h>
4091640Siwasaki#include <sys/power.h>
4167761Smsmith
4267761Smsmith#include "acpi.h"
4367761Smsmith#include <dev/acpica/acpivar.h>
4467761Smsmith
45119529Snjl/* Hooks for the ACPI CA debugging infrastructure */
4678999Smsmith#define _COMPONENT	ACPI_THERMAL
4791126SmsmithACPI_MODULE_NAME("THERMAL")
4869744Smsmith
4971874Smsmith#define TZ_ZEROC	2732
5071874Smsmith#define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
5167761Smsmith
5278915Smsmith#define TZ_NOTIFY_TEMPERATURE	0x80
5378915Smsmith#define TZ_NOTIFY_DEVICES	0x81
5478915Smsmith#define TZ_NOTIFY_LEVELS	0x82
5578915Smsmith
56119529Snjl/* Check for temperature changes every 30 seconds by default */
57119529Snjl#define TZ_POLLRATE	30
5878915Smsmith
59119529Snjl/* ACPI spec defines this */
60119529Snjl#define TZ_NUMLEVELS	10
6179375Smsmithstruct acpi_tz_zone {
6278915Smsmith    int		ac[TZ_NUMLEVELS];
6378915Smsmith    ACPI_BUFFER	al[TZ_NUMLEVELS];
6478915Smsmith    int		crt;
6578915Smsmith    int		hot;
6678915Smsmith    ACPI_BUFFER	psl;
6778915Smsmith    int		psv;
6878915Smsmith    int		tc1;
6978915Smsmith    int		tc2;
7078915Smsmith    int		tsp;
7178915Smsmith    int		tzp;
7278915Smsmith};
7378915Smsmith
7478915Smsmith
7567761Smsmithstruct acpi_tz_softc {
76119529Snjl    device_t			tz_dev;
77119529Snjl    ACPI_HANDLE			tz_handle;	/*Thermal zone handle*/
78119529Snjl    int				tz_temperature;	/*Current temperature*/
79119529Snjl    int				tz_active;	/*Current active cooling*/
8079375Smsmith#define TZ_ACTIVE_NONE		-1
81119529Snjl    int				tz_requested;	/*Minimum active cooling*/
82119529Snjl    int				tz_thflags;	/*Current temp-related flags*/
8379375Smsmith#define TZ_THFLAG_NONE		0
8479375Smsmith#define TZ_THFLAG_PSV		(1<<0)
8579375Smsmith#define TZ_THFLAG_HOT		(1<<2)
8679375Smsmith#define TZ_THFLAG_CRT		(1<<3)
8779283Smsmith    int				tz_flags;
88119529Snjl#define TZ_FLAG_NO_SCP		(1<<0)		/*No _SCP method*/
89119529Snjl#define TZ_FLAG_GETPROFILE	(1<<1)		/*Get power_profile in timeout*/
90119529Snjl    struct timespec		tz_cooling_started;
91119529Snjl					/*Current cooling starting time*/
9279283Smsmith
93119529Snjl    struct sysctl_ctx_list	tz_sysctl_ctx;
9479283Smsmith    struct sysctl_oid		*tz_sysctl_tree;
9578915Smsmith
96119529Snjl    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);
103119529Snjlstatic void	acpi_tz_monitor(void *Context);
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);
107119529Snjlstatic void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node,
108119529Snjl				 int *data);
10979283Smsmithstatic void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
11079375Smsmithstatic int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
111119529Snjlstatic void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify,
112119529Snjl				       void *context);
11391126Smsmithstatic void	acpi_tz_timeout(struct acpi_tz_softc *sc);
11491640Siwasakistatic void	acpi_tz_power_profile(void *arg);
11591126Smsmithstatic void	acpi_tz_thread(void *arg);
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
137119529Snjl/* Minimum cooling run time */
138119529Snjlstatic int			acpi_tz_min_runtime = 0;
13988420Siwasakistatic int			acpi_tz_polling_rate = TZ_POLLRATE;
14085699Siwasaki
141119529Snjl/* Timezone polling thread */
142119529Snjlstatic struct proc		*acpi_tz_proc;
143119529Snjl
14478915Smsmith/*
14578915Smsmith * Match an ACPI thermal zone.
14678915Smsmith */
14767761Smsmithstatic int
14867761Smsmithacpi_tz_probe(device_t dev)
14967761Smsmith{
15078999Smsmith    int		result;
151105282Sjhb    ACPI_LOCK_DECL;
15278999Smsmith
15378999Smsmith    ACPI_LOCK;
15478999Smsmith
155119529Snjl    /* No FUNCTION_TRACE - too noisy */
15669744Smsmith
157119529Snjl    if (acpi_get_type(dev) == ACPI_TYPE_THERMAL && !acpi_disabled("thermal")) {
158120453Snjl	device_set_desc(dev, "Thermal Zone");
15978999Smsmith	result = -10;
16078999Smsmith    } else {
16178999Smsmith	result = ENXIO;
16267761Smsmith    }
16378999Smsmith    ACPI_UNLOCK;
164119529Snjl    return (result);
16567761Smsmith}
16667761Smsmith
16778915Smsmith/*
16878915Smsmith * Attach to an ACPI thermal zone.
16978915Smsmith */
17067761Smsmithstatic int
17167761Smsmithacpi_tz_attach(device_t dev)
17267761Smsmith{
17367761Smsmith    struct acpi_tz_softc	*sc;
17479283Smsmith    struct acpi_softc		*acpi_sc;
17578915Smsmith    int				error;
17679283Smsmith    char			oidname[8];
177105282Sjhb    ACPI_LOCK_DECL;
17867761Smsmith
17996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
18069744Smsmith
18178999Smsmith    ACPI_LOCK;
18278999Smsmith
18367761Smsmith    sc = device_get_softc(dev);
18467761Smsmith    sc->tz_dev = dev;
18567761Smsmith    sc->tz_handle = acpi_get_handle(dev);
18679375Smsmith    sc->tz_requested = TZ_ACTIVE_NONE;
18788420Siwasaki    sc->tz_tmp_updating = 0;
18867761Smsmith
18978915Smsmith    /*
19078915Smsmith     * Parse the current state of the thermal zone and build control
19178915Smsmith     * structures.
19278915Smsmith     */
19378915Smsmith    if ((error = acpi_tz_establish(sc)) != 0)
19478999Smsmith	goto out;
19578915Smsmith
19678915Smsmith    /*
19778915Smsmith     * Register for any Notify events sent to this zone.
19878915Smsmith     */
19971874Smsmith    AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
20078999Smsmith			     acpi_tz_notify_handler, sc);
20170271Stakawata
20271874Smsmith    /*
20379283Smsmith     * Create our sysctl nodes.
20479283Smsmith     *
20579283Smsmith     * XXX we need a mechanism for adding nodes under ACPI.
20679283Smsmith     */
20779283Smsmith    if (device_get_unit(dev) == 0) {
20879283Smsmith	acpi_sc = acpi_device_get_parent_softc(dev);
20979283Smsmith	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
21079283Smsmith	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
211119529Snjl			      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
212119529Snjl			      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
21385699Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
21485699Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
21585699Siwasaki		       OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW,
216119529Snjl		       &acpi_tz_min_runtime, 0,
217119529Snjl		       "minimum cooling run time in sec");
21888420Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
21988420Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
22088420Siwasaki		       OID_AUTO, "polling_rate", CTLFLAG_RD | CTLFLAG_RW,
22188420Siwasaki		       &acpi_tz_polling_rate, 0, "monitor polling rate");
22279283Smsmith    }
22379283Smsmith    sysctl_ctx_init(&sc->tz_sysctl_ctx);
22479283Smsmith    sprintf(oidname, "tz%d", device_get_unit(dev));
22579283Smsmith    sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
226119529Snjl					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
227119529Snjl					 OID_AUTO, oidname, CTLFLAG_RD, 0, "");
22879283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
22979283Smsmith		   OID_AUTO, "temperature", CTLFLAG_RD,
23079283Smsmith		   &sc->tz_temperature, 0, "current thermal zone temperature");
23179375Smsmith    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23279375Smsmith		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
23379375Smsmith		    sc, 0, acpi_tz_active_sysctl, "I", "");
23479375Smsmith
23579283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23679375Smsmith		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
23779375Smsmith		   &sc->tz_thflags, 0, "thermal zone flags");
23879283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23979283Smsmith		   OID_AUTO, "_PSV", CTLFLAG_RD,
24079375Smsmith		   &sc->tz_zone.psv, 0, "");
24179283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
24279283Smsmith		   OID_AUTO, "_HOT", CTLFLAG_RD,
24379375Smsmith		   &sc->tz_zone.hot, 0, "");
24479283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
24579283Smsmith		   OID_AUTO, "_CRT", CTLFLAG_RD,
24679375Smsmith		   &sc->tz_zone.crt, 0, "");
24786399Siwasaki    SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
24886399Siwasaki		      OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac,
24986399Siwasaki		      sizeof(sc->tz_zone.ac), "I", "");
25079283Smsmith
25179283Smsmith    /*
25279375Smsmith     * Register our power profile event handler, and flag it for a manual
25379375Smsmith     * invocation by our timeout.  We defer it like this so that the rest
25479375Smsmith     * of the subsystem has time to come up.
25579375Smsmith     */
25691640Siwasaki    EVENTHANDLER_REGISTER(power_profile_change, acpi_tz_power_profile, sc, 0);
25779375Smsmith    sc->tz_flags |= TZ_FLAG_GETPROFILE;
25879375Smsmith
25979375Smsmith    /*
26071874Smsmith     * Don't bother evaluating/printing the temperature at this point;
26171874Smsmith     * on many systems it'll be bogus until the EC is running.
26271874Smsmith     */
26378999Smsmith
26491126Smsmith    /*
26591126Smsmith     * Create our thread; we only need one, it will service all of the
26691126Smsmith     * thermal zones.
26791126Smsmith     */
26891126Smsmith    if (acpi_tz_proc == NULL) {
26991126Smsmith	    error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_proc,
270104354Sscottl				   RFHIGHPID, 0, "acpi_thermal");
27191126Smsmith	    if (error != 0) {
272119529Snjl		    device_printf(sc->tz_dev, "could not create thread - %d",
273119529Snjl				  error);
27491126Smsmith		    goto out;
27591126Smsmith	    }
27691126Smsmith    }
27791126Smsmith
27878999Smsmith out:
27978999Smsmith    ACPI_UNLOCK;
28079375Smsmith
281119529Snjl    return_VALUE (error);
28267761Smsmith}
28370271Stakawata
28478915Smsmith/*
28578915Smsmith * Parse the current state of this thermal zone and set up to use it.
28678915Smsmith *
28778915Smsmith * Note that we may have previous state, which will have to be discarded.
28878915Smsmith */
28978915Smsmithstatic int
29078915Smsmithacpi_tz_establish(struct acpi_tz_softc *sc)
29178915Smsmith{
29278915Smsmith    ACPI_OBJECT	*obj;
29378915Smsmith    int		i;
29478915Smsmith    char	nbuf[8];
29578915Smsmith
29696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
29778915Smsmith
29878999Smsmith    ACPI_ASSERTLOCK;
29978999Smsmith
300119529Snjl    /* Power everything off and erase any existing state. */
30178915Smsmith    acpi_tz_all_off(sc);
30278915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
30379375Smsmith	if (sc->tz_zone.al[i].Pointer != NULL)
30479375Smsmith	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
30579375Smsmith    if (sc->tz_zone.psl.Pointer != NULL)
30679375Smsmith	AcpiOsFree(sc->tz_zone.psl.Pointer);
30779375Smsmith    bzero(&sc->tz_zone, sizeof(sc->tz_zone));
30878915Smsmith
309119529Snjl    /* Evaluate thermal zone parameters. */
31078915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
31178915Smsmith	sprintf(nbuf, "_AC%d", i);
31279375Smsmith	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
31378915Smsmith	sprintf(nbuf, "_AL%d", i);
31491126Smsmith	sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
31591126Smsmith	sc->tz_zone.al[i].Pointer = NULL;
31691126Smsmith	AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
31779375Smsmith	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
31878915Smsmith	if (obj != NULL) {
319119529Snjl	    /* Should be a package containing a list of power objects */
32078915Smsmith	    if (obj->Type != ACPI_TYPE_PACKAGE) {
321119529Snjl		device_printf(sc->tz_dev, "%s has unknown type %d, rejecting\n",
32278915Smsmith			      nbuf, obj->Type);
323119529Snjl		return_VALUE (ENXIO);
32478915Smsmith	    }
32578915Smsmith	}
32678915Smsmith    }
32779375Smsmith    acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
32879375Smsmith    acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
32991126Smsmith    sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
33091126Smsmith    sc->tz_zone.psl.Pointer = NULL;
33191126Smsmith    AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
33279375Smsmith    acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
33379375Smsmith    acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
33479375Smsmith    acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
33579375Smsmith    acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
33679375Smsmith    acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
33778915Smsmith
33878915Smsmith    /*
33979283Smsmith     * Sanity-check the values we've been given.
34079283Smsmith     *
34179283Smsmith     * XXX what do we do about systems that give us the same value for
34279283Smsmith     *     more than one of these setpoints?
34379283Smsmith     */
34479375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
34579375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
34679375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
34779283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
34879375Smsmith	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
34979283Smsmith
35079283Smsmith    /*
35178915Smsmith     * Power off everything that we've just been given.
35278915Smsmith     */
35378915Smsmith    acpi_tz_all_off(sc);
35478915Smsmith
355119529Snjl    return_VALUE (0);
35678915Smsmith}
35778915Smsmith
35885699Siwasakistatic char	*aclevel_string[] =	{
35985699Siwasaki	"NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
36085699Siwasaki	"_AC5", "_AC6", "_AC7", "_AC8", "_AC9" };
36185699Siwasaki
36285699Siwasakistatic __inline const char *
36385699Siwasakiacpi_tz_aclevel_string(int active)
36485699Siwasaki{
365119529Snjl	if (active < -1 || active >= TZ_NUMLEVELS)
36685699Siwasaki		return (aclevel_string[0]);
36785699Siwasaki
36885699Siwasaki	return (aclevel_string[active+1]);
36985699Siwasaki}
37085699Siwasaki
37178915Smsmith/*
37278915Smsmith * Evaluate the condition of a thermal zone, take appropriate actions.
37378915Smsmith */
37471874Smsmithstatic void
375119529Snjlacpi_tz_monitor(void *Context)
37671874Smsmith{
377119529Snjl    struct acpi_tz_softc *sc;
378119529Snjl    struct	timespec curtime;
37979283Smsmith    int		temp;
38078915Smsmith    int		i;
38179283Smsmith    int		newactive, newflags;
38286399Siwasaki    ACPI_STATUS	status;
38370271Stakawata
38496926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
38570271Stakawata
38678999Smsmith    ACPI_ASSERTLOCK;
38778999Smsmith
388119529Snjl    sc = (struct acpi_tz_softc *)Context;
389119529Snjl    if (sc->tz_tmp_updating)
39088420Siwasaki	goto out;
39188420Siwasaki    sc->tz_tmp_updating = 1;
39288420Siwasaki
393119529Snjl    /* Get the current temperature. */
394119529Snjl    status = acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp);
395119529Snjl    if (ACPI_FAILURE(status)) {
39686552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
39786552Siwasaki	    "error fetching current temperature -- %s\n",
39886552Siwasaki	     AcpiFormatException(status));
39978915Smsmith	/* XXX disable zone? go to max cooling? */
40088420Siwasaki	goto out;
40171874Smsmith    }
40288420Siwasaki
40382372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
40479283Smsmith    sc->tz_temperature = temp;
40578915Smsmith
40678915Smsmith    /*
40778915Smsmith     * Work out what we ought to be doing right now.
40879283Smsmith     *
40979283Smsmith     * Note that the _ACx levels sort from hot to cold.
41078915Smsmith     */
41179283Smsmith    newactive = TZ_ACTIVE_NONE;
41279375Smsmith    for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
41379375Smsmith	if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
41479283Smsmith	    newactive = i;
41582967Siwasaki	    if (sc->tz_active != newactive) {
416119529Snjl		ACPI_VPRINT(sc->tz_dev,
417119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
418119529Snjl			    "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
419119529Snjl			    TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
42085699Siwasaki		getnanotime(&sc->tz_cooling_started);
42182967Siwasaki	    }
42279375Smsmith	}
42379375Smsmith    }
42479283Smsmith
42585699Siwasaki    /*
42685699Siwasaki     * We are going to get _ACx level down (colder side), but give a guaranteed
42785699Siwasaki     * minimum cooling run time if requested.
42885699Siwasaki     */
42985699Siwasaki    if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
43085699Siwasaki	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
431119529Snjl
43285699Siwasaki	getnanotime(&curtime);
43385699Siwasaki	timespecsub(&curtime, &sc->tz_cooling_started);
434119529Snjl	if (curtime.tv_sec < acpi_tz_min_runtime)
43585699Siwasaki	    newactive = sc->tz_active;
43685699Siwasaki    }
43785699Siwasaki
438119529Snjl    /* Handle user override of active mode */
43979375Smsmith    if (sc->tz_requested > newactive)
44079375Smsmith	newactive = sc->tz_requested;
44178915Smsmith
44279375Smsmith    /* update temperature-related flags */
44379375Smsmith    newflags = TZ_THFLAG_NONE;
44479375Smsmith    if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv))
44579375Smsmith	newflags |= TZ_THFLAG_PSV;
44679375Smsmith    if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot))
44779375Smsmith	newflags |= TZ_THFLAG_HOT;
44879375Smsmith    if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt))
44979375Smsmith	newflags |= TZ_THFLAG_CRT;
45079375Smsmith
451119529Snjl    /* If the active cooling state has changed, we have to switch things. */
45279283Smsmith    if (newactive != sc->tz_active) {
453119529Snjl	/* Turn off the cooling devices that are on, if any are */
45479283Smsmith	if (sc->tz_active != TZ_ACTIVE_NONE)
455119529Snjl	    acpi_ForeachPackageObject(
456119529Snjl		(ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
457119529Snjl		acpi_tz_switch_cooler_off, sc);
45878915Smsmith
459119529Snjl	/* Turn on cooling devices that are required, if any are */
460119529Snjl	if (newactive != TZ_ACTIVE_NONE) {
461119529Snjl	    acpi_ForeachPackageObject(
462119529Snjl		(ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
463119529Snjl		acpi_tz_switch_cooler_on, sc);
464119529Snjl	}
46586552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
466119529Snjl		    "switched from %s to %s: %d.%dC\n",
467119529Snjl		    acpi_tz_aclevel_string(sc->tz_active),
468119529Snjl		    acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
46979283Smsmith	sc->tz_active = newactive;
47079283Smsmith    }
47178915Smsmith
472119529Snjl    /* XXX (de)activate any passive cooling that may be required. */
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     */
480119529Snjl    if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0 &&
481119529Snjl	(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) == 0) {
482119529Snjl
483119529Snjl	device_printf(sc->tz_dev,
484119529Snjl	    "WARNING - current temperature (%d.%dC) exceeds system limits\n",
48597274Sbde		      TZ_KELVTOC(sc->tz_temperature));
48679283Smsmith	/* shutdown_nice(RB_POWEROFF);*/
48778915Smsmith    }
48879375Smsmith    sc->tz_thflags = newflags;
48978915Smsmith
49088420Siwasakiout:
49188420Siwasaki    sc->tz_tmp_updating = 0;
49271874Smsmith    return_VOID;
49371874Smsmith}
49470271Stakawata
49578915Smsmith/*
49678915Smsmith * Turn off all the cooling devices.
49778915Smsmith */
49871874Smsmithstatic void
49978915Smsmithacpi_tz_all_off(struct acpi_tz_softc *sc)
50078915Smsmith{
50178915Smsmith    int		i;
50278915Smsmith
50396926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
50478999Smsmith
50578999Smsmith    ACPI_ASSERTLOCK;
50678915Smsmith
507119529Snjl    /* Scan all the _ALx objects and turn them all off. */
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;
521119529Snjl
52278915Smsmith    return_VOID;
52378915Smsmith}
52478915Smsmith
52578915Smsmith/*
52678915Smsmith * Given an object, verify that it's a reference to a device of some sort,
52778915Smsmith * and try to switch it off.
52878915Smsmith */
52978915Smsmithstatic void
53078915Smsmithacpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
53178915Smsmith{
53279375Smsmith    ACPI_HANDLE		cooler;
53378915Smsmith
53496926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
53578915Smsmith
53678999Smsmith    ACPI_ASSERTLOCK;
53778999Smsmith
53878915Smsmith    switch(obj->Type) {
539102470Siwasaki    case ACPI_TYPE_ANY:
540119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
541119529Snjl			 acpi_name(obj->Reference.Handle)));
542102470Siwasaki
543102470Siwasaki	acpi_pwr_switch_consumer(obj->Reference.Handle, ACPI_STATE_D3);
544102470Siwasaki	break;
54578915Smsmith    case ACPI_TYPE_STRING:
546119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
547119529Snjl			 obj->String.Pointer));
54878915Smsmith
54978915Smsmith	/*
55078915Smsmith	 * Find the handle for the device and turn it off.
55178915Smsmith	 * The String object here seems to contain a fully-qualified path, so we
55278915Smsmith	 * don't have to search for it in our parents.
55378915Smsmith	 *
55478915Smsmith	 * XXX This may not always be the case.
55578915Smsmith	 */
55691126Smsmith	if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler)))
55778915Smsmith	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
55878915Smsmith	break;
55978915Smsmith    default:
560119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS,
561119529Snjl			 "called to handle unsupported object type %d\n",
562119529Snjl			 obj->Type));
56378915Smsmith	break;
56478915Smsmith    }
565119529Snjl
56679375Smsmith    return_VOID;
56778915Smsmith}
56878915Smsmith
56978915Smsmith/*
57078915Smsmith * Given an object, verify that it's a reference to a device of some sort,
57178915Smsmith * and try to switch it on.
57278915Smsmith *
57378915Smsmith * XXX replication of off/on function code is bad, mmmkay?
57478915Smsmith */
57578915Smsmithstatic void
57678915Smsmithacpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
57778915Smsmith{
57878915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
57978999Smsmith    ACPI_HANDLE			cooler;
58079375Smsmith    ACPI_STATUS			status;
58179375Smsmith
58296926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
58378915Smsmith
58478999Smsmith    ACPI_ASSERTLOCK;
58578999Smsmith
58678915Smsmith    switch(obj->Type) {
587102470Siwasaki    case ACPI_TYPE_ANY:
588119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
589119529Snjl			 acpi_name(obj->Reference.Handle)));
590102470Siwasaki
591119529Snjl	status = acpi_pwr_switch_consumer(obj->Reference.Handle, ACPI_STATE_D0);
592119529Snjl	if (ACPI_FAILURE(status)) {
593102470Siwasaki	    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
594119529Snjl			"failed to activate %s - %s\n",
595119529Snjl			acpi_name(obj->Reference.Handle),
596119529Snjl			AcpiFormatException(status));
597102470Siwasaki	}
598102470Siwasaki	break;
59978915Smsmith    case ACPI_TYPE_STRING:
600119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
601119529Snjl			 obj->String.Pointer));
60278915Smsmith
60378999Smsmith	/*
60478999Smsmith	 * Find the handle for the device and turn it off.
60578999Smsmith	 * The String object here seems to contain a fully-qualified path, so we
60678999Smsmith	 * don't have to search for it in our parents.
60778999Smsmith	 *
60878999Smsmith	 * XXX This may not always be the case.
60978999Smsmith	 */
61091126Smsmith	if (ACPI_SUCCESS(AcpiGetHandle(NULL, obj->String.Pointer, &cooler))) {
611119529Snjl	    status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
612119529Snjl	    if (ACPI_FAILURE(status)) {
613119529Snjl		ACPI_VPRINT(sc->tz_dev,
614119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
615119529Snjl			    "failed to activate %s - %s\n",
616119529Snjl			    obj->String.Pointer, AcpiFormatException(status));
61779375Smsmith	    }
61879375Smsmith	} else {
61986552Siwasaki	    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
620119529Snjl			"couldn't find %s\n", obj->String.Pointer);
62179375Smsmith	}
62278915Smsmith	break;
62378915Smsmith    default:
624119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "unsupported object type %d\n",
62582372Smsmith			  obj->Type));
62678915Smsmith	break;
62778915Smsmith    }
628119529Snjl
629119529Snjl    return_VOID;
63078915Smsmith}
63178915Smsmith
63278915Smsmith/*
63378915Smsmith * Read/debug-print a parameter, default it to -1.
63478915Smsmith */
63578915Smsmithstatic void
63678915Smsmithacpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
63778915Smsmith{
63878915Smsmith
63996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
64078915Smsmith
64178999Smsmith    ACPI_ASSERTLOCK;
64278999Smsmith
64391126Smsmith    if (ACPI_FAILURE(acpi_EvaluateInteger(sc->tz_handle, node, data))) {
64478915Smsmith	*data = -1;
64578915Smsmith    } else {
646119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n",
647119529Snjl			 acpi_name(sc->tz_handle), node, *data));
64878915Smsmith    }
649119529Snjl
65078915Smsmith    return_VOID;
65178915Smsmith}
65279283Smsmith
65379283Smsmith/*
65479283Smsmith * Sanity-check a temperature value.  Assume that setpoints
65579283Smsmith * should be between 0C and 150C.
65679283Smsmith */
65779283Smsmithstatic void
65879283Smsmithacpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
65979283Smsmith{
660119529Snjl    if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 1500)) {
66179283Smsmith	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
66279283Smsmith		      what, TZ_KELVTOC(*val));
66379283Smsmith	*val = -1;
66479283Smsmith    }
66579283Smsmith}
66679375Smsmith
66779375Smsmith/*
66879375Smsmith * Respond to a sysctl on the active state node.
66979375Smsmith */
67079375Smsmithstatic int
67179375Smsmithacpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
67279375Smsmith{
67379375Smsmith    struct acpi_tz_softc	*sc;
67479375Smsmith    int				active;
67579375Smsmith    int		 		error;
676105282Sjhb    ACPI_LOCK_DECL;
67779375Smsmith
67879375Smsmith    ACPI_LOCK;
67979375Smsmith
68079375Smsmith    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
68179375Smsmith    active = sc->tz_active;
68279375Smsmith    error = sysctl_handle_int(oidp, &active, 0, req);
68379375Smsmith
684119529Snjl    /* Error or no new value */
685119529Snjl    if (error != 0 || req->newptr == NULL)
68679375Smsmith	goto out;
687119529Snjl    if (active < -1 || active >= TZ_NUMLEVELS) {
68879375Smsmith	error = EINVAL;
68979375Smsmith	goto out;
69079375Smsmith    }
69179375Smsmith
692119529Snjl    /* Set new preferred level and re-switch */
69379375Smsmith    sc->tz_requested = active;
69479375Smsmith    acpi_tz_monitor(sc);
69579375Smsmith
69679375Smsmith out:
69779375Smsmith    ACPI_UNLOCK;
698119529Snjl    return (error);
69979375Smsmith}
70079375Smsmith
70178915Smsmith/*
70278915Smsmith * Respond to a Notify event sent to the zone.
70378915Smsmith */
70478915Smsmithstatic void
70571874Smsmithacpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
70671874Smsmith{
70778915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
70878915Smsmith
70996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
71070271Stakawata
71178999Smsmith    ACPI_ASSERTLOCK;
71278999Smsmith
71378915Smsmith    switch(notify) {
71478915Smsmith    case TZ_NOTIFY_TEMPERATURE:
715119529Snjl	/* Temperature change occurred */
716119529Snjl	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, acpi_tz_monitor, sc);
71778915Smsmith	break;
71878915Smsmith    case TZ_NOTIFY_DEVICES:
71978915Smsmith    case TZ_NOTIFY_LEVELS:
720119529Snjl	/* Zone devices/setpoints changed */
721119529Snjl	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
722119529Snjl				(OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
72378915Smsmith	break;
72478915Smsmith    default:
72586552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
726119529Snjl		    "unknown Notify event 0x%x\n", notify);
72778915Smsmith	break;
72871874Smsmith    }
729119529Snjl
730121493Snjl    acpi_UserNotify("Thermal", h, notify);
731121493Snjl
73271874Smsmith    return_VOID;
73371874Smsmith}
73470271Stakawata
73578915Smsmith/*
73678915Smsmith * Poll the thermal zone.
73778915Smsmith */
73878915Smsmithstatic void
73991126Smsmithacpi_tz_timeout(struct acpi_tz_softc *sc)
74078915Smsmith{
741119529Snjl    /* Do we need to get the power profile settings? */
74279375Smsmith    if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
74391640Siwasaki	acpi_tz_power_profile((void *)sc);
74479375Smsmith	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
74579375Smsmith    }
74679375Smsmith
74791126Smsmith    ACPI_ASSERTLOCK;
74878915Smsmith
749119529Snjl    /* Check the current temperature and take action based on it */
75091126Smsmith    acpi_tz_monitor(sc);
75191126Smsmith
75278915Smsmith    /* XXX passive cooling actions? */
75378915Smsmith}
75479375Smsmith
75579375Smsmith/*
75679375Smsmith * System power profile may have changed; fetch and notify the
75779375Smsmith * thermal zone accordingly.
75879375Smsmith *
75979375Smsmith * Since this can be called from an arbitrary eventhandler, it needs
76079375Smsmith * to get the ACPI lock itself.
76179375Smsmith */
76279375Smsmithstatic void
76391640Siwasakiacpi_tz_power_profile(void *arg)
76479375Smsmith{
76579375Smsmith    ACPI_OBJECT_LIST		args;
76679375Smsmith    ACPI_OBJECT			obj;
76779375Smsmith    ACPI_STATUS			status;
76879375Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
76991640Siwasaki    int				state;
770105282Sjhb    ACPI_LOCK_DECL;
77179375Smsmith
77291640Siwasaki    state = power_profile_get_state();
773119529Snjl    if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
774119529Snjl	return;
77591640Siwasaki
77679375Smsmith    ACPI_LOCK;
77779375Smsmith
77879375Smsmith    /* check that we haven't decided there's no _SCP method */
779119529Snjl    if ((sc->tz_flags & TZ_FLAG_NO_SCP) == 0) {
78079375Smsmith
781119529Snjl	/* Call _SCP to set the new profile */
78279375Smsmith	obj.Type = ACPI_TYPE_INTEGER;
78391640Siwasaki	obj.Integer.Value = (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1;
78479375Smsmith	args.Count = 1;
78579375Smsmith	args.Pointer = &obj;
786119529Snjl	status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL);
787119529Snjl	if (ACPI_FAILURE(status)) {
78879385Smsmith	    if (status != AE_NOT_FOUND)
789119529Snjl		ACPI_VPRINT(sc->tz_dev,
790119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
791119529Snjl			    "can't evaluate %s._SCP - %s\n",
792119529Snjl			    acpi_name(sc->tz_handle),
793119529Snjl			    AcpiFormatException(status));
79479375Smsmith	    sc->tz_flags |= TZ_FLAG_NO_SCP;
79579375Smsmith	} else {
796119529Snjl	    /* We have to re-evaluate the entire zone now */
797119529Snjl	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
798119529Snjl				    (OSD_EXECUTION_CALLBACK)acpi_tz_establish,
799119529Snjl				    sc);
80079375Smsmith	}
80179375Smsmith    }
802119529Snjl
80379375Smsmith    ACPI_UNLOCK;
80479375Smsmith}
80579375Smsmith
80691126Smsmith/*
80791126Smsmith * Thermal zone monitor thread.
80891126Smsmith */
80991126Smsmithstatic void
81091126Smsmithacpi_tz_thread(void *arg)
81191126Smsmith{
81291126Smsmith    device_t	*devs;
81391126Smsmith    int		devcount, i;
814105282Sjhb    ACPI_LOCK_DECL;
81591126Smsmith
81696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
81791126Smsmith
81891126Smsmith    devs = NULL;
81991126Smsmith    devcount = 0;
82091126Smsmith
82191126Smsmith    for (;;) {
822119529Snjl	tsleep(&acpi_tz_proc, PZERO, "tzpoll", hz * acpi_tz_polling_rate);
82391126Smsmith
824105282Sjhb#if __FreeBSD_version >= 500000
82591215Smsmith	mtx_lock(&Giant);
826105282Sjhb#endif
82791215Smsmith
82891126Smsmith	if (devcount == 0)
82991126Smsmith	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
83091126Smsmith
83191126Smsmith	ACPI_LOCK;
83291126Smsmith	for (i = 0; i < devcount; i++)
83391126Smsmith	    acpi_tz_timeout(device_get_softc(devs[i]));
83491126Smsmith	ACPI_UNLOCK;
83591215Smsmith
836105282Sjhb#if __FreeBSD_version >= 500000
83791215Smsmith	mtx_unlock(&Giant);
838105282Sjhb#endif
83991126Smsmith    }
84091126Smsmith}
841