1 /*- 2 * Copyright (c) 2005 Nate Lawson 3 * Copyright (c) 2000 Munehiro Matsuda 4 * Copyright (c) 2000 Takanori Watanabe 5 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include "opt_acpi.h" 34 #include <sys/param.h> 35 #include <sys/kernel.h> 36 #include <sys/module.h> 37 #include <sys/bus.h> 38 #include <sys/ioccom.h> 39 40 #include <machine/bus.h> 41 #include <sys/rman.h> 42 #include <sys/malloc.h> 43 44 #include <contrib/dev/acpica/acpi.h> 45 #include <dev/acpica/acpivar.h> 46 #include <dev/acpica/acpiio.h> 47 48 MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat", "ACPI control method battery data"); 49 50 /* Number of times to retry initialization before giving up. */ 51 #define ACPI_CMBAT_RETRY_MAX 6 52 53 /* Check the battery once a minute. */ 54 #define CMBAT_POLLRATE (60 * hz) 55 56 /* Hooks for the ACPI CA debugging infrastructure */ 57 #define _COMPONENT ACPI_BATTERY 58 ACPI_MODULE_NAME("BATTERY") 59 60 #define ACPI_BATTERY_BST_CHANGE 0x80 61 #define ACPI_BATTERY_BIF_CHANGE 0x81 62 63 struct acpi_cmbat_softc { 64 device_t dev; 65 int flags; 66 67 struct acpi_bif bif; 68 struct acpi_bst bst; 69 struct timespec bst_lastupdated; 70 }; 71 72 ACPI_SERIAL_DECL(cmbat, "ACPI cmbat"); 73 74 static int acpi_cmbat_probe(device_t dev); 75 static int acpi_cmbat_attach(device_t dev); 76 static int acpi_cmbat_detach(device_t dev); 77 static int acpi_cmbat_resume(device_t dev); 78 static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, 79 void *context); 80 static int acpi_cmbat_info_expired(struct timespec *lastupdated); 81 static void acpi_cmbat_info_updated(struct timespec *lastupdated); 82 static void acpi_cmbat_get_bst(void *arg); 83 static void acpi_cmbat_get_bif_task(void *arg); 84 static void acpi_cmbat_get_bif(void *arg); 85 static int acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp); 86 static int acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp); 87 static void acpi_cmbat_init_battery(void *arg); 88 89 static device_method_t acpi_cmbat_methods[] = { 90 /* Device interface */ 91 DEVMETHOD(device_probe, acpi_cmbat_probe), 92 DEVMETHOD(device_attach, acpi_cmbat_attach), 93 DEVMETHOD(device_detach, acpi_cmbat_detach), 94 DEVMETHOD(device_resume, acpi_cmbat_resume), 95 96 /* ACPI battery interface */ 97 DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bif), 98 DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst), 99 100 {0, 0} 101 }; 102 103 static driver_t acpi_cmbat_driver = { 104 "battery", 105 acpi_cmbat_methods, 106 sizeof(struct acpi_cmbat_softc), 107 }; 108 109 static devclass_t acpi_cmbat_devclass; 110 DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0); 111 MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1); 112 113 static int 114 acpi_cmbat_probe(device_t dev) 115 { 116 static char *cmbat_ids[] = { "PNP0C0A", NULL }; 117 118 if (acpi_disabled("cmbat") || 119 ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL) 120 return (ENXIO); 121 122 device_set_desc(dev, "ACPI Control Method Battery"); 123 return (0); 124 } 125 126 static int 127 acpi_cmbat_attach(device_t dev) 128 { 129 int error; 130 ACPI_HANDLE handle; 131 struct acpi_cmbat_softc *sc; 132 133 sc = device_get_softc(dev); 134 handle = acpi_get_handle(dev); 135 sc->dev = dev; 136 137 timespecclear(&sc->bst_lastupdated); 138 139 error = acpi_battery_register(dev); 140 if (error != 0) { 141 device_printf(dev, "registering battery failed\n"); 142 return (error); 143 } 144 145 /* 146 * Install a system notify handler in addition to the device notify. 147 * Toshiba notebook uses this alternate notify for its battery. 148 */ 149 AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY, 150 acpi_cmbat_notify_handler, dev); 151 152 AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev); 153 154 return (0); 155 } 156 157 static int 158 acpi_cmbat_detach(device_t dev) 159 { 160 ACPI_HANDLE handle; 161 162 handle = acpi_get_handle(dev); 163 AcpiRemoveNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler); 164 acpi_battery_remove(dev); 165 return (0); 166 } 167 168 static int 169 acpi_cmbat_resume(device_t dev) 170 { 171 172 AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev); 173 return (0); 174 } 175 176 static void 177 acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 178 { 179 struct acpi_cmbat_softc *sc; 180 device_t dev; 181 182 dev = (device_t)context; 183 sc = device_get_softc(dev); 184 185 switch (notify) { 186 case ACPI_NOTIFY_DEVICE_CHECK: 187 case ACPI_BATTERY_BST_CHANGE: 188 /* 189 * Clear the last updated time. The next call to retrieve the 190 * battery status will get the new value for us. 191 */ 192 timespecclear(&sc->bst_lastupdated); 193 break; 194 case ACPI_NOTIFY_BUS_CHECK: 195 case ACPI_BATTERY_BIF_CHANGE: 196 /* 197 * Queue a callback to get the current battery info from thread 198 * context. It's not safe to block in a notify handler. 199 */ 200 AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bif_task, dev); 201 break; 202 } 203 204 acpi_UserNotify("CMBAT", h, notify); 205 } 206 207 static int 208 acpi_cmbat_info_expired(struct timespec *lastupdated) 209 { 210 struct timespec curtime; 211 212 ACPI_SERIAL_ASSERT(cmbat); 213 214 if (lastupdated == NULL) 215 return (TRUE); 216 if (!timespecisset(lastupdated)) 217 return (TRUE); 218 219 getnanotime(&curtime); 220 timespecsub(&curtime, lastupdated); 221 return (curtime.tv_sec < 0 || 222 curtime.tv_sec > acpi_battery_get_info_expire()); 223 } 224 225 static void 226 acpi_cmbat_info_updated(struct timespec *lastupdated) 227 { 228 229 ACPI_SERIAL_ASSERT(cmbat); 230 231 if (lastupdated != NULL) 232 getnanotime(lastupdated); 233 } 234 235 static void 236 acpi_cmbat_get_bst(void *arg) 237 { 238 struct acpi_cmbat_softc *sc; 239 ACPI_STATUS as; 240 ACPI_OBJECT *res; 241 ACPI_HANDLE h; 242 ACPI_BUFFER bst_buffer; 243 device_t dev; 244 245 ACPI_SERIAL_ASSERT(cmbat); 246 247 dev = arg; 248 sc = device_get_softc(dev); 249 h = acpi_get_handle(dev); 250 bst_buffer.Pointer = NULL; 251 bst_buffer.Length = ACPI_ALLOCATE_BUFFER; 252 253 if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) 254 goto end; 255 256 as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer); 257 if (ACPI_FAILURE(as)) { 258 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 259 "error fetching current battery status -- %s\n", 260 AcpiFormatException(as)); 261 goto end; 262 } 263 264 res = (ACPI_OBJECT *)bst_buffer.Pointer; 265 if (!ACPI_PKG_VALID(res, 4)) { 266 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 267 "battery status corrupted\n"); 268 goto end; 269 } 270 271 if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0) 272 goto end; 273 if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0) 274 goto end; 275 if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0) 276 goto end; 277 if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0) 278 goto end; 279 acpi_cmbat_info_updated(&sc->bst_lastupdated); 280 281 /* XXX If all batteries are critical, perhaps we should suspend. */ 282 if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) { 283 if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) { 284 sc->flags |= ACPI_BATT_STAT_CRITICAL; 285 device_printf(dev, "critically low charge!\n"); 286 } 287 } else 288 sc->flags &= ~ACPI_BATT_STAT_CRITICAL; 289 290 end: 291 if (bst_buffer.Pointer != NULL) 292 AcpiOsFree(bst_buffer.Pointer); 293 } 294 295 /* XXX There should be a cleaner way to do this locking. */ 296 static void 297 acpi_cmbat_get_bif_task(void *arg) 298 { 299 300 ACPI_SERIAL_BEGIN(cmbat); 301 acpi_cmbat_get_bif(arg); 302 ACPI_SERIAL_END(cmbat); 303 } 304 305 static void 306 acpi_cmbat_get_bif(void *arg) 307 { 308 struct acpi_cmbat_softc *sc; 309 ACPI_STATUS as; 310 ACPI_OBJECT *res; 311 ACPI_HANDLE h; 312 ACPI_BUFFER bif_buffer; 313 device_t dev; 314 315 ACPI_SERIAL_ASSERT(cmbat); 316 317 dev = arg; 318 sc = device_get_softc(dev); 319 h = acpi_get_handle(dev); 320 bif_buffer.Pointer = NULL; 321 bif_buffer.Length = ACPI_ALLOCATE_BUFFER; 322 323 as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer); 324 if (ACPI_FAILURE(as)) { 325 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 326 "error fetching current battery info -- %s\n", 327 AcpiFormatException(as)); 328 goto end; 329 } 330 331 res = (ACPI_OBJECT *)bif_buffer.Pointer; 332 if (!ACPI_PKG_VALID(res, 13)) { 333 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 334 "battery info corrupted\n"); 335 goto end; 336 } 337 338 if (acpi_PkgInt32(res, 0, &sc->bif.units) != 0) 339 goto end; 340 if (acpi_PkgInt32(res, 1, &sc->bif.dcap) != 0) 341 goto end; 342 if (acpi_PkgInt32(res, 2, &sc->bif.lfcap) != 0) 343 goto end; 344 if (acpi_PkgInt32(res, 3, &sc->bif.btech) != 0) 345 goto end; 346 if (acpi_PkgInt32(res, 4, &sc->bif.dvol) != 0) 347 goto end; 348 if (acpi_PkgInt32(res, 5, &sc->bif.wcap) != 0) 349 goto end; 350 if (acpi_PkgInt32(res, 6, &sc->bif.lcap) != 0) 351 goto end; 352 if (acpi_PkgInt32(res, 7, &sc->bif.gra1) != 0) 353 goto end; 354 if (acpi_PkgInt32(res, 8, &sc->bif.gra2) != 0) 355 goto end; 356 if (acpi_PkgStr(res, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN) != 0) 357 goto end; 358 if (acpi_PkgStr(res, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN) != 0) 359 goto end; 360 if (acpi_PkgStr(res, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN) != 0) 361 goto end; 362 if (acpi_PkgStr(res, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0) 363 goto end; 364 365 end: 366 if (bif_buffer.Pointer != NULL) 367 AcpiOsFree(bif_buffer.Pointer); 368 } 369 370 static int 371 acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp) 372 { 373 struct acpi_cmbat_softc *sc; 374 375 sc = device_get_softc(dev); 376 377 /* 378 * Just copy the data. The only value that should change is the 379 * last-full capacity, so we only update when we get a notify that says 380 * the info has changed. Many systems apparently take a long time to 381 * process a _BIF call so we avoid it if possible. 382 */ 383 ACPI_SERIAL_BEGIN(cmbat); 384 bifp->units = sc->bif.units; 385 bifp->dcap = sc->bif.dcap; 386 bifp->lfcap = sc->bif.lfcap; 387 bifp->btech = sc->bif.btech; 388 bifp->dvol = sc->bif.dvol; 389 bifp->wcap = sc->bif.wcap; 390 bifp->lcap = sc->bif.lcap; 391 bifp->gra1 = sc->bif.gra1; 392 bifp->gra2 = sc->bif.gra2; 393 strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model)); 394 strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial)); 395 strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type)); 396 strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo)); 397 ACPI_SERIAL_END(cmbat); 398 399 return (0); 400 } 401 402 static int 403 acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp) 404 { 405 struct acpi_cmbat_softc *sc; 406 407 sc = device_get_softc(dev); 408 409 ACPI_SERIAL_BEGIN(cmbat); 410 if (acpi_BatteryIsPresent(dev)) { 411 acpi_cmbat_get_bst(dev); 412 bstp->state = sc->bst.state; 413 bstp->rate = sc->bst.rate; 414 bstp->cap = sc->bst.cap; 415 bstp->volt = sc->bst.volt; 416 } else 417 bstp->state = ACPI_BATT_STAT_NOT_PRESENT; 418 ACPI_SERIAL_END(cmbat); 419 420 return (0); 421 } 422 423 static void 424 acpi_cmbat_init_battery(void *arg) 425 { 426 struct acpi_cmbat_softc *sc; 427 int retry, valid; 428 device_t dev; 429 430 dev = (device_t)arg; 431 sc = device_get_softc(dev); 432 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 433 "battery initialization start\n"); 434 435 /* 436 * Try repeatedly to get valid data from the battery. Since the 437 * embedded controller isn't always ready just after boot, we may have 438 * to wait a while. 439 */ 440 for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) { 441 /* batteries on DOCK can be ejected w/ DOCK during retrying */ 442 if (!device_is_attached(dev)) 443 return; 444 445 if (!acpi_BatteryIsPresent(dev)) 446 continue; 447 448 /* 449 * Only query the battery if this is the first try or the specific 450 * type of info is still invalid. 451 */ 452 ACPI_SERIAL_BEGIN(cmbat); 453 if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) { 454 timespecclear(&sc->bst_lastupdated); 455 acpi_cmbat_get_bst(dev); 456 } 457 if (retry == 0 || !acpi_battery_bif_valid(&sc->bif)) 458 acpi_cmbat_get_bif(dev); 459 460 valid = acpi_battery_bst_valid(&sc->bst) && 461 acpi_battery_bif_valid(&sc->bif); 462 ACPI_SERIAL_END(cmbat); 463 464 if (valid) 465 break; 466 } 467 468 if (retry == ACPI_CMBAT_RETRY_MAX) { 469 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 470 "battery initialization failed, giving up\n"); 471 } else { 472 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 473 "battery initialization done, tried %d times\n", retry + 1); 474 } 475 } 476