diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-11-01 17:25:05 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-11-01 17:25:05 +0000 |
commit | 3f708d39c7aa23bdb085328c4c818a0417fe65ea (patch) | |
tree | db464dc9ae61ab727c5cc4b84dfb2bfc092009ec | |
parent | e212fbadbc34f7ed1ab354d9bd79e59a2b4b7b35 (diff) |
gwr says:
New SCSI driver for the NCR5380, by David Jones.
Does DMA with interrupts. Much faster than our old
driver which did only PIO transfers. (Thanks David!)
Could be used on the amiga, and probably others...
-rw-r--r-- | sys/arch/sun3/dev/am9516.h | 83 | ||||
-rw-r--r-- | sys/arch/sun3/dev/ncr5380.doc | 146 | ||||
-rw-r--r-- | sys/arch/sun3/dev/ncr5380reg.h | 137 | ||||
-rw-r--r-- | sys/arch/sun3/dev/ncr5380sbc.c | 1969 | ||||
-rw-r--r-- | sys/arch/sun3/dev/ncr5380var.h | 163 | ||||
-rw-r--r-- | sys/arch/sun3/dev/ncr_si.c | 1270 | ||||
-rw-r--r-- | sys/arch/sun3/dev/ncr_sireg.h | 88 |
7 files changed, 3856 insertions, 0 deletions
diff --git a/sys/arch/sun3/dev/am9516.h b/sys/arch/sun3/dev/am9516.h new file mode 100644 index 00000000000..1f6dda3d31b --- /dev/null +++ b/sys/arch/sun3/dev/am9516.h @@ -0,0 +1,83 @@ +/* $NetBSD: am9516.h,v 1.1 1995/10/29 21:19:06 gwr Exp $ */ + +/* + * This file is derived from the file dev/devSCSI3.c from + * the Berkeley SPRITE distribution, which says: + * + * Copyright 1988 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +/* + * AMD 9516 UDC (Universal DMA Controller) Registers. + * This is used only in the OBIO version (3/50,3/60). + */ + +/* addresses of the udc registers accessed directly by driver */ +#define UDC_ADR_MODE 0x38 /* master mode register */ +#define UDC_ADR_COMMAND 0x2e /* command register (write only) */ +#define UDC_ADR_STATUS 0x2e /* status register (read only) */ +#define UDC_ADR_CAR_HIGH 0x26 /* chain addr reg, high word */ +#define UDC_ADR_CAR_LOW 0x22 /* chain addr reg, low word */ +#define UDC_ADR_CARA_HIGH 0x1a /* cur addr reg A, high word */ +#define UDC_ADR_CARA_LOW 0x0a /* cur addr reg A, low word */ +#define UDC_ADR_CARB_HIGH 0x12 /* cur addr reg B, high word */ +#define UDC_ADR_CARB_LOW 0x02 /* cur addr reg B, low word */ +#define UDC_ADR_CMR_HIGH 0x56 /* channel mode reg, high word */ +#define UDC_ADR_CMR_LOW 0x52 /* channel mode reg, low word */ +#define UDC_ADR_COUNT 0x32 /* number of words to transfer */ + +/* + * For a dma transfer, the appropriate udc registers are loaded from a + * table in memory pointed to by the chain address register. + */ +struct udc_table { + u_short rsel; /* tells udc which regs to load */ + u_short addrh; /* high word of main mem dma address */ + u_short addrl; /* low word of main mem dma address */ + u_short count; /* num words to transfer */ + u_short cmrh; /* high word of channel mode reg */ + u_short cmrl; /* low word of channel mode reg */ +}; + +/* indicates which udc registers are to be set based on info in above table */ +#define UDC_RSEL_RECV 0x0182 +#define UDC_RSEL_SEND 0x0282 + +/* setting of chain mode reg: selects how the dma op is to be executed */ +#define UDC_CMR_HIGH 0x0040 /* high word of channel mode reg */ +#define UDC_CMR_LSEND 0x00c2 /* low word of cmr when send */ +#define UDC_CMR_LRECV 0x00d2 /* low word of cmr when receiving */ + +/* setting for the master mode register */ +#define UDC_MODE 0xd /* enables udc chip */ + +/* setting for the low byte in the high word of an address */ +#define UDC_ADDR_INFO 0x40 /* inc addr after each word is dma'd */ + +/* udc commands */ +#define UDC_CMD_STRT_CHN 0xa0 /* start chaining */ +#define UDC_CMD_CIE 0x32 /* channel 1 interrupt enable */ +#define UDC_CMD_RESET 0x00 /* reset udc, same as hdw reset */ + +/* bits in the udc status register */ +#define UDC_SR_CIE 0x8000 /* channel interrupt enable */ +#define UDC_SR_IP 0x2000 /* interrupt pending */ +#define UDC_SR_CA 0x1000 /* channel abort */ +#define UDC_SR_NAC 0x0800 /* no auto reload or chaining*/ +#define UDC_SR_WFB 0x0400 /* waiting for bus */ +#define UDC_SR_SIP 0x0200 /* second interrupt pending */ +#define UDC_SR_HM 0x0040 /* hardware mask */ +#define UDC_SR_HRQ 0x0020 /* hardware request */ +#define UDC_SR_MCH 0x0010 /* match on upper comparator byte */ +#define UDC_SR_MCL 0x0008 /* match on lower comparator byte */ +#define UDC_SR_MC 0x0004 /* match condition ended dma */ +#define UDC_SR_EOP 0x0002 /* eop condition ended dma */ +#define UDC_SR_TC 0x0001 /* termination of count ended dma */ + diff --git a/sys/arch/sun3/dev/ncr5380.doc b/sys/arch/sun3/dev/ncr5380.doc new file mode 100644 index 00000000000..82b8c721b23 --- /dev/null +++ b/sys/arch/sun3/dev/ncr5380.doc @@ -0,0 +1,146 @@ +MI 5380 driver +============== + +(What? Documentation? Is this guy nuts? :-) + +Reselection +----------- + +This driver will permit reselection on non-polled commands if +sc->sc_flags & NCR5380_PERMIT_RESELECT is 1. This permits enabling of +reselection on a per-device basis. + +Disconnect/reselect is never permitted for polled commands. + + + +Interfacing the driver to MD code +--------------------------------- + +/sys/dev/ic/ncr5380.c is now stand-alone. DON'T include it after your +MD stuff! + +This allows for more than one 5380-based SCSI board in your system. This is +a real possibility for Amiga generic kernels. + +Your driver's softc structure must have an instance of struct ncr5380_softc +as the first thing in the structure. The MD code must initialize the +following: + +sci_*: pointers to the 5380 registers. All accesses are done through + these pointers. This indirection allows the driver to work with + boards that map the 5380 on even addresses only or do other + wierdnesses. + +int (*sc_pio_out)(sc, phase, datalen, data) +int (*sc_pio_in)(sc, phase, datalen, data) + These point to functions that do programmed I/O transfers to the bus and + from the bus, respectively. Arguments: + + sc points to the softc + phase the current SCSI bus phase + datalen length of data to transfer + data pointer to the buffer + + Both functions must return the number of bytes successfully transferred. + A transfer operation must be aborted if the target requests a different + phase before the transfer completes. + + If you have no special requirements, you can point these to + ncr5380_pio_out() and ncr5380_pio_in() respectively. If your board + can do pseudo-DMA, then you might want to point these to functions + that use this feature. + +void (*sc_dma_alloc)(sc) + This function is called to set up a DMA transfer. You must create and + return a "DMA handle" in sc->sc_dma_hand which identifies the DMA transfer. + The driver will pass you your DMA handle in sc->sc_dma_hand for future + operations. The contents of the DMA handle are immaterial to the MI + code - the DMA handle is for your bookkeeping only. Usually, you + create a structure and point to it here. + + For example, you can record the mapped and unmapped addresses of the + buffer. The Sun driver places an Am9516 UDC control block in the DMA + handle. + + If for some reason you decide not to do DMA for the transfer, make + sc->sc_dma_hand NULL. This might happen if the proposed transfer is + misaligned, or in the wrong type of memory, or... + +void (*sc_dma_start)(sc) + This function starts the transfer. + +void (*sc_dma_stop)(sc) + This function stops a transfer. sc->sc_datalen and sc->sc_dataptr must + be updated to reflect the portion of the DMA already done. + +void (*sc_dma_eop)(sc) + This function is called when the 5380 signals EOP. Either continue + the DMA or stop the DMA. + +void (*sc_dma_free)(sc) + This function frees the current DMA handle. + +u_char *sc_dataptr; +int sc_datalen; + These variables form the active SCSI data pointer. DMA code must start + DMA at the location given, and update the pointer/length in response to + DMA operations. + +u_short sc_dma_flags; + See ncr5380var.h + + + +Writing your DMA code +--------------------- + +DMA on a system with protected or virtual memory is always a problem. Even +though a disk transfer may be logically contiguous, the physical pages backing +the transfer may not be. There are two common solutions to this problem: + +DMA chains: the DMA is broken up into a list of contiguous segments. The first +segment is submitted to the DMA controller, and when it completes, the second +segment is submitted, without stopping the 5380. This is what the sc_dma_eop() +function can do efficiently - if you have a DMA chain, it can quickly load up +the next link in the chain. The sc_dma_alloc() function builds the chain and +sc_dma_free() releases any resources you used to build it. + +DVMA: Direct Virtual Memory Access. In this scheme, DMA requests go through +the MMU. Although you can't page fault, you can program the MMU to remap +things so the DMA controller sees contiguous data. In this mode, sc_dma_alloc() +is used to map the transfer into the address space reserved for DVMA and +sc_dma_free() is used to unmap it. + + +Interrupts +---------- + +ncr5380_sbc_intr() must be called when the 5380 interrupts the host. + +You must write an interrupt routine pretty much from scratch to check for +things generated by MD hardware. + + +Known problems +-------------- + +I'm getting this out now so that other ports can hack on it and integrate it. + +The sun3, DMA/Interrupt appears to be working now, but needs testing. + +Polled commands submitted while non-polled commands are in progress are not +handled correctly. This can happen if reselection is enabled and a new disk +is mounted while an I/O is in progress on another disk. + +The problem is: what to do if you get reselected while doing the selection +for the polled command? Currently, the driver busy waits for the non-polled +command to complete, but this is bogus. I need to complete the non-polled +command in polled mode, then do the polled command. + + +Timeouts in the driver are EXTREMELY sensitive to the characteristics of the +local implementation of delay(). The Sun3 version delays for a minimum of 5us. +However, the driver must assume that delay(1) will delay only 1us. For this +reason, performance on the Sun3 sucks in some places. + diff --git a/sys/arch/sun3/dev/ncr5380reg.h b/sys/arch/sun3/dev/ncr5380reg.h new file mode 100644 index 00000000000..0901fd1de53 --- /dev/null +++ b/sys/arch/sun3/dev/ncr5380reg.h @@ -0,0 +1,137 @@ +/* $NetBSD: ncr5380reg.h,v 1.1 1995/10/29 21:19:08 gwr Exp $ */ + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ +/* + * HISTORY (mach3) + * Revision 2.3 91/08/24 12:25:10 af + * Moved padding of regmap in impl file. + * [91/08/02 04:22:39 af] + * + * Revision 2.2 91/06/19 16:28:35 rvb + * From the NCR data sheets + * "NCR 5380 Family, SCSI Protocol Controller Data Manual" + * NCR Microelectronics Division, Colorado Spring, 6/98 T01891L + * [91/04/21 af] + * + */ + +/* + * File: scsi_5380.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 5/91 + * + * Defines for the NCR 5380 (SCSI chip), aka Am5380 + */ + +/* + * Register map + */ +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; + + +/* + * Initiator command register + */ +#define SCI_ICMD_DATA 0x01 /* rw: Assert data bus */ +#define SCI_ICMD_ATN 0x02 /* rw: Assert ATN signal */ +#define SCI_ICMD_SEL 0x04 /* rw: Assert SEL signal */ +#define SCI_ICMD_BSY 0x08 /* rw: Assert BSY signal */ +#define SCI_ICMD_ACK 0x10 /* rw: Assert ACK signal */ +#define SCI_ICMD_LST 0x20 /* r: Lost arbitration */ +#define SCI_ICMD_DIFF SCI_ICMD_LST /* w: Differential cable */ +#define SCI_ICMD_AIP 0x40 /* r: Arbitration in progress */ +#define SCI_ICMD_TEST SCI_ICMD_AIP /* w: Test mode */ +#define SCI_ICMD_RST 0x80 /* rw: Assert RST signal */ +/* Bits to keep when doing read/modify/write (leave out RST) */ +#define SCI_ICMD_RMASK 0x1F + + +/* + * Mode register + */ +#define SCI_MODE_ARB 0x01 /* rw: Start arbitration */ +#define SCI_MODE_DMA 0x02 /* rw: Enable DMA xfers */ +#define SCI_MODE_MONBSY 0x04 /* rw: Monitor BSY signal */ +#define SCI_MODE_DMA_IE 0x08 /* rw: Enable DMA complete interrupt */ +#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) */ + + +/* + * Target command register + */ +#define SCI_TCMD_IO 0x01 /* rw: Assert I/O signal */ +#define SCI_TCMD_CD 0x02 /* rw: Assert C/D signal */ +#define SCI_TCMD_MSG 0x04 /* rw: Assert MSG signal */ +#define SCI_TCMD_PHASE_MASK 0x07 /* r: Mask for current bus phase */ +#define SCI_TCMD_REQ 0x08 /* rw: Assert REQ signal */ +#define SCI_TCMD_LAST_SENT 0x80 /* ro: Last byte was xferred + * (not on 5380/1) */ + +#define SCI_PHASE(x) ((x)&0x7) + +/* + * Current (SCSI) Bus status (.sci_bus_csr) + */ +#define SCI_BUS_DBP 0x01 /* r: Data Bus parity */ +#define SCI_BUS_SEL 0x02 /* r: SEL signal */ +#define SCI_BUS_IO 0x04 /* r: I/O signal */ +#define SCI_BUS_CD 0x08 /* r: C/D signal */ +#define SCI_BUS_MSG 0x10 /* r: MSG signal */ +#define SCI_BUS_REQ 0x20 /* r: REQ signal */ +#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) + +/* + * Bus and Status register (.sci_csr) + */ +#define SCI_CSR_ACK 0x01 /* r: ACK signal */ +#define SCI_CSR_ATN 0x02 /* r: ATN signal */ +#define SCI_CSR_DISC 0x04 /* r: Disconnected (BSY==0) */ +#define SCI_CSR_PHASE_MATCH 0x08 /* r: Bus and SCI_TCMD match */ +#define SCI_CSR_INT 0x10 /* r: Interrupt request */ +#define SCI_CSR_PERR 0x20 /* r: Parity error */ +#define SCI_CSR_DREQ 0x40 /* r: DMA request */ +#define SCI_CSR_DONE 0x80 /* r: DMA count is zero */ diff --git a/sys/arch/sun3/dev/ncr5380sbc.c b/sys/arch/sun3/dev/ncr5380sbc.c new file mode 100644 index 00000000000..76e600cfa15 --- /dev/null +++ b/sys/arch/sun3/dev/ncr5380sbc.c @@ -0,0 +1,1969 @@ +/* $NetBSD: ncr5380sbc.c,v 1.1 1995/10/29 21:19:09 gwr Exp $ */ + +/* + * Copyright (c) 1995 David Jones, Gordon W. Ross + * Copyright (c) 1994 Jarle Greipsland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * 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 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is a machine-independent driver for the NCR5380 + * SCSI Bus Controller (SBC), also known as the Am5380. + * + * This code should work with any memory-mapped 5380, + * and can be shared by multiple adapters that address + * the 5380 with different register offset spacings. + * (This can happen on the atari, for example.) + * For porting/design info. see: ncr5380.doc + * + * Credits, history: + * + * David Jones is the author of most of the code that now + * appears in this file, and was the architect of the + * current overall structure (MI/MD code separation, etc.) + * + * 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. + * + * The message in/out code was taken nearly verbatim from + * the aic6360 driver by Jarle Greipsland. + * + * Several other NCR5380 drivers were used for reference + * while developing this driver, including work by: + * The Alice Group (mac68k port) namely: + * Allen K. Briggs, Chris P. Caputo, Michael L. Finch, + * Bradley A. Grantham, and Lawrence A. Kesteloot + * Michael L. Hitch (amiga drivers: sci.c) + * Leo Weppelman (atari driver: ncr5380.c) + * There are others too. Thanks, everyone. + */ + +#include <sys/types.h> +#include <sys/malloc.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_debug.h> +#include <scsi/scsi_message.h> +#include <scsi/scsiconf.h> + +#if 0 /* XXX - not yet... */ +#include <dev/ic/ncr5380reg.h> +#include <dev/ic/ncr5380var.h> +#else +#include "ncr5380reg.h" +#include "ncr5380var.h" +#endif + +static int ncr5380_wait_req __P((struct ncr5380_softc *)); +static int ncr5380_wait_not_req __P((struct ncr5380_softc *)); + +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 *)); +static void ncr5380_reselect __P((struct ncr5380_softc *)); + +static int ncr5380_msg_in __P((struct ncr5380_softc *)); +static int ncr5380_msg_out __P((struct ncr5380_softc *)); +static int ncr5380_data_xfer __P((struct ncr5380_softc *, int)); +static int ncr5380_command __P((struct ncr5380_softc *)); +static int ncr5380_status __P((struct ncr5380_softc *)); +static void ncr5380_machine __P((struct ncr5380_softc *)); + +/* + * Action flags returned by the info_tranfer functions: + * (These determine what happens next.) + */ +#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) */ + +/***************************************************************** + * Debugging stuff + *****************************************************************/ + +#ifdef DDB +int Debugger(); +#else +/* This is used only in recoverable places. */ +#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) + +#else /* DEBUG */ + +#define NCR_BREAK() /* nah */ +#define NCR_PRINT(b, s) + +#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", + "DATA_IN", + "COMMAND", + "STATUS", + "UNSPEC1", + "UNSPEC2", + "MSG_OUT", + "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 + *****************************************************************/ + +/* + * XXX: These timeouts might need to be tuned... + */ + +/* This one is used when waiting for a phase change. (X100uS.) */ +int ncr5380_wait_phase_timo = 1000 * 10 * 300; /* 5 min. */ + +/* These are used in the following inline functions. */ +int ncr5380_wait_req_timo = 1000 * 50; /* X2 = 100 mS. */ +int ncr5380_wait_nrq_timo = 1000 * 25; /* X2 = 50 mS. */ + +/* Return zero on success. */ +static __inline__ int ncr5380_wait_req(sc) + struct ncr5380_softc *sc; +{ + register int timo = ncr5380_wait_req_timo; + for (;;) { + if (*sc->sci_bus_csr & SCI_BUS_REQ) { + timo = 0; /* return 0 */ + break; + } + if (--timo < 0) + break; /* return -1 */ + delay(2); + } + return (timo); +} + +/* Return zero on success. */ +static __inline__ int ncr5380_wait_not_req(sc) + struct ncr5380_softc *sc; +{ + register int timo = ncr5380_wait_nrq_timo; + for (;;) { + if ((*sc->sci_bus_csr & SCI_BUS_REQ) == 0) { + timo = 0; /* return 0 */ + break; + } + if (--timo < 0) + break; /* return -1 */ + delay(2); + } + return (timo); +} + +/* Ask the target for a MSG_OUT phase. */ +static __inline__ void +ncr_sched_msgout(sc, msg_code) + struct ncr5380_softc *sc; + int msg_code; +{ + /* First time, raise ATN line. */ + if (sc->sc_msgpriq == 0) { + register u_char icmd; + icmd = *sc->sci_icmd & SCI_ICMD_RMASK; + *sc->sci_icmd = icmd | SCI_ICMD_ATN; + delay(2); + } + sc->sc_msgpriq |= msg_code; +} + + +int +ncr5380_pio_out(sc, phase, count, data) + struct ncr5380_softc *sc; + int phase, count; + unsigned char *data; +{ + register u_char icmd; + register int resid; + + icmd = *(sc->sci_icmd) & SCI_ICMD_RMASK; + + icmd |= SCI_ICMD_DATA; + *sc->sci_icmd = icmd; + + resid = count; + while (resid > 0) { + if (!SCI_BUSY(sc)) { + NCR_MISC(("ncr5380_pio_out: lost BSY\n")); + break; + } + if (ncr5380_wait_req(sc)) { + NCR_MISC(("ncr5380_pio_out: no REQ\n")); + break; + } + if (SCI_CUR_PHASE(*sc->sci_bus_csr) != phase) + break; + + /* Put the data on the bus. */ + *sc->sci_odata = *data++; + + /* Tell the target it's there. */ + icmd |= SCI_ICMD_ACK; + *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; + } + + /* OK, it's got it. */ + icmd &= ~SCI_ICMD_ACK; + *sc->sci_icmd = icmd; + + --resid; + } + + /* Stop driving the data bus. */ + icmd &= ~SCI_ICMD_DATA; + *sc->sci_icmd = icmd; + + return (count - resid); +} + + +int +ncr5380_pio_in(sc, phase, count, data) + struct ncr5380_softc *sc; + int phase, count; + unsigned char *data; +{ + register u_char icmd; + register int resid; + + icmd = *(sc->sci_icmd) & SCI_ICMD_RMASK; + + resid = count; + while (resid > 0) { + if (!SCI_BUSY(sc)) { + NCR_MISC(("ncr5380_pio_in: lost BSY\n")); + break; + } + if (ncr5380_wait_req(sc)) { + NCR_MISC(("ncr5380_pio_in: no REQ\n")); + break; + } + /* A phase change is not valid until AFTER REQ rises! */ + if (SCI_CUR_PHASE(*sc->sci_bus_csr) != phase) + break; + + /* Read the data bus. */ + *data++ = *sc->sci_data; + + /* Tell target we got it. */ + icmd |= SCI_ICMD_ACK; + *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; + } + + /* OK, we can drop ACK. */ + icmd &= ~SCI_ICMD_ACK; + *sc->sci_icmd = icmd; + + --resid; + } + + return (count - resid); +} + + +void +ncr5380_init(sc) + struct ncr5380_softc *sc; +{ + int i, j; + + 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_prevphase = PHASE_INVALID; + sc->sc_msg_flags = 0; /* XXX */ + + /* XXX: Reselect interrupts... */ + *sc->sci_sel_enb = 0x80; +} + + +void +ncr5380_reset_scsibus(sc) + struct ncr5380_softc *sc; +{ + + NCR_MISC(("ncr5380_reset_scsibus\n")); + + *sc->sci_icmd = SCI_ICMD_RST; + delay(500); + *sc->sci_icmd = 0; + + *sc->sci_mode = 0; + *sc->sci_tcmd = 0; + + SCI_CLR_INTR(sc); + /* XXX - Need long delay here! */ + delay(100000); + + /* XXX - Need to cancel disconnected requests. */ +} + + +/* + * Interrupt handler for the SCSI Bus Controller (SBC) + * This is also called by a timeout handler after it + * raises to the appropriate SPL. + */ +int +ncr5380_sbc_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"); + } + + if (sc->sc_current == NULL) { + /* + * Might be reselect. ncr5380_reselect() will check, and + * set up the connection if so. + */ + ncr5380_reselect(sc); + } + + /* + * The remaining documented interrupt causes are phase mismatch and + * 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. + */ + if (sc->sc_current) { + /* This will usually free-up the nexus. */ + ncr5380_machine(sc); + claimed = 1; + } + + /* Maybe we can start another command now... */ + if (sc->sc_current == NULL) + ncr5380_sched(sc); + + return claimed; +} + + +/***************************************************************** + * Interface to higher level + *****************************************************************/ + + +/* + * Enter a new SCSI command into the "issue" queue, and + * if there is work to do, start it going. + * + * WARNING: This can be called recursively! + * (see comment in ncr5380_done) + */ +int +ncr5380_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct ncr5380_softc *sc = xs->sc_link->adapter_softc; + int s, r, i, flags; + extern int cold; /* XXX */ + + /* thescsilink = xs->sc_link; */ + + flags = xs->flags; + /* + * XXX: Hack: During autoconfig, force polling mode. + * Needed as long as sdsize() can be called while cold, + * otherwise timeouts will never call back (grumble). + */ + if (cold) + 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. */ + for (i = 0; i < SCI_OPENINGS; i++) + if (sc->sc_ring[i].sr_xs == NULL) + break; + + if (i == SCI_OPENINGS) { + splx(s); + return TRY_AGAIN_LATER; + } + + /* 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 */ + sc->sc_ncmds++; + + /* + * 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 (sc->sc_current == NULL) + ncr5380_sched(sc); + + splx(s); + + /* The call to ncr5380_sched() may have finished it. */ + if (xs->flags & ITSDONE) { + return COMPLETE; + } + + if (flags & SCSI_POLL) + panic("ncr5380_scsi_cmd: poll didn't finish"); + + return SUCCESSFULLY_QUEUED; +} + + +/* + * 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) + */ +static void +ncr5380_sched(sc) + struct ncr5380_softc *sc; +{ + struct scsi_xfer *xs; + struct sci_req *sr; + int target, lun; + int error, i; + +#ifdef DIAGNOSTIC + if ((getsr() & PSL_IPL) < PSL_IPL2) + panic("ncr5380_sched: bad spl"); + + if (sc->sc_current) + panic("ncr5380_sched: bus already busy"); +#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. + */ + i = sc->sc_rr; + sr = NULL; + do { + if (sc->sc_ring[i].sr_xs) { + 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_rr = i; + break; + } + } + i++; + if (i == SCI_OPENINGS) + i = 0; + } while (i != sc->sc_rr); + + if (sr == NULL) + return; /* No more work to do. */ + + xs = sr->sr_xs; + error = ncr5380_select(sc, xs); + if (sc->sc_current) { + /* Lost the race! reselected out from under us! */ + if (sr->sr_flags & SR_IMMED) { + printf("%s: reselected while polling (reset)\n", + sc->sc_dev.dv_xname); + error = XS_BUSY; + goto reset; + } + /* Work with the reselected target. */ + sr = sc->sc_current; + xs = sr->sr_xs; + goto have_nexus; + } + sc->sc_current = sr; /* connected */ + + /* Initialize pointers, etc. */ + sc->sc_dataptr = sr->sr_data; + sc->sc_datalen = sr->sr_datalen; + sc->sc_dma_hand = sr->sr_dma_hand; + + 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); + ncr5380_reset_scsibus(sc); + /* fallthrough */ + case XS_SELTIMEOUT: + default: + NCR_MISC(("ncr5380_sched: select error=%d\n", error)); + xs->error = error; + ncr5380_done(sc); + goto next_job; + } + + /* + * Selection was successful. + * We now have a new command. + */ + 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; + 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... + */ + 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 (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; + + 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_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; + } + +finish: + + NCR_MISC(("ncr5380_done: finished target=%d, LUN=%d\n", + sr->sr_target, sr->sr_lun)); + + /* + * Dequeue the finished command + */ + sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL; + sc->sc_ncmds--; + sr->sr_xs = NULL; + + /* + * 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. + */ + xs->flags |= ITSDONE; + scsi_done(xs); + + /* Allow ncr5380_sched() to be called again. */ + sc->sc_current = NULL; /* idle */ +} + + +/* + * Reselect handler: checks for reselection, and if we are being + * reselected, it sets up sc->sc_current. + * + * We are reselected when: + * SEL is TRUE + * IO is TRUE + * BSY is FALSE + */ +void +ncr5380_reselect(sc) + struct ncr5380_softc *sc; +{ + int target, lun, phase, timo; + u_char bus, data, icmd, msg; + +#ifdef DIAGNOSTIC + if (sc->sc_current) + panic("ncr5380_reselect: already have nexus!"); +#endif + + /* + * First, check the select line. + * (That has to be set by now.) + */ + bus = *(sc->sci_bus_csr); + if ((bus & SCI_BUS_SEL) == 0) { + /* Not a selection or reselection. */ + return; + } + + /* + * Now wait for: SEL==1, BSY==0 + * before reading the data bus. + */ + for (timo = 25000;;) { + if ((bus & SCI_BUS_BSY) == 0) + break; + if (--timo <= 0) { + printf("%s: reselect, BSY stuck, bus=0x%x\n", + sc->sc_dev.dv_xname, bus); + /* Not much we can do. Reset the bus. */ + ncr5380_reset_scsibus(sc); + return; + } + delay(10); + bus = *(sc->sci_bus_csr); + /* If SEL went away, forget it. */ + if ((bus & SCI_BUS_SEL) == 0) + return; + /* Still have SEL, check BSY. */ + } + + /* + * Good. We have SEL=1 and BSY=0. Now wait for a + * "bus settle delay" before we sample the data bus + */ + delay(2); + data = *(sc->sci_data) & 0xFF; + /* XXX - Should check parity... */ + + /* + * Is this a reselect (I/O == 1) or have we been + * selected as a target? (I/O == 0) + */ + if ((bus & SCI_BUS_IO) == 0) { + printf("%s: selected as target, data=0x%x\n", + sc->sc_dev.dv_xname, data); + /* Not much we can do. Reset the bus. */ + ncr5380_reset_scsibus(sc); + return; + } + + /* + * OK, this is a reselection. + */ + for (target = 0; target < 7; target++) + if (data & (1 << target)) + break; + + if ((data & 0x7F) != (1 << target)) { + /* No selecting ID? or >2 IDs on bus? */ + printf("%s: bad reselect, data=0x%x\n", + sc->sc_dev.dv_xname, data); + return; + } + + NCR_MISC(("ncr5380_reselect: data=0x%x\n", data)); + + /* Raise BSY to acknowledge target reselection. */ + *(sc->sci_icmd) = SCI_ICMD_BSY; + + /* Wait for target to drop SEL. */ + for (timo = 1000;;) { + 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; + } + delay(5); + } + + /* Now we drop BSY, and we are connected. */ + *(sc->sci_icmd) = 0; + *sc->sci_sel_enb = 0; + SCI_CLR_INTR(sc); + + /* + * At this point the target should send an IDENTIFY message, + * which will permit us to determine the reselecting LUN. + * If not, we assume LUN 0. + */ + lun = 0; + /* Wait for REQ before reading bus phase. */ + if (ncr5380_wait_req(sc)) { + printf("%s: reselect, no REQ\n", + sc->sc_dev.dv_xname); + /* Try to send an ABORT message. */ + goto abort; + } + phase = SCI_CUR_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; + + /* Peek at the message byte without consuming it! */ + msg = *(sc->sci_data); + if ((msg & 0x80) == 0) { + printf("%s: reselect, not identify, msg=%d\n", + sc->sc_dev.dv_xname, msg); + goto abort; + } + 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) { + /* 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; + } + + printf("%s: phantom reselect: target=%d, LUN=%d\n", + sc->sc_dev.dv_xname, target, lun); +abort: + /* + * Try to send an ABORT message. + * Raise ATN, then raise ACK... + */ + icmd = SCI_ICMD_ATN; + *sc->sci_icmd = icmd; + delay(2); + + /* Now consume the IDENTIFY message. */ + ncr5380_pio_in(sc, PHASE_MSG_IN, 1, &msg); + + /* Finally try to send the ABORT. */ + sc->sc_prevphase = PHASE_MSG_IN; + sc->sc_msgpriq = SEND_ABORT; + *(sc->sci_tcmd) = PHASE_MSG_OUT; + ncr5380_msg_out(sc); + + *sc->sci_sel_enb = 0x80; +} + + +/* + * Select target: xs is the transfer that we are selecting for. + * sc->sc_current should be NULL. + * + * Returns: + * sc->sc_current != NULL ==> we were reselected (race!) + * XS_NOERROR ==> selection worked + * XS_BUSY ==> lost arbitration + * XS_SELTIMEOUT ==> no response to selection + */ +static int +ncr5380_select(sc, xs) + struct ncr5380_softc *sc; + struct scsi_xfer *xs; +{ + 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 */ + } + + /* + * Set phase bits to 0, otherwise the 5380 won't drive the bus during + * selection. + */ + *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 + * time-critical bus interactions. We set our ID bit + * in the output data register and set MODE_ARB. The + * 5380 watches for the required "bus free" period. + * If and when the "bus free" period is detected, the + * 5380 then drives BSY, drives the data bus, and sets + * the "arbitration in progress" (AIP) bit to let us + * know arbitration has started. We then wait for one + * arbitration delay (2.2uS) and check the ICMD_LST bit, + * which will be set if someone else drives SEL. + */ + *(sc->sci_odata) = 0x80; /* OUR_ID */ + *(sc->sci_mode) = SCI_MODE_ARB; + + /* Wait for ICMD_AIP. */ + for (timo = 50000;;) { + 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; + return XS_BUSY; + } + delay(2); + } + /* 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; + return XS_BUSY; + } + + /* + * No other device has declared itself the winner. + * The spec. says to check for higher IDs, but we + * are always the highest (ID=7) so don't bother. + * We can now declare victory by asserting SEL. + * + * Note that the 5380 is asserting BSY because we + * asked it to do arbitration. We will now hold + * BSY directly so we can turn off ARB mode. + */ + icmd = (SCI_ICMD_BSY | SCI_ICMD_SEL); + *sc->sci_icmd = icmd; + + /* + * "The SCSI device that wins arbitration shall wait + * at least a bus clear delay plus a bus settle delay + * after asserting the SEL signal before changing + * any [other] signal." (1.2uS. total) + */ + delay(2); + +#if 1 + /* + * XXX: Check one last time to see if we really + * 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; + return XS_BUSY; + } +#endif + /* Leave ARB mode Now that we drive BSY+SEL */ + *sc->sci_mode = 0; + *sc->sci_sel_enb = 0; + + /* + * Arbitration is complete. Now do selection: + * Drive the data bus with the ID bits for both + * 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; + icmd |= (SCI_ICMD_DATA | SCI_ICMD_ATN); + *(sc->sci_icmd) = icmd; + delay(2); /* two deskew delays. */ + + /* De-assert BSY (targets sample the data now). */ + icmd &= ~SCI_ICMD_BSY; + *(sc->sci_icmd) = icmd; + delay(3); /* Bus settle delay. */ + + /* + * Wait for the target to assert BSY. + * SCSI spec. says wait for 250 mS. + */ + for (timo = 25000;;) { + if (*sc->sci_bus_csr & SCI_BUS_BSY) + goto success; + if (--timo <= 0) + break; + delay(10); + } + + /* + * There is no reaction from the target. Start the selection + * timeout procedure. We release the databus but keep SEL+ATN + * asserted. After that we wait a 'selection abort time' (200 + * usecs) and 2 deskew delays (90 ns) and check BSY again. + * When BSY is asserted, we assume the selection succeeded, + * otherwise we release the bus. + */ + icmd &= ~SCI_ICMD_DATA; + *(sc->sci_icmd) = icmd; + delay(201); + if ((*sc->sci_bus_csr & SCI_BUS_BSY) == 0) { + /* Really no device on bus */ + *sc->sci_icmd = 0; + *sc->sci_mode = 0; + *sc->sci_sel_enb = 0x80; + NCR_MISC(("ncr5380_select: device down\n")); + return XS_SELTIMEOUT; + } + +success: + /* + * The target is now driving BSY, so we can stop + * driving SEL and the data bus (keep ATN true). + * Configure the ncr5380 to monitor BSY, parity. + */ + icmd &= ~(SCI_ICMD_DATA | SCI_ICMD_SEL); + *sc->sci_icmd = icmd; + + /* XXX - Make parity checking optional? */ + *sc->sci_mode = (SCI_MODE_MONBSY | SCI_MODE_PAR_CHK); + + NCR_MISC(("ncr5380_select: success\n")); + return XS_NOERROR; +} + + +/***************************************************************** + * Functions to handle each info. transfer phase: + *****************************************************************/ + +/* + * The message system: + * + * This is a revamped message system that now should easier accomodate + * new messages, if necessary. + * + * Currently we accept these messages: + * IDENTIFY (when reselecting) + * COMMAND COMPLETE # (expect bus free after messages marked #) + * NOOP + * MESSAGE REJECT + * SYNCHRONOUS DATA TRANSFER REQUEST + * SAVE DATA POINTER + * RESTORE POINTERS + * DISCONNECT # + * + * We may send these messages in prioritized order: + * BUS DEVICE RESET # if SCSI_RESET & xs->flags (or in weird sits.) + * MESSAGE PARITY ERROR par. err. during MSGI + * MESSAGE REJECT If we get a message we don't know how to handle + * ABORT # send on errors + * INITIATOR DETECTED ERROR also on errors (SCSI2) (during info xfer) + * IDENTIFY At the start of each transfer + * SYNCHRONOUS DATA TRANSFER REQUEST if appropriate + * NOOP if nothing else fits the bill ... + */ + +#define IS1BYTEMSG(m) (((m) != 0x01 && (m) < 0x20) || (m) >= 0x80) +#define IS2BYTEMSG(m) (((m) & 0xf0) == 0x20) +#define ISEXTMSG(m) ((m) == 0x01) + +/* + * Precondition: + * The SCSI bus is already in the MSGI phase and there is a message byte + * on the bus, along with an asserted REQ signal. + * + * Our return value determines whether our caller, ncr5380_machine() + * will expect to see another REQ (and possibly phase change). + */ +static int +ncr5380_msg_in(sc) + register struct ncr5380_softc *sc; +{ + struct sci_req *sr = sc->sc_current; + int n, phase, timo; + int act_flags; + register u_char icmd; + + 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; + goto nextbyte; + } + + /* This is a new MESSAGE IN phase. Clean up our state. */ + sc->sc_msg_flags &= ~NCR_DROP_MSGIN; + +nextmsg: + n = 0; + sc->sc_imp = &sc->sc_imess[n]; + +nextbyte: + /* + * Read a whole message, but don't ack the last byte. If we reject the + * message, we have to assert ATN during the message transfer phase + * itself. + */ + for (;;) { + /* + * Read a message byte. + * First, check BSY, REQ, phase... + */ + if (!SCI_BUSY(sc)) { + NCR_MISC(("ncr5380_msg_in: lost BSY\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; + return (act_flags); + } + phase = SCI_CUR_PHASE(*sc->sci_bus_csr); + if (phase != PHASE_MSG_IN) { + /* + * Target left MESSAGE IN, probably because it + * a) noticed our ATN signal, or + * b) ran out of messages. + */ + return (act_flags); + } + /* 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; + } + + /* Gather incoming message bytes if needed. */ + if ((sc->sc_msg_flags & NCR_DROP_MSGIN) == 0) { + if (n >= NCR_MAX_MSG_LEN) { + ncr_sched_msgout(sc, SEND_REJECT); + sc->sc_msg_flags |= NCR_DROP_MSGIN; + } else { + *sc->sc_imp++ = *sc->sci_data; + n++; + /* + * This testing is suboptimal, but most + * messages will be of the one byte variety, so + * it should not affect performance + * significantly. + */ + if (n == 1 && IS1BYTEMSG(sc->sc_imess[0])) + goto have_msg; + if (n == 2 && IS2BYTEMSG(sc->sc_imess[0])) + goto have_msg; + if (n >= 3 && ISEXTMSG(sc->sc_imess[0]) && + n == sc->sc_imess[1] + 2) + goto have_msg; + } + } + + /* + * If we reach this spot we're either: + * a) in the middle of a multi-byte message, or + * b) dropping bytes. + */ + + /* Ack the last byte read. */ + icmd |= SCI_ICMD_ACK; + *sc->sci_icmd = icmd; + + if (ncr5380_wait_not_req(sc)) { + NCR_MISC(("ncr5380_msg_in: stuck REQ\n")); + act_flags |= ACT_RESET_BUS; + return (act_flags); + } + + icmd &= ~SCI_ICMD_ACK; + *sc->sci_icmd = icmd; + /* 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; + } + /* 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; */ + /* Target is about to disconnect. */ + NCR_MISC(("ncr5380_msg_in: disconnect\n")); + act_flags |= ACT_DISCONNECT; + break; + + case MSG_PARITY_ERROR: + /* 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")); + switch (sc->sc_msgout) { + case SEND_IDENTIFY: + /* Really old target controller? */ + /* XXX ... */ + break; + case SEND_INIT_DET_ERR: + goto abort; + } + break; + + case MSG_NOOP: + break; + + case MSG_SAVEDATAPOINTER: + sr->sr_data = 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; + sc->sc_datalen = sr->sr_datalen; + sc->sc_dma_hand = sr->sr_dma_hand; + break; + + case MSG_EXTENDED: + switch (sc->sc_imess[2]) { + case MSG_EXT_SDTR: + case MSG_EXT_WDTR: + /* The ncr5380 can not do synchronous mode. */ + goto reject; + default: + printf("%s: unrecognized MESSAGE EXTENDED; sending REJECT\n", + sc->sc_dev.dv_xname); + NCR_BREAK(); + goto reject; + } + break; + + default: + printf("%s: unrecognized MESSAGE; sending REJECT\n", + sc->sc_dev.dv_xname); + NCR_BREAK(); + /* fallthrough */ + reject: + 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; + ncr_sched_msgout(sc, SEND_ABORT); + break; + } + + /* Ack the last byte read. */ + icmd |= SCI_ICMD_ACK; + *sc->sci_icmd = icmd; + + if (ncr5380_wait_not_req(sc)) { + NCR_MISC(("ncr5380_msg_in: stuck REQ\n")); + act_flags |= ACT_RESET_BUS; + } + + icmd &= ~SCI_ICMD_ACK; + *sc->sci_icmd = icmd; + + /* Go get the next message, if any. */ + if (act_flags == ACT_CONTINUE) + goto nextmsg; + + return (act_flags); +} + + +/* + * The message out (and in) stuff is a bit complicated: + * If the target requests another message (sequence) without + * having changed phase in between it really asks for a + * retransmit, probably due to parity error(s). + * The following messages can be sent: + * IDENTIFY @ These 4 stem from SCSI command activity + * SDTR @ + * WDTR @ + * DEV_RESET @ + * REJECT if MSGI doesn't make sense + * PARITY_ERROR if parity error while in MSGI + * INIT_DET_ERR if parity error while not in MSGI + * ABORT if INIT_DET_ERR rejected + * NOOP if asked for a message and there's nothing to send + * + * Note that we call this one with (sc_current == NULL) + * when sending ABORT for unwanted reselections. + */ +static int +ncr5380_msg_out(sc) + register struct ncr5380_softc *sc; +{ + struct sci_req *sr = sc->sc_current; + int n, phase, resel; + int progress, act_flags; + register u_char icmd; + + NCR_TRACE(("ncr_msgout ")); + + 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 |= (SCI_ICMD_ATN | SCI_ICMD_DATA); + *sc->sci_icmd = icmd; + + if (sc->sc_prevphase == PHASE_MSG_OUT) { + if (sc->sc_omp == sc->sc_omess) { + /* + * This is a retransmission. + * + * We get here if the target stayed in MESSAGE OUT + * phase. Section 5.1.9.2 of the SCSI 2 spec indicates + * that all of the previously transmitted messages must + * be sent again, in the same order. Therefore, we + * requeue all the previously transmitted messages, and + * 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; + } else { + /* This is a continuation of the previous message. */ + n = sc->sc_omp - sc->sc_omess; + goto nextbyte; + } + } + + /* No messages transmitted so far. */ + sc->sc_msgoutq = 0; + +nextmsg: + /* Pick up highest priority message. */ + sc->sc_msgout = sc->sc_msgpriq & -sc->sc_msgpriq; + sc->sc_msgpriq &= ~sc->sc_msgout; + sc->sc_msgoutq |= sc->sc_msgout; + + /* Build the outgoing message data. */ + switch (sc->sc_msgout) { + case SEND_IDENTIFY: + /* (sc->sc_state != NCR_CONNECTED) */ + if (sr == NULL) { + printf("%s: SEND_IDENTIFY while not connected; sending NOOP\n", + sc->sc_dev.dv_xname); + NCR_BREAK(); + goto noop; + } + resel = (sc->sc_flags & NCR5380_PERMIT_RESELECT) ? 1 : 0; + resel &= (sr->sr_flags & (SR_IMMED | SR_SENSE)) ? 0 : 1; + sc->sc_omess[0] = MSG_IDENTIFY(sr->sr_lun, resel); + n = 1; + break; + + case SEND_DEV_RESET: + /* Expect disconnect after this! */ + /* XXX: Kill jobs for this target? */ + act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE); + sc->sc_omess[0] = MSG_BUS_DEV_RESET; + n = 1; + break; + + case SEND_REJECT: + sc->sc_omess[0] = MSG_MESSAGE_REJECT; + n = 1; + break; + + case SEND_PARITY_ERROR: + sc->sc_omess[0] = MSG_PARITY_ERROR; + n = 1; + break; + + case SEND_INIT_DET_ERR: + sc->sc_omess[0] = MSG_INITIATOR_DET_ERR; + n = 1; + break; + + case SEND_ABORT: + /* Expect disconnect after this! */ + /* XXX: Set error flag? */ + act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE); + sc->sc_omess[0] = MSG_ABORT; + n = 1; + break; + + case 0: + printf("%s: unexpected MESSAGE OUT; sending NOOP\n", + sc->sc_dev.dv_xname); + NCR_BREAK(); + noop: + sc->sc_omess[0] = MSG_NOOP; + n = 1; + break; + + default: + printf("%s: weird MESSAGE OUT; sending NOOP\n", + sc->sc_dev.dv_xname); + NCR_BREAK(); + goto noop; + } + sc->sc_omp = &sc->sc_omess[n]; + +nextbyte: + /* Send message bytes. */ + while (n > 0) { + /* + * Send a message byte. + * First check BSY, REQ, phase... + */ + if (!SCI_BUSY(sc)) { + NCR_MISC(("ncr5380_msg_out: lost BSY\n")); + goto out; + } + if (ncr5380_wait_req(sc)) { + NCR_MISC(("ncr5380_msg_out: no REQ\n")); + goto out; + } + phase = SCI_CUR_PHASE(*sc->sci_bus_csr); + if (phase != PHASE_MSG_OUT) { + /* + * Target left MESSAGE OUT, possibly to reject + * our message. + */ + goto out; + } + + /* Yes, we can send this message byte. */ + --n; + + /* Clear ATN before last byte if this is the last message. */ + if (n == 0 && sc->sc_msgpriq == 0) { + icmd &= ~SCI_ICMD_ATN; + *sc->sci_icmd = icmd; + /* 2 deskew delays */ + delay(2); /* XXX */ + } + + /* Put data on the bus. */ + *sc->sci_odata = *--sc->sc_omp; + + /* Raise ACK to tell target data is on the bus. */ + icmd |= SCI_ICMD_ACK; + *sc->sci_icmd = icmd; + + /* Wait for REQ to be negated. */ + if (ncr5380_wait_not_req(sc)) { + NCR_MISC(("ncr5380_msg_out: stuck REQ\n")); + act_flags |= ACT_RESET_BUS; + goto out; + } + + /* Finally, drop ACK. */ + icmd &= ~SCI_ICMD_ACK; + *sc->sci_icmd = icmd; + } + progress++; + + /* We get here only if the entire message has been transmitted. */ + if ((sc->sc_msgpriq != 0) && (act_flags == ACT_CONTINUE)) { + /* There are more outgoing messages. */ + goto nextmsg; + } + + /* + * The last message has been transmitted. We need to remember the last + * message transmitted (in case the target switches to MESSAGE IN phase + * and sends a MESSAGE REJECT), and the list of messages transmitted + * this time around (in case the target stays in MESSAGE OUT phase to + * request a retransmit). + */ + +out: + /* Stop driving the data bus. */ + icmd &= ~SCI_ICMD_DATA; + *sc->sci_icmd = icmd; + + if (!progress) + act_flags |= ACT_RESET_BUS; + + return (act_flags); +} + + +/* + * Handle command phase. + */ +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; + + if (sr->sr_flags & SR_SENSE) { + rqs.opcode = REQUEST_SENSE; + rqs.byte2 = xs->sc_link->lun << 5; + rqs.length = sizeof(xs->sense); + + rqs.unused[0] = rqs.unused[1] = rqs.control = 0; + len = ncr5380_pio_out(sc, PHASE_CMD, 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, + (u_char *)xs->cmd); + } + + if (len != xs->cmdlen) { + printf("ncr5380_command: short transfer: wanted %d got %d.\n", + xs->cmdlen, len); + NCR_BREAK(); + if (len < 6) + return ACT_RESET_BUS; + } + + return ACT_CONTINUE; +} + + +/* + * Handle either data_in or data_out + */ +static int +ncr5380_data_xfer(sc, phase) + struct ncr5380_softc *sc; + int phase; +{ + struct sci_req *sr = sc->sc_current; + struct scsi_xfer *xs = sr->sr_xs; + 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) { + if (phase != PHASE_DATA_IN) { + printf("%s: sense phase error\n", sc->sc_dev.dv_xname); + return (ACT_ABORT_CMD); + } + NCR_MISC(("ncr5380_data_xfer: reading sense data\n")); + len = ncr5380_pio_in(sc, phase, sizeof(xs->sense), + (u_char *)&xs->sense); + return ACT_CONTINUE; /* 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); + } + + /* 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); + } + 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 && + (sc->sc_datalen >= sc->sc_min_dma_len)) + { + /* OK, really do DMA. */ + (*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; + } + + NCR_MISC(("ncr5380_data_xfer: doing PIO, len=%d\n", sc->sc_datalen)); + if (phase == PHASE_DATA_OUT) { + len = ncr5380_pio_out(sc, phase, sc->sc_datalen, sc->sc_dataptr); + } else { + len = ncr5380_pio_in (sc, phase, sc->sc_datalen, sc->sc_dataptr); + } + sc->sc_dataptr += len; + sc->sc_datalen -= len; + + NCR_MISC(("ncr5380_data_xfer: did PIO, resid=%d\n", sc->sc_datalen)); + return (ACT_CONTINUE); +} + + +static int +ncr5380_status(sc) + struct ncr5380_softc *sc; +{ + int len; + u_char status; + struct sci_req *sr = sc->sc_current; + struct scsi_xfer *xs = sr->sr_xs; + + 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). + * + * 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; + +#ifdef DIAGNOSTIC + if ((getsr() & PSL_IPL) < PSL_IPL2) + panic("ncr5380_machine: bad spl"); +#endif + + 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. + */ + 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 (!SCI_BUSY(sc)) { + /* Unexpected disconnect */ + printf("ncr5380_machine: unexpected disconnect.\n"); + xs->error = XS_DRIVER_STUFFUP; + act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE); + goto do_actions; + } + + /* + * Wait for REQ before reading the phase. + * Need to wait longer than usual here, because + * some devices are just plain slow... + */ + timo = ncr5380_wait_phase_timo; + for (;;) { + 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; + } + delay(100); + } + + phase = SCI_CUR_PHASE(*sc->sci_bus_csr); + NCR_MISC(("ncr5380_machine: phase=%s\n", + phase_names[phase & 7])); + + /* + * We make the assumption that the device knows what it's + * doing, so any phase is good. + */ + *sc->sci_tcmd = phase; /* acknowledge */ + + switch (phase) { + + case PHASE_DATA_OUT: + case PHASE_DATA_IN: + act_flags = ncr5380_data_xfer(sc, phase); + break; + + case PHASE_CMD: + act_flags = ncr5380_command(sc); + break; + + case PHASE_STATUS: + act_flags = ncr5380_status(sc); + break; + + case PHASE_MSG_OUT: + act_flags = ncr5380_msg_out(sc); + break; + + case PHASE_MSG_IN: + act_flags = ncr5380_msg_in(sc); + break; + + default: + printf("ncr5380_machine: Unexpected phase 0x%x\n", phase); + act_flags = ACT_ABORT_CMD; + break; + + } /* 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_INTR) { + /* Wait for DMA complete interrupt (or timeout). */ + NCR_MISC(("ncr5380_machine: wait for dma\n")); + return; + } + + if (act_flags & ACT_RESET_BUS) { + /* + * Reset the SCSI bus, usually due to a timeout. + * The error code XS_TIMEOUT allows retries. + */ + NCR_MISC(("ncr5380_machine: reset scsi bus\n")); + 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) { + /* Need to call scsi_done() */ + xs->resid = sc->sc_datalen; + /* Note: this will clear sc_current */ + ncr5380_done(sc); + act_flags |= ACT_DISCONNECT; + } + + if (act_flags & ACT_DISCONNECT) { + /* + * The device has dropped BSY (or will soon). + * Return and let ncr5380_sched() do its thing. + */ + *sc->sci_icmd = 0; + *sc->sci_mode = 0; + *sc->sci_tcmd = 0; + SCI_CLR_INTR(sc); + + *sc->sci_sel_enb = 0x80; + + /* + * 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. + */ + sc->sc_current = NULL; + return; + } +} diff --git a/sys/arch/sun3/dev/ncr5380var.h b/sys/arch/sun3/dev/ncr5380var.h new file mode 100644 index 00000000000..1c27eccd0f3 --- /dev/null +++ b/sys/arch/sun3/dev/ncr5380var.h @@ -0,0 +1,163 @@ +/* $NetBSD: ncr5380var.h,v 1.1 1995/10/29 21:19:10 gwr Exp $ */ + +/* + * Copyright (c) 1995 David Jones, Gordon W. Ross + * Copyright (c) 1994 Jarle Greipsland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * 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 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file defines the interface between the machine-dependent + * module and the machine-indepenedent ncr5380sbc.c module. + */ + +#define SCI_CLR_INTR(sc) (*(sc)->sci_iack) +#define SCI_BUSY(sc) (*sc->sci_bus_csr & SCI_BUS_BSY) + +#define PHASE_DATA_OUT 0x0 +#define PHASE_DATA_IN 0x1 +#define PHASE_CMD 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 + +#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 */ + 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 */ + int sr_status; /* Status code from last cmd */ +}; +#define SCI_OPENINGS 4 /* Up to 4 commands at once */ + + +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; + + /* Functions set from MD code */ + int (*sc_pio_out) __P((struct ncr5380_softc *, + int, int, u_char *)); + int (*sc_pio_in) __P((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_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 *)); + + int sc_flags; /* Misc. flags and capabilities */ +#define NCR5380_PERMIT_RESELECT 1 /* Allow disconnect/reselect */ + + int sc_min_dma_len; /* Smaller than this is done with PIO */ + + /* Begin MI shared data */ + + /* Active data pointer for current SCSI process */ + 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; + + /* Ring buffer of pending/active requests */ + struct sci_req sc_ring[SCI_OPENINGS]; + int sc_rr; /* Round-robin scan pointer */ + + /* Active requests, by target/LUN */ + struct sci_req *sc_matrix[8][8]; + + /* 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 */ +#define SEND_DEV_RESET 0x01 +#define SEND_PARITY_ERROR 0x02 +#define SEND_ABORT 0x04 +#define SEND_REJECT 0x08 +#define SEND_INIT_DET_ERR 0x10 +#define SEND_IDENTIFY 0x20 +#define SEND_SDTR 0x40 +#define SEND_WDTR 0x80 +#define NCR_MAX_MSG_LEN 8 + u_char sc_omess[NCR_MAX_MSG_LEN]; + u_char *sc_omp; /* Outgoing message pointer */ + u_char sc_imess[NCR_MAX_MSG_LEN]; + u_char *sc_imp; /* Incoming message pointer */ + +}; + +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_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 *)); + diff --git a/sys/arch/sun3/dev/ncr_si.c b/sys/arch/sun3/dev/ncr_si.c new file mode 100644 index 00000000000..951e37340d6 --- /dev/null +++ b/sys/arch/sun3/dev/ncr_si.c @@ -0,0 +1,1270 @@ +/* $NetBSD: ncr_si.c,v 1.1 1995/10/29 21:19:11 gwr Exp $ */ + +/* + * Copyright (c) 1995 David Jones + * Copyright (c) 1994 Adam Glass, Gordon W. Ross + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * 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 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * 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 + */ + +#include <sys/types.h> +#include <sys/malloc.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_debug.h> +#include <scsi/scsiconf.h> + +#include <machine/autoconf.h> +#include <machine/isr.h> +#include <machine/obio.h> +#include <machine/dvma.h> + +#if 0 /* XXX - not yet... */ +#include <dev/ic/ncr5380reg.h> +#include <dev/ic/ncr5380var.h> +#else +#include "ncr5380reg.h" +#include "ncr5380var.h" +#endif + +#include "ncr_sireg.h" +#include "am9516.h" + +/* + * Transfers smaller than this are done using PIO + * (on assumption they're not worth DMA overhead) + */ +#define MIN_DMA_LEN 128 + +/* + * Transfers lager than 63K need to be broken up + * because some of the device counters are 16 bits. + */ +#define MAX_DMA_LEN 0xFC00 + +/* + * How many uS. to delay after touching the am9616 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); +#endif + +/* + * This structure is used to keep track of mapped DMA requests. + * Note: combined the UDC command block with this structure, so + * 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) */ + /* DMA command block for the OBIO controller. */ + struct udc_table dh_cmd; +}; + +/* + * The first structure member has to be the ncr5380_softc + * so we can just cast to go back and fourth between them. + */ +struct si_softc { + struct ncr5380_softc ncr; + 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; +}; + +/* + * 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 */ + +/* How long to wait for DMA before declaring an error. */ +int si_dma_intr_timo = 500; /* ticks (sec. X 100) */ + +static char si_name[] = "si"; +static int si_match(); +static void si_attach(); +static int si_intr(void *arg); +static void si_reset_adapter(struct ncr5380_softc *sc); +static void si_minphys(struct buf *bp); + +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_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_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 *)); + + +static struct scsi_adapter si_ops = { + ncr5380_scsi_cmd, /* scsi_cmd() */ + si_minphys, /* scsi_minphys() */ + NULL, /* open_target_lu() */ + NULL, /* close_target_lu() */ +}; + +/* This is copied from julian's bt driver */ +/* "so we have a default dev struct for our link struct." */ +static struct scsi_device si_dev = { + NULL, /* Use default error handler. */ + NULL, /* Use default start handler. */ + NULL, /* Use default async handler. */ + NULL, /* Use default "done" routine. */ +}; + + +struct cfdriver ncr_sicd = { + NULL, si_name, si_match, si_attach, DV_DULL, + sizeof(struct si_softc), NULL, 0, +}; + +static int +si_print(aux, name) + void *aux; + char *name; +{ + if (name != NULL) + printf("%s: scsibus ", name); + return UNCONF; +} + +static int +si_match(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + struct cfdata *cf = vcf; + struct confargs *ca = args; + int x, probe_addr; + + /* Default interrupt priority always splbio==2 */ + if (ca->ca_intpri == -1) + ca->ca_intpri = 2; + + if ((cpu_machine_id == SUN3_MACH_50) || + (cpu_machine_id == SUN3_MACH_60) ) + { + /* Sun3/50 or Sun3/60 have only OBIO "si" */ + if (ca->ca_bustype != BUS_OBIO) + return(0); + if (ca->ca_paddr == -1) + ca->ca_paddr = OBIO_NCR_SCSI; + /* OK... */ + } else { + /* Other Sun3 models may have VME "si" or "sc" */ + if (ca->ca_bustype != BUS_VME16) + return (0); + if (ca->ca_paddr == -1) + return (0); + /* OK... */ + } + + /* Make sure there is something there... */ + x = bus_peek(ca->ca_bustype, ca->ca_paddr + 1, 1); + if (x == -1) + return (0); + + /* + * If this is a VME SCSI board, we have to determine whether + * it is an "sc" (Sun2) or "si" (Sun3) SCSI board. This can + * be determined using the fact that the "sc" board occupies + * 4K bytes in VME space but the "si" board occupies 2K bytes. + */ + if (ca->ca_bustype == BUS_VME16) { + /* Note, the "si" board should NOT respond here. */ + x = bus_peek(ca->ca_bustype, ca->ca_paddr + 0x801, 1); + if (x != -1) + return(0); + } + + return (1); +} + +static void +si_attach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct si_softc *sc = (struct si_softc *) self; + volatile struct si_regs *regs; + struct confargs *ca = args; + int i; + + switch (ca->ca_bustype) { + + 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: + printf("unknown\n"); + return; + } + printf("\n"); + + /* + * 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; + + /* + * 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; + + /* + * 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; + 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; + } 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; + } + sc->ncr.sc_flags = (si_permit_reselect) ? + NCR5380_PERMIT_RESELECT : 0; + sc->ncr.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); + sc->sc_dma = (struct si_dma_handle *) dvma_malloc(i); + if (sc->sc_dma == NULL) + panic("si: dvma_malloc failed\n"); + for (i = 0; i < SCI_OPENINGS; i++) + sc->sc_dma[i].dh_flags = 0; + +#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; +#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); +} + +static void +si_minphys(struct buf *bp) +{ + if (bp->b_bcount > MAX_DMA_LEN) { + printf("si_minphys len = %x.\n", MAX_DMA_LEN); + bp->b_bcount = MAX_DMA_LEN; + } + return (minphys(bp)); +} + + +static int +si_intr(void *arg) +{ + struct si_softc *sc = arg; + volatile struct si_regs *si = sc->sc_regs; + int claimed, rv = 0; + + /* DMA interrupt? */ + if (si->si_csr & SI_CSR_DMA_IP) { + rv |= SI_CSR_DMA_IP; + (*sc->ncr.sc_dma_stop)(&sc->ncr); + } + + /* SBC interrupt? */ + if (si->si_csr & SI_CSR_SBC_IP) { + rv |= SI_CSR_SBC_IP; + claimed = ncr5380_sbc_intr(&sc->ncr); +#ifdef DEBUG + if (!claimed) { + printf("si_intr: spurious from SBC\n"); + if (si_debug & 4) { + Debugger(); /* XXX */ + } + } +#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); +} + + +static void +si_reset_adapter(struct ncr5380_softc *ncr) +{ + struct si_softc *sc = (struct si_softc *)ncr; + volatile struct si_regs *si = sc->sc_regs; + +#ifdef DEBUG + if (si_debug) { + printf("si_reset_adapter\n"); + } +#endif + + /* + * The SCSI3 controller has an 8K FIFO to buffer data between the + * 5380 and the DMA. Make sure it starts out empty. + * + * The reset bits in the CSR are active low. + */ + si->si_csr = 0; + delay(10); + si->si_csr = SI_CSR_FIFO_RES | SI_CSR_SCSI_RES | SI_CSR_INTR_EN; + delay(10); + si->fifo_count = 0; + + if (sc->sc_adapter_type == BUS_VME16) { + si->dma_addrh = 0; + si->dma_addrl = 0; + si->dma_counth = 0; + si->dma_countl = 0; + si->si_iv_am = sc->sc_adapter_iv_am; + si->fifo_cnt_hi = 0; + } + + SCI_CLR_INTR(ncr); +} + + +/***************************************************************** + * Common functions for DMA + ****************************************************************/ + +/* + * Allocate a DMA handle and put it in sc->sc_dma. Prepare + * for DMA transfer. On the Sun3, this means mapping the buffer + * into DVMA space. dvma_mapin() flushes the cache for us. + */ +void +si_dma_alloc(ncr) + struct ncr5380_softc *ncr; +{ + struct si_softc *sc = (struct si_softc *)ncr; + volatile struct si_regs *si = sc->sc_regs; + 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; +#endif + + addr = (u_long) sc->ncr.sc_dataptr; + xlen = sc->ncr.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; + } + + /* Make sure our caller checked sc_min_dma_len. */ + if (xlen < MIN_DMA_LEN) + panic("si_dma_alloc: xlen=0x%x\n", xlen); + + /* + * 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(). + */ + if (xlen > MAX_DMA_LEN) { + printf("si_dma_alloc: excessive xlen=0x%x\n", xlen); + Debugger(); + xlen = MAX_DMA_LEN; + } + + /* Find free DMA handle. Guaranteed to find one since we have + as many DMA handles as the driver has processes. */ + for (i = 0; i < SCI_OPENINGS; i++) { + if ((sc->sc_dma[i].dh_flags & SIDH_BUSY) == 0) + goto found; + } + panic("si: no free DMA handles."); +found: + + dh = &sc->sc_dma[i]; + sc->ncr.sc_dma_hand = dh; + dh->dh_addr = addr; + dh->dh_len = xlen; + dh->dh_flags = SIDH_BUSY; + + /* Copy the "write" flag for convenience. */ + if (sc->ncr.sc_dma_flags & DMA5380_WRITE) + dh->dh_flags |= SIDH_OUT; + + /* + * We don't care about (sc_dma_flags & DMA5380_PHYS) + * because we always have to dup mappings anyway. + */ + 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_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(); + } +#endif + + return; /* success */ + +no_dma: + sc->ncr.sc_dma_hand = NULL; +} + + +void +si_dma_free(ncr) + struct ncr5380_softc *ncr; +{ + struct si_dma_handle *dh = ncr->sc_dma_hand; + + if (ncr->sc_dma_flags & DMA5380_INPROGRESS) + 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); + dh->dh_dvma = 0; + dh->dh_flags = 0; + } +} + + +/* + * Poll (spin-wait) for DMA completion. + * Same for either VME or OBIO. + */ +void +si_dma_poll(ncr) + struct ncr5380_softc *ncr; +{ + struct si_softc *sc = (struct si_softc *)ncr; + 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) + return; + + csr_mask = SI_CSR_SBC_IP | SI_CSR_DMA_IP | + SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR; + + tmo = 50000; /* X100 = 5 sec. */ + for (;;) { + if (si->si_csr & csr_mask) + break; + if (--tmo <= 0) { + printf("si: DMA timeout\n"); + sc->ncr.sc_dma_flags |= DMA5380_ERROR; + si_reset_adapter(&sc->ncr); + break; + } + delay(100); + } + +#ifdef DEBUG + if (si_debug) { + printf("si_dma_poll: done, csr=0x%x\n", si->si_csr); + } +#endif +} + + +/***************************************************************** + * VME functions for DMA + ****************************************************************/ + + +void +si_vme_dma_start(ncr) + struct ncr5380_softc *ncr; +{ + struct si_softc *sc = (struct si_softc *)ncr; + struct si_dma_handle *dh = sc->ncr.sc_dma_hand; + volatile struct si_regs *si = sc->sc_regs; + u_long data_pa; + + /* + * 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); + if (data_pa & 1) + panic("si_dma_start: bad pa=0x%x", data_pa); + +#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(); + } +#endif + + /* + * Set up the DMA controller. + * Note that (dh-dh_len < sc_datalen) + */ + 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); +#endif + + /* + * 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 */ + } else { + *sc->ncr.sci_icmd = 0; + *sc->ncr.sci_irecv = 0; /* start it */ + } + si->si_csr |= SI_CSR_DMA_EN; /* vme only */ + + 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); + } + +#ifdef DEBUG + if (si_debug & 2) { + printf("si_dma_start: started, flags=0x%x\n", + sc->ncr.sc_dma_flags); + } +#endif +} + + +void +si_vme_dma_eop(ncr) + struct ncr5380_softc *ncr; +{ + + /* Not needed - DMA was stopped prior to examining sci_csr */ +} + + +void +si_vme_dma_stop(ncr) + struct ncr5380_softc *ncr; +{ + struct si_softc *sc = (struct si_softc *)ncr; + struct si_dma_handle *dh = sc->ncr.sc_dma_hand; + volatile struct si_regs *si = sc->sc_regs; + int resid, ntrans, si_csr; + + if ((sc->ncr.sc_dma_flags & DMA5380_INPROGRESS) == 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); + } + +#ifdef DEBUG + log_intr(); +#endif + + /* 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); + } + + /* Note that timeout may have set the error flag. */ + if (sc->ncr.sc_dma_flags & DMA5380_ERROR) + goto out; + + resid = si->fifo_count; +#ifdef DEBUG + if (resid != 0) { + printf("si_dma_stop: fifo resid=%d (ok?)\n", resid); + Debugger(); + } +#endif + /* XXX - Was getting (resid==-1), fixed now. */ + if (resid & ~3) { + printf("si: fifo count: 0x%x\n", resid); + sc->ncr.sc_dma_flags |= DMA5380_ERROR; + goto out; + } + + /* 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 + + /* + * After a read, we may need to clean-up + * "Left-over bytes" (yuck!) + */ + if (((dh->dh_flags & SIDH_OUT) == 0) && + ((si->si_csr & SI_CSR_LOB) != 0)) + { + char *cp = sc->ncr.sc_dataptr; +#ifdef DEBUG + printf("si: Got Left-over bytes!\n"); +#endif + if (si->si_csr & SI_CSR_BPCON) { + /* have SI_CSR_BPCON */ + cp[-1] = (si->si_bprl & 0xff00) >> 8; + } else { + switch (si->si_csr & SI_CSR_LOB) { + case SI_CSR_LOB_THREE: + cp[-3] = (si->si_bprh & 0xff00) >> 8; + cp[-2] = (si->si_bprh & 0x00ff); + cp[-1] = (si->si_bprl & 0xff00) >> 8; + break; + case SI_CSR_LOB_TWO: + cp[-2] = (si->si_bprh & 0xff00) >> 8; + cp[-1] = (si->si_bprh & 0x00ff); + break; + case SI_CSR_LOB_ONE: + cp[-1] = (si->si_bprh & 0xff00) >> 8; + break; + } + } + } + +out: + si->dma_addrh = 0; + si->dma_addrl = 0; + + /* Put SBIC back in PIO mode. */ + *sc->ncr.sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE); + *sc->ncr.sci_icmd = 0; +} + + +/***************************************************************** + * OBIO functions for DMA + ****************************************************************/ + + +static __inline__ void +si_obio_udc_write(si, regnum, value) + volatile struct si_regs *si; + int regnum, value; +{ + delay(UDC_WAIT_USEC); + si->udc_addr = regnum; + delay(UDC_WAIT_USEC); + si->udc_data = value; +} + +static __inline__ int +si_obio_udc_read(si, regnum) + volatile struct si_regs *si; + int regnum; +{ + delay(UDC_WAIT_USEC); + si->udc_addr = regnum; + delay(UDC_WAIT_USEC); + return (si->udc_data); +} + + +void +si_obio_dma_start(ncr) + struct ncr5380_softc *ncr; +{ + struct si_softc *sc = (struct si_softc *)ncr; + struct si_dma_handle *dh = sc->ncr.sc_dma_hand; + volatile struct si_regs *si = sc->sc_regs; + struct udc_table *cmd; + u_long data_pa, cmd_pa; + + /* + * 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); + 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); + +#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(); + } +#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->cmrh = UDC_CMR_HIGH; + if (dh->dh_flags & SIDH_OUT) { + cmd->cmrl = UDC_CMR_LSEND; + cmd->rsel = UDC_RSEL_SEND; + } else { + cmd->cmrl = UDC_CMR_LRECV; + cmd->rsel = UDC_RSEL_RECV; + } + + /* Tell the DMA chip where the control block is. */ + cmd_pa = dvma_kvtopa((long)cmd, BUS_OBIO); + si_obio_udc_write(si, UDC_ADR_CAR_HIGH, + (cmd_pa & 0xff0000) >> 8); + si_obio_udc_write(si, UDC_ADR_CAR_LOW, + (cmd_pa & 0xffff)); + + /* Tell the chip to be a DMA master. */ + si_obio_udc_write(si, UDC_ADR_MODE, UDC_MODE); + + /* Tell the chip to interrupt on error. */ + si_obio_udc_write(si, UDC_ADR_COMMAND, UDC_CMD_CIE); + + /* Finally, give the UDC a "start chain" command. */ + si_obio_udc_write(si, UDC_ADR_COMMAND, UDC_CMD_STRT_CHN); + + /* + * 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 */ + } else { + *sc->ncr.sci_icmd = 0; + *sc->ncr.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); + } + +#ifdef DEBUG + if (si_debug & 2) { + printf("si_dma_start: started, flags=0x%x\n", + sc->ncr.sc_dma_flags); + } +#endif +} + + +void +si_obio_dma_eop(ncr) + struct ncr5380_softc *ncr; +{ + + /* Not needed - DMA was stopped prior to examining sci_csr */ +} + + +void +si_obio_dma_stop(ncr) + struct ncr5380_softc *ncr; +{ + struct si_softc *sc = (struct si_softc *)ncr; + struct si_dma_handle *dh = sc->ncr.sc_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) { +#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); + } + + 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); + } + + /* Note that timeout may have set the error flag. */ + if (sc->ncr.sc_dma_flags & DMA5380_ERROR) + goto out; + + /* After a read, wait for the FIFO to empty. */ + 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); + goto out; + } + delay(10); + } + } + + + resid = si->fifo_count; +#ifdef DEBUG + if (resid != 0) { + printf("si_dma_stop: fifo resid=%d (ok?)\n", resid); + Debugger(); + } +#endif + /* XXX - Was getting (resid==-1), fixed now. */ + if (resid & ~3) { + printf("si: fifo count: 0x%x\n", resid); + sc->ncr.sc_dma_flags |= DMA5380_ERROR; + goto out; + } + + /* 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 + + /* + * After a read, we may need to clean-up + * "Left-over bytes" (yuck!) + */ + if ((dh->dh_flags & SIDH_OUT) == 0) { + /* If odd transfer count, grab last byte by hand. */ + if (ntrans & 1) { + sc->ncr.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] = + (si->fifo_data & 0xff00) >> 8; + sc->ncr.sc_dataptr[-1] = + (si->fifo_data & 0x00ff); + } + } + +out: + /* Reset the UDC. */ + si_obio_udc_write(si, UDC_ADR_COMMAND, UDC_CMD_RESET); + + /* 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); + } +} +#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 new file mode 100644 index 00000000000..d617024b30b --- /dev/null +++ b/sys/arch/sun3/dev/ncr_sireg.h @@ -0,0 +1,88 @@ +/* $NetBSD: ncr_sireg.h,v 1.1 1995/10/29 21:19:12 gwr Exp $ */ + +/* + * Register map for the Sun3 SCSI Interface (si) + * The first part of this register map is an NCR5380 + * SCSI Bus Interface Controller (SBIC). The rest is a + * DMA controller and custom logic in one of two flavors, + * one for the OBIO interface (3/50,3/60) and one for the + * VME interface (3/160,3/260,etc.), where some registers + * are implemented only on one or the other, some on both. + */ + +/* + * Some of these registers apply to only one interface and some + * apply to both. The registers which apply to the Sun3/50 onboard + * version only are udc_rdata and udc_raddr. The registers which + * apply to the Sun3 vme version only are dma_addr, dma_count, bpr, + * iv_am, and bcrh. Thus, the sbc registers, fifo_data, bcr, and csr + * apply to both interfaces. + * One other feature of the vme interface: a write to the dma count + * register also causes a write to the fifo byte count register and + * vis versa. + */ + +struct si_regs { + sci_regmap_t sci; /* See scsi_5380.h */ + /* DMA controller registers */ + u_short dma_addrh; /* dma address (VME only) */ + u_short dma_addrl; /* (high word, low word) */ + u_short dma_counth; /* dma count (VME only) */ + u_short dma_countl; /* (high word, low word) */ + + /* AMD 9516 regs (OBIO only) see am9516.h */ + u_short udc_data; /* Am9516, reg data (OBIO only) */ + u_short udc_addr; /* Am9516, reg addr (OBIO only) */ + + /* These three registers are on both OBIO and VME versions. */ + u_short fifo_data; /* fifo data register */ + /* holds extra byte on odd */ + /* byte dma read */ + u_short fifo_count; /* fifo byte count */ + u_short si_csr; /* control/status register */ + + /* The rest of these are on the VME interface only: */ + u_short si_bprh; /* byte pack, high (VME only) */ + u_short si_bprl; /* byte pack, low (VME only) */ + u_short si_iv_am; /* bits 0-7: intr vector */ + /* bits 8-13: addr modifier (VME only) */ + /* bits 14-15: unused */ + u_short fifo_cnt_hi; /* high part of fifo_count (VME only) */ + + /* Whole thing repeats after 32 bytes. */ + u_short _space[3]; +}; + +/* possible values for the address modifier, sun3 vme version only */ +#define VME_SUPV_DATA_24 0x3d00 + +/* + * Status Register. + * Note: + * (r) indicates bit is read only. + * (rw) indicates bit is read or write. + * (v) vme host adaptor interface only. + * (o) sun3/50 onboard host adaptor interface only. + * (b) both vme and sun3/50 host adaptor interfaces. + */ +#define SI_CSR_DMA_ACTIVE 0x8000 /* (r,o) dma transfer active */ +#define SI_CSR_DMA_CONFLICT 0x4000 /* (r,b) reg accessed while dmaing */ +#define SI_CSR_DMA_BUS_ERR 0x2000 /* (r,b) bus error during dma */ +#define SI_CSR_ID 0x1000 /* (r,b) 0 for 3/50, 1 for SCSI-3, */ + /* 0 if SCSI-3 unmodified */ +#define SI_CSR_FIFO_FULL 0x0800 /* (r,b) fifo full */ +#define SI_CSR_FIFO_EMPTY 0x0400 /* (r,b) fifo empty */ +#define SI_CSR_SBC_IP 0x0200 /* (r,b) sbc interrupt pending */ +#define SI_CSR_DMA_IP 0x0100 /* (r,b) dma interrupt pending */ +#define SI_CSR_LOB 0x00c0 /* (r,v) number of leftover bytes */ +#define SI_CSR_LOB_THREE 0x00c0 /* (r,v) three leftover bytes */ +#define SI_CSR_LOB_TWO 0x0080 /* (r,v) two leftover bytes */ +#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_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 */ +#define SI_CSR_SCSI_RES 0x0001 /* (rw,b) reset sbc and udc, 0=reset */ + |