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

Cache object: facaaa56729aead3a214364b47df66bf


[ 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.