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