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/mips/cavium/octeon_gpio.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2011, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
    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 unmodified, this list of conditions, and the following
   12  *    disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 /*
   31  * GPIO driver for Cavium Octeon 
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __FBSDID("$FreeBSD: stable/12/sys/mips/cavium/octeon_gpio.c 326259 2017-11-27 15:07:26Z pfg $");
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/bus.h>
   40 
   41 #include <sys/kernel.h>
   42 #include <sys/module.h>
   43 #include <sys/rman.h>
   44 #include <sys/lock.h>
   45 #include <sys/mutex.h>
   46 #include <sys/gpio.h>
   47 
   48 #include <machine/bus.h>
   49 #include <machine/resource.h>
   50 
   51 #include <contrib/octeon-sdk/cvmx.h>
   52 #include <contrib/octeon-sdk/cvmx-gpio.h>
   53 #include <mips/cavium/octeon_irq.h>
   54 
   55 #include <mips/cavium/octeon_gpiovar.h>
   56 #include <dev/gpio/gpiobusvar.h>
   57 
   58 #include "gpio_if.h"
   59 
   60 #define DEFAULT_CAPS    (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
   61 
   62 struct octeon_gpio_pin {
   63         const char *name;
   64         int pin;
   65         int flags;
   66 };
   67 
   68 /*
   69  * on CAP100 GPIO 7 is "Factory defaults" button
   70  *
   71  */
   72 static struct octeon_gpio_pin octeon_gpio_pins[] = {
   73         { "F/D", 7,  GPIO_PIN_INPUT},
   74         { NULL, 0, 0},
   75 };
   76 
   77 /*
   78  * Helpers
   79  */
   80 static void octeon_gpio_pin_configure(struct octeon_gpio_softc *sc, 
   81     struct gpio_pin *pin, uint32_t flags);
   82 
   83 /*
   84  * Driver stuff
   85  */
   86 static void octeon_gpio_identify(driver_t *, device_t);
   87 static int octeon_gpio_probe(device_t dev);
   88 static int octeon_gpio_attach(device_t dev);
   89 static int octeon_gpio_detach(device_t dev);
   90 static int octeon_gpio_filter(void *arg);
   91 static void octeon_gpio_intr(void *arg);
   92 
   93 /*
   94  * GPIO interface
   95  */
   96 static device_t octeon_gpio_get_bus(device_t);
   97 static int octeon_gpio_pin_max(device_t dev, int *maxpin);
   98 static int octeon_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps);
   99 static int octeon_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t
  100     *flags);
  101 static int octeon_gpio_pin_getname(device_t dev, uint32_t pin, char *name);
  102 static int octeon_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags);
  103 static int octeon_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value);
  104 static int octeon_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val);
  105 static int octeon_gpio_pin_toggle(device_t dev, uint32_t pin);
  106 
  107 static void
  108 octeon_gpio_pin_configure(struct octeon_gpio_softc *sc, struct gpio_pin *pin,
  109     unsigned int flags)
  110 {
  111         uint32_t mask;
  112         cvmx_gpio_bit_cfgx_t gpio_cfgx;
  113 
  114         mask = 1 << pin->gp_pin;
  115         GPIO_LOCK(sc);
  116 
  117         /*
  118          * Manage input/output
  119          */
  120         if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
  121                 gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(pin->gp_pin));
  122                 pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
  123                 if (flags & GPIO_PIN_OUTPUT) {
  124                         pin->gp_flags |= GPIO_PIN_OUTPUT;
  125                         gpio_cfgx.s.tx_oe = 1;
  126                 }
  127                 else {
  128                         pin->gp_flags |= GPIO_PIN_INPUT;
  129                         gpio_cfgx.s.tx_oe = 0;
  130                 }
  131                 if (flags & GPIO_PIN_INVIN)
  132                         gpio_cfgx.s.rx_xor = 1;
  133                 else
  134                         gpio_cfgx.s.rx_xor = 0;
  135                 cvmx_write_csr(CVMX_GPIO_BIT_CFGX(pin->gp_pin), gpio_cfgx.u64);
  136         }
  137 
  138         GPIO_UNLOCK(sc);
  139 }
  140 
  141 static device_t
  142 octeon_gpio_get_bus(device_t dev)
  143 {
  144         struct octeon_gpio_softc *sc;
  145 
  146         sc = device_get_softc(dev);
  147 
  148         return (sc->busdev);
  149 }
  150 
  151 static int
  152 octeon_gpio_pin_max(device_t dev, int *maxpin)
  153 {
  154 
  155         *maxpin = OCTEON_GPIO_PINS - 1;
  156         return (0);
  157 }
  158 
  159 static int
  160 octeon_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
  161 {
  162         struct octeon_gpio_softc *sc = device_get_softc(dev);
  163         int i;
  164 
  165         for (i = 0; i < sc->gpio_npins; i++) {
  166                 if (sc->gpio_pins[i].gp_pin == pin)
  167                         break;
  168         }
  169 
  170         if (i >= sc->gpio_npins)
  171                 return (EINVAL);
  172 
  173         GPIO_LOCK(sc);
  174         *caps = sc->gpio_pins[i].gp_caps;
  175         GPIO_UNLOCK(sc);
  176 
  177         return (0);
  178 }
  179 
  180 static int
  181 octeon_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
  182 {
  183         struct octeon_gpio_softc *sc = device_get_softc(dev);
  184         int i;
  185 
  186         for (i = 0; i < sc->gpio_npins; i++) {
  187                 if (sc->gpio_pins[i].gp_pin == pin)
  188                         break;
  189         }
  190 
  191         if (i >= sc->gpio_npins)
  192                 return (EINVAL);
  193 
  194         GPIO_LOCK(sc);
  195         *flags = sc->gpio_pins[i].gp_flags;
  196         GPIO_UNLOCK(sc);
  197 
  198         return (0);
  199 }
  200 
  201 static int
  202 octeon_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
  203 {
  204         struct octeon_gpio_softc *sc = device_get_softc(dev);
  205         int i;
  206 
  207         for (i = 0; i < sc->gpio_npins; i++) {
  208                 if (sc->gpio_pins[i].gp_pin == pin)
  209                         break;
  210         }
  211 
  212         if (i >= sc->gpio_npins)
  213                 return (EINVAL);
  214 
  215         GPIO_LOCK(sc);
  216         memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME);
  217         GPIO_UNLOCK(sc);
  218 
  219         return (0);
  220 }
  221 
  222 static int
  223 octeon_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
  224 {
  225         int i;
  226         struct octeon_gpio_softc *sc = device_get_softc(dev);
  227 
  228         for (i = 0; i < sc->gpio_npins; i++) {
  229                 if (sc->gpio_pins[i].gp_pin == pin)
  230                         break;
  231         }
  232 
  233         if (i >= sc->gpio_npins)
  234                 return (EINVAL);
  235 
  236         octeon_gpio_pin_configure(sc, &sc->gpio_pins[i], flags);
  237 
  238         return (0);
  239 }
  240 
  241 static int
  242 octeon_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
  243 {
  244         struct octeon_gpio_softc *sc = device_get_softc(dev);
  245         int i;
  246 
  247         for (i = 0; i < sc->gpio_npins; i++) {
  248                 if (sc->gpio_pins[i].gp_pin == pin)
  249                         break;
  250         }
  251 
  252         if (i >= sc->gpio_npins)
  253                 return (EINVAL);
  254 
  255         GPIO_LOCK(sc);
  256         if (value)
  257                 cvmx_gpio_set(1 << pin);
  258         else
  259                 cvmx_gpio_clear(1 << pin);
  260         GPIO_UNLOCK(sc);
  261 
  262         return (0);
  263 }
  264 
  265 static int
  266 octeon_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
  267 {
  268         struct octeon_gpio_softc *sc = device_get_softc(dev);
  269         int i;
  270         uint64_t state;
  271 
  272         for (i = 0; i < sc->gpio_npins; i++) {
  273                 if (sc->gpio_pins[i].gp_pin == pin)
  274                         break;
  275         }
  276 
  277         if (i >= sc->gpio_npins)
  278                 return (EINVAL);
  279 
  280         GPIO_LOCK(sc);
  281         state = cvmx_gpio_read();
  282         *val = (state & (1 << pin)) ? 1 : 0;
  283         GPIO_UNLOCK(sc);
  284 
  285         return (0);
  286 }
  287 
  288 static int
  289 octeon_gpio_pin_toggle(device_t dev, uint32_t pin)
  290 {
  291         int i;
  292         uint64_t state;
  293         struct octeon_gpio_softc *sc = device_get_softc(dev);
  294 
  295         for (i = 0; i < sc->gpio_npins; i++) {
  296                 if (sc->gpio_pins[i].gp_pin == pin)
  297                         break;
  298         }
  299 
  300         if (i >= sc->gpio_npins)
  301                 return (EINVAL);
  302 
  303         GPIO_LOCK(sc);
  304         /*
  305          * XXX: Need to check if read returns actual state of output 
  306          * pins or we need to keep this information by ourself
  307          */
  308         state = cvmx_gpio_read();
  309         if (state & (1 << pin))
  310                 cvmx_gpio_clear(1 << pin);
  311         else
  312                 cvmx_gpio_set(1 << pin);
  313         GPIO_UNLOCK(sc);
  314 
  315         return (0);
  316 }
  317 
  318 static int
  319 octeon_gpio_filter(void *arg)
  320 {
  321         cvmx_gpio_bit_cfgx_t gpio_cfgx;
  322         void **cookie = arg;
  323         struct octeon_gpio_softc *sc = *cookie;
  324         long int irq = (cookie - sc->gpio_intr_cookies);
  325         
  326         if ((irq < 0) || (irq >= OCTEON_GPIO_IRQS))
  327                 return (FILTER_STRAY);
  328 
  329         gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(irq));
  330         /* Clear rising edge detector */
  331         if (gpio_cfgx.s.int_type == OCTEON_GPIO_IRQ_EDGE)
  332                 cvmx_gpio_interrupt_clear(1 << irq);
  333         /* disable interrupt  */
  334         gpio_cfgx.s.int_en = 0;
  335         cvmx_write_csr(CVMX_GPIO_BIT_CFGX(irq), gpio_cfgx.u64);
  336 
  337         return (FILTER_SCHEDULE_THREAD);
  338 }
  339 
  340 static void
  341 octeon_gpio_intr(void *arg)
  342 {
  343         cvmx_gpio_bit_cfgx_t gpio_cfgx;
  344         void **cookie = arg;
  345         struct octeon_gpio_softc *sc = *cookie;
  346         long int irq = (cookie - sc->gpio_intr_cookies);
  347 
  348         if ((irq < 0) || (irq >= OCTEON_GPIO_IRQS)) {
  349                 printf("%s: invalid GPIO IRQ: %ld\n", 
  350                     __func__, irq);
  351                 return;
  352         }
  353 
  354         GPIO_LOCK(sc);
  355         gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(irq));
  356         /* disable interrupt  */
  357         gpio_cfgx.s.int_en = 1;
  358         cvmx_write_csr(CVMX_GPIO_BIT_CFGX(irq), gpio_cfgx.u64);
  359 
  360         /* TODO: notify bus here or something */
  361         printf("GPIO IRQ for pin %ld\n", irq);
  362         GPIO_UNLOCK(sc);
  363 }
  364 
  365 static void
  366 octeon_gpio_identify(driver_t *drv, device_t parent)
  367 {
  368 
  369         BUS_ADD_CHILD(parent, 0, "gpio", 0);
  370 }
  371 
  372 static int
  373 octeon_gpio_probe(device_t dev)
  374 {
  375 
  376         device_set_desc(dev, "Cavium Octeon GPIO driver");
  377         return (0);
  378 }
  379 
  380 static int
  381 octeon_gpio_attach(device_t dev)
  382 {
  383         struct octeon_gpio_softc *sc = device_get_softc(dev);
  384         struct octeon_gpio_pin *pinp;
  385         cvmx_gpio_bit_cfgx_t gpio_cfgx;
  386         
  387         int i;
  388 
  389         KASSERT((device_get_unit(dev) == 0),
  390             ("octeon_gpio: Only one gpio module supported"));
  391 
  392         mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
  393 
  394         for ( i = 0; i < OCTEON_GPIO_IRQS; i++) {
  395                 if ((sc->gpio_irq_res[i] = bus_alloc_resource(dev, 
  396                     SYS_RES_IRQ, &sc->gpio_irq_rid[i], 
  397                     OCTEON_IRQ_GPIO0 + i, OCTEON_IRQ_GPIO0 + i, 1, 
  398                     RF_SHAREABLE | RF_ACTIVE)) == NULL) {
  399                         device_printf(dev, "unable to allocate IRQ resource\n");
  400                         octeon_gpio_detach(dev);
  401                         return (ENXIO);
  402                 }
  403 
  404                 sc->gpio_intr_cookies[i] = sc;
  405                 if ((bus_setup_intr(dev, sc->gpio_irq_res[i], INTR_TYPE_MISC, 
  406                     octeon_gpio_filter, octeon_gpio_intr, 
  407                     &(sc->gpio_intr_cookies[i]), &sc->gpio_ih[i]))) {
  408                         device_printf(dev,
  409                         "WARNING: unable to register interrupt handler\n");
  410                         octeon_gpio_detach(dev);
  411                         return (ENXIO);
  412                 }
  413         }
  414 
  415         sc->dev = dev;
  416         /* Configure all pins as input */
  417         /* disable interrupts for all pins */
  418         pinp = octeon_gpio_pins;
  419         i = 0;
  420         while (pinp->name) {
  421                 strncpy(sc->gpio_pins[i].gp_name, pinp->name, GPIOMAXNAME);
  422                 sc->gpio_pins[i].gp_pin = pinp->pin;
  423                 sc->gpio_pins[i].gp_caps = DEFAULT_CAPS;
  424                 sc->gpio_pins[i].gp_flags = 0;
  425                 octeon_gpio_pin_configure(sc, &sc->gpio_pins[i], pinp->flags);
  426                 pinp++;
  427                 i++;
  428         }
  429 
  430         sc->gpio_npins = i;
  431 
  432 #if 0
  433         /*
  434          * Sample: how to enable edge-triggered interrupt
  435          * for GPIO pin
  436          */
  437         gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(7));
  438         gpio_cfgx.s.int_en = 1;
  439         gpio_cfgx.s.int_type = OCTEON_GPIO_IRQ_EDGE;
  440         cvmx_write_csr(CVMX_GPIO_BIT_CFGX(7), gpio_cfgx.u64);
  441 #endif
  442 
  443         if (bootverbose) {
  444                 for (i = 0; i < 16; i++) {
  445                         gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(i));
  446                         device_printf(dev, "[pin%d] output=%d, invinput=%d, intr=%d, intr_type=%s\n", 
  447                             i, gpio_cfgx.s.tx_oe, gpio_cfgx.s.rx_xor, 
  448                             gpio_cfgx.s.int_en, gpio_cfgx.s.int_type ? "rising edge" : "level");
  449                 }
  450         }
  451         sc->busdev = gpiobus_attach_bus(dev);
  452         if (sc->busdev == NULL) {
  453                 octeon_gpio_detach(dev);
  454                 return (ENXIO);
  455         }
  456 
  457         return (0);
  458 }
  459 
  460 static int
  461 octeon_gpio_detach(device_t dev)
  462 {
  463         struct octeon_gpio_softc *sc = device_get_softc(dev);
  464         int i;
  465 
  466         KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized"));
  467 
  468         for ( i = 0; i < OCTEON_GPIO_IRQS; i++) {
  469                 if (sc->gpio_ih[i])
  470                         bus_teardown_intr(dev, sc->gpio_irq_res[i],
  471                             sc->gpio_ih[i]);
  472                 if (sc->gpio_irq_res[i])
  473                         bus_release_resource(dev, SYS_RES_IRQ,
  474                             sc->gpio_irq_rid[i], sc->gpio_irq_res[i]);
  475         }
  476         gpiobus_detach_bus(dev);
  477         mtx_destroy(&sc->gpio_mtx);
  478 
  479         return(0);
  480 }
  481 
  482 static device_method_t octeon_gpio_methods[] = {
  483         DEVMETHOD(device_identify, octeon_gpio_identify),
  484         DEVMETHOD(device_probe, octeon_gpio_probe),
  485         DEVMETHOD(device_attach, octeon_gpio_attach),
  486         DEVMETHOD(device_detach, octeon_gpio_detach),
  487 
  488         /* GPIO protocol */
  489         DEVMETHOD(gpio_get_bus, octeon_gpio_get_bus),
  490         DEVMETHOD(gpio_pin_max, octeon_gpio_pin_max),
  491         DEVMETHOD(gpio_pin_getname, octeon_gpio_pin_getname),
  492         DEVMETHOD(gpio_pin_getflags, octeon_gpio_pin_getflags),
  493         DEVMETHOD(gpio_pin_getcaps, octeon_gpio_pin_getcaps),
  494         DEVMETHOD(gpio_pin_setflags, octeon_gpio_pin_setflags),
  495         DEVMETHOD(gpio_pin_get, octeon_gpio_pin_get),
  496         DEVMETHOD(gpio_pin_set, octeon_gpio_pin_set),
  497         DEVMETHOD(gpio_pin_toggle, octeon_gpio_pin_toggle),
  498         {0, 0},
  499 };
  500 
  501 static driver_t octeon_gpio_driver = {
  502         "gpio",
  503         octeon_gpio_methods,
  504         sizeof(struct octeon_gpio_softc),
  505 };
  506 static devclass_t octeon_gpio_devclass;
  507 
  508 DRIVER_MODULE(octeon_gpio, ciu, octeon_gpio_driver, octeon_gpio_devclass, 0, 0);

Cache object: 43bbacb92d502fed98f6f54051839545


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