1151564Snjl/*- 2151564Snjl * Copyright (c) 2005 Hans Petter Selasky 3151564Snjl * All rights reserved. 4151564Snjl * 5151564Snjl * Redistribution and use in source and binary forms, with or without 6151564Snjl * modification, are permitted provided that the following conditions 7151564Snjl * are met: 8151564Snjl * 1. Redistributions of source code must retain the above copyright 9151564Snjl * notice, this list of conditions and the following disclaimer. 10151564Snjl * 2. Redistributions in binary form must reproduce the above copyright 11151564Snjl * notice, this list of conditions and the following disclaimer in the 12151564Snjl * documentation and/or other materials provided with the distribution. 13151564Snjl * 14151564Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15151564Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16151564Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17151564Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18151564Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19151564Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20151564Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21151564Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22151564Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23151564Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24151564Snjl * SUCH DAMAGE. 25151564Snjl */ 26151564Snjl 27151564Snjl#include <sys/cdefs.h> 28151564Snjl__FBSDID("$FreeBSD$"); 29151564Snjl 30151564Snjl#include "opt_acpi.h" 31151564Snjl#include <sys/param.h> 32151564Snjl#include <sys/kernel.h> 33151564Snjl#include <sys/module.h> 34151564Snjl#include <sys/bus.h> 35151564Snjl 36193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 37193530Sjkim 38151564Snjl#include <dev/acpica/acpivar.h> 39151564Snjl#include <dev/acpica/acpiio.h> 40151564Snjl#include <dev/acpica/acpi_smbus.h> 41151564Snjl 42151564Snjl/* Transactions have failed after 500 ms. */ 43151564Snjl#define SMBUS_TIMEOUT 50 44151564Snjl 45151564Snjlstruct acpi_smbat_softc { 46151564Snjl uint8_t sb_base_addr; 47151564Snjl device_t ec_dev; 48152677Sume 49152677Sume struct acpi_bif bif; 50152677Sume struct acpi_bst bst; 51152677Sume struct timespec bif_lastupdated; 52152677Sume struct timespec bst_lastupdated; 53151564Snjl}; 54151564Snjl 55151564Snjlstatic int acpi_smbat_probe(device_t dev); 56151564Snjlstatic int acpi_smbat_attach(device_t dev); 57151564Snjlstatic int acpi_smbat_shutdown(device_t dev); 58152677Sumestatic int acpi_smbat_info_expired(struct timespec *lastupdated); 59152677Sumestatic void acpi_smbat_info_updated(struct timespec *lastupdated); 60151564Snjlstatic int acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif); 61151564Snjlstatic int acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst); 62151564Snjl 63151564SnjlACPI_SERIAL_DECL(smbat, "ACPI Smart Battery"); 64151564Snjl 65227309Sedstatic SYSCTL_NODE(_debug_acpi, OID_AUTO, batt, CTLFLAG_RD, NULL, 66227309Sed "Battery debugging"); 67186026Ssilby 68186026Ssilby/* On some laptops with smart batteries, enabling battery monitoring 69186026Ssilby * software causes keystrokes from atkbd to be lost. This has also been 70186026Ssilby * reported on Linux, and is apparently due to the keyboard and I2C line 71186026Ssilby * for the battery being routed through the same chip. Whether that's 72186026Ssilby * accurate or not, adding extra sleeps to the status checking code 73186026Ssilby * causes the problem to go away. 74186026Ssilby * 75186026Ssilby * If you experience that problem, try a value of 10ms and move up 76186026Ssilby * from there. 77186026Ssilby */ 78186026Ssilbystatic int batt_sleep_ms; 79186026SsilbySYSCTL_INT(_debug_acpi_batt, OID_AUTO, batt_sleep_ms, CTLFLAG_RW, &batt_sleep_ms, 0, 80186026Ssilby "Sleep during battery status updates to prevent keystroke loss."); 81186026Ssilby 82151564Snjlstatic device_method_t acpi_smbat_methods[] = { 83151564Snjl /* device interface */ 84151564Snjl DEVMETHOD(device_probe, acpi_smbat_probe), 85151564Snjl DEVMETHOD(device_attach, acpi_smbat_attach), 86151564Snjl DEVMETHOD(device_shutdown, acpi_smbat_shutdown), 87151564Snjl 88151564Snjl /* ACPI battery interface */ 89151564Snjl DEVMETHOD(acpi_batt_get_status, acpi_smbat_get_bst), 90151564Snjl DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bif), 91151564Snjl 92246128Ssbz DEVMETHOD_END 93151564Snjl}; 94151564Snjl 95151564Snjlstatic driver_t acpi_smbat_driver = { 96151564Snjl "battery", 97151564Snjl acpi_smbat_methods, 98151564Snjl sizeof(struct acpi_smbat_softc), 99151564Snjl}; 100151564Snjl 101151564Snjlstatic devclass_t acpi_smbat_devclass; 102151564SnjlDRIVER_MODULE(acpi_smbat, acpi, acpi_smbat_driver, acpi_smbat_devclass, 0, 0); 103151564SnjlMODULE_DEPEND(acpi_smbat, acpi, 1, 1, 1); 104151564Snjl 105151564Snjlstatic int 106151564Snjlacpi_smbat_probe(device_t dev) 107151564Snjl{ 108151564Snjl static char *smbat_ids[] = {"ACPI0001", "ACPI0005", NULL}; 109151564Snjl ACPI_STATUS status; 110151564Snjl 111151564Snjl if (acpi_disabled("smbat") || 112151564Snjl ACPI_ID_PROBE(device_get_parent(dev), dev, smbat_ids) == NULL) 113151564Snjl return (ENXIO); 114151564Snjl status = AcpiEvaluateObject(acpi_get_handle(dev), "_EC", NULL, NULL); 115151564Snjl if (ACPI_FAILURE(status)) 116151564Snjl return (ENXIO); 117151564Snjl 118151564Snjl device_set_desc(dev, "ACPI Smart Battery"); 119151564Snjl return (0); 120151564Snjl} 121151564Snjl 122151564Snjlstatic int 123151564Snjlacpi_smbat_attach(device_t dev) 124151564Snjl{ 125151564Snjl struct acpi_smbat_softc *sc; 126151564Snjl uint32_t base; 127151564Snjl 128151564Snjl sc = device_get_softc(dev); 129151564Snjl if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) { 130151564Snjl device_printf(dev, "cannot get EC base address\n"); 131151564Snjl return (ENXIO); 132151564Snjl } 133151564Snjl sc->sb_base_addr = (base >> 8) & 0xff; 134151564Snjl 135151564Snjl /* XXX Only works with one EC, but nearly all systems only have one. */ 136151564Snjl sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0); 137151564Snjl if (sc->ec_dev == NULL) { 138151564Snjl device_printf(dev, "cannot find EC device\n"); 139151564Snjl return (ENXIO); 140151564Snjl } 141151564Snjl 142152677Sume timespecclear(&sc->bif_lastupdated); 143152677Sume timespecclear(&sc->bst_lastupdated); 144152677Sume 145151564Snjl if (acpi_battery_register(dev) != 0) { 146151564Snjl device_printf(dev, "cannot register battery\n"); 147151564Snjl return (ENXIO); 148151564Snjl } 149151564Snjl return (0); 150151564Snjl} 151151564Snjl 152151564Snjlstatic int 153151564Snjlacpi_smbat_shutdown(device_t dev) 154151564Snjl{ 155151564Snjl 156151564Snjl acpi_battery_remove(dev); 157151564Snjl return (0); 158151564Snjl} 159151564Snjl 160151564Snjlstatic int 161152677Sumeacpi_smbat_info_expired(struct timespec *lastupdated) 162152677Sume{ 163152677Sume struct timespec curtime; 164152677Sume 165152677Sume ACPI_SERIAL_ASSERT(smbat); 166152677Sume 167152677Sume if (lastupdated == NULL) 168152677Sume return (TRUE); 169152677Sume if (!timespecisset(lastupdated)) 170152677Sume return (TRUE); 171152677Sume 172152677Sume getnanotime(&curtime); 173152677Sume timespecsub(&curtime, lastupdated); 174152677Sume return (curtime.tv_sec < 0 || 175152677Sume curtime.tv_sec > acpi_battery_get_info_expire()); 176152677Sume} 177152677Sume 178152677Sumestatic void 179152677Sumeacpi_smbat_info_updated(struct timespec *lastupdated) 180152677Sume{ 181152677Sume 182152677Sume ACPI_SERIAL_ASSERT(smbat); 183152677Sume 184152677Sume if (lastupdated != NULL) 185152677Sume getnanotime(lastupdated); 186152677Sume} 187152677Sume 188152677Sumestatic int 189151564Snjlacpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, 190151564Snjl uint16_t *ptr) 191151564Snjl{ 192151564Snjl int error, to; 193202771Sjkim UINT64 val; 194151564Snjl 195151564Snjl ACPI_SERIAL_ASSERT(smbat); 196151564Snjl 197186026Ssilby if (batt_sleep_ms) 198186026Ssilby AcpiOsSleep(batt_sleep_ms); 199186026Ssilby 200151564Snjl val = addr; 201151564Snjl error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, 202151564Snjl val, 1); 203151564Snjl if (error) 204151564Snjl goto out; 205151564Snjl 206151564Snjl val = cmd; 207151564Snjl error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, 208151564Snjl val, 1); 209151564Snjl if (error) 210151564Snjl goto out; 211151564Snjl 212151564Snjl val = 0x09; /* | 0x80 if PEC */ 213151564Snjl error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 214151564Snjl val, 1); 215151564Snjl if (error) 216151564Snjl goto out; 217151564Snjl 218186026Ssilby if (batt_sleep_ms) 219186026Ssilby AcpiOsSleep(batt_sleep_ms); 220186026Ssilby 221151564Snjl for (to = SMBUS_TIMEOUT; to != 0; to--) { 222151564Snjl error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 223151564Snjl &val, 1); 224151564Snjl if (error) 225151564Snjl goto out; 226151564Snjl if (val == 0) 227151564Snjl break; 228151564Snjl AcpiOsSleep(10); 229151564Snjl } 230151564Snjl if (to == 0) { 231151564Snjl error = ETIMEDOUT; 232151564Snjl goto out; 233151564Snjl } 234151564Snjl 235151564Snjl error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); 236151564Snjl if (error) 237151564Snjl goto out; 238151564Snjl if (val & SMBUS_STS_MASK) { 239151564Snjl printf("%s: AE_ERROR 0x%x\n", 240151564Snjl __FUNCTION__, (int)(val & SMBUS_STS_MASK)); 241151564Snjl error = EIO; 242151564Snjl goto out; 243151564Snjl } 244151564Snjl 245151564Snjl error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA, 246151564Snjl &val, 2); 247151564Snjl if (error) 248151564Snjl goto out; 249151564Snjl 250151564Snjl *ptr = val; 251151564Snjl 252151564Snjlout: 253151564Snjl return (error); 254151564Snjl} 255151564Snjl 256151564Snjlstatic int 257151564Snjlacpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, 258151564Snjl uint8_t *ptr, uint16_t len) 259151564Snjl{ 260202771Sjkim UINT64 val; 261151564Snjl uint8_t to; 262151564Snjl int error; 263151564Snjl 264151564Snjl ACPI_SERIAL_ASSERT(smbat); 265151564Snjl 266186026Ssilby if (batt_sleep_ms) 267186026Ssilby AcpiOsSleep(batt_sleep_ms); 268186026Ssilby 269151564Snjl val = addr; 270151564Snjl error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, 271151564Snjl val, 1); 272151564Snjl if (error) 273151564Snjl goto out; 274151564Snjl 275151564Snjl val = cmd; 276151564Snjl error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, 277151564Snjl val, 1); 278151564Snjl if (error) 279151564Snjl goto out; 280151564Snjl 281151564Snjl val = 0x0B /* | 0x80 if PEC */ ; 282151564Snjl error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 283151564Snjl val, 1); 284151564Snjl if (error) 285151564Snjl goto out; 286151564Snjl 287186026Ssilby if (batt_sleep_ms) 288186026Ssilby AcpiOsSleep(batt_sleep_ms); 289186026Ssilby 290151564Snjl for (to = SMBUS_TIMEOUT; to != 0; to--) { 291151564Snjl error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 292151564Snjl &val, 1); 293151564Snjl if (error) 294151564Snjl goto out; 295151564Snjl if (val == 0) 296151564Snjl break; 297151564Snjl AcpiOsSleep(10); 298151564Snjl } 299151564Snjl if (to == 0) { 300151564Snjl error = ETIMEDOUT; 301151564Snjl goto out; 302151564Snjl } 303151564Snjl 304151564Snjl error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); 305151564Snjl if (error) 306151564Snjl goto out; 307151564Snjl if (val & SMBUS_STS_MASK) { 308151564Snjl printf("%s: AE_ERROR 0x%x\n", 309151564Snjl __FUNCTION__, (int)(val & SMBUS_STS_MASK)); 310151564Snjl error = EIO; 311151564Snjl goto out; 312151564Snjl } 313151564Snjl 314151564Snjl /* get length */ 315151564Snjl error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT, 316151564Snjl &val, 1); 317151564Snjl if (error) 318151564Snjl goto out; 319151564Snjl val = (val & 0x1f) + 1; 320151564Snjl 321151564Snjl bzero(ptr, len); 322151564Snjl if (len > val) 323151564Snjl len = val; 324151564Snjl 325186026Ssilby if (batt_sleep_ms) 326186026Ssilby AcpiOsSleep(batt_sleep_ms); 327186026Ssilby 328151564Snjl while (len--) { 329151564Snjl error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA 330151564Snjl + len, &val, 1); 331151564Snjl if (error) 332151564Snjl goto out; 333151564Snjl 334151564Snjl ptr[len] = val; 335186026Ssilby if (batt_sleep_ms) 336186031Ssilby AcpiOsSleep(batt_sleep_ms); 337151564Snjl } 338151564Snjl 339151564Snjlout: 340151564Snjl return (error); 341151564Snjl} 342151564Snjl 343151564Snjlstatic int 344151564Snjlacpi_smbat_get_bst(device_t dev, struct acpi_bst *bst) 345151564Snjl{ 346151564Snjl struct acpi_smbat_softc *sc; 347151564Snjl int error; 348151564Snjl uint32_t cap_units, factor; 349151564Snjl int16_t val; 350151564Snjl uint8_t addr; 351151564Snjl 352151564Snjl ACPI_SERIAL_BEGIN(smbat); 353151564Snjl 354151564Snjl addr = SMBATT_ADDRESS; 355151564Snjl error = ENXIO; 356151564Snjl sc = device_get_softc(dev); 357151564Snjl 358152677Sume if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) { 359152677Sume error = 0; 360152677Sume goto out; 361152677Sume } 362152677Sume 363151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) 364151564Snjl goto out; 365151564Snjl if (val & SMBATT_BM_CAPACITY_MODE) { 366151564Snjl factor = 10; 367151564Snjl cap_units = ACPI_BIF_UNITS_MW; 368151564Snjl } else { 369151564Snjl factor = 1; 370151564Snjl cap_units = ACPI_BIF_UNITS_MA; 371151564Snjl } 372151564Snjl 373151564Snjl /* get battery status */ 374151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val)) 375151564Snjl goto out; 376151564Snjl 377154273Sbruno sc->bst.state = 0; 378154273Sbruno if (val & SMBATT_BS_DISCHARGING) 379152677Sume sc->bst.state |= ACPI_BATT_STAT_DISCHARG; 380151564Snjl 381151564Snjl if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM) 382152677Sume sc->bst.state |= ACPI_BATT_STAT_CRITICAL; 383151564Snjl 384154273Sbruno /* 385154273Sbruno * If the rate is negative, it is discharging. Otherwise, 386154273Sbruno * it is charging. 387154273Sbruno */ 388154273Sbruno if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val)) 389154273Sbruno goto out; 390154273Sbruno 391154273Sbruno if (val > 0) { 392154273Sbruno sc->bst.rate = val * factor; 393216503Savg sc->bst.state &= ~SMBATT_BS_DISCHARGING; 394154273Sbruno sc->bst.state |= ACPI_BATT_STAT_CHARGING; 395154273Sbruno } else if (val < 0) 396154273Sbruno sc->bst.rate = (-val) * factor; 397154273Sbruno else 398154273Sbruno sc->bst.rate = 0; 399154273Sbruno 400151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val)) 401151564Snjl goto out; 402152677Sume sc->bst.cap = val * factor; 403151564Snjl 404151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val)) 405151564Snjl goto out; 406152677Sume sc->bst.volt = val; 407152677Sume 408152677Sume acpi_smbat_info_updated(&sc->bst_lastupdated); 409151564Snjl error = 0; 410151564Snjl 411151564Snjlout: 412152744Snjl if (error == 0) 413152744Snjl memcpy(bst, &sc->bst, sizeof(sc->bst)); 414151564Snjl ACPI_SERIAL_END(smbat); 415151564Snjl return (error); 416151564Snjl} 417151564Snjl 418151564Snjlstatic int 419151564Snjlacpi_smbat_get_bif(device_t dev, struct acpi_bif *bif) 420151564Snjl{ 421151564Snjl struct acpi_smbat_softc *sc; 422151564Snjl int error; 423151564Snjl uint32_t factor; 424151564Snjl uint16_t val; 425151564Snjl uint8_t addr; 426151564Snjl 427151564Snjl ACPI_SERIAL_BEGIN(smbat); 428151564Snjl 429151564Snjl addr = SMBATT_ADDRESS; 430151564Snjl error = ENXIO; 431151564Snjl sc = device_get_softc(dev); 432151564Snjl 433152677Sume if (!acpi_smbat_info_expired(&sc->bif_lastupdated)) { 434152677Sume error = 0; 435152677Sume goto out; 436152677Sume } 437152677Sume 438151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) 439151564Snjl goto out; 440151564Snjl if (val & SMBATT_BM_CAPACITY_MODE) { 441151564Snjl factor = 10; 442152677Sume sc->bif.units = ACPI_BIF_UNITS_MW; 443151564Snjl } else { 444151564Snjl factor = 1; 445152677Sume sc->bif.units = ACPI_BIF_UNITS_MA; 446151564Snjl } 447151564Snjl 448151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val)) 449151564Snjl goto out; 450152677Sume sc->bif.dcap = val * factor; 451151564Snjl 452151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val)) 453151564Snjl goto out; 454152677Sume sc->bif.lfcap = val * factor; 455152677Sume sc->bif.btech = 1; /* secondary (rechargeable) */ 456151564Snjl 457151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val)) 458151564Snjl goto out; 459152677Sume sc->bif.dvol = val; 460151564Snjl 461152677Sume sc->bif.wcap = sc->bif.dcap / 10; 462152677Sume sc->bif.lcap = sc->bif.dcap / 10; 463151564Snjl 464152677Sume sc->bif.gra1 = factor; /* not supported */ 465152677Sume sc->bif.gra2 = factor; /* not supported */ 466151564Snjl 467151564Snjl if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME, 468152677Sume sc->bif.model, sizeof(sc->bif.model))) 469151564Snjl goto out; 470151564Snjl 471151564Snjl if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val)) 472151564Snjl goto out; 473152677Sume snprintf(sc->bif.serial, sizeof(sc->bif.serial), "0x%04x", val); 474151564Snjl 475151564Snjl if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY, 476152677Sume sc->bif.type, sizeof(sc->bif.type))) 477151564Snjl goto out; 478151564Snjl 479151564Snjl if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA, 480152677Sume sc->bif.oeminfo, sizeof(sc->bif.oeminfo))) 481151564Snjl goto out; 482151564Snjl 483152744Snjl /* XXX check if device was replugged during read? */ 484152744Snjl 485152677Sume acpi_smbat_info_updated(&sc->bif_lastupdated); 486151564Snjl error = 0; 487151564Snjl 488151564Snjlout: 489152744Snjl if (error == 0) 490152744Snjl memcpy(bif, &sc->bif, sizeof(sc->bif)); 491151564Snjl ACPI_SERIAL_END(smbat); 492151564Snjl return (error); 493151564Snjl} 494