summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Reindl <martin@cvs.openbsd.org>2004-12-10 18:23:24 +0000
committerMartin Reindl <martin@cvs.openbsd.org>2004-12-10 18:23:24 +0000
commit1ca6c8bcdb555e355b99ab0309b6c17980abec54 (patch)
tree7c93a83462a73a39244753744f4e7868531fd18c
parentc7c5fb35e66fa66c54c0b75a68cd932ea5746c96 (diff)
pseudo-DMA for non-AV Quadras, inspired by NetBSD
with lots of help from miod@ additional testing by nick@ and otto@ ok miod@
-rw-r--r--sys/arch/mac68k/dev/esp.c341
-rw-r--r--sys/arch/mac68k/dev/espvar.h14
2 files changed, 318 insertions, 37 deletions
diff --git a/sys/arch/mac68k/dev/esp.c b/sys/arch/mac68k/dev/esp.c
index 4b9bd537693..eccae605de7 100644
--- a/sys/arch/mac68k/dev/esp.c
+++ b/sys/arch/mac68k/dev/esp.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: esp.c,v 1.19 2004/12/08 06:59:43 miod Exp $ */
-/* $NetBSD: esp.c,v 1.59 1996/10/13 02:59:48 christos Exp $ */
+/* $OpenBSD: esp.c,v 1.20 2004/12/10 18:23:23 martin Exp $ */
+/* $NetBSD: esp.c,v 1.17 1998/09/05 15:15:35 pk Exp $ */
/*
* Copyright (c) 1997 Jason R. Thorpe.
@@ -34,7 +34,6 @@
/*
* Copyright (c) 1994 Peter Galbavy
- * Copyright (c) 1995 Paul Kranenburg
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -90,6 +89,7 @@
#include <scsi/scsi_message.h>
#include <machine/cpu.h>
+#include <machine/bus.h>
#include <machine/param.h>
#include <dev/ic/ncr53c9xreg.h>
@@ -98,6 +98,7 @@
#include <machine/viareg.h>
#include <mac68k/dev/espvar.h>
+#include <mac68k/dev/obiovar.h>
void espattach(struct device *, struct device *, void *);
int espmatch(struct device *, void *, void *);
@@ -134,6 +135,16 @@ int esp_dma_setup(struct ncr53c9x_softc *, caddr_t *,
void esp_dma_go(struct ncr53c9x_softc *);
void esp_dma_stop(struct ncr53c9x_softc *);
int esp_dma_isactive(struct ncr53c9x_softc *);
+void esp_quick_write_reg(struct ncr53c9x_softc *, int, u_char);
+int esp_quick_dma_intr(struct ncr53c9x_softc *);
+int esp_quick_dma_setup(struct ncr53c9x_softc *, caddr_t *,
+ size_t *, int, size_t *);
+void esp_quick_dma_go(struct ncr53c9x_softc *);
+int esp_intr(void *);
+
+static __inline__ int esp_dafb_have_dreq(struct esp_softc *esc);
+static __inline__ int esp_iosb_have_dreq(struct esp_softc *esc);
+int (*esp_have_dreq) (struct esp_softc *esc);
struct ncr53c9x_glue esp_glue = {
esp_read_reg,
@@ -154,12 +165,16 @@ espmatch(parent, vcf, aux)
void *vcf, *aux;
{
struct cfdata *cf = vcf;
+ int found = 0;
- if ((cf->cf_unit == 0) && mac68k_machine.scsi96)
- return (1);
- if ((cf->cf_unit == 1) && mac68k_machine.scsi96_2)
- return (1);
- return (0);
+ if ((cf->cf_unit == 0) && mac68k_machine.scsi96) {
+ found = 1;
+ }
+ if ((cf->cf_unit == 1) && mac68k_machine.scsi96_2) {
+ found = 1;
+ }
+
+ return found;
}
/*
@@ -170,29 +185,75 @@ espattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
+ struct obio_attach_args *oa = (struct obio_attach_args *)aux;
extern vm_offset_t SCSIBase;
- struct esp_softc *esc = (void *)self;
- struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x;
+ struct esp_softc *esc = (void *)self;
+ struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x;
+ int quick = 0;
+ unsigned long reg_offset;
+
+ reg_offset = SCSIBase - IOBase;
+ esc->sc_tag = oa->oa_tag;
+
+ /*
+ * For Wombat, Primus and Optimus motherboards, DREQ is
+ * visible on bit 0 of the IOSB's emulated VIA2 vIFR (and
+ * the scsi registers are offset 0x1000 bytes from IOBase).
+ *
+ * For the Q700/900/950 it's at f9800024 for bus 0 and
+ * f9800028 for bus 1 (900/950). For these machines, that is also
+ * a (12-bit) configuration register for DAFB's control of the
+ * pseudo-DMA timing. The default value is 0x1d1.
+ */
+ esp_have_dreq = esp_dafb_have_dreq;
+ if (sc->sc_dev.dv_unit == 0) {
+ if (reg_offset == 0x10000) {
+ quick = 1;
+ esp_have_dreq = esp_iosb_have_dreq;
+ } else if (reg_offset == 0x18000) {
+ quick = 0;
+ } else {
+ if (bus_space_map(esc->sc_tag, 0xf9800024,
+ 4, 0, &esc->sc_bsh)) {
+ printf("failed to map 4 at 0xf9800024.\n");
+ } else {
+ quick = 1;
+ bus_space_write_4(esc->sc_tag,
+ esc->sc_bsh, 0, 0x1d1);
+ }
+ }
+ } else {
+ if (bus_space_map(esc->sc_tag, 0xf9800028, 4, 0,
+ &esc->sc_bsh)) {
+ printf("failed to map 4 at 0xf9800028.\n");
+ } else {
+ quick = 1;
+ bus_space_write_4(esc->sc_tag, esc->sc_bsh, 0, 0x1d1);
+ }
+ }
+ if (quick) {
+ esp_glue.gl_write_reg = esp_quick_write_reg;
+ esp_glue.gl_dma_intr = esp_quick_dma_intr;
+ esp_glue.gl_dma_setup = esp_quick_dma_setup;
+ esp_glue.gl_dma_go = esp_quick_dma_go;
+ }
/*
* Set up the glue for MI code early; we use some of it here.
*/
sc->sc_glue = &esp_glue;
- esc->sc_ih.vh_fn = ncr53c9x_intr;
+ esc->sc_ih.vh_fn = esp_intr;
esc->sc_ih.vh_arg = esc;
+ esc->sc_ih.vh_ipl = VIA2_SCSIIRQ;
/*
* Save the regs
*/
if (sc->sc_dev.dv_unit == 0) {
- unsigned long reg_offset;
-
esc->sc_reg = (volatile u_char *) SCSIBase;
- esc->sc_ih.vh_ipl = VIA2_SCSIIRQ;
via2_register_irq(&esc->sc_ih, self->dv_xname);
esc->irq_mask = V2IF_SCSIIRQ;
- reg_offset = SCSIBase - IOBase;
if (reg_offset == 0x10000) {
sc->sc_freq = 16500000;
} else {
@@ -200,13 +261,17 @@ espattach(parent, self, aux)
}
} else {
esc->sc_reg = (volatile u_char *) SCSIBase + 0x402;
- esc->sc_ih.vh_ipl = VIA2_SCSIDRQ;
via2_register_irq(&esc->sc_ih, self->dv_xname);
- esc->irq_mask = V2IF_SCSIDRQ; /* V2IF_T1? */
+ esc->irq_mask = 0;
sc->sc_freq = 25000000;
}
- printf(": address %p", esc->sc_reg);
+#ifdef DEBUG
+ printf(" address %p", esc->sc_reg);
+#endif
+ if (esp_glue.gl_dma_go == esp_quick_dma_go) {
+ printf(" (pseudo-DMA)");
+ }
sc->sc_id = 7;
@@ -218,7 +283,7 @@ espattach(parent, self, aux)
* to find out what rev the esp chip is, else the esp_reset
* will not set up the defaults correctly.
*/
- sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB;
+ sc->sc_cfg1 = sc->sc_id; /* | NCRCFG1_PARENB; */
sc->sc_cfg2 = NCRCFG2_SCSI2;
sc->sc_cfg3 = 0;
sc->sc_rev = NCR_VARIANT_NCR53C96;
@@ -236,7 +301,7 @@ espattach(parent, self, aux)
sc->sc_minsync = 0; /* No synchronous xfers w/o DMA */
/* Really no limit, but since we want to fit into the TCR... */
- sc->sc_maxxfer = 64 * 1024;
+ sc->sc_maxxfer = 8 * 1024; /*64 * 1024; XXX */
/*
* Now try to attach all the sub-devices
@@ -246,9 +311,11 @@ espattach(parent, self, aux)
/*
* Configure interrupts.
*/
- via2_reg(vPCR) = 0x22;
- via2_reg(vIFR) = esc->irq_mask;
- via2_reg(vIER) = 0x80 | esc->irq_mask;
+ if (esc->irq_mask) {
+ via2_reg(vPCR) = 0x22;
+ via2_reg(vIFR) = esc->irq_mask;
+ via2_reg(vIER) = 0x80 | esc->irq_mask;
+ }
}
/*
@@ -280,6 +347,21 @@ esp_write_reg(sc, reg, val)
esc->sc_reg[reg * 16] = v;
}
+void
+esp_dma_stop(sc)
+ struct ncr53c9x_softc *sc;
+{
+}
+
+int
+esp_dma_isactive(sc)
+ struct ncr53c9x_softc *sc;
+{
+ struct esp_softc *esc = (struct esp_softc *)sc;
+
+ return esc->sc_active;
+}
+
int
esp_dma_isintr(sc)
struct ncr53c9x_softc *sc;
@@ -319,8 +401,8 @@ esp_dma_intr(sc)
return 0;
}
- cnt = *esc->sc_pdmalen;
- if (*esc->sc_pdmalen == 0) {
+ cnt = *esc->sc_dmalen;
+ if (*esc->sc_dmalen == 0) {
printf("data interrupt, but no count left.");
}
@@ -365,9 +447,9 @@ esp_dma_intr(sc)
sc->sc_espstat = (u_char) espstat;
sc->sc_espintr = (u_char) espintr;
*esc->sc_dmaaddr = p;
- *esc->sc_pdmalen = cnt;
+ *esc->sc_dmalen = cnt;
- if (*esc->sc_pdmalen == 0) {
+ if (*esc->sc_dmalen == 0) {
esc->sc_tc = NCRSTAT_TC;
}
sc->sc_espstat |= esc->sc_tc;
@@ -385,7 +467,7 @@ esp_dma_setup(sc, addr, len, datain, dmasize)
struct esp_softc *esc = (struct esp_softc *)sc;
esc->sc_dmaaddr = addr;
- esc->sc_pdmalen = len;
+ esc->sc_dmalen = len;
esc->sc_datain = datain;
esc->sc_dmasize = *dmasize;
esc->sc_tc = 0;
@@ -401,23 +483,218 @@ esp_dma_go(sc)
if (esc->sc_datain == 0) {
esc->sc_reg[NCR_FIFO * 16] = **esc->sc_dmaaddr;
- (*esc->sc_pdmalen)--;
+ (*esc->sc_dmalen)--;
(*esc->sc_dmaaddr)++;
}
esc->sc_active = 1;
}
void
-esp_dma_stop(sc)
+esp_quick_write_reg(sc, reg, val)
struct ncr53c9x_softc *sc;
+ int reg;
+ u_char val;
{
+ struct esp_softc *esc = (struct esp_softc *)sc;
+ u_char v = val;
+
+ esc->sc_reg[reg * 16] = v;
}
int
-esp_dma_isactive(sc)
+esp_quick_dma_intr(sc)
struct ncr53c9x_softc *sc;
{
struct esp_softc *esc = (struct esp_softc *)sc;
+ int trans=0, resid=0;
- return esc->sc_active;
+ if (esc->sc_active == 0)
+ panic("dma_intr--inactive DMA\n");
+
+ esc->sc_active = 0;
+
+ if (esc->sc_dmasize == 0) {
+ int res;
+
+ res = 65536;
+ res -= NCR_READ_REG(sc, NCR_TCL);
+ res -= NCR_READ_REG(sc, NCR_TCM) << 8;
+ printf("dmaintr: discarded %d b (last transfer was %d b).\n",
+ res, esc->sc_prevdmasize);
+ return 0;
+ }
+
+ if (esc->sc_datain &&
+ (resid = (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF)) != 0) {
+ printf("dmaintr: empty FIFO of %d\n", resid);
+ DELAY(1);
+ }
+
+ if ((sc->sc_espstat & NCRSTAT_TC) == 0) {
+ resid += NCR_READ_REG(sc, NCR_TCL);
+ resid += NCR_READ_REG(sc, NCR_TCM) << 8;
+
+ if (resid == 0)
+ resid = 65536;
+ }
+
+ trans = esc->sc_dmasize - resid;
+ if (trans < 0) {
+ printf("dmaintr: trans < 0????");
+ trans = esc->sc_dmasize;
+ }
+
+ NCR_DMA(("dmaintr: trans %d, resid %d.\n", trans, resid));
+ *esc->sc_dmaaddr += trans;
+ *esc->sc_dmalen -= trans;
+
+ return 0;
+}
+
+int
+esp_quick_dma_setup(sc, addr, len, datain, dmasize)
+ struct ncr53c9x_softc *sc;
+ caddr_t *addr;
+ size_t *len;
+ int datain;
+ size_t *dmasize;
+{
+ struct esp_softc *esc = (struct esp_softc *)sc;
+
+ esc->sc_dmaaddr = addr;
+ esc->sc_dmalen = len;
+
+ esc->sc_pdmaddr = (u_int16_t *) *addr;
+ esc->sc_pdmalen = *len;
+
+ if (esc->sc_pdmalen & 1) {
+ esc->sc_pdmalen--;
+ esc->sc_pad = 1;
+ } else {
+ esc->sc_pad = 0;
+ }
+
+ esc->sc_datain = datain;
+ esc->sc_prevdmasize = esc->sc_dmasize;
+ esc->sc_dmasize = *dmasize;
+
+ return 0;
+}
+
+static __inline__ int
+esp_dafb_have_dreq(esc)
+ struct esp_softc *esc;
+{
+ u_int32_t r;
+
+ r = bus_space_read_4(esc->sc_tag, esc->sc_bsh, 0);
+ return (r & 0x200);
+}
+
+static __inline__ int
+esp_iosb_have_dreq(esc)
+ struct esp_softc *esc;
+{
+ return (via2_reg(vIFR) & V2IF_SCSIDRQ);
+}
+
+/* Faster spl constructs, without saving old values */
+#define __splx(s) __asm __volatile ("movew %0,sr" : : "di" (s));
+#define __splimp() __splx(mac68k_impipl)
+#define __splbio() __splx(mac68k_bioipl)
+
+void
+esp_quick_dma_go(sc)
+ struct ncr53c9x_softc *sc;
+{
+ struct esp_softc *esc = (struct esp_softc *)sc;
+ extern int *nofault;
+ label_t faultbuf;
+ u_int16_t volatile *pdma;
+ u_char volatile *statreg;
+ int espspl;
+
+ esc->sc_active = 1;
+
+ espspl = splbio();
+
+restart_dmago:
+ nofault = (int *) &faultbuf;
+ if (setjmp((label_t *) nofault)) {
+ int i=0;
+
+ nofault = (int *) 0;
+ statreg = esc->sc_reg + NCR_STAT * 16;
+ for (;;) {
+ if (*statreg & 0x80) {
+ goto gotintr;
+ }
+
+ if (esp_have_dreq(esc)) {
+ break;
+ }
+
+ DELAY(1);
+ if (i++ > 10000)
+ panic("esp_dma_go: Argh!");
+ }
+ goto restart_dmago;
+ }
+
+ statreg = esc->sc_reg + NCR_STAT * 16;
+ pdma = (u_int16_t *) (esc->sc_reg + 0x100);
+
+#define WAIT while (!esp_have_dreq(esc)) if (*statreg & 0x80) goto gotintr
+
+ if (esc->sc_datain == 0) {
+ while (esc->sc_pdmalen) {
+ WAIT;
+ __splimp(); *pdma = *(esc->sc_pdmaddr)++; __splbio();
+ esc->sc_pdmalen -= 2;
+ }
+ if (esc->sc_pad) {
+ unsigned short us;
+ unsigned char *c;
+ c = (unsigned char *) esc->sc_pdmaddr;
+ us = *c;
+ WAIT;
+ __splimp(); *pdma = us; __splbio();
+ }
+ } else {
+ while (esc->sc_pdmalen) {
+ WAIT;
+ __splimp(); *(esc->sc_pdmaddr)++ = *pdma; __splbio();
+ esc->sc_pdmalen -= 2;
+ }
+ if (esc->sc_pad) {
+ unsigned short us;
+ unsigned char *c;
+ WAIT;
+ __splimp(); us = *pdma; __splbio();
+ c = (unsigned char *) esc->sc_pdmaddr;
+ *c = us & 0xff;
+ }
+ }
+#undef WAIT
+ nofault = (int *) 0;
+
+ if ((*statreg & 0x80) == 0) {
+ splx(espspl);
+ return;
+ }
+
+gotintr:
+ ncr53c9x_intr(sc);
+ splx(espspl);
+}
+
+int
+esp_intr(void *v)
+{
+ struct esp_softc *esc = (struct esp_softc *)v;
+
+ if (esc->sc_reg[NCR_STAT * 16] & NCRSTAT_INT)
+ return (ncr53c9x_intr(v));
+
+ return (0);
}
diff --git a/sys/arch/mac68k/dev/espvar.h b/sys/arch/mac68k/dev/espvar.h
index 38370af83ef..9dd138dceb0 100644
--- a/sys/arch/mac68k/dev/espvar.h
+++ b/sys/arch/mac68k/dev/espvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: espvar.h,v 1.5 2004/12/08 06:59:43 miod Exp $ */
+/* $OpenBSD: espvar.h,v 1.6 2004/12/10 18:23:23 martin Exp $ */
/* $NetBSD: espvar.h,v 1.16 1996/10/13 02:59:50 christos Exp $ */
/*
@@ -33,6 +33,8 @@
struct esp_softc {
struct ncr53c9x_softc sc_ncr53c9x; /* glue to MI code */
+ bus_space_tag_t sc_tag;
+ bus_space_handle_t sc_bsh;
struct via2hand sc_ih;
volatile u_char *sc_reg; /* the registers */
@@ -40,11 +42,13 @@ struct esp_softc {
u_char irq_mask; /* mask for clearing IRQ */
int sc_active; /* Pseudo-DMA state vars */
- int sc_tc;
int sc_datain;
size_t sc_dmasize;
- size_t sc_dmatrans;
char **sc_dmaaddr;
- size_t *sc_pdmalen;
-
+ size_t *sc_dmalen;
+ int sc_tc; /* only used in non-quick */
+ u_int16_t *sc_pdmaddr; /* only used in quick */
+ int sc_pdmalen; /* only used in quick */
+ size_t sc_prevdmasize; /* only used in quick */
+ int sc_pad; /* only used in quick */
};