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