acpi_thermal.c revision 128047
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 128047 2004-04-09 06:55:50Z 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
52125366Snjl#define TZ_NOTIFY_TEMPERATURE	0x80 /* Temperature changed. */
53125366Snjl#define TZ_NOTIFY_LEVELS	0x81 /* Cooling levels changed. */
54125366Snjl#define TZ_NOTIFY_DEVICES	0x82 /* Device lists changed. */
55125366Snjl#define TZ_NOTIFY_CRITICAL	0xcc /* Fake notify that _CRT/_HOT reached. */
5678915Smsmith
57125335Snjl/* Check for temperature changes every 10 seconds by default */
58125335Snjl#define TZ_POLLRATE	10
5978915Smsmith
60125335Snjl/* Make sure the reported temperature is valid for this number of polls. */
61125335Snjl#define TZ_VALIDCHECKS	3
62125335Snjl
63125366Snjl/* Notify the user we will be shutting down in one more poll cycle. */
64125366Snjl#define TZ_NOTIFYCOUNT	(TZ_VALIDCHECKS - 1)
65125366Snjl
66119529Snjl/* ACPI spec defines this */
67119529Snjl#define TZ_NUMLEVELS	10
6879375Smsmithstruct acpi_tz_zone {
6978915Smsmith    int		ac[TZ_NUMLEVELS];
7078915Smsmith    ACPI_BUFFER	al[TZ_NUMLEVELS];
7178915Smsmith    int		crt;
7278915Smsmith    int		hot;
7378915Smsmith    ACPI_BUFFER	psl;
7478915Smsmith    int		psv;
7578915Smsmith    int		tc1;
7678915Smsmith    int		tc2;
7778915Smsmith    int		tsp;
7878915Smsmith    int		tzp;
7978915Smsmith};
8078915Smsmith
8167761Smsmithstruct acpi_tz_softc {
82119529Snjl    device_t			tz_dev;
83119529Snjl    ACPI_HANDLE			tz_handle;	/*Thermal zone handle*/
84119529Snjl    int				tz_temperature;	/*Current temperature*/
85119529Snjl    int				tz_active;	/*Current active cooling*/
8679375Smsmith#define TZ_ACTIVE_NONE		-1
87119529Snjl    int				tz_requested;	/*Minimum active cooling*/
88119529Snjl    int				tz_thflags;	/*Current temp-related flags*/
8979375Smsmith#define TZ_THFLAG_NONE		0
9079375Smsmith#define TZ_THFLAG_PSV		(1<<0)
9179375Smsmith#define TZ_THFLAG_HOT		(1<<2)
9279375Smsmith#define TZ_THFLAG_CRT		(1<<3)
9379283Smsmith    int				tz_flags;
94119529Snjl#define TZ_FLAG_NO_SCP		(1<<0)		/*No _SCP method*/
95119529Snjl#define TZ_FLAG_GETPROFILE	(1<<1)		/*Get power_profile in timeout*/
96119529Snjl    struct timespec		tz_cooling_started;
97119529Snjl					/*Current cooling starting time*/
9879283Smsmith
99119529Snjl    struct sysctl_ctx_list	tz_sysctl_ctx;
10079283Smsmith    struct sysctl_oid		*tz_sysctl_tree;
10178915Smsmith
102119529Snjl    struct acpi_tz_zone 	tz_zone;	/*Thermal zone parameters*/
10388420Siwasaki    int				tz_tmp_updating;
104125335Snjl    int				tz_validchecks;
10567761Smsmith};
10667761Smsmith
10767761Smsmithstatic int	acpi_tz_probe(device_t dev);
10867761Smsmithstatic int	acpi_tz_attach(device_t dev);
10978915Smsmithstatic int	acpi_tz_establish(struct acpi_tz_softc *sc);
110119529Snjlstatic void	acpi_tz_monitor(void *Context);
11178915Smsmithstatic void	acpi_tz_all_off(struct acpi_tz_softc *sc);
11278915Smsmithstatic void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
11378915Smsmithstatic void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
114119529Snjlstatic void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node,
115119529Snjl				 int *data);
11679283Smsmithstatic void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
11779375Smsmithstatic int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
118119529Snjlstatic void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify,
119119529Snjl				       void *context);
12091126Smsmithstatic void	acpi_tz_timeout(struct acpi_tz_softc *sc);
12191640Siwasakistatic void	acpi_tz_power_profile(void *arg);
12291126Smsmithstatic void	acpi_tz_thread(void *arg);
12391126Smsmith
12467761Smsmithstatic device_method_t acpi_tz_methods[] = {
12567761Smsmith    /* Device interface */
12667761Smsmith    DEVMETHOD(device_probe,	acpi_tz_probe),
12767761Smsmith    DEVMETHOD(device_attach,	acpi_tz_attach),
12867761Smsmith
12967761Smsmith    {0, 0}
13067761Smsmith};
13167761Smsmith
13267761Smsmithstatic driver_t acpi_tz_driver = {
13367761Smsmith    "acpi_tz",
13467761Smsmith    acpi_tz_methods,
13567761Smsmith    sizeof(struct acpi_tz_softc),
13667761Smsmith};
13767761Smsmith
13889054Smsmithstatic devclass_t acpi_tz_devclass;
13967761SmsmithDRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
14067761Smsmith
14179283Smsmithstatic struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
14279283Smsmithstatic struct sysctl_oid	*acpi_tz_sysctl_tree;
14379283Smsmith
144119529Snjl/* Minimum cooling run time */
145119529Snjlstatic int			acpi_tz_min_runtime = 0;
14688420Siwasakistatic int			acpi_tz_polling_rate = TZ_POLLRATE;
14785699Siwasaki
148119529Snjl/* Timezone polling thread */
149119529Snjlstatic struct proc		*acpi_tz_proc;
150119529Snjl
15178915Smsmith/*
15278915Smsmith * Match an ACPI thermal zone.
15378915Smsmith */
15467761Smsmithstatic int
15567761Smsmithacpi_tz_probe(device_t dev)
15667761Smsmith{
15778999Smsmith    int		result;
158105282Sjhb    ACPI_LOCK_DECL;
15978999Smsmith
16078999Smsmith    ACPI_LOCK;
16178999Smsmith
162119529Snjl    /* No FUNCTION_TRACE - too noisy */
16369744Smsmith
164119529Snjl    if (acpi_get_type(dev) == ACPI_TYPE_THERMAL && !acpi_disabled("thermal")) {
165120453Snjl	device_set_desc(dev, "Thermal Zone");
16678999Smsmith	result = -10;
16778999Smsmith    } else {
16878999Smsmith	result = ENXIO;
16967761Smsmith    }
17078999Smsmith    ACPI_UNLOCK;
171119529Snjl    return (result);
17267761Smsmith}
17367761Smsmith
17478915Smsmith/*
17578915Smsmith * Attach to an ACPI thermal zone.
17678915Smsmith */
17767761Smsmithstatic int
17867761Smsmithacpi_tz_attach(device_t dev)
17967761Smsmith{
18067761Smsmith    struct acpi_tz_softc	*sc;
18179283Smsmith    struct acpi_softc		*acpi_sc;
18278915Smsmith    int				error;
18379283Smsmith    char			oidname[8];
184105282Sjhb    ACPI_LOCK_DECL;
18567761Smsmith
18696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
18769744Smsmith
18878999Smsmith    ACPI_LOCK;
18978999Smsmith
19067761Smsmith    sc = device_get_softc(dev);
19167761Smsmith    sc->tz_dev = dev;
19267761Smsmith    sc->tz_handle = acpi_get_handle(dev);
19379375Smsmith    sc->tz_requested = TZ_ACTIVE_NONE;
19488420Siwasaki    sc->tz_tmp_updating = 0;
19567761Smsmith
19678915Smsmith    /*
19778915Smsmith     * Parse the current state of the thermal zone and build control
19878915Smsmith     * structures.
19978915Smsmith     */
20078915Smsmith    if ((error = acpi_tz_establish(sc)) != 0)
20178999Smsmith	goto out;
20278915Smsmith
20378915Smsmith    /*
204126388Snjl     * XXX Call _INI if it exists.  ACPICA should do this but only handles
205126388Snjl     * Device objects for now.
206126388Snjl     */
207126388Snjl    AcpiEvaluateObject(sc->tz_handle, "_INI", NULL, NULL);
208126388Snjl
209126388Snjl    /*
21078915Smsmith     * Register for any Notify events sent to this zone.
21178915Smsmith     */
21271874Smsmith    AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
21378999Smsmith			     acpi_tz_notify_handler, sc);
21470271Stakawata
21571874Smsmith    /*
21679283Smsmith     * Create our sysctl nodes.
21779283Smsmith     *
21879283Smsmith     * XXX we need a mechanism for adding nodes under ACPI.
21979283Smsmith     */
22079283Smsmith    if (device_get_unit(dev) == 0) {
22179283Smsmith	acpi_sc = acpi_device_get_parent_softc(dev);
22279283Smsmith	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
22379283Smsmith	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
224119529Snjl			      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
225119529Snjl			      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
22685699Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
22785699Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
22885699Siwasaki		       OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW,
229119529Snjl		       &acpi_tz_min_runtime, 0,
230119529Snjl		       "minimum cooling run time in sec");
23188420Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
23288420Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
23388420Siwasaki		       OID_AUTO, "polling_rate", CTLFLAG_RD | CTLFLAG_RW,
23488420Siwasaki		       &acpi_tz_polling_rate, 0, "monitor polling rate");
23579283Smsmith    }
23679283Smsmith    sysctl_ctx_init(&sc->tz_sysctl_ctx);
23779283Smsmith    sprintf(oidname, "tz%d", device_get_unit(dev));
23879283Smsmith    sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
239119529Snjl					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
240119529Snjl					 OID_AUTO, oidname, CTLFLAG_RD, 0, "");
24179283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
24279283Smsmith		   OID_AUTO, "temperature", CTLFLAG_RD,
24379283Smsmith		   &sc->tz_temperature, 0, "current thermal zone temperature");
24479375Smsmith    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
24579375Smsmith		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
24679375Smsmith		    sc, 0, acpi_tz_active_sysctl, "I", "");
24779375Smsmith
24879283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
24979375Smsmith		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
25079375Smsmith		   &sc->tz_thflags, 0, "thermal zone flags");
25179283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
25279283Smsmith		   OID_AUTO, "_PSV", CTLFLAG_RD,
25379375Smsmith		   &sc->tz_zone.psv, 0, "");
25479283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
25579283Smsmith		   OID_AUTO, "_HOT", CTLFLAG_RD,
25679375Smsmith		   &sc->tz_zone.hot, 0, "");
25779283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
25879283Smsmith		   OID_AUTO, "_CRT", CTLFLAG_RD,
25979375Smsmith		   &sc->tz_zone.crt, 0, "");
26086399Siwasaki    SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
26186399Siwasaki		      OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac,
26286399Siwasaki		      sizeof(sc->tz_zone.ac), "I", "");
26379283Smsmith
26479283Smsmith    /*
26579375Smsmith     * Register our power profile event handler, and flag it for a manual
26679375Smsmith     * invocation by our timeout.  We defer it like this so that the rest
26779375Smsmith     * of the subsystem has time to come up.
26879375Smsmith     */
26991640Siwasaki    EVENTHANDLER_REGISTER(power_profile_change, acpi_tz_power_profile, sc, 0);
27079375Smsmith    sc->tz_flags |= TZ_FLAG_GETPROFILE;
27179375Smsmith
27279375Smsmith    /*
27371874Smsmith     * Don't bother evaluating/printing the temperature at this point;
27471874Smsmith     * on many systems it'll be bogus until the EC is running.
27571874Smsmith     */
27678999Smsmith
27791126Smsmith    /*
27891126Smsmith     * Create our thread; we only need one, it will service all of the
27991126Smsmith     * thermal zones.
28091126Smsmith     */
28191126Smsmith    if (acpi_tz_proc == NULL) {
28291126Smsmith	    error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_proc,
283104354Sscottl				   RFHIGHPID, 0, "acpi_thermal");
28491126Smsmith	    if (error != 0) {
285119529Snjl		    device_printf(sc->tz_dev, "could not create thread - %d",
286119529Snjl				  error);
28791126Smsmith		    goto out;
28891126Smsmith	    }
28991126Smsmith    }
29091126Smsmith
29178999Smsmith out:
29278999Smsmith    ACPI_UNLOCK;
29379375Smsmith
294119529Snjl    return_VALUE (error);
29567761Smsmith}
29670271Stakawata
29778915Smsmith/*
29878915Smsmith * Parse the current state of this thermal zone and set up to use it.
29978915Smsmith *
30078915Smsmith * Note that we may have previous state, which will have to be discarded.
30178915Smsmith */
30278915Smsmithstatic int
30378915Smsmithacpi_tz_establish(struct acpi_tz_softc *sc)
30478915Smsmith{
30578915Smsmith    ACPI_OBJECT	*obj;
30678915Smsmith    int		i;
30778915Smsmith    char	nbuf[8];
30878915Smsmith
30996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
31078915Smsmith
31178999Smsmith    ACPI_ASSERTLOCK;
31278999Smsmith
313119529Snjl    /* Power everything off and erase any existing state. */
31478915Smsmith    acpi_tz_all_off(sc);
31578915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
31679375Smsmith	if (sc->tz_zone.al[i].Pointer != NULL)
31779375Smsmith	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
31879375Smsmith    if (sc->tz_zone.psl.Pointer != NULL)
31979375Smsmith	AcpiOsFree(sc->tz_zone.psl.Pointer);
32079375Smsmith    bzero(&sc->tz_zone, sizeof(sc->tz_zone));
32178915Smsmith
322119529Snjl    /* Evaluate thermal zone parameters. */
32378915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
32478915Smsmith	sprintf(nbuf, "_AC%d", i);
32579375Smsmith	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
32678915Smsmith	sprintf(nbuf, "_AL%d", i);
32791126Smsmith	sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
32891126Smsmith	sc->tz_zone.al[i].Pointer = NULL;
32991126Smsmith	AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
33079375Smsmith	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
33178915Smsmith	if (obj != NULL) {
332119529Snjl	    /* Should be a package containing a list of power objects */
33378915Smsmith	    if (obj->Type != ACPI_TYPE_PACKAGE) {
334119529Snjl		device_printf(sc->tz_dev, "%s has unknown type %d, rejecting\n",
33578915Smsmith			      nbuf, obj->Type);
336119529Snjl		return_VALUE (ENXIO);
33778915Smsmith	    }
33878915Smsmith	}
33978915Smsmith    }
34079375Smsmith    acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
34179375Smsmith    acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
34291126Smsmith    sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
34391126Smsmith    sc->tz_zone.psl.Pointer = NULL;
34491126Smsmith    AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
34579375Smsmith    acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
34679375Smsmith    acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
34779375Smsmith    acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
34879375Smsmith    acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
34979375Smsmith    acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
35078915Smsmith
35178915Smsmith    /*
35279283Smsmith     * Sanity-check the values we've been given.
35379283Smsmith     *
35479283Smsmith     * XXX what do we do about systems that give us the same value for
35579283Smsmith     *     more than one of these setpoints?
35679283Smsmith     */
35779375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
35879375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
35979375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
36079283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
36179375Smsmith	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
36279283Smsmith
36379283Smsmith    /*
36478915Smsmith     * Power off everything that we've just been given.
36578915Smsmith     */
36678915Smsmith    acpi_tz_all_off(sc);
36778915Smsmith
368119529Snjl    return_VALUE (0);
36978915Smsmith}
37078915Smsmith
37185699Siwasakistatic char	*aclevel_string[] =	{
37285699Siwasaki	"NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
37385699Siwasaki	"_AC5", "_AC6", "_AC7", "_AC8", "_AC9" };
37485699Siwasaki
37585699Siwasakistatic __inline const char *
37685699Siwasakiacpi_tz_aclevel_string(int active)
37785699Siwasaki{
378119529Snjl	if (active < -1 || active >= TZ_NUMLEVELS)
37985699Siwasaki		return (aclevel_string[0]);
38085699Siwasaki
38185699Siwasaki	return (aclevel_string[active+1]);
38285699Siwasaki}
38385699Siwasaki
38478915Smsmith/*
38578915Smsmith * Evaluate the condition of a thermal zone, take appropriate actions.
38678915Smsmith */
38771874Smsmithstatic void
388119529Snjlacpi_tz_monitor(void *Context)
38971874Smsmith{
390119529Snjl    struct acpi_tz_softc *sc;
391119529Snjl    struct	timespec curtime;
39279283Smsmith    int		temp;
39378915Smsmith    int		i;
39479283Smsmith    int		newactive, newflags;
39586399Siwasaki    ACPI_STATUS	status;
39670271Stakawata
39796926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
39870271Stakawata
39978999Smsmith    ACPI_ASSERTLOCK;
40078999Smsmith
401119529Snjl    sc = (struct acpi_tz_softc *)Context;
402119529Snjl    if (sc->tz_tmp_updating)
40388420Siwasaki	goto out;
40488420Siwasaki    sc->tz_tmp_updating = 1;
40588420Siwasaki
406119529Snjl    /* Get the current temperature. */
407126560Snjl    status = acpi_GetInteger(sc->tz_handle, "_TMP", &temp);
408119529Snjl    if (ACPI_FAILURE(status)) {
40986552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
41086552Siwasaki	    "error fetching current temperature -- %s\n",
41186552Siwasaki	     AcpiFormatException(status));
41278915Smsmith	/* XXX disable zone? go to max cooling? */
41388420Siwasaki	goto out;
41471874Smsmith    }
41588420Siwasaki
41682372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
41779283Smsmith    sc->tz_temperature = temp;
41878915Smsmith
41978915Smsmith    /*
42078915Smsmith     * Work out what we ought to be doing right now.
42179283Smsmith     *
42279283Smsmith     * Note that the _ACx levels sort from hot to cold.
42378915Smsmith     */
42479283Smsmith    newactive = TZ_ACTIVE_NONE;
42579375Smsmith    for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
42679375Smsmith	if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
42779283Smsmith	    newactive = i;
42882967Siwasaki	    if (sc->tz_active != newactive) {
429119529Snjl		ACPI_VPRINT(sc->tz_dev,
430119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
431119529Snjl			    "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
432119529Snjl			    TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
43385699Siwasaki		getnanotime(&sc->tz_cooling_started);
43482967Siwasaki	    }
43579375Smsmith	}
43679375Smsmith    }
43779283Smsmith
43885699Siwasaki    /*
43985699Siwasaki     * We are going to get _ACx level down (colder side), but give a guaranteed
44085699Siwasaki     * minimum cooling run time if requested.
44185699Siwasaki     */
44285699Siwasaki    if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
44385699Siwasaki	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
444119529Snjl
44585699Siwasaki	getnanotime(&curtime);
44685699Siwasaki	timespecsub(&curtime, &sc->tz_cooling_started);
447119529Snjl	if (curtime.tv_sec < acpi_tz_min_runtime)
44885699Siwasaki	    newactive = sc->tz_active;
44985699Siwasaki    }
45085699Siwasaki
451119529Snjl    /* Handle user override of active mode */
452126662Snjl    if (sc->tz_requested != TZ_ACTIVE_NONE && sc->tz_requested < newactive)
45379375Smsmith	newactive = sc->tz_requested;
45478915Smsmith
45579375Smsmith    /* update temperature-related flags */
45679375Smsmith    newflags = TZ_THFLAG_NONE;
457124439Snjl    if (sc->tz_zone.psv != -1 && temp >= sc->tz_zone.psv)
45879375Smsmith	newflags |= TZ_THFLAG_PSV;
459124439Snjl    if (sc->tz_zone.hot != -1 && temp >= sc->tz_zone.hot)
46079375Smsmith	newflags |= TZ_THFLAG_HOT;
461124439Snjl    if (sc->tz_zone.crt != -1 && temp >= sc->tz_zone.crt)
46279375Smsmith	newflags |= TZ_THFLAG_CRT;
46379375Smsmith
464119529Snjl    /* If the active cooling state has changed, we have to switch things. */
46579283Smsmith    if (newactive != sc->tz_active) {
466119529Snjl	/* Turn off the cooling devices that are on, if any are */
46779283Smsmith	if (sc->tz_active != TZ_ACTIVE_NONE)
468119529Snjl	    acpi_ForeachPackageObject(
469119529Snjl		(ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
470119529Snjl		acpi_tz_switch_cooler_off, sc);
47178915Smsmith
472119529Snjl	/* Turn on cooling devices that are required, if any are */
473119529Snjl	if (newactive != TZ_ACTIVE_NONE) {
474119529Snjl	    acpi_ForeachPackageObject(
475119529Snjl		(ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
476119529Snjl		acpi_tz_switch_cooler_on, sc);
477119529Snjl	}
47886552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
479119529Snjl		    "switched from %s to %s: %d.%dC\n",
480119529Snjl		    acpi_tz_aclevel_string(sc->tz_active),
481119529Snjl		    acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
48279283Smsmith	sc->tz_active = newactive;
48379283Smsmith    }
48478915Smsmith
485119529Snjl    /* XXX (de)activate any passive cooling that may be required. */
48678915Smsmith
48778915Smsmith    /*
488125335Snjl     * If the temperature is at _HOT or _CRT, increment our event count.
489125335Snjl     * If it has occurred enough times, shutdown the system.  This is
490125335Snjl     * needed because some systems will report an invalid high temperature
491125335Snjl     * for one poll cycle.  It is suspected this is due to the embedded
492125335Snjl     * controller timing out.  A typical value is 138C for one cycle on
493125335Snjl     * a system that is otherwise 65C.
494125366Snjl     *
495125366Snjl     * If we're almost at that threshold, notify the user through devd(8).
49678915Smsmith     */
497125335Snjl    if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) {
498125366Snjl	sc->tz_validchecks++;
499125366Snjl	if (sc->tz_validchecks == TZ_VALIDCHECKS) {
500125335Snjl	    device_printf(sc->tz_dev,
501125335Snjl		"WARNING - current temperature (%d.%dC) exceeds safe limits\n",
502125335Snjl		TZ_KELVTOC(sc->tz_temperature));
503125335Snjl	    shutdown_nice(RB_POWEROFF);
504125366Snjl	} else if (sc->tz_validchecks == TZ_NOTIFYCOUNT)
505125366Snjl	    acpi_UserNotify("Thermal", sc->tz_handle, TZ_NOTIFY_CRITICAL);
506125335Snjl    } else {
507125335Snjl	sc->tz_validchecks = 0;
50878915Smsmith    }
50979375Smsmith    sc->tz_thflags = newflags;
51078915Smsmith
51188420Siwasakiout:
51288420Siwasaki    sc->tz_tmp_updating = 0;
51371874Smsmith    return_VOID;
51471874Smsmith}
51570271Stakawata
51678915Smsmith/*
51778915Smsmith * Turn off all the cooling devices.
51878915Smsmith */
51971874Smsmithstatic void
52078915Smsmithacpi_tz_all_off(struct acpi_tz_softc *sc)
52178915Smsmith{
52278915Smsmith    int		i;
52378915Smsmith
52496926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
52578999Smsmith
52678999Smsmith    ACPI_ASSERTLOCK;
52778915Smsmith
528119529Snjl    /* Scan all the _ALx objects and turn them all off. */
52978915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
53079375Smsmith	if (sc->tz_zone.al[i].Pointer == NULL)
53178915Smsmith	    continue;
53279375Smsmith	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
53378915Smsmith				  acpi_tz_switch_cooler_off, sc);
53478915Smsmith    }
53578915Smsmith
53678915Smsmith    /*
53778915Smsmith     * XXX revert any passive-cooling options.
53878915Smsmith     */
53978915Smsmith
54079283Smsmith    sc->tz_active = TZ_ACTIVE_NONE;
54179375Smsmith    sc->tz_thflags = TZ_THFLAG_NONE;
542119529Snjl
54378915Smsmith    return_VOID;
54478915Smsmith}
54578915Smsmith
54678915Smsmith/*
54778915Smsmith * Given an object, verify that it's a reference to a device of some sort,
54878915Smsmith * and try to switch it off.
54978915Smsmith */
55078915Smsmithstatic void
55178915Smsmithacpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
55278915Smsmith{
553128047Snjl    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
554128047Snjl    ACPI_HANDLE			cooler;
555128047Snjl    ACPI_STATUS			status;
55678915Smsmith
55796926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
55878915Smsmith
55978999Smsmith    ACPI_ASSERTLOCK;
56078999Smsmith
561128047Snjl    cooler = acpi_GetReference(NULL, obj);
562128047Snjl    if (cooler == NULL) {
563128047Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
564128047Snjl	return_VOID;
565128047Snjl    }
566102470Siwasaki
567128047Snjl    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
568128047Snjl		     acpi_name(cooler)));
569128047Snjl    status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
570128047Snjl    if (ACPI_FAILURE(status)) {
571128047Snjl	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
572128047Snjl		    "failed to deactivate %s - %s\n", acpi_name(cooler),
573128047Snjl		    AcpiFormatException(status));
57478915Smsmith    }
575119529Snjl
57679375Smsmith    return_VOID;
57778915Smsmith}
57878915Smsmith
57978915Smsmith/*
58078915Smsmith * Given an object, verify that it's a reference to a device of some sort,
58178915Smsmith * and try to switch it on.
58278915Smsmith *
583128047Snjl * XXX replication of off/on function code is bad.
58478915Smsmith */
58578915Smsmithstatic void
58678915Smsmithacpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
58778915Smsmith{
58878915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
58978999Smsmith    ACPI_HANDLE			cooler;
59079375Smsmith    ACPI_STATUS			status;
59179375Smsmith
59296926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
59378915Smsmith
59478999Smsmith    ACPI_ASSERTLOCK;
59578999Smsmith
596128047Snjl    cooler = acpi_GetReference(NULL, obj);
597128047Snjl    if (cooler == NULL) {
598128047Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
599128047Snjl	return_VOID;
600128047Snjl    }
601102470Siwasaki
602128047Snjl    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
603128047Snjl		     acpi_name(cooler)));
604128047Snjl    status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
605128047Snjl    if (ACPI_FAILURE(status)) {
606128047Snjl	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
607128047Snjl		    "failed to activate %s - %s\n", acpi_name(cooler),
608128047Snjl		    AcpiFormatException(status));
60978915Smsmith    }
610119529Snjl
611119529Snjl    return_VOID;
61278915Smsmith}
61378915Smsmith
61478915Smsmith/*
61578915Smsmith * Read/debug-print a parameter, default it to -1.
61678915Smsmith */
61778915Smsmithstatic void
61878915Smsmithacpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
61978915Smsmith{
62078915Smsmith
62196926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
62278915Smsmith
62378999Smsmith    ACPI_ASSERTLOCK;
62478999Smsmith
625126560Snjl    if (ACPI_FAILURE(acpi_GetInteger(sc->tz_handle, node, data))) {
62678915Smsmith	*data = -1;
62778915Smsmith    } else {
628119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n",
629119529Snjl			 acpi_name(sc->tz_handle), node, *data));
63078915Smsmith    }
631119529Snjl
63278915Smsmith    return_VOID;
63378915Smsmith}
63479283Smsmith
63579283Smsmith/*
63679283Smsmith * Sanity-check a temperature value.  Assume that setpoints
63779283Smsmith * should be between 0C and 150C.
63879283Smsmith */
63979283Smsmithstatic void
64079283Smsmithacpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
64179283Smsmith{
642119529Snjl    if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 1500)) {
64379283Smsmith	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
64479283Smsmith		      what, TZ_KELVTOC(*val));
64579283Smsmith	*val = -1;
64679283Smsmith    }
64779283Smsmith}
64879375Smsmith
64979375Smsmith/*
65079375Smsmith * Respond to a sysctl on the active state node.
65179375Smsmith */
65279375Smsmithstatic int
65379375Smsmithacpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
65479375Smsmith{
65579375Smsmith    struct acpi_tz_softc	*sc;
65679375Smsmith    int				active;
65779375Smsmith    int		 		error;
658105282Sjhb    ACPI_LOCK_DECL;
65979375Smsmith
66079375Smsmith    ACPI_LOCK;
66179375Smsmith
66279375Smsmith    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
66379375Smsmith    active = sc->tz_active;
66479375Smsmith    error = sysctl_handle_int(oidp, &active, 0, req);
66579375Smsmith
666119529Snjl    /* Error or no new value */
667119529Snjl    if (error != 0 || req->newptr == NULL)
66879375Smsmith	goto out;
669119529Snjl    if (active < -1 || active >= TZ_NUMLEVELS) {
67079375Smsmith	error = EINVAL;
67179375Smsmith	goto out;
67279375Smsmith    }
67379375Smsmith
674119529Snjl    /* Set new preferred level and re-switch */
67579375Smsmith    sc->tz_requested = active;
67679375Smsmith    acpi_tz_monitor(sc);
67779375Smsmith
67879375Smsmith out:
67979375Smsmith    ACPI_UNLOCK;
680119529Snjl    return (error);
68179375Smsmith}
68279375Smsmith
68378915Smsmith/*
68478915Smsmith * Respond to a Notify event sent to the zone.
68578915Smsmith */
68678915Smsmithstatic void
68771874Smsmithacpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
68871874Smsmith{
68978915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
69078915Smsmith
69196926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
69270271Stakawata
69378999Smsmith    ACPI_ASSERTLOCK;
69478999Smsmith
69578915Smsmith    switch(notify) {
69678915Smsmith    case TZ_NOTIFY_TEMPERATURE:
697119529Snjl	/* Temperature change occurred */
698119529Snjl	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, acpi_tz_monitor, sc);
69978915Smsmith	break;
70078915Smsmith    case TZ_NOTIFY_DEVICES:
70178915Smsmith    case TZ_NOTIFY_LEVELS:
702119529Snjl	/* Zone devices/setpoints changed */
703119529Snjl	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
704119529Snjl				(OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
70578915Smsmith	break;
70678915Smsmith    default:
70786552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
708119529Snjl		    "unknown Notify event 0x%x\n", notify);
70978915Smsmith	break;
71071874Smsmith    }
711119529Snjl
712121493Snjl    acpi_UserNotify("Thermal", h, notify);
713121493Snjl
71471874Smsmith    return_VOID;
71571874Smsmith}
71670271Stakawata
71778915Smsmith/*
71878915Smsmith * Poll the thermal zone.
71978915Smsmith */
72078915Smsmithstatic void
72191126Smsmithacpi_tz_timeout(struct acpi_tz_softc *sc)
72278915Smsmith{
723119529Snjl    /* Do we need to get the power profile settings? */
72479375Smsmith    if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
72591640Siwasaki	acpi_tz_power_profile((void *)sc);
72679375Smsmith	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
72779375Smsmith    }
72879375Smsmith
72991126Smsmith    ACPI_ASSERTLOCK;
73078915Smsmith
731119529Snjl    /* Check the current temperature and take action based on it */
73291126Smsmith    acpi_tz_monitor(sc);
73391126Smsmith
73478915Smsmith    /* XXX passive cooling actions? */
73578915Smsmith}
73679375Smsmith
73779375Smsmith/*
73879375Smsmith * System power profile may have changed; fetch and notify the
73979375Smsmith * thermal zone accordingly.
74079375Smsmith *
74179375Smsmith * Since this can be called from an arbitrary eventhandler, it needs
74279375Smsmith * to get the ACPI lock itself.
74379375Smsmith */
74479375Smsmithstatic void
74591640Siwasakiacpi_tz_power_profile(void *arg)
74679375Smsmith{
74779375Smsmith    ACPI_STATUS			status;
74879375Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
74991640Siwasaki    int				state;
750105282Sjhb    ACPI_LOCK_DECL;
75179375Smsmith
75291640Siwasaki    state = power_profile_get_state();
753119529Snjl    if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
754119529Snjl	return;
75591640Siwasaki
75679375Smsmith    ACPI_LOCK;
75779375Smsmith
75879375Smsmith    /* check that we haven't decided there's no _SCP method */
759119529Snjl    if ((sc->tz_flags & TZ_FLAG_NO_SCP) == 0) {
76079375Smsmith
761119529Snjl	/* Call _SCP to set the new profile */
762126560Snjl	status = acpi_SetInteger(sc->tz_handle, "_SCP",
763126560Snjl	    (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1);
764119529Snjl	if (ACPI_FAILURE(status)) {
76579385Smsmith	    if (status != AE_NOT_FOUND)
766119529Snjl		ACPI_VPRINT(sc->tz_dev,
767119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
768119529Snjl			    "can't evaluate %s._SCP - %s\n",
769119529Snjl			    acpi_name(sc->tz_handle),
770119529Snjl			    AcpiFormatException(status));
77179375Smsmith	    sc->tz_flags |= TZ_FLAG_NO_SCP;
77279375Smsmith	} else {
773119529Snjl	    /* We have to re-evaluate the entire zone now */
774119529Snjl	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH,
775119529Snjl				    (OSD_EXECUTION_CALLBACK)acpi_tz_establish,
776119529Snjl				    sc);
77779375Smsmith	}
77879375Smsmith    }
779119529Snjl
78079375Smsmith    ACPI_UNLOCK;
78179375Smsmith}
78279375Smsmith
78391126Smsmith/*
78491126Smsmith * Thermal zone monitor thread.
78591126Smsmith */
78691126Smsmithstatic void
78791126Smsmithacpi_tz_thread(void *arg)
78891126Smsmith{
78991126Smsmith    device_t	*devs;
79091126Smsmith    int		devcount, i;
791105282Sjhb    ACPI_LOCK_DECL;
79291126Smsmith
79396926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
79491126Smsmith
79591126Smsmith    devs = NULL;
79691126Smsmith    devcount = 0;
79791126Smsmith
79891126Smsmith    for (;;) {
799119529Snjl	tsleep(&acpi_tz_proc, PZERO, "tzpoll", hz * acpi_tz_polling_rate);
80091126Smsmith
801105282Sjhb#if __FreeBSD_version >= 500000
80291215Smsmith	mtx_lock(&Giant);
803105282Sjhb#endif
80491215Smsmith
80591126Smsmith	if (devcount == 0)
80691126Smsmith	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
80791126Smsmith
80891126Smsmith	ACPI_LOCK;
80991126Smsmith	for (i = 0; i < devcount; i++)
81091126Smsmith	    acpi_tz_timeout(device_get_softc(devs[i]));
81191126Smsmith	ACPI_UNLOCK;
81291215Smsmith
813105282Sjhb#if __FreeBSD_version >= 500000
81491215Smsmith	mtx_unlock(&Giant);
815105282Sjhb#endif
81691126Smsmith    }
81791126Smsmith}
818