xref: /netbsd-src/sys/dev/acpi/acpi_bat.c (revision cd22f25e6f6d1cc1f197fe8c5468a80f51d1c4e1)
1 /*	$NetBSD: acpi_bat.c,v 1.66 2008/04/28 20:23:47 martin Exp $	*/
2 
3 /*-
4  * Copyright (c) 2003 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Charles M. Hannum of By Noon Software, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright 2001 Bill Sommerfeld.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. All advertising materials mentioning features or use of this software
45  *    must display the following acknowledgement:
46  *	This product includes software developed for the NetBSD Project by
47  *	Wasabi Systems, Inc.
48  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
49  *    or promote products derived from this software without specific prior
50  *    written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
54  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
55  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
56  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
59  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
60  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
61  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
62  * POSSIBILITY OF SUCH DAMAGE.
63  */
64 
65 #if 0
66 #define ACPI_BAT_DEBUG
67 #endif
68 
69 /*
70  * ACPI Battery Driver.
71  *
72  * ACPI defines two different battery device interfaces: "Control
73  * Method" batteries, in which AML methods are defined in order to get
74  * battery status and set battery alarm thresholds, and a "Smart
75  * Battery" device, which is an SMbus device accessed through the ACPI
76  * Embedded Controller device.
77  *
78  * This driver is for the "Control Method"-style battery only.
79  */
80 
81 #include <sys/cdefs.h>
82 __KERNEL_RCSID(0, "$NetBSD: acpi_bat.c,v 1.66 2008/04/28 20:23:47 martin Exp $");
83 
84 #include <sys/param.h>
85 #include <sys/systm.h>
86 #include <sys/kernel.h>		/* for hz */
87 #include <sys/device.h>
88 #include <sys/mutex.h>
89 #include <dev/sysmon/sysmonvar.h>
90 
91 #include <dev/acpi/acpica.h>
92 #include <dev/acpi/acpireg.h>
93 #include <dev/acpi/acpivar.h>
94 
95 /* sensor indexes */
96 #define ACPIBAT_PRESENT		0
97 #define ACPIBAT_DCAPACITY	1
98 #define ACPIBAT_LFCCAPACITY	2
99 #define ACPIBAT_TECHNOLOGY	3
100 #define ACPIBAT_DVOLTAGE	4
101 #define ACPIBAT_WCAPACITY	5
102 #define ACPIBAT_LCAPACITY	6
103 #define ACPIBAT_VOLTAGE		7
104 #define ACPIBAT_CHARGERATE	8
105 #define ACPIBAT_DISCHARGERATE	9
106 #define ACPIBAT_CAPACITY	10
107 #define ACPIBAT_CHARGING	11
108 #define ACPIBAT_CHARGE_STATE	12
109 #define ACPIBAT_NSENSORS	13  /* number of sensors */
110 
111 struct acpibat_softc {
112 	struct acpi_devnode *sc_node;	/* our ACPI devnode */
113 	int sc_flags;			/* see below */
114 	int sc_available;		/* available information level */
115 
116 	struct sysmon_envsys *sc_sme;
117 	envsys_data_t sc_sensor[ACPIBAT_NSENSORS];
118 	kmutex_t sc_mtx;
119 
120 	struct timeval sc_lastupdate, sc_updateinterval;
121 };
122 
123 static const char * const bat_hid[] = {
124 	"PNP0C0A",
125 	NULL
126 };
127 
128 /*
129  * These flags are used to examine the battery device data returned from
130  * the ACPI interface, specifically the "battery status"
131  */
132 #define ACPIBAT_PWRUNIT_MA	0x00000001  /* mA not mW */
133 
134 /*
135  * These flags are used to examine the battery charge/discharge/critical
136  * state returned from a get-status command.
137  */
138 #define ACPIBAT_ST_DISCHARGING	0x00000001  /* battery is discharging */
139 #define ACPIBAT_ST_CHARGING	0x00000002  /* battery is charging */
140 #define ACPIBAT_ST_CRITICAL	0x00000004  /* battery is critical */
141 
142 /*
143  * Flags for battery status from _STA return
144  */
145 #define ACPIBAT_STA_PRESENT	0x00000010  /* battery present */
146 
147 /*
148  * These flags are used to set internal state in our softc.
149  */
150 #define	ABAT_F_VERBOSE		0x01	/* verbose events */
151 #define ABAT_F_PWRUNIT_MA	0x02 	/* mA instead of mW */
152 #define ABAT_F_PRESENT		0x04	/* is the battery present? */
153 
154 #define ABAT_SET(sc, f)		(void)((sc)->sc_flags |= (f))
155 #define ABAT_CLEAR(sc, f)	(void)((sc)->sc_flags &= ~(f))
156 #define ABAT_ISSET(sc, f)	((sc)->sc_flags & (f))
157 
158 /*
159  * Available info level
160  */
161 
162 #define ABAT_ALV_NONE		0	/* none is available */
163 #define ABAT_ALV_PRESENCE	1	/* presence info is available */
164 #define ABAT_ALV_INFO		2	/* battery info is available */
165 #define ABAT_ALV_STAT		3	/* battery status is available */
166 
167 static int	acpibat_match(device_t, struct cfdata *, void *);
168 static void	acpibat_attach(device_t, struct device *, void *);
169 
170 CFATTACH_DECL_NEW(acpibat, sizeof(struct acpibat_softc),
171     acpibat_match, acpibat_attach, NULL, NULL);
172 
173 static void acpibat_clear_presence(struct acpibat_softc *);
174 static void acpibat_clear_info(struct acpibat_softc *);
175 static void acpibat_clear_stat(struct acpibat_softc *);
176 static int acpibat_battery_present(device_t);
177 static ACPI_STATUS acpibat_get_status(device_t);
178 static ACPI_STATUS acpibat_get_info(device_t);
179 static void acpibat_print_info(device_t);
180 static void acpibat_print_stat(device_t);
181 static void acpibat_update(void *);
182 
183 static void acpibat_init_envsys(device_t);
184 static void acpibat_notify_handler(ACPI_HANDLE, UINT32, void *);
185 static void acpibat_refresh(struct sysmon_envsys *, envsys_data_t *);
186 
187 /*
188  * acpibat_match:
189  *
190  *	Autoconfiguration `match' routine.
191  */
192 static int
193 acpibat_match(device_t parent, struct cfdata *match, void *aux)
194 {
195 	struct acpi_attach_args *aa = aux;
196 
197 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
198 		return 0;
199 
200 	return acpi_match_hid(aa->aa_node->ad_devinfo, bat_hid);
201 }
202 
203 /*
204  * acpibat_attach:
205  *
206  *	Autoconfiguration `attach' routine.
207  */
208 static void
209 acpibat_attach(device_t parent, device_t self, void *aux)
210 {
211 	struct acpibat_softc *sc = device_private(self);
212 	struct acpi_attach_args *aa = aux;
213 	ACPI_STATUS rv;
214 
215 	aprint_naive(": ACPI Battery (Control Method)\n");
216 	aprint_normal(": ACPI Battery (Control Method)\n");
217 
218 	sc->sc_node = aa->aa_node;
219 	mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE);
220 
221 	rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
222 				      ACPI_ALL_NOTIFY,
223 				      acpibat_notify_handler, self);
224 	if (ACPI_FAILURE(rv)) {
225 		aprint_error_dev(self,
226 		    "unable to register DEVICE/SYSTEM NOTIFY handler: %s\n",
227 		    AcpiFormatException(rv));
228 		return;
229 	}
230 
231 #ifdef ACPI_BAT_DEBUG
232 	ABAT_SET(sc, ABAT_F_VERBOSE);
233 #endif
234 
235 	if (!pmf_device_register(self, NULL, NULL))
236 		aprint_error_dev(self, "couldn't establish power handler\n");
237 
238 	acpibat_init_envsys(self);
239 }
240 
241 /*
242  * clear informations
243  */
244 
245 static void
246 acpibat_clear_presence(struct acpibat_softc *sc)
247 {
248 	acpibat_clear_info(sc);
249 	sc->sc_available = ABAT_ALV_NONE;
250 	ABAT_CLEAR(sc, ABAT_F_PRESENT);
251 }
252 
253 static void
254 acpibat_clear_info(struct acpibat_softc *sc)
255 {
256 	acpibat_clear_stat(sc);
257 	if (sc->sc_available > ABAT_ALV_PRESENCE)
258 		sc->sc_available = ABAT_ALV_PRESENCE;
259 
260 	sc->sc_sensor[ACPIBAT_DCAPACITY].state = ENVSYS_SINVALID;
261 	sc->sc_sensor[ACPIBAT_LFCCAPACITY].state = ENVSYS_SINVALID;
262 	sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SINVALID;
263 	sc->sc_sensor[ACPIBAT_TECHNOLOGY].state = ENVSYS_SINVALID;
264 	sc->sc_sensor[ACPIBAT_DVOLTAGE].state = ENVSYS_SINVALID;
265 	sc->sc_sensor[ACPIBAT_WCAPACITY].state = ENVSYS_SINVALID;
266 	sc->sc_sensor[ACPIBAT_LCAPACITY].state = ENVSYS_SINVALID;
267 }
268 
269 static void
270 acpibat_clear_stat(struct acpibat_softc *sc)
271 {
272 	if (sc->sc_available > ABAT_ALV_INFO)
273 		sc->sc_available = ABAT_ALV_INFO;
274 
275 	sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID;
276 	sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID;
277 	sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SINVALID;
278 	sc->sc_sensor[ACPIBAT_VOLTAGE].state = ENVSYS_SINVALID;
279 	sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SINVALID;
280 }
281 
282 
283 /*
284  * returns 0 for no battery, 1 for present, and -1 on error
285  */
286 static int
287 acpibat_battery_present(device_t dv)
288 {
289 	struct acpibat_softc *sc = device_private(dv);
290 	uint32_t sta;
291 	ACPI_INTEGER val;
292 	ACPI_STATUS rv;
293 
294 	rv = acpi_eval_integer(sc->sc_node->ad_handle, "_STA", &val);
295 	if (ACPI_FAILURE(rv)) {
296 		aprint_error_dev(dv, "failed to evaluate _STA: %s\n",
297 		    AcpiFormatException(rv));
298 		return -1;
299 	}
300 
301 	sta = (uint32_t)val;
302 
303 	mutex_enter(&sc->sc_mtx);
304 	sc->sc_available = ABAT_ALV_PRESENCE;
305 	if (sta & ACPIBAT_STA_PRESENT) {
306 		ABAT_SET(sc, ABAT_F_PRESENT);
307 		sc->sc_sensor[ACPIBAT_PRESENT].state = ENVSYS_SVALID;
308 		sc->sc_sensor[ACPIBAT_PRESENT].value_cur = 1;
309 	} else
310 		sc->sc_sensor[ACPIBAT_PRESENT].value_cur = 0;
311 
312 	mutex_exit(&sc->sc_mtx);
313 
314 	return (sta & ACPIBAT_STA_PRESENT) ? 1 : 0;
315 }
316 
317 /*
318  * acpibat_get_info
319  *
320  * 	Get, and possibly display, the battery info.
321  */
322 
323 static ACPI_STATUS
324 acpibat_get_info(device_t dv)
325 {
326 	struct acpibat_softc *sc = device_private(dv);
327 	ACPI_OBJECT *p1, *p2;
328 	ACPI_STATUS rv;
329 	ACPI_BUFFER buf;
330 	int capunit, rateunit;
331 
332 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BIF", &buf);
333 	if (ACPI_FAILURE(rv)) {
334 		aprint_error_dev(dv, "failed to evaluate _BIF: %s\n",
335 		    AcpiFormatException(rv));
336 		return rv;
337 	}
338 	p1 = (ACPI_OBJECT *)buf.Pointer;
339 
340 	if (p1->Type != ACPI_TYPE_PACKAGE) {
341 		aprint_error_dev(dv, "expected PACKAGE, got %d\n", p1->Type);
342 		goto out;
343 	}
344 	if (p1->Package.Count < 13) {
345 		aprint_error_dev(dv, "expected 13 elements, got %d\n",
346 		    p1->Package.Count);
347 		goto out;
348 	}
349 	p2 = p1->Package.Elements;
350 
351 	mutex_enter(&sc->sc_mtx);
352 	if ((p2[0].Integer.Value & ACPIBAT_PWRUNIT_MA) != 0) {
353 		ABAT_SET(sc, ABAT_F_PWRUNIT_MA);
354 		capunit = ENVSYS_SAMPHOUR;
355 		rateunit = ENVSYS_SAMPS;
356 	} else {
357 		ABAT_CLEAR(sc, ABAT_F_PWRUNIT_MA);
358 		capunit = ENVSYS_SWATTHOUR;
359 		rateunit = ENVSYS_SWATTS;
360 	}
361 
362 	sc->sc_sensor[ACPIBAT_DCAPACITY].units = capunit;
363 	sc->sc_sensor[ACPIBAT_LFCCAPACITY].units = capunit;
364 	sc->sc_sensor[ACPIBAT_WCAPACITY].units = capunit;
365 	sc->sc_sensor[ACPIBAT_LCAPACITY].units = capunit;
366 	sc->sc_sensor[ACPIBAT_CHARGERATE].units = rateunit;
367 	sc->sc_sensor[ACPIBAT_DISCHARGERATE].units = rateunit;
368 	sc->sc_sensor[ACPIBAT_CAPACITY].units = capunit;
369 
370 	sc->sc_sensor[ACPIBAT_DCAPACITY].value_cur = p2[1].Integer.Value * 1000;
371 	sc->sc_sensor[ACPIBAT_DCAPACITY].state = ENVSYS_SVALID;
372 	sc->sc_sensor[ACPIBAT_LFCCAPACITY].value_cur = p2[2].Integer.Value * 1000;
373 	sc->sc_sensor[ACPIBAT_LFCCAPACITY].state = ENVSYS_SVALID;
374 	sc->sc_sensor[ACPIBAT_CAPACITY].value_max = p2[2].Integer.Value * 1000;
375 	sc->sc_sensor[ACPIBAT_TECHNOLOGY].value_cur = p2[3].Integer.Value;
376 	sc->sc_sensor[ACPIBAT_TECHNOLOGY].state = ENVSYS_SVALID;
377 	sc->sc_sensor[ACPIBAT_DVOLTAGE].value_cur = p2[4].Integer.Value * 1000;
378 	sc->sc_sensor[ACPIBAT_DVOLTAGE].state = ENVSYS_SVALID;
379 	sc->sc_sensor[ACPIBAT_WCAPACITY].value_cur = p2[5].Integer.Value * 1000;
380 	sc->sc_sensor[ACPIBAT_WCAPACITY].value_max = p2[2].Integer.Value * 1000;
381 	sc->sc_sensor[ACPIBAT_WCAPACITY].state = ENVSYS_SVALID;
382 	sc->sc_sensor[ACPIBAT_WCAPACITY].flags |=
383 	    (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX);
384 	sc->sc_sensor[ACPIBAT_LCAPACITY].value_cur = p2[6].Integer.Value * 1000;
385 	sc->sc_sensor[ACPIBAT_LCAPACITY].value_max = p2[2].Integer.Value * 1000;
386 	sc->sc_sensor[ACPIBAT_LCAPACITY].state = ENVSYS_SVALID;
387 	sc->sc_sensor[ACPIBAT_LCAPACITY].flags |=
388 	    (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX);
389 	sc->sc_available = ABAT_ALV_INFO;
390 
391 	mutex_exit(&sc->sc_mtx);
392 
393 	aprint_verbose_dev(dv, "battery info: %s, %s, %s",
394 	    p2[12].String.Pointer, p2[11].String.Pointer, p2[9].String.Pointer);
395 	if (p2[10].String.Pointer)
396 		aprint_verbose(" %s", p2[10].String.Pointer);
397 
398 	aprint_verbose("\n");
399 
400 	rv = AE_OK;
401 
402 out:
403 	AcpiOsFree(buf.Pointer);
404 	return rv;
405 }
406 
407 /*
408  * acpibat_get_status:
409  *
410  *	Get, and possibly display, the current battery line status.
411  */
412 static ACPI_STATUS
413 acpibat_get_status(device_t dv)
414 {
415 	struct acpibat_softc *sc = device_private(dv);
416 	int status, battrate;
417 	ACPI_OBJECT *p1, *p2;
418 	ACPI_STATUS rv;
419 	ACPI_BUFFER buf;
420 
421 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_BST", &buf);
422 	if (ACPI_FAILURE(rv)) {
423 		aprint_error_dev(dv, "failed to evaluate _BST: %s\n",
424 		    AcpiFormatException(rv));
425 		return rv;
426 	}
427 	p1 = (ACPI_OBJECT *)buf.Pointer;
428 
429 	if (p1->Type != ACPI_TYPE_PACKAGE) {
430 		aprint_error_dev(dv, "expected PACKAGE, got %d\n",
431 		    p1->Type);
432 		rv = AE_ERROR;
433 		goto out;
434 	}
435 	if (p1->Package.Count < 4) {
436 		aprint_error_dev(dv, "expected 4 elts, got %d\n",
437 		    p1->Package.Count);
438 		rv = AE_ERROR;
439 		goto out;
440 	}
441 	p2 = p1->Package.Elements;
442 
443 	mutex_enter(&sc->sc_mtx);
444 
445 	status = p2[0].Integer.Value;
446 	battrate = p2[1].Integer.Value;
447 
448 	if (status & ACPIBAT_ST_CHARGING) {
449 		sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SVALID;
450 		sc->sc_sensor[ACPIBAT_CHARGERATE].value_cur = battrate * 1000;
451 		sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID;
452 		sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID;
453 		sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 1;
454 	} else if (status & ACPIBAT_ST_DISCHARGING) {
455 		sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SVALID;
456 		sc->sc_sensor[ACPIBAT_DISCHARGERATE].value_cur = battrate * 1000;
457 		sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID;
458 		sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID;
459 		sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 0;
460 	} else if (!(status & (ACPIBAT_ST_CHARGING|ACPIBAT_ST_DISCHARGING))) {
461 		sc->sc_sensor[ACPIBAT_CHARGING].state = ENVSYS_SVALID;
462 		sc->sc_sensor[ACPIBAT_CHARGING].value_cur = 0;
463 		sc->sc_sensor[ACPIBAT_CHARGERATE].state = ENVSYS_SINVALID;
464 		sc->sc_sensor[ACPIBAT_DISCHARGERATE].state = ENVSYS_SINVALID;
465 	}
466 
467 	sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur =
468 	    ENVSYS_BATTERY_CAPACITY_NORMAL;
469 
470 	sc->sc_sensor[ACPIBAT_CAPACITY].value_cur = p2[2].Integer.Value * 1000;
471 	sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SVALID;
472 	sc->sc_sensor[ACPIBAT_CAPACITY].flags |=
473 	    (ENVSYS_FPERCENT|ENVSYS_FVALID_MAX);
474 	sc->sc_sensor[ACPIBAT_VOLTAGE].value_cur = p2[3].Integer.Value * 1000;
475 	sc->sc_sensor[ACPIBAT_VOLTAGE].state = ENVSYS_SVALID;
476 
477 	if (sc->sc_sensor[ACPIBAT_CAPACITY].value_cur <
478 	    sc->sc_sensor[ACPIBAT_WCAPACITY].value_cur) {
479 		sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SWARNUNDER;
480 		sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur =
481 		    ENVSYS_BATTERY_CAPACITY_WARNING;
482 	}
483 
484 	if (sc->sc_sensor[ACPIBAT_CAPACITY].value_cur <
485 	    sc->sc_sensor[ACPIBAT_LCAPACITY].value_cur) {
486 		sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SCRITUNDER;
487 		sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur =
488 		    ENVSYS_BATTERY_CAPACITY_LOW;
489 	}
490 
491 	if (status & ACPIBAT_ST_CRITICAL) {
492 		sc->sc_sensor[ACPIBAT_CAPACITY].state = ENVSYS_SCRITICAL;
493 		sc->sc_sensor[ACPIBAT_CHARGE_STATE].value_cur =
494 		    ENVSYS_BATTERY_CAPACITY_CRITICAL;
495 	}
496 
497 	mutex_exit(&sc->sc_mtx);
498 
499 	rv = AE_OK;
500 
501 out:
502 	AcpiOsFree(buf.Pointer);
503 	return rv;
504 }
505 
506 #define SCALE(x)	((x)/1000000), (((x)%1000000)/1000)
507 #define CAPUNITS(sc)	(ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"Ah":"Wh")
508 #define RATEUNITS(sc)	(ABAT_ISSET((sc), ABAT_F_PWRUNIT_MA)?"A":"W")
509 static void
510 acpibat_print_info(device_t dv)
511 {
512 	struct acpibat_softc *sc = device_private(dv);
513 	const char *tech;
514 
515 	if (sc->sc_sensor[ACPIBAT_TECHNOLOGY].value_cur)
516 		tech = "secondary";
517 	else
518 		tech = "primary";
519 
520 	aprint_debug_dev(dv, "%s battery, Design %d.%03d%s "
521 	    "Last full %d.%03d%s Warn %d.%03d%s Low %d.%03d%s\n",
522 	    tech, SCALE(sc->sc_sensor[ACPIBAT_DCAPACITY].value_cur), CAPUNITS(sc),
523 	    SCALE(sc->sc_sensor[ACPIBAT_LFCCAPACITY].value_cur),CAPUNITS(sc),
524 	    SCALE(sc->sc_sensor[ACPIBAT_WCAPACITY].value_cur), CAPUNITS(sc),
525 	    SCALE(sc->sc_sensor[ACPIBAT_LCAPACITY].value_cur), CAPUNITS(sc));
526 }
527 
528 static void
529 acpibat_print_stat(device_t dv)
530 {
531 	struct acpibat_softc *sc = device_private(dv);
532 	const char *capstat, *chargestat;
533 	int percent, denom;
534 	int32_t value;
535 
536 	percent = 0;
537 
538 	if (sc->sc_sensor[ACPIBAT_CAPACITY].state == ENVSYS_SCRITUNDER)
539 		capstat = "CRITICAL UNDER ";
540 	else if (sc->sc_sensor[ACPIBAT_CAPACITY].state == ENVSYS_SCRITOVER)
541 		capstat = "CRITICAL OVER ";
542 	else
543 		capstat = "";
544 
545 	if (sc->sc_sensor[ACPIBAT_CHARGING].state != ENVSYS_SVALID) {
546 		chargestat = "idling";
547 		value = 0;
548 	} else if (sc->sc_sensor[ACPIBAT_CHARGING].value_cur == 0) {
549 		chargestat = "discharging";
550 		value = sc->sc_sensor[ACPIBAT_DISCHARGERATE].value_cur;
551 	} else {
552 		chargestat = "charging";
553 		value = sc->sc_sensor[ACPIBAT_CHARGERATE].value_cur;
554 	}
555 
556 	denom = sc->sc_sensor[ACPIBAT_LFCCAPACITY].value_cur / 100;
557 	if (denom > 0)
558 		percent = (sc->sc_sensor[ACPIBAT_CAPACITY].value_cur) / denom;
559 
560 	aprint_debug_dev(dv, "%s%s: %d.%03dV cap %d.%03d%s (%d%%) "
561 	    "rate %d.%03d%s\n", capstat, chargestat,
562 	    SCALE(sc->sc_sensor[ACPIBAT_VOLTAGE].value_cur),
563 	    SCALE(sc->sc_sensor[ACPIBAT_CAPACITY].value_cur), CAPUNITS(sc),
564 	    percent, SCALE(value), RATEUNITS(sc));
565 }
566 
567 static void
568 acpibat_update(void *arg)
569 {
570 	device_t dv = arg;
571 	struct acpibat_softc *sc = device_private(dv);
572 
573 	if (sc->sc_available < ABAT_ALV_INFO) {
574 		/* current information is invalid */
575 #if 0
576 		/*
577 		 * XXX: The driver sometimes unaware that the battery exist.
578 		 * (i.e. just after the boot or resuming)
579 		 * Thus, the driver should always check it here.
580 		 */
581 		if (sc->sc_available < ABAT_ALV_PRESENCE)
582 #endif
583 			/* presence is invalid */
584 			if (acpibat_battery_present(dv) < 0) {
585 				/* error */
586 				aprint_debug_dev(dv,
587 				    "cannot get battery presence.\n");
588 				return;
589 			}
590 
591 		if (ABAT_ISSET(sc, ABAT_F_PRESENT)) {
592 			/* the battery is present. */
593 			if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
594 				aprint_debug_dev(dv,
595 				    "battery is present.\n");
596 			if (ACPI_FAILURE(acpibat_get_info(dv)))
597 				return;
598 			if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
599 				acpibat_print_info(dv);
600 		} else {
601 			/* the battery is not present. */
602 			if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
603 				aprint_debug_dev(dv,
604 				    "battery is not present.\n");
605 			return;
606 		}
607 	} else {
608 		/* current information is valid */
609 		if (!ABAT_ISSET(sc, ABAT_F_PRESENT)) {
610 			/* the battery is not present. */
611 			return;
612 		}
613  	}
614 
615 	if (ACPI_FAILURE(acpibat_get_status(dv)))
616 		return;
617 
618 	if (ABAT_ISSET(sc, ABAT_F_VERBOSE))
619 		acpibat_print_stat(dv);
620 }
621 
622 /*
623  * acpibat_notify_handler:
624  *
625  *	Callback from ACPI interrupt handler to notify us of an event.
626  */
627 static void
628 acpibat_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
629 {
630 	device_t dv = context;
631 	struct acpibat_softc *sc = device_private(dv);
632 	int rv;
633 
634 #ifdef ACPI_BAT_DEBUG
635 	aprint_debug_dev(dv, "received notify message: 0x%x\n", notify);
636 #endif
637 
638 	switch (notify) {
639 	case ACPI_NOTIFY_BusCheck:
640 		break;
641 
642 	case ACPI_NOTIFY_DeviceCheck:
643 	case ACPI_NOTIFY_BatteryInformationChanged:
644 		mutex_enter(&sc->sc_mtx);
645 		acpibat_clear_presence(sc);
646 		mutex_exit(&sc->sc_mtx);
647 		rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, acpibat_update, dv);
648 		if (ACPI_FAILURE(rv))
649 			aprint_error_dev(dv,
650 			    "unable to queue status check: %s\n",
651 			    AcpiFormatException(rv));
652 		break;
653 
654 	case ACPI_NOTIFY_BatteryStatusChanged:
655 		mutex_enter(&sc->sc_mtx);
656 		acpibat_clear_stat(sc);
657 		mutex_exit(&sc->sc_mtx);
658 		rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, acpibat_update, dv);
659 		if (ACPI_FAILURE(rv))
660 			aprint_error_dev(dv,
661 			    "unable to queue status check: %s\n",
662 			    AcpiFormatException(rv));
663 		break;
664 
665 	default:
666 		aprint_error_dev(dv,
667 		    "received unknown notify message: 0x%x\n", notify);
668 	}
669 }
670 
671 static void
672 acpibat_init_envsys(device_t dv)
673 {
674 	struct acpibat_softc *sc = device_private(dv);
675 	int i, capunit, rateunit;
676 
677 	if (sc->sc_flags & ABAT_F_PWRUNIT_MA) {
678 		capunit = ENVSYS_SAMPHOUR;
679 		rateunit = ENVSYS_SAMPS;
680 	} else {
681 		capunit = ENVSYS_SWATTHOUR;
682 		rateunit = ENVSYS_SWATTS;
683 	}
684 
685 #define INITDATA(index, unit, string)					\
686 	sc->sc_sensor[index].state = ENVSYS_SVALID;			\
687 	sc->sc_sensor[index].units = unit;     				\
688  	strlcpy(sc->sc_sensor[index].desc, string,			\
689  	    sizeof(sc->sc_sensor[index].desc));
690 
691 	INITDATA(ACPIBAT_PRESENT, ENVSYS_INDICATOR, "present");
692 	INITDATA(ACPIBAT_DCAPACITY, capunit, "design cap");
693 	INITDATA(ACPIBAT_LFCCAPACITY, capunit, "last full cap");
694 	INITDATA(ACPIBAT_TECHNOLOGY, ENVSYS_INTEGER, "technology");
695 	INITDATA(ACPIBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage");
696 	INITDATA(ACPIBAT_WCAPACITY, capunit, "warn cap");
697 	INITDATA(ACPIBAT_LCAPACITY, capunit, "low cap");
698 	INITDATA(ACPIBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage");
699 	INITDATA(ACPIBAT_CHARGERATE, rateunit, "charge rate");
700 	INITDATA(ACPIBAT_DISCHARGERATE, rateunit, "discharge rate");
701 	INITDATA(ACPIBAT_CAPACITY, capunit, "charge");
702 	INITDATA(ACPIBAT_CHARGING, ENVSYS_BATTERY_CHARGE, "charging");
703 	INITDATA(ACPIBAT_CHARGE_STATE, ENVSYS_BATTERY_CAPACITY, "charge state");
704 
705 #undef INITDATA
706 
707 	/* Enable monitoring for the charge state sensor */
708 	sc->sc_sensor[ACPIBAT_CHARGE_STATE].monitor = true;
709 	sc->sc_sensor[ACPIBAT_CHARGE_STATE].flags |= ENVSYS_FMONSTCHANGED;
710 
711 	/* Disable userland monitoring on these sensors */
712 	sc->sc_sensor[ACPIBAT_VOLTAGE].flags = ENVSYS_FMONNOTSUPP;
713 	sc->sc_sensor[ACPIBAT_CHARGERATE].flags = ENVSYS_FMONNOTSUPP;
714 	sc->sc_sensor[ACPIBAT_DISCHARGERATE].flags = ENVSYS_FMONNOTSUPP;
715 	sc->sc_sensor[ACPIBAT_DCAPACITY].flags = ENVSYS_FMONNOTSUPP;
716 	sc->sc_sensor[ACPIBAT_LFCCAPACITY].flags = ENVSYS_FMONNOTSUPP;
717 	sc->sc_sensor[ACPIBAT_TECHNOLOGY].flags = ENVSYS_FMONNOTSUPP;
718 	sc->sc_sensor[ACPIBAT_DVOLTAGE].flags = ENVSYS_FMONNOTSUPP;
719 	sc->sc_sensor[ACPIBAT_WCAPACITY].flags = ENVSYS_FMONNOTSUPP;
720 	sc->sc_sensor[ACPIBAT_LCAPACITY].flags = ENVSYS_FMONNOTSUPP;
721 
722 	sc->sc_sme = sysmon_envsys_create();
723 	for (i = 0; i < ACPIBAT_NSENSORS; i++) {
724 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
725 						&sc->sc_sensor[i])) {
726 			aprint_error_dev(dv, "unable to add sensor%d\n", i);
727 			sysmon_envsys_destroy(sc->sc_sme);
728 			return;
729 		}
730 	}
731 
732 	sc->sc_sme->sme_name = device_xname(dv);
733 	sc->sc_sme->sme_cookie = dv;
734 	sc->sc_sme->sme_refresh = acpibat_refresh;
735 	sc->sc_sme->sme_class = SME_CLASS_BATTERY;
736 	sc->sc_sme->sme_flags = SME_INIT_REFRESH;
737 
738 	sc->sc_updateinterval.tv_sec = 1;
739 	sc->sc_updateinterval.tv_usec = 0;
740 
741 	if (sysmon_envsys_register(sc->sc_sme)) {
742 		aprint_error_dev(dv, "unable to register with sysmon\n");
743 		sysmon_envsys_destroy(sc->sc_sme);
744 	}
745 }
746 
747 static void
748 acpibat_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
749 {
750 	device_t dv = sme->sme_cookie;
751 	struct acpibat_softc *sc = device_private(dv);
752 
753 	if (ratecheck(&sc->sc_lastupdate, &sc->sc_updateinterval))
754 		acpibat_update(dv);
755 }
756