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/gpiospi.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, Aleksandr Rybalko <ray@dlink.ua>
    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 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD$");
   30 
   31 #include "opt_gpio.h"
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 
   36 #include <sys/bus.h>
   37 #include <sys/kernel.h>
   38 #include <sys/module.h>
   39 #include <sys/rman.h>
   40 #include <sys/sysctl.h>
   41 
   42 #include <sys/gpio.h>
   43 #include "gpiobus_if.h"
   44 
   45 #include <dev/gpio/gpiobusvar.h>
   46 
   47 #include <dev/spibus/spi.h>
   48 #include <dev/spibus/spibusvar.h>
   49 #include "spibus_if.h"
   50 
   51 #ifdef  GPIO_SPI_DEBUG
   52 #define dprintf printf
   53 #else
   54 #define dprintf(x, arg...)
   55 #endif  /* GPIO_SPI_DEBUG */
   56 
   57 struct gpio_spi_softc {
   58         device_t        sc_dev;
   59         device_t        sc_busdev;
   60         int             sc_freq;
   61         uint8_t         sc_sclk;
   62         uint8_t         sc_miso;
   63         uint8_t         sc_mosi;
   64         uint8_t         sc_cs0;
   65         uint8_t         sc_cs1;
   66         uint8_t         sc_cs2;
   67         uint8_t         sc_cs3;
   68 };
   69 
   70 static void gpio_spi_chip_activate(struct gpio_spi_softc *, int);
   71 static void gpio_spi_chip_deactivate(struct gpio_spi_softc *, int);
   72 
   73 static int
   74 gpio_spi_probe(device_t dev)
   75 {
   76         device_set_desc(dev, "GPIO SPI bit-banging driver");
   77         return (0);
   78 }
   79 
   80 static void
   81 gpio_delay(struct gpio_spi_softc *sc)
   82 {
   83         int d;
   84 
   85         d = sc->sc_freq / 1000000;
   86         if (d == 0)
   87                 d = 1;
   88 
   89         DELAY(d);
   90 }
   91 
   92 static int
   93 gpio_spi_attach(device_t dev)
   94 {
   95         uint32_t value;
   96         struct gpio_spi_softc *sc;
   97 
   98         sc = device_get_softc(dev);
   99         sc->sc_dev = dev;
  100         sc->sc_busdev = device_get_parent(dev);
  101 
  102         /* Required variables */
  103         if (resource_int_value(device_get_name(dev),
  104             device_get_unit(dev), "sclk", &value))
  105                  return (ENXIO);
  106         sc->sc_sclk = value & 0xff;
  107 
  108         if (resource_int_value(device_get_name(dev),
  109             device_get_unit(dev), "mosi", &value))
  110                  return (ENXIO);
  111         sc->sc_mosi = value & 0xff;
  112 
  113         /* Handle no miso; we just never read back from the device */
  114         if (resource_int_value(device_get_name(dev),
  115             device_get_unit(dev), "miso", &value))
  116                  value = 0xff;
  117         sc->sc_miso = value & 0xff;
  118 
  119         if (resource_int_value(device_get_name(dev),
  120             device_get_unit(dev), "cs0", &value))
  121                  return (ENXIO);
  122         sc->sc_cs0 = value & 0xff;
  123 
  124         /* Optional variables */
  125         if (resource_int_value(device_get_name(dev),
  126             device_get_unit(dev), "cs1", &value))
  127                 value = 0xff;
  128         sc->sc_cs1 = value & 0xff;
  129 
  130         if (resource_int_value(device_get_name(dev),
  131             device_get_unit(dev), "cs2", &value))
  132                 value = 0xff;
  133         sc->sc_cs2 = value & 0xff;
  134 
  135         if (resource_int_value(device_get_name(dev),
  136             device_get_unit(dev), "cs3", &value))
  137                 value = 0xff;
  138         sc->sc_cs3 = value & 0xff;
  139 
  140         /* Default to 100KHz */
  141         if (resource_int_value(device_get_name(dev),
  142             device_get_unit(dev), "freq", &value)) {
  143                 value = 100000;
  144         }
  145         sc->sc_freq = value;
  146 
  147         if (bootverbose) {
  148                 device_printf(dev, "frequency: %d Hz\n",
  149                     sc->sc_freq);
  150                 device_printf(dev,
  151                     "Use GPIO pins: sclk=%d, mosi=%d, miso=%d, "
  152                     "cs0=%d, cs1=%d, cs2=%d, cs3=%d\n",
  153                     sc->sc_sclk, sc->sc_mosi, sc->sc_miso,
  154                     sc->sc_cs0, sc->sc_cs1, sc->sc_cs2, sc->sc_cs3);
  155         }
  156 
  157         /* Set directions */
  158         GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_sclk,
  159             GPIO_PIN_OUTPUT|GPIO_PIN_PULLDOWN);
  160         GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_mosi,
  161             GPIO_PIN_OUTPUT|GPIO_PIN_PULLDOWN);
  162         if (sc->sc_miso != 0xff) {
  163                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_miso,
  164                     GPIO_PIN_INPUT|GPIO_PIN_PULLDOWN);
  165         }
  166 
  167         GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_cs0,
  168             GPIO_PIN_OUTPUT|GPIO_PIN_PULLUP);
  169 
  170         if (sc->sc_cs1 != 0xff)
  171                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_cs1,
  172                     GPIO_PIN_OUTPUT|GPIO_PIN_PULLUP);
  173         if (sc->sc_cs2 != 0xff)
  174                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_cs2,
  175                     GPIO_PIN_OUTPUT|GPIO_PIN_PULLUP);
  176         if (sc->sc_cs3 != 0xff)
  177                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sc_cs3,
  178                     GPIO_PIN_OUTPUT|GPIO_PIN_PULLUP);
  179 
  180         gpio_spi_chip_deactivate(sc, -1);
  181 
  182         device_add_child(dev, "spibus", -1);
  183         return (bus_generic_attach(dev));
  184 }
  185 
  186 static int
  187 gpio_spi_detach(device_t dev)
  188 {
  189 
  190         return (0);
  191 }
  192 
  193 static void
  194 gpio_spi_chip_activate(struct gpio_spi_softc *sc, int cs)
  195 {
  196 
  197         /* called with locked gpiobus */
  198         switch (cs) {
  199         case 0:
  200                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  201                     sc->sc_cs0, 0);
  202                 break;
  203         case 1:
  204                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  205                     sc->sc_cs1, 0);
  206                 break;
  207         case 2:
  208                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  209                     sc->sc_cs2, 0);
  210                 break;
  211         case 3:
  212                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  213                     sc->sc_cs3, 0);
  214                 break;
  215         default:
  216                 device_printf(sc->sc_dev, "don't have CS%d\n", cs);
  217         }
  218 
  219         gpio_delay(sc);
  220 }
  221 
  222 static void
  223 gpio_spi_chip_deactivate(struct gpio_spi_softc *sc, int cs)
  224 {
  225 
  226         /* called wth locked gpiobus */
  227         /*
  228          * Put CSx to high
  229          */
  230         switch (cs) {
  231         case -1:
  232                 /* All CS */
  233                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  234                     sc->sc_cs0, 1);
  235                 if (sc->sc_cs1 == 0xff) break;
  236                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  237                     sc->sc_cs1, 1);
  238                 if (sc->sc_cs2 == 0xff) break;
  239                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  240                     sc->sc_cs2, 1);
  241                 if (sc->sc_cs3 == 0xff) break;
  242                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  243                     sc->sc_cs3, 1);
  244                 break;
  245         case 0:
  246                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  247                     sc->sc_cs0, 1);
  248                 break;
  249         case 1:
  250                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  251                     sc->sc_cs1, 1);
  252                 break;
  253         case 2:
  254                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  255                     sc->sc_cs2, 1);
  256                 break;
  257         case 3:
  258                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  259                     sc->sc_cs3, 1);
  260                 break;
  261         default:
  262                 device_printf(sc->sc_dev, "don't have CS%d\n", cs);
  263         }
  264 }
  265 
  266 static uint8_t
  267 gpio_spi_txrx(struct gpio_spi_softc *sc, int cs, int mode, uint8_t data)
  268 {
  269         uint32_t mask, out = 0;
  270         unsigned int bit;
  271 
  272 
  273         /* called with locked gpiobus */
  274 
  275         for (mask = 0x80; mask > 0; mask >>= 1) {
  276                 if ((mode == SPIBUS_MODE_CPOL) ||
  277                     (mode == SPIBUS_MODE_CPHA)) {
  278                         /* If mode 1 or 2 */
  279 
  280                         /* first step */
  281                         GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  282                             sc->sc_mosi, (data & mask)?1:0);
  283                         GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  284                             sc->sc_sclk, 0);
  285                         gpio_delay(sc);
  286                         /* second step */
  287                         if (sc->sc_miso != 0xff) {
  288                                 GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev,
  289                                     sc->sc_miso, &bit);
  290                                 out |= bit?mask:0;
  291                         }
  292                         /* Data captured */
  293                         gpio_delay(sc);
  294                         GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  295                             sc->sc_sclk, 1);
  296                         gpio_delay(sc);
  297                 } else {
  298                         /* If mode 0 or 3 */
  299 
  300                         /* first step */
  301                         GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  302                             sc->sc_mosi, (data & mask)?1:0);
  303                         GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  304                             sc->sc_sclk, 1);
  305                         gpio_delay(sc);
  306                         /* second step */
  307                         if (sc->sc_miso != 0xff) {
  308                                 GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev,
  309                                     sc->sc_miso, &bit);
  310                                 out |= bit?mask:0;
  311                         }
  312                          /* Data captured */
  313                         gpio_delay(sc);
  314                         GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  315                             sc->sc_sclk, 0);
  316                         gpio_delay(sc);
  317                 }
  318         }
  319 
  320         return (out & 0xff);
  321 }
  322 
  323 static int
  324 gpio_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
  325 {
  326         struct gpio_spi_softc *sc;
  327         uint8_t *buf_in, *buf_out;
  328         struct spibus_ivar *devi = SPIBUS_IVAR(child);
  329         int i;
  330 
  331         sc = device_get_softc(dev);
  332 
  333         KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 
  334             ("TX/RX command sizes should be equal"));
  335         KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 
  336             ("TX/RX data sizes should be equal"));
  337 
  338         gpio_spi_chip_activate(sc, devi->cs);
  339 
  340         /* Preset pins */
  341         if ((devi->mode == SPIBUS_MODE_CPOL) ||
  342             (devi->mode == SPIBUS_MODE_CPHA)) {
  343                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  344                     sc->sc_sclk, 1);
  345         } else {
  346                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  347                     sc->sc_sclk, 0);
  348         }
  349 
  350         /*
  351          * Transfer command
  352          */
  353         buf_out = (uint8_t *)cmd->tx_cmd;
  354         buf_in = (uint8_t *)cmd->rx_cmd;
  355 
  356         for (i = 0; i < cmd->tx_cmd_sz; i++)
  357                 buf_in[i] = gpio_spi_txrx(sc, devi->cs, devi->mode, buf_out[i]);
  358 
  359         /*
  360          * Receive/transmit data (depends on command)
  361          */
  362         buf_out = (uint8_t *)cmd->tx_data;
  363         buf_in = (uint8_t *)cmd->rx_data;
  364         for (i = 0; i < cmd->tx_data_sz; i++)
  365                 buf_in[i] = gpio_spi_txrx(sc, devi->cs, devi->mode, buf_out[i]);
  366 
  367         /* Return pins to mode default */
  368         if ((devi->mode == SPIBUS_MODE_CPOL) ||
  369             (devi->mode == SPIBUS_MODE_CPHA)) {
  370                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  371                     sc->sc_sclk, 1);
  372         } else {
  373                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev,
  374                     sc->sc_sclk, 0);
  375         }
  376 
  377         gpio_spi_chip_deactivate(sc, devi->cs);
  378 
  379         return (0);
  380 }
  381 
  382 static device_method_t gpio_spi_methods[] = {
  383         /* Device interface */
  384         DEVMETHOD(device_probe,         gpio_spi_probe),
  385         DEVMETHOD(device_attach,        gpio_spi_attach),
  386         DEVMETHOD(device_detach,        gpio_spi_detach),
  387 
  388         DEVMETHOD(spibus_transfer,      gpio_spi_transfer),
  389 
  390         {0, 0}
  391 };
  392 
  393 static driver_t gpio_spi_driver = {
  394         "gpiospi",
  395         gpio_spi_methods,
  396         sizeof(struct gpio_spi_softc),
  397 };
  398 
  399 DRIVER_MODULE(gpiospi, gpiobus, gpio_spi_driver, 0, 0);
  400 DRIVER_MODULE(spibus, gpiospi, spibus_driver, 0, 0);
  401 MODULE_DEPEND(spi, gpiospi, 1, 1, 1);
  402 MODULE_DEPEND(gpiobus, gpiospi, 1, 1, 1);

Cache object: 48b3b7debce22d3bd81330361c65d6de


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