summaryrefslogtreecommitdiff
path: root/sys/arch/sun3/dev/si.c
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>1996-05-03 16:05:01 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>1996-05-03 16:05:01 +0000
commitf19f6f3c1fb3e32d0ec3a2cfc1155f5773773fac (patch)
treeebc4f86aa5ec5a8abcda7997297093a14dfb8d0b /sys/arch/sun3/dev/si.c
parentcb307111a88f57667598461d6faaf39bff1ef2f8 (diff)
sync with 0430.
Diffstat (limited to 'sys/arch/sun3/dev/si.c')
-rw-r--r--sys/arch/sun3/dev/si.c1167
1 files changed, 315 insertions, 852 deletions
diff --git a/sys/arch/sun3/dev/si.c b/sys/arch/sun3/dev/si.c
index b803393242e..893c5368c3a 100644
--- a/sys/arch/sun3/dev/si.c
+++ b/sys/arch/sun3/dev/si.c
@@ -1,10 +1,8 @@
-/* $NetBSD: si.c,v 1.22 1995/10/08 23:42:58 gwr Exp $ */
+/* $NetBSD: si.c,v 1.24 1996/03/26 15:01:10 gwr Exp $ */
/*
- * Copyright (C) 1994 Adam Glass, Gordon W. Ross
- * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo,
- * Michael L. Finch, Bradley A. Grantham, and
- * Lawrence A. Kesteloot
+ * Copyright (c) 1995 David Jones, Gordon W. Ross
+ * Copyright (c) 1994 Adam Glass
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -15,406 +13,262 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
+ * 3. The name of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * 4. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
- * This product includes software developed by the Alice Group.
- * 4. The names of the Alice Group or any of its members may not be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
+ * This product includes software developed by
+ * Adam Glass, David Jones, and Gordon Ross
*
- * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#define DEBUG 1
-
-/* XXX - Need to add support for real DMA. -gwr */
-/* #define PSEUDO_DMA 1 (broken) */
+/*
+ * This file contains only the machine-dependent parts of the
+ * Sun3 SCSI driver. (Autoconfig stuff and DMA functions.)
+ * The machine-independent parts are in ncr5380sbc.c
+ *
+ * Supported hardware includes:
+ * Sun SCSI-3 on OBIO (Sun3/50,Sun3/60)
+ * Sun SCSI-3 on VME (Sun3/160,Sun3/260)
+ *
+ * Could be made to support the Sun3/E if someone wanted to.
+ *
+ * Note: Both supported variants of the Sun SCSI-3 adapter have
+ * some really unusual "features" for this driver to deal with,
+ * generally related to the DMA engine. The OBIO variant will
+ * ignore any attempt to write the FIFO count register while the
+ * SCSI bus is in DATA_IN or DATA_OUT phase. This is dealt with
+ * by setting the FIFO count early in COMMAND or MSG_IN phase.
+ *
+ * The VME variant has a bit to enable or disable the DMA engine,
+ * but that bit also gates the interrupt line from the NCR5380!
+ * Therefore, in order to get any interrupt from the 5380, (i.e.
+ * for reselect) one must clear the DMA engine transfer count and
+ * then enable DMA. This has the further complication that you
+ * CAN NOT touch the NCR5380 while the DMA enable bit is set, so
+ * we have to turn DMA back off before we even look at the 5380.
+ *
+ * What wonderfully whacky hardware this is!
+ *
+ * Credits, history:
+ *
+ * David Jones wrote the initial version of this module, which
+ * included support for the VME adapter only. (no reselection).
+ *
+ * Gordon Ross added support for the OBIO adapter, and re-worked
+ * both the VME and OBIO code to support disconnect/reselect.
+ * (Required figuring out the hardware "features" noted above.)
+ *
+ * The autoconfiguration boilerplate came from Adam Glass.
+ */
-#include <sys/types.h>
-#include <sys/malloc.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/user.h>
-#include <sys/device.h>
-
-#include <machine/autoconf.h>
-#include <machine/isr.h>
-#include <machine/obio.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsiconf.h>
-#include "scsi_defs.h"
-#include "scsi_5380.h"
-#include "scsi_sunsi.h"
-
-#ifdef DEBUG
-static int si_debug = 0;
-static int si_flags = 0 /* | SDEV_DB2 */ ;
-#endif
-
-#define SCI_PHASE_DISC 0 /* sort of ... */
-#define SCI_CLR_INTR(regs) ((volatile)(regs->sci_iack))
-#define SCI_ACK(ptr,phase) (ptr)->sci_tcmd = (phase)
-#define SCSI_TIMEOUT_VAL 1000000
-#define WAIT_FOR_NOT_REQ(ptr) { \
- int scsi_timeout = SCSI_TIMEOUT_VAL; \
- while ( ((ptr)->sci_bus_csr & SCI_BUS_REQ) && \
- ((ptr)->sci_bus_csr & SCI_BUS_REQ) && \
- ((ptr)->sci_bus_csr & SCI_BUS_REQ) && \
- (--scsi_timeout) ); \
- if (!scsi_timeout) { \
- printf("scsi timeout--WAIT_FOR_NOT_REQ---%s, line %d.\n", \
- __FILE__, __LINE__); \
- goto scsi_timeout_error; \
- } \
- }
-#define WAIT_FOR_REQ(ptr) { \
- int scsi_timeout = SCSI_TIMEOUT_VAL; \
- while ( (((ptr)->sci_bus_csr & SCI_BUS_REQ) == 0) && \
- (((ptr)->sci_bus_csr & SCI_BUS_REQ) == 0) && \
- (((ptr)->sci_bus_csr & SCI_BUS_REQ) == 0) && \
- (--scsi_timeout) ); \
- if (!scsi_timeout) { \
- printf("scsi timeout--WAIT_FOR_REQ---%s, line %d.\n", \
- __FILE__, __LINE__); \
- goto scsi_timeout_error; \
- } \
- }
-#define WAIT_FOR_BSY(ptr) { \
- int scsi_timeout = SCSI_TIMEOUT_VAL; \
- while ( (((ptr)->sci_bus_csr & SCI_BUS_BSY) == 0) && \
- (((ptr)->sci_bus_csr & SCI_BUS_BSY) == 0) && \
- (((ptr)->sci_bus_csr & SCI_BUS_BSY) == 0) && \
- (--scsi_timeout) ); \
- if (!scsi_timeout) { \
- printf("scsi timeout--WAIT_FOR_BSY---%s, line %d.\n", \
- __FILE__, __LINE__); \
- goto scsi_timeout_error; \
- } \
- }
-
-#define ARBITRATION_RETRIES 1000
+#include <machine/autoconf.h>
+#include <machine/isr.h>
+#include <machine/obio.h>
+#include <machine/dvma.h>
-/* XXX - Always available, but might do nothing. */
-int Debugger();
+#define DEBUG XXX
-struct ncr5380_softc {
- struct device sc_dev;
- volatile void *sc_regs;
- int sc_adapter_type;
- int sc_adapter_iv_am; /* int. vec + address modifier */
- struct scsi_link sc_link;
-};
+#include <dev/ic/ncr5380reg.h>
+#include <dev/ic/ncr5380var.h>
-static void ncr5380_minphys(struct buf *bp);
-static int ncr5380_scsi_cmd(struct scsi_xfer *xs);
-static int ncr5380_reset_adapter(struct ncr5380_softc *);
-static int ncr5380_reset_scsibus(struct ncr5380_softc *);
-static int ncr5380_poll(int adapter, int timeout);
-static int ncr5380_send_cmd(struct scsi_xfer *xs);
+#include "sireg.h"
+#include "sivar.h"
-static int ncr_intr(void *);
+int si_debug = 0;
+#ifdef DEBUG
+static int si_link_flags = 0 /* | SDEV_DB2 */ ;
+#endif
-static int si_generic(int adapter, int id, int lun,
- struct scsi_generic *cmd, int cmdlen,
- void *databuf, int datalen);
-static int si_group0(int adapter, int id, int lun,
- int opcode, int addr, int len,
- int flags, caddr_t databuf, int datalen);
+/* How long to wait for DMA before declaring an error. */
+int si_dma_intr_timo = 500; /* ticks (sec. X 100) */
-static char scsi_name[] = "si";
+static void si_minphys __P((struct buf *));
+static int si_print __P((void *, char *));
-struct scsi_adapter ncr5380_switch = {
+static struct scsi_adapter si_ops = {
ncr5380_scsi_cmd, /* scsi_cmd() */
- ncr5380_minphys, /* scsi_minphys() */
+ si_minphys, /* scsi_minphys() */
NULL, /* open_target_lu() */
NULL, /* close_target_lu() */
};
/* This is copied from julian's bt driver */
/* "so we have a default dev struct for our link struct." */
-struct scsi_device ncr_dev = {
+static struct scsi_device si_dev = {
NULL, /* Use default error handler. */
NULL, /* Use default start handler. */
NULL, /* Use default async handler. */
NULL, /* Use default "done" routine. */
};
-static int si_match();
-static void si_attach();
+/*
+ * New-style autoconfig attachment. The cfattach
+ * structures are in si_obio.c and si_vme.c
+ */
-struct cfdriver sicd = {
- NULL, "si", si_match, si_attach, DV_DULL,
- sizeof(struct ncr5380_softc), NULL, 0,
+struct cfdriver si_cd = {
+ NULL, "si", DV_DULL
};
-static int
-si_print(aux, name)
- void *aux;
- char *name;
-{
- if (name != NULL)
- printf("%s: scsibus ", name);
- return UNCONF;
-}
-static int
-si_match(parent, vcf, args)
- struct device *parent;
- void *vcf, *args;
+void
+si_attach(sc)
+ struct si_softc *sc;
{
- struct cfdata *cf = vcf;
- struct confargs *ca = args;
- int x, probe_addr;
-
- /* Default interrupt priority always splbio==2 */
- if (ca->ca_intpri == -1)
- ca->ca_intpri = 2;
-
- if ((cpu_machine_id == SUN3_MACH_50) ||
- (cpu_machine_id == SUN3_MACH_60) )
- {
- /* Sun3/50 or Sun3/60 have only OBIO "si" */
- if (ca->ca_bustype != BUS_OBIO)
- return(0);
- if (ca->ca_paddr == -1)
- ca->ca_paddr = OBIO_NCR_SCSI;
- /* OK... */
- } else {
- /* Other Sun3 models may have VME "si" or "sc" */
- if (ca->ca_bustype != BUS_VME16)
- return (0);
- if (ca->ca_paddr == -1)
- return (0);
- /* OK... */
- }
-
- /* Make sure there is something there... */
- x = bus_peek(ca->ca_bustype, ca->ca_paddr + 1, 1);
- if (x == -1)
- return (0);
+ struct ncr5380_softc *ncr_sc = (void *)sc;
+ volatile struct si_regs *regs = sc->sc_regs;
+ int i;
/*
- * If this is a VME SCSI board, we have to determine whether
- * it is an "sc" (Sun2) or "si" (Sun3) SCSI board. This can
- * be determined using the fact that the "sc" board occupies
- * 4K bytes in VME space but the "si" board occupies 2K bytes.
+ * Fill in the prototype scsi_link.
*/
- if (ca->ca_bustype == BUS_VME16) {
- /* Note, the "si" board should NOT respond here. */
- x = bus_peek(ca->ca_bustype, ca->ca_paddr + 0x801, 1);
- if (x != -1)
- return(0);
- }
+ ncr_sc->sc_link.adapter_softc = sc;
+ ncr_sc->sc_link.adapter_target = 7;
+ ncr_sc->sc_link.adapter = &si_ops;
+ ncr_sc->sc_link.device = &si_dev;
- return (1);
-}
+#ifdef DEBUG
+ if (si_debug)
+ printf("si: Set TheSoftC=%x TheRegs=%x\n", sc, regs);
+ ncr_sc->sc_link.flags |= si_link_flags;
+#endif
-static void
-si_attach(parent, self, args)
- struct device *parent, *self;
- void *args;
-{
- struct ncr5380_softc *ncr5380 = (struct ncr5380_softc *) self;
- volatile struct si_regs *regs;
- struct confargs *ca = args;
-
- switch (ca->ca_bustype) {
-
- case BUS_OBIO:
- regs = (struct si_regs *)
- obio_alloc(ca->ca_paddr, sizeof(*regs));
- isr_add_autovect(ncr_intr, (void *)ncr5380,
- ca->ca_intpri);
- break;
-
- case BUS_VME16:
- regs = (struct si_regs *)
- bus_mapin(ca->ca_bustype, ca->ca_paddr, sizeof(*regs));
- isr_add_vectored(ncr_intr, (void *)ncr5380,
- ca->ca_intpri, ca->ca_intvec);
- break;
-
- default:
- printf("unknown\n");
- return;
- }
+ /*
+ * Initialize fields used by the MI code
+ */
+ ncr_sc->sci_r0 = &regs->sci.sci_r0;
+ ncr_sc->sci_r1 = &regs->sci.sci_r1;
+ ncr_sc->sci_r2 = &regs->sci.sci_r2;
+ ncr_sc->sci_r3 = &regs->sci.sci_r3;
+ ncr_sc->sci_r4 = &regs->sci.sci_r4;
+ ncr_sc->sci_r5 = &regs->sci.sci_r5;
+ ncr_sc->sci_r6 = &regs->sci.sci_r6;
+ ncr_sc->sci_r7 = &regs->sci.sci_r7;
- ncr5380->sc_adapter_type = ca->ca_bustype;
- ncr5380->sc_adapter_iv_am =
- VME_SUPV_DATA_24 | (ca->ca_intvec & 0xFF);
- ncr5380->sc_regs = regs;
+ /*
+ * Allocate DMA handles.
+ */
+ i = SCI_OPENINGS * sizeof(struct si_dma_handle);
+ sc->sc_dma = (struct si_dma_handle *)
+ malloc(i, M_DEVBUF, M_WAITOK);
+ if (sc->sc_dma == NULL)
+ panic("si: dvma_malloc failed\n");
+ for (i = 0; i < SCI_OPENINGS; i++)
+ sc->sc_dma[i].dh_flags = 0;
/*
- * fill in the prototype scsi_link.
+ * Initialize si board itself.
*/
- ncr5380->sc_link.adapter_softc = ncr5380;
- ncr5380->sc_link.adapter_target = 7;
- ncr5380->sc_link.adapter = &ncr5380_switch;
- ncr5380->sc_link.device = &ncr_dev;
- ncr5380->sc_link.openings = 2;
-#ifdef DEBUG
- ncr5380->sc_link.flags |= si_flags;
-#endif
+ si_reset_adapter(ncr_sc);
+ ncr5380_init(ncr_sc);
+ ncr5380_reset_scsibus(ncr_sc);
+ config_found(&(ncr_sc->sc_dev), &(ncr_sc->sc_link), si_print);
+}
- printf("\n");
- ncr5380_reset_adapter(ncr5380);
- ncr5380_reset_scsibus(ncr5380);
- config_found(self, &(ncr5380->sc_link), si_print);
+static int
+si_print(aux, name)
+ void *aux;
+ char *name;
+{
+ if (name != NULL)
+ printf("%s: scsibus ", name);
+ return UNCONF;
}
-#define MIN_PHYS 65536 /*BARF!!!!*/
static void
-ncr5380_minphys(struct buf *bp)
+si_minphys(struct buf *bp)
{
- if (bp->b_bcount > MIN_PHYS) {
- printf("Uh-oh... ncr5380_minphys setting bp->b_bcount = %x.\n", MIN_PHYS);
- bp->b_bcount = MIN_PHYS;
+ if (bp->b_bcount > MAX_DMA_LEN) {
+#ifdef DEBUG
+ if (si_debug) {
+ printf("si_minphys len = 0x%x.\n", bp->b_bcount);
+ Debugger();
+ }
+#endif
+ bp->b_bcount = MAX_DMA_LEN;
}
- minphys(bp);
+ return (minphys(bp));
}
-#undef MIN_PHYS
-static int
-ncr5380_scsi_cmd(struct scsi_xfer *xs)
+
+#define CSR_WANT (SI_CSR_SBC_IP | SI_CSR_DMA_IP | \
+ SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR )
+
+int
+si_intr(void *arg)
{
- int flags, s, r;
+ struct si_softc *sc = arg;
+ volatile struct si_regs *si = sc->sc_regs;
+ int dma_error, claimed;
+ u_short csr;
- flags = xs->flags;
- if (xs->bp) flags |= (SCSI_NOSLEEP);
- if ( flags & ITSDONE ) {
- printf("Already done?");
- xs->flags &= ~ITSDONE;
- }
- if ( ! ( flags & INUSE ) ) {
- printf("Not in use?");
- xs->flags |= INUSE;
- }
+ claimed = 0;
+ dma_error = 0;
- s = splbio();
+ /* SBC interrupt? DMA interrupt? */
+ csr = si->si_csr;
+ NCR_TRACE("si_intr: csr=0x%x\n", csr);
- if ( flags & SCSI_RESET ) {
- printf("flags & SCSIRESET.\n");
- ncr5380_reset_scsibus(xs->sc_link->adapter_softc);
- r = COMPLETE;
- } else {
- r = ncr5380_send_cmd(xs);
- xs->flags |= ITSDONE;
- scsi_done(xs);
+ if (csr & SI_CSR_DMA_CONFLICT) {
+ dma_error |= SI_CSR_DMA_CONFLICT;
+ printf("si_intr: DMA conflict\n");
}
-
- splx(s);
-
- switch(r) {
- case COMPLETE:
- case SUCCESSFULLY_QUEUED:
- r = SUCCESSFULLY_QUEUED;
- if (xs->flags & SCSI_POLL)
- r = COMPLETE;
- break;
- default:
- break;
+ if (csr & SI_CSR_DMA_BUS_ERR) {
+ dma_error |= SI_CSR_DMA_BUS_ERR;
+ printf("si_intr: DMA bus error\n");
}
- return r;
-}
-
-#ifdef DEBUG
-static int
-ncr5380_show_scsi_cmd(struct scsi_xfer *xs)
-{
- u_char *b = (u_char *) xs->cmd;
- int i = 0;
-
- if ( ! ( xs->flags & SCSI_RESET ) ) {
- printf("si(%d:%d:%d)-",
- xs->sc_link->scsibus,
- xs->sc_link->target,
- xs->sc_link->lun);
- while (i < xs->cmdlen) {
- if (i) printf(",");
- printf("%x",b[i++]);
- }
- printf("-\n");
- } else {
- printf("si(%d:%d:%d)-RESET-\n",
- xs->sc_link->scsibus,
- xs->sc_link->target,
- xs->sc_link->lun);
+ if (dma_error) {
+ if (sc->ncr_sc.sc_state & NCR_DOINGDMA)
+ sc->ncr_sc.sc_state |= NCR_ABORTING;
+ /* Make sure we will call the main isr. */
+ csr |= SI_CSR_DMA_IP;
}
-}
-#endif
-
-/*
- * Actual chip control.
- */
-static void
-ncr_sbc_intr(struct ncr5380_softc *ncr5380)
-{
- volatile sci_regmap_t *regs = ncr5380->sc_regs;
-
- if ((regs->sci_csr & SCI_CSR_INT) == 0) {
+ if (csr & (SI_CSR_SBC_IP | SI_CSR_DMA_IP)) {
+ claimed = ncr5380_intr(&sc->ncr_sc);
#ifdef DEBUG
- printf (" ncr_sbc_intr: spurrious\n");
+ if (!claimed) {
+ printf("si_intr: spurious from SBC\n");
+ if (si_debug & 4) {
+ Debugger(); /* XXX */
+ }
+ }
#endif
- return;
}
- SCI_CLR_INTR(regs);
-#ifdef DEBUG
- printf (" ncr_sbc_intr\n");
-#endif
-}
-
-static void
-ncr_dma_intr(struct ncr5380_softc *ncr5380)
-{
- volatile struct si_regs *regs = ncr5380->sc_regs;
-
-#ifdef DEBUG
- printf (" ncr_dma_intr\n");
-#endif
+ return (claimed);
}
-static int
-ncr_intr(void *arg)
-{
- struct ncr5380_softc *ncr5380 = arg;
- volatile struct si_regs *si = ncr5380->sc_regs;
- int rv = 0;
-
- /* Interrupts not enabled? Can not be for us. */
- if ((si->si_csr & SI_CSR_INTR_EN) == 0)
- return rv;
-
- if (si->si_csr & SI_CSR_DMA_IP) {
- ncr_dma_intr(ncr5380);
- rv++;
- }
- if (si->si_csr & SI_CSR_SBC_IP) {
- ncr_sbc_intr(ncr5380);
- rv++;
- }
- return rv;
-}
-static int
-ncr5380_reset_adapter(struct ncr5380_softc *sc)
+void
+si_reset_adapter(struct ncr5380_softc *ncr_sc)
{
+ struct si_softc *sc = (struct si_softc *)ncr_sc;
volatile struct si_regs *si = sc->sc_regs;
#ifdef DEBUG
@@ -423,585 +277,194 @@ ncr5380_reset_adapter(struct ncr5380_softc *sc)
}
#endif
- /* The reset bits in the CSR are active low. */
+ /*
+ * The SCSI3 controller has an 8K FIFO to buffer data between the
+ * 5380 and the DMA. Make sure it starts out empty.
+ *
+ * The reset bits in the CSR are active low.
+ */
si->si_csr = 0;
- delay(20);
- si->si_csr = SI_CSR_FIFO_RES | SI_CSR_SCSI_RES;
+ delay(10);
+ si->si_csr = SI_CSR_FIFO_RES | SI_CSR_SCSI_RES | SI_CSR_INTR_EN;
+ delay(10);
si->fifo_count = 0;
+
if (sc->sc_adapter_type == BUS_VME16) {
si->dma_addrh = 0;
si->dma_addrl = 0;
si->dma_counth = 0;
si->dma_countl = 0;
- si->iv_am = sc->sc_adapter_iv_am;
+ si->si_iv_am = sc->sc_adapter_iv_am;
+ si->fifo_cnt_hi = 0;
}
-}
-
-static int
-ncr5380_reset_scsibus(struct ncr5380_softc *ncr5380)
-{
- volatile sci_regmap_t *regs = ncr5380->sc_regs;
-#ifdef DEBUG
- if (si_debug) {
- printf("si_reset_scsibus\n");
- }
-#endif
-
- regs->sci_icmd = SCI_ICMD_RST;
- delay(100);
- regs->sci_icmd = 0;
-
- regs->sci_mode = 0;
- regs->sci_tcmd = SCI_PHASE_DISC;
- regs->sci_sel_enb = 0;
-
- SCI_CLR_INTR(regs);
- /* XXX - Need long delay here! */
+ SCI_CLR_INTR(ncr_sc);
}
-static int
-ncr5380_poll(int adapter, int timeout)
-{
-}
-
-static int
-ncr5380_send_cmd(struct scsi_xfer *xs)
-{
- int sense;
-#ifdef DIAGNOSTIC
- if ((getsr() & PSL_IPL) < PSL_IPL2)
- panic("ncr_send_cmd: bad spl");
-#endif
+/*****************************************************************
+ * Common functions for DMA
+ ****************************************************************/
-#ifdef DEBUG
- if (si_debug & 2)
- ncr5380_show_scsi_cmd(xs);
-#endif
-
- sense = si_generic( xs->sc_link->scsibus, xs->sc_link->target,
- xs->sc_link->lun, xs->cmd, xs->cmdlen,
- xs->data, xs->datalen );
-
- switch (sense) {
- case 0: /* success */
- xs->resid = 0;
- xs->error = XS_NOERROR;
- break;
-
- case 0x02: /* Check condition */
-#ifdef DEBUG
- if (si_debug)
- printf("check cond. target %d.\n",
- xs->sc_link->target);
-#endif
- delay(10); /* Phil's fix for slow devices. */
- si_group0(xs->sc_link->scsibus,
- xs->sc_link->target,
- xs->sc_link->lun,
- 0x3, 0x0,
- sizeof(struct scsi_sense_data),
- 0, (caddr_t) &(xs->sense),
- sizeof(struct scsi_sense_data));
- xs->error = XS_SENSE;
- break;
- case 0x08: /* Busy - common code will delay, retry. */
- xs->error = XS_BUSY;
- break;
- default: /* Dead - tell common code to give up. */
- xs->error = XS_DRIVER_STUFFUP;
- break;
-
- }
- return (COMPLETE);
-}
-
-static int
-si_select_target(register volatile sci_regmap_t *regs,
- u_char myid, u_char tid, int with_atn)
+/*
+ * Allocate a DMA handle and put it in sc->sc_dma. Prepare
+ * for DMA transfer. On the Sun3, this means mapping the buffer
+ * into DVMA space. dvma_mapin() flushes the cache for us.
+ */
+void
+si_dma_alloc(ncr_sc)
+ struct ncr5380_softc *ncr_sc;
{
- register u_char bid, icmd;
- int ret = SCSI_RET_RETRY;
- int arb_retries, arb_wait;
-
- /* for our purposes.. */
- myid = 1 << myid;
- tid = 1 << tid;
-
- regs->sci_sel_enb = 0; /* we don't want any interrupts. */
- regs->sci_tcmd = 0; /* get into a harmless state */
+ struct si_softc *sc = (struct si_softc *)ncr_sc;
+ struct sci_req *sr = ncr_sc->sc_current;
+ struct scsi_xfer *xs = sr->sr_xs;
+ struct si_dma_handle *dh;
+ int i, xlen;
+ u_long addr;
- arb_retries = ARBITRATION_RETRIES;
-
-retry_arbitration:
- regs->sci_mode = 0; /* get into a harmless state */
-wait_for_bus_free:
- if (--arb_retries <= 0) {
-#ifdef DEBUG
- if (si_debug) {
- printf("si_select: arb_retries expended; resetting...\n");
- }
+#ifdef DIAGNOSTIC
+ if (sr->sr_dma_hand != NULL)
+ panic("si_dma_alloc: already have DMA handle");
#endif
- ret = SCSI_RET_NEED_RESET;
- goto nosel;
- }
- icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
+ addr = (u_long) ncr_sc->sc_dataptr;
+ xlen = ncr_sc->sc_datalen;
- if (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) {
- /* Something is sitting on the SCSI bus... */
-#ifdef DEBUG
- /* Only complain once (the last time through). */
- if (si_debug && (arb_retries <= 1)) {
- printf("si_select_target: still BSY+SEL\n");
- }
-#endif
- /* Give it a little time, then try again. */
- delay(10);
- goto wait_for_bus_free;
+ /* If the DMA start addr is misaligned then do PIO */
+ if ((addr & 1) || (xlen & 1)) {
+ printf("si_dma_alloc: misaligned.\n");
+ return;
}
- regs->sci_odata = myid;
- regs->sci_mode = SCI_MODE_ARB;
-/* regs->sci_mode |= SCI_MODE_ARB; XXX? */
-
- /* AIP might not set if BSY went true after we checked */
- /* Wait up to about 100 usec. for it to appear. */
- arb_wait = 50; /* X2 */
- do {
- if (regs->sci_icmd & SCI_ICMD_AIP)
- goto got_aip;
- delay2us();
- } while (--arb_wait > 0);
- /* XXX - Could have missed it? */
-#ifdef DEBUG
- if (si_debug)
- printf("si_select_target: API did not appear\n");
-#endif
- goto retry_arbitration;
+ /* Make sure our caller checked sc_min_dma_len. */
+ if (xlen < MIN_DMA_LEN)
+ panic("si_dma_alloc: xlen=0x%x\n", xlen);
- got_aip:
-#ifdef DEBUG
- if (si_debug & 4) {
- printf("si_select_target: API after %d tries (last wait %d)\n",
- ARBITRATION_RETRIES - arb_retries,
- (50 - arb_wait));
+ /*
+ * Never attempt single transfers of more than 63k, because
+ * our count register may be only 16 bits (an OBIO adapter).
+ * This should never happen since already bounded by minphys().
+ * XXX - Should just segment these...
+ */
+ if (xlen > MAX_DMA_LEN) {
+ printf("si_dma_alloc: excessive xlen=0x%x\n", xlen);
+ Debugger();
+ ncr_sc->sc_datalen = xlen = MAX_DMA_LEN;
}
-#endif
- delay(3); /* 2.2 uSec. arbitration delay */
-
- if (regs->sci_icmd & SCI_ICMD_LST) {
-#ifdef DEBUG
- if (si_debug)
- printf ("lost 1\n");
-#endif
- goto retry_arbitration; /* XXX */
+ /* Find free DMA handle. Guaranteed to find one since we have
+ as many DMA handles as the driver has processes. */
+ for (i = 0; i < SCI_OPENINGS; i++) {
+ if ((sc->sc_dma[i].dh_flags & SIDH_BUSY) == 0)
+ goto found;
}
+ panic("si: no free DMA handles.");
+found:
- regs->sci_mode &= ~SCI_MODE_PAR_CHK;
- bid = regs->sci_data;
-
- if ((bid & ~myid) > myid) {
-#ifdef DEBUG
- if (si_debug)
- printf ("lost 2\n");
-#endif
- /* Trying again will not help. */
- goto lost;
- }
- if (regs->sci_icmd & SCI_ICMD_LST) {
-#ifdef DEBUG
- if (si_debug)
- printf ("lost 3\n");
-#endif
- goto lost;
- }
+ dh = &sc->sc_dma[i];
+ dh->dh_flags = SIDH_BUSY;
+ dh->dh_addr = (u_char*) addr;
+ dh->dh_maplen = xlen;
+ dh->dh_dvma = 0;
- /* Won arbitration, enter selection phase now */
- icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
- icmd |= (with_atn ? (SCI_ICMD_SEL|SCI_ICMD_ATN) : SCI_ICMD_SEL);
- regs->sci_icmd = icmd;
+ /* Copy the "write" flag for convenience. */
+ if (xs->flags & SCSI_DATA_OUT)
+ dh->dh_flags |= SIDH_OUT;
- if (regs->sci_icmd & SCI_ICMD_LST) {
-#ifdef DEBUG
- if (si_debug)
- printf ("nosel\n");
+#if 0
+ /*
+ * Some machines might not need to remap B_PHYS buffers.
+ * The sun3 does not map B_PHYS buffers into DVMA space,
+ * (they are mapped into normal KV space) so on the sun3
+ * we must always remap to a DVMA address here. Re-map is
+ * cheap anyway, because it's done by segments, not pages.
+ */
+ if (xs->bp && (xs->bp->b_flags & B_PHYS))
+ dh->dh_flags |= SIDH_PHYS;
#endif
- goto nosel;
- }
-
- /* XXX a target that violates specs might still drive the bus XXX */
- /* XXX should put our id out, and after the delay check nothi XXX */
- /* XXX ng else is out there. XXX */
-
- delay2us();
-
- regs->sci_sel_enb = 0;
-
- regs->sci_odata = myid | tid;
-
- icmd |= SCI_ICMD_BSY|SCI_ICMD_DATA;
- regs->sci_icmd = icmd;
-
-/* regs->sci_mode &= ~SCI_MODE_ARB; 2 deskew delays, too */
- regs->sci_mode = 0; /* 2 deskew delays, too */
-
- icmd &= ~SCI_ICMD_BSY;
- regs->sci_icmd = icmd;
-
- /* bus settle delay, 400ns */
- delay2us(); /* too much (was 2) ? */
- regs->sci_mode |= SCI_MODE_PAR_CHK;
-
- {
- register int timeo = 2500;/* 250 msecs in 100 usecs chunks */
- while ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) {
- if (--timeo > 0) {
- delay(100);
- } else {
- /* This is the "normal" no-such-device select error. */
-#ifdef DEBUG
- if (si_debug)
- printf("si_select: not BSY (nothing there)\n");
-#endif
- goto nodev;
- }
- }
+ dh->dh_dvma = (u_long) dvma_mapin((char *)addr, xlen);
+ if (!dh->dh_dvma) {
+ /* Can't remap segment */
+ printf("si_dma_alloc: can't remap %x/%x\n",
+ dh->dh_addr, dh->dh_maplen);
+ dh->dh_flags = 0;
+ return;
}
- icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL);
- regs->sci_icmd = icmd;
-/* regs->sci_sel_enb = myid;*/ /* looks like we should NOT have it */
- /* XXX - SCI_MODE_PAR_CHK ? */
- return SCSI_RET_SUCCESS;
-
-nodev:
- ret = SCSI_RET_DEVICE_DOWN;
- regs->sci_sel_enb = myid;
-nosel:
- regs->sci_icmd = 0;
- regs->sci_mode = 0;
- return ret;
-
-lost:
- regs->sci_icmd = 0;
- regs->sci_mode = 0;
-#ifdef DEBUG
- if (si_debug) {
- printf("si_select: lost arbitration\n");
- }
-#endif
- return ret;
-}
+ /* success */
+ sr->sr_dma_hand = dh;
-sci_data_out(regs, phase, count, data)
- register volatile sci_regmap_t *regs;
- unsigned char *data;
-{
- register unsigned char icmd;
- register int cnt=0;
-
- /* ..checks.. */
-
- icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
-loop:
- /* SCSI bus phase not valid until REQ is true. */
- WAIT_FOR_REQ(regs);
- if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase)
- return cnt;
-
- icmd |= SCI_ICMD_DATA;
- regs->sci_icmd = icmd;
- regs->sci_odata = *data++;
- icmd |= SCI_ICMD_ACK;
- regs->sci_icmd = icmd;
-
- icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_ACK);
- WAIT_FOR_NOT_REQ(regs);
- regs->sci_icmd = icmd;
- ++cnt;
- if (--count > 0)
- goto loop;
-scsi_timeout_error:
- return cnt;
+ return;
}
-sci_data_in(regs, phase, count, data)
- register volatile sci_regmap_t *regs;
- unsigned char *data;
-{
- register unsigned char icmd;
- register int cnt=0;
-
- /* ..checks.. */
-
- icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST);
-
-loop:
- /* SCSI bus phase not valid until REQ is true. */
- WAIT_FOR_REQ(regs);
- if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase)
- return cnt;
-
- *data++ = regs->sci_data;
- icmd |= SCI_ICMD_ACK;
- regs->sci_icmd = icmd;
- icmd &= ~SCI_ICMD_ACK;
- WAIT_FOR_NOT_REQ(regs);
- regs->sci_icmd = icmd;
- ++cnt;
- if (--count > 0)
- goto loop;
-
-scsi_timeout_error:
- return cnt;
-}
-
-/* Return -1 (error) or number of bytes sent (>=0). */
-static int
-si_command_transfer(register volatile sci_regmap_t *regs,
- int maxlen, u_char *data, u_char *status, u_char *msg)
+void
+si_dma_free(ncr_sc)
+ struct ncr5380_softc *ncr_sc;
{
- int xfer, phase;
-
- xfer = 0;
- regs->sci_icmd = 0;
-
- while (1) {
-
- WAIT_FOR_REQ(regs);
-
- phase = SCI_CUR_PHASE(regs->sci_bus_csr);
-
- switch (phase) {
- case SCSI_PHASE_CMD:
- SCI_ACK(regs,SCSI_PHASE_CMD);
- xfer += sci_data_out(regs, SCSI_PHASE_CMD,
- maxlen, data);
- goto out;
+ struct sci_req *sr = ncr_sc->sc_current;
+ struct si_dma_handle *dh = sr->sr_dma_hand;
- case SCSI_PHASE_DATA_IN:
- printf("command_transfer: Data in phase?\n");
- goto err;
-
- case SCSI_PHASE_DATA_OUT:
- printf("command_transfer: Data out phase?\n");
- goto err;
-
- case SCSI_PHASE_STATUS:
- SCI_ACK(regs,SCSI_PHASE_STATUS);
- printf("command_transfer: status in...\n");
- sci_data_in(regs, SCSI_PHASE_STATUS,
- 1, status);
- printf("command_transfer: status=0x%x\n", *status);
- goto err;
-
- case SCSI_PHASE_MESSAGE_IN:
- SCI_ACK(regs,SCSI_PHASE_MESSAGE_IN);
- printf("command_transfer: msg in?\n");
- sci_data_in(regs, SCSI_PHASE_MESSAGE_IN,
- 1, msg);
- break;
+#ifdef DIAGNOSTIC
+ if (dh == NULL)
+ panic("si_dma_free: no DMA handle");
+#endif
- case SCSI_PHASE_MESSAGE_OUT:
- SCI_ACK(regs,SCSI_PHASE_MESSAGE_OUT);
- sci_data_out(regs, SCSI_PHASE_MESSAGE_OUT,
- 1, msg);
- break;
+ if (ncr_sc->sc_state & NCR_DOINGDMA)
+ panic("si_dma_free: free while in progress");
- default:
- printf("command_transfer: Unexpected phase 0x%x\n", phase);
- goto err;
- }
+ if (dh->dh_flags & SIDH_BUSY) {
+ /* XXX - Should separate allocation and mapping. */
+ /* Give back the DVMA space. */
+ dvma_mapout((caddr_t)dh->dh_dvma, dh->dh_maplen);
+ dh->dh_dvma = 0;
+ dh->dh_flags = 0;
}
-scsi_timeout_error:
- err:
- xfer = -1;
- out:
- return xfer;
+ sr->sr_dma_hand = NULL;
}
-static int
-si_data_transfer(register volatile sci_regmap_t *regs,
- int maxlen, u_char *data, u_char *status, u_char *msg)
-{
- int retlen = 0, xfer, phase;
-
- regs->sci_icmd = 0;
-
- *status = 0;
-
- while (1) {
-
- WAIT_FOR_REQ(regs);
-
- phase = SCI_CUR_PHASE(regs->sci_bus_csr);
-
- switch (phase) {
- case SCSI_PHASE_CMD:
- printf("Command phase in data_transfer().\n");
- return retlen;
- case SCSI_PHASE_DATA_IN:
- SCI_ACK(regs,SCSI_PHASE_DATA_IN);
-#if PSEUDO_DMA
- xfer = sci_pdma_in(regs, SCSI_PHASE_DATA_IN,
- maxlen, data);
-#else
- xfer = sci_data_in(regs, SCSI_PHASE_DATA_IN,
- maxlen, data);
-#endif
- retlen += xfer;
- maxlen -= xfer;
- break;
- case SCSI_PHASE_DATA_OUT:
- SCI_ACK(regs,SCSI_PHASE_DATA_OUT);
-#if PSEUDO_DMA
- xfer = sci_pdma_out(regs, SCSI_PHASE_DATA_OUT,
- maxlen, data);
-#else
- xfer = sci_data_out(regs, SCSI_PHASE_DATA_OUT,
- maxlen, data);
-#endif
- retlen += xfer;
- maxlen -= xfer;
- break;
- case SCSI_PHASE_STATUS:
- SCI_ACK(regs,SCSI_PHASE_STATUS);
- sci_data_in(regs, SCSI_PHASE_STATUS,
- 1, status);
- break;
- case SCSI_PHASE_MESSAGE_IN:
- SCI_ACK(regs,SCSI_PHASE_MESSAGE_IN);
- sci_data_in(regs, SCSI_PHASE_MESSAGE_IN,
- 1, msg);
- if (*msg == 0) {
- return retlen;
- } else {
- printf( "message 0x%x in "
- "data_transfer.\n", *msg);
- }
- break;
- case SCSI_PHASE_MESSAGE_OUT:
- SCI_ACK(regs,SCSI_PHASE_MESSAGE_OUT);
- sci_data_out(regs, SCSI_PHASE_MESSAGE_OUT,
- 1, msg);
- break;
- default:
- printf( "Unexpected phase 0x%x in "
- "data_transfer().\n", phase);
-scsi_timeout_error:
- return retlen;
- break;
- }
- }
-}
-static int
-si_dorequest(struct ncr5380_softc *sc,
- int target, int lun, u_char *cmd, int cmdlen,
- char *databuf, int datalen, int *sent)
- /* Returns 0 on success, -1 on internal error, or the status byte */
+/*
+ * Poll (spin-wait) for DMA completion.
+ * Called right after xx_dma_start(), and
+ * xx_dma_stop() will be called next.
+ * Same for either VME or OBIO.
+ */
+void
+si_dma_poll(ncr_sc)
+ struct ncr5380_softc *ncr_sc;
{
- register volatile sci_regmap_t *regs = sc->sc_regs;
- int cmd_bytes_sent, r;
- u_char stat, msg, c;
-
-#ifdef DEBUG
- if (si_debug) {
- printf("si_dorequest: target=%d, lun=%d\n", target, lun);
- }
-#endif
-
- *sent = 0;
+ struct si_softc *sc = (struct si_softc *)ncr_sc;
+ struct sci_req *sr = ncr_sc->sc_current;
+ struct si_dma_handle *dh = sr->sr_dma_hand;
+ volatile struct si_regs *si = sc->sc_regs;
+ int tmo, csr_mask;
- if ( ( r = si_select_target(regs, 7, target, 1) ) != SCSI_RET_SUCCESS) {
-#ifdef DEBUG
- if (si_debug) {
- printf("si_dorequest: select returned %d\n", r);
- }
-#endif
+ /* Make sure DMA started successfully. */
+ if (ncr_sc->sc_state & NCR_ABORTING)
+ return;
- SCI_CLR_INTR(regs);
- switch (r) {
-
- case SCSI_RET_NEED_RESET:
- printf("si_dorequest: target=%d, lun=%d, resetting...\n",
- target, lun, r);
- ncr5380_reset_adapter(sc);
- ncr5380_reset_scsibus(sc);
- /* fall through */
- case SCSI_RET_RETRY:
- return 0x08; /* Busy - tell common code to retry. */
-
- default:
- printf("si_dorequest: target=%d, lun=%d, error=%d.\n",
- target, lun, r);
- /* fall through */
- case SCSI_RET_DEVICE_DOWN:
- return -1; /* Dead - tell common code to give up. */
- }
+ csr_mask = SI_CSR_SBC_IP | SI_CSR_DMA_IP |
+ SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR;
+
+ tmo = 50000; /* X100 = 5 sec. */
+ for (;;) {
+ if (si->si_csr & csr_mask)
+ break;
+ if (--tmo <= 0) {
+ printf("si: DMA timeout (while polling)\n");
+ /* Indicate timeout as MI code would. */
+ sr->sr_flags |= SR_OVERDUE;
+ break;
}
-
- c = 0x80 | lun;
-
- if ((cmd_bytes_sent = si_command_transfer(regs, cmdlen,
- (u_char *) cmd, &stat, &c)) != cmdlen)
- {
- SCI_CLR_INTR(regs);
- if (cmd_bytes_sent >= 0) {
- printf("Data underrun sending CCB (%d bytes of %d, sent).\n",
- cmd_bytes_sent, cmdlen);
- }
- return -1;
+ delay(100);
}
- *sent = si_data_transfer(regs, datalen, (u_char *)databuf,
- &stat, &msg);
#ifdef DEBUG
if (si_debug) {
- printf("si_dorequest: data transfered = %d\n", *sent);
+ printf("si_dma_poll: done, csr=0x%x\n", si->si_csr);
}
#endif
-
- return stat;
}
-static int
-si_generic(int adapter, int id, int lun, struct scsi_generic *cmd,
- int cmdlen, void *databuf, int datalen)
-{
- register struct ncr5380_softc *sc = sicd.cd_devs[adapter];
- int i, j, sent;
-
- if (cmd->opcode == TEST_UNIT_READY) /* XXX */
- cmd->bytes[0] = ((u_char) lun << 5);
-
- i = si_dorequest(sc, id, lun, (u_char *) cmd, cmdlen,
- databuf, datalen, &sent);
-
- return i;
-}
-
-static int
-si_group0(int adapter, int id, int lun, int opcode, int addr, int len,
- int flags, caddr_t databuf, int datalen)
-{
- register struct ncr5380_softc *sc = sicd.cd_devs[adapter];
- unsigned char cmd[6];
- int i, j, sent;
-
- cmd[0] = opcode; /* Operation code */
- cmd[1] = (lun << 5) | ((addr >> 16) & 0x1F); /* Lun & MSB of addr */
- cmd[2] = (addr >> 8) & 0xFF; /* addr */
- cmd[3] = addr & 0xFF; /* LSB of addr */
- cmd[4] = len; /* Allocation length */
- cmd[5] = flags; /* Link/Flag */
-
- i = si_dorequest(sc, id, lun, cmd, 6, databuf, datalen, &sent);
-
- return i;
-}