#define kATAXferDMADesc 512
#define kATAMaxDMADesc kATAXferDMADesc
#define kMaxATAXfer 512 * 2048
#include "AppleK2SATA.h"
#include <IOKit/ata/ATADeviceNub.h>
#include <IOKit/assert.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOTypes.h>
#include <IOKit/ata/IOATATypes.h>
#include <IOKit/ata/IOATAController.h>
#include <IOKit/ata/IOATACommand.h>
#include <IOKit/ata/IOATADevice.h>
#include <IOKit/ata/IOATABusInfo.h>
#include <IOKit/ata/IOATADevConfig.h>
#include <IOKit/pci/IOPCIDevice.h>
#include<IOKit/storage/IOStorageProtocolCharacteristics.h>
#include <libkern/OSByteOrder.h>
#include <libkern/OSAtomic.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#ifdef DLOG
#undef DLOG
#endif
#ifdef ATA_DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define ATARecordEventMACRO(type,param,bus,data) (void) (type); (void) (param); (void) (bus); (void) (data)
#define kCompatibleString "k2sata-1"
#define kModelPropertyKey "name"
#define kModel4String "k2sata-1"
#define kATASupportedPIOModes 0x001F // modes 4, 3, 2, 1, and 0
#define kATASupportedMultiDMAModes 0x0007 // modes 2, 1, and 0
#define kATASupportedUltraDMAModes 0x003F // modes 5,4,3,2, 1, and 0
#define kPIOCycleEntries 12
#define kMultiDMACycleEntries 9
#define kUltraDMACycleEntries 5
#define kATAMaxPIOMode 4
#define kATAMaxMultiDMAMode 2
#define kATAMaxUltraDMAMode 5
#define kK2SIM_REMOVE_MASK 0x90000
#define kK2SIM_INSERT_MASK 0x00040000
#define kK2SIM_ATAPI_MASK 0x20000000
#pragma mark -IOService Overrides -
#define super IOPCIATA
OSDefineMetaClassAndStructors ( AppleK2SATA, IOPCIATA )
bool
AppleK2SATA::init(OSDictionary* properties)
{
DLOG("AppleK2SATA init start\n");
for( int i = 0; i < 5; i++)
{
ioBaseAddrMap[ i ] = 0;
}
_devIntSrc = 0;
busChildNumber = 0;
isBusOnline = true;
isEmptyBay = false;
isCriticalSection = false;
isSleeping = false;
pmRootDomain = 0;
systemIsSleeping = false;
cleanupSystemSleep = false;
isHotSwap = false;
if (super::init(properties) == false)
{
DLOG("AppleK2SATA: super::init() failed\n");
return false;
}
DLOG("AppleK2SATA init done\n");
return true;
}
IOService*
AppleK2SATA::probe(IOService* provider, SInt32* score)
{
DLOG("AppleK2SATA starting probe\n");
return this;
}
bool
AppleK2SATA::start(IOService *provider)
{
static const IOPMPowerState powerStatesK2 [ 2 ] = {
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, IOPMPowerOn, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
DLOG("AppleK2SATA::start() begin\n");
if( ! provider->open(this) )
{
DLOG("AppleK2SATA provider did not open\n");
return false;
}
ATADeviceNub* newNub=0L;
OSData* removableType = OSDynamicCast( OSData, provider->getProperty( "removable" ) );
if( removableType != 0)
{
isHotSwap = true;
}
#ifdef kIOPropertyPhysicalInterconnectTypeSerialATA
setProperty ( kIOPropertyPhysicalInterconnectTypeKey, kIOPropertyPhysicalInterconnectTypeSerialATA);
#endif
if (!super::start( provider))
{
DLOG("AppleK2SATA: super::start() failed\n");
goto failurePath;
}
if( ! createDeviceInterrupt() )
{
DLOG("AppleK2SATA: createDeviceInterrupts failed\n");
goto failurePath;
}
if( isHotSwap )
{
systemIsSleeping = false;
pmRootDomain = getPMRootDomain();
if (pmRootDomain != 0) {
pmRootDomain->registerInterestedDriver(this);
}
PMinit();
registerPowerDriver(this,(IOPMPowerState *)powerStatesK2,2);
joinPMtree(this);
}
DLOG("AppleK2SATA::start() done\n");
if( !isEmptyBay )
{
for( UInt32 i = 0; i < 2; i++)
{
if( _devInfo[i].type != kUnknownATADeviceType )
{
DLOG("AppleK2SATA creating nub\n");
newNub = ATADeviceNub::ataDeviceNub( (IOATAController*)this, (ataUnitID) i, _devInfo[i].type );
if(kATAPIDeviceType == _devInfo[i].type)
{
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_ATAPI_MASK);
}
if( newNub )
{
DLOG("AppleK2SATA attach nub\n");
newNub->attach(this);
_nub[i] = (IOATADevice*) newNub;
DLOG("AppleK2SATA register nub\n");
newNub->registerService();
newNub->release();
newNub = 0L;
}
}
}
if( isHotSwap )
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_REMOVE_MASK);
} else {
DLOG("AppleK2SATA@%1d no device detected\n", busChildNumber);
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_INSERT_MASK);
}
DLOG("AppleK2SATA@%d, started\n", busChildNumber);
return true;
failurePath:
if( isHotSwap )
{
IOLog("AppleK2SATA@%u, hot swap start failed\n", (unsigned int) busChildNumber);
}
DLOG("AppleK2SATA@%d, start failed\n", busChildNumber);
return false;
}
void
AppleK2SATA::free()
{
for( int i = 0; i < 5; i++)
{
if( ioBaseAddrMap[i] )
{
ioBaseAddrMap[ i ]->release();
ioBaseAddrMap[i] = 0;
}
}
if( _devIntSrc )
{
_devIntSrc->release();
_devIntSrc = 0;
}
super::free();
}
IOReturn
AppleK2SATA::setPowerState ( unsigned long powerStateOrdinal, IOService* whatDevice )
{
if( powerStateOrdinal == 0
&& !(isSleeping) )
{
DLOG("AppleK2SATA@%d, p-ordinal=0 \n", busChildNumber);
isSleeping = true;
}
if( powerStateOrdinal == 1
&& (isSleeping) )
{
isSleeping = false;
DLOG("AppleK2SATA@%d, p-ordinal=1 \n", busChildNumber);
if( cleanupSystemSleep )
{
cleanupSystemSleep = false;
UInt32 sataStat = OSReadLittleInt32( SATAStatus, 0);
DLOG("AppleK2SATA@%d, cleanup system sleep sata status reg = %X\n", busChildNumber, sataStat);
IOSleep(10); sataStat = OSReadLittleInt32( SATAStatus, 0);
DLOG("AppleK2SATA@%d, second cleanup system sleep sata status reg = %X\n", busChildNumber, sataStat);
if( !(sataStat & 0x00000303)
&& ( _nub[0] != 0) )
{
DLOG("AppleK2SATA@%d, cleanup system sleep device removed = %X\n", busChildNumber);
handleRemovalIRQ( );
return IOPMAckImplied;
}
if( isEmptyBay && (sataStat & 0x00000303) )
{
DLOG("AppleK2SATA@%d, cleanup system sleep device inserted = %X\n", busChildNumber);
handleEmptyBayIRQ();
return IOPMAckImplied;
}
OSWriteLittleInt32( SATAError, 0, 0xffffffff);
DLOG("AppleK2SATA@%d, cleanup system sleep saved sim reg = %X\n", busChildNumber, savedSIMValue);
if( isEmptyBay )
{
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_INSERT_MASK);
DLOG("AppleK2SATA@%d, cleanup system sleep restoring sim reg = %X\n", busChildNumber, kK2SIM_INSERT_MASK);
} else {
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_REMOVE_MASK);
DLOG("AppleK2SATA@%d, cleanup system sleep restoring sim reg = %X\n", busChildNumber, kK2SIM_REMOVE_MASK);
}
}
}
return IOPMAckImplied;
}
IOReturn
AppleK2SATA::powerStateWillChangeTo (IOPMPowerFlags theFlags, unsigned long, IOService* whichDevice)
{
if ( ( whichDevice == pmRootDomain)
&& !systemIsSleeping )
{
if (!(theFlags & IOPMPowerOn) && !(theFlags & IOPMSoftSleep) )
{
DLOG("AppleK2SATA@%d, entering system sleep \n", busChildNumber);
systemIsSleeping = true;
savedSIMValue = OSReadLittleInt32( SIMRegister,0);
OSWriteLittleInt32( SIMRegister, 0, 0x0); }
}
return IOPMAckImplied;
}
IOReturn
AppleK2SATA::powerStateDidChangeTo (IOPMPowerFlags theFlags, unsigned long, IOService* whichDevice)
{
if ( (whichDevice == pmRootDomain)
&& systemIsSleeping )
{
if ((theFlags & IOPMPowerOn) || (theFlags & IOPMSoftSleep) )
{
DLOG("AppleK2SATA@%d, leaving system sleep \n", busChildNumber);
systemIsSleeping = false;
cleanupSystemSleep = true;
changePowerStateTo( 1 );
}
}
return IOPMAckImplied;
}
bool
AppleK2SATA::createDeviceInterrupt(void)
{
DLOG("AppleK2SATA createDeviceInterrupt started\n");
_devIntSrc = IOFilterInterruptEventSource::filterInterruptEventSource(
(OSObject *)this,
(IOInterruptEventSource::Action) &AppleK2SATA::sDeviceInterruptOccurred,
(IOFilterInterruptEventSource::Filter) &AppleK2SATA::sFilterInterrupt,
getProvider(),
0);
DLOG("AppleK2SATA createdDeviceInterruptsource = %x\n", _devIntSrc);
DLOG("_workLoop = %x\n", _workLoop);
if( !_devIntSrc || getWorkLoop()->addEventSource(_devIntSrc) )
{
DLOG("AppleK2SATA failed create dev intrpt source\n");
return false;
}
_devIntSrc->enable();
DLOG("AppleK2SATA createDeviceInterrupt done\n");
return true;
}
void
AppleK2SATA::sDeviceInterruptOccurred(OSObject * owner, IOInterruptEventSource *evtSrc, int count)
{
AppleK2SATA* self = (AppleK2SATA*) owner;
self->handleDeviceInterrupt();
}
bool
AppleK2SATA::sFilterInterrupt(OSObject *owner, IOFilterInterruptEventSource *src)
{
AppleK2SATA* self = (AppleK2SATA*) owner;
return self->interruptIsValid( src );
}
bool
AppleK2SATA::interruptIsValid( IOFilterInterruptEventSource* )
{
UInt32 intStatus = OSReadLittleInt32( GInterruptStatus, 0);
OSSynchronizeIO();
if( ! (intStatus & interruptBitMask ) ) {
return false;
}
UInt32 clearIntStatus = *StatusWide;
OSSynchronizeIO();
clearIntStatus++;
if( isHotSwap )
{
OSWriteLittleInt32( SIMRegister, 0, 0x00000000);
}
_devIntSrc->signalInterrupt();
return false;
}
IOReturn
AppleK2SATA::handleDeviceInterrupt(void)
{
if( isEmptyBay && !systemIsSleeping )
{
return handleEmptyBayIRQ();
}
if( isHotSwap && !systemIsSleeping && handleRemovalIRQ( ) )
{
return 0;
}
if(_currentCommand == 0 )
{
return 0;
}
IOReturn result = super::handleDeviceInterrupt();
*_bmStatusReg = 0x04;
OSSynchronizeIO();
return result;
}
IOWorkLoop*
AppleK2SATA::getWorkLoop() const
{
IOWorkLoop* wl = _workLoop;
if (!wl)
{
wl = IOWorkLoop::workLoop();
if (!wl)
return 0;
}
return wl;
}
void
AppleK2SATA::handleTimeout( void )
{
if( isBusOnline == false)
{
return;
}
#ifdef ATA_DEBUG
UInt8* myPCIHeader = ((UInt8*)GInterruptStatus) - 0x80 ;
#endif
kprintf("AppleK2SATA@%d, timeout occurred\n", busChildNumber);
DLOG("AppleK2SATA@%d, PCI Status register %4X\n", busChildNumber, OSReadLittleInt16(myPCIHeader, 0x06) );
DLOG("AppleK2SATA@%d, Bus Master Command register %2X\n", busChildNumber, *_bmCommandReg);
kprintf("AppleK2SATA@%d, Bus Master status register %2X\n", busChildNumber, *_bmStatusReg);
DLOG("AppleK2SATA@%d, Bus Master PRD register %4X\n", busChildNumber, OSReadLittleInt32(_bmPRDAddresReg, 0) );
DLOG("AppleK2SATA@%d, Sata Status register %X\n", busChildNumber, *SATAStatus);
kprintf("AppleK2SATA@%d, Sata Error register %X\n", busChildNumber, *SATAError);
DLOG("AppleK2SATA@%d, PRD Table physical addr %X\n", busChildNumber, _prdTablePhysical);
for (int i = 0; i < 32 ; i++)
{
kprintf(" PRD %2d Target phys addr = %8X count = %4X flags = %4X\n",
i,
OSSwapLittleToHostInt32((uintptr_t)_prdTable[i].bufferPtr),
OSSwapLittleToHostInt16(_prdTable[i].byteCount),
OSSwapLittleToHostInt16(_prdTable[i].flags) );
}
UInt8 cmdRegVal = *_bmCommandReg;
OSSynchronizeIO();
*_bmCommandReg = (cmdRegVal & 0xFE); OSSynchronizeIO();
stopDMA();
volatile UInt8 statusByte = *_tfStatusCmdReg;
OSSynchronizeIO();
DLOG("AppleK2SATA@%d, status register %2X\n", busChildNumber, statusByte);
statusByte++;
super::handleTimeout();
}
bool
AppleK2SATA::configureTFPointers(void)
{
DLOG("AppleK2SATA config TF Pointers \n");
char myLocString[2] = {0,0} ;
sprintf(myLocString, "%1s", getProvider()->getLocation());
busChildNumber = myLocString[0] - 0x30;
DLOG("AppleK2SATA busChildNumber = %d, string = %s \n", busChildNumber, getProvider()->getLocation());
ioBaseAddrMap[0] = getProvider()->mapDeviceMemoryWithIndex( 0 );
if ( ioBaseAddrMap[0] == 0)
{
return false;
}
volatile UInt8* baseAddress = (volatile UInt8*)ioBaseAddrMap[0]->getVirtualAddress();
GInterruptStatus = (volatile UInt32*) (baseAddress + 0x1f80);
switch( busChildNumber )
{
case 0:
interruptBitMask = 0x00000001;
break;
case 1:
baseAddress += 0x100;
interruptBitMask = 0x00000002;
break;
case 2:
baseAddress += 0x200;
interruptBitMask = 0x00000004;
break;
case 3:
baseAddress += 0x300;
interruptBitMask = 0x00000008;
break;
default:
DLOG("AppleK2SATA - bus number out of range!\n");
return false;
break;
}
_tfDataReg = (volatile UInt16*) (baseAddress + 0);
_tfFeatureReg = baseAddress + 0x4;
_tfSCountReg = baseAddress + 0x8;
_tfSectorNReg = baseAddress + 0xC;
_tfCylLoReg = baseAddress + 0x10;
_tfCylHiReg = baseAddress + 0x14;
_tfSDHReg = baseAddress + 0x18;
_tfStatusCmdReg = baseAddress + 0x1C;
StatusWide = (volatile UInt32*) (baseAddress + 0x1c);
_tfAltSDevCReg = baseAddress + 0x20;
_bmCommandReg = (baseAddress + 0x30);
_bmStatusReg = (baseAddress + 0x31);
_bmPRDAddresReg = (volatile UInt32*) (baseAddress + 0x34);
SATAStatus = (volatile UInt32*) (baseAddress + 0x40);
SATAError = (volatile UInt32*) (baseAddress + 0x44);
SATAControl = (volatile UInt32*) (baseAddress + 0x48);
SIMRegister = (volatile UInt32*) (baseAddress + 0x88);
SICR1 = (volatile UInt32*) (baseAddress + 0x80);
SICR2 = (volatile UInt32*) (baseAddress + 0x84);
*_bmStatusReg = 0x04;
*SATAError = 0xffffffff;
*SIMRegister = 0;
OSSynchronizeIO();
OSWriteLittleInt32( SATAControl, 0, 0x00000300); UInt32 intCont1 = OSReadLittleInt32( SICR1, 0);
DLOG("AppleK2SATA@%d internal control register 1 = %X\n", busChildNumber, intCont1);
intCont1 &= (~0x00040000); DLOG("AppleK2SATA@%d setting internal control register 1 to %X\n", busChildNumber, intCont1);
OSWriteLittleInt32( SICR1, 0, intCont1);
DLOG("AppleK2SATA@%d configTFPointers done\n", busChildNumber);
return true;
}
IOReturn
AppleK2SATA::provideBusInfo( IOATABusInfo* infoOut)
{
if( infoOut == 0)
{
DLOG("AppleK2SATA nil pointer in provideBusInfo\n");
return -1;
}
infoOut->zeroData();
if( isHotSwap )
{
infoOut->setSocketType( kSATABay );
} else {
infoOut->setSocketType( kInternalSATA ); }
infoOut->setPIOModes( kATASupportedPIOModes);
infoOut->setDMAModes( 0 );
infoOut->setUltraModes( kATASupportedUltraDMAModes );
infoOut->setExtendedLBA( true ); infoOut->setMaxBlocksExtended( 0x0800 );
UInt8 units = 0;
if( _devInfo[0].type != kUnknownATADeviceType )
units++;
if( _devInfo[1].type != kUnknownATADeviceType )
units++;
infoOut->setUnits( units);
return kATANoErr;
}
IOReturn
AppleK2SATA::getConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("AppleK2SATA bad param in getConfig\n");
return -1;
}
configRequest->setPIOMode( busTimings[unitNumber].ataPIOSpeedMode);
configRequest->setDMAMode(busTimings[unitNumber].ataMultiDMASpeed);
configRequest->setPIOCycleTime(busTimings[unitNumber].ataPIOCycleTime );
configRequest->setDMACycleTime(busTimings[unitNumber].ataMultiCycleTime);
configRequest->setPacketConfig( _devInfo[unitNumber].packetSend );
configRequest->setUltraMode(busTimings[unitNumber].ataUltraDMASpeedMode);
return kATANoErr;
}
IOReturn
AppleK2SATA::selectConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("AppleK2SATA bad param in setConfig\n");
return -1;
}
if( ( configRequest->getPIOMode() & kATASupportedPIOModes ) == 0x00 )
{
DLOG("AppleK2SATA setConfig PIO mode not supported\n");
return kATAModeNotSupported;
}
UInt16 ultraSupported = kATASupportedUltraDMAModes;
if( configRequest->getUltraMode() & ~ultraSupported )
{
DLOG("AppleK2SATA setConfig no ultra\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() & ~kATASupportedMultiDMAModes )
{
DLOG("AppleK2SATA setConfig DMA mode not supported\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() > 0x0000
&& configRequest->getUltraMode() > 0x0000 )
{
DLOG("AppleK2SATA err, only one DMA class allowed in config select\n");
return kATAModeNotSupported;
}
_devInfo[unitNumber].packetSend = configRequest->getPacketConfig();
DLOG("AppleK2SATA setConfig packetConfig = %d\n", _devInfo[unitNumber].packetSend );
return kATANoErr;
}
IOReturn
AppleK2SATA::selectIOTimerValue( IOATADevConfig* configRequest, UInt32 unitNumber)
{
busTimings[unitNumber].ataPIOSpeedMode = configRequest->getPIOMode();
busTimings[unitNumber].ataPIOCycleTime = configRequest->getPIOCycleTime();
busTimings[unitNumber].ataMultiDMASpeed = configRequest->getDMAMode();
busTimings[unitNumber].ataMultiCycleTime = configRequest->getDMACycleTime();
busTimings[unitNumber].ataUltraDMASpeedMode = configRequest->getUltraMode();
return getConfig( configRequest, unitNumber);
}
void
AppleK2SATA::selectIOTiming( ataUnitID unit )
{
return;
}
IOReturn
AppleK2SATA::startDMA( void )
{
IOReturn err = kATANoErr;
stopDMA();
_dmaState = kATADMAStarting;
err = createChannelCommands();
if( err )
{
DLOG("IOPCIATA error createChannelCmds err = %ld\n", (long int)err);
stopDMA();
return err;
}
if(_currentCommand->getOpcode() != kATAPIFnExecIO)
activateDMAEngine();
return err;
}
IOReturn
AppleK2SATA::issueCommand( void )
{
if( _currentCommand == 0 )
{
DLOG("IOATA can't issue nil command\n");
return kATAErrUnknownType;
}
IOReturn result = kIOReturnSuccess;
UInt16 specialFeature = 0x0000;
if( (_currentCommand->getFlags() & (mATAFlagProtocolATAPI | mATAFlagUseDMA) ) == (mATAFlagProtocolATAPI | mATAFlagUseDMA) )
{
if( (_currentCommand->getFlags() & mATAFlagIORead) )
{
specialFeature = 0x05;
} else if ( (_currentCommand->getFlags() & mATAFlagIOWrite) ) {
specialFeature = 0x01;
}
}
if( _currentCommand->getFlags() & mATAFlag48BitLBA )
{
IOExtendedLBA* extLBA = _currentCommand->getExtendedLBA();
OSWriteLittleInt32(_tfSDHReg , 0, extLBA->getDevice());
OSWriteLittleInt32(_tfFeatureReg, 0, extLBA->getFeatures16() | specialFeature );
OSWriteLittleInt32(_tfSCountReg, 0, extLBA->getSectorCount16() );
OSWriteLittleInt32(_tfSectorNReg, 0, extLBA->getLBALow16() );
OSWriteLittleInt32(_tfCylLoReg, 0, extLBA->getLBAMid16() );
OSWriteLittleInt32(_tfCylHiReg, 0, extLBA->getLBAHigh16() );
OSWriteLittleInt32(_tfStatusCmdReg, 0, extLBA->getCommand());
} else {
ataTaskFile* tfRegs = _currentCommand->getTaskFilePtr();
OSWriteLittleInt32(_tfSDHReg, 0, tfRegs->ataTFSDH);
OSWriteLittleInt32(_tfFeatureReg, 0, tfRegs->ataTFFeatures | specialFeature);
OSWriteLittleInt32(_tfSCountReg, 0, tfRegs->ataTFCount);
OSWriteLittleInt32(_tfSectorNReg, 0, tfRegs->ataTFSector);
OSWriteLittleInt32(_tfCylLoReg, 0, tfRegs->ataTFCylLo);
OSWriteLittleInt32(_tfCylHiReg, 0, tfRegs->ataTFCylHigh);
OSWriteLittleInt32(_tfStatusCmdReg, 0, tfRegs->ataTFCommand);
}
return result;
}
#pragma mark hot-removal
IOReturn
AppleK2SATA::handleQueueFlush( void )
{
UInt32 savedQstate = _queueState;
_queueState = IOATAController::kQueueLocked;
IOReturn errPerCommand = kIOReturnError;
if( isBusOnline == false )
{
errPerCommand = kIOReturnOffline;
}
IOATABusCommand* cmdPtr = 0;
while( cmdPtr = dequeueFirstCommand() )
{
cmdPtr->setResult(errPerCommand);
cmdPtr->executeCallback();
}
_queueState = savedQstate;
return kATANoErr;
}
bool
AppleK2SATA::checkTimeout( void )
{
if( isBusOnline == false )
{
return true;
}
return super::checkTimeout();
}
IOReturn
AppleK2SATA::executeCommand(IOATADevice* nub, IOATABusCommand* command)
{
if( isBusOnline == false)
{
return kIOReturnOffline;
}
return super::executeCommand(nub, command);
}
IOReturn
AppleK2SATA::handleCommand( void* param0,
void* param1,
void* param2,
void* param3 )
{
if( isBusOnline == false)
{
IOATABusCommand* command = (IOATABusCommand*) param0;
if( command == 0)
return -1;
command->setCommandInUse(false);
command->state = kATADone;
command->setResult( kIOReturnOffline );
command->executeCallback();
return kIOReturnOffline;
}
IOReturn result = super::handleCommand( param0, param1, param2, param3 );
return result;
}
void
AppleK2SATA::cleanUpAction(OSObject * owner,
void * arg0,
void * arg1,
void * ,
void * )
{
AppleK2SATA* self = (AppleK2SATA*) owner;
self->cleanUpBus();
}
void
AppleK2SATA::cleanUpBus(void)
{
if( _currentCommand != 0)
{
_currentCommand->setResult(kIOReturnOffline);
_currentCommand->executeCallback();
_currentCommand = 0;
}
}
UInt32
AppleK2SATA::scanForDrives( void )
{
UInt32 sataStat = OSReadLittleInt32( SATAStatus, 0);
UInt32 devicesFound = 0;
isEmptyBay = false;
switch( (sataStat & 0x00000007) )
{
case 0:
DLOG("AppleK2SATA@%1d no device detected\n", busChildNumber);
if( isHotSwap )
{
isEmptyBay = true;
}
break;
case 1:
DLOG("AppleK2SATA@%1d device detected, but phy not ready\n", busChildNumber);
if( isHotSwap )
{
isEmptyBay = true;
}
break;
case 3:
DLOG("AppleK2SATA@%1d device detected phy is ready \n", busChildNumber);
devicesFound = 1;
break;
default:
DLOG("AppleK2SATA@%1d Phy disabled or reserved bits set, %X\n", busChildNumber, sataStat);
if( isHotSwap )
{
isEmptyBay = true;
}
break;
}
if( devicesFound )
{
OSWriteLittleInt32(_tfAltSDevCReg, 0, mATADCRReset);
IODelay( 100 );
OSWriteLittleInt32(_tfAltSDevCReg, 0, 0);
IOSleep(100);
if( super::scanForDrives() )
{
return 1;
}
IOLog("AppleK2SATA@%1u phy detected but drive not in ready state\n", (unsigned int)busChildNumber);
IOLog("AppleK2SATA@%1u drive status: CL=%2x, CH=%2x, SC=%2x, SN=%2x, AltStat=%2x\n",
(unsigned int)busChildNumber,
*_tfCylLoReg,
*_tfCylHiReg,
*_tfSCountReg,
*_tfSectorNReg,
*_tfAltSDevCReg);
if( !isHotSwap )
{
return 0;
}
isEmptyBay = true;
}
if( isEmptyBay )
{
isBusOnline = false;
DLOG("AppleK2SATA@%1d empty port retained \n", busChildNumber);
return 1;
}
OSWriteLittleInt32( SATAControl, 0, 0x3);
return 0;
}
IOReturn
AppleK2SATA::selectDevice( ataUnitID unit )
{
UInt32 msLoops = _currentCommand->getTimeoutMS()/10;
if( msLoops > 3000 )
{
msLoops = 3000;
}
if( ! (kATADevice0DeviceID == unit) )
{
DLOG( "AppleK2SATA@%1d: invalid device ID selected\n", busChildNumber);
return kATAInvalidDevID;
}
selectIOTiming( unit );
UInt8 preReqMask = (mATABusy | mATADataRequest );
UInt8 preReqCondition = 0x00;
if( _devInfo[ unit ].type == kATADeviceType
&& _currentCommand->getOpcode() == kATAFnExecIO
&& _currentCommand->getTaskFilePtr()->ataTFCommand != 0x90
&& _currentCommand->getTaskFilePtr()->ataTFCommand != 0x91 )
{
preReqMask |= mATADriveReady;
preReqCondition |= mATADriveReady;
}
while ( !waitForU8Status( (mATABusy ), 0x00 ))
{
OSSynchronizeIO();
if( msLoops == 0
|| (*_tfStatusCmdReg & (mATABusy |mATADataRequest) ) == mATADataRequest
|| checkTimeout() )
{
DLOG( "AppleK2SATA@%1d: DRQ set, can't select device. %X\n", busChildNumber, OSReadLittleInt32(_tfStatusCmdReg,0) );
return kATAErrDevBusy;
}
msLoops--;
IOSleep(10);
}
_selectedUnit = unit;
return kATANoErr;
}
IOReturn
AppleK2SATA::registerAccess(bool isWrite)
{
UInt32 RegAccessMask = _currentCommand->getRegMask();
IOReturn err = kATANoErr;
bool isExtLBA = _currentCommand->getFlags() & mATAFlag48BitLBA;
IOExtendedLBA* extLBA = _currentCommand->getExtendedLBA();
if (RegAccessMask & mATAErrFeaturesValid) {
if(isWrite)
{
if(isExtLBA )
{
OSWriteLittleInt16(_tfFeatureReg, 0, extLBA->getFeatures16() );
} else {
*_tfFeatureReg = _currentCommand->getErrorReg();
}
}else{
if(isExtLBA )
{
extLBA->setFeatures16( OSReadLittleInt16(_tfFeatureReg, 0));
} else {
_currentCommand->setFeatures( *_tfFeatureReg) ;
}
}
}
if (RegAccessMask & mATASectorCntValid) {
if(isWrite)
{
if(isExtLBA )
{
OSWriteLittleInt16(_tfSCountReg, 0, extLBA->getSectorCount16() );
} else {
*_tfSCountReg = _currentCommand->getSectorCount();
}
}else{
if(isExtLBA )
{
extLBA->setSectorCount16( OSReadLittleInt16(_tfSCountReg, 0 ) );
} else {
_currentCommand->setSectorCount( *_tfSCountReg );
}
}
}
if (RegAccessMask & mATASectorNumValid) {
if(isWrite)
{
if(isExtLBA )
{
OSWriteLittleInt16(_tfSectorNReg, 0, extLBA->getLBALow16() );
} else {
*_tfSectorNReg = _currentCommand->getSectorNumber();
}
}else{
if(isExtLBA )
{
extLBA->setLBALow16( OSReadLittleInt16( _tfSectorNReg, 0 ));
} else {
_currentCommand->setSectorNumber( *_tfSectorNReg );
}
}
}
if (RegAccessMask & mATACylinderLoValid) {
if(isWrite)
{
if(isExtLBA )
{
OSWriteLittleInt16(_tfCylLoReg, 0, extLBA->getLBAMid16() );
} else {
*_tfCylLoReg = _currentCommand->getCylLo();
}
}else{
if(isExtLBA )
{
extLBA->setLBAMid16( OSReadLittleInt16( _tfCylLoReg, 0) );
} else {
_currentCommand->setCylLo( *_tfCylLoReg );
}
}
}
if (RegAccessMask & mATACylinderHiValid) {
if(isWrite)
{
if(isExtLBA )
{
OSWriteLittleInt16(_tfCylHiReg, 0, extLBA->getLBAHigh16() );
} else {
*_tfCylHiReg = _currentCommand->getCylHi();
}
}else{
if(isExtLBA )
{
extLBA->setLBAHigh16( OSReadLittleInt16(_tfCylHiReg, 0) );
} else {
_currentCommand->setCylHi( *_tfCylHiReg );
}
}
}
if (RegAccessMask & mATASDHValid) {
if(isWrite)
{
*_tfSDHReg = _currentCommand->getDevice_Head();
}else{
_currentCommand->setDevice_Head( *_tfSDHReg );
}
}
if (RegAccessMask & mATAAltSDevCValid) {
if(isWrite)
{
*_tfAltSDevCReg = _currentCommand->getAltStatus();
}else{
_currentCommand->setControl( *_tfAltSDevCReg );
}
}
if (RegAccessMask & mATADataValid) {
if(isWrite)
{
*_tfDataReg = _currentCommand->getDataReg();
}else{
_currentCommand->setDataReg( *_tfDataReg );
}
}
if (RegAccessMask & mATAStatusCmdValid) {
if(isWrite)
{
*_tfStatusCmdReg = _currentCommand->getStatus();
}else{
_currentCommand->setCommand(*_tfStatusCmdReg );
}
}
return err;
}
IOReturn
AppleK2SATA::softResetBus( bool doATAPI )
{
DLOG("AppleK2SATA@%d, reset bus\n", busChildNumber);
IOReturn result = kATANoErr;
UInt32 intCont1 = OSReadLittleInt32( SICR1, 0);
intCont1 &= (~0x00040000); OSWriteLittleInt32( SICR1, 0, intCont1);
if (doATAPI)
{
return super::softResetBus( doATAPI );
} else {
OSWriteLittleInt32(_tfAltSDevCReg, 0, mATADCRReset);
IODelay( 100 );
OSWriteLittleInt32(_tfAltSDevCReg, 0, 0);
DLOG("AppleK2SATA@%d, soft reset sequenced\n", busChildNumber);
_selectedUnit = kATADevice0DeviceID;
}
IOSleep(50);
UInt8 readyMask = mATABusy;
UInt8 readyOn = 0x00;
if( (_devInfo[0].type == kATADeviceType)
&& (!doATAPI) )
{
readyMask |= mATADriveReady; readyOn = mATADriveReady; }
bool resetFailed = true;
for( int i = 0; i < 3100; i++)
{
UInt32 status = OSReadLittleInt32( _tfStatusCmdReg, 0 );
if( ( status & readyMask )== readyOn)
{
resetFailed = false;
break;
}
IOSleep( 10 );
}
if( resetFailed )
{
DLOG("AppleK2SATA@%d, soft reset failed\n", busChildNumber);
result = kATATimeoutErr;
}
DLOG("AppleK2SATA@%d, soft reset completed\n", busChildNumber);
return result;
}
IOReturn
AppleK2SATA::synchronousIO(void)
{
IOReturn err = kATANoErr;
*_tfAltSDevCReg = 0x02; IOSleep(1);
err = asyncCommand();
DLOG("AppleK2SATA@%d, synchronous command sent: err = %ld state= %lx\n", busChildNumber, (long int) err, (int) _currentCommand->state);
if( err )
{
_currentCommand->state = IOATAController::kATAComplete;
} else {
for( UInt32 i = 0; i< 3000; i++)
{
if( waitForU8Status( mATABusy, 0x00 ) )
break;
IOSleep(10); }
}
if( _currentCommand->state == IOATAController::kATAPICmd )
{
DLOG("AppleK2SATA@%d,::synchronous issue packet\n", busChildNumber);
err = writePacket();
if( err == kATANoErr )
{
if( (_currentCommand->getFlags() & (mATAFlagIORead | mATAFlagIOWrite ) )
&& ((_currentCommand->getFlags() & mATAFlagUseDMA ) != mATAFlagUseDMA ) )
{
_currentCommand->state = IOATAController::kATADataTx;
} else {
_currentCommand->state = IOATAController::kATAStatus;
}
} else {
_currentCommand->state = IOATAController::kATAComplete;
}
}
if( _currentCommand->state == IOATAController::kATADataTx )
{
while( _currentCommand->state == IOATAController::kATADataTx )
{
err = asyncData();
if( err )
{
_currentCommand->state = IOATAController::kATAComplete;
break;
}
}
if( (_currentCommand->getFlags() & mATAFlagProtocolATAPI) == mATAFlagProtocolATAPI
&& _currentCommand->getPacketSize() > 0)
{
waitForU8Status( mATABusy, 0x00 );
}
}
if( _currentCommand->state == IOATAController::kATAStatus )
{
err = asyncStatus();
_currentCommand->state = IOATAController::kATAComplete;
}
UInt32 finalStatus = OSReadLittleInt32( _tfStatusCmdReg, 0 );
finalStatus++;
if( _currentCommand->state == IOATAController::kATAComplete )
{
completeIO(err);
}
*_tfAltSDevCReg = 0x00; IOSleep(1);
return err;
}
bool
AppleK2SATA::allocDMAChannel(void)
{
if( _bmCommandReg == 0
|| _bmStatusReg == 0
|| _bmPRDAddresReg == 0 )
{
DLOG("AppleK2SATA@%d, bm regs not initialised.\n", busChildNumber);
return false;
}
_prdTable = (PRD*)IOMallocContiguous( sizeof(PRD) * kATAMaxDMADesc,
0x10000,
&_prdTablePhysical );
if( ! _prdTable )
{
DLOG("AppleK2SATA@%d, alloc prd table failed\n",busChildNumber);
return false;
}
_DMACursor = IONaturalMemoryCursor::withSpecification(0x10000,
kMaxATAXfer
);
if( ! _DMACursor )
{
freeDMAChannel();
DLOG("AppleK2SATA@%d, alloc DMACursor failed\n", busChildNumber);
return false;
}
initATADMAChains(_prdTable);
return true;
}
bool
AppleK2SATA::freeDMAChannel(void)
{
if( _prdTable )
{
stopDMA();
IOFreeContiguous( (void*) _prdTable,
sizeof(PRD) * kATAMaxDMADesc);
}
return true;
}
void
AppleK2SATA::initATADMAChains (PRD* descPtr)
{
UInt32 i;
for (i = 0; i < kATAMaxDMADesc; i++)
{
descPtr->bufferPtr = 0;
descPtr->byteCount = 1;
descPtr->flags = OSSwapHostToLittleInt16( (UInt16) kLast_PRD);
descPtr++;
}
}
IOReturn
AppleK2SATA::createChannelCommands(void)
{
IOMemoryDescriptor* descriptor = _currentCommand->getBuffer();
IOMemoryCursor::PhysicalSegment physSegment;
UInt32 index = 0;
UInt8 *xferDataPtr, *ptr2EndData, *next64KBlock, *starting64KBlock;
UInt32 xferCount, count2Next64KBlock;
if( ! descriptor )
{
DLOG("AppleK2SATA@%d, nil buffer!\n", busChildNumber);
return -1;
}
IOByteCount bytesRemaining = _currentCommand->getByteCount() ;
if( bytesRemaining == 0
|| descriptor->getLength() == 0
|| bytesRemaining > descriptor->getLength() )
{
kprintf("AppleK2SATA@%d, DMA request with insufficient byte count, returning error\n", busChildNumber);
return -1;
}
IOByteCount xfrPosition = _currentCommand->getPosition() ;
IOByteCount transferSize = 0;
while( _DMACursor->getPhysicalSegments(
descriptor,
xfrPosition,
&physSegment,
1,
bytesRemaining, &transferSize) )
{
xferDataPtr = (UInt8*) physSegment.location;
xferCount = physSegment.length;
if( (UInt32)xferDataPtr & 0x01)
{
kprintf("AppleK2SATA@%d, target DMA address is odd addr = %X xferCount = %X.\n", busChildNumber, xferDataPtr, xferCount);
kprintf("AppleK2SATA@%d, transfer command xfer length is 0x%X \n", busChildNumber, _currentCommand->getByteCount() );
return kIOReturnNotAligned;
}
bytesRemaining -= xferCount;
xfrPosition += xferCount;
starting64KBlock = (UInt8*) ( (UInt32) xferDataPtr & 0xffff0000);
ptr2EndData = xferDataPtr + xferCount;
next64KBlock = (starting64KBlock + 0x10000);
while( xferCount > 0 )
{
if ( false ) {
count2Next64KBlock = next64KBlock - xferDataPtr;
if (index < kATAMaxDMADesc)
{
if( (UInt32) xferDataPtr & 0x01)
{
kprintf("AppleK2SATA@%d, 64k boundary crossed target DMA address is odd addr = %X bytes remaining = %X.\n", busChildNumber, xferDataPtr,bytesRemaining);
kprintf("AppleK2SATA@%d, PRD chain index %ld \n", busChildNumber, index);
kprintf("AppleK2SATA@%d, set PRD ptr=0x%X count d = %d continue flag\n", busChildNumber, xferDataPtr, (UInt16)count2Next64KBlock);
}
setPRD(xferDataPtr, (UInt16)count2Next64KBlock, &_prdTable[index], kContinue_PRD);
xferDataPtr = next64KBlock;
next64KBlock += 0x10000;
xferCount -= count2Next64KBlock;
index++;
} else {
kprintf("AppleK2SATA@%d, 64k boundary crossed, dma too big, PRD table exhausted A.\n", busChildNumber);
_dmaState = kATADMAError;
return -1;
}
} else {
if (index < kATAMaxDMADesc)
{
if( ((UInt32) xferDataPtr & 0x01) || (xferCount & 0x01))
{
kprintf("AppleK2SATA@%d, target DMA address is odd addr = %X bytes remaining = %X.\n", busChildNumber, xferDataPtr, bytesRemaining);
kprintf("AppleK2SATA@%d, PRD chain index %ld \n", busChildNumber, index);
kprintf("AppleK2SATA@%d, set PRD ptr=0x%X count d = %d flag by bytes remaining\n", busChildNumber, xferDataPtr, xferCount);
}
setPRD(xferDataPtr, (UInt16) xferCount, &_prdTable[index], (bytesRemaining == 0) ? kLast_PRD : kContinue_PRD);
xferCount = 0;
index++;
} else {
kprintf("AppleK2SATA@%d, dma too big, PRD table exhausted B.\n", busChildNumber);
_dmaState = kATADMAError;
return -1;
}
}
}
}
_dmaState = kATADMAStatus;
return kATANoErr;
}
bool
AppleK2SATA::ATAPISlaveExists( void )
{
return false;
}
IOReturn
AppleK2SATA::writePacket( void )
{
UInt32 packetSize = _currentCommand->getPacketSize();
UInt16* packetData = _currentCommand->getPacketData();
DLOG("AppleK2SATA@%d, writePacket\n", busChildNumber);
if ( packetSize == 0)
{
DLOG("AppleK2SATA@%d, writePacket no packet data\n", busChildNumber);
return kATANoErr;
}
UInt8 status = 0x00;
UInt32 breakDRQ = 3;
while ( !waitForU8Status( (mATABusy | mATADataRequest), mATADataRequest)
&& !checkTimeout()
&& (breakDRQ != 0) )
{
status = *_tfAltSDevCReg;
status &= (mATABusy | mATAError);
if( mATAError == status )
{
DLOG("AppleK2SATA@%d, writePacket error status =%x\n", busChildNumber, status);
return kATADeviceError;
}
breakDRQ--;
IOSleep( 10 ); }
if ( checkTimeout()
|| breakDRQ == 0)
{
DLOG("AppleK2SATA@%d, writePacket timed out status = %X\n", busChildNumber, status);
return kATATimeoutErr;
}
UInt32 packetLength = 6;
if( packetSize > 12 )
{
packetLength = 8;
}
for( UInt32 i = 0; i < packetLength; i++)
{
OSSynchronizeIO();
* _tfDataReg = *packetData;
packetData++;
}
UInt8 curStat = *_tfAltSDevCReg;
DLOG("AppleK2SATA@%d, writePacket packet data complete. status = %X\n", busChildNumber, curStat);
if( _currentCommand->getFlags() & (mATAFlagUseDMA) )
{
while(!waitForU8Status( (mATADataRequest), 0) )
{
curStat = *_tfAltSDevCReg;
DLOG("AppleK2SATA@%d, waiting for not DRQ after packet status = %x \n", busChildNumber, curStat);
;
}
curStat = *_tfAltSDevCReg;
DLOG("AppleK2SATA@%d, writePacket activate DMA engine. status = %X\n", busChildNumber, curStat);
activateDMAEngine();
}
return kATANoErr ;
}
IOReturn
AppleK2SATA::handleEmptyBayIRQ( void )
{
OSWriteLittleInt32( SIMRegister, 0, 0x0); #ifdef ATA_DEBUG
UInt32 errorBits = OSReadLittleInt32( SATAError, 0 );
#endif
DLOG("AppleK2SATA@%d, EmptyBay IRQ sata err reg = %X\n", busChildNumber, errorBits);
OSWriteLittleInt32( SATAError, 0, 0xffffffff);
OSWriteLittleInt32( SATAControl, 0, 00000301);
IODelay(100);
OSWriteLittleInt32( SATAControl, 0, 00000300);
SInt32 breakLoop = 1000;
UInt32 sataStat = OSReadLittleInt32( SATAStatus, 0);
while( breakLoop )
{
if( (sataStat & 3) == 3 )
break;
breakLoop --;
IOSleep(10);
sataStat = OSReadLittleInt32( SATAStatus, 0);
};
if(breakLoop == 0)
{
DLOG("AppleK2SATA@%d, EmptyBay IRQ Phy timeout sata status = %X\n", busChildNumber, sataStat);
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_INSERT_MASK);
return -1;
}
_devInfo[0].type = kATADeviceType;
isEmptyBay = false;
isBusOnline = true;
DLOG("AppleK2SATA@%d, EmptyBay IRQ creating thread.\n", busChildNumber);
IOCreateThread( (IOThreadFunc) &createNubsThread, this);
DLOG("AppleK2SATA@%d, EmptyBay IRQ creating thread done.\n", busChildNumber);
return kATANoErr;
}
IOReturn
AppleK2SATA::handleRemovalIRQ( void )
{
UInt32 errorBits = OSReadLittleInt32( SATAError, 0 );
if( (errorBits & kK2SIM_REMOVE_MASK) )
{
stopDMA();
OSWriteLittleInt32( SIMRegister, 0, 0x00000000);
DLOG("AppleK2SATA@%d, handleRemovalIRQ sata err reg = %X\n", busChildNumber, errorBits);
OSWriteLittleInt32( SATAError, 0, 0xffffffff);
UInt32 sataStat = OSReadLittleInt32( SATAStatus, 0);
DLOG("AppleK2SATA@%d, handleRemovalIRQ sata status reg = %X\n", busChildNumber, sataStat);
IOSleep(10); sataStat = OSReadLittleInt32( SATAStatus, 0);
if( sataStat & 0x00000303 )
{
DLOG("AppleK2SATA@%d, handleRemovalIRQ drive still attached sata status reg = %X\n", busChildNumber, sataStat);
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_REMOVE_MASK);
return kATANoErr;
}
isBusOnline = false;
handleQueueFlush();
cleanUpBus();
if( _nub[0])
{
_nub[0]->terminate();
_nub[0] = 0;
}
isEmptyBay = true;
IOSleep(10); OSWriteLittleInt32( SATAError, 0, 0xffffffff);
IOLog("AppleK2SATA@%1u device removed\n",(unsigned int) busChildNumber);
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_INSERT_MASK);
return -1;
}
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_REMOVE_MASK);
return kATANoErr;
}
void
AppleK2SATA::createNubsThread( void* param )
{
AppleK2SATA* self = (AppleK2SATA*) param;
self->createNubs();
IOExitThread();
}
void
AppleK2SATA::createNubs( void )
{
DLOG("AppleK2SATA@%d creating nub\n", busChildNumber);
ATADeviceNub* newNub = ATADeviceNub::ataDeviceNub( (IOATAController*)this, (ataUnitID) 0, _devInfo[0].type );
if(kATAPIDeviceType == _devInfo[0].type)
{
OSWriteLittleInt32( SIMRegister, 0, 0x20000000);
}
OSWriteLittleInt32( SATAError, 0, 0xffffffff);
OSWriteLittleInt32( SIMRegister, 0, kK2SIM_REMOVE_MASK);
if( newNub )
{
DLOG("AppleK2SATA@%d attach nub\n", busChildNumber);
newNub->attach(this);
_nub[0] = (IOATADevice*) newNub;
IOLog("AppleK2SATA@%u drive inserted\n",(unsigned int) busChildNumber);
newNub->registerService();
newNub->release();
newNub = 0L;
} else {
DLOG("AppleK2SATA@%d, EmptyBay IRQ failed to create nub.", busChildNumber);
}
}