acpi_thermal.c revision 220871
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 220871 2011-04-19 20:44:43Z mdf $");
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
48193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
49193530Sjkim#include <contrib/dev/acpica/include/accommon.h>
50193530Sjkim
5167761Smsmith#include <dev/acpica/acpivar.h>
5267761Smsmith
53119529Snjl/* Hooks for the ACPI CA debugging infrastructure */
5478999Smsmith#define _COMPONENT	ACPI_THERMAL
5591126SmsmithACPI_MODULE_NAME("THERMAL")
5669744Smsmith
5771874Smsmith#define TZ_ZEROC	2732
58160657Snjl#define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), abs(((x) - TZ_ZEROC) % 10)
5967761Smsmith
60125366Snjl#define TZ_NOTIFY_TEMPERATURE	0x80 /* Temperature changed. */
61125366Snjl#define TZ_NOTIFY_LEVELS	0x81 /* Cooling levels changed. */
62125366Snjl#define TZ_NOTIFY_DEVICES	0x82 /* Device lists changed. */
63125366Snjl#define TZ_NOTIFY_CRITICAL	0xcc /* Fake notify that _CRT/_HOT reached. */
6478915Smsmith
65125335Snjl/* Check for temperature changes every 10 seconds by default */
66125335Snjl#define TZ_POLLRATE	10
6778915Smsmith
68125335Snjl/* Make sure the reported temperature is valid for this number of polls. */
69125335Snjl#define TZ_VALIDCHECKS	3
70125335Snjl
71125366Snjl/* Notify the user we will be shutting down in one more poll cycle. */
72125366Snjl#define TZ_NOTIFYCOUNT	(TZ_VALIDCHECKS - 1)
73125366Snjl
74119529Snjl/* ACPI spec defines this */
75119529Snjl#define TZ_NUMLEVELS	10
7679375Smsmithstruct acpi_tz_zone {
7778915Smsmith    int		ac[TZ_NUMLEVELS];
7878915Smsmith    ACPI_BUFFER	al[TZ_NUMLEVELS];
7978915Smsmith    int		crt;
8078915Smsmith    int		hot;
8178915Smsmith    ACPI_BUFFER	psl;
8278915Smsmith    int		psv;
8378915Smsmith    int		tc1;
8478915Smsmith    int		tc2;
8578915Smsmith    int		tsp;
8678915Smsmith    int		tzp;
8778915Smsmith};
8878915Smsmith
8967761Smsmithstruct acpi_tz_softc {
90119529Snjl    device_t			tz_dev;
91119529Snjl    ACPI_HANDLE			tz_handle;	/*Thermal zone handle*/
92119529Snjl    int				tz_temperature;	/*Current temperature*/
93119529Snjl    int				tz_active;	/*Current active cooling*/
9479375Smsmith#define TZ_ACTIVE_NONE		-1
95178506Srpaulo#define TZ_ACTIVE_UNKNOWN	-2
96119529Snjl    int				tz_requested;	/*Minimum active cooling*/
97119529Snjl    int				tz_thflags;	/*Current temp-related flags*/
9879375Smsmith#define TZ_THFLAG_NONE		0
9979375Smsmith#define TZ_THFLAG_PSV		(1<<0)
10079375Smsmith#define TZ_THFLAG_HOT		(1<<2)
101148138Sume#define TZ_THFLAG_CRT		(1<<3)
10279283Smsmith    int				tz_flags;
103119529Snjl#define TZ_FLAG_NO_SCP		(1<<0)		/*No _SCP method*/
104119529Snjl#define TZ_FLAG_GETPROFILE	(1<<1)		/*Get power_profile in timeout*/
105133624Snjl#define TZ_FLAG_GETSETTINGS	(1<<2)		/*Get devs/setpoints*/
106119529Snjl    struct timespec		tz_cooling_started;
107119529Snjl					/*Current cooling starting time*/
10879283Smsmith
109119529Snjl    struct sysctl_ctx_list	tz_sysctl_ctx;
11079283Smsmith    struct sysctl_oid		*tz_sysctl_tree;
111133624Snjl    eventhandler_tag		tz_event;
112133624Snjl
113119529Snjl    struct acpi_tz_zone 	tz_zone;	/*Thermal zone parameters*/
114125335Snjl    int				tz_validchecks;
115148138Sume
116148138Sume    /* passive cooling */
117148138Sume    struct proc			*tz_cooling_proc;
118148703Sume    int				tz_cooling_proc_running;
119148138Sume    int				tz_cooling_enabled;
120148138Sume    int				tz_cooling_active;
121148138Sume    int				tz_cooling_updated;
122149201Sume    int				tz_cooling_saved_freq;
12367761Smsmith};
12467761Smsmith
125148138Sume#define CPUFREQ_MAX_LEVELS	64 /* XXX cpufreq should export this */
126148138Sume
12767761Smsmithstatic int	acpi_tz_probe(device_t dev);
12867761Smsmithstatic int	acpi_tz_attach(device_t dev);
12978915Smsmithstatic int	acpi_tz_establish(struct acpi_tz_softc *sc);
130119529Snjlstatic void	acpi_tz_monitor(void *Context);
13178915Smsmithstatic void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
13278915Smsmithstatic void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
133119529Snjlstatic void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node,
134119529Snjl				 int *data);
13579283Smsmithstatic void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
13679375Smsmithstatic int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
137148138Sumestatic int	acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS);
138160657Snjlstatic int	acpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS);
139174889Sumestatic int	acpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS);
140119529Snjlstatic void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify,
141119529Snjl				       void *context);
142133624Snjlstatic void	acpi_tz_signal(struct acpi_tz_softc *sc, int flags);
143133624Snjlstatic void	acpi_tz_timeout(struct acpi_tz_softc *sc, int flags);
14491640Siwasakistatic void	acpi_tz_power_profile(void *arg);
14591126Smsmithstatic void	acpi_tz_thread(void *arg);
146148138Sumestatic int	acpi_tz_cooling_is_available(struct acpi_tz_softc *sc);
147148138Sumestatic int	acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc);
14891126Smsmith
14967761Smsmithstatic device_method_t acpi_tz_methods[] = {
15067761Smsmith    /* Device interface */
15167761Smsmith    DEVMETHOD(device_probe,	acpi_tz_probe),
15267761Smsmith    DEVMETHOD(device_attach,	acpi_tz_attach),
15367761Smsmith
15467761Smsmith    {0, 0}
15567761Smsmith};
15667761Smsmith
15767761Smsmithstatic driver_t acpi_tz_driver = {
15867761Smsmith    "acpi_tz",
15967761Smsmith    acpi_tz_methods,
16067761Smsmith    sizeof(struct acpi_tz_softc),
16167761Smsmith};
16267761Smsmith
16389054Smsmithstatic devclass_t acpi_tz_devclass;
16467761SmsmithDRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
165128071SnjlMODULE_DEPEND(acpi_tz, acpi, 1, 1, 1);
16667761Smsmith
16779283Smsmithstatic struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
16879283Smsmithstatic struct sysctl_oid	*acpi_tz_sysctl_tree;
16979283Smsmith
170119529Snjl/* Minimum cooling run time */
171160657Snjlstatic int			acpi_tz_min_runtime;
17288420Siwasakistatic int			acpi_tz_polling_rate = TZ_POLLRATE;
173160657Snjlstatic int			acpi_tz_override;
17485699Siwasaki
175119529Snjl/* Timezone polling thread */
176119529Snjlstatic struct proc		*acpi_tz_proc;
177133624SnjlACPI_LOCK_DECL(thermal, "ACPI thermal zone");
178119529Snjl
179176329Sumestatic int			acpi_tz_cooling_unit = -1;
180176329Sume
18167761Smsmithstatic int
18267761Smsmithacpi_tz_probe(device_t dev)
18367761Smsmith{
18478999Smsmith    int		result;
185148138Sume
186119529Snjl    if (acpi_get_type(dev) == ACPI_TYPE_THERMAL && !acpi_disabled("thermal")) {
187120453Snjl	device_set_desc(dev, "Thermal Zone");
18878999Smsmith	result = -10;
189133624Snjl    } else
19078999Smsmith	result = ENXIO;
191119529Snjl    return (result);
19267761Smsmith}
19367761Smsmith
19467761Smsmithstatic int
19567761Smsmithacpi_tz_attach(device_t dev)
19667761Smsmith{
19767761Smsmith    struct acpi_tz_softc	*sc;
19879283Smsmith    struct acpi_softc		*acpi_sc;
19978915Smsmith    int				error;
20079283Smsmith    char			oidname[8];
20167761Smsmith
20296926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
20369744Smsmith
20467761Smsmith    sc = device_get_softc(dev);
20567761Smsmith    sc->tz_dev = dev;
20667761Smsmith    sc->tz_handle = acpi_get_handle(dev);
20779375Smsmith    sc->tz_requested = TZ_ACTIVE_NONE;
208178506Srpaulo    sc->tz_active = TZ_ACTIVE_UNKNOWN;
209135548Snjl    sc->tz_thflags = TZ_THFLAG_NONE;
210148138Sume    sc->tz_cooling_proc = NULL;
211148703Sume    sc->tz_cooling_proc_running = FALSE;
212148138Sume    sc->tz_cooling_active = FALSE;
213148138Sume    sc->tz_cooling_updated = FALSE;
214176329Sume    sc->tz_cooling_enabled = FALSE;
21567761Smsmith
21678915Smsmith    /*
21778915Smsmith     * Parse the current state of the thermal zone and build control
218133624Snjl     * structures.  We don't need to worry about interference with the
219133624Snjl     * control thread since we haven't fully attached this device yet.
22078915Smsmith     */
22178915Smsmith    if ((error = acpi_tz_establish(sc)) != 0)
222133624Snjl	return (error);
223133624Snjl
22478915Smsmith    /*
22578915Smsmith     * Register for any Notify events sent to this zone.
22678915Smsmith     */
227148138Sume    AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
22878999Smsmith			     acpi_tz_notify_handler, sc);
22970271Stakawata
23071874Smsmith    /*
23179283Smsmith     * Create our sysctl nodes.
23279283Smsmith     *
23379283Smsmith     * XXX we need a mechanism for adding nodes under ACPI.
23479283Smsmith     */
23579283Smsmith    if (device_get_unit(dev) == 0) {
23679283Smsmith	acpi_sc = acpi_device_get_parent_softc(dev);
23779283Smsmith	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
23879283Smsmith	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
239119529Snjl			      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
240119529Snjl			      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
24185699Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
24285699Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
243159476Snjl		       OID_AUTO, "min_runtime", CTLFLAG_RW,
244119529Snjl		       &acpi_tz_min_runtime, 0,
245119529Snjl		       "minimum cooling run time in sec");
24688420Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
24788420Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
248159476Snjl		       OID_AUTO, "polling_rate", CTLFLAG_RW,
24988420Siwasaki		       &acpi_tz_polling_rate, 0, "monitor polling rate");
250160657Snjl	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
251160657Snjl		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
252160657Snjl		       "user_override", CTLFLAG_RW, &acpi_tz_override, 0,
253160657Snjl		       "allow override of thermal settings");
25479283Smsmith    }
25579283Smsmith    sysctl_ctx_init(&sc->tz_sysctl_ctx);
25679283Smsmith    sprintf(oidname, "tz%d", device_get_unit(dev));
25779283Smsmith    sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
258119529Snjl					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
259119529Snjl					 OID_AUTO, oidname, CTLFLAG_RD, 0, "");
26079375Smsmith    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
261220798Smdf		    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
262220798Smdf		    &sc->tz_temperature, 0, sysctl_handle_int,
263220798Smdf		    "IK", "current thermal zone temperature");
264220798Smdf    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
26579375Smsmith		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
266160657Snjl		    sc, 0, acpi_tz_active_sysctl, "I", "cooling is active");
267148138Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
268148138Sume		    OID_AUTO, "passive_cooling", CTLTYPE_INT | CTLFLAG_RW,
269160657Snjl		    sc, 0, acpi_tz_cooling_sysctl, "I",
270160657Snjl		    "enable passive (speed reduction) cooling");
271148138Sume
27279283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
27379375Smsmith		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
27479375Smsmith		   &sc->tz_thflags, 0, "thermal zone flags");
275160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
276160657Snjl		    OID_AUTO, "_PSV", CTLTYPE_INT | CTLFLAG_RW,
277160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.psv),
278160657Snjl		    acpi_tz_temp_sysctl, "IK", "passive cooling temp setpoint");
279160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
280160657Snjl		    OID_AUTO, "_HOT", CTLTYPE_INT | CTLFLAG_RW,
281160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.hot),
282160657Snjl		    acpi_tz_temp_sysctl, "IK",
283160657Snjl		    "too hot temp setpoint (suspend now)");
284160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
285160657Snjl		    OID_AUTO, "_CRT", CTLTYPE_INT | CTLFLAG_RW,
286160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.crt),
287160657Snjl		    acpi_tz_temp_sysctl, "IK",
288160657Snjl		    "critical temp setpoint (shutdown now)");
289174889Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
290220798Smdf		    OID_AUTO, "_ACx", CTLTYPE_INT | CTLFLAG_RD,
291220871Smdf		    &sc->tz_zone.ac, sizeof(sc->tz_zone.ac),
292220871Smdf		    sysctl_handle_opaque, "IK", "");
293220798Smdf    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
294174889Sume		    OID_AUTO, "_TC1", CTLTYPE_INT | CTLFLAG_RW,
295174889Sume		    sc, offsetof(struct acpi_tz_softc, tz_zone.tc1),
296174889Sume		    acpi_tz_passive_sysctl, "I",
297174889Sume		    "thermal constant 1 for passive cooling");
298174889Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
299174889Sume		    OID_AUTO, "_TC2", CTLTYPE_INT | CTLFLAG_RW,
300174889Sume		    sc, offsetof(struct acpi_tz_softc, tz_zone.tc2),
301174889Sume		    acpi_tz_passive_sysctl, "I",
302174889Sume		    "thermal constant 2 for passive cooling");
303174889Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
304174889Sume		    OID_AUTO, "_TSP", CTLTYPE_INT | CTLFLAG_RW,
305174889Sume		    sc, offsetof(struct acpi_tz_softc, tz_zone.tsp),
306174889Sume		    acpi_tz_passive_sysctl, "I",
307174889Sume		    "thermal sampling period for passive cooling");
30879283Smsmith
30979283Smsmith    /*
310148138Sume     * Create thread to service all of the thermal zones.  Register
311148138Sume     * our power profile event handler.
31279375Smsmith     */
313133624Snjl    sc->tz_event = EVENTHANDLER_REGISTER(power_profile_change,
314133624Snjl	acpi_tz_power_profile, sc, 0);
315133624Snjl    if (acpi_tz_proc == NULL) {
316172836Sjulian	error = kproc_create(acpi_tz_thread, NULL, &acpi_tz_proc,
317133624Snjl	    RFHIGHPID, 0, "acpi_thermal");
318133624Snjl	if (error != 0) {
319133624Snjl	    device_printf(sc->tz_dev, "could not create thread - %d", error);
320133624Snjl	    goto out;
321133624Snjl	}
322133624Snjl    }
32379375Smsmith
324176329Sume    /*
325176329Sume     * Create a thread to handle passive cooling for 1st zone which
326176329Sume     * has _PSV, _TSP, _TC1 and _TC2.  Users can enable it for other
327176329Sume     * zones manually for now.
328176329Sume     *
329176329Sume     * XXX We enable only one zone to avoid multiple zones conflict
330176329Sume     * with each other since cpufreq currently sets all CPUs to the
331176329Sume     * given frequency whereas it's possible for different thermal
332176329Sume     * zones to specify independent settings for multiple CPUs.
333176329Sume     */
334176329Sume    if (acpi_tz_cooling_unit < 0 && acpi_tz_cooling_is_available(sc))
335176329Sume	sc->tz_cooling_enabled = TRUE;
336148138Sume    if (sc->tz_cooling_enabled) {
337176329Sume	error = acpi_tz_cooling_thread_start(sc);
338176329Sume	if (error != 0) {
339148138Sume	    sc->tz_cooling_enabled = FALSE;
340176329Sume	    goto out;
341176329Sume	}
342176329Sume	acpi_tz_cooling_unit = device_get_unit(dev);
343148138Sume    }
344148138Sume
34579375Smsmith    /*
346133624Snjl     * Flag the event handler for a manual invocation by our timeout.
347133624Snjl     * We defer it like this so that the rest of the subsystem has time
348133624Snjl     * to come up.  Don't bother evaluating/printing the temperature at
349133624Snjl     * this point; on many systems it'll be bogus until the EC is running.
35071874Smsmith     */
351133624Snjl    sc->tz_flags |= TZ_FLAG_GETPROFILE;
35278999Smsmith
353133624Snjlout:
354133624Snjl    if (error != 0) {
355133624Snjl	EVENTHANDLER_DEREGISTER(power_profile_change, sc->tz_event);
356133624Snjl	AcpiRemoveNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
357133624Snjl	    acpi_tz_notify_handler);
358133624Snjl	sysctl_ctx_free(&sc->tz_sysctl_ctx);
35991126Smsmith    }
360119529Snjl    return_VALUE (error);
36167761Smsmith}
36270271Stakawata
36378915Smsmith/*
36478915Smsmith * Parse the current state of this thermal zone and set up to use it.
36578915Smsmith *
36678915Smsmith * Note that we may have previous state, which will have to be discarded.
36778915Smsmith */
36878915Smsmithstatic int
36978915Smsmithacpi_tz_establish(struct acpi_tz_softc *sc)
37078915Smsmith{
37178915Smsmith    ACPI_OBJECT	*obj;
37278915Smsmith    int		i;
37378915Smsmith    char	nbuf[8];
374148138Sume
37596926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
37678915Smsmith
377134909Snjl    /* Erase any existing state. */
37878915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
37979375Smsmith	if (sc->tz_zone.al[i].Pointer != NULL)
38079375Smsmith	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
38179375Smsmith    if (sc->tz_zone.psl.Pointer != NULL)
38279375Smsmith	AcpiOsFree(sc->tz_zone.psl.Pointer);
38378915Smsmith
384149449Sume    /*
385149449Sume     * XXX: We initialize only ACPI_BUFFER to avoid race condition
386149449Sume     * with passive cooling thread which refers psv, tc1, tc2 and tsp.
387149449Sume     */
388149449Sume    bzero(sc->tz_zone.ac, sizeof(sc->tz_zone.ac));
389149449Sume    bzero(sc->tz_zone.al, sizeof(sc->tz_zone.al));
390149449Sume    bzero(&sc->tz_zone.psl, sizeof(sc->tz_zone.psl));
391149449Sume
392119529Snjl    /* Evaluate thermal zone parameters. */
39378915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
39478915Smsmith	sprintf(nbuf, "_AC%d", i);
39579375Smsmith	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
39678915Smsmith	sprintf(nbuf, "_AL%d", i);
39791126Smsmith	sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
39891126Smsmith	sc->tz_zone.al[i].Pointer = NULL;
39991126Smsmith	AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
40079375Smsmith	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
40178915Smsmith	if (obj != NULL) {
402119529Snjl	    /* Should be a package containing a list of power objects */
40378915Smsmith	    if (obj->Type != ACPI_TYPE_PACKAGE) {
404119529Snjl		device_printf(sc->tz_dev, "%s has unknown type %d, rejecting\n",
40578915Smsmith			      nbuf, obj->Type);
406119529Snjl		return_VALUE (ENXIO);
40778915Smsmith	    }
40878915Smsmith	}
40978915Smsmith    }
41079375Smsmith    acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
41179375Smsmith    acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
41291126Smsmith    sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
41391126Smsmith    sc->tz_zone.psl.Pointer = NULL;
41491126Smsmith    AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
41579375Smsmith    acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
41679375Smsmith    acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
41779375Smsmith    acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
41879375Smsmith    acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
41979375Smsmith    acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
42078915Smsmith
42178915Smsmith    /*
42279283Smsmith     * Sanity-check the values we've been given.
42379283Smsmith     *
42479283Smsmith     * XXX what do we do about systems that give us the same value for
42579283Smsmith     *     more than one of these setpoints?
42679283Smsmith     */
42779375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
42879375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
42979375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
43079283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
43179375Smsmith	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
43279283Smsmith
433119529Snjl    return_VALUE (0);
43478915Smsmith}
43578915Smsmith
436133624Snjlstatic char *aclevel_string[] = {
437133624Snjl    "NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
438133624Snjl    "_AC5", "_AC6", "_AC7", "_AC8", "_AC9"
439133624Snjl};
44085699Siwasaki
44185699Siwasakistatic __inline const char *
44285699Siwasakiacpi_tz_aclevel_string(int active)
44385699Siwasaki{
444133624Snjl    if (active < -1 || active >= TZ_NUMLEVELS)
445133624Snjl	return (aclevel_string[0]);
44685699Siwasaki
447133624Snjl    return (aclevel_string[active + 1]);
44885699Siwasaki}
44985699Siwasaki
45078915Smsmith/*
451149450Sume * Get the current temperature.
452149450Sume */
453149450Sumestatic int
454149450Sumeacpi_tz_get_temperature(struct acpi_tz_softc *sc)
455149450Sume{
456149450Sume    int		temp;
457149450Sume    ACPI_STATUS	status;
458167249Snjl    static char	*tmp_name = "_TMP";
459149450Sume
460149482Skan    ACPI_FUNCTION_NAME ("acpi_tz_get_temperature");
461149482Skan
462167249Snjl    /* Evaluate the thermal zone's _TMP method. */
463167249Snjl    status = acpi_GetInteger(sc->tz_handle, tmp_name, &temp);
464149450Sume    if (ACPI_FAILURE(status)) {
465149450Sume	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
466149450Sume	    "error fetching current temperature -- %s\n",
467149450Sume	     AcpiFormatException(status));
468149450Sume	return (FALSE);
469149450Sume    }
470149450Sume
471167249Snjl    /* Check it for validity. */
472167249Snjl    acpi_tz_sanity(sc, &temp, tmp_name);
473167249Snjl    if (temp == -1)
474167249Snjl	return (FALSE);
475167249Snjl
476149450Sume    ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
477149450Sume    sc->tz_temperature = temp;
478149450Sume    return (TRUE);
479149450Sume}
480149450Sume
481149450Sume/*
48278915Smsmith * Evaluate the condition of a thermal zone, take appropriate actions.
48378915Smsmith */
48471874Smsmithstatic void
485119529Snjlacpi_tz_monitor(void *Context)
48671874Smsmith{
487119529Snjl    struct acpi_tz_softc *sc;
488119529Snjl    struct	timespec curtime;
48979283Smsmith    int		temp;
49078915Smsmith    int		i;
49179283Smsmith    int		newactive, newflags;
49270271Stakawata
49396926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
49470271Stakawata
495119529Snjl    sc = (struct acpi_tz_softc *)Context;
49688420Siwasaki
497119529Snjl    /* Get the current temperature. */
498149450Sume    if (!acpi_tz_get_temperature(sc)) {
49978915Smsmith	/* XXX disable zone? go to max cooling? */
500133624Snjl	return_VOID;
50171874Smsmith    }
502149450Sume    temp = sc->tz_temperature;
50388420Siwasaki
50478915Smsmith    /*
50578915Smsmith     * Work out what we ought to be doing right now.
50679283Smsmith     *
50779283Smsmith     * Note that the _ACx levels sort from hot to cold.
50878915Smsmith     */
50979283Smsmith    newactive = TZ_ACTIVE_NONE;
51079375Smsmith    for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
511142195Snjl	if (sc->tz_zone.ac[i] != -1 && temp >= sc->tz_zone.ac[i]) {
51279283Smsmith	    newactive = i;
51382967Siwasaki	    if (sc->tz_active != newactive) {
514119529Snjl		ACPI_VPRINT(sc->tz_dev,
515119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
516119529Snjl			    "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i,
517119529Snjl			    TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i]));
51882967Siwasaki	    }
51979375Smsmith	}
52079375Smsmith    }
52179283Smsmith
52285699Siwasaki    /*
52385699Siwasaki     * We are going to get _ACx level down (colder side), but give a guaranteed
52485699Siwasaki     * minimum cooling run time if requested.
52585699Siwasaki     */
52685699Siwasaki    if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
527178506Srpaulo	sc->tz_active != TZ_ACTIVE_UNKNOWN &&
52885699Siwasaki	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
529119529Snjl
53085699Siwasaki	getnanotime(&curtime);
53185699Siwasaki	timespecsub(&curtime, &sc->tz_cooling_started);
532119529Snjl	if (curtime.tv_sec < acpi_tz_min_runtime)
53385699Siwasaki	    newactive = sc->tz_active;
53485699Siwasaki    }
53585699Siwasaki
536119529Snjl    /* Handle user override of active mode */
537176327Srpaulo    if (sc->tz_requested != TZ_ACTIVE_NONE && (newactive == TZ_ACTIVE_NONE
538176327Srpaulo        || sc->tz_requested < newactive))
53979375Smsmith	newactive = sc->tz_requested;
54078915Smsmith
54179375Smsmith    /* update temperature-related flags */
54279375Smsmith    newflags = TZ_THFLAG_NONE;
543124439Snjl    if (sc->tz_zone.psv != -1 && temp >= sc->tz_zone.psv)
54479375Smsmith	newflags |= TZ_THFLAG_PSV;
545124439Snjl    if (sc->tz_zone.hot != -1 && temp >= sc->tz_zone.hot)
54679375Smsmith	newflags |= TZ_THFLAG_HOT;
547124439Snjl    if (sc->tz_zone.crt != -1 && temp >= sc->tz_zone.crt)
54879375Smsmith	newflags |= TZ_THFLAG_CRT;
54979375Smsmith
550119529Snjl    /* If the active cooling state has changed, we have to switch things. */
551178506Srpaulo    if (sc->tz_active == TZ_ACTIVE_UNKNOWN) {
552178506Srpaulo	/*
553178506Srpaulo	 * We don't know which cooling device is on or off,
554178506Srpaulo	 * so stop them all, because we now know which
555178506Srpaulo	 * should be on (if any).
556178506Srpaulo	 */
557178506Srpaulo	for (i = 0; i < TZ_NUMLEVELS; i++) {
558178506Srpaulo	    if (sc->tz_zone.al[i].Pointer != NULL) {
559178506Srpaulo		acpi_ForeachPackageObject(
560178506Srpaulo		    (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
561178506Srpaulo		    acpi_tz_switch_cooler_off, sc);
562178506Srpaulo	    }
563178506Srpaulo	}
564178506Srpaulo	/* now we know that all devices are off */
565178506Srpaulo	sc->tz_active = TZ_ACTIVE_NONE;
566178506Srpaulo    }
567178506Srpaulo
56879283Smsmith    if (newactive != sc->tz_active) {
569119529Snjl	/* Turn off the cooling devices that are on, if any are */
57079283Smsmith	if (sc->tz_active != TZ_ACTIVE_NONE)
571119529Snjl	    acpi_ForeachPackageObject(
572119529Snjl		(ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer,
573119529Snjl		acpi_tz_switch_cooler_off, sc);
57478915Smsmith
575119529Snjl	/* Turn on cooling devices that are required, if any are */
576119529Snjl	if (newactive != TZ_ACTIVE_NONE) {
577119529Snjl	    acpi_ForeachPackageObject(
578119529Snjl		(ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer,
579119529Snjl		acpi_tz_switch_cooler_on, sc);
580119529Snjl	}
58186552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
582119529Snjl		    "switched from %s to %s: %d.%dC\n",
583119529Snjl		    acpi_tz_aclevel_string(sc->tz_active),
584119529Snjl		    acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
58579283Smsmith	sc->tz_active = newactive;
586142195Snjl	getnanotime(&sc->tz_cooling_started);
58779283Smsmith    }
58878915Smsmith
589119529Snjl    /* XXX (de)activate any passive cooling that may be required. */
59078915Smsmith
59178915Smsmith    /*
592125335Snjl     * If the temperature is at _HOT or _CRT, increment our event count.
593125335Snjl     * If it has occurred enough times, shutdown the system.  This is
594125335Snjl     * needed because some systems will report an invalid high temperature
595125335Snjl     * for one poll cycle.  It is suspected this is due to the embedded
596125335Snjl     * controller timing out.  A typical value is 138C for one cycle on
597125335Snjl     * a system that is otherwise 65C.
598125366Snjl     *
599125366Snjl     * If we're almost at that threshold, notify the user through devd(8).
60078915Smsmith     */
601125335Snjl    if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) {
602125366Snjl	sc->tz_validchecks++;
603125366Snjl	if (sc->tz_validchecks == TZ_VALIDCHECKS) {
604125335Snjl	    device_printf(sc->tz_dev,
605125335Snjl		"WARNING - current temperature (%d.%dC) exceeds safe limits\n",
606125335Snjl		TZ_KELVTOC(sc->tz_temperature));
607125335Snjl	    shutdown_nice(RB_POWEROFF);
608125366Snjl	} else if (sc->tz_validchecks == TZ_NOTIFYCOUNT)
609125366Snjl	    acpi_UserNotify("Thermal", sc->tz_handle, TZ_NOTIFY_CRITICAL);
610125335Snjl    } else {
611125335Snjl	sc->tz_validchecks = 0;
61278915Smsmith    }
61379375Smsmith    sc->tz_thflags = newflags;
61478915Smsmith
61571874Smsmith    return_VOID;
61671874Smsmith}
61770271Stakawata
61878915Smsmith/*
619148138Sume * Given an object, verify that it's a reference to a device of some sort,
62078915Smsmith * and try to switch it off.
62178915Smsmith */
62278915Smsmithstatic void
62378915Smsmithacpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
62478915Smsmith{
625128047Snjl    ACPI_HANDLE			cooler;
62678915Smsmith
62796926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
62878915Smsmith
629128047Snjl    cooler = acpi_GetReference(NULL, obj);
630128047Snjl    if (cooler == NULL) {
631128047Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
632128047Snjl	return_VOID;
633128047Snjl    }
634102470Siwasaki
635128047Snjl    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
636128047Snjl		     acpi_name(cooler)));
637128150Snjl    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
638119529Snjl
63979375Smsmith    return_VOID;
64078915Smsmith}
64178915Smsmith
64278915Smsmith/*
643148138Sume * Given an object, verify that it's a reference to a device of some sort,
64478915Smsmith * and try to switch it on.
64578915Smsmith *
646128047Snjl * XXX replication of off/on function code is bad.
64778915Smsmith */
64878915Smsmithstatic void
64978915Smsmithacpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
65078915Smsmith{
65178915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
65278999Smsmith    ACPI_HANDLE			cooler;
65379375Smsmith    ACPI_STATUS			status;
654148138Sume
65596926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
65678915Smsmith
657128047Snjl    cooler = acpi_GetReference(NULL, obj);
658128047Snjl    if (cooler == NULL) {
659128047Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
660128047Snjl	return_VOID;
661128047Snjl    }
662102470Siwasaki
663128047Snjl    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
664128047Snjl		     acpi_name(cooler)));
665128047Snjl    status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
666128047Snjl    if (ACPI_FAILURE(status)) {
667128047Snjl	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
668128047Snjl		    "failed to activate %s - %s\n", acpi_name(cooler),
669128047Snjl		    AcpiFormatException(status));
67078915Smsmith    }
671119529Snjl
672119529Snjl    return_VOID;
67378915Smsmith}
67478915Smsmith
67578915Smsmith/*
67678915Smsmith * Read/debug-print a parameter, default it to -1.
67778915Smsmith */
67878915Smsmithstatic void
67978915Smsmithacpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
68078915Smsmith{
68178915Smsmith
68296926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
68378915Smsmith
684126560Snjl    if (ACPI_FAILURE(acpi_GetInteger(sc->tz_handle, node, data))) {
68578915Smsmith	*data = -1;
68678915Smsmith    } else {
687119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n",
688119529Snjl			 acpi_name(sc->tz_handle), node, *data));
68978915Smsmith    }
690119529Snjl
691148138Sume    return_VOID;
69278915Smsmith}
69379283Smsmith
69479283Smsmith/*
69579283Smsmith * Sanity-check a temperature value.  Assume that setpoints
696167249Snjl * should be between 0C and 200C.
69779283Smsmith */
69879283Smsmithstatic void
69979283Smsmithacpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
70079283Smsmith{
701167249Snjl    if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 2000)) {
70279283Smsmith	device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
70379283Smsmith		      what, TZ_KELVTOC(*val));
70479283Smsmith	*val = -1;
70579283Smsmith    }
70679283Smsmith}
70779375Smsmith
70879375Smsmith/*
70979375Smsmith * Respond to a sysctl on the active state node.
710148138Sume */
71179375Smsmithstatic int
71279375Smsmithacpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
71379375Smsmith{
71479375Smsmith    struct acpi_tz_softc	*sc;
71579375Smsmith    int				active;
71679375Smsmith    int		 		error;
71779375Smsmith
71879375Smsmith    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
71979375Smsmith    active = sc->tz_active;
72079375Smsmith    error = sysctl_handle_int(oidp, &active, 0, req);
72179375Smsmith
722119529Snjl    /* Error or no new value */
723119529Snjl    if (error != 0 || req->newptr == NULL)
724133624Snjl	return (error);
725133624Snjl    if (active < -1 || active >= TZ_NUMLEVELS)
726133624Snjl	return (EINVAL);
72779375Smsmith
728119529Snjl    /* Set new preferred level and re-switch */
72979375Smsmith    sc->tz_requested = active;
730133624Snjl    acpi_tz_signal(sc, 0);
731133624Snjl    return (0);
73279375Smsmith}
73379375Smsmith
734148138Sumestatic int
735148138Sumeacpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS)
736148138Sume{
737148138Sume    struct acpi_tz_softc *sc;
738148138Sume    int enabled, error;
739148138Sume
740148138Sume    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
741148138Sume    enabled = sc->tz_cooling_enabled;
742148138Sume    error = sysctl_handle_int(oidp, &enabled, 0, req);
743148138Sume
744148138Sume    /* Error or no new value */
745148138Sume    if (error != 0 || req->newptr == NULL)
746148138Sume	return (error);
747148138Sume    if (enabled != TRUE && enabled != FALSE)
748148138Sume	return (EINVAL);
749148138Sume
750148138Sume    if (enabled) {
751148138Sume	if (acpi_tz_cooling_is_available(sc))
752148138Sume	    error = acpi_tz_cooling_thread_start(sc);
753148138Sume	else
754148138Sume	    error = ENODEV;
755148138Sume	if (error)
756148138Sume	    enabled = FALSE;
757148138Sume    }
758148138Sume    sc->tz_cooling_enabled = enabled;
759148138Sume    return (error);
760148138Sume}
761148138Sume
762160657Snjlstatic int
763160657Snjlacpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS)
764160657Snjl{
765160657Snjl    struct acpi_tz_softc	*sc;
766160657Snjl    int				temp, *temp_ptr;
767160657Snjl    int		 		error;
768160657Snjl
769160657Snjl    sc = oidp->oid_arg1;
770160657Snjl    temp_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2);
771160657Snjl    temp = *temp_ptr;
772160657Snjl    error = sysctl_handle_int(oidp, &temp, 0, req);
773160657Snjl
774160657Snjl    /* Error or no new value */
775160657Snjl    if (error != 0 || req->newptr == NULL)
776160657Snjl	return (error);
777160657Snjl
778160657Snjl    /* Only allow changing settings if override is set. */
779160657Snjl    if (!acpi_tz_override)
780160657Snjl	return (EPERM);
781160657Snjl
782160657Snjl    /* Check user-supplied value for sanity. */
783160657Snjl    acpi_tz_sanity(sc, &temp, "user-supplied temp");
784160657Snjl    if (temp == -1)
785160657Snjl	return (EINVAL);
786160657Snjl
787160657Snjl    *temp_ptr = temp;
788160657Snjl    return (0);
789160657Snjl}
790160657Snjl
791174889Sumestatic int
792174889Sumeacpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS)
793174889Sume{
794174889Sume    struct acpi_tz_softc	*sc;
795174889Sume    int				val, *val_ptr;
796174889Sume    int				error;
797174889Sume
798174889Sume    sc = oidp->oid_arg1;
799174889Sume    val_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2);
800174889Sume    val = *val_ptr;
801174889Sume    error = sysctl_handle_int(oidp, &val, 0, req);
802174889Sume
803174889Sume    /* Error or no new value */
804174889Sume    if (error != 0 || req->newptr == NULL)
805174889Sume	return (error);
806174889Sume
807174889Sume    /* Only allow changing settings if override is set. */
808174889Sume    if (!acpi_tz_override)
809174889Sume	return (EPERM);
810174889Sume
811174889Sume    *val_ptr = val;
812174889Sume    return (0);
813174889Sume}
814174889Sume
81578915Smsmithstatic void
81671874Smsmithacpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
81771874Smsmith{
81878915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
81978915Smsmith
82096926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
82170271Stakawata
822133624Snjl    switch (notify) {
82378915Smsmith    case TZ_NOTIFY_TEMPERATURE:
824119529Snjl	/* Temperature change occurred */
825133624Snjl	acpi_tz_signal(sc, 0);
82678915Smsmith	break;
82778915Smsmith    case TZ_NOTIFY_DEVICES:
82878915Smsmith    case TZ_NOTIFY_LEVELS:
829119529Snjl	/* Zone devices/setpoints changed */
830133624Snjl	acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS);
83178915Smsmith	break;
83278915Smsmith    default:
83386552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
834119529Snjl		    "unknown Notify event 0x%x\n", notify);
83578915Smsmith	break;
83671874Smsmith    }
837119529Snjl
838121493Snjl    acpi_UserNotify("Thermal", h, notify);
839121493Snjl
84071874Smsmith    return_VOID;
84171874Smsmith}
84270271Stakawata
843133624Snjlstatic void
844133624Snjlacpi_tz_signal(struct acpi_tz_softc *sc, int flags)
845133624Snjl{
846133624Snjl    ACPI_LOCK(thermal);
847133624Snjl    sc->tz_flags |= flags;
848133624Snjl    ACPI_UNLOCK(thermal);
849133624Snjl    wakeup(&acpi_tz_proc);
850133624Snjl}
851133624Snjl
85278915Smsmith/*
853134909Snjl * Notifies can be generated asynchronously but have also been seen to be
854134909Snjl * triggered by other thermal methods.  One system generates a notify of
855134909Snjl * 0x81 when the fan is turned on or off.  Another generates it when _SCP
856134909Snjl * is called.  To handle these situations, we check the zone via
857134909Snjl * acpi_tz_monitor() before evaluating changes to setpoints or the cooling
858134909Snjl * policy.
85978915Smsmith */
86078915Smsmithstatic void
861133624Snjlacpi_tz_timeout(struct acpi_tz_softc *sc, int flags)
86278915Smsmith{
863134909Snjl
864134909Snjl    /* Check the current temperature and take action based on it */
865134909Snjl    acpi_tz_monitor(sc);
866134909Snjl
867133624Snjl    /* If requested, get the power profile settings. */
868133624Snjl    if (flags & TZ_FLAG_GETPROFILE)
869133624Snjl	acpi_tz_power_profile(sc);
87079375Smsmith
871134909Snjl    /*
872134909Snjl     * If requested, check for new devices/setpoints.  After finding them,
873134909Snjl     * check if we need to switch fans based on the new values.
874134909Snjl     */
875134909Snjl    if (flags & TZ_FLAG_GETSETTINGS) {
876133624Snjl	acpi_tz_establish(sc);
877134909Snjl	acpi_tz_monitor(sc);
878134909Snjl    }
87978915Smsmith
88078915Smsmith    /* XXX passive cooling actions? */
88178915Smsmith}
88279375Smsmith
88379375Smsmith/*
88479375Smsmith * System power profile may have changed; fetch and notify the
88579375Smsmith * thermal zone accordingly.
88679375Smsmith *
88779375Smsmith * Since this can be called from an arbitrary eventhandler, it needs
88879375Smsmith * to get the ACPI lock itself.
88979375Smsmith */
89079375Smsmithstatic void
89191640Siwasakiacpi_tz_power_profile(void *arg)
89279375Smsmith{
89379375Smsmith    ACPI_STATUS			status;
89479375Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
89591640Siwasaki    int				state;
89679375Smsmith
89791640Siwasaki    state = power_profile_get_state();
898119529Snjl    if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
899119529Snjl	return;
90091640Siwasaki
90179375Smsmith    /* check that we haven't decided there's no _SCP method */
902119529Snjl    if ((sc->tz_flags & TZ_FLAG_NO_SCP) == 0) {
90379375Smsmith
904119529Snjl	/* Call _SCP to set the new profile */
905148138Sume	status = acpi_SetInteger(sc->tz_handle, "_SCP",
906126560Snjl	    (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1);
907119529Snjl	if (ACPI_FAILURE(status)) {
90879385Smsmith	    if (status != AE_NOT_FOUND)
909119529Snjl		ACPI_VPRINT(sc->tz_dev,
910119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
911119529Snjl			    "can't evaluate %s._SCP - %s\n",
912119529Snjl			    acpi_name(sc->tz_handle),
913119529Snjl			    AcpiFormatException(status));
91479375Smsmith	    sc->tz_flags |= TZ_FLAG_NO_SCP;
91579375Smsmith	} else {
916119529Snjl	    /* We have to re-evaluate the entire zone now */
917133624Snjl	    acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS);
91879375Smsmith	}
91979375Smsmith    }
92079375Smsmith}
92179375Smsmith
92291126Smsmith/*
92391126Smsmith * Thermal zone monitor thread.
92491126Smsmith */
92591126Smsmithstatic void
92691126Smsmithacpi_tz_thread(void *arg)
92791126Smsmith{
92891126Smsmith    device_t	*devs;
92991126Smsmith    int		devcount, i;
930133624Snjl    int		flags;
931133624Snjl    struct acpi_tz_softc **sc;
93291126Smsmith
93396926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
93491126Smsmith
93591126Smsmith    devs = NULL;
93691126Smsmith    devcount = 0;
937133624Snjl    sc = NULL;
93891126Smsmith
93991126Smsmith    for (;;) {
940133624Snjl	/* If the number of devices has changed, re-evaluate. */
941175014Sjhb	if (devclass_get_count(acpi_tz_devclass) != devcount) {
942133624Snjl	    if (devs != NULL) {
943133624Snjl		free(devs, M_TEMP);
944133624Snjl		free(sc, M_TEMP);
945133624Snjl	    }
946133624Snjl	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
947133624Snjl	    sc = malloc(sizeof(struct acpi_tz_softc *) * devcount, M_TEMP,
948133624Snjl			M_WAITOK | M_ZERO);
949133624Snjl	    for (i = 0; i < devcount; i++)
950133624Snjl		sc[i] = device_get_softc(devs[i]);
951133624Snjl	}
95291126Smsmith
953133624Snjl	/* Check for temperature events and act on them. */
954133624Snjl	for (i = 0; i < devcount; i++) {
955133624Snjl	    ACPI_LOCK(thermal);
956133624Snjl	    flags = sc[i]->tz_flags;
957133624Snjl	    sc[i]->tz_flags &= TZ_FLAG_NO_SCP;
958133624Snjl	    ACPI_UNLOCK(thermal);
959133624Snjl	    acpi_tz_timeout(sc[i], flags);
960133624Snjl	}
96191215Smsmith
962133624Snjl	/* If more work to do, don't go to sleep yet. */
963133624Snjl	ACPI_LOCK(thermal);
964133624Snjl	for (i = 0; i < devcount; i++) {
965133624Snjl	    if (sc[i]->tz_flags & ~TZ_FLAG_NO_SCP)
966133624Snjl		break;
967133624Snjl	}
96891126Smsmith
969133624Snjl	/*
970133624Snjl	 * If we have no more work, sleep for a while, setting PDROP so that
971133624Snjl	 * the mutex will not be reacquired.  Otherwise, drop the mutex and
972133624Snjl	 * loop to handle more events.
973133624Snjl	 */
974133624Snjl	if (i == devcount)
975133624Snjl	    msleep(&acpi_tz_proc, &thermal_mutex, PZERO | PDROP, "tzpoll",
976133624Snjl		hz * acpi_tz_polling_rate);
977133624Snjl	else
978133624Snjl	    ACPI_UNLOCK(thermal);
97991126Smsmith    }
98091126Smsmith}
981148138Sume
982148138Sumestatic int
983148138Sumeacpi_tz_cpufreq_restore(struct acpi_tz_softc *sc)
984148138Sume{
985148138Sume    device_t dev;
986148138Sume    int error;
987148138Sume
988148138Sume    if (!sc->tz_cooling_updated)
989148138Sume	return (0);
990148138Sume    if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL)
991148138Sume	return (ENXIO);
992148138Sume    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
993149201Sume	"temperature %d.%dC: resuming previous clock speed (%d MHz)\n",
994149201Sume	TZ_KELVTOC(sc->tz_temperature), sc->tz_cooling_saved_freq);
995148138Sume    error = CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN);
996148138Sume    if (error == 0)
997148138Sume	sc->tz_cooling_updated = FALSE;
998148138Sume    return (error);
999148138Sume}
1000148138Sume
1001148138Sumestatic int
1002148138Sumeacpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req)
1003148138Sume{
1004148138Sume    device_t dev;
1005148138Sume    struct cf_level *levels;
1006148138Sume    int num_levels, error, freq, desired_freq, perf, i;
1007148138Sume
1008148138Sume    levels = malloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT);
1009148138Sume    if (levels == NULL)
1010148138Sume	return (ENOMEM);
1011148138Sume
1012148138Sume    /*
1013148138Sume     * Find the main device, cpufreq0.  We don't yet support independent
1014148138Sume     * CPU frequency control on SMP.
1015148138Sume     */
1016148138Sume    if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) {
1017148138Sume	error = ENXIO;
1018148138Sume	goto out;
1019148138Sume    }
1020148138Sume
1021148138Sume    /* Get the current frequency. */
1022148138Sume    error = CPUFREQ_GET(dev, &levels[0]);
1023148138Sume    if (error)
1024148138Sume	goto out;
1025148138Sume    freq = levels[0].total_set.freq;
1026148138Sume
1027148138Sume    /* Get the current available frequency levels. */
1028148138Sume    num_levels = CPUFREQ_MAX_LEVELS;
1029148138Sume    error = CPUFREQ_LEVELS(dev, levels, &num_levels);
1030148138Sume    if (error) {
1031148138Sume	if (error == E2BIG)
1032148138Sume	    printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n");
1033148138Sume	goto out;
1034148138Sume    }
1035148138Sume
1036148138Sume    /* Calculate the desired frequency as a percent of the max frequency. */
1037148138Sume    perf = 100 * freq / levels[0].total_set.freq - req;
1038148138Sume    if (perf < 0)
1039148138Sume	perf = 0;
1040148138Sume    else if (perf > 100)
1041148138Sume	perf = 100;
1042148138Sume    desired_freq = levels[0].total_set.freq * perf / 100;
1043148138Sume
1044149201Sume    if (desired_freq < freq) {
1045148138Sume	/* Find the closest available frequency, rounding down. */
1046148138Sume	for (i = 0; i < num_levels; i++)
1047148138Sume	    if (levels[i].total_set.freq <= desired_freq)
1048148138Sume		break;
1049148138Sume
1050148138Sume	/* If we didn't find a relevant setting, use the lowest. */
1051148138Sume	if (i == num_levels)
1052148138Sume	    i--;
1053148138Sume    } else {
1054149201Sume	/* If we didn't decrease frequency yet, don't increase it. */
1055149201Sume	if (!sc->tz_cooling_updated) {
1056149201Sume	    sc->tz_cooling_active = FALSE;
1057149201Sume	    goto out;
1058149201Sume	}
1059149201Sume
1060149201Sume	/* Use saved cpu frequency as maximum value. */
1061149201Sume	if (desired_freq > sc->tz_cooling_saved_freq)
1062149201Sume	    desired_freq = sc->tz_cooling_saved_freq;
1063149201Sume
1064148138Sume	/* Find the closest available frequency, rounding up. */
1065148138Sume	for (i = num_levels - 1; i >= 0; i--)
1066148138Sume	    if (levels[i].total_set.freq >= desired_freq)
1067148138Sume		break;
1068148138Sume
1069148138Sume	/* If we didn't find a relevant setting, use the highest. */
1070148138Sume	if (i == -1)
1071148138Sume	    i++;
1072148138Sume
1073149201Sume	/* If we're going to the highest frequency, restore the old setting. */
1074149201Sume	if (i == 0 || desired_freq == sc->tz_cooling_saved_freq) {
1075149201Sume	    error = acpi_tz_cpufreq_restore(sc);
1076149201Sume	    if (error == 0)
1077149201Sume		sc->tz_cooling_active = FALSE;
1078149201Sume	    goto out;
1079149201Sume	}
1080148138Sume    }
1081148138Sume
1082148138Sume    /* If we are going to a new frequency, activate it. */
1083148138Sume    if (levels[i].total_set.freq != freq) {
1084148138Sume	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
1085148138Sume	    "temperature %d.%dC: %screasing clock speed "
1086148138Sume	    "from %d MHz to %d MHz\n",
1087148138Sume	    TZ_KELVTOC(sc->tz_temperature),
1088148138Sume	    (freq > levels[i].total_set.freq) ? "de" : "in",
1089148138Sume	    freq, levels[i].total_set.freq);
1090148138Sume	error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN);
1091149201Sume	if (error == 0 && !sc->tz_cooling_updated) {
1092149201Sume	    sc->tz_cooling_saved_freq = freq;
1093148138Sume	    sc->tz_cooling_updated = TRUE;
1094149201Sume	}
1095148138Sume    }
1096148138Sume
1097148138Sumeout:
1098148138Sume    if (levels)
1099148138Sume	free(levels, M_TEMP);
1100148138Sume    return (error);
1101148138Sume}
1102148138Sume
1103148138Sume/*
1104148138Sume * Passive cooling thread; monitors current temperature according to the
1105148138Sume * cooling interval and calculates whether to scale back CPU frequency.
1106148138Sume */
1107148138Sumestatic void
1108148138Sumeacpi_tz_cooling_thread(void *arg)
1109148138Sume{
1110148138Sume    struct acpi_tz_softc *sc;
1111149450Sume    int error, perf, curr_temp, prev_temp;
1112148138Sume
1113148138Sume    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1114148138Sume
1115148138Sume    sc = (struct acpi_tz_softc *)arg;
1116148138Sume
1117149450Sume    prev_temp = sc->tz_temperature;
1118148138Sume    while (sc->tz_cooling_enabled) {
1119149450Sume	if (sc->tz_cooling_active)
1120149450Sume	    (void)acpi_tz_get_temperature(sc);
1121149450Sume	curr_temp = sc->tz_temperature;
1122149450Sume	if (curr_temp >= sc->tz_zone.psv)
1123148138Sume	    sc->tz_cooling_active = TRUE;
1124148138Sume	if (sc->tz_cooling_active) {
1125149450Sume	    perf = sc->tz_zone.tc1 * (curr_temp - prev_temp) +
1126149450Sume		   sc->tz_zone.tc2 * (curr_temp - sc->tz_zone.psv);
1127148138Sume	    perf /= 10;
1128148138Sume
1129148138Sume	    if (perf != 0) {
1130148138Sume		error = acpi_tz_cpufreq_update(sc, perf);
1131148138Sume
1132148138Sume		/*
1133148138Sume		 * If error and not simply a higher priority setting was
1134148138Sume		 * active, disable cooling.
1135148138Sume		 */
1136148138Sume		if (error != 0 && error != EPERM) {
1137148138Sume		    device_printf(sc->tz_dev,
1138148138Sume			"failed to set new freq, disabling passive cooling\n");
1139148138Sume		    sc->tz_cooling_enabled = FALSE;
1140148138Sume		}
1141148138Sume	    }
1142148138Sume	}
1143149450Sume	prev_temp = curr_temp;
1144148138Sume	tsleep(&sc->tz_cooling_proc, PZERO, "cooling",
1145148138Sume	    hz * sc->tz_zone.tsp / 10);
1146148138Sume    }
1147148138Sume    if (sc->tz_cooling_active) {
1148148138Sume	acpi_tz_cpufreq_restore(sc);
1149148138Sume	sc->tz_cooling_active = FALSE;
1150148138Sume    }
1151148703Sume    sc->tz_cooling_proc = NULL;
1152148138Sume    ACPI_LOCK(thermal);
1153148703Sume    sc->tz_cooling_proc_running = FALSE;
1154148138Sume    ACPI_UNLOCK(thermal);
1155172836Sjulian    kproc_exit(0);
1156148138Sume}
1157148138Sume
1158148138Sume/*
1159148138Sume * TODO: We ignore _PSL (list of cooling devices) since cpufreq enumerates
1160148138Sume * all CPUs for us.  However, it's possible in the future _PSL will
1161148138Sume * reference non-CPU devices so we may want to support it then.
1162148138Sume */
1163148138Sumestatic int
1164148138Sumeacpi_tz_cooling_is_available(struct acpi_tz_softc *sc)
1165148138Sume{
1166148138Sume    return (sc->tz_zone.tc1 != -1 && sc->tz_zone.tc2 != -1 &&
1167148138Sume	sc->tz_zone.tsp != -1 && sc->tz_zone.tsp != 0 &&
1168148138Sume	sc->tz_zone.psv != -1);
1169148138Sume}
1170148138Sume
1171148138Sumestatic int
1172148138Sumeacpi_tz_cooling_thread_start(struct acpi_tz_softc *sc)
1173148138Sume{
1174148138Sume    int error;
1175148138Sume
1176148703Sume    ACPI_LOCK(thermal);
1177148703Sume    if (sc->tz_cooling_proc_running) {
1178148703Sume	ACPI_UNLOCK(thermal);
1179148703Sume	return (0);
1180148703Sume    }
1181148703Sume    sc->tz_cooling_proc_running = TRUE;
1182148703Sume    ACPI_UNLOCK(thermal);
1183148138Sume    error = 0;
1184148138Sume    if (sc->tz_cooling_proc == NULL) {
1185209062Savg	error = kproc_create(acpi_tz_cooling_thread, sc,
1186209062Savg	    &sc->tz_cooling_proc, RFHIGHPID, 0, "acpi_cooling%d",
1187148138Sume	    device_get_unit(sc->tz_dev));
1188148703Sume	if (error != 0) {
1189148138Sume	    device_printf(sc->tz_dev, "could not create thread - %d", error);
1190148703Sume	    ACPI_LOCK(thermal);
1191148703Sume	    sc->tz_cooling_proc_running = FALSE;
1192148703Sume	    ACPI_UNLOCK(thermal);
1193148703Sume	}
1194148138Sume    }
1195148138Sume    return (error);
1196148138Sume}
1197