1 /*- 2 * Copyright (c) 2005 Hans Petter Selasky 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include "opt_acpi.h" 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/bus.h> 35 36 #include <contrib/dev/acpica/include/acpi.h> 37 38 #include <dev/acpica/acpivar.h> 39 #include <dev/acpica/acpiio.h> 40 #include <dev/acpica/acpi_smbus.h> 41 42 /* Transactions have failed after 500 ms. */ 43 #define SMBUS_TIMEOUT 50 44 45 struct acpi_smbat_softc { 46 uint8_t sb_base_addr; 47 device_t ec_dev; 48 49 struct acpi_bif bif; 50 struct acpi_bst bst; 51 struct timespec bif_lastupdated; 52 struct timespec bst_lastupdated; 53 }; 54 55 static int acpi_smbat_probe(device_t dev); 56 static int acpi_smbat_attach(device_t dev); 57 static int acpi_smbat_shutdown(device_t dev); 58 static int acpi_smbat_info_expired(struct timespec *lastupdated); 59 static void acpi_smbat_info_updated(struct timespec *lastupdated); 60 static int acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif); 61 static int acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst); 62 63 ACPI_SERIAL_DECL(smbat, "ACPI Smart Battery"); 64 65 SYSCTL_DECL(_debug_acpi); 66 static SYSCTL_NODE(_debug_acpi, OID_AUTO, batt, CTLFLAG_RD, NULL, 67 "Battery debugging"); 68 69 /* On some laptops with smart batteries, enabling battery monitoring 70 * software causes keystrokes from atkbd to be lost. This has also been 71 * reported on Linux, and is apparently due to the keyboard and I2C line 72 * for the battery being routed through the same chip. Whether that's 73 * accurate or not, adding extra sleeps to the status checking code 74 * causes the problem to go away. 75 * 76 * If you experience that problem, try a value of 10ms and move up 77 * from there. 78 */ 79 static int batt_sleep_ms; 80 SYSCTL_INT(_debug_acpi_batt, OID_AUTO, batt_sleep_ms, CTLFLAG_RW, &batt_sleep_ms, 0, 81 "Sleep during battery status updates to prevent keystroke loss."); 82 83 static device_method_t acpi_smbat_methods[] = { 84 /* device interface */ 85 DEVMETHOD(device_probe, acpi_smbat_probe), 86 DEVMETHOD(device_attach, acpi_smbat_attach), 87 DEVMETHOD(device_shutdown, acpi_smbat_shutdown), 88 89 /* ACPI battery interface */ 90 DEVMETHOD(acpi_batt_get_status, acpi_smbat_get_bst), 91 DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bif), 92 93 {0, 0} 94 }; 95 96 static driver_t acpi_smbat_driver = { 97 "battery", 98 acpi_smbat_methods, 99 sizeof(struct acpi_smbat_softc), 100 }; 101 102 static devclass_t acpi_smbat_devclass; 103 DRIVER_MODULE(acpi_smbat, acpi, acpi_smbat_driver, acpi_smbat_devclass, 0, 0); 104 MODULE_DEPEND(acpi_smbat, acpi, 1, 1, 1); 105 106 static int 107 acpi_smbat_probe(device_t dev) 108 { 109 static char *smbat_ids[] = {"ACPI0001", "ACPI0005", NULL}; 110 ACPI_STATUS status; 111 112 if (acpi_disabled("smbat") || 113 ACPI_ID_PROBE(device_get_parent(dev), dev, smbat_ids) == NULL) 114 return (ENXIO); 115 status = AcpiEvaluateObject(acpi_get_handle(dev), "_EC", NULL, NULL); 116 if (ACPI_FAILURE(status)) 117 return (ENXIO); 118 119 device_set_desc(dev, "ACPI Smart Battery"); 120 return (0); 121 } 122 123 static int 124 acpi_smbat_attach(device_t dev) 125 { 126 struct acpi_smbat_softc *sc; 127 uint32_t base; 128 129 sc = device_get_softc(dev); 130 if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) { 131 device_printf(dev, "cannot get EC base address\n"); 132 return (ENXIO); 133 } 134 sc->sb_base_addr = (base >> 8) & 0xff; 135 136 /* XXX Only works with one EC, but nearly all systems only have one. */ 137 sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0); 138 if (sc->ec_dev == NULL) { 139 device_printf(dev, "cannot find EC device\n"); 140 return (ENXIO); 141 } 142 143 timespecclear(&sc->bif_lastupdated); 144 timespecclear(&sc->bst_lastupdated); 145 146 if (acpi_battery_register(dev) != 0) { 147 device_printf(dev, "cannot register battery\n"); 148 return (ENXIO); 149 } 150 return (0); 151 } 152 153 static int 154 acpi_smbat_shutdown(device_t dev) 155 { 156 157 acpi_battery_remove(dev); 158 return (0); 159 } 160 161 static int 162 acpi_smbat_info_expired(struct timespec *lastupdated) 163 { 164 struct timespec curtime; 165 166 ACPI_SERIAL_ASSERT(smbat); 167 168 if (lastupdated == NULL) 169 return (TRUE); 170 if (!timespecisset(lastupdated)) 171 return (TRUE); 172 173 getnanotime(&curtime); 174 timespecsub(&curtime, lastupdated); 175 return (curtime.tv_sec < 0 || 176 curtime.tv_sec > acpi_battery_get_info_expire()); 177 } 178 179 static void 180 acpi_smbat_info_updated(struct timespec *lastupdated) 181 { 182 183 ACPI_SERIAL_ASSERT(smbat); 184 185 if (lastupdated != NULL) 186 getnanotime(lastupdated); 187 } 188 189 static int 190 acpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, 191 uint16_t *ptr) 192 { 193 int error, to; 194 UINT64 val; 195 196 ACPI_SERIAL_ASSERT(smbat); 197 198 if (batt_sleep_ms) 199 AcpiOsSleep(batt_sleep_ms); 200 201 val = addr; 202 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, 203 val, 1); 204 if (error) 205 goto out; 206 207 val = cmd; 208 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, 209 val, 1); 210 if (error) 211 goto out; 212 213 val = 0x09; /* | 0x80 if PEC */ 214 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 215 val, 1); 216 if (error) 217 goto out; 218 219 if (batt_sleep_ms) 220 AcpiOsSleep(batt_sleep_ms); 221 222 for (to = SMBUS_TIMEOUT; to != 0; to--) { 223 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 224 &val, 1); 225 if (error) 226 goto out; 227 if (val == 0) 228 break; 229 AcpiOsSleep(10); 230 } 231 if (to == 0) { 232 error = ETIMEDOUT; 233 goto out; 234 } 235 236 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); 237 if (error) 238 goto out; 239 if (val & SMBUS_STS_MASK) { 240 printf("%s: AE_ERROR 0x%x\n", 241 __FUNCTION__, (int)(val & SMBUS_STS_MASK)); 242 error = EIO; 243 goto out; 244 } 245 246 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA, 247 &val, 2); 248 if (error) 249 goto out; 250 251 *ptr = val; 252 253 out: 254 return (error); 255 } 256 257 static int 258 acpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, 259 uint8_t *ptr, uint16_t len) 260 { 261 UINT64 val; 262 uint8_t to; 263 int error; 264 265 ACPI_SERIAL_ASSERT(smbat); 266 267 if (batt_sleep_ms) 268 AcpiOsSleep(batt_sleep_ms); 269 270 val = addr; 271 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, 272 val, 1); 273 if (error) 274 goto out; 275 276 val = cmd; 277 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, 278 val, 1); 279 if (error) 280 goto out; 281 282 val = 0x0B /* | 0x80 if PEC */ ; 283 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 284 val, 1); 285 if (error) 286 goto out; 287 288 if (batt_sleep_ms) 289 AcpiOsSleep(batt_sleep_ms); 290 291 for (to = SMBUS_TIMEOUT; to != 0; to--) { 292 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 293 &val, 1); 294 if (error) 295 goto out; 296 if (val == 0) 297 break; 298 AcpiOsSleep(10); 299 } 300 if (to == 0) { 301 error = ETIMEDOUT; 302 goto out; 303 } 304 305 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); 306 if (error) 307 goto out; 308 if (val & SMBUS_STS_MASK) { 309 printf("%s: AE_ERROR 0x%x\n", 310 __FUNCTION__, (int)(val & SMBUS_STS_MASK)); 311 error = EIO; 312 goto out; 313 } 314 315 /* get length */ 316 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT, 317 &val, 1); 318 if (error) 319 goto out; 320 val = (val & 0x1f) + 1; 321 322 bzero(ptr, len); 323 if (len > val) 324 len = val; 325 326 if (batt_sleep_ms) 327 AcpiOsSleep(batt_sleep_ms); 328 329 while (len--) { 330 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA 331 + len, &val, 1); 332 if (error) 333 goto out; 334 335 ptr[len] = val; 336 if (batt_sleep_ms) 337 AcpiOsSleep(batt_sleep_ms); 338 } 339 340 out: 341 return (error); 342 } 343 344 static int 345 acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst) 346 { 347 struct acpi_smbat_softc *sc; 348 int error; 349 uint32_t cap_units, factor; 350 int16_t val; 351 uint8_t addr; 352 353 ACPI_SERIAL_BEGIN(smbat); 354 355 addr = SMBATT_ADDRESS; 356 error = ENXIO; 357 sc = device_get_softc(dev); 358 359 if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) { 360 error = 0; 361 goto out; 362 } 363 364 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) 365 goto out; 366 if (val & SMBATT_BM_CAPACITY_MODE) { 367 factor = 10; 368 cap_units = ACPI_BIF_UNITS_MW; 369 } else { 370 factor = 1; 371 cap_units = ACPI_BIF_UNITS_MA; 372 } 373 374 /* get battery status */ 375 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val)) 376 goto out; 377 378 sc->bst.state = 0; 379 if (val & SMBATT_BS_DISCHARGING) 380 sc->bst.state |= ACPI_BATT_STAT_DISCHARG; 381 382 if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM) 383 sc->bst.state |= ACPI_BATT_STAT_CRITICAL; 384 385 /* 386 * If the rate is negative, it is discharging. Otherwise, 387 * it is charging. 388 */ 389 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val)) 390 goto out; 391 392 if (val > 0) { 393 sc->bst.rate = val * factor; 394 sc->bst.state &= ~SMBATT_BS_DISCHARGING; 395 sc->bst.state |= ACPI_BATT_STAT_CHARGING; 396 } else if (val < 0) 397 sc->bst.rate = (-val) * factor; 398 else 399 sc->bst.rate = 0; 400 401 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val)) 402 goto out; 403 sc->bst.cap = val * factor; 404 405 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val)) 406 goto out; 407 sc->bst.volt = val; 408 409 acpi_smbat_info_updated(&sc->bst_lastupdated); 410 error = 0; 411 412 out: 413 if (error == 0) 414 memcpy(bst, &sc->bst, sizeof(sc->bst)); 415 ACPI_SERIAL_END(smbat); 416 return (error); 417 } 418 419 static int 420 acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif) 421 { 422 struct acpi_smbat_softc *sc; 423 int error; 424 uint32_t factor; 425 uint16_t val; 426 uint8_t addr; 427 428 ACPI_SERIAL_BEGIN(smbat); 429 430 addr = SMBATT_ADDRESS; 431 error = ENXIO; 432 sc = device_get_softc(dev); 433 434 if (!acpi_smbat_info_expired(&sc->bif_lastupdated)) { 435 error = 0; 436 goto out; 437 } 438 439 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) 440 goto out; 441 if (val & SMBATT_BM_CAPACITY_MODE) { 442 factor = 10; 443 sc->bif.units = ACPI_BIF_UNITS_MW; 444 } else { 445 factor = 1; 446 sc->bif.units = ACPI_BIF_UNITS_MA; 447 } 448 449 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val)) 450 goto out; 451 sc->bif.dcap = val * factor; 452 453 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val)) 454 goto out; 455 sc->bif.lfcap = val * factor; 456 sc->bif.btech = 1; /* secondary (rechargeable) */ 457 458 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val)) 459 goto out; 460 sc->bif.dvol = val; 461 462 sc->bif.wcap = sc->bif.dcap / 10; 463 sc->bif.lcap = sc->bif.dcap / 10; 464 465 sc->bif.gra1 = factor; /* not supported */ 466 sc->bif.gra2 = factor; /* not supported */ 467 468 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME, 469 sc->bif.model, sizeof(sc->bif.model))) 470 goto out; 471 472 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val)) 473 goto out; 474 snprintf(sc->bif.serial, sizeof(sc->bif.serial), "0x%04x", val); 475 476 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY, 477 sc->bif.type, sizeof(sc->bif.type))) 478 goto out; 479 480 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA, 481 sc->bif.oeminfo, sizeof(sc->bif.oeminfo))) 482 goto out; 483 484 /* XXX check if device was replugged during read? */ 485 486 acpi_smbat_info_updated(&sc->bif_lastupdated); 487 error = 0; 488 489 out: 490 if (error == 0) 491 memcpy(bif, &sc->bif, sizeof(sc->bif)); 492 ACPI_SERIAL_END(smbat); 493 return (error); 494 } 495