#include <IOKit/scsi/IOSCSIParallelInterface.h>
#include <IOKit/pci/IOPCIDevice.h>
#include <mach/clock_types.h>
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/ppc/IODBDMA.h>
#include "mesh.h"
extern void IOGetTime( mach_timespec_t *clock_time );
extern void kprintf( const char *, ... );
extern void call_kdp();
#define super IOSCSIParallelController
OSDefineMetaClassAndStructors( meshSCSIController, IOSCSIParallelController ) ;
static globals g;
static const DBDMADescriptor gDescriptorList[] =
{
{ MESH_REG( kMeshInterruptMask, kMeshIntrMask ) }, { STOP( kcclStageCCLx ) },
{ RESERVE }, { RESERVE }, { RESERVE }, { RESERVE }, { RESERVE },
{ RESERVE }, { RESERVE }, { RESERVE }, { RESERVE }, { RESERVE },
{ MOVE_4( kcclBatchSize, 0, kRelAddressCP ) }, { MESH_REG( kMeshTransferCount1, 0 ) }, { MESH_REG( kMeshTransferCount0, 0 ) }, { MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA )},
{ RESERVE }, { RESERVE },
{ STAGE( kcclStageArb ) },
{ MESH_REG( kMeshSequence, kMeshArbitrateCmd ) }, { BR_IF_PROBLEM },
{ STAGE( kcclStageSelA ) },
{ CLEAR_CMD_DONE },
{ MESH_REG( kMeshSequence, kMeshSelectCmd | kMeshSeqAtn ) }, { BR_IF_PROBLEM },
{ STAGE( kcclStageMsgO ) },
{ CLEAR_CMD_DONE },
{ BRANCH( kcclLastMsgo ) },
{ MESH_REG( kMeshTransferCount1, 0x00 ) }, { MESH_REG( kMeshTransferCount0, 0xFF ) }, { MESH_REG( kMeshSequence, kMeshMessageOutCmd | kMeshSeqAtn | kMeshSeqDMA ) }, { MSGO( kcclMSGOdata, 255 ) }, { CLEAR_CMD_DONE },
{ MESH_REG( kMeshInterruptMask, 0 ) }, { MESH_REG_WAIT( kMeshSequence, kMeshStatusCmd | kMeshSeqAtn ) }, { CLEAR_INT_REG }, { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) },
{ MESH_REG( kMeshTransferCount1, 0x00 ) },
{ MESH_REG( kMeshTransferCount0, 0x01 ) },
{ MESH_REG( kMeshSequence, kMeshMessageOutCmd | kMeshSeqDMA ) }, { MSGO( kcclMSGOLast, 1 ) },
{ STAGE( kcclStageCmdO ) },
{ CLEAR_CMD_DONE },
{ MESH_REG( kMeshTransferCount1, 0x00 ) },
{ MESH_REG( kMeshTransferCount0, 0x06 ) }, { MESH_REG( kMeshSequence, kMeshCommandCmd | kMeshSeqDMA )}, { CMDO( 6 ) },
{ CLEAR_CMD_DONE },
{ STAGE( kcclStageXfer ) },
{ BRANCH( kcclDataXfer ) },
{ STAGE( kcclStageBucket ) },
{ MESH_REG( kMeshTransferCount1, 0x00 ) }, { MESH_REG( kMeshTransferCount0, 0x00 ) },
{ CLR_PHASEMM },
{ MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, { MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA ) }, { BUCKET }, { BR_NO_PROBLEM( kcclOverrunDBDMA ) }, { BR_IF_PROBLEM },
{ CLEAR_INT_REG }, { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) },
{ STAGE( kcclStageStat ) },
{ MESH_REG( kMeshTransferCount1, 0x00 ) },
{ MESH_REG( kMeshTransferCount0, 0x01 ) }, { MESH_REG( kMeshSequence, kMeshStatusCmd | kMeshSeqDMA )}, { STATUS_IN },
{ STAGE( kcclStageMsgI ) },
{ CLEAR_CMD_DONE },
{ MESH_REG( kMeshTransferCount1, 0x00 ) },
{ MESH_REG( kMeshTransferCount0, 0x01 ) }, { MESH_REG( kMeshSequence, kMeshMessageInCmd | kMeshSeqDMA )}, { MSGI( 1 ) },
{ STAGE( kcclStageFree ) },
{ CLEAR_CMD_DONE },
{ MESH_REG( kMeshSequence, kMeshEnableReselect ) }, { MESH_REG( kMeshSequence, kMeshBusFreeCmd ) }, { BR_IF_PROBLEM },
{ STAGE( kcclStageGood ) },
{ MESH_REG( kMeshInterruptMask, kMeshIntrMask ) }, { STOP( kcclStageStop ) },
};
const UInt32 gDescriptorListSize = sizeof( gDescriptorList );
#define MAX_DMA_XFER 0x0000F000 // round down to nearest page
enum
{
kLevelISR = 0x80,
kLevelLocked = 0x40,
kLevelSIH = 0x20,
kLevelLatched = 0x10
};
#if USE_ELG
static void AllocateEventLog( UInt32 size )
{
if ( g.evLogBuf )
return;
g.evLogFlag = 0;
g.evLogBuf = (UInt8*)IOMalloc( size );
if ( !g.evLogBuf )
{
kprintf( "probe - MESH evLog allocation failed " );
return;
}
bzero( g.evLogBuf, size );
g.evLogBufp = g.evLogBuf;
g.evLogBufe = g.evLogBufp + kEvLogSize - 0x20; g.evLogFlag = 0x0333;
IOLog( "AllocateEventLog - &globals=%8x buffer=%8x\n",
(UInt32)&g, (UInt32)g.evLogBuf );
return;
}
static void EvLog( UInt32 a, UInt32 b, UInt32 ascii, char* str )
{
register UInt32 *lp;
mach_timespec_t time;
if ( g.evLogFlag == 0 )
return;
IOGetTime( &time );
lp = (UInt32*)g.evLogBufp;
g.evLogBufp += 0x10;
if ( g.evLogBufp >= g.evLogBufe )
{ g.evLogBufp = g.evLogBuf;
if ( g.evLogFlag != 0xFEEDBEEF ) g.evLogFlag = 0;
}
*lp++ = (g.intLevel << 24) | ((time.tv_nsec >> 10) & 0x003FFFFF); *lp++ = a;
*lp++ = b;
*lp = ascii;
if( g.evLogFlag == 'step' )
{ static char code[ 5 ] = {0,0,0,0,0};
*(UInt32*)&code = ascii;
kprintf( "%8x mesh: %8x %8x %s\n", time.tv_nsec>>10, a, b, code );
IOLog( "%8x MESH: %8x %8x %s\n",
time.tv_nsec>>10, (unsigned int)a, (unsigned int)b, code );
}
return;
}
static void Pause( UInt32 a, UInt32 b, UInt32 ascii, char* str )
{
char work [ 256 ];
char name[] = "meshSCSIController:";
char *bp = work;
UInt8 x;
int i;
EvLog( a, b, ascii, str );
EvLog( '****', '** P', 'ause', "*** Pause" );
bcopy( name, bp, sizeof( name ) );
bp += sizeof( name ) - 1;
*bp++ = '{'; for ( i = 7; i >= 0; --i )
{
x = a & 0x0F;
if ( x < 10 )
x += '0';
else x += 'A' - 10;
bp[ i ] = x;
a >>= 4;
}
bp += 8;
*bp++ = ' ';
for ( i = 7; i >= 0; --i )
{
x = b & 0x0F;
if ( x < 10 )
x += '0';
else x += 'A' - 10;
bp[ i ] = x;
b >>= 4;
}
bp += 8;
*bp++ = '}';
*bp++ = ' ';
for ( i = sizeof( work ) - (int)(bp - work); i && (*bp++ = *str++); --i ) ;
Debugger( work );
return;
}
#endif // USE_ELG
bool meshSCSIController::configure( IOService *provider,
SCSIControllerInfo *controllerInfo )
{
IOReturn ioReturn = kIOReturnInternalError;
g.intLevel = 0;
g.meshInstance = this;
#if USE_ELG
AllocateEventLog( kEvLogSize );
ELG( g.evLogBufp, &g.evLogFlag, 'MESH', "configure - event logging set up." );
#endif
ELG( this, provider, 'Cnfg', "configure" );
fProvider = (IOPCIDevice*)provider;
ioReturn = initializeHardware();
if ( ioReturn != kIOReturnSuccess )
return false;
fInterruptEvent = IOInterruptEventSource::interruptEventSource(
(OSObject*)this,
(IOInterruptEventAction)&meshSCSIController::interruptOccurred,
provider,
0 );
if ( fInterruptEvent == NULL )
{
PAUSE( 0, 0, 'IES-', "registerMESHInterrupt - can't register interrupt action" );
return false;
}
getWorkLoop()->addEventSource( fInterruptEvent );
fInterruptEvent->enable();
fMemoryCursor = IOBigMemoryCursor::withSpecification( kMaxDMATransfer, kMaxDMATransfer );
if ( fMemoryCursor == NULL )
{
PAUSE( 0, kMaxDMATransfer, 'Mem-', "mesh::start - IOBigMemoryCursor::withSpecification NG" );
return false;
}
controllerInfo->initiatorId = 7;
controllerInfo->maxTargetsSupported = 8;
controllerInfo->maxLunsSupported = 8;
controllerInfo->minTransferPeriodpS = 100000;
controllerInfo->maxTransferOffset = 15;
controllerInfo->maxTransferWidth = 1;
controllerInfo->maxCommandsPerController= 0;
controllerInfo->maxCommandsPerTarget = 8; controllerInfo->maxCommandsPerLun = 0;
controllerInfo->tagAllocationMethod = kTagAllocationPerLun;
controllerInfo->maxTags = 256;
controllerInfo->commandPrivateDataSize = sizeof( PrivCmdData );
controllerInfo->disableCancelCommands = false;
#define number_of_power_states 2
static IOPMPowerState ourPowerStates[number_of_power_states] = {
{1,0,0,0,0,0,0,0,0,0,0,0},
{1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0}
};
PMinit();
registerPowerDriver(this,ourPowerStates,number_of_power_states);
getProvider()->joinPMtree(this);
return true;
}
IOReturn meshSCSIController::setPowerState( unsigned long powerStateOrdinal,
IOService *whatDevice )
{
if (powerStateOrdinal == 1)
setSCSIActiveTermState(true);
else
setSCSIActiveTermState(false);
return IOPMAckImplied;
}
void meshSCSIController:: setSCSIActiveTermState(bool enableTermPower)
{
IOService *heathrow;
heathrow = waitForService(serviceMatching("Heathrow"));
if(heathrow)
{
UInt32 heathrowFCROffset = 0x38;
UInt32 scsiEnMask = 1<<10;
UInt32 heathrowIDOffset = 0x34;
UInt32 scsiTermPowerMask = 1<<26;
heathrow->callPlatformFunction(OSSymbol::withCString("heathrow_safeWriteRegUInt32"),
false, (void *)heathrowFCROffset, (void *)scsiEnMask, enableTermPower ? (void *)scsiEnMask:0, 0);
heathrow->callPlatformFunction(OSSymbol::withCString("heathrow_safeWriteRegUInt32"),
false, (void *)heathrowIDOffset, (void *)scsiTermPowerMask, enableTermPower ? 0 : (void *)scsiTermPowerMask, 0);
}
}
void meshSCSIController::executeCommand( IOSCSIParallelCommand *scsiCommand )
{
SCSICDBInfo scsiCDB;
SCSITargetParms targetParms;
UInt8 msgByte;
UInt8 rc;
if ( fCmd || (g.intLevel & kLevelLatched) )
{
disableCommands();
rescheduleCommand( scsiCommand );
ELG( fCmd, scsiCommand, 'Busy', "executeCommand - bus busy so bounce this cmd" );
return;
}
fCmd = scsiCommand;
fCmdData = (PrivCmdData*)scsiCommand->getCommandData();
bzero( fCmdData, sizeof( *fCmdData ) );
scsiCommand->getTargetLun( &fCurrentTargetLun );
scsiCommand->getCDB( &scsiCDB );
scsiCommand->getDevice( kIOSCSIParallelDevice )->getTargetParms( &targetParms );
ELG( scsiCommand, *(UInt16*)&fCurrentTargetLun<<16 | (scsiCDB.cdbTag & 0xFFFF), 'Exec', "meshSCSIController::executeCommand" );
ELG( *(UInt32*)&scsiCDB.cdb[0], *(UInt32*)&scsiCDB.cdb[4] , '=CDB', "executeCommand - CDB" );
fMsgOutFlag = 0;
fMsgOutPtr = &fCCL[ kcclMSGOdata ];
msgByte = kSCSIMsgIdentify | kSCSIMsgEnableDisconnectMask | fCurrentTargetLun.lun;
if ( scsiCDB.cdbFlags & kCDBFlagsNoDisconnect )
msgByte &= ~kSCSIMsgEnableDisconnectMask;
*fMsgOutPtr++ = msgByte;
if ( scsiCDB.cdbTagMsg )
{
*fMsgOutPtr++ = scsiCDB.cdbTagMsg;
*fMsgOutPtr++ = scsiCDB.cdbTag;
ELG( 0, scsiCDB.cdbTagMsg<<16 | scsiCDB.cdbTag, ' tag', "meshSCSIController::executeCommand - tag" );
}
if ( scsiCDB.cdbAbortMsg )
{
ELG( scsiCommand->getOriginalCmd(), scsiCDB.cdbAbortMsg, 'Abor', "meshSCSIController::executeCommand - abort msg." );
*fMsgOutPtr++ = scsiCDB.cdbAbortMsg;
}
fCmdData->negotiatingSDTR = fCmdData->negotiatingSDTRComplete = false;
if ( scsiCDB.cdbFlags & kCDBFlagsNegotiateSDTR )
{
fCmdData->negotiatingSDTR = true;
*fMsgOutPtr++ = kSCSIMsgExtended;
*fMsgOutPtr++ = 3;
*fMsgOutPtr++ = kSCSIMsgSyncXferReq;
if( targetParms.transferPeriodpS < 100000 )
*fMsgOutPtr++ = 100000 / 4000;
else
*fMsgOutPtr++ = targetParms.transferPeriodpS / 4000;
*fMsgOutPtr++ = targetParms.transferOffset;
}
rc = startCommand();
if ( rc != kHardwareStartOK )
{
ELG( fCmd, 0, 'Exe-', "meshSCSIController::executeCommand - command bounced back" );
rescheduleCommand( fCmd );
fCmd = NULL;
}
return;
}
void meshSCSIController::cancelCommand( IOSCSIParallelCommand *scsiCommand )
{
IOSCSIParallelCommand *origCmd;
PrivCmdData *origCmdData;
SCSIResults results;
origCmd = scsiCommand->getOriginalCmd();
ELG( scsiCommand, origCmd, 'Can-', "meshSCSIController::cancelCommand" );
if ( origCmd )
{
origCmd->getResults( &results );
origCmdData = (PrivCmdData*)origCmd->getCommandData();
results.bytesTransferred = origCmdData->results.bytesTransferred;
origCmd->setResults( &results );
origCmd->complete();
}
scsiCommand->complete();
return;
}
void meshSCSIController::resetCommand( IOSCSIParallelCommand *scsiCommand )
{
ELG( scsiCommand, 0, 'Rst-', "meshSCSIController::resetCommand" );
resetBus();
fCmdData = (PrivCmdData*)scsiCommand->getCommandData();
bzero( &fCmdData->results, sizeof( fCmdData->results ) );
scsiCommand->setResults( &fCmdData->results );
scsiCommand->complete();
return;
}
IOReturn meshSCSIController::initializeHardware()
{
IOReturn ioReturn;
int i;
ELG( 0, 0, 'IniH', "initializeHardware" );
fInitiatorID = kInitiatorIDDefault;
fInitiatorIDMask = 1 << kInitiatorIDDefault;
for ( i = 0; i < 8; ++i)
fSyncParms[ i ] = kSyncParmsAsync;
ioReturn = getHardwareMemoryMaps();
if ( ioReturn == kIOReturnSuccess )
ioReturn = allocHdwAndChanMem();
if ( ioReturn == kIOReturnSuccess )
ioReturn = doHBASelfTest();
if ( ioReturn == kIOReturnSuccess )
{
ioReturn = resetBus();
fMESHAddr->sourceID = fInitiatorID;
}
return ioReturn;
}
IOReturn meshSCSIController::getHardwareMemoryMaps()
{
if ( !fSCSIMemoryMap )
{
fSCSIMemoryMap = fProvider->mapDeviceMemoryWithIndex( kMESHRegisterBase );
if ( !fSCSIMemoryMap )
{
ELG( 0, 0, 'Map-', "getHardwareMemoryMaps - can't map MESH." );
return kIOReturnInternalError;
}
fMESHPhysAddr = fSCSIMemoryMap->getPhysicalAddress();
fMESHAddr = (MeshRegister*)fSCSIMemoryMap->getVirtualAddress();
ELG( fMESHPhysAddr, fMESHAddr, '=MSH', "getHardwareMemoryMaps - MESH regs" );
g.meshAddr = (UInt32)fMESHAddr; }
if ( !fDBDMAMemoryMap )
{
fDBDMAMemoryMap = fProvider->mapDeviceMemoryWithIndex( kDBDMARegisterBase );
if ( !fDBDMAMemoryMap )
{
ELG( 0, 0, 'map-', "getHardwareMemoryMaps - can't map DBDMA." );
return kIOReturnInternalError;
}
dbdmaAddrPhys = fDBDMAMemoryMap->getPhysicalAddress();
dbdmaAddr = (UInt8*)fDBDMAMemoryMap->getVirtualAddress();
ELG( dbdmaAddrPhys, dbdmaAddr, '=DMA', "getHardwareMemoryMaps - DBDMA regs" );
#if CustomMiniMon
gMESH_DBDMA = (UInt32)dbdmaAddr;
gMESH_DBDMA_Phys = (UInt32)dbdmaAddrPhys;
#endif
}
return kIOReturnSuccess;
}
IOReturn meshSCSIController::allocHdwAndChanMem()
{
fSelectionTimeout = 250 / 10;
fCCLSize = page_size;
fCCL = (UInt8*)IOMallocContiguous( fCCLSize, page_size, &fCCLPhysAddr );
if ( !fCCL )
{ PAUSE( 0, fCCLSize, 'CCA-', "allocHdwAndChanMem - can't allocate channel command area.\n" );
return kIOReturnNoMemory;
}
fDBDMADescriptorMax = (fCCLSize - kcclDataXfer) / sizeof( DBDMADescriptor );
g.cclPhysAddr = (UInt32)fCCLPhysAddr; g.cclLogAddr = (UInt32)fCCL;
ELG( fCCLPhysAddr, fCCL, '=CCL', "allocHdwAndChanMem - CCL phys/logical addresses." );
initCP();
return kIOReturnSuccess;
}
void meshSCSIController::initCP()
{
register DBDMADescriptor *dst = (DBDMADescriptor*)fCCL;
register const DBDMADescriptor *src = gDescriptorList;
UInt32 i;
DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr;
ELG( src, dst, 'I CP', "initCP - Init the Channel Program" );
DBDMARegs->interruptSelect = 0x00000000;
DBDMARegs->waitSelect = 0x20002000;
DBDMARegs->branchSelect = 0xC000C000;
SynchronizeIO();
for ( i = 0; i < gDescriptorListSize; i += sizeof( DBDMADescriptor ) )
{
dst->operation = SWAP( src->operation );
switch ( src->result & kRelAddress )
{
case kRelAddressMESH:
dst->address = SWAP( src->address + fMESHPhysAddr );
break;
case kRelAddressCP:
dst->address = SWAP( src->address + (UInt32)fCCLPhysAddr );
break;
case kRelAddressPhys:
dst->address = SWAP( src->address );
break;
default:
dst->address = SWAP( src->address );
break;
}
switch ( src->result & kRelCmdDep )
{
case kRelCmdDepCP:
dst->cmdDep = SWAP( src->cmdDep + (UInt32)fCCLPhysAddr );
break;
case kRelCmdDepLabel:
dst->cmdDep = src->cmdDep;
break;
default:
dst->cmdDep = SWAP( src->cmdDep );
break;
}
dst->result = 0;
src++;
dst++;
}
return;
}
IOReturn meshSCSIController::doHBASelfTest()
{
IOReturn ioReturn = kIOReturnSuccess;
UInt8 tempByte;
ELG( fMESHPhysAddr, fMESHAddr, 'Test', "doHBASelfTest" );
if ( ioReturn == kIOReturnSuccess )
{
tempByte = fMESHAddr->MESHID & 0x1F;
if ( tempByte < kMeshMESHID_Value )
{
PAUSE( 0, tempByte, 'hba-', "doHBASelfTest - Invalid MESH chip ID .\n" );
ioReturn = kIOReturnNoDevice;
}
}
return ioReturn;
}
void meshSCSIController::interruptOccurred( IOInterruptEventSource *ies, int intCount )
{
g.intLevel |= kLevelISR;
g.intLevel &= ~kLevelLatched;
ELG( fCmd, *(UInt32*)&fCCL[ kcclStageLabel ], 'Int+', "interruptOccurred." );
doHardwareInterrupt();
g.intLevel &= ~kLevelISR;
return;
}
void meshSCSIController::doHardwareInterrupt()
{
DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr;
getHBARegsAndClear( true );
setIntMask( 0 );
fFlagReselecting = false;
if ( g.shadow.mesh.interrupt == 0 )
{
PAUSE( DBDMARegs->commandPtrLo,
(g.shadow.mesh.busStatus0 << 8) | g.shadow.mesh.busStatus1,
'ISR?',
"doHardwareInterrupt - spurious interrupt" );
if ( !fCmd )
{
setIntMask( kMeshIntrMask );
enableCommands();
}
return;
}
DBDMARegs->channelControl = SWAP( 0x20002000 ); SynchronizeIO();
DBDMARegs->channelControl = SWAP( 0x80000000 ); SynchronizeIO();
if ( *(UInt32*)&fCCL[ kcclStageLabel ] )
{
processInterrupt();
return;
}
if ( g.shadow.mesh.interrupt == kMeshIntrCmdDone )
{
if ( fCmd )
{
PAUSE( 0, 0, 'dhi-', "doHardwareInterrupt - MESH interrupt problem: need phase stabilizing wait.\n" );
return;
}
else
{
setIntMask( kMeshIntrMask );
enableCommands();
return;
}
}
if ( g.shadow.mesh.interrupt == (kMeshIntrCmdDone | kMeshIntrException)
&& g.shadow.mesh.exception == kMeshExcPhaseMM )
{
PAUSE( 0, 0, 'DHI-', "doHardwareInterrupt - MESH interrupt problem: phase mismatch interrupt.\n" );
}
else
{
processInterrupt();
}
return;
}
void meshSCSIController::processInterrupt()
{
UInt32 stage;
UInt32 cclIndex;
UInt32 count;
UInt8 phase;
IOReturn rc;
stage = *(UInt32*)&fCCL[ kcclStageLabel ];
cclIndex = SWAP( ((DBDMAChannelRegisters*)dbdmaAddr)->commandPtrLo ) - (UInt32)fCCLPhysAddr;
*(UInt32*)&fCCL[ kcclStageLabel ] = 0;
if ( g.shadow.mesh.error & kMeshErrSCSIRst )
{
ELG( fCmd, stage, 'BRst', "Process interrupt with no active request\n" );
fCmd = NULL;
super::resetOccurred();
setIntMask( kMeshIntrMask );
enableCommands();
return;
}
if ( fCmd == NULL )
{
if ( g.shadow.mesh.exception & kMeshExcResel )
{
handleReselectionInterrupt();
return;
}
ELG( 0, 0, 'Int0', "Process interrupt with no active request\n" );
setIntMask( kMeshIntrException | kMeshIntrError );
enableCommands();
return;
}
switch ( stage )
{
case kcclStageGood:
doInterruptStageGood();
break;
case kcclStageInit:
case kcclStageArb:
doInterruptStageArb();
break;
case kcclStageSelA:
doInterruptStageSelA();
break;
case kcclStageMsgO:
doInterruptStageMsgO();
break;
case kcclStageCmdO:
doInterruptStageCmdO();
break;
case kcclStageXfer:
doInterruptStageXfer();
break;
case kcclStageStat:
count = *(UInt32*)&fCCL[ kcclBatchSize ];
fCmdData->results.bytesTransferred += count;
if ( fReadAlignmentCount ) {
fCmdData->mdp->writeBytes( fReadAlignmentIndex, &fCCL[ kcclReadBuf8 ], fReadAlignmentCount );
fReadAlignmentCount = 0;
}
ELG( count, fCmdData->results.bytesTransferred, 'Uidx', "processInterrupt" );
*(UInt32*)&fCCL[ kcclBatchSize ] = 0;
if ( !(g.shadow.mesh.busStatus0 & kMeshReq) )
{
startBucket();
return;
}
phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
switch ( phase )
{
case kBusPhaseMSGI:
rc = DoMessageInPhase();
break;
case kBusPhaseDATO:
case kBusPhaseDATI:
startBucket();
break;
default:
PAUSE( 0, phase, 'pmm-', "processInterrupt - expected Status phase.\n" );
break;
}
break;
case kcclStageBucket:
count = *(UInt32*)&fCCL[ kcclBatchSize ];
fCmdData->results.bytesTransferred += count;
*(UInt32*)&fCCL[ kcclBatchSize ] = 0;
ELG( count, fCmdData->results.bytesTransferred, 'Buck',
"processInterrupt - bit bucket done.\n" );
phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
count = g.shadow.mesh.transferCount1 << 8 | g.shadow.mesh.transferCount0;
count += g.shadow.mesh.FIFOCount;
count &= 0xFFFF;
if ( (count != 0)
|| (fCmdData->xferCount != fCmdData->results.bytesTransferred)
|| !(phase > kBusPhaseDATI) )
{
fCmdData->results.adapterStatus = kSCSIAdapterStatusOverrun;
}
setSeqReg( kMeshFlushFIFO );
runDBDMA( kcclGetStatus, kcclStageStat );
break;
case kcclStageMsgI:
ELG( 0, fMESHAddr->busStatus0 << 8 | fMESHAddr->busStatus1, '?mi-',
"processInterrupt - Phase Mismatch on MsgIn stage.\n" );
fFlagIncompleteDBDMA = false;
if ( !(fMESHAddr->busStatus0 & kMeshReq) )
IOSleep( 1 );
getHBARegsAndClear( true );
phase = g.shadow.mesh.busStatus0 & (kMeshPhaseMask | kMeshReq);
if ( phase == (kMeshReq | kBusPhaseMSGI) )
{
DoMessageInPhase();
}
break;
case kcclStageFree:
default:
PAUSE( cclIndex, stage, 'P i-', "processInterrupt - strange or unknown interrupt for device.\n" );
break;
}
return;
}
UInt8 meshSCSIController::startCommand()
{
fCmd->getPointers( &fCmdData->mdp, &fCmdData->xferCount, &fCmdData->isWrite );
clearCPResults();
{
fCmdData->results.bytesTransferred = 0;
fCmdData->savedDataPosition = 0;
updateCP( false );
}
fMESHAddr->selectionTimeOut = fSelectionTimeout;
fMESHAddr->destinationID = fCurrentTargetLun.target;
fMESHAddr->syncParms = fSyncParms[ fCurrentTargetLun.target ];
SynchronizeIO();
runDBDMA( kcclStart, kcclStageInit );
if ( g.intLevel & kLevelLatched )
return kHardwareStartBusy;
return kHardwareStartOK;
}
void meshSCSIController::updateCP( bool reselecting )
{
DBDMADescriptor *descProto = (DBDMADescriptor*)&fCCL[ kcclPrototype ];
DBDMADescriptor *descriptorPtr;
DBDMADescriptor *descriptorMax;
DBDMADescriptor *preamblePtr;
SCSICDBInfo scsiCDB;
UInt32 dbdmaOpProto;
UInt32 dbdmaOp;
UInt32 meshSeq;
SInt32 transferLength;
UInt32 totalXferLen = 0;
UInt32 groupLength;
UInt8 syncParms;
UInt32 actualRanges;
IOPhysicalSegment range[ kMaxMemCursorSegs ];
DBDMADescriptor *dp;
UInt32 rangeLength, rangeLocation;
UInt32 i;
IOReturn ioReturn = kIOReturnSuccess;
fReadAlignmentCount = 0;
descriptorPtr = (DBDMADescriptor*)&fCCL[ kcclDataXfer ];
descriptorMax = &descriptorPtr[ fDBDMADescriptorMax - 16 ];
transferLength = fCmdData->xferCount - fCmdData->results.bytesTransferred;
ELG( descriptorPtr, transferLength, 'UpCP', "updateCP" );
if ( !reselecting )
{
setupMsgO();
fCmd->getCDB( &scsiCDB );
fCCL[ kcclCmdoMTC ] = scsiCDB.cdbLength;
fCCL[ kcclCmdoDTC ] = scsiCDB.cdbLength;
bcopy( &scsiCDB.cdb[0], &fCCL[ kcclCMDOdata ], scsiCDB.cdbLength );
}
if ( fCmdData->isWrite )
{ dbdmaOpProto = OUTPUT_MORE | kBranchIfFalse;
meshSeq = kMeshDataOutCmd | kMeshSeqDMA;
}
else
{ dbdmaOpProto = INPUT_MORE | kBranchIfFalse;
meshSeq = kMeshDataInCmd | kMeshSeqDMA;
}
*(UInt32*)&fCCL[ kcclBatchSize ] = 0;
while ( ioReturn == kIOReturnSuccess
&& transferLength > 0
&& descriptorPtr < descriptorMax )
{
preamblePtr = descriptorPtr;
groupLength = 0;
descriptorPtr += 4;
actualRanges = fMemoryCursor->getPhysicalSegments(
fCmdData->mdp,
fCmdData->results.bytesTransferred + totalXferLen,
range,
kMaxMemCursorSegs );
if ( actualRanges == 0 )
break;
ELG( range[0].length, range[0].location, 'Rng1', "updateCP - 1st range" );
for ( i = 0; i < actualRanges; i++ )
{ rangeLocation = range[i].location;
rangeLength = range[i].length;
groupLength += rangeLength;
if ( (i == 0) && (totalXferLen == 0) && (!fCmdData->isWrite) && (rangeLocation & 0x07) ) {
ELG( rangeLength, rangeLocation, 'Rd-8', "updateCP - non-8-byte-aligned read" );
fReadAlignmentIndex = fCmdData->results.bytesTransferred;
fReadAlignmentCount = 8 - (rangeLocation & 0x07);
dbdmaOp = dbdmaOpProto | fReadAlignmentCount;
rangeLength -= fReadAlignmentCount;
transferLength -= fReadAlignmentCount;
if ( transferLength <= 0 )
dbdmaOp |= (INPUT_MORE ^ INPUT_LAST);
descriptorPtr->operation = SWAP( dbdmaOp );
descriptorPtr->address = SWAP( (UInt32)fCCLPhysAddr + kcclReadBuf8 );
descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclProblem );
descriptorPtr->result = 0;
descriptorPtr++;
if ( rangeLength == 0 )
continue;
rangeLocation += fReadAlignmentCount;
}
dbdmaOp = dbdmaOpProto | rangeLength;
transferLength -= rangeLength;
if ( transferLength <= 0 )
dbdmaOp |= (INPUT_MORE ^ INPUT_LAST);
descriptorPtr->operation = SWAP( dbdmaOp );
descriptorPtr->address = SWAP( rangeLocation );
descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclProblem );
descriptorPtr->result = 0;
descriptorPtr++;
}
if ( groupLength == 0 )
{
ELG( 0, 0, 'Grp-', "updateCP - groupLength is 0" );
preamblePtr->operation = SWAP( NOP_CMD | kBranchIfFalse | kWaitIfTrue );
preamblePtr->address = 0;
preamblePtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclProblem );
preamblePtr->result = 0;
descriptorPtr = preamblePtr + 1;
ioReturn = kIOReturnInvalid;
}
else
{
totalXferLen += groupLength;
descProto[0].cmdDep = totalXferLen; descProto[1].cmdDep = SWAP( groupLength >> 8 );
descProto[2].cmdDep = SWAP( groupLength & 0xFF );
descProto[3].cmdDep = SWAP( meshSeq );
bcopy( descProto, preamblePtr, sizeof( DBDMADescriptor ) * 4 );
ELG( preamblePtr, totalXferLen, '=Tot', "updateCP - set preamble" );
if ( transferLength > 0 )
{
bcopy( &fCCL[ kcclBrProblem ], descriptorPtr, sizeof( DBDMADescriptor ) );
++descriptorPtr;
descriptorPtr->operation = SWAP( STORE_QUAD | KEY_SYSTEM | 1 );
descriptorPtr->address = SWAP( fMESHPhysAddr + kMeshInterrupt );
descriptorPtr->cmdDep = SWAP( kMeshIntrCmdDone );
descriptorPtr->result = 0;
++descriptorPtr;
}
}
}
syncParms = fSyncParms[ fCurrentTargetLun.target ];
fMESHAddr->syncParms = syncParms;
SynchronizeIO();
ELG( fMsgOutFlag, syncParms, 'SynP', "updateCP - sync parms" );
if ( ((syncParms & 0xF0) || (fMsgOutFlag & kFlagMsgOut_SDTR)) && (totalXferLen > 0) && (transferLength == 0) ) {
fFlagIncompleteDBDMA = false;
descriptorPtr->operation = SWAP( STORE_QUAD | KEY_SYSTEM | 1 );
descriptorPtr->address = SWAP( fMESHPhysAddr + kMeshInterruptMask );
descriptorPtr->cmdDep = SWAP( kMeshIntrError );
descriptorPtr->result = 0;
++descriptorPtr;
descriptorPtr->operation = SWAP( NOP_CMD | kBranchIfFalse );
descriptorPtr->address = 0;
descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclProblem );
descriptorPtr->result = 0;
if ( (totalXferLen <= 16) && fCmdData->isWrite )
{
descriptorPtr->operation = SWAP( NOP_CMD | kWaitIfTrue | kBranchIfFalse );
}
++descriptorPtr;
descriptorPtr->operation = SWAP( NOP_CMD | kWaitIfTrue | kBranchIfFalse );
descriptorPtr->address = 0;
descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclSyncCleanUp );
descriptorPtr->result = 0;
descriptorPtr++;
descriptorPtr->operation = SWAP( NOP_CMD | kBranchAlways );
descriptorPtr->address = 0;
descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclOverrun );
descriptorPtr->result = 0;
descriptorPtr++;
dp = (DBDMADescriptor*)&fCCL[ kcclOverrunMESH ];
if ( fCmdData->isWrite )
{ dp->cmdDep = SWAP( kMeshDataOutCmd | kMeshSeqDMA );
dp = (DBDMADescriptor*)&fCCL[ kcclOverrunDBDMA ];
dp->operation = SWAP( OUTPUT_LAST | kBranchIfFalse | 8 );
}
else
{ dp->cmdDep = SWAP( kMeshDataInCmd | kMeshSeqDMA );
dp = (DBDMADescriptor*)&fCCL[ kcclOverrunDBDMA ];
dp->operation = SWAP( INPUT_LAST | kBranchIfFalse | 8 );
}
}
else
{
if ( transferLength > 0 )
fFlagIncompleteDBDMA = true;
else fFlagIncompleteDBDMA = false;
if ( fFlagIncompleteDBDMA )
{
--descriptorPtr;
}
else if ( totalXferLen > 0 )
{
bcopy( &fCCL[ kcclBrProblem ], descriptorPtr, sizeof( DBDMADescriptor ) );
descriptorPtr++;
}
descriptorPtr->operation = SWAP( NOP_CMD | kBranchAlways );
descriptorPtr->address = 0;
descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclGetStatus );
descriptorPtr->result = 0;
if ( fFlagIncompleteDBDMA )
{
descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclMESHintr );
ELG( descriptorPtr, transferLength, 'Part', "updateCP - built partial CCL." );
}
descriptorPtr++;
}
return;
}
void meshSCSIController::clearCPResults()
{
register DBDMADescriptor *dp = (DBDMADescriptor*)&fCCL[ kcclStart ];
register int i;
for ( i = (gDescriptorListSize - kcclStart) / sizeof ( DBDMADescriptor ); i; --i )
{
dp->result = 0;
dp++;
}
return;
}
void meshSCSIController::setupMsgO()
{
UInt8 msgoSize;
fMsgOutPtr--;
msgoSize = fMsgOutPtr - &fCCL[ kcclMSGOdata ];
if( msgoSize == 0 )
{
*(UInt32*)&fCCL[ kcclMsgoBranch ] = SWAP( NOP_CMD | kBranchAlways );
}
else
{ fCCL[ kcclMsgoMTC ] = msgoSize;
fCCL[ kcclMsgoDTC ] = msgoSize;
*(UInt32*)&fCCL[ kcclMsgoBranch ] = SWAP( NOP_CMD );
}
fCCL[ kcclMSGOLast ] = *fMsgOutPtr;
return;
}
void meshSCSIController::runDBDMA( UInt32 offset, UInt32 stageLabel )
{
register UInt8 intReg;
mach_timespec_t arbEndTime, curTime;
DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr;
fMsgInFlag = 0;
*(UInt32*)&fCCL[ kcclStageLabel ] = stageLabel;
setIntMask( kMeshIntrException | kMeshIntrError );
intReg = fMESHAddr->interrupt;
switch ( intReg )
{
case kMeshIntrCmdDone:
if ( !fFlagReselecting )
fMESHAddr->interrupt = kMeshIntrCmdDone; SynchronizeIO();
case 0:
flush_dcache( (vm_offset_t)fCCL, fCCLSize, false );
if ( offset == kcclStart )
{
fFlagReselecting = false;
setSeqReg( kMeshArbitrateCmd );
IOGetTime( &arbEndTime );
arbEndTime.tv_nsec += 50000;
if ( arbEndTime.tv_nsec >= NSEC_PER_SEC )
{ arbEndTime.tv_nsec -= NSEC_PER_SEC;
arbEndTime.tv_sec += 1;
}
while ( true )
{
getHBARegsAndClear( false );
IOGetTime( &curTime );
if ( g.shadow.mesh.interrupt & kMeshIntrCmdDone )
break;
if ( curTime.tv_sec < arbEndTime.tv_sec ) continue;
if ( curTime.tv_nsec >= arbEndTime.tv_nsec ) break;
}
if ( g.shadow.mesh.interrupt == kMeshIntrCmdDone )
{
fMESHAddr->interrupt = kMeshIntrCmdDone;
SynchronizeIO();
setSeqReg( kMeshDisableReselect );
offset = 0x150; *(UInt32*)&fCCL[ kcclStageLabel ] = kcclStageSelA;
}
else
{
if ( !(g.shadow.mesh.exception & (kMeshExcArbLost | kMeshExcResel)) )
{
ELG( '****', '****', 'HACK', "runDBDMA - Arbitrate HACK." );
setSeqReg( kMeshResetMESH );
getHBARegsAndClear( true );
setSeqReg( kMeshEnableReselect );
getHBARegsAndClear( false );
if ( g.shadow.mesh.interrupt == 0 )
PAUSE( 0, 0, 'Arb*', "runDBDMA - Arbitrate/Reselect problem." );
}
if ( g.shadow.mesh.interrupt )
break;
}
}
getHBARegsAndClear( false ); ELG( 0, offset<<16 | stageLabel, 'DMA+', "runDBDMA" );
DBDMARegs->commandPtrLo = SWAP( (UInt32)fCCLPhysAddr + offset );
SynchronizeIO();
DBDMARegs->channelControl = SWAP( 0x80008000 ); SynchronizeIO();
return;
}
ELG( 0, intReg, 'Pnd-', "runDBDMA - interrupt probably pending (reselect?)." );
getHBARegsAndClear( false );
*(UInt32*)&fCCL[ kcclStageLabel ] = kcclStageIdle;
g.intLevel |= kLevelLatched;
setIntMask( kMeshIntrMask );
return;
}
void meshSCSIController::completeCommand()
{
SCSINegotiationResults negotiationResult, *negResult;
UInt32 transferPeriod;
ELG( fCmdData->results.bytesTransferred, fCmdData->results.scsiStatus, ' IOC', "meshSCSIController::completeCommand" );
switch ( fCmdData->results.scsiStatus )
{
case kSCSIStatusGood:
break;
case kSCSIStatusCheckCondition:
ELG( 0, 0, 'Chek', "meshSCSIController::completeCommand - Check Condition" );
break;
case kSCSIStatusQueueFull:
default:
ELG( fCmd, fCmdData->results.scsiStatus, 'Sta?', "meshSCSIController::completeCommand - bad status" );
break;
}
negResult = 0;
if ( fCmdData->negotiatingSDTR )
{
bzero( &negotiationResult, sizeof(SCSINegotiationResults) );
negotiationResult.returnCode = kIOReturnIOError;
if ( fCmdData->negotiatingSDTRComplete == true )
{
negotiationResult.returnCode = kIOReturnSuccess;
negotiationResult.transferOffset = fSyncParms[ fCurrentTargetLun.target ] >> 4;
if ( negotiationResult.transferOffset != 0 )
{
transferPeriod = fSyncParms[ fCurrentTargetLun.target ] & 0x0f;
if ( transferPeriod == 0 )
{
negotiationResult.transferPeriodpS = 100;
}
else
{
negotiationResult.transferPeriodpS = 80 + 40 * transferPeriod;
}
}
negotiationResult.transferWidth = 1;
}
negResult = &negotiationResult;
}
fCmd->setResults( &fCmdData->results, negResult );
fCmd->complete();
fCmd = NULL;
fCmdData = NULL;
fCurrentTargetLun.target = kInvalidTarget;
fCurrentTargetLun.lun = kInvalidLUN;
return;
}
void meshSCSIController::free()
{
DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr;
if ( fMESHAddr )
{
setSeqReg( kMeshResetMESH );
}
if ( DBDMARegs )
{
DBDMARegs->channelControl = SWAP( 0x20002000 ); SynchronizeIO();
DBDMARegs->channelControl = SWAP( 0x80000000 ); SynchronizeIO();
}
if ( fInterruptEvent )
{
fInterruptEvent->disable();
fInterruptEvent->release();
fInterruptEvent = 0;
}
if ( fCCL )
{
IOFreeContiguous( (void*)fCCL, fCCLSize );
fCCL = NULL;
}
if ( fMemoryCursor )
{
fMemoryCursor->release();
fMemoryCursor = 0;
}
if ( g.evLogBuf )
{
IOFree( (void*)g.evLogBuf, kEvLogSize );
g.evLogBuf = 0;
}
super::free();
return;
}
void meshSCSIController::startBucket()
{
DBDMADescriptor *dp;
UInt32 dbdmaOp;
UInt32 meshSeq;
ELG( fCmd, fCmdData, 'Bkt-', "startBucket" );
if ( fCmdData->isWrite )
{ dbdmaOp = OUTPUT_MORE | kBranchIfFalse | 8;
meshSeq = kMeshDataOutCmd | kMeshSeqDMA;
}
else
{ dbdmaOp = INPUT_MORE | kBranchIfFalse | 8;
meshSeq = kMeshDataInCmd | kMeshSeqDMA;
}
dp = (DBDMADescriptor*)&fCCL[ kcclOverrunMESH ]; dp->cmdDep = meshSeq;
dp = (DBDMADescriptor*)&fCCL[ kcclOverrunDBDMA ]; dp->operation = dbdmaOp;
runDBDMA( kcclDataXfer, kcclStageBucket );
return;
}
void meshSCSIController::doInterruptStageGood()
{
UInt32 totalXferLen;
totalXferLen = *(UInt32*)&fCCL[ kcclBatchSize ];
*(UInt32*)&fCCL[ kcclBatchSize ] = 0;
ELG( fCmd, totalXferLen, 'Good', "doInterruptStageGood" );
fCmdData->results.bytesTransferred += totalXferLen;
if ( fReadAlignmentCount ) {
fCmdData->mdp->writeBytes( fReadAlignmentIndex, &fCCL[ kcclReadBuf8 ], fReadAlignmentCount );
fReadAlignmentCount = 0;
}
if ( fFlagIncompleteDBDMA == false )
{
fCmdData->results.returnCode = kIOReturnSuccess;
fCmdData->results.scsiStatus = fCCL[ kcclStatusData ];
fCmd->setResults( &fCmdData->results );
completeCommand();
}
else
{
updateCP( true );
runDBDMA( kcclDataXfer, kcclStageXfer );
return;
}
if ( g.shadow.mesh.exception & kMeshExcResel )
handleReselectionInterrupt();
else
{
setIntMask( kMeshIntrException | kMeshIntrError );
enableCommands();
}
return;
}
void meshSCSIController::doInterruptStageXfer()
{
UInt32 count;
UInt8 phase;
IOReturn rc;
int goAround;
count = fCmdData->results.bytesTransferred;
updateCurrentIndex();
do
{ goAround = false;
setSeqReg( kMeshFlushFIFO );
phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
switch ( phase )
{
case kBusPhaseSTS:
fFlagIncompleteDBDMA = false;
runDBDMA( kcclGetStatus, kcclStageStat );
break;
case kBusPhaseMSGI:
rc = DoMessageInPhase();
if ( rc == kIOReturnSuccess && fCmd )
goAround = true;
break;
case kBusPhaseDATO:
case kBusPhaseDATI:
if ( count != fCmdData->results.bytesTransferred )
{
PAUSE( 0, phase, 'dat-', "doInterruptStageXfer - unexpected Data phase.\n" );
}
else
{
runDBDMA( kcclDataXfer, kcclStageXfer );
}
break;
default:
PAUSE( 0, phase, 'Phs-', "doInterruptStageXfer - bogus phase.\n" );
break;
}
} while ( goAround );
return;
}
void meshSCSIController::doInterruptStageArb()
{
PAUSE( 0, 0, 'Arb-', "doInterruptStageArb - Lost arbitration.\n" );
disableCommands();
rescheduleCommand( fCmd );
fCmd = 0;
if ( g.shadow.mesh.exception & kMeshExcResel )
{ if ( g.shadow.mesh.error & kMeshErrDisconnected )
{
ELG( ' Rst', 'MESH', 'UEP-', "doInterruptStageArb - Resel/Unexpected Disconnect.\n" );
setSeqReg( kMeshResetMESH );
getHBARegsAndClear( true );
setSeqReg( kMeshEnableReselect );
setIntMask( kMeshIntrMask );
return;
}
handleReselectionInterrupt();
}
else
{
ELG( 0, 0, 'ARB-', "doInterruptStageArb - Lost arbitration without reselect.\n" );
}
return;
}
void meshSCSIController::doInterruptStageSelA()
{
ELG( fCmd, 0, 'Sel-', "doInterruptStageSelA - Selection stage.\n" );
if ( fCmd )
{ fCmdData->results.adapterStatus = kSCSIAdapterStatusSelectionTimeout;
completeCommand();
}
setSeqReg( kMeshEnableReselect );
setSeqReg( kMeshBusFreeCmd );
getHBARegsAndClear( false );
if ( g.shadow.mesh.exception & kMeshExcResel )
{
handleReselectionInterrupt();
}
else
{ setIntMask( kMeshIntrMask );
}
return;
}
void meshSCSIController::doInterruptStageMsgO()
{
UInt8 phase;
IOReturn rc;
phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
ELG( fCmd, phase, 'Mgo-', "doInterruptStageMsgO - error during msg-out phase.\n" );
switch ( phase )
{
case kBusPhaseMSGI:
rc = DoMessageInPhase();
if ( rc != kIOReturnSuccess )
{
PAUSE( 0, rc, ' MI-',
"doInterruptStageMsgO - MsgIn during MsgOut phase.\n" );
}
else
{ ELG( 0, fMsgInFlag, 'rej?', "doInterruptStageMsgO - got MsgIn.\n" );
if ( fMsgInFlag & kFlagMsgIn_Reject )
abortActiveCommand();
}
break;
default:
PAUSE( fMsgInFlag, phase, 'mgo-',
"doInterruptStageMsgO - unknown phase during MsgOut phase.\n" );
break;
}
return;
}
void meshSCSIController::doInterruptStageCmdO()
{
SCSICDBInfo scsiCDB;
UInt8 phase;
IOReturn rc;
fCmd->getCDB( &scsiCDB );
if ( scsiCDB.cdbAbortMsg )
{
setSeqReg( kMeshFlushFIFO );
getHBARegsAndClear( true );
ELG( fCmd, fCmd->getOriginalCmd(), 'Abo-', "doInterruptStageCmdO - Aborting." );
completeCommand();
setIntMask( kMeshIntrMask );
enableCommands();
return;
}
phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
ELG( fCmd, phase, 'CMD?', "doInterruptStageCmdO - anomaly during Cmd phase.\n" );
if ( phase == kBusPhaseMSGI )
{
rc = DoMessageInPhase();
if ( rc != kIOReturnSuccess )
{
PAUSE( 0, rc, ' mi-',
"doInterruptStageCmdO - MsgIn during Cmd phase.\n" );
}
else
{
if ( !fCmd )
return;
phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
switch ( phase )
{
case kBusPhaseSTS:
runDBDMA( kcclCmdoStage, kcclStageInit );
break;
case kBusPhaseMSGO:
fMsgOutPtr = &fCCL[ kcclMSGOdata ];
setupMsgO();
runDBDMA( kcclMsgoStage, kcclStageInit );
break;
case kBusPhaseCMD:
runDBDMA( kcclCmdoStage, kcclStageInit );
break;
}
}
}
else if ( phase == kBusPhaseSTS )
{
setSeqReg( kMeshFlushFIFO );
fFlagIncompleteDBDMA = false;
runDBDMA( kcclGetStatus, kcclStageStat );
}
else
{
PAUSE( 0, phase, 'Phs?', "doInterruptStageCmdO - error during Command phase.\n" );
}
return;
}
enum {
kSCSIMsgOneByteMax = 0x1Fu,
kSCSIMsgTwoByteMin = 0x20u,
kSCSIMsgTwoByteMax = 0x2Fu
};
IOReturn meshSCSIController::DoMessageInPhase()
{
register UInt8 messageByte;
UInt32 index = 0;
IOReturn ioReturn = kIOReturnSuccess;
setIntMask( 0 );
setSeqReg( kMeshFlushFIFO );
fMsgInCount = 0;
fMsgInState = kMsgInInit;
while ( fMsgInState != kMsgInReady
&& ioReturn == kIOReturnSuccess )
{
fMESHAddr->transferCount1 = 0;
fMESHAddr->transferCount0 = 1;
setSeqReg( kMeshMessageInCmd );
ioReturn = waitForMesh( true );
if ( ioReturn != kIOReturnSuccess )
{
PAUSE( *(UInt32*)&fCurrentTargetLun, ioReturn, 'Mgi-', "DoMessageInPhase - Target hung: message in timeout.\n" );
break;
}
if ( (g.shadow.mesh.exception & kMeshExcPhaseMM)
|| (g.shadow.mesh.busStatus0 & kMeshPhaseMask) != kBusPhaseMSGI )
{
break;
}
if ( g.shadow.mesh.FIFOCount == 0 )
{
PAUSE( *(UInt16*)&fCurrentTargetLun, 0, 'mgi-', "DoMessageInPhase - no message byte.\n" );
break;
}
fMsgInBuffer[ index++ ] = messageByte = fMESHAddr->xFIFO;
switch ( fMsgInState )
{
case kMsgInInit:
if ( (messageByte == kSCSIMsgCmdComplete)
|| (messageByte >= (UInt8)kSCSIMsgIdentify) )
{
fMsgInState = kMsgInReady;
}
else if ( messageByte == kSCSIMsgExtended )
{
fMsgInState = kMsgInCounting;
}
else if ( messageByte <= kSCSIMsgOneByteMax )
{
fMsgInState = kMsgInReady;
}
else if ( messageByte >= kSCSIMsgTwoByteMin
&& messageByte <= kSCSIMsgTwoByteMax )
{
fMsgInState = kMsgInReading;
fMsgInCount = 1;
}
else
{
fMsgInState = kMsgInReady;
}
break;
case kMsgInCounting:
fMsgInCount = messageByte;
fMsgInState = kMsgInReading;
break;
case kMsgInReading:
if ( --fMsgInCount <= 0 )
fMsgInState = kMsgInReady;
break;
default:
PAUSE( 0, 0, 'Msg-', "DoMessageInPhase - Bogus MSGI state!\n" );
fMsgInState = kMsgInReady;
break;
}
if ( fMsgInState == kMsgInReady )
{
ProcessMSGI();
fMsgInState = kMsgInInit;
index = 0;
if ( fMsgInBuffer[0] == kSCSIMsgDisconnect
|| fMsgInBuffer[0] == kSCSIMsgCmdComplete )
ioReturn = kIOReturnIOError;
if ( fMsgInFlag & kFlagMsgIn_Reject )
{
abortActiveCommand();
break;
}
if ( fFlagReselecting )
break;
}
}
if ( fMsgInState != kMsgInInit )
{
PAUSE( *(UInt32*)&fCurrentTargetLun, fMsgInState, 'MGI-', "DoMessageInPhase - incomplete message.\n" );
if ( ioReturn == kIOReturnSuccess )
ioReturn = kIOReturnIOError;
}
return ioReturn;
}
void meshSCSIController::ProcessMSGI()
{
UInt8 sdtr;
UInt8 period;
ELG( fCmd, *(UInt32*)fMsgInBuffer, '<Msg', "ProcessMSGI" );
switch ( fMsgInBuffer[0] )
{
case kSCSIMsgCmdComplete:
if ( fCmd )
{
setSeqReg( kMeshEnableReselect );
setIntMask( kMeshIntrMask );
setSeqReg( kMeshBusFreeCmd );
completeCommand();
waitForMesh( false );
g.intLevel |= kLevelLatched;
setIntMask( 0 );
}
goto exit;
case kSCSIMsgLinkedCmdComplete:
case kSCSIMsgLinkedCmdCompleteFlag:
PAUSE( *(UInt16*)&fCurrentTargetLun, 0, 'pmi-', "ProcessMSGI - linked command complete not supported.\n" );
abortActiveCommand();
break;
case kSCSIMsgNop:
break;
case kSCSIMsgRestorePointers:
if ( fCmd )
fCmdData->results.bytesTransferred = fCmdData->savedDataPosition;
break;
case kSCSIMsgSaveDataPointers:
if ( fCmd )
fCmdData->savedDataPosition = fCmdData->results.bytesTransferred;
break;
case kSCSIMsgDisconnect:
fMsgInFlag |= kFlagMsgIn_Disconnect;
disconnect();
setSeqReg( kMeshEnableReselect );
setIntMask( kMeshIntrMask );
setSeqReg( kMeshBusFreeCmd );
waitForMesh( false );
g.intLevel |= kLevelLatched;
setIntMask( 0 );
break;
case kSCSIMsgRejectMsg:
ELG( *(UInt16*)&fCurrentTargetLun, fMsgOutFlag, 'Rej-', "ProcessMSGI - Reject." );
fMsgInFlag |= kFlagMsgIn_Reject;
break;
case kSCSIMsgSimpleQueueTag:
fTagType = fMsgInBuffer[0];
fTag = fMsgInBuffer[1];
ELG( 0, fTag, '=Tag', "Simple Queue Tag" );
break;
case kSCSIMsgExtended:
switch ( fMsgInBuffer[ 2 ] )
{
case kSCSIMsgWideDataXferReq:
ELG( *(UInt32*)&fMsgInBuffer[0], 0, 'TINW', "ProcessMSGI - TIN WDTR" );
issueReject();
break;
case kSCSIMsgSyncXferReq:
period = fMsgInBuffer[ 3 ] * 4;
if ( !fCmdData->negotiatingSDTR )
{
ELG( *(UInt32*)&fMsgInBuffer[0], *(UInt32*)&fMsgInBuffer[4], 'TINS', "ProcessMSGI - TIN SDTR" );
issueReject();
}
else
{
if ( fMsgInBuffer[ 4 ] == 0 )
{
sdtr = kSyncParmsAsync;
}
else
{
if ( period == 100 )
{
sdtr = kSyncParmsFast & 0x0F;
}
else
{
sdtr = (UInt8)((period - 41) / 40);
ELG( *(UInt32*)&fMsgInBuffer[0], fMsgInBuffer[4]<<24 | sdtr, 'SDTR', "ProcessMSGI - SDTR" );
}
}
sdtr |= (fMsgInBuffer[ 4 ] << 4);
fMESHAddr->syncParms = sdtr;
SynchronizeIO();
fSyncParms[ fCurrentTargetLun.target ] = sdtr;
fCmdData->negotiatingSDTRComplete = true;
}
break;
default:
PAUSE( *(UInt16*)&fCurrentTargetLun, fMsgInBuffer[0], 'PMi-', "ProcessMSGI - unsupported extended message.\n" );
abortActiveCommand();
break;
}
break;
default:
if ( fMsgInBuffer[0] >= kSCSIMsgIdentify )
{
fCurrentTargetLun.lun = fMsgInBuffer[0] & 0x07;
}
else
{
PAUSE( *(UInt16*)&fCurrentTargetLun, fMsgInBuffer[0], 'mi -', "ProcessMSGI - unsupported message: rejected.\n" );
abortActiveCommand();
}
}
exit:
return;
}
void meshSCSIController::issueReject()
{
ELG( 0, 0, 'RjM-', "issueReject" );
fMESHAddr->busStatus0 = kMeshAtn;
SynchronizeIO();
IODelay( 30 );
setSeqReg( kMeshBusFreeCmd );
waitForMesh( true );
fMESHAddr->busStatus0 = 0;
SynchronizeIO();
IOSleep( 1 );
fMESHAddr->transferCount1 = 0;
fMESHAddr->transferCount0 = 1;
SynchronizeIO();
setSeqReg( kMeshMessageOutCmd );
fMESHAddr->xFIFO = kSCSIMsgRejectMsg;
SynchronizeIO();
waitForMesh( true );
setSeqReg( kMeshBusFreeCmd );
waitForMesh( true );
return;
}
void meshSCSIController::handleReselectionInterrupt()
{
IOReturn ioReturn;
fFlagReselecting = true;
disableCommands();
fMESHAddr->interrupt = kMeshIntrMask;
if ( g.shadow.mesh.error & kMeshErrDisconnected )
{
setSeqReg( kMeshBusFreeCmd );
waitForMesh( true ); PAUSE( 0, 0, 'Dsc-',
"handleReselectionInterrupt: Caught disconnected glitch\n" );
}
if ( fMESHAddr->FIFOCount == 0 )
{
PAUSE( 0, 0, 'HRI-', "handleReselectionInterrupt - Empty FIFO in reselection.\n" );
return;
}
else
{
if ( getReselectionTargetID() )
{
if ( DoMessageInPhase() != kIOReturnSuccess )
{
PAUSE( 0, 0, 'Id -', "handleReselectionInterrupt - Expected Identify byte after reselection.\n" );
}
}
else return;
}
fTag = kInvalidTag;
ioReturn = reselectNexus();
if ( ioReturn != kIOReturnSuccess )
{
if ( DoMessageInPhase() != kIOReturnSuccess )
{
PAUSE( 0, 0, 'tag-', "handleReselectionInterrupt - Expected tag message.\n" );
}
ioReturn = reselectNexus();
}
if ( ioReturn == kIOReturnSuccess )
{
ELG( fCmd,
fTag<<16 | fCurrentTargetLun.lun<<8 | fCurrentTargetLun.target,
'Resl', "handleReselectionInterrupt" );
clearCPResults();
updateCP( true );
runDBDMA( kcclReselect, kcclStageInit );
}
else
{
PAUSE( *(UInt16*)&fCurrentTargetLun, fTag, 'Rsl-',
"handleReselectionInterrupt - No command for reselection attempt.\n" );
abortActiveCommand();
}
return;
}
bool meshSCSIController::getReselectionTargetID()
{
register UInt8 targetID = 0;
register UInt8 bitValue = 0;
register UInt8 targetBits;
bool success = false;
targetBits = fMESHAddr->xFIFO;
targetBits &= ~fInitiatorIDMask;
if ( targetBits )
{
bitValue = targetBits;
if ( bitValue > 0x0F )
{
targetID += 4;
bitValue >>= 4;
}
if ( bitValue > 0x03 )
{
targetID += 2;
bitValue >>= 2;
}
if ( bitValue > 0x01 )
{
targetID += 1;
}
targetBits &= ~(1 << targetID);
if ( targetBits == 0 )
{
success = true;
fCurrentTargetLun.target = targetID;
}
}
if ( !success )
PAUSE( targetID, targetBits, 'rsl-', "getReselectionTargetID - Expected Identify byte after reselection.\n" );
return success;
}
IOReturn meshSCSIController::resetBus()
{
UInt8 defaultSelectionTimeout = 25; DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr;
ELG( 0, 0, 'RstB', "resetBus" );
DBDMARegs->channelControl = SWAP( 0x20002000 ); SynchronizeIO();
DBDMARegs->channelControl = SWAP( 0x80000000 ); SynchronizeIO();
setIntMask( 0 );
setSeqReg( kMeshResetMESH );
getHBARegsAndClear( true );
fFlagIncompleteDBDMA = false;
fCmd = NULL;
fCmdData = NULL;
fCurrentTargetLun.target = kInvalidTarget;
fCurrentTargetLun.lun = kInvalidLUN;
fMsgInState = kMsgInInit;
fMsgOutPtr = &fCCL[ kcclMSGOdata ];
fMESHAddr->busStatus1 = kMeshRst;
SynchronizeIO();
IODelay( 25 );
fMESHAddr->busStatus1 = 0;
SynchronizeIO();
IOSleep( APPLE_SCSI_RESET_DELAY );
setSeqReg( kMeshResetMESH );
getHBARegsAndClear( true );
fMESHAddr->selectionTimeOut = defaultSelectionTimeout;
SynchronizeIO();
enableCommands();
return kIOReturnSuccess;
}
IOReturn meshSCSIController::waitForMesh( bool clearInterrupts )
{
mach_timespec_t time, startTime, endTime, waitTime;
IOReturn ioReturn = kIOReturnSuccess;
IOGetTime( &time );
startTime = endTime = time;
waitTime.tv_sec = 0;
waitTime.tv_nsec = 250000000; ADD_MACH_TIMESPEC( &endTime, &waitTime );
for ( g.shadow.mesh.interrupt = 0; g.shadow.mesh.interrupt == 0; )
{
getHBARegsAndClear( clearInterrupts );
IOGetTime( &time );
if ( time.tv_sec < endTime.tv_sec )
continue;
if ( time.tv_nsec > endTime.tv_nsec )
{
PAUSE( 0, 0, 'WFM-', "waitForMesh - MESH chip does not respond to command.\n" );
ioReturn = kIOReturnInternalError;
break;
}
}
ELG( time.tv_sec - startTime.tv_sec, time.tv_nsec - startTime.tv_nsec, ' WFM', "waitForMesh" );
return ioReturn;
}
void meshSCSIController::setSeqReg( MeshCommand meshCommand )
{
ELG( (fMESHAddr->interruptMask<<16) | fMESHAddr->interrupt, meshCommand, '=Seq', "setSeqReg" );
if ( fMESHAddr->interruptMask & kMeshIntrCmdDone
&& meshCommand <= kMeshBusFreeCmd )
ELG( fMESHAddr->interrupt, fMESHAddr->interruptMask, 'Trig',
"setSeqReg - may trigger interrupt." );
fMESHAddr->sequence = (UInt8)meshCommand;
SynchronizeIO();
IODelay( 1 );
return;
}
void meshSCSIController::getHBARegsAndClear( bool clearInts )
{
register MeshRegister *mesh = fMESHAddr;
g.shadow.mesh.interrupt = mesh->interrupt;
g.shadow.mesh.error = mesh->error;
g.shadow.mesh.exception = mesh->exception;
g.shadow.mesh.FIFOCount = mesh->FIFOCount;
g.shadow.mesh.busStatus0 = mesh->busStatus0;
g.shadow.mesh.busStatus1 = mesh->busStatus1;
g.shadow.mesh.transferCount1 = mesh->transferCount1;
g.shadow.mesh.transferCount0 = mesh->transferCount0;
g.shadow.mesh.sequence = mesh->sequence; g.shadow.mesh.interruptMask = mesh->interruptMask; g.shadow.mesh.syncParms = mesh->syncParms; g.shadow.mesh.destinationID = mesh->destinationID;
ELG( g.shadow.longWord[ 0 ], g.shadow.longWord[ 1 ], clearInts ? 'Regs' : 'regs', "getHBARegsAndClear." );
if ( g.shadow.mesh.error ) { ELG( g.shadow.mesh.interruptMask, g.shadow.mesh.sequence, 'Err-',
"getHBARegsAndClear - MESH error detected" );
mesh->interrupt = kMeshIntrError;
SynchronizeIO();
}
if ( g.shadow.mesh.exception )
g.shadow.mesh.interrupt |= kMeshIntrException;
if ( clearInts && g.shadow.mesh.interrupt )
{
mesh->interrupt = g.shadow.mesh.interrupt;
SynchronizeIO();
}
return;
}
void meshSCSIController::setIntMask( UInt8 mask )
{
ELG( (fMESHAddr->interrupt<<16) | fMESHAddr->interruptMask, mask, 'Mask', "setIntMask" );
fMESHAddr->interruptMask = mask;
SynchronizeIO();
return;
}
void meshSCSIController::abortActiveCommand()
{
IOReturn ioReturn;
ELG( fCmd, 0, '-AB*', "abortActiveCommand" );
if ( fCmd )
{
fCmdData->results.adapterStatus = kSCSIAdapterStatusProtocolError; completeCommand();
}
getHBARegsAndClear( true );
setIntMask( 0 );
fMsgInFlag = 0;
fMESHAddr->busStatus0 = kMeshAtn;
SynchronizeIO();
setSeqReg( kMeshBusFreeCmd );
waitForMesh( true );
if ( (g.shadow.mesh.busStatus0 & (kMeshPhaseMask | kMeshReq))
== (kBusPhaseMSGO | kMeshReq) )
{
setSeqReg( kMeshFlushFIFO );
fMESHAddr->transferCount0 = 1;
fMESHAddr->transferCount1 = 0;
fMESHAddr->busStatus0 = 0;
SynchronizeIO();
setSeqReg( kMeshMessageOutCmd );
fMESHAddr->xFIFO = kSCSIMsgAbort;
ioReturn = waitForMesh( true );
if ( ioReturn == kIOReturnSuccess )
{
setSeqReg( kMeshEnableReselect );
setIntMask( kMeshIntrMask );
setSeqReg( kMeshBusFreeCmd );
g.intLevel |= kLevelLatched;
return;
}
}
ELG( 0, 0, '-AB-', "abortActiveCommand - target refused to enter MSGO phase" );
resetBus();
super::resetOccurred();
return;
}
void meshSCSIController::disconnect()
{
fCmd = NULL;
fCmdData = NULL;
fCurrentTargetLun.target = kInvalidTarget;
fCurrentTargetLun.lun = kInvalidLUN;
return;
}
IOReturn meshSCSIController::reselectNexus()
{
fCmd = findCommandWithNexus( fCurrentTargetLun, fTag );
if ( fCmd )
{ fCmdData = (PrivCmdData*)fCmd->getCommandData();
ELG( fCmd, *(UInt16*)&fCurrentTargetLun<<16 | (UInt16)fTag, '=Nex', "reselectNexus" );
return kIOReturnSuccess;
}
else
{ if ( fTag != kInvalidTag )
PAUSE( 0, *(UInt16*)&fCurrentTargetLun<<16 | (UInt16)fTag, 'Nex-', "reselectNexus" );
}
return kIOReturnInternalError;
}
void meshSCSIController::updateCurrentIndex()
{
UInt32 count;
UInt32 length = g.shadow.mesh.FIFOCount;
UInt8 buffer[ 16 ];
UInt32 i;
count = *(UInt32*)&fCCL[ kcclBatchSize ];
if ( count == 0 )
return;
count -= g.shadow.mesh.transferCount1 << 8;
count -= g.shadow.mesh.transferCount0;
fCmdData->results.bytesTransferred += count;
*(UInt32*)&fCCL[ kcclBatchSize ] = 0;
if ( fReadAlignmentCount ) {
fCmdData->mdp->writeBytes( fReadAlignmentIndex, &fCCL[ kcclReadBuf8 ], fReadAlignmentCount );
fReadAlignmentCount = 0;
}
if ( g.shadow.mesh.FIFOCount )
{
fCmdData->results.bytesTransferred -= g.shadow.mesh.FIFOCount;
if ( fCmdData->isWrite )
{
setSeqReg( kMeshFlushFIFO );
}
else
{
ELG( fCmdData->results.bytesTransferred, g.shadow.mesh.FIFOCount, 'FIFO',
"updateCurrentIndex - copy FIFO to user buffer." );
count = fCmdData->xferCount - fCmdData->results.bytesTransferred;
if ( count > length )
count = length;
for ( i = 0; i < count; i++ )
buffer[ i ] = fMESHAddr->xFIFO;
fCmdData->mdp->writeBytes( fCmdData->results.bytesTransferred, buffer, count );
fCmdData->results.bytesTransferred += count;
}
}
ELG( 0, fCmdData->results.bytesTransferred, 'UpIx', "updateCurrentIndex" );
return;
}