summaryrefslogtreecommitdiff
path: root/sys/arch/sparc/dev/dma.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 17:20:17 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 17:20:17 +0000
commit0c04730b6718e2a9cd357673c6601fb978d07256 (patch)
tree8522f5db91f122a7907fb298a279ee46fc8255f9 /sys/arch/sparc/dev/dma.c
parent3e81c8c2af67101fa47ce68b5ee9a02c44c97a62 (diff)
new driver by me
Diffstat (limited to 'sys/arch/sparc/dev/dma.c')
-rw-r--r--sys/arch/sparc/dev/dma.c415
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;
}