45#include <dev/acpica/acpivar.h> 46#include <dev/acpica/acpiio.h> 47 48MALLOC_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 58ACPI_MODULE_NAME("BATTERY") 59 60#define ACPI_BATTERY_BST_CHANGE 0x80 61#define ACPI_BATTERY_BIF_CHANGE 0x81 62 63struct acpi_cmbat_softc { 64 device_t dev; 65 int flags; 66 67 struct acpi_bif bif; 68 struct acpi_bst bst; 69 struct timespec bif_lastupdated; 70 struct timespec bst_lastupdated; 71}; 72 73ACPI_SERIAL_DECL(cmbat, "ACPI cmbat"); 74 75static int acpi_cmbat_probe(device_t dev); 76static int acpi_cmbat_attach(device_t dev); 77static int acpi_cmbat_detach(device_t dev); 78static int acpi_cmbat_resume(device_t dev); 79static void acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, 80 void *context); 81static int acpi_cmbat_info_expired(struct timespec *lastupdated); 82static void acpi_cmbat_info_updated(struct timespec *lastupdated); 83static void acpi_cmbat_get_bst(device_t dev); 84static void acpi_cmbat_get_bif(device_t dev); 85static int acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp); 86static int acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp); 87static void acpi_cmbat_init_battery(void *arg); 88 89static 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 103static driver_t acpi_cmbat_driver = { 104 "battery", 105 acpi_cmbat_methods, 106 sizeof(struct acpi_cmbat_softc), 107}; 108 109static devclass_t acpi_cmbat_devclass; 110DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0); 111MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1); 112 113static int 114acpi_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 126static int 127acpi_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->bif_lastupdated); 138 timespecclear(&sc->bst_lastupdated); 139 140 error = acpi_battery_register(dev); 141 if (error != 0) { 142 device_printf(dev, "registering battery failed\n"); 143 return (error); 144 } 145 146 /* 147 * Install a system notify handler in addition to the device notify. 148 * Toshiba notebook uses this alternate notify for its battery. 149 */ 150 AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY, 151 acpi_cmbat_notify_handler, dev); 152 153 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); 154 155 return (0); 156} 157 158static int 159acpi_cmbat_detach(device_t dev) 160{ 161 162 acpi_battery_remove(dev); 163 return (0); 164} 165 166static int 167acpi_cmbat_resume(device_t dev) 168{ 169 170 AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cmbat_init_battery, dev); 171 return (0); 172} 173 174static void 175acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 176{ 177 struct acpi_cmbat_softc *sc; 178 device_t dev; 179 180 dev = (device_t)context; 181 sc = device_get_softc(dev); 182 183 /* 184 * Clear the appropriate last updated time. The next call to retrieve 185 * the battery status will get the new value for us. We don't need to 186 * acquire a lock since we are only clearing the time stamp and since 187 * calling _BST/_BIF can trigger a notify, we could deadlock also. 188 */ 189 switch (notify) { 190 case ACPI_NOTIFY_DEVICE_CHECK: 191 case ACPI_BATTERY_BST_CHANGE: 192 timespecclear(&sc->bst_lastupdated); 193 break; 194 case ACPI_NOTIFY_BUS_CHECK: 195 case ACPI_BATTERY_BIF_CHANGE: 196 timespecclear(&sc->bif_lastupdated); 197 break; 198 } 199 200 acpi_UserNotify("CMBAT", h, notify); 201} 202 203static int 204acpi_cmbat_info_expired(struct timespec *lastupdated) 205{ 206 struct timespec curtime; 207 208 ACPI_SERIAL_ASSERT(cmbat); 209 210 if (lastupdated == NULL) 211 return (TRUE); 212 if (!timespecisset(lastupdated)) 213 return (TRUE); 214 215 getnanotime(&curtime); 216 timespecsub(&curtime, lastupdated); 217 return (curtime.tv_sec < 0 || 218 curtime.tv_sec > acpi_battery_get_info_expire()); 219} 220 221static void 222acpi_cmbat_info_updated(struct timespec *lastupdated) 223{ 224 225 ACPI_SERIAL_ASSERT(cmbat); 226 227 if (lastupdated != NULL) 228 getnanotime(lastupdated); 229} 230 231static void 232acpi_cmbat_get_bst(device_t dev) 233{ 234 struct acpi_cmbat_softc *sc; 235 ACPI_STATUS as; 236 ACPI_OBJECT *res; 237 ACPI_HANDLE h; 238 ACPI_BUFFER bst_buffer; 239 240 ACPI_SERIAL_ASSERT(cmbat); 241 242 sc = device_get_softc(dev); 243 h = acpi_get_handle(dev); 244 bst_buffer.Pointer = NULL; 245 bst_buffer.Length = ACPI_ALLOCATE_BUFFER; 246 247 if (!acpi_cmbat_info_expired(&sc->bst_lastupdated)) 248 goto end; 249 250 as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer); 251 if (ACPI_FAILURE(as)) { 252 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 253 "error fetching current battery status -- %s\n", 254 AcpiFormatException(as)); 255 goto end; 256 } 257 258 res = (ACPI_OBJECT *)bst_buffer.Pointer; 259 if (!ACPI_PKG_VALID(res, 4)) { 260 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 261 "battery status corrupted\n"); 262 goto end; 263 } 264 265 if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0) 266 goto end; 267 if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0) 268 goto end; 269 if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0) 270 goto end; 271 if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0) 272 goto end; 273 acpi_cmbat_info_updated(&sc->bst_lastupdated); 274 275 /* XXX If all batteries are critical, perhaps we should suspend. */ 276 if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) { 277 if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) { 278 sc->flags |= ACPI_BATT_STAT_CRITICAL; 279 device_printf(dev, "critically low charge!\n"); 280 } 281 } else 282 sc->flags &= ~ACPI_BATT_STAT_CRITICAL; 283 284end: 285 if (bst_buffer.Pointer != NULL) 286 AcpiOsFree(bst_buffer.Pointer); 287} 288 289static void 290acpi_cmbat_get_bif(device_t dev) 291{ 292 struct acpi_cmbat_softc *sc; 293 ACPI_STATUS as; 294 ACPI_OBJECT *res; 295 ACPI_HANDLE h; 296 ACPI_BUFFER bif_buffer; 297 298 ACPI_SERIAL_ASSERT(cmbat); 299 300 sc = device_get_softc(dev); 301 h = acpi_get_handle(dev); 302 bif_buffer.Pointer = NULL; 303 bif_buffer.Length = ACPI_ALLOCATE_BUFFER; 304 305 if (!acpi_cmbat_info_expired(&sc->bif_lastupdated)) 306 goto end; 307 308 as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer); 309 if (ACPI_FAILURE(as)) { 310 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 311 "error fetching current battery info -- %s\n", 312 AcpiFormatException(as)); 313 goto end; 314 } 315 316 res = (ACPI_OBJECT *)bif_buffer.Pointer; 317 if (!ACPI_PKG_VALID(res, 13)) { 318 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 319 "battery info corrupted\n"); 320 goto end; 321 } 322 323 if (acpi_PkgInt32(res, 0, &sc->bif.units) != 0) 324 goto end; 325 if (acpi_PkgInt32(res, 1, &sc->bif.dcap) != 0) 326 goto end; 327 if (acpi_PkgInt32(res, 2, &sc->bif.lfcap) != 0) 328 goto end; 329 if (acpi_PkgInt32(res, 3, &sc->bif.btech) != 0) 330 goto end; 331 if (acpi_PkgInt32(res, 4, &sc->bif.dvol) != 0) 332 goto end; 333 if (acpi_PkgInt32(res, 5, &sc->bif.wcap) != 0) 334 goto end; 335 if (acpi_PkgInt32(res, 6, &sc->bif.lcap) != 0) 336 goto end; 337 if (acpi_PkgInt32(res, 7, &sc->bif.gra1) != 0) 338 goto end; 339 if (acpi_PkgInt32(res, 8, &sc->bif.gra2) != 0) 340 goto end; 341 if (acpi_PkgStr(res, 9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN) != 0) 342 goto end; 343 if (acpi_PkgStr(res, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN) != 0) 344 goto end; 345 if (acpi_PkgStr(res, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN) != 0) 346 goto end; 347 if (acpi_PkgStr(res, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0) 348 goto end; 349 acpi_cmbat_info_updated(&sc->bif_lastupdated); 350 351end: 352 if (bif_buffer.Pointer != NULL) 353 AcpiOsFree(bif_buffer.Pointer); 354} 355 356static int 357acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp) 358{ 359 struct acpi_cmbat_softc *sc; 360 361 sc = device_get_softc(dev); 362 363 ACPI_SERIAL_BEGIN(cmbat); 364 acpi_cmbat_get_bif(dev); 365 bifp->units = sc->bif.units; 366 bifp->dcap = sc->bif.dcap; 367 bifp->lfcap = sc->bif.lfcap; 368 bifp->btech = sc->bif.btech; 369 bifp->dvol = sc->bif.dvol; 370 bifp->wcap = sc->bif.wcap; 371 bifp->lcap = sc->bif.lcap; 372 bifp->gra1 = sc->bif.gra1; 373 bifp->gra2 = sc->bif.gra2; 374 strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model)); 375 strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial)); 376 strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type)); 377 strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo)); 378 ACPI_SERIAL_END(cmbat); 379 380 return (0); 381} 382 383static int 384acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp) 385{ 386 struct acpi_cmbat_softc *sc; 387 388 sc = device_get_softc(dev); 389 390 ACPI_SERIAL_BEGIN(cmbat); 391 if (acpi_BatteryIsPresent(dev)) { 392 acpi_cmbat_get_bst(dev); 393 bstp->state = sc->bst.state; 394 bstp->rate = sc->bst.rate; 395 bstp->cap = sc->bst.cap; 396 bstp->volt = sc->bst.volt; 397 } else 398 bstp->state = ACPI_BATT_STAT_NOT_PRESENT; 399 ACPI_SERIAL_END(cmbat); 400 401 return (0); 402} 403 404static void 405acpi_cmbat_init_battery(void *arg) 406{ 407 struct acpi_cmbat_softc *sc; 408 int retry, valid; 409 device_t dev; 410 411 dev = (device_t)arg; 412 sc = device_get_softc(dev); 413 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 414 "battery initialization start\n"); 415 416 /* 417 * Try repeatedly to get valid data from the battery. Since the 418 * embedded controller isn't always ready just after boot, we may have 419 * to wait a while. 420 */ 421 for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) { 422 if (!acpi_BatteryIsPresent(dev)) 423 continue; 424 425 ACPI_SERIAL_BEGIN(cmbat); 426 timespecclear(&sc->bst_lastupdated); 427 timespecclear(&sc->bif_lastupdated); 428 acpi_cmbat_get_bst(dev); 429 acpi_cmbat_get_bif(dev); 430 valid = acpi_battery_bst_valid(&sc->bst) && 431 acpi_battery_bif_valid(&sc->bif); 432 ACPI_SERIAL_END(cmbat); 433 434 if (valid) 435 break; 436 } 437 438 if (retry == ACPI_CMBAT_RETRY_MAX) { 439 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 440 "battery initialization failed, giving up\n"); 441 } else { 442 ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), 443 "battery initialization done, tried %d times\n", retry + 1); 444 } 445}
|