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/gpio/chvgpio.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2017 Tom Jones <tj@enoti.me>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  */
   29 
   30 /*
   31  * Copyright (c) 2016 Mark Kettenis
   32  *
   33  * Permission to use, copy, modify, and distribute this software for any
   34  * purpose with or without fee is hereby granted, provided that the above
   35  * copyright notice and this permission notice appear in all copies.
   36  *
   37  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   38  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   39  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   40  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   41  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   42  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   43  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   44  */
   45 #include <sys/cdefs.h>
   46 __FBSDID("$FreeBSD$");
   47 
   48 #include <sys/param.h>
   49 #include <sys/systm.h>
   50 #include <sys/bus.h>
   51 #include <sys/gpio.h>
   52 #include <sys/clock.h>
   53 #include <sys/kernel.h>
   54 #include <sys/module.h>
   55 #include <sys/endian.h>
   56 #include <sys/rman.h>
   57 #include <sys/types.h>
   58 #include <sys/malloc.h>
   59 
   60 #include <machine/bus.h>
   61 #include <machine/resource.h>
   62 
   63 #include <contrib/dev/acpica/include/acpi.h>
   64 #include <contrib/dev/acpica/include/accommon.h>
   65 
   66 #include <dev/acpica/acpivar.h>
   67 #include <dev/gpio/gpiobusvar.h>
   68 
   69 #include "opt_platform.h"
   70 #include "opt_acpi.h"
   71 #include "gpio_if.h"
   72 
   73 #include "chvgpio_reg.h"
   74 
   75 /*
   76  *     Macros for driver mutex locking
   77  */
   78 #define CHVGPIO_LOCK(_sc)               mtx_lock_spin(&(_sc)->sc_mtx)
   79 #define CHVGPIO_UNLOCK(_sc)             mtx_unlock_spin(&(_sc)->sc_mtx)
   80 #define CHVGPIO_LOCK_INIT(_sc) \
   81         mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
   82         "chvgpio", MTX_SPIN)
   83 #define CHVGPIO_LOCK_DESTROY(_sc)       mtx_destroy(&(_sc)->sc_mtx)
   84 #define CHVGPIO_ASSERT_LOCKED(_sc)      mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
   85 #define CHVGPIO_ASSERT_UNLOCKED(_sc)    mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
   86 
   87 struct chvgpio_softc {
   88         device_t        sc_dev;
   89         device_t        sc_busdev;
   90         struct mtx      sc_mtx;
   91 
   92         ACPI_HANDLE     sc_handle;
   93 
   94         int             sc_mem_rid;
   95         struct resource *sc_mem_res;
   96 
   97         int             sc_irq_rid;
   98         struct resource *sc_irq_res;
   99         void            *intr_handle;
  100 
  101         const char      *sc_bank_prefix;
  102         const int       *sc_pins;
  103         int             sc_npins;
  104         int             sc_ngroups;
  105         const char **sc_pin_names;
  106 };
  107 
  108 static void chvgpio_intr(void *);
  109 static int chvgpio_probe(device_t);
  110 static int chvgpio_attach(device_t);
  111 static int chvgpio_detach(device_t);
  112 
  113 static inline int
  114 chvgpio_pad_cfg0_offset(int pin)
  115 {
  116         return (CHVGPIO_PAD_CFG0 + 1024 * (pin / 15) + 8 * (pin % 15));
  117 }
  118 
  119 static inline int
  120 chvgpio_read_pad_cfg0(struct chvgpio_softc *sc, int pin)
  121 {
  122         return bus_read_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin));
  123 }
  124 
  125 static inline void
  126 chvgpio_write_pad_cfg0(struct chvgpio_softc *sc, int pin, uint32_t val)
  127 {
  128         bus_write_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin), val);
  129 }
  130 
  131 static inline int
  132 chvgpio_read_pad_cfg1(struct chvgpio_softc *sc, int pin)
  133 {
  134         return bus_read_4(sc->sc_mem_res, chvgpio_pad_cfg0_offset(pin) + 4);
  135 }
  136 
  137 static device_t
  138 chvgpio_get_bus(device_t dev)
  139 {
  140         struct chvgpio_softc *sc;
  141 
  142         sc = device_get_softc(dev);
  143 
  144         return (sc->sc_busdev);
  145 }
  146 
  147 static int
  148 chvgpio_pin_max(device_t dev, int *maxpin)
  149 {
  150         struct chvgpio_softc *sc;
  151 
  152         sc = device_get_softc(dev);
  153 
  154         *maxpin = sc->sc_npins - 1;
  155 
  156         return (0);
  157 }
  158 
  159 static int
  160 chvgpio_valid_pin(struct chvgpio_softc *sc, int pin)
  161 {
  162         if (pin < 0)
  163                 return EINVAL;
  164         if ((pin / 15) >= sc->sc_ngroups)
  165                 return EINVAL;
  166         if ((pin % 15) >= sc->sc_pins[pin / 15])
  167                 return EINVAL;
  168         return (0);
  169 }
  170 
  171 static int
  172 chvgpio_pin_getname(device_t dev, uint32_t pin, char *name)
  173 {
  174         struct chvgpio_softc *sc;
  175 
  176         sc = device_get_softc(dev);
  177         if (chvgpio_valid_pin(sc, pin) != 0)
  178                 return (EINVAL);
  179 
  180         /* return pin name from datasheet */
  181         snprintf(name, GPIOMAXNAME, "%s", sc->sc_pin_names[pin]);
  182         name[GPIOMAXNAME - 1] = '\0';
  183         return (0);
  184 }
  185 
  186 static int
  187 chvgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
  188 {
  189         struct chvgpio_softc *sc;
  190 
  191         sc = device_get_softc(dev);
  192         if (chvgpio_valid_pin(sc, pin) != 0)
  193                 return (EINVAL);
  194 
  195         *caps = 0;
  196         if (chvgpio_valid_pin(sc, pin))
  197                 *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
  198 
  199         return (0);
  200 }
  201 
  202 static int
  203 chvgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
  204 {
  205         struct chvgpio_softc *sc;
  206         uint32_t val;
  207 
  208         sc = device_get_softc(dev);
  209         if (chvgpio_valid_pin(sc, pin) != 0)
  210                 return (EINVAL);
  211 
  212         *flags = 0;
  213 
  214         /* Get the current pin state */
  215         CHVGPIO_LOCK(sc);
  216         val = chvgpio_read_pad_cfg0(sc, pin);
  217 
  218         if (val & CHVGPIO_PAD_CFG0_GPIOCFG_GPIO ||
  219                 val & CHVGPIO_PAD_CFG0_GPIOCFG_GPO)
  220                 *flags |= GPIO_PIN_OUTPUT;
  221 
  222         if (val & CHVGPIO_PAD_CFG0_GPIOCFG_GPIO ||
  223                 val & CHVGPIO_PAD_CFG0_GPIOCFG_GPI)
  224                 *flags |= GPIO_PIN_INPUT;
  225 
  226         val = chvgpio_read_pad_cfg1(sc, pin);
  227 
  228         CHVGPIO_UNLOCK(sc);
  229         return (0);
  230 }
  231 
  232 static int
  233 chvgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
  234 {
  235         struct chvgpio_softc *sc;
  236         uint32_t val;
  237         uint32_t allowed;
  238 
  239         sc = device_get_softc(dev);
  240         if (chvgpio_valid_pin(sc, pin) != 0)
  241                 return (EINVAL);
  242 
  243         allowed = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
  244 
  245         /*
  246          * Only direction flag allowed
  247          */
  248         if (flags & ~allowed)
  249                 return (EINVAL);
  250 
  251         /*
  252          * Not both directions simultaneously
  253          */
  254         if ((flags & allowed) == allowed)
  255                 return (EINVAL);
  256 
  257         /* Set the GPIO mode and state */
  258         CHVGPIO_LOCK(sc);
  259         val = chvgpio_read_pad_cfg0(sc, pin);
  260         if (flags & GPIO_PIN_INPUT)
  261                 val = val & CHVGPIO_PAD_CFG0_GPIOCFG_GPI;
  262         if (flags & GPIO_PIN_OUTPUT)
  263                 val = val & CHVGPIO_PAD_CFG0_GPIOCFG_GPO;
  264         chvgpio_write_pad_cfg0(sc, pin, val);
  265         CHVGPIO_UNLOCK(sc);
  266 
  267         return (0);
  268 }
  269 
  270 static int
  271 chvgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
  272 {
  273         struct chvgpio_softc *sc;
  274         uint32_t val;
  275 
  276         sc = device_get_softc(dev);
  277         if (chvgpio_valid_pin(sc, pin) != 0)
  278                 return (EINVAL);
  279 
  280         CHVGPIO_LOCK(sc);
  281         val = chvgpio_read_pad_cfg0(sc, pin);
  282         if (value == GPIO_PIN_LOW)
  283                 val = val & ~CHVGPIO_PAD_CFG0_GPIOTXSTATE;
  284         else
  285                 val = val | CHVGPIO_PAD_CFG0_GPIOTXSTATE;
  286         chvgpio_write_pad_cfg0(sc, pin, val);
  287         CHVGPIO_UNLOCK(sc);
  288 
  289         return (0);
  290 }
  291 
  292 static int
  293 chvgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
  294 {
  295         struct chvgpio_softc *sc;
  296         uint32_t val;
  297 
  298         sc = device_get_softc(dev);
  299         if (chvgpio_valid_pin(sc, pin) != 0)
  300                 return (EINVAL);
  301 
  302         CHVGPIO_LOCK(sc);
  303 
  304         /* Read pin value */
  305         val = chvgpio_read_pad_cfg0(sc, pin);
  306         if (val & CHVGPIO_PAD_CFG0_GPIORXSTATE)
  307                 *value = GPIO_PIN_HIGH;
  308         else
  309                 *value = GPIO_PIN_LOW;
  310 
  311         CHVGPIO_UNLOCK(sc);
  312 
  313         return (0);
  314 }
  315 
  316 static int
  317 chvgpio_pin_toggle(device_t dev, uint32_t pin)
  318 {
  319         struct chvgpio_softc *sc;
  320         uint32_t val;
  321 
  322         sc = device_get_softc(dev);
  323         if (chvgpio_valid_pin(sc, pin) != 0)
  324                 return (EINVAL);
  325 
  326         CHVGPIO_LOCK(sc);
  327 
  328         /* Toggle the pin */
  329         val = chvgpio_read_pad_cfg0(sc, pin);
  330         val = val ^ CHVGPIO_PAD_CFG0_GPIOTXSTATE;
  331         chvgpio_write_pad_cfg0(sc, pin, val);
  332 
  333         CHVGPIO_UNLOCK(sc);
  334 
  335         return (0);
  336 }
  337 
  338 static char *chvgpio_hids[] = {
  339         "INT33FF",
  340         NULL
  341 };
  342 
  343 static int
  344 chvgpio_probe(device_t dev)
  345 {
  346     if (acpi_disabled("chvgpio") ||
  347     ACPI_ID_PROBE(device_get_parent(dev), dev, chvgpio_hids) == NULL)
  348         return (ENXIO);
  349 
  350     device_set_desc(dev, "Intel Cherry View GPIO");
  351     return (0);
  352 }
  353 
  354 static int
  355 chvgpio_attach(device_t dev)
  356 {
  357         struct chvgpio_softc *sc;
  358         ACPI_STATUS status;
  359         int uid;
  360         int i;
  361         int error;
  362 
  363         sc = device_get_softc(dev);
  364         sc->sc_dev = dev;
  365         sc->sc_handle = acpi_get_handle(dev);
  366 
  367         status = acpi_GetInteger(sc->sc_handle, "_UID", &uid);
  368         if (ACPI_FAILURE(status)) {
  369                 device_printf(dev, "failed to read _UID\n");
  370                 return (ENXIO);
  371         }
  372 
  373         CHVGPIO_LOCK_INIT(sc);
  374 
  375         switch (uid) {
  376         case SW_UID:
  377                 sc->sc_bank_prefix = SW_BANK_PREFIX;
  378                 sc->sc_pins = chv_southwest_pins;
  379                 sc->sc_pin_names = chv_southwest_pin_names;
  380                 break;
  381         case N_UID:
  382                 sc->sc_bank_prefix = N_BANK_PREFIX;
  383                 sc->sc_pins = chv_north_pins;
  384                 sc->sc_pin_names = chv_north_pin_names;
  385                 break;
  386         case E_UID:
  387                 sc->sc_bank_prefix = E_BANK_PREFIX;
  388                 sc->sc_pins = chv_east_pins;
  389                 sc->sc_pin_names = chv_east_pin_names;
  390                 break;
  391         case SE_UID:
  392                 sc->sc_bank_prefix = SE_BANK_PREFIX;
  393                 sc->sc_pins = chv_southeast_pins;
  394                 sc->sc_pin_names = chv_southeast_pin_names;
  395                 break;
  396         default:
  397                 device_printf(dev, "invalid _UID value: %d\n", uid);
  398                 return (ENXIO);
  399         }
  400 
  401         for (i = 0; sc->sc_pins[i] >= 0; i++) {
  402                 sc->sc_npins += sc->sc_pins[i];
  403                 sc->sc_ngroups++;
  404         }
  405 
  406         sc->sc_mem_rid = 0;
  407         sc->sc_mem_res = bus_alloc_resource_any(sc->sc_dev, SYS_RES_MEMORY,
  408                 &sc->sc_mem_rid, RF_ACTIVE);
  409         if (sc->sc_mem_res == NULL) {
  410                 CHVGPIO_LOCK_DESTROY(sc);
  411                 device_printf(dev, "can't allocate memory resource\n");
  412                 return (ENOMEM);
  413         }
  414 
  415         sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
  416                 &sc->sc_irq_rid, RF_ACTIVE);
  417 
  418         if (!sc->sc_irq_res) {
  419                 CHVGPIO_LOCK_DESTROY(sc);
  420                 bus_release_resource(dev, SYS_RES_MEMORY,
  421                         sc->sc_mem_rid, sc->sc_mem_res);
  422                 device_printf(dev, "can't allocate irq resource\n");
  423                 return (ENOMEM);
  424         }
  425 
  426         error = bus_setup_intr(sc->sc_dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
  427                 NULL, chvgpio_intr, sc, &sc->intr_handle);
  428 
  429 
  430         if (error) {
  431                 device_printf(sc->sc_dev, "unable to setup irq: error %d\n", error);
  432                 CHVGPIO_LOCK_DESTROY(sc);
  433                 bus_release_resource(dev, SYS_RES_MEMORY,
  434                         sc->sc_mem_rid, sc->sc_mem_res);
  435                 bus_release_resource(dev, SYS_RES_IRQ,
  436                         sc->sc_irq_rid, sc->sc_irq_res);
  437                 return (ENXIO);
  438         }
  439 
  440         /* Mask and ack all interrupts. */
  441         bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_MASK, 0);
  442         bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 0xffff);
  443 
  444         sc->sc_busdev = gpiobus_attach_bus(dev);
  445         if (sc->sc_busdev == NULL) {
  446                 CHVGPIO_LOCK_DESTROY(sc);
  447                 bus_release_resource(dev, SYS_RES_MEMORY,
  448                         sc->sc_mem_rid, sc->sc_mem_res);
  449                 bus_release_resource(dev, SYS_RES_IRQ,
  450                         sc->sc_irq_rid, sc->sc_irq_res);
  451                 return (ENXIO);
  452         }
  453 
  454         return (0);
  455 }
  456 
  457 static void
  458 chvgpio_intr(void *arg)
  459 {
  460         struct chvgpio_softc *sc = arg;
  461         uint32_t reg;
  462         int line;
  463 
  464         reg = bus_read_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS);
  465         for (line = 0; line < 16; line++) {
  466                 if ((reg & (1 << line)) == 0)
  467                         continue;
  468                 bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 1 << line);
  469         }
  470 }
  471 
  472 static int
  473 chvgpio_detach(device_t dev)
  474 {
  475         struct chvgpio_softc *sc;
  476         sc = device_get_softc(dev);
  477 
  478         if (sc->sc_busdev)
  479                 gpiobus_detach_bus(dev);
  480 
  481         if (sc->intr_handle != NULL)
  482             bus_teardown_intr(sc->sc_dev, sc->sc_irq_res, sc->intr_handle);
  483         if (sc->sc_irq_res != NULL)
  484                 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res);
  485         if (sc->sc_mem_res != NULL)
  486                 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, sc->sc_mem_res);
  487 
  488         CHVGPIO_LOCK_DESTROY(sc);
  489 
  490     return (0);
  491 }
  492 
  493 static device_method_t chvgpio_methods[] = {
  494         DEVMETHOD(device_probe,         chvgpio_probe),
  495         DEVMETHOD(device_attach,        chvgpio_attach),
  496         DEVMETHOD(device_detach,        chvgpio_detach),
  497 
  498         /* GPIO protocol */
  499         DEVMETHOD(gpio_get_bus,         chvgpio_get_bus),
  500         DEVMETHOD(gpio_pin_max,         chvgpio_pin_max),
  501         DEVMETHOD(gpio_pin_getname,     chvgpio_pin_getname),
  502         DEVMETHOD(gpio_pin_getflags,    chvgpio_pin_getflags),
  503         DEVMETHOD(gpio_pin_getcaps,     chvgpio_pin_getcaps),
  504         DEVMETHOD(gpio_pin_setflags,    chvgpio_pin_setflags),
  505         DEVMETHOD(gpio_pin_get,         chvgpio_pin_get),
  506         DEVMETHOD(gpio_pin_set,         chvgpio_pin_set),
  507         DEVMETHOD(gpio_pin_toggle,      chvgpio_pin_toggle),
  508 
  509         DEVMETHOD_END
  510 };
  511 
  512 static driver_t chvgpio_driver = {
  513     .name = "gpio",
  514     .methods = chvgpio_methods,
  515     .size = sizeof(struct chvgpio_softc)
  516 };
  517 
  518 static devclass_t chvgpio_devclass;
  519 DRIVER_MODULE(chvgpio, acpi, chvgpio_driver, chvgpio_devclass, NULL , NULL);
  520 MODULE_DEPEND(chvgpio, acpi, 1, 1, 1);
  521 MODULE_DEPEND(chvgpio, gpiobus, 1, 1, 1);
  522 
  523 MODULE_VERSION(chvgpio, 1);

Cache object: 222258b27b4c8852244b161922046658


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