xref: /netbsd-src/sys/dev/acpi/acpi_fan.c (revision 3772555307d774424243e6d1cdc66aa160602c26)
1*37725553Sthorpej /*	$NetBSD: acpi_fan.c,v 1.9 2021/01/29 15:49:55 thorpej Exp $ */
237f5de3aSjruoho 
337f5de3aSjruoho /*-
437f5de3aSjruoho  * Copyright (c) 2011 Jukka Ruohonen <jruohonen@iki.fi>
537f5de3aSjruoho  * All rights reserved.
637f5de3aSjruoho  *
737f5de3aSjruoho  * Redistribution and use in source and binary forms, with or without
837f5de3aSjruoho  * modification, are permitted provided that the following conditions
937f5de3aSjruoho  * are met:
1037f5de3aSjruoho  *
1137f5de3aSjruoho  * 1. Redistributions of source code must retain the above copyright
1237f5de3aSjruoho  *    notice, this list of conditions and the following disclaimer.
1337f5de3aSjruoho  * 2. Redistributions in binary form must reproduce the above copyright
1437f5de3aSjruoho  *    notice, this list of conditions and the following disclaimer in the
1537f5de3aSjruoho  *    documentation and/or other materials provided with the distribution.
1637f5de3aSjruoho  *
1737f5de3aSjruoho  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1837f5de3aSjruoho  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1937f5de3aSjruoho  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2037f5de3aSjruoho  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2137f5de3aSjruoho  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2237f5de3aSjruoho  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2337f5de3aSjruoho  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2437f5de3aSjruoho  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2537f5de3aSjruoho  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2637f5de3aSjruoho  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2737f5de3aSjruoho  * SUCH DAMAGE.
2837f5de3aSjruoho  */
2937f5de3aSjruoho #include <sys/cdefs.h>
30*37725553Sthorpej __KERNEL_RCSID(0, "$NetBSD: acpi_fan.c,v 1.9 2021/01/29 15:49:55 thorpej Exp $");
3137f5de3aSjruoho 
3237f5de3aSjruoho #include <sys/param.h>
3337f5de3aSjruoho #include <sys/module.h>
3437f5de3aSjruoho 
3537f5de3aSjruoho #include <dev/acpi/acpireg.h>
3637f5de3aSjruoho #include <dev/acpi/acpivar.h>
3737f5de3aSjruoho #include <dev/acpi/acpi_power.h>
3837f5de3aSjruoho 
3937f5de3aSjruoho #include <dev/sysmon/sysmonvar.h>
4037f5de3aSjruoho 
4137f5de3aSjruoho #define _COMPONENT		ACPI_RESOURCE_COMPONENT
4237f5de3aSjruoho ACPI_MODULE_NAME		("acpi_fan")
4337f5de3aSjruoho 
4437f5de3aSjruoho struct acpifan_softc {
4537f5de3aSjruoho 	device_t		 sc_dev;
4637f5de3aSjruoho 	struct acpi_devnode	*sc_node;
4737f5de3aSjruoho 	struct sysmon_envsys	*sc_sme;
4837f5de3aSjruoho 	envsys_data_t		 sc_sensor;
4937f5de3aSjruoho };
5037f5de3aSjruoho 
51*37725553Sthorpej static const struct device_compatible_entry compat_data[] = {
52*37725553Sthorpej 	{ .compat = "PNP0C0B" },
53*37725553Sthorpej 	DEVICE_COMPAT_EOL
5437f5de3aSjruoho };
5537f5de3aSjruoho 
5637f5de3aSjruoho static int	acpifan_match(device_t, cfdata_t, void *);
5737f5de3aSjruoho static void	acpifan_attach(device_t, device_t, void *);
5837f5de3aSjruoho static int	acpifan_detach(device_t, int);
5937f5de3aSjruoho static bool	acpifan_suspend(device_t, const pmf_qual_t *);
6037f5de3aSjruoho static bool	acpifan_resume(device_t, const pmf_qual_t *);
6137f5de3aSjruoho static bool	acpifan_shutdown(device_t, int);
6237f5de3aSjruoho static bool	acpifan_sensor_init(device_t);
63f82830f3Sjruoho static void	acpifan_sensor_state(void *);
6437f5de3aSjruoho static void	acpifan_sensor_refresh(struct sysmon_envsys *,envsys_data_t *);
6537f5de3aSjruoho 
6637f5de3aSjruoho CFATTACH_DECL_NEW(acpifan, sizeof(struct acpifan_softc),
6737f5de3aSjruoho     acpifan_match, acpifan_attach, acpifan_detach, NULL);
6837f5de3aSjruoho 
6937f5de3aSjruoho static int
acpifan_match(device_t parent,cfdata_t match,void * aux)7037f5de3aSjruoho acpifan_match(device_t parent, cfdata_t match, void *aux)
7137f5de3aSjruoho {
7237f5de3aSjruoho 	struct acpi_attach_args *aa = aux;
7337f5de3aSjruoho 
74*37725553Sthorpej 	return acpi_compatible_match(aa, compat_data);
7537f5de3aSjruoho }
7637f5de3aSjruoho 
7737f5de3aSjruoho static void
acpifan_attach(device_t parent,device_t self,void * aux)7837f5de3aSjruoho acpifan_attach(device_t parent, device_t self, void *aux)
7937f5de3aSjruoho {
8037f5de3aSjruoho 	struct acpifan_softc *sc = device_private(self);
8137f5de3aSjruoho 	struct acpi_attach_args *aa = aux;
8237f5de3aSjruoho 	ACPI_HANDLE tmp;
8337f5de3aSjruoho 	ACPI_STATUS rv;
8437f5de3aSjruoho 
8537f5de3aSjruoho 	sc->sc_sme = NULL;
8637f5de3aSjruoho 	sc->sc_dev = self;
8737f5de3aSjruoho 	sc->sc_node = aa->aa_node;
8837f5de3aSjruoho 
8937f5de3aSjruoho 	aprint_naive("\n");
9037f5de3aSjruoho 	aprint_normal(": ACPI Fan\n");
9137f5de3aSjruoho 
9237f5de3aSjruoho 	if (acpifan_sensor_init(self) != true)
9337f5de3aSjruoho 		aprint_error_dev(self, "failed to initialize\n");
9437f5de3aSjruoho 
9537f5de3aSjruoho 	(void)acpi_power_register(sc->sc_node->ad_handle);
9637f5de3aSjruoho 	(void)pmf_device_register1(self, acpifan_suspend,
9737f5de3aSjruoho 	    acpifan_resume, acpifan_shutdown);
9837f5de3aSjruoho 
9937f5de3aSjruoho 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "_FIF", &tmp);
10037f5de3aSjruoho 
10137f5de3aSjruoho 	if (ACPI_SUCCESS(rv))
10237f5de3aSjruoho 		aprint_verbose_dev(self, "ACPI 4.0 functionality present\n");
10337f5de3aSjruoho }
10437f5de3aSjruoho 
10537f5de3aSjruoho static int
acpifan_detach(device_t self,int flags)10637f5de3aSjruoho acpifan_detach(device_t self, int flags)
10737f5de3aSjruoho {
10837f5de3aSjruoho 	struct acpifan_softc *sc = device_private(self);
10937f5de3aSjruoho 
110930b9f2eSjruoho 	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
111930b9f2eSjruoho 
11237f5de3aSjruoho 	pmf_device_deregister(self);
11337f5de3aSjruoho 	acpi_power_deregister(sc->sc_node->ad_handle);
11437f5de3aSjruoho 
11537f5de3aSjruoho 	if (sc->sc_sme != NULL)
11637f5de3aSjruoho 		sysmon_envsys_unregister(sc->sc_sme);
11737f5de3aSjruoho 
11837f5de3aSjruoho 	return 0;
11937f5de3aSjruoho }
12037f5de3aSjruoho 
12137f5de3aSjruoho static bool
acpifan_suspend(device_t self,const pmf_qual_t * qual)12237f5de3aSjruoho acpifan_suspend(device_t self, const pmf_qual_t *qual)
12337f5de3aSjruoho {
12437f5de3aSjruoho 	struct acpifan_softc *sc = device_private(self);
12537f5de3aSjruoho 
12637f5de3aSjruoho 	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
12737f5de3aSjruoho 
12837f5de3aSjruoho 	return true;
12937f5de3aSjruoho }
13037f5de3aSjruoho 
13137f5de3aSjruoho static bool
acpifan_resume(device_t self,const pmf_qual_t * qual)13237f5de3aSjruoho acpifan_resume(device_t self, const pmf_qual_t *qual)
13337f5de3aSjruoho {
13437f5de3aSjruoho 	struct acpifan_softc *sc = device_private(self);
13537f5de3aSjruoho 
1361e7e299cSjruoho 	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D3);
13737f5de3aSjruoho 
13837f5de3aSjruoho 	return true;
13937f5de3aSjruoho }
14037f5de3aSjruoho 
14137f5de3aSjruoho static bool
acpifan_shutdown(device_t self,int how)14237f5de3aSjruoho acpifan_shutdown(device_t self, int how)
14337f5de3aSjruoho {
14437f5de3aSjruoho 	struct acpifan_softc *sc = device_private(self);
14537f5de3aSjruoho 
146930b9f2eSjruoho 	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
14737f5de3aSjruoho 
14837f5de3aSjruoho 	return true;
14937f5de3aSjruoho }
15037f5de3aSjruoho 
15137f5de3aSjruoho static bool
acpifan_sensor_init(device_t self)15237f5de3aSjruoho acpifan_sensor_init(device_t self)
15337f5de3aSjruoho {
15437f5de3aSjruoho 	struct acpifan_softc *sc = device_private(self);
1551e7e299cSjruoho 	int state;
15637f5de3aSjruoho 
1571e7e299cSjruoho 	if (acpi_power_get(sc->sc_node->ad_handle, &state) != true)
15837f5de3aSjruoho 		return false;
15937f5de3aSjruoho 
16037f5de3aSjruoho 	sc->sc_sme = sysmon_envsys_create();
16137f5de3aSjruoho 
16237f5de3aSjruoho 	acpifan_sensor_state(self);
16337f5de3aSjruoho 	sc->sc_sensor.units = ENVSYS_INDICATOR;
16437f5de3aSjruoho 
16537f5de3aSjruoho 	(void)strlcpy(sc->sc_sensor.desc, "state", sizeof(sc->sc_sensor.desc));
16637f5de3aSjruoho 
16737f5de3aSjruoho 	sc->sc_sme->sme_cookie = self;
16837f5de3aSjruoho 	sc->sc_sme->sme_flags = SME_POLL_ONLY;
16937f5de3aSjruoho 	sc->sc_sme->sme_name = device_xname(self);
17037f5de3aSjruoho 	sc->sc_sme->sme_refresh = acpifan_sensor_refresh;
17137f5de3aSjruoho 
17237f5de3aSjruoho 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0)
17337f5de3aSjruoho 		goto fail;
17437f5de3aSjruoho 
17537f5de3aSjruoho 	if (sysmon_envsys_register(sc->sc_sme) != 0)
17637f5de3aSjruoho 		goto fail;
17737f5de3aSjruoho 
17837f5de3aSjruoho 	return true;
17937f5de3aSjruoho 
18037f5de3aSjruoho fail:
18137f5de3aSjruoho 	sysmon_envsys_destroy(sc->sc_sme);
18237f5de3aSjruoho 	sc->sc_sme = NULL;
18337f5de3aSjruoho 
18437f5de3aSjruoho 	return false;
18537f5de3aSjruoho }
18637f5de3aSjruoho 
18737f5de3aSjruoho static void
acpifan_sensor_state(void * arg)188f82830f3Sjruoho acpifan_sensor_state(void *arg)
18937f5de3aSjruoho {
190f82830f3Sjruoho 	struct acpifan_softc *sc;
191f82830f3Sjruoho 	device_t self;
192f82830f3Sjruoho 	int state;
193f82830f3Sjruoho 
194f82830f3Sjruoho 	self = arg;
195f82830f3Sjruoho 	sc = device_private(self);
196f82830f3Sjruoho 	state = ACPI_STATE_ERROR;
19737f5de3aSjruoho 
19837f5de3aSjruoho 	(void)acpi_power_get(sc->sc_node->ad_handle, &state);
19937f5de3aSjruoho 
20037f5de3aSjruoho 	switch (state) {
20137f5de3aSjruoho 
20237f5de3aSjruoho 	case ACPI_STATE_D0:
20337f5de3aSjruoho 	case ACPI_STATE_D1:
20437f5de3aSjruoho 	case ACPI_STATE_D2:
20537f5de3aSjruoho 		sc->sc_sensor.value_cur = 1;
20637f5de3aSjruoho 		sc->sc_sensor.state = ENVSYS_SVALID;
20737f5de3aSjruoho 		break;
20837f5de3aSjruoho 
20937f5de3aSjruoho 	case ACPI_STATE_D3:
21037f5de3aSjruoho 		sc->sc_sensor.value_cur = 0;
21137f5de3aSjruoho 		sc->sc_sensor.state = ENVSYS_SVALID;
21237f5de3aSjruoho 		break;
21337f5de3aSjruoho 
21437f5de3aSjruoho 	default:
21537f5de3aSjruoho 		sc->sc_sensor.state = ENVSYS_SINVALID;
21637f5de3aSjruoho 		break;
21737f5de3aSjruoho 	}
21837f5de3aSjruoho }
21937f5de3aSjruoho 
22037f5de3aSjruoho static void
acpifan_sensor_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)22137f5de3aSjruoho acpifan_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
22237f5de3aSjruoho {
22337f5de3aSjruoho 	device_t self = sme->sme_cookie;
22437f5de3aSjruoho 
225f82830f3Sjruoho 	(void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpifan_sensor_state, self);
22637f5de3aSjruoho }
22737f5de3aSjruoho 
22876a2f91aSpgoyette MODULE(MODULE_CLASS_DRIVER, acpifan, "sysmon_envsys");
22937f5de3aSjruoho 
2305f852a84Sjruoho #ifdef _MODULE
23137f5de3aSjruoho #include "ioconf.c"
2325f852a84Sjruoho #endif
23337f5de3aSjruoho 
23437f5de3aSjruoho static int
acpifan_modcmd(modcmd_t cmd,void * aux)2355f852a84Sjruoho acpifan_modcmd(modcmd_t cmd, void *aux)
23637f5de3aSjruoho {
2375f852a84Sjruoho 	int rv = 0;
23837f5de3aSjruoho 
23937f5de3aSjruoho 	switch (cmd) {
24037f5de3aSjruoho 
24137f5de3aSjruoho 	case MODULE_CMD_INIT:
2425f852a84Sjruoho 
2435f852a84Sjruoho #ifdef _MODULE
2445f852a84Sjruoho 		rv = config_init_component(cfdriver_ioconf_acpifan,
24537f5de3aSjruoho 		    cfattach_ioconf_acpifan, cfdata_ioconf_acpifan);
2465f852a84Sjruoho #endif
2475f852a84Sjruoho 		break;
24837f5de3aSjruoho 
24937f5de3aSjruoho 	case MODULE_CMD_FINI:
2505f852a84Sjruoho 
2515f852a84Sjruoho #ifdef _MODULE
2525f852a84Sjruoho 		rv = config_fini_component(cfdriver_ioconf_acpifan,
25337f5de3aSjruoho 		    cfattach_ioconf_acpifan, cfdata_ioconf_acpifan);
2545f852a84Sjruoho #endif
2555f852a84Sjruoho 		break;
25637f5de3aSjruoho 
25737f5de3aSjruoho 	default:
2585f852a84Sjruoho 		rv = ENOTTY;
25937f5de3aSjruoho 	}
26037f5de3aSjruoho 
2615f852a84Sjruoho 	return rv;
2625f852a84Sjruoho }
263