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