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

Cache object: 7e36b700f4567bfcbd03089205902be8


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