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