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

          Line data    Source code
       1             : /*      $OpenBSD: ccpmic.c,v 1.2 2018/05/21 15:00:25 kettenis Exp $     */
       2             : /*
       3             :  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
       4             :  *
       5             :  * Permission to use, copy, modify, and distribute this software for any
       6             :  * purpose with or without fee is hereby granted, provided that the above
       7             :  * copyright notice and this permission notice appear in all copies.
       8             :  *
       9             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      10             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      11             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      12             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      13             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      14             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      15             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      16             :  */
      17             : 
      18             : #include <sys/param.h>
      19             : #include <sys/systm.h>
      20             : #include <sys/device.h>
      21             : #include <sys/kernel.h>
      22             : #include <sys/malloc.h>
      23             : 
      24             : #include <dev/acpi/acpireg.h>
      25             : #include <dev/acpi/acpivar.h>
      26             : #include <dev/acpi/acpidev.h>
      27             : #include <dev/acpi/amltypes.h>
      28             : #include <dev/acpi/dsdt.h>
      29             : 
      30             : #include <dev/i2c/i2cvar.h>
      31             : 
      32             : #define CCPMIC_GPIO0P0CTLO              0x2b
      33             : #define CCPMIC_GPIO0P0CTLI              0x33
      34             : #define CCPMIC_GPIO1P0CTLO              0x3b
      35             : #define CCPMIC_GPIO1P0CTLI              0x43
      36             : #define CCPMIC_GPIOPANELCTL             0x52
      37             : #define  CCPMIC_GPIOCTLO_RVAL_2KUP      (1 << 1)
      38             : #define  CCPMIC_GPIOCTLO_DRV_REN        (1 << 3)
      39             : #define  CCPMIC_GPIOCTLO_DIR_OUT        (1 << 5)
      40             : #define  CCPMIC_GPIOCTLI_VALUE          (1 << 0)
      41             : 
      42             : #define CCPMIC_GPIOCTLO_INPUT \
      43             :     (CCPMIC_GPIOCTLO_DRV_REN | CCPMIC_GPIOCTLO_RVAL_2KUP)
      44             : #define CCPMIC_GPIOCTLO_OUTPUT \
      45             :     (CCPMIC_GPIOCTLO_INPUT | CCPMIC_GPIOCTLO_DIR_OUT)
      46             : 
      47             : #define CCPMIC_V1P8SX                   0x5d
      48             : #define CCPMIC_V1P2SX                   0x61
      49             : #define CCPMIC_V2P85SX                  0x66
      50             : #define  CCPMIC_PWR_ON                  (1 << 0)
      51             : #define  CCPMIC_PWR_SEL                 (1 << 1)
      52             : #define CCPMIC_SYS2_THRM_RSLT_H         0x78
      53             : #define CCPMIC_SYS2_THRM_RSLT_L         0x79
      54             : 
      55             : #define CCPMIC_REGIONSPACE_THERMAL      0x8c
      56             : #define CCPMIC_REGIONSPACE_POWER        0x8d
      57             : 
      58             : #define CCPMIC_NPINS                    16
      59             : 
      60             : struct acpi_lpat {
      61             :         int32_t temp;
      62             :         int32_t raw;
      63             : };
      64             : 
      65             : struct ccpmic_softc {
      66             :         struct device   sc_dev;
      67             :         struct acpi_softc *sc_acpi;
      68             :         struct aml_node *sc_node;
      69             :         i2c_tag_t       sc_tag;
      70             :         i2c_addr_t      sc_addr;
      71             : 
      72             :         struct acpi_lpat *sc_lpat;
      73             :         size_t          sc_lpat_len;
      74             : 
      75             :         struct acpi_gpio sc_gpio;
      76             : };
      77             : 
      78             : int     ccpmic_match(struct device *, void *, void *);
      79             : void    ccpmic_attach(struct device *, struct device *, void *);
      80             : 
      81             : struct cfattach ccpmic_ca = {
      82             :         sizeof(struct ccpmic_softc), ccpmic_match, ccpmic_attach
      83             : };
      84             : 
      85             : struct cfdriver ccpmic_cd = {
      86             :         NULL, "ccpmic", DV_DULL
      87             : };
      88             : 
      89             : uint8_t ccpmic_read_1(struct ccpmic_softc *, uint8_t, int);
      90             : void    ccpmic_write_1(struct ccpmic_softc *, uint8_t, uint8_t, int);
      91             : void    ccpmic_get_lpat(struct ccpmic_softc *);
      92             : int32_t ccpmic_raw_to_temp(struct ccpmic_softc *, int32_t);
      93             : int     ccpmic_thermal_opreg_handler(void *, int, uint64_t, int, uint64_t *);
      94             : int     ccpmic_power_opreg_handler(void *, int, uint64_t, int, uint64_t *);
      95             : int     ccpmic_read_pin(void *, int);
      96             : void    ccpmic_write_pin(void *, int, int);
      97             : 
      98             : int
      99           0 : ccpmic_match(struct device *parent, void *match, void *aux)
     100             : {
     101           0 :         struct i2c_attach_args *ia = aux;
     102             : 
     103           0 :         return (strcmp(ia->ia_name, "INT33FD") == 0);
     104             : }
     105             : 
     106             : void
     107           0 : ccpmic_attach(struct device *parent, struct device *self, void *aux)
     108             : {
     109           0 :         struct ccpmic_softc *sc = (struct ccpmic_softc *)self;
     110           0 :         struct i2c_attach_args *ia = aux;
     111             : 
     112           0 :         sc->sc_tag = ia->ia_tag;
     113           0 :         sc->sc_addr = ia->ia_addr;
     114           0 :         sc->sc_acpi = acpi_softc;
     115           0 :         sc->sc_node = ia->ia_cookie;
     116             : 
     117           0 :         printf("\n");
     118             : 
     119           0 :         ccpmic_get_lpat(sc);
     120           0 :         if (sc->sc_lpat == NULL)
     121           0 :                 return;
     122             : 
     123           0 :         sc->sc_gpio.cookie = sc;
     124           0 :         sc->sc_gpio.read_pin = ccpmic_read_pin;
     125           0 :         sc->sc_gpio.write_pin = ccpmic_write_pin;
     126           0 :         sc->sc_node->gpio = &sc->sc_gpio;
     127           0 :         acpi_register_gpio(sc->sc_acpi, sc->sc_node);
     128             : 
     129             :         /* Register OEM defined address space. */
     130           0 :         aml_register_regionspace(sc->sc_node, CCPMIC_REGIONSPACE_THERMAL,
     131             :             sc, ccpmic_thermal_opreg_handler);
     132           0 :         aml_register_regionspace(sc->sc_node, CCPMIC_REGIONSPACE_POWER,
     133             :             sc, ccpmic_power_opreg_handler);
     134           0 : }
     135             : 
     136             : uint8_t
     137           0 : ccpmic_read_1(struct ccpmic_softc *sc, uint8_t reg, int flags)
     138             : {
     139           0 :         uint8_t val;
     140             :         int error;
     141             : 
     142           0 :         iic_acquire_bus(sc->sc_tag, flags);
     143           0 :         error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
     144             :             &reg, sizeof(reg), &val, sizeof(val), flags);
     145           0 :         iic_release_bus(sc->sc_tag, flags);
     146             : 
     147           0 :         if (error) {
     148           0 :                 printf("%s: can't read register 0x%02x\n",
     149           0 :                     sc->sc_dev.dv_xname, reg);
     150           0 :                 val = 0xff;
     151           0 :         }
     152             : 
     153           0 :         return val;
     154           0 : }
     155             : 
     156             : void
     157           0 : ccpmic_write_1(struct ccpmic_softc *sc, uint8_t reg, uint8_t val, int flags)
     158             : {
     159             :         int error;
     160             : 
     161           0 :         iic_acquire_bus(sc->sc_tag, flags);
     162           0 :         error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
     163             :             &reg, sizeof(reg), &val, sizeof(val), flags);
     164           0 :         iic_release_bus(sc->sc_tag, flags);
     165             : 
     166           0 :         if (error) {
     167           0 :                 printf("%s: can't write register 0x%02x\n",
     168           0 :                     sc->sc_dev.dv_xname, reg);
     169           0 :         }
     170           0 : }
     171             : 
     172             : void
     173           0 : ccpmic_get_lpat(struct ccpmic_softc *sc)
     174             : {
     175           0 :         struct aml_value res;
     176             :         int i;
     177             : 
     178           0 :         if (aml_evalname(sc->sc_acpi, sc->sc_node, "LPAT", 0, NULL, &res))
     179           0 :                 return;
     180           0 :         if (res.type != AML_OBJTYPE_PACKAGE)
     181             :                 goto out;
     182           0 :         if (res.length < 4 || (res.length % 2) != 0)
     183             :                 goto out;
     184             : 
     185           0 :         sc->sc_lpat_len = res.length / 2;
     186           0 :         sc->sc_lpat = mallocarray(sc->sc_lpat_len, sizeof(struct acpi_lpat),
     187             :             M_DEVBUF, M_WAITOK);
     188             : 
     189           0 :         for (i = 0; i < sc->sc_lpat_len; i++) {
     190           0 :                 sc->sc_lpat[i].temp = aml_val2int(res.v_package[2 * i]);
     191           0 :                 sc->sc_lpat[i].raw = aml_val2int(res.v_package[2 * i + 1]);
     192             :         }
     193             : 
     194             : out:
     195           0 :         aml_freevalue(&res);
     196           0 : }
     197             : 
     198             : int32_t
     199           0 : ccpmic_raw_to_temp(struct ccpmic_softc *sc, int32_t raw)
     200             : {
     201           0 :         struct acpi_lpat *lpat = sc->sc_lpat;
     202             :         int32_t raw0, delta_raw;
     203             :         int32_t temp0, delta_temp;
     204             :         int i;
     205             : 
     206           0 :         for (i = 1; i < sc->sc_lpat_len; i++) {
     207             :                 /* Coefficient can be positive or negative. */
     208           0 :                 if (raw >= lpat[i - 1].raw && raw <= lpat[i].raw)
     209             :                         break;
     210           0 :                 if (raw <= lpat[i - 1].raw && raw >= lpat[i].raw)
     211             :                         break;
     212             :         }
     213           0 :         if (i == sc->sc_lpat_len)
     214           0 :                 return -1;
     215             : 
     216           0 :         raw0 = lpat[i - 1].raw;
     217           0 :         temp0 = lpat[i - 1].temp;
     218           0 :         delta_raw = lpat[i].raw - raw0;
     219           0 :         delta_temp = lpat[i].temp - temp0;
     220             : 
     221           0 :         return temp0 + (raw - raw0) * delta_temp / delta_raw;
     222           0 : }
     223             : 
     224             : struct ccpmic_regmap {
     225             :         uint8_t address;
     226             :         uint8_t hi, lo;
     227             : };
     228             : 
     229             : struct ccpmic_regmap ccpmic_thermal_regmap[] = {
     230             :         { 0x18, CCPMIC_SYS2_THRM_RSLT_H, CCPMIC_SYS2_THRM_RSLT_L }, /* TMP2 */
     231             : };
     232             : 
     233             : int
     234           0 : ccpmic_thermal_opreg_handler(void *cookie, int iodir, uint64_t address,
     235             :     int size, uint64_t *value)
     236             : {
     237           0 :         struct ccpmic_softc *sc = cookie;
     238             :         int32_t temp;
     239             :         uint16_t raw;
     240             :         uint8_t lo, hi;
     241             :         int i;
     242             : 
     243             :         /* Only allow 32-bit read access. */
     244           0 :         if (size != 4 || iodir != ACPI_IOREAD)
     245           0 :                 return -1;
     246             : 
     247           0 :         for (i = 0; i < nitems(ccpmic_thermal_regmap); i++) {
     248           0 :                 if (address == ccpmic_thermal_regmap[i].address)
     249             :                         break;
     250             :         }
     251           0 :         if (i == nitems(ccpmic_thermal_regmap)) {
     252           0 :                 printf("%s: addr 0x%02llx\n", __func__, address);
     253           0 :                 return -1;
     254             :         }
     255             : 
     256           0 :         lo = ccpmic_thermal_regmap[i].lo;
     257           0 :         hi = ccpmic_thermal_regmap[i].hi;
     258           0 :         raw = ccpmic_read_1(sc, lo, 0);
     259           0 :         raw |= (ccpmic_read_1(sc, hi, 0) & 0x03) << 8;
     260             : 
     261           0 :         temp = ccpmic_raw_to_temp(sc, raw);
     262           0 :         if (temp < 0)
     263           0 :                 return -1;
     264             : 
     265           0 :         *value = temp;
     266           0 :         return 0;
     267           0 : }
     268             : 
     269             : struct ccpmic_regmap ccpmic_power_regmap[] = {
     270             :         { 0x24, CCPMIC_V2P85SX },       /* X285 */
     271             :         { 0x48, CCPMIC_V1P8SX },        /* V18X */
     272             :         { 0x50, CCPMIC_V1P2SX },        /* V12X */
     273             : };
     274             : 
     275             : int
     276           0 : ccpmic_power_opreg_handler(void *cookie, int iodir, uint64_t address,
     277             :     int size, uint64_t *value)
     278             : {
     279           0 :         struct ccpmic_softc *sc = cookie;
     280             :         uint8_t reg, val;
     281             :         int i;
     282             : 
     283             :         /* Only allow 32-bit access. */
     284           0 :         if (size != 4)
     285           0 :                 return -1;
     286             : 
     287           0 :         for (i = 0; i < nitems(ccpmic_power_regmap); i++) {
     288           0 :                 if (address == ccpmic_power_regmap[i].address)
     289             :                         break;
     290             :         }
     291           0 :         if (i == nitems(ccpmic_power_regmap)) {
     292           0 :                 printf("%s: addr 0x%02llx\n", __func__, address);
     293           0 :                 return -1;
     294             :         }
     295             : 
     296           0 :         reg = ccpmic_power_regmap[i].hi;
     297           0 :         val = ccpmic_read_1(sc, reg, 0);
     298           0 :         if (iodir == ACPI_IOREAD) {
     299           0 :                 if ((val & CCPMIC_PWR_SEL) && (val & CCPMIC_PWR_ON))
     300           0 :                         *value = 1;
     301             :                 else
     302           0 :                         *value = 0;
     303             :         } else {
     304           0 :                 if (*value)
     305           0 :                         val |= CCPMIC_PWR_ON;
     306             :                 else
     307           0 :                         val &= ~CCPMIC_PWR_ON;
     308           0 :                 ccpmic_write_1(sc, reg, val | CCPMIC_PWR_SEL, 0);
     309             :         }
     310             : 
     311           0 :         return 0;
     312           0 : }
     313             : 
     314             : /* 
     315             :  * We have 16 real GPIOs and a bunch of virtual ones.  The virtual
     316             :  * ones are mostly there to deal with a limitation of Microsoft
     317             :  * Windows.  We only implement the "panel" control GPIO, which
     318             :  * actually maps onto a real GPIO.
     319             :  */
     320             : 
     321             : int
     322           0 : ccpmic_read_pin(void *cookie, int pin)
     323             : {
     324           0 :         struct ccpmic_softc *sc = cookie;
     325             :         uint8_t reg;
     326             : 
     327           0 :         if (pin >= CCPMIC_NPINS)
     328           0 :                 return 0;
     329             : 
     330           0 :         reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLO : CCPMIC_GPIO1P0CTLO) + pin % 8;
     331           0 :         ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_INPUT, 0);
     332           0 :         reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLI : CCPMIC_GPIO1P0CTLI) + pin % 8;
     333           0 :         return ccpmic_read_1(sc, reg, 0) & CCPMIC_GPIOCTLI_VALUE;
     334           0 : }
     335             : 
     336             : void
     337           0 : ccpmic_write_pin(void *cookie, int pin, int value)
     338             : {
     339           0 :         struct ccpmic_softc *sc = cookie;
     340             :         uint8_t reg;
     341             : 
     342           0 :         if (pin == 0x5e) {
     343             :                 reg = CCPMIC_GPIOPANELCTL;
     344           0 :                 ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_OUTPUT | !!value, 0);
     345           0 :                 return;
     346             :         }
     347             : 
     348           0 :         if (pin >= CCPMIC_NPINS)
     349           0 :                 return;
     350             : 
     351           0 :         reg = ((pin < 8) ? CCPMIC_GPIO0P0CTLO : CCPMIC_GPIO1P0CTLO) + pin % 8;
     352           0 :         ccpmic_write_1(sc, reg, CCPMIC_GPIOCTLO_OUTPUT | !!value, 0);
     353           0 : }

Generated by: LCOV version 1.13