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