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