xref: /netbsd-src/sys/dev/acpi/acpi_fan.c (revision 4fee23f98c45552038ad6b5bd05124a41302fb01)
1 /*	$NetBSD: acpi_fan.c,v 1.5 2011/06/20 17:21:50 pgoyette 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.5 2011/06/20 17:21:50 pgoyette 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
42 ACPI_MODULE_NAME		("acpi_fan")
43 
44 struct 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 	int			 sc_state;
50 };
51 
52 const char * const acpi_fan_ids[] = {
53 	"PNP0C0B",
54 	NULL
55 };
56 
57 static int	acpifan_match(device_t, cfdata_t, void *);
58 static void	acpifan_attach(device_t, device_t, void *);
59 static int	acpifan_detach(device_t, int);
60 static bool	acpifan_suspend(device_t, const pmf_qual_t *);
61 static bool	acpifan_resume(device_t, const pmf_qual_t *);
62 static bool	acpifan_shutdown(device_t, int);
63 static bool	acpifan_sensor_init(device_t);
64 static void	acpifan_sensor_state(void *);
65 static void	acpifan_sensor_refresh(struct sysmon_envsys *,envsys_data_t *);
66 
67 CFATTACH_DECL_NEW(acpifan, sizeof(struct acpifan_softc),
68     acpifan_match, acpifan_attach, acpifan_detach, NULL);
69 
70 static int
71 acpifan_match(device_t parent, cfdata_t match, void *aux)
72 {
73 	struct acpi_attach_args *aa = aux;
74 
75 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
76 		return 0;
77 
78 	return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_fan_ids);
79 }
80 
81 static void
82 acpifan_attach(device_t parent, device_t self, void *aux)
83 {
84 	struct acpifan_softc *sc = device_private(self);
85 	struct acpi_attach_args *aa = aux;
86 	ACPI_HANDLE tmp;
87 	ACPI_STATUS rv;
88 
89 	sc->sc_sme = NULL;
90 	sc->sc_dev = self;
91 	sc->sc_node = aa->aa_node;
92 	sc->sc_state = ACPI_STATE_ERROR;
93 
94 	aprint_naive("\n");
95 	aprint_normal(": ACPI Fan\n");
96 
97 	if (acpifan_sensor_init(self) != true)
98 		aprint_error_dev(self, "failed to initialize\n");
99 
100 	(void)acpi_power_register(sc->sc_node->ad_handle);
101 	(void)pmf_device_register1(self, acpifan_suspend,
102 	    acpifan_resume, acpifan_shutdown);
103 
104 	rv = AcpiGetHandle(sc->sc_node->ad_handle, "_FIF", &tmp);
105 
106 	if (ACPI_SUCCESS(rv))
107 		aprint_verbose_dev(self, "ACPI 4.0 functionality present\n");
108 }
109 
110 static int
111 acpifan_detach(device_t self, int flags)
112 {
113 	struct acpifan_softc *sc = device_private(self);
114 
115 	if (sc->sc_state != ACPI_STATE_ERROR)
116 		(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
117 
118 	pmf_device_deregister(self);
119 	acpi_power_deregister(sc->sc_node->ad_handle);
120 
121 	if (sc->sc_sme != NULL)
122 		sysmon_envsys_unregister(sc->sc_sme);
123 
124 	return 0;
125 }
126 
127 static bool
128 acpifan_suspend(device_t self, const pmf_qual_t *qual)
129 {
130 	struct acpifan_softc *sc = device_private(self);
131 
132 	if (sc->sc_state == ACPI_STATE_ERROR)
133 		return true;
134 
135 	(void)acpi_power_get(sc->sc_node->ad_handle, &sc->sc_state);
136 	(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
137 
138 	return true;
139 }
140 
141 static bool
142 acpifan_resume(device_t self, const pmf_qual_t *qual)
143 {
144 	struct acpifan_softc *sc = device_private(self);
145 
146 	if (sc->sc_state != ACPI_STATE_ERROR)
147 		(void)acpi_power_set(sc->sc_node->ad_handle, sc->sc_state);
148 
149 	return true;
150 }
151 
152 static bool
153 acpifan_shutdown(device_t self, int how)
154 {
155 	struct acpifan_softc *sc = device_private(self);
156 
157 	if (sc->sc_state != ACPI_STATE_ERROR)
158 		(void)acpi_power_set(sc->sc_node->ad_handle, ACPI_STATE_D0);
159 
160 	return true;
161 }
162 
163 static bool
164 acpifan_sensor_init(device_t self)
165 {
166 	struct acpifan_softc *sc = device_private(self);
167 
168 	if (acpi_power_get(sc->sc_node->ad_handle, &sc->sc_state) != true)
169 		return false;
170 
171 	sc->sc_sme = sysmon_envsys_create();
172 
173 	acpifan_sensor_state(self);
174 	sc->sc_sensor.units = ENVSYS_INDICATOR;
175 	sc->sc_sensor.state = ENVSYS_SINVALID;
176 
177 	(void)strlcpy(sc->sc_sensor.desc, "state", sizeof(sc->sc_sensor.desc));
178 
179 	sc->sc_sme->sme_cookie = self;
180 	sc->sc_sme->sme_flags = SME_POLL_ONLY;
181 	sc->sc_sme->sme_name = device_xname(self);
182 	sc->sc_sme->sme_refresh = acpifan_sensor_refresh;
183 
184 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0)
185 		goto fail;
186 
187 	if (sysmon_envsys_register(sc->sc_sme) != 0)
188 		goto fail;
189 
190 	return true;
191 
192 fail:
193 	sysmon_envsys_destroy(sc->sc_sme);
194 	sc->sc_sme = NULL;
195 
196 	return false;
197 }
198 
199 static void
200 acpifan_sensor_state(void *arg)
201 {
202 	struct acpifan_softc *sc;
203 	device_t self;
204 	int state;
205 
206 	self = arg;
207 	sc = device_private(self);
208 	state = ACPI_STATE_ERROR;
209 
210 	(void)acpi_power_get(sc->sc_node->ad_handle, &state);
211 
212 	switch (state) {
213 
214 	case ACPI_STATE_D0:
215 	case ACPI_STATE_D1:
216 	case ACPI_STATE_D2:
217 		sc->sc_sensor.value_cur = 1;
218 		sc->sc_sensor.state = ENVSYS_SVALID;
219 		break;
220 
221 	case ACPI_STATE_D3:
222 		sc->sc_sensor.value_cur = 0;
223 		sc->sc_sensor.state = ENVSYS_SVALID;
224 		break;
225 
226 	default:
227 		sc->sc_sensor.state = ENVSYS_SINVALID;
228 		break;
229 	}
230 }
231 
232 static void
233 acpifan_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
234 {
235 	device_t self = sme->sme_cookie;
236 
237 	(void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpifan_sensor_state, self);
238 }
239 
240 MODULE(MODULE_CLASS_DRIVER, acpifan, NULL);
241 
242 #ifdef _MODULE
243 #include "ioconf.c"
244 #endif
245 
246 static int
247 acpifan_modcmd(modcmd_t cmd, void *aux)
248 {
249 	int rv = 0;
250 
251 	switch (cmd) {
252 
253 	case MODULE_CMD_INIT:
254 
255 #ifdef _MODULE
256 		rv = config_init_component(cfdriver_ioconf_acpifan,
257 		    cfattach_ioconf_acpifan, cfdata_ioconf_acpifan);
258 #endif
259 		break;
260 
261 	case MODULE_CMD_FINI:
262 
263 #ifdef _MODULE
264 		rv = config_fini_component(cfdriver_ioconf_acpifan,
265 		    cfattach_ioconf_acpifan, cfdata_ioconf_acpifan);
266 #endif
267 		break;
268 
269 	default:
270 		rv = ENOTTY;
271 	}
272 
273 	return rv;
274 }
275