802 lines
23 KiB
C
802 lines
23 KiB
C
/* Copyright (C) 1991, 1992 by Always Technology Corporation.
|
|
This module contains information proprietary to
|
|
Always Technology Corporation, and is be treated as confidential.
|
|
*/
|
|
|
|
#include "environ.h"
|
|
#include "rqm.h"
|
|
#include "api.h"
|
|
#include "apiscsi.h"
|
|
#include "debug.h"
|
|
|
|
#include "33c93.h"
|
|
#include "in2000.h"
|
|
|
|
#define StatMask 0xc1 // Bits of interest in FIFO status register
|
|
|
|
#define FIFOThresh 16 // Minimum xfer lenth for FIFOed xfers
|
|
#define FIFOPad 32 // How many bytes must be added to writes to push data out of FIFO
|
|
#define FIFOFillOffset 64 // Minimum amout of room to leave at top of FIFO
|
|
#define FIFOSize 2048 // Total size of FIFO
|
|
#define MaxPreFill (FIFOSize / 2)
|
|
|
|
#define AuxStat HA->IOBase+INAuxOff
|
|
#define WDSelect HA->IOBase+INWDSelOff
|
|
#define WDData HA->IOBase+INWDDataOff
|
|
#define INData HA->IOBase+INDataOff
|
|
|
|
#define lengthof(x) (sizeof(x) / sizeof(x[0]))
|
|
|
|
// Prototypes:
|
|
int IN2000_ISR(ADAPTER_PTR HA);
|
|
U32 IN2000_Service(int Func, ADAPTER_PTR HA, U32 Misc);
|
|
|
|
#define SetWDReg(HA,WDReg) outb(HA->Ext->AD.IN2000U.IOMap[INWDSelOff], (WDReg))
|
|
#define ReadWDData(HA) inb(HA->Ext->AD.IN2000U.IOMap[INWDDataOff])
|
|
#define ReadWDReg(HA,reg) (SetWDReg(HA,reg), ReadWDData(HA))
|
|
#define WriteWDData(HA, val) outb(HA->Ext->AD.IN2000U.IOMap[INWDDataOff], (val))
|
|
#define WriteWDReg(HA,reg,val) SetWDReg(HA,(reg));WriteWDData(HA, val)
|
|
extern void WD33c93_ISR(ADAPTER_PTR HA);
|
|
extern void WD33C93_Init(ADAPTER_PTR HA);
|
|
extern BOOLEAN GlobalAllowSync;
|
|
|
|
|
|
typedef struct {
|
|
|
|
U8 OwnID;
|
|
U8 CtrlReg;
|
|
U8 TimeOutReg;
|
|
U8 SourceReg;
|
|
|
|
} StateBuffer;
|
|
|
|
|
|
LOCAL void
|
|
IN2000ReInit (ADAPTER_PTR HA)
|
|
{
|
|
// Reset the chip with a reset command. The reset is complete when
|
|
// the interrupt register is set
|
|
WriteWDReg(HA, WDCMDReg, WDResetCmd);
|
|
|
|
while ((ReadWDReg(HA, WDAuxStatReg) & IntPending) == 0)
|
|
;
|
|
ReadWDReg(HA, WDStatusReg); /* Clear the interrupt */
|
|
|
|
WD33C93_Init(HA);
|
|
|
|
HA->Ext->AD.IN2000U.CurrIntMask = INFIFOMask;
|
|
outb(HA->IOBase+INIntMaskOff, INFIFOMask); /* Mask off FIFO, allow 33c93 ints. */
|
|
}
|
|
|
|
|
|
LOCAL void
|
|
IN2000ResetBus (ADAPTER_PTR HA)
|
|
{
|
|
unsigned j;
|
|
|
|
TRACE(2, ("IN2000ResetBus(): \n"));
|
|
HA->Ext->AD.IN2000U.CurrIntMask = INFIFOMask;
|
|
outb(HA->Ext->AD.IN2000U.IOMap[INIntMaskOff], INFIFOMask); /* Mask off FIFO, allow 33c93 ints. */
|
|
|
|
// Issue a SCSI bus reset; SCSI-2 says reset can any length > 25uS,
|
|
// however, some devices lose their mind if reset is too long,
|
|
// So, we'll try for 50uS, assumeing an 8-MHz bus:
|
|
outb(HA->Ext->AD.IN2000U.IOMap[INResetOff], 0); // Assert reset
|
|
for (j=30; j; j--) // don't make reset too short, figure 10Mhz ISA bus, 4 cycles / IO
|
|
inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]); // but too long breaks some drives
|
|
inb(HA->Ext->AD.IN2000U.IOMap[INHWRevOff]); // de-assert reset
|
|
|
|
WriteWDReg(HA, WDCMDReg, WDResetCmd); // Reset chip, intr. will re-initialize it
|
|
|
|
}
|
|
|
|
|
|
LOCAL U32
|
|
IN2000Init (ADAPTER_PTR HA)
|
|
{
|
|
int j;
|
|
|
|
/* Mask off ints while we're in init. routine */
|
|
outb(HA->IOBase+INIntMaskOff, INFIFOMask | INSBICMask);
|
|
|
|
// Set up the I/O port map:
|
|
for (j=0; j<= 15; j++)
|
|
HA->Ext->AD.IN2000U.IOMap[j] = HA->IOBase + j;
|
|
|
|
// Issue a SCSI bus reset; SCSI-2 says reset can any length > 25uS,
|
|
// however, some devices lose their mind if reset is too long,
|
|
// So, we'll try for 50uS, assumeing an 8-MHz bus:
|
|
inb(HA->IOBase + INHWRevOff); // Precautionary deassert reset
|
|
outb(HA->IOBase + INResetOff, 0); // Reset the board
|
|
for (j=30; j; j--) // don't make reset too short, figure 10Mhz ISA bus, 4 cycles / IO
|
|
inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]); // but too long breaks some drives
|
|
inb(HA->IOBase + INHWRevOff); /* de-assert reset */
|
|
APINotifyReset(HA);
|
|
|
|
((StateBuffer *)(HA->Ext->InitialState))->OwnID = 7;
|
|
((StateBuffer *)(HA->Ext->InitialState))->CtrlReg = ReadWDReg(HA, WDControlReg);
|
|
((StateBuffer *)(HA->Ext->InitialState))->TimeOutReg = ReadWDReg(HA, WDTimeoutReg);
|
|
((StateBuffer *)(HA->Ext->InitialState))->SourceReg = ReadWDReg(HA, WDSourceReg);
|
|
|
|
HA->Ext->SBIC.WD33C93.WDSelPort = HA->IOBase;
|
|
HA->Ext->SBIC.WD33C93.WDDataPort = HA->IOBase+1;
|
|
|
|
HA->Ext->SBIC.WD33C93.MHz = 10; /* The IN-2000 uses a 10Mhz 33c93 */
|
|
HA->Ext->SBIC.WD33C93.AsyncValue = 0x30;
|
|
|
|
IN2000ReInit(HA);
|
|
|
|
HA->State.Allow = 1; // Allow request processing
|
|
return 0; // OK
|
|
}
|
|
|
|
|
|
|
|
int
|
|
Find_IN2000 (ADAPTER_PTR HA, unsigned *Context)
|
|
{
|
|
static const unsigned BaseList[]=
|
|
{0x100, 0x110, 0x200, 0x220}; /* Bits are inverted */
|
|
static const unsigned IRQList[]={10, 11, 14, 15};
|
|
unsigned Switch;
|
|
IOHandle INBase;
|
|
int HWVers;
|
|
unsigned Terminal = lengthof(BaseList);
|
|
|
|
|
|
// For Chicago:
|
|
//
|
|
// If HA->IOBase is entered != 0, then we need to find the index of the
|
|
// matching I/O address. If we find one, limit the terminus of the
|
|
// primary check below, so we check only one instance. If we don't find
|
|
// a match, then the primary loop below will fail to start. Not pretty,
|
|
// but it works.
|
|
|
|
if (HA->IOBaseAddr != 0) {
|
|
|
|
for (*Context = 0; *Context < lengthof(BaseList); (*Context)++)
|
|
if (HA->IOBaseAddr == BaseList[*Context])
|
|
break;
|
|
|
|
Terminal = min(*Context + 1, lengthof(BaseList));
|
|
|
|
}
|
|
|
|
|
|
TRACE(4, ("Find_IN2000(): HA Ptr = %x,Context = %x\n", HA, *Context));
|
|
for (; *Context < Terminal; DeregisterIO(HA, INBase), (*Context)++) {
|
|
|
|
INBase = RegisterIO(HA, BaseList[*Context], 15, AddrSpaceIO);
|
|
Switch = inb(INBase+INSwitchOff);
|
|
TRACE(5,("Find_IN2000(): Switch value read was: %02x\n", Switch));
|
|
if ((Switch & 3) != *Context) /* Do switch settings match? */
|
|
continue;
|
|
|
|
/* Check the version number port, see if it appears IN-2000-ish */
|
|
HWVers = inb(INBase+INHWRevOff);
|
|
TRACE(5,("Find_IN2000(): H/W version read as: %02x\n", HWVers));
|
|
if ((HWVers < 0x20) || (HWVers > 0x29))
|
|
continue;
|
|
|
|
if (HWVers < MinHWVers) {
|
|
|
|
LogMessage(HA, NILL, 0, 0, MSG_BAD_FIRMWARE, HWVers);
|
|
|
|
TRACE(1,("Version of the IN-2000 SPROM at I/O address %x is %02x. Please"
|
|
" call Always\nfor upgrade instructions. Board is being "
|
|
"ignored.\n\n", INBase, HWVers));
|
|
continue;
|
|
|
|
}
|
|
|
|
if (Switch & 0x04)
|
|
HA->IRQNumber = IRQList[(Switch >> 3) & 0x3];
|
|
else {
|
|
|
|
LogMessage(HA, NILL, 0, 0, MSG_NO_INT_ENABLE, BaseList[*Context]);
|
|
TRACE(1,("IN-2000 at I/O %xh must have its interrupts enabled."
|
|
" Board is being ignored.\n\n", INBase));
|
|
continue;
|
|
|
|
}
|
|
|
|
HA->IOBase = INBase;
|
|
HA->IOBaseAddr = BaseList[*Context];
|
|
HA->IOAddrLen = 15;
|
|
HA->SCSI_ID = 7;
|
|
HA->Service = IN2000_Service;
|
|
HA->ISR = IN2000_ISR;
|
|
HA->Name = "IN-2000";
|
|
|
|
#if defined(WINNT)
|
|
// Test the DOS 5/Sync. switch (8); On, supports DOS 5 & synchronous
|
|
HA->Supports.Synchronous = ((Switch & 0x20) == 0); // Support sync. if switch 8 is on
|
|
#else
|
|
HA->Supports.Synchronous = GlobalAllowSync; // Support sync. if switch 8 is on
|
|
#endif
|
|
|
|
HA->Supports.Identify = TRUE;
|
|
HA->Physical.BusType = BT_ISA;
|
|
|
|
(*Context)++; // Found one, so inc. for next entry
|
|
return 1;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
SetUpXfer (ADAPTER_PTR HA, IO_REQ_PTR Req, unsigned Dir)
|
|
{
|
|
unsigned i;
|
|
|
|
HA->Ext->AD.IN2000U.CBuff = (char FAR *)&(((char FAR *)(ReqDataPtr(Req)))[(unsigned)(HA->ReqCurrentIndex)]);
|
|
HA->Ext->AD.IN2000U.CRemain = HA->ReqCurrentCount;
|
|
HA->Ext->AD.IN2000U.CurrDir = (Dir == SCSIIn) ? IN2000DataIn : IN2000DataOut;
|
|
TRACE(5,("SetUpXfer(): %ld (0x%lx) bytes to transfered to/from 0x%08lx\n", HA->Ext->AD.IN2000U.CRemain, HA->Ext->AD.IN2000U.CRemain, HA->Ext->AD.IN2000U.CBuff));
|
|
outb(HA->Ext->AD.IN2000U.IOMap[INFIFOResetOff], 1); /* Reset the FIFO */
|
|
|
|
if (HA->ReqCurrentCount >= FIFOThresh) {
|
|
|
|
TRACE(5,("SetUpXfer(): Setting up for FIFO xfer\n"));
|
|
if (Dir == SCSIIn) {
|
|
|
|
TRACE(5,("SetUpXfer(): Setting FIFO direction to read\n"));
|
|
outb(HA->Ext->AD.IN2000U.IOMap[INDirOff], 1); /* set read mode */
|
|
HA->Ext->AD.IN2000U.CurrIntMask =
|
|
(HA->Ext->AD.IN2000U.CRemain >= (FIFOSize-FIFOFillOffset)) ? 0 : INFIFOMask;
|
|
|
|
}
|
|
|
|
i = (ReadWDReg(HA, WDControlReg) & ~DMAModeMask) | DMABus;
|
|
do {
|
|
WriteWDReg(HA, WDControlReg, i);
|
|
} while (i != ReadWDReg(HA, WDControlReg));
|
|
|
|
if (Dir != SCSIIn) { // Doing DATA OUT
|
|
|
|
/* The IN-2000 FIFO mechinism requires pre-loading on write
|
|
operations. At least 32 bytes must be pre-loaded, or else
|
|
data loss may occur. Upon transfering the final bytes to the
|
|
FIFO, it must be padded by writing 32 bytes of junk, to move
|
|
the valid data up from the first 32 byte "twilight-zone". This
|
|
will be done in the FIFO fill routine.
|
|
*/
|
|
|
|
i = (unsigned)min(HA->ReqCurrentCount, (U32)MaxPreFill); // Don't overfill FIFO
|
|
|
|
TRACE(5,("SetUpXfer(): Preloading %d bytes for write.\n", i));
|
|
repoutsw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16 FAR *)HA->Ext->AD.IN2000U.CBuff, (i+1)/2); // Pre-fill FIFO
|
|
HA->Ext->AD.IN2000U.CBuff += i; // Offset buffer ptr by amount written
|
|
HA->Ext->AD.IN2000U.CRemain -= i; // Decrement remaining count
|
|
|
|
if (HA->Ext->AD.IN2000U.CRemain) // is there more stuff after this?
|
|
HA->Ext->AD.IN2000U.CurrIntMask = 0; // Then don't mask FIFO ints
|
|
else { // If not, send pad characters
|
|
|
|
TRACE(5, ("SetupXfer(): Padding FIFO\n"));
|
|
for (i=(FIFOPad / 2); i; i--)
|
|
outw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16)i);
|
|
HA->Ext->AD.IN2000U.CurrIntMask = INFIFOMask; // Block FIFO ints
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
TRACE(5, ("SetupXfer(): Using byte I/O\n"));
|
|
i = (ReadWDReg(HA, WDControlReg) & ~DMAModeMask) | DMAPIO;
|
|
do {
|
|
WriteWDReg(HA, WDControlReg, i);
|
|
} while (i != ReadWDReg(HA, WDControlReg));
|
|
|
|
}
|
|
|
|
TRACE(5,("SetUpXfer(): SetUpXfer complete\n"));
|
|
|
|
}
|
|
|
|
|
|
U8 REGPARMS
|
|
FIFOStat (const IOHandle Port)
|
|
{
|
|
#ifdef ASMIO
|
|
|
|
asm {
|
|
mov dx, word ptr Port
|
|
in al, dx
|
|
}
|
|
InAgain:
|
|
asm {
|
|
mov ah, al
|
|
in al, dx
|
|
sub ah, al
|
|
jnz InAgain
|
|
}
|
|
return _AX; /* AH is already zeroed for unsigned promotion */
|
|
#else
|
|
|
|
int i;
|
|
U8 Stat1, Stat2;
|
|
|
|
Stat2 = inb(Port) & StatMask;
|
|
do {
|
|
|
|
Stat1 = Stat2;
|
|
|
|
for (i=3; i; i--)
|
|
Stat2 &= inb(Port); // Find which bits are stable for 4 reads
|
|
|
|
Stat2 &= StatMask; // Most sig. two bits, and int bit are interesting
|
|
|
|
} while (Stat1 != Stat2);
|
|
|
|
return Stat1;
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
EmptyFIFO (ADAPTER_PTR HA)
|
|
{
|
|
union {
|
|
U32 l;
|
|
unsigned char b[4];
|
|
} Count;
|
|
unsigned InFIFO, i;
|
|
|
|
/*
|
|
Update the pointers; First get the number of bytes
|
|
the SCSI chip thinks remain to be transfered. Then compare
|
|
to the number of bytes the HA structure says remain. The
|
|
differance is the number of bytes in the FIFO.
|
|
|
|
In the case of read data, we need to read the bytes out of
|
|
the FIFO. The number of bytes in the FIFO is the number
|
|
of bytes the structure says we've read, minus what the SCSI
|
|
chip has sent to the FIFO. The buffer pointer is then
|
|
incremented, and the remaining count is decremented by that
|
|
amount.
|
|
|
|
For write data, the number of bytes in the FIFO is the amount
|
|
the SCSI chip has yet to write, minus what the driver has yet
|
|
to send to the FIFO. The data in the FIFO is dropped, the
|
|
buffer pointer is set back, and the remaining count is
|
|
incremented.
|
|
*/
|
|
|
|
if ((HA->Ext->AD.IN2000U.CurrDir == IN2000DataIn) && (HA->Ext->AD.IN2000U.CRemain == 0)) {
|
|
|
|
HA->ReqCurrentIndex += HA->ReqCurrentCount;
|
|
HA->ReqCurrentCount = 0;
|
|
|
|
} else {
|
|
|
|
#if defined(NATIVE32)
|
|
|
|
Count.l = (U32)ReadWDReg(HA, WDCountReg);
|
|
Count.l = (Count.l * 256) + (U32)ReadWDData(HA);
|
|
Count.l = (Count.l * 256) + (U32)ReadWDData(HA);
|
|
|
|
#else
|
|
|
|
Count.b[3] = 0;
|
|
Count.b[2] = ReadWDReg(HA, WDCountReg);
|
|
Count.b[1] = ReadWDData(HA);
|
|
Count.b[0] = ReadWDData(HA);
|
|
|
|
#endif
|
|
|
|
TRACE(4,("EmptyFIFO(): Value of Xfer count registers: 0x%08lx\n", Count.l));
|
|
|
|
if (HA->Ext->AD.IN2000U.CurrDir == IN2000DataIn) {
|
|
|
|
// Get number we have untransfered, minus what the chip has left untransfered
|
|
// to give the number held in the FIFO:
|
|
InFIFO = (unsigned)(HA->Ext->AD.IN2000U.CRemain - Count.l);
|
|
|
|
TRACE(4,("EmptyFIFO(): CRemain=0x%08lx, in FIFO to read: %04x\n", HA->Ext->AD.IN2000U.CRemain, InFIFO));
|
|
|
|
if (InFIFO > 0) {
|
|
|
|
TRACE(5, ("EmptyFIFO(): final read %d bytes\n", InFIFO));
|
|
repinsw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16 FAR *)HA->Ext->AD.IN2000U.CBuff, (InFIFO+1)/2);
|
|
|
|
}
|
|
}
|
|
|
|
// When the transfer was set up, the count registers where loaded with
|
|
// ReqCurrCount(); now the counters reflect the number of bytes untransfered
|
|
// Increment the index by (StartBytesToXfer - RemainBytesToXfer); and save
|
|
// away the new remaining count:
|
|
HA->ReqCurrentIndex += HA->ReqCurrentCount - Count.l;
|
|
HA->ReqCurrentCount = Count.l;
|
|
TRACE(4,("EmptyFIFO(): New remaining count: %ld(dec)\n", HA->ReqCurrentCount));
|
|
|
|
}
|
|
|
|
TRACE(5,("EmptyFIFO(): Reseting xfer mode.\n"));
|
|
HA->Ext->AD.IN2000U.CurrIntMask |= INFIFOMask; /* Block the FIFO ints */
|
|
outb(HA->Ext->AD.IN2000U.IOMap[INFIFOResetOff], 1); /* Reset the FIFO */
|
|
|
|
i = (ReadWDReg(HA, WDControlReg) & 0x1f);
|
|
do { /* Clear WD Bus mode */
|
|
WriteWDReg(HA, WDControlReg, i);
|
|
} while (ReadWDReg(HA, WDControlReg) != i);
|
|
|
|
HA->Ext->AD.IN2000U.CurrDir = IN2000NoData;
|
|
HA->Ext->AD.IN2000U.CRemain = 0;
|
|
|
|
}
|
|
|
|
|
|
void
|
|
FIFO_ISR (ADAPTER_PTR HA)
|
|
{
|
|
unsigned S;
|
|
|
|
#if defined(KEEP_STATS)
|
|
HA->DataInterrupts++;
|
|
#endif
|
|
|
|
// Stay in here as long as there is no 33C93 interrupt (bit 0), and there is
|
|
// at least 512 bytes in the FIFO (0xc0), and there is data remaining.
|
|
// FIFOStat is called to repeatedly read the FIFO status port, since it
|
|
// may be unstable for a single read.
|
|
|
|
while (!((S = FIFOStat(HA->Ext->AD.IN2000U.IOMap[INFIFOOff])) & 1)
|
|
&& (S & 0xc0) && HA->Ext->AD.IN2000U.CRemain) {
|
|
|
|
TRACE(5, ("IN2000_ISR(): FIFO status port read as %x\n", S));
|
|
|
|
// Value read from port (bits 1-7) is number of bytes / 16; Bit one is the
|
|
// WD interrupt pending bit; so the count is effectively already multiplied
|
|
// by two. Multiply it again by 8 to get the number of bytes in FIFO
|
|
// S = (S & 0xc0) * 8;
|
|
S = 512;
|
|
|
|
if (HA->Ext->AD.IN2000U.CurrDir == IN2000DataIn) {
|
|
|
|
// if ((U32)S > HA->Ext->AD.IN2000U.CRemain) {
|
|
//
|
|
// TRACE(0, ("FIFO_ISR(): FIFO says %ld, expected remaining is %ld\n", S, HA->Ext->AD.IN2000U.CRemain));
|
|
// S = (unsigned)HA->Ext->AD.IN2000U.CRemain;
|
|
//
|
|
// }
|
|
S = (unsigned)min(S, HA->Ext->AD.IN2000U.CRemain);
|
|
TRACE(4, ("FIFO_ISR(): reading %d bytes to %lx\n", S, HA->Ext->AD.IN2000U.CBuff));
|
|
|
|
#if !defined(ASMIO)
|
|
|
|
repinsw(HA->Ext->AD.IN2000U.IOMap[INDataOff], HA->Ext->AD.IN2000U.CBuff, S/2);
|
|
|
|
#else /* ASMIO */
|
|
|
|
#if sizeof(HA) == 4 /* far HA ptr */
|
|
asm {
|
|
mov ax, di
|
|
les bx, HA
|
|
mov dx, es:[bx].IOBase
|
|
les di, es:[bx].AD.IN2000U.CBuff
|
|
}
|
|
#else /* near HA ptr */
|
|
asm {
|
|
mov ax, di
|
|
mov bx, HA
|
|
mov dx, [bx].IOBase
|
|
les di, [bx].AD.IN2000U.CBuff
|
|
}
|
|
#endif
|
|
asm {
|
|
add dx, INDataOff
|
|
mov cx, S
|
|
shr cx, 1
|
|
cld
|
|
rep insw
|
|
mov di, ax
|
|
}
|
|
|
|
#endif /* ASMIO */
|
|
|
|
} else {
|
|
|
|
// Leave 16 bytes (FIFOFillOffset) in FIFO for write flush (see below):
|
|
S = (unsigned)min(min(S - FIFOFillOffset, FIFOSize/2), HA->Ext->AD.IN2000U.CRemain);
|
|
|
|
TRACE(5, ("FIFO_ISR(): Writing next %d chunk from %lx\n", S, HA->Ext->AD.IN2000U.CBuff));
|
|
repoutsw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16 FAR *)HA->Ext->AD.IN2000U.CBuff, S/2);
|
|
|
|
}
|
|
|
|
HA->Ext->AD.IN2000U.CBuff += S;
|
|
HA->Ext->AD.IN2000U.CRemain -= S;
|
|
TRACE(5, ("FIFO_ISR(): New remaining is %ld (0x%lx)\n", HA->Ext->AD.IN2000U.CRemain, HA->Ext->AD.IN2000U.CRemain));
|
|
// if (HA->Ext->AD.IN2000U.CRemain)
|
|
// for(S=16; S && ((inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]) & 0xc1) == 0); S--) ;
|
|
}
|
|
|
|
/* The FIFO logic on the IN-2000 requires writing FIFOPad bytes of garbage
|
|
into the FIFO to push the end of the valid data out. This flush
|
|
occurs here:
|
|
*/
|
|
|
|
if (HA->Ext->AD.IN2000U.CRemain == 0) { /* Don't expect any more FIFO ints */
|
|
|
|
HA->Ext->AD.IN2000U.CurrIntMask |= INFIFOMask; /* Block the FIFO ints */
|
|
if (HA->Ext->AD.IN2000U.CurrDir == IN2000DataOut) { // Pad the FIFO
|
|
|
|
for (S=(FIFOPad / 2); S; S--)
|
|
outw(HA->Ext->AD.IN2000U.IOMap[INDataOff], (U16)S);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
IN2000_ISR (ADAPTER_PTR HA)
|
|
{
|
|
unsigned char Stat, Taken = 0;
|
|
|
|
TRACE(5, ("IN2000_ISR(): \n"));
|
|
outb(HA->Ext->AD.IN2000U.IOMap[INIntMaskOff], INFIFOMask | INSBICMask);
|
|
|
|
HA->Ext->AD.IN2000U.LastPollHadIntPending = FALSE;
|
|
|
|
while (((Stat = inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff])) & 1)
|
|
|| ((Stat & 0xc0) && (HA->Ext->AD.IN2000U.CRemain != 0))) {
|
|
|
|
Taken = 1;
|
|
TRACE(5, ("IN2000_ISR(): FIFOStatus is : %x\n", Stat));
|
|
if ( !(Stat & 1) )
|
|
FIFO_ISR(HA);
|
|
|
|
while(inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]) & 0x1)
|
|
WD33c93_ISR(HA);
|
|
|
|
}
|
|
|
|
outb(HA->Ext->AD.IN2000U.IOMap[INIntMaskOff], HA->Ext->AD.IN2000U.CurrIntMask);
|
|
return Taken;
|
|
}
|
|
|
|
|
|
void
|
|
IN2000_Initiate (ADAPTER_PTR HA, IO_REQ_PTR Req, const int StartLevel)
|
|
{
|
|
#if defined(COMPOUND_CMD)
|
|
unsigned char C;
|
|
#endif
|
|
|
|
TRACE(5, ("IN2000_Initiate(): initiating\n"));
|
|
|
|
critical(HA); // Block interrupts for now
|
|
|
|
HA->ReqCurrentCount = 0; // Next DXFER phase will cause a GetXferSegment()
|
|
HA->ReqCurrentIndex = 0;
|
|
|
|
HA->Ext->SBIC.WD33C93.State = WD_NO_STATE; // Currently not in any state
|
|
|
|
WriteWDReg(HA, WDDestIDReg, ReqTargetID(Req)); // Set the ID of the target
|
|
|
|
HA->State.Busy = 1; // Mark flag for adapter in use
|
|
|
|
if (HA->DevInfo[ReqTargetID(Req)].Flags.UseSync) { // Do we use sync. xfers on this device?
|
|
|
|
WriteWDReg(HA, WDSyncReg, HA->DevInfo[ReqTargetID(Req)].HASync1); // Then write the Sync. values
|
|
|
|
} else {
|
|
|
|
WriteWDReg(HA, WDSyncReg, HA->Ext->SBIC.WD33C93.AsyncValue); // Alright then, async. values
|
|
|
|
}
|
|
|
|
// enable reselection
|
|
WriteWDReg(HA, WDSourceReg, EnableRSel);
|
|
|
|
SCSIMakeIdentify(HA, ReqTargetLUN(Req), (BOOLEAN)(ReqAllowDisconnect(Req) && HA->CurrDev->Flags.Allow_Disc)); // Then build Identify with disconnect
|
|
|
|
#if defined(COMPOUND_CMD)
|
|
|
|
// WD Compound commands only know group 0, 1, & 5 CDBs:
|
|
C = ReqCDB(Req)[0] & 0xe0;
|
|
if ((HA->Ext->MO_Count > 1) || !(C <= 0x10 || C == 0x50)) {
|
|
|
|
WriteWDReg(HA, WDCMDReg, WDSelATNCmd); // Select with attention
|
|
TRACE(3, ("IN2000_Initiate(): Using discreet commands\n"));
|
|
|
|
} else {
|
|
|
|
TRACE(3, ("IN2000_Initiate(): Using compound commands\n"));
|
|
|
|
WriteWDReg(HA, WDControlReg, EnableIDI);
|
|
WriteWDReg(HA, WDTarLUNReg, ReqTargetLUN(Req) | ((BOOLEAN)(ReqAllowDisconnect(Req) && HA->CurrDev->Flags.Allow_Disc)) ? 0x40 : 0); // Set ID of target LUN
|
|
|
|
if ((HA->ReqCurrentCount >= FIFOThresh) && (ReqDataIn(Req) || ReqDataOut(Req))) {
|
|
|
|
TRACE(4, ("IN2000_Initiate(): Early prepare for data xfer; Preparing for %ld byte xfer\n", HA->ReqCurrentCount));
|
|
HA->State.DataXfer = 1;
|
|
HA->Ext->SBIC.WD33C93.State |= WD_BLOCK_XFER;
|
|
|
|
HA->Ext->AD.IN2000U.CurrIntMask = 0;
|
|
SetUpXfer(HA, HA->CurrReq, ReqDataIn(Req));
|
|
outb(HA->Ext->SBIC.WD33C93.WDSelPort, WDCountReg);
|
|
outb(HA->Ext->SBIC.WD33C93.WDDataPort, (((char FAR *)&ReqCurrCount(HA->CurrReq))[2]));
|
|
outb(HA->Ext->SBIC.WD33C93.WDDataPort, (((char FAR *)&ReqCurrCount(HA->CurrReq))[1]));
|
|
outb(HA->Ext->SBIC.WD33C93.WDDataPort, (((char FAR *)&ReqCurrCount(HA->CurrReq))[0]));
|
|
|
|
} else {
|
|
|
|
HA->Ext->AD.IN2000U.CurrIntMask = INFIFOMask;
|
|
outb(HA->Ext->SBIC.WD33C93.WDSelPort, WDCountReg);
|
|
outb(HA->Ext->SBIC.WD33C93.WDDataPort, 0);
|
|
outb(HA->Ext->SBIC.WD33C93.WDDataPort, 0);
|
|
outb(HA->Ext->SBIC.WD33C93.WDDataPort, 0);
|
|
|
|
}
|
|
|
|
if (StartLevel <= 1)
|
|
outb(HA->Ext->AD.IN2000U.IOMap[INIntMaskOff], HA->Ext->AD.IN2000U.CurrIntMask);
|
|
|
|
SetWDReg(HA, WDCDBReg); // Send the CDB
|
|
repoutsb(WDData, ReqCDB(Req), ReqCDBLen(Req));
|
|
HA->Ext->SBIC.WD33C93.State |= WD_COMPOUND_CMD; // Flag the use of LEVEL II commands
|
|
|
|
WriteWDReg(HA, WDCMDReg, WDSelATNXCmd); // Start select & xfer w/ attention
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
WriteWDReg(HA, WDCMDReg, WDSelATNCmd); // Select with attention
|
|
|
|
#endif
|
|
|
|
HA->ReqStarting = StartLevel;
|
|
inb(HA->Ext->AD.IN2000U.IOMap[INLEDOnOff]); // Turn on LED
|
|
uncritical(HA); // OK, allow ints again
|
|
|
|
TRACE(5, ("IN2000_Initiate(): initiating complete\n"));
|
|
|
|
}
|
|
|
|
|
|
U32
|
|
IN2000_Service (int Func, ADAPTER_PTR HA, U32 Misc)
|
|
{
|
|
int j;
|
|
|
|
switch (Func) {
|
|
|
|
case HA_INITIALIZE:
|
|
|
|
return IN2000Init(HA);
|
|
break;
|
|
|
|
|
|
case HA_START:
|
|
|
|
TRACE(2, ("IN2000_Service(): Got HA_START command\n"));
|
|
HA->State.Allow = 1;
|
|
StartNext(HA, 1);
|
|
break;
|
|
|
|
|
|
case HA_STOP:
|
|
|
|
TRACE(2, ("IN2000_Service(): Got HA_STOP command\n"));
|
|
HA->State.Allow = 0;
|
|
break;
|
|
|
|
|
|
case HA_TICKLE:
|
|
|
|
if (!(HA->State.Busy) && (HA->State.Allow)) {
|
|
|
|
TRACE(5, ("IN2000_Service(): Tickling adapter\n"));
|
|
StartNext(HA,1);
|
|
|
|
} else {
|
|
|
|
TRACE(5, ("IN2000_Service(): Tickle ignored; Busy = %d, Allow = %d\n", HA->State.Busy, HA->State.Allow));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case HA_TIMER:
|
|
|
|
j = inb(HA->Ext->AD.IN2000U.IOMap[INFIFOOff]) & StatMask;
|
|
if (HA->Ext->AD.IN2000U.LastPollHadIntPending && j ) {
|
|
|
|
if (IN2000_ISR(HA)) {
|
|
|
|
LogMessage(HA, HA->CurrReq, 0, 0, MSG_NO_INTERRUPTS, __LINE__);
|
|
TRACE(0, ("IN2000_Service(): Serviced interrupt on timer\n"));
|
|
|
|
}
|
|
|
|
} else
|
|
HA->Ext->AD.IN2000U.LastPollHadIntPending = j;
|
|
break;
|
|
|
|
|
|
case HA_LED:
|
|
|
|
if ((int)Misc) inb(HA->Ext->AD.IN2000U.IOMap[INLEDOnOff]);
|
|
else inb(HA->Ext->AD.IN2000U.IOMap[INLEDOffOff]);
|
|
break;
|
|
|
|
|
|
case HA_INITIATE:
|
|
|
|
IN2000_Initiate(HA, HA->CurrReq, (unsigned)Misc);
|
|
break;
|
|
|
|
|
|
case HA_DATA_SETUP:
|
|
|
|
SetUpXfer(HA, HA->CurrReq, (unsigned)Misc);
|
|
if (HA->ReqCurrentCount < FIFOThresh)
|
|
return HAServiceResponse_UseByteIO;
|
|
else
|
|
return HA->Ext->AD.IN2000U.CRemain;
|
|
// break;
|
|
|
|
|
|
case HA_DATA_CMPLT:
|
|
|
|
if (HA->Ext->AD.IN2000U.CurrDir != IN2000NoData)
|
|
EmptyFIFO(HA);
|
|
break;
|
|
|
|
case HA_RESET_BUS:
|
|
|
|
IN2000ResetBus(HA);
|
|
break;
|
|
|
|
|
|
case HA_REVERT_STATE:
|
|
|
|
// Restore the board back to its preveous state. This is used by
|
|
// Netware / Chicago to switch back to BIOS mode.
|
|
WriteWDReg(HA, WDOwnIDReg, ((StateBuffer *)(HA->Ext->InitialState))->OwnID);
|
|
|
|
critical(HA);
|
|
|
|
// Reset chip, then wait for reset complete interrupt. This causes the chip
|
|
// to accept the set ID.
|
|
WriteWDReg(HA, WDCMDReg, WDResetCmd);
|
|
|
|
while ((ReadWDReg(HA, WDAuxStatReg) & IntPending) == 0)
|
|
;
|
|
ReadWDReg(HA, WDStatusReg); // Clear the interrupt
|
|
|
|
uncritical(HA);
|
|
|
|
WriteWDReg(HA, WDControlReg, ((StateBuffer *)(HA->Ext->InitialState))->CtrlReg);
|
|
WriteWDReg(HA, WDTimeoutReg, ((StateBuffer *)(HA->Ext->InitialState))->TimeOutReg);
|
|
WriteWDReg(HA, WDSourceReg, ((StateBuffer *)(HA->Ext->InitialState))->SourceReg);
|
|
break;
|
|
|
|
|
|
case HA_RESTORE_STATE:
|
|
|
|
IN2000ReInit(HA);
|
|
IN2000ResetBus(HA);
|
|
break;
|
|
|
|
|
|
case HA_POWER_MODE:
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|