/* * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /** * Copyright 1997-2000 Apple Computer Inc. All Rights Reserved. * author Mike Johnson * * Set tabs every 4 characters. * * Edit History * 25feb99 mlj Initial conversion from banana sources. */ #include #include #include #include #include #include "mesh.h" extern void IOGetTime( mach_timespec_t *clock_time ); extern void kprintf( const char *, ... ); extern void call_kdp(); // for debugging #define super IOSCSIParallelController OSDefineMetaClassAndStructors( meshSCSIController, IOSCSIParallelController ) ; static globals g; /**** Instantiate the globals ****/ /* Channel Program. Note that this script must match the offsets */ /* specified in mesh.h. This script is copied into the */ /* channel command area (with appropriate entries byte-swapped so */ /* it ends up with the correct endian-ness). */ /* Lines beginning with "slash, star, star, slash" are modified */ /* by the driver before it starts the Channel Program. */ static const DBDMADescriptor gDescriptorList[] = { /* 0x00 kcclProblem - Branch here for anomalies */ { MESH_REG( kMeshInterruptMask, kMeshIntrMask ) }, // Enable MESH interrupt { STOP( kcclStageCCLx ) }, // anomaly /* 0x20 through 0x60 - Data for information phases: */ { RESERVE }, // kcclCMDOdata - CDB ( 6,10,12,16 bytes) { RESERVE }, // kcclMSGOdata - MSGO data (last byte @3F) { RESERVE }, // kcclMSGIdata - MSGI data & STATUS { RESERVE /**** NO LONGER USED ****/ }, // kcclSenseCDB - CDB for (auto) Sense { RESERVE }, // kcclBatchSize, kcclStageLabel /* 0x70 - kcclSense - AutoSense commands - no longer used: */ { RESERVE }, // Spare { RESERVE }, // Spare { RESERVE }, // Spare { RESERVE }, // Spare { RESERVE }, // Spare /* 0xC0 - kcclPrototype - Prototype MESH 4-command Transfer sequence: */ { MOVE_4( kcclBatchSize, 0, kRelAddressCP ) }, // MESH batch size { MESH_REG( kMeshTransferCount1, 0 ) }, // Set high order Transfer Count { MESH_REG( kMeshTransferCount0, 0 ) }, // Set low order Transfer Count { MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA )}, // Assume Data-In { RESERVE }, // kcclReadBuf8 { RESERVE }, // spare /* 0x120 kcclStart - Arbitrate (START CHANNEL PROGRAM HERE): */ /* 0x140 kcclBrProblem */ { STAGE( kcclStageArb ) }, { MESH_REG( kMeshSequence, kMeshArbitrateCmd ) }, // issue Arbitrate { BR_IF_PROBLEM }, // branch if exception or error /* 0x150 - Select with Attention: */ { STAGE( kcclStageSelA ) }, { CLEAR_CMD_DONE }, { MESH_REG( kMeshSequence, kMeshSelectCmd | kMeshSeqAtn ) }, // select with attention { BR_IF_PROBLEM }, // branch if failed /* 0x190 kcclMsgoStage - Message-Out: */ { STAGE( kcclStageMsgO ) }, { CLEAR_CMD_DONE }, /* 0x1B0 kcclMsgoBranch - modify this BRANCH to fall through for multibyte messages: */ /**/{ BRANCH( kcclLastMsgo ) }, // kcclMsgoBranch - go do only byte of Msg /* 0x1C0 - do all but last byte of multibyte message: */ { MESH_REG( kMeshTransferCount1, 0x00 ) }, // count does include last byte /**/{ MESH_REG( kMeshTransferCount0, 0xFF ) }, // kcclMsgoMTC - modify MESH xfer count here { MESH_REG( kMeshSequence, kMeshMessageOutCmd | kMeshSeqAtn | kMeshSeqDMA ) }, // DMA MsgO with ATN /**/{ MSGO( kcclMSGOdata, 255 ) }, // kcclMsgoDTC - output all but last byte { CLEAR_CMD_DONE }, /* 0x210 kcclLastMsgo - wait for REQ signal before dropping ATN: */ { MESH_REG( kMeshInterruptMask, 0 ) }, // inhibit MESH interrupt { MESH_REG_WAIT( kMeshSequence, kMeshStatusCmd | kMeshSeqAtn ) }, // gen PhaseMM { CLEAR_INT_REG }, // clear PhaseMM & CmdDone { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints /* 0x250 - put out the last or only byte of Message-Out phase: */ { MESH_REG( kMeshTransferCount1, 0x00 ) }, { MESH_REG( kMeshTransferCount0, 0x01 ) }, { MESH_REG( kMeshSequence, kMeshMessageOutCmd | kMeshSeqDMA ) },// no more ATN { MSGO( kcclMSGOLast, 1 ) }, /* 0x290 kcclCmdoStage - Command Out: */ { STAGE( kcclStageCmdO ) }, { CLEAR_CMD_DONE }, { MESH_REG( kMeshTransferCount1, 0x00 ) }, /**/{ MESH_REG( kMeshTransferCount0, 0x06 ) }, // kcclCmdoMTC - Set MESH xfer count to 6 { MESH_REG( kMeshSequence, kMeshCommandCmd | kMeshSeqDMA )}, // Command phase with DMA on /**/{ CMDO( 6 ) }, // kcclCmdoDTC - output the CDB /* 0x2F0 - DATA XFER - branch to the built CCL @ 0x05D0: */ /* also, kcclReselect - reselect code enters here: */ { CLEAR_CMD_DONE }, { STAGE( kcclStageXfer ) }, { BRANCH( kcclDataXfer ) }, // go do Xfer CCL /* 0x320 kcclOverrun - dump excess data in the bit bucket: */ /* Exc and Err are still disabled. */ { STAGE( kcclStageBucket ) }, { MESH_REG( kMeshTransferCount1, 0x00 ) }, // set MESH Transfer Count to max { MESH_REG( kMeshTransferCount0, 0x00 ) }, { CLR_PHASEMM }, { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints /**/{ MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA ) }, // set Seq Reg /**/{ BUCKET }, // OUT/INPUT_LAST the bits { BR_NO_PROBLEM( kcclOverrunDBDMA ) }, // loop til PhaseMismatch { BR_IF_PROBLEM }, // take the interrupt now /* 0x3B0 kcclSyncCleanUp - clean up after Sync xfer: */ { CLEAR_INT_REG }, // clear PhaseMM & CmdDone (& Err?) { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints /* 0x3D0 kcclGetStatus - setup CCL for status, command complete and bus free: */ { STAGE( kcclStageStat ) }, { MESH_REG( kMeshTransferCount1, 0x00 ) }, { MESH_REG( kMeshTransferCount0, 0x01 ) }, // set MESH xfer count to 1 { MESH_REG( kMeshSequence, kMeshStatusCmd | kMeshSeqDMA )},// Status-in phase with DMA on { STATUS_IN }, // input the status byte /* 0x420 - Message In: */ { STAGE( kcclStageMsgI ) }, { CLEAR_CMD_DONE }, { MESH_REG( kMeshTransferCount1, 0x00 ) }, { MESH_REG( kMeshTransferCount0, 0x01 ) }, // set MESH xfer count to 1 { MESH_REG( kMeshSequence, kMeshMessageInCmd | kMeshSeqDMA )}, // Status-in phase with DMA on { MSGI( 1 ) }, // get the Message-In byte /* 0x480 - Bus Free: */ { STAGE( kcclStageFree ) }, { CLEAR_CMD_DONE }, { MESH_REG( kMeshSequence, kMeshEnableReselect ) }, // Enable Reselect { MESH_REG( kMeshSequence, kMeshBusFreeCmd ) }, // Bus Free phase { BR_IF_PROBLEM }, // branch if failed /* 0x4D0 kcclMESHintr - Good completion: */ { STAGE( kcclStageGood ) }, { MESH_REG( kMeshInterruptMask, kMeshIntrMask ) }, // latch MESH interrupt { STOP( kcclStageStop ) }, // Stop /* The rest of the Channel Program area is used for autosense */ /* and data transfer channel commands: */ /* kcclDataXfer Start of data transfer channel commands */ }; /* end gDescriptorList structure */ const UInt32 gDescriptorListSize = sizeof( gDescriptorList ); /* MAX_DMA_XFER is set so that we don't have to worry about the */ /* ambiguous "zero" value in the MESH and DBDMA transfer registers */ /* that can mean either 65536 bytes or zero bytes. */ #define MAX_DMA_XFER 0x0000F000 // round down to nearest page enum /***** values for g.intLevel: *****/ { kLevelISR = 0x80, /* In Interrupt Service Routine */ kLevelLocked = 0x40, /* MESH interrupts locked out */ kLevelSIH = 0x20, /* In Secondary Interrupt Handler */ kLevelLatched = 0x10 /* Interrupt latched */ }; #if USE_ELG static void AllocateEventLog( UInt32 size ) { if ( g.evLogBuf ) return; g.evLogFlag = 0; /* assume insufficient memory */ 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; // ??? overran buffer? // g.evLogFlag = 0xFEEDBEEF; // continuous wraparound // g.evLogFlag = 'step'; // stop at each ELG g.evLogFlag = 0x0333; // any nonzero - don't wrap - stop logging at buffer end IOLog( "AllocateEventLog - &globals=%8x buffer=%8x\n", (UInt32)&g, (UInt32)g.evLogBuf ); return; }/* end AllocateEventLog */ static void EvLog( UInt32 a, UInt32 b, UInt32 ascii, char* str ) { register UInt32 *lp; /* Long pointer */ mach_timespec_t time; if ( g.evLogFlag == 0 ) return; IOGetTime( &time ); lp = (UInt32*)g.evLogBufp; g.evLogBufp += 0x10; if ( g.evLogBufp >= g.evLogBufe ) /* handle buffer wrap around if any */ { g.evLogBufp = g.evLogBuf; if ( g.evLogFlag != 0xFEEDBEEF ) // make 0xFEEDBEEF a symbolic ??? g.evLogFlag = 0; /* stop tracing if wrap undesired */ } /* compose interrupt level with 3 byte time stamp: */ *lp++ = (g.intLevel << 24) | ((time.tv_nsec >> 10) & 0x003FFFFF); // ~ 1 microsec resolution *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 %s\n", time.tv_nsec>>10, a, b, code, str ); 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; }/* end EvLog */ 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++ = '{'; // prepend p1 in hex: 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++ = ' '; // prepend p2 in hex: 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 ) ; // kprintf( work ); // panic( work ); Debugger( work ); // call_kdp(); // ??? use kdp=3 in boot parameters return; }/* end Pause */ #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 /* USE_ELG */ ELG( this, provider, 'Cnfg', "configure" ); fProvider = (IOPCIDevice*)provider; ioReturn = initializeHardware(); if ( ioReturn != kIOReturnSuccess ) return false; /* Register our interrupt handler routine: */ 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(); /* allocate a big-endian memory cursor: */ fMemoryCursor = IOBigMemoryCursor::withSpecification( kMaxDMATransfer, kMaxDMATransfer ); if ( fMemoryCursor == NULL ) { PAUSE( 0, kMaxDMATransfer, 'Mem-', "mesh::start - IOBigMemoryCursor::withSpecification NG" ); return false; } /* Fill in the SCSIControllerInfo structure and return: */ controllerInfo->initiatorId = 7; controllerInfo->maxTargetsSupported = 8; controllerInfo->maxLunsSupported = 8; controllerInfo->minTransferPeriodpS = 100000; /* picoSecs for 10 MHz */ controllerInfo->maxTransferOffset = 15; controllerInfo->maxTransferWidth = 1; controllerInfo->maxCommandsPerController= 0; controllerInfo->maxCommandsPerTarget = 8; // 0 is unlimited controllerInfo->maxCommandsPerLun = 0; controllerInfo->tagAllocationMethod = kTagAllocationPerLun; controllerInfo->maxTags = 256; controllerInfo->commandPrivateDataSize = sizeof( PrivCmdData ); controllerInfo->disableCancelCommands = false; return true; }/* end configure */ 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 ]; /* Identify byte: */ msgByte = kSCSIMsgIdentify | kSCSIMsgEnableDisconnectMask | fCurrentTargetLun.lun; if ( scsiCDB.cdbFlags & kCDBFlagsNoDisconnect ) msgByte &= ~kSCSIMsgEnableDisconnectMask; *fMsgOutPtr++ = msgByte; /* Tag msg: */ if ( scsiCDB.cdbTagMsg ) { *fMsgOutPtr++ = scsiCDB.cdbTagMsg; *fMsgOutPtr++ = scsiCDB.cdbTag; ELG( 0, scsiCDB.cdbTagMsg<<16 | scsiCDB.cdbTag, ' tag', "meshSCSIController::executeCommand - tag" ); } /* Abort msg: */ if ( scsiCDB.cdbAbortMsg ) { ELG( scsiCommand->getOriginalCmd(), scsiCDB.cdbAbortMsg, 'Abor', "meshSCSIController::executeCommand - abort msg." ); *fMsgOutPtr++ = scsiCDB.cdbAbortMsg; } /* Sync negotiation msg: */ fCmdData->negotiatingSDTR = fCmdData->negotiatingSDTRComplete = false; if ( scsiCDB.cdbFlags & kCDBFlagsNegotiateSDTR ) { fCmdData->negotiatingSDTR = true; *fMsgOutPtr++ = kSCSIMsgExtended; *fMsgOutPtr++ = 3; *fMsgOutPtr++ = kSCSIMsgSyncXferReq; *fMsgOutPtr++ = targetParms.transferPeriodpS / 4000; *fMsgOutPtr++ = targetParms.transferOffset; } /***** Try to start the command on the hardware: *****/ rc = startCommand(); /* Call the hardware layer. */ if ( rc != kHardwareStartOK ) { /* Hardware can't start now */ ELG( fCmd, 0, 'Exe-', "meshSCSIController::executeCommand - command bounced back" ); rescheduleCommand( fCmd ); fCmd = NULL; } return; }/* end executeCommand */ void meshSCSIController::cancelCommand( IOSCSIParallelCommand *scsiCommand ) { IOSCSIParallelCommand *origCmd; PrivCmdData *origCmdData; SCSIResults results; origCmd = scsiCommand->getOriginalCmd(); ELG( scsiCommand, origCmd, 'Can-', "meshSCSIController::cancelCommand" ); if ( origCmd ) /* if original command still around, complete it: */ { origCmd->getResults( &results ); origCmdData = (PrivCmdData*)origCmd->getCommandData(); results.bytesTransferred = origCmdData->results.bytesTransferred; origCmd->setResults( &results ); origCmd->complete(); } scsiCommand->complete(); return; }/* end cancelCommand */ 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; }/* end resetCommand */ /* Fetch the device's bus address and interrupt port number. */ /* Also, allocate one page of memory for the channel program. */ IOReturn meshSCSIController::initializeHardware() { IOReturn ioReturn; int i; ELG( 0, 0, 'IniH', "initializeHardware" ); fInitiatorID = kInitiatorIDDefault; fInitiatorIDMask = 1 << kInitiatorIDDefault; /* BusID bitmask for reselection. */ 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; }/* end initializeHardware */ 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; // for debugging, miniMon ... } 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 /* CustomMiniMon */ } return kIOReturnSuccess; }/* end getHardwareMemoryMaps */ /* Fetch the device's bus address and allocate one page of memory */ /* for the channel command. (Strictly speaking, we don't need an */ /* entire page, but we can use the rest of the page for a permanent */ /* status log). */ /* @param deviceDescription Specify the device to initialize. */ /* @return kIOReturnSuccess if successful, else an error status. */ IOReturn meshSCSIController::allocHdwAndChanMem() { /* Set the default selection timeout to the MESH value (10 msec units). */ fSelectionTimeout = 250 / 10; // ??? symbolic /* Allocate a page of wired-down memory in the kernel: */ 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; } /* Remember the number of DBDMA descriptors that */ /* can be used for data transfer channel commands. */ fDBDMADescriptorMax = (fCCLSize - kcclDataXfer) / sizeof( DBDMADescriptor ); g.cclPhysAddr = (UInt32)fCCLPhysAddr; // for debugging ease g.cclLogAddr = (UInt32)fCCL; ELG( fCCLPhysAddr, fCCL, '=CCL', "allocHdwAndChanMem - CCL phys/logical addresses." ); initCP(); /* What do we do on failure? Should we try to deallocate */ /* the stuff we created, or will the system do this for us? */ return kIOReturnSuccess; }/* end allocHdwAndChanMem */ /* Perform one-time-only channel command program initialization. */ 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" ); /* Set the interrupt, branch, and wait DBDMA registers. */ /* Caution: the following MESH interrupt register bits are */ /* EndianSwapped, reverse polarity and in a different position. */ /* The pattern is: 0xvv00mm00, where mm is a mask byte */ /* and vv is a value byte to match. (After EndianSwapping). */ /* 0x80 means NO errors (kMeshIntrError) */ /* 0x40 means NO exceptions (kMeshIntrException) */ /* 0x20 means NO command done (kMeshIntrCmdDone) */ /* Branch Select is used with BRANCH_FALSE */ // DBDMASetInterruptSelect( 0x00000000 ); /* Never let DBDMA interrupt */ // DBDMASetWaitSelect( 0x00200020 ); /* Wait until command done */ // DBDMASetBranchSelect( 0x00C000C0 ); /* Branch if exception or error */ DBDMARegs->interruptSelect = 0x00000000; /* Never let DBDMA interrupt */ DBDMARegs->waitSelect = 0x20002000; /* Wait until command done */ DBDMARegs->branchSelect = 0xC000C000; /* Br if Exc or Err */ SynchronizeIO(); /* Relocate and EndianSwap the global channel command list */ /* into the page that is shared with the DBDMA device. */ for ( i = 0; i < gDescriptorListSize; i += sizeof( DBDMADescriptor ) ) { dst->operation = SWAP( src->operation ); /* copy command with count */ 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++; } /* FOR all elements in the descriptor list */ return; }/* end initCP */ /* doHBASelfTest - MESH chip self-test. (Minimal: it could be extended.) */ 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; }/* end doHBASelfTest */ void meshSCSIController::interruptOccurred( IOInterruptEventSource *ies, int intCount ) { // DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr; // ELG( ies, intCount, 'Int+', "interruptOccurred" ); g.intLevel |= kLevelISR; /* set ISR flag */ g.intLevel &= ~kLevelLatched; /* clear latched */ // ELG( DBDMARegs->channelStatus, DBDMARegs->commandPtrLo, 'Int+', "interruptOccurred." ); ELG( fCmd, *(UInt32*)&fCCL[ kcclStageLabel ], 'Int+', "interruptOccurred." ); // ELG( *(UInt32*)0xF3000024, *(UInt32*)0xF300002C, 'Int ', "interruptOccurred." ); doHardwareInterrupt(); /**** HANDLE THE INTERRUPT ****/ // ELG( fCmd, *(UInt32*)0xF300002C, 'Intx', "interruptOccurred." ); g.intLevel &= ~kLevelISR; /* clear ISR flag */ return; }/* end interruptOccurred */ /* doHardwareInterrupt - called from Workloop in superclass */ void meshSCSIController::doHardwareInterrupt() { DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr; getHBARegsAndClear( true ); /* get the MESH registers */ setIntMask( 0 ); /* Disable MESH interrupts */ fFlagReselecting = false; if ( g.shadow.mesh.interrupt == 0 ) { /* Interrupts can occur with no bits set in the */ /* interrupt register one way: */ /* - Eating interrupts in the driver (the ASIC */ /* latches the interrupt even though the */ /* driver or Channel Program clears the MESH */ /* interrupt register). */ PAUSE( DBDMARegs->commandPtrLo, (g.shadow.mesh.busStatus0 << 8) | g.shadow.mesh.busStatus1, 'ISR?', "doHardwareInterrupt - spurious interrupt" ); if ( !fCmd ) { /* if no request: */ setIntMask( kMeshIntrMask ); /* Enable interrupts */ enableCommands(); /* let superclass issue another command */ } return; }/* end IF no bit set in interrupt register */ /// dbdma_flush( DBDMA_MESH_SCSI ); /* DBDMA may be hung in */ /// dbdma_stop( DBDMA_MESH_SCSI ); /* middle of transfer. */ DBDMARegs->channelControl = SWAP( 0x20002000 ); // set FLUSH bit SynchronizeIO(); DBDMARegs->channelControl = SWAP( 0x80000000 ); // clr RUN bit SynchronizeIO(); // invalidate_cache_v( (vm_offset_t)fCCL, fCCLSize ); /* If the DBDMA was running a channel command, handle this */ /* (this could be done at a lower priority level). */ if ( *(UInt32*)&fCCL[ kcclStageLabel ] ) { processInterrupt(); return; } /* This was not a DBDMA completion. */ /* See if the last MESH operation completed */ /* without errors or exceptions. */ if ( g.shadow.mesh.interrupt == kMeshIntrCmdDone ) { /* This was presumably a Programmed IO completion. */ if ( fCmd ) { /* The command has not completed yet. */ /* We need to wait for a phase stabilizing interrupt. */ PAUSE( 0, 0, 'dhi-', "doHardwareInterrupt - MESH interrupt problem: need phase stabilizing wait.\n" ); return; } else { /* There is no active command. */ /* This is presumably a bus-free completion. */ setIntMask( kMeshIntrMask ); /* Re-enable interrupts */ enableCommands(); /* let superclass issue another command */ return; } }/* end IF CmdDone without Err or Exc */ /* None of the above "completion" states occurred. */ /* Either a command completed unsuccessfully, or we */ /* were reselected. First, check for phase mismatch. */ 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 { /* Handle reselection and all other problems separately. */ processInterrupt(); } return; }/* end doHardwareInterrupt */ /* Respond to a DBDMA channel command completion interrupt */ /* or some error or exception condition. */ void meshSCSIController::processInterrupt() { UInt32 stage; /* Stage in the Channel Program */ UInt32 cclIndex; /* Index of CCL descriptor */ UInt32 count; /* transfer count */ UInt8 phase; /* Current bus phase */ IOReturn rc; /* Get the state of the DBDMA: */ stage = *(UInt32*)&fCCL[ kcclStageLabel ]; cclIndex = SWAP( ((DBDMAChannelRegisters*)dbdmaAddr)->commandPtrLo ) - (UInt32)fCCLPhysAddr; *(UInt32*)&fCCL[ kcclStageLabel ] = 0; /* Check for SCSI Bus Reset Detected: */ if ( g.shadow.mesh.error & kMeshErrSCSIRst ) { ELG( fCmd, stage, 'BRst', "Process interrupt with no active request\n" ); fCmd = NULL; super::resetOccurred(); setIntMask( kMeshIntrMask ); enableCommands(); /* let superclass issue another command */ return; } if ( fCmd == NULL ) { if ( g.shadow.mesh.exception & kMeshExcResel ) { handleReselectionInterrupt(); return; } /* There is no active request and we are not reselecting. */ /* Can get here if Reject/Abort occurs or after a BusFree */ /* command is put in the Sequence register and we exit the */ /* interrupt. */ ELG( 0, 0, 'Int0', "Process interrupt with no active request\n" ); setIntMask( kMeshIntrException | kMeshIntrError ); enableCommands(); /* let superclass issue another command */ return; }/* end IF had no active command */ /* There is an active request - switch on stage of the DBDMA: */ switch ( stage ) { case kcclStageGood: /* Normal completion */ doInterruptStageGood(); break; case kcclStageInit: /* Value before DBDMA runs */ case kcclStageArb: /* Arbitration anomaly */ doInterruptStageArb(); break; case kcclStageSelA: /* Selection anomaly */ doInterruptStageSelA(); break; case kcclStageMsgO: /* Message Out */ doInterruptStageMsgO(); break; case kcclStageCmdO: /* Command stage anomaly */ doInterruptStageCmdO(); break; case kcclStageXfer: doInterruptStageXfer(); /* DMA transfer complete */ break; case kcclStageStat: /* Synchronous, odd transfer, data-out */ /* OR no data, disconnect */ /* Don't use updateCurrentIndex here because */ /* kcclStageStat destroys TC with a 1. */ count = *(UInt32*)&fCCL[ kcclBatchSize ]; /* Our transfer count */ fCmdData->results.bytesTransferred += count; /* Increment data index */ if ( fReadAlignmentCount ) // Hack for Radar 1670626 { fCmdData->mdp->writeBytes( fReadAlignmentIndex, &fCCL[ kcclReadBuf8 ], fReadAlignmentCount ); fReadAlignmentCount = 0; } ELG( count, fCmdData->results.bytesTransferred, 'Uidx', "processInterrupt" ); *(UInt32*)&fCCL[ kcclBatchSize ] = 0; /* Clear our count */ /* Analyze the current bus signals: */ if ( !(g.shadow.mesh.busStatus0 & kMeshReq) ) { /* Get here if Sync Read or Write is too short as */ /* in reading 512 bytes from a 2K block of CD-ROM. */ startBucket(); return; }/* end IF no REQ signal */ phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; switch ( phase ) { case kBusPhaseMSGI: rc = DoMessageInPhase(); break; case kBusPhaseDATO: case kBusPhaseDATI: /* Get here if Async Read or Write is too short as */ /* in reading 512 bytes from a 2K block of CD-ROM */ startBucket(); break; default: PAUSE( 0, phase, 'pmm-', "processInterrupt - expected Status phase.\n" ); break; }/* end SWITCH on phase */ break; case kcclStageBucket: count = *(UInt32*)&fCCL[ kcclBatchSize ]; /* Our transfer count */ fCmdData->results.bytesTransferred += count; /* Increment data index */ *(UInt32*)&fCCL[ kcclBatchSize ] = 0; /* Clear our count */ ELG( count, fCmdData->results.bytesTransferred, 'Buck', "processInterrupt - bit bucket done.\n" ); /* MESH doesn't always have a phase mismatch when completing a */ /* synchronous data write. It may be that the drive is going to */ /* MsgIn with SDP and/or disconnect instead of Status and the */ /* timing is different. If the current phase is not a data phase, */ /* AND the TC and FIFO count indicate no data overflowed AND */ /* and all the data transferred, don't set an error condition. */ 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 ); /* flush the FIFO */ runDBDMA( kcclGetStatus, kcclStageStat ); break; case kcclStageMsgI: /* DBDMA stopped in MsgIn stage after Status stage: */ /* Radar 2253653 - A SCSI scanner is taking a long time to drop Req */ /* after MESH asserts Ack to the Status byte. Because the Status */ /* command got cmdDone and MESH doesn't wait for the next phase, */ /* the channel program proceeds to the next phase and issues a */ /* DMA,MsgIn to the Sequence register which drops Ack while Req */ /* is still asserted. MESH shouldn't do that. MESH then sees a */ /* Phase Mismatch on Status phase while expecting MsgIn phase thus */ /* causing an Exception interrupt. */ ELG( 0, fMESHAddr->busStatus0 << 8 | fMESHAddr->busStatus1, '?mi-', "processInterrupt - Phase Mismatch on MsgIn stage.\n" ); fFlagIncompleteDBDMA = false; /* indicate no-more-data */ if ( !(fMESHAddr->busStatus0 & kMeshReq) ) IOSleep( 1 ); getHBARegsAndClear( true ); /* get and log the MESH registers */ phase = g.shadow.mesh.busStatus0 & (kMeshPhaseMask | kMeshReq); if ( phase == (kMeshReq | kBusPhaseMSGI) ) { DoMessageInPhase(); /* hopefully get Command Complete message */ } break; case kcclStageFree: /* DBDMA stopped in BusFree stage: */ default: /* Can't happen? */ PAUSE( cclIndex, stage, 'P i-', "processInterrupt - strange or unknown interrupt for device.\n" ); break; }/* end SWITCH on Channel Program stage */ return; }/* end processInterrupt */ /* Start a SCSI transaction for the specified command. */ UInt8 meshSCSIController::startCommand() { fCmd->getPointers( &fCmdData->mdp, &fCmdData->xferCount, &fCmdData->isWrite ); clearCPResults(); /* clear the result field in all the Channel Commands */ { fCmdData->results.bytesTransferred = 0; fCmdData->savedDataPosition = 0; updateCP( false ); /* Update the DBDMA Channel Program */ } /***** Can a caller override the default timeout? *****/ fMESHAddr->selectionTimeOut = fSelectionTimeout; fMESHAddr->destinationID = fCurrentTargetLun.target; // fMESHAddr->syncParms = kSyncParmsAsync; fMESHAddr->syncParms = fSyncParms[ fCurrentTargetLun.target ]; SynchronizeIO(); runDBDMA( kcclStart, kcclStageInit ); if ( g.intLevel & kLevelLatched ) /* return Busy if reselecting */ return kHardwareStartBusy; /* or some other issue. */ return kHardwareStartOK; }/* end startCommand */ /* Initialize the data transfer channel command list for a normal SCSI */ /* command. The channel command list has a complex structure of */ /* transfer groups and items, where: */ /* transfer group The number of bytes transferred by a single */ /* MESH operation. This will be from 1 to */ /* kMaxDMATransfer (65536 - 4096). */ /* transfer item The number of bytes transferred by a single */ /* DBDMA operation. These bytes are guaranteed */ /* to be physically-contiguous. */ /* Thus, the data transfer CCL looks like the following: */ /* Prolog 1: Load MESH with the first group count. */ /* Item 1.1: Load DBDMA with the first physical address and */ /* item count. */ /* Item 1.2 etc: Load DBDMA with the next physical address and */ /* item count. */ /* Prolog 2, etc. Load MESH with the next group count. */ /* Item 2.1, etc. Load DBDMA with the next group of physical */ /* addresses. */ /* Stop/Branch If all of the data transfer commands fit in the */ /* channel command list, branch to the Status phase*/ /* channel command. Otherwise, stop transfer */ /* (which stops in Data phase) and rebuild the */ /* command list for the next set of data. */ /* Note that the last DBDMA command must be INPUT_LAST or OUTPUT_LAST */ /* to handle synchronous transfer odd-byte disconnect. */ void meshSCSIController::updateCP( bool reselecting ) { DBDMADescriptor *descProto = (DBDMADescriptor*)&fCCL[ kcclPrototype ]; DBDMADescriptor *descriptorPtr; /* current data descriptor */ DBDMADescriptor *descriptorMax; /* beyond the last data descriptor */ DBDMADescriptor *preamblePtr; /* current prolog descriptor */ SCSICDBInfo scsiCDB; UInt32 dbdmaOpProto; /* prototype Opcode for DBDMA */ UInt32 dbdmaOp; /* Opcode for DBDMA */ UInt32 meshSeq; /* Opcode for MESH request */ SInt32 transferLength; /* Number of bytes left to transfer */ UInt32 totalXferLen = 0; /* Total length of this transfer */ UInt32 groupLength; /* Number of bytes in this group */ UInt8 syncParms; /* Fast synchronous param value */ UInt32 actualRanges; IOPhysicalSegment range[ kMaxMemCursorSegs ]; DBDMADescriptor *dp; UInt32 rangeLength, rangeLocation; UInt32 i; IOReturn ioReturn = kIOReturnSuccess; fReadAlignmentCount = 0; /* Clear Read misalignment condition */ /* How many descriptors can we store (need some slop for the */ /* terminator commands). Get a pointer to the first free */ /* descriptor and the total number of bytes left to transfer in */ /* this IO request. */ descriptorPtr = (DBDMADescriptor*)&fCCL[ kcclDataXfer ]; descriptorMax = &descriptorPtr[ fDBDMADescriptorMax - 16 ]; transferLength = fCmdData->xferCount - fCmdData->results.bytesTransferred; ELG( descriptorPtr, transferLength, 'UpCP', "updateCP" ); if ( !reselecting ) { setupMsgO(); /* Setup for Message Out phase. */ fCmd->getCDB( &scsiCDB ); /* Setup for Command phase: */ fCCL[ kcclCmdoMTC ] = scsiCDB.cdbLength; /* MESH transfer count */ fCCL[ kcclCmdoDTC ] = scsiCDB.cdbLength; /* DBDMA count */ bcopy( &scsiCDB.cdb[0], &fCCL[ kcclCMDOdata ], scsiCDB.cdbLength ); }/* end IF not reselecting */ /* Generate MESH "sequence" & DBDMA "operation" for Input or Output: */ 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 ) { /* Do one group, ie, enough CCs to fill a MESH transfer count. */ /* There are more data to be transferred, and CCL space to store*/ /* another group of data. First, leave space for the preamble. */ preamblePtr = descriptorPtr; groupLength = 0; descriptorPtr += 4; /* Preamble takes 4 descriptors */ /* Do one group of pages: */ 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; // 29apr99 mlj Radar 1670626 - the DBDMAs in Grand Central (and // either Heathrow or O'Hare) have a bug. On the initial // data xfer of a Read, if the buffer is not aligned on an // 8-byte boundary, and the transfer ends before the boundary is // reached, then the memory in front of the buffer is trashed. // If these conditions apply, we read the misaligned bytes // into the CCL at kcclReadBuf8 now and copy them to the // user buffer later when we get an interrupt. The 1st range // is split and 2 DBDMA Channel Commands are generated. if ( (i == 0) // 1st range of group && (totalXferLen == 0) // 1st group of batch && (!fCmdData->isWrite) // READing && (rangeLocation & 0x07) ) // not 8-byte aligned { 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); /* add LAST to cmd */ descriptorPtr->operation = SWAP( dbdmaOp ); descriptorPtr->address = SWAP( (UInt32)fCCLPhysAddr + kcclReadBuf8 ); descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclProblem ); descriptorPtr->result = 0; // for debugging descriptorPtr++; if ( rangeLength == 0 ) continue; rangeLocation += fReadAlignmentCount; }/* end IF READing and buffer is misaligned at batch start */ dbdmaOp = dbdmaOpProto | rangeLength; transferLength -= rangeLength; if ( transferLength <= 0 ) dbdmaOp |= (INPUT_MORE ^ INPUT_LAST); /* add LAST to cmd */ descriptorPtr->operation = SWAP( dbdmaOp ); descriptorPtr->address = SWAP( rangeLocation ); descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclProblem ); descriptorPtr->result = 0; // for debugging descriptorPtr++; }/* end FOR each range in this group */ if ( groupLength == 0 ) { /* Nothing was built - we apparently failed to get */ /* a physical address. Note: there is a potential problem with */ /* the following sequence as the *previous* DBDMA command, if */ /* any, should be changed to set xxPUT_LAST. */ 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; /* Exit the outer loop */ } else { totalXferLen += groupLength; /* This group is complete. Fill in the preamble. */ /* The preamble consists of the following commands: */ /* [0] Move to kcclBatchSize */ /* [1] Store group length high-byte in MESH */ /* transfer count 1 register */ /* [2] Store group length low-byte in MESH */ /* transfer count 1 register */ /* [3] Store the input/output command in the MESH */ /* sequence register. */ /* If the command finishes prematurely (perhaps the */ /* device wants to disconnect), the interrupt service */ /* routine will use totalXferLen - the residual byte */ /* count to determine the number of bytes xferred. */ descProto[0].cmdDep = totalXferLen; // update batch size 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 there is another group, wait for */ /* cmdDone and clear it: */ if ( transferLength > 0 ) { /* Wait for CmdDone: */ bcopy( &fCCL[ kcclBrProblem ], descriptorPtr, sizeof( DBDMADescriptor ) ); ++descriptorPtr; /* Clear CmdDone: */ /* HACK - if we reached the end of the CCL page, */ /* we don't want to clear cmdDone because we will lose */ /* an interrupt. So, this instruction may be deleted */ /* down below. (Radar 2298440) */ descriptorPtr->operation = SWAP( STORE_QUAD | KEY_SYSTEM | 1 ); descriptorPtr->address = SWAP( fMESHPhysAddr + kMeshInterrupt ); descriptorPtr->cmdDep = SWAP( kMeshIntrCmdDone ); descriptorPtr->result = 0; ++descriptorPtr; }/* end IF not last group */ }/* end if/ELSE a group was built */ }/* end outer WHILE */ /* All of the data have been transferred (or we ran off the end */ /* of the CCL). Update the transfer start index to reflect on */ /* what we attempt to transfer in this DATA operation. If */ /* we completed DATA phase, branch to the Status Phase CCL; */ /* if not, stop the channel command so we can reload the CCL */ /* with the next big chunk. */ /* When the transfer completes, the last prolog will have stored*/ /* the total number of bytes transferred in a known location in */ /* the CCL area. */ /* Now, append the data transfer postamble to handle */ /* synchronous odd-byte disconnect and jump to status phase */ /* (or just stop if there's more DMA) */ /* Do some synchronous data transfer cleanup: */ syncParms = fSyncParms[ fCurrentTargetLun.target ]; fMESHAddr->syncParms = syncParms; SynchronizeIO(); ELG( fMsgOutFlag, syncParms, 'SynP', "updateCP - sync parms" ); if ( ((syncParms & 0xF0) || (fMsgOutFlag & kFlagMsgOut_SDTR)) // Sync? && (totalXferLen > 0) // any data moving? && (transferLength == 0) ) // end of xfer? { fFlagIncompleteDBDMA = false; /* indicate complete xfer */ /* MESH has a problem at the end of Synchronous transfers. */ /* If the target is fast enough, it can move from data phase to */ /* Status phase while MESH still has ACKed bytes in its FIFO and */ /* the DBDMA is still running. MESH raises PhaseMismatch Exception */ /* causing an interrupt in which we must empty the FIFO and move */ /* the bytes to the user's buffer by programmed IO. */ /* If the target is not fast enough, we can save the interrupt and */ /* bypass the mess. */ /* So, we do the following: */ /* 1) Enable only MESH Err interrupts; disable Exc and CmdDone. */ /* 2) Don't Wait; Branch if an interrupt may have already occurred.*/ /* 3) Wait for cmdDone at least for TC = FIFO count = 0 and */ /* maybe including PhaseMismatch. Branch to SyncCleanup if PMM. */ /* 4) Assume an interphase condition as opposed to an */ /* overrun condition and Branch Always to get Status. */ /* If the Channel Program gets this far, the OUTPUT_LAST */ /* has finished writing its data to the FIFO and MESH may still */ /* be putting bytes on the bus OR the INPUT_LAST has read all */ /* its data from the FIFO and MESH has already ACKed them. */ /* There may be or not some time before REQ appears again, */ /* either for data overrun or the next phase. */ /* Disable Exc and CmdDone (leave Err enabled): */ descriptorPtr->operation = SWAP( STORE_QUAD | KEY_SYSTEM | 1 ); descriptorPtr->address = SWAP( fMESHPhysAddr + kMeshInterruptMask ); descriptorPtr->cmdDep = SWAP( kMeshIntrError ); descriptorPtr->result = 0; ++descriptorPtr; /* Take the interrupt if PhaseMismatch not definitely caught. */ /* Branch (don't wait for cmdDone) if Exc may have already occurred: */ descriptorPtr->operation = SWAP( NOP_CMD | kBranchIfFalse ); descriptorPtr->address = 0; descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclProblem ); descriptorPtr->result = 0; /* Radar 2281306 ( and 2272931 ): */ /* Output may completely fit in the FIFO and not make it out */ /* to the SCSI bus if the target disconnects after the command. */ /* If that's possible, wait here for cmdDone and */ /* take the PhaseMismatch interrupt. This situation occurred on a */ /* Mode Select with an output of 12 bytes. Do this to prevent */ /* the Stage from advancing from kcclStageXfer so that proper */ /* cleanup can take place. */ if ( (totalXferLen <= 16) && fCmdData->isWrite ) { descriptorPtr->operation = SWAP( NOP_CMD | kWaitIfTrue | kBranchIfFalse ); } ++descriptorPtr; /* Possible PhaseMisMatch caught after FIFO emptied. */ /* Wait for cmdDone. If Exc, branch to SyncCleanUp: */ descriptorPtr->operation = SWAP( NOP_CMD | kWaitIfTrue | kBranchIfFalse ); descriptorPtr->address = 0; descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclSyncCleanUp ); descriptorPtr->result = 0; descriptorPtr++; /* Interphase condition or possible overrun. */ /* 29sep98 PhaseMismatch occurred even after */ /* CmdDone was set. */ /* Branch Always to assume we will bit bucket some data: */ descriptorPtr->operation = SWAP( NOP_CMD | kBranchAlways ); descriptorPtr->address = 0; descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclOverrun ); descriptorPtr->result = 0; descriptorPtr++; /* Fix up the DataOverrun code just in case: */ 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 ); } }/* end IF last of Synchronous transfer */ else { /* Async or incomplete Sync. Append Branches to finish this process: */ /* If this is a partial transfer, set 'incomplete' flag. */ if ( transferLength > 0 ) fFlagIncompleteDBDMA = true; /* set incomplete */ else fFlagIncompleteDBDMA = false; /* assume complete xfer */ if ( fFlagIncompleteDBDMA ) { /* Delete the ccl to clear cmdDone: */ --descriptorPtr; /* see HACK note above. */ } else if ( totalXferLen > 0 ) { /* If something moved AND (Radar 2298440) xfer completed, */ /* Wait & Branch if problem: */ /* Radar 2272931 - If entire output fits in FIFO, then */ /* the OUTPUT_LAST completes OK without a PhaseMismatch if */ /* the target disconnects right after the command phase. */ bcopy( &fCCL[ kcclBrProblem ], descriptorPtr, sizeof( DBDMADescriptor ) ); descriptorPtr++; } /* Assume all's well - Branch to get status: */ descriptorPtr->operation = SWAP( NOP_CMD | kBranchAlways ); descriptorPtr->address = 0; descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclGetStatus ); descriptorPtr->result = 0; /* If this is a partial transfer, set 'incomplete' flag and */ /* change the Branch from GetStatus to Good: */ if ( fFlagIncompleteDBDMA ) { /* change last Branch from Status to Good: */ descriptorPtr->cmdDep = SWAP( (UInt32)fCCLPhysAddr + kcclMESHintr ); ELG( descriptorPtr, transferLength, 'Part', "updateCP - built partial CCL." ); } descriptorPtr++; }/* end if/ELSE Async or partial xfer */ return; }/* end updateCP */ void meshSCSIController::clearCPResults() { register DBDMADescriptor *dp = (DBDMADescriptor*)&fCCL[ kcclStart ]; register int i; /* Don't clear the reserved areas or prototypes */ for ( i = (gDescriptorListSize - kcclStart) / sizeof ( DBDMADescriptor ); i; --i ) { dp->result = 0; dp++; } return; }/* end clearCPResults */ /* Set up the channel commands for MsgO phase. */ void meshSCSIController::setupMsgO() { UInt8 msgoSize; fMsgOutPtr--; /* treat the last or only byte special (drop ATN) */ msgoSize = fMsgOutPtr - &fCCL[ kcclMSGOdata ]; if( msgoSize == 0 ) { /* Identify byte only: */ *(UInt32*)&fCCL[ kcclMsgoBranch ] = SWAP( NOP_CMD | kBranchAlways ); } else /* multibyte message - set counts for all but last byte: */ { fCCL[ kcclMsgoMTC ] = msgoSize; fCCL[ kcclMsgoDTC ] = msgoSize; /* NOP the BRANCH: */ *(UInt32*)&fCCL[ kcclMsgoBranch ] = SWAP( NOP_CMD ); } fCCL[ kcclMSGOLast ] = *fMsgOutPtr; /* position last byte */ return; }/* end setupMsgO */ /* Start a Channel Program at the given offset */ /* with the specified stage label. */ void meshSCSIController::runDBDMA( UInt32 offset, UInt32 stageLabel ) { register UInt8 intReg; mach_timespec_t arbEndTime, curTime; DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr; fMsgInFlag = 0; /* clear message-in flags. */ *(UInt32*)&fCCL[ kcclStageLabel ] = stageLabel; /* set the stage */ /* Let MESH interrupt only for errors or exceptions, but not cmdDone */ setIntMask( kMeshIntrException | kMeshIntrError ); intReg = fMESHAddr->interrupt; switch ( intReg ) { case kMeshIntrCmdDone: if ( !fFlagReselecting ) // ??? Don't drop ACK fm MSG-IN or Sync data flows /* clear any pending command interrupts (but not reselect et al) */ fMESHAddr->interrupt = kMeshIntrCmdDone; SynchronizeIO(); /***** fall through *****/ case 0: /* This is a Go: */ /* Flush any CCL and related data to the CCL physical page */ /* that may still be sitting in cache: */ flush_dcache( (vm_offset_t)fCCL, fCCLSize, false ); // ELG( *(UInt32*)0xF3000020, *(UInt32*)0xF300002C, 'G C+', "runDBDMA." ); if ( offset == kcclStart ) { fFlagReselecting = false; setSeqReg( kMeshArbitrateCmd ); /* ARBITRATE */ /* wait 50 mikes or cmdDone, whichever comes first: */ 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 ); /* get regs without hosing */ 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; }/* end wait cmdDone or 50 mikes */ if ( g.shadow.mesh.interrupt == kMeshIntrCmdDone ) { /* No err, no exc: Arbitration won: */ fMESHAddr->interrupt = kMeshIntrCmdDone; SynchronizeIO(); setSeqReg( kMeshDisableReselect ); /* disable reselect */ offset = 0x150; // ??? fix this. Point to Select/Atn *(UInt32*)&fCCL[ kcclStageLabel ] = kcclStageSelA; /* set stage to Select */ }/* end IF won Arbitration */ else /* Arbitration not won - CAUTION - HACK AHEAD. */ { /* Sometimes, MESH does not return ArbLost as it says in */ /* the documentation. Instead, it waits for the winner to */ /* get off the bus (usually after the 250 ms timeout) and */ /* then MESH continues its arbitration. This wastes 250 ms */ /* of valuable bus time. Further, IOmega's Zip drive has a */ /* nasty bug whereby if its reselection is snubbed and it */ /* times out, it leaves the I/O signal asserted on the bus */ /* even as other activity on the bus unrelated to the Zip */ /* is ongoing. */ /* We don't need to hack if ArbLost is indicated correctly */ /* or Reselect is indicated. If either is true, don't bother*/ /* starting the DBDMA; rather, let the interrupt already */ /* latched handle the situation. */ if ( !(g.shadow.mesh.exception & (kMeshExcArbLost | kMeshExcResel)) ) { ELG( '****', '****', 'HACK', "runDBDMA - Arbitrate HACK." ); // ??? check FIFO count for presence of Target ID ??? setSeqReg( kMeshResetMESH ); /* hack it: whack it */ getHBARegsAndClear( true ); /* get regs/clear */ setSeqReg( kMeshEnableReselect ); /* Let reselect again */ getHBARegsAndClear( false ); /* get regs/preserve */ if ( g.shadow.mesh.interrupt == 0 ) PAUSE( 0, 0, 'Arb*', "runDBDMA - Arbitrate/Reselect problem." ); } if ( g.shadow.mesh.interrupt ) /* If Err or Exc set, */ break; /* break SWITCH - Don't start the DBDMA. */ }/* end ELSE lost Arbitration */ }/* end IF DBDMA to start at Arbitrate */ getHBARegsAndClear( false ); // ??? debug: see if ACK still set ELG( 0, offset<<16 | stageLabel, 'DMA+', "runDBDMA" ); /// dbdma_start( DBDMA_MESH_SCSI, (dbdma_command_t*)((UInt32)fCCLPhysAddr + offset) ); DBDMARegs->commandPtrLo = SWAP( (UInt32)fCCLPhysAddr + offset ); SynchronizeIO(); DBDMARegs->channelControl = SWAP( 0x80008000 ); // set RUN bit SynchronizeIO(); return; }/* end SWITCH on interrupt register */ ELG( 0, intReg, 'Pnd-', "runDBDMA - interrupt probably pending (reselect?)." ); getHBARegsAndClear( false ); /* display regs without clearing */ *(UInt32*)&fCCL[ kcclStageLabel ] = kcclStageIdle; /* set stage to none */ g.intLevel |= kLevelLatched; /* set latched-interrupt flag. */ setIntMask( kMeshIntrMask ); /* make sure MESH interrupts enabled */ return; }/* end runDBDMA */ 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; }/* end SWITCH on SCSI status */ 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; }/* end completeCommand */ void meshSCSIController::free() { DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr; if ( fMESHAddr ) { setSeqReg( kMeshResetMESH ); } if ( DBDMARegs ) { // dbdma_stop( DBDMA_MESH_SCSI ); DBDMARegs->channelControl = SWAP( 0x20002000 ); // set FLUSH bit SynchronizeIO(); DBDMARegs->channelControl = SWAP( 0x80000000 ); // clr RUN bit 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; }/* end free */ /* startBucket - Start the channel commands to run the bit bucket. */ void meshSCSIController::startBucket() { DBDMADescriptor *dp; /* current data descriptor */ UInt32 dbdmaOp; /* Opcode for DBDMA */ UInt32 meshSeq; /* Opcode for MESH request */ ELG( fCmd, fCmdData, 'Bkt-', "startBucket" ); /* Generate MESH "sequence" & DBDMA "operation" for Input or Output: */ 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; }/* end startBucket */ /* The Channel Program ran to completion without problems. */ void meshSCSIController::doInterruptStageGood() { UInt32 totalXferLen; /* Retrieve the total number of bytes transferred */ /* in the last data phase and reset it: */ totalXferLen = *(UInt32*)&fCCL[ kcclBatchSize ]; *(UInt32*)&fCCL[ kcclBatchSize ] = 0; ELG( fCmd, totalXferLen, 'Good', "doInterruptStageGood" ); /* We are completing a normal command. */ /* Update the transfer count and current data pointer. */ fCmdData->results.bytesTransferred += totalXferLen; if ( fReadAlignmentCount ) // Hack for Radar 1670626 { fCmdData->mdp->writeBytes( fReadAlignmentIndex, &fCCL[ kcclReadBuf8 ], fReadAlignmentCount ); fReadAlignmentCount = 0; } if ( fFlagIncompleteDBDMA == false ) { /* Yes, the IO is really complete: */ fCmdData->results.returnCode = kIOReturnSuccess; fCmdData->results.scsiStatus = fCCL[ kcclStatusData ]; fCmd->setResults( &fCmdData->results ); completeCommand(); } else { /* The CCL ended, but the caller expected more data. */ /* Restart the CCL. */ /* Don't regenerate arbitration or command stuff. */ updateCP( true ); runDBDMA( kcclDataXfer, kcclStageXfer ); return; }/* end ELSE need to continue Channel Program */ /* Since IO completed (otherwise, we would have exited in the */ /* "return" above), check whether a reselection attempt */ /* is piggy-backed on top of the good DBDMA completion. */ if ( g.shadow.mesh.exception & kMeshExcResel ) handleReselectionInterrupt(); else /* Nothing happening. Try to start another request. */ { setIntMask( kMeshIntrException | kMeshIntrError ); /* Re-enable ints for reselect */ enableCommands(); /* let superclass issue another cmd */ } return; }/* end doInterruptStageGood */ /* Process a normal data phase interrupt. IO is not complete. */ /* There are several reasons why we might get here: */ /* -- autosense completion (which could be a separate stage) */ /* -- DMA completion with more DMA to do */ /* -- Bus phase mismatch (short transfer or disconnect, MsgIn) */ /* Note that we know that we are not in autosense. */ void meshSCSIController::doInterruptStageXfer() { UInt32 count; /* DMA transfer count */ UInt8 phase; /* Current bus phase */ IOReturn rc; int goAround; count = fCmdData->results.bytesTransferred; updateCurrentIndex(); do { goAround = false; /* assume loop not repeated */ setSeqReg( kMeshFlushFIFO ); /* We've cleaned up the mess from the previous data transfer. */ /* Look at the current bus phase. The channel command waited */ /* for REQ to be set before interrupting the processor. */ phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; /* REQ is set, right? */ switch ( phase ) { case kBusPhaseSTS: fFlagIncompleteDBDMA = false; /* indicate no-more-data */ runDBDMA( kcclGetStatus, kcclStageStat ); break; case kBusPhaseMSGI: rc = DoMessageInPhase(); if ( rc == kIOReturnSuccess && fCmd ) goAround = true; /* msg ok & not disconnect */ break; case kBusPhaseDATO: case kBusPhaseDATI: if ( count != fCmdData->results.bytesTransferred ) { /* Data phase had already started: */ PAUSE( 0, phase, 'dat-', "doInterruptStageXfer - unexpected Data phase.\n" ); } else { /* try starting data phase again */ runDBDMA( kcclDataXfer, kcclStageXfer ); } break; default: PAUSE( 0, phase, 'Phs-', "doInterruptStageXfer - bogus phase.\n" ); break; }/* end SWITCH on phase */ } while ( goAround ); return; }/* end doInterruptStageXfer */ /* doInterruptStageArb - Process an anomaly during arbitration. */ 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 ) { /* 18sep98 - Sometimes MESH gets real confused when its */ /* arbitration loses to a target's reselect arbitration. */ /* The registers show Exc:ArbLost, Resel and Err:UnExpDisc. */ /* The FIFO count is 1 (should be SCSI ID bits) while the */ /* BusStatus0,1 registers show IO and Sel both of which are */ /* set by the reselecting Target. */ /* The SCSI bus analyzer shows the following events occcurring */ /* within a few microseconds of BSY being set by the target: */ /* bus free for at least hundreds of microseconds */ /* Target raises BSY along with its ID bit */ /* Target raises SEL */ /* Target raises IO to indicate reselection */ /* Target adds MESH's ID bit */ /* Target drops BSY */ /* MESH raises BSY to accept reselection */ /* **** MESH drops BSY **** here is where MESH is confused */ /* Target stays on bus for 250 milliseconds. */ /* To solve this problem, whack MESH with a RstMESH. */ ELG( ' Rst', 'MESH', 'UEP-', "doInterruptStageArb - Resel/Unexpected Disconnect.\n" ); setSeqReg( kMeshResetMESH ); /* completes quickly */ getHBARegsAndClear( true ); /* clear cmdDone */ setSeqReg( kMeshEnableReselect ); setIntMask( kMeshIntrMask ); /* Enable Interrupts */ return; /* now wait for another reselect interrupt */ } handleReselectionInterrupt(); } else { /* 22sep97 - lost arbitration without reselection. */ /* Probably lost the reselect condition processing an */ /* error or something. */ ELG( 0, 0, 'ARB-', "doInterruptStageArb - Lost arbitration without reselect.\n" ); } return; }/* end doInterruptStageArb */ /* Process an anomaly during target selection. */ void meshSCSIController::doInterruptStageSelA() { ELG( fCmd, 0, 'Sel-', "doInterruptStageSelA - Selection stage.\n" ); if ( fCmd ) { fCmdData->results.adapterStatus = kSCSIAdapterStatusSelectionTimeout; completeCommand(); } setSeqReg( kMeshEnableReselect ); setSeqReg( kMeshBusFreeCmd ); /* clear ATN signal MESH left on */ getHBARegsAndClear( false ); /* check MESH registers */ if ( g.shadow.mesh.exception & kMeshExcResel ) { handleReselectionInterrupt(); } else { setIntMask( kMeshIntrMask ); /* Enable Interrupts */ /* enableCommands on the Bus-Free interrupt. */ /* If we do it here, we'll get a command, start the DBDMA, */ /* and get the Bus-Free interrupt seeming to be spurious. */ } return; }/* end doInterruptStageSelA */ /* Process an anomaly during Message-Out phase. */ /* Target probably doing Message Reject (0x07). */ void meshSCSIController::doInterruptStageMsgO() { UInt8 phase; IOReturn rc; phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; /* phase me */ ELG( fCmd, phase, 'Mgo-', "doInterruptStageMsgO - error during msg-out phase.\n" ); switch ( phase ) /* Probably negotiating Sync */ { case kBusPhaseMSGI: rc = DoMessageInPhase(); if ( rc != kIOReturnSuccess ) { PAUSE( 0, rc, ' MI-', "doInterruptStageMsgO - MsgIn during MsgOut phase.\n" ); // ??? need to get to bus-free from here // ??? need to blow off the IO } 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; }/* end doInterruptStageMsgO */ /* doInterruptStageCmdO - Process an anomaly during command stage. */ void meshSCSIController::doInterruptStageCmdO() { SCSICDBInfo scsiCDB; UInt8 phase; IOReturn rc; /* See if this is part of the normal AbortTag/BusDeviceReset process: */ fCmd->getCDB( &scsiCDB ); if ( scsiCDB.cdbAbortMsg ) { setSeqReg( kMeshFlushFIFO ); /* flush the FIFO */ getHBARegsAndClear( true ); /* clear cmdDone */ ELG( fCmd, fCmd->getOriginalCmd(), 'Abo-', "doInterruptStageCmdO - Aborting." ); completeCommand(); /* complete the cmd with Abort msg */ setIntMask( kMeshIntrMask ); /* Re-enable interrupts */ enableCommands(); /* let superclass issue another command */ return; } /* Not aborting - something bad happened: */ phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; /* phase me */ ELG( fCmd, phase, 'CMD?', "doInterruptStageCmdO - anomaly during Cmd phase.\n" ); if ( phase == kBusPhaseMSGI ) { /* We are probably negotiating SDTR or */ /* getting rejected on a nonzero LUN. */ rc = DoMessageInPhase(); if ( rc != kIOReturnSuccess ) { PAUSE( 0, rc, ' mi-', "doInterruptStageCmdO - MsgIn during Cmd phase.\n" ); } else { /* Message processed - where do we go from here? */ if ( !fCmd ) /* if Rejected, */ return; /* 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 ) /* Probably Check Condition */ { /* Perhaps block # invalid */ fFlagIncompleteDBDMA = false; /* indicate no-more-data */ runDBDMA( kcclGetStatus, kcclStageStat ); } else { PAUSE( 0, phase, 'Phs?', "doInterruptStageCmdO - error during Command phase.\n" ); } return; }/* end doInterruptStageCmdO */ /* We are in MSGI phase. Read the bytes. Return true if an entire */ /* message was read (we may still be in MSGI phase). Note that this */ /* is done by programmed IO, which will fail (logging the error) if */ /* the target sets MSGI but does not send us a message quickly enough. */ /* This method is called from the normal data transfer interrupt when */ /* the target enters message in phase, and from the reselection */ /* interrupt handler when we read a valid reselection target ID. */ /* Note that MESH interrupts are disabled on exit. */ enum /// ??? header file { kSCSIMsgOneByteMax = 0x1Fu, kSCSIMsgTwoByteMin = 0x20u, kSCSIMsgTwoByteMax = 0x2Fu }; IOReturn meshSCSIController::DoMessageInPhase() { register UInt8 messageByte; UInt32 index = 0; IOReturn ioReturn = kIOReturnSuccess; /* We do not necessarily have a valid command in this method. */ /* While we're processing Message-In bytes, we don't want any */ /* MESH hardware interrupts. */ setIntMask( 0 ); /* no MESH interrupt latching */ setSeqReg( kMeshFlushFIFO ); /* Flush the FIFO */ fMsgInCount = 0; fMsgInState = kMsgInInit; while ( fMsgInState != kMsgInReady /* Disconnect makes fCmd */ && ioReturn == kIOReturnSuccess ) /* go away */ { fMESHAddr->transferCount1 = 0; fMESHAddr->transferCount0 = 1; /* get single byte */ setSeqReg( kMeshMessageInCmd ); /* issue MsgIn */ ioReturn = waitForMesh( true ); /* wait for cmdDone */ if ( ioReturn != kIOReturnSuccess ) { PAUSE( *(UInt32*)&fCurrentTargetLun, ioReturn, 'Mgi-', "DoMessageInPhase - Target hung: message in timeout.\n" ); break; /* Bus reset here? */ } if ( (g.shadow.mesh.exception & kMeshExcPhaseMM) || (g.shadow.mesh.busStatus0 & kMeshPhaseMask) != kBusPhaseMSGI ) { break; /* exit loop if no longer in Msg-In phase */ } if ( g.shadow.mesh.FIFOCount == 0 ) { PAUSE( *(UInt16*)&fCurrentTargetLun, 0, 'mgi-', "DoMessageInPhase - no message byte.\n" ); break; } fMsgInBuffer[ index++ ] = messageByte = fMESHAddr->xFIFO; /***** get msg byte *****/ switch ( fMsgInState ) { case kMsgInInit: /* This is the first message byte. */ if ( (messageByte == kSCSIMsgCmdComplete) || (messageByte >= (UInt8)kSCSIMsgIdentify) ) { /* This is 1-byte cmdComplete or Identify message. */ fMsgInState = kMsgInReady; } else if ( messageByte == kSCSIMsgExtended ) { /* This is an extended message. The next byte has the count. */ fMsgInState = kMsgInCounting; } else if ( messageByte <= kSCSIMsgOneByteMax ) { /* These are other 1-byte messages. */ fMsgInState = kMsgInReady; } else if ( messageByte >= kSCSIMsgTwoByteMin && messageByte <= kSCSIMsgTwoByteMax ) { /* This is a two-byte message. */ /* Set the count and read the next byte. */ fMsgInState = kMsgInReading; /* Need one more */ fMsgInCount = 1; } else { /* This is an unknown message. */ fMsgInState = kMsgInReady; } break; case kMsgInCounting: /* Count byte of multi-byte message: */ fMsgInCount = messageByte; fMsgInState = kMsgInReading; break; case kMsgInReading: /* Body of multi-byte message: */ if ( --fMsgInCount <= 0 ) fMsgInState = kMsgInReady; break; default: PAUSE( 0, 0, 'Msg-', "DoMessageInPhase - Bogus MSGI state!\n" ); fMsgInState = kMsgInReady; break; }/* end SWITCH on MSGI state */ if ( fMsgInState == kMsgInReady ) { ProcessMSGI(); fMsgInState = kMsgInInit; index = 0; if ( fMsgInBuffer[0] == kSCSIMsgDisconnect || fMsgInBuffer[0] == kSCSIMsgCmdComplete ) /* Radar 2253653 */ ioReturn = kIOReturnIOError; /* break out of WHILE loop */ if ( fMsgInFlag & kFlagMsgIn_Reject ) { abortActiveCommand(); break; } if ( fFlagReselecting ) break; /* Take Identify only - leave +ACK */ }/* end IF have a complete message-in to process */ }/* end WHILE there are more message bytes */ /***** If the target switches out of MSGI phase without *****/ /***** sending a complete message, we should do some *****/ /***** sort of error recovery. *****/ if ( fMsgInState != kMsgInInit ) { PAUSE( *(UInt32*)&fCurrentTargetLun, fMsgInState, 'MGI-', "DoMessageInPhase - incomplete message.\n" ); if ( ioReturn == kIOReturnSuccess ) ioReturn = kIOReturnIOError; /* General IO error */ } return ioReturn; }/* end DoMessageInPhase */ /* ProcessMSGI - DoMessageInPhase has read a complete message. */ /* Process it (this will probably change our internal state). */ void meshSCSIController::ProcessMSGI() { /* Note that, during reselection, we may not have */ /* a current target or LUN, nor possibly a valid command */ UInt8 sdtr; UInt8 period; ELG( fCmd, *(UInt32*)fMsgInBuffer, 'results.bytesTransferred = fCmdData->savedDataPosition; break; case kSCSIMsgSaveDataPointers: if ( fCmd ) fCmdData->savedDataPosition = fCmdData->results.bytesTransferred; break; case kSCSIMsgDisconnect: /* Move this request to the disconnect queue, enable reselection, */ /* re-enable MESH interrupts, and wait (here) for bus free, but */ /* don't eat the interrupt. */ fMsgInFlag |= kFlagMsgIn_Disconnect; disconnect(); /* requeue active */ setSeqReg( kMeshEnableReselect ); /* enable reselect */ setIntMask( kMeshIntrMask ); /* Enable Ints */ setSeqReg( kMeshBusFreeCmd ); /* issue BusFree */ /* wait for Bus Free command to complete: */ waitForMesh( false ); /* don't clear possible reselect */ /* Interrupt for bus-free now latched. Prevent a double interrupt, */ /* 1 from bus-free + 1 from reselect from occurring. */ /* This fixes the following BADNESS: */ /* Issue bus-free for disconnect. */ /* Interrupt occurs in microseconds - even before exiting */ /* "interruptOccurred" routine. */ /* Mach queues message to driverKit. */ /* Exit "interruptOccurred" routine. */ /* DriverKit dequeues and starts handling 1st Mach message. */ /* Interrupt occurs for reselect while driverKit running. */ /* Mach queues 2nd message to driverKit. */ /* DriverKit invokes MESH driver for 1st msg. */ /* MESH driver sees cmdDone fm bus-free AND reselect exception.*/ /* MESH driver handles reselect by setting up and running */ /* DBDMA. MESH driver exits. */ /* DriverKit invokes MESH driver with 2nd Mach message. */ /* MESH driver handles this as a DBDMA completion and royally */ /* messes up. */ g.intLevel |= kLevelLatched; /* set latched-interrupt flag */ setIntMask( 0 ); /* prevent multiple MESH ints */ 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: /* Have multi-byte message, presumably Synchronous Negotiation. */ /* Radar 2425000 - Some drives will do Target Initiated Negotiation */ /* if not disabled by jumper. The negotiation can occur before */ /* the Command phase or after. Both Wide and Sync can occur in the */ /* same transaction. I tried to be nice and respond with a */ /* corresponding negotiation but ended up issuing a Message-Reject. */ /* The biggest problem occurs if the drive negotiates Synchronous */ /* just before Data phase. As soon as the target sees the Ack from */ /* the last byte of the message, it starts spewing Reqs for data */ /* for which we are not yet ready. */ switch ( fMsgInBuffer[ 2 ] ) /* switch on the msg code byte */ { case kSCSIMsgWideDataXferReq: /* handle Wide negotiation: */ ELG( *(UInt32*)&fMsgInBuffer[0], 0, 'TINW', "ProcessMSGI - TIN WDTR" ); issueReject(); break; case kSCSIMsgSyncXferReq: /* handle sync negotiation: */ /* Get period in nanoseconds */ period = fMsgInBuffer[ 3 ] * 4; /* SCSI uses 4ns granularity */ /* determine target responding or initiating? */ if ( !fCmdData->negotiatingSDTR ) { /* Target Initiating Negotiation - reject it: */ ELG( *(UInt32*)&fMsgInBuffer[0], *(UInt32*)&fMsgInBuffer[4], 'TINS', "ProcessMSGI - TIN SDTR" ); issueReject(); }/* end IF target is initiating negotiation */ else { if ( fMsgInBuffer[ 4 ] == 0 ) /* check offset */ { sdtr = kSyncParmsAsync; /* Offset == 0 implies async */ } else /* synchronous: */ { if ( period == 100 ) /* special-case 100=FAST */ { sdtr = kSyncParmsFast & 0x0F; } else /* Older CD-ROMs get here. */ { /* The MESH manual says: */ /* period = 4 * clk + 2 * clk * P */ /* where: */ /* period is the target nanoseconds */ /* clk is the MESH clock rate which is */ /* 20 nanoseconds for a 50 MHz clock */ /* P is the 1-nibble period code we stuff */ /* in the syncParms register */ /* So: */ /* period = 4 * 20 + 2 * 20 * P */ /* period = 80 + 40 * P */ /* P = (period - 80) / 40 */ /* Since P must round up for safety: */ /* P = ((period - 80) + 39) / 40 */ /* P = (period - 41) / 40 */ /* A value of P == 3 results in 5 MB/s */ sdtr = (UInt8)((period - 41) / 40); ELG( *(UInt32*)&fMsgInBuffer[0], fMsgInBuffer[4]<<24 | sdtr, 'SDTR', "ProcessMSGI - SDTR" ); } }/* end ELSE have offset ergo Synchronous */ /* OR in the offset. */ sdtr |= (fMsgInBuffer[ 4 ] << 4); fMESHAddr->syncParms = sdtr; SynchronizeIO(); fSyncParms[ fCurrentTargetLun.target ] = sdtr; fCmdData->negotiatingSDTRComplete = true; }/* end ELSE Target is responding to negotiation */ break; default: PAUSE( *(UInt16*)&fCurrentTargetLun, fMsgInBuffer[0], 'PMi-', "ProcessMSGI - unsupported extended message.\n" ); abortActiveCommand(); break; }/* end SWITCH on extended message code */ break; default: /* Better be Identify with LUN: */ if ( fMsgInBuffer[0] >= kSCSIMsgIdentify ) { fCurrentTargetLun.lun = fMsgInBuffer[0] & 0x07; } else { PAUSE( *(UInt16*)&fCurrentTargetLun, fMsgInBuffer[0], 'mi -', "ProcessMSGI - unsupported message: rejected.\n" ); abortActiveCommand(); } }/* end SWITCH on message selection */ exit: return; }/* end ProcessMSGI */ void meshSCSIController::issueReject() { ELG( 0, 0, 'RjM-', "issueReject" ); fMESHAddr->busStatus0 = kMeshAtn; /***** Raise Atn *****/ SynchronizeIO(); IODelay( 30 ); setSeqReg( kMeshBusFreeCmd ); /* clr ACK & provoke PMM */ waitForMesh( true ); /* wait til phase change & PMM */ fMESHAddr->busStatus0 = 0; /***** Clear Atn *****/ SynchronizeIO(); IOSleep( 1 ); /* yield time to other processes*/ fMESHAddr->transferCount1 = 0; /* TC hi */ fMESHAddr->transferCount0 = 1; /* TC lo */ SynchronizeIO(); setSeqReg( kMeshMessageOutCmd ); fMESHAddr->xFIFO = kSCSIMsgRejectMsg; SynchronizeIO(); waitForMesh( true ); /* wait for TC = FIFOcnt = 0 (cmdDone) */ setSeqReg( kMeshBusFreeCmd ); /* clr ACK & provoke PMM */ waitForMesh( true ); /* wait for phase change */ return; }/* end issueReject */ /* Process a reselection interrupt. */ void meshSCSIController::handleReselectionInterrupt() { IOReturn ioReturn; fFlagReselecting = true; disableCommands(); /* tell superclass we're busy */ fMESHAddr->interrupt = kMeshIntrMask; /* clr Exc, Err regs to prevent SeqErr*/ /* Sometimes MESH gives a bogus Disconnected error during Reselection. */ /* 31mar98 - Issuing an Abort message, causes "unexpected disconnect". */ /* When Err:UnexpDisc and Exc:Resel are simultaneously set, the */ /* busStatus0,1 registers may not be current. */ if ( g.shadow.mesh.error & kMeshErrDisconnected ) { setSeqReg( kMeshBusFreeCmd ); waitForMesh( true ); // now maybe busStatus0,1 are live PAUSE( 0, 0, 'Dsc-', "handleReselectionInterrupt: Caught disconnected glitch\n" ); }/* End IF bus disconnect error */ /* Read the target ID (which should be our initiator ID OR'd with the */ /* Target and the Identify byte with the reselecting LUN. Store this */ /* in fCurrentTargetLun. Note that, during reselection, we will */ /* have a NULL gCurrentCommand and a valid fCurrentTargetLun. */ /* If we get a valid reselection target, call the message in phase */ /* directly to read the LUN byte. */ /* @return true if successful. */ if ( fMESHAddr->FIFOCount == 0 ) { PAUSE( 0, 0, 'HRI-', "handleReselectionInterrupt - Empty FIFO in reselection.\n" ); return; } else /* get the Target ID bit from the bus out of the FIFO */ { /* then, get the msg-in Identify byte for the LUN. */ if ( getReselectionTargetID() ) { if ( DoMessageInPhase() != kIOReturnSuccess ) /* get Identify */ { PAUSE( 0, 0, 'Id -', "handleReselectionInterrupt - Expected Identify byte after reselection.\n" ); } } else return; } /* Try to find an untagged command for this Target/LUN: */ fTag = kInvalidTag; ioReturn = reselectNexus(); if ( ioReturn != kIOReturnSuccess ) { /* No untagged command, try to get a Tag. Hope that */ /* you're still in Message-In phase at this point. */ if ( DoMessageInPhase() != kIOReturnSuccess ) /* get Tag msg */ { PAUSE( 0, 0, 'tag-', "handleReselectionInterrupt - Expected tag message.\n" ); /// need to bus device reset since it seems confused. } ioReturn = reselectNexus(); } if ( ioReturn == kIOReturnSuccess ) { ELG( fCmd, fTag<<16 | fCurrentTargetLun.lun<<8 | fCurrentTargetLun.target, 'Resl', "handleReselectionInterrupt" ); /* If reselectNexus succeeded, fCmd is set to the command. */ /* Clear out the channel command Results and build the channel */ /* command to continue operation. */ clearCPResults(); updateCP( true ); /* Bypass arbitrate/select/command sequence */ runDBDMA( kcclReselect, kcclStageInit ); } else { /* There is no associated command. */ /* Reject the reselection attempt. */ PAUSE( *(UInt16*)&fCurrentTargetLun, fTag, 'Rsl-', "handleReselectionInterrupt - No command for reselection attempt.\n" ); abortActiveCommand(); } return; }/* end handleReselectionInterrupt */ /* Validate the target's reselection byte (put on the bus before */ /* reselecting us). Erase the initiator ID and convert the other */ /* bit into an index. The algorithm should be faster than a */ /* sequential search, but it probably doesn't matter much. */ /* Return true if successful (fCurrentTarget is now valid). */ bool meshSCSIController::getReselectionTargetID() { register UInt8 targetID = 0; register UInt8 bitValue = 0; /* Suppress warning */ register UInt8 targetBits; bool success = false; targetBits = fMESHAddr->xFIFO; /***** Read the FIFO *****/ targetBits &= ~fInitiatorIDMask; /* Remove our bit */ if ( targetBits ) { /* Is there another bit? */ bitValue = targetBits; if ( bitValue > 0x0F ) { targetID += 4; bitValue >>= 4; } if ( bitValue > 0x03 ) { targetID += 2; bitValue >>= 2; } if ( bitValue > 0x01 ) { targetID += 1; } targetBits &= ~(1 << targetID); /* Remove the target mask */ if ( targetBits == 0 ) { /* Was exactly one set? */ success = true; /* Yes: success! */ fCurrentTargetLun.target = targetID; /* Save the current target */ } } if ( !success ) PAUSE( targetID, targetBits, 'rsl-', "getReselectionTargetID - Expected Identify byte after reselection.\n" ); return success; }/* end getReselectionTargetID */ IOReturn meshSCSIController::resetBus() { UInt8 defaultSelectionTimeout = 25; // mlj ??? fix this value DBDMAChannelRegisters *DBDMARegs = (DBDMAChannelRegisters*)dbdmaAddr; ELG( 0, 0, 'RstB', "resetBus" ); /* Stop the DBDMA: */ DBDMARegs->channelControl = SWAP( 0x20002000 ); // set FLUSH bit SynchronizeIO(); DBDMARegs->channelControl = SWAP( 0x80000000 ); // clr RUN bit SynchronizeIO(); /* Reset interrupts, the MESH Hardware Bus Adapter, and the DMA engine. */ setIntMask( 0 ); setSeqReg( kMeshResetMESH ); /* completes quickly */ getHBARegsAndClear( true ); /* clear cmdDone */ /// dbdma_reset( DBDMA_MESH_SCSI ); /* Init state variables: */ fFlagIncompleteDBDMA = false; /* Smash all active command state (just in case): */ fCmd = NULL; fCmdData = NULL; fCurrentTargetLun.target = kInvalidTarget; fCurrentTargetLun.lun = kInvalidLUN; fMsgInState = kMsgInInit; fMsgOutPtr = &fCCL[ kcclMSGOdata ]; fMESHAddr->busStatus1 = kMeshRst; /***** ASSERT RESET SIGNAL *****/ SynchronizeIO(); IODelay( 25 ); /* leave asserted for 25 mikes */ fMESHAddr->busStatus1 = 0; /***** CLEAR RESET SIGNAL *****/ SynchronizeIO(); /* Delay for 250 msec after resetting the bus. */ /* This serves two purposes: it gives the MESH time to */ /* stabilize (about 10 msec is sufficient) and gives */ /* some devices time to re-initialize themselves. */ IOSleep( APPLE_SCSI_RESET_DELAY ); /* Give Targets time to clean up */ setSeqReg( kMeshResetMESH ); /* clear Err condition */ getHBARegsAndClear( true ); /* check regs */ fMESHAddr->selectionTimeOut = defaultSelectionTimeout; SynchronizeIO(); enableCommands(); /* let superclass issue another command */ return kIOReturnSuccess; }/* end resetBus */ /* Wait for an immediate (non-interrupting) command to complete. */ /* Note that it spins while waiting. It is timed to prevent a buggy */ /* chip or target from hanging the system. */ 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; // mlj - make it 250 milliseconds for SONY CD-ROM; 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 ) { /* It took too long! We're dead. */ PAUSE( 0, 0, 'WFM-', "waitForMesh - MESH chip does not respond to command.\n" ); ioReturn = kIOReturnInternalError; break; } }/* end FOR */ ELG( time.tv_sec - startTime.tv_sec, time.tv_nsec - startTime.tv_nsec, ' WFM', "waitForMesh" ); return ioReturn; }/* end waitForMesh */ /* Send a command to the MESH chip. This may cause an interrupt. */ 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; /***** DO IT *****/ SynchronizeIO(); IODelay( 1 ); /* G3 is too fast */ return; }/* end setSeqReg */ /* Retrieve the MESH volatile register contents, */ /* storing them in the global register shadow. */ /* @param clearInts YES to clear MESH interrupts. */ 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; // debugging g.shadow.mesh.interruptMask = mesh->interruptMask; // debugging g.shadow.mesh.syncParms = mesh->syncParms; // debugging g.shadow.mesh.destinationID = mesh->destinationID; // debugging ELG( g.shadow.longWord[ 0 ], g.shadow.longWord[ 1 ], clearInts ? 'Regs' : 'regs', "getHBARegsAndClear." ); if ( g.shadow.mesh.error ) // this occurs when DBDMA -> Seq while reselect { // OR Exc:reselect occurs just before busFree->Seq reg. ELG( g.shadow.mesh.interruptMask, g.shadow.mesh.sequence, 'Err-', "getHBARegsAndClear - MESH error detected" ); mesh->interrupt = kMeshIntrError; SynchronizeIO(); } /* It is possible to have the Reselected bit set in the Exception */ /* register without an Exception bit in the interrupt register. */ /* This may be caused by timing window where we clear the interrupt */ /* register with the interrupt register instead of 0x07. */ /* Handle this by faking an exception. */ /* 04may98 - it is also possible to have PhaseMisMatch set in the */ /* Exception register without Exception indicated in the Interrupt */ /* register. This happened when a Synchronous output finished and */ /* the target went to Message-In phase with Save-Data-Pointer. */ if ( g.shadow.mesh.exception ) g.shadow.mesh.interrupt |= kMeshIntrException; if ( clearInts && g.shadow.mesh.interrupt ) { mesh->interrupt = g.shadow.mesh.interrupt; SynchronizeIO(); } return; }/* end getHBARegsAndClear */ void meshSCSIController::setIntMask( UInt8 mask ) { ELG( (fMESHAddr->interrupt<<16) | fMESHAddr->interruptMask, mask, 'Mask', "setIntMask" ); fMESHAddr->interruptMask = mask; /* enable whatever */ SynchronizeIO(); return; }/* end setIntMask */ void meshSCSIController::abortActiveCommand() { IOReturn ioReturn; ELG( fCmd, 0, '-AB*', "abortActiveCommand" ); if ( fCmd ) { fCmdData->results.adapterStatus = kSCSIAdapterStatusProtocolError; // ??? use kIOAborted? completeCommand(); } getHBARegsAndClear( true ); /* clear possible cmdDone et al */ setIntMask( 0 ); /* Disable MESH interrupts */ fMsgInFlag = 0; /* clear kFlagMsgIn_Reject et al */ fMESHAddr->busStatus0 = kMeshAtn; /***** Raise ATN signal *****/ SynchronizeIO(); setSeqReg( kMeshBusFreeCmd ); /* clear ACK */ waitForMesh( true ); /* wait for PhaseMM */ if ( (g.shadow.mesh.busStatus0 & (kMeshPhaseMask | kMeshReq)) == (kBusPhaseMSGO | kMeshReq) ) { /* this is what we want: */ setSeqReg( kMeshFlushFIFO ); /* Flush the FIFO */ fMESHAddr->transferCount0 = 1; /* set TC low = 1 */ fMESHAddr->transferCount1 = 0; fMESHAddr->busStatus0 = 0; /***** clear ATN signal *****/ SynchronizeIO(); /* Issue the Message Out sending the Abort on its way. */ /* Note that this will cause an Unexpected-Disconnect. */ setSeqReg( kMeshMessageOutCmd ); /* drop ATN signal */ fMESHAddr->xFIFO = kSCSIMsgAbort; /* put out the Abort byte */ ioReturn = waitForMesh( true ); /* wait for cmdDone */ if ( ioReturn == kIOReturnSuccess ) { setSeqReg( kMeshEnableReselect ); /* bus about to go free */ setIntMask( kMeshIntrMask ); /* Enable interrupts */ setSeqReg( kMeshBusFreeCmd ); /* Clr ACK & go Bus-Free */ g.intLevel |= kLevelLatched; /* set latched-int flag */ return; } }/* end IF MSGO phase and REQ is set */ /***** USE THE HAMMER - NUKE THE BUS: *****/ ELG( 0, 0, '-AB-', "abortActiveCommand - target refused to enter MSGO phase" ); resetBus(); super::resetOccurred(); return; }/* end abortActiveCommand */ /* IO associated with fCmd has disconnected. */ /* Place it on the disconnected command queue and */ /* enable another transaction. */ void meshSCSIController::disconnect() { fCmd = NULL; fCmdData = NULL; fCurrentTargetLun.target = kInvalidTarget; fCurrentTargetLun.lun = kInvalidLUN; /* Since there is no active command, the caller */ /* must configure the bus interface to wait for */ /* bus free, then allow reselection. */ return; }/* end disconnect */ /* The specified target, LUN, and queueTag is trying to reselect. */ /* If we have a xxxCommandBuffer for this TLQ nexus on disconnectQ, */ /* remove it, make it the current fCmd, and return YES. */ /* Else return NO. A value of zero for queueTag indicates a */ /* nontagged command (zero is never used as the queue tag value for */ /* a tagged command). */ 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; }/* end reselectNexus */ void meshSCSIController::updateCurrentIndex() { UInt32 count; /* DMA transfer count */ UInt32 length = g.shadow.mesh.FIFOCount; UInt8 buffer[ 16 ]; UInt32 i; /* Calculate the number of bytes xferred by this channel command. */ /* We don't trust the DBDMA residual count. */ count = *(UInt32*)&fCCL[ kcclBatchSize ]; /* Our transfer count */ if ( count == 0 ) /* If batch is empty, */ return; /* look at nothing else.*/ count -= g.shadow.mesh.transferCount1 << 8; /* MESH residual high */ count -= g.shadow.mesh.transferCount0; /* MESH residual low */ fCmdData->results.bytesTransferred += count; /* Increment data index */ *(UInt32*)&fCCL[ kcclBatchSize ] = 0; /* Clear DBDMA count */ if ( fReadAlignmentCount ) // Hack for Radar 1670626 { fCmdData->mdp->writeBytes( fReadAlignmentIndex, &fCCL[ kcclReadBuf8 ], fReadAlignmentCount ); fReadAlignmentCount = 0; } /* Check the FIFO, if empty, increment the current data pointer. */ /* If there is stuff in it, we have more work to do. */ if ( g.shadow.mesh.FIFOCount ) /* If data in FIFO: */ { /* We didn't process these bytes in the FIFO - adjust index */ fCmdData->results.bytesTransferred -= g.shadow.mesh.FIFOCount; if ( fCmdData->isWrite ) /* If Writing: */ { setSeqReg( kMeshFlushFIFO ); } else /* Must be Reading: */ { /* On a Read with data left in the FIFO, we must copy */ /* the FIFO directly into the user's data buffer: */ 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; /* FYI - emptying the FIFO causes cmdDone to get set. */ for ( i = 0; i < count; i++ ) buffer[ i ] = fMESHAddr->xFIFO; fCmdData->mdp->writeBytes( fCmdData->results.bytesTransferred, buffer, count ); fCmdData->results.bytesTransferred += count; }/* end if/ELSE must be Reading */ }/* end IF FIFO was not empty */ ELG( 0, fCmdData->results.bytesTransferred, 'UpIx', "updateCurrentIndex" ); return; }/* end updateCurrentIndex */