|
|
Sample Programs from NeXSTEP 3.3
/* Copyright (c) 1994 NeXT Computer, Inc. All rights reserved.
*
* AMD_ChipPrivate.m - methods used only by AMD_Chip module.
*
* HISTORY
* 2 Nov 94 Doug Mitchell at NeXT
* Created.
*/
#import "AMD_Chip.h"
#import "AMD_ChipPrivate.h"
#import "AMD_Private.h"
#import "AMD_x86.h"
#import "AMD_Regs.h"
#import "AMD_Types.h"
#import "AMD_ddm.h"
#import "bringup.h"
#import <driverkit/generalFuncs.h>
#import <kernserv/prototypes.h>
@implementation AMD_SCSI(ChipPrivate)
/*
* Determine if SCSI interrupt is pending.
*/
- (sintPending_t)scsiInterruptPending
{
unsigned char sstat;
sstat = READ_REG(scsiStat);
if (sstat & SS_INTERRUPT) {
return SINT_DEVICE;
}
else {
return SINT_NONE;
}
}
/*
* Methods invoked upon interrupt. One per legal scState. All assume that
* status and interrupt status have been captured in saveStatus and
* saveIntrStatus.
*/
/*
* Disconnected - only legal event here is reselection.
*/
- (void)fsmDisconnected
{
ddm_chip("fsmDisconnected\n", 1,2,3,4,5);
ASSERT(activeCmd == NULL);
if(saveIntrStatus & IS_RESELECTED) {
/*
* We've been reselected.
*/
unsigned char selectByte;
unsigned fifoDepth;
unsigned char msg;
/*
* Make sure there's a selection byte and an
* identify message in the fifo.
*/
fifoDepth = READ_REG(currFifoState) & FS_FIFO_LEVEL_MASK;
ddm_chip("reselect: fifoDepth %d\n", fifoDepth, 2,3,4,5);
if(fifoDepth != 2) {
ddm_err("reselection, fifoDepth %d\n", fifoDepth,
2,3,4,5);
IOLog("AMD53C974: Bad FIFO count (%d) on Reselect\n",
fifoDepth);
[self hwAbort:SR_IOST_HW
reason:NULL];
return;
}
/*
* make sure target set his bit.
*/
if ((selectByte = READ_REG(scsiFifo) &~ (1 << hostId)) == 0) {
ddm_err("fsmDisconnected: reselection failed"
" - no target bit\n", 1,2,3,4,5);
[self hwAbort:SR_IOST_BV
reason:"No target bit on Reselect"];
return;
}
/*
* figure out target from bit that's on.
*/
for (reselTarget = 0;
(selectByte & 1) == 0;
reselTarget++, selectByte>>=1) {
continue;
}
/*
* first message byte must be identify.
*/
msg = READ_REG(scsiFifo);
if (saveStatus & SS_PARITYERROR) {
ddm_err("fsmDisconnected: reselected parity error\n",
1,2,3,4,5);
[self hwAbort:SR_IOST_PARITY
reason:"Parity error on Reselect"];
return;
}
if ((msg & MSG_IDENTIFYMASK) == 0) {
ddm_err("fsmDisconnected: reselection failed - "
"bad msg byte (0x%x)\n", msg, 2,3,4,5);
[self hwAbort:SR_IOST_BV
reason:"Bad ID Message on Reselect"];
return;
}
reselLun = msg & MSG_ID_LUNMASK;
currMsgInCnt = 0;
/*
* At this point, the chip is waiting for us to validate
* the identify message. If cmd queueing is enabled
* for this target, the target is waiting to send a queue
* tag message, so we have to tell the chip to
* drop ACK before we proceed with the reselection.
*
* In case of sync mode, we need to load target context right
* now, before dropping ACK, because the target might go
* straight to a data in or data out as soon as ACK drops.
*/
[self targetContext:reselTarget];
scState = SCS_ACCEPTINGMSG;
reselPending = 1;
WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO);
WRITE_REG(scsiCmd, SCMD_MSG_ACCEPTED);
ddm_chip("reselPending: target %d lun %d\n",
reselTarget, reselLun, 3,4,5);
} else if(saveIntrStatus & IS_SCSIRESET) {
/*
* TBD - for now ignore, we get one of these by resetting the
* chip. If an I/O is pending, it'll probably time out.
* Maybe we want to return SR_IOST_RESET on the pending
* command...
*/
ddm_chip("fsmDisconnected: ignoring reset interrupt\n",
1,2,3,4,5);
} else {
/*
* I'm confused....
*/
[self hwAbort:SR_IOST_BV
reason:"bad interrupt while disconnected"];
}
}
/*
* One of three things can happen here - the selection could succeed (though
* with possible imcomplete message out), it could time out, or we can be
* reselected.
*/
#define CATCH_SELECT_TO 1
- (void)fsmSelecting
{
unsigned char fifoDepth;
unsigned char phase;
IOSCSIRequest *scsiReq = activeCmd->scsiReq;
ddm_chip("fsmSelecting\n", 1,2,3,4,5);
ASSERT(activeCmd != NULL);
if (saveIntrStatus & IS_DISCONNECT) {
/*
* selection timed-out. Abort this request.
*/
#if CATCH_SELECT_TO
/* DEBUG ONLY */
if(scsiReq->cdb.cdb_opcode != C6OP_INQUIRY) {
IOLog("Unexpected Select Timeout\n");
}
#endif CATCH_SELECT_TO
ddm_chip("***SELECTION TIMEOUT for target %d\n",
activeCmd->scsiReq->target, 2,3,4,5);
WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO);
scState = SCS_DISCONNECTED;
scsiReq->driverStatus = SR_IOST_SELTO;
[self ioComplete:activeCmd];
activeCmd = NULL;
}
else if(saveIntrStatus == (IS_SUCCESSFUL_OP|IS_SERVICE_REQ)) {
ddm_chip("selection seqstep=%d\n",
saveSeqStep & INS_STATE_MASK, 2,3,4,5);
switch (saveSeqStep & INS_STATE_MASK) {
case 0:
/*
* No message phase. If we really wanted one,
* this could be significant...
*/
if(activeCmd->queueTag != QUEUE_TAG_NONTAGGED) {
/*
* This target can't do command queueing.
*/
[self disableMode:AM_CmdQueue];
}
if(SDTR_State == SNS_HOST_INIT_NEEDED) {
/*
* We were trying to do an SDTR.
*/
[self disableMode:AM_Sync];
SDTR_State = SNS_NONE;
}
/*
* OK, let's try to continue following phase
* changes.
*/
scState = SCS_INITIATOR;
break;
case 3: /* didn't complete cmd phase, parity? */
case 4: /* everything worked */
case 1: /* everything worked, SCMD_SELECT_ATN_STOP
* case */
/*
* We're connected. Start following the target's phase
* changes.
*
* If we're trying to do sync negotiation,
* this is the place to do it. In that case, we
* sent a SCMD_SELECT_ATN_STOP command, and
* ATN is now asserted (and we're hopefully in
* msg out phase). We want to send 5 bytes.
* Drop them into currMsgOut[] and prime the
* msgOutState machine.
*/
if(SDTR_State == SNS_HOST_INIT_NEEDED) {
[self createSDTR:currMsgOut inboundMsg:NULL];
currMsgOutCnt = MSG_SDTR_LENGTH;
msgOutState = MOS_WAITING;
}
scState = SCS_INITIATOR;
break;
case 2:
/*
* Either no command phase, or imcomplete message
* transfer.
*/
fifoDepth = READ_REG(currFifoState) &
FS_FIFO_LEVEL_MASK;
phase = saveStatus & SS_PHASEMASK;
ddm_chip("INCOMPLETE SELECT; fifoDepth %d phase %s\n",
fifoDepth,
IOFindNameForValue(phase, scsiPhaseValues),
3,4,5);
if(activeCmd->queueTag != QUEUE_TAG_NONTAGGED) {
/*
* This target can't do command queueing.
*/
[self disableMode:AM_CmdQueue];
}
/*
* Spec says ATN is asserted if all message bytes
* were not sent.
*/
if(fifoDepth > activeCmd->cdbLength) {
WRITE_REG(scsiCmd, SCMD_CLR_ATN);
}
/*
* OK, let's try to continue following phase
* changes.
*/
scState = SCS_INITIATOR;
break;
default:
[self hwAbort:SR_IOST_HW
reason:"Selection sequence Error"];
break;
}
}
else if(saveIntrStatus & IS_RESELECTED) {
/*
* We got reselected while trying to do a selection.
* Enqueue this cmdBuf on the HEAD of pendingQ, then deal
* with the reselect.
* Tricky case, we have to "deactivate" this command
* since this hwStart attempt failed.
*/
queue_enter_first(&pendingQ, activeCmd, commandBuf *, link);
[self deactivateCmd:activeCmd];
ddm_chip("reselect while trying to select target %d\n",
activeCmd->scsiReq->target, 2,3,4,5);
activeCmd = NULL;
scState = SCS_DISCONNECTED;
/*
* Go deal with reselect.
*/
[self fsmDisconnected];
}
else {
ddm_err("fsmSelecting: Bogus select/reselect interrupt\n",
1,2,3,4,5);
[self hwAbort:SR_IOST_HW
reason: "Bogus select/reselect interrupt"];
return;
}
return;
}
/*
* This one is illegal.
*/
- (void)fsmInitiator
{
ddm_chip("fsmInitiator\n", 1,2,3,4,5);
[self hwAbort:SR_IOST_HW reason:"Interrupt as Initiator"];
}
/*
* We just did a SCMD_INIT_CMD_CMPLT command, hopefully all that's left is
* to drop ACK. Command Complete message is handled in fscAcceptingMsg.
*/
- (void)fsmCompleting
{
ddm_chip("fsmCompleting\n", 1,2,3,4,5);
ASSERT(activeCmd != NULL);
if(saveIntrStatus & IS_DISCONNECT) {
ddm_err("unexpected completing disconnect\n",
1,2,3,4,5);
return;
}
if(saveIntrStatus & IS_SUCCESSFUL_OP) {
/*
* Got both status and msg in fifo; Ack is still true.
*/
if((READ_REG(currFifoState) & FS_FIFO_LEVEL_MASK) != 2) {
/*
* This is pretty bogus - we expect a status and
* msg in the fifo.
*/
[self hwAbort:SR_IOST_HW reason:"InitComplete fifo"
" level"];
return;
}
activeCmd->scsiReq->scsiStatus = READ_REG(scsiFifo);
currMsgInCnt = 1;
currMsgIn[0] = READ_REG(scsiFifo);
ddm_chip("fsmCompleting: status 0x%x msg 0x%x\n",
activeCmd->scsiReq->scsiStatus,
currMsgIn[0], 3,4,5);
if (saveStatus & SS_PARITYERROR) {
ddm_err("fsmCompleting: parity error on msg in\n",
1,2,3,4,5);
[self hwAbort:SR_IOST_PARITY
reason:"Parity error on message in"];
return;
}
scState = SCS_ACCEPTINGMSG;
WRITE_REG(scsiCmd, SCMD_MSG_ACCEPTED);
return;
} else {
/*
* Must have just got a status byte only. This is kind of
* weird, but let's try to handle it.
*/
ddm_err("fsmCompleting: status only on complete\n",
1,2,3,4,5);
if((READ_REG(currFifoState) & FS_FIFO_LEVEL_MASK) != 1) {
[self hwAbort:SR_IOST_HW
reason:"Bad Fifo level on Cmd Complete"];
return;
}
activeCmd->scsiReq->scsiStatus = READ_REG(scsiFifo);
if(saveStatus & SS_PARITYERROR) {
ddm_err("fsmCompleting: parity error on status\n",
1,2,3,4,5);
[self hwAbort:SR_IOST_PARITY
reason:"Parity Error on Cmd Complete"];
return;
}
/*
* Back to watching phase changes. Why the target isn't in
* message in we have yet to find out.
*/
scState = SCS_INITIATOR;
}
}
/*
* DMA Complete.
*/
- (void)fsmDMAing
{
u_int bytesMoved;
ddm_chip("fsmDMAing\n", 1,2,3,4,5);
ASSERT(activeCmd != NULL);
bytesMoved = [self dmaTerminate];
if(bytesMoved > activeCmd->currentByteCount) {
ddm_err("fsmDMAing: DMA transfer count exceeeded\n",
1,2,3,4,5);
ddm_err(" expected %d, moved %d\n",
activeCmd->currentByteCount, bytesMoved, 3,4,5);
bytesMoved = activeCmd->currentByteCount;
}
((char *)activeCmd->currentPtr) += bytesMoved;
activeCmd->currentByteCount -= bytesMoved;
if(saveStatus & SS_PARITYERROR) {
ddm_err("fsmDMAing: SCSI Data Parity Error\n", 1,2,3,4,5);
[self hwAbort:SR_IOST_PARITY reason:"SCSI Data Parity Error"];
return;
}
/*
* Back to watching phase changes.
*/
scState = SCS_INITIATOR;
}
/*
* Just completed the SCMD_TRANSFER_INFO operation for message in. ACK is
* still true. Stash the current message byte in currMsgIn[] and proceed to
* fsmAcceptingMsg after a SCMD_MSG_ACCEPTED.
*/
- (void)fsmGettingMsg
{
BOOL setAtn = NO;
ASSERT((activeCmd != NULL) || reselPending);
if(saveIntrStatus & IS_DISCONNECT) {
ddm_chip("fsmGettingMsg: message In Disconnect\n", 1,2,3,4,5);
/*
* This error is handled on return...
*/
return;
}
if((READ_REG(currFifoState) & FS_FIFO_LEVEL_MASK) != 1) {
ddm_chip("Message In fifo error\n", 1,2,3,4,5);
[self hwAbort:SR_IOST_HW reason:"Message In fifo error"];
return;
}
currMsgIn[currMsgInCnt++] = READ_REG(scsiFifo);
if(currMsgInCnt > AMD_MSG_SIZE) {
[self hwAbort:SR_IOST_BV
reason:"Too Many Message bytes received"];
}
if(saveStatus & SS_PARITYERROR) {
ddm_err("fsmGettingMsg: parity error on Message In\n",
1,2,3,4,5);
[self hwAbort:SR_IOST_PARITY
reason:"parity error on Message In"];
return;
}
ddm_chip("fsmGettingMsg: currMsgIn[%d] = 0x%x (%s)\n", currMsgInCnt-1,
currMsgIn[currMsgInCnt-1],
IOFindNameForValue(currMsgIn[currMsgInCnt-1],
scsiMsgValues), 4,5);
/*
* Handle special cases.
*/
/*
* 1. If this is the last byte of an unsolicited sync negotiation,
* we have to assert ATN right now. The message is actually
* fully parsed, and a response SDTR message created, in
* fsmAcceptingMsg.
*
* This parsing is pretty crude; if we come up with other special
* cases, we might rewrite this or come up with some state variables
* to help us.
*/
if((currMsgInCnt >= MSG_SDTR_LENGTH) && (SDTR_State == SNS_NONE)) {
int start = currMsgInCnt - MSG_SDTR_LENGTH;
if((currMsgIn[start] == MSG_EXTENDED) &&
(currMsgIn[start+1] == (MSG_SDTR_LENGTH - 2)) &&
(currMsgIn[start+2] == MSG_SDTR)) {
ddm_chip("UNSOLICITED SDTR IN; setting ATN\n",
1,2,3,4,5);
WRITE_REG(scsiCmd, SCMD_SET_ATN);
setAtn = YES;
SDTR_State = SNS_TARGET_INIT;
}
}
/*
* 2. If this was a message reject, it's possible that an extended
* message out was prematurely aborted, with ATN still true.
* Clear it so we don't do another (needless) message out.
* Avoid this, of course, if we set ATN in this method for
* any reason.
*/
if((currMsgIn[currMsgInCnt - 1] == MSG_MSGREJECT) &&
(currMsgOutCnt > 1) &&
!setAtn) {
ddm_chip("fsmGettingMsg: Message Reject; clearing ATN\n",
1,2,3,4,5);
WRITE_REG(scsiCmd, SCMD_CLR_ATN);
}
/*
* No need to clear FIFO; its depth was one on entry, and we read
* the byte. Note that clearing FIFO after the SCS_ACCEPTINGMSG
* might disturb possible sync data in transfer.
*/
WRITE_REG(scsiCmd, SCMD_MSG_ACCEPTED);
scState = SCS_ACCEPTINGMSG;
}
/*
* Just finished a message in; Ack is false. If phase is still
* message in, we're in the midst of an extended message or additional
* message bytes on reselect. Otherwise, message in is complete;
* process currMsgIn[].
*/
- (void)fsmAcceptingMsg
{
unsigned char phase = saveStatus & SS_PHASEMASK;
unsigned index;
perTargetData *perTargetPtr;
ddm_chip("fsmAcceptingMsg: phase %s\n",
IOFindNameForValue(phase, scsiPhaseValues), 2,3,4,5);
if((phase == PHASE_MSGIN) && !(saveIntrStatus & IS_DISCONNECT)) {
/*
* More message bytes to follow.
* We have to qualify with !IS_DISCONNECT to cover the
* case of some targets (like the Exabyte tape drive)
* which bogusly keep CD, IO, and MSG asserted after
* they drop BSY upon command complete.
*/
WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO);
WRITE_REG(scsiCmd, SCMD_TRANSFER_INFO);
scState = SCS_GETTINGMSG;
return;
}
/*
* Message in complete. Handle message(s) in currMsgIn[].
*/
if(reselPending) {
/*
* We're expecting either:
* -- no messages, or
* -- queue tag and/or Save Pointers.
*/
unsigned char tag = QUEUE_TAG_NONTAGGED;
ASSERT(activeCmd == NULL);
for(index=0; index<currMsgInCnt; index++) {
switch(currMsgIn[index]) {
case MSG_RESTOREPTRS:
ddm_chip("unnecessary restore pointers\n",
1,2,3,4,5);
continue; /* to next message */
case MSG_SIMPLE_QUEUE_TAG:
if(index == (currMsgInCnt-1)) {
[self hwAbort: SR_IOST_BV
reason:"Queue tag message, no tag"];
return;
}
tag = currMsgIn[++index];
break;
/*
* handle others here...?
*/
default:
IOLog("AMD53C974: bad msg (0x%x) after identify\n",
currMsgIn[index]);
[self hwAbort: SR_IOST_BV reason:NULL];
return;
}
}
if([self reselect:reselTarget
lun:reselLun
queueTag:tag] == YES) {
/*
* Found a disconnected commandBuf to reconnect.
*
* IDENTIFY msg implies restore ptrs.
*/
reselPending = 0;
ASSERT(activeCmd != NULL);
activeCmd->currentPtr = activeCmd->savedPtr;
activeCmd->currentByteCount =
activeCmd->savedByteCount;
scState = SCS_INITIATOR;
return;
}
else {
IOLog("AMD53C974: Illegal reselect (target %d lun "
"%d tag %d)\n", reselTarget, reselLun, tag);
[self hwAbort: SR_IOST_BV
reason:NULL];
return;
}
} /* reselect pending */
/*
* Handle all other messages.
*/
ASSERT(activeCmd != NULL);
perTargetPtr = &perTarget[activeCmd->scsiReq->target];
for(index=0; index<currMsgInCnt; index++) {
switch(currMsgIn[index]) {
case MSG_CMDCMPLT:
/*
* Bus really should be free; we came here from
* fsmCompleting.
*/
if(!(saveIntrStatus & IS_DISCONNECT)) {
ddm_err("fsmAcceptingMsg: Command Complete"
" but no Disconnect\n", 1,2,3,4,5);
[self hwAbort:SR_IOST_BV
reason:"No Disconnect On Command"
" Complete"];
return;
}
/*
* TA DA!
*/
scState = SCS_DISCONNECTED;
activeCmd->scsiReq->driverStatus = SR_IOST_GOOD;
[self ioComplete:activeCmd];
activeCmd = NULL;
return;
case MSG_DISCONNECT:
/*
* This could be in fsmGettingMsg, where we could
* handle it gracefully by doing a MSGREJ, but this
* is such a bogus error that we'll just reset the
* offender.
*/
if(!activeCmd->discEnable) {
ddm_chip("***Illegal Disconnect attempt\n",
1,2,3,4,5);
IOLog("AMD53C974: Illegal disconnect attempt"
" on target %d\n",
activeCmd->scsiReq->target);
[self hwAbort:SR_IOST_BV
reason:NULL];
return;
}
scState = SCS_DISCONNECTED;
[self disconnect];
return;
case MSG_SAVEPTRS:
activeCmd->savedPtr = activeCmd->currentPtr;
activeCmd->savedByteCount =
activeCmd->currentByteCount;
break;
case MSG_RESTOREPTRS:
activeCmd->currentPtr = activeCmd->savedPtr;
activeCmd->currentByteCount =
activeCmd->savedByteCount;
break;
case MSG_MSGREJECT:
/*
* look at last message sent; may have to
* disable sync or cmd queue mode for this target.
* This assumes that we don't send SDTR and queue tag
* in the same message.
*/
ddm_chip("fsmAcceptingMsg: MESSAGE REJECT RECEIVED "
"from target %d\n",
activeCmd->scsiReq->target, 2,3,4,5);
if(currMsgOutCnt == 0) {
/*
* Huh? We haven't sent a message recently...
*/
[self hwAbort:SR_IOST_BV
reason:"Unexpected Message Reject"];
return;
}
switch(currMsgOut[0]) {
case MSG_SIMPLE_QUEUE_TAG:
[self disableMode:AM_CmdQueue];
break;
case MSG_EXTENDED:
/*
* Only one we ever send is sync negotiation..
*/
if(currMsgOut[2] == MSG_SDTR) {
[self disableMode:AM_Sync];
SDTR_State = SNS_NONE;
break;
}
else {
[self hwAbort:SR_IOST_INT
reason:"Currupted Message Buffer"];
return;
}
default:
IOLog("AMD53C974: %s Message Rejected\n",
IOFindNameForValue(currMsgOut[0],
scsiMsgValues));
/* oh well... */
break;
}
/*
* In any case, we're definitely thru with the
* outbound message buffer.
*/
currMsgOutCnt = 0;
break;
case MSG_LNKCMDCMPLT:
case MSG_LNKCMDCMPLTFLAG:
/*
* This should never happen, because hwStart trashes
* commands with the LINK bit on.
*/
[self hwAbort:SR_IOST_BV reason:"Linked command"];
return;
case MSG_EXTENDED:
/*
* The only valid one is sync negotiation....
*/
switch(currMsgIn[index+2]) {
case MSG_SDTR:
if(currMsgIn[index+1] != (MSG_SDTR_LENGTH-2)) {
[self hwAbort:SR_IOST_BV
reason:"Bad Extended Msg Length"];
return;
}
switch(SDTR_State) {
case SNS_HOST_INIT:
/*
* Just completed SDTR that we initiated.
*/
if([self parseSDTR:&currMsgIn[index]]
== NO) {
[self hwAbort:SR_IOST_HW
reason:"Bad SDTR Parameters"];
return;
}
/*
* Successful SDTR.
*/
ddm_chip("host-init SDTR COMPLETE\n",
1,2,3,4,5);
SDTR_State = SNS_NONE;
break;
case SNS_TARGET_INIT:
/*
* Target-initiated negotiaited. This
* was detected in fsmGettingMsg, where
* we set ATN true.
* Cons up a response and prime the message
* out state machine to send it.
*/
[self createSDTR : currMsgOut
inboundMsg : &currMsgIn[index]];
currMsgOutCnt = MSG_SDTR_LENGTH;
msgOutState = MOS_WAITING;
break;
default:
IOPanic("AMD53C974: Bad SDTR_State");
}
/*
* Skip over the rest of this message; index
* should point to the last byte of this message.
*/
index += (MSG_SDTR_LENGTH - 1);
break;
default:
IOLog("AMD53C974: Unexpected Extended Message "
"(0x%x) Received\n",
currMsgIn[index+2]);
[self hwAbort:SR_IOST_BV reason:NULL];
return;
}
break;
default:
/*
* all others are unacceptable.
*/
IOLog("AMD53C974: Illegal message (0x%x)\n",
currMsgIn[index]);
[self messageOut:MSG_MSGREJECT];
}
} /* for index */
/*
* Default case for 'break' from above switch - back to following
* phase changes.
*/
scState = SCS_INITIATOR;
}
/*
* Just completed the SCMD_TRANSFER_INFO operation for message out.
*/
- (void)fsmSendingMsg
{
ddm_chip("fsmSendingMsg\n", 1,2,3,4,5);
ASSERT(activeCmd != NULL);
scState = SCS_INITIATOR;
if(SDTR_State == SNS_TARGET_INIT) {
/*
* If the message we just sent was a SDTR, we've just
* completed a target-initiated SDTR sequence.
* Note this assumes that an outbound SDTR in this
* situation is the only message in currMsgOut[].
* This will have to change if we send a queue tag and
* SDTR in the sqame message.
*/
if((currMsgOutCnt == MSG_SDTR_LENGTH) &&
(currMsgOut[0] == MSG_EXTENDED) &&
(currMsgOut[1] == (MSG_SDTR_LENGTH - 2)) &&
(currMsgOut[2] == MSG_SDTR)) {
ddm_chip("fsmSendingMsg: target-init SDTR complete\n",
1,2,3,4,5);
if([self parseSDTR:currMsgOut] == NO) {
/*
* Shouldn't fail; we generated this
* message ourself...
*/
IOPanic("AMD53C974: SDTR Problem\n");
}
SDTR_State = SNS_NONE;
}
}
}
/*
* Just completed the SCMD_TRANSFER_INFO operation for command.
*/
- (void)fsmSendingCmd
{
ddm_chip("fsmSendingCmd\n", 1,2,3,4,5);
ASSERT(activeCmd != NULL);
scState = SCS_INITIATOR;
}
/*
* Follow SCSI Phase change. Called while SCS_INITIATOR.
*/
- (void)fsmPhaseChange
{
int phase;
char *cp;
cdb_t *cdbp;
int i;
sc_status_t rtn;
ddm_chip("fsmPhaseChange\n", 1,2,3,4,5);
ASSERT(activeCmd != NULL);
/*
* Advance msg out state machine -- SCSI spec says if
* we do a msg out phase and then see another phase
* we can assume msg was transmitted without error.
* However, if we're in msg in, we may have a message reject
* coming in, so we'll keep currMsgOut[] valid in that case.
*
* FIXME - one case which this would not cover is the queue tag
* message saved in currMsgOut[] during selection. We don't
* go to MOS_SAWMSGOUT in that case. problem?
*/
phase = saveStatus & SS_PHASEMASK;
if ((phase != PHASE_MSGOUT) &&
(phase != PHASE_MSGIN) &&
(msgOutState == MOS_SAWMSGOUT)) {
msgOutState = MOS_NONE;
currMsgOutCnt = 0;
}
/*
* If we just sent a host-initiated SDTR and the target went
* to something other than phase in, we assume that the negotiation
* failed. This is in violation of the spec, but the Sony CDROM
* does this.
*/
if((SDTR_State == SNS_HOST_INIT) && (phase != PHASE_MSGIN)) {
ddm_chip("IMPLIED SNS_HOST_INIT Reject\n", 1,2,3,4,5);
[self disableMode:AM_Sync];
SDTR_State = SNS_NONE;
}
/*
* make sure we start off with a clean slate.
*
* NO - this can disturb possible sync data in! We'll need to cover
* this in individual cases elsewhere...
*/
/* WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO); */
ddm_chip("fsmPhaseChange: phase = %s\n",
IOFindNameForValue(phase, scsiPhaseValues), 2,3,4,5);
switch (phase) {
case PHASE_COMMAND:
/*
* The normal case here is after a host-initiated SDTR
* sequence.
*/
ddm_chip("fsmPhaseChange: command phase\n", 1,2,3,4,5);
WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO);
cdbp = &activeCmd->scsiReq->cdb;
cp = (char *)cdbp;
for(i=0; i<activeCmd->cdbLength; i++) {
WRITE_REG(scsiFifo, *cp++);
}
#if 0
/*
* This causes extra bytes to sit around in the fifo
* if we go straight to data phase after this, and
* we can't clear the fifo at that time in case
* we're in sync data in...
*/
/*
* fill fifo to avoid spurious command phase for target
* chips that try to get max command length
*/
for (i = 12 - activeCmd->cdbLength; i > 0; i--)
WRITE_REG(scsiFifo, 0);
#endif 0
WRITE_REG(scsiCmd, SCMD_TRANSFER_INFO);
scState = SCS_SENDINGCMD;
break;
case PHASE_DATAOUT: /* To Target from Initiator (write) */
if(activeCmd->scsiReq->read) {
[self hwAbort:SR_IOST_BV reason:"bad i/o direction"];
break;
}
if(rtn = [self dmaStart]) {
[self hwAbort:rtn
reason: IOFindNameForValue(rtn,
IOScStatusStrings)];
break;
}
break;
case PHASE_DATAIN: /* From Target to Initiator (read) */
if(!activeCmd->scsiReq->read) {
[self hwAbort:SR_IOST_BV reason:"bad i/o direction"];
break;
}
if(rtn = [self dmaStart]) {
[self hwAbort:rtn
reason: IOFindNameForValue(rtn,
IOScStatusStrings)];
break;
}
break;
case PHASE_STATUS: /* Status from Target to Initiator */
/*
* fsmCompleting will collect the STATUS byte
* (and hopefully a MSG) from the fifo when this
* completes.
*/
scState = SCS_COMPLETING;
currMsgInCnt = 0;
WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO);
WRITE_REG(scsiCmd, SCMD_INIT_CMD_CMPLT);
break;
case PHASE_MSGIN: /* Message from Target to Initiator */
scState = SCS_GETTINGMSG;
currMsgInCnt = 0;
WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO);
WRITE_REG(scsiCmd, SCMD_TRANSFER_INFO);
break;
case PHASE_MSGOUT: /* Message from Initiator to Target */
WRITE_REG(scsiCmd, SCMD_CLEAR_FIFO);
if(msgOutState == MOS_WAITING) {
int i;
ASSERT(currMsgOutCnt != 0);
for(i=0; i<currMsgOutCnt; i++) {
ddm_chip("msg out: writing 0x%x\n",
currMsgOut[i], 2,3,4,5);
WRITE_REG(scsiFifo, currMsgOut[i]);
}
msgOutState = MOS_SAWMSGOUT;
if(SDTR_State == SNS_HOST_INIT_NEEDED) {
/*
* sending SDTR message after select.
*/
ASSERT(currMsgOut[0] == MSG_EXTENDED);
ASSERT(currMsgOut[2] == MSG_SDTR);
ASSERT(currMsgOutCnt == MSG_SDTR_LENGTH);
ddm_chip("going to SNS_HOST_INIT\n",
1,2,3,4,5);
SDTR_State = SNS_HOST_INIT;
}
} else {
/*
* Target went to msg out and we don't have
* anything to send! Just give it a nop.
*/
ddm_chip("msg out: sending MSG_NOP\n", 1,2,3,4,5);
WRITE_REG(scsiFifo, MSG_NOP);
}
scState = SCS_SENDINGMSG;
/*
* ATN is automatically cleared when transfer info completes.
*/
WRITE_REG(scsiCmd, SCMD_TRANSFER_INFO);
break;
default:
[self hwAbort:SR_IOST_HW reason:"Bad SCSI phase"];
break;
}
}
/*
* Set up to send single-byte message.
*/
- (void) messageOut : (u_char)msg
{
ddm_chip("messageOut (0x%x)\n", msg, 2,3,4,5);
currMsgOut[0] = msg;
currMsgOutCnt = 1;
msgOutState = MOS_WAITING;
WRITE_REG(scsiCmd, SCMD_SET_ATN);
}
/*
* Load syncPeriod, syncOffset for activeCmd per perTarget values.
*/
- (void)targetContext : (unsigned) target
{
perTargetData *perTargetPtr;
perTargetPtr = &perTarget[target];
if(!syncModeEnable ||
perTargetPtr->syncDisable ||
(perTargetPtr->syncXferOffset == 0)) {
/*
* Easy case, async.
*/
WRITE_REG(syncOffset, 0);
ddm_chip("targetContext(%d): ASYNC\n", target, 2,3,4,5);
#ifdef DEBUG
syncOffsetShadow = 0;
#endif DEBUG
}
else {
unsigned char periodReg;
unsigned char offsetReg;
periodReg = nsPeriodToSyncPeriodReg(
perTargetPtr->syncXferPeriod,
fastModeEnable, scsiClockRate);
WRITE_REG(syncPeriod, periodReg);
/*
* FIXME - might eventually want to use non-default
* values for RAD and RAA...
*/
offsetReg = (perTargetPtr->syncXferOffset |
SOR_RAD_DEFAULT | SOR_RAA_DEFAULT);
WRITE_REG(syncOffset, offsetReg);
ddm_chip("targetContext(%d): period 0x%x offset %d\n",
target, periodReg, offsetReg, 4,5);
#ifdef DEBUG
syncOffsetShadow = offsetReg;
syncPeriodShadow = periodReg;
#endif DEBUG
}
}
/*
* Parse and validate 5-byte SDTR message. If valid, save in perTarget
* and in hardware. Returns YES if valid, else NO.
*
* Specified message buffer could be from either currMsgIn[] or
* currMsgOut[].
*/
- (BOOL)parseSDTR : (unsigned char *)sdtrMessage
{
unsigned nsPeriod;
unsigned char fastClock;
unsigned minPeriod;
perTargetData *perTargetPtr;
ASSERT(activeCmd != NULL);
perTargetPtr = &perTarget[activeCmd->scsiReq->target];
if(sdtrMessage[0] != MSG_EXTENDED) {
goto Bad;
}
if(sdtrMessage[1] != (MSG_SDTR_LENGTH - 2)) {
goto Bad;
}
if(sdtrMessage[2] != MSG_SDTR) {
goto Bad;
}
/*
* period
*/
nsPeriod = SDTR_TO_NS_PERIOD(sdtrMessage[3]);
fastClock = READ_REG(control3) & CR3_FAST_CLOCK;
if(fastClock && fastModeEnable) {
minPeriod = MIN_PERIOD_FASTCLK_FASTSCSI;
}
else {
minPeriod = MIN_PERIOD_NORM;
}
if(nsPeriod < minPeriod) {
goto Bad;
}
perTargetPtr->syncXferPeriod = nsPeriod;
/*
* Offset
*/
if(sdtrMessage[4] > AMD_MAX_SYNC_OFFSET) {
goto Bad;
}
perTargetPtr->syncXferOffset = sdtrMessage[4];
/*
* Success.
*/
perTargetPtr->syncDisable = 0;
perTargetPtr->syncNegotNeeded = 0;
[self targetContext:activeCmd->scsiReq->target];
ddm_chip("parseSDTR SUCCESS: %02x %02x %02x %02x %02x\n",
sdtrMessage[0], sdtrMessage[1], sdtrMessage[2],
sdtrMessage[3], sdtrMessage[4]);
ddm_chip(" period %d offset %d\n", perTargetPtr->syncXferPeriod,
perTargetPtr->syncXferOffset, 3,4,5);
return YES;
Bad:
ddm_chip("parseSDTR FAIL: 02%x %02x %02x %02x %02x\n",
sdtrMessage[0], sdtrMessage[1], sdtrMessage[2],
sdtrMessage[3], sdtrMessage[4]);
return NO;
}
/*
* Cons up a SDTR message appropriate for both our hardware and a possible
* target-generated SDTR message. If inboundMsg is NULL, we just use
* the parameters we want.
*/
- (void)createSDTR : (unsigned char *)outboundMsg // required
inboundMsg : (unsigned char *)inboundMsg
{
unsigned desiredNsPeriod;
unsigned inboundNsPeriod;
unsigned offset = AMD_MAX_SYNC_OFFSET;
unsigned char fastClock;
outboundMsg[0] = MSG_EXTENDED;
outboundMsg[1] = MSG_SDTR_LENGTH - 2;
outboundMsg[2] = MSG_SDTR;
/*
* period
*/
fastClock = READ_REG(control3) & CR3_FAST_CLOCK;
if(fastClock && fastModeEnable) {
desiredNsPeriod = MIN_PERIOD_FASTCLK_FASTSCSI;
}
else {
desiredNsPeriod = MIN_PERIOD_NORM;
}
if(inboundMsg) {
inboundNsPeriod = SDTR_TO_NS_PERIOD(inboundMsg[3]);
}
else {
inboundNsPeriod = desiredNsPeriod;
}
if(inboundNsPeriod > desiredNsPeriod) {
/*
* Target is slower than us
*/
desiredNsPeriod = inboundNsPeriod;
}
outboundMsg[3] = NS_PERIOD_TO_SDTR(desiredNsPeriod);
/*
* Offset
*/
if(inboundMsg) {
offset = inboundMsg[4];
if(offset > AMD_MAX_SYNC_OFFSET) {
/*
* target's buffer smaller than ours
*/
offset = AMD_MAX_SYNC_OFFSET;
}
}
outboundMsg[4] = offset;
ddm_chip("createSDTR: %02x %02x %02x %02x %02x\n",
outboundMsg[0], outboundMsg[1], outboundMsg[2],
outboundMsg[3], outboundMsg[4]);
ddm_chip(" period %d offset %d\n", desiredNsPeriod, offset, 3,4,5);
}
/*
* Disable specified mode for activeCmd's target. If mode is currently
* enabled, we'll log a message to the console.
*/
- (void)disableMode : (AMD_Mode)mode
{
int target;
perTargetData *perTargetPtr;
const char *modeStr = NULL;
ASSERT(activeCmd != NULL);
target = activeCmd->scsiReq->target;
perTargetPtr = &perTarget[target];
switch(mode) {
case AM_Sync:
if(perTargetPtr->syncDisable == 0) {
perTargetPtr->syncDisable = 1;
modeStr = "Synchronous Transfer Mode";
}
[self targetContext:activeCmd->scsiReq->target];
break;
case AM_CmdQueue:
if(perTargetPtr->cmdQueueDisable == 0) {
perTargetPtr->cmdQueueDisable = 1;
modeStr = "Command Queueing";
}
break;
}
ddm_chip("DISABLING %s for target %d\n", modeStr, target, 3,4,5);
if(modeStr) {
IOLog("AMD53C974: DISABLING %s for target %d\n",
modeStr, target);
}
}
@end /* AMD_SCSI(ChipPrivate) */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.