at91_spi.c revision 183670
1/*- 2 * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25#include <sys/cdefs.h> 26__FBSDID("$FreeBSD: head/sys/arm/at91/at91_spi.c 183670 2008-10-07 17:23:16Z imp $"); 27 28#include <sys/param.h> 29#include <sys/systm.h> 30#include <sys/bus.h> 31#include <sys/conf.h> 32#include <sys/kernel.h> 33#include <sys/mbuf.h> 34#include <sys/malloc.h> 35#include <sys/module.h> 36#include <sys/mutex.h> 37#include <sys/rman.h> 38#include <machine/bus.h> 39 40#include <arm/at91/at91_spireg.h> 41#include <arm/at91/at91_pdcreg.h> 42 43#include <dev/spibus/spi.h> 44#include "spibus_if.h" 45 46struct at91_spi_softc 47{ 48 device_t dev; /* Myself */ 49 void *intrhand; /* Interrupt handle */ 50 struct resource *irq_res; /* IRQ resource */ 51 struct resource *mem_res; /* Memory resource */ 52 bus_dma_tag_t dmatag; /* bus dma tag for mbufs */ 53 bus_dmamap_t map[4]; /* Maps for the transaction */ 54 int rxdone; 55}; 56 57static inline uint32_t 58RD4(struct at91_spi_softc *sc, bus_size_t off) 59{ 60 return bus_read_4(sc->mem_res, off); 61} 62 63static inline void 64WR4(struct at91_spi_softc *sc, bus_size_t off, uint32_t val) 65{ 66 bus_write_4(sc->mem_res, off, val); 67} 68 69/* bus entry points */ 70static int at91_spi_probe(device_t dev); 71static int at91_spi_attach(device_t dev); 72static int at91_spi_detach(device_t dev); 73 74/* helper routines */ 75static int at91_spi_activate(device_t dev); 76static void at91_spi_deactivate(device_t dev); 77static void at91_spi_intr(void *arg); 78 79static int 80at91_spi_probe(device_t dev) 81{ 82 device_set_desc(dev, "SPI"); 83 return (0); 84} 85 86static int 87at91_spi_attach(device_t dev) 88{ 89 struct at91_spi_softc *sc = device_get_softc(dev); 90 int err, i; 91 92 sc->dev = dev; 93 err = at91_spi_activate(dev); 94 if (err) 95 goto out; 96 97 /* 98 * Allocate DMA tags and maps 99 */ 100 err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, 101 BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 2058, 1, 102 2048, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->dmatag); 103 if (err != 0) 104 goto out; 105 for (i = 0; i < 4; i++) { 106 err = bus_dmamap_create(sc->dmatag, 0, &sc->map[i]); 107 if (err != 0) 108 goto out; 109 } 110 111 // reset the SPI 112 WR4(sc, SPI_CR, SPI_CR_SWRST); 113 WR4(sc, SPI_IDR, 0xffffffff); 114 115 WR4(sc, SPI_MR, (0xf << 24) | SPI_MR_MSTR | SPI_MR_MODFDIS | 116 (0xE << 16)); 117 118 WR4(sc, SPI_CSR0, SPI_CSR_CPOL | (4 << 16) | (2 << 8)); 119 WR4(sc, SPI_CR, SPI_CR_SPIEN); 120 121 WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS); 122 WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS); 123 WR4(sc, PDC_RNPR, 0); 124 WR4(sc, PDC_RNCR, 0); 125 WR4(sc, PDC_TNPR, 0); 126 WR4(sc, PDC_TNCR, 0); 127 WR4(sc, PDC_RPR, 0); 128 WR4(sc, PDC_RCR, 0); 129 WR4(sc, PDC_TPR, 0); 130 WR4(sc, PDC_TCR, 0); 131 RD4(sc, SPI_RDR); 132 RD4(sc, SPI_SR); 133 134 device_add_child(dev, "spibus", -1); 135 bus_generic_attach(dev); 136out:; 137 if (err) 138 at91_spi_deactivate(dev); 139 return (err); 140} 141 142static int 143at91_spi_detach(device_t dev) 144{ 145 return (EBUSY); /* XXX */ 146} 147 148static int 149at91_spi_activate(device_t dev) 150{ 151 struct at91_spi_softc *sc; 152 int rid, err = ENOMEM; 153 154 sc = device_get_softc(dev); 155 rid = 0; 156 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 157 RF_ACTIVE); 158 if (sc->mem_res == NULL) 159 goto errout; 160 rid = 0; 161 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 162 RF_ACTIVE); 163 if (sc->irq_res == NULL) 164 goto errout; 165 err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 166 NULL, at91_spi_intr, sc, &sc->intrhand); 167 if (err != 0) 168 goto errout; 169 return (0); 170errout: 171 at91_spi_deactivate(dev); 172 return (err); 173} 174 175static void 176at91_spi_deactivate(device_t dev) 177{ 178 struct at91_spi_softc *sc; 179 180 sc = device_get_softc(dev); 181 if (sc->intrhand) 182 bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 183 sc->intrhand = 0; 184 bus_generic_detach(sc->dev); 185 if (sc->mem_res) 186 bus_release_resource(dev, SYS_RES_IOPORT, 187 rman_get_rid(sc->mem_res), sc->mem_res); 188 sc->mem_res = 0; 189 if (sc->irq_res) 190 bus_release_resource(dev, SYS_RES_IRQ, 191 rman_get_rid(sc->irq_res), sc->irq_res); 192 sc->irq_res = 0; 193 return; 194} 195 196static void 197at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 198{ 199 if (error != 0) 200 return; 201 *(bus_addr_t *)arg = segs[0].ds_addr; 202} 203 204static int 205at91_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 206{ 207 struct at91_spi_softc *sc; 208 int i, j, rxdone, err, mode[4]; 209 bus_addr_t addr; 210 211 sc = device_get_softc(dev); 212 WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS); 213 i = 0; 214 if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_cmd, 215 cmd->tx_cmd_sz, at91_getaddr, &addr, 0) != 0) 216 goto out; 217 WR4(sc, PDC_TPR, addr); 218 WR4(sc, PDC_TCR, cmd->tx_cmd_sz); 219 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE); 220 mode[i++] = BUS_DMASYNC_POSTWRITE; 221 if (cmd->tx_data_sz > 0) { 222 if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_data, 223 cmd->tx_data_sz, at91_getaddr, &addr, 0) != 0) 224 goto out; 225 WR4(sc, PDC_TNPR, addr); 226 WR4(sc, PDC_TNCR, cmd->tx_data_sz); 227 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE); 228 mode[i++] = BUS_DMASYNC_POSTWRITE; 229 } 230 if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_cmd, 231 cmd->tx_cmd_sz, at91_getaddr, &addr, 0) != 0) 232 goto out; 233 WR4(sc, PDC_RPR, addr); 234 WR4(sc, PDC_RCR, cmd->tx_cmd_sz); 235 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD); 236 mode[i++] = BUS_DMASYNC_POSTREAD; 237 if (cmd->rx_data_sz > 0) { 238 if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_data, 239 cmd->tx_data_sz, at91_getaddr, &addr, 0) != 0) 240 goto out; 241 WR4(sc, PDC_RNPR, addr); 242 WR4(sc, PDC_RNCR, cmd->rx_data_sz); 243 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD); 244 mode[i++] = BUS_DMASYNC_POSTREAD; 245 } 246 WR4(sc, SPI_IER, SPI_SR_ENDRX); 247 WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN | PDC_PTCR_RXTEN); 248 249 rxdone = sc->rxdone; 250 do { 251 err = tsleep(&sc->rxdone, PCATCH | PZERO, "spi", hz); 252 } while (rxdone == sc->rxdone && err != EINTR); 253 WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS); 254 if (err == 0) { 255 for (j = 0; j < i; j++) 256 bus_dmamap_sync(sc->dmatag, sc->map[j], mode[j]); 257 } 258 for (j = 0; j < i; j++) 259 bus_dmamap_unload(sc->dmatag, sc->map[j]); 260 return (err); 261out:; 262 for (j = 0; j < i; j++) 263 bus_dmamap_unload(sc->dmatag, sc->map[j]); 264 return (EIO); 265} 266 267static void 268at91_spi_intr(void *arg) 269{ 270 struct at91_spi_softc *sc = (struct at91_spi_softc*)arg; 271 uint32_t sr; 272 273 sr = RD4(sc, SPI_SR) & RD4(sc, SPI_IMR); 274 if (sr & SPI_SR_ENDRX) { 275 sc->rxdone++; 276 WR4(sc, SPI_IDR, SPI_SR_ENDRX); 277 wakeup(&sc->rxdone); 278 } 279 if (sr & ~SPI_SR_ENDRX) { 280 device_printf(sc->dev, "Unexpected ISR %#x\n", sr); 281 WR4(sc, SPI_IDR, sr & ~SPI_SR_ENDRX); 282 } 283} 284 285static devclass_t at91_spi_devclass; 286 287static device_method_t at91_spi_methods[] = { 288 /* Device interface */ 289 DEVMETHOD(device_probe, at91_spi_probe), 290 DEVMETHOD(device_attach, at91_spi_attach), 291 DEVMETHOD(device_detach, at91_spi_detach), 292 293 /* spibus interface */ 294 DEVMETHOD(spibus_transfer, at91_spi_transfer), 295 { 0, 0 } 296}; 297 298static driver_t at91_spi_driver = { 299 "at91_spi", 300 at91_spi_methods, 301 sizeof(struct at91_spi_softc), 302}; 303 304DRIVER_MODULE(at91_spi, atmelarm, at91_spi_driver, at91_spi_devclass, 0, 0); 305