acpibat.c revision 1.37
1/* $OpenBSD: acpibat.c,v 1.37 2006/12/26 23:58:08 marco 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
33int	acpibat_match(struct device *, void *, void *);
34void	acpibat_attach(struct device *, struct device *, void *);
35
36struct cfattach acpibat_ca = {
37	sizeof(struct acpibat_softc), acpibat_match, acpibat_attach
38};
39
40struct cfdriver acpibat_cd = {
41	NULL, "acpibat", DV_DULL
42};
43
44void	acpibat_monitor(struct acpibat_softc *);
45void	acpibat_refresh(void *);
46int	acpibat_getbif(struct acpibat_softc *);
47int	acpibat_getbst(struct acpibat_softc *);
48int	acpibat_notify(struct aml_node *, int, void *);
49
50int
51acpibat_match(struct device *parent, void *match, void *aux)
52{
53	struct acpi_attach_args	*aa = aux;
54	struct cfdata		*cf = match;
55
56	/* sanity */
57	if (aa->aaa_name == NULL ||
58	    strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
59	    aa->aaa_table != NULL)
60		return (0);
61
62	return (1);
63}
64
65void
66acpibat_attach(struct device *parent, struct device *self, void *aux)
67{
68	struct acpibat_softc	*sc = (struct acpibat_softc *)self;
69	struct acpi_attach_args	*aa = aux;
70	struct aml_value	res;
71
72	sc->sc_acpi = (struct acpi_softc *)parent;
73	sc->sc_devnode = aa->aaa_node->child;
74
75	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
76		dnprintf(10, "%s: no _STA\n", DEVNAME(sc));
77		return;
78	}
79
80	if ((sc->sc_bat_present = res.v_integer & STA_BATTERY) != 0) {
81		acpibat_getbif(sc);
82		acpibat_getbst(sc);
83		printf(": %s: model: %s serial: %s type: %s oem: %s\n",
84		    sc->sc_devnode->parent->name,
85		    sc->sc_bif.bif_model,
86		    sc->sc_bif.bif_serial,
87		    sc->sc_bif.bif_type,
88		    sc->sc_bif.bif_oem);
89	} else
90		printf(": %s: not present\n", sc->sc_devnode->parent->name);
91
92	aml_freevalue(&res);
93
94	/* create sensors */
95	acpibat_monitor(sc);
96
97	/* populate sensors */
98	acpibat_refresh(sc);
99
100	aml_register_notify(sc->sc_devnode->parent, aa->aaa_dev,
101	    acpibat_notify, sc, ACPIDEV_POLL);
102}
103
104void
105acpibat_monitor(struct acpibat_softc *sc)
106{
107	int			type;
108
109	/* assume _BIF and _BST have been called */
110	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
111	    sizeof(sc->sc_sensdev.xname));
112
113	type = sc->sc_bif.bif_power_unit ? SENSOR_AMPHOUR : SENSOR_WATTHOUR;
114
115	strlcpy(sc->sc_sens[0].desc, "last full capacity",
116	    sizeof(sc->sc_sens[0].desc));
117	sc->sc_sens[0].type = type;
118	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[0]);
119	sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000;
120
121	strlcpy(sc->sc_sens[1].desc, "warning capacity",
122	    sizeof(sc->sc_sens[1].desc));
123	sc->sc_sens[1].type = type;
124	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[1]);
125	sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000;
126
127	strlcpy(sc->sc_sens[2].desc, "low capacity",
128	    sizeof(sc->sc_sens[2].desc));
129	sc->sc_sens[2].type = type;
130	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[2]);
131	sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000;
132
133	strlcpy(sc->sc_sens[3].desc, "voltage", sizeof(sc->sc_sens[3].desc));
134	sc->sc_sens[3].type = SENSOR_VOLTS_DC;
135	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[3]);
136	sc->sc_sens[3].status = SENSOR_S_OK;
137	sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
138
139	strlcpy(sc->sc_sens[4].desc, "battery unknown",
140	    sizeof(sc->sc_sens[4].desc));
141	sc->sc_sens[4].type = SENSOR_INTEGER;
142	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[4]);
143	sc->sc_sens[4].status = SENSOR_S_UNKNOWN;
144	sc->sc_sens[4].value = sc->sc_bst.bst_state;
145
146	strlcpy(sc->sc_sens[5].desc, "rate", sizeof(sc->sc_sens[5].desc));
147	sc->sc_sens[5].type = SENSOR_INTEGER;
148	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[5]);
149	sc->sc_sens[5].value = sc->sc_bst.bst_rate;
150
151	strlcpy(sc->sc_sens[6].desc, "remaining capacity",
152	    sizeof(sc->sc_sens[6].desc));
153	sc->sc_sens[6].type = type;
154	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[6]);
155	sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000;
156
157	strlcpy(sc->sc_sens[7].desc, "current voltage",
158	    sizeof(sc->sc_sens[7].desc));
159	sc->sc_sens[7].type = SENSOR_VOLTS_DC;
160	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[7]);
161	sc->sc_sens[7].status = SENSOR_S_OK;
162	sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
163
164	sensordev_install(&sc->sc_sensdev);
165}
166
167void
168acpibat_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->parent->name);
175
176	if (!sc->sc_bat_present) {
177		for (i = 0; i < 8; i++)
178			sc->sc_sens[i].value = 0;
179		strlcpy(sc->sc_sens[4].desc, "battery removed",
180		    sizeof(sc->sc_sens[4].desc));
181		sc->sc_sens[4].status = SENSOR_S_OK;
182		return;
183	}
184
185	acpibat_getbif(sc);
186	acpibat_getbst(sc);
187
188	sc->sc_sens[0].value = sc->sc_bif.bif_last_capacity * 1000;
189	sc->sc_sens[1].value = sc->sc_bif.bif_warning * 1000;
190	sc->sc_sens[2].value = sc->sc_bif.bif_low * 1000;
191	sc->sc_sens[3].value = sc->sc_bif.bif_voltage * 1000;
192
193	sc->sc_sens[4].status = SENSOR_S_OK;
194	if (sc->sc_bst.bst_state & BST_DISCHARGE)
195		strlcpy(sc->sc_sens[4].desc, "battery discharging",
196		    sizeof(sc->sc_sens[4].desc));
197	else if (sc->sc_bst.bst_state & BST_CHARGE)
198		strlcpy(sc->sc_sens[4].desc, "battery charging",
199		    sizeof(sc->sc_sens[4].desc));
200	else if (sc->sc_bst.bst_state & BST_CRITICAL) {
201		strlcpy(sc->sc_sens[4].desc, "battery critical",
202		    sizeof(sc->sc_sens[4].desc));
203		sc->sc_sens[4].status = SENSOR_S_CRIT;
204	} else /* whenever there is no status the battery is full */
205		strlcpy(sc->sc_sens[4].desc, "battery full",
206		    sizeof(sc->sc_sens[4].desc));
207
208	sc->sc_sens[4].value = sc->sc_bst.bst_state;
209	sc->sc_sens[5].value = sc->sc_bst.bst_rate;
210	sc->sc_sens[6].value = sc->sc_bst.bst_capacity * 1000;
211	sc->sc_sens[7].value = sc->sc_bst.bst_voltage * 1000;
212}
213
214int
215acpibat_getbif(struct acpibat_softc *sc)
216{
217	struct aml_value        res;
218	int			rv = EINVAL;
219
220	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &res)) {
221		dnprintf(10, "%s: no _STA\n", DEVNAME(sc));
222		goto out;
223	}
224	aml_freevalue(&res);
225
226	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BIF", 0, NULL, &res)) {
227		dnprintf(10, "%s: no _BIF\n", DEVNAME(sc));
228		goto out;
229	}
230
231	if (res.length != 13) {
232		dnprintf(10, "%s: invalid _BIF, battery info not saved\n",
233		    DEVNAME(sc));
234		goto out;
235	}
236
237	memset(&sc->sc_bif, 0, sizeof sc->sc_bif);
238	sc->sc_bif.bif_power_unit = aml_val2int(res.v_package[0]);
239	sc->sc_bif.bif_capacity = aml_val2int(res.v_package[1]);
240	sc->sc_bif.bif_last_capacity = aml_val2int(res.v_package[2]);
241	sc->sc_bif.bif_technology = aml_val2int(res.v_package[3]);
242	sc->sc_bif.bif_voltage = aml_val2int(res.v_package[4]);
243	sc->sc_bif.bif_warning = aml_val2int(res.v_package[5]);
244	sc->sc_bif.bif_low = aml_val2int(res.v_package[6]);
245	sc->sc_bif.bif_cap_granu1 = aml_val2int(res.v_package[7]);
246	sc->sc_bif.bif_cap_granu2 = aml_val2int(res.v_package[8]);
247
248	strlcpy(sc->sc_bif.bif_model, aml_strval(res.v_package[9]),
249		sizeof(sc->sc_bif.bif_model));
250	strlcpy(sc->sc_bif.bif_serial, aml_strval(res.v_package[10]),
251		sizeof(sc->sc_bif.bif_serial));
252	strlcpy(sc->sc_bif.bif_type, aml_strval(res.v_package[11]),
253		sizeof(sc->sc_bif.bif_type));
254	strlcpy(sc->sc_bif.bif_oem, aml_strval(res.v_package[12]),
255		sizeof(sc->sc_bif.bif_oem));
256
257	dnprintf(60, "power_unit: %u capacity: %u last_cap: %u tech: %u "
258	    "volt: %u warn: %u low: %u gran1: %u gran2: %d model: %s "
259	    "serial: %s type: %s oem: %s\n",
260	    sc->sc_bif.bif_power_unit,
261	    sc->sc_bif.bif_capacity,
262	    sc->sc_bif.bif_last_capacity,
263	    sc->sc_bif.bif_technology,
264	    sc->sc_bif.bif_voltage,
265	    sc->sc_bif.bif_warning,
266	    sc->sc_bif.bif_low,
267	    sc->sc_bif.bif_cap_granu1,
268	    sc->sc_bif.bif_cap_granu2,
269	    sc->sc_bif.bif_model,
270	    sc->sc_bif.bif_serial,
271	    sc->sc_bif.bif_type,
272	    sc->sc_bif.bif_oem);
273
274	rv = 0;
275out:
276	aml_freevalue(&res);
277	return (rv);
278}
279
280int
281acpibat_getbst(struct acpibat_softc *sc)
282{
283	struct aml_value	res;
284	int			rv = EINVAL;
285
286	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BST", 0, NULL, &res)) {
287		dnprintf(10, "%s: no _BST\n", DEVNAME(sc));
288		goto out;
289	}
290
291	if (res.length != 4) {
292		dnprintf(10, "%s: invalid _BST, battery status not saved\n",
293		    DEVNAME(sc));
294		goto out;
295	}
296
297	sc->sc_bst.bst_state = aml_val2int(res.v_package[0]);
298	sc->sc_bst.bst_rate = aml_val2int(res.v_package[1]);
299	sc->sc_bst.bst_capacity = aml_val2int(res.v_package[2]);
300	sc->sc_bst.bst_voltage = aml_val2int(res.v_package[3]);
301
302	dnprintf(60, "state: %u rate: %u cap: %u volt: %u ",
303	    sc->sc_bst.bst_state,
304	    sc->sc_bst.bst_rate,
305	    sc->sc_bst.bst_capacity,
306	    sc->sc_bst.bst_voltage);
307
308	rv = 0;
309out:
310	aml_freevalue(&res);
311	return (rv);
312}
313
314/* XXX it has been observed that some systems do not propagate battery
315 * insertion events up to the driver.  What seems to happen is that DSDT
316 * does receive an interrupt however the originator bit is not set.
317 * This seems to happen when one inserts a 100% full battery.  Removal
318 * of the power cord or insertion of a not 100% full battery breaks this
319 * behavior and all events will then be sent upwards.  Currently there
320 * is no known work-around for it.
321 */
322
323int
324acpibat_notify(struct aml_node *node, int notify_type, void *arg)
325{
326	struct acpibat_softc	*sc = arg;
327
328	dnprintf(10, "acpibat_notify: %.2x %s\n", notify_type,
329	    sc->sc_devnode->parent->name);
330
331	switch (notify_type) {
332	case 0x80:	/* _BST changed */
333		if (!sc->sc_bat_present) {
334			printf("%s: %s: inserted\n", DEVNAME(sc),
335			    sc->sc_devnode->parent->name);
336			sc->sc_bat_present = 1;
337		}
338		break;
339	case 0x81:	/* _BIF changed */
340		/* XXX consider this a device removal */
341		if (sc->sc_bat_present) {
342			printf("%s: %s: removed\n", DEVNAME(sc),
343			    sc->sc_devnode->parent->name);
344			sc->sc_bat_present = 0;
345		}
346		break;
347	default:
348		break;
349	}
350
351	acpibat_refresh(sc);
352
353	return (0);
354}
355