LCOV - code coverage report
Current view: top level - dev/acpi - acpisbs.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 154 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 7 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* $OpenBSD: acpisbs.c,v 1.7 2018/06/29 17:39:18 kettenis Exp $ */
       2             : /*
       3             :  * Smart Battery subsystem device driver
       4             :  * ACPI 5.0 spec section 10
       5             :  *
       6             :  * Copyright (c) 2016-2017 joshua stein <jcs@openbsd.org>
       7             :  *
       8             :  * Permission to use, copy, modify, and distribute this software for any
       9             :  * purpose with or without fee is hereby granted, provided that the above
      10             :  * copyright notice and this permission notice appear in all copies.
      11             :  *
      12             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      13             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      14             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      15             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      16             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      17             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      18             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      19             :  */
      20             : 
      21             : /*
      22             :  * TODO: support multiple batteries based on _SBS, make sc_battery an array and
      23             :  * poll each battery independently
      24             :  */
      25             : 
      26             : #include <sys/param.h>
      27             : #include <sys/systm.h>
      28             : #include <sys/device.h>
      29             : #include <sys/malloc.h>
      30             : 
      31             : #include <dev/acpi/acpireg.h>
      32             : #include <dev/acpi/acpivar.h>
      33             : #include <dev/acpi/acpidev.h>
      34             : #include <dev/acpi/amltypes.h>
      35             : #include <dev/acpi/dsdt.h>
      36             : 
      37             : #include <sys/sensors.h>
      38             : 
      39             : /* #define ACPISBS_DEBUG */
      40             : 
      41             : #ifdef ACPISBS_DEBUG
      42             : #define DPRINTF(x) printf x
      43             : #else
      44             : #define DPRINTF(x)
      45             : #endif
      46             : 
      47             : /* how often (in seconds) to re-poll data */
      48             : #define ACPISBS_POLL_FREQ       30
      49             : 
      50             : /* number of polls for reading data */
      51             : #define SMBUS_TIMEOUT           50
      52             : 
      53             : #define CHECK(kind, cmd, val, senst, sens) { \
      54             :         SMBUS_READ_##kind, SMBATT_CMD_##cmd, \
      55             :         offsetof(struct acpisbs_battery, val), \
      56             :         (SMBUS_READ_##kind == SMBUS_READ_BLOCK ? SMBUS_DATA_SIZE : 2), \
      57             :         #val, senst, sens }
      58             : 
      59             : struct acpisbs_battery_check {
      60             :         uint8_t mode;
      61             :         uint8_t command;
      62             :         size_t  offset;
      63             :         int     len;
      64             :         char    *name;
      65             :         int     sensor_type;
      66             :         char    *sensor_desc;
      67             : } acpisbs_battery_checks[] = {
      68             :         /* mode must be checked first */
      69             :         CHECK(WORD, BATTERY_MODE, mode, -1,
      70             :             "mode flags"),
      71             :         CHECK(WORD, TEMPERATURE, temperature, SENSOR_TEMP,
      72             :             "internal temperature"),
      73             :         CHECK(WORD, VOLTAGE, voltage, SENSOR_VOLTS_DC,
      74             :             "voltage"),
      75             :         CHECK(WORD, CURRENT, current, SENSOR_AMPS,
      76             :             "current being supplied"),
      77             :         CHECK(WORD, AVERAGE_CURRENT, avg_current, SENSOR_AMPS,
      78             :             "average current supplied"),
      79             :         CHECK(WORD, RELATIVE_STATE_OF_CHARGE, rel_charge, SENSOR_PERCENT,
      80             :             "remaining capacity"),
      81             :         CHECK(WORD, ABSOLUTE_STATE_OF_CHARGE, abs_charge, SENSOR_PERCENT,
      82             :             "remaining of design capacity"),
      83             :         CHECK(WORD, REMAINING_CAPACITY, capacity, SENSOR_AMPHOUR,
      84             :             "remaining capacity"),
      85             :         CHECK(WORD, FULL_CHARGE_CAPACITY, full_capacity, SENSOR_AMPHOUR,
      86             :             "capacity when fully charged"),
      87             :         CHECK(WORD, RUN_TIME_TO_EMPTY, run_time, SENSOR_INTEGER,
      88             :             "remaining run time minutes"),
      89             :         CHECK(WORD, AVERAGE_TIME_TO_EMPTY, avg_empty_time, SENSOR_INTEGER,
      90             :             "avg remaining minutes"),
      91             :         CHECK(WORD, AVERAGE_TIME_TO_FULL, avg_full_time, SENSOR_INTEGER,
      92             :             "avg minutes until full charge"),
      93             :         CHECK(WORD, CHARGING_CURRENT, charge_current, SENSOR_AMPS,
      94             :             "desired charging rate"),
      95             :         CHECK(WORD, CHARGING_VOLTAGE, charge_voltage, SENSOR_VOLTS_DC,
      96             :             "desired charging voltage"),
      97             :         CHECK(WORD, BATTERY_STATUS, status, -1,
      98             :             "status"),
      99             :         CHECK(WORD, CYCLE_COUNT, cycle_count, SENSOR_INTEGER,
     100             :             "charge and discharge cycles"),
     101             :         CHECK(WORD, DESIGN_CAPACITY, design_capacity, SENSOR_AMPHOUR,
     102             :             "capacity of new battery"),
     103             :         CHECK(WORD, DESIGN_VOLTAGE, design_voltage, SENSOR_VOLTS_DC,
     104             :             "voltage of new battery"),
     105             :         CHECK(WORD, SERIAL_NUMBER, serial, -1,
     106             :             "serial number"),
     107             : 
     108             :         CHECK(BLOCK, MANUFACTURER_NAME, manufacturer, -1,
     109             :             "manufacturer name"),
     110             :         CHECK(BLOCK, DEVICE_NAME, device_name, -1,
     111             :             "battery model number"),
     112             :         CHECK(BLOCK, DEVICE_CHEMISTRY, device_chemistry, -1,
     113             :             "battery chemistry"),
     114             : #if 0
     115             :         CHECK(WORD, SPECIFICATION_INFO, spec, -1,
     116             :             NULL),
     117             :         CHECK(WORD, MANUFACTURE_DATE, manufacture_date, -1,
     118             :             "date battery was manufactured"),
     119             :         CHECK(BLOCK, MANUFACTURER_DATA, oem_data, -1,
     120             :             "manufacturer-specific data"),
     121             : #endif
     122             : };
     123             : 
     124             : extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
     125             : extern void acpiec_write(struct acpiec_softc *, uint8_t, int, uint8_t *);
     126             : 
     127             : int     acpisbs_match(struct device *, void *, void *);
     128             : void    acpisbs_attach(struct device *, struct device *, void *);
     129             : void    acpisbs_setup_sensors(struct acpisbs_softc *);
     130             : void    acpisbs_refresh_sensors(struct acpisbs_softc *);
     131             : void    acpisbs_read(struct acpisbs_softc *);
     132             : int     acpisbs_notify(struct aml_node *, int, void *);
     133             : 
     134             : int     acpi_smbus_read(struct acpisbs_softc *, uint8_t, uint8_t, int, void *);
     135             : 
     136             : const struct cfattach acpisbs_ca = {
     137             :         sizeof(struct acpisbs_softc),
     138             :         acpisbs_match,
     139             :         acpisbs_attach,
     140             : };
     141             : 
     142             : struct cfdriver acpisbs_cd = {
     143             :         NULL, "acpisbs", DV_DULL
     144             : };
     145             : 
     146             : const char *acpisbs_hids[] = {
     147             :         ACPI_DEV_SBS,
     148             :         NULL
     149             : };
     150             : 
     151             : int
     152           0 : acpisbs_match(struct device *parent, void *match, void *aux)
     153             : {
     154           0 :         struct acpi_attach_args *aa = aux;
     155           0 :         struct cfdata *cf = match;
     156             : 
     157           0 :         return (acpi_matchhids(aa, acpisbs_hids, cf->cf_driver->cd_name));
     158             : }
     159             : 
     160             : void
     161           0 : acpisbs_attach(struct device *parent, struct device *self, void *aux)
     162             : {
     163           0 :         struct acpisbs_softc *sc = (struct acpisbs_softc *)self;
     164           0 :         struct acpi_attach_args *aa = aux;
     165           0 :         int64_t sbs, val;
     166             : 
     167           0 :         sc->sc_acpi = (struct acpi_softc *)parent;
     168           0 :         sc->sc_devnode = aa->aaa_node;
     169           0 :         sc->sc_batteries_present = 0;
     170             : 
     171           0 :         memset(&sc->sc_battery, 0, sizeof(sc->sc_battery));
     172             : 
     173           0 :         getmicrotime(&sc->sc_lastpoll);
     174             : 
     175           0 :         if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_SBS", 0, NULL, &sbs))
     176           0 :                 return;
     177             : 
     178             :         /*
     179             :          * The parent node of the device block containing the _HID must also
     180             :          * have an _EC node, which contains the base address and query value.
     181             :          */
     182           0 :         if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode->parent, "_EC", 0,
     183             :             NULL, &val))
     184           0 :                 return;
     185           0 :         sc->sc_ec_base = (val >> 8) & 0xff;
     186             : 
     187           0 :         if (!sc->sc_acpi->sc_ec)
     188           0 :                 return;
     189           0 :         sc->sc_ec = sc->sc_acpi->sc_ec;
     190             : 
     191           0 :         printf(": %s", sc->sc_devnode->name);
     192             : 
     193           0 :         if (sbs > 0)
     194           0 :                 acpisbs_read(sc);
     195             : 
     196           0 :         if (sc->sc_batteries_present) {
     197           0 :                 if (sc->sc_battery.device_name[0])
     198           0 :                         printf(" model \"%s\"", sc->sc_battery.device_name);
     199           0 :                 if (sc->sc_battery.serial)
     200           0 :                         printf(" serial %d", sc->sc_battery.serial);
     201           0 :                 if (sc->sc_battery.device_chemistry[0])
     202           0 :                         printf(" type %s", sc->sc_battery.device_chemistry);
     203           0 :                 if (sc->sc_battery.manufacturer[0])
     204           0 :                         printf(" oem \"%s\"", sc->sc_battery.manufacturer);
     205             :         }
     206             : 
     207           0 :         printf("\n");
     208             : 
     209           0 :         acpisbs_setup_sensors(sc);
     210           0 :         acpisbs_refresh_sensors(sc);
     211             : 
     212             :         /*
     213             :          * Request notification of SCI events on the subsystem itself, but also
     214             :          * periodically poll as a fallback in case those events never arrive.
     215             :          */
     216           0 :         aml_register_notify(sc->sc_devnode->parent, aa->aaa_dev,
     217           0 :             acpisbs_notify, sc, ACPIDEV_POLL);
     218             : 
     219           0 :         sc->sc_acpi->sc_havesbs = 1;
     220           0 : }
     221             : 
     222             : void
     223           0 : acpisbs_read(struct acpisbs_softc *sc)
     224             : {
     225             :         int i;
     226             : 
     227           0 :         for (i = 0; i < nitems(acpisbs_battery_checks); i++) {
     228           0 :                 struct acpisbs_battery_check check = acpisbs_battery_checks[i];
     229           0 :                 void *p = (void *)&sc->sc_battery + check.offset;
     230             : 
     231           0 :                 acpi_smbus_read(sc, check.mode, check.command, check.len, p);
     232             : 
     233             :                 if (check.mode == SMBUS_READ_BLOCK)
     234             :                         DPRINTF(("%s: %s: %s\n", sc->sc_dev.dv_xname,
     235             :                             check.name, (char *)p));
     236             :                 else
     237             :                         DPRINTF(("%s: %s: %u\n", sc->sc_dev.dv_xname,
     238             :                             check.name, *(uint16_t *)p));
     239             : 
     240           0 :                 if (check.command == SMBATT_CMD_BATTERY_MODE) {
     241           0 :                         uint16_t *ival = (uint16_t *)p;
     242           0 :                         if (*ival == 0) {
     243             :                                 /* battery not present, skip further checks */
     244           0 :                                 sc->sc_batteries_present = 0;
     245           0 :                                 break;
     246             :                         }
     247             : 
     248           0 :                         sc->sc_batteries_present = 1;
     249             : 
     250           0 :                         if (*ival & SMBATT_BM_CAPACITY_MODE)
     251           0 :                                 sc->sc_battery.units = ACPISBS_UNITS_MW;
     252             :                         else
     253           0 :                                 sc->sc_battery.units = ACPISBS_UNITS_MA;
     254           0 :                 }
     255           0 :         }
     256           0 : }
     257             : 
     258             : void
     259           0 : acpisbs_setup_sensors(struct acpisbs_softc *sc)
     260             : {
     261             :         int i;
     262             : 
     263           0 :         memset(&sc->sc_sensordev, 0, sizeof(sc->sc_sensordev));
     264           0 :         strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
     265             :             sizeof(sc->sc_sensordev.xname));
     266             : 
     267           0 :         sc->sc_sensors = mallocarray(sizeof(struct ksensor),
     268             :             nitems(acpisbs_battery_checks), M_DEVBUF, M_WAITOK | M_ZERO);
     269             : 
     270           0 :         for (i = 0; i < nitems(acpisbs_battery_checks); i++) {
     271           0 :                 struct acpisbs_battery_check check = acpisbs_battery_checks[i];
     272             : 
     273           0 :                 if (check.sensor_type < 0)
     274           0 :                         continue;
     275             : 
     276           0 :                 strlcpy(sc->sc_sensors[i].desc, check.sensor_desc,
     277             :                     sizeof(sc->sc_sensors[i].desc));
     278             : 
     279           0 :                 if (check.sensor_type == SENSOR_AMPHOUR &&
     280           0 :                     sc->sc_battery.units == ACPISBS_UNITS_MW)
     281             :                         /* translate to watt-hours */
     282           0 :                         sc->sc_sensors[i].type = SENSOR_WATTHOUR;
     283             :                 else
     284           0 :                         sc->sc_sensors[i].type = check.sensor_type;
     285             : 
     286           0 :                 sc->sc_sensors[i].value = 0;
     287           0 :                 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
     288           0 :         }
     289             : 
     290           0 :         sensordev_install(&sc->sc_sensordev);
     291           0 : }
     292             : 
     293             : void
     294           0 : acpisbs_refresh_sensors(struct acpisbs_softc *sc)
     295             : {
     296             :         int i;
     297             : 
     298           0 :         for (i = 0; i < nitems(acpisbs_battery_checks); i++) {
     299           0 :                 struct acpisbs_battery_check check = acpisbs_battery_checks[i];
     300           0 :                 void *p = (void *)&sc->sc_battery + check.offset;
     301           0 :                 uint16_t *ival = (uint16_t *)p;
     302             : 
     303           0 :                 if (check.sensor_type < 0)
     304           0 :                         continue;
     305             : 
     306           0 :                 if (sc->sc_batteries_present) {
     307           0 :                         sc->sc_sensors[i].flags = 0;
     308           0 :                         sc->sc_sensors[i].status = SENSOR_S_OK;
     309             : 
     310           0 :                         switch (check.sensor_type) {
     311             :                         case SENSOR_AMPS:
     312           0 :                                 sc->sc_sensors[i].value = *ival * 100;
     313           0 :                                 break;
     314             : 
     315             :                         case SENSOR_AMPHOUR:
     316             :                         case SENSOR_WATTHOUR:
     317           0 :                                 sc->sc_sensors[i].value = *ival * 10000;
     318           0 :                                 break;
     319             : 
     320             :                         case SENSOR_PERCENT:
     321           0 :                                 sc->sc_sensors[i].value = *ival * 1000;
     322           0 :                                 break;
     323             : 
     324             : #if 0
     325             :                         case SENSOR_STRING:
     326             :                                 strlcpy(sc->sc_sensors[i].string, (char *)p,
     327             :                                     sizeof(sc->sc_sensors[i].string));
     328             :                                 break;
     329             : #endif
     330             :                         case SENSOR_TEMP:
     331             :                                 /* .1 degK */
     332           0 :                                 sc->sc_sensors[i].value = (*ival * 10000) +
     333             :                                     273150000;
     334           0 :                                 break;
     335             : 
     336             :                         case SENSOR_VOLTS_DC:
     337           0 :                                 sc->sc_sensors[i].value = *ival * 1000;
     338           0 :                                 break;
     339             : 
     340             :                         default:
     341           0 :                                 if (*ival == ACPISBS_VALUE_UNKNOWN) {
     342           0 :                                         sc->sc_sensors[i].value = 0;
     343           0 :                                         sc->sc_sensors[i].status =
     344             :                                             SENSOR_S_UNKNOWN;
     345           0 :                                         sc->sc_sensors[i].flags =
     346             :                                             SENSOR_FUNKNOWN;
     347           0 :                                 } else
     348           0 :                                         sc->sc_sensors[i].value = *ival;
     349             :                         }
     350             :                 } else {
     351           0 :                         sc->sc_sensors[i].value = 0;
     352           0 :                         sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
     353           0 :                         sc->sc_sensors[i].flags = SENSOR_FUNKNOWN;
     354             :                 }
     355           0 :         }
     356           0 : }
     357             : 
     358             : int
     359           0 : acpisbs_notify(struct aml_node *node, int notify_type, void *arg)
     360             : {
     361           0 :         struct acpisbs_softc *sc = arg;
     362           0 :         struct timeval tv;
     363             : 
     364             :         DPRINTF(("%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, notify_type));
     365             : 
     366           0 :         getmicrotime(&tv);
     367             : 
     368           0 :         switch (notify_type) {
     369             :         case 0x00:
     370             :                 /* fallback poll */
     371             :         case 0x80:
     372             :                 /*
     373             :                  * EC SCI will come for every data point, so only run once in a
     374             :                  * while
     375             :                  */
     376           0 :                 if (tv.tv_sec - sc->sc_lastpoll.tv_sec > ACPISBS_POLL_FREQ) {
     377           0 :                         acpisbs_read(sc);
     378           0 :                         acpisbs_refresh_sensors(sc);
     379           0 :                         getmicrotime(&sc->sc_lastpoll);
     380           0 :                 }
     381             :                 break;
     382             :         default:
     383             :                 break;
     384             :         }
     385             : 
     386           0 :         return 0;
     387           0 : }
     388             : 
     389             : int
     390           0 : acpi_smbus_read(struct acpisbs_softc *sc, uint8_t type, uint8_t cmd, int len,
     391             :     void *buf)
     392             : {
     393             :         int j;
     394           0 :         uint8_t addr = SMBATT_ADDRESS;
     395           0 :         uint8_t val;
     396             : 
     397           0 :         acpiec_write(sc->sc_ec, sc->sc_ec_base + SMBUS_ADDR, 1, &addr);
     398           0 :         acpiec_write(sc->sc_ec, sc->sc_ec_base + SMBUS_CMD, 1, &cmd);
     399           0 :         acpiec_write(sc->sc_ec, sc->sc_ec_base + SMBUS_PRTCL, 1, &type);
     400             : 
     401           0 :         for (j = SMBUS_TIMEOUT; j > 0; j--) {
     402           0 :                 acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_PRTCL, 1, &val);
     403           0 :                 if (val == 0)
     404             :                         break;
     405             :         }
     406           0 :         if (j == 0) {
     407           0 :                 printf("%s: %s: timeout reading 0x%x\n", sc->sc_dev.dv_xname,
     408           0 :                     __func__, addr);
     409           0 :                 return 1;
     410             :         }
     411             : 
     412           0 :         acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_STS, 1, &val);
     413           0 :         if (val & SMBUS_STS_MASK) {
     414           0 :                 printf("%s: %s: error reading status: 0x%x\n",
     415           0 :                     sc->sc_dev.dv_xname, __func__, addr);
     416           0 :                 return 1;
     417             :         }
     418             : 
     419           0 :         switch (type) {
     420             :         case SMBUS_READ_WORD: {
     421           0 :                 uint8_t word[2];
     422           0 :                 acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_DATA, 2,
     423             :                     (uint8_t *)&word);
     424             : 
     425           0 :                 *(uint16_t *)buf = (word[1] << 8) | word[0];
     426             : 
     427             :                 break;
     428           0 :         }
     429             :         case SMBUS_READ_BLOCK:
     430           0 :                 bzero(buf, len);
     431             : 
     432             :                 /* find number of bytes to read */
     433           0 :                 acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_BCNT, 1, &val);
     434           0 :                 val &= 0x1f;
     435           0 :                 if (len > val)
     436           0 :                         len = val;
     437             : 
     438           0 :                 for (j = 0; j < len; j++) {
     439           0 :                         acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_DATA + j,
     440             :                             1, &val);
     441           0 :                         ((char *)buf)[j] = val;
     442             :                 }
     443             :                 break;
     444             :         default:
     445           0 :                 printf("%s: %s: unknown mode 0x%x\n", sc->sc_dev.dv_xname,
     446             :                     __func__, type);
     447           0 :                 return 1;
     448             :         }
     449             : 
     450           0 :         return 0;
     451           0 : }

Generated by: LCOV version 1.13