acpi_thermal.c revision 79375
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 79375 2001-07-07 01:49:15Z msmith $
2867761Smsmith */
2967761Smsmith
3067761Smsmith#include "opt_acpi.h"
3167761Smsmith#include <sys/param.h>
3267761Smsmith#include <sys/kernel.h>
3367761Smsmith#include <sys/bus.h>
3478915Smsmith#include <sys/reboot.h>
3579283Smsmith#include <sys/sysctl.h>
3667761Smsmith
3767761Smsmith#include "acpi.h"
3867761Smsmith
3967761Smsmith#include <dev/acpica/acpivar.h>
4067761Smsmith
4169744Smsmith/*
4269744Smsmith * Hooks for the ACPI CA debugging infrastructure
4369744Smsmith */
4478999Smsmith#define _COMPONENT	ACPI_THERMAL
4569744SmsmithMODULE_NAME("THERMAL")
4669744Smsmith
4771874Smsmith#define TZ_ZEROC	2732
4871874Smsmith#define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10)
4967761Smsmith
5078915Smsmith#define TZ_NOTIFY_TEMPERATURE	0x80
5178915Smsmith#define TZ_NOTIFY_DEVICES	0x81
5278915Smsmith#define TZ_NOTIFY_LEVELS	0x82
5378915Smsmith
5478915Smsmith#define TZ_POLLRATE	(hz * 10)	/* every ten seconds */
5578915Smsmith
5678915Smsmith#define TZ_NUMLEVELS	10		/* defined by ACPI spec */
5779375Smsmithstruct acpi_tz_zone {
5878915Smsmith    int		ac[TZ_NUMLEVELS];
5978915Smsmith    ACPI_BUFFER	al[TZ_NUMLEVELS];
6078915Smsmith    int		crt;
6178915Smsmith    int		hot;
6278915Smsmith    ACPI_BUFFER	psl;
6378915Smsmith    int		psv;
6478915Smsmith    int		tc1;
6578915Smsmith    int		tc2;
6678915Smsmith    int		tsp;
6778915Smsmith    int		tzp;
6878915Smsmith};
6978915Smsmith
7078915Smsmith
7167761Smsmithstruct acpi_tz_softc {
7279375Smsmith    device_t			tz_dev;			/* device handle */
7379375Smsmith    ACPI_HANDLE			tz_handle;		/* thermal zone handle */
7479375Smsmith    struct callout_handle	tz_timeout;		/* poll routine handle */
7579375Smsmith    int				tz_temperature;		/* current temperature */
7679375Smsmith    int				tz_active;		/* current active cooling */
7779375Smsmith#define TZ_ACTIVE_NONE		-1
7879375Smsmith    int				tz_requested;		/* user-requested minimum active cooling */
7979375Smsmith    int				tz_thflags;		/* current temperature-related flags */
8079375Smsmith#define TZ_THFLAG_NONE		0
8179375Smsmith#define TZ_THFLAG_PSV		(1<<0)
8279375Smsmith#define TZ_THFLAG_HOT		(1<<2)
8379375Smsmith#define TZ_THFLAG_CRT		(1<<3)
8479283Smsmith    int				tz_flags;
8579375Smsmith#define TZ_FLAG_NO_SCP		(1<<0)			/* no _SCP method */
8679375Smsmith#define TZ_FLAG_GETPROFILE	(1<<1)			/* fetch powerprofile in timeout */
8779283Smsmith
8879375Smsmith    struct sysctl_ctx_list	tz_sysctl_ctx;		/* sysctl tree */
8979283Smsmith    struct sysctl_oid		*tz_sysctl_tree;
9078915Smsmith
9179375Smsmith    struct acpi_tz_zone 	tz_zone;		/* thermal zone parameters */
9267761Smsmith};
9367761Smsmith
9467761Smsmithstatic int	acpi_tz_probe(device_t dev);
9567761Smsmithstatic int	acpi_tz_attach(device_t dev);
9678915Smsmithstatic int	acpi_tz_establish(struct acpi_tz_softc *sc);
9778999Smsmithstatic void	acpi_tz_monitor(struct acpi_tz_softc *sc);
9878915Smsmithstatic void	acpi_tz_all_off(struct acpi_tz_softc *sc);
9978915Smsmithstatic void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
10078915Smsmithstatic void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
10178915Smsmithstatic void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
10279283Smsmithstatic void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
10379375Smsmithstatic int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
10471874Smsmithstatic void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
10578915Smsmithstatic void	acpi_tz_timeout(void *arg);
10679375Smsmithstatic void	acpi_tz_powerprofile(void *arg);
10771874Smsmith
10867761Smsmithstatic device_method_t acpi_tz_methods[] = {
10967761Smsmith    /* Device interface */
11067761Smsmith    DEVMETHOD(device_probe,	acpi_tz_probe),
11167761Smsmith    DEVMETHOD(device_attach,	acpi_tz_attach),
11267761Smsmith
11367761Smsmith    {0, 0}
11467761Smsmith};
11567761Smsmith
11667761Smsmithstatic driver_t acpi_tz_driver = {
11767761Smsmith    "acpi_tz",
11867761Smsmith    acpi_tz_methods,
11967761Smsmith    sizeof(struct acpi_tz_softc),
12067761Smsmith};
12167761Smsmith
12267761Smsmithdevclass_t acpi_tz_devclass;
12367761SmsmithDRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
12467761Smsmith
12579283Smsmithstatic struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
12679283Smsmithstatic struct sysctl_oid	*acpi_tz_sysctl_tree;
12779283Smsmith
12878915Smsmith/*
12978915Smsmith * Match an ACPI thermal zone.
13078915Smsmith */
13167761Smsmithstatic int
13267761Smsmithacpi_tz_probe(device_t dev)
13367761Smsmith{
13478999Smsmith    int		result;
13578999Smsmith
13678999Smsmith    ACPI_LOCK;
13778999Smsmith
13878915Smsmith    /* no FUNCTION_TRACE - too noisy */
13969744Smsmith
14069744Smsmith    if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
14169744Smsmith	!acpi_disabled("thermal")) {
14267761Smsmith	device_set_desc(dev, "thermal zone");
14378999Smsmith	result = -10;
14478999Smsmith    } else {
14578999Smsmith	result = ENXIO;
14667761Smsmith    }
14778999Smsmith    ACPI_UNLOCK;
14878999Smsmith    return(result);
14967761Smsmith}
15067761Smsmith
15178915Smsmith/*
15278915Smsmith * Attach to an ACPI thermal zone.
15378915Smsmith */
15467761Smsmithstatic int
15567761Smsmithacpi_tz_attach(device_t dev)
15667761Smsmith{
15767761Smsmith    struct acpi_tz_softc	*sc;
15879283Smsmith    struct acpi_softc		*acpi_sc;
15978915Smsmith    int				error;
16079283Smsmith    char			oidname[8];
16179283Smsmith    int				i;
16267761Smsmith
16377432Smsmith    FUNCTION_TRACE(__func__);
16469744Smsmith
16578999Smsmith    ACPI_LOCK;
16678999Smsmith
16767761Smsmith    sc = device_get_softc(dev);
16867761Smsmith    sc->tz_dev = dev;
16967761Smsmith    sc->tz_handle = acpi_get_handle(dev);
17079375Smsmith    sc->tz_requested = TZ_ACTIVE_NONE;
17167761Smsmith
17278915Smsmith    /*
17378915Smsmith     * Parse the current state of the thermal zone and build control
17478915Smsmith     * structures.
17578915Smsmith     */
17678915Smsmith    if ((error = acpi_tz_establish(sc)) != 0)
17778999Smsmith	goto out;
17878915Smsmith
17978915Smsmith    /*
18078915Smsmith     * Register for any Notify events sent to this zone.
18178915Smsmith     */
18271874Smsmith    AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
18378999Smsmith			     acpi_tz_notify_handler, sc);
18470271Stakawata
18571874Smsmith    /*
18679283Smsmith     * Create our sysctl nodes.
18779283Smsmith     *
18879283Smsmith     * XXX we need a mechanism for adding nodes under ACPI.
18979283Smsmith     */
19079283Smsmith    if (device_get_unit(dev) == 0) {
19179283Smsmith	acpi_sc = acpi_device_get_parent_softc(dev);
19279283Smsmith	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
19379283Smsmith	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
19479283Smsmith					      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
19579283Smsmith					      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
19679283Smsmith    }
19779283Smsmith    sysctl_ctx_init(&sc->tz_sysctl_ctx);
19879283Smsmith    sprintf(oidname, "tz%d", device_get_unit(dev));
19979283Smsmith    sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
20079283Smsmith					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
20179283Smsmith					 oidname, CTLFLAG_RD, 0, "");
20279283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
20379283Smsmith		   OID_AUTO, "temperature", CTLFLAG_RD,
20479283Smsmith		   &sc->tz_temperature, 0, "current thermal zone temperature");
20579375Smsmith    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
20679375Smsmith		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
20779375Smsmith		    sc, 0, acpi_tz_active_sysctl, "I", "");
20879375Smsmith
20979283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
21079375Smsmith		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
21179375Smsmith		   &sc->tz_thflags, 0, "thermal zone flags");
21279283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
21379283Smsmith		   OID_AUTO, "_PSV", CTLFLAG_RD,
21479375Smsmith		   &sc->tz_zone.psv, 0, "");
21579283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
21679283Smsmith		   OID_AUTO, "_HOT", CTLFLAG_RD,
21779375Smsmith		   &sc->tz_zone.hot, 0, "");
21879283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
21979283Smsmith		   OID_AUTO, "_CRT", CTLFLAG_RD,
22079375Smsmith		   &sc->tz_zone.crt, 0, "");
22179283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
22279283Smsmith	sprintf(oidname, "_AC%d", i);
22379283Smsmith	SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
22479283Smsmith		       OID_AUTO, oidname, CTLFLAG_RD,
22579375Smsmith		       &sc->tz_zone.ac[i], 0, "");
22679283Smsmith    }
22779283Smsmith
22879283Smsmith    /*
22979375Smsmith     * Register our power profile event handler, and flag it for a manual
23079375Smsmith     * invocation by our timeout.  We defer it like this so that the rest
23179375Smsmith     * of the subsystem has time to come up.
23279375Smsmith     */
23379375Smsmith    EVENTHANDLER_REGISTER(powerprofile_change, acpi_tz_powerprofile, sc, 0);
23479375Smsmith    sc->tz_flags |= TZ_FLAG_GETPROFILE;
23579375Smsmith
23679375Smsmith    /*
23771874Smsmith     * Don't bother evaluating/printing the temperature at this point;
23871874Smsmith     * on many systems it'll be bogus until the EC is running.
23971874Smsmith     */
24078999Smsmith
24178999Smsmith out:
24278999Smsmith    ACPI_UNLOCK;
24379375Smsmith
24479375Smsmith    /*
24579375Smsmith     * Start the timeout routine, with enough delay for the rest of the
24679375Smsmith     * subsystem to come up.
24779375Smsmith     */
24879375Smsmith    sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
24979375Smsmith
25078999Smsmith    return_VALUE(error);
25167761Smsmith}
25270271Stakawata
25378915Smsmith/*
25478915Smsmith * Parse the current state of this thermal zone and set up to use it.
25578915Smsmith *
25678915Smsmith * Note that we may have previous state, which will have to be discarded.
25778915Smsmith */
25878915Smsmithstatic int
25978915Smsmithacpi_tz_establish(struct acpi_tz_softc *sc)
26078915Smsmith{
26178915Smsmith    ACPI_OBJECT	*obj;
26278915Smsmith    int		i;
26378915Smsmith    char	nbuf[8];
26478915Smsmith
26578915Smsmith    FUNCTION_TRACE(__func__);
26678915Smsmith
26778999Smsmith    ACPI_ASSERTLOCK;
26878999Smsmith
26978915Smsmith    /*
27078915Smsmith     * Power everything off and erase any existing state.
27178915Smsmith     */
27278915Smsmith    acpi_tz_all_off(sc);
27378915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
27479375Smsmith	if (sc->tz_zone.al[i].Pointer != NULL)
27579375Smsmith	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
27679375Smsmith    if (sc->tz_zone.psl.Pointer != NULL)
27779375Smsmith	AcpiOsFree(sc->tz_zone.psl.Pointer);
27879375Smsmith    bzero(&sc->tz_zone, sizeof(sc->tz_zone));
27978915Smsmith
28078915Smsmith    /*
28178915Smsmith     * Evaluate thermal zone parameters.
28278915Smsmith     */
28378915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
28478915Smsmith	sprintf(nbuf, "_AC%d", i);
28579375Smsmith	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
28678915Smsmith	sprintf(nbuf, "_AL%d", i);
28779375Smsmith	acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
28879375Smsmith	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
28978915Smsmith	if (obj != NULL) {
29078915Smsmith	    /* should be a package containing a list of power objects */
29178915Smsmith	    if (obj->Type != ACPI_TYPE_PACKAGE) {
29278915Smsmith		device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
29378915Smsmith			      nbuf, obj->Type);
29478915Smsmith		return_VALUE(ENXIO);
29578915Smsmith	    }
29678915Smsmith	}
29778915Smsmith    }
29879375Smsmith    acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
29979375Smsmith    acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
30079375Smsmith    acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
30179375Smsmith    acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
30279375Smsmith    acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
30379375Smsmith    acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
30479375Smsmith    acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
30579375Smsmith    acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
30678915Smsmith
30778915Smsmith    /*
30879283Smsmith     * Sanity-check the values we've been given.
30979283Smsmith     *
31079283Smsmith     * XXX what do we do about systems that give us the same value for
31179283Smsmith     *     more than one of these setpoints?
31279283Smsmith     */
31379375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
31479375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
31579375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
31679283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
31779375Smsmith	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
31879283Smsmith
31979283Smsmith    /*
32078915Smsmith     * Power off everything that we've just been given.
32178915Smsmith     */
32278915Smsmith    acpi_tz_all_off(sc);
32378915Smsmith
32478915Smsmith    return_VALUE(0);
32578915Smsmith}
32678915Smsmith
32778915Smsmith/*
32878915Smsmith * Evaluate the condition of a thermal zone, take appropriate actions.
32978915Smsmith */
33071874Smsmithstatic void
33178915Smsmithacpi_tz_monitor(struct acpi_tz_softc *sc)
33271874Smsmith{
33379283Smsmith    int		temp;
33478915Smsmith    int		i;
33579283Smsmith    int		newactive, newflags;
33670271Stakawata
33777432Smsmith    FUNCTION_TRACE(__func__);
33870271Stakawata
33978999Smsmith    ACPI_ASSERTLOCK;
34078999Smsmith
34178915Smsmith    /*
34278915Smsmith     * Get the current temperature.
34378915Smsmith     */
34478915Smsmith    if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) {
34578915Smsmith	device_printf(sc->tz_dev, "error fetching current temperature\n");
34678915Smsmith	/* XXX disable zone? go to max cooling? */
34771874Smsmith	return_VOID;
34871874Smsmith    }
34978999Smsmith    DEBUG_PRINT(TRACE_VALUES, ("got %d.%dC\n", TZ_KELVTOC(temp)));
35079283Smsmith    sc->tz_temperature = temp;
35178915Smsmith
35278915Smsmith    /*
35378915Smsmith     * Work out what we ought to be doing right now.
35479283Smsmith     *
35579283Smsmith     * Note that the _ACx levels sort from hot to cold.
35678915Smsmith     */
35779283Smsmith    newactive = TZ_ACTIVE_NONE;
35879375Smsmith    for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
35979375Smsmith	if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
36079375Smsmith	    device_printf(sc->tz_dev, "_AC%d: temperature %d > setpoint %d\n",
36179375Smsmith			  i, temp, sc->tz_zone.ac[i]);
36279283Smsmith	    newactive = i;
36379375Smsmith	}
36479375Smsmith    }
36579283Smsmith
36679375Smsmith    /* handle user override of active mode */
36779375Smsmith    if (sc->tz_requested > newactive)
36879375Smsmith	newactive = sc->tz_requested;
36978915Smsmith
37079375Smsmith    /* update temperature-related flags */
37179375Smsmith    newflags = TZ_THFLAG_NONE;
37279375Smsmith    if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv))
37379375Smsmith	newflags |= TZ_THFLAG_PSV;
37479375Smsmith    if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot))
37579375Smsmith	newflags |= TZ_THFLAG_HOT;
37679375Smsmith    if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt))
37779375Smsmith	newflags |= TZ_THFLAG_CRT;
37879375Smsmith
37978915Smsmith    /*
38079283Smsmith     * If the active cooling state has changed, we have to switch things.
38178915Smsmith     */
38279283Smsmith    if (newactive != sc->tz_active) {
38378915Smsmith
38479283Smsmith	/* turn off the cooling devices that are on, if any are */
38579283Smsmith	if (sc->tz_active != TZ_ACTIVE_NONE)
38679375Smsmith	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
38779283Smsmith				      acpi_tz_switch_cooler_off, sc);
38878915Smsmith
38979283Smsmith	/* turn on cooling devices that are required, if any are */
39079283Smsmith	if (newactive != TZ_ACTIVE_NONE)
39179375Smsmith	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
39279283Smsmith				      acpi_tz_switch_cooler_on, sc);
39379375Smsmith	device_printf(sc->tz_dev, "switched from _AC%d to _AC%d\n", sc->tz_active, newactive);
39479283Smsmith	sc->tz_active = newactive;
39579283Smsmith    }
39678915Smsmith
39778915Smsmith    /*
39879283Smsmith     * XXX (de)activate any passive cooling that may be required.
39978915Smsmith     */
40078915Smsmith
40178915Smsmith    /*
40279283Smsmith     * If we have just become _HOT or _CRT, warn the user.
40379283Smsmith     *
40479283Smsmith     * We should actually shut down at this point, but it's not clear
40579283Smsmith     * that some systems don't actually map _CRT to the same value as _AC0.
40678915Smsmith     */
40779375Smsmith    if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) &&
40879375Smsmith	!(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT))) {
40979283Smsmith	device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n",
41079283Smsmith		      TZ_KELVTOC(sc->tz_temperature), sc->tz_temperature);
41179283Smsmith	/* shutdown_nice(RB_POWEROFF);*/
41278915Smsmith    }
41379375Smsmith    sc->tz_thflags = newflags;
41478915Smsmith
41571874Smsmith    return_VOID;
41671874Smsmith}
41770271Stakawata
41878915Smsmith/*
41978915Smsmith * Turn off all the cooling devices.
42078915Smsmith */
42171874Smsmithstatic void
42278915Smsmithacpi_tz_all_off(struct acpi_tz_softc *sc)
42378915Smsmith{
42478915Smsmith    int		i;
42578915Smsmith
42678915Smsmith    FUNCTION_TRACE(__func__);
42778999Smsmith
42878999Smsmith    ACPI_ASSERTLOCK;
42978915Smsmith
43078915Smsmith    /*
43179375Smsmith     * Scan all the _ALx objects, and turn them all off.
43278915Smsmith     */
43378915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
43479375Smsmith	if (sc->tz_zone.al[i].Pointer == NULL)
43578915Smsmith	    continue;
43679375Smsmith	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
43778915Smsmith				  acpi_tz_switch_cooler_off, sc);
43878915Smsmith    }
43978915Smsmith
44078915Smsmith    /*
44178915Smsmith     * XXX revert any passive-cooling options.
44278915Smsmith     */
44378915Smsmith
44479283Smsmith    sc->tz_active = TZ_ACTIVE_NONE;
44579375Smsmith    sc->tz_thflags = TZ_THFLAG_NONE;
44678915Smsmith    return_VOID;
44778915Smsmith}
44878915Smsmith
44978915Smsmith/*
45078915Smsmith * Given an object, verify that it's a reference to a device of some sort,
45178915Smsmith * and try to switch it off.
45278915Smsmith */
45378915Smsmithstatic void
45478915Smsmithacpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
45578915Smsmith{
45679375Smsmith    ACPI_HANDLE		cooler;
45778915Smsmith
45878915Smsmith    FUNCTION_TRACE(__func__);
45978915Smsmith
46078999Smsmith    ACPI_ASSERTLOCK;
46178999Smsmith
46278915Smsmith    switch(obj->Type) {
46378915Smsmith    case ACPI_TYPE_STRING:
46478915Smsmith	DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer));
46578915Smsmith
46678915Smsmith	/*
46778915Smsmith	 * Find the handle for the device and turn it off.
46878915Smsmith	 * The String object here seems to contain a fully-qualified path, so we
46978915Smsmith	 * don't have to search for it in our parents.
47078915Smsmith	 *
47178915Smsmith	 * XXX This may not always be the case.
47278915Smsmith	 */
47379375Smsmith	if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK)
47478915Smsmith	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
47578915Smsmith	break;
47678915Smsmith
47778915Smsmith    default:
47878915Smsmith	DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
47978915Smsmith				    obj->Type));
48078915Smsmith	break;
48178915Smsmith    }
48279375Smsmith    return_VOID;
48378915Smsmith}
48478915Smsmith
48578915Smsmith/*
48678915Smsmith * Given an object, verify that it's a reference to a device of some sort,
48778915Smsmith * and try to switch it on.
48878915Smsmith *
48978915Smsmith * XXX replication of off/on function code is bad, mmmkay?
49078915Smsmith */
49178915Smsmithstatic void
49278915Smsmithacpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
49378915Smsmith{
49478915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
49578999Smsmith    ACPI_HANDLE			cooler;
49679375Smsmith    ACPI_STATUS			status;
49779375Smsmith
49878915Smsmith    FUNCTION_TRACE(__func__);
49978915Smsmith
50078999Smsmith    ACPI_ASSERTLOCK;
50178999Smsmith
50278915Smsmith    switch(obj->Type) {
50378915Smsmith    case ACPI_TYPE_STRING:
50479375Smsmith	DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s on\n", obj->String.Pointer));
50578915Smsmith
50678999Smsmith	/*
50778999Smsmith	 * Find the handle for the device and turn it off.
50878999Smsmith	 * The String object here seems to contain a fully-qualified path, so we
50978999Smsmith	 * don't have to search for it in our parents.
51078999Smsmith	 *
51178999Smsmith	 * XXX This may not always be the case.
51278999Smsmith	 */
51379375Smsmith	if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK) {
51479375Smsmith	    if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0))) {
51579375Smsmith		device_printf(sc->tz_dev, "failed to activate %s - %s\n",
51679375Smsmith			      obj->String.Pointer, acpi_strerror(status));
51779375Smsmith	    }
51879375Smsmith	} else {
51979375Smsmith	    device_printf(sc->tz_dev, "couldn't find %s\n", obj->String.Pointer);
52079375Smsmith	}
52178915Smsmith	break;
52278915Smsmith
52378915Smsmith    default:
52478915Smsmith	DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n",
52578915Smsmith				    obj->Type));
52678915Smsmith	break;
52778915Smsmith    }
52878915Smsmith	return_VOID;
52978915Smsmith}
53078915Smsmith
53178915Smsmith/*
53278915Smsmith * Read/debug-print a parameter, default it to -1.
53378915Smsmith */
53478915Smsmithstatic void
53578915Smsmithacpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
53678915Smsmith{
53778915Smsmith
53878915Smsmith    FUNCTION_TRACE(__func__);
53978915Smsmith
54078999Smsmith    ACPI_ASSERTLOCK;
54178999Smsmith
54278915Smsmith    if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) {
54378915Smsmith	*data = -1;
54478915Smsmith    } else {
54578915Smsmith	DEBUG_PRINT(TRACE_VALUES, ("%s.%s = %d\n", acpi_name(sc->tz_handle),
54678915Smsmith				   node, *data));
54778915Smsmith    }
54878915Smsmith    return_VOID;
54978915Smsmith}
55079283Smsmith
55179283Smsmith/*
55279283Smsmith * Sanity-check a temperature value.  Assume that setpoints
55379283Smsmith * should be between 0C and 150C.
55479283Smsmith */
55579283Smsmithstatic void
55679283Smsmithacpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
55779283Smsmith{
55879283Smsmith    if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) {
55979283Smsmith	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
56079283Smsmith		      what, TZ_KELVTOC(*val));
56179283Smsmith	*val = -1;
56279283Smsmith    }
56379283Smsmith}
56479375Smsmith
56579375Smsmith/*
56679375Smsmith * Respond to a sysctl on the active state node.
56779375Smsmith */
56879375Smsmithstatic int
56979375Smsmithacpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
57079375Smsmith{
57179375Smsmith    struct acpi_tz_softc	*sc;
57279375Smsmith    int				active;
57379375Smsmith    int		 		error;
57479375Smsmith
57579375Smsmith    ACPI_LOCK;
57679375Smsmith
57779375Smsmith    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
57879375Smsmith    active = sc->tz_active;
57979375Smsmith    error = sysctl_handle_int(oidp, &active, 0, req);
58079375Smsmith
58179375Smsmith    /* error or no new value */
58279375Smsmith    if ((error != 0) || (req->newptr == NULL))
58379375Smsmith	goto out;
58478915Smsmith
58579375Smsmith    /* range check */
58679375Smsmith    if ((active < -1) || (active >= TZ_NUMLEVELS)) {
58779375Smsmith	error = EINVAL;
58879375Smsmith	goto out;
58979375Smsmith    }
59079375Smsmith
59179375Smsmith    /* set new preferred level and re-switch */
59279375Smsmith    sc->tz_requested = active;
59379375Smsmith    acpi_tz_monitor(sc);
59479375Smsmith
59579375Smsmith out:
59679375Smsmith    ACPI_UNLOCK;
59779375Smsmith    return(error);
59879375Smsmith}
59979375Smsmith
60078915Smsmith/*
60178915Smsmith * Respond to a Notify event sent to the zone.
60278915Smsmith */
60378915Smsmithstatic void
60471874Smsmithacpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
60571874Smsmith{
60678915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
60778915Smsmith
60877432Smsmith    FUNCTION_TRACE(__func__);
60970271Stakawata
61078999Smsmith    ACPI_ASSERTLOCK;
61178999Smsmith
61278915Smsmith    switch(notify) {
61378915Smsmith    case TZ_NOTIFY_TEMPERATURE:
61478999Smsmith	/* temperature change occurred */
61579375Smsmith	device_printf(sc->tz_dev, "notified of temperature reaching setpoint\n");
61678999Smsmith	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
61778915Smsmith	break;
61878915Smsmith    case TZ_NOTIFY_DEVICES:
61978915Smsmith    case TZ_NOTIFY_LEVELS:
62078999Smsmith	/* zone devices/setpoints changed */
62179375Smsmith	device_printf(sc->tz_dev, "notified of zone configuration change\n");
62278999Smsmith	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
62378915Smsmith	break;
62478915Smsmith    default:
62578915Smsmith	device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify);
62678915Smsmith	break;
62771874Smsmith    }
62871874Smsmith    return_VOID;
62971874Smsmith}
63070271Stakawata
63178915Smsmith/*
63278915Smsmith * Poll the thermal zone.
63378915Smsmith */
63478915Smsmithstatic void
63578915Smsmithacpi_tz_timeout(void *arg)
63678915Smsmith{
63778915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
63878915Smsmith
63979375Smsmith    /* do we need to get the power profile settings? */
64079375Smsmith    if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
64179375Smsmith	acpi_tz_powerprofile(arg);
64279375Smsmith	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
64379375Smsmith    }
64479375Smsmith
64578999Smsmith    ACPI_LOCK;
64678999Smsmith
64778915Smsmith    /* check temperature, take action */
64878999Smsmith    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
64978915Smsmith
65078915Smsmith    /* XXX passive cooling actions? */
65178915Smsmith
65278915Smsmith    /* re-register ourself */
65378915Smsmith    sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
65478999Smsmith
65578999Smsmith    ACPI_UNLOCK;
65678915Smsmith}
65779375Smsmith
65879375Smsmith/*
65979375Smsmith * System power profile may have changed; fetch and notify the
66079375Smsmith * thermal zone accordingly.
66179375Smsmith *
66279375Smsmith * Since this can be called from an arbitrary eventhandler, it needs
66379375Smsmith * to get the ACPI lock itself.
66479375Smsmith */
66579375Smsmithstatic void
66679375Smsmithacpi_tz_powerprofile(void *arg)
66779375Smsmith{
66879375Smsmith    ACPI_OBJECT_LIST		args;
66979375Smsmith    ACPI_OBJECT			obj;
67079375Smsmith    ACPI_STATUS			status;
67179375Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
67279375Smsmith
67379375Smsmith    ACPI_LOCK;
67479375Smsmith
67579375Smsmith    /* check that we haven't decided there's no _SCP method */
67679375Smsmith    if (!(sc->tz_flags & TZ_FLAG_NO_SCP)) {
67779375Smsmith
67879375Smsmith	/* call _SCP to set the new profile */
67979375Smsmith	obj.Type = ACPI_TYPE_INTEGER;
68079375Smsmith	obj.Integer.Value = (powerprofile_get_state() == POWERPROFILE_PERFORMANCE) ? 0 : 1;
68179375Smsmith	args.Count = 1;
68279375Smsmith	args.Pointer = &obj;
68379375Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL))) {
68479375Smsmith	    device_printf(sc->tz_dev, "can't evaluate %s._SCP - %s\n", acpi_name(sc->tz_handle),
68579375Smsmith			  acpi_strerror(status));	/* XXX silence this at some point */
68679375Smsmith	    sc->tz_flags |= TZ_FLAG_NO_SCP;
68779375Smsmith	} else {
68879375Smsmith	    /* we have to re-evaluate the entire zone now */
68979375Smsmith	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
69079375Smsmith	}
69179375Smsmith    }
69279375Smsmith    ACPI_UNLOCK;
69379375Smsmith}
69479375Smsmith
695