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 unchanged lines hidden (view full) --- 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 160358 2006-07-14 21:35:59Z 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/lock.h> 34#include <sys/mbuf.h> 35#include <sys/malloc.h> 36#include <sys/module.h> 37#include <sys/mutex.h> 38#include <sys/rman.h> 39#include <machine/bus.h> 40 41#include <arm/at91/at91_spireg.h> |
42#include <arm/at91/at91_pdcreg.h> |
43 |
44#include <dev/spibus/spi.h> 45#include "spibus_if.h" 46 |
47struct at91_spi_softc 48{ 49 device_t dev; /* Myself */ 50 void *intrhand; /* Interrupt handle */ 51 struct resource *irq_res; /* IRQ resource */ 52 struct resource *mem_res; /* Memory resource */ 53 struct mtx sc_mtx; /* basically a perimeter lock */ |
54 bus_dma_tag_t dmatag; /* bus dma tag for mbufs */ 55 bus_dmamap_t map[4]; /* Maps for the transaction */ |
56}; 57 58static inline uint32_t 59RD4(struct at91_spi_softc *sc, bus_size_t off) 60{ 61 return bus_read_4(sc->mem_res, off); 62} 63 --- 6 unchanged lines hidden (view full) --- 70#define AT91_SPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 71#define AT91_SPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 72#define AT91_SPI_LOCK_INIT(_sc) \ 73 mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 74 "spi", MTX_DEF) 75#define AT91_SPI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 76#define AT91_SPI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 77#define AT91_SPI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); |
78 79static devclass_t at91_spi_devclass; 80 81/* bus entry points */ 82 83static int at91_spi_probe(device_t dev); 84static int at91_spi_attach(device_t dev); 85static int at91_spi_detach(device_t dev); |
86 87/* helper routines */ 88static int at91_spi_activate(device_t dev); 89static void at91_spi_deactivate(device_t dev); 90 |
91static int 92at91_spi_probe(device_t dev) 93{ 94 device_set_desc(dev, "SPI"); 95 return (0); 96} 97 98static int 99at91_spi_attach(device_t dev) 100{ 101 struct at91_spi_softc *sc = device_get_softc(dev); |
102 int err, i; |
103 104 sc->dev = dev; 105 err = at91_spi_activate(dev); 106 if (err) 107 goto out; 108 109 AT91_SPI_LOCK_INIT(sc); 110 111 /* |
112 * Allocate DMA tags and maps |
113 */ |
114 err = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, 115 BUS_SPACE_MAXADDR, NULL, NULL, 2058, 1, 2048, BUS_DMA_ALLOCNOW, 116 NULL, NULL, &sc->dmatag); 117 if (err != 0) |
118 goto out; |
119 for (i = 0; i < 4; i++) { 120 err = bus_dmamap_create(sc->dmatag, 0, &sc->map[i]); 121 if (err != 0) 122 goto out; |
123 } |
124 |
125 // reset the SPI |
126 WR4(sc, SPI_CR, SPI_CR_SWRST); |
127 128 WR4(sc, SPI_MR, (0xf << 24) | SPI_MR_MSTR | SPI_MR_MODFDIS | 129 (0xE << 16)); 130 131 WR4(sc, SPI_CSR0, SPI_CSR_CPOL | (4 << 16) | (2 << 8)); 132 WR4(sc, SPI_CR, SPI_CR_SPIEN); 133 134 WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS); 135 WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS); 136 WR4(sc, PDC_RNPR, 0); 137 WR4(sc, PDC_RNCR, 0); 138 WR4(sc, PDC_TNPR, 0); 139 WR4(sc, PDC_TNCR, 0); 140 WR4(sc, PDC_RPR, 0); 141 WR4(sc, PDC_RCR, 0); 142 WR4(sc, PDC_TPR, 0); 143 WR4(sc, PDC_TCR, 0); 144 WR4(sc, PDC_PTCR, PDC_PTCR_RXTEN); 145 WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN); 146 RD4(sc, SPI_RDR); 147 RD4(sc, SPI_SR); 148 149 device_add_child(dev, "spibus", -1); 150 bus_generic_attach(dev); |
151out:; 152 if (err) 153 at91_spi_deactivate(dev); 154 return (err); 155} 156 157static int 158at91_spi_detach(device_t dev) --- 41 unchanged lines hidden (view full) --- 200 if (sc->irq_res) 201 bus_release_resource(dev, SYS_RES_IRQ, 202 rman_get_rid(sc->irq_res), sc->irq_res); 203 sc->irq_res = 0; 204 return; 205} 206 207static void |
208at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) |
209{ |
210 if (error != 0) |
211 return; |
212 *(bus_addr_t *)arg = segs[0].ds_addr; |
213} 214 |
215static int |
216at91_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) |
217{ 218 struct at91_spi_softc *sc; |
219 int i; 220 bus_addr_t addr; |
221 |
222 sc = device_get_softc(dev); 223 WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS); 224 i = 0; 225 if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_cmd, 226 cmd->tx_cmd_sz, at91_getaddr, &addr, 0) != 0) 227 goto out; 228 WR4(sc, PDC_TPR, addr); 229 WR4(sc, PDC_TCR, cmd->tx_cmd_sz); 230 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE); 231 i++; 232 if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_data, 233 cmd->tx_data_sz, at91_getaddr, &addr, 0) != 0) 234 goto out; 235 WR4(sc, PDC_TNPR, addr); 236 WR4(sc, PDC_TNCR, cmd->tx_cmd_sz); 237 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE); 238 i++; 239 if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_cmd, 240 cmd->tx_cmd_sz, at91_getaddr, &addr, 0) != 0) 241 goto out; 242 WR4(sc, PDC_RPR, addr); 243 WR4(sc, PDC_RCR, cmd->tx_cmd_sz); 244 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD); 245 i++; 246 if (bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_data, 247 cmd->tx_data_sz, at91_getaddr, &addr, 0) != 0) 248 goto out; 249 WR4(sc, PDC_RNPR, addr); 250 WR4(sc, PDC_RNCR, cmd->tx_data_sz); 251 bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD); |
252 |
253 WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN | PDC_PTCR_RXTEN); |
254 |
255 // wait for completion 256 // XXX should be done as an ISR of some sort. 257 while (RD4(sc, SPI_SR) & SPI_SR_ENDRX) 258 DELAY(700); |
259 |
260 // Sync the buffers after the DMA is done, and unload them. 261 bus_dmamap_sync(sc->dmatag, sc->map[0], BUS_DMASYNC_POSTWRITE); 262 bus_dmamap_sync(sc->dmatag, sc->map[1], BUS_DMASYNC_POSTWRITE); 263 bus_dmamap_sync(sc->dmatag, sc->map[2], BUS_DMASYNC_POSTREAD); 264 bus_dmamap_sync(sc->dmatag, sc->map[3], BUS_DMASYNC_POSTREAD); 265 for (i = 0; i < 4; i++) 266 bus_dmamap_unload(sc->dmatag, sc->map[i]); 267 return (0); 268out:; 269 while (i-- > 0) 270 bus_dmamap_unload(sc->dmatag, sc->map[i]); 271 return (EIO); |
272} 273 |
274static device_method_t at91_spi_methods[] = { 275 /* Device interface */ 276 DEVMETHOD(device_probe, at91_spi_probe), 277 DEVMETHOD(device_attach, at91_spi_attach), 278 DEVMETHOD(device_detach, at91_spi_detach), 279 |
280 /* spibus interface */ 281 DEVMETHOD(spibus_transfer, at91_spi_transfer), |
282 { 0, 0 } 283}; 284 285static driver_t at91_spi_driver = { 286 "at91_spi", 287 at91_spi_methods, 288 sizeof(struct at91_spi_softc), 289}; 290 291DRIVER_MODULE(at91_spi, atmelarm, at91_spi_driver, at91_spi_devclass, 0, 0); |