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$");
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>
3991126Smsmith#include <sys/proc.h>
40128991Snjl#include <sys/reboot.h>
4179283Smsmith#include <sys/sysctl.h>
4291126Smsmith#include <sys/unistd.h>
4391640Siwasaki#include <sys/power.h>
4467761Smsmith
45148138Sume#include "cpufreq_if.h"
46148138Sume
47193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
48193530Sjkim#include <contrib/dev/acpica/include/accommon.h>
49193530Sjkim
5067761Smsmith#include <dev/acpica/acpivar.h>
5167761Smsmith
52119529Snjl/* Hooks for the ACPI CA debugging infrastructure */
5378999Smsmith#define _COMPONENT	ACPI_THERMAL
5491126SmsmithACPI_MODULE_NAME("THERMAL")
5569744Smsmith
5671874Smsmith#define TZ_ZEROC	2732
57160657Snjl#define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), abs(((x) - TZ_ZEROC) % 10)
5867761Smsmith
59125366Snjl#define TZ_NOTIFY_TEMPERATURE	0x80 /* Temperature changed. */
60125366Snjl#define TZ_NOTIFY_LEVELS	0x81 /* Cooling levels changed. */
61125366Snjl#define TZ_NOTIFY_DEVICES	0x82 /* Device lists changed. */
62125366Snjl#define TZ_NOTIFY_CRITICAL	0xcc /* Fake notify that _CRT/_HOT reached. */
6378915Smsmith
64125335Snjl/* Check for temperature changes every 10 seconds by default */
65125335Snjl#define TZ_POLLRATE	10
6678915Smsmith
67125335Snjl/* Make sure the reported temperature is valid for this number of polls. */
68125335Snjl#define TZ_VALIDCHECKS	3
69125335Snjl
70125366Snjl/* Notify the user we will be shutting down in one more poll cycle. */
71125366Snjl#define TZ_NOTIFYCOUNT	(TZ_VALIDCHECKS - 1)
72125366Snjl
73119529Snjl/* ACPI spec defines this */
74119529Snjl#define TZ_NUMLEVELS	10
7579375Smsmithstruct acpi_tz_zone {
7678915Smsmith    int		ac[TZ_NUMLEVELS];
7778915Smsmith    ACPI_BUFFER	al[TZ_NUMLEVELS];
7878915Smsmith    int		crt;
7978915Smsmith    int		hot;
8078915Smsmith    ACPI_BUFFER	psl;
8178915Smsmith    int		psv;
8278915Smsmith    int		tc1;
8378915Smsmith    int		tc2;
8478915Smsmith    int		tsp;
8578915Smsmith    int		tzp;
8678915Smsmith};
8778915Smsmith
8867761Smsmithstruct acpi_tz_softc {
89119529Snjl    device_t			tz_dev;
90119529Snjl    ACPI_HANDLE			tz_handle;	/*Thermal zone handle*/
91119529Snjl    int				tz_temperature;	/*Current temperature*/
92119529Snjl    int				tz_active;	/*Current active cooling*/
9379375Smsmith#define TZ_ACTIVE_NONE		-1
94178506Srpaulo#define TZ_ACTIVE_UNKNOWN	-2
95119529Snjl    int				tz_requested;	/*Minimum active cooling*/
96119529Snjl    int				tz_thflags;	/*Current temp-related flags*/
9779375Smsmith#define TZ_THFLAG_NONE		0
9879375Smsmith#define TZ_THFLAG_PSV		(1<<0)
9979375Smsmith#define TZ_THFLAG_HOT		(1<<2)
100148138Sume#define TZ_THFLAG_CRT		(1<<3)
10179283Smsmith    int				tz_flags;
102119529Snjl#define TZ_FLAG_NO_SCP		(1<<0)		/*No _SCP method*/
103119529Snjl#define TZ_FLAG_GETPROFILE	(1<<1)		/*Get power_profile in timeout*/
104133624Snjl#define TZ_FLAG_GETSETTINGS	(1<<2)		/*Get devs/setpoints*/
105119529Snjl    struct timespec		tz_cooling_started;
106119529Snjl					/*Current cooling starting time*/
10779283Smsmith
108119529Snjl    struct sysctl_ctx_list	tz_sysctl_ctx;
10979283Smsmith    struct sysctl_oid		*tz_sysctl_tree;
110133624Snjl    eventhandler_tag		tz_event;
111133624Snjl
112119529Snjl    struct acpi_tz_zone 	tz_zone;	/*Thermal zone parameters*/
113125335Snjl    int				tz_validchecks;
114255077Sdumbbell    int				tz_insane_tmp_notified;
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
125241538Savg#define	TZ_ACTIVE_LEVEL(act)	((act) >= 0 ? (act) : TZ_NUMLEVELS)
126241538Savg
127148138Sume#define CPUFREQ_MAX_LEVELS	64 /* XXX cpufreq should export this */
128148138Sume
12967761Smsmithstatic int	acpi_tz_probe(device_t dev);
13067761Smsmithstatic int	acpi_tz_attach(device_t dev);
13178915Smsmithstatic int	acpi_tz_establish(struct acpi_tz_softc *sc);
132119529Snjlstatic void	acpi_tz_monitor(void *Context);
13378915Smsmithstatic void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
13478915Smsmithstatic void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
135119529Snjlstatic void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node,
136119529Snjl				 int *data);
13779283Smsmithstatic void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
13879375Smsmithstatic int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
139148138Sumestatic int	acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS);
140160657Snjlstatic int	acpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS);
141174889Sumestatic int	acpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS);
142119529Snjlstatic void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify,
143119529Snjl				       void *context);
144133624Snjlstatic void	acpi_tz_signal(struct acpi_tz_softc *sc, int flags);
145133624Snjlstatic void	acpi_tz_timeout(struct acpi_tz_softc *sc, int flags);
14691640Siwasakistatic void	acpi_tz_power_profile(void *arg);
14791126Smsmithstatic void	acpi_tz_thread(void *arg);
148148138Sumestatic int	acpi_tz_cooling_is_available(struct acpi_tz_softc *sc);
149148138Sumestatic int	acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc);
15091126Smsmith
15167761Smsmithstatic device_method_t acpi_tz_methods[] = {
15267761Smsmith    /* Device interface */
15367761Smsmith    DEVMETHOD(device_probe,	acpi_tz_probe),
15467761Smsmith    DEVMETHOD(device_attach,	acpi_tz_attach),
15567761Smsmith
156246128Ssbz    DEVMETHOD_END
15767761Smsmith};
15867761Smsmith
15967761Smsmithstatic driver_t acpi_tz_driver = {
16067761Smsmith    "acpi_tz",
16167761Smsmith    acpi_tz_methods,
16267761Smsmith    sizeof(struct acpi_tz_softc),
16367761Smsmith};
16467761Smsmith
165255077Sdumbbellstatic char *acpi_tz_tmp_name = "_TMP";
166255077Sdumbbell
16789054Smsmithstatic devclass_t acpi_tz_devclass;
16867761SmsmithDRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0);
169128071SnjlMODULE_DEPEND(acpi_tz, acpi, 1, 1, 1);
17067761Smsmith
17179283Smsmithstatic struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
17279283Smsmithstatic struct sysctl_oid	*acpi_tz_sysctl_tree;
17379283Smsmith
174119529Snjl/* Minimum cooling run time */
175160657Snjlstatic int			acpi_tz_min_runtime;
17688420Siwasakistatic int			acpi_tz_polling_rate = TZ_POLLRATE;
177160657Snjlstatic int			acpi_tz_override;
17885699Siwasaki
179119529Snjl/* Timezone polling thread */
180119529Snjlstatic struct proc		*acpi_tz_proc;
181133624SnjlACPI_LOCK_DECL(thermal, "ACPI thermal zone");
182119529Snjl
183176329Sumestatic int			acpi_tz_cooling_unit = -1;
184176329Sume
18567761Smsmithstatic int
18667761Smsmithacpi_tz_probe(device_t dev)
18767761Smsmith{
18878999Smsmith    int		result;
189148138Sume
190119529Snjl    if (acpi_get_type(dev) == ACPI_TYPE_THERMAL && !acpi_disabled("thermal")) {
191120453Snjl	device_set_desc(dev, "Thermal Zone");
19278999Smsmith	result = -10;
193133624Snjl    } else
19478999Smsmith	result = ENXIO;
195119529Snjl    return (result);
19667761Smsmith}
19767761Smsmith
19867761Smsmithstatic int
19967761Smsmithacpi_tz_attach(device_t dev)
20067761Smsmith{
20167761Smsmith    struct acpi_tz_softc	*sc;
20279283Smsmith    struct acpi_softc		*acpi_sc;
20378915Smsmith    int				error;
20479283Smsmith    char			oidname[8];
20567761Smsmith
20696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
20769744Smsmith
20867761Smsmith    sc = device_get_softc(dev);
20967761Smsmith    sc->tz_dev = dev;
21067761Smsmith    sc->tz_handle = acpi_get_handle(dev);
21179375Smsmith    sc->tz_requested = TZ_ACTIVE_NONE;
212178506Srpaulo    sc->tz_active = TZ_ACTIVE_UNKNOWN;
213135548Snjl    sc->tz_thflags = TZ_THFLAG_NONE;
214148138Sume    sc->tz_cooling_proc = NULL;
215148703Sume    sc->tz_cooling_proc_running = FALSE;
216148138Sume    sc->tz_cooling_active = FALSE;
217148138Sume    sc->tz_cooling_updated = FALSE;
218176329Sume    sc->tz_cooling_enabled = FALSE;
21967761Smsmith
22078915Smsmith    /*
22178915Smsmith     * Parse the current state of the thermal zone and build control
222133624Snjl     * structures.  We don't need to worry about interference with the
223133624Snjl     * control thread since we haven't fully attached this device yet.
22478915Smsmith     */
22578915Smsmith    if ((error = acpi_tz_establish(sc)) != 0)
226133624Snjl	return (error);
227133624Snjl
22878915Smsmith    /*
22978915Smsmith     * Register for any Notify events sent to this zone.
23078915Smsmith     */
231148138Sume    AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
23278999Smsmith			     acpi_tz_notify_handler, sc);
23370271Stakawata
23471874Smsmith    /*
23579283Smsmith     * Create our sysctl nodes.
23679283Smsmith     *
23779283Smsmith     * XXX we need a mechanism for adding nodes under ACPI.
23879283Smsmith     */
23979283Smsmith    if (device_get_unit(dev) == 0) {
24079283Smsmith	acpi_sc = acpi_device_get_parent_softc(dev);
24179283Smsmith	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
24279283Smsmith	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
243119529Snjl			      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
244119529Snjl			      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
24585699Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
24685699Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
247159476Snjl		       OID_AUTO, "min_runtime", CTLFLAG_RW,
248119529Snjl		       &acpi_tz_min_runtime, 0,
249119529Snjl		       "minimum cooling run time in sec");
25088420Siwasaki	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
25188420Siwasaki		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
252159476Snjl		       OID_AUTO, "polling_rate", CTLFLAG_RW,
253227642Seadler		       &acpi_tz_polling_rate, 0, "monitor polling interval in seconds");
254160657Snjl	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
255160657Snjl		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
256160657Snjl		       "user_override", CTLFLAG_RW, &acpi_tz_override, 0,
257160657Snjl		       "allow override of thermal settings");
25879283Smsmith    }
25979283Smsmith    sysctl_ctx_init(&sc->tz_sysctl_ctx);
26079283Smsmith    sprintf(oidname, "tz%d", device_get_unit(dev));
26179283Smsmith    sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
262119529Snjl					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
263119529Snjl					 OID_AUTO, oidname, CTLFLAG_RD, 0, "");
26479375Smsmith    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
265220798Smdf		    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
266220798Smdf		    &sc->tz_temperature, 0, sysctl_handle_int,
267220798Smdf		    "IK", "current thermal zone temperature");
268220798Smdf    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
26979375Smsmith		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
270160657Snjl		    sc, 0, acpi_tz_active_sysctl, "I", "cooling is active");
271148138Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
272148138Sume		    OID_AUTO, "passive_cooling", CTLTYPE_INT | CTLFLAG_RW,
273160657Snjl		    sc, 0, acpi_tz_cooling_sysctl, "I",
274160657Snjl		    "enable passive (speed reduction) cooling");
275148138Sume
27679283Smsmith    SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
27779375Smsmith		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
27879375Smsmith		   &sc->tz_thflags, 0, "thermal zone flags");
279160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
280160657Snjl		    OID_AUTO, "_PSV", CTLTYPE_INT | CTLFLAG_RW,
281160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.psv),
282160657Snjl		    acpi_tz_temp_sysctl, "IK", "passive cooling temp setpoint");
283160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
284160657Snjl		    OID_AUTO, "_HOT", CTLTYPE_INT | CTLFLAG_RW,
285160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.hot),
286160657Snjl		    acpi_tz_temp_sysctl, "IK",
287160657Snjl		    "too hot temp setpoint (suspend now)");
288160657Snjl    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
289160657Snjl		    OID_AUTO, "_CRT", CTLTYPE_INT | CTLFLAG_RW,
290160657Snjl		    sc, offsetof(struct acpi_tz_softc, tz_zone.crt),
291160657Snjl		    acpi_tz_temp_sysctl, "IK",
292160657Snjl		    "critical temp setpoint (shutdown now)");
293174889Sume    SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
294220798Smdf		    OID_AUTO, "_ACx", CTLTYPE_INT | CTLFLAG_RD,
295220871Smdf		    &sc->tz_zone.ac, sizeof(sc->tz_zone.ac),
296220871Smdf		    sysctl_handle_opaque, "IK", "");
297220798Smdf    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
328176329Sume    /*
329176329Sume     * Create a thread to handle passive cooling for 1st zone which
330176329Sume     * has _PSV, _TSP, _TC1 and _TC2.  Users can enable it for other
331176329Sume     * zones manually for now.
332176329Sume     *
333176329Sume     * XXX We enable only one zone to avoid multiple zones conflict
334176329Sume     * with each other since cpufreq currently sets all CPUs to the
335176329Sume     * given frequency whereas it's possible for different thermal
336176329Sume     * zones to specify independent settings for multiple CPUs.
337176329Sume     */
338176329Sume    if (acpi_tz_cooling_unit < 0 && acpi_tz_cooling_is_available(sc))
339176329Sume	sc->tz_cooling_enabled = TRUE;
340148138Sume    if (sc->tz_cooling_enabled) {
341176329Sume	error = acpi_tz_cooling_thread_start(sc);
342176329Sume	if (error != 0) {
343148138Sume	    sc->tz_cooling_enabled = FALSE;
344176329Sume	    goto out;
345176329Sume	}
346176329Sume	acpi_tz_cooling_unit = device_get_unit(dev);
347148138Sume    }
348148138Sume
34979375Smsmith    /*
350133624Snjl     * Flag the event handler for a manual invocation by our timeout.
351133624Snjl     * We defer it like this so that the rest of the subsystem has time
352133624Snjl     * to come up.  Don't bother evaluating/printing the temperature at
353133624Snjl     * this point; on many systems it'll be bogus until the EC is running.
35471874Smsmith     */
355133624Snjl    sc->tz_flags |= TZ_FLAG_GETPROFILE;
35678999Smsmith
357133624Snjlout:
358133624Snjl    if (error != 0) {
359133624Snjl	EVENTHANDLER_DEREGISTER(power_profile_change, sc->tz_event);
360133624Snjl	AcpiRemoveNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
361133624Snjl	    acpi_tz_notify_handler);
362133624Snjl	sysctl_ctx_free(&sc->tz_sysctl_ctx);
36391126Smsmith    }
364119529Snjl    return_VALUE (error);
36567761Smsmith}
36670271Stakawata
36778915Smsmith/*
36878915Smsmith * Parse the current state of this thermal zone and set up to use it.
36978915Smsmith *
37078915Smsmith * Note that we may have previous state, which will have to be discarded.
37178915Smsmith */
37278915Smsmithstatic int
37378915Smsmithacpi_tz_establish(struct acpi_tz_softc *sc)
37478915Smsmith{
37578915Smsmith    ACPI_OBJECT	*obj;
37678915Smsmith    int		i;
37778915Smsmith    char	nbuf[8];
378148138Sume
37996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
38078915Smsmith
381134909Snjl    /* Erase any existing state. */
38278915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
38379375Smsmith	if (sc->tz_zone.al[i].Pointer != NULL)
38479375Smsmith	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
38579375Smsmith    if (sc->tz_zone.psl.Pointer != NULL)
38679375Smsmith	AcpiOsFree(sc->tz_zone.psl.Pointer);
38778915Smsmith
388149449Sume    /*
389149449Sume     * XXX: We initialize only ACPI_BUFFER to avoid race condition
390149449Sume     * with passive cooling thread which refers psv, tc1, tc2 and tsp.
391149449Sume     */
392149449Sume    bzero(sc->tz_zone.ac, sizeof(sc->tz_zone.ac));
393149449Sume    bzero(sc->tz_zone.al, sizeof(sc->tz_zone.al));
394149449Sume    bzero(&sc->tz_zone.psl, sizeof(sc->tz_zone.psl));
395149449Sume
396119529Snjl    /* Evaluate thermal zone parameters. */
39778915Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++) {
39878915Smsmith	sprintf(nbuf, "_AC%d", i);
39979375Smsmith	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
40078915Smsmith	sprintf(nbuf, "_AL%d", i);
40191126Smsmith	sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
40291126Smsmith	sc->tz_zone.al[i].Pointer = NULL;
40391126Smsmith	AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
40479375Smsmith	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
40578915Smsmith	if (obj != NULL) {
406119529Snjl	    /* Should be a package containing a list of power objects */
40778915Smsmith	    if (obj->Type != ACPI_TYPE_PACKAGE) {
408119529Snjl		device_printf(sc->tz_dev, "%s has unknown type %d, rejecting\n",
40978915Smsmith			      nbuf, obj->Type);
410119529Snjl		return_VALUE (ENXIO);
41178915Smsmith	    }
41278915Smsmith	}
41378915Smsmith    }
41479375Smsmith    acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
41579375Smsmith    acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
41691126Smsmith    sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
41791126Smsmith    sc->tz_zone.psl.Pointer = NULL;
41891126Smsmith    AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
41979375Smsmith    acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
42079375Smsmith    acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
42179375Smsmith    acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
42279375Smsmith    acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
42379375Smsmith    acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
42478915Smsmith
42578915Smsmith    /*
42679283Smsmith     * Sanity-check the values we've been given.
42779283Smsmith     *
42879283Smsmith     * XXX what do we do about systems that give us the same value for
42979283Smsmith     *     more than one of these setpoints?
43079283Smsmith     */
43179375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
43279375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
43379375Smsmith    acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
43479283Smsmith    for (i = 0; i < TZ_NUMLEVELS; i++)
43579375Smsmith	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
43679283Smsmith
437119529Snjl    return_VALUE (0);
43878915Smsmith}
43978915Smsmith
440133624Snjlstatic char *aclevel_string[] = {
441133624Snjl    "NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
442133624Snjl    "_AC5", "_AC6", "_AC7", "_AC8", "_AC9"
443133624Snjl};
44485699Siwasaki
44585699Siwasakistatic __inline const char *
44685699Siwasakiacpi_tz_aclevel_string(int active)
44785699Siwasaki{
448133624Snjl    if (active < -1 || active >= TZ_NUMLEVELS)
449133624Snjl	return (aclevel_string[0]);
45085699Siwasaki
451133624Snjl    return (aclevel_string[active + 1]);
45285699Siwasaki}
45385699Siwasaki
45478915Smsmith/*
455149450Sume * Get the current temperature.
456149450Sume */
457149450Sumestatic int
458149450Sumeacpi_tz_get_temperature(struct acpi_tz_softc *sc)
459149450Sume{
460149450Sume    int		temp;
461149450Sume    ACPI_STATUS	status;
462149450Sume
463149482Skan    ACPI_FUNCTION_NAME ("acpi_tz_get_temperature");
464149482Skan
465167249Snjl    /* Evaluate the thermal zone's _TMP method. */
466255077Sdumbbell    status = acpi_GetInteger(sc->tz_handle, acpi_tz_tmp_name, &temp);
467149450Sume    if (ACPI_FAILURE(status)) {
468149450Sume	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
469149450Sume	    "error fetching current temperature -- %s\n",
470149450Sume	     AcpiFormatException(status));
471149450Sume	return (FALSE);
472149450Sume    }
473149450Sume
474167249Snjl    /* Check it for validity. */
475255077Sdumbbell    acpi_tz_sanity(sc, &temp, acpi_tz_tmp_name);
476167249Snjl    if (temp == -1)
477167249Snjl	return (FALSE);
478167249Snjl
479149450Sume    ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
480149450Sume    sc->tz_temperature = temp;
481149450Sume    return (TRUE);
482149450Sume}
483149450Sume
484149450Sume/*
48578915Smsmith * Evaluate the condition of a thermal zone, take appropriate actions.
48678915Smsmith */
48771874Smsmithstatic void
488119529Snjlacpi_tz_monitor(void *Context)
48971874Smsmith{
490119529Snjl    struct acpi_tz_softc *sc;
491119529Snjl    struct	timespec curtime;
49279283Smsmith    int		temp;
49378915Smsmith    int		i;
49479283Smsmith    int		newactive, newflags;
49570271Stakawata
49696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
49770271Stakawata
498119529Snjl    sc = (struct acpi_tz_softc *)Context;
49988420Siwasaki
500119529Snjl    /* Get the current temperature. */
501149450Sume    if (!acpi_tz_get_temperature(sc)) {
50278915Smsmith	/* XXX disable zone? go to max cooling? */
503133624Snjl	return_VOID;
50471874Smsmith    }
505149450Sume    temp = sc->tz_temperature;
50688420Siwasaki
50778915Smsmith    /*
50878915Smsmith     * Work out what we ought to be doing right now.
50979283Smsmith     *
51079283Smsmith     * Note that the _ACx levels sort from hot to cold.
51178915Smsmith     */
51279283Smsmith    newactive = TZ_ACTIVE_NONE;
51379375Smsmith    for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
514245266Smav	if (sc->tz_zone.ac[i] != -1 && temp >= sc->tz_zone.ac[i])
51579283Smsmith	    newactive = i;
51679375Smsmith    }
51779283Smsmith
51885699Siwasaki    /*
51985699Siwasaki     * We are going to get _ACx level down (colder side), but give a guaranteed
52085699Siwasaki     * minimum cooling run time if requested.
52185699Siwasaki     */
52285699Siwasaki    if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
523178506Srpaulo	sc->tz_active != TZ_ACTIVE_UNKNOWN &&
52485699Siwasaki	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
525119529Snjl
52685699Siwasaki	getnanotime(&curtime);
52785699Siwasaki	timespecsub(&curtime, &sc->tz_cooling_started);
528119529Snjl	if (curtime.tv_sec < acpi_tz_min_runtime)
52985699Siwasaki	    newactive = sc->tz_active;
53085699Siwasaki    }
53185699Siwasaki
532119529Snjl    /* Handle user override of active mode */
533176327Srpaulo    if (sc->tz_requested != TZ_ACTIVE_NONE && (newactive == TZ_ACTIVE_NONE
534176327Srpaulo        || sc->tz_requested < newactive))
53579375Smsmith	newactive = sc->tz_requested;
53678915Smsmith
53779375Smsmith    /* update temperature-related flags */
53879375Smsmith    newflags = TZ_THFLAG_NONE;
539124439Snjl    if (sc->tz_zone.psv != -1 && temp >= sc->tz_zone.psv)
54079375Smsmith	newflags |= TZ_THFLAG_PSV;
541124439Snjl    if (sc->tz_zone.hot != -1 && temp >= sc->tz_zone.hot)
54279375Smsmith	newflags |= TZ_THFLAG_HOT;
543124439Snjl    if (sc->tz_zone.crt != -1 && temp >= sc->tz_zone.crt)
54479375Smsmith	newflags |= TZ_THFLAG_CRT;
54579375Smsmith
546119529Snjl    /* If the active cooling state has changed, we have to switch things. */
547178506Srpaulo    if (sc->tz_active == TZ_ACTIVE_UNKNOWN) {
548178506Srpaulo	/*
549178506Srpaulo	 * We don't know which cooling device is on or off,
550178506Srpaulo	 * so stop them all, because we now know which
551178506Srpaulo	 * should be on (if any).
552178506Srpaulo	 */
553178506Srpaulo	for (i = 0; i < TZ_NUMLEVELS; i++) {
554178506Srpaulo	    if (sc->tz_zone.al[i].Pointer != NULL) {
555178506Srpaulo		acpi_ForeachPackageObject(
556178506Srpaulo		    (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
557178506Srpaulo		    acpi_tz_switch_cooler_off, sc);
558178506Srpaulo	    }
559178506Srpaulo	}
560178506Srpaulo	/* now we know that all devices are off */
561178506Srpaulo	sc->tz_active = TZ_ACTIVE_NONE;
562178506Srpaulo    }
563178506Srpaulo
56479283Smsmith    if (newactive != sc->tz_active) {
565241538Savg	/* Turn off unneeded cooling devices that are on, if any are */
566241538Savg	for (i = TZ_ACTIVE_LEVEL(sc->tz_active);
567241538Savg	     i < TZ_ACTIVE_LEVEL(newactive); i++) {
568119529Snjl	    acpi_ForeachPackageObject(
569241538Savg		(ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
570119529Snjl		acpi_tz_switch_cooler_off, sc);
571241538Savg	}
572119529Snjl	/* Turn on cooling devices that are required, if any are */
573241538Savg	for (i = TZ_ACTIVE_LEVEL(sc->tz_active) - 1;
574241538Savg	     i >= TZ_ACTIVE_LEVEL(newactive); i--) {
575119529Snjl	    acpi_ForeachPackageObject(
576241538Savg		(ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
577119529Snjl		acpi_tz_switch_cooler_on, sc);
578119529Snjl	}
579241538Savg
58086552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
581119529Snjl		    "switched from %s to %s: %d.%dC\n",
582119529Snjl		    acpi_tz_aclevel_string(sc->tz_active),
583119529Snjl		    acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
58479283Smsmith	sc->tz_active = newactive;
585142195Snjl	getnanotime(&sc->tz_cooling_started);
58679283Smsmith    }
58778915Smsmith
588119529Snjl    /* XXX (de)activate any passive cooling that may be required. */
58978915Smsmith
59078915Smsmith    /*
591125335Snjl     * If the temperature is at _HOT or _CRT, increment our event count.
592125335Snjl     * If it has occurred enough times, shutdown the system.  This is
593125335Snjl     * needed because some systems will report an invalid high temperature
594125335Snjl     * for one poll cycle.  It is suspected this is due to the embedded
595125335Snjl     * controller timing out.  A typical value is 138C for one cycle on
596125335Snjl     * a system that is otherwise 65C.
597125366Snjl     *
598125366Snjl     * If we're almost at that threshold, notify the user through devd(8).
59978915Smsmith     */
600125335Snjl    if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) {
601125366Snjl	sc->tz_validchecks++;
602125366Snjl	if (sc->tz_validchecks == TZ_VALIDCHECKS) {
603125335Snjl	    device_printf(sc->tz_dev,
604125335Snjl		"WARNING - current temperature (%d.%dC) exceeds safe limits\n",
605125335Snjl		TZ_KELVTOC(sc->tz_temperature));
606125335Snjl	    shutdown_nice(RB_POWEROFF);
607125366Snjl	} else if (sc->tz_validchecks == TZ_NOTIFYCOUNT)
608125366Snjl	    acpi_UserNotify("Thermal", sc->tz_handle, TZ_NOTIFY_CRITICAL);
609125335Snjl    } else {
610125335Snjl	sc->tz_validchecks = 0;
61178915Smsmith    }
61279375Smsmith    sc->tz_thflags = newflags;
61378915Smsmith
61471874Smsmith    return_VOID;
61571874Smsmith}
61670271Stakawata
61778915Smsmith/*
618148138Sume * Given an object, verify that it's a reference to a device of some sort,
61978915Smsmith * and try to switch it off.
62078915Smsmith */
62178915Smsmithstatic void
62278915Smsmithacpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
62378915Smsmith{
624128047Snjl    ACPI_HANDLE			cooler;
62578915Smsmith
62696926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
62778915Smsmith
628128047Snjl    cooler = acpi_GetReference(NULL, obj);
629128047Snjl    if (cooler == NULL) {
630128047Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
631128047Snjl	return_VOID;
632128047Snjl    }
633102470Siwasaki
634128047Snjl    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
635128047Snjl		     acpi_name(cooler)));
636128150Snjl    acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
637119529Snjl
63879375Smsmith    return_VOID;
63978915Smsmith}
64078915Smsmith
64178915Smsmith/*
642148138Sume * Given an object, verify that it's a reference to a device of some sort,
64378915Smsmith * and try to switch it on.
64478915Smsmith *
645128047Snjl * XXX replication of off/on function code is bad.
64678915Smsmith */
64778915Smsmithstatic void
64878915Smsmithacpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
64978915Smsmith{
65078915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
65178999Smsmith    ACPI_HANDLE			cooler;
65279375Smsmith    ACPI_STATUS			status;
653148138Sume
65496926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
65578915Smsmith
656128047Snjl    cooler = acpi_GetReference(NULL, obj);
657128047Snjl    if (cooler == NULL) {
658128047Snjl	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
659128047Snjl	return_VOID;
660128047Snjl    }
661102470Siwasaki
662128047Snjl    ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
663128047Snjl		     acpi_name(cooler)));
664128047Snjl    status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
665128047Snjl    if (ACPI_FAILURE(status)) {
666128047Snjl	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
667128047Snjl		    "failed to activate %s - %s\n", acpi_name(cooler),
668128047Snjl		    AcpiFormatException(status));
66978915Smsmith    }
670119529Snjl
671119529Snjl    return_VOID;
67278915Smsmith}
67378915Smsmith
67478915Smsmith/*
67578915Smsmith * Read/debug-print a parameter, default it to -1.
67678915Smsmith */
67778915Smsmithstatic void
67878915Smsmithacpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
67978915Smsmith{
68078915Smsmith
68196926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
68278915Smsmith
683126560Snjl    if (ACPI_FAILURE(acpi_GetInteger(sc->tz_handle, node, data))) {
68478915Smsmith	*data = -1;
68578915Smsmith    } else {
686119529Snjl	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n",
687119529Snjl			 acpi_name(sc->tz_handle), node, *data));
68878915Smsmith    }
689119529Snjl
690148138Sume    return_VOID;
69178915Smsmith}
69279283Smsmith
69379283Smsmith/*
69479283Smsmith * Sanity-check a temperature value.  Assume that setpoints
695167249Snjl * should be between 0C and 200C.
69679283Smsmith */
69779283Smsmithstatic void
69879283Smsmithacpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
69979283Smsmith{
700167249Snjl    if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 2000)) {
701255077Sdumbbell	/*
702255077Sdumbbell	 * If the value we are checking is _TMP, warn the user only
703255077Sdumbbell	 * once. This avoids spamming messages if, for instance, the
704255077Sdumbbell	 * sensor is broken and always returns an invalid temperature.
705255077Sdumbbell	 *
706255077Sdumbbell	 * This is only done for _TMP; other values always emit a
707255077Sdumbbell	 * warning.
708255077Sdumbbell	 */
709255077Sdumbbell	if (what != acpi_tz_tmp_name || !sc->tz_insane_tmp_notified) {
710255077Sdumbbell	    device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
711255077Sdumbbell			  what, TZ_KELVTOC(*val));
712255077Sdumbbell
713255077Sdumbbell	    /* Don't warn the user again if the read value doesn't improve. */
714255077Sdumbbell	    if (what == acpi_tz_tmp_name)
715255077Sdumbbell		sc->tz_insane_tmp_notified = 1;
716255077Sdumbbell	}
71779283Smsmith	*val = -1;
718255077Sdumbbell	return;
71979283Smsmith    }
720255077Sdumbbell
721255077Sdumbbell    /* This value is correct. Warn if it's incorrect again. */
722255077Sdumbbell    if (what == acpi_tz_tmp_name)
723255077Sdumbbell	sc->tz_insane_tmp_notified = 0;
72479283Smsmith}
72579375Smsmith
72679375Smsmith/*
72779375Smsmith * Respond to a sysctl on the active state node.
728148138Sume */
72979375Smsmithstatic int
73079375Smsmithacpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
73179375Smsmith{
73279375Smsmith    struct acpi_tz_softc	*sc;
73379375Smsmith    int				active;
73479375Smsmith    int		 		error;
73579375Smsmith
73679375Smsmith    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
73779375Smsmith    active = sc->tz_active;
73879375Smsmith    error = sysctl_handle_int(oidp, &active, 0, req);
73979375Smsmith
740119529Snjl    /* Error or no new value */
741119529Snjl    if (error != 0 || req->newptr == NULL)
742133624Snjl	return (error);
743133624Snjl    if (active < -1 || active >= TZ_NUMLEVELS)
744133624Snjl	return (EINVAL);
74579375Smsmith
746119529Snjl    /* Set new preferred level and re-switch */
74779375Smsmith    sc->tz_requested = active;
748133624Snjl    acpi_tz_signal(sc, 0);
749133624Snjl    return (0);
75079375Smsmith}
75179375Smsmith
752148138Sumestatic int
753148138Sumeacpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS)
754148138Sume{
755148138Sume    struct acpi_tz_softc *sc;
756148138Sume    int enabled, error;
757148138Sume
758148138Sume    sc = (struct acpi_tz_softc *)oidp->oid_arg1;
759148138Sume    enabled = sc->tz_cooling_enabled;
760148138Sume    error = sysctl_handle_int(oidp, &enabled, 0, req);
761148138Sume
762148138Sume    /* Error or no new value */
763148138Sume    if (error != 0 || req->newptr == NULL)
764148138Sume	return (error);
765148138Sume    if (enabled != TRUE && enabled != FALSE)
766148138Sume	return (EINVAL);
767148138Sume
768148138Sume    if (enabled) {
769148138Sume	if (acpi_tz_cooling_is_available(sc))
770148138Sume	    error = acpi_tz_cooling_thread_start(sc);
771148138Sume	else
772148138Sume	    error = ENODEV;
773148138Sume	if (error)
774148138Sume	    enabled = FALSE;
775148138Sume    }
776148138Sume    sc->tz_cooling_enabled = enabled;
777148138Sume    return (error);
778148138Sume}
779148138Sume
780160657Snjlstatic int
781160657Snjlacpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS)
782160657Snjl{
783160657Snjl    struct acpi_tz_softc	*sc;
784160657Snjl    int				temp, *temp_ptr;
785160657Snjl    int		 		error;
786160657Snjl
787160657Snjl    sc = oidp->oid_arg1;
788160657Snjl    temp_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2);
789160657Snjl    temp = *temp_ptr;
790160657Snjl    error = sysctl_handle_int(oidp, &temp, 0, req);
791160657Snjl
792160657Snjl    /* Error or no new value */
793160657Snjl    if (error != 0 || req->newptr == NULL)
794160657Snjl	return (error);
795160657Snjl
796160657Snjl    /* Only allow changing settings if override is set. */
797160657Snjl    if (!acpi_tz_override)
798160657Snjl	return (EPERM);
799160657Snjl
800160657Snjl    /* Check user-supplied value for sanity. */
801160657Snjl    acpi_tz_sanity(sc, &temp, "user-supplied temp");
802160657Snjl    if (temp == -1)
803160657Snjl	return (EINVAL);
804160657Snjl
805160657Snjl    *temp_ptr = temp;
806160657Snjl    return (0);
807160657Snjl}
808160657Snjl
809174889Sumestatic int
810174889Sumeacpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS)
811174889Sume{
812174889Sume    struct acpi_tz_softc	*sc;
813174889Sume    int				val, *val_ptr;
814174889Sume    int				error;
815174889Sume
816174889Sume    sc = oidp->oid_arg1;
817174889Sume    val_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2);
818174889Sume    val = *val_ptr;
819174889Sume    error = sysctl_handle_int(oidp, &val, 0, req);
820174889Sume
821174889Sume    /* Error or no new value */
822174889Sume    if (error != 0 || req->newptr == NULL)
823174889Sume	return (error);
824174889Sume
825174889Sume    /* Only allow changing settings if override is set. */
826174889Sume    if (!acpi_tz_override)
827174889Sume	return (EPERM);
828174889Sume
829174889Sume    *val_ptr = val;
830174889Sume    return (0);
831174889Sume}
832174889Sume
83378915Smsmithstatic void
83471874Smsmithacpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
83571874Smsmith{
83678915Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
83778915Smsmith
83896926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
83970271Stakawata
840133624Snjl    switch (notify) {
84178915Smsmith    case TZ_NOTIFY_TEMPERATURE:
842119529Snjl	/* Temperature change occurred */
843133624Snjl	acpi_tz_signal(sc, 0);
84478915Smsmith	break;
84578915Smsmith    case TZ_NOTIFY_DEVICES:
84678915Smsmith    case TZ_NOTIFY_LEVELS:
847119529Snjl	/* Zone devices/setpoints changed */
848133624Snjl	acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS);
84978915Smsmith	break;
85078915Smsmith    default:
85186552Siwasaki	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
852119529Snjl		    "unknown Notify event 0x%x\n", notify);
85378915Smsmith	break;
85471874Smsmith    }
855119529Snjl
856121493Snjl    acpi_UserNotify("Thermal", h, notify);
857121493Snjl
85871874Smsmith    return_VOID;
85971874Smsmith}
86070271Stakawata
861133624Snjlstatic void
862133624Snjlacpi_tz_signal(struct acpi_tz_softc *sc, int flags)
863133624Snjl{
864133624Snjl    ACPI_LOCK(thermal);
865133624Snjl    sc->tz_flags |= flags;
866133624Snjl    ACPI_UNLOCK(thermal);
867133624Snjl    wakeup(&acpi_tz_proc);
868133624Snjl}
869133624Snjl
87078915Smsmith/*
871134909Snjl * Notifies can be generated asynchronously but have also been seen to be
872134909Snjl * triggered by other thermal methods.  One system generates a notify of
873134909Snjl * 0x81 when the fan is turned on or off.  Another generates it when _SCP
874134909Snjl * is called.  To handle these situations, we check the zone via
875134909Snjl * acpi_tz_monitor() before evaluating changes to setpoints or the cooling
876134909Snjl * policy.
87778915Smsmith */
87878915Smsmithstatic void
879133624Snjlacpi_tz_timeout(struct acpi_tz_softc *sc, int flags)
88078915Smsmith{
881134909Snjl
882134909Snjl    /* Check the current temperature and take action based on it */
883134909Snjl    acpi_tz_monitor(sc);
884134909Snjl
885133624Snjl    /* If requested, get the power profile settings. */
886133624Snjl    if (flags & TZ_FLAG_GETPROFILE)
887133624Snjl	acpi_tz_power_profile(sc);
88879375Smsmith
889134909Snjl    /*
890134909Snjl     * If requested, check for new devices/setpoints.  After finding them,
891134909Snjl     * check if we need to switch fans based on the new values.
892134909Snjl     */
893134909Snjl    if (flags & TZ_FLAG_GETSETTINGS) {
894133624Snjl	acpi_tz_establish(sc);
895134909Snjl	acpi_tz_monitor(sc);
896134909Snjl    }
89778915Smsmith
89878915Smsmith    /* XXX passive cooling actions? */
89978915Smsmith}
90079375Smsmith
90179375Smsmith/*
90279375Smsmith * System power profile may have changed; fetch and notify the
90379375Smsmith * thermal zone accordingly.
90479375Smsmith *
90579375Smsmith * Since this can be called from an arbitrary eventhandler, it needs
90679375Smsmith * to get the ACPI lock itself.
90779375Smsmith */
90879375Smsmithstatic void
90991640Siwasakiacpi_tz_power_profile(void *arg)
91079375Smsmith{
91179375Smsmith    ACPI_STATUS			status;
91279375Smsmith    struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
91391640Siwasaki    int				state;
91479375Smsmith
91591640Siwasaki    state = power_profile_get_state();
916119529Snjl    if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
917119529Snjl	return;
91891640Siwasaki
91979375Smsmith    /* check that we haven't decided there's no _SCP method */
920119529Snjl    if ((sc->tz_flags & TZ_FLAG_NO_SCP) == 0) {
92179375Smsmith
922119529Snjl	/* Call _SCP to set the new profile */
923148138Sume	status = acpi_SetInteger(sc->tz_handle, "_SCP",
924126560Snjl	    (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1);
925119529Snjl	if (ACPI_FAILURE(status)) {
92679385Smsmith	    if (status != AE_NOT_FOUND)
927119529Snjl		ACPI_VPRINT(sc->tz_dev,
928119529Snjl			    acpi_device_get_parent_softc(sc->tz_dev),
929119529Snjl			    "can't evaluate %s._SCP - %s\n",
930119529Snjl			    acpi_name(sc->tz_handle),
931119529Snjl			    AcpiFormatException(status));
93279375Smsmith	    sc->tz_flags |= TZ_FLAG_NO_SCP;
93379375Smsmith	} else {
934119529Snjl	    /* We have to re-evaluate the entire zone now */
935133624Snjl	    acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS);
93679375Smsmith	}
93779375Smsmith    }
93879375Smsmith}
93979375Smsmith
94091126Smsmith/*
94191126Smsmith * Thermal zone monitor thread.
94291126Smsmith */
94391126Smsmithstatic void
94491126Smsmithacpi_tz_thread(void *arg)
94591126Smsmith{
94691126Smsmith    device_t	*devs;
94791126Smsmith    int		devcount, i;
948133624Snjl    int		flags;
949133624Snjl    struct acpi_tz_softc **sc;
95091126Smsmith
95196926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
95291126Smsmith
95391126Smsmith    devs = NULL;
95491126Smsmith    devcount = 0;
955133624Snjl    sc = NULL;
95691126Smsmith
95791126Smsmith    for (;;) {
958133624Snjl	/* If the number of devices has changed, re-evaluate. */
959175014Sjhb	if (devclass_get_count(acpi_tz_devclass) != devcount) {
960133624Snjl	    if (devs != NULL) {
961133624Snjl		free(devs, M_TEMP);
962133624Snjl		free(sc, M_TEMP);
963133624Snjl	    }
964133624Snjl	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
965133624Snjl	    sc = malloc(sizeof(struct acpi_tz_softc *) * devcount, M_TEMP,
966133624Snjl			M_WAITOK | M_ZERO);
967133624Snjl	    for (i = 0; i < devcount; i++)
968133624Snjl		sc[i] = device_get_softc(devs[i]);
969133624Snjl	}
97091126Smsmith
971133624Snjl	/* Check for temperature events and act on them. */
972133624Snjl	for (i = 0; i < devcount; i++) {
973133624Snjl	    ACPI_LOCK(thermal);
974133624Snjl	    flags = sc[i]->tz_flags;
975133624Snjl	    sc[i]->tz_flags &= TZ_FLAG_NO_SCP;
976133624Snjl	    ACPI_UNLOCK(thermal);
977133624Snjl	    acpi_tz_timeout(sc[i], flags);
978133624Snjl	}
97991215Smsmith
980133624Snjl	/* If more work to do, don't go to sleep yet. */
981133624Snjl	ACPI_LOCK(thermal);
982133624Snjl	for (i = 0; i < devcount; i++) {
983133624Snjl	    if (sc[i]->tz_flags & ~TZ_FLAG_NO_SCP)
984133624Snjl		break;
985133624Snjl	}
98691126Smsmith
987133624Snjl	/*
988133624Snjl	 * If we have no more work, sleep for a while, setting PDROP so that
989133624Snjl	 * the mutex will not be reacquired.  Otherwise, drop the mutex and
990133624Snjl	 * loop to handle more events.
991133624Snjl	 */
992133624Snjl	if (i == devcount)
993133624Snjl	    msleep(&acpi_tz_proc, &thermal_mutex, PZERO | PDROP, "tzpoll",
994133624Snjl		hz * acpi_tz_polling_rate);
995133624Snjl	else
996133624Snjl	    ACPI_UNLOCK(thermal);
99791126Smsmith    }
99891126Smsmith}
999148138Sume
1000148138Sumestatic int
1001148138Sumeacpi_tz_cpufreq_restore(struct acpi_tz_softc *sc)
1002148138Sume{
1003148138Sume    device_t dev;
1004148138Sume    int error;
1005148138Sume
1006148138Sume    if (!sc->tz_cooling_updated)
1007148138Sume	return (0);
1008148138Sume    if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL)
1009148138Sume	return (ENXIO);
1010148138Sume    ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
1011149201Sume	"temperature %d.%dC: resuming previous clock speed (%d MHz)\n",
1012149201Sume	TZ_KELVTOC(sc->tz_temperature), sc->tz_cooling_saved_freq);
1013148138Sume    error = CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN);
1014148138Sume    if (error == 0)
1015148138Sume	sc->tz_cooling_updated = FALSE;
1016148138Sume    return (error);
1017148138Sume}
1018148138Sume
1019148138Sumestatic int
1020148138Sumeacpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req)
1021148138Sume{
1022148138Sume    device_t dev;
1023148138Sume    struct cf_level *levels;
1024148138Sume    int num_levels, error, freq, desired_freq, perf, i;
1025148138Sume
1026148138Sume    levels = malloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT);
1027148138Sume    if (levels == NULL)
1028148138Sume	return (ENOMEM);
1029148138Sume
1030148138Sume    /*
1031148138Sume     * Find the main device, cpufreq0.  We don't yet support independent
1032148138Sume     * CPU frequency control on SMP.
1033148138Sume     */
1034148138Sume    if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) {
1035148138Sume	error = ENXIO;
1036148138Sume	goto out;
1037148138Sume    }
1038148138Sume
1039148138Sume    /* Get the current frequency. */
1040148138Sume    error = CPUFREQ_GET(dev, &levels[0]);
1041148138Sume    if (error)
1042148138Sume	goto out;
1043148138Sume    freq = levels[0].total_set.freq;
1044148138Sume
1045148138Sume    /* Get the current available frequency levels. */
1046148138Sume    num_levels = CPUFREQ_MAX_LEVELS;
1047148138Sume    error = CPUFREQ_LEVELS(dev, levels, &num_levels);
1048148138Sume    if (error) {
1049148138Sume	if (error == E2BIG)
1050148138Sume	    printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n");
1051148138Sume	goto out;
1052148138Sume    }
1053148138Sume
1054148138Sume    /* Calculate the desired frequency as a percent of the max frequency. */
1055148138Sume    perf = 100 * freq / levels[0].total_set.freq - req;
1056148138Sume    if (perf < 0)
1057148138Sume	perf = 0;
1058148138Sume    else if (perf > 100)
1059148138Sume	perf = 100;
1060148138Sume    desired_freq = levels[0].total_set.freq * perf / 100;
1061148138Sume
1062149201Sume    if (desired_freq < freq) {
1063148138Sume	/* Find the closest available frequency, rounding down. */
1064148138Sume	for (i = 0; i < num_levels; i++)
1065148138Sume	    if (levels[i].total_set.freq <= desired_freq)
1066148138Sume		break;
1067148138Sume
1068148138Sume	/* If we didn't find a relevant setting, use the lowest. */
1069148138Sume	if (i == num_levels)
1070148138Sume	    i--;
1071148138Sume    } else {
1072149201Sume	/* If we didn't decrease frequency yet, don't increase it. */
1073149201Sume	if (!sc->tz_cooling_updated) {
1074149201Sume	    sc->tz_cooling_active = FALSE;
1075149201Sume	    goto out;
1076149201Sume	}
1077149201Sume
1078149201Sume	/* Use saved cpu frequency as maximum value. */
1079149201Sume	if (desired_freq > sc->tz_cooling_saved_freq)
1080149201Sume	    desired_freq = sc->tz_cooling_saved_freq;
1081149201Sume
1082148138Sume	/* Find the closest available frequency, rounding up. */
1083148138Sume	for (i = num_levels - 1; i >= 0; i--)
1084148138Sume	    if (levels[i].total_set.freq >= desired_freq)
1085148138Sume		break;
1086148138Sume
1087148138Sume	/* If we didn't find a relevant setting, use the highest. */
1088148138Sume	if (i == -1)
1089148138Sume	    i++;
1090148138Sume
1091149201Sume	/* If we're going to the highest frequency, restore the old setting. */
1092149201Sume	if (i == 0 || desired_freq == sc->tz_cooling_saved_freq) {
1093149201Sume	    error = acpi_tz_cpufreq_restore(sc);
1094149201Sume	    if (error == 0)
1095149201Sume		sc->tz_cooling_active = FALSE;
1096149201Sume	    goto out;
1097149201Sume	}
1098148138Sume    }
1099148138Sume
1100148138Sume    /* If we are going to a new frequency, activate it. */
1101148138Sume    if (levels[i].total_set.freq != freq) {
1102148138Sume	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
1103148138Sume	    "temperature %d.%dC: %screasing clock speed "
1104148138Sume	    "from %d MHz to %d MHz\n",
1105148138Sume	    TZ_KELVTOC(sc->tz_temperature),
1106148138Sume	    (freq > levels[i].total_set.freq) ? "de" : "in",
1107148138Sume	    freq, levels[i].total_set.freq);
1108148138Sume	error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN);
1109149201Sume	if (error == 0 && !sc->tz_cooling_updated) {
1110149201Sume	    sc->tz_cooling_saved_freq = freq;
1111148138Sume	    sc->tz_cooling_updated = TRUE;
1112149201Sume	}
1113148138Sume    }
1114148138Sume
1115148138Sumeout:
1116148138Sume    if (levels)
1117148138Sume	free(levels, M_TEMP);
1118148138Sume    return (error);
1119148138Sume}
1120148138Sume
1121148138Sume/*
1122148138Sume * Passive cooling thread; monitors current temperature according to the
1123148138Sume * cooling interval and calculates whether to scale back CPU frequency.
1124148138Sume */
1125148138Sumestatic void
1126148138Sumeacpi_tz_cooling_thread(void *arg)
1127148138Sume{
1128148138Sume    struct acpi_tz_softc *sc;
1129149450Sume    int error, perf, curr_temp, prev_temp;
1130148138Sume
1131148138Sume    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1132148138Sume
1133148138Sume    sc = (struct acpi_tz_softc *)arg;
1134148138Sume
1135149450Sume    prev_temp = sc->tz_temperature;
1136148138Sume    while (sc->tz_cooling_enabled) {
1137149450Sume	if (sc->tz_cooling_active)
1138149450Sume	    (void)acpi_tz_get_temperature(sc);
1139149450Sume	curr_temp = sc->tz_temperature;
1140149450Sume	if (curr_temp >= sc->tz_zone.psv)
1141148138Sume	    sc->tz_cooling_active = TRUE;
1142148138Sume	if (sc->tz_cooling_active) {
1143149450Sume	    perf = sc->tz_zone.tc1 * (curr_temp - prev_temp) +
1144149450Sume		   sc->tz_zone.tc2 * (curr_temp - sc->tz_zone.psv);
1145148138Sume	    perf /= 10;
1146148138Sume
1147148138Sume	    if (perf != 0) {
1148148138Sume		error = acpi_tz_cpufreq_update(sc, perf);
1149148138Sume
1150148138Sume		/*
1151148138Sume		 * If error and not simply a higher priority setting was
1152148138Sume		 * active, disable cooling.
1153148138Sume		 */
1154148138Sume		if (error != 0 && error != EPERM) {
1155148138Sume		    device_printf(sc->tz_dev,
1156148138Sume			"failed to set new freq, disabling passive cooling\n");
1157148138Sume		    sc->tz_cooling_enabled = FALSE;
1158148138Sume		}
1159148138Sume	    }
1160148138Sume	}
1161149450Sume	prev_temp = curr_temp;
1162148138Sume	tsleep(&sc->tz_cooling_proc, PZERO, "cooling",
1163148138Sume	    hz * sc->tz_zone.tsp / 10);
1164148138Sume    }
1165148138Sume    if (sc->tz_cooling_active) {
1166148138Sume	acpi_tz_cpufreq_restore(sc);
1167148138Sume	sc->tz_cooling_active = FALSE;
1168148138Sume    }
1169148703Sume    sc->tz_cooling_proc = NULL;
1170148138Sume    ACPI_LOCK(thermal);
1171148703Sume    sc->tz_cooling_proc_running = FALSE;
1172148138Sume    ACPI_UNLOCK(thermal);
1173172836Sjulian    kproc_exit(0);
1174148138Sume}
1175148138Sume
1176148138Sume/*
1177148138Sume * TODO: We ignore _PSL (list of cooling devices) since cpufreq enumerates
1178148138Sume * all CPUs for us.  However, it's possible in the future _PSL will
1179148138Sume * reference non-CPU devices so we may want to support it then.
1180148138Sume */
1181148138Sumestatic int
1182148138Sumeacpi_tz_cooling_is_available(struct acpi_tz_softc *sc)
1183148138Sume{
1184148138Sume    return (sc->tz_zone.tc1 != -1 && sc->tz_zone.tc2 != -1 &&
1185148138Sume	sc->tz_zone.tsp != -1 && sc->tz_zone.tsp != 0 &&
1186148138Sume	sc->tz_zone.psv != -1);
1187148138Sume}
1188148138Sume
1189148138Sumestatic int
1190148138Sumeacpi_tz_cooling_thread_start(struct acpi_tz_softc *sc)
1191148138Sume{
1192148138Sume    int error;
1193148138Sume
1194148703Sume    ACPI_LOCK(thermal);
1195148703Sume    if (sc->tz_cooling_proc_running) {
1196148703Sume	ACPI_UNLOCK(thermal);
1197148703Sume	return (0);
1198148703Sume    }
1199148703Sume    sc->tz_cooling_proc_running = TRUE;
1200148703Sume    ACPI_UNLOCK(thermal);
1201148138Sume    error = 0;
1202148138Sume    if (sc->tz_cooling_proc == NULL) {
1203209062Savg	error = kproc_create(acpi_tz_cooling_thread, sc,
1204209062Savg	    &sc->tz_cooling_proc, RFHIGHPID, 0, "acpi_cooling%d",
1205148138Sume	    device_get_unit(sc->tz_dev));
1206148703Sume	if (error != 0) {
1207148138Sume	    device_printf(sc->tz_dev, "could not create thread - %d", error);
1208148703Sume	    ACPI_LOCK(thermal);
1209148703Sume	    sc->tz_cooling_proc_running = FALSE;
1210148703Sume	    ACPI_UNLOCK(thermal);
1211148703Sume	}
1212148138Sume    }
1213148138Sume    return (error);
1214148138Sume}
1215