2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 /* Sym8xxExecute.m created by russb2 on Sat 30-May-1998 */
25 #include "Sym8xxController.h"
29 unsigned int ml_phys_read( vm_offset_t paddr
);
33 static UInt32 dropInt
= 0;
36 void Sym8xxSCSIController::Sym8xxStartSRB( SRB
*srb
)
39 srb
->nexus
.targetParms
.scntl3Reg
= adapter
->targetClocks
[srb
->target
].scntl3Reg
;
40 srb
->nexus
.targetParms
.sxferReg
= adapter
->targetClocks
[srb
->target
].sxferReg
;
42 adapter
->nexusPtrsVirt
[srb
->nexus
.tag
] = &srb
->nexus
;
43 adapter
->nexusPtrsPhys
[srb
->nexus
.tag
] = (Nexus
*)OSSwapHostToLittleInt32( (UInt32
)&srb
->srbPhys
->nexus
);
44 adapter
->schedMailBox
[mailBoxIndex
++] = (Nexus
*)OSSwapHostToLittleInt32 ( (UInt32
)&srb
->srbPhys
->nexus
);
46 Sym8xxSignalScript( srb
);
50 /*-----------------------------------------------------------------------------*
51 * Interrupts from the Symbios chipset are dispatched here at task time under the
53 *-----------------------------------------------------------------------------*/
54 void Sym8xxSCSIController::interruptOccurred( IOInterruptEventSource
*ies
, int intCount
)
59 * The chipset's ISTAT reg gives us the general interrupting condiditions,
60 * with DSTAT and SIST providing more detailed information.
62 istatReg
= Sym8xxReadRegs( chipBaseAddr
, ISTAT
, ISTAT_SIZE
);
64 /* The INTF bit in ISTAT indicates that the script is signalling the driver
65 * that its IODone mailbox is full and that we should process a completed
66 * request. The script continues to run after posting this interrupt unlike
67 * other chipset interrupts which require the driver to restart the script
70 if ( istatReg
& INTF
)
72 Sym8xxWriteRegs( chipBaseAddr
, ISTAT
, ISTAT_SIZE
, istatReg
);
74 if ( dropInt
++ > 100 )
77 SCRIPT_VAR(R_ld_IOdone_mailbox
) = 0;
81 Sym8xxProcessIODone();
85 * Handle remaining interrupting conditions
87 if ( istatReg
& (SIP
| DIP
) )
89 Sym8xxProcessInterrupt();
92 while ( istatReg
& (SIP
| DIP
| INTF
) );
94 getWorkLoop()->enableAllInterrupts();
98 /*-----------------------------------------------------------------------------*
99 * Process a request posted in the script's IODone mailbox.
101 *-----------------------------------------------------------------------------*/
102 void Sym8xxSCSIController::Sym8xxProcessIODone()
106 IODoneMailBox
*pMailBox
;
110 * The IODone mailbox contains an index into our Nexus pointer tables.
112 * The Nexus struct is part of the SRB so we can get our SRB address
113 * by subtracting the offset of the Nexus struct in the SRB.
115 pMailBox
= (IODoneMailBox
*)&SCRIPT_VAR(R_ld_IOdone_mailbox
);
116 nexus
= adapter
->nexusPtrsVirt
[pMailBox
->nexus
];
117 srb
= (SRB
*)((UInt32
)nexus
- offsetof(SRB
, nexus
));
119 srb
->srbSCSIStatus
= pMailBox
->status
;
121 if ( srb
->srbSCSIStatus
== kSCSIStatusCheckCondition
)
123 Sym8xxCheckRequestSense( srb
);
126 Sym8xxUpdateXferOffset( srb
);
129 * Clear the completed Nexus pointer from our tables and clear the
132 adapter
->nexusPtrsVirt
[pMailBox
->nexus
] = (Nexus
*) -1;
133 adapter
->nexusPtrsPhys
[pMailBox
->nexus
] = (Nexus
*) -1;
134 SCRIPT_VAR(R_ld_IOdone_mailbox
) = 0;
137 * Wake up the client's thread to do post-processing
139 Sym8xxCompleteSRB( srb
);
141 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_select_phase
];
143 /*-----------------------------------------------------------------------------*
146 *-----------------------------------------------------------------------------*/
147 void Sym8xxSCSIController::Sym8xxCompleteSRB( SRB
*srb
)
149 IOSCSIParallelCommand
*scsiCommand
;
150 SCSIResults scsiResults
;
151 SCSINegotiationResults negotiationResult
, *negResult
;
154 scsiCommand
= srb
->scsiCommand
;
156 bzero( &scsiResults
, sizeof(scsiResults
) );
158 scsiResults
.adapterStatus
= srb
->srbAdapterStatus
;
159 scsiResults
.returnCode
= srb
->srbReturnCode
;
162 if ( srb
== abortSRB
)
165 if ( abortReqPending
== true )
167 abortReqPending
= false;
173 scsiResults
.bytesTransferred
= srb
->xferDone
;
174 scsiResults
.scsiStatus
= srb
->srbSCSIStatus
;
179 if ( (srb
->srbCDBFlags
& kCDBFlagsNegotiateSDTR
) || (srb
->srbCDBFlags
& kCDBFlagsNegotiateWDTR
) )
181 bzero( &negotiationResult
, sizeof(struct SCSINegotiationResults
) );
183 if ( ((srb
->srbCDBFlags
& kCDBFlagsNegotiateSDTR
) && srb
->negotiateSDTRComplete
== false) ||
184 ((srb
->srbCDBFlags
& kCDBFlagsNegotiateWDTR
) && srb
->negotiateWDTRComplete
== false) )
186 negotiationResult
.returnCode
= kIOReturnIOError
;
189 negotiationResult
.transferPeriodpS
= transferPeriod
;
190 negotiationResult
.transferOffset
= transferOffset
;
191 negotiationResult
.transferWidth
= transferWidth
;
192 negotiationResult
.transferOptions
= 0;
194 negResult
= &negotiationResult
;
197 scsiCommand
->setResults( &scsiResults
, negResult
);
198 scsiCommand
->complete();
201 /*-----------------------------------------------------------------------------*
202 * General script interrupt processing
204 *-----------------------------------------------------------------------------*/
205 void Sym8xxSCSIController::Sym8xxProcessInterrupt()
217 * Read DSTAT/SIST regs to determine why the script stopped.
219 dstatReg
= Sym8xxReadRegs( chipBaseAddr
, DSTAT
, DSTAT_SIZE
);
221 sistReg
= Sym8xxReadRegs( chipBaseAddr
, SIST
, SIST_SIZE
);
223 // printf( "SCSI(Symbios8xx): SIST = %04x DSTAT = %02x\n\r", sistReg, dstatReg );
226 * This Script var tells us what the script thinks it was doing when the interrupt occurred.
228 scriptPhase
= OSSwapHostToLittleInt32( SCRIPT_VAR(R_ld_phase_flag
) );
231 * SCSI Bus reset detected
233 * Clean up the carnage.
234 * Note: This may be either an adapter or target initiated reset.
236 if ( sistReg
& RSTI
)
238 Sym8xxProcessSCSIBusReset();
243 * Calculate our current SRB/Nexus.
245 * Read a script var to determine the index of the nexus it was processing
246 * when the interrupt occurred. The script will invalidate the index if there
247 * is no target currently connected or the script cannot determine which target
250 nexusIndex
= OSSwapHostToLittleInt32(SCRIPT_VAR(R_ld_nexus_index
));
251 if ( nexusIndex
>= MAX_SCSI_TAG
)
253 Sym8xxProcessNoNexus();
256 nexus
= adapter
->nexusPtrsVirt
[nexusIndex
];
257 if ( nexus
== (Nexus
*) -1 )
259 Sym8xxProcessNoNexus();
262 srb
= (SRB
*)((UInt32
)nexus
- offsetof(SRB
, nexus
));
264 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_phase_handler
];
267 * Parity and SCSI Gross Errors.
269 * Abort the current connection. The abort completion will trigger
270 * clean-up of the current SRB/Nexus.
274 srb
->srbAdapterStatus
= kSCSIAdapterStatusParityError
;
275 Sym8xxAbortCurrent( srb
);
278 else if ( sistReg
& SGE
)
280 srb
->srbAdapterStatus
= kSCSIAdapterStatusProtocolError
;
281 Sym8xxAbortCurrent( srb
);
285 * Unexpected disconnect.
287 * If we were currently trying to abort this connection then mark the abort
288 * as completed. For all cases clean-up and wake-up the client thread.
290 else if ( sistReg
& UDC
)
292 if ( srb
->srbAdapterStatus
== kSCSIAdapterStatusSuccess
)
294 srb
->srbAdapterStatus
= kSCSIAdapterStatusProtocolError
;
296 adapter
->nexusPtrsVirt
[nexusIndex
] = (Nexus
*) -1;
297 adapter
->nexusPtrsPhys
[nexusIndex
] = (Nexus
*) -1;
299 if ( scriptPhase
== A_kphase_ABORT_CURRENT
)
301 abortCurrentSRB
= NULL
;
304 Sym8xxCompleteSRB( srb
);
306 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_select_phase
];
312 * If we are in MsgOut phase then calculate how much of the message we sent. For
313 * now, however, we dont handle the target rejecting messages, so the request is aborted.
315 * If we are in DataIn/DataOut phase. We update the SRB/Nexus with our current data
318 else if ( sistReg
& MA
)
320 if ( scriptPhase
== A_kphase_MSG_OUT
)
322 srb
->srbMsgResid
= Sym8xxCheckFifo( srb
, &fifoCnt
);
323 nexus
->msg
.ppData
= OSSwapHostToLittleInt32( OSSwapHostToLittleInt32(nexus
->msg
.ppData
)
324 + OSSwapHostToLittleInt32(nexus
->msg
.length
)
325 - srb
->srbMsgResid
);
326 nexus
->msg
.length
= OSSwapHostToLittleInt32( srb
->srbMsgResid
);
328 Sym8xxAbortCurrent( srb
);
330 else if ( (scriptPhase
== A_kphase_DATA_OUT
) || (scriptPhase
== A_kphase_DATA_IN
) )
332 Sym8xxAdjustDataPtrs( srb
, nexus
);
336 IOLog("SCSI(Symbios8xx): Unexpected phase mismatch - scriptPhase = %08x\n\r", (int)scriptPhase
);
337 Sym8xxAbortCurrent( srb
);
346 * Clean-up the current request.
348 else if ( sistReg
& STO
)
350 srb
->srbAdapterStatus
= kSCSIAdapterStatusSelectionTimeout
;
352 adapter
->nexusPtrsVirt
[nexusIndex
] = (Nexus
*) -1;
353 adapter
->nexusPtrsPhys
[nexusIndex
] = (Nexus
*) -1;
354 SCRIPT_VAR(R_ld_IOdone_mailbox
) = 0;
356 Sym8xxCompleteSRB( srb
);
358 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_select_phase
];
362 * Handle script initiated interrupts
364 else if ( dstatReg
& SIR
)
366 dspsReg
= Sym8xxReadRegs( chipBaseAddr
, DSPS
, DSPS_SIZE
);
368 // printf( "SCSI(Symbios8xx): DSPS = %08x\n\r", dspsReg );
373 * Non-zero SCSI status
375 * Send request sense CDB or complete request depending on SCSI status value
378 Sym8xxProcessIODone();
382 * Received SDTR/WDTR message from target.
384 * Prepare reply message if we requested negotiation. Otherwise reject
385 * target initiated negotiation.
387 case A_negotiateSDTR
:
388 Sym8xxNegotiateSDTR( srb
, nexus
);
391 case A_negotiateWDTR
:
392 Sym8xxNegotiateWDTR( srb
, nexus
);
396 * Partial SG List completed.
398 * Refresh the list from the remaining addresses to be transfered and set the
399 * script engine to branch into the list.
401 case A_sglist_complete
:
402 Sym8xxUpdateSGList( srb
);
403 scriptRestartAddr
= (UInt32
)&srb
->srbPhys
->nexus
.sgListData
[2];
407 * Completed abort request
409 * Clean-up the aborted request.
411 case A_abort_current
:
412 adapter
->nexusPtrsVirt
[nexusIndex
] = (Nexus
*) -1;
413 adapter
->nexusPtrsPhys
[nexusIndex
] = (Nexus
*) -1;
415 abortCurrentSRB
= NULL
;
417 Sym8xxCompleteSRB( srb
);
419 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_select_phase
];
423 * Script detected protocol errors
425 * Abort the current request.
427 case A_unknown_phase
:
428 srb
->srbAdapterStatus
= kSCSIAdapterStatusProtocolError
;
429 Sym8xxAbortCurrent( srb
);
432 case A_unknown_msg_reject
:
433 case A_unexpected_msg
:
434 case A_unexpected_ext_msg
:
435 srb
->srbAdapterStatus
= kSCSIAdapterStatusMsgReject
;
436 Sym8xxAbortCurrent( srb
);
440 IOLog( "SCSI(Symbios8xx): Unknown Script Int = %08x\n\r", (int)dspsReg
);
441 Sym8xxAbortCurrent( srb
);
446 * Illegal script instruction.
448 * We're toast! Abort the current request and hope for the best!
450 else if ( dstatReg
& IID
)
452 dspReg
= Sym8xxReadRegs( chipBaseAddr
, DSP
, DSP_SIZE
);
454 IOLog("SCSI(Symbios8xx): Illegal script instruction - dsp = %08x srb=%08x\n\r", (int)dspReg
, (int)srb
);
456 Sym8xxAbortCurrent( srb
);
459 if ( scriptRestartAddr
)
461 Sym8xxWriteRegs( chipBaseAddr
, DSP
, DSP_SIZE
, scriptRestartAddr
);
466 /*-----------------------------------------------------------------------------*
467 * Current Data Pointer calculations
469 * To do data transfers the driver generates a list of script instructions
470 * in system storage to deliver data to the requested physical addresses. The
471 * script branches to the list when the target enters data transfer phase.
473 * When the target changes phase during a data transfer, data is left trapped
474 * inside the various script engine registers. This routine determines how much
475 * data was not actually transfered to/from the target and generates a new
476 * S/G List entry for the partial transfer and a branch back into the original
477 * S/G list. These script instructions are stored in two reserved slots at the
478 * top of the original S/G List.
480 *-----------------------------------------------------------------------------*/
481 void Sym8xxSCSIController::Sym8xxAdjustDataPtrs( SRB
*srb
, Nexus
*nexus
)
492 * Determine SG element residual
494 * This routine returns how much of the current S/G List element the
495 * script was processing remains to be sent/received. All the information
496 * required to do this is stored in the script engine's registers.
498 sgResid
= Sym8xxCheckFifo( srb
, &fifoCnt
);
501 * Determine which script instruction in our SGList we were executing when
502 * the target changed phase.
504 * The script engine's dspReg tells us where the script thinks it was. Based
505 * on the physical address of our current SRB/Nexus we can calculate
506 * an index into our S/G List.
508 dspReg
= Sym8xxReadRegs( chipBaseAddr
, DSP
, DSP_SIZE
);
510 i
= ((dspReg
- (UInt32
)srb
->srbPhys
->nexus
.sgListData
) / sizeof(SGEntry
)) - 1;
512 if ( i
> MAX_SGLIST_ENTRIES
-1 )
514 IOLog("SCSI(Symbios8xx): Bad sgListIndex\n\r");
515 Sym8xxAbortCurrent( srb
);
520 * Wide/odd-byte transfers.
522 * When dealing with Wide data transfers, if a S/G List ends with an odd-transfer count, then a
523 * valid received data byte is left in the script engine's SWIDE register. The least painful way
524 * to recover this byte is to construct a small script thunk to transfer one additional byte. The
525 * script will automatically draw this byte from the SWIDE register rather than the SCSI bus.
526 * The script thunk then branches back to script's PhaseHandler entrypoint.
529 nexusPhys
= &srb
->srbPhys
->nexus
;
531 scntl2Reg
= Sym8xxReadRegs( chipBaseAddr
, SCNTL2
, SCNTL2_SIZE
);
532 if ( scntl2Reg
& WSR
)
534 adapter
->xferSWideInst
[0] = OSSwapHostToLittleInt32( srb
->directionMask
| 1 );
535 adapter
->xferSWideInst
[1] = nexus
->sgListData
[i
].physAddr
;
536 adapter
->xferSWideInst
[2] = OSSwapHostToLittleInt32( 0x80080000 );
537 adapter
->xferSWideInst
[3] = OSSwapHostToLittleInt32( (UInt32
)&chipRamAddrPhys
[Ent_phase_handler
] );
539 scriptRestartAddr
= (UInt32
) adapterPhys
->xferSWideInst
;
542 * Note: There is an assumption here that the sgResid count will be > 1. It appears
543 * that the script engine does not generate a phase-mismatch interrupt until
544 * we attempt to move > 1 byte from the SCSI bus and the only byte available is
551 * Calculate partial S/G List instruction and branch
553 * Fill in slots 0/1 of the SGList based on the SGList index (i) and SGList residual count
554 * (sgResid) calculated above.
557 sgDone
= (OSSwapHostToLittleInt32( nexus
->sgListData
[i
].length
) & 0x00ffffff) - sgResid
;
559 nexus
->sgListData
[0].length
= OSSwapHostToLittleInt32( sgResid
| srb
->directionMask
);
560 nexus
->sgListData
[0].physAddr
= OSSwapHostToLittleInt32( OSSwapHostToLittleInt32(nexus
->sgListData
[i
].physAddr
) + sgDone
);
562 * If a previously calculated SGList 0 entry was interrupted again, we dont need to calculate
563 * a new branch address since the previous one is still valid.
567 nexus
->sgListData
[1].length
= OSSwapHostToLittleInt32( 0x80080000 );
568 nexus
->sgListData
[1].physAddr
= OSSwapHostToLittleInt32( (UInt32
)&nexusPhys
->sgListData
[i
+1] );
569 nexus
->sgNextIndex
= i
+ 1;
571 nexus
->ppSGList
= (SGEntry
*)OSSwapHostToLittleInt32( (UInt32
) &nexusPhys
->sgListData
[0] );
574 * The script sets this Nexus variable to non-zero each time it calls the driver generated
575 * S/G list. This allows the driver's completion routines to differentiate between a successful
576 * transfer vs no data transfer at all.
578 nexus
->dataXferCalled
= 0;
583 /*-----------------------------------------------------------------------------*
584 * Determine SG element residual
586 * This routine returns how much of the current S/G List element the
587 * script was processing remains to be sent/received. All the information
588 * required to do this is stored in the script engine's registers.
590 *-----------------------------------------------------------------------------*/
591 UInt32
Sym8xxSCSIController::Sym8xxCheckFifo( SRB
*srb
, UInt32
*pfifoCnt
)
595 UInt32 scriptPhase
= 0;
598 UInt32 ctest5Reg
= 0;
605 scriptPhase
= OSSwapHostToLittleInt32( SCRIPT_VAR(R_ld_phase_flag
) );
607 fSCSISend
= (scriptPhase
== A_kphase_DATA_OUT
) || (scriptPhase
== A_kphase_MSG_OUT
);
609 fXferSync
= ((scriptPhase
== A_kphase_DATA_OUT
) || (scriptPhase
== A_kphase_DATA_IN
))
610 && (srb
->nexus
.targetParms
.sxferReg
& 0x1F);
612 dbcReg
= Sym8xxReadRegs( chipBaseAddr
, DBC
, DBC_SIZE
) & 0x00ffffff;
614 if ( !(dstatReg
& DFE
) )
616 ctest5Reg
= Sym8xxReadRegs( chipBaseAddr
, CTEST5
, CTEST5_SIZE
);
617 dfifoReg
= Sym8xxReadRegs( chipBaseAddr
, DFIFO
, DFIFO_SIZE
);
619 if ( ctest5Reg
& DFS
)
621 fifoCnt
= ((((ctest5Reg
& 0x03) << 8) | dfifoReg
) - dbcReg
) & 0x3ff;
625 fifoCnt
= (dfifoReg
- dbcReg
) & 0x7f;
629 sstat0Reg
= Sym8xxReadRegs( chipBaseAddr
, SSTAT0
, SSTAT0_SIZE
);
630 sstat2Reg
= Sym8xxReadRegs( chipBaseAddr
, SSTAT2
, SSTAT2_SIZE
);
634 fifoCnt
+= (sstat0Reg
& OLF
) ? 1 : 0;
635 fifoCnt
+= (sstat2Reg
& OLF1
) ? 1 : 0;
639 fifoCnt
+= (sstat0Reg
& ORF
) ? 1 : 0;
640 fifoCnt
+= (sstat2Reg
& ORF1
) ? 1 : 0;
647 sstat1Reg
= Sym8xxReadRegs( chipBaseAddr
, SSTAT0
, SSTAT0_SIZE
);
648 fifoCnt
+= (sstat1Reg
>> 4) | (sstat2Reg
& FF4
);
652 fifoCnt
+= (sstat0Reg
& ILF
) ? 1 : 0;
653 fifoCnt
+= (sstat2Reg
& ILF1
) ? 1 : 0;
657 sgResid
= dbcReg
+ fifoCnt
;
663 /*-----------------------------------------------------------------------------*
664 * Calculate transfer counts.
666 * This routine updates srb->xferDone with the amount of data transferred
667 * by the last S/G List executed.
669 *-----------------------------------------------------------------------------*/
670 void Sym8xxSCSIController::Sym8xxUpdateXferOffset( SRB
*srb
)
676 * srb->xferOffset contains the client buffer offset INCLUDING the range
677 * covered by the current SGList.
679 xferOffset
= srb
->xferOffset
;
682 * If script did not complete the current transfer list then we need to determine
683 * how much of the list was completed.
685 if ( srb
->nexus
.dataXferCalled
== 0 )
688 * srb->xferOffsetPrev contains the client buffer offset EXCLUDING the
689 * range covered by the current SGList.
691 xferOffset
= srb
->xferOffsetPrev
;
694 * Calculate bytes transferred for partially completed list.
696 * To calculate the amount of this list completed, we sum the residual amount
697 * in SGList Slot 0 and the completed list elements 2 to sgNextIndex-1.
699 if ( srb
->nexus
.sgNextIndex
!= 0 )
701 xferOffset
+= OSSwapHostToLittleInt32( srb
->nexus
.sgListData
[srb
->nexus
.sgNextIndex
-1].length
)
702 - OSSwapHostToLittleInt32( srb
->nexus
.sgListData
[0].length
);
704 for ( i
=2; i
< srb
->nexus
.sgNextIndex
-1; i
++ )
706 xferOffset
+= OSSwapHostToLittleInt32( srb
->nexus
.sgListData
[i
].length
) & 0x00ffffff;
712 * The script leaves the result of any Ignore Wide Residual message received from the target
713 * during the transfer.
715 xferOffset
-= srb
->nexus
.wideResidCount
;
720 UInt32 resid
= srb
->xferOffset
- xferOffset
;
723 IOLog( "SCSI(Symbios8xx): Incomplete transfer - Req Count = %08x Act Count = %08x - srb = %08x\n\r",
724 srb
->xferCount
, xferOffset
, (UInt32
)srb
);
729 srb
->xferDone
= xferOffset
;
732 /*-----------------------------------------------------------------------------*
733 * No SRB/Nexus Processing.
735 * In some cases (mainly Aborts) not having a SRB/Nexus is normal. In other
736 * cases it indicates a problem such a reconnection from a target that we
739 *-----------------------------------------------------------------------------*/
740 void Sym8xxSCSIController::Sym8xxProcessNoNexus()
744 UInt32 scriptPhase
= (UInt32
)-1 ;
746 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_select_phase
];
748 dspsReg
= Sym8xxReadRegs( chipBaseAddr
, DSPS
, DSPS_SIZE
);
750 scriptPhase
= OSSwapHostToLittleInt32( SCRIPT_VAR(R_ld_phase_flag
) );
753 * If we were trying to abort or disconnect a target and the bus
754 * is now free we consider the abort to have completed.
758 if ( (scriptPhase
== A_kphase_ABORT_MAILBOX
) && abortSRB
)
760 Sym8xxCompleteSRB( abortSRB
);
761 SCRIPT_VAR(R_ld_AbortBdr_mailbox
) = 0;
763 else if ( scriptPhase
== A_kphase_ABORT_CURRENT
)
765 abortCurrentSRB
= NULL
;
769 * If we were trying to connect to a target to send it an abort message, and
770 * we timed out, we consider the abort as completed.
772 * Note: In this case the target may be hung, but at least its not on the bus.
774 else if ( sistReg
& STO
)
776 if ( (scriptPhase
== A_kphase_ABORT_MAILBOX
) && abortSRB
)
778 Sym8xxCompleteSRB( abortSRB
);
779 SCRIPT_VAR(R_ld_AbortBdr_mailbox
) = 0;
784 * If the script died, without a vaild nexusIndex, we abort anything that is currently
785 * connected and hope for the best!
787 else if ( dstatReg
& IID
)
789 dspReg
= Sym8xxReadRegs( chipBaseAddr
, DSP
, DSP_SIZE
);
790 IOLog("SCSI(Symbios8xx): Illegal script instruction - dsp = %08x srb=0\n\r", (int)dspReg
);
791 Sym8xxAbortCurrent( (SRB
*)-1 );
795 * Script signaled conditions
797 else if ( dstatReg
& SIR
)
801 case A_abort_current
:
802 abortCurrentSRB
= NULL
;
805 case A_abort_mailbox
:
806 Sym8xxCompleteSRB( abortSRB
);
807 SCRIPT_VAR(R_ld_AbortBdr_mailbox
) = 0;
811 Sym8xxAbortCurrent( (SRB
*)-1 );
816 Sym8xxAbortCurrent( (SRB
*)-1 );
819 if ( scriptRestartAddr
)
821 Sym8xxWriteRegs( chipBaseAddr
, DSP
, DSP_SIZE
, scriptRestartAddr
);
826 /*-----------------------------------------------------------------------------*
827 * Abort currently connected target.
829 *-----------------------------------------------------------------------------*/
830 void Sym8xxSCSIController::Sym8xxAbortCurrent( SRB
*srb
)
832 if ( abortCurrentSRB
)
834 if ( abortCurrentSRB
!= srb
)
836 IOLog("SCSI(Symbios8xx): Multiple abort immediate SRBs - resetting\n\r");
837 Sym8xxSCSIBusReset( (SRB
*)0 );
842 abortCurrentSRB
= srb
;
844 if ( srb
!= (SRB
*)-1 )
846 if ( srb
->srbAdapterStatus
== kSCSIAdapterStatusSuccess
)
848 srb
->srbAdapterStatus
= kSCSIAdapterStatusProtocolError
;
853 * Issue abort or abort tag depending on whether the is a tagged request
855 SCRIPT_VAR(R_ld_AbortCode
) = OSSwapHostToLittleInt32( ((srb
!= (SRB
*)-1) && (srb
->nexus
.tag
>= MIN_SCSI_TAG
)) ? 0x0d : 0x06 );
856 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_issueAbort_BDR
];
861 /*-----------------------------------------------------------------------------*
862 * This routine clears the script engine's SCSI and DMA fifos.
864 *-----------------------------------------------------------------------------*/
865 void Sym8xxSCSIController::Sym8xxClearFifo()
871 stest2Reg
= Sym8xxReadRegs( chipBaseAddr
, STEST2
, STEST2_SIZE
);
872 if ( stest2Reg
& ROF
)
874 Sym8xxWriteRegs( chipBaseAddr
, STEST2
, STEST2_SIZE
, stest2Reg
);
877 ctest3Reg
= Sym8xxReadRegs( chipBaseAddr
, CTEST3
, CTEST3_SIZE
);
879 Sym8xxWriteRegs( chipBaseAddr
, CTEST3
, CTEST3_SIZE
, ctest3Reg
);
881 stest3Reg
= Sym8xxReadRegs( chipBaseAddr
, STEST3
, STEST3_SIZE
);
883 Sym8xxWriteRegs( chipBaseAddr
,STEST3
, STEST3_SIZE
, stest3Reg
);
887 ctest3Reg
= Sym8xxReadRegs( chipBaseAddr
, CTEST3
, CTEST3_SIZE
);
888 stest2Reg
= Sym8xxReadRegs( chipBaseAddr
, STEST3
, STEST3_SIZE
);
889 stest3Reg
= Sym8xxReadRegs( chipBaseAddr
, STEST3
, STEST3_SIZE
);
891 while( (ctest3Reg
& CLF
) || (stest3Reg
& CSF
) || (stest2Reg
& ROF
) );
894 /*-----------------------------------------------------------------------------*
895 * This routine processes the target's response to our SDTR message.
897 * We calculate the values for the script engine's timing registers
898 * for synchronous registers, and update our tables indicating that
899 * requested data transfer mode is in-effect.
901 *-----------------------------------------------------------------------------*/
902 void Sym8xxSCSIController::Sym8xxNegotiateSDTR( SRB
*srb
, Nexus
*nexus
)
909 * If we were not negotiating, the send MsgReject to targets negotiation
912 if ( !(srb
->srbCDBFlags
& kCDBFlagsNegotiateSDTR
) )
914 Sym8xxSendMsgReject( srb
);
919 * Get pointer to negotiation message received from target.
921 pMsg
= (UInt8
*) &SCRIPT_VAR(R_ld_message
);
924 * The target's SDTR response contains the (transfer period / 4).
926 * We set our sync clock divisor to 1, 2, or 4 giving us a clock rates
928 * 80Mhz (Period = 12.5ns),
929 * 40Mhz (Period = 25.0ns)
930 * 20Mhz (Period = 50.0ns)
932 * This is further divided by the value in the sxfer reg to give us the final sync clock rate.
934 * The requested sync period is scaled up by 1000 and the clock periods are scaled up by 10
935 * giving a result scaled up by 100. This is rounded-up and converted to sxfer reg values.
939 nexus
->targetParms
.scntl3Reg
&= 0x0f;
940 nexus
->targetParms
.sxferReg
= 0x00;
944 syncPeriod
= (UInt32
)pMsg
[3] << 2;
945 if ( syncPeriod
< 100 )
947 nexus
->targetParms
.scntl3Reg
|= SCNTL3_INIT_875_ULTRA
;
948 x
= (syncPeriod
* 1000) / 125;
950 else if ( syncPeriod
< 200 )
952 nexus
->targetParms
.scntl3Reg
|= SCNTL3_INIT_875_FAST
;
953 x
= (syncPeriod
* 1000) / 250;
957 nexus
->targetParms
.scntl3Reg
|= SCNTL3_INIT_875_SLOW
;
958 x
= (syncPeriod
* 1000) / 500;
961 if ( x
% 100 ) x
+= 100;
964 * sxferReg Bits: 5-0 - Transfer offset
965 * 7-6 - Sync Clock Divisor (0 = sync clock / 4)
967 nexus
->targetParms
.sxferReg
= ((x
/100 - 4) << 5) | pMsg
[4];
969 transferPeriod
= syncPeriod
* 1000;
970 transferOffset
= pMsg
[4];
972 srb
->negotiateSDTRComplete
= true;
976 * Update our per-target tables and set-up the hardware regs for this request.
978 * On reconnection attempts, the script will use our per-target tables to set-up
979 * the scntl3 and sxfer registers in the script engine.
981 adapter
->targetClocks
[srb
->target
].sxferReg
= nexus
->targetParms
.sxferReg
;
982 adapter
->targetClocks
[srb
->target
].scntl3Reg
= nexus
->targetParms
.scntl3Reg
;
984 Sym8xxWriteRegs( chipBaseAddr
, SCNTL3
, SCNTL3_SIZE
, nexus
->targetParms
.scntl3Reg
);
985 Sym8xxWriteRegs( chipBaseAddr
, SXFER
, SXFER_SIZE
, nexus
->targetParms
.sxferReg
);
987 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_clearACK
];
990 /*-----------------------------------------------------------------------------*
991 * This routine processes the target's response to our WDTR message.
993 * In addition, if there is a pending SDTR message, this routine sends it
996 *-----------------------------------------------------------------------------*/
997 void Sym8xxSCSIController::Sym8xxNegotiateWDTR( SRB
*srb
, Nexus
*nexus
)
1000 UInt32 msgBytesSent
;
1001 UInt32 msgBytesLeft
;
1004 * If we were not negotiating, the send MsgReject to targets negotiation
1007 if ( !(srb
->srbCDBFlags
& kCDBFlagsNegotiateWDTR
) )
1009 Sym8xxSendMsgReject( srb
);
1014 * Set Wide (16-bit) vs Narrow (8-bit) data transfer mode based on target's response.
1016 pMsg
= (UInt8
*) &SCRIPT_VAR(R_ld_message
);
1020 nexus
->targetParms
.scntl3Reg
|= EWS
;
1025 nexus
->targetParms
.scntl3Reg
&= ~EWS
;
1030 * Update our per-target tables and set-up the hardware regs for this request.
1032 * On reconnection attempts, the script will use our per-target tables to set-up
1033 * the scntl3 and sxfer registers in the script engine.
1036 adapter
->targetClocks
[srb
->target
].scntl3Reg
= nexus
->targetParms
.scntl3Reg
;
1037 Sym8xxWriteRegs( chipBaseAddr
, SCNTL3
, SCNTL3_SIZE
, nexus
->targetParms
.scntl3Reg
);
1039 srb
->negotiateWDTRComplete
= true;
1042 * If there any pending messages left for the target, send them now,
1044 msgBytesSent
= OSSwapHostToLittleInt32( nexus
->msg
.length
);
1045 msgBytesLeft
= srb
->srbMsgLength
- msgBytesSent
;
1048 nexus
->msg
.length
= OSSwapHostToLittleInt32( msgBytesLeft
);
1049 nexus
->msg
.ppData
= OSSwapHostToLittleInt32( OSSwapHostToLittleInt32( nexus
->msg
.ppData
) + msgBytesSent
);
1050 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_issueMessageOut
];
1054 * Otherwise, tell the script we're done with MsgOut phase.
1058 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_clearACK
];
1062 /*-----------------------------------------------------------------------------*
1063 * Reject message received from target.
1065 *-----------------------------------------------------------------------------*/
1066 void Sym8xxSCSIController::Sym8xxSendMsgReject( SRB
*srb
)
1068 srb
->nexus
.msg
.ppData
= OSSwapHostToLittleInt32((UInt32
)&srb
->srbPhys
->nexus
.msgData
);
1069 srb
->nexus
.msg
.length
= OSSwapHostToLittleInt32(0x01);
1070 srb
->nexus
.msgData
[0] = 0x07;
1072 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_issueMessageOut
];
1076 /*-----------------------------------------------------------------------------*
1077 * This routine initiates a SCSI Bus Reset.
1079 * This may be an internally generated request as part of error recovery or
1080 * a client's bus reset request.
1082 *-----------------------------------------------------------------------------*/
1083 void Sym8xxSCSIController::Sym8xxSCSIBusReset( SRB
*srb
)
1089 srb
->srbReturnCode
= kIOReturnBusy
;
1090 Sym8xxCompleteSRB( srb
);
1096 Sym8xxAbortScript();
1098 Sym8xxWriteRegs( chipBaseAddr
, SCNTL1
, SCNTL1_SIZE
, SCNTL1_SCSI_RST
);
1100 Sym8xxWriteRegs( chipBaseAddr
, SCNTL1
, SCNTL1_SIZE
, SCNTL1_INIT
);
1103 /*-----------------------------------------------------------------------------*
1104 * This routine handles a SCSI Bus Reset interrupt.
1106 * The SCSI Bus reset may be generated by a target on the bus, internally from
1107 * the driver's error recovery or from a client request.
1109 * Once the reset is detected we establish a settle period where new client requests
1110 * are blocked in the client thread. In addition we flush all currently executing
1111 * scsi requests back to the client.
1113 *-----------------------------------------------------------------------------*/
1114 void Sym8xxSCSIController::Sym8xxProcessSCSIBusReset()
1121 * We clear the script's request mailboxes. Any work in the script mailboxes is
1122 * already in the NexusPtr tables so we have already have handled the SRB/Nexus
1125 for ( i
=0; i
< MAX_SCHED_MAILBOXES
; i
++ )
1127 adapter
->schedMailBox
[i
] = 0;
1130 SCRIPT_VAR(R_ld_AbortBdr_mailbox
) = 0;
1131 SCRIPT_VAR(R_ld_IOdone_mailbox
) = 0;
1132 SCRIPT_VAR(R_ld_counter
) = 0;
1137 * Reset the data transfer mode/clocks in our per-target tables back to Async/Narrow 8-bit
1139 for ( i
=0; i
< MAX_SCSI_TARGETS
; i
++ )
1141 adapter
->targetClocks
[i
].scntl3Reg
= SCNTL3_INIT_875
;
1142 adapter
->targetClocks
[i
].sxferReg
= 0;
1145 scriptRestartAddr
= (UInt32
) &chipRamAddrPhys
[Ent_select_phase
];
1146 Sym8xxWriteRegs( chipBaseAddr
, DSP
, DSP_SIZE
, scriptRestartAddr
);
1150 resetSRB
->srbReturnCode
= kIOReturnBusy
;
1151 Sym8xxCompleteSRB( resetSRB
);
1154 else if ( initialReset
== true )
1156 initialReset
= false;
1164 /*-----------------------------------------------------------------------------*
1165 * This routine sets the SIGP bit in the script engine's ISTAT
1166 * register. This signals the script to wake-up for a WAIT for
1167 * reselection instruction. The script will then check the mailboxes
1170 *-----------------------------------------------------------------------------*/
1171 void Sym8xxSCSIController::Sym8xxSignalScript( SRB
*srb
)
1173 Sym8xxWriteRegs( chipBaseAddr
, ISTAT
, ISTAT_SIZE
, SIGP
);
1176 /*-----------------------------------------------------------------------------*
1182 *-----------------------------------------------------------------------------*/
1183 void Sym8xxSCSIController::Sym8xxCheckRequestSense( SRB
*srb
)
1185 IOSCSIParallelCommand
*scsiCommand
;
1186 IOMemoryDescriptor
*reqSenseDesc
;
1188 scsiCommand
= srb
->scsiCommand
;
1190 scsiCommand
->getPointers( &reqSenseDesc
, 0, 0, true );
1192 if ( reqSenseDesc
!= 0 )
1194 Sym8xxCancelMailBox( srb
->target
, srb
->lun
, true );
1198 /*-----------------------------------------------------------------------------*
1199 * This routine does a mailbox abort.
1201 * This type of abort is used for targets not currently connected to the SCSI Bus.
1203 * The script will select the target and send a tag (if required) followed by the
1204 * appropriate abort message (abort/abort-tag)
1206 *-----------------------------------------------------------------------------*/
1207 void Sym8xxSCSIController::Sym8xxAbortBdr( SRB
*srb
)
1209 IOAbortBdrMailBox abortMailBox
;
1214 * Setup a script variable containing the abort information.
1216 abortMailBox
.identify
= srb
->nexus
.msgData
[0];
1217 abortMailBox
.tag
= srb
->nexus
.msgData
[1];
1218 abortMailBox
.message
= srb
->nexus
.msgData
[2];
1219 abortMailBox
.scsi_id
= srb
->target
;
1221 SCRIPT_VAR(R_ld_AbortBdr_mailbox
) = *(UInt32
*) &abortMailBox
;
1223 Sym8xxSignalScript( srb
);
1226 /*-----------------------------------------------------------------------------*
1231 *-----------------------------------------------------------------------------*/
1232 bool Sym8xxSCSIController::Sym8xxCancelMailBox( Nexus
*nexusCancel
)
1237 nexusPhys
= (Nexus
*)OSSwapHostToLittleInt32( (UInt32
)nexusCancel
);
1238 for ( i
=0; i
< MAX_SCHED_MAILBOXES
; i
++ )
1240 if ( nexusPhys
== adapter
->schedMailBox
[i
] )
1242 adapter
->schedMailBox
[i
] = (Nexus
*)OSSwapHostToLittleInt32( kMailBoxCancel
);
1250 /*-----------------------------------------------------------------------------*
1255 *-----------------------------------------------------------------------------*/
1256 void Sym8xxSCSIController::Sym8xxCancelMailBox( UInt32 target
, UInt32 lun
, bool fReschedule
)
1268 tagPos
= offsetof(Nexus
, tag
) & 0x03;
1269 tagShift
= 24 - (tagPos
<< 3);
1271 for ( i
=0; i
< MAX_SCHED_MAILBOXES
; i
++ )
1273 nexusPhys
= (Nexus
*)OSSwapHostToLittleInt32( (UInt32
)adapter
->schedMailBox
[i
] );
1274 if ( (nexusPhys
!= (Nexus
*)kMailBoxEmpty
) && (nexusPhys
!= (Nexus
*)kMailBoxCancel
) )
1277 * Read the 'tag' byte given Nexus physical address from the mailBox.
1278 * Look-up the virtual address of the corresponding Nexus struct.
1280 tag
= ml_phys_read((UInt32
)&nexusPhys
->tag
- tagPos
);
1281 tag
= (tag
>> tagShift
) & 0xff;
1283 nexus
= adapter
->nexusPtrsVirt
[tag
];
1284 if ( nexus
== (Nexus
*)-1 )
1290 * If the SCSI target of the mailbox entry matches the abort SRB target,
1291 * then we may have a winner.
1293 srb
= (SRB
*)((UInt32
)nexus
- offsetof(SRB
, nexus
));
1295 if ( srb
->target
== target
)
1298 * For a device reset, we cancel all requests for that target regardless of lun.
1299 * For an abort all, we must match on both target and lun
1301 if ( (lun
== (UInt32
)-1) || (srb
->lun
== lun
) )
1303 adapter
->schedMailBox
[i
] = (Nexus
*)OSSwapHostToLittleInt32( kMailBoxCancel
);
1305 if ( fReschedule
== true )
1307 rescheduleCommand( srb
->scsiCommand
);
1315 /*-----------------------------------------------------------------------------*
1316 * This routine is used to shutdown the script engine in an orderly fashion.
1318 * Normally the script engine automatically stops when an interrupt is generated. However,
1319 * in the case of timeouts we need to change the script engine's dsp reg (instruction pointer).
1320 * to issue an abort.
1322 *-----------------------------------------------------------------------------*/
1323 void Sym8xxSCSIController::Sym8xxAbortScript()
1325 mach_timespec_t currentTime
;
1326 mach_timespec_t startTime
;
1328 getWorkLoop()->disableAllInterrupts();
1331 * We set the ABRT bit in ISTAT and spin until the script engine acknowledges the
1332 * abort or we timeout.
1334 Sym8xxWriteRegs( chipBaseAddr
, ISTAT
, ISTAT_SIZE
, ABRT
);
1336 IOGetTime( &startTime
);
1340 IOGetTime( ¤tTime
);
1341 SUB_MACH_TIMESPEC( ¤tTime
, &startTime
);
1343 istatReg
= Sym8xxReadRegs( chipBaseAddr
, ISTAT
, ISTAT_SIZE
);
1345 if ( istatReg
& SIP
)
1347 Sym8xxReadRegs( chipBaseAddr
, SIST
, SIST_SIZE
);
1351 if ( istatReg
& DIP
)
1353 Sym8xxWriteRegs( chipBaseAddr
, ISTAT
, ISTAT_SIZE
, 0x00 );
1354 Sym8xxReadRegs( chipBaseAddr
, DSTAT
, DSTAT_SIZE
);
1358 while ( currentTime
.tv_nsec
< (kAbortScriptTimeoutMS
* 1000 * 1000) );
1361 Sym8xxWriteRegs( chipBaseAddr
, ISTAT
, ISTAT_SIZE
, istatReg
);
1363 getWorkLoop()->enableAllInterrupts();
1365 if ( currentTime
.tv_nsec
>= (kAbortScriptTimeoutMS
* 1000 * 1000) )
1367 IOLog( "SCSI(Symbios8xx): Abort script failed - resetting bus\n\r" );