acpi_thermal.c revision 85699
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 85699 2001-10-29 18:09:43Z iwasaki $
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
5085699Siwasaki#define TZ_DPRINT(dev, x...) do {					\
5185699Siwasaki	if (acpi_get_verbose(acpi_device_get_parent_softc(dev)))	\
5285699Siwasaki		device_printf(dev, x);					\
5385699Siwasaki} while (0)
5485699Siwasaki
5578915Smsmith#define TZ_NOTIFY_TEMPERATURE	0x80
5678915Smsmith#define TZ_NOTIFY_DEVICES	0x81
5778915Smsmith#define TZ_NOTIFY_LEVELS	0x82
5878915Smsmith
5978915Smsmith#define TZ_POLLRATE	(hz * 10)	/* every ten seconds */
6078915Smsmith
6178915Smsmith#define TZ_NUMLEVELS	10		/* defined by ACPI spec */
6279375Smsmithstruct acpi_tz_zone {
6378915Smsmith    int		ac[TZ_NUMLEVELS];
6478915Smsmith    ACPI_BUFFER	al[TZ_NUMLEVELS];
6578915Smsmith    int		crt;
6678915Smsmith    int		hot;
6778915Smsmith    ACPI_BUFFER	psl;
6878915Smsmith    int		psv;
6978915Smsmith    int		tc1;
7078915Smsmith    int		tc2;
7178915Smsmith    int		tsp;
7278915Smsmith    int		tzp;
7378915Smsmith};
7478915Smsmith
7578915Smsmith
7667761Smsmithstruct acpi_tz_softc {
7779375Smsmith    device_t			tz_dev;			/* device handle */
7879375Smsmith    ACPI_HANDLE			tz_handle;		/* thermal zone handle */
7979375Smsmith    struct callout_handle	tz_timeout;		/* poll routine handle */
8079375Smsmith    int				tz_temperature;		/* current temperature */
8179375Smsmith    int				tz_active;		/* current active cooling */
8279375Smsmith#define TZ_ACTIVE_NONE		-1
8379375Smsmith    int				tz_requested;		/* user-requested minimum active cooling */
8479375Smsmith    int				tz_thflags;		/* current temperature-related flags */
8579375Smsmith#define TZ_THFLAG_NONE		0
8679375Smsmith#define TZ_THFLAG_PSV		(1<<0)
8779375Smsmith#define TZ_THFLAG_HOT		(1<<2)
8879375Smsmith#define TZ_THFLAG_CRT		(1<<3)
8979283Smsmith    int				tz_flags;
9079375Smsmith#define TZ_FLAG_NO_SCP		(1<<0)			/* no _SCP method */
9179375Smsmith#define TZ_FLAG_GETPROFILE	(1<<1)			/* fetch powerprofile in timeout */
9285699Siwasaki    struct timespec		tz_cooling_started;	/* current cooling starting time */
9379283Smsmith
9479375Smsmith    struct sysctl_ctx_list	tz_sysctl_ctx;		/* sysctl tree */
9579283Smsmith    struct sysctl_oid		*tz_sysctl_tree;
9678915Smsmith
9779375Smsmith    struct acpi_tz_zone 	tz_zone;		/* thermal zone parameters */
9867761Smsmith};
9967761Smsmith
10067761Smsmithstatic int	acpi_tz_probe(device_t dev);
10167761Smsmithstatic int	acpi_tz_attach(device_t dev);
10278915Smsmithstatic int	acpi_tz_establish(struct acpi_tz_softc *sc);
10378999Smsmithstatic void	acpi_tz_monitor(struct acpi_tz_softc *sc);
10478915Smsmithstatic void	acpi_tz_all_off(struct acpi_tz_softc *sc);
10578915Smsmithstatic void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
10678915Smsmithstatic void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
10778915Smsmithstatic void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data);
10879283Smsmithstatic void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
10979375Smsmithstatic int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
11071874Smsmithstatic void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context);
11178915Smsmithstatic void	acpi_tz_timeout(void *arg);
11279375Smsmithstatic void	acpi_tz_powerprofile(void *arg);
11371874Smsmith
11467761Smsmithstatic device_method_t acpi_tz_methods[] = {
11567761Smsmith    /* Device interface */
11667761Smsmith    DEVMETHOD(device_probe,	acpi_tz_probe),
11767761Smsmith    DEVMETHOD(device_attach,	acpi_tz_attach),
11867761Smsmith
11967761Smsmith    {0, 0}
12067761Smsmith};
12167761Smsmith
12267761Smsmithstatic driver_t acpi_tz_driver = {
12367761Smsmith    "acpi_tz",
12467761Smsmith    acpi_tz_methods,
12567761Smsmith    sizeof(struct acpi_tz_softc),
12667761Smsmith};
12767761Smsmith
12867761Smsmithdevclass_t acpi_tz_devclass;
12967761SmsmithDRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
13067761Smsmith
13179283Smsmithstatic struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
13279283Smsmithstatic struct sysctl_oid	*acpi_tz_sysctl_tree;
13379283Smsmith
13485699Siwasakistatic int			acpi_tz_min_runtime = 0;/* minimum cooling run time */
13585699Siwasaki
13678915Smsmith/*
13778915Smsmith * Match an ACPI thermal zone.
13878915Smsmith */
13967761Smsmithstatic int
14067761Smsmithacpi_tz_probe(device_t dev)
14167761Smsmith{
14278999Smsmith    int		result;
14378999Smsmith
14478999Smsmith    ACPI_LOCK;
14578999Smsmith
14678915Smsmith    /* no FUNCTION_TRACE - too noisy */
14769744Smsmith
14869744Smsmith    if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) &&
14969744Smsmith	!acpi_disabled("thermal")) {
15067761Smsmith	device_set_desc(dev, "thermal zone");
15178999Smsmith	result = -10;
15278999Smsmith    } else {
15378999Smsmith	result = ENXIO;
15467761Smsmith    }
15578999Smsmith    ACPI_UNLOCK;
15678999Smsmith    return(result);
15767761Smsmith}
15867761Smsmith
15978915Smsmith/*
16078915Smsmith * Attach to an ACPI thermal zone.
16178915Smsmith */
16267761Smsmithstatic int
16367761Smsmithacpi_tz_attach(device_t dev)
16467761Smsmith{
16567761Smsmith    struct acpi_tz_softc	*sc;
16679283Smsmith    struct acpi_softc		*acpi_sc;
16778915Smsmith    int				error;
16879283Smsmith    char			oidname[8];
16979283Smsmith    int				i;
17067761Smsmith
17177432Smsmith    FUNCTION_TRACE(__func__);
17269744Smsmith
17378999Smsmith    ACPI_LOCK;
17478999Smsmith
17567761Smsmith    sc = device_get_softc(dev);
17667761Smsmith    sc->tz_dev = dev;
17767761Smsmith    sc->tz_handle = acpi_get_handle(dev);
17879375Smsmith    sc->tz_requested = TZ_ACTIVE_NONE;
17967761Smsmith
18078915Smsmith    /*
18178915Smsmith     * Parse the current state of the thermal zone and build control
18278915Smsmith     * structures.
18378915Smsmith     */
18478915Smsmith    if ((error = acpi_tz_establish(sc)) != 0)
18578999Smsmith	goto out;
18678915Smsmith
18778915Smsmith    /*
18878915Smsmith     * Register for any Notify events sent to this zone.
18978915Smsmith     */
19071874Smsmith    AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
19178999Smsmith			     acpi_tz_notify_handler, sc);
19270271Stakawata
19371874Smsmith    /*
19479283Smsmith     * Create our sysctl nodes.
19579283Smsmith     *
19679283Smsmith     * XXX we need a mechanism for adding nodes under ACPI.
19779283Smsmith     */
19879283Smsmith    if (device_get_unit(dev) == 0) {
19979283Smsmith	acpi_sc = acpi_device_get_parent_softc(dev);
20079283Smsmith	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
20179283Smsmith	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
20279283Smsmith					      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
20379283Smsmith					      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
20485699Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
20585699Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
20685699Siwasaki		       OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW,
20785699Siwasaki		       &acpi_tz_min_runtime, 0, "minimum cooling run time in sec");
20879283Smsmith    }
20979283Smsmith    sysctl_ctx_init(&sc->tz_sysctl_ctx);
21079283Smsmith    sprintf(oidname, "tz%d", device_get_unit(dev));
21179283Smsmith    sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
21279283Smsmith					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
21379283Smsmith					 oidname, CTLFLAG_RD, 0, "");
21479283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
21579283Smsmith		   OID_AUTO, "temperature", CTLFLAG_RD,
21679283Smsmith		   &sc->tz_temperature, 0, "current thermal zone temperature");
21779375Smsmith    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
21879375Smsmith		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
21979375Smsmith		    sc, 0, acpi_tz_active_sysctl, "I", "");
22079375Smsmith
22179283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
22279375Smsmith		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
22379375Smsmith		   &sc->tz_thflags, 0, "thermal zone flags");
22479283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
22579283Smsmith		   OID_AUTO, "_PSV", CTLFLAG_RD,
22679375Smsmith		   &sc->tz_zone.psv, 0, "");
22779283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
22879283Smsmith		   OID_AUTO, "_HOT", CTLFLAG_RD,
22979375Smsmith		   &sc->tz_zone.hot, 0, "");
23079283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23179283Smsmith		   OID_AUTO, "_CRT", CTLFLAG_RD,
23279375Smsmith		   &sc->tz_zone.crt, 0, "");
23379283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
23479283Smsmith	sprintf(oidname, "_AC%d", i);
23579283Smsmith	SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
23679283Smsmith		       OID_AUTO, oidname, CTLFLAG_RD,
23779375Smsmith		       &sc->tz_zone.ac[i], 0, "");
23879283Smsmith    }
23979283Smsmith
24079283Smsmith    /*
24179375Smsmith     * Register our power profile event handler, and flag it for a manual
24279375Smsmith     * invocation by our timeout.  We defer it like this so that the rest
24379375Smsmith     * of the subsystem has time to come up.
24479375Smsmith     */
24579375Smsmith    EVENTHANDLER_REGISTER(powerprofile_change, acpi_tz_powerprofile, sc, 0);
24679375Smsmith    sc->tz_flags |= TZ_FLAG_GETPROFILE;
24779375Smsmith
24879375Smsmith    /*
24971874Smsmith     * Don't bother evaluating/printing the temperature at this point;
25071874Smsmith     * on many systems it'll be bogus until the EC is running.
25171874Smsmith     */
25278999Smsmith
25378999Smsmith out:
25478999Smsmith    ACPI_UNLOCK;
25579375Smsmith
25679375Smsmith    /*
25779375Smsmith     * Start the timeout routine, with enough delay for the rest of the
25879375Smsmith     * subsystem to come up.
25979375Smsmith     */
26079375Smsmith    sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
26179375Smsmith
26278999Smsmith    return_VALUE(error);
26367761Smsmith}
26470271Stakawata
26578915Smsmith/*
26678915Smsmith * Parse the current state of this thermal zone and set up to use it.
26778915Smsmith *
26878915Smsmith * Note that we may have previous state, which will have to be discarded.
26978915Smsmith */
27078915Smsmithstatic int
27178915Smsmithacpi_tz_establish(struct acpi_tz_softc *sc)
27278915Smsmith{
27378915Smsmith    ACPI_OBJECT	*obj;
27478915Smsmith    int		i;
27578915Smsmith    char	nbuf[8];
27678915Smsmith
27778915Smsmith    FUNCTION_TRACE(__func__);
27878915Smsmith
27978999Smsmith    ACPI_ASSERTLOCK;
28078999Smsmith
28178915Smsmith    /*
28278915Smsmith     * Power everything off and erase any existing state.
28378915Smsmith     */
28478915Smsmith    acpi_tz_all_off(sc);
28578915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
28679375Smsmith	if (sc->tz_zone.al[i].Pointer != NULL)
28779375Smsmith	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
28879375Smsmith    if (sc->tz_zone.psl.Pointer != NULL)
28979375Smsmith	AcpiOsFree(sc->tz_zone.psl.Pointer);
29079375Smsmith    bzero(&sc->tz_zone, sizeof(sc->tz_zone));
29178915Smsmith
29278915Smsmith    /*
29378915Smsmith     * Evaluate thermal zone parameters.
29478915Smsmith     */
29578915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
29678915Smsmith	sprintf(nbuf, "_AC%d", i);
29779375Smsmith	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
29878915Smsmith	sprintf(nbuf, "_AL%d", i);
29979375Smsmith	acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
30079375Smsmith	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
30178915Smsmith	if (obj != NULL) {
30278915Smsmith	    /* should be a package containing a list of power objects */
30378915Smsmith	    if (obj->Type != ACPI_TYPE_PACKAGE) {
30478915Smsmith		device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n",
30578915Smsmith			      nbuf, obj->Type);
30678915Smsmith		return_VALUE(ENXIO);
30778915Smsmith	    }
30878915Smsmith	}
30978915Smsmith    }
31079375Smsmith    acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
31179375Smsmith    acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
31279375Smsmith    acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
31379375Smsmith    acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
31479375Smsmith    acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
31579375Smsmith    acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
31679375Smsmith    acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
31779375Smsmith    acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
31878915Smsmith
31978915Smsmith    /*
32079283Smsmith     * Sanity-check the values we've been given.
32179283Smsmith     *
32279283Smsmith     * XXX what do we do about systems that give us the same value for
32379283Smsmith     *     more than one of these setpoints?
32479283Smsmith     */
32579375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
32679375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
32779375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
32879283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
32979375Smsmith	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
33079283Smsmith
33179283Smsmith    /*
33278915Smsmith     * Power off everything that we've just been given.
33378915Smsmith     */
33478915Smsmith    acpi_tz_all_off(sc);
33578915Smsmith
33678915Smsmith    return_VALUE(0);
33778915Smsmith}
33878915Smsmith
33985699Siwasakistatic char	*aclevel_string[] =	{
34085699Siwasaki	"NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
34185699Siwasaki	"_AC5", "_AC6", "_AC7", "_AC8", "_AC9" };
34285699Siwasaki
34385699Siwasakistatic __inline const char *
34485699Siwasakiacpi_tz_aclevel_string(int active)
34585699Siwasaki{
34685699Siwasaki	if (active < -1 || active >= TZ_NUMLEVELS) {
34785699Siwasaki		return (aclevel_string[0]);
34885699Siwasaki	}
34985699Siwasaki
35085699Siwasaki	return (aclevel_string[active+1]);
35185699Siwasaki}
35285699Siwasaki
35378915Smsmith/*
35478915Smsmith * Evaluate the condition of a thermal zone, take appropriate actions.
35578915Smsmith */
35671874Smsmithstatic void
35778915Smsmithacpi_tz_monitor(struct acpi_tz_softc *sc)
35871874Smsmith{
35979283Smsmith    int		temp;
36078915Smsmith    int		i;
36179283Smsmith    int		newactive, newflags;
36285699Siwasaki    struct	timespec curtime;
36370271Stakawata
36477432Smsmith    FUNCTION_TRACE(__func__);
36570271Stakawata
36678999Smsmith    ACPI_ASSERTLOCK;
36778999Smsmith
36878915Smsmith    /*
36978915Smsmith     * Get the current temperature.
37078915Smsmith     */
37178915Smsmith    if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) {
37278915Smsmith	device_printf(sc->tz_dev, "error fetching current temperature\n");
37378915Smsmith	/* XXX disable zone? go to max cooling? */
37471874Smsmith	return_VOID;
37571874Smsmith    }
37682372Smsmith    ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
37779283Smsmith    sc->tz_temperature = temp;
37878915Smsmith
37978915Smsmith    /*
38078915Smsmith     * Work out what we ought to be doing right now.
38179283Smsmith     *
38279283Smsmith     * Note that the _ACx levels sort from hot to cold.
38378915Smsmith     */
38479283Smsmith    newactive = TZ_ACTIVE_NONE;
38579375Smsmith    for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
38679375Smsmith	if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) {
38779283Smsmith	    newactive = i;
38882967Siwasaki	    if (sc->tz_active != newactive) {
38985699Siwasaki		TZ_DPRINT(sc->tz_dev,
39085699Siwasaki			  "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
39185699Siwasaki			  TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
39285699Siwasaki		getnanotime(&sc->tz_cooling_started);
39382967Siwasaki	    }
39479375Smsmith	}
39579375Smsmith    }
39679283Smsmith
39785699Siwasaki    /*
39885699Siwasaki     * We are going to get _ACx level down (colder side), but give a guaranteed
39985699Siwasaki     * minimum cooling run time if requested.
40085699Siwasaki     */
40185699Siwasaki    if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
40285699Siwasaki	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
40385699Siwasaki	getnanotime(&curtime);
40485699Siwasaki	timespecsub(&curtime, &sc->tz_cooling_started);
40585699Siwasaki	if (curtime.tv_sec < acpi_tz_min_runtime) {
40685699Siwasaki	    newactive = sc->tz_active;
40785699Siwasaki	}
40885699Siwasaki    }
40985699Siwasaki
41079375Smsmith    /* handle user override of active mode */
41179375Smsmith    if (sc->tz_requested > newactive)
41279375Smsmith	newactive = sc->tz_requested;
41378915Smsmith
41479375Smsmith    /* update temperature-related flags */
41579375Smsmith    newflags = TZ_THFLAG_NONE;
41679375Smsmith    if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv))
41779375Smsmith	newflags |= TZ_THFLAG_PSV;
41879375Smsmith    if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot))
41979375Smsmith	newflags |= TZ_THFLAG_HOT;
42079375Smsmith    if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt))
42179375Smsmith	newflags |= TZ_THFLAG_CRT;
42279375Smsmith
42378915Smsmith    /*
42479283Smsmith     * If the active cooling state has changed, we have to switch things.
42578915Smsmith     */
42679283Smsmith    if (newactive != sc->tz_active) {
42778915Smsmith
42879283Smsmith	/* turn off the cooling devices that are on, if any are */
42979283Smsmith	if (sc->tz_active != TZ_ACTIVE_NONE)
43079375Smsmith	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
43179283Smsmith				      acpi_tz_switch_cooler_off, sc);
43278915Smsmith
43379283Smsmith	/* turn on cooling devices that are required, if any are */
43479283Smsmith	if (newactive != TZ_ACTIVE_NONE)
43579375Smsmith	    acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
43679283Smsmith				      acpi_tz_switch_cooler_on, sc);
43785699Siwasaki	TZ_DPRINT(sc->tz_dev, "switched from %s to %s: %d.%dC\n",
43885699Siwasaki		  acpi_tz_aclevel_string(sc->tz_active),
43985699Siwasaki		  acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
44079283Smsmith	sc->tz_active = newactive;
44179283Smsmith    }
44278915Smsmith
44378915Smsmith    /*
44479283Smsmith     * XXX (de)activate any passive cooling that may be required.
44578915Smsmith     */
44678915Smsmith
44778915Smsmith    /*
44879283Smsmith     * If we have just become _HOT or _CRT, warn the user.
44979283Smsmith     *
45079283Smsmith     * We should actually shut down at this point, but it's not clear
45179283Smsmith     * that some systems don't actually map _CRT to the same value as _AC0.
45278915Smsmith     */
45379375Smsmith    if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) &&
45479375Smsmith	!(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT))) {
45579283Smsmith	device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n",
45679283Smsmith		      TZ_KELVTOC(sc->tz_temperature), sc->tz_temperature);
45779283Smsmith	/* shutdown_nice(RB_POWEROFF);*/
45878915Smsmith    }
45979375Smsmith    sc->tz_thflags = newflags;
46078915Smsmith
46171874Smsmith    return_VOID;
46271874Smsmith}
46370271Stakawata
46478915Smsmith/*
46578915Smsmith * Turn off all the cooling devices.
46678915Smsmith */
46771874Smsmithstatic void
46878915Smsmithacpi_tz_all_off(struct acpi_tz_softc *sc)
46978915Smsmith{
47078915Smsmith    int		i;
47178915Smsmith
47278915Smsmith    FUNCTION_TRACE(__func__);
47378999Smsmith
47478999Smsmith    ACPI_ASSERTLOCK;
47578915Smsmith
47678915Smsmith    /*
47779375Smsmith     * Scan all the _ALx objects, and turn them all off.
47878915Smsmith     */
47978915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
48079375Smsmith	if (sc->tz_zone.al[i].Pointer == NULL)
48178915Smsmith	    continue;
48279375Smsmith	acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
48378915Smsmith				  acpi_tz_switch_cooler_off, sc);
48478915Smsmith    }
48578915Smsmith
48678915Smsmith    /*
48778915Smsmith     * XXX revert any passive-cooling options.
48878915Smsmith     */
48978915Smsmith
49079283Smsmith    sc->tz_active = TZ_ACTIVE_NONE;
49179375Smsmith    sc->tz_thflags = TZ_THFLAG_NONE;
49278915Smsmith    return_VOID;
49378915Smsmith}
49478915Smsmith
49578915Smsmith/*
49678915Smsmith * Given an object, verify that it's a reference to a device of some sort,
49778915Smsmith * and try to switch it off.
49878915Smsmith */
49978915Smsmithstatic void
50078915Smsmithacpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
50178915Smsmith{
50279375Smsmith    ACPI_HANDLE		cooler;
50378915Smsmith
50478915Smsmith    FUNCTION_TRACE(__func__);
50578915Smsmith
50678999Smsmith    ACPI_ASSERTLOCK;
50778999Smsmith
50878915Smsmith    switch(obj->Type) {
50978915Smsmith    case ACPI_TYPE_STRING:
51082372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n", obj->String.Pointer));
51178915Smsmith
51278915Smsmith	/*
51378915Smsmith	 * Find the handle for the device and turn it off.
51478915Smsmith	 * The String object here seems to contain a fully-qualified path, so we
51578915Smsmith	 * don't have to search for it in our parents.
51678915Smsmith	 *
51778915Smsmith	 * XXX This may not always be the case.
51878915Smsmith	 */
51979375Smsmith	if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK)
52078915Smsmith	    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
52178915Smsmith	break;
52278915Smsmith
52378915Smsmith    default:
52482372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
52582372Smsmith			  obj->Type));
52678915Smsmith	break;
52778915Smsmith    }
52879375Smsmith    return_VOID;
52978915Smsmith}
53078915Smsmith
53178915Smsmith/*
53278915Smsmith * Given an object, verify that it's a reference to a device of some sort,
53378915Smsmith * and try to switch it on.
53478915Smsmith *
53578915Smsmith * XXX replication of off/on function code is bad, mmmkay?
53678915Smsmith */
53778915Smsmithstatic void
53878915Smsmithacpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
53978915Smsmith{
54078915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
54178999Smsmith    ACPI_HANDLE			cooler;
54279375Smsmith    ACPI_STATUS			status;
54379375Smsmith
54478915Smsmith    FUNCTION_TRACE(__func__);
54578915Smsmith
54678999Smsmith    ACPI_ASSERTLOCK;
54778999Smsmith
54878915Smsmith    switch(obj->Type) {
54978915Smsmith    case ACPI_TYPE_STRING:
55082372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n", obj->String.Pointer));
55178915Smsmith
55278999Smsmith	/*
55378999Smsmith	 * Find the handle for the device and turn it off.
55478999Smsmith	 * The String object here seems to contain a fully-qualified path, so we
55578999Smsmith	 * don't have to search for it in our parents.
55678999Smsmith	 *
55778999Smsmith	 * XXX This may not always be the case.
55878999Smsmith	 */
55979375Smsmith	if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK) {
56079375Smsmith	    if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0))) {
56179375Smsmith		device_printf(sc->tz_dev, "failed to activate %s - %s\n",
56280078Smsmith			      obj->String.Pointer, AcpiFormatException(status));
56379375Smsmith	    }
56479375Smsmith	} else {
56579375Smsmith	    device_printf(sc->tz_dev, "couldn't find %s\n", obj->String.Pointer);
56679375Smsmith	}
56778915Smsmith	break;
56878915Smsmith
56978915Smsmith    default:
57082372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n",
57182372Smsmith			  obj->Type));
57278915Smsmith	break;
57378915Smsmith    }
57478915Smsmith	return_VOID;
57578915Smsmith}
57678915Smsmith
57778915Smsmith/*
57878915Smsmith * Read/debug-print a parameter, default it to -1.
57978915Smsmith */
58078915Smsmithstatic void
58178915Smsmithacpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
58278915Smsmith{
58378915Smsmith
58478915Smsmith    FUNCTION_TRACE(__func__);
58578915Smsmith
58678999Smsmith    ACPI_ASSERTLOCK;
58778999Smsmith
58878915Smsmith    if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) {
58978915Smsmith	*data = -1;
59078915Smsmith    } else {
59182372Smsmith	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n", acpi_name(sc->tz_handle),
59282372Smsmith			  node, *data));
59378915Smsmith    }
59478915Smsmith    return_VOID;
59578915Smsmith}
59679283Smsmith
59779283Smsmith/*
59879283Smsmith * Sanity-check a temperature value.  Assume that setpoints
59979283Smsmith * should be between 0C and 150C.
60079283Smsmith */
60179283Smsmithstatic void
60279283Smsmithacpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
60379283Smsmith{
60479283Smsmith    if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) {
60579283Smsmith	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
60679283Smsmith		      what, TZ_KELVTOC(*val));
60779283Smsmith	*val = -1;
60879283Smsmith    }
60979283Smsmith}
61079375Smsmith
61179375Smsmith/*
61279375Smsmith * Respond to a sysctl on the active state node.
61379375Smsmith */
61479375Smsmithstatic int
61579375Smsmithacpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
61679375Smsmith{
61779375Smsmith    struct acpi_tz_softc	*sc;
61879375Smsmith    int				active;
61979375Smsmith    int		 		error;
62079375Smsmith
62179375Smsmith    ACPI_LOCK;
62279375Smsmith
62379375Smsmith    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
62479375Smsmith    active = sc->tz_active;
62579375Smsmith    error = sysctl_handle_int(oidp, &active, 0, req);
62679375Smsmith
62779375Smsmith    /* error or no new value */
62879375Smsmith    if ((error != 0) || (req->newptr == NULL))
62979375Smsmith	goto out;
63078915Smsmith
63179375Smsmith    /* range check */
63279375Smsmith    if ((active < -1) || (active >= TZ_NUMLEVELS)) {
63379375Smsmith	error = EINVAL;
63479375Smsmith	goto out;
63579375Smsmith    }
63679375Smsmith
63779375Smsmith    /* set new preferred level and re-switch */
63879375Smsmith    sc->tz_requested = active;
63979375Smsmith    acpi_tz_monitor(sc);
64079375Smsmith
64179375Smsmith out:
64279375Smsmith    ACPI_UNLOCK;
64379375Smsmith    return(error);
64479375Smsmith}
64579375Smsmith
64678915Smsmith/*
64778915Smsmith * Respond to a Notify event sent to the zone.
64878915Smsmith */
64978915Smsmithstatic void
65071874Smsmithacpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
65171874Smsmith{
65278915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
65378915Smsmith
65477432Smsmith    FUNCTION_TRACE(__func__);
65570271Stakawata
65678999Smsmith    ACPI_ASSERTLOCK;
65778999Smsmith
65878915Smsmith    switch(notify) {
65978915Smsmith    case TZ_NOTIFY_TEMPERATURE:
66078999Smsmith	/* temperature change occurred */
66178999Smsmith	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
66278915Smsmith	break;
66378915Smsmith    case TZ_NOTIFY_DEVICES:
66478915Smsmith    case TZ_NOTIFY_LEVELS:
66578999Smsmith	/* zone devices/setpoints changed */
66678999Smsmith	AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
66778915Smsmith	break;
66878915Smsmith    default:
66978915Smsmith	device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify);
67078915Smsmith	break;
67171874Smsmith    }
67271874Smsmith    return_VOID;
67371874Smsmith}
67470271Stakawata
67578915Smsmith/*
67678915Smsmith * Poll the thermal zone.
67778915Smsmith */
67878915Smsmithstatic void
67978915Smsmithacpi_tz_timeout(void *arg)
68078915Smsmith{
68178915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
68278915Smsmith
68379375Smsmith    /* do we need to get the power profile settings? */
68479375Smsmith    if (sc->tz_flags & TZ_FLAG_GETPROFILE) {
68579375Smsmith	acpi_tz_powerprofile(arg);
68679375Smsmith	sc->tz_flags &= ~TZ_FLAG_GETPROFILE;
68779375Smsmith    }
68879375Smsmith
68978999Smsmith    ACPI_LOCK;
69078999Smsmith
69178915Smsmith    /* check temperature, take action */
69278999Smsmith    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc);
69378915Smsmith
69478915Smsmith    /* XXX passive cooling actions? */
69578915Smsmith
69678915Smsmith    /* re-register ourself */
69778915Smsmith    sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE);
69878999Smsmith
69978999Smsmith    ACPI_UNLOCK;
70078915Smsmith}
70179375Smsmith
70279375Smsmith/*
70379375Smsmith * System power profile may have changed; fetch and notify the
70479375Smsmith * thermal zone accordingly.
70579375Smsmith *
70679375Smsmith * Since this can be called from an arbitrary eventhandler, it needs
70779375Smsmith * to get the ACPI lock itself.
70879375Smsmith */
70979375Smsmithstatic void
71079375Smsmithacpi_tz_powerprofile(void *arg)
71179375Smsmith{
71279375Smsmith    ACPI_OBJECT_LIST		args;
71379375Smsmith    ACPI_OBJECT			obj;
71479375Smsmith    ACPI_STATUS			status;
71579375Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
71679375Smsmith
71779375Smsmith    ACPI_LOCK;
71879375Smsmith
71979375Smsmith    /* check that we haven't decided there's no _SCP method */
72079375Smsmith    if (!(sc->tz_flags & TZ_FLAG_NO_SCP)) {
72179375Smsmith
72279375Smsmith	/* call _SCP to set the new profile */
72379375Smsmith	obj.Type = ACPI_TYPE_INTEGER;
72479375Smsmith	obj.Integer.Value = (powerprofile_get_state() == POWERPROFILE_PERFORMANCE) ? 0 : 1;
72579375Smsmith	args.Count = 1;
72679375Smsmith	args.Pointer = &obj;
72779375Smsmith	if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL))) {
72879385Smsmith	    if (status != AE_NOT_FOUND)
72979385Smsmith		device_printf(sc->tz_dev, "can't evaluate %s._SCP - %s\n", acpi_name(sc->tz_handle),
73080078Smsmith			      AcpiFormatException(status));
73179375Smsmith	    sc->tz_flags |= TZ_FLAG_NO_SCP;
73279375Smsmith	} else {
73379375Smsmith	    /* we have to re-evaluate the entire zone now */
73479375Smsmith	    AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc);
73579375Smsmith	}
73679375Smsmith    }
73779375Smsmith    ACPI_UNLOCK;
73879375Smsmith}
73979375Smsmith
740