Home | History | Annotate | Line # | Download | only in dev
      1 /* $NetBSD: pbbat.c,v 1.2 2025/04/09 00:10:02 nat Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2025 Nathanial Sloss <nathanialsloss (at) yahoo.com.au>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 /* Based on acpibat(4). */
     30 
     31 /*-
     32  * Copyright (c) 2003 The NetBSD Foundation, Inc.
     33  * All rights reserved.
     34  *
     35  * This code is derived from software contributed to The NetBSD Foundation
     36  * by Charles M. Hannum of By Noon Software, Inc.
     37  *
     38  * Redistribution and use in source and binary forms, with or without
     39  * modification, are permitted provided that the following conditions
     40  * are met:
     41  * 1. Redistributions of source code must retain the above copyright
     42  *    notice, this list of conditions and the following disclaimer.
     43  * 2. Redistributions in binary form must reproduce the above copyright
     44  *    notice, this list of conditions and the following disclaimer in the
     45  *    documentation and/or other materials provided with the distribution.
     46  *
     47  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     48  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     49  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     50  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     51  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     57  * POSSIBILITY OF SUCH DAMAGE.
     58  */
     59 
     60 /*
     61  * Copyright 2001 Bill Sommerfeld.
     62  * All rights reserved.
     63  *
     64  * Redistribution and use in source and binary forms, with or without
     65  * modification, are permitted provided that the following conditions
     66  * are met:
     67  * 1. Redistributions of source code must retain the above copyright
     68  *    notice, this list of conditions and the following disclaimer.
     69  * 2. Redistributions in binary form must reproduce the above copyright
     70  *    notice, this list of conditions and the following disclaimer in the
     71  *    documentation and/or other materials provided with the distribution.
     72  * 3. All advertising materials mentioning features or use of this software
     73  *    must display the following acknowledgement:
     74  *	This product includes software developed for the NetBSD Project by
     75  *	Wasabi Systems, Inc.
     76  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     77  *    or promote products derived from this software without specific prior
     78  *    written permission.
     79  *
     80  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     81  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     82  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     83  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     84  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     85  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     86  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     87  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     88  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     89  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     90  * POSSIBILITY OF SUCH DAMAGE.
     91  */
     92 
     93 /* AC Adaptor attachment and logic based on macppc/smartbat(4). */
     94 
     95 /*-
     96  * Copyright (c) 2007 Michael Lorenz
     97  *               2008 Magnus Henoch
     98  * All rights reserved.
     99  *
    100  * Redistribution and use in source and binary forms, with or without
    101  * modification, are permitted provided that the following conditions
    102  * are met:
    103  * 1. Redistributions of source code must retain the above copyright
    104  *    notice, this list of conditions and the following disclaimer.
    105  * 2. Redistributions in binary form must reproduce the above copyright
    106  *    notice, this list of conditions and the following disclaimer in the
    107  *    documentation and/or other materials provided with the distribution.
    108  *
    109  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
    110  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    111  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    112  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
    113  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    114  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    115  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    116  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    117  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    118  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    119  * POSSIBILITY OF SUCH DAMAGE.
    120  */
    121 
    122 #include <sys/cdefs.h>
    123 __KERNEL_RCSID(0, "$NetBSD: pbbat.c,v 1.2 2025/04/09 00:10:02 nat Exp $");
    124 
    125 #include <sys/param.h>
    126 #include <sys/device.h>
    127 #include <sys/kmem.h>
    128 #include <sys/types.h>
    129 #include <sys/systm.h>
    130 
    131 #include <dev/sysmon/sysmonvar.h>
    132 
    133 #include <machine/param.h>
    134 #include <machine/cpu.h>
    135 
    136 #include <mac68k/dev/pm_direct.h>
    137 
    138 static int	pbbatmatch(device_t, cfdata_t, void *);
    139 static void	pbbatattach(device_t, device_t, void *);
    140 
    141 static void	bat_get_pm_limits(device_t);
    142 static uint16_t	bat_get_status(device_t);
    143 static void	bat_init_envsys(device_t);
    144 static void	bat_update_status(void *);
    145 
    146 extern int	pm_pmgrop_pm1(PMData *);
    147 
    148 struct pbatt_softc {
    149 	device_t		 sc_dev;
    150 	struct sysmon_envsys	*sc_ac_sme;
    151 	envsys_data_t		 sc_ac_sensor[1];
    152 	struct sysmon_pswitch	 sc_sm_acpower;
    153 	int8_t			 sc_ac_state;
    154 	struct sysmon_envsys	*sc_bat_sme;
    155 	envsys_data_t		*sc_bat_sensor;
    156 	struct timeval		 sc_last;
    157 	kmutex_t		 sc_mutex;
    158 	int32_t			 sc_dcapacity;
    159 	int32_t			 sc_dvoltage;
    160 	int32_t			 sc_disrate;
    161 	int32_t			 sc_chargerate;
    162 	int32_t			 sc_empty;
    163 	int32_t			 sc_lcapacity;
    164 	int32_t			 sc_wcapacity;
    165 	int                      sc_present;
    166 };
    167 
    168 #define PBBAT_AC_PRESENT	 0
    169 
    170 /* AC Adaptor states */
    171 #define PBBAT_AC_UNKNOWN	-1
    172 #define PBBAT_AC_DISCONNECTED	 0
    173 #define PBBAT_AC_CONNECTED	 1
    174 
    175 enum {
    176 	PBBAT_PRESENT		 = 0,
    177 	PBBAT_DVOLTAGE		 = 1,
    178 	PBBAT_VOLTAGE		 = 2,
    179 	PBBAT_DCAPACITY		 = 3,
    180 	PBBAT_LFCCAPACITY	 = 4,
    181 	PBBAT_CAPACITY		 = 5,
    182 	PBBAT_CHARGERATE	 = 6,
    183 	PBBAT_DISCHARGERATE	 = 7,
    184 	PBBAT_CHARGING		 = 8,
    185 	PBBAT_CHARGE_STATE	 = 9,
    186 	PBBAT_COUNT		 = 10
    187 };
    188 
    189 /* Driver definition */
    190 CFATTACH_DECL_NEW(pbbat, sizeof(struct pbatt_softc),
    191     pbbatmatch, pbbatattach, NULL, NULL);
    192 
    193 /* Battery voltage definitions (mV) */
    194 #define VOLTS_DESIGN	6000
    195 #define WATTS_DESIGN	60000	/* mW */
    196 #define VOLTS_CHARGING	6600
    197 #define VOLTS_NOBATT	7700
    198 
    199 #define VOLTS_MULTI	35	/* PM value multiplier. */
    200 #define LIMIT_SCALE	(100 * 100 / (VOLTS_DESIGN / 1000))
    201 
    202 #define PM_BATT_VOLTS	0x68	/* 0x69 is a duplicate. */
    203 #define PM_BATT_LIMITS	0x6a
    204 
    205 static int
    206 pbbatmatch(device_t parent, cfdata_t cf, void *aux)
    207 {
    208 	switch (mac68k_machine.machineid) {
    209 		case MACH_MACPB140:
    210 		case MACH_MACPB145:
    211 		case MACH_MACPB160:
    212 		case MACH_MACPB165:
    213 		case MACH_MACPB165C:
    214 		case MACH_MACPB170:
    215 		case MACH_MACPB180:
    216 		case MACH_MACPB180C:
    217 			return 1;
    218 			break;
    219 		default:
    220 			return 0;
    221 	}
    222 
    223 	return 0;
    224 }
    225 
    226 static void
    227 pbbatattach(device_t parent, device_t self, void *aux)
    228 {
    229 	struct pbatt_softc *sc = device_private(self);
    230 
    231 	aprint_naive(": PowerBook Battery\n");
    232 	aprint_normal(": PowerBook Battery\n");
    233 
    234 	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
    235 	sc->sc_bat_sensor = kmem_zalloc(PBBAT_COUNT *
    236 	    sizeof(*sc->sc_bat_sensor), KM_SLEEP);
    237 
    238 	memset(&sc->sc_sm_acpower, 0, sizeof(struct sysmon_pswitch));
    239 	sc->sc_ac_state = PBBAT_AC_UNKNOWN;
    240 	sc->sc_sm_acpower.smpsw_name = "AC Power";
    241 	sc->sc_sm_acpower.smpsw_type = PSWITCH_TYPE_ACADAPTER;
    242 	if (sysmon_pswitch_register(&sc->sc_sm_acpower) != 0)
    243 		printf("%s: unable to register AC power status with sysmon\n",
    244 		    device_xname(sc->sc_dev));
    245 
    246 	config_interrupts(self, bat_init_envsys);
    247 }
    248 
    249 static void
    250 bat_get_pm_limits(device_t self)
    251 {
    252 	int s;
    253 	int rval;
    254 	PMData pmdata;
    255 	struct pbatt_softc *sc = device_private(self);
    256 
    257 	s = splhigh();
    258 
    259 	pmdata.command = PM_BATT_LIMITS;
    260 	pmdata.num_data = 0;
    261 	pmdata.data[0] = pmdata.data[1] = 0;
    262 	pmdata.s_buf = pmdata.data;
    263 	pmdata.r_buf = pmdata.data;
    264 	rval = pm_pmgrop_pm1(&pmdata);
    265 	if (rval != 0) {
    266 #ifdef ADB_DEBUG
    267 		if (adb_debug)
    268 			printf("pm: PM is not ready. error code=%08x\n", rval);
    269 #endif
    270 		splx(s);
    271 		return;
    272 	}
    273 
    274 	splx(s);
    275 
    276 	sc->sc_empty = (pmdata.data[1] & 0xff) * VOLTS_MULTI;
    277 	sc->sc_lcapacity = (pmdata.data[0] & 0xff) * VOLTS_MULTI;
    278 	sc->sc_wcapacity = sc->sc_lcapacity * 12 / 10;
    279 
    280 	return;
    281 }
    282 
    283 static uint16_t
    284 bat_get_voltage(void)
    285 {
    286 	int s;
    287 	int rval;
    288 	PMData pmdata;
    289 
    290 	s = splhigh();
    291 
    292 	pmdata.command = PM_BATT_VOLTS;
    293 	pmdata.num_data = 0;
    294 	pmdata.data[0] = pmdata.data[1] = 0;
    295 	pmdata.s_buf = pmdata.data;
    296 	pmdata.r_buf = pmdata.data;
    297 	rval = pm_pmgrop_pm1(&pmdata);
    298 	if (rval != 0) {
    299 #ifdef ADB_DEBUG
    300 		if (adb_debug)
    301 			printf("pm: PM is not ready. error code=%08x\n", rval);
    302 #endif
    303 		splx(s);
    304 		return 0;
    305 	}
    306 
    307 	splx(s);
    308 
    309 	return (pmdata.data[1] & 0xff) * VOLTS_MULTI;
    310 }
    311 
    312 static void
    313 bat_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    314 {
    315 	device_t self = sme->sme_cookie;
    316 	struct pbatt_softc *sc = device_private(self);
    317 	struct timeval tv, tmp;
    318 
    319 	tmp.tv_sec = 10;
    320 	tmp.tv_usec = 0;
    321 
    322 	microuptime(&tv);
    323 	timersub(&tv, &tmp, &tv);
    324 	if (timercmp(&tv, &sc->sc_last, <) != 0)
    325 		return;
    326 
    327 	bat_update_status(self);
    328 }
    329 
    330 static void
    331 bat_refresh_ac(struct sysmon_envsys *sme, envsys_data_t *edata)
    332 {
    333 	struct pbatt_softc *sc = sme->sme_cookie;
    334 	int which = edata->sensor;
    335 
    336 	mutex_enter(&sc->sc_mutex);
    337 	switch (which) {
    338 		case PBBAT_AC_PRESENT:
    339 			edata->value_cur =
    340 			    (sc->sc_ac_state == PBBAT_AC_CONNECTED ? 1 : 0);
    341 			edata->state = ENVSYS_SVALID;
    342 			break;
    343 		default:
    344 			edata->value_cur = 0;
    345 			edata->state = ENVSYS_SINVALID;
    346 	}
    347 	mutex_exit(&sc->sc_mutex);
    348 }
    349 
    350 static void
    351 bat_get_info(device_t dv)
    352 {
    353 	struct pbatt_softc *sc = device_private(dv);
    354 	int capunit;
    355 
    356 	capunit = ENVSYS_SWATTHOUR;
    357 
    358 	sc->sc_bat_sensor[PBBAT_DCAPACITY].units = capunit;
    359 	sc->sc_bat_sensor[PBBAT_CHARGERATE].units = capunit;
    360 	sc->sc_bat_sensor[PBBAT_DISCHARGERATE].units = capunit;
    361 	sc->sc_bat_sensor[PBBAT_LFCCAPACITY].units = capunit;
    362 	sc->sc_bat_sensor[PBBAT_CAPACITY].units = capunit;
    363 
    364 	/* Design capacity. */
    365 
    366 	/*
    367 	 * This is a guesstimate - repacked battery runs at 10 Watts/h for an
    368 	 * 1 hour.
    369          */
    370 
    371 	sc->sc_bat_sensor[PBBAT_DCAPACITY].value_cur = WATTS_DESIGN * 1000;
    372 	sc->sc_bat_sensor[PBBAT_DCAPACITY].state = ENVSYS_SVALID;
    373 
    374 	/* Design voltage. */
    375 	sc->sc_bat_sensor[PBBAT_DVOLTAGE].value_cur = VOLTS_DESIGN * 1000;
    376 	sc->sc_bat_sensor[PBBAT_DVOLTAGE].state = ENVSYS_SVALID;
    377 
    378 	sc->sc_bat_sensor[PBBAT_LFCCAPACITY].state = ENVSYS_SINVALID;
    379 
    380 	bat_get_pm_limits(dv);
    381 
    382 	sc->sc_bat_sensor[PBBAT_CAPACITY].value_max = 100 * 1000 * 1000;
    383 }
    384 
    385 static void
    386 bat_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
    387     sysmon_envsys_lim_t *limits, uint32_t *props)
    388 {
    389 	device_t self = sme->sme_cookie;
    390 	struct pbatt_softc *sc = device_private(self);
    391 
    392 	if (edata->sensor != PBBAT_CAPACITY)
    393 		return;
    394 
    395 	limits->sel_critmin = sc->sc_lcapacity * LIMIT_SCALE;
    396 	limits->sel_warnmin = sc->sc_wcapacity * LIMIT_SCALE;
    397 
    398 	*props |= PROP_BATTCAP | PROP_BATTWARN | PROP_DRIVER_LIMITS;
    399 }
    400 
    401 static void
    402 bat_update_status(void *arg)
    403 {
    404 	device_t dv = arg;
    405 	struct pbatt_softc *sc = device_private(dv);
    406 	int i;
    407 	uint16_t val;
    408 
    409 	mutex_enter(&sc->sc_mutex);
    410 
    411 	val = bat_get_status(dv);
    412 	if (val != 0) {
    413 		if (sc->sc_present == 0)
    414 			bat_get_info(dv);
    415 	} else {
    416 		i = PBBAT_DVOLTAGE;
    417 		while (i < PBBAT_COUNT) {
    418 			sc->sc_bat_sensor[i].state = ENVSYS_SINVALID;
    419 			i++;
    420 		}
    421 	}
    422 
    423 	sc->sc_present = (val >= VOLTS_NOBATT ? 0 : 1);
    424 
    425 	microuptime(&sc->sc_last);
    426 
    427 	mutex_exit(&sc->sc_mutex);
    428 }
    429 
    430 static void
    431 bat_init_envsys(device_t dv)
    432 {
    433 	struct pbatt_softc *sc = device_private(dv);
    434 	int i;
    435 
    436 #define INITDATA(index, unit, string)					\
    437 	sc->sc_ac_sensor[index].units = unit;     			\
    438 	sc->sc_ac_sensor[index].state = ENVSYS_SINVALID;		\
    439 	snprintf(sc->sc_ac_sensor[index].desc,				\
    440 	    sizeof(sc->sc_ac_sensor[index].desc), "%s", string);
    441 
    442 		INITDATA(PBBAT_AC_PRESENT, ENVSYS_INDICATOR, "connected");
    443 #undef INITDATA
    444 
    445 	sc->sc_ac_sme = sysmon_envsys_create();
    446 
    447 	if (sysmon_envsys_sensor_attach(sc->sc_ac_sme, &sc->sc_ac_sensor[0])) {
    448 		sysmon_envsys_destroy(sc->sc_ac_sme);
    449 		return;
    450 	}
    451 
    452 	sc->sc_ac_sme->sme_name = "AC Adaptor";
    453 	sc->sc_ac_sme->sme_cookie = sc;
    454 	sc->sc_ac_sme->sme_refresh = bat_refresh_ac;
    455 	sc->sc_ac_sme->sme_class = SME_CLASS_ACADAPTER;
    456 
    457 	if (sysmon_envsys_register(sc->sc_ac_sme)) {
    458 		aprint_error("%s: unable to register AC with sysmon\n",
    459 		    device_xname(sc->sc_dev));
    460 		sysmon_envsys_destroy(sc->sc_ac_sme);
    461 	}
    462 
    463 #define INITDATA(index, unit, string)					\
    464 	do {								\
    465 		sc->sc_bat_sensor[index].state = ENVSYS_SVALID;		\
    466 		sc->sc_bat_sensor[index].units = unit;			\
    467 		(void)strlcpy(sc->sc_bat_sensor[index].desc, string,	\
    468 		    sizeof(sc->sc_bat_sensor[index].desc));			\
    469 	} while (/* CONSTCOND */ 0)
    470 
    471 	INITDATA(PBBAT_PRESENT, ENVSYS_INDICATOR, "present");
    472 	INITDATA(PBBAT_DCAPACITY, ENVSYS_SWATTHOUR, "design cap");
    473 	INITDATA(PBBAT_LFCCAPACITY, ENVSYS_SWATTHOUR, "last full cap");
    474 	INITDATA(PBBAT_DVOLTAGE, ENVSYS_SVOLTS_DC, "design voltage");
    475 	INITDATA(PBBAT_VOLTAGE, ENVSYS_SVOLTS_DC, "voltage");
    476 	INITDATA(PBBAT_CAPACITY, ENVSYS_SWATTHOUR, "charge");
    477 	INITDATA(PBBAT_CHARGERATE, ENVSYS_SWATTS, "charge rate");
    478 	INITDATA(PBBAT_DISCHARGERATE, ENVSYS_SWATTS, "discharge rate");
    479 	INITDATA(PBBAT_CHARGING, ENVSYS_BATTERY_CHARGE, "charging");
    480 	INITDATA(PBBAT_CHARGE_STATE, ENVSYS_BATTERY_CAPACITY, "charge state");
    481 
    482 #undef INITDATA
    483 
    484 	sc->sc_bat_sensor[PBBAT_CHARGE_STATE].value_cur =
    485 		ENVSYS_BATTERY_CAPACITY_NORMAL;
    486 
    487 	sc->sc_bat_sensor[PBBAT_CAPACITY].flags |=
    488 	    ENVSYS_FPERCENT | ENVSYS_FVALID_MAX | ENVSYS_FMONLIMITS;
    489 
    490 	sc->sc_bat_sensor[PBBAT_CHARGE_STATE].flags |= ENVSYS_FMONSTCHANGED;
    491 
    492 	/* Disable userland monitoring on these sensors. */
    493 	sc->sc_bat_sensor[PBBAT_VOLTAGE].flags = ENVSYS_FMONNOTSUPP;
    494 	sc->sc_bat_sensor[PBBAT_CHARGERATE].flags = ENVSYS_FMONNOTSUPP;
    495 	sc->sc_bat_sensor[PBBAT_DISCHARGERATE].flags = ENVSYS_FMONNOTSUPP;
    496 	sc->sc_bat_sensor[PBBAT_DCAPACITY].flags = ENVSYS_FMONNOTSUPP;
    497 	sc->sc_bat_sensor[PBBAT_LFCCAPACITY].flags = ENVSYS_FMONNOTSUPP;
    498 	sc->sc_bat_sensor[PBBAT_DVOLTAGE].flags = ENVSYS_FMONNOTSUPP;
    499 
    500 	sc->sc_bat_sensor[PBBAT_CHARGERATE].flags |= ENVSYS_FHAS_ENTROPY;
    501 	sc->sc_bat_sensor[PBBAT_DISCHARGERATE].flags |= ENVSYS_FHAS_ENTROPY;
    502 
    503 	sc->sc_bat_sme = sysmon_envsys_create();
    504 
    505 	for (i = 0; i < PBBAT_COUNT; i++) {
    506 		if (sysmon_envsys_sensor_attach(sc->sc_bat_sme,
    507 			&sc->sc_bat_sensor[i]))
    508 			goto fail;
    509 	}
    510 
    511 	sc->sc_bat_sme->sme_name = device_xname(dv);
    512 	sc->sc_bat_sme->sme_cookie = dv;
    513 	sc->sc_bat_sme->sme_refresh = bat_refresh;
    514 	sc->sc_bat_sme->sme_class = SME_CLASS_BATTERY;
    515 	sc->sc_bat_sme->sme_flags = SME_POLL_ONLY;
    516 	sc->sc_bat_sme->sme_get_limits = bat_get_limits;
    517 
    518 	if (sysmon_envsys_register(sc->sc_bat_sme))
    519 		goto fail;
    520 
    521 	bat_get_pm_limits(dv);
    522 	bat_update_status(dv);
    523 
    524 	return;
    525 fail:
    526 	aprint_error("failed to initialize sysmon\n");
    527 
    528 	sysmon_envsys_destroy(sc->sc_bat_sme);
    529 	kmem_free(sc->sc_bat_sensor, PBBAT_COUNT * sizeof(*sc->sc_bat_sensor));
    530 
    531 	sc->sc_bat_sme = NULL;
    532 	sc->sc_bat_sensor = NULL;
    533 }
    534 
    535 static uint16_t
    536 bat_get_status(device_t dv)
    537 {
    538 	struct pbatt_softc *sc = device_private(dv);
    539 	uint16_t val;
    540 
    541 	val = bat_get_voltage();
    542 
    543 	sc->sc_bat_sensor[PBBAT_PRESENT].state = ENVSYS_SVALID;
    544 	sc->sc_bat_sensor[PBBAT_PRESENT].value_cur = 1;
    545 
    546 	if (val > VOLTS_NOBATT) {
    547 		sc->sc_bat_sensor[PBBAT_PRESENT].value_cur = 0;
    548 		sc->sc_bat_sensor[PBBAT_CHARGING].state = ENVSYS_SVALID;
    549 		sc->sc_bat_sensor[PBBAT_CHARGING].value_cur = 0;
    550 		sc->sc_bat_sensor[PBBAT_CHARGERATE].state = ENVSYS_SINVALID;
    551 		sc->sc_bat_sensor[PBBAT_DISCHARGERATE].state = ENVSYS_SINVALID;
    552 		if (sc->sc_ac_state != PBBAT_AC_CONNECTED) {
    553 			sysmon_pswitch_event(&sc->sc_sm_acpower,
    554 			    PSWITCH_EVENT_PRESSED);
    555 		}
    556 		sc->sc_ac_state = PBBAT_AC_CONNECTED;
    557 	} else if (val > VOLTS_CHARGING) {
    558 		sc->sc_bat_sensor[PBBAT_CHARGING].state = ENVSYS_SVALID;
    559 		if (sc->sc_chargerate)
    560 			sc->sc_bat_sensor[PBBAT_CHARGING].value_cur = 1;
    561 		else
    562 			sc->sc_bat_sensor[PBBAT_CHARGING].value_cur = 0;
    563 		sc->sc_bat_sensor[PBBAT_CHARGERATE].state = ENVSYS_SVALID;
    564 		sc->sc_bat_sensor[PBBAT_CHARGERATE].value_cur =
    565 		    sc->sc_chargerate;
    566 		sc->sc_bat_sensor[PBBAT_DISCHARGERATE].state = ENVSYS_SINVALID;
    567 		if (sc->sc_ac_state != PBBAT_AC_CONNECTED) {
    568 			sysmon_pswitch_event(&sc->sc_sm_acpower,
    569 			    PSWITCH_EVENT_PRESSED);
    570 		}
    571 		sc->sc_ac_state = PBBAT_AC_CONNECTED;
    572 	} else {
    573 		sc->sc_bat_sensor[PBBAT_CHARGING].value_cur = 0;
    574 		sc->sc_bat_sensor[PBBAT_CHARGING].state = ENVSYS_SVALID;
    575 		sc->sc_bat_sensor[PBBAT_CHARGERATE].state = ENVSYS_SINVALID;
    576 		sc->sc_bat_sensor[PBBAT_DISCHARGERATE].state = ENVSYS_SVALID;
    577 		sc->sc_bat_sensor[PBBAT_DISCHARGERATE].value_cur =
    578 		    sc->sc_disrate;
    579 		if (sc->sc_ac_state != PBBAT_AC_DISCONNECTED) {
    580 			sysmon_pswitch_event(&sc->sc_sm_acpower,
    581 			    PSWITCH_EVENT_RELEASED);
    582 		}
    583 		sc->sc_ac_state = PBBAT_AC_DISCONNECTED;
    584 	}
    585 
    586 	/* Remaining capacity. */
    587 	sc->sc_chargerate = sc->sc_bat_sensor[PBBAT_CAPACITY].value_cur;
    588 	sc->sc_disrate = sc->sc_bat_sensor[PBBAT_CAPACITY].value_cur;
    589 
    590 	sc->sc_bat_sensor[PBBAT_CAPACITY].value_cur =
    591 	    (val - sc->sc_empty) * 10 * LIMIT_SCALE;
    592 
    593 	sc->sc_chargerate =
    594 	    (sc->sc_bat_sensor[PBBAT_CAPACITY].value_cur - sc->sc_chargerate) * 10;
    595 	sc->sc_disrate =
    596 	    (sc->sc_disrate - sc->sc_bat_sensor[PBBAT_CAPACITY].value_cur) * 10;
    597 
    598 	/* Battery voltage. */
    599 	sc->sc_bat_sensor[PBBAT_VOLTAGE].value_cur = val * 1000;
    600 	sc->sc_bat_sensor[PBBAT_VOLTAGE].state =
    601 	    (val >= VOLTS_NOBATT ? ENVSYS_SINVALID : ENVSYS_SVALID);
    602 
    603 	if (val < sc->sc_lcapacity) {
    604 		sc->sc_bat_sensor[PBBAT_CAPACITY].state = ENVSYS_SCRITUNDER;
    605 		sc->sc_bat_sensor[PBBAT_CHARGE_STATE].value_cur =
    606 		    ENVSYS_BATTERY_CAPACITY_CRITICAL;
    607 	} else if (val < sc->sc_wcapacity) {
    608 		sc->sc_bat_sensor[PBBAT_CAPACITY].state = ENVSYS_SWARNUNDER;
    609 		sc->sc_bat_sensor[PBBAT_CHARGE_STATE].value_cur =
    610 		    ENVSYS_BATTERY_CAPACITY_WARNING;
    611 	} else {
    612 		sc->sc_bat_sensor[PBBAT_CHARGE_STATE].value_cur =
    613 		    ENVSYS_BATTERY_CAPACITY_NORMAL;
    614 	}
    615 
    616 	sc->sc_bat_sensor[PBBAT_CHARGE_STATE].state = ENVSYS_SVALID;
    617 
    618 	return val;
    619 }
    620