The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/acpica/acpi_smbat.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 }

Cache object: 6a80913c58363554766f7059cdd7dac9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.