Annotation of sys/arch/arm/xscale/pxa2x0_dmac.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: pxa2x0_dmac.c,v 1.3 2006/04/04 11:37:05 pascoe Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: /*
! 20: * DMA Controller Handler for the Intel PXA2X0 processor.
! 21: */
! 22: #include <sys/cdefs.h>
! 23:
! 24: #include <sys/param.h>
! 25: #include <sys/systm.h>
! 26: #include <sys/malloc.h>
! 27: #include <sys/evcount.h>
! 28: #include <uvm/uvm_extern.h>
! 29:
! 30: #include <machine/bus.h>
! 31: #include <machine/intr.h>
! 32: #include <machine/lock.h>
! 33:
! 34: #include <arm/xscale/pxa2x0reg.h>
! 35: #include <arm/xscale/pxa2x0var.h>
! 36: #include <arm/xscale/pxa2x0_dmac.h>
! 37:
! 38: typedef void (*pxadmac_intrhandler)(void *);
! 39:
! 40: struct pxadmac_softc {
! 41: struct device sc_dev;
! 42: bus_space_tag_t sc_bust;
! 43: bus_space_handle_t sc_bush;
! 44: void *sc_ih;
! 45: int sc_nchan;
! 46: int sc_npri;
! 47: pxadmac_intrhandler sc_intrhandlers[DMAC_N_CHANNELS_PXA27X];
! 48: void *sc_intrargs[DMAC_N_CHANNELS_PXA27X];
! 49: };
! 50:
! 51: int pxadmac_intr(void *);
! 52:
! 53: /*
! 54: * DMAC autoconf glue
! 55: */
! 56: int pxadmac_match(struct device *, void *, void *);
! 57: void pxadmac_attach(struct device *, struct device *, void *);
! 58:
! 59: struct cfattach pxadmac_ca = {
! 60: sizeof(struct pxadmac_softc), pxadmac_match, pxadmac_attach
! 61: };
! 62:
! 63: struct cfdriver pxadmac_cd = {
! 64: NULL, "pxadmac", DV_DULL
! 65: };
! 66:
! 67: static struct pxadmac_softc *pxadmac_softc = NULL;
! 68:
! 69: int
! 70: pxadmac_match(struct device *parent, void *cf, void *aux)
! 71: {
! 72: struct pxaip_attach_args *pxa = aux;
! 73:
! 74: if (pxadmac_softc != NULL || pxa->pxa_addr != PXA2X0_DMAC_BASE)
! 75: return (0);
! 76:
! 77: return (1);
! 78: }
! 79:
! 80: void
! 81: pxadmac_attach(struct device *parent, struct device *self, void *args)
! 82: {
! 83: struct pxadmac_softc *sc = (struct pxadmac_softc *)self;
! 84: struct pxaip_attach_args *pxa = args;
! 85: bus_size_t bus_size;
! 86:
! 87: sc->sc_bust = pxa->pxa_iot;
! 88:
! 89: printf(": DMA Controller\n");
! 90:
! 91: if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) == CPU_ID_PXA27X) {
! 92: sc->sc_nchan = DMAC_N_CHANNELS_PXA27X;
! 93: sc->sc_npri = DMAC_N_PRIORITIES_PXA27X;
! 94: bus_size = PXA27X_DMAC_SIZE;
! 95: } else {
! 96: sc->sc_nchan = DMAC_N_CHANNELS;
! 97: sc->sc_npri = DMAC_N_PRIORITIES;
! 98: bus_size = PXA2X0_DMAC_SIZE;
! 99: }
! 100:
! 101: if (bus_space_map(sc->sc_bust, pxa->pxa_addr, bus_size, 0,
! 102: &sc->sc_bush)) {
! 103: printf("%s: Can't map registers!\n", sc->sc_dev.dv_xname);
! 104: return;
! 105: }
! 106:
! 107: sc->sc_ih = pxa2x0_intr_establish(pxa->pxa_intr, IPL_BIO,
! 108: pxadmac_intr, sc, "pxadmac");
! 109: if (sc->sc_ih == NULL) {
! 110: printf(": unable to establish interrupt\n");
! 111: bus_space_unmap(sc->sc_bust, sc->sc_bush, bus_size);
! 112: return;
! 113: }
! 114:
! 115: pxadmac_softc = sc;
! 116: }
! 117:
! 118: /* Perform non-descriptor based DMA to a FIFO */
! 119: int
! 120: pxa2x0_dma_to_fifo(int periph, int chan, bus_addr_t fifo_addr, int width,
! 121: int burstsize, bus_addr_t src_addr, int length, void (*intr)(void *),
! 122: void *intrarg)
! 123: {
! 124: struct pxadmac_softc *sc = pxadmac_softc;
! 125: uint32_t cmd;
! 126:
! 127: if (periph < 0 || periph > 63 || periph == 23) {
! 128: printf("pxa2x0_dma_to_fifo: bogus peripheral %d", periph);
! 129: return EINVAL;
! 130: }
! 131:
! 132: if (chan < 0 || chan >= sc->sc_nchan) {
! 133: printf("pxa2x0_dma_to_fifo: bogus dma channel %d", chan);
! 134: return EINVAL;
! 135: }
! 136:
! 137: if (length < 0 || length > DCMD_LENGTH_MASK) {
! 138: printf("pxa2x0_dma_to_fifo: bogus length %d", length);
! 139: return EINVAL;
! 140: }
! 141:
! 142: cmd = (length & DCMD_LENGTH_MASK) | DCMD_INCSRCADDR | DCMD_FLOWTRG
! 143: | DCMD_ENDIRQEN;
! 144:
! 145: switch (width) {
! 146: case 1:
! 147: cmd |= DCMD_WIDTH_1;
! 148: break;
! 149: case 4:
! 150: cmd |= DCMD_WIDTH_4;
! 151: break;
! 152: default:
! 153: printf("pxa2x0_dma_to_fifo: bogus width %d", width);
! 154: return EINVAL;
! 155: }
! 156:
! 157: switch (burstsize) {
! 158: case 8:
! 159: cmd |= DCMD_SIZE_8;
! 160: break;
! 161: case 16:
! 162: cmd |= DCMD_SIZE_16;
! 163: break;
! 164: case 32:
! 165: cmd |= DCMD_SIZE_32;
! 166: break;
! 167: default:
! 168: printf("pxa2x0_dma_to_fifo: bogus burstsize %d", burstsize);
! 169: return EINVAL;
! 170: }
! 171:
! 172: /* XXX: abort anything already in progress, hopefully nothing. */
! 173: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
! 174: DCSR_NODESCFETCH);
! 175:
! 176: /* Save handler for interrupt-on-completion. */
! 177: sc->sc_intrhandlers[chan] = intr;
! 178: sc->sc_intrargs[chan] = intrarg;
! 179:
! 180: /* Map peripheral to channel for flow control setup. */
! 181: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DRCMR(periph),
! 182: chan | DRCMR_MAPVLD);
! 183:
! 184: /* Setup transfer addresses. */
! 185: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DDADR(chan),
! 186: DDADR_STOP);
! 187: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DSADR(chan),
! 188: src_addr);
! 189: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DTADR(chan),
! 190: fifo_addr);
! 191: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCMD(chan),
! 192: cmd);
! 193:
! 194: /* Start the transfer. */
! 195: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
! 196: DCSR_RUN | DCSR_NODESCFETCH);
! 197:
! 198: return 0;
! 199: }
! 200:
! 201: /* Perform non-descriptor based DMA from a FIFO */
! 202: int
! 203: pxa2x0_dma_from_fifo(int periph, int chan, bus_addr_t fifo_addr, int width,
! 204: int burstsize, bus_addr_t trg_addr, int length, void (*intr)(void *),
! 205: void *intrarg)
! 206: {
! 207: struct pxadmac_softc *sc = pxadmac_softc;
! 208: uint32_t cmd;
! 209:
! 210: if (periph < 0 || periph > 63 || periph == 23) {
! 211: printf("pxa2x0_dma_from_fifo: bogus peripheral %d", periph);
! 212: return EINVAL;
! 213: }
! 214:
! 215: if (chan < 0 || chan >= sc->sc_nchan) {
! 216: printf("pxa2x0_dma_from_fifo: bogus dma channel %d", chan);
! 217: return EINVAL;
! 218: }
! 219:
! 220: if (length < 0 || length > DCMD_LENGTH_MASK) {
! 221: printf("pxa2x0_dma_from_fifo: bogus length %d", length);
! 222: return EINVAL;
! 223: }
! 224:
! 225: cmd = (length & DCMD_LENGTH_MASK) | DCMD_INCTRGADDR | DCMD_FLOWSRC
! 226: | DCMD_ENDIRQEN;
! 227:
! 228: switch (width) {
! 229: case 1:
! 230: cmd |= DCMD_WIDTH_1;
! 231: break;
! 232: case 4:
! 233: cmd |= DCMD_WIDTH_4;
! 234: break;
! 235: default:
! 236: printf("pxa2x0_dma_from_fifo: bogus width %d", width);
! 237: return EINVAL;
! 238: }
! 239:
! 240: switch (burstsize) {
! 241: case 8:
! 242: cmd |= DCMD_SIZE_8;
! 243: break;
! 244: case 16:
! 245: cmd |= DCMD_SIZE_16;
! 246: break;
! 247: case 32:
! 248: cmd |= DCMD_SIZE_32;
! 249: break;
! 250: default:
! 251: printf("pxa2x0_dma_from_fifo: bogus burstsize %d", burstsize);
! 252: return EINVAL;
! 253: }
! 254:
! 255: /* XXX: abort anything already in progress, hopefully nothing. */
! 256: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
! 257: DCSR_NODESCFETCH);
! 258:
! 259: /* Save handler for interrupt-on-completion. */
! 260: sc->sc_intrhandlers[chan] = intr;
! 261: sc->sc_intrargs[chan] = intrarg;
! 262:
! 263: /* Map peripheral to channel for flow control setup. */
! 264: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DRCMR(periph),
! 265: chan | DRCMR_MAPVLD);
! 266:
! 267: /* Setup transfer addresses. */
! 268: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DDADR(chan),
! 269: DDADR_STOP);
! 270: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DSADR(chan),
! 271: fifo_addr);
! 272: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DTADR(chan),
! 273: trg_addr);
! 274: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCMD(chan),
! 275: cmd);
! 276:
! 277: /* Start the transfer. */
! 278: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
! 279: DCSR_RUN | DCSR_NODESCFETCH);
! 280:
! 281: return 0;
! 282: }
! 283:
! 284: int
! 285: pxadmac_intr(void *v)
! 286: {
! 287: struct pxadmac_softc *sc = v;
! 288: u_int32_t dint, dcsr;
! 289: int chan;
! 290:
! 291: /* Interrupt for us? */
! 292: dint = bus_space_read_4(sc->sc_bust, sc->sc_bush, DMAC_DINT);
! 293: if (!dint)
! 294: return 0;
! 295:
! 296: /* Process individual channels and run handlers. */
! 297: /* XXX: this does not respect priority order for channels. */
! 298: for (chan = 0; dint != 0 && chan < 32; chan++) {
! 299: /* Don't ack channels that weren't ready at call time. */
! 300: if ((dint & (1 << chan)) == 0)
! 301: continue;
! 302: dint &= ~(1 << chan);
! 303:
! 304: /* Acknowledge individual channel interrupt. */
! 305: dcsr = bus_space_read_4(sc->sc_bust, sc->sc_bush,
! 306: DMAC_DCSR(chan));
! 307: bus_space_write_4(sc->sc_bust, sc->sc_bush, DMAC_DCSR(chan),
! 308: dcsr & 0x7C80021F);
! 309:
! 310: /* Call the registered handler. */
! 311: if (sc->sc_intrhandlers[chan])
! 312: sc->sc_intrhandlers[chan](sc->sc_intrargs[chan]);
! 313: }
! 314:
! 315: return 1;
! 316: }
CVSweb