#include "AppleKiwiATA.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 <libkern/OSByteOrder.h>
#include <libkern/OSAtomic.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 "kiwi-ata"
#define kModelPropertyKey "name"
#define kModel4String "ata-6"
#define kPCIInlineKey "pci_inline"
#define kMode6Key "mode6"
#define kATASupportedPIOModes 0x0008
#define kATASupportedMultiDMAModes 0x0004
#define kATASupportedUltraDMAModes 0x0034
#define kATAAddSupport133 0x0074
#define kPartATimingLE 0x270d6925
#define kPartBTiming33LE 0x2a07cd35
#define kPartBTiming66LE 0x1a03cd35
#define kPartBTiming100LE 0x1a02cb35
#define kPartBTiming133LE 0x1a01cb35
#pragma mark -IOService Overrides -
#define super IOPCIATA
OSDefineMetaClassAndStructors ( AppleKiwiATA, IOPCIATA )
bool
AppleKiwiATA::init(OSDictionary* properties)
{
DLOG("AppleKiwiATA init start\n");
for( int i = 0; i < 4; i++)
{
ioBaseAddrMap[ i ] = 0;
}
_devIntSrc = 0;
busChildNumber = 0;
isBusOnline = true;
chipRoot = 0;
forcePCIInline = true;
mode6Capable = false;
reconfigureTiming = false; if (super::init(properties) == false)
{
DLOG("AppleKiwiATA: super::init() failed\n");
return false;
}
DLOG("AppleKiwiATA init done\n");
return true;
}
IOService*
AppleKiwiATA::probe(IOService* provider, SInt32* score)
{
OSData *compatibleEntry;
DLOG("AppleKiwiATA starting probe\n");
compatibleEntry = OSDynamicCast( OSData, provider->getProperty( "compatible" ) );
if ( compatibleEntry == 0 )
{
DLOG("AppleKiwiATA failed getting compatible property\n");
return 0;
}
if ( compatibleEntry->isEqualTo( kCompatibleString, sizeof(kCompatibleString)-1 ) == false )
{
DLOG("AppleKiwiATA compatible property doesn't match\n");
return 0;
}
OSData *registryEntry;
registryEntry = OSDynamicCast( OSData, provider->getProperty( kModelPropertyKey ) );
if( registryEntry == 0)
{
DLOG("AppleKiwiATA unknown model property.\n");
return 0;
}
if ( !provider->getProperty( kPCIInlineKey ) )
{
DLOG("AppleKiwiATA pci_inline enable lock\n");
forcePCIInline = true;
} else {
DLOG("AppleKiwiATA pci_inline disable lock\n");
forcePCIInline = false;
}
if ( !provider->getProperty( kMode6Key ) )
{
DLOG("AppleKiwiATA mode 5\n");
mode6Capable = false;
} else {
DLOG("AppleKiwiATA mode 6\n");
mode6Capable = true;
}
return this;
}
bool
AppleKiwiATA::start(IOService *provider)
{
DLOG("AppleKiwiATA::start() begin\n");
if( ! provider->open(this) )
{
DLOG("AppleKiwiATA provider did not open\n");
getProvider()->message( 'fail', 0 );
return false;
}
chipRoot = (AppleKiwiRoot*) getProvider()->getProvider();
ATADeviceNub* newNub=0L;
getLock(true);
if (!super::start( provider))
{
DLOG("AppleKiwiATA: super::start() failed\n");
goto failurePath;
}
if( ! createDeviceInterrupt() )
{
DLOG("AppleKiwiATA: createDeviceInterrupts failed\n");
goto failurePath;
}
DLOG("AppleKiwiATA::start() done\n");
getLock( false );
for( UInt32 i = 0; i < 2; i++)
{
if( _devInfo[i].type != kUnknownATADeviceType )
{
DLOG("AppleKiwiATA creating nub\n");
newNub = ATADeviceNub::ataDeviceNub( (IOATAController*)this, (ataUnitID) i, _devInfo[i].type );
if( newNub )
{
DLOG("AppleKiwiATA attach nub\n");
newNub->attach(this);
_nub[i] = (IOATADevice*) newNub;
DLOG("AppleKiwiATA register nub\n");
newNub->registerService();
newNub = 0L;
}
}
}
getProvider()->message( 'onli', 0 );
return true;
failurePath:
getProvider()->message( 'fail', 0 );
getLock( false );
return false;
}
void
AppleKiwiATA::free()
{
for( int i = 0; i < 4; i++)
{
if( ioBaseAddrMap[i] )
{
ioBaseAddrMap[ i ]->release();
ioBaseAddrMap[i] = 0;
}
}
if( _devIntSrc )
{
_devIntSrc->release();
_devIntSrc = 0;
}
super::free();
}
void
AppleKiwiATA::getLock(bool lock)
{
if( forcePCIInline )
{
chipRoot->getLock(lock);
}
}
bool
AppleKiwiATA::createDeviceInterrupt(void)
{
DLOG("AppleKiwiATA createDeviceInterrupt started\n");
_devIntSrc = IOInterruptEventSource::interruptEventSource(
(OSObject *)this,
(IOInterruptEventSource::Action) &AppleKiwiATA::sDeviceInterruptOccurred,
getProvider(),
0);
DLOG("AppleKiwiATA createdDeviceInterruptsource = %x\n", _devIntSrc);
DLOG("_workLoop = %x\n", _workLoop);
if( !_devIntSrc || getWorkLoop()->addEventSource(_devIntSrc) )
{
DLOG("AppleKiwiATA failed create dev intrpt source\n");
return false;
}
UInt32 intMaskLE = (busChildNumber == 0)? 0x00000200 : 0x00000400;
*globalControlReg &= ~intMaskLE;
OSSynchronizeIO();
_devIntSrc->enable();
DLOG("AppleKiwiATA createDeviceInterrupt done\n");
return true;
}
void
AppleKiwiATA::sDeviceInterruptOccurred(OSObject * owner, IOInterruptEventSource *evtSrc, int count)
{
AppleKiwiATA* self = (AppleKiwiATA*) owner;
self->handleDeviceInterrupt();
}
bool
AppleKiwiATA::interruptIsValid( IOFilterInterruptEventSource* )
{
return true;
}
IOReturn
AppleKiwiATA::handleDeviceInterrupt(void)
{
if( isBusOnline == false)
{
return kIOReturnOffline;
}
if( _currentCommand == 0)
{
return 0;
}
UInt32 intMaskLE = (busChildNumber == 0)? 0x00000200 : 0x00000400;
getLock(true);
UInt32 gcrStatus = *globalControlReg;
UInt8 bmStatus = *_bmStatusReg;
OSSynchronizeIO();
if( ! (gcrStatus & 0x00000020 ) ) {
getLock( false );
return 0;
}
if( (_currentCommand->getFlags() & mATAFlagUseDMA ) == mATAFlagUseDMA )
{
while( !(bmStatus & 0x04) )
{
bmStatus = *_bmStatusReg;
OSSynchronizeIO();
}
}
*globalControlReg |= intMaskLE;
OSSynchronizeIO();
*_bmStatusReg = 0x04;
OSSynchronizeIO();
IOReturn result = super::handleDeviceInterrupt();
*globalControlReg &= ~intMaskLE;
OSSynchronizeIO();
getLock( false );
return result;
}
IOWorkLoop*
AppleKiwiATA::getWorkLoop() const
{
DLOG("AppleKiwiATA::getWorkLoop\n");
IOWorkLoop* wl = _workLoop;
if (!wl)
{
wl = IOWorkLoop::workLoop();
if (!wl)
return 0;
}
return wl;
}
void
AppleKiwiATA::handleTimeout( void )
{
if( isBusOnline == false)
{
return;
}
getLock(true);
stopDMA();
volatile UInt8 statusByte = *_tfStatusCmdReg;
OSSynchronizeIO();
statusByte++;
super::handleTimeout();
getLock( false);
}
bool
AppleKiwiATA::configureTFPointers(void)
{
DLOG("AppleKiwiATA config TF Pointers \n");
OSString* locationCompare = OSString::withCString( "1" );
if( locationCompare->isEqualTo( getProvider()->getLocation() ))
{
busChildNumber = 1;
}
locationCompare->release();
locationCompare = NULL;
DLOG("AppleKiwiATA busChildNumber = %d, string = %1s \n", busChildNumber, getProvider()->getLocation());
ioBaseAddrMap[0] = getProvider()->mapDeviceMemoryWithIndex( busChildNumber == 0 ? 0 : 2 );
if ( ioBaseAddrMap[0] == NULL )
{
return false;
}
volatile UInt8* baseAddress = (volatile UInt8*)ioBaseAddrMap[0]->getVirtualAddress();
_tfDataReg = (volatile UInt16*) (baseAddress + 0x00);
_tfFeatureReg = baseAddress + 0x01;
_tfSCountReg = baseAddress + 0x02;
_tfSectorNReg = baseAddress + 0x03;
_tfCylLoReg = baseAddress + 0x04;
_tfCylHiReg = baseAddress + 0x05;
_tfSDHReg = baseAddress + 0x06;
_tfStatusCmdReg = baseAddress + 0x07;
DLOG("AppleKiwiATA base address 0 = %lX \n", baseAddress);
ioBaseAddrMap[1] = getProvider()->mapDeviceMemoryWithIndex( busChildNumber == 0 ? 1 : 3 );
if ( ioBaseAddrMap[1] == NULL )
{
return false;
}
baseAddress = (volatile UInt8 *)ioBaseAddrMap[1]->getVirtualAddress();
_tfAltSDevCReg = baseAddress + 2;
DLOG("AppleKiwiATA base address 1 = %lX altStatus at %lx \n", baseAddress, _tfAltSDevCReg);
ioBaseAddrMap[2] = getProvider()->mapDeviceMemoryWithIndex( 4 );
if ( ioBaseAddrMap[2] == NULL )
{
return false;
}
volatile UInt8* bmAddress = (volatile UInt8*)ioBaseAddrMap[2]->getVirtualAddress();
if( busChildNumber == 1)
{
bmAddress += 0x08; }
DLOG("AppleKiwiATA base address 2 = %lX \n", bmAddress);
_bmCommandReg = bmAddress;
_bmStatusReg = bmAddress + 2;
_bmPRDAddresReg = (volatile UInt32*) (bmAddress + 4);
ioBaseAddrMap[3] = getProvider()->mapDeviceMemoryWithIndex( 5 );
if ( ioBaseAddrMap[3] == NULL )
{
return false;
}
volatile UInt8* bar5Address = (volatile UInt8*)ioBaseAddrMap[3]->getVirtualAddress();
DLOG("AppleKiwiATA base address 5 = %lx \n", bar5Address);
if( busChildNumber == 1)
{
globalControlReg = (volatile UInt32*) (bar5Address + 0x1208); timingAReg0 =(volatile UInt32*) (bar5Address + 0x120c);
timingBReg0 =(volatile UInt32*) (bar5Address + 0x1210);
timingAReg1 =(volatile UInt32*) (bar5Address + 0x1214);
timingBReg1 =(volatile UInt32*) (bar5Address + 0x1218);
} else {
globalControlReg =(volatile UInt32*) (bar5Address + 0x1108); timingAReg0 =(volatile UInt32*) (bar5Address + 0x110c);
timingBReg0 =(volatile UInt32*) (bar5Address + 0x1110);
timingAReg1 =(volatile UInt32*) (bar5Address + 0x1114);
timingBReg1 =(volatile UInt32*) (bar5Address + 0x1118);
}
*globalControlReg &= (~ 0x00000800); OSSynchronizeIO();
busTimings[0].ataUltraDMASpeedMode = 32;
busTimings[1].ataUltraDMASpeedMode = 32;
DLOG("AppleKiwiATA GCR = %lx \n", *globalControlReg);
IOSleep(50);
DLOG("AppleKiwiATA configTFPointers done\n");
return true;
}
IOReturn
AppleKiwiATA::provideBusInfo( IOATABusInfo* infoOut)
{
if( infoOut == 0)
{
DLOG("AppleKiwiATA nil pointer in provideBusInfo\n");
return -1;
}
infoOut->zeroData();
infoOut->setSocketType( kMediaBaySocket );
infoOut->setPIOModes( kATASupportedPIOModes);
infoOut->setDMAModes( kATASupportedMultiDMAModes );
if( mode6Capable )
{
infoOut->setUltraModes( kATAAddSupport133);
DLOG( "AppleKiwiATA: indicating udma mode 6 available\n");
} else {
infoOut->setUltraModes( kATASupportedUltraDMAModes );
}
infoOut->setExtendedLBA( true ); infoOut->setMaxBlocksExtended( 0x0100 );
UInt8 units = 0;
if( _devInfo[0].type != kUnknownATADeviceType )
units++;
if( _devInfo[1].type != kUnknownATADeviceType )
units++;
infoOut->setUnits( units);
return kATANoErr;
}
IOReturn
AppleKiwiATA::getConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("AppleKiwiATA 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
AppleKiwiATA::selectConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("AppleKiwiATA bad param in setConfig\n");
return -1;
}
if( ( configRequest->getPIOMode() & kATASupportedPIOModes ) == 0x00 )
{
DLOG("AppleKiwiATA setConfig PIO mode not supported\n");
return kATAModeNotSupported;
}
UInt16 ultraSupported = kATASupportedUltraDMAModes;
if( mode6Capable )
{
ultraSupported = kATAAddSupport133;
}
if( configRequest->getUltraMode() & ~ultraSupported )
{
DLOG("AppleKiwiATA setConfig no ultra\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() & ~kATASupportedMultiDMAModes )
{
DLOG("AppleKiwiATA setConfig DMA mode not supported\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() > 0x0000
&& configRequest->getUltraMode() > 0x0000 )
{
DLOG("AppleKiwiATA err, only one DMA class allowed in config select\n");
return kATAModeNotSupported;
}
_devInfo[unitNumber].packetSend = configRequest->getPacketConfig();
DLOG("AppleKiwiATA setConfig packetConfig = %d\n", _devInfo[unitNumber].packetSend );
return selectIOTimerValue(configRequest, unitNumber);;
}
IOReturn
AppleKiwiATA::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();
IOLog("AppleKiwiATA: PIO Mode %d UDMA mode %d \n",
bitSigToNumeric(busTimings[unitNumber].ataPIOSpeedMode),
bitSigToNumeric(busTimings[unitNumber].ataUltraDMASpeedMode) );
return getConfig( configRequest, unitNumber);
}
void
AppleKiwiATA::selectIOTiming( ataUnitID unit )
{
if(mode6Capable)
{
UInt32 bTiming = kPartBTiming33LE;
switch(bitSigToNumeric(busTimings[unit].ataUltraDMASpeedMode) )
{
case 2:
bTiming = kPartBTiming33LE;
break;
case 4:
bTiming = kPartBTiming66LE;
break;
case 5:
bTiming = kPartBTiming100LE;
break;
case 6:
bTiming = kPartBTiming133LE;
break;
default:
IOLog("AppleKiwiATA: error setting timing registers\n");
break;
}
if( unit == 0)
{
*timingAReg0 = kPartATimingLE;
*timingBReg0 = bTiming;
} else {
*timingAReg1 = kPartATimingLE;
*timingBReg1 = bTiming;
}
OSSynchronizeIO();
}
return;
}
IOReturn
AppleKiwiATA::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;
}
return err;
}
IOReturn
AppleKiwiATA::issueCommand( void )
{
IOReturn result = super::issueCommand();
if( result == kATANoErr
&& (_currentCommand->getFlags() & mATAFlagUseDMA ) == mATAFlagUseDMA )
{
activateDMAEngine();
}
return result;
}
#pragma mark hot-removal
IOReturn
AppleKiwiATA::message (UInt32 type, IOService* provider, void* argument)
{
switch( type )
{
case kATARemovedEvent:
DLOG( "AppleKiwiATA got remove event.\n");
if(isBusOnline == true)
{
isBusOnline = false;
_queueState = IOATAController::kQueueLocked;
_devIntSrc->disable();
stopTimer();
_workLoop->removeEventSource(_devIntSrc);
_workLoop->removeEventSource(_timer);
getLock(true);
stopDMA();
*globalControlReg |= 0x00000800; OSSynchronizeIO();
handleQueueFlush();
if( _currentCommand != 0)
{
DLOG( "AppleKiwiATA Calling cleanup bus.\n");
_cmdGate->runAction( (IOCommandGate::Action)
&AppleKiwiATA::cleanUpAction,
0, 0, 0, 0);
}
_workLoop->removeEventSource(_cmdGate);
getLock(false);
DLOG( "AppleKiwiATA notify the clients.\n");
terminate( );
getProvider()->message( 'ofln', 0 );
}
break;
default:
DLOG( "AppleKiwiATA got some other event = %d\n", (int) type);
return super::message(type, provider, argument);
break;
}
return kATANoErr;
}
IOReturn
AppleKiwiATA::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
AppleKiwiATA::checkTimeout( void )
{
if( isBusOnline == false )
{
return true;
}
return super::checkTimeout();
}
IOReturn
AppleKiwiATA::executeCommand(IOATADevice* nub, IOATABusCommand* command)
{
if( isBusOnline == false)
{
return kIOReturnOffline;
}
return super::executeCommand(nub, command);
}
IOReturn
AppleKiwiATA::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;
}
getLock(true);
IOReturn result = super::handleCommand( param0, param1, param2, param3 );
getLock( false );
return result;
}
void
AppleKiwiATA::cleanUpAction(OSObject * owner,
void * arg0,
void * arg1,
void * ,
void * )
{
AppleKiwiATA* self = (AppleKiwiATA*) owner;
self->cleanUpBus();
}
void
AppleKiwiATA::cleanUpBus(void)
{
if( _currentCommand != 0)
{
_currentCommand->setResult(kIOReturnOffline);
_currentCommand->executeCallback();
_currentCommand = 0;
}
}