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