[BACK]Return to pxa2x0_dmac.c CVS log [TXT][DIR] Up to [local] / sys / arch / arm / xscale

Annotation of sys/arch/arm/xscale/pxa2x0_dmac.c, Revision 1.1.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