acpi_smbat.c revision 155869
1169695Skan/*- 2169695Skan * Copyright (c) 2005 Hans Petter Selasky 3169695Skan * All rights reserved. 4169695Skan * 5169695Skan * Redistribution and use in source and binary forms, with or without 6169695Skan * modification, are permitted provided that the following conditions 7169695Skan * are met: 8169695Skan * 1. Redistributions of source code must retain the above copyright 9169695Skan * notice, this list of conditions and the following disclaimer. 10169695Skan * 2. Redistributions in binary form must reproduce the above copyright 11169695Skan * notice, this list of conditions and the following disclaimer in the 12169695Skan * documentation and/or other materials provided with the distribution. 13169695Skan * 14169695Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15169695Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16169695Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17169695Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18169695Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19169695Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169695Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21169695Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22169695Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23169695Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24169695Skan * SUCH DAMAGE. 25169695Skan */ 26169695Skan 27169695Skan#include <sys/cdefs.h> 28169695Skan__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_smbat.c 155869 2006-02-21 03:16:58Z njl $"); 29169695Skan 30169695Skan#include "opt_acpi.h" 31169695Skan#include <sys/param.h> 32169695Skan#include <sys/kernel.h> 33169695Skan#include <sys/module.h> 34169695Skan#include <sys/bus.h> 35169695Skan 36169695Skan#include <contrib/dev/acpica/acpi.h> 37169695Skan#include <dev/acpica/acpivar.h> 38169695Skan#include <dev/acpica/acpiio.h> 39169695Skan#include <dev/acpica/acpi_smbus.h> 40169695Skan 41169695Skan/* Transactions have failed after 500 ms. */ 42169695Skan#define SMBUS_TIMEOUT 50 43169695Skan 44169695Skanstruct acpi_smbat_softc { 45169695Skan uint8_t sb_base_addr; 46169695Skan device_t ec_dev; 47169695Skan 48169695Skan struct acpi_bif bif; 49169695Skan struct acpi_bst bst; 50169695Skan struct timespec bif_lastupdated; 51169695Skan struct timespec bst_lastupdated; 52169695Skan}; 53169695Skan 54169695Skanstatic int acpi_smbat_probe(device_t dev); 55169695Skanstatic int acpi_smbat_attach(device_t dev); 56169695Skanstatic int acpi_smbat_shutdown(device_t dev); 57169695Skanstatic int acpi_smbat_info_expired(struct timespec *lastupdated); 58169695Skanstatic void acpi_smbat_info_updated(struct timespec *lastupdated); 59169695Skanstatic int acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif); 60169695Skanstatic int acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst); 61169695Skan 62169695SkanACPI_SERIAL_DECL(smbat, "ACPI Smart Battery"); 63169695Skan 64169695Skanstatic device_method_t acpi_smbat_methods[] = { 65169695Skan /* device interface */ 66169695Skan DEVMETHOD(device_probe, acpi_smbat_probe), 67169695Skan DEVMETHOD(device_attach, acpi_smbat_attach), 68169695Skan DEVMETHOD(device_shutdown, acpi_smbat_shutdown), 69169695Skan 70169695Skan /* ACPI battery interface */ 71169695Skan DEVMETHOD(acpi_batt_get_status, acpi_smbat_get_bst), 72169695Skan DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bif), 73169695Skan 74169695Skan {0, 0} 75169695Skan}; 76169695Skan 77169695Skanstatic driver_t acpi_smbat_driver = { 78169695Skan "battery", 79169695Skan acpi_smbat_methods, 80169695Skan sizeof(struct acpi_smbat_softc), 81169695Skan}; 82169695Skan 83169695Skanstatic devclass_t acpi_smbat_devclass; 84169695SkanDRIVER_MODULE(acpi_smbat, acpi, acpi_smbat_driver, acpi_smbat_devclass, 0, 0); 85169695SkanMODULE_DEPEND(acpi_smbat, acpi, 1, 1, 1); 86169695Skan 87169695Skanstatic int 88169695Skanacpi_smbat_probe(device_t dev) 89169695Skan{ 90169695Skan static char *smbat_ids[] = {"ACPI0001", "ACPI0005", NULL}; 91169695Skan ACPI_STATUS status; 92169695Skan 93169695Skan if (acpi_disabled("smbat") || 94169695Skan ACPI_ID_PROBE(device_get_parent(dev), dev, smbat_ids) == NULL) 95169695Skan return (ENXIO); 96169695Skan status = AcpiEvaluateObject(acpi_get_handle(dev), "_EC", NULL, NULL); 97169695Skan if (ACPI_FAILURE(status)) 98169695Skan return (ENXIO); 99169695Skan 100169695Skan device_set_desc(dev, "ACPI Smart Battery"); 101169695Skan return (0); 102169695Skan} 103169695Skan 104169695Skanstatic int 105169695Skanacpi_smbat_attach(device_t dev) 106169695Skan{ 107169695Skan struct acpi_smbat_softc *sc; 108169695Skan uint32_t base; 109169695Skan 110169695Skan sc = device_get_softc(dev); 111169695Skan if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) { 112169695Skan device_printf(dev, "cannot get EC base address\n"); 113169695Skan return (ENXIO); 114169695Skan } 115169695Skan sc->sb_base_addr = (base >> 8) & 0xff; 116169695Skan 117169695Skan /* XXX Only works with one EC, but nearly all systems only have one. */ 118169695Skan sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0); 119169695Skan if (sc->ec_dev == NULL) { 120169695Skan device_printf(dev, "cannot find EC device\n"); 121169695Skan return (ENXIO); 122169695Skan } 123169695Skan 124169695Skan timespecclear(&sc->bif_lastupdated); 125169695Skan timespecclear(&sc->bst_lastupdated); 126169695Skan 127169695Skan if (acpi_battery_register(dev) != 0) { 128169695Skan device_printf(dev, "cannot register battery\n"); 129169695Skan return (ENXIO); 130169695Skan } 131169695Skan return (0); 132169695Skan} 133169695Skan 134169695Skanstatic int 135169695Skanacpi_smbat_shutdown(device_t dev) 136169695Skan{ 137169695Skan 138169695Skan acpi_battery_remove(dev); 139169695Skan return (0); 140169695Skan} 141169695Skan 142169695Skanstatic int 143169695Skanacpi_smbat_info_expired(struct timespec *lastupdated) 144169695Skan{ 145169695Skan struct timespec curtime; 146169695Skan 147169695Skan ACPI_SERIAL_ASSERT(smbat); 148169695Skan 149169695Skan if (lastupdated == NULL) 150169695Skan return (TRUE); 151169695Skan if (!timespecisset(lastupdated)) 152169695Skan return (TRUE); 153169695Skan 154169695Skan getnanotime(&curtime); 155169695Skan timespecsub(&curtime, lastupdated); 156169695Skan return (curtime.tv_sec < 0 || 157169695Skan curtime.tv_sec > acpi_battery_get_info_expire()); 158169695Skan} 159169695Skan 160169695Skanstatic void 161169695Skanacpi_smbat_info_updated(struct timespec *lastupdated) 162169695Skan{ 163169695Skan 164169695Skan ACPI_SERIAL_ASSERT(smbat); 165169695Skan 166169695Skan if (lastupdated != NULL) 167169695Skan getnanotime(lastupdated); 168169695Skan} 169169695Skan 170169695Skanstatic int 171169695Skanacpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, 172169695Skan uint16_t *ptr) 173169695Skan{ 174169695Skan int error, to; 175169695Skan ACPI_INTEGER val; 176169695Skan 177169695Skan ACPI_SERIAL_ASSERT(smbat); 178169695Skan 179169695Skan val = addr; 180169695Skan error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, 181169695Skan val, 1); 182169695Skan if (error) 183169695Skan goto out; 184169695Skan 185169695Skan val = cmd; 186169695Skan error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, 187169695Skan val, 1); 188169695Skan if (error) 189169695Skan goto out; 190169695Skan 191169695Skan val = 0x09; /* | 0x80 if PEC */ 192169695Skan error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 193169695Skan val, 1); 194169695Skan if (error) 195169695Skan goto out; 196169695Skan 197169695Skan for (to = SMBUS_TIMEOUT; to != 0; to--) { 198169695Skan error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 199169695Skan &val, 1); 200169695Skan if (error) 201169695Skan goto out; 202169695Skan if (val == 0) 203169695Skan break; 204169695Skan AcpiOsSleep(10); 205169695Skan } 206169695Skan if (to == 0) { 207169695Skan error = ETIMEDOUT; 208169695Skan goto out; 209169695Skan } 210169695Skan 211169695Skan error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); 212169695Skan if (error) 213169695Skan goto out; 214169695Skan if (val & SMBUS_STS_MASK) { 215169695Skan printf("%s: AE_ERROR 0x%x\n", 216169695Skan __FUNCTION__, (int)(val & SMBUS_STS_MASK)); 217169695Skan error = EIO; 218169695Skan goto out; 219169695Skan } 220169695Skan 221169695Skan error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA, 222169695Skan &val, 2); 223169695Skan if (error) 224169695Skan goto out; 225169695Skan 226169695Skan *ptr = val; 227169695Skan 228169695Skanout: 229169695Skan return (error); 230169695Skan} 231169695Skan 232169695Skanstatic int 233169695Skanacpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, 234169695Skan uint8_t *ptr, uint16_t len) 235169695Skan{ 236169695Skan ACPI_INTEGER val; 237169695Skan uint8_t to; 238169695Skan int error; 239169695Skan 240169695Skan ACPI_SERIAL_ASSERT(smbat); 241169695Skan 242169695Skan val = addr; 243169695Skan error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, 244169695Skan val, 1); 245169695Skan if (error) 246169695Skan goto out; 247169695Skan 248169695Skan val = cmd; 249169695Skan error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, 250169695Skan val, 1); 251169695Skan if (error) 252169695Skan goto out; 253169695Skan 254169695Skan val = 0x0B /* | 0x80 if PEC */ ; 255169695Skan error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 256169695Skan val, 1); 257169695Skan if (error) 258169695Skan goto out; 259169695Skan 260169695Skan for (to = SMBUS_TIMEOUT; to != 0; to--) { 261169695Skan error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 262169695Skan &val, 1); 263169695Skan if (error) 264169695Skan goto out; 265169695Skan if (val == 0) 266169695Skan break; 267169695Skan AcpiOsSleep(10); 268169695Skan } 269169695Skan if (to == 0) { 270169695Skan error = ETIMEDOUT; 271169695Skan goto out; 272169695Skan } 273169695Skan 274169695Skan error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); 275169695Skan if (error) 276169695Skan goto out; 277169695Skan if (val & SMBUS_STS_MASK) { 278169695Skan printf("%s: AE_ERROR 0x%x\n", 279169695Skan __FUNCTION__, (int)(val & SMBUS_STS_MASK)); 280169695Skan error = EIO; 281169695Skan goto out; 282169695Skan } 283169695Skan 284169695Skan /* get length */ 285169695Skan error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT, 286169695Skan &val, 1); 287169695Skan if (error) 288169695Skan goto out; 289169695Skan val = (val & 0x1f) + 1; 290169695Skan 291169695Skan bzero(ptr, len); 292169695Skan if (len > val) 293169695Skan len = val; 294169695Skan 295169695Skan while (len--) { 296169695Skan error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA 297169695Skan + len, &val, 1); 298169695Skan if (error) 299169695Skan goto out; 300169695Skan 301169695Skan ptr[len] = val; 302169695Skan } 303169695Skan 304169695Skanout: 305169695Skan return (error); 306169695Skan} 307169695Skan 308169695Skanstatic int 309169695Skanacpi_smbat_get_bst(device_t dev, struct acpi_bst *bst) 310169695Skan{ 311169695Skan struct acpi_smbat_softc *sc; 312169695Skan int error; 313169695Skan uint32_t cap_units, factor; 314169695Skan int16_t val; 315169695Skan uint8_t addr; 316169695Skan 317169695Skan ACPI_SERIAL_BEGIN(smbat); 318169695Skan 319169695Skan addr = SMBATT_ADDRESS; 320169695Skan error = ENXIO; 321169695Skan sc = device_get_softc(dev); 322169695Skan 323169695Skan if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) { 324169695Skan error = 0; 325169695Skan goto out; 326169695Skan } 327169695Skan 328169695Skan if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) 329169695Skan goto out; 330169695Skan if (val & SMBATT_BM_CAPACITY_MODE) { 331169695Skan factor = 10; 332169695Skan cap_units = ACPI_BIF_UNITS_MW; 333169695Skan } else { 334169695Skan factor = 1; 335169695Skan cap_units = ACPI_BIF_UNITS_MA; 336169695Skan } 337169695Skan 338169695Skan /* get battery status */ 339169695Skan if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val)) 340169695Skan goto out; 341169695Skan 342169695Skan sc->bst.state = 0; 343169695Skan if (val & SMBATT_BS_DISCHARGING) 344169695Skan sc->bst.state |= ACPI_BATT_STAT_DISCHARG; 345169695Skan 346169695Skan if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM) 347169695Skan sc->bst.state |= ACPI_BATT_STAT_CRITICAL; 348169695Skan 349169695Skan /* 350169695Skan * If the rate is negative, it is discharging. Otherwise, 351169695Skan * it is charging. 352169695Skan */ 353169695Skan if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val)) 354169695Skan goto out; 355169695Skan 356169695Skan if (val > 0) { 357169695Skan sc->bst.rate = val * factor; 358169695Skan sc->bst.state |= ACPI_BATT_STAT_CHARGING; 359169695Skan } else if (val < 0) 360169695Skan sc->bst.rate = (-val) * factor; 361169695Skan else 362169695Skan sc->bst.rate = 0; 363169695Skan 364169695Skan if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val)) 365169695Skan goto out; 366169695Skan sc->bst.cap = val * factor; 367169695Skan 368169695Skan if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val)) 369169695Skan goto out; 370169695Skan sc->bst.volt = val; 371169695Skan 372169695Skan acpi_smbat_info_updated(&sc->bst_lastupdated); 373169695Skan error = 0; 374169695Skan 375169695Skanout: 376169695Skan if (error == 0) 377169695Skan memcpy(bst, &sc->bst, sizeof(sc->bst)); 378169695Skan ACPI_SERIAL_END(smbat); 379169695Skan return (error); 380169695Skan} 381169695Skan 382169695Skanstatic int 383169695Skanacpi_smbat_get_bif(device_t dev, struct acpi_bif *bif) 384169695Skan{ 385169695Skan struct acpi_smbat_softc *sc; 386169695Skan int error; 387169695Skan uint32_t factor; 388169695Skan uint16_t val; 389169695Skan uint8_t addr; 390 391 ACPI_SERIAL_BEGIN(smbat); 392 393 addr = SMBATT_ADDRESS; 394 error = ENXIO; 395 sc = device_get_softc(dev); 396 397 if (!acpi_smbat_info_expired(&sc->bif_lastupdated)) { 398 error = 0; 399 goto out; 400 } 401 402 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) 403 goto out; 404 if (val & SMBATT_BM_CAPACITY_MODE) { 405 factor = 10; 406 sc->bif.units = ACPI_BIF_UNITS_MW; 407 } else { 408 factor = 1; 409 sc->bif.units = ACPI_BIF_UNITS_MA; 410 } 411 412 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val)) 413 goto out; 414 sc->bif.dcap = val * factor; 415 416 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val)) 417 goto out; 418 sc->bif.lfcap = val * factor; 419 sc->bif.btech = 1; /* secondary (rechargeable) */ 420 421 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val)) 422 goto out; 423 sc->bif.dvol = val; 424 425 sc->bif.wcap = sc->bif.dcap / 10; 426 sc->bif.lcap = sc->bif.dcap / 10; 427 428 sc->bif.gra1 = factor; /* not supported */ 429 sc->bif.gra2 = factor; /* not supported */ 430 431 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME, 432 sc->bif.model, sizeof(sc->bif.model))) 433 goto out; 434 435 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val)) 436 goto out; 437 snprintf(sc->bif.serial, sizeof(sc->bif.serial), "0x%04x", val); 438 439 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY, 440 sc->bif.type, sizeof(sc->bif.type))) 441 goto out; 442 443 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA, 444 sc->bif.oeminfo, sizeof(sc->bif.oeminfo))) 445 goto out; 446 447 /* XXX check if device was replugged during read? */ 448 449 acpi_smbat_info_updated(&sc->bif_lastupdated); 450 error = 0; 451 452out: 453 if (error == 0) 454 memcpy(bif, &sc->bif, sizeof(sc->bif)); 455 ACPI_SERIAL_END(smbat); 456 return (error); 457} 458