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

Cache object: c5bc7da1766f8ee60549b03021df96be


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