xref: /netbsd-src/sys/dev/acpi/acpi_fan.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: acpi_fan.c,v 1.3 2011/01/13 14:25:33 jruoho 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.3 2011/01/13 14:25:33 jruoho 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 
176 	(void)strlcpy(sc->sc_sensor.desc, "state", sizeof(sc->sc_sensor.desc));
177 
178 	sc->sc_sme->sme_cookie = self;
179 	sc->sc_sme->sme_flags = SME_POLL_ONLY;
180 	sc->sc_sme->sme_name = device_xname(self);
181 	sc->sc_sme->sme_refresh = acpifan_sensor_refresh;
182 
183 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0)
184 		goto fail;
185 
186 	if (sysmon_envsys_register(sc->sc_sme) != 0)
187 		goto fail;
188 
189 	return true;
190 
191 fail:
192 	sysmon_envsys_destroy(sc->sc_sme);
193 	sc->sc_sme = NULL;
194 
195 	return false;
196 }
197 
198 static void
199 acpifan_sensor_state(void *arg)
200 {
201 	struct acpifan_softc *sc;
202 	device_t self;
203 	int state;
204 
205 	self = arg;
206 	sc = device_private(self);
207 	state = ACPI_STATE_ERROR;
208 
209 	(void)acpi_power_get(sc->sc_node->ad_handle, &state);
210 
211 	switch (state) {
212 
213 	case ACPI_STATE_D0:
214 	case ACPI_STATE_D1:
215 	case ACPI_STATE_D2:
216 		sc->sc_sensor.value_cur = 1;
217 		sc->sc_sensor.state = ENVSYS_SVALID;
218 		break;
219 
220 	case ACPI_STATE_D3:
221 		sc->sc_sensor.value_cur = 0;
222 		sc->sc_sensor.state = ENVSYS_SVALID;
223 		break;
224 
225 	default:
226 		sc->sc_sensor.state = ENVSYS_SINVALID;
227 		break;
228 	}
229 }
230 
231 static void
232 acpifan_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
233 {
234 	device_t self = sme->sme_cookie;
235 
236 	(void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpifan_sensor_state, self);
237 }
238 
239 #ifdef _MODULE
240 
241 MODULE(MODULE_CLASS_DRIVER, acpifan, NULL);
242 
243 #include "ioconf.c"
244 
245 static int
246 acpifan_modcmd(modcmd_t cmd, void *context)
247 {
248 
249 	switch (cmd) {
250 
251 	case MODULE_CMD_INIT:
252 		return config_init_component(cfdriver_ioconf_acpifan,
253 		    cfattach_ioconf_acpifan, cfdata_ioconf_acpifan);
254 
255 	case MODULE_CMD_FINI:
256 		return config_fini_component(cfdriver_ioconf_acpifan,
257 		    cfattach_ioconf_acpifan, cfdata_ioconf_acpifan);
258 
259 	default:
260 		return ENOTTY;
261 	}
262 }
263 
264 #endif	/* _MODULE */
265