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/at91/at91_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) 2006 M. Warner Losh.
    3  * Copyright (c) 2011-2012 Ian Lepore.
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following 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 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 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 <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/bus.h>
   34 #include <sys/conf.h>
   35 #include <sys/kernel.h>
   36 #include <sys/lock.h>
   37 #include <sys/mbuf.h>
   38 #include <sys/malloc.h>
   39 #include <sys/module.h>
   40 #include <sys/rman.h>
   41 #include <sys/sx.h>
   42 
   43 #include <machine/bus.h>
   44 
   45 #include <arm/at91/at91_spireg.h>
   46 #include <arm/at91/at91_pdcreg.h>
   47 
   48 #include <dev/spibus/spi.h>
   49 
   50 #include "spibus_if.h"
   51 
   52 struct at91_spi_softc
   53 {
   54         device_t dev;                   /* Myself */
   55         void *intrhand;                 /* Interrupt handle */
   56         struct resource *irq_res;       /* IRQ resource */
   57         struct resource *mem_res;       /* Memory resource */
   58         bus_dma_tag_t dmatag;           /* bus dma tag for transfers */
   59         bus_dmamap_t map[4];            /* Maps for the transaction */
   60         struct sx xfer_mtx;             /* Enforce one transfer at a time */
   61         uint32_t xfer_done;             /* interrupt<->mainthread signaling */
   62 };
   63 
   64 #define CS_TO_MR(cs)    ((~(1 << (cs)) & 0x0f) << 16)
   65 
   66 static inline uint32_t
   67 RD4(struct at91_spi_softc *sc, bus_size_t off)
   68 {
   69 
   70         return (bus_read_4(sc->mem_res, off));
   71 }
   72 
   73 static inline void
   74 WR4(struct at91_spi_softc *sc, bus_size_t off, uint32_t val)
   75 {
   76 
   77         bus_write_4(sc->mem_res, off, val);
   78 }
   79 
   80 /* bus entry points */
   81 static int at91_spi_attach(device_t dev);
   82 static int at91_spi_detach(device_t dev);
   83 static int at91_spi_probe(device_t dev);
   84 static int at91_spi_transfer(device_t dev, device_t child,
   85     struct spi_command *cmd);
   86 
   87 /* helper routines */
   88 static void at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs,
   89     int error);
   90 static int at91_spi_activate(device_t dev);
   91 static void at91_spi_deactivate(device_t dev);
   92 static void at91_spi_intr(void *arg);
   93 
   94 static int
   95 at91_spi_probe(device_t dev)
   96 {
   97 
   98         device_set_desc(dev, "AT91 SPI");
   99         return (0);
  100 }
  101 
  102 static int
  103 at91_spi_attach(device_t dev)
  104 {
  105         struct at91_spi_softc *sc;
  106         int err;
  107         uint32_t csr;
  108 
  109         sc = device_get_softc(dev);
  110 
  111         sc->dev = dev;
  112         sx_init(&sc->xfer_mtx, device_get_nameunit(dev));
  113 
  114         /*
  115          * Allocate resources.
  116          */
  117         err = at91_spi_activate(dev);
  118         if (err)
  119                 goto out;
  120 
  121         /*
  122          * Set up the hardware.
  123          */
  124 
  125         WR4(sc, SPI_CR, SPI_CR_SWRST);
  126         /* "Software Reset must be Written Twice" erratum */
  127         WR4(sc, SPI_CR, SPI_CR_SWRST);
  128         WR4(sc, SPI_IDR, 0xffffffff);
  129 
  130         WR4(sc, SPI_MR, (0xf << 24) | SPI_MR_MSTR | SPI_MR_MODFDIS |
  131             CS_TO_MR(0));
  132 
  133         /*
  134          * For now, run the bus at the slowest speed possible as otherwise we
  135          * may encounter data corruption on transmit as seen with ETHERNUT5
  136          * and AT45DB321D even though both board and slave device can take
  137          * more.
  138          * This also serves as a work-around for the "NPCSx rises if no data
  139          * data is to be transmitted" erratum.  The ideal workaround for the
  140          * latter is to take the chip select control away from the peripheral
  141          * and manage it directly as a GPIO line.  The easy solution is to
  142          * slow down the bus so dramatically that it just never gets starved
  143          * as may be seen when the OCHI controller is running and consuming
  144          * memory and APB bandwidth.
  145          * Also, currently we lack a way for lettting both the board and the
  146          * slave devices take their maximum supported SPI clocks into account.
  147          */
  148         csr = SPI_CSR_CPOL | (4 << 16) | (0xff << 8);
  149         WR4(sc, SPI_CSR0, csr);
  150         WR4(sc, SPI_CSR1, csr);
  151         WR4(sc, SPI_CSR2, csr);
  152         WR4(sc, SPI_CSR3, csr);
  153 
  154         WR4(sc, SPI_CR, SPI_CR_SPIEN);
  155 
  156         WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS);
  157         WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS);
  158         WR4(sc, PDC_RNPR, 0);
  159         WR4(sc, PDC_RNCR, 0);
  160         WR4(sc, PDC_TNPR, 0);
  161         WR4(sc, PDC_TNCR, 0);
  162         WR4(sc, PDC_RPR, 0);
  163         WR4(sc, PDC_RCR, 0);
  164         WR4(sc, PDC_TPR, 0);
  165         WR4(sc, PDC_TCR, 0);
  166         RD4(sc, SPI_RDR);
  167         RD4(sc, SPI_SR);
  168 
  169         device_add_child(dev, "spibus", -1);
  170         bus_generic_attach(dev);
  171 out:
  172         if (err)
  173                 at91_spi_deactivate(dev);
  174         return (err);
  175 }
  176 
  177 static int
  178 at91_spi_detach(device_t dev)
  179 {
  180 
  181         return (EBUSY); /* XXX */
  182 }
  183 
  184 static int
  185 at91_spi_activate(device_t dev)
  186 {
  187         struct at91_spi_softc *sc;
  188         int err, i, rid;
  189 
  190         sc = device_get_softc(dev);
  191         err = ENOMEM;
  192 
  193         rid = 0;
  194         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  195             RF_ACTIVE);
  196         if (sc->mem_res == NULL)
  197                 goto out;
  198 
  199         rid = 0;
  200         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
  201             RF_ACTIVE);
  202         if (sc->irq_res == NULL)
  203                 goto out;
  204         err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
  205             NULL, at91_spi_intr, sc, &sc->intrhand);
  206         if (err != 0)
  207                 goto out;
  208 
  209         err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
  210             BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 2048, 1,
  211             2048, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->dmatag);
  212         if (err != 0)
  213                 goto out;
  214 
  215         for (i = 0; i < 4; i++) {
  216                 err = bus_dmamap_create(sc->dmatag, 0,  &sc->map[i]);
  217                 if (err != 0)
  218                         goto out;
  219         }
  220 out:
  221         if (err != 0)
  222                 at91_spi_deactivate(dev);
  223         return (err);
  224 }
  225 
  226 static void
  227 at91_spi_deactivate(device_t dev)
  228 {
  229         struct at91_spi_softc *sc;
  230         int i;
  231 
  232         sc = device_get_softc(dev);
  233         bus_generic_detach(dev);
  234 
  235         for (i = 0; i < 4; i++)
  236                 if (sc->map[i])
  237                         bus_dmamap_destroy(sc->dmatag, sc->map[i]);
  238 
  239         if (sc->dmatag)
  240                 bus_dma_tag_destroy(sc->dmatag);
  241 
  242         if (sc->intrhand)
  243                 bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
  244         sc->intrhand = NULL;
  245         if (sc->irq_res)
  246                 bus_release_resource(dev, SYS_RES_IRQ,
  247                     rman_get_rid(sc->irq_res), sc->irq_res);
  248         sc->irq_res = NULL;
  249 
  250         if (sc->mem_res)
  251                 bus_release_resource(dev, SYS_RES_MEMORY,
  252                     rman_get_rid(sc->mem_res), sc->mem_res);
  253         sc->mem_res = NULL;
  254 }
  255 
  256 static void
  257 at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs __unused,
  258     int error)
  259 {
  260 
  261         if (error != 0)
  262                 return;
  263         *(bus_addr_t *)arg = segs[0].ds_addr;
  264 }
  265 
  266 static int
  267 at91_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
  268 {
  269         struct at91_spi_softc *sc;
  270         bus_addr_t addr;
  271         int err, i, j, mode[4];
  272 
  273         KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
  274             ("%s: TX/RX command sizes should be equal", __func__));
  275         KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
  276             ("%s: TX/RX data sizes should be equal", __func__));
  277 
  278         sc = device_get_softc(dev);
  279         i = 0;
  280 
  281         sx_xlock(&sc->xfer_mtx);
  282 
  283         /*
  284          * Disable transfers while we set things up.
  285          */
  286         WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
  287 
  288 #ifdef SPI_CHIPSEL_SUPPORT
  289         if (cmd->cs < 0 || cmd->cs > 3) {
  290                 device_printf(dev,
  291                     "Invalid chip select %d requested by %s\n", cmd->cs,
  292                     device_get_nameunit(child));
  293                 err = EINVAL;
  294                 goto out;
  295         }
  296 #ifdef SPI_CHIP_SELECT_HIGH_SUPPORT
  297         if (at91_is_rm92() && cmd->cs == 0 &&
  298             (cmd->flags & SPI_CHIP_SELECT_HIGH) != 0) {
  299                 device_printf(dev,
  300                     "Invalid chip select high requested by %s\n",
  301                     device_get_nameunit(child));
  302                 err = EINVAL;
  303                 goto out;
  304         }
  305 #endif
  306         WR4(sc, SPI_MR, (RD4(sc, SPI_MR) & ~0x000f0000) | CS_TO_MR(cmd->cs));
  307 #endif
  308 
  309         /*
  310          * Set up the TX side of the transfer.
  311          */
  312         if ((err = bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_cmd,
  313             cmd->tx_cmd_sz, at91_getaddr, &addr, 0)) != 0)
  314                 goto out;
  315         WR4(sc, PDC_TPR, addr);
  316         WR4(sc, PDC_TCR, cmd->tx_cmd_sz);
  317         bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE);
  318         mode[i++] = BUS_DMASYNC_POSTWRITE;
  319         if (cmd->tx_data_sz > 0) {
  320                 if ((err = bus_dmamap_load(sc->dmatag, sc->map[i],
  321                     cmd->tx_data, cmd->tx_data_sz, at91_getaddr, &addr, 0)) !=
  322                     0)
  323                         goto out;
  324                 WR4(sc, PDC_TNPR, addr);
  325                 WR4(sc, PDC_TNCR, cmd->tx_data_sz);
  326                 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE);
  327                 mode[i++] = BUS_DMASYNC_POSTWRITE;
  328         }
  329 
  330         /*
  331          * Set up the RX side of the transfer.
  332          */
  333         if ((err = bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_cmd,
  334             cmd->rx_cmd_sz, at91_getaddr, &addr, 0)) != 0)
  335                 goto out;
  336         WR4(sc, PDC_RPR, addr);
  337         WR4(sc, PDC_RCR, cmd->rx_cmd_sz);
  338         bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD);
  339         mode[i++] = BUS_DMASYNC_POSTREAD;
  340         if (cmd->rx_data_sz > 0) {
  341                 if ((err = bus_dmamap_load(sc->dmatag, sc->map[i],
  342                     cmd->rx_data, cmd->rx_data_sz, at91_getaddr, &addr, 0)) !=
  343                     0)
  344                         goto out;
  345                 WR4(sc, PDC_RNPR, addr);
  346                 WR4(sc, PDC_RNCR, cmd->rx_data_sz);
  347                 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD);
  348                 mode[i++] = BUS_DMASYNC_POSTREAD;
  349         }
  350 
  351         /*
  352          * Start the transfer, wait for it to complete.
  353          */
  354         sc->xfer_done = 0;
  355         WR4(sc, SPI_IER, SPI_SR_RXBUFF);
  356         WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN | PDC_PTCR_RXTEN);
  357         do
  358                 err = tsleep(&sc->xfer_done, PCATCH | PZERO, "at91_spi", hz);
  359         while (sc->xfer_done == 0 && err != EINTR);
  360 
  361         /*
  362          * Stop the transfer and clean things up.
  363          */
  364         WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS);
  365         if (err == 0)
  366                 for (j = 0; j < i; j++)
  367                         bus_dmamap_sync(sc->dmatag, sc->map[j], mode[j]);
  368 out:
  369         for (j = 0; j < i; j++)
  370                 bus_dmamap_unload(sc->dmatag, sc->map[j]);
  371 
  372         sx_xunlock(&sc->xfer_mtx);
  373 
  374         return (err);
  375 }
  376 
  377 static void
  378 at91_spi_intr(void *arg)
  379 {
  380         struct at91_spi_softc *sc;
  381         uint32_t sr;
  382 
  383         sc = (struct at91_spi_softc*)arg;
  384 
  385         sr = RD4(sc, SPI_SR) & RD4(sc, SPI_IMR);
  386         if ((sr & SPI_SR_RXBUFF) != 0) {
  387                 sc->xfer_done = 1;
  388                 WR4(sc, SPI_IDR, SPI_SR_RXBUFF);
  389                 wakeup(&sc->xfer_done);
  390         }
  391         if ((sr & ~SPI_SR_RXBUFF) != 0) {
  392                 device_printf(sc->dev, "Unexpected ISR %#x\n", sr);
  393                 WR4(sc, SPI_IDR, sr & ~SPI_SR_RXBUFF);
  394         }
  395 }
  396 
  397 static devclass_t at91_spi_devclass;
  398 
  399 static device_method_t at91_spi_methods[] = {
  400         /* Device interface */
  401         DEVMETHOD(device_probe,         at91_spi_probe),
  402         DEVMETHOD(device_attach,        at91_spi_attach),
  403         DEVMETHOD(device_detach,        at91_spi_detach),
  404 
  405         /* spibus interface */
  406         DEVMETHOD(spibus_transfer,      at91_spi_transfer),
  407 
  408         DEVMETHOD_END
  409 };
  410 
  411 static driver_t at91_spi_driver = {
  412         "spi",
  413         at91_spi_methods,
  414         sizeof(struct at91_spi_softc),
  415 };
  416 
  417 DRIVER_MODULE(at91_spi, atmelarm, at91_spi_driver, at91_spi_devclass, NULL,
  418     NULL);

Cache object: 3045647b1108cde757792585c5d61624


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