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