acpi_thermal.c revision 174889
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 174889 2007-12-24 16:32:14Z ume $");
30119418Sobrien
3167761Smsmith#include "opt_acpi.h"
3267761Smsmith#include <sys/param.h>
3367761Smsmith#include <sys/kernel.h>
34133624Snjl#include <sys/bus.h>
35148138Sume#include <sys/cpu.h>
3691126Smsmith#include <sys/kthread.h>
37133624Snjl#include <sys/malloc.h>
38129879Sphk#include <sys/module.h>
3967761Smsmith#include <sys/bus.h>
4091126Smsmith#include <sys/proc.h>
41128991Snjl#include <sys/reboot.h>
4279283Smsmith#include <sys/sysctl.h>
4391126Smsmith#include <sys/unistd.h>
4491640Siwasaki#include <sys/power.h>
4567761Smsmith
46148138Sume#include "cpufreq_if.h"
47148138Sume
48150003Sobrien#include <contrib/dev/acpica/acpi.h>
4967761Smsmith#include <dev/acpica/acpivar.h>
5067761Smsmith
51119529Snjl/* Hooks for the ACPI CA debugging infrastructure */
5278999Smsmith#define _COMPONENT	ACPI_THERMAL
5391126SmsmithACPI_MODULE_NAME("THERMAL")
5469744Smsmith
5571874Smsmith#define TZ_ZEROC	2732
56160657Snjl#define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), abs(((x) - TZ_ZEROC) % 10)
5767761Smsmith
58125366Snjl#define TZ_NOTIFY_TEMPERATURE	0x80 /* Temperature changed. */
59125366Snjl#define TZ_NOTIFY_LEVELS	0x81 /* Cooling levels changed. */
60125366Snjl#define TZ_NOTIFY_DEVICES	0x82 /* Device lists changed. */
61125366Snjl#define TZ_NOTIFY_CRITICAL	0xcc /* Fake notify that _CRT/_HOT reached. */
6278915Smsmith
63125335Snjl/* Check for temperature changes every 10 seconds by default */
64125335Snjl#define TZ_POLLRATE	10
6578915Smsmith
66125335Snjl/* Make sure the reported temperature is valid for this number of polls. */
67125335Snjl#define TZ_VALIDCHECKS	3
68125335Snjl
69125366Snjl/* Notify the user we will be shutting down in one more poll cycle. */
70125366Snjl#define TZ_NOTIFYCOUNT	(TZ_VALIDCHECKS - 1)
71125366Snjl
72119529Snjl/* ACPI spec defines this */
73119529Snjl#define TZ_NUMLEVELS	10
7479375Smsmithstruct acpi_tz_zone {
7578915Smsmith    int		ac[TZ_NUMLEVELS];
7678915Smsmith    ACPI_BUFFER	al[TZ_NUMLEVELS];
7778915Smsmith    int		crt;
7878915Smsmith    int		hot;
7978915Smsmith    ACPI_BUFFER	psl;
8078915Smsmith    int		psv;
8178915Smsmith    int		tc1;
8278915Smsmith    int		tc2;
8378915Smsmith    int		tsp;
8478915Smsmith    int		tzp;
8578915Smsmith};
8678915Smsmith
8767761Smsmithstruct acpi_tz_softc {
88119529Snjl    device_t			tz_dev;
89119529Snjl    ACPI_HANDLE			tz_handle;	/*Thermal zone handle*/
90119529Snjl    int				tz_temperature;	/*Current temperature*/
91119529Snjl    int				tz_active;	/*Current active cooling*/
9279375Smsmith#define TZ_ACTIVE_NONE		-1
93119529Snjl    int				tz_requested;	/*Minimum active cooling*/
94119529Snjl    int				tz_thflags;	/*Current temp-related flags*/
9579375Smsmith#define TZ_THFLAG_NONE		0
9679375Smsmith#define TZ_THFLAG_PSV		(1<<0)
9779375Smsmith#define TZ_THFLAG_HOT		(1<<2)
98148138Sume#define TZ_THFLAG_CRT		(1<<3)
9979283Smsmith    int				tz_flags;
100119529Snjl#define TZ_FLAG_NO_SCP		(1<<0)		/*No _SCP method*/
101119529Snjl#define TZ_FLAG_GETPROFILE	(1<<1)		/*Get power_profile in timeout*/
102133624Snjl#define TZ_FLAG_GETSETTINGS	(1<<2)		/*Get devs/setpoints*/
103119529Snjl    struct timespec		tz_cooling_started;
104119529Snjl					/*Current cooling starting time*/
10579283Smsmith
106119529Snjl    struct sysctl_ctx_list	tz_sysctl_ctx;
10779283Smsmith    struct sysctl_oid		*tz_sysctl_tree;
108133624Snjl    eventhandler_tag		tz_event;
109133624Snjl
110119529Snjl    struct acpi_tz_zone 	tz_zone;	/*Thermal zone parameters*/
111125335Snjl    int				tz_validchecks;
112148138Sume
113148138Sume    /* passive cooling */
114148138Sume    struct proc			*tz_cooling_proc;
115148703Sume    int				tz_cooling_proc_running;
116148138Sume    int				tz_cooling_enabled;
117148138Sume    int				tz_cooling_active;
118148138Sume    int				tz_cooling_updated;
119149201Sume    int				tz_cooling_saved_freq;
12067761Smsmith};
12167761Smsmith
122148138Sume#define CPUFREQ_MAX_LEVELS	64 /* XXX cpufreq should export this */
123148138Sume
12467761Smsmithstatic int	acpi_tz_probe(device_t dev);
12567761Smsmithstatic int	acpi_tz_attach(device_t dev);
12678915Smsmithstatic int	acpi_tz_establish(struct acpi_tz_softc *sc);
127119529Snjlstatic void	acpi_tz_monitor(void *Context);
12878915Smsmithstatic void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
12978915Smsmithstatic void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
130119529Snjlstatic void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node,
131119529Snjl				 int *data);
13279283Smsmithstatic void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
13379375Smsmithstatic int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
134148138Sumestatic int	acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS);
135160657Snjlstatic int	acpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS);
136174889Sumestatic int	acpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS);
137119529Snjlstatic void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify,
138119529Snjl				       void *context);
139133624Snjlstatic void	acpi_tz_signal(struct acpi_tz_softc *sc, int flags);
140133624Snjlstatic void	acpi_tz_timeout(struct acpi_tz_softc *sc, int flags);
14191640Siwasakistatic void	acpi_tz_power_profile(void *arg);
14291126Smsmithstatic void	acpi_tz_thread(void *arg);
143148138Sumestatic int	acpi_tz_cooling_is_available(struct acpi_tz_softc *sc);
144148138Sumestatic int	acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc);
14591126Smsmith
14667761Smsmithstatic device_method_t acpi_tz_methods[] = {
14767761Smsmith    /* Device interface */
14867761Smsmith    DEVMETHOD(device_probe,	acpi_tz_probe),
14967761Smsmith    DEVMETHOD(device_attach,	acpi_tz_attach),
15067761Smsmith
15167761Smsmith    {0, 0}
15267761Smsmith};
15367761Smsmith
15467761Smsmithstatic driver_t acpi_tz_driver = {
15567761Smsmith    "acpi_tz",
15667761Smsmith    acpi_tz_methods,
15767761Smsmith    sizeof(struct acpi_tz_softc),
15867761Smsmith};
15967761Smsmith
16089054Smsmithstatic devclass_t acpi_tz_devclass;
16167761SmsmithDRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
162128071SnjlMODULE_DEPEND(acpi_tz, acpi, 1, 1, 1);
16367761Smsmith
16479283Smsmithstatic struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
16579283Smsmithstatic struct sysctl_oid	*acpi_tz_sysctl_tree;
16679283Smsmith
167119529Snjl/* Minimum cooling run time */
168160657Snjlstatic int			acpi_tz_min_runtime;
16988420Siwasakistatic int			acpi_tz_polling_rate = TZ_POLLRATE;
170160657Snjlstatic int			acpi_tz_override;
17185699Siwasaki
172119529Snjl/* Timezone polling thread */
173119529Snjlstatic struct proc		*acpi_tz_proc;
174133624SnjlACPI_LOCK_DECL(thermal, "ACPI thermal zone");
175119529Snjl
17667761Smsmithstatic int
17767761Smsmithacpi_tz_probe(device_t dev)
17867761Smsmith{
17978999Smsmith    int		result;
180148138Sume
181119529Snjl    if (acpi_get_type(dev) == ACPI_TYPE_THERMAL && !acpi_disabled("thermal")) {
182120453Snjl	device_set_desc(dev, "Thermal Zone");
18378999Smsmith	result = -10;
184133624Snjl    } else
18578999Smsmith	result = ENXIO;
186119529Snjl    return (result);
18767761Smsmith}
18867761Smsmith
18967761Smsmithstatic int
19067761Smsmithacpi_tz_attach(device_t dev)
19167761Smsmith{
19267761Smsmith    struct acpi_tz_softc	*sc;
19379283Smsmith    struct acpi_softc		*acpi_sc;
19478915Smsmith    int				error;
19579283Smsmith    char			oidname[8];
19667761Smsmith
19796926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
19869744Smsmith
19967761Smsmith    sc = device_get_softc(dev);
20067761Smsmith    sc->tz_dev = dev;
20167761Smsmith    sc->tz_handle = acpi_get_handle(dev);
20279375Smsmith    sc->tz_requested = TZ_ACTIVE_NONE;
203135548Snjl    sc->tz_active = TZ_ACTIVE_NONE;
204135548Snjl    sc->tz_thflags = TZ_THFLAG_NONE;
205148138Sume    sc->tz_cooling_proc = NULL;
206148703Sume    sc->tz_cooling_proc_running = FALSE;
207148138Sume    sc->tz_cooling_active = FALSE;
208148138Sume    sc->tz_cooling_updated = FALSE;
20967761Smsmith
21078915Smsmith    /*
211148138Sume     * Always attempt to enable passive cooling for tz0.  Users can enable
212148138Sume     * it for other zones manually for now.
213148138Sume     *
214148138Sume     * XXX We need to test if multiple zones conflict with each other
215148138Sume     * since cpufreq currently sets all CPUs to the given frequency whereas
216148138Sume     * it's possible for different thermal zones to specify independent
217148138Sume     * settings for multiple CPUs.
218148138Sume     */
219148138Sume    sc->tz_cooling_enabled = (device_get_unit(dev) == 0);
220148138Sume
221148138Sume    /*
22278915Smsmith     * Parse the current state of the thermal zone and build control
223133624Snjl     * structures.  We don't need to worry about interference with the
224133624Snjl     * control thread since we haven't fully attached this device yet.
22578915Smsmith     */
22678915Smsmith    if ((error = acpi_tz_establish(sc)) != 0)
227133624Snjl	return (error);
228133624Snjl
22978915Smsmith    /*
23078915Smsmith     * Register for any Notify events sent to this zone.
23178915Smsmith     */
232148138Sume    AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
23378999Smsmith			     acpi_tz_notify_handler, sc);
23470271Stakawata
23571874Smsmith    /*
23679283Smsmith     * Create our sysctl nodes.
23779283Smsmith     *
23879283Smsmith     * XXX we need a mechanism for adding nodes under ACPI.
23979283Smsmith     */
24079283Smsmith    if (device_get_unit(dev) == 0) {
24179283Smsmith	acpi_sc = acpi_device_get_parent_softc(dev);
24279283Smsmith	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
24379283Smsmith	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
244119529Snjl			      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
245119529Snjl			      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
24685699Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
24785699Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
248159476Snjl		       OID_AUTO, "min_runtime", CTLFLAG_RW,
249119529Snjl		       &acpi_tz_min_runtime, 0,
250119529Snjl		       "minimum cooling run time in sec");
25188420Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
25288420Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
253159476Snjl		       OID_AUTO, "polling_rate", CTLFLAG_RW,
25488420Siwasaki		       &acpi_tz_polling_rate, 0, "monitor polling rate");
255160657Snjl	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
256160657Snjl		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
257160657Snjl		       "user_override", CTLFLAG_RW, &acpi_tz_override, 0,
258160657Snjl		       "allow override of thermal settings");
25979283Smsmith    }
26079283Smsmith    sysctl_ctx_init(&sc->tz_sysctl_ctx);
26179283Smsmith    sprintf(oidname, "tz%d", device_get_unit(dev));
26279283Smsmith    sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
263119529Snjl					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
264119529Snjl					 OID_AUTO, oidname, CTLFLAG_RD, 0, "");
265134541Speter    SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
266134541Speter		      OID_AUTO, "temperature", CTLFLAG_RD, &sc->tz_temperature,
267134961Snjl		      sizeof(sc->tz_temperature), "IK",
268134961Snjl		      "current thermal zone temperature");
26979375Smsmith    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
27079375Smsmith		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
271160657Snjl		    sc, 0, acpi_tz_active_sysctl, "I", "cooling is active");
272148138Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
273148138Sume		    OID_AUTO, "passive_cooling", CTLTYPE_INT | CTLFLAG_RW,
274160657Snjl		    sc, 0, acpi_tz_cooling_sysctl, "I",
275160657Snjl		    "enable passive (speed reduction) cooling");
276148138Sume
27779283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
27879375Smsmith		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
27979375Smsmith		   &sc->tz_thflags, 0, "thermal zone flags");
280160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
281160657Snjl		    OID_AUTO, "_PSV", CTLTYPE_INT | CTLFLAG_RW,
282160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.psv),
283160657Snjl		    acpi_tz_temp_sysctl, "IK", "passive cooling temp setpoint");
284160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
285160657Snjl		    OID_AUTO, "_HOT", CTLTYPE_INT | CTLFLAG_RW,
286160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.hot),
287160657Snjl		    acpi_tz_temp_sysctl, "IK",
288160657Snjl		    "too hot temp setpoint (suspend now)");
289160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
290160657Snjl		    OID_AUTO, "_CRT", CTLTYPE_INT | CTLFLAG_RW,
291160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.crt),
292160657Snjl		    acpi_tz_temp_sysctl, "IK",
293160657Snjl		    "critical temp setpoint (shutdown now)");
29486399Siwasaki    SYSCTL_ADD_OPAQUE(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
29586399Siwasaki		      OID_AUTO, "_ACx", CTLFLAG_RD, &sc->tz_zone.ac,
296134541Speter		      sizeof(sc->tz_zone.ac), "IK", "");
297174889Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
298174889Sume		    OID_AUTO, "_TC1", CTLTYPE_INT | CTLFLAG_RW,
299174889Sume		    sc, offsetof(struct acpi_tz_softc, tz_zone.tc1),
300174889Sume		    acpi_tz_passive_sysctl, "I",
301174889Sume		    "thermal constant 1 for passive cooling");
302174889Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
303174889Sume		    OID_AUTO, "_TC2", CTLTYPE_INT | CTLFLAG_RW,
304174889Sume		    sc, offsetof(struct acpi_tz_softc, tz_zone.tc2),
305174889Sume		    acpi_tz_passive_sysctl, "I",
306174889Sume		    "thermal constant 2 for passive cooling");
307174889Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
308174889Sume		    OID_AUTO, "_TSP", CTLTYPE_INT | CTLFLAG_RW,
309174889Sume		    sc, offsetof(struct acpi_tz_softc, tz_zone.tsp),
310174889Sume		    acpi_tz_passive_sysctl, "I",
311174889Sume		    "thermal sampling period for passive cooling");
31279283Smsmith
31379283Smsmith    /*
314148138Sume     * Create thread to service all of the thermal zones.  Register
315148138Sume     * our power profile event handler.
31679375Smsmith     */
317133624Snjl    sc->tz_event = EVENTHANDLER_REGISTER(power_profile_change,
318133624Snjl	acpi_tz_power_profile, sc, 0);
319133624Snjl    if (acpi_tz_proc == NULL) {
320172836Sjulian	error = kproc_create(acpi_tz_thread, NULL, &acpi_tz_proc,
321133624Snjl	    RFHIGHPID, 0, "acpi_thermal");
322133624Snjl	if (error != 0) {
323133624Snjl	    device_printf(sc->tz_dev, "could not create thread - %d", error);
324133624Snjl	    goto out;
325133624Snjl	}
326133624Snjl    }
32779375Smsmith
328148138Sume    /* Create a thread to handle passive cooling for each zone if enabled. */
329148138Sume    if (sc->tz_cooling_enabled) {
330148138Sume	if (acpi_tz_cooling_is_available(sc)) {
331148138Sume	    error = acpi_tz_cooling_thread_start(sc);
332148138Sume	    if (error != 0) {
333148138Sume		sc->tz_cooling_enabled = FALSE;
334148138Sume		goto out;
335148138Sume	    }
336148138Sume	} else
337148138Sume	    sc->tz_cooling_enabled = FALSE;
338148138Sume    }
339148138Sume
34079375Smsmith    /*
341133624Snjl     * Flag the event handler for a manual invocation by our timeout.
342133624Snjl     * We defer it like this so that the rest of the subsystem has time
343133624Snjl     * to come up.  Don't bother evaluating/printing the temperature at
344133624Snjl     * this point; on many systems it'll be bogus until the EC is running.
34571874Smsmith     */
346133624Snjl    sc->tz_flags |= TZ_FLAG_GETPROFILE;
34778999Smsmith
348133624Snjlout:
349133624Snjl    if (error != 0) {
350133624Snjl	EVENTHANDLER_DEREGISTER(power_profile_change, sc->tz_event);
351133624Snjl	AcpiRemoveNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
352133624Snjl	    acpi_tz_notify_handler);
353133624Snjl	sysctl_ctx_free(&sc->tz_sysctl_ctx);
35491126Smsmith    }
355119529Snjl    return_VALUE (error);
35667761Smsmith}
35770271Stakawata
35878915Smsmith/*
35978915Smsmith * Parse the current state of this thermal zone and set up to use it.
36078915Smsmith *
36178915Smsmith * Note that we may have previous state, which will have to be discarded.
36278915Smsmith */
36378915Smsmithstatic int
36478915Smsmithacpi_tz_establish(struct acpi_tz_softc *sc)
36578915Smsmith{
36678915Smsmith    ACPI_OBJECT	*obj;
36778915Smsmith    int		i;
36878915Smsmith    char	nbuf[8];
369148138Sume
37096926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
37178915Smsmith
372134909Snjl    /* Erase any existing state. */
37378915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
37479375Smsmith	if (sc->tz_zone.al[i].Pointer != NULL)
37579375Smsmith	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
37679375Smsmith    if (sc->tz_zone.psl.Pointer != NULL)
37779375Smsmith	AcpiOsFree(sc->tz_zone.psl.Pointer);
37878915Smsmith
379149449Sume    /*
380149449Sume     * XXX: We initialize only ACPI_BUFFER to avoid race condition
381149449Sume     * with passive cooling thread which refers psv, tc1, tc2 and tsp.
382149449Sume     */
383149449Sume    bzero(sc->tz_zone.ac, sizeof(sc->tz_zone.ac));
384149449Sume    bzero(sc->tz_zone.al, sizeof(sc->tz_zone.al));
385149449Sume    bzero(&sc->tz_zone.psl, sizeof(sc->tz_zone.psl));
386149449Sume
387119529Snjl    /* Evaluate thermal zone parameters. */
38878915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
38978915Smsmith	sprintf(nbuf, "_AC%d", i);
39079375Smsmith	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
39178915Smsmith	sprintf(nbuf, "_AL%d", i);
39291126Smsmith	sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
39391126Smsmith	sc->tz_zone.al[i].Pointer = NULL;
39491126Smsmith	AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
39579375Smsmith	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
39678915Smsmith	if (obj != NULL) {
397119529Snjl	    /* Should be a package containing a list of power objects */
39878915Smsmith	    if (obj->Type != ACPI_TYPE_PACKAGE) {
399119529Snjl		device_printf(sc->tz_dev, "%s has unknown type %d, rejecting\n",
40078915Smsmith			      nbuf, obj->Type);
401119529Snjl		return_VALUE (ENXIO);
40278915Smsmith	    }
40378915Smsmith	}
40478915Smsmith    }
40579375Smsmith    acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
40679375Smsmith    acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
40791126Smsmith    sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
40891126Smsmith    sc->tz_zone.psl.Pointer = NULL;
40991126Smsmith    AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
41079375Smsmith    acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
41179375Smsmith    acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
41279375Smsmith    acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
41379375Smsmith    acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
41479375Smsmith    acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
41578915Smsmith
41678915Smsmith    /*
41779283Smsmith     * Sanity-check the values we've been given.
41879283Smsmith     *
41979283Smsmith     * XXX what do we do about systems that give us the same value for
42079283Smsmith     *     more than one of these setpoints?
42179283Smsmith     */
42279375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
42379375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
42479375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
42579283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
42679375Smsmith	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
42779283Smsmith
428119529Snjl    return_VALUE (0);
42978915Smsmith}
43078915Smsmith
431133624Snjlstatic char *aclevel_string[] = {
432133624Snjl    "NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
433133624Snjl    "_AC5", "_AC6", "_AC7", "_AC8", "_AC9"
434133624Snjl};
43585699Siwasaki
43685699Siwasakistatic __inline const char *
43785699Siwasakiacpi_tz_aclevel_string(int active)
43885699Siwasaki{
439133624Snjl    if (active < -1 || active >= TZ_NUMLEVELS)
440133624Snjl	return (aclevel_string[0]);
44185699Siwasaki
442133624Snjl    return (aclevel_string[active + 1]);
44385699Siwasaki}
44485699Siwasaki
44578915Smsmith/*
446149450Sume * Get the current temperature.
447149450Sume */
448149450Sumestatic int
449149450Sumeacpi_tz_get_temperature(struct acpi_tz_softc *sc)
450149450Sume{
451149450Sume    int		temp;
452149450Sume    ACPI_STATUS	status;
453167249Snjl    static char	*tmp_name = "_TMP";
454149450Sume
455149482Skan    ACPI_FUNCTION_NAME ("acpi_tz_get_temperature");
456149482Skan
457167249Snjl    /* Evaluate the thermal zone's _TMP method. */
458167249Snjl    status = acpi_GetInteger(sc->tz_handle, tmp_name, &temp);
459149450Sume    if (ACPI_FAILURE(status)) {
460149450Sume	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
461149450Sume	    "error fetching current temperature -- %s\n",
462149450Sume	     AcpiFormatException(status));
463149450Sume	return (FALSE);
464149450Sume    }
465149450Sume
466167249Snjl    /* Check it for validity. */
467167249Snjl    acpi_tz_sanity(sc, &temp, tmp_name);
468167249Snjl    if (temp == -1)
469167249Snjl	return (FALSE);
470167249Snjl
471149450Sume    ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
472149450Sume    sc->tz_temperature = temp;
473149450Sume    return (TRUE);
474149450Sume}
475149450Sume
476149450Sume/*
47778915Smsmith * Evaluate the condition of a thermal zone, take appropriate actions.
47878915Smsmith */
47971874Smsmithstatic void
480119529Snjlacpi_tz_monitor(void *Context)
48171874Smsmith{
482119529Snjl    struct acpi_tz_softc *sc;
483119529Snjl    struct	timespec curtime;
48479283Smsmith    int		temp;
48578915Smsmith    int		i;
48679283Smsmith    int		newactive, newflags;
48770271Stakawata
48896926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
48970271Stakawata
490119529Snjl    sc = (struct acpi_tz_softc *)Context;
49188420Siwasaki
492119529Snjl    /* Get the current temperature. */
493149450Sume    if (!acpi_tz_get_temperature(sc)) {
49478915Smsmith	/* XXX disable zone? go to max cooling? */
495133624Snjl	return_VOID;
49671874Smsmith    }
497149450Sume    temp = sc->tz_temperature;
49888420Siwasaki
49978915Smsmith    /*
50078915Smsmith     * Work out what we ought to be doing right now.
50179283Smsmith     *
50279283Smsmith     * Note that the _ACx levels sort from hot to cold.
50378915Smsmith     */
50479283Smsmith    newactive = TZ_ACTIVE_NONE;
50579375Smsmith    for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
506142195Snjl	if (sc->tz_zone.ac[i] != -1 && temp >= sc->tz_zone.ac[i]) {
50779283Smsmith	    newactive = i;
50882967Siwasaki	    if (sc->tz_active != newactive) {
509119529Snjl		ACPI_VPRINT(sc->tz_dev,
510119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
511119529Snjl			    "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
512119529Snjl			    TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
51382967Siwasaki	    }
51479375Smsmith	}
51579375Smsmith    }
51679283Smsmith
51785699Siwasaki    /*
51885699Siwasaki     * We are going to get _ACx level down (colder side), but give a guaranteed
51985699Siwasaki     * minimum cooling run time if requested.
52085699Siwasaki     */
52185699Siwasaki    if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
52285699Siwasaki	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
523119529Snjl
52485699Siwasaki	getnanotime(&curtime);
52585699Siwasaki	timespecsub(&curtime, &sc->tz_cooling_started);
526119529Snjl	if (curtime.tv_sec < acpi_tz_min_runtime)
52785699Siwasaki	    newactive = sc->tz_active;
52885699Siwasaki    }
52985699Siwasaki
530119529Snjl    /* Handle user override of active mode */
531126662Snjl    if (sc->tz_requested != TZ_ACTIVE_NONE && sc->tz_requested < newactive)
53279375Smsmith	newactive = sc->tz_requested;
53378915Smsmith
53479375Smsmith    /* update temperature-related flags */
53579375Smsmith    newflags = TZ_THFLAG_NONE;
536124439Snjl    if (sc->tz_zone.psv != -1 && temp >= sc->tz_zone.psv)
53779375Smsmith	newflags |= TZ_THFLAG_PSV;
538124439Snjl    if (sc->tz_zone.hot != -1 && temp >= sc->tz_zone.hot)
53979375Smsmith	newflags |= TZ_THFLAG_HOT;
540124439Snjl    if (sc->tz_zone.crt != -1 && temp >= sc->tz_zone.crt)
54179375Smsmith	newflags |= TZ_THFLAG_CRT;
54279375Smsmith
543119529Snjl    /* If the active cooling state has changed, we have to switch things. */
54479283Smsmith    if (newactive != sc->tz_active) {
545119529Snjl	/* Turn off the cooling devices that are on, if any are */
54679283Smsmith	if (sc->tz_active != TZ_ACTIVE_NONE)
547119529Snjl	    acpi_ForeachPackageObject(
548119529Snjl		(ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
549119529Snjl		acpi_tz_switch_cooler_off, sc);
55078915Smsmith
551119529Snjl	/* Turn on cooling devices that are required, if any are */
552119529Snjl	if (newactive != TZ_ACTIVE_NONE) {
553119529Snjl	    acpi_ForeachPackageObject(
554119529Snjl		(ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
555119529Snjl		acpi_tz_switch_cooler_on, sc);
556119529Snjl	}
55786552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
558119529Snjl		    "switched from %s to %s: %d.%dC\n",
559119529Snjl		    acpi_tz_aclevel_string(sc->tz_active),
560119529Snjl		    acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
56179283Smsmith	sc->tz_active = newactive;
562142195Snjl	getnanotime(&sc->tz_cooling_started);
56379283Smsmith    }
56478915Smsmith
565119529Snjl    /* XXX (de)activate any passive cooling that may be required. */
56678915Smsmith
56778915Smsmith    /*
568125335Snjl     * If the temperature is at _HOT or _CRT, increment our event count.
569125335Snjl     * If it has occurred enough times, shutdown the system.  This is
570125335Snjl     * needed because some systems will report an invalid high temperature
571125335Snjl     * for one poll cycle.  It is suspected this is due to the embedded
572125335Snjl     * controller timing out.  A typical value is 138C for one cycle on
573125335Snjl     * a system that is otherwise 65C.
574125366Snjl     *
575125366Snjl     * If we're almost at that threshold, notify the user through devd(8).
57678915Smsmith     */
577125335Snjl    if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) {
578125366Snjl	sc->tz_validchecks++;
579125366Snjl	if (sc->tz_validchecks == TZ_VALIDCHECKS) {
580125335Snjl	    device_printf(sc->tz_dev,
581125335Snjl		"WARNING - current temperature (%d.%dC) exceeds safe limits\n",
582125335Snjl		TZ_KELVTOC(sc->tz_temperature));
583125335Snjl	    shutdown_nice(RB_POWEROFF);
584125366Snjl	} else if (sc->tz_validchecks == TZ_NOTIFYCOUNT)
585125366Snjl	    acpi_UserNotify("Thermal", sc->tz_handle, TZ_NOTIFY_CRITICAL);
586125335Snjl    } else {
587125335Snjl	sc->tz_validchecks = 0;
58878915Smsmith    }
58979375Smsmith    sc->tz_thflags = newflags;
59078915Smsmith
59171874Smsmith    return_VOID;
59271874Smsmith}
59370271Stakawata
59478915Smsmith/*
595148138Sume * Given an object, verify that it's a reference to a device of some sort,
59678915Smsmith * and try to switch it off.
59778915Smsmith */
59878915Smsmithstatic void
59978915Smsmithacpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
60078915Smsmith{
601128047Snjl    ACPI_HANDLE			cooler;
60278915Smsmith
60396926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
60478915Smsmith
605128047Snjl    cooler = acpi_GetReference(NULL, obj);
606128047Snjl    if (cooler == NULL) {
607128047Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
608128047Snjl	return_VOID;
609128047Snjl    }
610102470Siwasaki
611128047Snjl    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
612128047Snjl		     acpi_name(cooler)));
613128150Snjl    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
614119529Snjl
61579375Smsmith    return_VOID;
61678915Smsmith}
61778915Smsmith
61878915Smsmith/*
619148138Sume * Given an object, verify that it's a reference to a device of some sort,
62078915Smsmith * and try to switch it on.
62178915Smsmith *
622128047Snjl * XXX replication of off/on function code is bad.
62378915Smsmith */
62478915Smsmithstatic void
62578915Smsmithacpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
62678915Smsmith{
62778915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
62878999Smsmith    ACPI_HANDLE			cooler;
62979375Smsmith    ACPI_STATUS			status;
630148138Sume
63196926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
63278915Smsmith
633128047Snjl    cooler = acpi_GetReference(NULL, obj);
634128047Snjl    if (cooler == NULL) {
635128047Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
636128047Snjl	return_VOID;
637128047Snjl    }
638102470Siwasaki
639128047Snjl    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
640128047Snjl		     acpi_name(cooler)));
641128047Snjl    status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
642128047Snjl    if (ACPI_FAILURE(status)) {
643128047Snjl	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
644128047Snjl		    "failed to activate %s - %s\n", acpi_name(cooler),
645128047Snjl		    AcpiFormatException(status));
64678915Smsmith    }
647119529Snjl
648119529Snjl    return_VOID;
64978915Smsmith}
65078915Smsmith
65178915Smsmith/*
65278915Smsmith * Read/debug-print a parameter, default it to -1.
65378915Smsmith */
65478915Smsmithstatic void
65578915Smsmithacpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
65678915Smsmith{
65778915Smsmith
65896926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
65978915Smsmith
660126560Snjl    if (ACPI_FAILURE(acpi_GetInteger(sc->tz_handle, node, data))) {
66178915Smsmith	*data = -1;
66278915Smsmith    } else {
663119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n",
664119529Snjl			 acpi_name(sc->tz_handle), node, *data));
66578915Smsmith    }
666119529Snjl
667148138Sume    return_VOID;
66878915Smsmith}
66979283Smsmith
67079283Smsmith/*
67179283Smsmith * Sanity-check a temperature value.  Assume that setpoints
672167249Snjl * should be between 0C and 200C.
67379283Smsmith */
67479283Smsmithstatic void
67579283Smsmithacpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
67679283Smsmith{
677167249Snjl    if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 2000)) {
67879283Smsmith	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
67979283Smsmith		      what, TZ_KELVTOC(*val));
68079283Smsmith	*val = -1;
68179283Smsmith    }
68279283Smsmith}
68379375Smsmith
68479375Smsmith/*
68579375Smsmith * Respond to a sysctl on the active state node.
686148138Sume */
68779375Smsmithstatic int
68879375Smsmithacpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
68979375Smsmith{
69079375Smsmith    struct acpi_tz_softc	*sc;
69179375Smsmith    int				active;
69279375Smsmith    int		 		error;
69379375Smsmith
69479375Smsmith    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
69579375Smsmith    active = sc->tz_active;
69679375Smsmith    error = sysctl_handle_int(oidp, &active, 0, req);
69779375Smsmith
698119529Snjl    /* Error or no new value */
699119529Snjl    if (error != 0 || req->newptr == NULL)
700133624Snjl	return (error);
701133624Snjl    if (active < -1 || active >= TZ_NUMLEVELS)
702133624Snjl	return (EINVAL);
70379375Smsmith
704119529Snjl    /* Set new preferred level and re-switch */
70579375Smsmith    sc->tz_requested = active;
706133624Snjl    acpi_tz_signal(sc, 0);
707133624Snjl    return (0);
70879375Smsmith}
70979375Smsmith
710148138Sumestatic int
711148138Sumeacpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS)
712148138Sume{
713148138Sume    struct acpi_tz_softc *sc;
714148138Sume    int enabled, error;
715148138Sume
716148138Sume    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
717148138Sume    enabled = sc->tz_cooling_enabled;
718148138Sume    error = sysctl_handle_int(oidp, &enabled, 0, req);
719148138Sume
720148138Sume    /* Error or no new value */
721148138Sume    if (error != 0 || req->newptr == NULL)
722148138Sume	return (error);
723148138Sume    if (enabled != TRUE && enabled != FALSE)
724148138Sume	return (EINVAL);
725148138Sume
726148138Sume    if (enabled) {
727148138Sume	if (acpi_tz_cooling_is_available(sc))
728148138Sume	    error = acpi_tz_cooling_thread_start(sc);
729148138Sume	else
730148138Sume	    error = ENODEV;
731148138Sume	if (error)
732148138Sume	    enabled = FALSE;
733148138Sume    }
734148138Sume    sc->tz_cooling_enabled = enabled;
735148138Sume    return (error);
736148138Sume}
737148138Sume
738160657Snjlstatic int
739160657Snjlacpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS)
740160657Snjl{
741160657Snjl    struct acpi_tz_softc	*sc;
742160657Snjl    int				temp, *temp_ptr;
743160657Snjl    int		 		error;
744160657Snjl
745160657Snjl    sc = oidp->oid_arg1;
746160657Snjl    temp_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2);
747160657Snjl    temp = *temp_ptr;
748160657Snjl    error = sysctl_handle_int(oidp, &temp, 0, req);
749160657Snjl
750160657Snjl    /* Error or no new value */
751160657Snjl    if (error != 0 || req->newptr == NULL)
752160657Snjl	return (error);
753160657Snjl
754160657Snjl    /* Only allow changing settings if override is set. */
755160657Snjl    if (!acpi_tz_override)
756160657Snjl	return (EPERM);
757160657Snjl
758160657Snjl    /* Check user-supplied value for sanity. */
759160657Snjl    acpi_tz_sanity(sc, &temp, "user-supplied temp");
760160657Snjl    if (temp == -1)
761160657Snjl	return (EINVAL);
762160657Snjl
763160657Snjl    *temp_ptr = temp;
764160657Snjl    return (0);
765160657Snjl}
766160657Snjl
767174889Sumestatic int
768174889Sumeacpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS)
769174889Sume{
770174889Sume    struct acpi_tz_softc	*sc;
771174889Sume    int				val, *val_ptr;
772174889Sume    int				error;
773174889Sume
774174889Sume    sc = oidp->oid_arg1;
775174889Sume    val_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2);
776174889Sume    val = *val_ptr;
777174889Sume    error = sysctl_handle_int(oidp, &val, 0, req);
778174889Sume
779174889Sume    /* Error or no new value */
780174889Sume    if (error != 0 || req->newptr == NULL)
781174889Sume	return (error);
782174889Sume
783174889Sume    /* Only allow changing settings if override is set. */
784174889Sume    if (!acpi_tz_override)
785174889Sume	return (EPERM);
786174889Sume
787174889Sume    *val_ptr = val;
788174889Sume    return (0);
789174889Sume}
790174889Sume
79178915Smsmithstatic void
79271874Smsmithacpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
79371874Smsmith{
79478915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
79578915Smsmith
79696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
79770271Stakawata
798133624Snjl    switch (notify) {
79978915Smsmith    case TZ_NOTIFY_TEMPERATURE:
800119529Snjl	/* Temperature change occurred */
801133624Snjl	acpi_tz_signal(sc, 0);
80278915Smsmith	break;
80378915Smsmith    case TZ_NOTIFY_DEVICES:
80478915Smsmith    case TZ_NOTIFY_LEVELS:
805119529Snjl	/* Zone devices/setpoints changed */
806133624Snjl	acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS);
80778915Smsmith	break;
80878915Smsmith    default:
80986552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
810119529Snjl		    "unknown Notify event 0x%x\n", notify);
81178915Smsmith	break;
81271874Smsmith    }
813119529Snjl
814121493Snjl    acpi_UserNotify("Thermal", h, notify);
815121493Snjl
81671874Smsmith    return_VOID;
81771874Smsmith}
81870271Stakawata
819133624Snjlstatic void
820133624Snjlacpi_tz_signal(struct acpi_tz_softc *sc, int flags)
821133624Snjl{
822133624Snjl    ACPI_LOCK(thermal);
823133624Snjl    sc->tz_flags |= flags;
824133624Snjl    ACPI_UNLOCK(thermal);
825133624Snjl    wakeup(&acpi_tz_proc);
826133624Snjl}
827133624Snjl
82878915Smsmith/*
829134909Snjl * Notifies can be generated asynchronously but have also been seen to be
830134909Snjl * triggered by other thermal methods.  One system generates a notify of
831134909Snjl * 0x81 when the fan is turned on or off.  Another generates it when _SCP
832134909Snjl * is called.  To handle these situations, we check the zone via
833134909Snjl * acpi_tz_monitor() before evaluating changes to setpoints or the cooling
834134909Snjl * policy.
83578915Smsmith */
83678915Smsmithstatic void
837133624Snjlacpi_tz_timeout(struct acpi_tz_softc *sc, int flags)
83878915Smsmith{
839134909Snjl
840134909Snjl    /* Check the current temperature and take action based on it */
841134909Snjl    acpi_tz_monitor(sc);
842134909Snjl
843133624Snjl    /* If requested, get the power profile settings. */
844133624Snjl    if (flags & TZ_FLAG_GETPROFILE)
845133624Snjl	acpi_tz_power_profile(sc);
84679375Smsmith
847134909Snjl    /*
848134909Snjl     * If requested, check for new devices/setpoints.  After finding them,
849134909Snjl     * check if we need to switch fans based on the new values.
850134909Snjl     */
851134909Snjl    if (flags & TZ_FLAG_GETSETTINGS) {
852133624Snjl	acpi_tz_establish(sc);
853134909Snjl	acpi_tz_monitor(sc);
854134909Snjl    }
85578915Smsmith
85678915Smsmith    /* XXX passive cooling actions? */
85778915Smsmith}
85879375Smsmith
85979375Smsmith/*
86079375Smsmith * System power profile may have changed; fetch and notify the
86179375Smsmith * thermal zone accordingly.
86279375Smsmith *
86379375Smsmith * Since this can be called from an arbitrary eventhandler, it needs
86479375Smsmith * to get the ACPI lock itself.
86579375Smsmith */
86679375Smsmithstatic void
86791640Siwasakiacpi_tz_power_profile(void *arg)
86879375Smsmith{
86979375Smsmith    ACPI_STATUS			status;
87079375Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
87191640Siwasaki    int				state;
87279375Smsmith
87391640Siwasaki    state = power_profile_get_state();
874119529Snjl    if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
875119529Snjl	return;
87691640Siwasaki
87779375Smsmith    /* check that we haven't decided there's no _SCP method */
878119529Snjl    if ((sc->tz_flags & TZ_FLAG_NO_SCP) == 0) {
87979375Smsmith
880119529Snjl	/* Call _SCP to set the new profile */
881148138Sume	status = acpi_SetInteger(sc->tz_handle, "_SCP",
882126560Snjl	    (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1);
883119529Snjl	if (ACPI_FAILURE(status)) {
88479385Smsmith	    if (status != AE_NOT_FOUND)
885119529Snjl		ACPI_VPRINT(sc->tz_dev,
886119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
887119529Snjl			    "can't evaluate %s._SCP - %s\n",
888119529Snjl			    acpi_name(sc->tz_handle),
889119529Snjl			    AcpiFormatException(status));
89079375Smsmith	    sc->tz_flags |= TZ_FLAG_NO_SCP;
89179375Smsmith	} else {
892119529Snjl	    /* We have to re-evaluate the entire zone now */
893133624Snjl	    acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS);
89479375Smsmith	}
89579375Smsmith    }
89679375Smsmith}
89779375Smsmith
89891126Smsmith/*
89991126Smsmith * Thermal zone monitor thread.
90091126Smsmith */
90191126Smsmithstatic void
90291126Smsmithacpi_tz_thread(void *arg)
90391126Smsmith{
90491126Smsmith    device_t	*devs;
90591126Smsmith    int		devcount, i;
906133624Snjl    int		flags;
907133624Snjl    struct acpi_tz_softc **sc;
90891126Smsmith
90996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
91091126Smsmith
91191126Smsmith    devs = NULL;
91291126Smsmith    devcount = 0;
913133624Snjl    sc = NULL;
91491126Smsmith
91591126Smsmith    for (;;) {
916133624Snjl	/* If the number of devices has changed, re-evaluate. */
917133624Snjl	if (devclass_get_maxunit(acpi_tz_devclass) != devcount) {
918133624Snjl	    if (devs != NULL) {
919133624Snjl		free(devs, M_TEMP);
920133624Snjl		free(sc, M_TEMP);
921133624Snjl	    }
922133624Snjl	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
923133624Snjl	    sc = malloc(sizeof(struct acpi_tz_softc *) * devcount, M_TEMP,
924133624Snjl			M_WAITOK | M_ZERO);
925133624Snjl	    for (i = 0; i < devcount; i++)
926133624Snjl		sc[i] = device_get_softc(devs[i]);
927133624Snjl	}
92891126Smsmith
929133624Snjl	/* Check for temperature events and act on them. */
930133624Snjl	for (i = 0; i < devcount; i++) {
931133624Snjl	    ACPI_LOCK(thermal);
932133624Snjl	    flags = sc[i]->tz_flags;
933133624Snjl	    sc[i]->tz_flags &= TZ_FLAG_NO_SCP;
934133624Snjl	    ACPI_UNLOCK(thermal);
935133624Snjl	    acpi_tz_timeout(sc[i], flags);
936133624Snjl	}
93791215Smsmith
938133624Snjl	/* If more work to do, don't go to sleep yet. */
939133624Snjl	ACPI_LOCK(thermal);
940133624Snjl	for (i = 0; i < devcount; i++) {
941133624Snjl	    if (sc[i]->tz_flags & ~TZ_FLAG_NO_SCP)
942133624Snjl		break;
943133624Snjl	}
94491126Smsmith
945133624Snjl	/*
946133624Snjl	 * If we have no more work, sleep for a while, setting PDROP so that
947133624Snjl	 * the mutex will not be reacquired.  Otherwise, drop the mutex and
948133624Snjl	 * loop to handle more events.
949133624Snjl	 */
950133624Snjl	if (i == devcount)
951133624Snjl	    msleep(&acpi_tz_proc, &thermal_mutex, PZERO | PDROP, "tzpoll",
952133624Snjl		hz * acpi_tz_polling_rate);
953133624Snjl	else
954133624Snjl	    ACPI_UNLOCK(thermal);
95591126Smsmith    }
95691126Smsmith}
957148138Sume
958148138Sumestatic int
959148138Sumeacpi_tz_cpufreq_restore(struct acpi_tz_softc *sc)
960148138Sume{
961148138Sume    device_t dev;
962148138Sume    int error;
963148138Sume
964148138Sume    if (!sc->tz_cooling_updated)
965148138Sume	return (0);
966148138Sume    if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL)
967148138Sume	return (ENXIO);
968148138Sume    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
969149201Sume	"temperature %d.%dC: resuming previous clock speed (%d MHz)\n",
970149201Sume	TZ_KELVTOC(sc->tz_temperature), sc->tz_cooling_saved_freq);
971148138Sume    error = CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN);
972148138Sume    if (error == 0)
973148138Sume	sc->tz_cooling_updated = FALSE;
974148138Sume    return (error);
975148138Sume}
976148138Sume
977148138Sumestatic int
978148138Sumeacpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req)
979148138Sume{
980148138Sume    device_t dev;
981148138Sume    struct cf_level *levels;
982148138Sume    int num_levels, error, freq, desired_freq, perf, i;
983148138Sume
984148138Sume    levels = malloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT);
985148138Sume    if (levels == NULL)
986148138Sume	return (ENOMEM);
987148138Sume
988148138Sume    /*
989148138Sume     * Find the main device, cpufreq0.  We don't yet support independent
990148138Sume     * CPU frequency control on SMP.
991148138Sume     */
992148138Sume    if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) {
993148138Sume	error = ENXIO;
994148138Sume	goto out;
995148138Sume    }
996148138Sume
997148138Sume    /* Get the current frequency. */
998148138Sume    error = CPUFREQ_GET(dev, &levels[0]);
999148138Sume    if (error)
1000148138Sume	goto out;
1001148138Sume    freq = levels[0].total_set.freq;
1002148138Sume
1003148138Sume    /* Get the current available frequency levels. */
1004148138Sume    num_levels = CPUFREQ_MAX_LEVELS;
1005148138Sume    error = CPUFREQ_LEVELS(dev, levels, &num_levels);
1006148138Sume    if (error) {
1007148138Sume	if (error == E2BIG)
1008148138Sume	    printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n");
1009148138Sume	goto out;
1010148138Sume    }
1011148138Sume
1012148138Sume    /* Calculate the desired frequency as a percent of the max frequency. */
1013148138Sume    perf = 100 * freq / levels[0].total_set.freq - req;
1014148138Sume    if (perf < 0)
1015148138Sume	perf = 0;
1016148138Sume    else if (perf > 100)
1017148138Sume	perf = 100;
1018148138Sume    desired_freq = levels[0].total_set.freq * perf / 100;
1019148138Sume
1020149201Sume    if (desired_freq < freq) {
1021148138Sume	/* Find the closest available frequency, rounding down. */
1022148138Sume	for (i = 0; i < num_levels; i++)
1023148138Sume	    if (levels[i].total_set.freq <= desired_freq)
1024148138Sume		break;
1025148138Sume
1026148138Sume	/* If we didn't find a relevant setting, use the lowest. */
1027148138Sume	if (i == num_levels)
1028148138Sume	    i--;
1029148138Sume    } else {
1030149201Sume	/* If we didn't decrease frequency yet, don't increase it. */
1031149201Sume	if (!sc->tz_cooling_updated) {
1032149201Sume	    sc->tz_cooling_active = FALSE;
1033149201Sume	    goto out;
1034149201Sume	}
1035149201Sume
1036149201Sume	/* Use saved cpu frequency as maximum value. */
1037149201Sume	if (desired_freq > sc->tz_cooling_saved_freq)
1038149201Sume	    desired_freq = sc->tz_cooling_saved_freq;
1039149201Sume
1040148138Sume	/* Find the closest available frequency, rounding up. */
1041148138Sume	for (i = num_levels - 1; i >= 0; i--)
1042148138Sume	    if (levels[i].total_set.freq >= desired_freq)
1043148138Sume		break;
1044148138Sume
1045148138Sume	/* If we didn't find a relevant setting, use the highest. */
1046148138Sume	if (i == -1)
1047148138Sume	    i++;
1048148138Sume
1049149201Sume	/* If we're going to the highest frequency, restore the old setting. */
1050149201Sume	if (i == 0 || desired_freq == sc->tz_cooling_saved_freq) {
1051149201Sume	    error = acpi_tz_cpufreq_restore(sc);
1052149201Sume	    if (error == 0)
1053149201Sume		sc->tz_cooling_active = FALSE;
1054149201Sume	    goto out;
1055149201Sume	}
1056148138Sume    }
1057148138Sume
1058148138Sume    /* If we are going to a new frequency, activate it. */
1059148138Sume    if (levels[i].total_set.freq != freq) {
1060148138Sume	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
1061148138Sume	    "temperature %d.%dC: %screasing clock speed "
1062148138Sume	    "from %d MHz to %d MHz\n",
1063148138Sume	    TZ_KELVTOC(sc->tz_temperature),
1064148138Sume	    (freq > levels[i].total_set.freq) ? "de" : "in",
1065148138Sume	    freq, levels[i].total_set.freq);
1066148138Sume	error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN);
1067149201Sume	if (error == 0 && !sc->tz_cooling_updated) {
1068149201Sume	    sc->tz_cooling_saved_freq = freq;
1069148138Sume	    sc->tz_cooling_updated = TRUE;
1070149201Sume	}
1071148138Sume    }
1072148138Sume
1073148138Sumeout:
1074148138Sume    if (levels)
1075148138Sume	free(levels, M_TEMP);
1076148138Sume    return (error);
1077148138Sume}
1078148138Sume
1079148138Sume/*
1080148138Sume * Passive cooling thread; monitors current temperature according to the
1081148138Sume * cooling interval and calculates whether to scale back CPU frequency.
1082148138Sume */
1083148138Sumestatic void
1084148138Sumeacpi_tz_cooling_thread(void *arg)
1085148138Sume{
1086148138Sume    struct acpi_tz_softc *sc;
1087149450Sume    int error, perf, curr_temp, prev_temp;
1088148138Sume
1089148138Sume    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1090148138Sume
1091148138Sume    sc = (struct acpi_tz_softc *)arg;
1092148138Sume
1093149450Sume    prev_temp = sc->tz_temperature;
1094148138Sume    while (sc->tz_cooling_enabled) {
1095149450Sume	if (sc->tz_cooling_active)
1096149450Sume	    (void)acpi_tz_get_temperature(sc);
1097149450Sume	curr_temp = sc->tz_temperature;
1098149450Sume	if (curr_temp >= sc->tz_zone.psv)
1099148138Sume	    sc->tz_cooling_active = TRUE;
1100148138Sume	if (sc->tz_cooling_active) {
1101149450Sume	    perf = sc->tz_zone.tc1 * (curr_temp - prev_temp) +
1102149450Sume		   sc->tz_zone.tc2 * (curr_temp - sc->tz_zone.psv);
1103148138Sume	    perf /= 10;
1104148138Sume
1105148138Sume	    if (perf != 0) {
1106148138Sume		error = acpi_tz_cpufreq_update(sc, perf);
1107148138Sume
1108148138Sume		/*
1109148138Sume		 * If error and not simply a higher priority setting was
1110148138Sume		 * active, disable cooling.
1111148138Sume		 */
1112148138Sume		if (error != 0 && error != EPERM) {
1113148138Sume		    device_printf(sc->tz_dev,
1114148138Sume			"failed to set new freq, disabling passive cooling\n");
1115148138Sume		    sc->tz_cooling_enabled = FALSE;
1116148138Sume		}
1117148138Sume	    }
1118148138Sume	}
1119149450Sume	prev_temp = curr_temp;
1120148138Sume	tsleep(&sc->tz_cooling_proc, PZERO, "cooling",
1121148138Sume	    hz * sc->tz_zone.tsp / 10);
1122148138Sume    }
1123148138Sume    if (sc->tz_cooling_active) {
1124148138Sume	acpi_tz_cpufreq_restore(sc);
1125148138Sume	sc->tz_cooling_active = FALSE;
1126148138Sume    }
1127148703Sume    sc->tz_cooling_proc = NULL;
1128148138Sume    ACPI_LOCK(thermal);
1129148703Sume    sc->tz_cooling_proc_running = FALSE;
1130148138Sume    ACPI_UNLOCK(thermal);
1131172836Sjulian    kproc_exit(0);
1132148138Sume}
1133148138Sume
1134148138Sume/*
1135148138Sume * TODO: We ignore _PSL (list of cooling devices) since cpufreq enumerates
1136148138Sume * all CPUs for us.  However, it's possible in the future _PSL will
1137148138Sume * reference non-CPU devices so we may want to support it then.
1138148138Sume */
1139148138Sumestatic int
1140148138Sumeacpi_tz_cooling_is_available(struct acpi_tz_softc *sc)
1141148138Sume{
1142148138Sume    return (sc->tz_zone.tc1 != -1 && sc->tz_zone.tc2 != -1 &&
1143148138Sume	sc->tz_zone.tsp != -1 && sc->tz_zone.tsp != 0 &&
1144148138Sume	sc->tz_zone.psv != -1);
1145148138Sume}
1146148138Sume
1147148138Sumestatic int
1148148138Sumeacpi_tz_cooling_thread_start(struct acpi_tz_softc *sc)
1149148138Sume{
1150148138Sume    int error;
1151148138Sume    char name[16];
1152148138Sume
1153148703Sume    ACPI_LOCK(thermal);
1154148703Sume    if (sc->tz_cooling_proc_running) {
1155148703Sume	ACPI_UNLOCK(thermal);
1156148703Sume	return (0);
1157148703Sume    }
1158148703Sume    sc->tz_cooling_proc_running = TRUE;
1159148703Sume    ACPI_UNLOCK(thermal);
1160148138Sume    error = 0;
1161148138Sume    if (sc->tz_cooling_proc == NULL) {
1162148138Sume	snprintf(name, sizeof(name), "acpi_cooling%d",
1163148138Sume	    device_get_unit(sc->tz_dev));
1164172836Sjulian	error = kproc_create(acpi_tz_cooling_thread, sc,
1165148138Sume	    &sc->tz_cooling_proc, RFHIGHPID, 0, name);
1166148703Sume	if (error != 0) {
1167148138Sume	    device_printf(sc->tz_dev, "could not create thread - %d", error);
1168148703Sume	    ACPI_LOCK(thermal);
1169148703Sume	    sc->tz_cooling_proc_running = FALSE;
1170148703Sume	    ACPI_UNLOCK(thermal);
1171148703Sume	}
1172148138Sume    }
1173148138Sume    return (error);
1174148138Sume}
1175