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/arm/xilinx/zy7_spi.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) 2018 Thomas Skibo <thomasskibo@yahoo.com>
    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, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  * $FreeBSD$
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/conf.h>
   35 #include <sys/kernel.h>
   36 #include <sys/module.h>
   37 #include <sys/sysctl.h>
   38 #include <sys/lock.h>
   39 #include <sys/mutex.h>
   40 #include <sys/resource.h>
   41 #include <sys/rman.h>
   42 #include <sys/uio.h>
   43 
   44 #include <machine/bus.h>
   45 #include <machine/resource.h>
   46 #include <machine/stdarg.h>
   47 
   48 #include <dev/fdt/fdt_common.h>
   49 #include <dev/ofw/ofw_bus.h>
   50 #include <dev/ofw/ofw_bus_subr.h>
   51 
   52 #include <dev/spibus/spi.h>
   53 #include <dev/spibus/spibusvar.h>
   54 
   55 #include "spibus_if.h"
   56 
   57 static struct ofw_compat_data compat_data[] = {
   58         {"xlnx,zy7_spi",                1},
   59         {"xlnx,zynq-spi-1.0",           1},
   60         {"cdns,spi-r1p6",               1},
   61         {NULL,                          0}
   62 };
   63 
   64 struct zy7_spi_softc {
   65         device_t                dev;
   66         device_t                child;
   67         struct mtx              sc_mtx;
   68         struct resource         *mem_res;
   69         struct resource         *irq_res;
   70         void                    *intrhandle;
   71 
   72         uint32_t                cfg_reg_shadow;
   73         uint32_t                spi_clock;
   74         uint32_t                ref_clock;
   75         unsigned int            spi_clk_real_freq;
   76         unsigned int            rx_overflows;
   77         unsigned int            tx_underflows;
   78         unsigned int            interrupts;
   79         unsigned int            stray_ints;
   80         struct spi_command      *cmd;
   81         int                     tx_bytes;       /* tx_cmd_sz + tx_data_sz */
   82         int                     tx_bytes_sent;
   83         int                     rx_bytes;       /* rx_cmd_sz + rx_data_sz */
   84         int                     rx_bytes_rcvd;
   85         int                     busy;
   86 };
   87 
   88 #define ZY7_SPI_DEFAULT_SPI_CLOCK       50000000
   89 
   90 #define SPI_SC_LOCK(sc)         mtx_lock(&(sc)->sc_mtx)
   91 #define SPI_SC_UNLOCK(sc)               mtx_unlock(&(sc)->sc_mtx)
   92 #define SPI_SC_LOCK_INIT(sc) \
   93         mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF)
   94 #define SPI_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx)
   95 #define SPI_SC_ASSERT_LOCKED(sc)        mtx_assert(&(sc)->sc_mtx, MA_OWNED)
   96 
   97 #define RD4(sc, off)            (bus_read_4((sc)->mem_res, (off)))
   98 #define WR4(sc, off, val)       (bus_write_4((sc)->mem_res, (off), (val)))
   99 
  100 /*
  101  * SPI device registers.
  102  * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
  103  * (v1.12.1) December 6, 2017.  Xilinx doc UG585.
  104  */
  105 #define ZY7_SPI_CONFIG_REG              0x0000
  106 #define   ZY7_SPI_CONFIG_MODEFAIL_GEN_EN        (1 << 17)
  107 #define   ZY7_SPI_CONFIG_MAN_STRT               (1 << 16)
  108 #define   ZY7_SPI_CONFIG_MAN_STRT_EN            (1 << 15)
  109 #define   ZY7_SPI_CONFIG_MAN_CS                 (1 << 14)
  110 #define   ZY7_SPI_CONFIG_CS_MASK                (0xf << 10)
  111 #define   ZY7_SPI_CONFIG_CS(x)                  ((0xf ^ (1 << (x))) << 10)
  112 #define   ZY7_SPI_CONFIG_PERI_SEL               (1 << 9)
  113 #define   ZY7_SPI_CONFIG_REF_CLK                (1 << 8)
  114 #define   ZY7_SPI_CONFIG_BAUD_RATE_DIV_MASK     (7 << 3)
  115 #define   ZY7_SPI_CONFIG_BAUD_RATE_DIV_SHIFT    3
  116 #define   ZY7_SPI_CONFIG_BAUD_RATE_DIV(x)       ((x) << 3) /* divide by 2<<x */
  117 #define   ZY7_SPI_CONFIG_CLK_PH                 (1 << 2)   /* clock phase */
  118 #define   ZY7_SPI_CONFIG_CLK_POL                (1 << 1)   /* clock polatiry */
  119 #define   ZY7_SPI_CONFIG_MODE_SEL               (1 << 0)   /* master enable */
  120 
  121 #define ZY7_SPI_INTR_STAT_REG           0x0004
  122 #define ZY7_SPI_INTR_EN_REG             0x0008
  123 #define ZY7_SPI_INTR_DIS_REG            0x000c
  124 #define ZY7_SPI_INTR_MASK_REG           0x0010
  125 #define   ZY7_SPI_INTR_TX_FIFO_UNDERFLOW        (1 << 6)
  126 #define   ZY7_SPI_INTR_RX_FIFO_FULL             (1 << 5)
  127 #define   ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY        (1 << 4)
  128 #define   ZY7_SPI_INTR_TX_FIFO_FULL             (1 << 3)
  129 #define   ZY7_SPI_INTR_TX_FIFO_NOT_FULL         (1 << 2)
  130 #define   ZY7_SPI_INTR_MODE_FAULT               (1 << 1)
  131 #define   ZY7_SPI_INTR_RX_OVERFLOW              (1 << 0)
  132 
  133 #define ZY7_SPI_EN_REG                  0x0014
  134 #define   ZY7_SPI_ENABLE                (1 << 0)
  135 
  136 #define ZY7_SPI_DELAY_CTRL_REG          0x0018
  137 #define   ZY7_SPI_DELAY_CTRL_BTWN_MASK          (0xff << 16)
  138 #define   ZY7_SPI_DELAY_CTRL_BTWN_SHIFT         16
  139 #define   ZY7_SPI_DELAY_CTRL_AFTER_MASK         (0xff << 8)
  140 #define   ZY7_SPI_DELAY_CTRL_AFTER_SHIFT        8
  141 #define   ZY7_SPI_DELAY_CTRL_INIT_MASK          (0xff << 0)
  142 #define   ZY7_SPI_DELAY_CTRL_INIT_SHIFT         0
  143 
  144 #define ZY7_SPI_TX_DATA_REG             0x001c
  145 #define ZY7_SPI_RX_DATA_REG             0x0020
  146 
  147 #define ZY7_SPI_SLV_IDLE_COUNT_REG      0x0024
  148 
  149 #define ZY7_SPI_TX_THRESH_REG           0x0028
  150 #define ZY7_SPI_RX_THRESH_REG           0x002c
  151 
  152 /* Fill hardware fifo with command and data bytes. */
  153 static void
  154 zy7_spi_write_fifo(struct zy7_spi_softc *sc, int nbytes)
  155 {
  156         uint8_t byte;
  157 
  158         while (nbytes > 0) {
  159                 if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz)
  160                         /* Writing command. */
  161                         byte = *((uint8_t *)sc->cmd->tx_cmd +
  162                                  sc->tx_bytes_sent);
  163                 else
  164                         /* Writing data. */
  165                         byte = *((uint8_t *)sc->cmd->tx_data +
  166                                  (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz));
  167 
  168                 WR4(sc, ZY7_SPI_TX_DATA_REG, (uint32_t)byte);
  169 
  170                 sc->tx_bytes_sent++;
  171                 nbytes--;
  172         }
  173 }
  174 
  175 /* Read hardware fifo data into command response and data buffers. */
  176 static void
  177 zy7_spi_read_fifo(struct zy7_spi_softc *sc)
  178 {
  179         uint8_t byte;
  180 
  181         do {
  182                 byte = RD4(sc, ZY7_SPI_RX_DATA_REG) & 0xff;
  183 
  184                 if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz)
  185                         /* Reading command. */
  186                         *((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd) =
  187                             byte;
  188                 else
  189                         /* Reading data. */
  190                         *((uint8_t *)sc->cmd->rx_data +
  191                             (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz)) =
  192                             byte;
  193 
  194                 sc->rx_bytes_rcvd++;
  195 
  196         } while (sc->rx_bytes_rcvd < sc->rx_bytes &&
  197             (RD4(sc, ZY7_SPI_INTR_STAT_REG) &
  198                 ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
  199 }
  200 
  201 /* End a transfer early by draining rx fifo and disabling interrupts. */
  202 static void
  203 zy7_spi_abort_transfer(struct zy7_spi_softc *sc)
  204 {
  205         /* Drain receive fifo. */
  206         while ((RD4(sc, ZY7_SPI_INTR_STAT_REG) &
  207                 ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
  208                 (void)RD4(sc, ZY7_SPI_RX_DATA_REG);
  209 
  210         /* Shut down interrupts. */
  211         WR4(sc, ZY7_SPI_INTR_DIS_REG,
  212             ZY7_SPI_INTR_RX_OVERFLOW |
  213             ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
  214             ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
  215 }
  216 
  217 static void
  218 zy7_spi_intr(void *arg)
  219 {
  220         struct zy7_spi_softc *sc = (struct zy7_spi_softc *)arg;
  221         uint32_t istatus;
  222 
  223         SPI_SC_LOCK(sc);
  224 
  225         sc->interrupts++;
  226 
  227         istatus = RD4(sc, ZY7_SPI_INTR_STAT_REG);
  228 
  229         /* Stray interrupts can happen if a transfer gets interrupted. */
  230         if (!sc->busy) {
  231                 sc->stray_ints++;
  232                 SPI_SC_UNLOCK(sc);
  233                 return;
  234         }
  235 
  236         if ((istatus & ZY7_SPI_INTR_RX_OVERFLOW) != 0) {
  237                 device_printf(sc->dev, "rx fifo overflow!\n");
  238                 sc->rx_overflows++;
  239 
  240                 /* Clear status bit. */
  241                 WR4(sc, ZY7_SPI_INTR_STAT_REG,
  242                     ZY7_SPI_INTR_RX_OVERFLOW);
  243         }
  244 
  245         /* Empty receive fifo before any more transmit data is sent. */
  246         if (sc->rx_bytes_rcvd < sc->rx_bytes &&
  247             (istatus & ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
  248                 zy7_spi_read_fifo(sc);
  249                 if (sc->rx_bytes_rcvd == sc->rx_bytes)
  250                         /* Disable receive interrupts. */
  251                         WR4(sc, ZY7_SPI_INTR_DIS_REG,
  252                             ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
  253                             ZY7_SPI_INTR_RX_OVERFLOW);
  254         }
  255 
  256         /* Count tx underflows.  They probably shouldn't happen. */
  257         if ((istatus & ZY7_SPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
  258                 sc->tx_underflows++;
  259 
  260                 /* Clear status bit. */
  261                 WR4(sc, ZY7_SPI_INTR_STAT_REG,
  262                     ZY7_SPI_INTR_TX_FIFO_UNDERFLOW);
  263         }
  264 
  265         /* Fill transmit fifo. */
  266         if (sc->tx_bytes_sent < sc->tx_bytes &&
  267             (istatus & ZY7_SPI_INTR_TX_FIFO_NOT_FULL) != 0) {
  268                 zy7_spi_write_fifo(sc, MIN(96, sc->tx_bytes -
  269                         sc->tx_bytes_sent));
  270 
  271                 if (sc->tx_bytes_sent == sc->tx_bytes) {
  272                         /* Disable transmit FIFO interrupt, enable receive
  273                          * FIFO interrupt.
  274                          */
  275                         WR4(sc, ZY7_SPI_INTR_DIS_REG,
  276                             ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
  277                         WR4(sc, ZY7_SPI_INTR_EN_REG,
  278                             ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY);
  279                 }
  280         }
  281 
  282         /* Finished with transfer? */
  283         if (sc->tx_bytes_sent == sc->tx_bytes &&
  284             sc->rx_bytes_rcvd == sc->rx_bytes) {
  285                 /* De-assert CS. */
  286                 sc->cfg_reg_shadow &=
  287                     ~(ZY7_SPI_CONFIG_CLK_PH | ZY7_SPI_CONFIG_CLK_POL);
  288                 sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS_MASK;
  289                 WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
  290 
  291                 wakeup(sc->dev);
  292         }
  293 
  294         SPI_SC_UNLOCK(sc);
  295 }
  296 
  297 /* Initialize hardware. */
  298 static int
  299 zy7_spi_init_hw(struct zy7_spi_softc *sc)
  300 {
  301         uint32_t baud_div;
  302 
  303         /* Find best clock divider. Divide by 2 not supported. */
  304         baud_div = 1;
  305         while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
  306             baud_div < 8)
  307                 baud_div++;
  308         if (baud_div >= 8) {
  309                 device_printf(sc->dev, "cannot configure clock divider: ref=%d"
  310                     " spi=%d.\n", sc->ref_clock, sc->spi_clock);
  311                 return (EINVAL);
  312         }
  313         sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
  314 
  315         /* Set up configuration register. */
  316         sc->cfg_reg_shadow =
  317             ZY7_SPI_CONFIG_MAN_CS |
  318             ZY7_SPI_CONFIG_CS_MASK |
  319             ZY7_SPI_CONFIG_BAUD_RATE_DIV(baud_div) |
  320             ZY7_SPI_CONFIG_MODE_SEL;
  321         WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
  322 
  323         /* Set thresholds. */
  324         WR4(sc, ZY7_SPI_TX_THRESH_REG, 32);
  325         WR4(sc, ZY7_SPI_RX_THRESH_REG, 1);
  326 
  327         /* Clear and disable all interrupts. */
  328         WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
  329         WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
  330 
  331         /* Enable SPI. */
  332         WR4(sc, ZY7_SPI_EN_REG, ZY7_SPI_ENABLE);
  333 
  334         return (0);
  335 }
  336 
  337 static void
  338 zy7_spi_add_sysctls(device_t dev)
  339 {
  340         struct zy7_spi_softc *sc = device_get_softc(dev);
  341         struct sysctl_ctx_list *ctx;
  342         struct sysctl_oid_list *child;
  343 
  344         ctx = device_get_sysctl_ctx(dev);
  345         child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
  346 
  347         SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
  348             &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
  349 
  350         SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
  351             &sc->rx_overflows, 0, "RX FIFO overflow events");
  352 
  353         SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
  354             &sc->tx_underflows, 0, "TX FIFO underflow events");
  355 
  356         SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
  357             &sc->interrupts, 0, "interrupt calls");
  358 
  359         SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
  360             &sc->stray_ints, 0, "stray interrupts");
  361 }
  362 
  363 static int
  364 zy7_spi_probe(device_t dev)
  365 {
  366 
  367         if (!ofw_bus_status_okay(dev))
  368                 return (ENXIO);
  369 
  370         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  371                 return (ENXIO);
  372 
  373         device_set_desc(dev, "Zynq SPI Controller");
  374 
  375         return (BUS_PROBE_DEFAULT);
  376 }
  377 
  378 static int zy7_spi_detach(device_t);
  379 
  380 static int
  381 zy7_spi_attach(device_t dev)
  382 {
  383         struct zy7_spi_softc *sc;
  384         int rid, err;
  385         phandle_t node;
  386         pcell_t cell;
  387 
  388         sc = device_get_softc(dev);
  389         sc->dev = dev;
  390 
  391         SPI_SC_LOCK_INIT(sc);
  392 
  393         /* Get ref-clock and spi-clock properties. */
  394         node = ofw_bus_get_node(dev);
  395         if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
  396                 sc->ref_clock = fdt32_to_cpu(cell);
  397         else {
  398                 device_printf(dev, "must have ref-clock property\n");
  399                 return (ENXIO);
  400         }
  401         if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
  402                 sc->spi_clock = fdt32_to_cpu(cell);
  403         else
  404                 sc->spi_clock = ZY7_SPI_DEFAULT_SPI_CLOCK;
  405 
  406         /* Get memory resource. */
  407         rid = 0;
  408         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  409             RF_ACTIVE);
  410         if (sc->mem_res == NULL) {
  411                 device_printf(dev, "could not allocate memory resources.\n");
  412                 zy7_spi_detach(dev);
  413                 return (ENOMEM);
  414         }
  415 
  416         /* Allocate IRQ. */
  417         rid = 0;
  418         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
  419             RF_ACTIVE);
  420         if (sc->irq_res == NULL) {
  421                 device_printf(dev, "could not allocate IRQ resource.\n");
  422                 zy7_spi_detach(dev);
  423                 return (ENOMEM);
  424         }
  425 
  426         /* Activate the interrupt. */
  427         err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
  428             NULL, zy7_spi_intr, sc, &sc->intrhandle);
  429         if (err) {
  430                 device_printf(dev, "could not setup IRQ.\n");
  431                 zy7_spi_detach(dev);
  432                 return (err);
  433         }
  434 
  435         /* Configure the device. */
  436         err = zy7_spi_init_hw(sc);
  437         if (err) {
  438                 zy7_spi_detach(dev);
  439                 return (err);
  440         }
  441 
  442         sc->child = device_add_child(dev, "spibus", -1);
  443 
  444         zy7_spi_add_sysctls(dev);
  445 
  446         /* Attach spibus driver as a child later when interrupts work. */
  447         config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev);
  448 
  449         return (0);
  450 }
  451 
  452 static int
  453 zy7_spi_detach(device_t dev)
  454 {
  455         struct zy7_spi_softc *sc = device_get_softc(dev);
  456 
  457         if (device_is_attached(dev))
  458                 bus_generic_detach(dev);
  459 
  460         /* Delete child bus. */
  461         if (sc->child)
  462                 device_delete_child(dev, sc->child);
  463 
  464         /* Disable hardware. */
  465         if (sc->mem_res != NULL) {
  466                 /* Disable SPI. */
  467                 WR4(sc, ZY7_SPI_EN_REG, 0);
  468 
  469                 /* Clear and disable all interrupts. */
  470                 WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
  471                 WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
  472         }
  473 
  474         /* Teardown and release interrupt. */
  475         if (sc->irq_res != NULL) {
  476                 if (sc->intrhandle)
  477                         bus_teardown_intr(dev, sc->irq_res, sc->intrhandle);
  478                 bus_release_resource(dev, SYS_RES_IRQ,
  479                     rman_get_rid(sc->irq_res), sc->irq_res);
  480         }
  481 
  482         /* Release memory resource. */
  483         if (sc->mem_res != NULL)
  484                 bus_release_resource(dev, SYS_RES_MEMORY,
  485                     rman_get_rid(sc->mem_res), sc->mem_res);
  486 
  487         SPI_SC_LOCK_DESTROY(sc);
  488 
  489         return (0);
  490 }
  491 
  492 static phandle_t
  493 zy7_spi_get_node(device_t bus, device_t dev)
  494 {
  495 
  496         return (ofw_bus_get_node(bus));
  497 }
  498 
  499 static int
  500 zy7_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
  501 {
  502         struct zy7_spi_softc *sc = device_get_softc(dev);
  503         uint32_t cs;
  504         uint32_t mode;
  505         int err = 0;
  506 
  507         KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
  508             ("TX/RX command sizes should be equal"));
  509         KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
  510             ("TX/RX data sizes should be equal"));
  511 
  512         /* Get chip select and mode for this child. */
  513         spibus_get_cs(child, &cs);
  514         cs &= ~SPIBUS_CS_HIGH;
  515         if (cs > 2) {
  516                 device_printf(dev, "Invalid chip select %d requested by %s",
  517                     cs, device_get_nameunit(child));
  518                 return (EINVAL);
  519         }
  520         spibus_get_mode(child, &mode);
  521 
  522         SPI_SC_LOCK(sc);
  523 
  524         /* Wait for controller available. */
  525         while (sc->busy != 0) {
  526                 err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi0", 0);
  527                 if (err) {
  528                         SPI_SC_UNLOCK(sc);
  529                         return (err);
  530                 }
  531         }
  532 
  533         /* Start transfer. */
  534         sc->busy = 1;
  535         sc->cmd = cmd;
  536         sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
  537         sc->tx_bytes_sent = 0;
  538         sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
  539         sc->rx_bytes_rcvd = 0;
  540 
  541         /* Enable interrupts.  zy7_spi_intr() will handle transfer. */
  542         WR4(sc, ZY7_SPI_INTR_EN_REG,
  543             ZY7_SPI_INTR_TX_FIFO_NOT_FULL |
  544             ZY7_SPI_INTR_RX_OVERFLOW);
  545 
  546         /* Handle polarity and phase. */
  547         if (mode == SPIBUS_MODE_CPHA || mode == SPIBUS_MODE_CPOL_CPHA)
  548                 sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_PH;
  549         if (mode == SPIBUS_MODE_CPOL || mode == SPIBUS_MODE_CPOL_CPHA)
  550                 sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_POL;
  551 
  552         /* Assert CS. */
  553         sc->cfg_reg_shadow &= ~ZY7_SPI_CONFIG_CS_MASK;
  554         sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS(cs);
  555         WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
  556 
  557         /* Wait for completion. */
  558         err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi1", hz * 2);
  559         if (err)
  560                 zy7_spi_abort_transfer(sc);
  561 
  562         /* Release controller. */
  563         sc->busy = 0;
  564         wakeup_one(dev);
  565 
  566         SPI_SC_UNLOCK(sc);
  567 
  568         return (err);
  569 }
  570 
  571 static device_method_t zy7_spi_methods[] = {
  572         /* Device interface */
  573         DEVMETHOD(device_probe,         zy7_spi_probe),
  574         DEVMETHOD(device_attach,        zy7_spi_attach),
  575         DEVMETHOD(device_detach,        zy7_spi_detach),
  576 
  577         /* SPI interface */
  578         DEVMETHOD(spibus_transfer,      zy7_spi_transfer),
  579 
  580         /* ofw_bus interface */
  581         DEVMETHOD(ofw_bus_get_node,     zy7_spi_get_node),
  582 
  583         DEVMETHOD_END
  584 };
  585 
  586 static driver_t zy7_spi_driver = {
  587         "zy7_spi",
  588         zy7_spi_methods,
  589         sizeof(struct zy7_spi_softc),
  590 };
  591 static devclass_t zy7_spi_devclass;
  592 
  593 DRIVER_MODULE(zy7_spi, simplebus, zy7_spi_driver, zy7_spi_devclass, 0, 0);
  594 DRIVER_MODULE(ofw_spibus, zy7_spi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0);
  595 SIMPLEBUS_PNP_INFO(compat_data);
  596 MODULE_DEPEND(zy7_spi, ofw_spibus, 1, 1, 1);

Cache object: c3c0a91c86f96639a192aeaa6e9f695f


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