xref: /dflybsd-src/sys/dev/acpica/acpi_smbat.c (revision 944cd60c7b4392d637be82be7baafe9ac12a3061)
15db2f26eSSascha Wildner /*-
25db2f26eSSascha Wildner  * Copyright (c) 2005 Hans Petter Selasky
35db2f26eSSascha Wildner  * All rights reserved.
45db2f26eSSascha Wildner  *
55db2f26eSSascha Wildner  * Redistribution and use in source and binary forms, with or without
65db2f26eSSascha Wildner  * modification, are permitted provided that the following conditions
75db2f26eSSascha Wildner  * are met:
85db2f26eSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
95db2f26eSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
105db2f26eSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
115db2f26eSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
125db2f26eSSascha Wildner  *    documentation and/or other materials provided with the distribution.
135db2f26eSSascha Wildner  *
145db2f26eSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
155db2f26eSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
165db2f26eSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
175db2f26eSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
185db2f26eSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
195db2f26eSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
205db2f26eSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
215db2f26eSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
225db2f26eSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
235db2f26eSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
245db2f26eSSascha Wildner  * SUCH DAMAGE.
255db2f26eSSascha Wildner  *
265db2f26eSSascha Wildner  * $FreeBSD: src/sys/dev/acpica/acpi_smbat.c,v 1.11 2011/11/07 15:43:11 ed Exp $
275db2f26eSSascha Wildner  */
285db2f26eSSascha Wildner 
295db2f26eSSascha Wildner #include "opt_acpi.h"
305db2f26eSSascha Wildner #include <sys/param.h>
315db2f26eSSascha Wildner #include <sys/kernel.h>
325db2f26eSSascha Wildner #include <sys/module.h>
335db2f26eSSascha Wildner #include <sys/bus.h>
345db2f26eSSascha Wildner 
355db2f26eSSascha Wildner #include "acpi.h"
365db2f26eSSascha Wildner #include <acpivar.h>
375db2f26eSSascha Wildner #include <acpiio.h>
385db2f26eSSascha Wildner #include <acpi_smbus.h>
395db2f26eSSascha Wildner 
405db2f26eSSascha Wildner /* Transactions have failed after 500 ms. */
415db2f26eSSascha Wildner #define SMBUS_TIMEOUT	50
425db2f26eSSascha Wildner 
435db2f26eSSascha Wildner struct acpi_smbat_softc {
445db2f26eSSascha Wildner 	uint8_t		sb_base_addr;
455db2f26eSSascha Wildner 	device_t	ec_dev;
465db2f26eSSascha Wildner 
475db2f26eSSascha Wildner 	struct acpi_bif	bif;
485db2f26eSSascha Wildner 	struct acpi_bst	bst;
495db2f26eSSascha Wildner 	struct timespec	bif_lastupdated;
505db2f26eSSascha Wildner 	struct timespec	bst_lastupdated;
515db2f26eSSascha Wildner };
525db2f26eSSascha Wildner 
535db2f26eSSascha Wildner static int	acpi_smbat_probe(device_t dev);
545db2f26eSSascha Wildner static int	acpi_smbat_attach(device_t dev);
555db2f26eSSascha Wildner static int	acpi_smbat_shutdown(device_t dev);
565db2f26eSSascha Wildner static int	acpi_smbat_info_expired(struct timespec *lastupdated);
575db2f26eSSascha Wildner static void	acpi_smbat_info_updated(struct timespec *lastupdated);
585db2f26eSSascha Wildner static int	acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif);
595db2f26eSSascha Wildner static int	acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst);
605db2f26eSSascha Wildner 
615db2f26eSSascha Wildner ACPI_SERIAL_DECL(smbat, "ACPI Smart Battery");
625db2f26eSSascha Wildner 
635db2f26eSSascha Wildner static SYSCTL_NODE(_debug_acpi, OID_AUTO, batt, CTLFLAG_RD, NULL,
645db2f26eSSascha Wildner     "Battery debugging");
655db2f26eSSascha Wildner 
665db2f26eSSascha Wildner /* On some laptops with smart batteries, enabling battery monitoring
675db2f26eSSascha Wildner  * software causes keystrokes from atkbd to be lost.  This has also been
685db2f26eSSascha Wildner  * reported on Linux, and is apparently due to the keyboard and I2C line
695db2f26eSSascha Wildner  * for the battery being routed through the same chip.  Whether that's
705db2f26eSSascha Wildner  * accurate or not, adding extra sleeps to the status checking code
715db2f26eSSascha Wildner  * causes the problem to go away.
725db2f26eSSascha Wildner  *
735db2f26eSSascha Wildner  * If you experience that problem, try a value of 10ms and move up
745db2f26eSSascha Wildner  * from there.
755db2f26eSSascha Wildner  */
765db2f26eSSascha Wildner static int      batt_sleep_ms;
775db2f26eSSascha Wildner SYSCTL_INT(_debug_acpi_batt, OID_AUTO, batt_sleep_ms, CTLFLAG_RW, &batt_sleep_ms, 0,
785db2f26eSSascha Wildner     "Sleep during battery status updates to prevent keystroke loss.");
795db2f26eSSascha Wildner 
805db2f26eSSascha Wildner static device_method_t acpi_smbat_methods[] = {
815db2f26eSSascha Wildner 	/* device interface */
825db2f26eSSascha Wildner 	DEVMETHOD(device_probe, acpi_smbat_probe),
835db2f26eSSascha Wildner 	DEVMETHOD(device_attach, acpi_smbat_attach),
845db2f26eSSascha Wildner 	DEVMETHOD(device_shutdown, acpi_smbat_shutdown),
855db2f26eSSascha Wildner 
865db2f26eSSascha Wildner 	/* ACPI battery interface */
875db2f26eSSascha Wildner 	DEVMETHOD(acpi_batt_get_status, acpi_smbat_get_bst),
885db2f26eSSascha Wildner 	DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bif),
895db2f26eSSascha Wildner 
90d3c9c58eSSascha Wildner 	DEVMETHOD_END
915db2f26eSSascha Wildner };
925db2f26eSSascha Wildner 
935db2f26eSSascha Wildner static driver_t	acpi_smbat_driver = {
945db2f26eSSascha Wildner 	"battery",
955db2f26eSSascha Wildner 	acpi_smbat_methods,
965db2f26eSSascha Wildner 	sizeof(struct acpi_smbat_softc),
975025fc65SMatthew Dillon 	.gpri = KOBJ_GPRI_ACPI
985db2f26eSSascha Wildner };
995db2f26eSSascha Wildner 
1005db2f26eSSascha Wildner static devclass_t acpi_smbat_devclass;
101dfc199f7SSascha Wildner DRIVER_MODULE(acpi_smbat, acpi, acpi_smbat_driver, acpi_smbat_devclass,
102dfc199f7SSascha Wildner     NULL, NULL);
1035db2f26eSSascha Wildner MODULE_DEPEND(acpi_smbat, acpi, 1, 1, 1);
1045db2f26eSSascha Wildner 
1055db2f26eSSascha Wildner static int
acpi_smbat_probe(device_t dev)1065db2f26eSSascha Wildner acpi_smbat_probe(device_t dev)
1075db2f26eSSascha Wildner {
1085db2f26eSSascha Wildner 	static char *smbat_ids[] = {"ACPI0001", "ACPI0005", NULL};
1095db2f26eSSascha Wildner 	ACPI_STATUS status;
1105db2f26eSSascha Wildner 
1115db2f26eSSascha Wildner 	if (acpi_disabled("smbat") ||
1125db2f26eSSascha Wildner 	    ACPI_ID_PROBE(device_get_parent(dev), dev, smbat_ids) == NULL)
1135db2f26eSSascha Wildner 		return (ENXIO);
1145db2f26eSSascha Wildner 	status = AcpiEvaluateObject(acpi_get_handle(dev), "_EC", NULL, NULL);
1155db2f26eSSascha Wildner 	if (ACPI_FAILURE(status))
1165db2f26eSSascha Wildner 		return (ENXIO);
1175db2f26eSSascha Wildner 
1185db2f26eSSascha Wildner 	device_set_desc(dev, "ACPI Smart Battery");
1195db2f26eSSascha Wildner 	return (0);
1205db2f26eSSascha Wildner }
1215db2f26eSSascha Wildner 
1225db2f26eSSascha Wildner static int
acpi_smbat_attach(device_t dev)1235db2f26eSSascha Wildner acpi_smbat_attach(device_t dev)
1245db2f26eSSascha Wildner {
1255db2f26eSSascha Wildner 	struct acpi_smbat_softc *sc;
1265db2f26eSSascha Wildner 	uint32_t base;
1275db2f26eSSascha Wildner 
1285db2f26eSSascha Wildner 	sc = device_get_softc(dev);
1290742f40dSImre Vadasz 	ACPI_SERIAL_INIT(smbat);
1305db2f26eSSascha Wildner 	if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) {
1315db2f26eSSascha Wildner 		device_printf(dev, "cannot get EC base address\n");
1325db2f26eSSascha Wildner 		return (ENXIO);
1335db2f26eSSascha Wildner 	}
1345db2f26eSSascha Wildner 	sc->sb_base_addr = (base >> 8) & 0xff;
1355db2f26eSSascha Wildner 
1365db2f26eSSascha Wildner 	/* XXX Only works with one EC, but nearly all systems only have one. */
1375db2f26eSSascha Wildner 	sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0);
1385db2f26eSSascha Wildner 	if (sc->ec_dev == NULL) {
1395db2f26eSSascha Wildner 		device_printf(dev, "cannot find EC device\n");
1405db2f26eSSascha Wildner 		return (ENXIO);
1415db2f26eSSascha Wildner 	}
1425db2f26eSSascha Wildner 
1435db2f26eSSascha Wildner 	timespecclear(&sc->bif_lastupdated);
1445db2f26eSSascha Wildner 	timespecclear(&sc->bst_lastupdated);
1455db2f26eSSascha Wildner 
1465db2f26eSSascha Wildner 	if (acpi_battery_register(dev) != 0) {
1475db2f26eSSascha Wildner 		device_printf(dev, "cannot register battery\n");
1485db2f26eSSascha Wildner 		return (ENXIO);
1495db2f26eSSascha Wildner 	}
1505db2f26eSSascha Wildner 	return (0);
1515db2f26eSSascha Wildner }
1525db2f26eSSascha Wildner 
1535db2f26eSSascha Wildner static int
acpi_smbat_shutdown(device_t dev)1545db2f26eSSascha Wildner acpi_smbat_shutdown(device_t dev)
1555db2f26eSSascha Wildner {
1565db2f26eSSascha Wildner 
1575db2f26eSSascha Wildner 	acpi_battery_remove(dev);
1585db2f26eSSascha Wildner 	return (0);
1595db2f26eSSascha Wildner }
1605db2f26eSSascha Wildner 
1615db2f26eSSascha Wildner static int
acpi_smbat_info_expired(struct timespec * lastupdated)1625db2f26eSSascha Wildner acpi_smbat_info_expired(struct timespec *lastupdated)
1635db2f26eSSascha Wildner {
1645db2f26eSSascha Wildner 	struct timespec	curtime;
1655db2f26eSSascha Wildner 
1665db2f26eSSascha Wildner 	ACPI_SERIAL_ASSERT(smbat);
1675db2f26eSSascha Wildner 
1685db2f26eSSascha Wildner 	if (lastupdated == NULL)
1695db2f26eSSascha Wildner 		return (TRUE);
1705db2f26eSSascha Wildner 	if (!timespecisset(lastupdated))
1715db2f26eSSascha Wildner 		return (TRUE);
1725db2f26eSSascha Wildner 
1735db2f26eSSascha Wildner 	getnanotime(&curtime);
174*944cd60cSSascha Wildner 	timespecsub(&curtime, lastupdated, &curtime);
1755db2f26eSSascha Wildner 	return (curtime.tv_sec < 0 ||
1765db2f26eSSascha Wildner 	    curtime.tv_sec > acpi_battery_get_info_expire());
1775db2f26eSSascha Wildner }
1785db2f26eSSascha Wildner 
1795db2f26eSSascha Wildner static void
acpi_smbat_info_updated(struct timespec * lastupdated)1805db2f26eSSascha Wildner acpi_smbat_info_updated(struct timespec *lastupdated)
1815db2f26eSSascha Wildner {
1825db2f26eSSascha Wildner 
1835db2f26eSSascha Wildner 	ACPI_SERIAL_ASSERT(smbat);
1845db2f26eSSascha Wildner 
1855db2f26eSSascha Wildner 	if (lastupdated != NULL)
1865db2f26eSSascha Wildner 		getnanotime(lastupdated);
1875db2f26eSSascha Wildner }
1885db2f26eSSascha Wildner 
1895db2f26eSSascha Wildner static int
acpi_smbus_read_2(struct acpi_smbat_softc * sc,uint8_t addr,uint8_t cmd,uint16_t * ptr)1905db2f26eSSascha Wildner acpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd,
1915db2f26eSSascha Wildner     uint16_t *ptr)
1925db2f26eSSascha Wildner {
1935db2f26eSSascha Wildner 	int error, to;
1945db2f26eSSascha Wildner 	UINT64 val;
1955db2f26eSSascha Wildner 
1965db2f26eSSascha Wildner 	ACPI_SERIAL_ASSERT(smbat);
1975db2f26eSSascha Wildner 
1985db2f26eSSascha Wildner 	if (batt_sleep_ms)
1995db2f26eSSascha Wildner 	    AcpiOsSleep(batt_sleep_ms);
2005db2f26eSSascha Wildner 
2015db2f26eSSascha Wildner 	val = addr;
2025db2f26eSSascha Wildner 	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR,
2035db2f26eSSascha Wildner 	    val, 1);
2045db2f26eSSascha Wildner 	if (error)
2055db2f26eSSascha Wildner 		goto out;
2065db2f26eSSascha Wildner 
2075db2f26eSSascha Wildner 	val = cmd;
2085db2f26eSSascha Wildner 	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD,
2095db2f26eSSascha Wildner 	    val, 1);
2105db2f26eSSascha Wildner 	if (error)
2115db2f26eSSascha Wildner 		goto out;
2125db2f26eSSascha Wildner 
2135db2f26eSSascha Wildner 	val = 0x09; /* | 0x80 if PEC */
2145db2f26eSSascha Wildner 	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
2155db2f26eSSascha Wildner 	    val, 1);
2165db2f26eSSascha Wildner 	if (error)
2175db2f26eSSascha Wildner 		goto out;
2185db2f26eSSascha Wildner 
2195db2f26eSSascha Wildner 	if (batt_sleep_ms)
2205db2f26eSSascha Wildner 	    AcpiOsSleep(batt_sleep_ms);
2215db2f26eSSascha Wildner 
2225db2f26eSSascha Wildner 	for (to = SMBUS_TIMEOUT; to != 0; to--) {
2235db2f26eSSascha Wildner 		error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
2245db2f26eSSascha Wildner 		    &val, 1);
2255db2f26eSSascha Wildner 		if (error)
2265db2f26eSSascha Wildner 			goto out;
2275db2f26eSSascha Wildner 		if (val == 0)
2285db2f26eSSascha Wildner 			break;
2295db2f26eSSascha Wildner 		AcpiOsSleep(10);
2305db2f26eSSascha Wildner 	}
2315db2f26eSSascha Wildner 	if (to == 0) {
2325db2f26eSSascha Wildner 		error = ETIMEDOUT;
2335db2f26eSSascha Wildner 		goto out;
2345db2f26eSSascha Wildner 	}
2355db2f26eSSascha Wildner 
2365db2f26eSSascha Wildner 	error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1);
2375db2f26eSSascha Wildner 	if (error)
2385db2f26eSSascha Wildner 		goto out;
2395db2f26eSSascha Wildner 	if (val & SMBUS_STS_MASK) {
2405db2f26eSSascha Wildner 		kprintf("%s: AE_ERROR 0x%x\n",
2417fd4e1a1SSascha Wildner 		       __func__, (int)(val & SMBUS_STS_MASK));
2425db2f26eSSascha Wildner 		error = EIO;
2435db2f26eSSascha Wildner 		goto out;
2445db2f26eSSascha Wildner 	}
2455db2f26eSSascha Wildner 
2465db2f26eSSascha Wildner 	error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA,
2475db2f26eSSascha Wildner 	    &val, 2);
2485db2f26eSSascha Wildner 	if (error)
2495db2f26eSSascha Wildner 		goto out;
2505db2f26eSSascha Wildner 
2515db2f26eSSascha Wildner 	*ptr = val;
2525db2f26eSSascha Wildner 
2535db2f26eSSascha Wildner out:
2545db2f26eSSascha Wildner 	return (error);
2555db2f26eSSascha Wildner }
2565db2f26eSSascha Wildner 
2575db2f26eSSascha Wildner static int
acpi_smbus_read_multi_1(struct acpi_smbat_softc * sc,uint8_t addr,uint8_t cmd,uint8_t * ptr,uint16_t len)2585db2f26eSSascha Wildner acpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd,
2595db2f26eSSascha Wildner     uint8_t *ptr, uint16_t len)
2605db2f26eSSascha Wildner {
2615db2f26eSSascha Wildner 	UINT64 val;
2625db2f26eSSascha Wildner 	uint8_t	to;
2635db2f26eSSascha Wildner 	int error;
2645db2f26eSSascha Wildner 
2655db2f26eSSascha Wildner 	ACPI_SERIAL_ASSERT(smbat);
2665db2f26eSSascha Wildner 
2675db2f26eSSascha Wildner 	if (batt_sleep_ms)
2685db2f26eSSascha Wildner 	    AcpiOsSleep(batt_sleep_ms);
2695db2f26eSSascha Wildner 
2705db2f26eSSascha Wildner 	val = addr;
2715db2f26eSSascha Wildner 	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR,
2725db2f26eSSascha Wildner 	    val, 1);
2735db2f26eSSascha Wildner 	if (error)
2745db2f26eSSascha Wildner 		goto out;
2755db2f26eSSascha Wildner 
2765db2f26eSSascha Wildner 	val = cmd;
2775db2f26eSSascha Wildner 	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD,
2785db2f26eSSascha Wildner 	    val, 1);
2795db2f26eSSascha Wildner 	if (error)
2805db2f26eSSascha Wildner 		goto out;
2815db2f26eSSascha Wildner 
2825db2f26eSSascha Wildner 	val = 0x0B /* | 0x80 if PEC */ ;
2835db2f26eSSascha Wildner 	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
2845db2f26eSSascha Wildner 	    val, 1);
2855db2f26eSSascha Wildner 	if (error)
2865db2f26eSSascha Wildner 		goto out;
2875db2f26eSSascha Wildner 
2885db2f26eSSascha Wildner 	if (batt_sleep_ms)
2895db2f26eSSascha Wildner 	    AcpiOsSleep(batt_sleep_ms);
2905db2f26eSSascha Wildner 
2915db2f26eSSascha Wildner 	for (to = SMBUS_TIMEOUT; to != 0; to--) {
2925db2f26eSSascha Wildner 		error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
2935db2f26eSSascha Wildner 		    &val, 1);
2945db2f26eSSascha Wildner 		if (error)
2955db2f26eSSascha Wildner 			goto out;
2965db2f26eSSascha Wildner 		if (val == 0)
2975db2f26eSSascha Wildner 			break;
2985db2f26eSSascha Wildner 		AcpiOsSleep(10);
2995db2f26eSSascha Wildner 	}
3005db2f26eSSascha Wildner 	if (to == 0) {
3015db2f26eSSascha Wildner 		error = ETIMEDOUT;
3025db2f26eSSascha Wildner 		goto out;
3035db2f26eSSascha Wildner 	}
3045db2f26eSSascha Wildner 
3055db2f26eSSascha Wildner 	error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1);
3065db2f26eSSascha Wildner 	if (error)
3075db2f26eSSascha Wildner 		goto out;
3085db2f26eSSascha Wildner 	if (val & SMBUS_STS_MASK) {
3095db2f26eSSascha Wildner 		kprintf("%s: AE_ERROR 0x%x\n",
3107fd4e1a1SSascha Wildner 		       __func__, (int)(val & SMBUS_STS_MASK));
3115db2f26eSSascha Wildner 		error = EIO;
3125db2f26eSSascha Wildner 		goto out;
3135db2f26eSSascha Wildner 	}
3145db2f26eSSascha Wildner 
3155db2f26eSSascha Wildner 	/* get length */
3165db2f26eSSascha Wildner 	error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT,
3175db2f26eSSascha Wildner 	    &val, 1);
3185db2f26eSSascha Wildner 	if (error)
3195db2f26eSSascha Wildner 		goto out;
3205db2f26eSSascha Wildner 	val = (val & 0x1f) + 1;
3215db2f26eSSascha Wildner 
3225db2f26eSSascha Wildner 	bzero(ptr, len);
3235db2f26eSSascha Wildner 	if (len > val)
3245db2f26eSSascha Wildner 		len = val;
3255db2f26eSSascha Wildner 
3265db2f26eSSascha Wildner 	if (batt_sleep_ms)
3275db2f26eSSascha Wildner 	    AcpiOsSleep(batt_sleep_ms);
3285db2f26eSSascha Wildner 
3295db2f26eSSascha Wildner 	while (len--) {
3305db2f26eSSascha Wildner 		error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA
3315db2f26eSSascha Wildner 		    + len, &val, 1);
3325db2f26eSSascha Wildner 		if (error)
3335db2f26eSSascha Wildner 			goto out;
3345db2f26eSSascha Wildner 
3355db2f26eSSascha Wildner 		ptr[len] = val;
3365db2f26eSSascha Wildner 		if (batt_sleep_ms)
3375db2f26eSSascha Wildner 		    AcpiOsSleep(batt_sleep_ms);
3385db2f26eSSascha Wildner 	}
3395db2f26eSSascha Wildner 
3405db2f26eSSascha Wildner out:
3415db2f26eSSascha Wildner 	return (error);
3425db2f26eSSascha Wildner }
3435db2f26eSSascha Wildner 
3445db2f26eSSascha Wildner static int
acpi_smbat_get_bst(device_t dev,struct acpi_bst * bst)3455db2f26eSSascha Wildner acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst)
3465db2f26eSSascha Wildner {
3475db2f26eSSascha Wildner 	struct acpi_smbat_softc *sc;
3485db2f26eSSascha Wildner 	int error;
34973aefc74SSascha Wildner 	uint32_t factor;
3505db2f26eSSascha Wildner 	int16_t val;
3515db2f26eSSascha Wildner 	uint8_t	addr;
3525db2f26eSSascha Wildner 
3535db2f26eSSascha Wildner 	ACPI_SERIAL_BEGIN(smbat);
3545db2f26eSSascha Wildner 
3555db2f26eSSascha Wildner 	addr = SMBATT_ADDRESS;
3565db2f26eSSascha Wildner 	error = ENXIO;
3575db2f26eSSascha Wildner 	sc = device_get_softc(dev);
3585db2f26eSSascha Wildner 
3595db2f26eSSascha Wildner 	if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) {
3605db2f26eSSascha Wildner 		error = 0;
3615db2f26eSSascha Wildner 		goto out;
3625db2f26eSSascha Wildner 	}
3635db2f26eSSascha Wildner 
3645db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
3655db2f26eSSascha Wildner 		goto out;
36673aefc74SSascha Wildner 	if (val & SMBATT_BM_CAPACITY_MODE)
3675db2f26eSSascha Wildner 		factor = 10;
36873aefc74SSascha Wildner 	else
3695db2f26eSSascha Wildner 		factor = 1;
3705db2f26eSSascha Wildner 
3715db2f26eSSascha Wildner 	/* get battery status */
3725db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val))
3735db2f26eSSascha Wildner 		goto out;
3745db2f26eSSascha Wildner 
3755db2f26eSSascha Wildner 	sc->bst.state = 0;
3765db2f26eSSascha Wildner 	if (val & SMBATT_BS_DISCHARGING)
3775db2f26eSSascha Wildner 		sc->bst.state |= ACPI_BATT_STAT_DISCHARG;
3785db2f26eSSascha Wildner 
3795db2f26eSSascha Wildner 	if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM)
3805db2f26eSSascha Wildner 		sc->bst.state |= ACPI_BATT_STAT_CRITICAL;
3815db2f26eSSascha Wildner 
3825db2f26eSSascha Wildner 	/*
3835db2f26eSSascha Wildner 	 * If the rate is negative, it is discharging.  Otherwise,
3845db2f26eSSascha Wildner 	 * it is charging.
3855db2f26eSSascha Wildner 	 */
3865db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val))
3875db2f26eSSascha Wildner 		goto out;
3885db2f26eSSascha Wildner 
3895db2f26eSSascha Wildner 	if (val > 0) {
3905db2f26eSSascha Wildner 		sc->bst.rate = val * factor;
3915db2f26eSSascha Wildner 		sc->bst.state &= ~SMBATT_BS_DISCHARGING;
3925db2f26eSSascha Wildner 		sc->bst.state |= ACPI_BATT_STAT_CHARGING;
3935db2f26eSSascha Wildner 	} else if (val < 0)
3945db2f26eSSascha Wildner 		sc->bst.rate = (-val) * factor;
3955db2f26eSSascha Wildner 	else
3965db2f26eSSascha Wildner 		sc->bst.rate = 0;
3975db2f26eSSascha Wildner 
3985db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val))
3995db2f26eSSascha Wildner 		goto out;
4005db2f26eSSascha Wildner 	sc->bst.cap = val * factor;
4015db2f26eSSascha Wildner 
4025db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val))
4035db2f26eSSascha Wildner 		goto out;
4045db2f26eSSascha Wildner 	sc->bst.volt = val;
4055db2f26eSSascha Wildner 
4065db2f26eSSascha Wildner 	acpi_smbat_info_updated(&sc->bst_lastupdated);
4075db2f26eSSascha Wildner 	error = 0;
4085db2f26eSSascha Wildner 
4095db2f26eSSascha Wildner out:
4105db2f26eSSascha Wildner 	if (error == 0)
4115db2f26eSSascha Wildner 		memcpy(bst, &sc->bst, sizeof(sc->bst));
4125db2f26eSSascha Wildner 	ACPI_SERIAL_END(smbat);
4135db2f26eSSascha Wildner 	return (error);
4145db2f26eSSascha Wildner }
4155db2f26eSSascha Wildner 
4165db2f26eSSascha Wildner static int
acpi_smbat_get_bif(device_t dev,struct acpi_bif * bif)4175db2f26eSSascha Wildner acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif)
4185db2f26eSSascha Wildner {
4195db2f26eSSascha Wildner 	struct acpi_smbat_softc *sc;
4205db2f26eSSascha Wildner 	int error;
4215db2f26eSSascha Wildner 	uint32_t factor;
4225db2f26eSSascha Wildner 	uint16_t val;
4235db2f26eSSascha Wildner 	uint8_t addr;
4245db2f26eSSascha Wildner 
4255db2f26eSSascha Wildner 	ACPI_SERIAL_BEGIN(smbat);
4265db2f26eSSascha Wildner 
4275db2f26eSSascha Wildner 	addr = SMBATT_ADDRESS;
4285db2f26eSSascha Wildner 	error = ENXIO;
4295db2f26eSSascha Wildner 	sc = device_get_softc(dev);
4305db2f26eSSascha Wildner 
4315db2f26eSSascha Wildner 	if (!acpi_smbat_info_expired(&sc->bif_lastupdated)) {
4325db2f26eSSascha Wildner 		error = 0;
4335db2f26eSSascha Wildner 		goto out;
4345db2f26eSSascha Wildner 	}
4355db2f26eSSascha Wildner 
4365db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
4375db2f26eSSascha Wildner 		goto out;
4385db2f26eSSascha Wildner 	if (val & SMBATT_BM_CAPACITY_MODE) {
4395db2f26eSSascha Wildner 		factor = 10;
4405db2f26eSSascha Wildner 		sc->bif.units = ACPI_BIF_UNITS_MW;
4415db2f26eSSascha Wildner 	} else {
4425db2f26eSSascha Wildner 		factor = 1;
4435db2f26eSSascha Wildner 		sc->bif.units = ACPI_BIF_UNITS_MA;
4445db2f26eSSascha Wildner 	}
4455db2f26eSSascha Wildner 
4465db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val))
4475db2f26eSSascha Wildner 		goto out;
4485db2f26eSSascha Wildner 	sc->bif.dcap = val * factor;
4495db2f26eSSascha Wildner 
4505db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val))
4515db2f26eSSascha Wildner 		goto out;
4525db2f26eSSascha Wildner 	sc->bif.lfcap = val * factor;
4535db2f26eSSascha Wildner 	sc->bif.btech = 1;		/* secondary (rechargeable) */
4545db2f26eSSascha Wildner 
4555db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val))
4565db2f26eSSascha Wildner 		goto out;
4575db2f26eSSascha Wildner 	sc->bif.dvol = val;
4585db2f26eSSascha Wildner 
4595db2f26eSSascha Wildner 	sc->bif.wcap = sc->bif.dcap / 10;
4605db2f26eSSascha Wildner 	sc->bif.lcap = sc->bif.dcap / 10;
4615db2f26eSSascha Wildner 
4625db2f26eSSascha Wildner 	sc->bif.gra1 = factor;	/* not supported */
4635db2f26eSSascha Wildner 	sc->bif.gra2 = factor;	/* not supported */
4645db2f26eSSascha Wildner 
4655db2f26eSSascha Wildner 	if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME,
4665db2f26eSSascha Wildner 	    sc->bif.model, sizeof(sc->bif.model)))
4675db2f26eSSascha Wildner 		goto out;
4685db2f26eSSascha Wildner 
4695db2f26eSSascha Wildner 	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val))
4705db2f26eSSascha Wildner 		goto out;
4715db2f26eSSascha Wildner 	ksnprintf(sc->bif.serial, sizeof(sc->bif.serial), "0x%04x", val);
4725db2f26eSSascha Wildner 
4735db2f26eSSascha Wildner 	if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY,
4745db2f26eSSascha Wildner 	    sc->bif.type, sizeof(sc->bif.type)))
4755db2f26eSSascha Wildner 		goto out;
4765db2f26eSSascha Wildner 
4775db2f26eSSascha Wildner 	if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA,
4785db2f26eSSascha Wildner 	    sc->bif.oeminfo, sizeof(sc->bif.oeminfo)))
4795db2f26eSSascha Wildner 		goto out;
4805db2f26eSSascha Wildner 
4815db2f26eSSascha Wildner 	/* XXX check if device was replugged during read? */
4825db2f26eSSascha Wildner 
4835db2f26eSSascha Wildner 	acpi_smbat_info_updated(&sc->bif_lastupdated);
4845db2f26eSSascha Wildner 	error = 0;
4855db2f26eSSascha Wildner 
4865db2f26eSSascha Wildner out:
4875db2f26eSSascha Wildner 	if (error == 0)
4885db2f26eSSascha Wildner 		memcpy(bif, &sc->bif, sizeof(sc->bif));
4895db2f26eSSascha Wildner 	ACPI_SERIAL_END(smbat);
4905db2f26eSSascha Wildner 	return (error);
4915db2f26eSSascha Wildner }
492