1/*	$NetBSD: acpi_fan.c,v 1.9 2021/01/29 15:49:55 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2011 Jukka Ruohonen <jruohonen@iki.fi>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: acpi_fan.c,v 1.9 2021/01/29 15:49:55 thorpej Exp $");
31
32#include <sys/param.h>
33#include <sys/module.h>
34
35#include <dev/acpi/acpireg.h>
36#include <dev/acpi/acpivar.h>
37#include <dev/acpi/acpi_power.h>
38
39#include <dev/sysmon/sysmonvar.h>
40
41#define _COMPONENT		ACPI_RESOURCE_COMPONENT
42ACPI_MODULE_NAME		("acpi_fan")
43
44struct acpifan_softc {
45	device_t		 sc_dev;
46	struct acpi_devnode	*sc_node;
47	struct sysmon_envsys	*sc_sme;
48	envsys_data_t		 sc_sensor;
49};
50
51static const struct device_compatible_entry compat_data[] = {
52	{ .compat = "PNP0C0B" },
53	DEVICE_COMPAT_EOL
54};
55
56static int	acpifan_match(device_t, cfdata_t, void *);
57static void	acpifan_attach(device_t, device_t, void *);
58static int	acpifan_detach(device_t, int);
59static bool	acpifan_suspend(device_t, const pmf_qual_t *);
60static bool	acpifan_resume(device_t, const pmf_qual_t *);
61static bool	acpifan_shutdown(device_t, int);
62static bool	acpifan_sensor_init(device_t);
63static void	acpifan_sensor_state(void *);
64static void	acpifan_sensor_refresh(struct sysmon_envsys *,envsys_data_t *);
65
66CFATTACH_DECL_NEW(acpifan, sizeof(struct acpifan_softc),
67    acpifan_match, acpifan_attach, acpifan_detach, NULL);
68
69static int
70acpifan_match(device_t parent, cfdata_t match, void *aux)
71{
72	struct acpi_attach_args *aa = aux;
73
74	return acpi_compatible_match(aa, compat_data);
75}
76
77static void
78acpifan_attach(device_t parent, device_t self, void *aux)
79{
80	struct acpifan_softc *sc = device_private(self);
81	struct acpi_attach_args *aa = aux;
82	ACPI_HANDLE tmp;
83	ACPI_STATUS rv;
84
85	sc->sc_sme = NULL;
86	sc->sc_dev = self;
87	sc->sc_node = aa->aa_node;
88
89	aprint_naive("\n");
90	aprint_normal(": ACPI Fan\n");
91
92	if (acpifan_sensor_init(self) != true)
93		aprint_error_dev(self, "failed to initialize\n");
94
95	(void)acpi_power_register(sc->sc_node->ad_handle);
96	(void)pmf_device_register1(self, acpifan_suspend,
97	    acpifan_resume, acpifan_shutdown);
98
99	rv = AcpiGetHandle(sc->sc_node->ad_handle, "_FIF", &tmp);
100
101	if (ACPI_SUCCESS(rv))
102		aprint_verbose_dev(self, "ACPI 4.0 functionality present\n");
103}
104
105static int
106acpifan_detach(device_t self, int flags)
107{
108	struct acpifan_softc *sc = device_private(self);
109
110	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
111
112	pmf_device_deregister(self);
113	acpi_power_deregister(sc->sc_node->ad_handle);
114
115	if (sc->sc_sme != NULL)
116		sysmon_envsys_unregister(sc->sc_sme);
117
118	return 0;
119}
120
121static bool
122acpifan_suspend(device_t self, const pmf_qual_t *qual)
123{
124	struct acpifan_softc *sc = device_private(self);
125
126	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
127
128	return true;
129}
130
131static bool
132acpifan_resume(device_t self, const pmf_qual_t *qual)
133{
134	struct acpifan_softc *sc = device_private(self);
135
136	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D3);
137
138	return true;
139}
140
141static bool
142acpifan_shutdown(device_t self, int how)
143{
144	struct acpifan_softc *sc = device_private(self);
145
146	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
147
148	return true;
149}
150
151static bool
152acpifan_sensor_init(device_t self)
153{
154	struct acpifan_softc *sc = device_private(self);
155	int state;
156
157	if (acpi_power_get(sc->sc_node->ad_handle, &state) != true)
158		return false;
159
160	sc->sc_sme = sysmon_envsys_create();
161
162	acpifan_sensor_state(self);
163	sc->sc_sensor.units = ENVSYS_INDICATOR;
164
165	(void)strlcpy(sc->sc_sensor.desc, "state", sizeof(sc->sc_sensor.desc));
166
167	sc->sc_sme->sme_cookie = self;
168	sc->sc_sme->sme_flags = SME_POLL_ONLY;
169	sc->sc_sme->sme_name = device_xname(self);
170	sc->sc_sme->sme_refresh = acpifan_sensor_refresh;
171
172	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0)
173		goto fail;
174
175	if (sysmon_envsys_register(sc->sc_sme) != 0)
176		goto fail;
177
178	return true;
179
180fail:
181	sysmon_envsys_destroy(sc->sc_sme);
182	sc->sc_sme = NULL;
183
184	return false;
185}
186
187static void
188acpifan_sensor_state(void *arg)
189{
190	struct acpifan_softc *sc;
191	device_t self;
192	int state;
193
194	self = arg;
195	sc = device_private(self);
196	state = ACPI_STATE_ERROR;
197
198	(void)acpi_power_get(sc->sc_node->ad_handle, &state);
199
200	switch (state) {
201
202	case ACPI_STATE_D0:
203	case ACPI_STATE_D1:
204	case ACPI_STATE_D2:
205		sc->sc_sensor.value_cur = 1;
206		sc->sc_sensor.state = ENVSYS_SVALID;
207		break;
208
209	case ACPI_STATE_D3:
210		sc->sc_sensor.value_cur = 0;
211		sc->sc_sensor.state = ENVSYS_SVALID;
212		break;
213
214	default:
215		sc->sc_sensor.state = ENVSYS_SINVALID;
216		break;
217	}
218}
219
220static void
221acpifan_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
222{
223	device_t self = sme->sme_cookie;
224
225	(void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpifan_sensor_state, self);
226}
227
228MODULE(MODULE_CLASS_DRIVER, acpifan, "sysmon_envsys");
229
230#ifdef _MODULE
231#include "ioconf.c"
232#endif
233
234static int
235acpifan_modcmd(modcmd_t cmd, void *aux)
236{
237	int rv = 0;
238
239	switch (cmd) {
240
241	case MODULE_CMD_INIT:
242
243#ifdef _MODULE
244		rv = config_init_component(cfdriver_ioconf_acpifan,
245		    cfattach_ioconf_acpifan, cfdata_ioconf_acpifan);
246#endif
247		break;
248
249	case MODULE_CMD_FINI:
250
251#ifdef _MODULE
252		rv = config_fini_component(cfdriver_ioconf_acpifan,
253		    cfattach_ioconf_acpifan, cfdata_ioconf_acpifan);
254#endif
255		break;
256
257	default:
258		rv = ENOTTY;
259	}
260
261	return rv;
262}
263