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/riscv/sifive/sifive_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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2019 Axiado Corporation
    5  * All rights reserved.
    6  *
    7  * This software was developed in part by Philip Paeps and Kristof Provost
    8  * under contract for Axiado Corporation.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/bus.h>
   38 #include <sys/kernel.h>
   39 #include <sys/lock.h>
   40 #include <sys/module.h>
   41 #include <sys/mutex.h>
   42 #include <sys/rman.h>
   43 
   44 #include <machine/bus.h>
   45 #include <machine/cpu.h>
   46 
   47 #include <dev/extres/clk/clk.h>
   48 
   49 #include <dev/ofw/ofw_bus.h>
   50 #include <dev/ofw/ofw_bus_subr.h>
   51 #include <dev/ofw/openfirm.h>
   52 
   53 #include <dev/spibus/spi.h>
   54 #include <dev/spibus/spibusvar.h>
   55 
   56 #include "spibus_if.h"
   57 
   58 #if 1
   59 #define DBGPRINT(dev, fmt, args...) \
   60         device_printf(dev, "%s: " fmt "\n", __func__, ## args)
   61 #else
   62 #define DBGPRINT(dev, fmt, args...)
   63 #endif
   64 
   65 static struct resource_spec sfspi_spec[] = {
   66         { SYS_RES_MEMORY, 0, RF_ACTIVE },
   67         RESOURCE_SPEC_END
   68 };
   69 
   70 struct sfspi_softc {
   71         device_t                dev;
   72         device_t                parent;
   73 
   74         struct mtx              mtx;
   75 
   76         struct resource         *res;
   77         bus_space_tag_t         bst;
   78         bus_space_handle_t      bsh;
   79 
   80         void                    *ih;
   81 
   82         clk_t                   clk;
   83         uint64_t                freq;
   84         uint32_t                cs_max;
   85 };
   86 
   87 #define SFSPI_LOCK(sc)                  mtx_lock(&(sc)->mtx)
   88 #define SFSPI_UNLOCK(sc)                mtx_unlock(&(sc)->mtx)
   89 #define SFSPI_ASSERT_LOCKED(sc)         mtx_assert(&(sc)->mtx, MA_OWNED);
   90 #define SFSPI_ASSERT_UNLOCKED(sc)       mtx_assert(&(sc)->mtx, MA_NOTOWNED);
   91 
   92 /*
   93  * Register offsets.
   94  * From Sifive-Unleashed-FU540-C000-v1.0.pdf page 101.
   95  */
   96 #define SFSPI_REG_SCKDIV        0x00 /* Serial clock divisor */
   97 #define SFSPI_REG_SCKMODE       0x04 /* Serial clock mode */
   98 #define SFSPI_REG_CSID          0x10 /* Chip select ID */
   99 #define SFSPI_REG_CSDEF         0x14 /* Chip select default */
  100 #define SFSPI_REG_CSMODE        0x18 /* Chip select mode */
  101 #define SFSPI_REG_DELAY0        0x28 /* Delay control 0 */
  102 #define SFSPI_REG_DELAY1        0x2C /* Delay control 1 */
  103 #define SFSPI_REG_FMT           0x40 /* Frame format */
  104 #define SFSPI_REG_TXDATA        0x48 /* Tx FIFO data */
  105 #define SFSPI_REG_RXDATA        0x4C /* Rx FIFO data */
  106 #define SFSPI_REG_TXMARK        0x50 /* Tx FIFO watermark */
  107 #define SFSPI_REG_RXMARK        0x54 /* Rx FIFO watermark */
  108 #define SFSPI_REG_FCTRL         0x60 /* SPI flash interface control* */
  109 #define SFSPI_REG_FFMT          0x64 /* SPI flash instruction format* */
  110 #define SFSPI_REG_IE            0x70 /* SPI interrupt enable */
  111 #define SFSPI_REG_IP            0x74 /* SPI interrupt pending */
  112 
  113 #define SFSPI_SCKDIV_MASK       0xfff
  114 
  115 #define SFSPI_CSDEF_ALL         ((1 << sc->cs_max)-1)
  116 
  117 #define SFSPI_CSMODE_AUTO       0x0U
  118 #define SFSPI_CSMODE_HOLD       0x2U
  119 #define SFSPI_CSMODE_OFF        0x3U
  120 
  121 #define SFSPI_TXDATA_DATA_MASK  0xff
  122 #define SFSPI_TXDATA_FULL       (1 << 31)
  123 
  124 #define SFSPI_RXDATA_DATA_MASK  0xff
  125 #define SFSPI_RXDATA_EMPTY      (1 << 31)
  126 
  127 #define SFSPI_SCKMODE_PHA       (1 << 0)
  128 #define SFSPI_SCKMODE_POL       (1 << 1)
  129 
  130 #define SFSPI_FMT_PROTO_SINGLE  0x0U
  131 #define SFSPI_FMT_PROTO_DUAL    0x1U
  132 #define SFSPI_FMT_PROTO_QUAD    0x2U
  133 #define SFSPI_FMT_PROTO_MASK    0x3U
  134 #define SFSPI_FMT_ENDIAN        (1 << 2)
  135 #define SFSPI_FMT_DIR           (1 << 3)
  136 #define SFSPI_FMT_LEN(x)        ((uint32_t)(x) << 16)
  137 #define SFSPI_FMT_LEN_MASK      (0xfU << 16)
  138 
  139 #define SFSPI_FIFO_DEPTH        8
  140 
  141 #define SFSPI_READ(_sc, _reg)           \
  142     bus_space_read_4((_sc)->bst, (_sc)->bsh, (_reg))
  143 #define SFSPI_WRITE(_sc, _reg, _val)    \
  144     bus_space_write_4((_sc)->bst, (_sc)->bsh, (_reg), (_val))
  145 
  146 static void
  147 sfspi_tx(struct sfspi_softc *sc, uint8_t *buf, uint32_t bufsiz)
  148 {
  149         uint32_t val;
  150         uint8_t *p, *end;
  151 
  152         KASSERT(buf != NULL, ("TX buffer cannot be NULL"));
  153 
  154         end = buf + bufsiz;
  155         for (p = buf; p < end; p++) {
  156                 do {
  157                         val = SFSPI_READ(sc, SFSPI_REG_TXDATA);
  158                 } while (val & SFSPI_TXDATA_FULL);
  159                 val = *p;
  160                 SFSPI_WRITE(sc, SFSPI_REG_TXDATA, val);
  161         }
  162 }
  163 
  164 static void
  165 sfspi_rx(struct sfspi_softc *sc, uint8_t *buf, uint32_t bufsiz)
  166 {
  167         uint32_t val;
  168         uint8_t *p, *end;
  169 
  170         KASSERT(buf != NULL, ("RX buffer cannot be NULL"));
  171         KASSERT(bufsiz <= SFSPI_FIFO_DEPTH,
  172             ("Cannot receive more than %d bytes at a time\n",
  173             SFSPI_FIFO_DEPTH));
  174 
  175         end = buf + bufsiz;
  176         for (p = buf; p < end; p++) {
  177                 do {
  178                         val = SFSPI_READ(sc, SFSPI_REG_RXDATA);
  179                 } while (val & SFSPI_RXDATA_EMPTY);
  180                 *p = val & SFSPI_RXDATA_DATA_MASK;
  181         };
  182 }
  183 
  184 static int
  185 sfspi_xfer_buf(struct sfspi_softc *sc, uint8_t *rxbuf, uint8_t *txbuf,
  186     uint32_t txlen, uint32_t rxlen)
  187 {
  188         uint32_t bytes;
  189 
  190         KASSERT(txlen == rxlen, ("TX and RX lengths must be equal"));
  191         KASSERT(rxbuf != NULL, ("RX buffer cannot be NULL"));
  192         KASSERT(txbuf != NULL, ("TX buffer cannot be NULL"));
  193 
  194         while (txlen) {
  195                 bytes = (txlen > SFSPI_FIFO_DEPTH) ? SFSPI_FIFO_DEPTH : txlen;
  196                 sfspi_tx(sc, txbuf, bytes);
  197                 txbuf += bytes;
  198                 sfspi_rx(sc, rxbuf, bytes);
  199                 rxbuf += bytes;
  200                 txlen -= bytes;
  201         }
  202 
  203         return (0);
  204 }
  205 
  206 static int
  207 sfspi_setup(struct sfspi_softc *sc, uint32_t cs, uint32_t mode,
  208     uint32_t freq)
  209 {
  210         uint32_t csmode, fmt, sckdiv, sckmode;
  211 
  212         SFSPI_ASSERT_LOCKED(sc);
  213 
  214         /*
  215          * Fsck = Fin / 2 * (div + 1)
  216          * -> div = Fin / (2 * Fsck) - 1
  217          */
  218         sckdiv = (howmany(sc->freq >> 1, freq) - 1) & SFSPI_SCKDIV_MASK;
  219         SFSPI_WRITE(sc, SFSPI_REG_SCKDIV, sckdiv);
  220 
  221         switch (mode) {
  222         case SPIBUS_MODE_NONE:
  223                 sckmode = 0;
  224                 break;
  225         case SPIBUS_MODE_CPHA:
  226                 sckmode = SFSPI_SCKMODE_PHA;
  227                 break;
  228         case SPIBUS_MODE_CPOL:
  229                 sckmode = SFSPI_SCKMODE_POL;
  230                 break;
  231         case SPIBUS_MODE_CPOL_CPHA:
  232                 sckmode = SFSPI_SCKMODE_PHA | SFSPI_SCKMODE_POL;
  233                 break;
  234         default:
  235                 return (EINVAL);
  236         }
  237         SFSPI_WRITE(sc, SFSPI_REG_SCKMODE, sckmode);
  238 
  239         csmode = SFSPI_CSMODE_HOLD;
  240         if (cs & SPIBUS_CS_HIGH)
  241                 csmode = SFSPI_CSMODE_AUTO;
  242         SFSPI_WRITE(sc, SFSPI_REG_CSMODE, csmode);
  243 
  244         SFSPI_WRITE(sc, SFSPI_REG_CSID, cs & ~SPIBUS_CS_HIGH);
  245 
  246         fmt = SFSPI_FMT_PROTO_SINGLE | SFSPI_FMT_LEN(8);
  247         SFSPI_WRITE(sc, SFSPI_REG_FMT, fmt);
  248 
  249         return (0);
  250 }
  251 
  252 static int
  253 sfspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
  254 {
  255         struct sfspi_softc *sc;
  256         uint32_t clock, cs, csdef, mode;
  257         int err;
  258 
  259         KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
  260             ("TX and RX command sizes must be equal"));
  261         KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
  262             ("TX and RX data sizes must be equal"));
  263 
  264         sc = device_get_softc(dev);
  265         spibus_get_cs(child, &cs);
  266         spibus_get_clock(child, &clock);
  267         spibus_get_mode(child, &mode);
  268 
  269         if (cs > sc->cs_max) {
  270                 device_printf(sc->dev, "Invalid chip select %u\n", cs);
  271                 return (EINVAL);
  272         }
  273 
  274         SFSPI_LOCK(sc);
  275         device_busy(sc->dev);
  276 
  277         err = sfspi_setup(sc, cs, mode, clock);
  278         if (err != 0) {
  279                 SFSPI_UNLOCK(sc);
  280                 return (err);
  281         }
  282 
  283         err = 0;
  284         if (cmd->tx_cmd_sz > 0)
  285                 err = sfspi_xfer_buf(sc, cmd->rx_cmd, cmd->tx_cmd,
  286                     cmd->tx_cmd_sz, cmd->rx_cmd_sz);
  287         if (cmd->tx_data_sz > 0 && err == 0)
  288                 err = sfspi_xfer_buf(sc, cmd->rx_data, cmd->tx_data,
  289                     cmd->tx_data_sz, cmd->rx_data_sz);
  290 
  291         /* Deassert chip select. */
  292         csdef = SFSPI_CSDEF_ALL & ~(1 << cs);
  293         SFSPI_WRITE(sc, SFSPI_REG_CSDEF, csdef);
  294         SFSPI_WRITE(sc, SFSPI_REG_CSDEF, SFSPI_CSDEF_ALL);
  295 
  296         device_unbusy(sc->dev);
  297         SFSPI_UNLOCK(sc);
  298 
  299         return (err);
  300 }
  301 
  302 static int
  303 sfspi_attach(device_t dev)
  304 {
  305         struct sfspi_softc *sc;
  306         int error;
  307 
  308         sc = device_get_softc(dev);
  309         sc->dev = dev;
  310 
  311         mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
  312 
  313         error = bus_alloc_resources(dev, sfspi_spec, &sc->res);
  314         if (error) {
  315                 device_printf(dev, "Couldn't allocate resources\n");
  316                 goto fail;
  317         }
  318         sc->bst = rman_get_bustag(sc->res);
  319         sc->bsh = rman_get_bushandle(sc->res);
  320 
  321         error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
  322         if (error) {
  323                 device_printf(dev, "Couldn't allocate clock: %d\n", error);
  324                 goto fail;
  325         }
  326         error = clk_enable(sc->clk);
  327         if (error) {
  328                 device_printf(dev, "Couldn't enable clock: %d\n", error);
  329                 goto fail;
  330         }
  331 
  332         error = clk_get_freq(sc->clk, &sc->freq);
  333         if (error) {
  334                 device_printf(sc->dev, "Couldn't get frequency: %d\n", error);
  335                 goto fail;
  336         }
  337 
  338         /*
  339          * From Sifive-Unleashed-FU540-C000-v1.0.pdf page 103:
  340          * csdef is cs_width bits wide and all ones on reset.
  341          */
  342         sc->cs_max = SFSPI_READ(sc, SFSPI_REG_CSDEF);
  343 
  344         /*
  345          * We don't support the direct-mapped flash interface.
  346          * Disable it.
  347          */
  348         SFSPI_WRITE(sc, SFSPI_REG_FCTRL, 0x0);
  349 
  350         /* Probe and attach the spibus when interrupts are available. */
  351         sc->parent = device_add_child(dev, "spibus", -1);
  352         config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev);
  353 
  354         return (0);
  355 
  356 fail:
  357         bus_release_resources(dev, sfspi_spec, &sc->res);
  358         mtx_destroy(&sc->mtx);
  359         return (error);
  360 }
  361 
  362 static int
  363 sfspi_probe(device_t dev)
  364 {
  365 
  366         if (!ofw_bus_status_okay(dev))
  367                 return (ENXIO);
  368 
  369         if (!ofw_bus_is_compatible(dev, "sifive,spi0"))
  370                 return (ENXIO);
  371 
  372         device_set_desc(dev, "SiFive SPI controller");
  373 
  374         return (BUS_PROBE_DEFAULT);
  375 }
  376 
  377 static phandle_t
  378 sfspi_get_node(device_t bus, device_t dev)
  379 {
  380 
  381         return (ofw_bus_get_node(bus));
  382 }
  383 
  384 static device_method_t sfspi_methods[] = {
  385         DEVMETHOD(device_probe,         sfspi_probe),
  386         DEVMETHOD(device_attach,        sfspi_attach),
  387 
  388         DEVMETHOD(spibus_transfer,      sfspi_transfer),
  389 
  390         DEVMETHOD(ofw_bus_get_node,     sfspi_get_node),
  391 
  392         DEVMETHOD_END
  393 };
  394 
  395 static driver_t sfspi_driver = {
  396         "sifive_spi",
  397         sfspi_methods,
  398         sizeof(struct sfspi_softc)
  399 };
  400 
  401 DRIVER_MODULE(sifive_spi, simplebus, sfspi_driver, 0, 0);
  402 DRIVER_MODULE(ofw_spibus, sifive_spi, ofw_spibus_driver, 0, 0);
  403 MODULE_DEPEND(sifive_spi, ofw_spibus, 1, 1, 1);

Cache object: 3362c162c473710f99b99ddc8d19b861


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