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_bix bix; 50 struct acpi_bst bst; 51 struct timespec bix_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_bix(device_t dev, void *, size_t); 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 static SYSCTL_NODE(_debug_acpi, OID_AUTO, batt, 66 CTLFLAG_RD | CTLFLAG_MPSAFE, 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_bix), 92 93 DEVMETHOD_END 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 int rv; 112 113 if (acpi_disabled("smbat")) 114 return (ENXIO); 115 rv = ACPI_ID_PROBE(device_get_parent(dev), dev, smbat_ids, NULL); 116 if (rv > 0) 117 return (rv); 118 status = AcpiEvaluateObject(acpi_get_handle(dev), "_EC", NULL, NULL); 119 if (ACPI_FAILURE(status)) 120 return (ENXIO); 121 device_set_desc(dev, "ACPI Smart Battery"); 122 return (rv); 123 } 124 125 static int 126 acpi_smbat_attach(device_t dev) 127 { 128 struct acpi_smbat_softc *sc; 129 uint32_t base; 130 131 sc = device_get_softc(dev); 132 if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) { 133 device_printf(dev, "cannot get EC base address\n"); 134 return (ENXIO); 135 } 136 sc->sb_base_addr = (base >> 8) & 0xff; 137 138 /* XXX Only works with one EC, but nearly all systems only have one. */ 139 sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0); 140 if (sc->ec_dev == NULL) { 141 device_printf(dev, "cannot find EC device\n"); 142 return (ENXIO); 143 } 144 145 timespecclear(&sc->bix_lastupdated); 146 timespecclear(&sc->bst_lastupdated); 147 148 if (acpi_battery_register(dev) != 0) { 149 device_printf(dev, "cannot register battery\n"); 150 return (ENXIO); 151 } 152 return (0); 153 } 154 155 static int 156 acpi_smbat_shutdown(device_t dev) 157 { 158 159 acpi_battery_remove(dev); 160 return (0); 161 } 162 163 static int 164 acpi_smbat_info_expired(struct timespec *lastupdated) 165 { 166 struct timespec curtime; 167 168 ACPI_SERIAL_ASSERT(smbat); 169 170 if (lastupdated == NULL) 171 return (TRUE); 172 if (!timespecisset(lastupdated)) 173 return (TRUE); 174 175 getnanotime(&curtime); 176 timespecsub(&curtime, lastupdated, &curtime); 177 return (curtime.tv_sec < 0 || 178 curtime.tv_sec > acpi_battery_get_info_expire()); 179 } 180 181 static void 182 acpi_smbat_info_updated(struct timespec *lastupdated) 183 { 184 185 ACPI_SERIAL_ASSERT(smbat); 186 187 if (lastupdated != NULL) 188 getnanotime(lastupdated); 189 } 190 191 static int 192 acpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, 193 uint16_t *ptr) 194 { 195 int error, to; 196 UINT64 val; 197 198 ACPI_SERIAL_ASSERT(smbat); 199 200 if (batt_sleep_ms) 201 AcpiOsSleep(batt_sleep_ms); 202 203 val = addr; 204 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, 205 val, 1); 206 if (error) 207 goto out; 208 209 val = cmd; 210 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, 211 val, 1); 212 if (error) 213 goto out; 214 215 val = 0x09; /* | 0x80 if PEC */ 216 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 217 val, 1); 218 if (error) 219 goto out; 220 221 if (batt_sleep_ms) 222 AcpiOsSleep(batt_sleep_ms); 223 224 for (to = SMBUS_TIMEOUT; to != 0; to--) { 225 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 226 &val, 1); 227 if (error) 228 goto out; 229 if (val == 0) 230 break; 231 AcpiOsSleep(10); 232 } 233 if (to == 0) { 234 error = ETIMEDOUT; 235 goto out; 236 } 237 238 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); 239 if (error) 240 goto out; 241 if (val & SMBUS_STS_MASK) { 242 printf("%s: AE_ERROR 0x%x\n", 243 __FUNCTION__, (int)(val & SMBUS_STS_MASK)); 244 error = EIO; 245 goto out; 246 } 247 248 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA, 249 &val, 2); 250 if (error) 251 goto out; 252 253 *ptr = val; 254 255 out: 256 return (error); 257 } 258 259 static int 260 acpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd, 261 uint8_t *ptr, uint16_t len) 262 { 263 UINT64 val; 264 uint8_t to; 265 int error; 266 267 ACPI_SERIAL_ASSERT(smbat); 268 269 if (batt_sleep_ms) 270 AcpiOsSleep(batt_sleep_ms); 271 272 val = addr; 273 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR, 274 val, 1); 275 if (error) 276 goto out; 277 278 val = cmd; 279 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD, 280 val, 1); 281 if (error) 282 goto out; 283 284 val = 0x0B /* | 0x80 if PEC */ ; 285 error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 286 val, 1); 287 if (error) 288 goto out; 289 290 if (batt_sleep_ms) 291 AcpiOsSleep(batt_sleep_ms); 292 293 for (to = SMBUS_TIMEOUT; to != 0; to--) { 294 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL, 295 &val, 1); 296 if (error) 297 goto out; 298 if (val == 0) 299 break; 300 AcpiOsSleep(10); 301 } 302 if (to == 0) { 303 error = ETIMEDOUT; 304 goto out; 305 } 306 307 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1); 308 if (error) 309 goto out; 310 if (val & SMBUS_STS_MASK) { 311 printf("%s: AE_ERROR 0x%x\n", 312 __FUNCTION__, (int)(val & SMBUS_STS_MASK)); 313 error = EIO; 314 goto out; 315 } 316 317 /* get length */ 318 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT, 319 &val, 1); 320 if (error) 321 goto out; 322 val = (val & 0x1f) + 1; 323 324 bzero(ptr, len); 325 if (len > val) 326 len = val; 327 328 if (batt_sleep_ms) 329 AcpiOsSleep(batt_sleep_ms); 330 331 while (len--) { 332 error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA 333 + len, &val, 1); 334 if (error) 335 goto out; 336 337 ptr[len] = val; 338 if (batt_sleep_ms) 339 AcpiOsSleep(batt_sleep_ms); 340 } 341 342 out: 343 return (error); 344 } 345 346 static int 347 acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst) 348 { 349 struct acpi_smbat_softc *sc; 350 int error; 351 uint32_t factor; 352 int16_t val; 353 uint8_t addr; 354 355 ACPI_SERIAL_BEGIN(smbat); 356 357 addr = SMBATT_ADDRESS; 358 error = ENXIO; 359 sc = device_get_softc(dev); 360 361 if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) { 362 error = 0; 363 goto out; 364 } 365 366 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) 367 goto out; 368 if (val & SMBATT_BM_CAPACITY_MODE) 369 factor = 10; 370 else 371 factor = 1; 372 373 /* get battery status */ 374 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val)) 375 goto out; 376 377 sc->bst.state = 0; 378 if (val & SMBATT_BS_DISCHARGING) 379 sc->bst.state |= ACPI_BATT_STAT_DISCHARG; 380 381 if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM) 382 sc->bst.state |= ACPI_BATT_STAT_CRITICAL; 383 384 /* 385 * If the rate is negative, it is discharging. Otherwise, 386 * it is charging. 387 */ 388 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val)) 389 goto out; 390 391 if (val > 0) { 392 sc->bst.rate = val * factor; 393 sc->bst.state &= ~SMBATT_BS_DISCHARGING; 394 sc->bst.state |= ACPI_BATT_STAT_CHARGING; 395 } else if (val < 0) 396 sc->bst.rate = (-val) * factor; 397 else 398 sc->bst.rate = 0; 399 400 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val)) 401 goto out; 402 sc->bst.cap = val * factor; 403 404 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val)) 405 goto out; 406 sc->bst.volt = val; 407 408 acpi_smbat_info_updated(&sc->bst_lastupdated); 409 error = 0; 410 411 out: 412 if (error == 0) 413 memcpy(bst, &sc->bst, sizeof(sc->bst)); 414 ACPI_SERIAL_END(smbat); 415 return (error); 416 } 417 418 static int 419 acpi_smbat_get_bix(device_t dev, void *bix, size_t len) 420 { 421 struct acpi_smbat_softc *sc; 422 int error; 423 uint32_t factor; 424 uint16_t val; 425 uint8_t addr; 426 427 if (len != sizeof(struct acpi_bix) && 428 len != sizeof(struct acpi_bif)) 429 return (-1); 430 431 ACPI_SERIAL_BEGIN(smbat); 432 433 addr = SMBATT_ADDRESS; 434 error = ENXIO; 435 sc = device_get_softc(dev); 436 437 if (!acpi_smbat_info_expired(&sc->bix_lastupdated)) { 438 error = 0; 439 goto out; 440 } 441 442 sc->bix.rev = ACPI_BIX_REV_BIF; 443 444 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val)) 445 goto out; 446 if (val & SMBATT_BM_CAPACITY_MODE) { 447 factor = 10; 448 sc->bix.units = ACPI_BIX_UNITS_MW; 449 } else { 450 factor = 1; 451 sc->bix.units = ACPI_BIX_UNITS_MA; 452 } 453 454 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val)) 455 goto out; 456 sc->bix.dcap = val * factor; 457 458 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val)) 459 goto out; 460 sc->bix.lfcap = val * factor; 461 sc->bix.btech = 1; /* secondary (rechargeable) */ 462 463 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val)) 464 goto out; 465 sc->bix.dvol = val; 466 467 sc->bix.wcap = sc->bix.dcap / 10; 468 sc->bix.lcap = sc->bix.dcap / 10; 469 470 sc->bix.gra1 = factor; /* not supported */ 471 sc->bix.gra2 = factor; /* not supported */ 472 473 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME, 474 sc->bix.model, sizeof(sc->bix.model))) 475 goto out; 476 477 if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val)) 478 goto out; 479 snprintf(sc->bix.serial, sizeof(sc->bix.serial), "0x%04x", val); 480 481 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY, 482 sc->bix.type, sizeof(sc->bix.type))) 483 goto out; 484 485 if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA, 486 sc->bix.oeminfo, sizeof(sc->bix.oeminfo))) 487 goto out; 488 489 /* XXX check if device was replugged during read? */ 490 491 acpi_smbat_info_updated(&sc->bix_lastupdated); 492 error = 0; 493 494 out: 495 if (error == 0) 496 memcpy(bix, &sc->bix, len); 497 ACPI_SERIAL_END(smbat); 498 return (error); 499 } 500