xref: /openbsd-src/sys/dev/acpi/acpibat.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /* $OpenBSD: acpibat.c,v 1.58 2010/11/10 21:40:55 kettenis Exp $ */
2 /*
3  * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/proc.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/sensors.h>
24 
25 #include <machine/bus.h>
26 #include <machine/apmvar.h>
27 
28 #include <dev/acpi/acpireg.h>
29 #include <dev/acpi/acpivar.h>
30 #include <dev/acpi/acpidev.h>
31 #include <dev/acpi/amltypes.h>
32 #include <dev/acpi/dsdt.h>
33 
34 int	acpibat_match(struct device *, void *, void *);
35 void	acpibat_attach(struct device *, struct device *, void *);
36 
37 struct cfattach acpibat_ca = {
38 	sizeof(struct acpibat_softc), acpibat_match, acpibat_attach
39 };
40 
41 struct cfdriver acpibat_cd = {
42 	NULL, "acpibat", DV_DULL
43 };
44 
45 const char *acpibat_hids[] = { ACPI_DEV_CMB, 0 };
46 
47 void	acpibat_monitor(struct acpibat_softc *);
48 void	acpibat_refresh(void *);
49 int	acpibat_getbif(struct acpibat_softc *);
50 int	acpibat_getbst(struct acpibat_softc *);
51 int	acpibat_notify(struct aml_node *, int, void *);
52 
53 int
54 acpibat_match(struct device *parent, void *match, void *aux)
55 {
56 	struct acpi_attach_args	*aa = aux;
57 	struct cfdata		*cf = match;
58 
59 	/* sanity */
60 	return (acpi_matchhids(aa, acpibat_hids, cf->cf_driver->cd_name));
61 }
62 
63 void
64 acpibat_attach(struct device *parent, struct device *self, void *aux)
65 {
66 	struct acpibat_softc	*sc = (struct acpibat_softc *)self;
67 	struct acpi_attach_args	*aa = aux;
68 	int64_t			sta;
69 
70 	sc->sc_acpi = (struct acpi_softc *)parent;
71 	sc->sc_devnode = aa->aaa_node;
72 
73 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &sta)) {
74 		dnprintf(10, "%s: no _STA\n", DEVNAME(sc));
75 		return;
76 	}
77 
78 	if ((sta & STA_BATTERY) != 0) {
79 		sc->sc_bat_present = 1;
80 		acpibat_getbif(sc);
81 		acpibat_getbst(sc);
82 
83 		printf(": %s", sc->sc_devnode->name);
84 		if (sc->sc_bif.bif_model[0])
85 			printf(" model \"%s\"", sc->sc_bif.bif_model);
86 		if (sc->sc_bif.bif_serial[0])
87 			printf(" serial %s", sc->sc_bif.bif_serial);
88 		if (sc->sc_bif.bif_type[0])
89 			printf(" type %s", sc->sc_bif.bif_type);
90 		if (sc->sc_bif.bif_oem[0])
91 			printf(" oem \"%s\"", sc->sc_bif.bif_oem);
92 		printf("\n");
93 	} else {
94 		sc->sc_bat_present = 0;
95 		printf(": %s not present\n", sc->sc_devnode->name);
96 	}
97 
98 	/* create sensors */
99 	acpibat_monitor(sc);
100 
101 	/* populate sensors */
102 	acpibat_refresh(sc);
103 
104 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
105 	    acpibat_notify, sc, ACPIDEV_POLL);
106 }
107 
108 void
109 acpibat_monitor(struct acpibat_softc *sc)
110 {
111 	int			type;
112 
113 	/* assume _BIF and _BST have been called */
114 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
115 	    sizeof(sc->sc_sensdev.xname));
116 
117 	type = sc->sc_bif.bif_power_unit ? SENSOR_AMPHOUR : SENSOR_WATTHOUR;
118 
119 	strlcpy(sc->sc_sens[0].desc, "last full capacity",
120 	    sizeof(sc->sc_sens[0].desc));
121 	sc->sc_sens[0].type = type;
122 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[0]);
123 	sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000;
124 
125 	strlcpy(sc->sc_sens[1].desc, "warning capacity",
126 	    sizeof(sc->sc_sens[1].desc));
127 	sc->sc_sens[1].type = type;
128 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[1]);
129 	sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000;
130 
131 	strlcpy(sc->sc_sens[2].desc, "low capacity",
132 	    sizeof(sc->sc_sens[2].desc));
133 	sc->sc_sens[2].type = type;
134 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[2]);
135 	sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000;
136 
137 	strlcpy(sc->sc_sens[3].desc, "voltage", sizeof(sc->sc_sens[3].desc));
138 	sc->sc_sens[3].type = SENSOR_VOLTS_DC;
139 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[3]);
140 	sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
141 
142 	strlcpy(sc->sc_sens[4].desc, "battery unknown",
143 	    sizeof(sc->sc_sens[4].desc));
144 	sc->sc_sens[4].type = SENSOR_INTEGER;
145 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[4]);
146 	sc->sc_sens[4].value = sc->sc_bst.bst_state;
147 
148 	strlcpy(sc->sc_sens[5].desc, "rate", sizeof(sc->sc_sens[5].desc));
149 	sc->sc_sens[5].type = SENSOR_INTEGER;
150 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[5]);
151 	sc->sc_sens[5].value = sc->sc_bst.bst_rate;
152 
153 	strlcpy(sc->sc_sens[6].desc, "remaining capacity",
154 	    sizeof(sc->sc_sens[6].desc));
155 	sc->sc_sens[6].type = type;
156 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[6]);
157 	sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000;
158 
159 	strlcpy(sc->sc_sens[7].desc, "current voltage",
160 	    sizeof(sc->sc_sens[7].desc));
161 	sc->sc_sens[7].type = SENSOR_VOLTS_DC;
162 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[7]);
163 	sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
164 
165 	sensordev_install(&sc->sc_sensdev);
166 }
167 
168 void
169 acpibat_refresh(void *arg)
170 {
171 	struct acpibat_softc	*sc = arg;
172 	int			i;
173 
174 	dnprintf(30, "%s: %s: refresh\n", DEVNAME(sc),
175 	    sc->sc_devnode->name);
176 
177 	if (!sc->sc_bat_present) {
178 		for (i = 0; i < 8; i++) {
179 			sc->sc_sens[i].value = 0;
180 			sc->sc_sens[i].status = SENSOR_S_UNSPEC;
181 			sc->sc_sens[i].flags = SENSOR_FINVALID;
182 		}
183 		/* override state */
184 		strlcpy(sc->sc_sens[4].desc, "battery removed",
185 		    sizeof(sc->sc_sens[4].desc));
186 		return;
187 	}
188 
189 	/* _BIF values are static, sensor 0..3 */
190 	if (sc->sc_bif.bif_last_capacity == BIF_UNKNOWN) {
191 		sc->sc_sens[0].value = 0;
192 		sc->sc_sens[0].status = SENSOR_S_UNKNOWN;
193 		sc->sc_sens[0].flags = SENSOR_FUNKNOWN;
194 	} else {
195 		sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000;
196 		sc->sc_sens[0].status = SENSOR_S_UNSPEC;
197 		sc->sc_sens[0].flags = 0;
198 	}
199 	sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000;
200 	sc->sc_sens[1].flags = 0;
201 	sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000;
202 	sc->sc_sens[2].flags = 0;
203 	if (sc->sc_bif.bif_voltage == BIF_UNKNOWN) {
204 		sc->sc_sens[3].value = 0;
205 		sc->sc_sens[3].status = SENSOR_S_UNKNOWN;
206 		sc->sc_sens[3].flags = SENSOR_FUNKNOWN;
207 	} else {
208 		sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
209 		sc->sc_sens[3].status = SENSOR_S_UNSPEC;
210 		sc->sc_sens[3].flags = 0;
211 	}
212 
213 	/* _BST values are dynamic, sensor 4..7 */
214 	sc->sc_sens[4].status = SENSOR_S_OK;
215 	sc->sc_sens[4].flags = 0;
216 	if (sc->sc_bif.bif_last_capacity == BIF_UNKNOWN ||
217 	    sc->sc_bst.bst_capacity == BST_UNKNOWN) {
218 		sc->sc_sens[4].status = SENSOR_S_UNKNOWN;
219 		sc->sc_sens[4].flags = SENSOR_FUNKNOWN;
220 		strlcpy(sc->sc_sens[4].desc, "battery unknown",
221 		    sizeof(sc->sc_sens[4].desc));
222 	} else if (sc->sc_bst.bst_capacity >= sc->sc_bif.bif_last_capacity)
223 		strlcpy(sc->sc_sens[4].desc, "battery full",
224 		    sizeof(sc->sc_sens[4].desc));
225 	else if (sc->sc_bst.bst_state & BST_DISCHARGE)
226 		strlcpy(sc->sc_sens[4].desc, "battery discharging",
227 		    sizeof(sc->sc_sens[4].desc));
228 	else if (sc->sc_bst.bst_state & BST_CHARGE)
229 		strlcpy(sc->sc_sens[4].desc, "battery charging",
230 		    sizeof(sc->sc_sens[4].desc));
231 	else if (sc->sc_bst.bst_state & BST_CRITICAL) {
232 		strlcpy(sc->sc_sens[4].desc, "battery critical",
233 		    sizeof(sc->sc_sens[4].desc));
234 		sc->sc_sens[4].status = SENSOR_S_CRIT;
235 	} else
236 		strlcpy(sc->sc_sens[4].desc, "battery idle",
237 		    sizeof(sc->sc_sens[4].desc));
238 	sc->sc_sens[4].value = sc->sc_bst.bst_state;
239 
240 	if (sc->sc_bst.bst_rate == BST_UNKNOWN) {
241 		sc->sc_sens[5].value = 0;
242 		sc->sc_sens[5].status = SENSOR_S_UNKNOWN;
243 		sc->sc_sens[5].flags = SENSOR_FUNKNOWN;
244 	} else {
245 		sc->sc_sens[5].value = sc->sc_bst.bst_rate;
246 		sc->sc_sens[5].status = SENSOR_S_UNSPEC;
247 		sc->sc_sens[5].flags = 0;
248 	}
249 
250 	if (sc->sc_bst.bst_capacity == BST_UNKNOWN) {
251 		sc->sc_sens[6].value = 0;
252 		sc->sc_sens[6].status = SENSOR_S_UNKNOWN;
253 		sc->sc_sens[6].flags = SENSOR_FUNKNOWN;
254 	} else {
255 		sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000;
256 		sc->sc_sens[6].flags = 0;
257 
258 		if (sc->sc_bst.bst_capacity < sc->sc_bif.bif_low)
259 			/* XXX we should shutdown the system */
260 			sc->sc_sens[6].status = SENSOR_S_CRIT;
261 		else if (sc->sc_bst.bst_capacity < sc->sc_bif.bif_warning)
262 			sc->sc_sens[6].status = SENSOR_S_WARN;
263 		else
264 			sc->sc_sens[6].status = SENSOR_S_OK;
265 	}
266 
267 	if (sc->sc_bst.bst_voltage == BST_UNKNOWN) {
268 		sc->sc_sens[7].value = 0;
269 		sc->sc_sens[7].status = SENSOR_S_UNKNOWN;
270 		sc->sc_sens[7].flags = SENSOR_FUNKNOWN;
271 	} else {
272 		sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
273 		sc->sc_sens[7].status = SENSOR_S_UNSPEC;
274 		sc->sc_sens[7].flags = 0;
275 	}
276 	acpi_record_event(sc->sc_acpi, APM_POWER_CHANGE);
277 }
278 
279 int
280 acpibat_getbif(struct acpibat_softc *sc)
281 {
282 	struct aml_value	res;
283 	int			rv = EINVAL;
284 
285 	if (!sc->sc_bat_present) {
286 		memset(&sc->sc_bif, 0, sizeof(sc->sc_bif));
287 		return (0);
288 	}
289 
290 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BIF", 0, NULL, &res)) {
291 		dnprintf(10, "%s: no _BIF\n", DEVNAME(sc));
292 		goto out;
293 	}
294 
295 	if (res.length != 13) {
296 		dnprintf(10, "%s: invalid _BIF, battery info not saved\n",
297 		    DEVNAME(sc));
298 		goto out;
299 	}
300 
301 	sc->sc_bif.bif_power_unit = aml_val2int(res.v_package[0]);
302 	sc->sc_bif.bif_capacity = aml_val2int(res.v_package[1]);
303 	sc->sc_bif.bif_last_capacity = aml_val2int(res.v_package[2]);
304 	sc->sc_bif.bif_technology = aml_val2int(res.v_package[3]);
305 	sc->sc_bif.bif_voltage = aml_val2int(res.v_package[4]);
306 	sc->sc_bif.bif_warning = aml_val2int(res.v_package[5]);
307 	sc->sc_bif.bif_low = aml_val2int(res.v_package[6]);
308 	sc->sc_bif.bif_cap_granu1 = aml_val2int(res.v_package[7]);
309 	sc->sc_bif.bif_cap_granu2 = aml_val2int(res.v_package[8]);
310 
311 	strlcpy(sc->sc_bif.bif_model, aml_val_to_string(res.v_package[9]),
312 		sizeof(sc->sc_bif.bif_model));
313 	strlcpy(sc->sc_bif.bif_serial, aml_val_to_string(res.v_package[10]),
314 		sizeof(sc->sc_bif.bif_serial));
315 	strlcpy(sc->sc_bif.bif_type, aml_val_to_string(res.v_package[11]),
316 		sizeof(sc->sc_bif.bif_type));
317 	strlcpy(sc->sc_bif.bif_oem, aml_val_to_string(res.v_package[12]),
318 		sizeof(sc->sc_bif.bif_oem));
319 
320 	dnprintf(60, "power_unit: %u capacity: %u last_cap: %u tech: %u "
321 	    "volt: %u warn: %u low: %u gran1: %u gran2: %d model: %s "
322 	    "serial: %s type: %s oem: %s\n",
323 	    sc->sc_bif.bif_power_unit,
324 	    sc->sc_bif.bif_capacity,
325 	    sc->sc_bif.bif_last_capacity,
326 	    sc->sc_bif.bif_technology,
327 	    sc->sc_bif.bif_voltage,
328 	    sc->sc_bif.bif_warning,
329 	    sc->sc_bif.bif_low,
330 	    sc->sc_bif.bif_cap_granu1,
331 	    sc->sc_bif.bif_cap_granu2,
332 	    sc->sc_bif.bif_model,
333 	    sc->sc_bif.bif_serial,
334 	    sc->sc_bif.bif_type,
335 	    sc->sc_bif.bif_oem);
336 
337 	rv = 0;
338 out:
339 	aml_freevalue(&res);
340 	return (rv);
341 }
342 
343 int
344 acpibat_getbst(struct acpibat_softc *sc)
345 {
346 	struct aml_value	res;
347 	int			rv = EINVAL;
348 
349 	if (!sc->sc_bat_present) {
350 		memset(&sc->sc_bst, 0, sizeof(sc->sc_bst));
351 		return (0);
352 	}
353 
354 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BST", 0, NULL, &res)) {
355 		dnprintf(10, "%s: no _BST\n", DEVNAME(sc));
356 		goto out;
357 	}
358 
359 	if (res.length != 4) {
360 		dnprintf(10, "%s: invalid _BST, battery status not saved\n",
361 		    DEVNAME(sc));
362 		goto out;
363 	}
364 
365 	sc->sc_bst.bst_state = aml_val2int(res.v_package[0]);
366 	sc->sc_bst.bst_rate = aml_val2int(res.v_package[1]);
367 	sc->sc_bst.bst_capacity = aml_val2int(res.v_package[2]);
368 	sc->sc_bst.bst_voltage = aml_val2int(res.v_package[3]);
369 
370 	dnprintf(60, "state: %u rate: %u cap: %u volt: %u ",
371 	    sc->sc_bst.bst_state,
372 	    sc->sc_bst.bst_rate,
373 	    sc->sc_bst.bst_capacity,
374 	    sc->sc_bst.bst_voltage);
375 
376 	rv = 0;
377 out:
378 	aml_freevalue(&res);
379 	return (rv);
380 }
381 
382 /*
383  * XXX it has been observed that some systems do not propagate battery
384  * insertion events up to the driver.  What seems to happen is that DSDT
385  * does receive an interrupt however the originator bit is not set.
386  * This seems to happen when one inserts a 100% full battery.  Removal
387  * of the power cord or insertion of a not 100% full battery breaks this
388  * behavior and all events will then be sent upwards.  Currently there
389  * is no known work-around for it.
390  */
391 
392 int
393 acpibat_notify(struct aml_node *node, int notify_type, void *arg)
394 {
395 	struct acpibat_softc	*sc = arg;
396 	int64_t			sta;
397 
398 	dnprintf(10, "acpibat_notify: %.2x %s\n", notify_type,
399 	    sc->sc_devnode->name);
400 
401 	/* Check if installed state of battery has changed */
402 	if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta) == 0) {
403 		if (sta & STA_BATTERY)
404 			sc->sc_bat_present = 1;
405 		else
406 			sc->sc_bat_present = 0;
407 	}
408 
409 	switch (notify_type) {
410 	case 0x00:	/* Poll sensors */
411 	case 0x80:	/* _BST changed */
412 		acpibat_getbst(sc);
413 		break;
414 	case 0x81:	/* _BIF changed */
415 		acpibat_getbif(sc);
416 		break;
417 	default:
418 		break;
419 	}
420 
421 	acpibat_refresh(sc);
422 
423 	return (0);
424 }
425