xref: /dflybsd-src/sys/dev/acpica/acpi_thermal.c (revision 944cd60c7b4392d637be82be7baafe9ac12a3061)
15db2f26eSSascha Wildner /*-
25db2f26eSSascha Wildner  * Copyright (c) 2000, 2001 Michael Smith
35db2f26eSSascha Wildner  * Copyright (c) 2000 BSDi
45db2f26eSSascha Wildner  * All rights reserved.
55db2f26eSSascha Wildner  *
65db2f26eSSascha Wildner  * Redistribution and use in source and binary forms, with or without
75db2f26eSSascha Wildner  * modification, are permitted provided that the following conditions
85db2f26eSSascha Wildner  * are met:
95db2f26eSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
105db2f26eSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
115db2f26eSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
125db2f26eSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
135db2f26eSSascha Wildner  *    documentation and/or other materials provided with the distribution.
145db2f26eSSascha Wildner  *
155db2f26eSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
165db2f26eSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
175db2f26eSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
185db2f26eSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
195db2f26eSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
205db2f26eSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
215db2f26eSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
225db2f26eSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
235db2f26eSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
245db2f26eSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
255db2f26eSSascha Wildner  * SUCH DAMAGE.
265db2f26eSSascha Wildner  *
273fe1280fSSascha Wildner  * $FreeBSD: head/sys/dev/acpica/acpi_thermal.c 255077 2013-08-30 19:21:12Z dumbbell $
285db2f26eSSascha Wildner  */
295db2f26eSSascha Wildner 
305db2f26eSSascha Wildner #include "opt_acpi.h"
315db2f26eSSascha Wildner #include <sys/param.h>
325db2f26eSSascha Wildner #include <sys/kernel.h>
335db2f26eSSascha Wildner #include <sys/bus.h>
345db2f26eSSascha Wildner #include <sys/kthread.h>
355db2f26eSSascha Wildner #include <sys/malloc.h>
365db2f26eSSascha Wildner #include <sys/module.h>
375db2f26eSSascha Wildner #include <sys/proc.h>
385db2f26eSSascha Wildner #include <sys/reboot.h>
395db2f26eSSascha Wildner #include <sys/sysctl.h>
405db2f26eSSascha Wildner #include <sys/unistd.h>
415db2f26eSSascha Wildner #include <sys/power.h>
425db2f26eSSascha Wildner #include <sys/sensors.h>
435db2f26eSSascha Wildner 
445db2f26eSSascha Wildner #include "acpi.h"
455db2f26eSSascha Wildner #include "accommon.h"
465db2f26eSSascha Wildner 
475db2f26eSSascha Wildner #include <dev/acpica/acpivar.h>
485db2f26eSSascha Wildner 
495db2f26eSSascha Wildner /* Hooks for the ACPICA debugging infrastructure */
505db2f26eSSascha Wildner #define _COMPONENT	ACPI_THERMAL
515db2f26eSSascha Wildner ACPI_MODULE_NAME("THERMAL")
525db2f26eSSascha Wildner 
535db2f26eSSascha Wildner #define TZ_ZEROC	2732
545db2f26eSSascha Wildner #define TZ_KELVTOC(x)	(((x) - TZ_ZEROC) / 10), abs(((x) - TZ_ZEROC) % 10)
555db2f26eSSascha Wildner 
565db2f26eSSascha Wildner #define TZ_NOTIFY_TEMPERATURE	0x80 /* Temperature changed. */
575db2f26eSSascha Wildner #define TZ_NOTIFY_LEVELS	0x81 /* Cooling levels changed. */
585db2f26eSSascha Wildner #define TZ_NOTIFY_DEVICES	0x82 /* Device lists changed. */
595db2f26eSSascha Wildner #define TZ_NOTIFY_CRITICAL	0xcc /* Fake notify that _CRT/_HOT reached. */
605db2f26eSSascha Wildner 
615db2f26eSSascha Wildner /* Check for temperature changes every 10 seconds by default */
625db2f26eSSascha Wildner #define TZ_POLLRATE	10
635db2f26eSSascha Wildner 
645db2f26eSSascha Wildner /* Make sure the reported temperature is valid for this number of polls. */
655db2f26eSSascha Wildner #define TZ_VALIDCHECKS	3
665db2f26eSSascha Wildner 
675db2f26eSSascha Wildner /* Notify the user we will be shutting down in one more poll cycle. */
685db2f26eSSascha Wildner #define TZ_NOTIFYCOUNT	(TZ_VALIDCHECKS - 1)
695db2f26eSSascha Wildner 
705db2f26eSSascha Wildner /* ACPI spec defines this */
715db2f26eSSascha Wildner #define TZ_NUMLEVELS	10
725db2f26eSSascha Wildner struct acpi_tz_zone {
735db2f26eSSascha Wildner     int		ac[TZ_NUMLEVELS];
745db2f26eSSascha Wildner     ACPI_BUFFER	al[TZ_NUMLEVELS];
755db2f26eSSascha Wildner     int		crt;
765db2f26eSSascha Wildner     int		hot;
775db2f26eSSascha Wildner     ACPI_BUFFER	psl;
785db2f26eSSascha Wildner     int		psv;
795db2f26eSSascha Wildner     int		tc1;
805db2f26eSSascha Wildner     int		tc2;
815db2f26eSSascha Wildner     int		tsp;
825db2f26eSSascha Wildner     int		tzp;
835db2f26eSSascha Wildner };
845db2f26eSSascha Wildner 
855db2f26eSSascha Wildner struct acpi_tz_softc {
865db2f26eSSascha Wildner     device_t			tz_dev;
875db2f26eSSascha Wildner     ACPI_HANDLE			tz_handle;	/*Thermal zone handle*/
885db2f26eSSascha Wildner     int				tz_temperature;	/*Current temperature*/
895db2f26eSSascha Wildner     int				tz_active;	/*Current active cooling*/
905db2f26eSSascha Wildner #define TZ_ACTIVE_NONE		-1
915db2f26eSSascha Wildner #define TZ_ACTIVE_UNKNOWN	-2
925db2f26eSSascha Wildner     int				tz_requested;	/*Minimum active cooling*/
935db2f26eSSascha Wildner     int				tz_thflags;	/*Current temp-related flags*/
945db2f26eSSascha Wildner #define TZ_THFLAG_NONE		0
955db2f26eSSascha Wildner #define TZ_THFLAG_PSV		(1<<0)
965db2f26eSSascha Wildner #define TZ_THFLAG_HOT		(1<<2)
975db2f26eSSascha Wildner #define TZ_THFLAG_CRT		(1<<3)
985db2f26eSSascha Wildner     int				tz_flags;
995db2f26eSSascha Wildner #define TZ_FLAG_NO_SCP		(1<<0)		/*No _SCP method*/
1005db2f26eSSascha Wildner #define TZ_FLAG_GETPROFILE	(1<<1)		/*Get power_profile in timeout*/
1015db2f26eSSascha Wildner #define TZ_FLAG_GETSETTINGS	(1<<2)		/*Get devs/setpoints*/
1025db2f26eSSascha Wildner     struct timespec		tz_cooling_started;
1035db2f26eSSascha Wildner 					/*Current cooling starting time*/
1045db2f26eSSascha Wildner 
1055db2f26eSSascha Wildner     struct sysctl_ctx_list	tz_sysctl_ctx;
1065db2f26eSSascha Wildner     struct sysctl_oid		*tz_sysctl_tree;
1075db2f26eSSascha Wildner     eventhandler_tag		tz_event;
1085db2f26eSSascha Wildner 
1095db2f26eSSascha Wildner     struct acpi_tz_zone 	tz_zone;	/*Thermal zone parameters*/
110905ed3cfSSascha Wildner     time_t			tz_error_time;	/*Lookup error timestamp*/
1115db2f26eSSascha Wildner     int				tz_validchecks;
1123fe1280fSSascha Wildner     int				tz_insane_tmp_notified;
1135db2f26eSSascha Wildner 
1145db2f26eSSascha Wildner     /* passive cooling */
1155db2f26eSSascha Wildner     struct thread		*tz_cooling_proc;
1165db2f26eSSascha Wildner     int				tz_cooling_proc_running;
1175db2f26eSSascha Wildner     int				tz_cooling_enabled;
1185db2f26eSSascha Wildner     int				tz_cooling_active;
1195db2f26eSSascha Wildner     int				tz_cooling_updated;
1205db2f26eSSascha Wildner     int				tz_cooling_saved_freq;
1215db2f26eSSascha Wildner     /* sensors(9) related */
1225db2f26eSSascha Wildner     struct ksensordev		sensordev;
1235db2f26eSSascha Wildner     struct ksensor		sensor;
1245db2f26eSSascha Wildner };
1255db2f26eSSascha Wildner 
126905ed3cfSSascha Wildner /* silence errors after X seconds, try again after Y seconds */
127531566e6SImre Vadász #define TZ_SILENCE_ERROR						\
128531566e6SImre Vadász     ((acpi_tz_polling_rate <= 0 ? TZ_POLLRATE : acpi_tz_polling_rate) * 2 + 1)
129905ed3cfSSascha Wildner #define TZ_RETRY_ERROR		7200
130905ed3cfSSascha Wildner 
1313fe1280fSSascha Wildner #define	TZ_ACTIVE_LEVEL(act)	((act) >= 0 ? (act) : TZ_NUMLEVELS)
1323fe1280fSSascha Wildner 
1335db2f26eSSascha Wildner #define CPUFREQ_MAX_LEVELS	64 /* XXX cpufreq should export this */
1345db2f26eSSascha Wildner 
1355db2f26eSSascha Wildner static int	acpi_tz_probe(device_t dev);
1365db2f26eSSascha Wildner static int	acpi_tz_attach(device_t dev);
1375db2f26eSSascha Wildner static int	acpi_tz_establish(struct acpi_tz_softc *sc);
1385db2f26eSSascha Wildner static void	acpi_tz_monitor(void *Context);
1395db2f26eSSascha Wildner static void	acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg);
1405db2f26eSSascha Wildner static void	acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg);
1415db2f26eSSascha Wildner static void	acpi_tz_getparam(struct acpi_tz_softc *sc, char *node,
1425db2f26eSSascha Wildner 				 int *data);
1435db2f26eSSascha Wildner static void	acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what);
144531566e6SImre Vadász static int	acpi_tz_polling_sysctl(SYSCTL_HANDLER_ARGS);
1455db2f26eSSascha Wildner static int	acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS);
1465db2f26eSSascha Wildner static int	acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS);
1475db2f26eSSascha Wildner static int	acpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS);
1485db2f26eSSascha Wildner static int	acpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS);
1495db2f26eSSascha Wildner static void	acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify,
1505db2f26eSSascha Wildner 				       void *context);
1515db2f26eSSascha Wildner static void	acpi_tz_signal(struct acpi_tz_softc *sc, int flags);
1525db2f26eSSascha Wildner static void	acpi_tz_timeout(struct acpi_tz_softc *sc, int flags);
1535db2f26eSSascha Wildner static void	acpi_tz_power_profile(void *arg);
1545db2f26eSSascha Wildner static void	acpi_tz_thread(void *arg);
1555db2f26eSSascha Wildner static int	acpi_tz_cooling_is_available(struct acpi_tz_softc *sc);
1565db2f26eSSascha Wildner static int	acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc);
1575db2f26eSSascha Wildner 
1585db2f26eSSascha Wildner static device_method_t acpi_tz_methods[] = {
1595db2f26eSSascha Wildner     /* Device interface */
1605db2f26eSSascha Wildner     DEVMETHOD(device_probe,	acpi_tz_probe),
1615db2f26eSSascha Wildner     DEVMETHOD(device_attach,	acpi_tz_attach),
1625db2f26eSSascha Wildner 
163d3c9c58eSSascha Wildner     DEVMETHOD_END
1645db2f26eSSascha Wildner };
1655db2f26eSSascha Wildner 
1665db2f26eSSascha Wildner static driver_t acpi_tz_driver = {
1675db2f26eSSascha Wildner     "acpi_tz",
1685db2f26eSSascha Wildner     acpi_tz_methods,
1695db2f26eSSascha Wildner     sizeof(struct acpi_tz_softc),
1705025fc65SMatthew Dillon     .gpri = KOBJ_GPRI_ACPI
1715db2f26eSSascha Wildner };
1725db2f26eSSascha Wildner 
1733fe1280fSSascha Wildner static char *acpi_tz_tmp_name = "_TMP";
1743fe1280fSSascha Wildner 
1755db2f26eSSascha Wildner static devclass_t acpi_tz_devclass;
1765db2f26eSSascha Wildner DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, NULL, NULL);
1775db2f26eSSascha Wildner MODULE_DEPEND(acpi_tz, acpi, 1, 1, 1);
1785db2f26eSSascha Wildner 
1795db2f26eSSascha Wildner static struct sysctl_ctx_list	acpi_tz_sysctl_ctx;
1805db2f26eSSascha Wildner static struct sysctl_oid	*acpi_tz_sysctl_tree;
1815db2f26eSSascha Wildner 
1825db2f26eSSascha Wildner /* Minimum cooling run time */
1835db2f26eSSascha Wildner static int			acpi_tz_min_runtime;
1845db2f26eSSascha Wildner static int			acpi_tz_polling_rate = TZ_POLLRATE;
1855db2f26eSSascha Wildner static int			acpi_tz_override;
1865db2f26eSSascha Wildner 
1875db2f26eSSascha Wildner /* Timezone polling thread */
1885db2f26eSSascha Wildner static struct thread		*acpi_tz_td;
1895db2f26eSSascha Wildner ACPI_LOCK_DECL(thermal, "ACPI thermal zone");
1905db2f26eSSascha Wildner 
1915db2f26eSSascha Wildner static int			acpi_tz_cooling_unit = -1;
1925db2f26eSSascha Wildner 
1935db2f26eSSascha Wildner static int
acpi_tz_probe(device_t dev)1945db2f26eSSascha Wildner acpi_tz_probe(device_t dev)
1955db2f26eSSascha Wildner {
1965db2f26eSSascha Wildner     int		result;
1975db2f26eSSascha Wildner 
1985db2f26eSSascha Wildner     if (acpi_get_type(dev) == ACPI_TYPE_THERMAL && !acpi_disabled("thermal")) {
1995db2f26eSSascha Wildner 	device_set_desc(dev, "Thermal Zone");
2005db2f26eSSascha Wildner 	result = -10;
2015db2f26eSSascha Wildner     } else
2025db2f26eSSascha Wildner 	result = ENXIO;
2035db2f26eSSascha Wildner     return (result);
2045db2f26eSSascha Wildner }
2055db2f26eSSascha Wildner 
2065db2f26eSSascha Wildner static int
acpi_tz_attach(device_t dev)2075db2f26eSSascha Wildner acpi_tz_attach(device_t dev)
2085db2f26eSSascha Wildner {
2095db2f26eSSascha Wildner     struct acpi_tz_softc	*sc;
2105db2f26eSSascha Wildner     struct acpi_softc		*acpi_sc;
2115db2f26eSSascha Wildner     int				error;
2125db2f26eSSascha Wildner     char			oidname[8];
2135db2f26eSSascha Wildner 
2145db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
21511f5cde4SImre Vadasz     if (device_get_unit(dev) == 0)
21611f5cde4SImre Vadasz 	ACPI_LOCK_INIT(thermal, "acpitz");
2175db2f26eSSascha Wildner 
2187252c37cSMatthew Dillon     ACPI_LOCK(thermal);
2197252c37cSMatthew Dillon 
2205db2f26eSSascha Wildner     sc = device_get_softc(dev);
2215db2f26eSSascha Wildner     sc->tz_dev = dev;
2225db2f26eSSascha Wildner     sc->tz_handle = acpi_get_handle(dev);
2235db2f26eSSascha Wildner     sc->tz_requested = TZ_ACTIVE_NONE;
2245db2f26eSSascha Wildner     sc->tz_active = TZ_ACTIVE_UNKNOWN;
2255db2f26eSSascha Wildner     sc->tz_thflags = TZ_THFLAG_NONE;
2265db2f26eSSascha Wildner     sc->tz_cooling_proc = NULL;
2275db2f26eSSascha Wildner     sc->tz_cooling_proc_running = FALSE;
2285db2f26eSSascha Wildner     sc->tz_cooling_active = FALSE;
2295db2f26eSSascha Wildner     sc->tz_cooling_updated = FALSE;
2305db2f26eSSascha Wildner     sc->tz_cooling_enabled = FALSE;
2315db2f26eSSascha Wildner 
2325db2f26eSSascha Wildner     /*
2335db2f26eSSascha Wildner      * Parse the current state of the thermal zone and build control
2345db2f26eSSascha Wildner      * structures.  We don't need to worry about interference with the
2355db2f26eSSascha Wildner      * control thread since we haven't fully attached this device yet.
2365db2f26eSSascha Wildner      */
2377252c37cSMatthew Dillon     if ((error = acpi_tz_establish(sc)) != 0) {
2387252c37cSMatthew Dillon 	ACPI_UNLOCK(thermal);
2395db2f26eSSascha Wildner 	return (error);
2407252c37cSMatthew Dillon     }
2415db2f26eSSascha Wildner 
2425db2f26eSSascha Wildner     /*
2435db2f26eSSascha Wildner      * Register for any Notify events sent to this zone.
2445db2f26eSSascha Wildner      */
2455db2f26eSSascha Wildner     AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
2465db2f26eSSascha Wildner 			     acpi_tz_notify_handler, sc);
2475db2f26eSSascha Wildner 
2485db2f26eSSascha Wildner     /*
2495db2f26eSSascha Wildner      * Create our sysctl nodes.
2505db2f26eSSascha Wildner      *
2515db2f26eSSascha Wildner      * XXX we need a mechanism for adding nodes under ACPI.
2525db2f26eSSascha Wildner      */
2535db2f26eSSascha Wildner     if (device_get_unit(dev) == 0) {
2545db2f26eSSascha Wildner 	acpi_sc = acpi_device_get_parent_softc(dev);
2555db2f26eSSascha Wildner 	sysctl_ctx_init(&acpi_tz_sysctl_ctx);
2565db2f26eSSascha Wildner 	acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx,
2575db2f26eSSascha Wildner 			      SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
2585db2f26eSSascha Wildner 			      OID_AUTO, "thermal", CTLFLAG_RD, 0, "");
2595db2f26eSSascha Wildner 	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
2605db2f26eSSascha Wildner 		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
2615db2f26eSSascha Wildner 		       OID_AUTO, "min_runtime", CTLFLAG_RW,
2625db2f26eSSascha Wildner 		       &acpi_tz_min_runtime, 0,
2635db2f26eSSascha Wildner 		       "minimum cooling run time in sec");
264531566e6SImre Vadász 	SYSCTL_ADD_PROC(&acpi_tz_sysctl_ctx,
2655db2f26eSSascha Wildner 		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
266531566e6SImre Vadász 		       OID_AUTO, "polling_rate", CTLTYPE_INT | CTLFLAG_RW,
267531566e6SImre Vadász 		       &acpi_tz_polling_rate, 0, acpi_tz_polling_sysctl,
268531566e6SImre Vadász 		       "I", "monitor polling interval in seconds");
2695db2f26eSSascha Wildner 	SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx,
2705db2f26eSSascha Wildner 		       SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO,
2715db2f26eSSascha Wildner 		       "user_override", CTLFLAG_RW, &acpi_tz_override, 0,
2725db2f26eSSascha Wildner 		       "allow override of thermal settings");
2735db2f26eSSascha Wildner     }
2745db2f26eSSascha Wildner     sysctl_ctx_init(&sc->tz_sysctl_ctx);
2755db2f26eSSascha Wildner     ksprintf(oidname, "tz%d", device_get_unit(dev));
2765db2f26eSSascha Wildner     sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx,
2775db2f26eSSascha Wildner 					 SYSCTL_CHILDREN(acpi_tz_sysctl_tree),
2785db2f26eSSascha Wildner 					 OID_AUTO, oidname, CTLFLAG_RD, 0, "");
2795db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
2805db2f26eSSascha Wildner 		    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
2815db2f26eSSascha Wildner 		    &sc->tz_temperature, 0, sysctl_handle_int,
2825db2f26eSSascha Wildner 		    "IK", "current thermal zone temperature");
2835db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
2845db2f26eSSascha Wildner 		    OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW,
2855db2f26eSSascha Wildner 		    sc, 0, acpi_tz_active_sysctl, "I", "cooling is active");
2865db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
2875db2f26eSSascha Wildner 		    OID_AUTO, "passive_cooling", CTLTYPE_INT | CTLFLAG_RW,
2885db2f26eSSascha Wildner 		    sc, 0, acpi_tz_cooling_sysctl, "I",
2895db2f26eSSascha Wildner 		    "enable passive (speed reduction) cooling");
2905db2f26eSSascha Wildner 
2915db2f26eSSascha Wildner     SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
2925db2f26eSSascha Wildner 		   OID_AUTO, "thermal_flags", CTLFLAG_RD,
2935db2f26eSSascha Wildner 		   &sc->tz_thflags, 0, "thermal zone flags");
2945db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
2955db2f26eSSascha Wildner 		    OID_AUTO, "_PSV", CTLTYPE_INT | CTLFLAG_RW,
2965db2f26eSSascha Wildner 		    sc, offsetof(struct acpi_tz_softc, tz_zone.psv),
2975db2f26eSSascha Wildner 		    acpi_tz_temp_sysctl, "IK", "passive cooling temp setpoint");
2985db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
2995db2f26eSSascha Wildner 		    OID_AUTO, "_HOT", CTLTYPE_INT | CTLFLAG_RW,
3005db2f26eSSascha Wildner 		    sc, offsetof(struct acpi_tz_softc, tz_zone.hot),
3015db2f26eSSascha Wildner 		    acpi_tz_temp_sysctl, "IK",
3025db2f26eSSascha Wildner 		    "too hot temp setpoint (suspend now)");
3035db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
3045db2f26eSSascha Wildner 		    OID_AUTO, "_CRT", CTLTYPE_INT | CTLFLAG_RW,
3055db2f26eSSascha Wildner 		    sc, offsetof(struct acpi_tz_softc, tz_zone.crt),
3065db2f26eSSascha Wildner 		    acpi_tz_temp_sysctl, "IK",
3075db2f26eSSascha Wildner 		    "critical temp setpoint (shutdown now)");
3085db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
3095db2f26eSSascha Wildner 		    OID_AUTO, "_ACx", CTLTYPE_INT | CTLFLAG_RD,
3105db2f26eSSascha Wildner 		    &sc->tz_zone.ac, sizeof(sc->tz_zone.ac),
3115db2f26eSSascha Wildner 		    sysctl_handle_opaque, "IK", "");
3125db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
3135db2f26eSSascha Wildner 		    OID_AUTO, "_TC1", CTLTYPE_INT | CTLFLAG_RW,
3145db2f26eSSascha Wildner 		    sc, offsetof(struct acpi_tz_softc, tz_zone.tc1),
3155db2f26eSSascha Wildner 		    acpi_tz_passive_sysctl, "I",
3165db2f26eSSascha Wildner 		    "thermal constant 1 for passive cooling");
3175db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
3185db2f26eSSascha Wildner 		    OID_AUTO, "_TC2", CTLTYPE_INT | CTLFLAG_RW,
3195db2f26eSSascha Wildner 		    sc, offsetof(struct acpi_tz_softc, tz_zone.tc2),
3205db2f26eSSascha Wildner 		    acpi_tz_passive_sysctl, "I",
3215db2f26eSSascha Wildner 		    "thermal constant 2 for passive cooling");
3225db2f26eSSascha Wildner     SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree),
3235db2f26eSSascha Wildner 		    OID_AUTO, "_TSP", CTLTYPE_INT | CTLFLAG_RW,
3245db2f26eSSascha Wildner 		    sc, offsetof(struct acpi_tz_softc, tz_zone.tsp),
3255db2f26eSSascha Wildner 		    acpi_tz_passive_sysctl, "I",
3265db2f26eSSascha Wildner 		    "thermal sampling period for passive cooling");
3275db2f26eSSascha Wildner 
3285db2f26eSSascha Wildner     /*
3295db2f26eSSascha Wildner      * Create thread to service all of the thermal zones.  Register
3305db2f26eSSascha Wildner      * our power profile event handler.
3315db2f26eSSascha Wildner      */
3325db2f26eSSascha Wildner     sc->tz_event = EVENTHANDLER_REGISTER(power_profile_change,
3335db2f26eSSascha Wildner 					 acpi_tz_power_profile, sc, 0);
3345db2f26eSSascha Wildner     if (acpi_tz_td == NULL) {
3355db2f26eSSascha Wildner 	error = kthread_create(acpi_tz_thread, NULL, &acpi_tz_td,
3365db2f26eSSascha Wildner 	    "acpi_thermal");
3375db2f26eSSascha Wildner 	if (error != 0) {
3385db2f26eSSascha Wildner 	    device_printf(sc->tz_dev, "could not create thread - %d", error);
3395db2f26eSSascha Wildner 	    goto out;
3405db2f26eSSascha Wildner 	}
3415db2f26eSSascha Wildner     }
3425db2f26eSSascha Wildner 
3435db2f26eSSascha Wildner     /*
3445db2f26eSSascha Wildner      * Create a thread to handle passive cooling for 1st zone which
3455db2f26eSSascha Wildner      * has _PSV, _TSP, _TC1 and _TC2.  Users can enable it for other
3465db2f26eSSascha Wildner      * zones manually for now.
3475db2f26eSSascha Wildner      *
3485db2f26eSSascha Wildner      * XXX We enable only one zone to avoid multiple zones conflict
3495db2f26eSSascha Wildner      * with each other since cpufreq currently sets all CPUs to the
3505db2f26eSSascha Wildner      * given frequency whereas it's possible for different thermal
3515db2f26eSSascha Wildner      * zones to specify independent settings for multiple CPUs.
3525db2f26eSSascha Wildner      */
3535db2f26eSSascha Wildner     if (acpi_tz_cooling_unit < 0 && acpi_tz_cooling_is_available(sc))
3545db2f26eSSascha Wildner 	sc->tz_cooling_enabled = TRUE;
3555db2f26eSSascha Wildner     if (sc->tz_cooling_enabled) {
3565db2f26eSSascha Wildner 	error = acpi_tz_cooling_thread_start(sc);
3575db2f26eSSascha Wildner 	if (error != 0) {
3585db2f26eSSascha Wildner 	    sc->tz_cooling_enabled = FALSE;
3595db2f26eSSascha Wildner 	    goto out;
3605db2f26eSSascha Wildner 	}
3615db2f26eSSascha Wildner 	acpi_tz_cooling_unit = device_get_unit(dev);
3625db2f26eSSascha Wildner     }
3635db2f26eSSascha Wildner 
3645db2f26eSSascha Wildner     /*
3655db2f26eSSascha Wildner      * Flag the event handler for a manual invocation by our timeout.
3665db2f26eSSascha Wildner      * We defer it like this so that the rest of the subsystem has time
3675db2f26eSSascha Wildner      * to come up.  Don't bother evaluating/printing the temperature at
3685db2f26eSSascha Wildner      * this point; on many systems it'll be bogus until the EC is running.
3695db2f26eSSascha Wildner      */
3705db2f26eSSascha Wildner     sc->tz_flags |= TZ_FLAG_GETPROFILE;
3715db2f26eSSascha Wildner 
3725db2f26eSSascha Wildner     /* Attach sensors(9). */
3735db2f26eSSascha Wildner     strlcpy(sc->sensordev.xname, device_get_nameunit(sc->tz_dev),
3745db2f26eSSascha Wildner         sizeof(sc->sensordev.xname));
3755db2f26eSSascha Wildner 
3765db2f26eSSascha Wildner     sc->sensor.type = SENSOR_TEMP;
3775db2f26eSSascha Wildner     sensor_attach(&sc->sensordev, &sc->sensor);
3785db2f26eSSascha Wildner 
3795db2f26eSSascha Wildner     sensordev_install(&sc->sensordev);
3805db2f26eSSascha Wildner 
3815db2f26eSSascha Wildner out:
3825db2f26eSSascha Wildner     if (error != 0) {
3835db2f26eSSascha Wildner 	EVENTHANDLER_DEREGISTER(power_profile_change, sc->tz_event);
3845db2f26eSSascha Wildner 	AcpiRemoveNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY,
3855db2f26eSSascha Wildner 	    acpi_tz_notify_handler);
3865db2f26eSSascha Wildner 	sysctl_ctx_free(&sc->tz_sysctl_ctx);
3875db2f26eSSascha Wildner     }
3887252c37cSMatthew Dillon     ACPI_UNLOCK(thermal);
3897252c37cSMatthew Dillon 
3905db2f26eSSascha Wildner     return_VALUE (error);
3915db2f26eSSascha Wildner }
3925db2f26eSSascha Wildner 
3935db2f26eSSascha Wildner /*
3945db2f26eSSascha Wildner  * Parse the current state of this thermal zone and set up to use it.
3955db2f26eSSascha Wildner  *
3965db2f26eSSascha Wildner  * Note that we may have previous state, which will have to be discarded.
3975db2f26eSSascha Wildner  */
3985db2f26eSSascha Wildner static int
acpi_tz_establish(struct acpi_tz_softc * sc)3995db2f26eSSascha Wildner acpi_tz_establish(struct acpi_tz_softc *sc)
4005db2f26eSSascha Wildner {
4015db2f26eSSascha Wildner     ACPI_OBJECT	*obj;
4025db2f26eSSascha Wildner     int		i;
4035db2f26eSSascha Wildner     char	nbuf[8];
4045db2f26eSSascha Wildner 
4055db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
4065db2f26eSSascha Wildner 
4075db2f26eSSascha Wildner     /* Erase any existing state. */
4085db2f26eSSascha Wildner     for (i = 0; i < TZ_NUMLEVELS; i++)
4095db2f26eSSascha Wildner 	if (sc->tz_zone.al[i].Pointer != NULL)
4105db2f26eSSascha Wildner 	    AcpiOsFree(sc->tz_zone.al[i].Pointer);
4115db2f26eSSascha Wildner     if (sc->tz_zone.psl.Pointer != NULL)
4125db2f26eSSascha Wildner 	AcpiOsFree(sc->tz_zone.psl.Pointer);
4135db2f26eSSascha Wildner 
4145db2f26eSSascha Wildner     /*
4155db2f26eSSascha Wildner      * XXX: We initialize only ACPI_BUFFER to avoid race condition
4165db2f26eSSascha Wildner      * with passive cooling thread which refers psv, tc1, tc2 and tsp.
4175db2f26eSSascha Wildner      */
4185db2f26eSSascha Wildner     bzero(sc->tz_zone.ac, sizeof(sc->tz_zone.ac));
4195db2f26eSSascha Wildner     bzero(sc->tz_zone.al, sizeof(sc->tz_zone.al));
4205db2f26eSSascha Wildner     bzero(&sc->tz_zone.psl, sizeof(sc->tz_zone.psl));
4215db2f26eSSascha Wildner 
4225db2f26eSSascha Wildner     /* Evaluate thermal zone parameters. */
4235db2f26eSSascha Wildner     for (i = 0; i < TZ_NUMLEVELS; i++) {
4245db2f26eSSascha Wildner 	ksprintf(nbuf, "_AC%d", i);
4255db2f26eSSascha Wildner 	acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]);
4265db2f26eSSascha Wildner 	ksprintf(nbuf, "_AL%d", i);
4275db2f26eSSascha Wildner 	sc->tz_zone.al[i].Length = ACPI_ALLOCATE_BUFFER;
4285db2f26eSSascha Wildner 	sc->tz_zone.al[i].Pointer = NULL;
4295db2f26eSSascha Wildner 	AcpiEvaluateObject(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]);
4305db2f26eSSascha Wildner 	obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer;
4315db2f26eSSascha Wildner 	if (obj != NULL) {
4325db2f26eSSascha Wildner 	    /* Should be a package containing a list of power objects */
4335db2f26eSSascha Wildner 	    if (obj->Type != ACPI_TYPE_PACKAGE) {
4345db2f26eSSascha Wildner 		device_printf(sc->tz_dev, "%s has unknown type %d, rejecting\n",
4355db2f26eSSascha Wildner 			      nbuf, obj->Type);
4365db2f26eSSascha Wildner 		return_VALUE (ENXIO);
4375db2f26eSSascha Wildner 	    }
4385db2f26eSSascha Wildner 	}
4395db2f26eSSascha Wildner     }
4405db2f26eSSascha Wildner     acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt);
4415db2f26eSSascha Wildner     acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot);
4425db2f26eSSascha Wildner     sc->tz_zone.psl.Length = ACPI_ALLOCATE_BUFFER;
4435db2f26eSSascha Wildner     sc->tz_zone.psl.Pointer = NULL;
4445db2f26eSSascha Wildner     AcpiEvaluateObject(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl);
4455db2f26eSSascha Wildner     acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv);
4465db2f26eSSascha Wildner     acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1);
4475db2f26eSSascha Wildner     acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2);
4485db2f26eSSascha Wildner     acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp);
4495db2f26eSSascha Wildner     acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp);
4505db2f26eSSascha Wildner 
4515db2f26eSSascha Wildner     /*
4525db2f26eSSascha Wildner      * Sanity-check the values we've been given.
4535db2f26eSSascha Wildner      *
4545db2f26eSSascha Wildner      * XXX what do we do about systems that give us the same value for
4555db2f26eSSascha Wildner      *     more than one of these setpoints?
4565db2f26eSSascha Wildner      */
4575db2f26eSSascha Wildner     acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT");
4585db2f26eSSascha Wildner     acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT");
4595db2f26eSSascha Wildner     acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV");
4605db2f26eSSascha Wildner     for (i = 0; i < TZ_NUMLEVELS; i++)
4615db2f26eSSascha Wildner 	acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx");
4625db2f26eSSascha Wildner 
4635db2f26eSSascha Wildner     return_VALUE (0);
4645db2f26eSSascha Wildner }
4655db2f26eSSascha Wildner 
4665db2f26eSSascha Wildner static char *aclevel_string[] = {
4675db2f26eSSascha Wildner     "NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4",
4685db2f26eSSascha Wildner     "_AC5", "_AC6", "_AC7", "_AC8", "_AC9"
4695db2f26eSSascha Wildner };
4705db2f26eSSascha Wildner 
4715db2f26eSSascha Wildner static __inline const char *
acpi_tz_aclevel_string(int active)4725db2f26eSSascha Wildner acpi_tz_aclevel_string(int active)
4735db2f26eSSascha Wildner {
4745db2f26eSSascha Wildner     if (active < -1 || active >= TZ_NUMLEVELS)
4755db2f26eSSascha Wildner 	return (aclevel_string[0]);
4765db2f26eSSascha Wildner 
4775db2f26eSSascha Wildner     return (aclevel_string[active + 1]);
4785db2f26eSSascha Wildner }
4795db2f26eSSascha Wildner 
4805db2f26eSSascha Wildner /*
4815db2f26eSSascha Wildner  * Get the current temperature.
4825db2f26eSSascha Wildner  */
4835db2f26eSSascha Wildner static int
acpi_tz_get_temperature(struct acpi_tz_softc * sc)4845db2f26eSSascha Wildner acpi_tz_get_temperature(struct acpi_tz_softc *sc)
4855db2f26eSSascha Wildner {
4865db2f26eSSascha Wildner     int		temp;
4875db2f26eSSascha Wildner     ACPI_STATUS	status;
4885db2f26eSSascha Wildner 
4895db2f26eSSascha Wildner     ACPI_FUNCTION_NAME ("acpi_tz_get_temperature");
4905db2f26eSSascha Wildner 
491905ed3cfSSascha Wildner     /*
492905ed3cfSSascha Wildner      * Silence lookup errors after 10 seconds, then retry every two hours.
493905ed3cfSSascha Wildner      */
494905ed3cfSSascha Wildner     if (sc->tz_error_time &&
495905ed3cfSSascha Wildner 	time_uptime - sc->tz_error_time > TZ_SILENCE_ERROR) {
496905ed3cfSSascha Wildner 	    if (time_uptime - sc->tz_error_time < TZ_RETRY_ERROR)
497905ed3cfSSascha Wildner 		return (FALSE);
498905ed3cfSSascha Wildner 	sc->tz_error_time = time_uptime - TZ_SILENCE_ERROR;
499905ed3cfSSascha Wildner     }
500905ed3cfSSascha Wildner 
5015db2f26eSSascha Wildner     /* Evaluate the thermal zone's _TMP method. */
5023fe1280fSSascha Wildner     status = acpi_GetInteger(sc->tz_handle, acpi_tz_tmp_name, &temp);
5035db2f26eSSascha Wildner     if (ACPI_FAILURE(status)) {
5045db2f26eSSascha Wildner 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
5055db2f26eSSascha Wildner 	    "error fetching current temperature -- %s\n",
5065db2f26eSSascha Wildner 	     AcpiFormatException(status));
507905ed3cfSSascha Wildner 	if (sc->tz_error_time == 0)
508905ed3cfSSascha Wildner 	    sc->tz_error_time = time_uptime;
5095db2f26eSSascha Wildner 	return (FALSE);
5105db2f26eSSascha Wildner     }
5115db2f26eSSascha Wildner 
5125db2f26eSSascha Wildner     /* Check it for validity. */
5133fe1280fSSascha Wildner     acpi_tz_sanity(sc, &temp, acpi_tz_tmp_name);
514905ed3cfSSascha Wildner     if (temp == -1) {
515905ed3cfSSascha Wildner 	if (sc->tz_error_time == 0)
516905ed3cfSSascha Wildner 	    sc->tz_error_time = time_uptime;
5175db2f26eSSascha Wildner 	return (FALSE);
518905ed3cfSSascha Wildner     }
5195db2f26eSSascha Wildner 
5205db2f26eSSascha Wildner     ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp)));
5215db2f26eSSascha Wildner     sc->tz_temperature = temp;
522905ed3cfSSascha Wildner     sc->tz_error_time = 0;
5235db2f26eSSascha Wildner     /* Update sensor */
5245db2f26eSSascha Wildner     if(sc->tz_temperature == -1)
5255db2f26eSSascha Wildner         sc->sensor.flags &= ~SENSOR_FINVALID;
5265db2f26eSSascha Wildner     sc->sensor.value = sc->tz_temperature * 100000 - 50000;
5275db2f26eSSascha Wildner     return (TRUE);
5285db2f26eSSascha Wildner }
5295db2f26eSSascha Wildner 
5305db2f26eSSascha Wildner /*
5315db2f26eSSascha Wildner  * Evaluate the condition of a thermal zone, take appropriate actions.
5325db2f26eSSascha Wildner  */
5335db2f26eSSascha Wildner static void
acpi_tz_monitor(void * Context)5345db2f26eSSascha Wildner acpi_tz_monitor(void *Context)
5355db2f26eSSascha Wildner {
5365db2f26eSSascha Wildner     struct acpi_tz_softc *sc;
5375db2f26eSSascha Wildner     struct	timespec curtime;
5385db2f26eSSascha Wildner     int		temp;
5395db2f26eSSascha Wildner     int		i;
5405db2f26eSSascha Wildner     int		newactive, newflags;
5415db2f26eSSascha Wildner 
5425db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
5435db2f26eSSascha Wildner 
5445db2f26eSSascha Wildner     sc = (struct acpi_tz_softc *)Context;
5455db2f26eSSascha Wildner 
5465db2f26eSSascha Wildner     /* Get the current temperature. */
5475db2f26eSSascha Wildner     if (!acpi_tz_get_temperature(sc)) {
5485db2f26eSSascha Wildner 	/* XXX disable zone? go to max cooling? */
5495db2f26eSSascha Wildner 	return_VOID;
5505db2f26eSSascha Wildner     }
5515db2f26eSSascha Wildner     temp = sc->tz_temperature;
5525db2f26eSSascha Wildner 
5535db2f26eSSascha Wildner     /*
5545db2f26eSSascha Wildner      * Work out what we ought to be doing right now.
5555db2f26eSSascha Wildner      *
5565db2f26eSSascha Wildner      * Note that the _ACx levels sort from hot to cold.
5575db2f26eSSascha Wildner      */
5585db2f26eSSascha Wildner     newactive = TZ_ACTIVE_NONE;
5595db2f26eSSascha Wildner     for (i = TZ_NUMLEVELS - 1; i >= 0; i--) {
5603fe1280fSSascha Wildner 	if (sc->tz_zone.ac[i] != -1 && temp >= sc->tz_zone.ac[i])
5615db2f26eSSascha Wildner 	    newactive = i;
5625db2f26eSSascha Wildner     }
5635db2f26eSSascha Wildner 
5645db2f26eSSascha Wildner     /*
5655db2f26eSSascha Wildner      * We are going to get _ACx level down (colder side), but give a guaranteed
5665db2f26eSSascha Wildner      * minimum cooling run time if requested.
5675db2f26eSSascha Wildner      */
5685db2f26eSSascha Wildner     if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE &&
5695db2f26eSSascha Wildner 	sc->tz_active != TZ_ACTIVE_UNKNOWN &&
5705db2f26eSSascha Wildner 	(newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) {
5715db2f26eSSascha Wildner 
5725db2f26eSSascha Wildner 	getnanotime(&curtime);
573*944cd60cSSascha Wildner 	timespecsub(&curtime, &sc->tz_cooling_started, &curtime);
5745db2f26eSSascha Wildner 	if (curtime.tv_sec < acpi_tz_min_runtime)
5755db2f26eSSascha Wildner 	    newactive = sc->tz_active;
5765db2f26eSSascha Wildner     }
5775db2f26eSSascha Wildner 
5785db2f26eSSascha Wildner     /* Handle user override of active mode */
5795db2f26eSSascha Wildner     if (sc->tz_requested != TZ_ACTIVE_NONE && (newactive == TZ_ACTIVE_NONE
5805db2f26eSSascha Wildner         || sc->tz_requested < newactive))
5815db2f26eSSascha Wildner 	newactive = sc->tz_requested;
5825db2f26eSSascha Wildner 
5835db2f26eSSascha Wildner     /* update temperature-related flags */
5845db2f26eSSascha Wildner     newflags = TZ_THFLAG_NONE;
5855db2f26eSSascha Wildner     if (sc->tz_zone.psv != -1 && temp >= sc->tz_zone.psv)
5865db2f26eSSascha Wildner 	newflags |= TZ_THFLAG_PSV;
5875db2f26eSSascha Wildner     if (sc->tz_zone.hot != -1 && temp >= sc->tz_zone.hot)
5885db2f26eSSascha Wildner 	newflags |= TZ_THFLAG_HOT;
5895db2f26eSSascha Wildner     if (sc->tz_zone.crt != -1 && temp >= sc->tz_zone.crt)
5905db2f26eSSascha Wildner 	newflags |= TZ_THFLAG_CRT;
5915db2f26eSSascha Wildner 
5925db2f26eSSascha Wildner     /* If the active cooling state has changed, we have to switch things. */
5935db2f26eSSascha Wildner     if (sc->tz_active == TZ_ACTIVE_UNKNOWN) {
5945db2f26eSSascha Wildner 	/*
5955db2f26eSSascha Wildner 	 * We don't know which cooling device is on or off,
5965db2f26eSSascha Wildner 	 * so stop them all, because we now know which
5975db2f26eSSascha Wildner 	 * should be on (if any).
5985db2f26eSSascha Wildner 	 */
5995db2f26eSSascha Wildner 	for (i = 0; i < TZ_NUMLEVELS; i++) {
6005db2f26eSSascha Wildner 	    if (sc->tz_zone.al[i].Pointer != NULL) {
6015db2f26eSSascha Wildner 		acpi_ForeachPackageObject(
6025db2f26eSSascha Wildner 		    (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
6035db2f26eSSascha Wildner 		    acpi_tz_switch_cooler_off, sc);
6045db2f26eSSascha Wildner 	    }
6055db2f26eSSascha Wildner 	}
6065db2f26eSSascha Wildner 	/* now we know that all devices are off */
6075db2f26eSSascha Wildner 	sc->tz_active = TZ_ACTIVE_NONE;
6085db2f26eSSascha Wildner     }
6095db2f26eSSascha Wildner 
6105db2f26eSSascha Wildner     if (newactive != sc->tz_active) {
6113fe1280fSSascha Wildner 	/* Turn off unneeded cooling devices that are on, if any are */
6123fe1280fSSascha Wildner 	for (i = TZ_ACTIVE_LEVEL(sc->tz_active);
6133fe1280fSSascha Wildner 	     i < TZ_ACTIVE_LEVEL(newactive); i++) {
6145db2f26eSSascha Wildner 	    acpi_ForeachPackageObject(
6153fe1280fSSascha Wildner 		(ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
6165db2f26eSSascha Wildner 		acpi_tz_switch_cooler_off, sc);
6173fe1280fSSascha Wildner 	}
6185db2f26eSSascha Wildner 	/* Turn on cooling devices that are required, if any are */
6193fe1280fSSascha Wildner 	for (i = TZ_ACTIVE_LEVEL(sc->tz_active) - 1;
6203fe1280fSSascha Wildner 	     i >= TZ_ACTIVE_LEVEL(newactive); i--) {
6215db2f26eSSascha Wildner 	    acpi_ForeachPackageObject(
6223fe1280fSSascha Wildner 		(ACPI_OBJECT *)sc->tz_zone.al[i].Pointer,
6235db2f26eSSascha Wildner 		acpi_tz_switch_cooler_on, sc);
6245db2f26eSSascha Wildner 	}
6253fe1280fSSascha Wildner 
6265db2f26eSSascha Wildner 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
6275db2f26eSSascha Wildner 		    "switched from %s to %s: %d.%dC\n",
6285db2f26eSSascha Wildner 		    acpi_tz_aclevel_string(sc->tz_active),
6295db2f26eSSascha Wildner 		    acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp));
6305db2f26eSSascha Wildner 	sc->tz_active = newactive;
6315db2f26eSSascha Wildner 	getnanotime(&sc->tz_cooling_started);
6325db2f26eSSascha Wildner     }
6335db2f26eSSascha Wildner 
6345db2f26eSSascha Wildner     /* XXX (de)activate any passive cooling that may be required. */
6355db2f26eSSascha Wildner 
6365db2f26eSSascha Wildner     /*
6375db2f26eSSascha Wildner      * If the temperature is at _HOT or _CRT, increment our event count.
6385db2f26eSSascha Wildner      * If it has occurred enough times, shutdown the system.  This is
6395db2f26eSSascha Wildner      * needed because some systems will report an invalid high temperature
6405db2f26eSSascha Wildner      * for one poll cycle.  It is suspected this is due to the embedded
6415db2f26eSSascha Wildner      * controller timing out.  A typical value is 138C for one cycle on
6425db2f26eSSascha Wildner      * a system that is otherwise 65C.
6435db2f26eSSascha Wildner      *
6445db2f26eSSascha Wildner      * If we're almost at that threshold, notify the user through devd(8).
6455db2f26eSSascha Wildner      */
6465db2f26eSSascha Wildner     if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) != 0) {
6475db2f26eSSascha Wildner 	sc->tz_validchecks++;
6485db2f26eSSascha Wildner 	if (sc->tz_validchecks == TZ_VALIDCHECKS) {
6495db2f26eSSascha Wildner 	    device_printf(sc->tz_dev,
6505db2f26eSSascha Wildner 		"WARNING - current temperature (%d.%dC) exceeds safe limits\n",
6515db2f26eSSascha Wildner 		TZ_KELVTOC(sc->tz_temperature));
6525db2f26eSSascha Wildner 	    shutdown_nice(RB_POWEROFF);
6535db2f26eSSascha Wildner 	} else if (sc->tz_validchecks == TZ_NOTIFYCOUNT)
6545db2f26eSSascha Wildner 	    acpi_UserNotify("Thermal", sc->tz_handle, TZ_NOTIFY_CRITICAL);
6555db2f26eSSascha Wildner     } else {
6565db2f26eSSascha Wildner 	sc->tz_validchecks = 0;
6575db2f26eSSascha Wildner     }
6585db2f26eSSascha Wildner     sc->tz_thflags = newflags;
6595db2f26eSSascha Wildner 
6605db2f26eSSascha Wildner     return_VOID;
6615db2f26eSSascha Wildner }
6625db2f26eSSascha Wildner 
6635db2f26eSSascha Wildner /*
6645db2f26eSSascha Wildner  * Given an object, verify that it's a reference to a device of some sort,
6655db2f26eSSascha Wildner  * and try to switch it off.
6665db2f26eSSascha Wildner  */
6675db2f26eSSascha Wildner static void
acpi_tz_switch_cooler_off(ACPI_OBJECT * obj,void * arg)6685db2f26eSSascha Wildner acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg)
6695db2f26eSSascha Wildner {
6705db2f26eSSascha Wildner     ACPI_HANDLE			cooler;
6715db2f26eSSascha Wildner 
6725db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
6735db2f26eSSascha Wildner 
6745db2f26eSSascha Wildner     cooler = acpi_GetReference(NULL, obj);
6755db2f26eSSascha Wildner     if (cooler == NULL) {
6765db2f26eSSascha Wildner 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
6775db2f26eSSascha Wildner 	return_VOID;
6785db2f26eSSascha Wildner     }
6795db2f26eSSascha Wildner 
6805db2f26eSSascha Wildner     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n",
6815db2f26eSSascha Wildner 		     acpi_name(cooler)));
6825db2f26eSSascha Wildner     acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3);
6835db2f26eSSascha Wildner 
6845db2f26eSSascha Wildner     return_VOID;
6855db2f26eSSascha Wildner }
6865db2f26eSSascha Wildner 
6875db2f26eSSascha Wildner /*
6885db2f26eSSascha Wildner  * Given an object, verify that it's a reference to a device of some sort,
6895db2f26eSSascha Wildner  * and try to switch it on.
6905db2f26eSSascha Wildner  *
6915db2f26eSSascha Wildner  * XXX replication of off/on function code is bad.
6925db2f26eSSascha Wildner  */
6935db2f26eSSascha Wildner static void
acpi_tz_switch_cooler_on(ACPI_OBJECT * obj,void * arg)6945db2f26eSSascha Wildner acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg)
6955db2f26eSSascha Wildner {
6965db2f26eSSascha Wildner     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
6975db2f26eSSascha Wildner     ACPI_HANDLE			cooler;
6985db2f26eSSascha Wildner     ACPI_STATUS			status;
6995db2f26eSSascha Wildner 
7005db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
7015db2f26eSSascha Wildner 
7025db2f26eSSascha Wildner     cooler = acpi_GetReference(NULL, obj);
7035db2f26eSSascha Wildner     if (cooler == NULL) {
7045db2f26eSSascha Wildner 	ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get handle\n"));
7055db2f26eSSascha Wildner 	return_VOID;
7065db2f26eSSascha Wildner     }
7075db2f26eSSascha Wildner 
7085db2f26eSSascha Wildner     ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n",
7095db2f26eSSascha Wildner 		     acpi_name(cooler)));
7105db2f26eSSascha Wildner     status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0);
7115db2f26eSSascha Wildner     if (ACPI_FAILURE(status)) {
7125db2f26eSSascha Wildner 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
7135db2f26eSSascha Wildner 		    "failed to activate %s - %s\n", acpi_name(cooler),
7145db2f26eSSascha Wildner 		    AcpiFormatException(status));
7155db2f26eSSascha Wildner     }
7165db2f26eSSascha Wildner 
7175db2f26eSSascha Wildner     return_VOID;
7185db2f26eSSascha Wildner }
7195db2f26eSSascha Wildner 
7205db2f26eSSascha Wildner /*
7215db2f26eSSascha Wildner  * Read/debug-print a parameter, default it to -1.
7225db2f26eSSascha Wildner  */
7235db2f26eSSascha Wildner static void
acpi_tz_getparam(struct acpi_tz_softc * sc,char * node,int * data)7245db2f26eSSascha Wildner acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data)
7255db2f26eSSascha Wildner {
7265db2f26eSSascha Wildner 
7275db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
7285db2f26eSSascha Wildner 
7295db2f26eSSascha Wildner     if (ACPI_FAILURE(acpi_GetInteger(sc->tz_handle, node, data))) {
7305db2f26eSSascha Wildner 	*data = -1;
7315db2f26eSSascha Wildner     } else {
7325db2f26eSSascha Wildner 	ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n",
7335db2f26eSSascha Wildner 			 acpi_name(sc->tz_handle), node, *data));
7345db2f26eSSascha Wildner     }
7355db2f26eSSascha Wildner 
7365db2f26eSSascha Wildner     return_VOID;
7375db2f26eSSascha Wildner }
7385db2f26eSSascha Wildner 
7395db2f26eSSascha Wildner /*
7407cb11cedSSascha Wildner  * Handle sysctl for reading and changing the thermal-zone polling rate.
741531566e6SImre Vadász  */
742531566e6SImre Vadász static int
acpi_tz_polling_sysctl(SYSCTL_HANDLER_ARGS)743531566e6SImre Vadász acpi_tz_polling_sysctl(SYSCTL_HANDLER_ARGS)
744531566e6SImre Vadász {
745531566e6SImre Vadász     int val, error;
746531566e6SImre Vadász 
747531566e6SImre Vadász     val = acpi_tz_polling_rate;
748531566e6SImre Vadász     error = sysctl_handle_int(oidp, &val, 0, req);
749531566e6SImre Vadász 
750531566e6SImre Vadász     /* Error or no new value */
751531566e6SImre Vadász     if (error != 0 || req->newptr == NULL)
752531566e6SImre Vadász 	return (error);
753531566e6SImre Vadász     if (val < 0 || val > 3600)
754531566e6SImre Vadász 	return (EINVAL);
755531566e6SImre Vadász 
756531566e6SImre Vadász     acpi_tz_polling_rate = val;
757531566e6SImre Vadász     wakeup(&acpi_tz_td);
758531566e6SImre Vadász     return (error);
759531566e6SImre Vadász }
760531566e6SImre Vadász 
761531566e6SImre Vadász /*
7625db2f26eSSascha Wildner  * Sanity-check a temperature value.  Assume that setpoints
7635db2f26eSSascha Wildner  * should be between 0C and 200C.
7645db2f26eSSascha Wildner  */
7655db2f26eSSascha Wildner static void
acpi_tz_sanity(struct acpi_tz_softc * sc,int * val,char * what)7665db2f26eSSascha Wildner acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what)
7675db2f26eSSascha Wildner {
7685db2f26eSSascha Wildner     if (*val != -1 && (*val < TZ_ZEROC || *val > TZ_ZEROC + 2000)) {
7693fe1280fSSascha Wildner 	/*
7703fe1280fSSascha Wildner 	 * If the value we are checking is _TMP, warn the user only
7713fe1280fSSascha Wildner 	 * once. This avoids spamming messages if, for instance, the
7723fe1280fSSascha Wildner 	 * sensor is broken and always returns an invalid temperature.
7733fe1280fSSascha Wildner 	 *
7743fe1280fSSascha Wildner 	 * This is only done for _TMP; other values always emit a
7753fe1280fSSascha Wildner 	 * warning.
7763fe1280fSSascha Wildner 	 */
7773fe1280fSSascha Wildner 	if (what != acpi_tz_tmp_name || !sc->tz_insane_tmp_notified) {
7785db2f26eSSascha Wildner 	    device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n",
7795db2f26eSSascha Wildner 			  what, TZ_KELVTOC(*val));
7803fe1280fSSascha Wildner 
7813fe1280fSSascha Wildner 	    /* Don't warn the user again if the read value doesn't improve. */
7823fe1280fSSascha Wildner 	    if (what == acpi_tz_tmp_name)
7833fe1280fSSascha Wildner 		sc->tz_insane_tmp_notified = 1;
7845db2f26eSSascha Wildner 	}
7853fe1280fSSascha Wildner 	*val = -1;
7863fe1280fSSascha Wildner 	return;
7873fe1280fSSascha Wildner     }
7883fe1280fSSascha Wildner 
7893fe1280fSSascha Wildner     /* This value is correct. Warn if it's incorrect again. */
7903fe1280fSSascha Wildner     if (what == acpi_tz_tmp_name)
7913fe1280fSSascha Wildner 	sc->tz_insane_tmp_notified = 0;
7925db2f26eSSascha Wildner }
7935db2f26eSSascha Wildner 
7945db2f26eSSascha Wildner /*
7955db2f26eSSascha Wildner  * Respond to a sysctl on the active state node.
7965db2f26eSSascha Wildner  */
7975db2f26eSSascha Wildner static int
acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)7985db2f26eSSascha Wildner acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS)
7995db2f26eSSascha Wildner {
8005db2f26eSSascha Wildner     struct acpi_tz_softc	*sc;
8015db2f26eSSascha Wildner     int				active;
8025db2f26eSSascha Wildner     int		 		error;
8035db2f26eSSascha Wildner 
8045db2f26eSSascha Wildner     sc = (struct acpi_tz_softc *)oidp->oid_arg1;
8055db2f26eSSascha Wildner     active = sc->tz_active;
8065db2f26eSSascha Wildner     error = sysctl_handle_int(oidp, &active, 0, req);
8075db2f26eSSascha Wildner 
8085db2f26eSSascha Wildner     /* Error or no new value */
8095db2f26eSSascha Wildner     if (error != 0 || req->newptr == NULL)
8105db2f26eSSascha Wildner 	return (error);
8115db2f26eSSascha Wildner     if (active < -1 || active >= TZ_NUMLEVELS)
8125db2f26eSSascha Wildner 	return (EINVAL);
8135db2f26eSSascha Wildner 
8145db2f26eSSascha Wildner     /* Set new preferred level and re-switch */
8155db2f26eSSascha Wildner     sc->tz_requested = active;
8165db2f26eSSascha Wildner     acpi_tz_signal(sc, 0);
8175db2f26eSSascha Wildner     return (0);
8185db2f26eSSascha Wildner }
8195db2f26eSSascha Wildner 
8205db2f26eSSascha Wildner static int
acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS)8215db2f26eSSascha Wildner acpi_tz_cooling_sysctl(SYSCTL_HANDLER_ARGS)
8225db2f26eSSascha Wildner {
8235db2f26eSSascha Wildner     struct acpi_tz_softc *sc;
8245db2f26eSSascha Wildner     int enabled, error;
8255db2f26eSSascha Wildner 
8265db2f26eSSascha Wildner     sc = (struct acpi_tz_softc *)oidp->oid_arg1;
8275db2f26eSSascha Wildner     enabled = sc->tz_cooling_enabled;
8285db2f26eSSascha Wildner     error = sysctl_handle_int(oidp, &enabled, 0, req);
8295db2f26eSSascha Wildner 
8305db2f26eSSascha Wildner     /* Error or no new value */
8315db2f26eSSascha Wildner     if (error != 0 || req->newptr == NULL)
8325db2f26eSSascha Wildner 	return (error);
8335db2f26eSSascha Wildner     if (enabled != TRUE && enabled != FALSE)
8345db2f26eSSascha Wildner 	return (EINVAL);
8355db2f26eSSascha Wildner 
8365db2f26eSSascha Wildner     if (enabled) {
8375db2f26eSSascha Wildner 	if (acpi_tz_cooling_is_available(sc))
8385db2f26eSSascha Wildner 	    error = acpi_tz_cooling_thread_start(sc);
8395db2f26eSSascha Wildner 	else
8405db2f26eSSascha Wildner 	    error = ENODEV;
8415db2f26eSSascha Wildner 	if (error)
8425db2f26eSSascha Wildner 	    enabled = FALSE;
8435db2f26eSSascha Wildner     }
8445db2f26eSSascha Wildner     sc->tz_cooling_enabled = enabled;
8455db2f26eSSascha Wildner     return (error);
8465db2f26eSSascha Wildner }
8475db2f26eSSascha Wildner 
8485db2f26eSSascha Wildner static int
acpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS)8495db2f26eSSascha Wildner acpi_tz_temp_sysctl(SYSCTL_HANDLER_ARGS)
8505db2f26eSSascha Wildner {
8515db2f26eSSascha Wildner     struct acpi_tz_softc	*sc;
8525db2f26eSSascha Wildner     int				temp, *temp_ptr;
8535db2f26eSSascha Wildner     int		 		error;
8545db2f26eSSascha Wildner 
8555db2f26eSSascha Wildner     sc = oidp->oid_arg1;
8565db2f26eSSascha Wildner     temp_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2);
8575db2f26eSSascha Wildner     temp = *temp_ptr;
8585db2f26eSSascha Wildner     error = sysctl_handle_int(oidp, &temp, 0, req);
8595db2f26eSSascha Wildner 
8605db2f26eSSascha Wildner     /* Error or no new value */
8615db2f26eSSascha Wildner     if (error != 0 || req->newptr == NULL)
8625db2f26eSSascha Wildner 	return (error);
8635db2f26eSSascha Wildner 
8645db2f26eSSascha Wildner     /* Only allow changing settings if override is set. */
8655db2f26eSSascha Wildner     if (!acpi_tz_override)
8665db2f26eSSascha Wildner 	return (EPERM);
8675db2f26eSSascha Wildner 
8685db2f26eSSascha Wildner     /* Check user-supplied value for sanity. */
8695db2f26eSSascha Wildner     acpi_tz_sanity(sc, &temp, "user-supplied temp");
8705db2f26eSSascha Wildner     if (temp == -1)
8715db2f26eSSascha Wildner 	return (EINVAL);
8725db2f26eSSascha Wildner 
8735db2f26eSSascha Wildner     *temp_ptr = temp;
8745db2f26eSSascha Wildner     return (0);
8755db2f26eSSascha Wildner }
8765db2f26eSSascha Wildner 
8775db2f26eSSascha Wildner static int
acpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS)8785db2f26eSSascha Wildner acpi_tz_passive_sysctl(SYSCTL_HANDLER_ARGS)
8795db2f26eSSascha Wildner {
8805db2f26eSSascha Wildner     struct acpi_tz_softc	*sc;
8815db2f26eSSascha Wildner     int				val, *val_ptr;
8825db2f26eSSascha Wildner     int				error;
8835db2f26eSSascha Wildner 
8845db2f26eSSascha Wildner     sc = oidp->oid_arg1;
8855db2f26eSSascha Wildner     val_ptr = (int *)((uintptr_t)sc + oidp->oid_arg2);
8865db2f26eSSascha Wildner     val = *val_ptr;
8875db2f26eSSascha Wildner     error = sysctl_handle_int(oidp, &val, 0, req);
8885db2f26eSSascha Wildner 
8895db2f26eSSascha Wildner     /* Error or no new value */
8905db2f26eSSascha Wildner     if (error != 0 || req->newptr == NULL)
8915db2f26eSSascha Wildner 	return (error);
8925db2f26eSSascha Wildner 
8935db2f26eSSascha Wildner     /* Only allow changing settings if override is set. */
8945db2f26eSSascha Wildner     if (!acpi_tz_override)
8955db2f26eSSascha Wildner 	return (EPERM);
8965db2f26eSSascha Wildner 
8975db2f26eSSascha Wildner     *val_ptr = val;
8985db2f26eSSascha Wildner     return (0);
8995db2f26eSSascha Wildner }
9005db2f26eSSascha Wildner 
9015db2f26eSSascha Wildner static void
acpi_tz_notify_handler(ACPI_HANDLE h,UINT32 notify,void * context)9025db2f26eSSascha Wildner acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
9035db2f26eSSascha Wildner {
9045db2f26eSSascha Wildner     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)context;
9055db2f26eSSascha Wildner 
9065db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
9075db2f26eSSascha Wildner 
9085db2f26eSSascha Wildner     switch (notify) {
9095db2f26eSSascha Wildner     case TZ_NOTIFY_TEMPERATURE:
9105db2f26eSSascha Wildner 	/* Temperature change occurred */
9115db2f26eSSascha Wildner 	acpi_tz_signal(sc, 0);
9125db2f26eSSascha Wildner 	break;
9135db2f26eSSascha Wildner     case TZ_NOTIFY_DEVICES:
9145db2f26eSSascha Wildner     case TZ_NOTIFY_LEVELS:
9155db2f26eSSascha Wildner 	/* Zone devices/setpoints changed */
9165db2f26eSSascha Wildner 	acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS);
9175db2f26eSSascha Wildner 	break;
9185db2f26eSSascha Wildner     default:
919d1fb95bdSSascha Wildner 	device_printf(sc->tz_dev, "unknown notify: %#x\n", notify);
9205db2f26eSSascha Wildner 	break;
9215db2f26eSSascha Wildner     }
9225db2f26eSSascha Wildner 
9235db2f26eSSascha Wildner     acpi_UserNotify("Thermal", h, notify);
9245db2f26eSSascha Wildner 
9255db2f26eSSascha Wildner     return_VOID;
9265db2f26eSSascha Wildner }
9275db2f26eSSascha Wildner 
9285db2f26eSSascha Wildner static void
acpi_tz_signal(struct acpi_tz_softc * sc,int flags)9295db2f26eSSascha Wildner acpi_tz_signal(struct acpi_tz_softc *sc, int flags)
9305db2f26eSSascha Wildner {
9315db2f26eSSascha Wildner     ACPI_LOCK(thermal);
9325db2f26eSSascha Wildner     sc->tz_flags |= flags;
9335db2f26eSSascha Wildner     ACPI_UNLOCK(thermal);
9345db2f26eSSascha Wildner     wakeup(&acpi_tz_td);
9355db2f26eSSascha Wildner }
9365db2f26eSSascha Wildner 
9375db2f26eSSascha Wildner /*
9385db2f26eSSascha Wildner  * Notifies can be generated asynchronously but have also been seen to be
9395db2f26eSSascha Wildner  * triggered by other thermal methods.  One system generates a notify of
9405db2f26eSSascha Wildner  * 0x81 when the fan is turned on or off.  Another generates it when _SCP
9415db2f26eSSascha Wildner  * is called.  To handle these situations, we check the zone via
9425db2f26eSSascha Wildner  * acpi_tz_monitor() before evaluating changes to setpoints or the cooling
9435db2f26eSSascha Wildner  * policy.
9445db2f26eSSascha Wildner  */
9455db2f26eSSascha Wildner static void
acpi_tz_timeout(struct acpi_tz_softc * sc,int flags)9465db2f26eSSascha Wildner acpi_tz_timeout(struct acpi_tz_softc *sc, int flags)
9475db2f26eSSascha Wildner {
9485db2f26eSSascha Wildner 
9495db2f26eSSascha Wildner     /* Check the current temperature and take action based on it */
9505db2f26eSSascha Wildner     acpi_tz_monitor(sc);
9515db2f26eSSascha Wildner 
9525db2f26eSSascha Wildner     /* If requested, get the power profile settings. */
9535db2f26eSSascha Wildner     if (flags & TZ_FLAG_GETPROFILE)
9545db2f26eSSascha Wildner 	acpi_tz_power_profile(sc);
9555db2f26eSSascha Wildner 
9565db2f26eSSascha Wildner     /*
9575db2f26eSSascha Wildner      * If requested, check for new devices/setpoints.  After finding them,
9585db2f26eSSascha Wildner      * check if we need to switch fans based on the new values.
9595db2f26eSSascha Wildner      */
9605db2f26eSSascha Wildner     if (flags & TZ_FLAG_GETSETTINGS) {
9615db2f26eSSascha Wildner 	acpi_tz_establish(sc);
9625db2f26eSSascha Wildner 	acpi_tz_monitor(sc);
9635db2f26eSSascha Wildner     }
9645db2f26eSSascha Wildner 
9655db2f26eSSascha Wildner     /* XXX passive cooling actions? */
9665db2f26eSSascha Wildner }
9675db2f26eSSascha Wildner 
9685db2f26eSSascha Wildner /*
9695db2f26eSSascha Wildner  * System power profile may have changed; fetch and notify the
9705db2f26eSSascha Wildner  * thermal zone accordingly.
9715db2f26eSSascha Wildner  *
9725db2f26eSSascha Wildner  * Since this can be called from an arbitrary eventhandler, it needs
9735db2f26eSSascha Wildner  * to get the ACPI lock itself.
9745db2f26eSSascha Wildner  */
9755db2f26eSSascha Wildner static void
acpi_tz_power_profile(void * arg)9765db2f26eSSascha Wildner acpi_tz_power_profile(void *arg)
9775db2f26eSSascha Wildner {
9785db2f26eSSascha Wildner     ACPI_STATUS			status;
9795db2f26eSSascha Wildner     struct acpi_tz_softc	*sc = (struct acpi_tz_softc *)arg;
9805db2f26eSSascha Wildner     int				state;
9815db2f26eSSascha Wildner 
9825db2f26eSSascha Wildner     state = power_profile_get_state();
9835db2f26eSSascha Wildner     if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
9845db2f26eSSascha Wildner 	return;
9855db2f26eSSascha Wildner 
9865db2f26eSSascha Wildner     /* check that we haven't decided there's no _SCP method */
9875db2f26eSSascha Wildner     if ((sc->tz_flags & TZ_FLAG_NO_SCP) == 0) {
9885db2f26eSSascha Wildner 
9895db2f26eSSascha Wildner 	/* Call _SCP to set the new profile */
9905db2f26eSSascha Wildner 	status = acpi_SetInteger(sc->tz_handle, "_SCP",
9915db2f26eSSascha Wildner 	    (state == POWER_PROFILE_PERFORMANCE) ? 0 : 1);
9925db2f26eSSascha Wildner 	if (ACPI_FAILURE(status)) {
9935db2f26eSSascha Wildner 	    if (status != AE_NOT_FOUND)
9945db2f26eSSascha Wildner 		ACPI_VPRINT(sc->tz_dev,
9955db2f26eSSascha Wildner 			    acpi_device_get_parent_softc(sc->tz_dev),
9965db2f26eSSascha Wildner 			    "can't evaluate %s._SCP - %s\n",
9975db2f26eSSascha Wildner 			    acpi_name(sc->tz_handle),
9985db2f26eSSascha Wildner 			    AcpiFormatException(status));
9995db2f26eSSascha Wildner 	    sc->tz_flags |= TZ_FLAG_NO_SCP;
10005db2f26eSSascha Wildner 	} else {
10015db2f26eSSascha Wildner 	    /* We have to re-evaluate the entire zone now */
10025db2f26eSSascha Wildner 	    acpi_tz_signal(sc, TZ_FLAG_GETSETTINGS);
10035db2f26eSSascha Wildner 	}
10045db2f26eSSascha Wildner     }
10055db2f26eSSascha Wildner }
10065db2f26eSSascha Wildner 
10075db2f26eSSascha Wildner /*
10085db2f26eSSascha Wildner  * Thermal zone monitor thread.
10095db2f26eSSascha Wildner  */
10105db2f26eSSascha Wildner static void
acpi_tz_thread(void * arg)10115db2f26eSSascha Wildner acpi_tz_thread(void *arg)
10125db2f26eSSascha Wildner {
10135db2f26eSSascha Wildner     device_t	*devs;
10145db2f26eSSascha Wildner     int		devcount, i;
10155db2f26eSSascha Wildner     int		flags;
10165db2f26eSSascha Wildner     struct acpi_tz_softc **sc;
10175db2f26eSSascha Wildner 
10185db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
10195db2f26eSSascha Wildner 
10205db2f26eSSascha Wildner     devs = NULL;
10215db2f26eSSascha Wildner     devcount = 0;
10225db2f26eSSascha Wildner     sc = NULL;
10235db2f26eSSascha Wildner 
10247252c37cSMatthew Dillon     ACPI_LOCK(acpi);		/* wait for ACPI to finish nominal attach */
10257252c37cSMatthew Dillon     ACPI_UNLOCK(acpi);
10267252c37cSMatthew Dillon 
1027a639f788SMatthew Dillon     lwkt_gettoken(&acpi_token);
10285db2f26eSSascha Wildner     for (;;) {
10295db2f26eSSascha Wildner 	/* If the number of devices has changed, re-evaluate. */
10305db2f26eSSascha Wildner 	if (devclass_get_count(acpi_tz_devclass) != devcount) {
10315db2f26eSSascha Wildner 	    if (devs != NULL) {
10325db2f26eSSascha Wildner 		kfree(devs, M_TEMP);
10335db2f26eSSascha Wildner 		kfree(sc, M_TEMP);
10345db2f26eSSascha Wildner 	    }
10355db2f26eSSascha Wildner 	    devclass_get_devices(acpi_tz_devclass, &devs, &devcount);
10365db2f26eSSascha Wildner 	    sc = kmalloc(sizeof(struct acpi_tz_softc *) * devcount, M_TEMP,
10375db2f26eSSascha Wildner 			M_WAITOK | M_ZERO);
10385db2f26eSSascha Wildner 	    for (i = 0; i < devcount; i++)
10395db2f26eSSascha Wildner 		sc[i] = device_get_softc(devs[i]);
10405db2f26eSSascha Wildner 	}
10415db2f26eSSascha Wildner 
10425db2f26eSSascha Wildner 	/* Check for temperature events and act on them. */
10435db2f26eSSascha Wildner 	for (i = 0; i < devcount; i++) {
10445db2f26eSSascha Wildner 	    ACPI_LOCK(thermal);
10455db2f26eSSascha Wildner 	    flags = sc[i]->tz_flags;
10465db2f26eSSascha Wildner 	    sc[i]->tz_flags &= TZ_FLAG_NO_SCP;
10475db2f26eSSascha Wildner 	    ACPI_UNLOCK(thermal);
10485db2f26eSSascha Wildner 	    acpi_tz_timeout(sc[i], flags);
10495db2f26eSSascha Wildner 	}
10505db2f26eSSascha Wildner 
10515db2f26eSSascha Wildner 	/* If more work to do, don't go to sleep yet. */
10525db2f26eSSascha Wildner 	ACPI_LOCK(thermal);
10535db2f26eSSascha Wildner 	for (i = 0; i < devcount; i++) {
10545db2f26eSSascha Wildner 	    if (sc[i]->tz_flags & ~TZ_FLAG_NO_SCP)
10555db2f26eSSascha Wildner 		break;
10565db2f26eSSascha Wildner 	}
10575db2f26eSSascha Wildner 
10585db2f26eSSascha Wildner 	/*
10595db2f26eSSascha Wildner 	 * Interlocked sleep until signaled or we timeout.
10605db2f26eSSascha Wildner 	 */
10615db2f26eSSascha Wildner 	if (i == devcount) {
10625db2f26eSSascha Wildner 	    tsleep_interlock(&acpi_tz_td, 0);
10635db2f26eSSascha Wildner 	    ACPI_UNLOCK(thermal);
10647252c37cSMatthew Dillon 	    tsleep(&acpi_tz_td, PINTERLOCKED, "tzpoll",
1065531566e6SImre Vadász 		(acpi_tz_polling_rate <= 0 ? 0 : hz * acpi_tz_polling_rate));
10665db2f26eSSascha Wildner 	} else {
10675db2f26eSSascha Wildner 	    ACPI_UNLOCK(thermal);
10685db2f26eSSascha Wildner 	}
10695db2f26eSSascha Wildner     }
1070a639f788SMatthew Dillon     lwkt_reltoken(&acpi_token);
10715db2f26eSSascha Wildner }
10725db2f26eSSascha Wildner 
10735db2f26eSSascha Wildner #ifdef __FreeBSD__
10745db2f26eSSascha Wildner static int
acpi_tz_cpufreq_restore(struct acpi_tz_softc * sc)10755db2f26eSSascha Wildner acpi_tz_cpufreq_restore(struct acpi_tz_softc *sc)
10765db2f26eSSascha Wildner {
10775db2f26eSSascha Wildner     device_t dev;
10785db2f26eSSascha Wildner     int error;
10795db2f26eSSascha Wildner 
10805db2f26eSSascha Wildner     if (!sc->tz_cooling_updated)
10815db2f26eSSascha Wildner 	return (0);
10825db2f26eSSascha Wildner     if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL)
10835db2f26eSSascha Wildner 	return (ENXIO);
10845db2f26eSSascha Wildner     ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
10855db2f26eSSascha Wildner 	"temperature %d.%dC: resuming previous clock speed (%d MHz)\n",
10865db2f26eSSascha Wildner 	TZ_KELVTOC(sc->tz_temperature), sc->tz_cooling_saved_freq);
10875db2f26eSSascha Wildner     error = CPUFREQ_SET(dev, NULL, CPUFREQ_PRIO_KERN);
10885db2f26eSSascha Wildner     if (error == 0)
10895db2f26eSSascha Wildner 	sc->tz_cooling_updated = FALSE;
10905db2f26eSSascha Wildner     return (error);
10915db2f26eSSascha Wildner }
10925db2f26eSSascha Wildner 
10935db2f26eSSascha Wildner static int
acpi_tz_cpufreq_update(struct acpi_tz_softc * sc,int req)10945db2f26eSSascha Wildner acpi_tz_cpufreq_update(struct acpi_tz_softc *sc, int req)
10955db2f26eSSascha Wildner {
10965db2f26eSSascha Wildner     device_t dev;
10975db2f26eSSascha Wildner     struct cf_level *levels;
10985db2f26eSSascha Wildner     int num_levels, error, freq, desired_freq, perf, i;
10995db2f26eSSascha Wildner 
11005db2f26eSSascha Wildner     levels = kmalloc(CPUFREQ_MAX_LEVELS * sizeof(*levels), M_TEMP, M_NOWAIT);
11015db2f26eSSascha Wildner     if (levels == NULL)
11025db2f26eSSascha Wildner 	return (ENOMEM);
11035db2f26eSSascha Wildner 
11045db2f26eSSascha Wildner     /*
11055db2f26eSSascha Wildner      * Find the main device, cpufreq0.  We don't yet support independent
11065db2f26eSSascha Wildner      * CPU frequency control on SMP.
11075db2f26eSSascha Wildner      */
11085db2f26eSSascha Wildner     if ((dev = devclass_get_device(devclass_find("cpufreq"), 0)) == NULL) {
11095db2f26eSSascha Wildner 	error = ENXIO;
11105db2f26eSSascha Wildner 	goto out;
11115db2f26eSSascha Wildner     }
11125db2f26eSSascha Wildner 
11135db2f26eSSascha Wildner     /* Get the current frequency. */
11145db2f26eSSascha Wildner     error = CPUFREQ_GET(dev, &levels[0]);
11155db2f26eSSascha Wildner     if (error)
11165db2f26eSSascha Wildner 	goto out;
11175db2f26eSSascha Wildner     freq = levels[0].total_set.freq;
11185db2f26eSSascha Wildner 
11195db2f26eSSascha Wildner     /* Get the current available frequency levels. */
11205db2f26eSSascha Wildner     num_levels = CPUFREQ_MAX_LEVELS;
11215db2f26eSSascha Wildner     error = CPUFREQ_LEVELS(dev, levels, &num_levels);
11225db2f26eSSascha Wildner     if (error) {
11235db2f26eSSascha Wildner 	if (error == E2BIG)
11245db2f26eSSascha Wildner 	    printf("cpufreq: need to increase CPUFREQ_MAX_LEVELS\n");
11255db2f26eSSascha Wildner 	goto out;
11265db2f26eSSascha Wildner     }
11275db2f26eSSascha Wildner 
11285db2f26eSSascha Wildner     /* Calculate the desired frequency as a percent of the max frequency. */
11295db2f26eSSascha Wildner     perf = 100 * freq / levels[0].total_set.freq - req;
11305db2f26eSSascha Wildner     if (perf < 0)
11315db2f26eSSascha Wildner 	perf = 0;
11325db2f26eSSascha Wildner     else if (perf > 100)
11335db2f26eSSascha Wildner 	perf = 100;
11345db2f26eSSascha Wildner     desired_freq = levels[0].total_set.freq * perf / 100;
11355db2f26eSSascha Wildner 
11365db2f26eSSascha Wildner     if (desired_freq < freq) {
11375db2f26eSSascha Wildner 	/* Find the closest available frequency, rounding down. */
11385db2f26eSSascha Wildner 	for (i = 0; i < num_levels; i++)
11395db2f26eSSascha Wildner 	    if (levels[i].total_set.freq <= desired_freq)
11405db2f26eSSascha Wildner 		break;
11415db2f26eSSascha Wildner 
11425db2f26eSSascha Wildner 	/* If we didn't find a relevant setting, use the lowest. */
11435db2f26eSSascha Wildner 	if (i == num_levels)
11445db2f26eSSascha Wildner 	    i--;
11455db2f26eSSascha Wildner     } else {
11465db2f26eSSascha Wildner 	/* If we didn't decrease frequency yet, don't increase it. */
11475db2f26eSSascha Wildner 	if (!sc->tz_cooling_updated) {
11485db2f26eSSascha Wildner 	    sc->tz_cooling_active = FALSE;
11495db2f26eSSascha Wildner 	    goto out;
11505db2f26eSSascha Wildner 	}
11515db2f26eSSascha Wildner 
11525db2f26eSSascha Wildner 	/* Use saved cpu frequency as maximum value. */
11535db2f26eSSascha Wildner 	if (desired_freq > sc->tz_cooling_saved_freq)
11545db2f26eSSascha Wildner 	    desired_freq = sc->tz_cooling_saved_freq;
11555db2f26eSSascha Wildner 
11565db2f26eSSascha Wildner 	/* Find the closest available frequency, rounding up. */
11575db2f26eSSascha Wildner 	for (i = num_levels - 1; i >= 0; i--)
11585db2f26eSSascha Wildner 	    if (levels[i].total_set.freq >= desired_freq)
11595db2f26eSSascha Wildner 		break;
11605db2f26eSSascha Wildner 
11615db2f26eSSascha Wildner 	/* If we didn't find a relevant setting, use the highest. */
11625db2f26eSSascha Wildner 	if (i == -1)
11635db2f26eSSascha Wildner 	    i++;
11645db2f26eSSascha Wildner 
11655db2f26eSSascha Wildner 	/* If we're going to the highest frequency, restore the old setting. */
11665db2f26eSSascha Wildner 	if (i == 0 || desired_freq == sc->tz_cooling_saved_freq) {
11675db2f26eSSascha Wildner 	    error = acpi_tz_cpufreq_restore(sc);
11685db2f26eSSascha Wildner 	    if (error == 0)
11695db2f26eSSascha Wildner 		sc->tz_cooling_active = FALSE;
11705db2f26eSSascha Wildner 	    goto out;
11715db2f26eSSascha Wildner 	}
11725db2f26eSSascha Wildner     }
11735db2f26eSSascha Wildner 
11745db2f26eSSascha Wildner     /* If we are going to a new frequency, activate it. */
11755db2f26eSSascha Wildner     if (levels[i].total_set.freq != freq) {
11765db2f26eSSascha Wildner 	ACPI_VPRINT(sc->tz_dev, acpi_device_get_parent_softc(sc->tz_dev),
11775db2f26eSSascha Wildner 	    "temperature %d.%dC: %screasing clock speed "
11785db2f26eSSascha Wildner 	    "from %d MHz to %d MHz\n",
11795db2f26eSSascha Wildner 	    TZ_KELVTOC(sc->tz_temperature),
11805db2f26eSSascha Wildner 	    (freq > levels[i].total_set.freq) ? "de" : "in",
11815db2f26eSSascha Wildner 	    freq, levels[i].total_set.freq);
11825db2f26eSSascha Wildner 	error = CPUFREQ_SET(dev, &levels[i], CPUFREQ_PRIO_KERN);
11835db2f26eSSascha Wildner 	if (error == 0 && !sc->tz_cooling_updated) {
11845db2f26eSSascha Wildner 	    sc->tz_cooling_saved_freq = freq;
11855db2f26eSSascha Wildner 	    sc->tz_cooling_updated = TRUE;
11865db2f26eSSascha Wildner 	}
11875db2f26eSSascha Wildner     }
11885db2f26eSSascha Wildner 
11895db2f26eSSascha Wildner out:
11905db2f26eSSascha Wildner     if (levels)
11915db2f26eSSascha Wildner 	free(levels, M_TEMP);
11925db2f26eSSascha Wildner     return (error);
11935db2f26eSSascha Wildner }
11945db2f26eSSascha Wildner #endif
11955db2f26eSSascha Wildner 
11965db2f26eSSascha Wildner /*
11975db2f26eSSascha Wildner  * Passive cooling thread; monitors current temperature according to the
11985db2f26eSSascha Wildner  * cooling interval and calculates whether to scale back CPU frequency.
11995db2f26eSSascha Wildner  */
12005db2f26eSSascha Wildner static void
acpi_tz_cooling_thread(void * arg)12015db2f26eSSascha Wildner acpi_tz_cooling_thread(void *arg)
12025db2f26eSSascha Wildner {
12035db2f26eSSascha Wildner     struct acpi_tz_softc *sc;
12045db2f26eSSascha Wildner     int perf, curr_temp, prev_temp;
12055db2f26eSSascha Wildner #ifdef __FreeBSD__
12065db2f26eSSascha Wildner     int error;
12075db2f26eSSascha Wildner #endif
12085db2f26eSSascha Wildner 
12095db2f26eSSascha Wildner     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
12105db2f26eSSascha Wildner 
12117252c37cSMatthew Dillon     ACPI_LOCK(acpi);		/* wait for ACPI to finish nominal attach */
12127252c37cSMatthew Dillon     ACPI_UNLOCK(acpi);
12137252c37cSMatthew Dillon 
12145db2f26eSSascha Wildner     sc = (struct acpi_tz_softc *)arg;
1215a639f788SMatthew Dillon     lwkt_gettoken(&acpi_token);
12165db2f26eSSascha Wildner 
12175db2f26eSSascha Wildner     prev_temp = sc->tz_temperature;
12185db2f26eSSascha Wildner     while (sc->tz_cooling_enabled) {
12195db2f26eSSascha Wildner 	if (sc->tz_cooling_active)
12205db2f26eSSascha Wildner 	    (void)acpi_tz_get_temperature(sc);
12215db2f26eSSascha Wildner 	curr_temp = sc->tz_temperature;
12225db2f26eSSascha Wildner 	if (curr_temp >= sc->tz_zone.psv)
12235db2f26eSSascha Wildner 	    sc->tz_cooling_active = TRUE;
12245db2f26eSSascha Wildner 	if (sc->tz_cooling_active) {
12255db2f26eSSascha Wildner 	    perf = sc->tz_zone.tc1 * (curr_temp - prev_temp) +
12265db2f26eSSascha Wildner 		   sc->tz_zone.tc2 * (curr_temp - sc->tz_zone.psv);
12275db2f26eSSascha Wildner 	    perf /= 10;
12285db2f26eSSascha Wildner 
12295db2f26eSSascha Wildner 	    if (perf != 0) {
12305db2f26eSSascha Wildner #ifdef __FreeBSD__
12315db2f26eSSascha Wildner 		error = acpi_tz_cpufreq_update(sc, perf);
12325db2f26eSSascha Wildner 
12335db2f26eSSascha Wildner 		/*
12345db2f26eSSascha Wildner 		 * If error and not simply a higher priority setting was
12355db2f26eSSascha Wildner 		 * active, disable cooling.
12365db2f26eSSascha Wildner 		 */
12375db2f26eSSascha Wildner 		if (error != 0 && error != EPERM) {
12385db2f26eSSascha Wildner 		    device_printf(sc->tz_dev,
12395db2f26eSSascha Wildner 			"failed to set new freq, disabling passive cooling\n");
12405db2f26eSSascha Wildner 		    sc->tz_cooling_enabled = FALSE;
12415db2f26eSSascha Wildner 		}
12425db2f26eSSascha Wildner #endif
12435db2f26eSSascha Wildner 	    }
12445db2f26eSSascha Wildner 	}
12455db2f26eSSascha Wildner 	prev_temp = curr_temp;
12465db2f26eSSascha Wildner 	tsleep(&sc->tz_cooling_proc, 0, "cooling",
12475db2f26eSSascha Wildner 	    hz * sc->tz_zone.tsp / 10);
12485db2f26eSSascha Wildner     }
12495db2f26eSSascha Wildner     if (sc->tz_cooling_active) {
12505db2f26eSSascha Wildner #ifdef __FreeBSD__
12515db2f26eSSascha Wildner 	acpi_tz_cpufreq_restore(sc);
12525db2f26eSSascha Wildner #endif
12535db2f26eSSascha Wildner 	sc->tz_cooling_active = FALSE;
12545db2f26eSSascha Wildner     }
12555db2f26eSSascha Wildner     sc->tz_cooling_proc = NULL;
12565db2f26eSSascha Wildner     ACPI_LOCK(thermal);
12575db2f26eSSascha Wildner     sc->tz_cooling_proc_running = FALSE;
12585db2f26eSSascha Wildner     ACPI_UNLOCK(thermal);
1259a639f788SMatthew Dillon 
1260a639f788SMatthew Dillon     lwkt_reltoken(&acpi_token);
12615db2f26eSSascha Wildner }
12625db2f26eSSascha Wildner 
12635db2f26eSSascha Wildner /*
12645db2f26eSSascha Wildner  * TODO: We ignore _PSL (list of cooling devices) since cpufreq enumerates
12655db2f26eSSascha Wildner  * all CPUs for us.  However, it's possible in the future _PSL will
12665db2f26eSSascha Wildner  * reference non-CPU devices so we may want to support it then.
12675db2f26eSSascha Wildner  */
12685db2f26eSSascha Wildner static int
acpi_tz_cooling_is_available(struct acpi_tz_softc * sc)12695db2f26eSSascha Wildner acpi_tz_cooling_is_available(struct acpi_tz_softc *sc)
12705db2f26eSSascha Wildner {
12715db2f26eSSascha Wildner     return (sc->tz_zone.tc1 != -1 && sc->tz_zone.tc2 != -1 &&
12725db2f26eSSascha Wildner 	sc->tz_zone.tsp != -1 && sc->tz_zone.tsp != 0 &&
12735db2f26eSSascha Wildner 	sc->tz_zone.psv != -1);
12745db2f26eSSascha Wildner }
12755db2f26eSSascha Wildner 
12765db2f26eSSascha Wildner static int
acpi_tz_cooling_thread_start(struct acpi_tz_softc * sc)12775db2f26eSSascha Wildner acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc)
12785db2f26eSSascha Wildner {
12795db2f26eSSascha Wildner     int error;
12805db2f26eSSascha Wildner 
12815db2f26eSSascha Wildner     ACPI_LOCK(thermal);
12825db2f26eSSascha Wildner     if (sc->tz_cooling_proc_running) {
12835db2f26eSSascha Wildner 	ACPI_UNLOCK(thermal);
12845db2f26eSSascha Wildner 	return (0);
12855db2f26eSSascha Wildner     }
12865db2f26eSSascha Wildner     sc->tz_cooling_proc_running = TRUE;
12875db2f26eSSascha Wildner     ACPI_UNLOCK(thermal);
12885db2f26eSSascha Wildner     error = 0;
12895db2f26eSSascha Wildner     if (sc->tz_cooling_proc == NULL) {
12905db2f26eSSascha Wildner 	error = kthread_create(acpi_tz_cooling_thread, sc,
12915db2f26eSSascha Wildner 	    &sc->tz_cooling_proc,
12925db2f26eSSascha Wildner 	    "acpi_cooling%d", device_get_unit(sc->tz_dev));
12935db2f26eSSascha Wildner 	if (error != 0) {
12945db2f26eSSascha Wildner 	    device_printf(sc->tz_dev, "could not create thread - %d", error);
12955db2f26eSSascha Wildner 	    ACPI_LOCK(thermal);
12965db2f26eSSascha Wildner 	    sc->tz_cooling_proc_running = FALSE;
12975db2f26eSSascha Wildner 	    ACPI_UNLOCK(thermal);
12985db2f26eSSascha Wildner 	}
12995db2f26eSSascha Wildner     }
13005db2f26eSSascha Wildner     return (error);
13015db2f26eSSascha Wildner }
1302