diff options
author | Martin Reindl <martin@cvs.openbsd.org> | 2004-12-10 18:23:24 +0000 |
---|---|---|
committer | Martin Reindl <martin@cvs.openbsd.org> | 2004-12-10 18:23:24 +0000 |
commit | 1ca6c8bcdb555e355b99ab0309b6c17980abec54 (patch) | |
tree | 7c93a83462a73a39244753744f4e7868531fd18c | |
parent | c7c5fb35e66fa66c54c0b75a68cd932ea5746c96 (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.c | 341 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/espvar.h | 14 |
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 */ }; |