From: deraadt Date: Sun, 19 Nov 1995 13:51:25 +0000 (+0000) Subject: from netbsd: X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=3b7cd49182bd8fd2b4572dcd2b593b122fd0a6d1;p=openbsd from netbsd: Latest, greatest version of the new NCR driver. DMA works! Interrupts work! Disconnect/reselect works! To be paranoid, leave that all disabled for now... --- diff --git a/sys/arch/sun3/dev/ncr5380reg.h b/sys/arch/sun3/dev/ncr5380reg.h index 0901fd1de53..4a1e87101b2 100644 --- a/sys/arch/sun3/dev/ncr5380reg.h +++ b/sys/arch/sun3/dev/ncr5380reg.h @@ -1,4 +1,4 @@ -/* $NetBSD: ncr5380reg.h,v 1.1 1995/10/29 21:19:08 gwr Exp $ */ +/* $NetBSD: ncr5380reg.h,v 1.1.2.1 1995/11/18 07:08:23 gwr Exp $ */ /* * Mach Operating System @@ -48,27 +48,50 @@ */ /* - * Register map + * Register map: Note not declared here anymore! + * All the 5380 registers are accessed through individual + * pointers initialized by MD code. This allows the 5380 + * MI functions to be shared between MD drivers that have + * different padding between the registers (i.e. amiga). */ -typedef struct { - volatile unsigned char sci_data; /* r: Current data */ -#define sci_odata sci_data /* w: Out data */ - volatile unsigned char sci_icmd; /* rw: Initiator command */ - volatile unsigned char sci_mode; /* rw: Mode */ - volatile unsigned char sci_tcmd; /* rw: Target command */ - volatile unsigned char sci_bus_csr; /* r: Bus Status */ -#define sci_sel_enb sci_bus_csr /* w: Select enable */ - volatile unsigned char sci_csr; /* r: Status */ -#define sci_dma_send sci_csr /* w: Start dma send data */ - volatile unsigned char sci_idata; /* r: Input data */ -#define sci_trecv sci_idata /* w: Start dma receive, target */ - volatile unsigned char sci_iack; /* r: Interrupt Acknowledge */ -#define sci_irecv sci_iack /* w: Start dma receive, initiator */ -} sci_regmap_t; +#if 0 /* example only */ +struct ncr5380regs { + volatile u_char sci_r0; + volatile u_char sci_r1; + volatile u_char sci_r2; + volatile u_char sci_r3; + volatile u_char sci_r4; + volatile u_char sci_r5; + volatile u_char sci_r6; + volatile u_char sci_r7; +}; +#endif + +/* + * Machine-independent code uses these names: + */ +#define sci_data sci_r0 /* r: Current data */ +#define sci_odata sci_r0 /* w: Out data */ + +#define sci_icmd sci_r1 /* rw: Initiator command */ +#define sci_mode sci_r2 /* rw: Mode */ +#define sci_tcmd sci_r3 /* rw: Target command */ + +#define sci_bus_csr sci_r4 /* r: Bus Status */ +#define sci_sel_enb sci_r4 /* w: Select enable */ + +#define sci_csr sci_r5 /* r: Status */ +#define sci_dma_send sci_r5 /* w: Start dma send data */ + +#define sci_idata sci_r6 /* r: Input data */ +#define sci_trecv sci_r6 /* w: Start dma receive, target */ + +#define sci_iack sci_r7 /* r: Interrupt Acknowledge */ +#define sci_irecv sci_r7 /* w: Start dma receive, initiator */ /* - * Initiator command register + * R1: Initiator command register */ #define SCI_ICMD_DATA 0x01 /* rw: Assert data bus */ #define SCI_ICMD_ATN 0x02 /* rw: Assert ATN signal */ @@ -85,7 +108,7 @@ typedef struct { /* - * Mode register + * R2: Mode register */ #define SCI_MODE_ARB 0x01 /* rw: Start arbitration */ #define SCI_MODE_DMA 0x02 /* rw: Enable DMA xfers */ @@ -94,11 +117,11 @@ typedef struct { #define SCI_MODE_PERR_IE 0x10 /* rw: Interrupt on parity errors */ #define SCI_MODE_PAR_CHK 0x20 /* rw: Check parity */ #define SCI_MODE_TARGET 0x40 /* rw: Target mode (Initiator if 0) */ -#define SCI_MODE_BLOCKDMA 0x80 /* rw: Block-mode DMA handshake (MBZ) */ +#define SCI_MODE_BLOCKDMA 0x80 /* rw: Block-mode DMA handshake */ /* - * Target command register + * R3: Target command register */ #define SCI_TCMD_IO 0x01 /* rw: Assert I/O signal */ #define SCI_TCMD_CD 0x02 /* rw: Assert C/D signal */ @@ -108,10 +131,10 @@ typedef struct { #define SCI_TCMD_LAST_SENT 0x80 /* ro: Last byte was xferred * (not on 5380/1) */ -#define SCI_PHASE(x) ((x)&0x7) +#define SCI_TCMD_PHASE(x) ((x) & 0x7) /* - * Current (SCSI) Bus status (.sci_bus_csr) + * R4: Current (SCSI) Bus status (.sci_bus_csr) */ #define SCI_BUS_DBP 0x01 /* r: Data Bus parity */ #define SCI_BUS_SEL 0x02 /* r: SEL signal */ @@ -122,10 +145,10 @@ typedef struct { #define SCI_BUS_BSY 0x40 /* r: BSY signal */ #define SCI_BUS_RST 0x80 /* r: RST signal */ -#define SCI_CUR_PHASE(x) SCI_PHASE((x)>>2) +#define SCI_BUS_PHASE(x) (((x) >> 2) & 7) /* - * Bus and Status register (.sci_csr) + * R5: Bus and Status register (.sci_csr) */ #define SCI_CSR_ACK 0x01 /* r: ACK signal */ #define SCI_CSR_ATN 0x02 /* r: ATN signal */ diff --git a/sys/arch/sun3/dev/ncr5380sbc.c b/sys/arch/sun3/dev/ncr5380sbc.c index 76e600cfa15..18900d6cedf 100644 --- a/sys/arch/sun3/dev/ncr5380sbc.c +++ b/sys/arch/sun3/dev/ncr5380sbc.c @@ -1,4 +1,4 @@ -/* $NetBSD: ncr5380sbc.c,v 1.1 1995/10/29 21:19:09 gwr Exp $ */ +/* $NetBSD: ncr5380sbc.c,v 1.1.2.1 1995/11/18 07:08:34 gwr Exp $ */ /* * Copyright (c) 1995 David Jones, Gordon W. Ross @@ -50,7 +50,8 @@ * * Gordon Ross integrated the message phase code, added lots of * comments about what happens when and why (re. SCSI spec.), - * and debugged some reentrance problems. + * debugged some reentrance problems, and added several new + * "hooks" needed for the Sun3 "si" adapters. * * The message in/out code was taken nearly verbatim from * the aic6360 driver by Jarle Greipsland. @@ -66,20 +67,22 @@ */ #include -#include #include #include +#include #include +#include #include #include #include -#include #include #include #include #include +#define DEBUG XXX + #if 0 /* XXX - not yet... */ #include #include @@ -95,7 +98,7 @@ static void ncr5380_sched __P((struct ncr5380_softc *)); static void ncr5380_done __P((struct ncr5380_softc *)); static int ncr5380_select - __P((struct ncr5380_softc *, struct scsi_xfer *)); + __P((struct ncr5380_softc *, struct sci_req *)); static void ncr5380_reselect __P((struct ncr5380_softc *)); static int ncr5380_msg_in __P((struct ncr5380_softc *)); @@ -112,9 +115,8 @@ static void ncr5380_machine __P((struct ncr5380_softc *)); #define ACT_CONTINUE 0x00 /* No flags: expect another phase */ #define ACT_DISCONNECT 0x01 /* Target is disconnecting */ #define ACT_CMD_DONE 0x02 /* Need to call scsi_done() */ -#define ACT_ABORT_CMD 0x04 /* Need to forcibly disconnect it */ -#define ACT_RESET_BUS 0x08 /* Need bus reset (cmd timeout) */ -#define ACT_WAIT_INTR 0x10 /* Wait for interrupt (DMA) */ +#define ACT_RESET_BUS 0x04 /* Need bus reset (cmd timeout) */ +#define ACT_WAIT_DMA 0x10 /* Wait for DMA to complete */ /***************************************************************** * Debugging stuff @@ -127,31 +129,21 @@ int Debugger(); #define Debugger() printf("Debug: ncr5380.c:%d\n", __LINE__) #endif -#define DEBUG XXX - #ifdef DEBUG -int ncr5380_debug = 0; -/* extern struct scsi_link *thescsilink; */ - -#define NCR_BREAK() Debugger() -#define NCR_PRINT(b, s) \ - do {if ((ncr5380_debug & (b)) != 0) printf s;} while (0) +#define NCR_DBG_BREAK 1 +#define NCR_DBG_CMDS 2 +int ncr5380_debug = NCR_DBG_BREAK; +#define NCR_BREAK() \ + do { if (ncr5380_debug & NCR_DBG_BREAK) Debugger(); } while (0) +static void ncr5380_show_scsi_cmd __P((struct scsi_xfer *)); +static void ncr5380_show_sense __P((struct scsi_xfer *)); #else /* DEBUG */ - -#define NCR_BREAK() /* nah */ -#define NCR_PRINT(b, s) - +#define NCR_BREAK() /* nada */ +#define ncr5380_show_scsi_cmd(xs) /* nada */ +#define ncr5380_show_sense(xs) /* nada */ #endif /* DEBUG */ -#define NCR_MISC(s) NCR_PRINT(0x01, s) -#define NCR_MSGS(s) NCR_PRINT(0x02, s) -#define NCR_CMDS(s) NCR_PRINT(0x04, s) -#define NCR_TRACE(s) NCR_PRINT(0x10, s) -#define NCR_START(s) NCR_PRINT(0x20, s) - -#ifdef DEBUG - static char * phase_names[8] = { "DATA_OUT", @@ -164,47 +156,6 @@ phase_names[8] = { "MSG_IN", }; -static int -ncr5380_show_scsi_cmd(xs) - 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); - } -} - - -static void -ncr5380_show_sense(xs) - struct scsi_xfer *xs; -{ - u_char *b = (u_char *)&xs->sense; - int i; - - printf("sense:"); - for (i = 0; i < sizeof(xs->sense); i++) - printf(" %02x", b[i]); - printf("\n"); -} -#endif - - /***************************************************************** * Actual chip control *****************************************************************/ @@ -279,6 +230,7 @@ ncr5380_pio_out(sc, phase, count, data) { register u_char icmd; register int resid; + register int error; icmd = *(sc->sci_icmd) & SCI_ICMD_RMASK; @@ -288,14 +240,14 @@ ncr5380_pio_out(sc, phase, count, data) resid = count; while (resid > 0) { if (!SCI_BUSY(sc)) { - NCR_MISC(("ncr5380_pio_out: lost BSY\n")); + NCR_TRACE("pio_out: lost BSY, resid=%d\n", resid); break; } if (ncr5380_wait_req(sc)) { - NCR_MISC(("ncr5380_pio_out: no REQ\n")); + NCR_TRACE("pio_out: no REQ, resid=%d\n", resid); break; } - if (SCI_CUR_PHASE(*sc->sci_bus_csr) != phase) + if (SCI_BUS_PHASE(*sc->sci_bus_csr) != phase) break; /* Put the data on the bus. */ @@ -306,15 +258,17 @@ ncr5380_pio_out(sc, phase, count, data) *sc->sci_icmd = icmd; /* Wait for target to get it. */ - if (ncr5380_wait_not_req(sc)) { - NCR_MISC(("ncr5380_pio_out: stuck REQ\n")); - break; - } + error = ncr5380_wait_not_req(sc); - /* OK, it's got it. */ + /* OK, it's got it (or we gave up waiting). */ icmd &= ~SCI_ICMD_ACK; *sc->sci_icmd = icmd; + if (error) { + NCR_TRACE("pio_out: stuck REQ, resid=%d\n", resid); + break; + } + --resid; } @@ -334,21 +288,22 @@ ncr5380_pio_in(sc, phase, count, data) { register u_char icmd; register int resid; + register int error; icmd = *(sc->sci_icmd) & SCI_ICMD_RMASK; resid = count; while (resid > 0) { if (!SCI_BUSY(sc)) { - NCR_MISC(("ncr5380_pio_in: lost BSY\n")); + NCR_TRACE("pio_in: lost BSY, resid=%d\n", resid); break; } if (ncr5380_wait_req(sc)) { - NCR_MISC(("ncr5380_pio_in: no REQ\n")); + NCR_TRACE("pio_in: no REQ, resid=%d\n", resid); break; } /* A phase change is not valid until AFTER REQ rises! */ - if (SCI_CUR_PHASE(*sc->sci_bus_csr) != phase) + if (SCI_BUS_PHASE(*sc->sci_bus_csr) != phase) break; /* Read the data bus. */ @@ -359,15 +314,17 @@ ncr5380_pio_in(sc, phase, count, data) *sc->sci_icmd = icmd; /* Wait for target to drop REQ... */ - if (ncr5380_wait_not_req(sc)) { - NCR_MISC(("ncr5380_pio_in: stuck REQ\n")); - break; - } + error = ncr5380_wait_not_req(sc); /* OK, we can drop ACK. */ icmd &= ~SCI_ICMD_ACK; *sc->sci_icmd = icmd; + if (error) { + NCR_TRACE("pio_in: stuck REQ, resid=%d\n", resid); + break; + } + --resid; } @@ -381,18 +338,34 @@ ncr5380_init(sc) { int i, j; +#ifdef DEBUG + ncr5380_debug_sc = sc; +#endif + for (i = 0; i < SCI_OPENINGS; i++) sc->sc_ring[i].sr_xs = NULL; for (i = 0; i < 8; i++) for (j = 0; j < 8; j++) sc->sc_matrix[i][j] = NULL; - sc->sc_link.openings = SCI_OPENINGS; + sc->sc_link.openings = 2; /* XXX - Not SCI_OPENINGS */ sc->sc_prevphase = PHASE_INVALID; - sc->sc_msg_flags = 0; /* XXX */ + sc->sc_state = NCR_IDLE; - /* XXX: Reselect interrupts... */ + *sc->sci_tcmd = PHASE_INVALID; + *sc->sci_icmd = 0; + *sc->sci_mode = 0; + *sc->sci_sel_enb = 0; + SCI_CLR_INTR(sc); + + /* XXX: Enable reselect interrupts... */ *sc->sci_sel_enb = 0x80; + + /* Another hack (Er.. hook!) for the sun3 si: */ + if (sc->sc_intr_on) { + NCR_TRACE("init: intr ON\n", 0); + sc->sc_intr_on(sc); + } } @@ -401,14 +374,15 @@ ncr5380_reset_scsibus(sc) struct ncr5380_softc *sc; { - NCR_MISC(("ncr5380_reset_scsibus\n")); + NCR_TRACE("reset_scsibus, cur=0x%x\n", + (long) sc->sc_current); *sc->sci_icmd = SCI_ICMD_RST; delay(500); *sc->sci_icmd = 0; *sc->sci_mode = 0; - *sc->sci_tcmd = 0; + *sc->sci_tcmd = PHASE_INVALID; SCI_CLR_INTR(sc); /* XXX - Need long delay here! */ @@ -420,40 +394,34 @@ ncr5380_reset_scsibus(sc) /* * Interrupt handler for the SCSI Bus Controller (SBC) - * This is also called by a timeout handler after it - * raises to the appropriate SPL. + * This may also called for a DMA timeout (at splbio). */ int -ncr5380_sbc_intr(sc) +ncr5380_intr(sc) struct ncr5380_softc *sc; { int claimed = 0; - u_char st; - - if (sc->sc_dma_flags & DMA5380_INPROGRESS) { - /* Stop DMA to prevent register conflicts */ - NCR_MISC(("ncr5380_sbc_intr: call DMA stop\n")); - (*sc->sc_dma_stop)(sc); - /* Will check for error in ncr5380_machine */ - /* We know the nexus is busy during DMA. */ - } - - /* OK, now we can look at the SBC. */ - st = *sc->sci_csr; - SCI_CLR_INTR(sc); - NCR_MISC(("ncr5380_sbc_intr: st=0x%x\n", st)); - - /* XXX - There may be a better place to check... */ - if (st & SCI_CSR_PERR) { - printf("ncr5380_sbc_intr: parity error!\n"); - } + /* + * Do not touch SBC regs here unless sc_current == NULL + * or it will complain about "register conflict" errors. + * Instead, just let ncr5380_machine() deal with it. + */ + NCR_TRACE("intr: top, state=%d\n", sc->sc_state); - if (sc->sc_current == NULL) { + if (sc->sc_state == NCR_IDLE) { /* - * Might be reselect. ncr5380_reselect() will check, and - * set up the connection if so. + * Might be reselect. ncr5380_reselect() will check, + * and set up the connection if so. This will verify + * that sc_current == NULL at the beginning... */ + + /* Another hack (Er.. hook!) for the sun3 si: */ + if (sc->sc_intr_off) { + NCR_TRACE("intr: for reselect, intr off\n", 0); + sc->sc_intr_off(sc); + } + ncr5380_reselect(sc); } @@ -462,23 +430,135 @@ ncr5380_sbc_intr(sc) * disconnect. In addition, the sunsi controller may produce a state * where SCI_CSR_DONE is false, yet DMA is complete. * - * The procedure in all these cases is to let ncr5380_machine() figure - * out what to do next. + * The procedure in all these cases is to let ncr5380_machine() + * figure out what to do next. */ - if (sc->sc_current) { + if (sc->sc_state & NCR_WORKING) { + NCR_TRACE("intr: call machine, cur=0x%x\n", + (long) sc->sc_current); /* This will usually free-up the nexus. */ ncr5380_machine(sc); + NCR_TRACE("intr: machine done, cur=0x%x\n", + (long) sc->sc_current); claimed = 1; } - /* Maybe we can start another command now... */ - if (sc->sc_current == NULL) + /* Maybe we can run some commands now... */ + if (sc->sc_state == NCR_IDLE) { + NCR_TRACE("intr: call sched, cur=0x%x\n", + (long) sc->sc_current); ncr5380_sched(sc); + NCR_TRACE("intr: sched done, cur=0x%x\n", + (long) sc->sc_current); + } return claimed; } +/* + * Abort the current command (i.e. due to timeout) + */ +void +ncr5380_abort(sc) + struct ncr5380_softc *sc; +{ + + /* + * Finish it now. If DMA is in progress, we + * can not call ncr_sched_msgout() because + * that hits the SBC (avoid DMA conflict). + */ + + /* Another hack (Er.. hook!) for the sun3 si: */ + if (sc->sc_intr_off) { + NCR_TRACE("abort: intr off\n", 0); + sc->sc_intr_off(sc); + } + + sc->sc_state |= NCR_ABORTING; + if ((sc->sc_state & NCR_DOINGDMA) == 0) { + ncr_sched_msgout(sc, SEND_ABORT); + } + NCR_TRACE("abort: call machine, cur=0x%x\n", + (long) sc->sc_current); + ncr5380_machine(sc); + NCR_TRACE("abort: machine done, cur=0x%x\n", + (long) sc->sc_current); + + /* Another hack (Er.. hook!) for the sun3 si: */ + if (sc->sc_intr_on) { + NCR_TRACE("abort: intr ON\n", 0); + sc->sc_intr_on(sc); + } +} + +/* + * Timeout handler, scheduled for each SCSI command. + */ +void +ncr5380_cmd_timeout(arg) + void *arg; +{ + struct sci_req *sr = arg; + struct scsi_xfer *xs; + struct scsi_link *sc_link; + struct ncr5380_softc *sc; + int s; + + s = splbio(); + + /* Get all our variables... */ + xs = sr->sr_xs; + if (xs == NULL) { + printf("ncr5380_cmd_timeout: no scsi_xfer\n"); + goto out; + } + sc_link = xs->sc_link; + sc = sc_link->adapter_softc; + + printf("%s: cmd timeout, targ=%d, lun=%d\n", + sc->sc_dev.dv_xname, + sr->sr_target, sr->sr_lun); + + /* + * Mark the overdue job as failed, and arrange for + * ncr5380_machine to terminate it. If the victim + * is the current job, call ncr5380_machine() now. + * Otherwise arrange for ncr5380_sched() to do it. + */ + sr->sr_flags |= SR_OVERDUE; + if (sc->sc_current == sr) { + NCR_TRACE("cmd_tmo: call abort, sr=0x%x\n", (long) sr); + ncr5380_abort(sc); + } else { + /* + * The driver may be idle, or busy with another job. + * Arrange for ncr5380_sched() to do the deed. + */ + NCR_TRACE("cmd_tmo: clear matrix, t/l=0x%02x\n", + (sr->sr_target << 4) | sr->sr_lun); + sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL; + } + + /* + * We may have aborted the current job, or may have + * already been idle. In either case, we should now + * be idle, so try to start another job. + */ + if (sc->sc_state == NCR_IDLE) { + NCR_TRACE("cmd_tmo: call sched, cur=0x%x\n", + (long) sc->sc_current); + ncr5380_sched(sc); + NCR_TRACE("cmd_tmo: sched done, cur=0x%x\n", + (long) sc->sc_current); + } + +out: + splx(s); +} + + /***************************************************************** * Interface to higher level *****************************************************************/ @@ -495,11 +575,12 @@ int ncr5380_scsi_cmd(xs) struct scsi_xfer *xs; { - struct ncr5380_softc *sc = xs->sc_link->adapter_softc; - int s, r, i, flags; + struct ncr5380_softc *sc; + struct sci_req *sr; + int s, rv, i, flags; extern int cold; /* XXX */ - /* thescsilink = xs->sc_link; */ + sc = xs->sc_link->adapter_softc; flags = xs->flags; /* @@ -510,88 +591,257 @@ ncr5380_scsi_cmd(xs) if (cold) flags |= SCSI_POLL; + if (sc->sc_flags & NCR5380_FORCE_POLLING) + flags |= SCSI_POLL; + if (flags & SCSI_DATA_UIO) panic("ncr5380: scsi data uio requested"); - if (sc->sc_current && (flags & SCSI_POLL)) - panic("ncr5380_scsi_cmd: can't poll while busy"); - s = splbio(); - /* Find lowest empty slot in ring buffer. */ + if (flags & SCSI_POLL) { + /* Terminate any current command. */ + sr = sc->sc_current; + if (sr) { + printf("%s: polled request aborting %d/%d\n", + sc->sc_dev.dv_xname, + sr->sr_target, sr->sr_lun); + ncr5380_abort(sc); + } + if (sc->sc_state != NCR_IDLE) { + panic("ncr5380_scsi_cmd: polled request, abort failed"); + } + } + + /* + * Find lowest empty slot in ring buffer. + * XXX: What about "fairness" and cmd order? + */ for (i = 0; i < SCI_OPENINGS; i++) if (sc->sc_ring[i].sr_xs == NULL) - break; + goto new; - if (i == SCI_OPENINGS) { - splx(s); - return TRY_AGAIN_LATER; - } + rv = TRY_AGAIN_LATER; + NCR_TRACE("scsi_cmd: no openings, rv=%d\n", rv); + goto out; +new: /* Create queue entry */ - sc->sc_ring[i].sr_xs = xs; - sc->sc_ring[i].sr_target = xs->sc_link->target; - sc->sc_ring[i].sr_lun = xs->sc_link->lun; - sc->sc_ring[i].sr_data = xs->data; - sc->sc_ring[i].sr_datalen = xs->datalen; - sc->sc_ring[i].sr_dma_hand = NULL; - sc->sc_ring[i].sr_flags = (flags & SCSI_POLL) ? SR_IMMED : 0; - sc->sc_ring[i].sr_status = -1; /* no value */ + sr = &sc->sc_ring[i]; + sr->sr_xs = xs; + sr->sr_target = xs->sc_link->target; + sr->sr_lun = xs->sc_link->lun; + sr->sr_dma_hand = NULL; + sr->sr_dataptr = xs->data; + sr->sr_datalen = xs->datalen; + sr->sr_flags = (flags & SCSI_POLL) ? SR_IMMED : 0; + sr->sr_status = -1; /* no value */ sc->sc_ncmds++; + rv = SUCCESSFULLY_QUEUED; + + NCR_TRACE("scsi_cmd: new sr=0x%x\n", (long)sr); + + if (flags & SCSI_POLL) { + /* Force this new command to be next. */ + sc->sc_rr = i; + } /* - * Consider starting another command if the bus is idle. - * To stop recursion, this is non-NULL when scsi_done() - * calls back here to issue the next command. + * If we were idle, run some commands... */ - if (sc->sc_current == NULL) + if (sc->sc_state == NCR_IDLE) { + NCR_TRACE("scsi_cmd: call sched, cur=0x%x\n", + (long) sc->sc_current); ncr5380_sched(sc); + NCR_TRACE("scsi_cmd: sched done, cur=0x%x\n", + (long) sc->sc_current); + } + if (flags & SCSI_POLL) { + /* Make sure ncr5380_sched() finished it. */ + if ((xs->flags & ITSDONE) == 0) + panic("ncr5380_scsi_cmd: poll didn't finish"); + rv = COMPLETE; + } + +out: splx(s); + return (rv); +} + + +/* + * POST PROCESSING OF SCSI_CMD (usually current) + * Called by ncr5380_sched(), ncr5380_machine() + */ +static void +ncr5380_done(sc) + struct ncr5380_softc *sc; +{ + struct sci_req *sr; + struct scsi_xfer *xs; + +#ifdef DIAGNOSTIC + if ((getsr() & PSL_IPL) < PSL_IPL2) + panic("ncr5380_done: bad spl"); + if (sc->sc_state == NCR_IDLE) + panic("ncr5380_done: state=idle"); + if (sc->sc_current == NULL) + panic("ncr5380_done: current=0"); +#endif + + sr = sc->sc_current; + xs = sr->sr_xs; + + NCR_TRACE("done: top, cur=0x%x\n", (long) sc->sc_current); - /* The call to ncr5380_sched() may have finished it. */ - if (xs->flags & ITSDONE) { - return COMPLETE; + /* + * Clean up DMA resources for this command. + */ + if (sr->sr_dma_hand) { + NCR_TRACE("done: dma_free, dh=0x%x\n", + (long) sr->sr_dma_hand); + (*sc->sc_dma_free)(sc); + } +#ifdef DIAGNOSTIC + if (sr->sr_dma_hand) + panic("ncr5380_done: dma free did not"); +#endif + + if (sc->sc_state & NCR_ABORTING) { + NCR_TRACE("done: aborting, error=%d\n", xs->error); + if (xs->error == XS_NOERROR) + xs->error = XS_TIMEOUT; } - if (flags & SCSI_POLL) - panic("ncr5380_scsi_cmd: poll didn't finish"); + NCR_TRACE("done: check error=%d\n", (long) xs->error); + + /* If error is already set, ignore sr_status value. */ + if (xs->error != XS_NOERROR) + goto finish; + + NCR_TRACE("done: check status=%d\n", sr->sr_status); + + switch (sr->sr_status) { + case SCSI_OK: /* 0 */ + if (sr->sr_flags & SR_SENSE) { + if (ncr5380_debug & NCR_DBG_CMDS) { + ncr5380_show_sense(xs); + } + xs->error = XS_SENSE; + } + break; + + case SCSI_CHECK: + if (sr->sr_flags & SR_SENSE) { + /* Sense command also asked for sense? */ + printf("ncr5380_done: sense asked for sense\n"); + NCR_BREAK(); + xs->error = XS_DRIVER_STUFFUP; + break; + } + sr->sr_flags |= SR_SENSE; + NCR_TRACE("done: get sense, sr=0x%x\n", (long) sr); + /* + * Leave queued, but clear sc_current so we start over + * with selection. Guaranteed to get the same request. + */ + sc->sc_state = NCR_IDLE; + sc->sc_current = NULL; + sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL; + return; /* XXX */ + + case SCSI_BUSY: + xs->error = XS_BUSY; + break; - return SUCCESSFULLY_QUEUED; + case -1: + /* This is our "impossible" initial value. */ + /* fallthrough */ + default: + printf("%s: target %d, bad status=%d\n", + sc->sc_dev.dv_xname, sr->sr_target, sr->sr_status); + xs->error = XS_DRIVER_STUFFUP; + break; + } + +finish: + + NCR_TRACE("done: finish, error=%d\n", xs->error); + + /* + * Dequeue the finished command, but don't clear sc_state until + * after the call to scsi_done(), because that may call back to + * ncr5380_scsi_cmd() - unwanted recursion! + * + * Keeping sc->sc_state != idle terminates the recursion. + */ +#ifdef DIAGNOSTIC + if ((sc->sc_state & NCR_WORKING) == 0) + panic("ncr5380_done: bad state"); +#endif + + /* Clear our pointers to the request. */ + sc->sc_current = NULL; + sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL; + untimeout(ncr5380_cmd_timeout, sr); + + /* Make the request free. */ + sr->sr_xs = NULL; + sc->sc_ncmds--; + + /* Tell common SCSI code it is done. */ + xs->flags |= ITSDONE; + scsi_done(xs); + + sc->sc_state = NCR_IDLE; + /* Now ncr5380_sched() may be called again. */ } /* * Schedule a SCSI operation. This routine should return * only after it achieves one of the following conditions: - * Bus is busy (sc->sc_current != NULL) - * All targets are busy (i.e. disconnected) + * Busy (sc->sc_state != NCR_IDLE) + * No more work can be started. */ static void ncr5380_sched(sc) struct ncr5380_softc *sc; { - struct scsi_xfer *xs; struct sci_req *sr; + struct scsi_xfer *xs; int target, lun; int error, i; #ifdef DIAGNOSTIC if ((getsr() & PSL_IPL) < PSL_IPL2) panic("ncr5380_sched: bad spl"); +#endif + /* Another hack (Er.. hook!) for the sun3 si: */ + if (sc->sc_intr_off) { + NCR_TRACE("sched: top, intr off\n", 0); + sc->sc_intr_off(sc); + } + +next_job: + /* + * Grab the next job from queue. Must be idle. + */ +#ifdef DIAGNOSTIC + if (sc->sc_state != NCR_IDLE) + panic("ncr5380_sched: not idle"); if (sc->sc_current) - panic("ncr5380_sched: bus already busy"); + panic("ncr5380_sched: current set"); #endif -next_job: /* - * Grab the next job from queue. - * * Always start the search where we last looked. * The REQUEST_SENSE logic depends on this to * choose the same job as was last picked, so it * can just clear sc_current and reschedule. + * (Avoids loss of "contingent allegiance".) */ i = sc->sc_rr; sr = NULL; @@ -600,7 +850,8 @@ next_job: target = sc->sc_ring[i].sr_target; lun = sc->sc_ring[i].sr_lun; if (sc->sc_matrix[target][lun] == NULL) { - sc->sc_matrix[target][lun] = sr = &sc->sc_ring[i]; + sc->sc_matrix[target][lun] = + sr = &sc->sc_ring[i]; sc->sc_rr = i; break; } @@ -610,37 +861,61 @@ next_job: i = 0; } while (i != sc->sc_rr); - if (sr == NULL) + if (sr == NULL) { + NCR_TRACE("sched: no work, cur=0x%x\n", + (long) sc->sc_current); + + /* Another hack (Er.. hook!) for the sun3 si: */ + if (sc->sc_intr_on) { + NCR_TRACE("sched: ret, intr ON\n", 0); + sc->sc_intr_on(sc); + } + return; /* No more work to do. */ + } - xs = sr->sr_xs; - error = ncr5380_select(sc, xs); + NCR_TRACE("sched: select for t/l=0x%02x\n", + (sr->sr_target << 4) | sr->sr_lun); + + sc->sc_state = NCR_WORKING; + error = ncr5380_select(sc, sr); if (sc->sc_current) { /* Lost the race! reselected out from under us! */ + /* Work with the reselected job. */ if (sr->sr_flags & SR_IMMED) { - printf("%s: reselected while polling (reset)\n", + printf("%s: reselected while polling (abort)\n", sc->sc_dev.dv_xname); - error = XS_BUSY; - goto reset; + /* Abort the reselected job. */ + sc->sc_state |= NCR_ABORTING; + sc->sc_msgpriq |= SEND_ABORT; } - /* Work with the reselected target. */ sr = sc->sc_current; xs = sr->sr_xs; + NCR_TRACE("sched: reselect, new sr=0x%x\n", (long)sr); goto have_nexus; } + + /* Normal selection result */ sc->sc_current = sr; /* connected */ + xs = sr->sr_xs; - /* Initialize pointers, etc. */ - sc->sc_dataptr = sr->sr_data; + /* + * Initialize pointers, etc. for this job + */ + sc->sc_dataptr = sr->sr_dataptr; sc->sc_datalen = sr->sr_datalen; - sc->sc_dma_hand = sr->sr_dma_hand; + sc->sc_prevphase = PHASE_INVALID; + sc->sc_msgpriq = SEND_IDENTIFY; + sc->sc_msgoutq = 0; + sc->sc_msgout = 0; + + NCR_TRACE("sched: select rv=%d\n", error); switch (error) { case XS_NOERROR: break; case XS_BUSY: - reset: /* XXX - Reset and try again. */ printf("%s: SCSI bus busy, resetting...\n", sc->sc_dev.dv_xname); @@ -648,141 +923,119 @@ next_job: /* fallthrough */ case XS_SELTIMEOUT: default: - NCR_MISC(("ncr5380_sched: select error=%d\n", error)); - xs->error = error; + xs->error = error; /* from select */ + NCR_TRACE("sched: call done, sr=0x%x\n", (long)sr); ncr5380_done(sc); + + /* Paranoia: clear everything. */ + sc->sc_dataptr = NULL; + sc->sc_datalen = 0; + sc->sc_prevphase = PHASE_INVALID; + sc->sc_msgpriq = 0; + sc->sc_msgoutq = 0; + sc->sc_msgout = 0; + goto next_job; } /* - * Selection was successful. - * We now have a new command. + * Selection was successful. Normally, this means + * we are starting a new command. However, this + * might be the termination of an overdue job. */ - NCR_CMDS(("next cmd: ", - ncr5380_show_scsi_cmd(xs) )); - sc->sc_prevphase = PHASE_MSG_IN; - if (xs->flags & SCSI_RESET) { - sc->sc_msgpriq = SEND_DEV_RESET; + if (sr->sr_flags & SR_OVERDUE) { + NCR_TRACE("sched: overdue, sr=0x%x\n", (long)sr); + sc->sc_state |= NCR_ABORTING; + sc->sc_msgpriq |= SEND_ABORT; goto have_nexus; } - sc->sc_msgpriq = SEND_IDENTIFY; - if (sc->sc_dataptr && sc->sc_dma_alloc && - (sc->sc_datalen >= sc->sc_min_dma_len)) - { - /* Allocate DMA space */ - sc->sc_dma_flags = 0; - if (xs->flags & SCSI_DATA_OUT) - sc->sc_dma_flags |= DMA5380_WRITE; - if (xs->bp && (xs->bp->b_flags & B_PHYS)) - sc->sc_dma_flags |= DMA5380_PHYS; - if (sr->sr_flags & SR_IMMED) - sc->sc_dma_flags |= DMA5380_POLL; - /* This may also set DMA5380_POLL */ - (*sc->sc_dma_alloc)(sc); - /* Now, _maybe_ sc_dma_hand is set. */ - } - sr->sr_dma_hand = sc->sc_dma_hand; - -have_nexus: - ncr5380_machine(sc); /* - * What state did ncr5380_machine() leave us in? - * Hopefully it sometimes completes a job... + * This may be the continuation of some job that + * completed with a "check condition" code. */ - if (sc->sc_current) - return; /* Have work in progress... */ - - goto next_job; -} - - -/* - * POST PROCESSING OF SCSI_CMD (usually current) - * Called by ncr5380_sched(), ncr5380_machine() - */ -static void -ncr5380_done(sc) - struct ncr5380_softc *sc; -{ - struct sci_req *sr = sc->sc_current; - struct scsi_xfer *xs = sr->sr_xs; - - /* Clean up DMA associated with this command */ - if (sc->sc_dma_hand) { - (*sc->sc_dma_free)(sc); - sc->sc_dma_hand = NULL; + if (sr->sr_flags & SR_SENSE) { + NCR_TRACE("sched: get sense, sr=0x%x\n", (long)sr); + /* Do not allocate DMA, nor set timeout. */ + goto have_nexus; } - if (xs->error) - goto finish; - - NCR_MISC(("ncr5380_done, sr_status=%d\n", sr->sr_status)); - - switch (sr->sr_status) { - case SCSI_OK: /* 0 */ - if (sr->sr_flags & SR_SENSE) { - /* ncr5380_show_sense(xs); */ - xs->error = XS_SENSE; - } - break; + /* + * OK, we are starting a new command. + * Initialize and allocate resources for the new command. + * Device reset is special (only uses MSG_OUT phase). + * Normal commands start in MSG_OUT phase where we will + * send and IDENDIFY message, and then expect CMD phase. + */ + if (ncr5380_debug & NCR_DBG_CMDS) { + printf("ncr5380_sched: begin, target=%d, LUN=%d\n", + xs->sc_link->target, xs->sc_link->lun); + ncr5380_show_scsi_cmd(xs); + } + if (xs->flags & SCSI_RESET) { + NCR_TRACE("sched: cmd=reset, sr=0x%x\n", (long)sr); + /* Not an error, so do not set NCR_ABORTING */ + sc->sc_msgpriq |= SEND_DEV_RESET; + goto have_nexus; + } - case SCSI_CHECK: - if (sr->sr_flags & SR_SENSE) { - /* Sense command also asked for sense? */ - printf("ncr5380_done: sense asked for sense\n"); +#ifdef DIAGNOSTIC + if ((xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) == 0) { + if (sc->sc_dataptr) { + printf("%s: ptr but no data in/out flags?\n"); NCR_BREAK(); - xs->error = XS_DRIVER_STUFFUP; - break; + sc->sc_dataptr = NULL; } - sr->sr_flags |= SR_SENSE; - NCR_MISC(("ncr5380_done: start request sense\n")); - NCR_CMDS(("cmd: ", ncr5380_show_scsi_cmd(xs) )); - /* - * Leave queued, but clear sc_current so we start over - * with selection. Guaranteed to get the same request. - */ - sc->sc_current = NULL; - sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL; - return; - - case SCSI_BUSY: - xs->error = XS_BUSY; - break; - - case -1: - /* This is our "impossible" initial value. */ - /* fallthrough */ - default: - printf("%s: target %d, bad status=%d\n", - sc->sc_dev.dv_xname, sr->sr_target, sr->sr_status); - xs->error = XS_DRIVER_STUFFUP; - break; } +#endif -finish: + /* Allocate DMA space (maybe) */ + if (sc->sc_dataptr && sc->sc_dma_alloc && + (sc->sc_datalen >= sc->sc_min_dma_len)) + { + NCR_TRACE("sched: dma_alloc, len=%d\n", sc->sc_datalen); + (*sc->sc_dma_alloc)(sc); + } - NCR_MISC(("ncr5380_done: finished target=%d, LUN=%d\n", - sr->sr_target, sr->sr_lun)); + /* + * Initialization hook called just after select, + * at the beginning of COMMAND phase. + * (but AFTER the DMA allocation is done) + * + * The evil Sun "si" adapter (OBIO variant) needs some + * setup done to the DMA engine BEFORE the target puts + * the SCSI bus into any DATA phase. + */ + if (sr->sr_dma_hand && sc->sc_dma_setup) { + NCR_TRACE("sched: dma_setup, dh=0x%x\n", + (long) sr->sr_dma_hand); + sc->sc_dma_setup(sc); + } /* - * Dequeue the finished command + * Schedule a timeout for the job we are starting. */ - sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL; - sc->sc_ncmds--; - sr->sr_xs = NULL; + if ((sr->sr_flags & SR_IMMED) == 0) { + i = (xs->timeout * hz) / 1000; + NCR_TRACE("sched: set timeout=%d\n", i); + timeout(ncr5380_cmd_timeout, sr, i); + } + +have_nexus: + NCR_TRACE("sched: call machine, cur=0x%x\n", + (long) sc->sc_current); + ncr5380_machine(sc); + NCR_TRACE("sched: machine done, cur=0x%x\n", + (long) sc->sc_current); /* - * Do not clear sc_current quite yet. The call to scsi_done() - * below may recursively call ncr5380_scsi_cmd() and leaving - * sc->sc_current != NULL ends the recursion after the new - * command is enqueued. + * What state did ncr5380_machine() leave us in? + * Hopefully it sometimes completes a job... */ - xs->flags |= ITSDONE; - scsi_done(xs); + if (sc->sc_state == NCR_IDLE) + goto next_job; - /* Allow ncr5380_sched() to be called again. */ - sc->sc_current = NULL; /* idle */ + return; /* Have work in progress. */ } @@ -799,17 +1052,23 @@ void ncr5380_reselect(sc) struct ncr5380_softc *sc; { + struct sci_req *sr; int target, lun, phase, timo; u_char bus, data, icmd, msg; #ifdef DIAGNOSTIC + /* + * Note: sc_state will be "idle" when ncr5380_intr() + * calls, or "working" when ncr5380_select() calls. + * (So don't test that in this DIAGNOSTIC) + */ if (sc->sc_current) - panic("ncr5380_reselect: already have nexus!"); + panic("ncr5380_reselect: current set"); #endif /* * First, check the select line. - * (That has to be set by now.) + * (That has to be set first.) */ bus = *(sc->sci_bus_csr); if ((bus & SCI_BUS_SEL) == 0) { @@ -818,12 +1077,16 @@ ncr5380_reselect(sc) } /* - * Now wait for: SEL==1, BSY==0 - * before reading the data bus. + * The target will assert BSY first (for bus arbitration), + * then raise SEL, and finally drop BSY. Only then is the + * data bus required to have valid selection ID bits set. + * Wait for: SEL==1, BSY==0 before reading the data bus. */ - for (timo = 25000;;) { + timo = ncr5380_wait_nrq_timo; + for (;;) { if ((bus & SCI_BUS_BSY) == 0) break; + /* Probably never get here... */ if (--timo <= 0) { printf("%s: reselect, BSY stuck, bus=0x%x\n", sc->sc_dev.dv_xname, bus); @@ -838,6 +1101,8 @@ ncr5380_reselect(sc) return; /* Still have SEL, check BSY. */ } + NCR_TRACE("reselect, valid data after %d loops\n", + ncr5380_wait_nrq_timo - timo); /* * Good. We have SEL=1 and BSY=0. Now wait for a @@ -873,24 +1138,25 @@ ncr5380_reselect(sc) return; } - NCR_MISC(("ncr5380_reselect: data=0x%x\n", data)); + NCR_TRACE("reselect: target=0x%x\n", target); /* Raise BSY to acknowledge target reselection. */ *(sc->sci_icmd) = SCI_ICMD_BSY; /* Wait for target to drop SEL. */ - for (timo = 1000;;) { + timo = ncr5380_wait_nrq_timo; + for (;;) { bus = *(sc->sci_bus_csr); if ((bus & SCI_BUS_SEL) == 0) break; /* success */ if (--timo <= 0) { printf("%s: reselect, SEL stuck, bus=0x%x\n", sc->sc_dev.dv_xname, bus); - /* Not much we can do. Reset the bus. */ - ncr5380_reset_scsibus(sc); - return; + NCR_BREAK(); + /* assume connected (fail later if not) */ + break; } - delay(5); + delay(2); } /* Now we drop BSY, and we are connected. */ @@ -911,15 +1177,15 @@ ncr5380_reselect(sc) /* Try to send an ABORT message. */ goto abort; } - phase = SCI_CUR_PHASE(*sc->sci_bus_csr); + phase = SCI_BUS_PHASE(*sc->sci_bus_csr); if (phase != PHASE_MSG_IN) { printf("%s: reselect, phase=%d\n", sc->sc_dev.dv_xname, phase); goto abort; } - /* Ack. the new phase. */ - *(sc->sci_tcmd) = phase; + /* Ack. the change to PHASE_MSG_IN */ + *(sc->sci_tcmd) = PHASE_MSG_IN; /* Peek at the message byte without consuming it! */ msg = *(sc->sci_data); @@ -931,15 +1197,35 @@ ncr5380_reselect(sc) lun = msg & 7; /* We now know target/LUN. Do we have the request? */ - sc->sc_current = sc->sc_matrix[target][lun]; - if (sc->sc_current) { + sr = sc->sc_matrix[target][lun]; + if (sr) { + /* We now have a nexus. */ + sc->sc_state |= NCR_WORKING; + sc->sc_current = sr; + NCR_TRACE("reselect: resume sr=0x%x\n", (long)sr); + + /* Implicit restore pointers message */ + sc->sc_dataptr = sr->sr_dataptr; + sc->sc_datalen = sr->sr_datalen; + + sc->sc_prevphase = PHASE_INVALID; + sc->sc_msgpriq = 0; + sc->sc_msgoutq = 0; + sc->sc_msgout = 0; + + /* + * Another hack for the Sun3 "si", which needs + * some setup done to its DMA engine before the + * target puts the SCSI bus into any DATA phase. + */ + if (sr->sr_dma_hand && sc->sc_dma_setup) { + NCR_TRACE("reselect: call DMA setup, dh=0x%x\n", + (long) sr->sr_dma_hand); + sc->sc_dma_setup(sc); + } + /* Now consume the IDENTIFY message. */ ncr5380_pio_in(sc, PHASE_MSG_IN, 1, &msg); - /* Implicit restore pointers message */ - sc->sc_dataptr = sc->sc_current->sr_data; - sc->sc_datalen = sc->sc_current->sr_datalen; - sc->sc_dma_hand = sc->sc_current->sr_dma_hand; - /* We now have a nexus. */ return; } @@ -947,9 +1233,12 @@ ncr5380_reselect(sc) sc->sc_dev.dv_xname, target, lun); abort: /* - * Try to send an ABORT message. - * Raise ATN, then raise ACK... + * Try to send an ABORT message. This makes us + * temporarily busy, but no current command... */ + sc->sc_state |= NCR_ABORTING; + + /* Raise ATN, delay, raise ACK... */ icmd = SCI_ICMD_ATN; *sc->sci_icmd = icmd; delay(2); @@ -958,12 +1247,16 @@ abort: ncr5380_pio_in(sc, PHASE_MSG_IN, 1, &msg); /* Finally try to send the ABORT. */ - sc->sc_prevphase = PHASE_MSG_IN; + sc->sc_prevphase = PHASE_INVALID; sc->sc_msgpriq = SEND_ABORT; - *(sc->sci_tcmd) = PHASE_MSG_OUT; ncr5380_msg_out(sc); + *(sc->sci_tcmd) = PHASE_INVALID; + *sc->sci_sel_enb = 0; + SCI_CLR_INTR(sc); *sc->sci_sel_enb = 0x80; + + sc->sc_state &= ~NCR_ABORTING; } @@ -978,32 +1271,28 @@ abort: * XS_SELTIMEOUT ==> no response to selection */ static int -ncr5380_select(sc, xs) +ncr5380_select(sc, sr) struct ncr5380_softc *sc; - struct scsi_xfer *xs; + struct sci_req *sr; { int timo; u_char bus, data, icmd; - NCR_MISC(("ncr5380_select: begin, target=%d\n", - xs->sc_link->target)); - /* Check for reselect */ ncr5380_reselect(sc); if (sc->sc_current) { - NCR_MISC(("ncr5380_select: reselected!\n")); - return XS_NOERROR; /* reselected */ + NCR_TRACE("select: reselect, cur=0x%x\n", + (long) sc->sc_current); + return XS_BUSY; /* reselected */ } /* * Set phase bits to 0, otherwise the 5380 won't drive the bus during * selection. */ + *sc->sci_tcmd = PHASE_DATA_OUT; *sc->sci_icmd = icmd = 0; *sc->sci_mode = 0; - *sc->sci_tcmd = 0; - - NCR_MISC(("ncr5380_select: arbitration\n")); /* * Arbitrate for the bus. The 5380 takes care of the @@ -1021,25 +1310,30 @@ ncr5380_select(sc, xs) *(sc->sci_mode) = SCI_MODE_ARB; /* Wait for ICMD_AIP. */ - for (timo = 50000;;) { + timo = ncr5380_wait_req_timo; + for (;;) { if (*(sc->sci_icmd) & SCI_ICMD_AIP) break; if (--timo <= 0) { /* Did not see any "bus free" period. */ - NCR_MISC(("ncr5380_select: failed, bus busy\n")); *sc->sci_mode = 0; + NCR_TRACE("select: bus busy, rc=%d\n", XS_BUSY); return XS_BUSY; } delay(2); } + NCR_TRACE("select: have AIP after %d loops\n", + ncr5380_wait_req_timo - timo); + /* Got AIP. Wait one arbitration delay (2.2 uS.) */ delay(3); /* Check for ICMD_LST */ if (*(sc->sci_icmd) & SCI_ICMD_LST) { /* Some other target asserted SEL. */ - NCR_MISC(("ncr5380_select: lost arbitration\n")); *sc->sci_mode = 0; + NCR_TRACE("select: lost one, rc=%d\n", XS_BUSY); + ncr5380_reselect(sc); /* XXX */ return XS_BUSY; } @@ -1070,9 +1364,9 @@ ncr5380_select(sc, xs) * XXX: did win arbitration. (too paranoid?) */ if (*(sc->sci_icmd) & SCI_ICMD_LST) { - NCR_MISC(("ncr5380_select: lost arb. after SEL\n")); *sc->sci_icmd = 0; *sc->sci_mode = 0; + NCR_TRACE("select: lost two, rc=%d\n", XS_BUSY); return XS_BUSY; } #endif @@ -1086,9 +1380,8 @@ ncr5380_select(sc, xs) * the host and target. Also set ATN now, to * ask the target for a messgae out phase. */ - NCR_MISC(("ncr5380_select: selection\n")); - data = (1 << xs->sc_link->target); - *(sc->sci_odata) = 0x80 | data; + data = 0x80 | (1 << sr->sr_target); + *(sc->sci_odata) = data; icmd |= (SCI_ICMD_DATA | SCI_ICMD_ATN); *(sc->sci_icmd) = icmd; delay(2); /* two deskew delays. */ @@ -1123,10 +1416,13 @@ ncr5380_select(sc, xs) delay(201); if ((*sc->sci_bus_csr & SCI_BUS_BSY) == 0) { /* Really no device on bus */ + *sc->sci_tcmd = PHASE_INVALID; *sc->sci_icmd = 0; *sc->sci_mode = 0; + *sc->sci_sel_enb = 0; + SCI_CLR_INTR(sc); *sc->sci_sel_enb = 0x80; - NCR_MISC(("ncr5380_select: device down\n")); + NCR_TRACE("select: device down, rc=%d\n", XS_SELTIMEOUT); return XS_SELTIMEOUT; } @@ -1142,7 +1438,6 @@ success: /* XXX - Make parity checking optional? */ *sc->sci_mode = (SCI_MODE_MONBSY | SCI_MODE_PAR_CHK); - NCR_MISC(("ncr5380_select: success\n")); return XS_NOERROR; } @@ -1199,17 +1494,21 @@ ncr5380_msg_in(sc) int act_flags; register u_char icmd; + /* acknowledge phase change */ + *sc->sci_tcmd = PHASE_MSG_IN; + act_flags = ACT_CONTINUE; icmd = *sc->sci_icmd & SCI_ICMD_RMASK; if (sc->sc_prevphase == PHASE_MSG_IN) { /* This is a continuation of the previous message. */ n = sc->sc_imp - sc->sc_imess; + NCR_TRACE("msg_in: continuation, n=%d\n", n); goto nextbyte; } /* This is a new MESSAGE IN phase. Clean up our state. */ - sc->sc_msg_flags &= ~NCR_DROP_MSGIN; + sc->sc_state &= ~NCR_DROP_MSGIN; nextmsg: n = 0; @@ -1227,18 +1526,17 @@ nextbyte: * First, check BSY, REQ, phase... */ if (!SCI_BUSY(sc)) { - NCR_MISC(("ncr5380_msg_in: lost BSY\n")); + NCR_TRACE("msg_in: lost BSY, n=%d\n", n); /* XXX - Assume the command completed? */ act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE); return (act_flags); } if (ncr5380_wait_req(sc)) { - NCR_MISC(("ncr5380_msg_in: BSY but no REQ\n")); - /* XXX - Try asserting ATN...? */ - act_flags |= ACT_RESET_BUS; + NCR_TRACE("msg_in: BSY but no REQ, n=%d\n", n); + /* Just let ncr5380_machine() handle it... */ return (act_flags); } - phase = SCI_CUR_PHASE(*sc->sci_bus_csr); + phase = SCI_BUS_PHASE(*sc->sci_bus_csr); if (phase != PHASE_MSG_IN) { /* * Target left MESSAGE IN, probably because it @@ -1250,14 +1548,14 @@ nextbyte: /* Still in MESSAGE IN phase, and REQ is asserted. */ if (*sc->sci_csr & SCI_CSR_PERR) { ncr_sched_msgout(sc, SEND_PARITY_ERROR); - sc->sc_msg_flags |= NCR_DROP_MSGIN; + sc->sc_state |= NCR_DROP_MSGIN; } /* Gather incoming message bytes if needed. */ - if ((sc->sc_msg_flags & NCR_DROP_MSGIN) == 0) { + if ((sc->sc_state & NCR_DROP_MSGIN) == 0) { if (n >= NCR_MAX_MSG_LEN) { ncr_sched_msgout(sc, SEND_REJECT); - sc->sc_msg_flags |= NCR_DROP_MSGIN; + sc->sc_state |= NCR_DROP_MSGIN; } else { *sc->sc_imp++ = *sc->sci_data; n++; @@ -1288,58 +1586,44 @@ nextbyte: *sc->sci_icmd = icmd; if (ncr5380_wait_not_req(sc)) { - NCR_MISC(("ncr5380_msg_in: stuck REQ\n")); + NCR_TRACE("msg_in: drop, stuck REQ, n=%d\n", n); act_flags |= ACT_RESET_BUS; - return (act_flags); } icmd &= ~SCI_ICMD_ACK; *sc->sci_icmd = icmd; + + if (act_flags != ACT_CONTINUE) + return (act_flags); + /* back to nextbyte */ } have_msg: /* We now have a complete message. Parse it. */ - NCR_MSGS(("n=%d imess=0x%02x ", n, sc->sc_imess[0])); - - /* Make sure we are connected. */ - if (sc->sc_current == NULL) { - printf("%s: unexpected MESSAGE IN; sending DEVICE RESET\n", - sc->sc_dev.dv_xname); - NCR_BREAK(); - goto reset; - } - sr = sc->sc_current; switch (sc->sc_imess[0]) { case MSG_CMDCOMPLETE: - /* sc->sc_state = NCR_CMDCOMPLETE; */ - if (sc->sc_datalen < 0) { - printf("%s: %d extra bytes from %d:%d\n", - sc->sc_dev.dv_xname, -sc->sc_datalen, - sr->sr_target, sr->sr_lun); - sc->sc_datalen = 0; - } + NCR_TRACE("msg_in: CMDCOMPLETE\n", 0); /* Target is about to disconnect. */ - NCR_MISC(("ncr5380_msg_in: cmdcomplete\n")); act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE); break; case MSG_DISCONNECT: - /* sc->sc_state = NCR_DISCONNECT; */ + NCR_TRACE("msg_in: DISCONNECT\n", 0); /* Target is about to disconnect. */ - NCR_MISC(("ncr5380_msg_in: disconnect\n")); act_flags |= ACT_DISCONNECT; break; case MSG_PARITY_ERROR: + NCR_TRACE("msg_in: PARITY_ERROR\n", 0); /* Resend the last message. */ ncr_sched_msgout(sc, sc->sc_msgout); break; case MSG_MESSAGE_REJECT: /* The target rejects the last message we sent. */ - NCR_MSGS(("ncr5380_msg_in: reject\n")); + NCR_TRACE("msg_in: got reject for 0x%x\n", sc->sc_msgout); switch (sc->sc_msgout) { case SEND_IDENTIFY: /* Really old target controller? */ @@ -1351,18 +1635,19 @@ have_msg: break; case MSG_NOOP: + NCR_TRACE("msg_in: NOOP\n", 0); break; case MSG_SAVEDATAPOINTER: - sr->sr_data = sc->sc_dataptr; + NCR_TRACE("msg_in: SAVE_PTRS\n", 0); + sr->sr_dataptr = sc->sc_dataptr; sr->sr_datalen = sc->sc_datalen; - sr->sr_dma_hand = sc->sc_dma_hand; break; case MSG_RESTOREPOINTERS: - sc->sc_dataptr = sr->sr_data; + NCR_TRACE("msg_in: RESTORE_PTRS\n", 0); + sc->sc_dataptr = sr->sr_dataptr; sc->sc_datalen = sr->sr_datalen; - sc->sc_dma_hand = sr->sr_dma_hand; break; case MSG_EXTENDED: @@ -1380,6 +1665,7 @@ have_msg: break; default: + NCR_TRACE("msg_in: eh? imsg=0x%x\n", sc->sc_imess[0]); printf("%s: unrecognized MESSAGE; sending REJECT\n", sc->sc_dev.dv_xname); NCR_BREAK(); @@ -1388,13 +1674,8 @@ have_msg: ncr_sched_msgout(sc, SEND_REJECT); break; - reset: - sc->sc_msg_flags |= NCR_ABORTING; - ncr_sched_msgout(sc, SEND_DEV_RESET); - break; - abort: - sc->sc_msg_flags |= NCR_ABORTING; + sc->sc_state |= NCR_ABORTING; ncr_sched_msgout(sc, SEND_ABORT); break; } @@ -1404,7 +1685,7 @@ have_msg: *sc->sci_icmd = icmd; if (ncr5380_wait_not_req(sc)) { - NCR_MISC(("ncr5380_msg_in: stuck REQ\n")); + NCR_TRACE("msg_in: last, stuck REQ, n=%d\n", n); act_flags |= ACT_RESET_BUS; } @@ -1447,16 +1728,17 @@ ncr5380_msg_out(sc) int progress, act_flags; register u_char icmd; - NCR_TRACE(("ncr_msgout ")); + /* acknowledge phase change */ + *sc->sci_tcmd = PHASE_MSG_OUT; progress = 0; /* did we send any messages? */ act_flags = ACT_CONTINUE; - icmd = *sc->sci_icmd & SCI_ICMD_RMASK; /* * Set ATN. If we're just sending a trivial 1-byte message, * we'll clear ATN later on anyway. Also drive the data bus. */ + icmd = *sc->sci_icmd & SCI_ICMD_RMASK; icmd |= (SCI_ICMD_ATN | SCI_ICMD_DATA); *sc->sci_icmd = icmd; @@ -1473,11 +1755,12 @@ ncr5380_msg_out(sc) * start again from the top. Our simple priority * scheme keeps the messages in the right order. */ - NCR_MSGS(("ncr5380_msg_out: retransmit\n")); sc->sc_msgpriq |= sc->sc_msgoutq; + NCR_TRACE("msg_out: retrans priq=0x%x\n", sc->sc_msgpriq); } else { /* This is a continuation of the previous message. */ n = sc->sc_omp - sc->sc_omess; + NCR_TRACE("msg_out: continuation, n=%d\n", n); goto nextbyte; } } @@ -1494,7 +1777,7 @@ nextmsg: /* Build the outgoing message data. */ switch (sc->sc_msgout) { case SEND_IDENTIFY: - /* (sc->sc_state != NCR_CONNECTED) */ + NCR_TRACE("msg_out: SEND_IDENTIFY\n", 0); if (sr == NULL) { printf("%s: SEND_IDENTIFY while not connected; sending NOOP\n", sc->sc_dev.dv_xname); @@ -1508,6 +1791,7 @@ nextmsg: break; case SEND_DEV_RESET: + NCR_TRACE("msg_out: SEND_DEV_RESET\n", 0); /* Expect disconnect after this! */ /* XXX: Kill jobs for this target? */ act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE); @@ -1516,21 +1800,25 @@ nextmsg: break; case SEND_REJECT: + NCR_TRACE("msg_out: SEND_REJECT\n", 0); sc->sc_omess[0] = MSG_MESSAGE_REJECT; n = 1; break; case SEND_PARITY_ERROR: + NCR_TRACE("msg_out: SEND_PARITY_ERROR\n", 0); sc->sc_omess[0] = MSG_PARITY_ERROR; n = 1; break; case SEND_INIT_DET_ERR: + NCR_TRACE("msg_out: SEND_INIT_DET_ERR\n", 0); sc->sc_omess[0] = MSG_INITIATOR_DET_ERR; n = 1; break; case SEND_ABORT: + NCR_TRACE("msg_out: SEND_ABORT\n", 0); /* Expect disconnect after this! */ /* XXX: Set error flag? */ act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE); @@ -1543,6 +1831,7 @@ nextmsg: sc->sc_dev.dv_xname); NCR_BREAK(); noop: + NCR_TRACE("msg_out: send NOOP\n", 0); sc->sc_omess[0] = MSG_NOOP; n = 1; break; @@ -1563,19 +1852,20 @@ nextbyte: * First check BSY, REQ, phase... */ if (!SCI_BUSY(sc)) { - NCR_MISC(("ncr5380_msg_out: lost BSY\n")); + NCR_TRACE("msg_out: lost BSY, n=%d\n", n); goto out; } if (ncr5380_wait_req(sc)) { - NCR_MISC(("ncr5380_msg_out: no REQ\n")); + NCR_TRACE("msg_out: no REQ, n=%d\n", n); goto out; } - phase = SCI_CUR_PHASE(*sc->sci_bus_csr); + phase = SCI_BUS_PHASE(*sc->sci_bus_csr); if (phase != PHASE_MSG_OUT) { /* * Target left MESSAGE OUT, possibly to reject * our message. */ + NCR_TRACE("msg_out: new phase=%d\n", phase); goto out; } @@ -1599,19 +1889,23 @@ nextbyte: /* Wait for REQ to be negated. */ if (ncr5380_wait_not_req(sc)) { - NCR_MISC(("ncr5380_msg_out: stuck REQ\n")); + NCR_TRACE("msg_out: stuck REQ, n=%d\n", n); act_flags |= ACT_RESET_BUS; - goto out; } /* Finally, drop ACK. */ icmd &= ~SCI_ICMD_ACK; *sc->sci_icmd = icmd; + + /* Stuck bus or something... */ + if (act_flags & ACT_RESET_BUS) + goto out; + } progress++; /* We get here only if the entire message has been transmitted. */ - if ((sc->sc_msgpriq != 0) && (act_flags == ACT_CONTINUE)) { + if (sc->sc_msgpriq != 0) { /* There are more outgoing messages. */ goto nextmsg; } @@ -1643,10 +1937,13 @@ static int ncr5380_command(sc) struct ncr5380_softc *sc; { - int len; - struct scsi_sense rqs; struct sci_req *sr = sc->sc_current; struct scsi_xfer *xs = sr->sr_xs; + struct scsi_sense rqs; + int len; + + /* acknowledge phase change */ + *sc->sci_tcmd = PHASE_COMMAND; if (sr->sr_flags & SR_SENSE) { rqs.opcode = REQUEST_SENSE; @@ -1654,22 +1951,29 @@ ncr5380_command(sc) rqs.length = sizeof(xs->sense); rqs.unused[0] = rqs.unused[1] = rqs.control = 0; - len = ncr5380_pio_out(sc, PHASE_CMD, sizeof(rqs), + len = ncr5380_pio_out(sc, PHASE_COMMAND, sizeof(rqs), (u_char *)&rqs); } else { /* Assume command can be sent in one go. */ /* XXX: Do this using DMA, and get a phase change intr? */ - len = ncr5380_pio_out(sc, PHASE_CMD, xs->cmdlen, + len = ncr5380_pio_out(sc, PHASE_COMMAND, xs->cmdlen, (u_char *)xs->cmd); } if (len != xs->cmdlen) { +#ifdef DEBUG printf("ncr5380_command: short transfer: wanted %d got %d.\n", xs->cmdlen, len); + ncr5380_show_scsi_cmd(xs); NCR_BREAK(); - if (len < 6) - return ACT_RESET_BUS; +#endif + if (len < 6) { + xs->error = XS_DRIVER_STUFFUP; + sc->sc_state |= NCR_ABORTING; + ncr_sched_msgout(sc, SEND_ABORT); + } + } return ACT_CONTINUE; @@ -1689,69 +1993,67 @@ ncr5380_data_xfer(sc, phase) int expected_phase; int i, len; - if (sc->sc_msg_flags & NCR_ABORTING) { - sc->sc_msg_flags &= ~NCR_ABORTING; - printf("%s: data phase after abort\n", - sc->sc_dev.dv_xname); - return ACT_RESET_BUS; - } - if (sr->sr_flags & SR_SENSE) { + NCR_TRACE("data_xfer: get sense, sr=0x%x\n", (long)sr); if (phase != PHASE_DATA_IN) { printf("%s: sense phase error\n", sc->sc_dev.dv_xname); - return (ACT_ABORT_CMD); + goto abort; } - NCR_MISC(("ncr5380_data_xfer: reading sense data\n")); + /* acknowledge phase change */ + *sc->sci_tcmd = PHASE_DATA_IN; len = ncr5380_pio_in(sc, phase, sizeof(xs->sense), (u_char *)&xs->sense); - return ACT_CONTINUE; /* XXX */ + return ACT_CONTINUE; + } + + /* + * When aborting a command, disallow any data phase. + */ + if (sc->sc_state & NCR_ABORTING) { + printf("%s: aborting, but phase=%s (reset)\n", + sc->sc_dev.dv_xname, + phase_names[phase & 7]); + return ACT_RESET_BUS; /* XXX */ } /* Validate expected phase (data_in or data_out) */ expected_phase = (xs->flags & SCSI_DATA_OUT) ? PHASE_DATA_OUT : PHASE_DATA_IN; if (phase != expected_phase) { - printf("%s: data phase error\n", sc->sc_dev.dv_xname); - return (ACT_ABORT_CMD); + printf("%s: data phase error\n", + sc->sc_dev.dv_xname); + goto abort; } /* Make sure we have some data to move. */ if (sc->sc_datalen <= 0) { - printf("%s: can not transfer more data\n"); - return (ACT_ABORT_CMD); + printf("%s: can not transfer more data\n", + sc->sc_dev.dv_xname); + goto abort; } - NCR_MISC(("ncr5380_data_xfer: todo=%d\n", sc->sc_datalen)); /* * Attempt DMA only if dma_alloc gave us a DMA handle AND * there is enough left to transfer so DMA is worth while. - * Note that we can come back here after a DMA transfer moves - * all but the last few bytes of a request, in which case - * we should finish the request using PIO. In this case - * there WILL be a dma_handle, but we should not use it. - * Data alignment was checked in DMA alloc. */ - if (sc->sc_dma_hand && + if (sr->sr_dma_hand && (sc->sc_datalen >= sc->sc_min_dma_len)) { - /* OK, really do DMA. */ + /* + * OK, really start DMA. Note, the MI start function + * is responsible for setting the TCMD register, etc. + * (Acknowledge the phase change there, not here.) + */ + NCR_TRACE("data_xfer: dma_start, dh=0x%x\n", + (long) sr->sr_dma_hand); (*sc->sc_dma_start)(sc); - if (sc->sc_dma_flags & DMA5380_POLL) { - (*sc->sc_dma_poll)(sc); - (*sc->sc_dma_stop)(sc); - } - if (sc->sc_dma_flags & DMA5380_INPROGRESS) { - /* Interrupt will let us continue */ - NCR_MISC(("ncr5380_data_xfer: expect DMA intr.\n")); - return ACT_WAIT_INTR; - } - /* Must have done polled DMA. */ - SCI_CLR_INTR(sc); /* XXX */ - /* Let _machine call us again... */ - return ACT_CONTINUE; + return ACT_WAIT_DMA; } - NCR_MISC(("ncr5380_data_xfer: doing PIO, len=%d\n", sc->sc_datalen)); + NCR_TRACE("data_xfer: doing PIO, len=%d\n", sc->sc_datalen); + + /* acknowledge phase change */ + *sc->sci_tcmd = phase; if (phase == PHASE_DATA_OUT) { len = ncr5380_pio_out(sc, phase, sc->sc_datalen, sc->sc_dataptr); } else { @@ -1760,7 +2062,12 @@ ncr5380_data_xfer(sc, phase) sc->sc_dataptr += len; sc->sc_datalen -= len; - NCR_MISC(("ncr5380_data_xfer: did PIO, resid=%d\n", sc->sc_datalen)); + NCR_TRACE("data_xfer: did PIO, resid=%d\n", sc->sc_datalen); + return (ACT_CONTINUE); + +abort: + sc->sc_state |= NCR_ABORTING; + ncr_sched_msgout(sc, SEND_ABORT); return (ACT_CONTINUE); } @@ -1774,59 +2081,62 @@ ncr5380_status(sc) struct sci_req *sr = sc->sc_current; struct scsi_xfer *xs = sr->sr_xs; - len = ncr5380_pio_in(sc, PHASE_STATUS, 1, &status); + /* acknowledge phase change */ + *sc->sci_tcmd = PHASE_STATUS; + len = ncr5380_pio_in(sc, PHASE_STATUS, 1, &status); if (len) { sr->sr_status = status; } else { printf("ncr5380_status: none?\n"); } + return ACT_CONTINUE; } /* - * This is the big state machine that follows SCSI phase changes. - * This is somewhat like a co-routine. It will do a SCSI command, - * and exit if the command is complete, or if it must wait (usually - * for DMA). + * This is the big state machine that follows SCSI phase changes. + * This is somewhat like a co-routine. It will do a SCSI command, + * and exit if the command is complete, or if it must wait, i.e. + * for DMA to complete or for reselect to resume the job. * - * The bus must be selected, and we need to know which command is - * being undertaken. + * The bus must be selected, and we need to know which command is + * being undertaken. */ static void ncr5380_machine(sc) struct ncr5380_softc *sc; { - struct sci_req *sr = sc->sc_current; - struct scsi_xfer *xs = sr->sr_xs; - int phase, act_flags, timo; + struct sci_req *sr; + struct scsi_xfer *xs; + int act_flags, phase, timo; #ifdef DIAGNOSTIC if ((getsr() & PSL_IPL) < PSL_IPL2) panic("ncr5380_machine: bad spl"); + if (sc->sc_state == NCR_IDLE) + panic("ncr5380_machine: state=idle"); + if (sc->sc_current == NULL) + panic("ncr5380_machine: no current cmd"); #endif + sr = sc->sc_current; + xs = sr->sr_xs; act_flags = ACT_CONTINUE; -next_phase: /* - * We can arrive back here due to a DMA interrupt - * or DMA timeout error. On error, reset it. - * If DMA failed, we probably can't talk to it. + * This will be called by ncr5380_intr() when DMA is + * complete. Must stop DMA before touching the 5380 or + * there will be "register conflict" errors. */ - if (sc->sc_dma_flags & DMA5380_ERROR) { - printf("ncr5380: DMA error\n"); - NCR_BREAK(); - /* Make sure we don't keep trying it... */ - if (sc->sc_dma_hand) { - (*sc->sc_dma_free)(sc); - sc->sc_dma_hand = NULL; - } - act_flags |= ACT_RESET_BUS; - goto do_actions; + if (sc->sc_state & NCR_DOINGDMA) { + /* Pick-up where where we left off... */ + goto dma_done; } +next_phase: + if (!SCI_BUSY(sc)) { /* Unexpected disconnect */ printf("ncr5380_machine: unexpected disconnect.\n"); @@ -1845,24 +2155,38 @@ next_phase: if (*sc->sci_bus_csr & SCI_BUS_REQ) break; if (--timo <= 0) { - printf("%s: no REQ for next phase, reset\n", - sc->sc_dev.dv_xname); - /* Reset implies disconnect, cmd fail. */ - act_flags |= ACT_RESET_BUS; - goto do_actions; + if (sc->sc_state & NCR_ABORTING) { + printf("%s: no REQ while aborting, reset\n", + sc->sc_dev.dv_xname); + act_flags |= ACT_RESET_BUS; + goto do_actions; + } + printf("%s: no REQ for next phase, abort\n", + sc->sc_dev.dv_xname); + sc->sc_state |= NCR_ABORTING; + ncr_sched_msgout(sc, SEND_ABORT); + goto next_phase; } delay(100); } - phase = SCI_CUR_PHASE(*sc->sci_bus_csr); - NCR_MISC(("ncr5380_machine: phase=%s\n", - phase_names[phase & 7])); + phase = SCI_BUS_PHASE(*sc->sci_bus_csr); + NCR_TRACE("machine: phase=%s\n", + (long) phase_names[phase & 7]); + + /* + * We assume that the device knows what it's doing, + * so any phase is good. + */ +#if 0 /* - * We make the assumption that the device knows what it's - * doing, so any phase is good. + * XXX: Do not ACK the phase yet! do it later... + * XXX: ... each phase routine does that itself. + * In particular, DMA needs it done LATER. */ - *sc->sci_tcmd = phase; /* acknowledge */ + *sc->sci_tcmd = phase; /* acknowledge phase change */ +#endif switch (phase) { @@ -1871,7 +2195,7 @@ next_phase: act_flags = ncr5380_data_xfer(sc, phase); break; - case PHASE_CMD: + case PHASE_COMMAND: act_flags = ncr5380_command(sc); break; @@ -1889,61 +2213,89 @@ next_phase: default: printf("ncr5380_machine: Unexpected phase 0x%x\n", phase); - act_flags = ACT_ABORT_CMD; - break; + sc->sc_state |= NCR_ABORTING; + ncr_sched_msgout(sc, SEND_ABORT); + goto next_phase; } /* switch */ sc->sc_prevphase = phase; - /* short-cut */ - if (act_flags == ACT_CONTINUE) - goto next_phase; - do_actions: __asm("_ncr5380_actions:"); - NCR_MISC(("ncr5380_machine: act_flags=0x%x\n", act_flags)); + if (act_flags & ACT_WAIT_DMA) { + act_flags &= ~ACT_WAIT_DMA; + /* Wait for DMA to complete (polling, or interrupt). */ + if ((sr->sr_flags & SR_IMMED) == 0) { + NCR_TRACE("machine: wait for DMA intr.\n", 0); + return; /* will resume at dma_done */ + } + /* Busy-wait for it to finish. */ + NCR_TRACE("machine: dma_poll, dh=0x%x\n", + (long) sr->sr_dma_hand); + (*sc->sc_dma_poll)(sc); + dma_done: + /* Return here after interrupt. */ + if (sr->sr_flags & SR_OVERDUE) + sc->sc_state |= NCR_ABORTING; + NCR_TRACE("machine: dma_stop, dh=0x%x\n", + (long) sr->sr_dma_hand); + (*sc->sc_dma_stop)(sc); + SCI_CLR_INTR(sc); /* XXX */ + /* + * While DMA is running we can not touch the SBC, + * so various places just set NCR_ABORTING and + * expect us the "kick it" when DMA is done. + */ + if (sc->sc_state & NCR_ABORTING) { + ncr_sched_msgout(sc, SEND_ABORT); + } + } - if (act_flags & ACT_WAIT_INTR) { - /* Wait for DMA complete interrupt (or timeout). */ - NCR_MISC(("ncr5380_machine: wait for dma\n")); - return; + /* + * Check for parity error. + * XXX - better place to check? + */ + if (*(sc->sci_csr) & SCI_CSR_PERR) { + printf("%s: parity error!\n", + sc->sc_dev.dv_xname); + /* XXX: sc->sc_state |= NCR_ABORTING; */ + ncr_sched_msgout(sc, SEND_PARITY_ERROR); } + if (act_flags == ACT_CONTINUE) + goto next_phase; + /* All other actions "break" from the loop. */ + + NCR_TRACE("machine: act_flags=0x%x\n", act_flags); + if (act_flags & ACT_RESET_BUS) { + act_flags |= ACT_CMD_DONE; /* * Reset the SCSI bus, usually due to a timeout. * The error code XS_TIMEOUT allows retries. */ - NCR_MISC(("ncr5380_machine: reset scsi bus\n")); + sc->sc_state |= NCR_ABORTING; + printf("%s: reset SCSI bus for TID=%d LUN=%d\n", + sc->sc_dev.dv_xname, + sr->sr_target, sr->sr_lun); ncr5380_reset_scsibus(sc); - xs->error = XS_TIMEOUT; - act_flags = (ACT_DISCONNECT | ACT_CMD_DONE); - } - - /* Try ONCE to send an ABORT message. */ - if (act_flags & ACT_ABORT_CMD) { - act_flags &= ~ACT_ABORT_CMD; - /* Try to send MSG_ABORT to the target. */ - NCR_MISC(("ncr5380_machine: send abort\n")); - sc->sc_msg_flags |= NCR_ABORTING; - ncr_sched_msgout(sc, SEND_ABORT); - goto next_phase; - } - - if (sc->sc_msg_flags & NCR_ABORTING) { - sc->sc_msg_flags &= ~NCR_ABORTING; - printf("ncr5380_machine: command aborted\n"); - xs->error = XS_DRIVER_STUFFUP; - act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE); } if (act_flags & ACT_CMD_DONE) { + act_flags |= ACT_DISCONNECT; /* Need to call scsi_done() */ + /* XXX: from the aic6360 driver, but why? */ + if (sc->sc_datalen < 0) { + printf("%s: %d extra bytes from %d:%d\n", + sc->sc_dev.dv_xname, -sc->sc_datalen, + sr->sr_target, sr->sr_lun); + sc->sc_datalen = 0; + } xs->resid = sc->sc_datalen; /* Note: this will clear sc_current */ + NCR_TRACE("machine: call done, cur=0x%x\n", (long)sr); ncr5380_done(sc); - act_flags |= ACT_DISCONNECT; } if (act_flags & ACT_DISCONNECT) { @@ -1953,17 +2305,204 @@ do_actions: */ *sc->sci_icmd = 0; *sc->sci_mode = 0; - *sc->sci_tcmd = 0; + *sc->sci_tcmd = PHASE_INVALID; + *sc->sci_sel_enb = 0; SCI_CLR_INTR(sc); - *sc->sci_sel_enb = 0x80; + if ((act_flags & ACT_CMD_DONE) == 0) { + __asm("_ncr5380_disconnected:"); + NCR_TRACE("machine: discon, cur=0x%x\n", (long)sr); + } + /* * We may be here due to a disconnect message, * in which case we did NOT call ncr5380_done, - * so we need to clear sc_current. + * and we need to clear sc_current. */ + sc->sc_state = NCR_IDLE; sc->sc_current = NULL; + + /* Paranoia: clear everything. */ + sc->sc_dataptr = NULL; + sc->sc_datalen = 0; + sc->sc_prevphase = PHASE_INVALID; + sc->sc_msgpriq = 0; + sc->sc_msgoutq = 0; + sc->sc_msgout = 0; + + /* Our caller will re-enable interrupts. */ + } +} + + +#ifdef DEBUG + +static void +ncr5380_show_scsi_cmd(xs) + 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); + } +} + + +static void +ncr5380_show_sense(xs) + struct scsi_xfer *xs; +{ + u_char *b = (u_char *)&xs->sense; + int i; + + printf("sense:"); + for (i = 0; i < sizeof(xs->sense); i++) + printf(" %02x", b[i]); + printf("\n"); +} + +int ncr5380_traceidx = 0; + +#define TRACE_MAX 1024 +struct trace_ent { + char *msg; + long val; +} ncr5380_tracebuf[TRACE_MAX]; + +void +ncr5380_trace(msg, val) + char *msg; + long val; +{ + register struct trace_ent *tr; + register int s; + + s = splhigh(); + + tr = &ncr5380_tracebuf[ncr5380_traceidx]; + + ncr5380_traceidx++; + if (ncr5380_traceidx >= TRACE_MAX) + ncr5380_traceidx = 0; + + tr->msg = msg; + tr->val = val; + + splx(s); +} + +#ifdef DDB +void +ncr5380_clear_trace() +{ + ncr5380_traceidx = 0; + bzero((char*) ncr5380_tracebuf, sizeof(ncr5380_tracebuf)); +} + +void +ncr5380_show_trace() +{ + struct trace_ent *tr; + int idx; + + idx = ncr5380_traceidx; + do { + tr = &ncr5380_tracebuf[idx]; + idx++; + if (idx >= TRACE_MAX) + idx = 0; + if (tr->msg) + db_printf(tr->msg, tr->val); + } while (idx != ncr5380_traceidx); +} + +void +ncr5380_show_req(sr) + struct sci_req *sr; +{ + struct scsi_xfer *xs = sr->sr_xs; + + db_printf("TID=%d ", sr->sr_target); + db_printf("LUN=%d ", sr->sr_lun); + db_printf("dh=0x%x ", sr->sr_dma_hand); + db_printf("dptr=0x%x ", sr->sr_dataptr); + db_printf("dlen=0x%x ", sr->sr_datalen); + db_printf("flags=%d ", sr->sr_flags); + db_printf("stat=%d ", sr->sr_status); + + if (xs == NULL) { + db_printf("(xs=NULL)\n"); + return; + } + db_printf("\n"); +#ifdef SCSIDEBUG + show_scsi_xs(xs); +#else + db_printf("xs=0x%x\n", xs); +#endif +} + +void +ncr5380_show_state() +{ + struct ncr5380_softc *sc; + struct sci_req *sr; + int i, j, k; + + sc = ncr5380_debug_sc; + + if (sc == NULL) { + db_printf("ncr5380_debug_sc == NULL\n"); return; } + + db_printf("sc_ncmds=%d\n", sc->sc_ncmds); + k = -1; /* which is current? */ + for (i = 0; i < SCI_OPENINGS; i++) { + sr = &sc->sc_ring[i]; + if (sr->sr_xs) { + if (sr == sc->sc_current) + k = i; + db_printf("req %d: (sr=0x%x)", i, (long)sr); + ncr5380_show_req(sr); + } + } + db_printf("sc_rr=%d, current=%d\n", sc->sc_rr, k); + + db_printf("Active request matrix:\n"); + for(i = 0; i < 8; i++) { /* targets */ + for (j = 0; j < 8; j++) { /* LUN */ + sr = sc->sc_matrix[i][j]; + if (sr) { + db_printf("TID=%d LUN=%d sr=0x%x\n", i, j, (long)sr); + } + } + } + + db_printf("sc_state=0x%x\n", sc->sc_state); + db_printf("sc_current=0x%x\n", sc->sc_current); + db_printf("sc_dataptr=0x%x\n", sc->sc_dataptr); + db_printf("sc_datalen=0x%x\n", sc->sc_datalen); + + db_printf("sc_prevphase=%d\n", sc->sc_prevphase); + db_printf("sc_msgpriq=0x%x\n", sc->sc_msgpriq); } + +#endif /* DDB */ +#endif /* DEBUG */ diff --git a/sys/arch/sun3/dev/ncr5380var.h b/sys/arch/sun3/dev/ncr5380var.h index 1c27eccd0f3..1ae35b4d58b 100644 --- a/sys/arch/sun3/dev/ncr5380var.h +++ b/sys/arch/sun3/dev/ncr5380var.h @@ -1,4 +1,4 @@ -/* $NetBSD: ncr5380var.h,v 1.1 1995/10/29 21:19:10 gwr Exp $ */ +/* $NetBSD: ncr5380var.h,v 1.1.2.1 1995/11/18 07:08:41 gwr Exp $ */ /* * Copyright (c) 1995 David Jones, Gordon W. Ross @@ -40,48 +40,54 @@ #define SCI_CLR_INTR(sc) (*(sc)->sci_iack) #define SCI_BUSY(sc) (*sc->sci_bus_csr & SCI_BUS_BSY) +/* These are NOT artibtrary, but map to bits in sci_tcmd */ #define PHASE_DATA_OUT 0x0 #define PHASE_DATA_IN 0x1 -#define PHASE_CMD 0x2 +#define PHASE_COMMAND 0x2 #define PHASE_STATUS 0x3 #define PHASE_UNSPEC1 0x4 #define PHASE_UNSPEC2 0x5 #define PHASE_MSG_OUT 0x6 #define PHASE_MSG_IN 0x7 -#define PHASE_INVALID -1 +/* + * This illegal phase is used to prevent the 5380 from having + * a phase-match condition when we don't want one, such as + * when setting up the DMA engine or whatever... + */ +#define PHASE_INVALID PHASE_UNSPEC1 -#define SCSI_PHASE(x) ((x)&0x7) /* Per-request state. This is required in order to support reselection. */ struct sci_req { struct scsi_xfer *sr_xs; /* Pointer to xfer struct, NULL=unused */ int sr_target, sr_lun; /* For fast access */ void *sr_dma_hand; /* Current DMA hnadle */ - u_char *sr_data; /* Saved data pointer */ + u_char *sr_dataptr; /* Saved data pointer */ int sr_datalen; int sr_flags; /* Internal error code */ #define SR_IMMED 1 /* Immediate command */ #define SR_SENSE 2 /* We are getting sense */ -#define SR_ERROR 4 /* Error occurred */ +#define SR_OVERDUE 4 /* Timeout while not current */ +#define SR_ERROR 8 /* Error occurred */ int sr_status; /* Status code from last cmd */ }; -#define SCI_OPENINGS 4 /* Up to 4 commands at once */ +#define SCI_OPENINGS 16 /* How many commands we can enqueue. */ struct ncr5380_softc { struct device sc_dev; struct scsi_link sc_link; - /* Pointers to 5380 registers. MD code must set these up. */ - volatile u_char *sci_data; - volatile u_char *sci_icmd; - volatile u_char *sci_mode; - volatile u_char *sci_tcmd; - volatile u_char *sci_bus_csr; - volatile u_char *sci_csr; - volatile u_char *sci_idata; - volatile u_char *sci_iack; + /* Pointers to 5380 registers. See ncr5380reg.h */ + volatile u_char *sci_r0; + volatile u_char *sci_r1; + volatile u_char *sci_r2; + volatile u_char *sci_r3; + volatile u_char *sci_r4; + volatile u_char *sci_r5; + volatile u_char *sci_r6; + volatile u_char *sci_r7; /* Functions set from MD code */ int (*sc_pio_out) __P((struct ncr5380_softc *, @@ -90,35 +96,40 @@ struct ncr5380_softc { int, int, u_char *)); void (*sc_dma_alloc) __P((struct ncr5380_softc *)); void (*sc_dma_free) __P((struct ncr5380_softc *)); + + void (*sc_dma_setup) __P((struct ncr5380_softc *)); void (*sc_dma_start) __P((struct ncr5380_softc *)); void (*sc_dma_poll) __P((struct ncr5380_softc *)); void (*sc_dma_eop) __P((struct ncr5380_softc *)); void (*sc_dma_stop) __P((struct ncr5380_softc *)); + void (*sc_intr_on) __P((struct ncr5380_softc *)); + void (*sc_intr_off) __P((struct ncr5380_softc *)); + int sc_flags; /* Misc. flags and capabilities */ #define NCR5380_PERMIT_RESELECT 1 /* Allow disconnect/reselect */ +#define NCR5380_FORCE_POLLING 2 /* Do not use interrupts. */ int sc_min_dma_len; /* Smaller than this is done with PIO */ /* Begin MI shared data */ - /* Active data pointer for current SCSI process */ + int sc_state; +#define NCR_IDLE 0 /* Ready for new work. */ +#define NCR_WORKING 0x01 /* Some command is in progress. */ +#define NCR_ABORTING 0x02 /* Bailing out */ +#define NCR_DOINGDMA 0x04 /* The FIFO data path is active! */ +#define NCR_DROP_MSGIN 0x10 /* Discard all msgs (parity err detected) */ + + /* The request that has the bus now. */ + struct sci_req *sc_current; + + /* Active data pointer for current SCSI command. */ u_char *sc_dataptr; int sc_datalen; - void *sc_dma_hand; /* DMA handle */ - u_int sc_dma_flags; -#define DMA5380_INPROGRESS 1 /* MD: DMA is curently in progress */ -#define DMA5380_WRITE 2 /* MI: DMA is to output to SCSI */ -#define DMA5380_POLL 4 /* MI: Poll for DMA completion */ -#define DMA5380_ERROR 8 /* MD: DMA operation failed */ -#define DMA5380_PHYS 16 /* MI: Buffer has B_PHYS set */ - /* Begin MI private data */ - /* The request that has the bus now. */ - struct sci_req *sc_current; - /* The number of operations in progress on the bus */ volatile int sc_ncmds; @@ -131,10 +142,7 @@ struct ncr5380_softc { /* Message stuff */ int sc_prevphase; - int sc_msg_flags; -#define NCR_DROP_MSGIN 1 -#define NCR_ABORTING 2 -#define NCR_NEED_RESET 4 + u_int sc_msgpriq; /* Messages we want to send */ u_int sc_msgoutq; /* Messages sent during last MESSAGE OUT */ u_int sc_msgout; /* Message last transmitted */ @@ -156,8 +164,15 @@ struct ncr5380_softc { void ncr5380_init __P((struct ncr5380_softc *)); void ncr5380_reset_scsibus __P((struct ncr5380_softc *)); -int ncr5380_sbc_intr __P((struct ncr5380_softc *)); +int ncr5380_intr __P((struct ncr5380_softc *)); int ncr5380_scsi_cmd __P((struct scsi_xfer *)); int ncr5380_pio_in __P((struct ncr5380_softc *, int, int, u_char *)); int ncr5380_pio_out __P((struct ncr5380_softc *, int, int, u_char *)); +#ifdef DEBUG +struct ncr5380_softc *ncr5380_debug_sc; +void ncr5380_trace __P((char *msg, long val)); +#define NCR_TRACE(msg, val) ncr5380_trace(msg, val) +#else +#define NCR_TRACE(msg, val) /* nada */ +#endif diff --git a/sys/arch/sun3/dev/ncr_si.c b/sys/arch/sun3/dev/ncr_si.c index 951e37340d6..07119e08f30 100644 --- a/sys/arch/sun3/dev/ncr_si.c +++ b/sys/arch/sun3/dev/ncr_si.c @@ -1,8 +1,8 @@ -/* $NetBSD: ncr_si.c,v 1.1 1995/10/29 21:19:11 gwr Exp $ */ +/* $NetBSD: ncr_si.c,v 1.1.2.1 1995/11/18 07:08:48 gwr Exp $ */ /* - * Copyright (c) 1995 David Jones - * Copyright (c) 1994 Adam Glass, Gordon W. Ross + * 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 @@ -18,7 +18,7 @@ * 4. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by - * Adam Glass, David Jones and Gordon Ross + * Adam Glass, David Jones, and Gordon Ross * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES @@ -36,17 +36,51 @@ * 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 -#include #include #include +#include #include +#include #include #include #include -#include #include #include @@ -57,6 +91,8 @@ #include #include +#define DEBUG XXX + #if 0 /* XXX - not yet... */ #include #include @@ -75,28 +111,21 @@ #define MIN_DMA_LEN 128 /* - * Transfers lager than 63K need to be broken up - * because some of the device counters are 16 bits. + * Transfers lager than 65535 bytes need to be split-up. + * (Some of the FIFO logic has only 16 bits counters.) + * Make the size an integer multiple of the page size + * to avoid buf/cluster remap problems. (paranoid?) */ -#define MAX_DMA_LEN 0xFC00 +#define MAX_DMA_LEN 0xE000 /* - * How many uS. to delay after touching the am9616 UDC. + * How many uS. to delay after touching the am9516 UDC. */ #define UDC_WAIT_USEC 5 -#define DEBUG XXX - #ifdef DEBUG int si_debug = 0; -static int si_flags = 0 /* | SDEV_DB2 */ ; -static int Seq = 0; -static int Nwrite = 0; -struct si_softc *TheSoftC; -volatile struct si_regs *TheRegs; -void log_intr(void); -void log_start(void); -void si_printregs(void); +static int si_link_flags = 0 /* | SDEV_DB2 */ ; #endif /* @@ -105,12 +134,12 @@ void si_printregs(void); * the array of these has to be in DVMA space. */ struct si_dma_handle { - u_long dh_addr; /* KVA of start of buffer */ - int dh_len; /* Original data length */ - u_long dh_dvma; /* VA of buffer in DVMA space */ int dh_flags; #define SIDH_BUSY 1 /* This DH is in use */ #define SIDH_OUT 2 /* DMA does data out (write) */ + u_char * dh_addr; /* KVA of start of buffer */ + int dh_maplen; /* Length of KVA mapping. */ + long dh_dvma; /* VA of buffer in DVMA space */ /* DMA command block for the OBIO controller. */ struct udc_table dh_cmd; }; @@ -120,22 +149,19 @@ struct si_dma_handle { * so we can just cast to go back and fourth between them. */ struct si_softc { - struct ncr5380_softc ncr; + struct ncr5380_softc ncr_sc; volatile struct si_regs *sc_regs; int sc_adapter_type; int sc_adapter_iv_am; /* int. vec + address modifier */ - int sc_timo; struct si_dma_handle *sc_dma; + int sc_xlen; /* length of current DMA segment. */ }; -/* - * XXX: Note that reselect CAN NOT WORK given the current need - * to set the damn FIFO count logic during dma_alloc, because - * the DMA might be need by another target in the mean time. - * (It works when there is only one target/lun though...) - */ -int si_permit_reselect = 0; /* XXX: Do not set this yet. */ -int si_polled_dma = 0; /* Set if interrupts don't work */ +/* Options. Interesting values are: 1,3,7 */ +int si_options = 0; +#define SI_ENABLE_DMA 1 /* Use DMA (maybe polled) */ +#define SI_DMA_INTR 2 /* DMA completion interrupts */ +#define SI_DO_RESELECT 4 /* Allow disconnect/reselect */ /* How long to wait for DMA before declaring an error. */ int si_dma_intr_timo = 500; /* ticks (sec. X 100) */ @@ -151,10 +177,15 @@ void si_dma_alloc __P((struct ncr5380_softc *)); void si_dma_free __P((struct ncr5380_softc *)); void si_dma_poll __P((struct ncr5380_softc *)); +void si_vme_dma_setup __P((struct ncr5380_softc *)); void si_vme_dma_start __P((struct ncr5380_softc *)); void si_vme_dma_eop __P((struct ncr5380_softc *)); void si_vme_dma_stop __P((struct ncr5380_softc *)); +void si_vme_intr_on __P((struct ncr5380_softc *)); +void si_vme_intr_off __P((struct ncr5380_softc *)); + +void si_obio_dma_setup __P((struct ncr5380_softc *)); void si_obio_dma_start __P((struct ncr5380_softc *)); void si_obio_dma_eop __P((struct ncr5380_softc *)); void si_obio_dma_stop __P((struct ncr5380_softc *)); @@ -178,8 +209,8 @@ static struct scsi_device si_dev = { struct cfdriver ncr_sicd = { - NULL, si_name, si_match, si_attach, DV_DULL, - sizeof(struct si_softc), NULL, 0, + NULL, si_name, si_match, si_attach, + DV_DULL, sizeof(struct si_softc), NULL, 0, }; static int @@ -250,6 +281,7 @@ si_attach(parent, self, args) void *args; { struct si_softc *sc = (struct si_softc *) self; + struct ncr5380_softc *ncr_sc = (struct ncr5380_softc *)sc; volatile struct si_regs *regs; struct confargs *ca = args; int i; @@ -259,17 +291,11 @@ si_attach(parent, self, args) case BUS_OBIO: regs = (struct si_regs *) obio_alloc(ca->ca_paddr, sizeof(*regs)); - isr_add_autovect(si_intr, (void *)sc, - ca->ca_intpri); break; case BUS_VME16: regs = (struct si_regs *) bus_mapin(ca->ca_bustype, ca->ca_paddr, sizeof(*regs)); - isr_add_vectored(si_intr, (void *)sc, - ca->ca_intpri, ca->ca_intvec); - sc->sc_adapter_iv_am = - VME_SUPV_DATA_24 | (ca->ca_intvec & 0xFF); break; default: @@ -281,51 +307,62 @@ si_attach(parent, self, args) /* * Fill in the prototype scsi_link. */ - sc->ncr.sc_link.adapter_softc = sc; - sc->ncr.sc_link.adapter_target = 7; - sc->ncr.sc_link.adapter = &si_ops; - sc->ncr.sc_link.device = &si_dev; + 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; /* * Initialize fields used by the MI code */ - sc->ncr.sci_data = ®s->sci.sci_data; - sc->ncr.sci_icmd = ®s->sci.sci_icmd; - sc->ncr.sci_mode = ®s->sci.sci_mode; - sc->ncr.sci_tcmd = ®s->sci.sci_tcmd; - sc->ncr.sci_bus_csr = ®s->sci.sci_bus_csr; - sc->ncr.sci_csr = ®s->sci.sci_csr; - sc->ncr.sci_idata = ®s->sci.sci_idata; - sc->ncr.sci_iack = ®s->sci.sci_iack; + ncr_sc->sci_r0 = ®s->sci.sci_r0; + ncr_sc->sci_r1 = ®s->sci.sci_r1; + ncr_sc->sci_r2 = ®s->sci.sci_r2; + ncr_sc->sci_r3 = ®s->sci.sci_r3; + ncr_sc->sci_r4 = ®s->sci.sci_r4; + ncr_sc->sci_r5 = ®s->sci.sci_r5; + ncr_sc->sci_r6 = ®s->sci.sci_r6; + ncr_sc->sci_r7 = ®s->sci.sci_r7; /* * MD function pointers used by the MI code. */ - sc->ncr.sc_pio_out = ncr5380_pio_out; - sc->ncr.sc_pio_in = ncr5380_pio_in; - sc->ncr.sc_dma_alloc = si_dma_alloc; - sc->ncr.sc_dma_free = si_dma_free; - sc->ncr.sc_dma_poll = si_dma_poll; + ncr_sc->sc_pio_out = ncr5380_pio_out; + ncr_sc->sc_pio_in = ncr5380_pio_in; + ncr_sc->sc_dma_alloc = si_dma_alloc; + ncr_sc->sc_dma_free = si_dma_free; + ncr_sc->sc_dma_poll = si_dma_poll; + ncr_sc->sc_intr_on = NULL; + ncr_sc->sc_intr_off = NULL; if (ca->ca_bustype == BUS_VME16) { - sc->ncr.sc_dma_start = si_vme_dma_start; - sc->ncr.sc_dma_eop = si_vme_dma_stop; - sc->ncr.sc_dma_stop = si_vme_dma_stop; + ncr_sc->sc_dma_setup = si_vme_dma_setup; + ncr_sc->sc_dma_start = si_vme_dma_start; + ncr_sc->sc_dma_eop = si_vme_dma_stop; + ncr_sc->sc_dma_stop = si_vme_dma_stop; + if (si_options & SI_DO_RESELECT) { + /* + * Need to enable interrupts (and DMA!) + * on this H/W for reselect to work. + */ + ncr_sc->sc_intr_on = si_vme_intr_on; + ncr_sc->sc_intr_off = si_vme_intr_off; + } } else { - sc->ncr.sc_dma_start = si_obio_dma_start; - sc->ncr.sc_dma_eop = si_obio_dma_stop; - sc->ncr.sc_dma_stop = si_obio_dma_stop; + ncr_sc->sc_dma_setup = si_obio_dma_setup; + ncr_sc->sc_dma_start = si_obio_dma_start; + ncr_sc->sc_dma_eop = si_obio_dma_stop; + ncr_sc->sc_dma_stop = si_obio_dma_stop; } - sc->ncr.sc_flags = (si_permit_reselect) ? - NCR5380_PERMIT_RESELECT : 0; - sc->ncr.sc_min_dma_len = MIN_DMA_LEN; + ncr_sc->sc_flags = 0; + if (si_options & SI_DO_RESELECT) + ncr_sc->sc_flags |= NCR5380_PERMIT_RESELECT; + if ((si_options & SI_DMA_INTR) == 0) + ncr_sc->sc_flags |= NCR5380_FORCE_POLLING; + ncr_sc->sc_min_dma_len = MIN_DMA_LEN; /* * Initialize fields used only here in the MD code. */ - sc->sc_regs = regs; - sc->sc_adapter_type = ca->ca_bustype; - /* sc_adapter_iv_am = (was set above) */ - sc->sc_timo = 0; /* no timeout armed. */ /* Need DVMA-capable memory for the UDC command blocks. */ i = SCI_OPENINGS * sizeof(struct si_dma_handle); @@ -335,51 +372,86 @@ si_attach(parent, self, args) for (i = 0; i < SCI_OPENINGS; i++) sc->sc_dma[i].dh_flags = 0; + sc->sc_regs = regs; + sc->sc_adapter_type = ca->ca_bustype; + + /* Now ready for interrupts. */ + if (ca->ca_bustype == BUS_OBIO) { + isr_add_autovect(si_intr, (void *)sc, + ca->ca_intpri); + } else { + isr_add_vectored(si_intr, (void *)sc, + ca->ca_intpri, ca->ca_intvec); + sc->sc_adapter_iv_am = + VME_SUPV_DATA_24 | (ca->ca_intvec & 0xFF); + } + #ifdef DEBUG if (si_debug) - printf("Set TheSoftC=%x TheRegs=%x\n", sc, regs); - TheSoftC = sc; - TheRegs = regs; - sc->ncr.sc_link.flags |= si_flags; + printf("si: Set TheSoftC=%x TheRegs=%x\n", sc, regs); + ncr_sc->sc_link.flags |= si_link_flags; #endif /* * Initialize si board itself. */ - si_reset_adapter(&sc->ncr); - ncr5380_init(&sc->ncr); - ncr5380_reset_scsibus(&sc->ncr); - config_found(self, &(sc->ncr.sc_link), si_print); + si_reset_adapter(ncr_sc); + ncr5380_init(ncr_sc); + ncr5380_reset_scsibus(ncr_sc); + config_found(self, &(ncr_sc->sc_link), si_print); } static void si_minphys(struct buf *bp) { if (bp->b_bcount > MAX_DMA_LEN) { - printf("si_minphys len = %x.\n", 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; } return (minphys(bp)); } +#define CSR_WANT (SI_CSR_SBC_IP | SI_CSR_DMA_IP | \ + SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR ) + static int si_intr(void *arg) { struct si_softc *sc = arg; volatile struct si_regs *si = sc->sc_regs; - int claimed, rv = 0; + int dma_error, claimed; + u_short csr; + + claimed = 0; + dma_error = 0; + + /* SBC interrupt? DMA interrupt? */ + csr = si->si_csr; + NCR_TRACE("si_intr: csr=0x%x\n", csr); - /* DMA interrupt? */ - if (si->si_csr & SI_CSR_DMA_IP) { - rv |= SI_CSR_DMA_IP; - (*sc->ncr.sc_dma_stop)(&sc->ncr); + if (csr & SI_CSR_DMA_CONFLICT) { + dma_error |= SI_CSR_DMA_CONFLICT; + printf("si_intr: DMA conflict\n"); + } + if (csr & SI_CSR_DMA_BUS_ERR) { + dma_error |= SI_CSR_DMA_BUS_ERR; + printf("si_intr: DMA bus error\n"); + } + 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; } - /* SBC interrupt? */ - if (si->si_csr & SI_CSR_SBC_IP) { - rv |= SI_CSR_SBC_IP; - claimed = ncr5380_sbc_intr(&sc->ncr); + if (csr & (SI_CSR_SBC_IP | SI_CSR_DMA_IP)) { + claimed = ncr5380_intr(&sc->ncr_sc); #ifdef DEBUG if (!claimed) { printf("si_intr: spurious from SBC\n"); @@ -390,37 +462,14 @@ si_intr(void *arg) #endif } - return (rv); -} - - -static void -si_dma_timeout(arg) - void *arg; -{ - struct si_softc *sc = arg; - int s; - - s = splbio(); - - sc->sc_timo = 0; - - /* Timeout during DMA transfer? */ - if (sc->ncr.sc_dma_flags & DMA5380_INPROGRESS) { - sc->ncr.sc_dma_flags |= DMA5380_ERROR; - printf("si: DMA timeout (resetting)\n"); - si_reset_adapter(&sc->ncr); - ncr5380_sbc_intr(&sc->ncr); - } - - splx(s); + return (claimed); } static void -si_reset_adapter(struct ncr5380_softc *ncr) +si_reset_adapter(struct ncr5380_softc *ncr_sc) { - struct si_softc *sc = (struct si_softc *)ncr; + struct si_softc *sc = (struct si_softc *)ncr_sc; volatile struct si_regs *si = sc->sc_regs; #ifdef DEBUG @@ -450,7 +499,7 @@ si_reset_adapter(struct ncr5380_softc *ncr) si->fifo_cnt_hi = 0; } - SCI_CLR_INTR(ncr); + SCI_CLR_INTR(ncr_sc); } @@ -464,28 +513,34 @@ si_reset_adapter(struct ncr5380_softc *ncr) * into DVMA space. dvma_mapin() flushes the cache for us. */ void -si_dma_alloc(ncr) - struct ncr5380_softc *ncr; +si_dma_alloc(ncr_sc) + struct ncr5380_softc *ncr_sc; { - struct si_softc *sc = (struct si_softc *)ncr; - volatile struct si_regs *si = sc->sc_regs; + 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; -#if 1 - /* XXX - In case we don't trust interrupts... */ - if (si_polled_dma) - sc->ncr.sc_dma_flags |= DMA5380_POLL; +#ifdef DIAGNOSTIC + if (sr->sr_dma_hand != NULL) + panic("si_dma_alloc: already have DMA handle"); +#endif + +#if 1 /* XXX - Temporary */ + /* XXX - In case we think DMA is completely broken... */ + if ((si_options & SI_ENABLE_DMA) == 0) + return; #endif - addr = (u_long) sc->ncr.sc_dataptr; - xlen = sc->ncr.sc_datalen; + addr = (u_long) ncr_sc->sc_dataptr; + xlen = ncr_sc->sc_datalen; /* If the DMA start addr is misaligned then do PIO */ if ((addr & 1) || (xlen & 1)) { printf("si_dma_alloc: misaligned.\n"); - goto no_dma; + return; } /* Make sure our caller checked sc_min_dma_len. */ @@ -496,11 +551,12 @@ si_dma_alloc(ncr) * 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(); - xlen = MAX_DMA_LEN; + ncr_sc->sc_datalen = xlen = MAX_DMA_LEN; } /* Find free DMA handle. Guaranteed to find one since we have @@ -513,97 +569,87 @@ si_dma_alloc(ncr) found: dh = &sc->sc_dma[i]; - sc->ncr.sc_dma_hand = dh; - dh->dh_addr = addr; - dh->dh_len = xlen; dh->dh_flags = SIDH_BUSY; + dh->dh_addr = (u_char*) addr; + dh->dh_maplen = xlen; + dh->dh_dvma = 0; /* Copy the "write" flag for convenience. */ - if (sc->ncr.sc_dma_flags & DMA5380_WRITE) + if (xs->flags & SCSI_DATA_OUT) dh->dh_flags |= SIDH_OUT; +#if 0 /* - * We don't care about (sc_dma_flags & DMA5380_PHYS) - * because we always have to dup mappings anyway. + * 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 + 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_len); + dh->dh_addr, dh->dh_maplen); dh->dh_flags = 0; - goto no_dma; - } - - /* - * Note: We have to initialize the FIFO logic NOW, - * (just after selection, before talking on the bus) - * because after this point, (much to my surprise) - * writes to the fifo_count register ARE IGNORED!!! - */ - si->fifo_count = 0; /* also hits dma_count */ - if (dh->dh_flags & SIDH_OUT) { - si->si_csr |= SI_CSR_SEND; - } else { - si->si_csr &= ~SI_CSR_SEND; - } - si->si_csr &= ~SI_CSR_FIFO_RES; /* active low */ - delay(10); - si->si_csr |= SI_CSR_FIFO_RES; - delay(10); - si->fifo_count = xlen; - if (sc->sc_adapter_type == BUS_VME16) - si->fifo_cnt_hi = 0; - -#ifdef DEBUG - if ((si->fifo_count > xlen) || (si->fifo_count < (xlen - 1))) { - printf("si_dma_alloc: fifo_count=0x%x, xlen=0x%x\n", - si->fifo_count, xlen); - Debugger(); + return; } -#endif - return; /* success */ + /* success */ + sr->sr_dma_hand = dh; -no_dma: - sc->ncr.sc_dma_hand = NULL; + return; } void -si_dma_free(ncr) - struct ncr5380_softc *ncr; +si_dma_free(ncr_sc) + struct ncr5380_softc *ncr_sc; { - struct si_dma_handle *dh = ncr->sc_dma_hand; + struct sci_req *sr = ncr_sc->sc_current; + struct si_dma_handle *dh = sr->sr_dma_hand; + +#ifdef DIAGNOSTIC + if (dh == NULL) + panic("si_dma_free: no DMA handle"); +#endif - if (ncr->sc_dma_flags & DMA5380_INPROGRESS) + if (ncr_sc->sc_state & NCR_DOINGDMA) panic("si_dma_free: free while in progress"); 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_len); + dvma_mapout((caddr_t)dh->dh_dvma, dh->dh_maplen); dh->dh_dvma = 0; dh->dh_flags = 0; } + sr->sr_dma_hand = NULL; } /* * 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) - struct ncr5380_softc *ncr; +si_dma_poll(ncr_sc) + struct ncr5380_softc *ncr_sc; { - struct si_softc *sc = (struct si_softc *)ncr; + 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; - /* Make sure the DMA actually started... */ - /* XXX - Check DMA5380_INPROGRESS instead? */ - if (sc->ncr.sc_dma_flags & DMA5380_ERROR) + /* Make sure DMA started successfully. */ + if (ncr_sc->sc_state & NCR_ABORTING) return; csr_mask = SI_CSR_SBC_IP | SI_CSR_DMA_IP | @@ -614,9 +660,9 @@ si_dma_poll(ncr) if (si->si_csr & csr_mask) break; if (--tmo <= 0) { - printf("si: DMA timeout\n"); - sc->ncr.sc_dma_flags |= DMA5380_ERROR; - si_reset_adapter(&sc->ncr); + printf("si: DMA timeout (while polling)\n"); + /* Indicate timeout as MI code would. */ + sr->sr_flags |= SR_OVERDUE; break; } delay(100); @@ -635,97 +681,182 @@ si_dma_poll(ncr) ****************************************************************/ +/* + * This is called when the bus is going idle, + * so we want to enable the SBC interrupts. + * That is controlled by the DMA enable! + * Who would have guessed! + * What a NASTY trick! + */ +void +si_vme_intr_on(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + struct si_softc *sc = (struct si_softc *)ncr_sc; + volatile struct si_regs *si = sc->sc_regs; + + si_vme_dma_setup(ncr_sc); + si->si_csr |= SI_CSR_DMA_EN; +} + +/* + * This is called when the bus is idle and we are + * about to start playing with the SBC chip. + */ +void +si_vme_intr_off(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + struct si_softc *sc = (struct si_softc *)ncr_sc; + volatile struct si_regs *si = sc->sc_regs; + + si->si_csr &= ~SI_CSR_DMA_EN; +} + +/* + * This function is called during the COMMAND or MSG_IN phase + * that preceeds a DATA_IN or DATA_OUT phase, in case we need + * to setup the DMA engine before the bus enters a DATA phase. + * + * XXX: The VME adapter appears to suppress SBC interrupts + * when the FIFO is not empty or the FIFO count is non-zero! + * + * On the VME version we just clear the DMA count and address + * here (to make sure it stays idle) and do the real setup + * later, in dma_start. + */ +void +si_vme_dma_setup(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + struct si_softc *sc = (struct si_softc *)ncr_sc; + volatile struct si_regs *si = sc->sc_regs; + + /* Reset the FIFO */ + si->si_csr &= ~SI_CSR_FIFO_RES; /* active low */ + si->si_csr |= SI_CSR_FIFO_RES; + + /* Set direction (assume recv here) */ + si->si_csr &= ~SI_CSR_SEND; + /* Assume worst alignment */ + si->si_csr |= SI_CSR_BPCON; + + si->dma_addrh = 0; + si->dma_addrl = 0; + + si->dma_counth = 0; + si->dma_countl = 0; + + /* Clear FIFO counter. (also hits dma_count) */ + si->fifo_cnt_hi = 0; + si->fifo_count = 0; +} + + void -si_vme_dma_start(ncr) - struct ncr5380_softc *ncr; +si_vme_dma_start(ncr_sc) + struct ncr5380_softc *ncr_sc; { - struct si_softc *sc = (struct si_softc *)ncr; - struct si_dma_handle *dh = sc->ncr.sc_dma_hand; + 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; - u_long data_pa; + long data_pa; + int xlen; /* * Get the DVMA mapping for this segment. * XXX - Should separate allocation and mapin. */ - data_pa = dvma_kvtopa((long)dh->dh_dvma, sc->sc_adapter_type) + - ((u_long)sc->ncr.sc_dataptr - dh->dh_addr); + data_pa = dvma_kvtopa(dh->dh_dvma, sc->sc_adapter_type); + data_pa += (ncr_sc->sc_dataptr - dh->dh_addr); if (data_pa & 1) panic("si_dma_start: bad pa=0x%x", data_pa); + xlen = ncr_sc->sc_datalen; + xlen &= ~1; + sc->sc_xlen = xlen; /* XXX: or less... */ #ifdef DEBUG if (si_debug & 2) { printf("si_dma_start: dh=0x%x, pa=0x%x, xlen=%d\n", - dh, data_pa, dh->dh_len); - } -#endif - - /* Already setup FIFO in si_dma_alloc() */ - -#ifdef DEBUG - if ((si->fifo_count > dh->dh_len) || - (si->fifo_count < (dh->dh_len - 1))) - { - printf("si_dma_start: fifo_count=0x%x, xlen=0x%x\n", - si->fifo_count, dh->dh_len); - Debugger(); + dh, data_pa, xlen); } #endif /* * Set up the DMA controller. - * Note that (dh-dh_len < sc_datalen) */ + si->si_csr &= ~SI_CSR_FIFO_RES; /* active low */ + si->si_csr |= SI_CSR_FIFO_RES; + + /* Set direction (send/recv) */ + if (dh->dh_flags & SIDH_OUT) { + si->si_csr |= SI_CSR_SEND; + } else { + si->si_csr &= ~SI_CSR_SEND; + } + if (data_pa & 2) { si->si_csr |= SI_CSR_BPCON; } else { si->si_csr &= ~SI_CSR_BPCON; } - si->dma_addrh = data_pa >> 16; - si->dma_addrl = data_pa & 0xFFFF; - si->dma_counth = dh->dh_len >> 16; - si->dma_countl = dh->dh_len & 0xFFFF; -#if 0 /* XXX: Whack the FIFO again? */ - si->si_csr &= ~SI_CSR_FIFO_RES; - delay(10); - si->si_csr |= SI_CSR_FIFO_RES; - delay(10); + si->dma_addrh = (ushort)(data_pa >> 16); + si->dma_addrl = (ushort)(data_pa & 0xFFFF); + + si->dma_counth = (ushort)(xlen >> 16); + si->dma_countl = (ushort)(xlen & 0xFFFF); + +#if 1 + /* Set it anyway, even though dma_count hits it? */ + si->fifo_cnt_hi = (ushort)(xlen >> 16); + si->fifo_count = (ushort)(xlen & 0xFFFF); +#endif + +#ifdef DEBUG + if (si->fifo_count != xlen) { + printf("si_dma_start: fifo_count=0x%x, xlen=0x%x\n", + si->fifo_count, xlen); + Debugger(); + } #endif /* - * Put the SBIC into DMA mode and start the transfer. + * Acknowledge the phase change. (After DMA setup!) + * Put the SBIC into DMA mode, and start the transfer. */ - *sc->ncr.sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE); if (dh->dh_flags & SIDH_OUT) { - *sc->ncr.sci_icmd = SCI_ICMD_DATA; - *sc->ncr.sci_dma_send = 0; /* start it */ + *ncr_sc->sci_tcmd = PHASE_DATA_OUT; + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_icmd = SCI_ICMD_DATA; + *ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE); + *ncr_sc->sci_dma_send = 0; /* start it */ } else { - *sc->ncr.sci_icmd = 0; - *sc->ncr.sci_irecv = 0; /* start it */ + *ncr_sc->sci_tcmd = PHASE_DATA_IN; + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_icmd = 0; + *ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE); + *ncr_sc->sci_irecv = 0; /* start it */ } - si->si_csr |= SI_CSR_DMA_EN; /* vme only */ - sc->ncr.sc_dma_flags |= DMA5380_INPROGRESS; + /* Let'er rip! */ + si->si_csr |= SI_CSR_DMA_EN; - if ((sc->ncr.sc_dma_flags & DMA5380_POLL) == 0) { - /* Expect an interrupt when DMA completes. */ - sc->sc_timo = si_dma_intr_timo; - timeout(si_dma_timeout, sc, sc->sc_timo); - } + ncr_sc->sc_state |= NCR_DOINGDMA; #ifdef DEBUG if (si_debug & 2) { printf("si_dma_start: started, flags=0x%x\n", - sc->ncr.sc_dma_flags); + ncr_sc->sc_state); } #endif } void -si_vme_dma_eop(ncr) - struct ncr5380_softc *ncr; +si_vme_dma_eop(ncr_sc) + struct ncr5380_softc *ncr_sc; { /* Not needed - DMA was stopped prior to examining sci_csr */ @@ -733,69 +864,71 @@ si_vme_dma_eop(ncr) void -si_vme_dma_stop(ncr) - struct ncr5380_softc *ncr; +si_vme_dma_stop(ncr_sc) + struct ncr5380_softc *ncr_sc; { - struct si_softc *sc = (struct si_softc *)ncr; - struct si_dma_handle *dh = sc->ncr.sc_dma_hand; + 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 resid, ntrans, si_csr; + int resid, ntrans; - if ((sc->ncr.sc_dma_flags & DMA5380_INPROGRESS) == 0) { + if ((ncr_sc->sc_state & NCR_DOINGDMA) == 0) { #ifdef DEBUG printf("si_dma_stop: dma not running\n"); #endif return; } - sc->ncr.sc_dma_flags &= ~DMA5380_INPROGRESS; - if (sc->sc_timo) { - sc->sc_timo = 0; - untimeout(si_dma_timeout, sc); - } + ncr_sc->sc_state &= ~NCR_DOINGDMA; -#ifdef DEBUG - log_intr(); -#endif + /* First, halt the DMA engine. */ + si->si_csr &= ~SI_CSR_DMA_EN; /* VME only */ - /* First, save csr bits and halt DMA. */ - si_csr = si->si_csr; - si_csr &= ~SI_CSR_DMA_EN; /* VME only */ - si->si_csr = si_csr; - - if (si_csr & (SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR)) { - printf("si: DMA error, csr=0x%x, reset\n", si_csr); - sc->ncr.sc_dma_flags |= DMA5380_ERROR; - si_reset_adapter(&sc->ncr); + if (si->si_csr & (SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR)) { + printf("si: DMA error, csr=0x%x, reset\n", si->si_csr); + sr->sr_xs->error = XS_DRIVER_STUFFUP; + ncr_sc->sc_state |= NCR_ABORTING; + si_reset_adapter(ncr_sc); } /* Note that timeout may have set the error flag. */ - if (sc->ncr.sc_dma_flags & DMA5380_ERROR) + if (ncr_sc->sc_state & NCR_ABORTING) goto out; - resid = si->fifo_count; + /* + * Now try to figure out how much actually transferred + * + * The fifo_count does not reflect how many bytes were + * actually transferred for VME. + * + * SCSI-3 VME interface is a little funny on writes: + * if we have a disconnect, the dma has overshot by + * one byte and needs to be incremented. This is + * true if we have not transferred either all data + * or no data. XXX - from Matt Jacob + */ + + resid = si->fifo_count & 0xFFFF; + ntrans = sc->sc_xlen - resid; + #ifdef DEBUG - if (resid != 0) { - printf("si_dma_stop: fifo resid=%d (ok?)\n", resid); - Debugger(); + if (si_debug & 2) { + printf("si_dma_stop: resid=0x%x ntrans=0x%x\n", + resid, ntrans); } #endif - /* XXX - Was getting (resid==-1), fixed now. */ - if (resid & ~3) { + + if (ntrans < MIN_DMA_LEN) { printf("si: fifo count: 0x%x\n", resid); - sc->ncr.sc_dma_flags |= DMA5380_ERROR; + ncr_sc->sc_state |= NCR_ABORTING; goto out; } + if (ntrans > ncr_sc->sc_datalen) + panic("si_dma_stop: excess transfer"); /* Adjust data pointer */ - ntrans = dh->dh_len - resid; - sc->ncr.sc_dataptr += ntrans; - sc->ncr.sc_datalen -= ntrans; - -#ifdef DEBUG - if (si_debug & 2) { - printf("si_dma_stop: ntrans=0x%x\n", ntrans); - } -#endif + ncr_sc->sc_dataptr += ntrans; + ncr_sc->sc_datalen -= ntrans; /* * After a read, we may need to clean-up @@ -804,7 +937,7 @@ si_vme_dma_stop(ncr) if (((dh->dh_flags & SIDH_OUT) == 0) && ((si->si_csr & SI_CSR_LOB) != 0)) { - char *cp = sc->ncr.sc_dataptr; + char *cp = ncr_sc->sc_dataptr; #ifdef DEBUG printf("si: Got Left-over bytes!\n"); #endif @@ -833,9 +966,15 @@ out: si->dma_addrh = 0; si->dma_addrl = 0; + si->dma_counth = 0; + si->dma_countl = 0; + + si->fifo_cnt_hi = 0; + si->fifo_count = 0; + /* Put SBIC back in PIO mode. */ - *sc->ncr.sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE); - *sc->ncr.sci_icmd = 0; + *ncr_sc->sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE); + *ncr_sc->sci_icmd = 0; } @@ -867,58 +1006,118 @@ si_obio_udc_read(si, regnum) } +/* + * This function is called during the COMMAND or MSG_IN phase + * that preceeds a DATA_IN or DATA_OUT phase, in case we need + * to setup the DMA engine before the bus enters a DATA phase. + * + * The OBIO "si" IGNORES any attempt to set the FIFO count + * register after the SCSI bus goes into any DATA phase, so + * this function has to setup the evil FIFO logic. + */ void -si_obio_dma_start(ncr) - struct ncr5380_softc *ncr; +si_obio_dma_setup(ncr_sc) + struct ncr5380_softc *ncr_sc; { - struct si_softc *sc = (struct si_softc *)ncr; - struct si_dma_handle *dh = sc->ncr.sc_dma_hand; + struct si_softc *sc = (struct si_softc *)ncr_sc; + volatile struct si_regs *si = sc->sc_regs; + struct sci_req *sr; + struct si_dma_handle *dh; + int send = 0; + int xlen = 0; + + /* Let this work even without a dma hand, for testing... */ + if ((sr = ncr_sc->sc_current) != NULL) { + if ((dh = sr->sr_dma_hand) != NULL) { + send = dh->dh_flags & SIDH_OUT; + xlen = ncr_sc->sc_datalen; + xlen &= ~1; + } + } + +#ifdef DEBUG + if (si_debug) { + printf("si_dma_setup: send=%d xlen=%d\n", send, xlen); + } +#endif + + /* Reset the FIFO */ + si->si_csr &= ~SI_CSR_FIFO_RES; /* active low */ + si->si_csr |= SI_CSR_FIFO_RES; + + /* Set direction (send/recv) */ + if (send) { + si->si_csr |= SI_CSR_SEND; + } else { + si->si_csr &= ~SI_CSR_SEND; + } + + /* Set the FIFO counter. */ + si->fifo_count = xlen; + +#ifdef DEBUG + if ((si->fifo_count > xlen) || (si->fifo_count < (xlen - 1))) { + printf("si_dma_setup: fifo_count=0x%x, xlen=0x%x\n", + si->fifo_count, xlen); + Debugger(); + } +#endif +} + + +void +si_obio_dma_start(ncr_sc) + struct ncr5380_softc *ncr_sc; +{ + 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; struct udc_table *cmd; - u_long data_pa, cmd_pa; + long data_pa, cmd_pa; + int xlen; /* * Get the DVMA mapping for this segment. * XXX - Should separate allocation and mapin. */ - data_pa = dvma_kvtopa((long)dh->dh_dvma, sc->sc_adapter_type) + - ((u_long)sc->ncr.sc_dataptr - dh->dh_addr); + data_pa = dvma_kvtopa(dh->dh_dvma, sc->sc_adapter_type); + data_pa += (ncr_sc->sc_dataptr - dh->dh_addr); if (data_pa & 1) panic("si_dma_start: bad pa=0x%x", data_pa); - if (dh->dh_len & 1) - panic("si_dma_start: bad len=0x%x", dh->dh_len); + xlen = ncr_sc->sc_datalen; + xlen &= ~1; + sc->sc_xlen = xlen; /* XXX: or less... */ #ifdef DEBUG if (si_debug & 2) { printf("si_dma_start: dh=0x%x, pa=0x%x, xlen=%d\n", - dh, data_pa, dh->dh_len); + dh, data_pa, xlen); } #endif - /* Already setup FIFO in si_dma_alloc() */ + /* + * Set up the DMA controller. + * Already set FIFO count in dma_setup. + */ #ifdef DEBUG - if ((si->fifo_count > dh->dh_len) || - (si->fifo_count < (dh->dh_len - 1))) + if ((si->fifo_count > xlen) || + (si->fifo_count < (xlen - 1))) { printf("si_dma_start: fifo_count=0x%x, xlen=0x%x\n", - si->fifo_count, dh->dh_len); + si->fifo_count, xlen); Debugger(); } #endif - /* - * Set up the DMA controller. - * Note that (dh-dh_len < sc_datalen) - */ - /* * The OBIO controller needs a command block. */ cmd = &dh->dh_cmd; cmd->addrh = ((data_pa & 0xFF0000) >> 8) | UDC_ADDR_INFO; cmd->addrl = data_pa & 0xFFFF; - cmd->count = dh->dh_len / 2; /* bytes -> words */ + cmd->count = xlen / 2; /* bytes -> words */ cmd->cmrh = UDC_CMR_HIGH; if (dh->dh_flags & SIDH_OUT) { cmd->cmrl = UDC_CMR_LSEND; @@ -945,37 +1144,37 @@ si_obio_dma_start(ncr) si_obio_udc_write(si, UDC_ADR_COMMAND, UDC_CMD_STRT_CHN); /* - * Put the SBIC into DMA mode and start the transfer. + * Acknowledge the phase change. (After DMA setup!) + * Put the SBIC into DMA mode, and start the transfer. */ - *sc->ncr.sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE); if (dh->dh_flags & SIDH_OUT) { - *sc->ncr.sci_icmd = SCI_ICMD_DATA; - *sc->ncr.sci_dma_send = 0; /* start it */ + *ncr_sc->sci_tcmd = PHASE_DATA_OUT; + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_icmd = SCI_ICMD_DATA; + *ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE); + *ncr_sc->sci_dma_send = 0; /* start it */ } else { - *sc->ncr.sci_icmd = 0; - *sc->ncr.sci_irecv = 0; /* start it */ + *ncr_sc->sci_tcmd = PHASE_DATA_IN; + SCI_CLR_INTR(ncr_sc); + *ncr_sc->sci_icmd = 0; + *ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE); + *ncr_sc->sci_irecv = 0; /* start it */ } - sc->ncr.sc_dma_flags |= DMA5380_INPROGRESS; - - if ((sc->ncr.sc_dma_flags & DMA5380_POLL) == 0) { - /* Expect an interrupt when DMA completes. */ - sc->sc_timo = si_dma_intr_timo; - timeout(si_dma_timeout, sc, sc->sc_timo); - } + ncr_sc->sc_state |= NCR_DOINGDMA; #ifdef DEBUG if (si_debug & 2) { printf("si_dma_start: started, flags=0x%x\n", - sc->ncr.sc_dma_flags); + ncr_sc->sc_state); } #endif } void -si_obio_dma_eop(ncr) - struct ncr5380_softc *ncr; +si_obio_dma_eop(ncr_sc) + struct ncr5380_softc *ncr_sc; { /* Not needed - DMA was stopped prior to examining sci_csr */ @@ -983,78 +1182,81 @@ si_obio_dma_eop(ncr) void -si_obio_dma_stop(ncr) - struct ncr5380_softc *ncr; +si_obio_dma_stop(ncr_sc) + struct ncr5380_softc *ncr_sc; { - struct si_softc *sc = (struct si_softc *)ncr; - struct si_dma_handle *dh = sc->ncr.sc_dma_hand; + 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 resid, ntrans, tmo, udc_cnt; - if ((sc->ncr.sc_dma_flags & DMA5380_INPROGRESS) == 0) { + if ((ncr_sc->sc_state & NCR_DOINGDMA) == 0) { #ifdef DEBUG printf("si_dma_stop: dma not running\n"); #endif return; } - sc->ncr.sc_dma_flags &= ~DMA5380_INPROGRESS; - if (sc->sc_timo) { - sc->sc_timo = 0; - untimeout(si_dma_timeout, sc); - } + ncr_sc->sc_state &= ~NCR_DOINGDMA; if (si->si_csr & (SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR)) { printf("si: DMA error, csr=0x%x, reset\n", si->si_csr); - sc->ncr.sc_dma_flags |= DMA5380_ERROR; - si_reset_adapter(&sc->ncr); + sr->sr_xs->error = XS_DRIVER_STUFFUP; + ncr_sc->sc_state |= NCR_ABORTING; + si_reset_adapter(ncr_sc); } /* Note that timeout may have set the error flag. */ - if (sc->ncr.sc_dma_flags & DMA5380_ERROR) + if (ncr_sc->sc_state & NCR_ABORTING) goto out; - /* After a read, wait for the FIFO to empty. */ + /* + * After a read, wait for the FIFO to empty. + * Note: this only works on the OBIO version. + */ if ((dh->dh_flags & SIDH_OUT) == 0) { - /* XXX: This bit is not reliable. Beware! -dej */ tmo = 200000; /* X10 = 2 sec. */ for (;;) { if (si->si_csr & SI_CSR_FIFO_EMPTY) break; if (--tmo <= 0) { printf("si: dma fifo did not empty, reset\n"); - sc->ncr.sc_dma_flags |= DMA5380_ERROR; - si_reset_adapter(&sc->ncr); + ncr_sc->sc_state |= NCR_ABORTING; + /* si_reset_adapter(ncr_sc); */ goto out; } delay(10); } } + /* + * Now try to figure out how much actually transferred + * + * The fifo_count might not reflect how many bytes were + * actually transferred for VME. + */ + + resid = si->fifo_count & 0xFFFF; + ntrans = sc->sc_xlen - resid; - resid = si->fifo_count; #ifdef DEBUG - if (resid != 0) { - printf("si_dma_stop: fifo resid=%d (ok?)\n", resid); - Debugger(); + if (si_debug & 2) { + printf("si_dma_stop: resid=0x%x ntrans=0x%x\n", + resid, ntrans); } #endif - /* XXX - Was getting (resid==-1), fixed now. */ - if (resid & ~3) { + + if (ntrans < MIN_DMA_LEN) { printf("si: fifo count: 0x%x\n", resid); - sc->ncr.sc_dma_flags |= DMA5380_ERROR; + ncr_sc->sc_state |= NCR_ABORTING; goto out; } + if (ntrans > ncr_sc->sc_datalen) + panic("si_dma_stop: excess transfer"); /* Adjust data pointer */ - ntrans = dh->dh_len - resid; - sc->ncr.sc_dataptr += ntrans; - sc->ncr.sc_datalen -= ntrans; - -#ifdef DEBUG - if (si_debug & 2) { - printf("si_dma_stop: ntrans=0x%x\n", ntrans); - } -#endif + ncr_sc->sc_dataptr += ntrans; + ncr_sc->sc_datalen -= ntrans; /* * After a read, we may need to clean-up @@ -1063,16 +1265,16 @@ si_obio_dma_stop(ncr) if ((dh->dh_flags & SIDH_OUT) == 0) { /* If odd transfer count, grab last byte by hand. */ if (ntrans & 1) { - sc->ncr.sc_dataptr[-1] = + ncr_sc->sc_dataptr[-1] = (si->fifo_data & 0xff00) >> 8; goto out; } /* UDC might not have transfered the last word. */ udc_cnt = si_obio_udc_read(si, UDC_ADR_COUNT); - if (((udc_cnt * 2) - si->fifo_count) == 2) { - sc->ncr.sc_dataptr[-2] = + if (((udc_cnt * 2) - resid) == 2) { + ncr_sc->sc_dataptr[-2] = (si->fifo_data & 0xff00) >> 8; - sc->ncr.sc_dataptr[-1] = + ncr_sc->sc_dataptr[-1] = (si->fifo_data & 0x00ff); } } @@ -1080,191 +1282,10 @@ si_obio_dma_stop(ncr) out: /* Reset the UDC. */ si_obio_udc_write(si, UDC_ADR_COMMAND, UDC_CMD_RESET); + si->fifo_count = 0; /* Put SBIC back in PIO mode. */ - *sc->ncr.sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE); - *sc->ncr.sci_icmd = 0; -} - - -#if 0 -int hexdigit(char c) -{ - - if (c >= '0' && c <= '9') return c - '0'; - else return c - ('a' - 10); -} - - -struct scsi_link *thescsilink; - -void sicmdloop(void) -{ -char hexbuf[40]; -int c, i, pos; -u_char cmdbuf[6]; - - while (1) { - pos = 0; - while (1) { - c = cngetc(); - if ((c == 0x7F || c == 0x08) && pos) { - pos--; - cnputc(0x08); - cnputc(' '); - cnputc(0x08); - } - else if (c == '\r' || c == '\n') { - hexbuf[pos] = 0; - break; - } - else { - hexbuf[pos++] = c; - cnputc(c); - } - } - - pos = 0; - for (i = 0; i < 6; i++) { - while (hexbuf[pos] == ' ') pos++; - cmdbuf[i] = 16 * hexdigit(hexbuf[pos++]) + hexdigit(hexbuf[pos++]); - } - - scsi_scsi_cmd(thescsilink, (struct scsi_generic *)cmdbuf, 6, - 0, 0, 1, 1000, 0, SCSI_POLL); - } + *ncr_sc->sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE); + *ncr_sc->sci_icmd = 0; } -#endif - - -#ifdef DEBUG - -#define NQENT 10 -static int Qptr = 0; -static struct qent { - int seq; - int si_csr; - int sci_csr; - int sci_bus_csr; - long dma_addr, dma_count, fifo_len; - long start_addr, start_count, start_len; -} Qents[NQENT]; - -void log_start(void) -{ -struct si_softc *sc = TheSoftC; -volatile struct si_regs *si = sc->sc_regs; - - Qents[Qptr].start_addr = (si->dma_addrh << 16) + si->dma_addrl; - Qents[Qptr].start_count = (si->dma_counth << 16) + si->dma_countl; - Qents[Qptr].start_len = si->fifo_count; - Qents[Qptr].seq = Seq++; -} - - -void log_intr(void) -{ -struct si_softc *sc = TheSoftC; -volatile struct si_regs *si = sc->sc_regs; - - Qents[Qptr].si_csr = si->si_csr; - while (!(si->si_csr & SI_CSR_FIFO_EMPTY)) { - printf("log_intr: FIFO not empty before DMA disable at %d\n", Seq); - } - - if (sc->sc_adapter_type == BUS_VME16) - si->si_csr &= ~SI_CSR_DMA_EN; - if (!(si->si_csr & SI_CSR_FIFO_EMPTY)) { - printf("log_intr: FIFO not empty after DMA disable at %d\n", Seq); - } - Qents[Qptr].dma_addr = (si->dma_addrh << 16) + si->dma_addrl; - - Qents[Qptr].dma_count = (si->dma_counth << 16) + si->dma_countl; - Qents[Qptr].fifo_len = si->fifo_count; - Qents[Qptr].sci_csr = *sc->ncr.sci_csr; - Qents[Qptr].sci_bus_csr = *sc->ncr.sci_bus_csr; - Qptr++; - if (Qptr == NQENT) Qptr = 0; -} - - -void si_printregs(void) -{ -struct si_softc *sc = TheSoftC; -volatile struct si_regs *si = TheRegs; -struct sci_req *sr; -int i; -int a, b, c, d; - - if (!TheRegs) { - printf("TheRegs == NULL, please re-set!\n"); - return; - } - - c = si->si_csr; - printf("si->si_csr=%04x\n", c); - si->si_csr &= ~SI_CSR_DMA_EN; - - a = si->dma_addrh; b = si->dma_addrl; - printf("si->dma_addr=%04x%04x\n", a, b); - - /* printf("si->dma_addr=%04x%04x\n", si->dma_addrh, si->dma_addrl); */ - printf("si->dma_count=%04x%04x\n", si->dma_counth, si->dma_countl); - printf("si->fifo_count=%04x\n", si->fifo_count); - printf("sci_icmd=%02x\n", si->sci.sci_icmd); - printf("sci_mode=%02x\n", si->sci.sci_mode); - printf("sci_tcmd=%02x\n", si->sci.sci_tcmd); - printf("sci_bus_csr=%02x\n", si->sci.sci_bus_csr); - printf("sci_csr=%02x\n", si->sci.sci_csr); - printf("sci_data=%02x\n\n", si->sci.sci_data); - - if (!TheSoftC) { - printf("TheSoftC == NULL, can't continue.\n"); - return; - } - - printf("DMA handles:\n"); - for (i = 0; i < SCI_OPENINGS; i++) { - if (sc->sc_dma[i].dh_flags & SIDH_BUSY) { - printf("%d: %x/%x => %x/%d %s\n", i, - sc->sc_dma[i].dh_addr, - sc->sc_dma[i].dh_len, - sc->sc_dma[i].dh_dvma, - sc->sc_dma[i].dh_flags, - (&sc->sc_dma[i] == sc->ncr.sc_dma_hand) ? "(active)" : ""); - } - else { - printf("%d: idle\n", i); - } - } - - printf("i\nsci_req queue:\n"); - for (i = 0; i < SCI_OPENINGS; i++) { - sr = &sc->ncr.sc_ring[i]; - printf("%d: %d/%d %x/%x => %x\n", i, sr->sr_target, sr->sr_lun, - sr->sr_data, sr->sr_datalen, - sr->sr_dma_hand); - } - printf("Total commands (sc_ncmds): %d\n", sc->ncr.sc_ncmds); - printf("\nCurrent SCSI data pointer: %x/%x\n", sc->ncr.sc_dataptr, - sc->ncr.sc_datalen); -} - -void print_qent(void) -{ -int i; - - i = Qptr; - do { - printf("%d: si_csr=%04x csr=%02x bus_csr=%02x addr=%08x count=%08x fifo=%08x\n", - Qents[i].seq, Qents[i].si_csr, Qents[i].sci_csr, Qents[i].sci_bus_csr, - Qents[i].dma_addr, Qents[i].dma_count, Qents[i].fifo_len); - printf(" from addr=%08x count=%08x fifo=%08x\n", - Qents[i].start_addr, Qents[i].start_count, Qents[i].start_len); - i++; - if (i == NQENT) i = 0; - } while (i != Qptr); -} - -#endif diff --git a/sys/arch/sun3/dev/ncr_sireg.h b/sys/arch/sun3/dev/ncr_sireg.h index d617024b30b..450ed652e3c 100644 --- a/sys/arch/sun3/dev/ncr_sireg.h +++ b/sys/arch/sun3/dev/ncr_sireg.h @@ -1,4 +1,4 @@ -/* $NetBSD: ncr_sireg.h,v 1.1 1995/10/29 21:19:12 gwr Exp $ */ +/* $NetBSD: ncr_sireg.h,v 1.1.2.1 1995/11/18 07:08:55 gwr Exp $ */ /* * Register map for the Sun3 SCSI Interface (si) @@ -22,8 +22,23 @@ * vis versa. */ +/* + * Am5380 Register map (no padding) + */ +struct ncr5380regs { + volatile u_char sci_r0; + volatile u_char sci_r1; + volatile u_char sci_r2; + volatile u_char sci_r3; + volatile u_char sci_r4; + volatile u_char sci_r5; + volatile u_char sci_r6; + volatile u_char sci_r7; +}; + struct si_regs { - sci_regmap_t sci; /* See scsi_5380.h */ + struct ncr5380regs sci; + /* DMA controller registers */ u_short dma_addrh; /* dma address (VME only) */ u_short dma_addrl; /* (high word, low word) */ @@ -80,7 +95,7 @@ struct si_regs { #define SI_CSR_LOB_ONE 0x0040 /* (r,v) one leftover byte */ #define SI_CSR_BPCON 0x0020 /* (rw,v) byte packing control */ /* dma is in 0=longwords, 1=words */ -#define SI_CSR_DMA_EN 0x0010 /* (rw,v) dma enable */ +#define SI_CSR_DMA_EN 0x0010 /* (rw,v) dma/interrupt enable */ #define SI_CSR_SEND 0x0008 /* (rw,b) dma dir, 1=to device */ #define SI_CSR_INTR_EN 0x0004 /* (rw,b) interrupts enable */ #define SI_CSR_FIFO_RES 0x0002 /* (rw,b) inits fifo, 0=reset */