diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 17:20:17 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 17:20:17 +0000 |
commit | 0c04730b6718e2a9cd357673c6601fb978d07256 (patch) | |
tree | 8522f5db91f122a7907fb298a279ee46fc8255f9 /sys/arch/sparc/dev/dma.c | |
parent | 3e81c8c2af67101fa47ce68b5ee9a02c44c97a62 (diff) |
new driver by me
Diffstat (limited to 'sys/arch/sparc/dev/dma.c')
-rw-r--r-- | sys/arch/sparc/dev/dma.c | 415 |
1 files changed, 251 insertions, 164 deletions
diff --git a/sys/arch/sparc/dev/dma.c b/sys/arch/sparc/dev/dma.c index 1a88992dd2e..dfb0f2a3e9b 100644 --- a/sys/arch/sparc/dev/dma.c +++ b/sys/arch/sparc/dev/dma.c @@ -1,7 +1,9 @@ -/* $NetBSD: dma.c,v 1.10 1995/08/18 10:43:49 pk Exp $ */ +/* $NetBSD: dma.c,v 1.8 1995/02/01 12:37:21 pk Exp $ */ /* * Copyright (c) 1994 Peter Galbavy. All rights reserved. + * Copyright (c) 1995 Theo de Raadt. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -12,7 +14,8 @@ * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: - * This product includes software developed by Peter Galbavy. + * This product includes software developed by Peter Galbavy and + * Theo de Raadt. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * @@ -47,19 +50,20 @@ #include <scsi/scsiconf.h> #include <sparc/dev/sbusvar.h> +#include <sparc/dev/sbusreg.h> #include <sparc/dev/dmareg.h> #include <sparc/dev/dmavar.h> #include <sparc/dev/espreg.h> #include <sparc/dev/espvar.h> -int dmaprint __P((void *, char *)); +#include <sparc/sparc/cache.h> + +/*#define DMA_TEST*/ + +extern int sbus_print __P((void *, char *)); + void dmaattach __P((struct device *, struct device *, void *)); int dmamatch __P((struct device *, void *, void *)); -void dma_reset __P((struct dma_softc *)); -void dma_enintr __P((struct dma_softc *)); -int dma_isintr __P((struct dma_softc *)); -void dma_start __P((struct dma_softc *, caddr_t *, size_t *, int)); -int dmaintr __P((struct dma_softc *)); struct cfdriver dmacd = { NULL, "dma", dmamatch, dmaattach, @@ -67,29 +71,21 @@ struct cfdriver dmacd = { }; struct cfdriver ledmacd = { - NULL, "ledma", matchbyname, dmaattach, + NULL, "ledma", dmamatch, dmaattach, DV_DULL, sizeof(struct dma_softc) }; struct cfdriver espdmacd = { - NULL, "espdma", matchbyname, dmaattach, + NULL, "espdma", dmamatch, dmaattach, DV_DULL, sizeof(struct dma_softc) }; int -dmaprint(aux, name) - void *aux; - char *name; -{ - return -1; -} - -int dmamatch(parent, vcf, aux) - struct device *parent; - void *vcf, *aux; + struct device *parent; + void *vcf, *aux; { - struct cfdata *cf = vcf; + struct cfdata *cf = vcf; register struct confargs *ca = aux; register struct romaux *ra = &ca->ca_ra; @@ -106,17 +102,16 @@ dmamatch(parent, vcf, aux) */ void dmaattach(parent, self, aux) - struct device *parent, *self; - void *aux; + struct device *parent, *self; + void *aux; { register struct confargs *ca = aux; - struct dma_softc *sc = (void *)self; - int node, base, slot; - char *name; + struct confargs oca; + struct dma_softc *sc = (void *)self; + int node, base, slot; + char *name; - /* - * do basic sbus stuff (I think) - */ + /* XXX modifying ra_vaddr is bad! */ if (ca->ca_ra.ra_vaddr == NULL) ca->ca_ra.ra_vaddr = mapiodev(ca->ca_ra.ra_paddr, ca->ca_ra.ra_len, ca->ca_bustype); @@ -125,133 +120,156 @@ dmaattach(parent, self, aux) sc->sc_regs = (struct dma_regs *) ca->ca_ra.ra_vaddr; /* - * find the ESP by poking around the esp device structures - * * What happens here is that if the esp driver has not been * configured, then this returns a NULL pointer. Then when the * esp actually gets configured, it does the opposing test, and * if the sc->sc_dma field in it's softc is NULL, then tries to * find the matching dma driver. - * */ sc->sc_esp = ((struct esp_softc *) getdevunit("esp", sc->sc_dev.dv_unit)); - - /* - * and a back pointer to us, for DMA - */ if (sc->sc_esp) sc->sc_esp->sc_dma = sc; printf(": rev "); sc->sc_rev = sc->sc_regs->csr & D_DEV_ID; switch (sc->sc_rev) { - case DMAREV_0: - printf("0"); + case DMAREV_4300: + printf("4/300\n"); break; case DMAREV_1: - printf("1"); + printf("1\n"); + break; + case DMAREV_ESC1: + printf("ESC1\n"); break; case DMAREV_PLUS: - printf("1+"); + printf("1+\n"); break; case DMAREV_2: - printf("2"); + printf("2\n"); break; default: - printf("unknown"); + printf("unknown\n"); } - printf("\n"); - /* indirect functions */ - sc->enintr = dma_enintr; - sc->isintr = dma_isintr; - sc->reset = dma_reset; - sc->start = dma_start; - sc->intr = dmaintr; - - sc->sc_node = ca->ca_ra.ra_node; #if defined(SUN4C) || defined(SUN4M) - if (ca->ca_bustype == BUS_SBUS) + if (ca->ca_bustype == BUS_SBUS) { + sc->sc_node = ca->ca_ra.ra_node; sbus_establish(&sc->sc_sd, &sc->sc_dev); -#endif /* SUN4C || SUN4M */ -#ifdef notyet - /* return if we are a plain "dma" with no children */ - if (strcmp(getpropstring(node, "name"), "dma") == 0) - return; - - /* search through children */ - for (node = firstchild(sc->sc_node); node; node = nextsibling(node)) { - name = getpropstring(node, "name"); - if (!romprop(&ca->ca_ra, name, node)) - continue; - base = (int)ca->ca_ra.ra_paddr; - if (SBUS_ABS(base)) { - ca->ca_slot = SBUS_ABS_TO_SLOT(base); - ca->ca_offset = SBUS_ABS_TO_OFFSET(base); - } else { - ca->ca_slot = slot = ca->ca_ra.ra_iospace; - ca->ca_offset = base; - ca->ca_ra.ra_paddr = (void *)SBUS_ADDR(slot, base); + /* + * If the device is in an SBUS slave slot, report + * it (but we don't care, because the corresponding + * ESP will also realize the same thing.) + */ + (void) sbus_slavecheck(self, ca); + + /* + * if our name is not "dma", we may have subdevices + * below us in the device tree (like an esp) + * XXX: TDR: should we do this even if it is "dma"? + */ + if (strcmp(ca->ca_ra.ra_name, "dma") == 0) + return; + + /* search through children */ + for (node = firstchild(sc->sc_node); node; + node = nextsibling(node)) { + name = getpropstring(node, "name"); + if (!romprop(&oca.ca_ra, name, node)) + continue; + + /* + * advance bootpath if it currently points to us + * XXX There appears to be strangeness in the unit + * number on at least one espdma system (SS5 says + * espdma5, but nothing is available to compare + * against that digit 5... + */ + if (ca->ca_ra.ra_bp && + !strcmp(ca->ca_ra.ra_bp->name, ca->ca_ra.ra_name) && + ca->ca_ra.ra_bp->val[1] == (int)ca->ca_ra.ra_paddr) + oca.ca_ra.ra_bp = ca->ca_ra.ra_bp + 1; + else + oca.ca_ra.ra_bp = NULL; + + base = (int)oca.ca_ra.ra_paddr; + if (SBUS_ABS(base)) { + oca.ca_slot = SBUS_ABS_TO_SLOT(base); + oca.ca_offset = SBUS_ABS_TO_OFFSET(base); + } else { + oca.ca_slot = slot = ca->ca_ra.ra_iospace; + oca.ca_offset = base; + oca.ca_ra.ra_paddr = (void *)SBUS_ADDR(slot, base); + } + oca.ca_bustype = BUS_SBUS; + (void) config_found(&sc->sc_dev, (void *)&oca, sbus_print); } - (void) config_found(&sc->sc_dev, (void *)&ca, dmaprint); + } -#endif +#endif /* SUN4C || SUN4M */ } void -dma_reset(sc) - struct dma_softc *sc; +dmareset(sc) + struct dma_softc *sc; { - DMAWAIT1(sc); /* let things drain */ + DMAWAIT_PEND(sc); DMACSR(sc) |= D_RESET; /* reset DMA */ DELAY(200); /* what should this be ? */ DMACSR(sc) &= ~D_RESET; /* de-assert reset line */ - DMACSR(sc) |= D_INT_EN; /* enable interrupts */ - if (sc->sc_rev > DMAREV_1) - DMACSR(sc) |= D_FASTER; - sc->sc_active = 0; /* and of course we aren't */ -} + switch (sc->sc_rev) { + case DMAREV_1: + case DMAREV_4300: + case DMAREV_ESC1: + break; + case DMAREV_PLUS: + case DMAREV_2: + if (sc->sc_esp->sc_rev >= ESP100A) + DMACSR(sc) |= D_FASTER; + break; + } -void -dma_enintr(sc) - struct dma_softc *sc; -{ - sc->sc_regs->csr |= D_INT_EN; + sc->sc_active = 0; /* and of course we aren't */ } int -dma_isintr(sc) +dmapending(sc) struct dma_softc *sc; { return (sc->sc_regs->csr & (D_INT_PEND|D_ERR_PEND)); } -#define ESPMAX ((sc->sc_esp->sc_rev > ESP100A) ? \ - (16 * 1024 * 1024) : (64 * 1024)) -#define DMAMAX(a) (0x01000000 - ((a) & 0x00ffffff)) +/* bytes between loc and the end of this 16M region of memory */ +#define DMAMAX(loc) (0x01000000 - ((loc) & 0x00ffffff)) + +#define ESPMAX ((sc->sc_esp->sc_rev > ESP100A) ? \ + (16 * 1024 * 1024) : (64 * 1024)) -/*!!!*/int xdmadebug = 0; /* - * start a dma transfer or keep it going + * Start a dma transfer or keep it going. + * We do the loading of the transfer counter. + * XXX: what about write-back caches? */ void -dma_start(sc, addr, len, datain) - struct dma_softc *sc; - caddr_t *addr; - size_t *len; - int datain; +dmastart(sc, addr, len, datain, poll) + struct dma_softc *sc; + void *addr; + size_t *len; + int datain, poll; { - /* we do the loading of the transfer counter */ - volatile caddr_t esp = sc->sc_esp->sc_reg; - size_t size; + struct espregs *espr = sc->sc_esp->sc_regs; + int size; + + if (sc->sc_active) + panic("dma: dmastart() called while active\n"); sc->sc_dmaaddr = addr; sc->sc_dmalen = len; - - ESP_DMA(("%s: start %d@0x%08x,%d\n", sc->sc_dev.dv_xname, *sc->sc_dmalen, *sc->sc_dmaaddr, datain ? 1 : 0)); + sc->sc_dmapolling = poll; + sc->sc_dmadev2mem = datain; /* * the rules say we cannot transfer more than the limit @@ -260,108 +278,177 @@ dma_start(sc, addr, len, datain) */ size = min(*sc->sc_dmalen, ESPMAX); size = min(size, DMAMAX((size_t) *sc->sc_dmaaddr)); - sc->sc_dmasize = size; - - ESP_DMA(("dma_start: dmasize = %d\n", sc->sc_dmasize)); - - esp[ESP_TCL] = size; - esp[ESP_TCM] = size >> 8; - if (sc->sc_esp->sc_rev > ESP100A) { - esp[ESP_TCH] = size >> 16; - } - /* load the count in */ - ESPCMD(sc->sc_esp, ESPCMD_NOP|ESPCMD_DMA); + sc->sc_segsize = size; + +#ifdef DMA_TEST + printf("%s: start %d@0x%08x [%s scsi] [chunk=%d] %d\n", + sc->sc_dev.dv_xname, + *sc->sc_dmalen, *sc->sc_dmaaddr, + datain ? "read from" : "write to", + sc->sc_segsize, poll); +#endif - DMAWAIT1(sc); - - /* clear errors and D_TC flag */ - DMACSR(sc) |= D_INVALIDATE; - DMAWAIT1(sc); + espr->espr_tcl = size; + espr->espr_tcm = size >> 8; + if (sc->sc_esp->sc_rev > ESP100A) + espr->espr_tch = size >> 16; + espr->espr_cmd = ESPCMD_DMA|ESPCMD_NOP; /* load the count in */ DMADDR(sc) = *sc->sc_dmaaddr; - DMACSR(sc) |= datain|D_EN_DMA|D_INT_EN; + DMACSR(sc) = (DMACSR(sc) & ~(D_WRITE|D_INT_EN)) | D_EN_DMA | + (datain ? D_WRITE : 0) | (poll ? 0 : D_INT_EN); - /* and clear from last read if this is a write */ - if (!datain) - DMACSR(sc) &= ~D_WRITE; - - /* - * and kick the SCSI - * Note that if `size' is 0, we've already transceived all - * the bytes we want but we're still in the DATA PHASE. - * Apparently, the device needs padding. Also, a transfer - * size of 0 means "maximum" to the chip DMA logic. - */ - ESPCMD(sc->sc_esp, (size==0?ESPCMD_TRPAD:ESPCMD_TRANS)|ESPCMD_DMA); + /* and kick the SCSI */ + espr->espr_cmd = ESPCMD_DMA|ESPCMD_TRANS; sc->sc_active = 1; } /* * Pseudo (chained) interrupt from the esp driver to kick the - * current running DMA transfer. I am replying on espintr() to - * pickup and clean errors for now + * current running DMA transfer. espintr() cleans up errors. * - * return 1 if it was a DMA continue. + * return 1 if a dma operation is being continued (for when all + * the data could not be transferred in one dma operation). */ int -dmaintr(sc) - struct dma_softc *sc; +dmaintr(sc, restart) + struct dma_softc *sc; + int restart; { - volatile caddr_t esp = sc->sc_esp->sc_reg; - int trans = 0, resid = 0; + struct espregs *espr = sc->sc_esp->sc_regs; + int trans = 0, resid; - ESP_DMA(("%s: intr\n", sc->sc_dev.dv_xname)); +#ifdef DMA_TEST + printf("%s: intr\n", sc->sc_dev.dv_xname); +#endif if (DMACSR(sc) & D_ERR_PEND) { printf("%s: error", sc->sc_dev.dv_xname); + if (sc->sc_rev == DMAREV_4300) + DMAWAIT_PEND(sc); DMACSR(sc) |= D_INVALIDATE; - return 0; + return (0); } - /* This is an "assertion" :) */ if (sc->sc_active == 0) panic("dmaintr: DMA wasn't active"); - /* DMA has stopped */ + /* DMA has stopped, but flush it first */ + dmadrain(sc); + if (DMACSR(sc) & D_DRAINING) + printf("drain failed %d left\n", DMACSR(sc) & D_DRAINING); DMACSR(sc) &= ~D_EN_DMA; sc->sc_active = 0; - if (sc->sc_dmasize == 0) { - /* A "Transfer Pad" operation completed */ - ESP_DMA(("dmaintr: discarded %d bytes (tcl=%d, tcm=%d)\n", esp[ESP_TCL] | (esp[ESP_TCM] << 8), esp[ESP_TCL], esp[ESP_TCM])); - return 0; - } - - if (!(DMACSR(sc) & D_WRITE) && - (resid = (esp[ESP_FFLAG] & ESPFIFO_FF)) != 0) { + /* + * XXX: The subtracting of resid and throwing away up to 31 + * bytes cannot be the best/right way to do this. There's got + * to be a better way, such as letting the DMA take control + * again and putting it into memory, or pulling it out and + * putting it in memory by ourselves. + * XXX in the meantime, just do this job silently + */ + resid = 0; + if (!sc->sc_dmadev2mem && + (resid = (espr->espr_fflag & ESPFIFO_FF)) != 0) { +#if 0 printf("empty FIFO of %d ", resid); - ESPCMD(sc->sc_esp, ESPCMD_FLUSH); +#endif + espr->espr_cmd = ESPCMD_FLUSH; DELAY(1); } - - resid += esp[ESP_TCL] | (esp[ESP_TCM] << 8) | - (sc->sc_esp->sc_rev > ESP100A ? (esp[ESP_TCH] << 16) : 0); - trans = sc->sc_dmasize - resid; + resid += espr->espr_tcl | (espr->espr_tcm << 8) | + (sc->sc_esp->sc_rev > ESP100A ? (espr->espr_tch << 16) : 0); + trans = sc->sc_segsize - resid; if (trans < 0) { /* transferred < 0 ? */ printf("%s: xfer (%d) > req (%d)\n", - sc->sc_dev.dv_xname, trans, sc->sc_dmasize); - trans = sc->sc_dmasize; + sc->sc_dev.dv_xname, trans, sc->sc_segsize); + trans = sc->sc_segsize; } - ESP_DMA(("dmaintr: tcl=%d, tcm=%d, tch=%d; resid=%d, trans=%d\n", esp[ESP_TCL],esp[ESP_TCM], esp[ESP_TCH], trans, resid)); +#ifdef DMA_TEST + printf("dmaintr: resid=%d, trans=%d\n", resid, trans); +#endif - if (DMACSR(sc) & D_WRITE) + if (sc->sc_dmadev2mem && vactype != VAC_NONE) cache_flush(*sc->sc_dmaaddr, trans); + sc->sc_segsize -= trans; *sc->sc_dmalen -= trans; *sc->sc_dmaaddr += trans; - if (*sc->sc_dmalen == 0 || - sc->sc_esp->sc_phase != sc->sc_esp->sc_prevphase) - return 0; +#ifdef DMA_TEST + printf("%s: %d/%d bytes left\n", sc->sc_dev.dv_xname, + *sc->sc_dmalen, sc->sc_segsize); +#endif + + /* completely finished with the DMA transaction */ + if (sc->sc_segsize == 0 && *sc->sc_dmalen == 0) + return (0); + if (restart == 0) + return (1); /* and again */ - dma_start(sc, sc->sc_dmaaddr, sc->sc_dmalen, DMACSR(sc) & D_WRITE); - return 1; + dmastart(sc, sc->sc_dmaaddr, sc->sc_dmalen, sc->sc_dmadev2mem, + sc->sc_dmapolling); + return (1); +} + +/* + * We have to ask rev 1 and 4/300 dma controllers to drain their fifo. + * Apparently the other chips have drained by the time we get an + * interrupt, but we check anyways. + */ +void +dmadrain(sc) + struct dma_softc *sc; +{ + switch (sc->sc_rev) { + case DMAREV_1: + case DMAREV_4300: + case DMAREV_PLUS: + case DMAREV_2: + if (DMACSR(sc) & D_DRAINING) + DMAWAIT_DRAIN(sc); + break; + case DMAREV_ESC1: + DMAWAIT_PEND(sc) + DMAWAIT_DRAIN(sc); /* XXX: needed? */ + break; + } + DMACSR(sc) |= D_INVALIDATE; +} + +/* + * XXX + * During autoconfig we are in polled mode and we execute some commands. + * eventually we execute the last polled command. esp interrupts go through + * the dma chip's D_INT_EN gate. thus, because we have our data, we're happy + * and return. the esp chip has not, however, become unbusy. as soon as we + * execute our first non-polled command, we find the esp state machine is + * non-idle. it has not finished getting off the scsi bus, because it didn't + * get interrupts (and the polled code has long since gone it's merry way.) + * + * therefore, whenever we finish with a polled mode command, we enable + * interrupts so that we can get our data. it is probably safe to do so, + * since the scsi transfer has happened without error. the interrupts that + * will happen have no bearing on the higher level scsi subsystem, since it + * just functions to let the esp chip "clean up" it's state. + */ +void +dmaenintr(sc) + struct dma_softc *sc; +{ + DMACSR(sc) |= D_INT_EN; +} + +int +dmadisintr(sc) + struct dma_softc *sc; +{ + int x = DMACSR(sc) & D_INT_EN; + + DMACSR(sc) &= ~D_INT_EN; + return x; } |