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

          Line data    Source code
       1             : /*      $OpenBSD: chvgpio.c,v 1.8 2017/11/29 22:51:01 kettenis Exp $    */
       2             : /*
       3             :  * Copyright (c) 2016 Mark Kettenis
       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/malloc.h>
      20             : #include <sys/systm.h>
      21             : 
      22             : #include <dev/acpi/acpireg.h>
      23             : #include <dev/acpi/acpivar.h>
      24             : #include <dev/acpi/acpidev.h>
      25             : #include <dev/acpi/amltypes.h>
      26             : #include <dev/acpi/dsdt.h>
      27             : 
      28             : #define CHVGPIO_INTERRUPT_STATUS        0x0300
      29             : #define CHVGPIO_INTERRUPT_MASK          0x0380
      30             : #define CHVGPIO_PAD_CFG0                0x4400
      31             : #define CHVGPIO_PAD_CFG1                0x4404
      32             : 
      33             : #define CHVGPIO_PAD_CFG0_GPIORXSTATE            0x00000001
      34             : #define CHVGPIO_PAD_CFG0_GPIOTXSTATE            0x00000002
      35             : #define CHVGPIO_PAD_CFG0_INTSEL_MASK            0xf0000000
      36             : #define CHVGPIO_PAD_CFG0_INTSEL_SHIFT           28
      37             : 
      38             : #define CHVGPIO_PAD_CFG1_INTWAKECFG_MASK        0x00000007
      39             : #define CHVGPIO_PAD_CFG1_INTWAKECFG_FALLING     0x00000001
      40             : #define CHVGPIO_PAD_CFG1_INTWAKECFG_RISING      0x00000002
      41             : #define CHVGPIO_PAD_CFG1_INTWAKECFG_BOTH        0x00000003
      42             : #define CHVGPIO_PAD_CFG1_INTWAKECFG_LEVEL       0x00000004
      43             : #define CHVGPIO_PAD_CFG1_INVRXTX_MASK           0x000000f0
      44             : #define CHVGPIO_PAD_CFG1_INVRXTX_RXDATA         0x00000040
      45             : 
      46             : /* OEM defined RegionSpace. */
      47             : #define CHVGPIO_REGIONSPACE_BASE        0x90
      48             : 
      49             : struct chvgpio_intrhand {
      50             :         int (*ih_func)(void *);
      51             :         void *ih_arg;
      52             : };
      53             : 
      54             : struct chvgpio_softc {
      55             :         struct device sc_dev;
      56             :         struct acpi_softc *sc_acpi;
      57             :         struct aml_node *sc_node;
      58             : 
      59             :         bus_space_tag_t sc_memt;
      60             :         bus_space_handle_t sc_memh;
      61             :         bus_addr_t sc_addr;
      62             :         bus_size_t sc_size;
      63             : 
      64             :         int sc_irq;
      65             :         int sc_irq_flags;
      66             :         void *sc_ih;
      67             : 
      68             :         const int *sc_pins;
      69             :         int sc_npins;
      70             :         int sc_ngroups;
      71             : 
      72             :         struct chvgpio_intrhand sc_pin_ih[16];
      73             : 
      74             :         struct acpi_gpio sc_gpio;
      75             : };
      76             : 
      77             : static inline int
      78           0 : chvgpio_pad_cfg0_offset(int pin)
      79             : {
      80           0 :         return (CHVGPIO_PAD_CFG0 + 1024 * (pin / 15) + 8 * (pin % 15));
      81             : }
      82             : 
      83             : static inline int
      84           0 : chvgpio_read_pad_cfg0(struct chvgpio_softc *sc, int pin)
      85             : {
      86           0 :         return bus_space_read_4(sc->sc_memt, sc->sc_memh,
      87             :             chvgpio_pad_cfg0_offset(pin));
      88             : }
      89             : 
      90             : static inline void
      91           0 : chvgpio_write_pad_cfg0(struct chvgpio_softc *sc, int pin, uint32_t val)
      92             : {
      93           0 :         bus_space_write_4(sc->sc_memt, sc->sc_memh,
      94             :             chvgpio_pad_cfg0_offset(pin), val);
      95           0 : }
      96             : 
      97             : static inline int
      98           0 : chvgpio_read_pad_cfg1(struct chvgpio_softc *sc, int pin)
      99             : {
     100           0 :         return bus_space_read_4(sc->sc_memt, sc->sc_memh,
     101             :             chvgpio_pad_cfg0_offset(pin) + 4);
     102             : }
     103             : 
     104             : static inline void
     105           0 : chvgpio_write_pad_cfg1(struct chvgpio_softc *sc, int pin, uint32_t val)
     106             : {
     107           0 :         bus_space_write_4(sc->sc_memt, sc->sc_memh,
     108             :             chvgpio_pad_cfg0_offset(pin) + 4, val);
     109           0 : }
     110             : 
     111             : int     chvgpio_match(struct device *, void *, void *);
     112             : void    chvgpio_attach(struct device *, struct device *, void *);
     113             : 
     114             : struct cfattach chvgpio_ca = {
     115             :         sizeof(struct chvgpio_softc), chvgpio_match, chvgpio_attach
     116             : };
     117             : 
     118             : struct cfdriver chvgpio_cd = {
     119             :         NULL, "chvgpio", DV_DULL
     120             : };
     121             : 
     122             : const char *chvgpio_hids[] = {
     123             :         "INT33FF",
     124             :         NULL
     125             : };
     126             : 
     127             : /*
     128             :  * The pads for the pins are arranged in groups of maximal 15 pins.
     129             :  * The arrays below give the number of pins per group, such that we
     130             :  * can validate the (untrusted) pin numbers from ACPI.
     131             :  */
     132             : 
     133             : const int chv_southwest_pins[] = {
     134             :         8, 8, 8, 8, 8, 8, 8, -1
     135             : };
     136             : 
     137             : const int chv_north_pins[] = {
     138             :         9, 13, 12, 12, 13, -1
     139             : };
     140             : 
     141             : const int chv_east_pins[] = {
     142             :         12, 12, -1
     143             : };
     144             : 
     145             : const int chv_southeast_pins[] = {
     146             :         8, 12, 6, 8, 10, 11, -1
     147             : };
     148             : 
     149             : int     chvgpio_parse_resources(int, union acpi_resource *, void *);
     150             : int     chvgpio_check_pin(struct chvgpio_softc *, int);
     151             : int     chvgpio_read_pin(void *, int);
     152             : void    chvgpio_write_pin(void *, int, int);
     153             : void    chvgpio_intr_establish(void *, int, int, int (*)(), void *);
     154             : int     chvgpio_intr(void *);
     155             : int     chvgpio_opreg_handler(void *, int, uint64_t, int, uint64_t *);
     156             : 
     157             : int
     158           0 : chvgpio_match(struct device *parent, void *match, void *aux)
     159             : {
     160           0 :         struct acpi_attach_args *aaa = aux;
     161           0 :         struct cfdata *cf = match;
     162             : 
     163           0 :         return acpi_matchhids(aaa, chvgpio_hids, cf->cf_driver->cd_name);
     164             : }
     165             : 
     166             : void
     167           0 : chvgpio_attach(struct device *parent, struct device *self, void *aux)
     168             : {
     169           0 :         struct acpi_attach_args *aaa = aux;
     170           0 :         struct chvgpio_softc *sc = (struct chvgpio_softc *)self;
     171           0 :         struct aml_value res;
     172           0 :         int64_t uid;
     173             :         int i;
     174             : 
     175           0 :         sc->sc_acpi = (struct acpi_softc *)parent;
     176           0 :         sc->sc_node = aaa->aaa_node;
     177           0 :         printf(": %s", sc->sc_node->name);
     178             : 
     179           0 :         if (aml_evalinteger(sc->sc_acpi, sc->sc_node, "_UID", 0, NULL, &uid)) {
     180           0 :                 printf(", can't find uid\n");
     181           0 :                 return;
     182             :         }
     183             : 
     184           0 :         printf(" uid %lld", uid);
     185             : 
     186           0 :         switch (uid) {
     187             :         case 1:
     188           0 :                 sc->sc_pins = chv_southwest_pins;
     189           0 :                 break;
     190             :         case 2:
     191           0 :                 sc->sc_pins = chv_north_pins;
     192           0 :                 break;
     193             :         case 3:
     194           0 :                 sc->sc_pins = chv_east_pins;
     195           0 :                 break;
     196             :         case 4:
     197           0 :                 sc->sc_pins = chv_southeast_pins;
     198           0 :                 break;
     199             :         default:
     200           0 :                 printf("\n");
     201           0 :                 return;
     202             :         }
     203             : 
     204           0 :         for (i = 0; sc->sc_pins[i] >= 0; i++) {
     205           0 :                 sc->sc_npins += sc->sc_pins[i];
     206           0 :                 sc->sc_ngroups++;
     207             :         }
     208             : 
     209           0 :         if (aml_evalname(sc->sc_acpi, sc->sc_node, "_CRS", 0, NULL, &res)) {
     210           0 :                 printf(", can't find registers\n");
     211           0 :                 return;
     212             :         }
     213             : 
     214           0 :         aml_parse_resource(&res, chvgpio_parse_resources, sc);
     215           0 :         printf(" addr 0x%lx/0x%lx", sc->sc_addr, sc->sc_size);
     216           0 :         if (sc->sc_addr == 0 || sc->sc_size == 0) {
     217           0 :                 printf("\n");
     218           0 :                 return;
     219             :         }
     220           0 :         aml_freevalue(&res);
     221             : 
     222           0 :         printf(" irq %d", sc->sc_irq);
     223             : 
     224           0 :         sc->sc_memt = aaa->aaa_memt;
     225           0 :         if (bus_space_map(sc->sc_memt, sc->sc_addr, sc->sc_size, 0,
     226           0 :             &sc->sc_memh)) {
     227           0 :                 printf(", can't map registers\n");
     228           0 :                 return;
     229             :         }
     230             : 
     231           0 :         sc->sc_ih = acpi_intr_establish(sc->sc_irq, sc->sc_irq_flags, IPL_BIO,
     232           0 :             chvgpio_intr, sc, sc->sc_dev.dv_xname);
     233           0 :         if (sc->sc_ih == NULL) {
     234           0 :                 printf(", can't establish interrupt\n");
     235             :                 goto unmap;
     236             :         }
     237             : 
     238           0 :         sc->sc_gpio.cookie = sc;
     239           0 :         sc->sc_gpio.read_pin = chvgpio_read_pin;
     240           0 :         sc->sc_gpio.write_pin = chvgpio_write_pin;
     241           0 :         sc->sc_gpio.intr_establish = chvgpio_intr_establish;
     242           0 :         sc->sc_node->gpio = &sc->sc_gpio;
     243             : 
     244             :         /* Mask and ack all interrupts. */
     245           0 :         bus_space_write_4(sc->sc_memt, sc->sc_memh,
     246             :             CHVGPIO_INTERRUPT_MASK, 0);
     247           0 :         bus_space_write_4(sc->sc_memt, sc->sc_memh,
     248             :             CHVGPIO_INTERRUPT_STATUS, 0xffff);
     249             : 
     250           0 :         printf(", %d pins\n", sc->sc_npins);
     251             : 
     252             :         /* Register OEM defined address space. */
     253           0 :         aml_register_regionspace(sc->sc_node, CHVGPIO_REGIONSPACE_BASE + uid,
     254             :             sc, chvgpio_opreg_handler);
     255             : 
     256           0 :         acpi_register_gpio(sc->sc_acpi, sc->sc_node);
     257           0 :         return;
     258             : 
     259             : unmap:
     260           0 :         bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_size);
     261           0 : }
     262             : 
     263             : int
     264           0 : chvgpio_parse_resources(int crsidx, union acpi_resource *crs, void *arg)
     265             : {
     266           0 :         struct chvgpio_softc *sc = arg;
     267           0 :         int type = AML_CRSTYPE(crs);
     268             : 
     269           0 :         switch (type) {
     270             :         case LR_MEM32FIXED:
     271           0 :                 sc->sc_addr = crs->lr_m32fixed._bas;
     272           0 :                 sc->sc_size = crs->lr_m32fixed._len;
     273           0 :                 break;
     274             :         case LR_EXTIRQ:
     275           0 :                 sc->sc_irq = crs->lr_extirq.irq[0];
     276           0 :                 sc->sc_irq_flags = crs->lr_extirq.flags;
     277           0 :                 break;
     278             :         default:
     279           0 :                 printf(" type 0x%x\n", type);
     280           0 :                 break;
     281             :         }
     282             : 
     283           0 :         return 0;
     284             : }
     285             : 
     286             : int
     287           0 : chvgpio_check_pin(struct chvgpio_softc *sc, int pin)
     288             : {
     289           0 :         if (pin < 0)
     290           0 :                 return EINVAL;
     291           0 :         if ((pin / 15) >= sc->sc_ngroups)
     292           0 :                 return EINVAL;
     293           0 :         if ((pin % 15) >= sc->sc_pins[pin / 15])
     294           0 :                 return EINVAL;
     295             : 
     296           0 :         return 0;
     297           0 : }
     298             : 
     299             : int
     300           0 : chvgpio_read_pin(void *cookie, int pin)
     301             : {
     302           0 :         struct chvgpio_softc *sc = cookie;
     303             :         uint32_t reg;
     304             : 
     305           0 :         KASSERT(chvgpio_check_pin(sc, pin) == 0);
     306             : 
     307           0 :         reg = chvgpio_read_pad_cfg0(sc, pin);
     308           0 :         return (reg & CHVGPIO_PAD_CFG0_GPIORXSTATE);
     309             : }
     310             : 
     311             : void
     312           0 : chvgpio_write_pin(void *cookie, int pin, int value)
     313             : {
     314           0 :         struct chvgpio_softc *sc = cookie;
     315             :         uint32_t reg;
     316             : 
     317           0 :         KASSERT(chvgpio_check_pin(sc, pin) == 0);
     318             : 
     319           0 :         reg = chvgpio_read_pad_cfg0(sc, pin);
     320           0 :         if (value)
     321           0 :                 reg |= CHVGPIO_PAD_CFG0_GPIOTXSTATE;
     322             :         else
     323           0 :                 reg &= ~CHVGPIO_PAD_CFG0_GPIOTXSTATE;
     324           0 :         chvgpio_write_pad_cfg0(sc, pin, reg);
     325           0 : }
     326             : 
     327             : void
     328           0 : chvgpio_intr_establish(void *cookie, int pin, int flags,
     329             :     int (*func)(void *), void *arg)
     330             : {
     331           0 :         struct chvgpio_softc *sc = cookie;
     332             :         uint32_t reg;
     333             :         int line;
     334             : 
     335           0 :         KASSERT(chvgpio_check_pin(sc, pin) == 0);
     336             : 
     337           0 :         reg = chvgpio_read_pad_cfg0(sc, pin);
     338           0 :         reg &= CHVGPIO_PAD_CFG0_INTSEL_MASK;
     339           0 :         line = reg >> CHVGPIO_PAD_CFG0_INTSEL_SHIFT;
     340             : 
     341           0 :         sc->sc_pin_ih[line].ih_func = func;
     342           0 :         sc->sc_pin_ih[line].ih_arg = arg;
     343             : 
     344           0 :         reg = chvgpio_read_pad_cfg1(sc, pin);
     345           0 :         reg &= ~CHVGPIO_PAD_CFG1_INTWAKECFG_MASK;
     346           0 :         reg &= ~CHVGPIO_PAD_CFG1_INVRXTX_MASK;
     347           0 :         switch (flags & (LR_GPIO_MODE | LR_GPIO_POLARITY)) {
     348             :         case LR_GPIO_LEVEL | LR_GPIO_ACTLO:
     349           0 :                 reg |= CHVGPIO_PAD_CFG1_INVRXTX_RXDATA;
     350             :                 /* FALLTHROUGH */
     351             :         case LR_GPIO_LEVEL | LR_GPIO_ACTHI:
     352           0 :                 reg |= CHVGPIO_PAD_CFG1_INTWAKECFG_LEVEL;
     353           0 :                 break;
     354             :         case LR_GPIO_EDGE | LR_GPIO_ACTLO:
     355           0 :                 reg |= CHVGPIO_PAD_CFG1_INTWAKECFG_FALLING;
     356           0 :                 break;
     357             :         case LR_GPIO_EDGE | LR_GPIO_ACTHI:
     358           0 :                 reg |= CHVGPIO_PAD_CFG1_INTWAKECFG_RISING;
     359           0 :                 break;
     360             :         case LR_GPIO_EDGE | LR_GPIO_ACTBOTH:
     361           0 :                 reg |= CHVGPIO_PAD_CFG1_INTWAKECFG_BOTH;
     362           0 :                 break;
     363             :         default:
     364           0 :                 printf("%s: unsupported interrupt mode/polarity\n",
     365           0 :                     sc->sc_dev.dv_xname);
     366           0 :                 break;
     367             :         }
     368           0 :         chvgpio_write_pad_cfg1(sc, pin, reg);
     369             : 
     370           0 :         reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
     371             :             CHVGPIO_INTERRUPT_MASK);
     372           0 :         bus_space_write_4(sc->sc_memt, sc->sc_memh,
     373             :             CHVGPIO_INTERRUPT_MASK, reg | (1 << line));
     374           0 : }
     375             : 
     376             : int
     377           0 : chvgpio_intr(void *arg)
     378             : {
     379           0 :         struct chvgpio_softc *sc = arg;
     380             :         uint32_t reg;
     381             :         int rc = 0;
     382             :         int line;
     383             : 
     384           0 :         reg = bus_space_read_4(sc->sc_memt, sc->sc_memh,
     385             :             CHVGPIO_INTERRUPT_STATUS);
     386           0 :         for (line = 0; line < 16; line++) {
     387           0 :                 if ((reg & (1 << line)) == 0)
     388             :                         continue;
     389             : 
     390           0 :                 bus_space_write_4(sc->sc_memt,sc->sc_memh,
     391             :                     CHVGPIO_INTERRUPT_STATUS, 1 << line);
     392           0 :                 if (sc->sc_pin_ih[line].ih_func)
     393           0 :                         sc->sc_pin_ih[line].ih_func(sc->sc_pin_ih[line].ih_arg);
     394             :                 rc = 1;
     395           0 :         }
     396             : 
     397           0 :         return rc;
     398             : }
     399             : 
     400             : int
     401           0 : chvgpio_opreg_handler(void *cookie, int iodir, uint64_t address, int size,
     402             :     uint64_t *value)
     403             : {
     404           0 :         struct chvgpio_softc *sc = cookie;
     405             : 
     406             :         /* Only allow 32-bit access. */
     407           0 :         if (size != 4 || address > sc->sc_size - size)
     408           0 :                 return -1;
     409             : 
     410           0 :         if (iodir == ACPI_IOREAD)
     411           0 :                 *value = bus_space_read_4(sc->sc_memt, sc->sc_memh, address);
     412             :         else
     413           0 :                 bus_space_write_4(sc->sc_memt, sc->sc_memh, address, *value);
     414             : 
     415           0 :         return 0;
     416           0 : }

Generated by: LCOV version 1.13