at91_spi.c revision 185265
1155324Simp/*- 2155324Simp * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3155324Simp * 4155324Simp * Redistribution and use in source and binary forms, with or without 5155324Simp * modification, are permitted provided that the following conditions 6155324Simp * are met: 7155324Simp * 1. Redistributions of source code must retain the above copyright 8155324Simp * notice, this list of conditions and the following disclaimer. 9155324Simp * 2. Redistributions in binary form must reproduce the above copyright 10155324Simp * notice, this list of conditions and the following disclaimer in the 11155324Simp * documentation and/or other materials provided with the distribution. 12155324Simp * 13185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16185265Simp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23185265Simp * SUCH DAMAGE. 24155324Simp */ 25155324Simp 26155324Simp#include <sys/cdefs.h> 27155324Simp__FBSDID("$FreeBSD: head/sys/arm/at91/at91_spi.c 185265 2008-11-25 00:13:26Z imp $"); 28155324Simp 29155324Simp#include <sys/param.h> 30155324Simp#include <sys/systm.h> 31155324Simp#include <sys/bus.h> 32155324Simp#include <sys/conf.h> 33155324Simp#include <sys/kernel.h> 34155324Simp#include <sys/mbuf.h> 35155324Simp#include <sys/malloc.h> 36155324Simp#include <sys/module.h> 37155324Simp#include <sys/mutex.h> 38155324Simp#include <sys/rman.h> 39155324Simp#include <machine/bus.h> 40155324Simp 41155324Simp#include <arm/at91/at91_spireg.h> 42160358Simp#include <arm/at91/at91_pdcreg.h> 43155324Simp 44160358Simp#include <dev/spibus/spi.h> 45160358Simp#include "spibus_if.h" 46160358Simp 47155324Simpstruct at91_spi_softc 48155324Simp{ 49155324Simp device_t dev; /* Myself */ 50155324Simp void *intrhand; /* Interrupt handle */ 51155324Simp struct resource *irq_res; /* IRQ resource */ 52155324Simp struct resource *mem_res; /* Memory resource */ 53160358Simp bus_dma_tag_t dmatag; /* bus dma tag for mbufs */ 54160358Simp bus_dmamap_t map[4]; /* Maps for the transaction */ 55163526Simp int rxdone; 56155324Simp}; 57155324Simp 58155324Simpstatic inline uint32_t 59155324SimpRD4(struct at91_spi_softc *sc, bus_size_t off) 60155324Simp{ 61155324Simp return bus_read_4(sc->mem_res, off); 62155324Simp} 63155324Simp 64155324Simpstatic inline void 65155324SimpWR4(struct at91_spi_softc *sc, bus_size_t off, uint32_t val) 66155324Simp{ 67155324Simp bus_write_4(sc->mem_res, off, val); 68155324Simp} 69155324Simp 70155324Simp/* bus entry points */ 71155324Simpstatic int at91_spi_probe(device_t dev); 72155324Simpstatic int at91_spi_attach(device_t dev); 73155324Simpstatic int at91_spi_detach(device_t dev); 74155324Simp 75155324Simp/* helper routines */ 76155324Simpstatic int at91_spi_activate(device_t dev); 77155324Simpstatic void at91_spi_deactivate(device_t dev); 78163526Simpstatic void at91_spi_intr(void *arg); 79155324Simp 80155324Simpstatic int 81155324Simpat91_spi_probe(device_t dev) 82155324Simp{ 83155324Simp device_set_desc(dev, "SPI"); 84155324Simp return (0); 85155324Simp} 86155324Simp 87155324Simpstatic int 88155324Simpat91_spi_attach(device_t dev) 89155324Simp{ 90155324Simp struct at91_spi_softc *sc = device_get_softc(dev); 91160358Simp int err, i; 92155324Simp 93155324Simp sc->dev = dev; 94155324Simp err = at91_spi_activate(dev); 95155324Simp if (err) 96155324Simp goto out; 97155324Simp 98155324Simp /* 99160358Simp * Allocate DMA tags and maps 100155324Simp */ 101183670Simp err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, 102183670Simp BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 2058, 1, 103183670Simp 2048, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->dmatag); 104160358Simp if (err != 0) 105155324Simp goto out; 106160358Simp for (i = 0; i < 4; i++) { 107160358Simp err = bus_dmamap_create(sc->dmatag, 0, &sc->map[i]); 108160358Simp if (err != 0) 109160358Simp goto out; 110155324Simp } 111155324Simp 112160358Simp // reset the SPI 113155324Simp WR4(sc, SPI_CR, SPI_CR_SWRST); 114163526Simp WR4(sc, SPI_IDR, 0xffffffff); 115160358Simp 116160358Simp WR4(sc, SPI_MR, (0xf << 24) | SPI_MR_MSTR | SPI_MR_MODFDIS | 117160358Simp (0xE << 16)); 118160358Simp 119160358Simp WR4(sc, SPI_CSR0, SPI_CSR_CPOL | (4 << 16) | (2 << 8)); 120160358Simp WR4(sc, SPI_CR, SPI_CR_SPIEN); 121160358Simp 122160358Simp WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS); 123160358Simp WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS); 124160358Simp WR4(sc, PDC_RNPR, 0); 125160358Simp WR4(sc, PDC_RNCR, 0); 126160358Simp WR4(sc, PDC_TNPR, 0); 127160358Simp WR4(sc, PDC_TNCR, 0); 128160358Simp WR4(sc, PDC_RPR, 0); 129160358Simp WR4(sc, PDC_RCR, 0); 130160358Simp WR4(sc, PDC_TPR, 0); 131160358Simp WR4(sc, PDC_TCR, 0); 132160358Simp RD4(sc, SPI_RDR); 133160358Simp RD4(sc, SPI_SR); 134160358Simp 135160358Simp device_add_child(dev, "spibus", -1); 136160358Simp bus_generic_attach(dev); 137155324Simpout:; 138155324Simp if (err) 139155324Simp at91_spi_deactivate(dev); 140155324Simp return (err); 141155324Simp} 142155324Simp 143155324Simpstatic int 144155324Simpat91_spi_detach(device_t dev) 145155324Simp{ 146155324Simp return (EBUSY); /* XXX */ 147155324Simp} 148155324Simp 149155324Simpstatic int 150155324Simpat91_spi_activate(device_t dev) 151155324Simp{ 152155324Simp struct at91_spi_softc *sc; 153163526Simp int rid, err = ENOMEM; 154155324Simp 155155324Simp sc = device_get_softc(dev); 156155324Simp rid = 0; 157155324Simp sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 158155324Simp RF_ACTIVE); 159155324Simp if (sc->mem_res == NULL) 160155324Simp goto errout; 161155324Simp rid = 0; 162155324Simp sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 163155324Simp RF_ACTIVE); 164163526Simp if (sc->irq_res == NULL) 165155324Simp goto errout; 166163526Simp err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 167166901Spiso NULL, at91_spi_intr, sc, &sc->intrhand); 168163526Simp if (err != 0) 169163526Simp goto errout; 170155324Simp return (0); 171155324Simperrout: 172155324Simp at91_spi_deactivate(dev); 173163526Simp return (err); 174155324Simp} 175155324Simp 176155324Simpstatic void 177155324Simpat91_spi_deactivate(device_t dev) 178155324Simp{ 179155324Simp struct at91_spi_softc *sc; 180155324Simp 181155324Simp sc = device_get_softc(dev); 182155324Simp if (sc->intrhand) 183155324Simp bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 184155324Simp sc->intrhand = 0; 185155324Simp bus_generic_detach(sc->dev); 186155324Simp if (sc->mem_res) 187155324Simp bus_release_resource(dev, SYS_RES_IOPORT, 188155324Simp rman_get_rid(sc->mem_res), sc->mem_res); 189155324Simp sc->mem_res = 0; 190155324Simp if (sc->irq_res) 191155324Simp bus_release_resource(dev, SYS_RES_IRQ, 192155324Simp rman_get_rid(sc->irq_res), sc->irq_res); 193155324Simp sc->irq_res = 0; 194155324Simp return; 195155324Simp} 196155324Simp 197155324Simpstatic void 198160358Simpat91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 199155324Simp{ 200160358Simp if (error != 0) 201155324Simp return; 202160358Simp *(bus_addr_t *)arg = segs[0].ds_addr; 203155324Simp} 204155324Simp 205155324Simpstatic int 206160358Simpat91_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 207155324Simp{ 208155324Simp struct at91_spi_softc *sc; 209163526Simp int i, j, rxdone, err, mode[4]; 210160358Simp bus_addr_t addr; 211155324Simp 212160358Simp sc = device_get_softc(dev); 213160358Simp WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS); 214160358Simp i = 0; 215160358Simp if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_cmd, 216160358Simp cmd->tx_cmd_sz, at91_getaddr, &addr, 0) != 0) 217160358Simp goto out; 218160358Simp WR4(sc, PDC_TPR, addr); 219160358Simp WR4(sc, PDC_TCR, cmd->tx_cmd_sz); 220160358Simp bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE); 221163526Simp mode[i++] = BUS_DMASYNC_POSTWRITE; 222163526Simp if (cmd->tx_data_sz > 0) { 223163526Simp if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_data, 224163526Simp cmd->tx_data_sz, at91_getaddr, &addr, 0) != 0) 225163526Simp goto out; 226163526Simp WR4(sc, PDC_TNPR, addr); 227164741Simp WR4(sc, PDC_TNCR, cmd->tx_data_sz); 228163526Simp bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE); 229163526Simp mode[i++] = BUS_DMASYNC_POSTWRITE; 230163526Simp } 231160358Simp if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_cmd, 232160358Simp cmd->tx_cmd_sz, at91_getaddr, &addr, 0) != 0) 233160358Simp goto out; 234160358Simp WR4(sc, PDC_RPR, addr); 235160358Simp WR4(sc, PDC_RCR, cmd->tx_cmd_sz); 236160358Simp bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD); 237163526Simp mode[i++] = BUS_DMASYNC_POSTREAD; 238164741Simp if (cmd->rx_data_sz > 0) { 239163526Simp if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_data, 240163526Simp cmd->tx_data_sz, at91_getaddr, &addr, 0) != 0) 241163526Simp goto out; 242163526Simp WR4(sc, PDC_RNPR, addr); 243164741Simp WR4(sc, PDC_RNCR, cmd->rx_data_sz); 244163526Simp bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD); 245163526Simp mode[i++] = BUS_DMASYNC_POSTREAD; 246163526Simp } 247163526Simp WR4(sc, SPI_IER, SPI_SR_ENDRX); 248160358Simp WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN | PDC_PTCR_RXTEN); 249155324Simp 250163526Simp rxdone = sc->rxdone; 251163526Simp do { 252167082Sjhb err = tsleep(&sc->rxdone, PCATCH | PZERO, "spi", hz); 253163526Simp } while (rxdone == sc->rxdone && err != EINTR); 254163526Simp WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS); 255163526Simp if (err == 0) { 256163526Simp for (j = 0; j < i; j++) 257163526Simp bus_dmamap_sync(sc->dmatag, sc->map[j], mode[j]); 258163526Simp } 259163526Simp for (j = 0; j < i; j++) 260163526Simp bus_dmamap_unload(sc->dmatag, sc->map[j]); 261163526Simp return (err); 262160358Simpout:; 263163526Simp for (j = 0; j < i; j++) 264163526Simp bus_dmamap_unload(sc->dmatag, sc->map[j]); 265160358Simp return (EIO); 266155324Simp} 267155324Simp 268163526Simpstatic void 269163526Simpat91_spi_intr(void *arg) 270163526Simp{ 271163526Simp struct at91_spi_softc *sc = (struct at91_spi_softc*)arg; 272163526Simp uint32_t sr; 273163526Simp 274163526Simp sr = RD4(sc, SPI_SR) & RD4(sc, SPI_IMR); 275163526Simp if (sr & SPI_SR_ENDRX) { 276163526Simp sc->rxdone++; 277163526Simp WR4(sc, SPI_IDR, SPI_SR_ENDRX); 278163526Simp wakeup(&sc->rxdone); 279163526Simp } 280163526Simp if (sr & ~SPI_SR_ENDRX) { 281163526Simp device_printf(sc->dev, "Unexpected ISR %#x\n", sr); 282163526Simp WR4(sc, SPI_IDR, sr & ~SPI_SR_ENDRX); 283163526Simp } 284163526Simp} 285163526Simp 286163526Simpstatic devclass_t at91_spi_devclass; 287163526Simp 288155324Simpstatic device_method_t at91_spi_methods[] = { 289155324Simp /* Device interface */ 290155324Simp DEVMETHOD(device_probe, at91_spi_probe), 291155324Simp DEVMETHOD(device_attach, at91_spi_attach), 292155324Simp DEVMETHOD(device_detach, at91_spi_detach), 293155324Simp 294160358Simp /* spibus interface */ 295160358Simp DEVMETHOD(spibus_transfer, at91_spi_transfer), 296155324Simp { 0, 0 } 297155324Simp}; 298155324Simp 299155324Simpstatic driver_t at91_spi_driver = { 300155324Simp "at91_spi", 301155324Simp at91_spi_methods, 302155324Simp sizeof(struct at91_spi_softc), 303155324Simp}; 304155324Simp 305155324SimpDRIVER_MODULE(at91_spi, atmelarm, at91_spi_driver, at91_spi_devclass, 0, 0); 306