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     int rv;
  347     
  348     if (acpi_disabled("chvgpio"))
  349         return (ENXIO);
  350     rv = ACPI_ID_PROBE(device_get_parent(dev), dev, chvgpio_hids, NULL);
  351     if (rv <= 0)
  352         device_set_desc(dev, "Intel Cherry View GPIO");
  353     return (rv);
  354 }
  355 
  356 static int
  357 chvgpio_attach(device_t dev)
  358 {
  359         struct chvgpio_softc *sc;
  360         ACPI_STATUS status;
  361         int uid;
  362         int i;
  363         int error;
  364 
  365         sc = device_get_softc(dev);
  366         sc->sc_dev = dev;
  367         sc->sc_handle = acpi_get_handle(dev);
  368 
  369         status = acpi_GetInteger(sc->sc_handle, "_UID", &uid);
  370         if (ACPI_FAILURE(status)) {
  371                 device_printf(dev, "failed to read _UID\n");
  372                 return (ENXIO);
  373         }
  374 
  375         CHVGPIO_LOCK_INIT(sc);
  376 
  377         switch (uid) {
  378         case SW_UID:
  379                 sc->sc_bank_prefix = SW_BANK_PREFIX;
  380                 sc->sc_pins = chv_southwest_pins;
  381                 sc->sc_pin_names = chv_southwest_pin_names;
  382                 break;
  383         case N_UID:
  384                 sc->sc_bank_prefix = N_BANK_PREFIX;
  385                 sc->sc_pins = chv_north_pins;
  386                 sc->sc_pin_names = chv_north_pin_names;
  387                 break;
  388         case E_UID:
  389                 sc->sc_bank_prefix = E_BANK_PREFIX;
  390                 sc->sc_pins = chv_east_pins;
  391                 sc->sc_pin_names = chv_east_pin_names;
  392                 break;
  393         case SE_UID:
  394                 sc->sc_bank_prefix = SE_BANK_PREFIX;
  395                 sc->sc_pins = chv_southeast_pins;
  396                 sc->sc_pin_names = chv_southeast_pin_names;
  397                 break;
  398         default:
  399                 device_printf(dev, "invalid _UID value: %d\n", uid);
  400                 return (ENXIO);
  401         }
  402 
  403         for (i = 0; sc->sc_pins[i] >= 0; i++) {
  404                 sc->sc_npins += sc->sc_pins[i];
  405                 sc->sc_ngroups++;
  406         }
  407 
  408         sc->sc_mem_rid = 0;
  409         sc->sc_mem_res = bus_alloc_resource_any(sc->sc_dev, SYS_RES_MEMORY,
  410                 &sc->sc_mem_rid, RF_ACTIVE);
  411         if (sc->sc_mem_res == NULL) {
  412                 CHVGPIO_LOCK_DESTROY(sc);
  413                 device_printf(dev, "can't allocate memory resource\n");
  414                 return (ENOMEM);
  415         }
  416 
  417         sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
  418                 &sc->sc_irq_rid, RF_ACTIVE);
  419 
  420         if (!sc->sc_irq_res) {
  421                 CHVGPIO_LOCK_DESTROY(sc);
  422                 bus_release_resource(dev, SYS_RES_MEMORY,
  423                         sc->sc_mem_rid, sc->sc_mem_res);
  424                 device_printf(dev, "can't allocate irq resource\n");
  425                 return (ENOMEM);
  426         }
  427 
  428         error = bus_setup_intr(sc->sc_dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
  429                 NULL, chvgpio_intr, sc, &sc->intr_handle);
  430 
  431 
  432         if (error) {
  433                 device_printf(sc->sc_dev, "unable to setup irq: error %d\n", error);
  434                 CHVGPIO_LOCK_DESTROY(sc);
  435                 bus_release_resource(dev, SYS_RES_MEMORY,
  436                         sc->sc_mem_rid, sc->sc_mem_res);
  437                 bus_release_resource(dev, SYS_RES_IRQ,
  438                         sc->sc_irq_rid, sc->sc_irq_res);
  439                 return (ENXIO);
  440         }
  441 
  442         /* Mask and ack all interrupts. */
  443         bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_MASK, 0);
  444         bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 0xffff);
  445 
  446         sc->sc_busdev = gpiobus_attach_bus(dev);
  447         if (sc->sc_busdev == NULL) {
  448                 CHVGPIO_LOCK_DESTROY(sc);
  449                 bus_release_resource(dev, SYS_RES_MEMORY,
  450                         sc->sc_mem_rid, sc->sc_mem_res);
  451                 bus_release_resource(dev, SYS_RES_IRQ,
  452                         sc->sc_irq_rid, sc->sc_irq_res);
  453                 return (ENXIO);
  454         }
  455 
  456         return (0);
  457 }
  458 
  459 static void
  460 chvgpio_intr(void *arg)
  461 {
  462         struct chvgpio_softc *sc = arg;
  463         uint32_t reg;
  464         int line;
  465 
  466         reg = bus_read_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS);
  467         for (line = 0; line < 16; line++) {
  468                 if ((reg & (1 << line)) == 0)
  469                         continue;
  470                 bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 1 << line);
  471         }
  472 }
  473 
  474 static int
  475 chvgpio_detach(device_t dev)
  476 {
  477         struct chvgpio_softc *sc;
  478         sc = device_get_softc(dev);
  479 
  480         if (sc->sc_busdev)
  481                 gpiobus_detach_bus(dev);
  482 
  483         if (sc->intr_handle != NULL)
  484             bus_teardown_intr(sc->sc_dev, sc->sc_irq_res, sc->intr_handle);
  485         if (sc->sc_irq_res != NULL)
  486                 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res);
  487         if (sc->sc_mem_res != NULL)
  488                 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, sc->sc_mem_res);
  489 
  490         CHVGPIO_LOCK_DESTROY(sc);
  491 
  492     return (0);
  493 }
  494 
  495 static device_method_t chvgpio_methods[] = {
  496         DEVMETHOD(device_probe,         chvgpio_probe),
  497         DEVMETHOD(device_attach,        chvgpio_attach),
  498         DEVMETHOD(device_detach,        chvgpio_detach),
  499 
  500         /* GPIO protocol */
  501         DEVMETHOD(gpio_get_bus,         chvgpio_get_bus),
  502         DEVMETHOD(gpio_pin_max,         chvgpio_pin_max),
  503         DEVMETHOD(gpio_pin_getname,     chvgpio_pin_getname),
  504         DEVMETHOD(gpio_pin_getflags,    chvgpio_pin_getflags),
  505         DEVMETHOD(gpio_pin_getcaps,     chvgpio_pin_getcaps),
  506         DEVMETHOD(gpio_pin_setflags,    chvgpio_pin_setflags),
  507         DEVMETHOD(gpio_pin_get,         chvgpio_pin_get),
  508         DEVMETHOD(gpio_pin_set,         chvgpio_pin_set),
  509         DEVMETHOD(gpio_pin_toggle,      chvgpio_pin_toggle),
  510 
  511         DEVMETHOD_END
  512 };
  513 
  514 static driver_t chvgpio_driver = {
  515     .name = "gpio",
  516     .methods = chvgpio_methods,
  517     .size = sizeof(struct chvgpio_softc)
  518 };
  519 
  520 DRIVER_MODULE(chvgpio, acpi, chvgpio_driver, NULL, NULL);
  521 MODULE_DEPEND(chvgpio, acpi, 1, 1, 1);
  522 MODULE_DEPEND(chvgpio, gpiobus, 1, 1, 1);
  523 
  524 MODULE_VERSION(chvgpio, 1);

Cache object: 870f8b176959c3bb1e224ae749cbae3e


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