NT4/private/ntos/miniport/trantor/source/pc9010.c
2020-09-30 17:12:29 +02:00

852 lines
22 KiB
C

//---------------------------------------------------------------------
//
// File: PC9010.C
//
// PC9010 access file.
//
// These routines are independent of the card the PC9010 is on. The
// cardxxxx.h file must define the following routines:
//
// PC9010PortPut
// PC9010PortGet
// PC9010PortSet
// PC9010PortClear
// PC9010PortTest
//
// PC9010PortGetWord
// PC9010PortGetBufferWord
// PC9010PortPutWord
// PC9010PortPutBufferWord
//
// These routines could be defined by some other include file instead of
// cardxxxx.h, as the pc9010 defines the needed N5380xxxxxxxx routines.
//
// NOTES:
// 8 bit mode is not supported now.
// When data overrun occurs, the wrong number of bytes sent is returned
// this occurs only during writes to a scsi device and the device
// does not accept all bytes sent. The fifo of the pc9010 fills
// and we don't know how many of the bytes have been transfered
// across the bus...
//
// Revisions:
// 02-24-92 KJB First, does not support 8 bit mode, since we will
// not be shipping any 8 bit adapters!
// 03-11-93 JAP Changed retcode equates to reflect new names.
// 03-11-92 KJB Changed to use new N5380.H names.
// 03-26-93 JAP Fixed up typedef and prototype inconsistencies
// 04-05-93 KJB DEBUG_LEVEL used by DebugPrint for NT.
// 05-17-93 KJB Fixed warning message.
//
//---------------------------------------------------------------------
#include CARDTXXX_H
// switch setting on PC9010 for 16 bit transfers
// warning specific to T160 right now...
#define TRANSFER_MODE_16BIT 0x2
//
// Local Routines
//
USHORT PC9010ReadBytes8Bit(PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen);
USHORT PC9010ReadBytes16Bit(PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen);
USHORT PC9010WriteBytes8Bit(PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen);
USHORT PC9010WriteBytes16Bit(PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen);
VOID PC9010FifoDataTransferSetup(PADAPTER_INFO g,
BOOLEAN *mode_16bit);
USHORT PC9010WaitTillFifoReady(PADAPTER_INFO g,
BOOLEAN (*ReadyRoutine)(PADAPTER_INFO g) );
BOOLEAN PC9010Read16ReadyRoutine(PADAPTER_INFO g);
BOOLEAN PC9010Write16ReadyRoutine(PADAPTER_INFO g);
BOOLEAN PC9010FifoEmptyRoutine(PADAPTER_INFO g);
//
// Redefined Routines
//
#define PC9010WaitTillFifoEmpty(g) \
PC9010WaitTillFifoReady(g, PC9010FifoEmptyRoutine);
//
// PC9010CheckAdapter
//
// This routine checks for the presense of a pc9010.
//
BOOLEAN PC9010CheckAdapter (PADAPTER_INFO g)
{
UCHAR tmp;
// try to clear the config bit of the control register
PC9010PortClear (g, PC9010_CONTROL, CTR_CONFIG);
if (PC9010PortTest (g, PC9010_CONTROL, CTR_CONFIG)) {
goto not_pc9010;
}
// try to set the config bit of the control register
PC9010PortSet (g, PC9010_CONTROL, CTR_CONFIG);
if (!PC9010PortTest (g, PC9010_CONTROL, CTR_CONFIG)) {
goto not_pc9010;
}
// the config bit is set, now read the configuration info
PC9010PortGet (g, PC9010_CONFIG, &tmp);
if (tmp != PC9010_JEDEC_ID) {
goto not_pc9010;
}
// this next byte sould be # of continuation chars, = 0
PC9010PortGet (g, PC9010_CONFIG, &tmp);
if (tmp) {
goto not_pc9010;
}
// now we will assume we have a pc9010
// initialize registers to 0
PC9010PortPut (g, PC9010_CONTROL, 0);
N5380PortPut (g, N5380_INITIATOR_COMMAND, 0);
N5380PortPut (g, N5380_MODE, 0);
return TRUE;
not_pc9010:
// the pc9010 was not found
return FALSE;
}
//
// PC9010WaitTillFifoReady
//
// Waits until fifo is ready to transfer something, checks for phase
// change and will timeout.
//
// The procedural parameter, ReadyRoutine, allows this routine to
// function for all modes: read, write, 16 and 8 bit modes.
//
USHORT PC9010WaitTillFifoReady (PADAPTER_INFO g,
BOOLEAN (*ReadyRoutine)(PADAPTER_INFO g))
{
ULONG i;
USHORT rval = 0;
for (i = 0; i < TIMEOUT_QUICK; i++) {
// is the fifo ready?
if ((*ReadyRoutine)(g)) {
return 0;
}
}
// ok, it did not come back quickly, we will yield to other processes
for (i = 0; i < TIMEOUT_REQUEST; i++) {
// is the fifo ready?
if ((*ReadyRoutine)(g)) {
return 0;
}
// check for phase mismatch
// disable the fifo, temporarily
PC9010PortClear (g, PC9010_CONTROL, CTR_FEN);
// note: the PC9010 will take 3 machine clocks to recognize this,
// the C code will provide these with no problem!
while (PC9010PortTest (g, PC9010_CONTROL, CTR_FEN));
// check for request and phase mismatch
if (N5380PortTest (g, N5380_CURRENT_STATUS, CS_REQ) &&
!N5380PortTest (g, N5380_DMA_STATUS, DS_PHASE_MATCH)) {
// phase mismatch, means data under/overrun
rval = RET_STATUS_DATA_OVERRUN;
DebugPrint((DEBUG_LEVEL,"Error - 0 - PC9010WaitTillFifoReady\n"));
goto error;
}
// re-enable the fifo
PC9010PortSet (g, PC9010_CONTROL, CTR_FEN);
ScsiPortStallExecution (1);
}
rval = RET_STATUS_TIMEOUT;
error:
DebugPrint((DEBUG_LEVEL,"Error - 1 - PC9010WaitTillFifoReady\n"));
// return with an error
return rval;
}
#if 0
// We can use the other routine
//
// PC9010WaitTillFifoEmpty
//
// Waits until fifo is empty to transfer something.
//
USHORT PC9010WaitTillFifoEmpty (PADAPTER_INFO g)
{
ULONG i;
USHORT rval = 0;
UCHAR tmp;
// see if the flag comes back quickly
for (i = 0; i < TIMEOUT_QUICK; i++) {
// is the fifo empty?
if (PC9010PortTest (g, PC9010_FIFO_STATUS, FSR_FEMP)) {
return 0;
}
}
// ok, it did not come back quickly, we will yield to other processes
for (i = 0; i < TIMEOUT_REQUEST; i++) {
// is the fifo empty?
if (PC9010PortTest (g, PC9010_FIFO_STATUS, FSR_FEMP)) {
return 0;
}
ScsiPortStallExecution (1);
}
rval = RET_STATUS_TIMEOUT;
error:
DebugPrint((DEBUG_LEVEL,"Error - 1 - PC9010WaitTillFifoEmpty\n"));
// return with an error
return rval;
}
#endif
//
// PC9010FifoEmptyRoutine
//
// Check to see if the fifo is ready to read 16 bits.
//
BOOLEAN PC9010FifoEmptyRoutine (PADAPTER_INFO g)
{
// if both lanes of fifo are not empty then return true
return (PC9010PortTest (g, PC9010_FIFO_STATUS, FSR_FEMP));
}
//
// PC9010Read16ReadyRoutine
//
// Check to see if the fifo is ready to read 16 bits.
//
BOOLEAN PC9010Read16ReadyRoutine (PADAPTER_INFO g)
{
// if both lanes of fifo are not empty then return true
return (!PC9010PortTest (g, PC9010_FIFO_STATUS,
FSR_FLEMP | FSR_FHEMP));
}
//
// PC9010FifoDataTransferSetup
//
// Setup the PC9010 chip for data transfer, either read or write.
//
VOID PC9010FifoDataTransferSetup (PADAPTER_INFO g,
BOOLEAN *mode_16bit)
{
USHORT rval = 0;
UCHAR tmp;
// disable the fifo
PC9010PortClear (g, PC9010_CONTROL, CTR_FEN);
// reset the fifo
PC9010PortSet (g, PC9010_CONTROL, CTR_FRST);
// clear reset, config, swsel, dir , selecting low switch bank
PC9010PortClear (g, PC9010_CONTROL,
CTR_FRST | CTR_CONFIG | CTR_SWSEL | CTR_FDIR);
// check for 16 bit mode, switch 2 = 0
// note: this is specific to a trantor card: the t160...
PC9010PortGet (g, PC9010_CONFIG, &tmp);
*mode_16bit = !(tmp & TRANSFER_MODE_16BIT);
// set the 16 bit mode or 8 bit for the fifo
if (*mode_16bit) {
PC9010PortSet (g, PC9010_CONTROL, CTR_F16);
}
else {
PC9010PortClear (g, PC9010_CONTROL, CTR_F16);
}
}
//
// PC9010ReadBytes8Bit
//
// Reads bytes for the PC9010 in 8 bit mode.
//
USHORT PC9010ReadBytes8Bit (PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen)
{
return RET_STATUS_ERROR;
}
//
// PC9010ReadBytes16Bit
//
// Reads bytes for the PC9010 in 16 bit mode.
//
USHORT PC9010ReadBytes16Bit (PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen)
{
PUCHAR pBuffer = pbytes;
PUCHAR pBufferEnd = &pBuffer[len];
USHORT rval = 0;
USHORT tmpw;
// 16 bit read loop
while (pBuffer != pBufferEnd) {
// can we do a big transfer?
if (PC9010PortTest (g, PC9010_FIFO_STATUS, FSR_FFUL)) {
PC9010PortGetBufferWord (g, PC9010_FIFO, pBuffer, 64);
pBuffer += 128;
}
else {
// is either fifo lane empty
if (PC9010PortTest (g, PC9010_FIFO_STATUS,
FSR_FLEMP | FSR_FHEMP)) {
// At least one half of the FIFO is empty. While waiting to
// read more data, monitor the 5380 to check for SCSI bus phase
// change (data underrun) or other errors.
// NOTE: The lo half of the FIFO may contain data. If we're only
// waiting for one more byte, transfer it immediately and exit.
// If the FIFO is completely empty (if low FIFO empty, high FIFO
// is also empty, since data goes into low FIFO first), or we're
// waiting for more than 1 additional byte then it's necessary to
// check for SCSI errors.
if (!PC9010PortTest (g, PC9010_FIFO_STATUS,
FSR_FLEMP) && pBuffer == pBufferEnd-1) {
// the low lane has a byte, and it is the last byte of
// the transfer
// read a word and discard the high byte
PC9010PortGetWord (g, PC9010_FIFO, &tmpw);
*pBuffer = (UCHAR)tmpw;
pBuffer += 1;
}
else {
// could be a byte in fifo, but
// there are no words in the fifo, possible
// phase change, or we have to wait
if (rval = PC9010WaitTillFifoReady(g,
PC9010Read16ReadyRoutine)) {
// there has been a phase change, or timeout
if (rval == RET_STATUS_TIMEOUT) {
// for timeouts, just exit...
goto done_error;
}
// phase change, transfer any data remaining in fifo
// is either fifo lane empty?
if (PC9010PortTest (g, PC9010_FIFO_STATUS,
FSR_FLEMP | FSR_FHEMP)) {
// is the low lane empty?
if (PC9010PortTest (g, PC9010_FIFO_STATUS,
FSR_FLEMP)) {
// low lane is empty, all bytes have been xferred
goto done_error;
}
// one byte remaining in low lane, transfer it
// read a word and discard the high byte
PC9010PortGetWord (g, PC9010_FIFO, &tmpw);
*pBuffer = (UCHAR)tmpw;
pBuffer +=1;
// that was the last byte ever, exit
goto done_error;
}
// there is at least a word in the fifo, fall through,
// we will get this phase error again later when
// all words have been transferred.
}
}
}
else {
// both lanes have at least one byte
PC9010PortGetWord (g, PC9010_FIFO, pBuffer);
pBuffer +=2;
}
}
}
done_error:
// if all bytes have been transferred and a phase change occured,
// then there was no over/underrun at all
if (rval == RET_STATUS_DATA_OVERRUN) {
if (pBuffer == pBufferEnd) {
// all bytes were transferred, no error
rval = 0;
}
}
// store the transfer len
*pActualLen = pBuffer - pbytes;
return rval;
}
//
// PC9010WriteBytes8Bit
//
// Writes bytes for the PC9010 in 8 bit mode.
//
USHORT PC9010WriteBytes8Bit (PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen)
{
return RET_STATUS_ERROR;
}
//
// PC9010WriteBytes16Bit
//
// Writes bytes for the PC9010 in 16 bit mode.
// The len must be an even number of bytes...
//
USHORT PC9010WriteBytes16Bit (PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen)
{
PUCHAR pBuffer = pbytes;
PUCHAR pBufferEnd = &pBuffer[len];
USHORT rval = 0;
// 16 bit read loop
while (pBuffer != pBufferEnd) {
// can we do a big transfer?
if (PC9010PortTest (g, PC9010_FIFO_STATUS, FSR_FEMP)) {
ULONG count;
// The FIFO is empty. Fill the FIFO in one burst.
//
// Transfer the lesser of the fifo size or the remaining number
// of bytes.
//
// Since we're doing word transfers we need to be careful if the
// number of remaining bytes is odd and less than the FIFO size.
// We handle this by bursting only an even number of bytes. If
// there is an odd balance remaining we pick it up later.
count = pBufferEnd-pBuffer;
if (count >= PC9010_FIFO_SIZE) {
// we have at least FIFO_SIZE bytes to send, fill the fifo
PC9010PortPutBufferWord(g, PC9010_FIFO,
pBuffer, 64);
pBuffer += PC9010_FIFO_SIZE;
}
else {
// write only the number of words we have
// if we have only one byte, we will get that later
PC9010PortPutBufferWord (g, PC9010_FIFO,
pBuffer, count/2);
pBuffer += count;
}
}
else {
// is either fifo lane full?
if (!PC9010PortTest (g, PC9010_FIFO_STATUS,
FSR_FLFUL | FSR_FHFUL)) {
// neither lane is full, we can write a word...
PC9010PortPutWord (g, PC9010_FIFO,
*(PUSHORT)pBuffer);
pBuffer += 2;
}
else {
// at least one of the fifo lanes is full, wait until ready
// checking for phase mismatch or timeout
if (rval = PC9010WaitTillFifoReady (g,
PC9010Write16ReadyRoutine)) {
// there has been a phase change, or timeout
// just exit...
goto done_error;
}
}
}
}
// If there has been no error, wait for the FIFO to empty.
//
// NOTE: The way this is currently coded, a SCSI error could occur
// after the last byte is written to the FIFO but before the last
// byte is written from the FIFO to the 5380, causing the code to
// hang. To solve this problem the 5380 should probably be set for
// interrupting on phase change and on loss of BSY. Then, code
// can wait for one of those events, the FIFO to empty, or a
// time-out. -RCB
rval = PC9010WaitTillFifoEmpty (g);
done_error:
// store the transfer len
*pActualLen = pBuffer - pbytes;
return rval;
}
//
// PC9010Write16ReadyRoutine
//
// Check to see if the fifo is ready to read 16 bits.
//
BOOLEAN PC9010Write16ReadyRoutine (PADAPTER_INFO g)
{
// if both lanes of fifo are not full then return true
return (!PC9010PortTest (g, PC9010_FIFO_STATUS,
FSR_FLFUL | FSR_FHFUL));
}
//
// PC9010ReadBytesFast
//
// Read the bytes from a nPC9010 as fast as possible.
//
USHORT PC9010ReadBytesFast (PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen, UCHAR phase)
{
USHORT rval = 0;
BOOLEAN mode_16bit;
// TEST CODE!!! to throw off the byte count!!
// rval = ScsiReadBytesSlow(g,pbytes,1,pActualLen,phase);
// pbytes++;
// len--;
// prepare the PC9010 for data transfer
PC9010FifoDataTransferSetup(g,&mode_16bit);
// start internal 5380 for data transfer
N5380EnableDmaRead (g);
// enable our fifo now
PC9010PortSet (g,PC9010_CONTROL,CTR_FEN);
//-----------------------------------------------------------------------
// READ LOOP
//
// NOTE: In both 8-bit and 16-bit read loops we tacitly assume
// that the target will never try to send more bytes than we
// have requested. In other words, if there are bytes in the
// FIFO, in some conditions we'll transfer the bytes without
// checking whether or not the host has actually requested that
// many bytes.
//
//-----------------------------------------------------------------------
// If the transfer length is longer than the FIFO is deep and
// is less than or equal to 2048 bytes, wait briefly for the
// FIFO to fill. This behavior is designed to optimize the
// performance of short transfers where "byte banging" the
// FIFO actually leads to lower performance than waiting for
// a burst. If the transfer length is outside this range, or
// if the FIFO doesn't fill quickly, go ahead with the transfer
// immediately.
//
// THE REASONING:
//
// Transfers shorter than the FIFO depth could never fill the
// FIFO, so there is no sense waiting for FIFO full.
//
// On the other end of the range, "byte-banging" typically
// occurs for no more than one depth of the FIFO (128 bytes).
// So, the time to "byte-bang" 128 bytes starts to become a
// very small fraction of the overall transfer time when 2048
// bytes (one CD-ROM sector) or more are transferred.
//
// How long is a brief wait? If we poll the FIFO flags 128
// times (one FIFO depth) and the FIFO is still not full, then
// the SCSI device is clearly slower than we are. In this case
// we might as well start "byte-banging". Fast SCSI devices
// will easily fill the FIFO in the time it takes to poll the
// FIFO flags that many times.
if (len > PC9010_FIFO_SIZE && len < 2048) {
ULONG i;
// loop for a while, waiting for fifo to fill
for (i = 0; i < PC9010_FIFO_SIZE; i++) {
if (PC9010PortTest (g, PC9010_FIFO_STATUS, FSR_FFUL)) {
break;
}
}
}
if (mode_16bit) {
rval = PC9010ReadBytes16Bit (g, pbytes, len, pActualLen);
} else {
rval = PC9010ReadBytes8Bit (g, pbytes, len, pActualLen);
}
// disable our fifo, wait for it to register as disabled
PC9010PortClear (g, PC9010_CONTROL, CTR_FEN);
while (PC9010PortTest (g, PC9010_CONTROL, CTR_FEN));
// disable 5380 dma
N5380DisableDmaRead (g);
return rval;
}
//
// PC9010WriteBytesFast
//
// Write the bytes from a nPC9010 as fast as possible.
//
USHORT PC9010WriteBytesFast (PADAPTER_INFO g, PUCHAR pbytes,
ULONG len, PULONG pActualLen, UCHAR phase)
{
USHORT rval = 0;
BOOLEAN mode_16bit;
ULONG bytes_left = 0;
// TEST CODE!!! to throw off the byte count!!
// rval = ScsiWriteBytesSlow(g,pbytes,1,pActualLen,phase);
// pbytes++;
// len--;
// prepare the PC9010 for data transfer
PC9010FifoDataTransferSetup (g, &mode_16bit);
// start internal 5380 for data transfer
N5380EnableDmaWrite (g);
// enable our fifo now, setting direction flag
PC9010PortSet (g, PC9010_CONTROL, CTR_FEN | CTR_FDIR);
if (mode_16bit) {
// transfer only an even number of bytes
// transfer the odd byte later
bytes_left = len&1;
rval = PC9010WriteBytes16Bit (g, pbytes,
len-bytes_left, pActualLen);
}
else {
rval = PC9010WriteBytes8Bit (g, pbytes, len, pActualLen);
}
// disable our fifo, wait for it to register as disabled
PC9010PortClear (g, PC9010_CONTROL, CTR_FEN);
while (PC9010PortTest (g, PC9010_CONTROL, CTR_FEN));
// disable 5380 dma
N5380DisableDmaWrite (g);
// do we have to transfer one byte?
if (!rval && bytes_left) {
ULONG tmp_len;
rval = ScsiWriteBytesSlow (g, &pbytes[len-1],
bytes_left, &tmp_len, phase);
*pActualLen += tmp_len;
}
return rval;
}
//
// PC9010DisableInterrupt
//
// Disable interrupts on the PC9010
//
VOID PC9010DisableInterrupt (PADAPTER_INFO g)
{
// disable the interrupt on the 5380
N5380DisableInterrupt (g);
// disable interrupt in the PC9010 for 5380 ints
PC9010PortClear (g, PC9010_CONTROL, CTR_IRQEN);
}
//
// PC9010EnableInterrupt
//
// Enable interrupts from the PC9010
//
VOID PC9010EnableInterrupt (PADAPTER_INFO g)
{
// set the dma bit of 5380 so we can get phase mismatch ints
N5380EnableInterrupt (g);
// enable interrupt in the PC9010
PC9010PortPut (g, PC9010_CONTROL, CTR_IRQEN);
}
//
// PC9010ResetBus
//
// Reset the SCSI bus
//
VOID PC9010ResetBus (PADAPTER_INFO g)
{
// reset the PC9010
PC9010PortPut (g, PC9010_CONTROL, CTR_FRST);
// disable interrupts
PC9010DisableInterrupt (g);
// reset the scsi bus
N5380ResetBus (g);
}