#include "KeyLargoATA.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/platform/AppleMacIODevice.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 "keylargo-ata"
#define kModelPropertyKey "model"
#define kCableTypeKey "cable-type"
#define kModel4String "ata-4"
#define k80ConductorString "80-conductor"
#define kExtendedLBAKey "lba-48"
#define kATASupportedPIOModes 0x001F // modes 4, 3, 2, 1, and 0
#define kATASupportedMultiDMAModes 0x0007 // modes 2, 1, and 0
#define kATASupportedUltraDMAModes 0x001f // modes 4, 3, 2, 1, and 0
#define kKeyLargoPIOCycleEntries 11
#define kKeyLargoMultiDMACycleEntries 9
#define kKeyLargoUltraDMACycleEntries 5
#define kATAMaxPIOMode 4
#define kATAMaxMultiDMAMode 2
#define kATAMaxUltraDMAMode 4
#pragma mark -IOService Overrides -
#define super MacIOATA
OSDefineMetaClassAndStructors( KeyLargoATA, MacIOATA )
bool
KeyLargoATA::init(OSDictionary* properties)
{
DLOG("KeyLargoATA init start\n");
if (super::init(properties) == false)
{
DLOG("KeyLargoATA: super::init() failed\n");
return false;
}
isUltraCell = false;
cableIs80Conductor = false;
_needsResync = false;
isExtLBA = false;
DLOG("KeyLargoATA init done\n");
return true;
}
IOService*
KeyLargoATA::probe(IOService* provider, SInt32* score)
{
OSData *compatibleEntry;
DLOG("KeyLargoATA starting probe\n");
compatibleEntry = OSDynamicCast( OSData, provider->getProperty( "compatible" ) );
if ( compatibleEntry == 0 )
{
DLOG("KeyLargoATA failed getting compatible property\n");
return 0;
}
if ( compatibleEntry->isEqualTo( kCompatibleString, sizeof(kCompatibleString)-1 ) == false )
{
DLOG("KeyLargoATA compatible property doesn't match\n");
return 0;
}
OSData *registryEntry;
registryEntry = OSDynamicCast( OSData, provider->getProperty( kModelPropertyKey ) );
if( registryEntry == 0)
{
DLOG("KeyLargoATA unknown model property.\n");
return 0;
}
isUltraCell = registryEntry->isEqualTo( kModel4String, sizeof(kModel4String)-1 );
if( isUltraCell )
{
registryEntry = OSDynamicCast( OSData, provider->getProperty( kCableTypeKey ) );
if( registryEntry == 0)
{
DLOG("KeyLargoATA unknown cable-type.\n");
return 0;
}
cableIs80Conductor = registryEntry->isEqualTo( k80ConductorString, sizeof(k80ConductorString)-1 );
}
registryEntry = OSDynamicCast( OSData, provider->getProperty( kExtendedLBAKey ) );
if( registryEntry != 0)
{
DLOG("KeyLargoATA Extended LBA.\n");
isExtLBA = true;
}
if( isUltraCell )
{
busTimings[0].cycleRegValue = busTimings[1].cycleRegValue = 0x0000038C | 0x00084000;
} else {
busTimings[0].cycleRegValue = busTimings[1].cycleRegValue = 0x00000526 | 0x00084000;
}
busTimings[0].ataPIOSpeedMode = busTimings[1].ataPIOSpeedMode = 0x01 ; busTimings[0].ataPIOCycleTime = busTimings[1].ataPIOCycleTime = 600 ; busTimings[0].ataMultiDMASpeed = busTimings[1].ataMultiDMASpeed = 0x01; busTimings[0].ataMultiCycleTime = busTimings[1].ataMultiCycleTime = 480; busTimings[0].ataUltraDMASpeedMode = busTimings[1].ataUltraDMASpeedMode = 0x00;
return this;
}
bool
KeyLargoATA::start(IOService *provider)
{
DLOG("KeyLargoATA::start() begin\n");
if( ! OSDynamicCast(AppleMacIODevice, provider ) )
{
DLOG("KeyLargoATA provider not AppleMacIODevice!\n");
return false;
}
if( ! provider->open(this) )
{
DLOG("KeyLargo provider did not open\n");
return false;
}
ATADeviceNub* newNub=0L;
if (!super::start( provider))
{
DLOG("KeyLargoATA: super::start() failed\n");
provider->close(this);
return false;
}
DLOG("KeyLargoATA::start() done\n");
for( UInt32 i = 0; i < 2; i++)
{
if( _devInfo[i].type != kUnknownATADeviceType )
{
DLOG("KeyLargoATA creating nub\n");
newNub = ATADeviceNub::ataDeviceNub( (IOATAController*)this, (ataUnitID) i, _devInfo[i].type );
if( newNub )
{
DLOG("KeyLargoATA attach nub\n");
newNub->attach(this);
_nub[i] = (IOATADevice*) newNub;
DLOG("KeyLargoATA register nub\n");
newNub->registerService();
newNub = 0L;
}
}
}
return true;
}
void
KeyLargoATA::free()
{
super::free();
}
IOWorkLoop*
KeyLargoATA::getWorkLoop() const
{
DLOG("KeyLargoATA::getWorkLoop\n");
IOWorkLoop* wl = _workLoop;
if (!wl)
{
wl = IOWorkLoop::workLoop();
if (!wl)
return 0;
}
return wl;
}
bool
KeyLargoATA::configureTFPointers(void)
{
DLOG("KeyLargoATA config TF Pointers \n");
if( ! super::configureTFPointers() )
{
return false;
}
DLOG("KeyLargoATA setting default timing \n");
OSWriteSwapInt32(_timingConfigReg, 0, busTimings[0].cycleRegValue );
DLOG("KeyLargoATA configTFPointers done\n");
return true;
}
IOReturn
KeyLargoATA::provideBusInfo( IOATABusInfo* infoOut)
{
if( infoOut == 0)
{
DLOG("KeyLargoATA nil pointer in provideBusInfo\n");
return -1;
}
infoOut->zeroData();
if( isMediaBay )
{
infoOut->setSocketType( kMediaBaySocket );
} else {
infoOut->setSocketType( kInternalATASocket ); }
infoOut->setPIOModes( kATASupportedPIOModes);
infoOut->setDMAModes( kATASupportedMultiDMAModes );
if( isUltraCell )
{
if( cableIs80Conductor )
{
infoOut->setUltraModes( kATASupportedUltraDMAModes );
}else{
infoOut->setUltraModes( 0x0007 ); }
} else {
infoOut->setUltraModes( 0x00 );
}
if(isExtLBA )
{
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
KeyLargoATA::getConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("KeyLargoATA 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
KeyLargoATA::selectConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("KeyLargoATA bad param in setConfig\n");
return -1;
}
if( ( configRequest->getPIOMode() & kATASupportedPIOModes ) == 0x00 )
{
DLOG("KeyLargoATA setConfig PIO mode not supported\n");
return kATAModeNotSupported;
}
UInt16 ultraSupported = 0x0000;
if( isUltraCell )
{
ultraSupported = cableIs80Conductor ? kATASupportedUltraDMAModes : 0x0007;
}
if( configRequest->getUltraMode() & ~ultraSupported )
{
DLOG("KeyLargoATA setConfig no ultra\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() & ~kATASupportedMultiDMAModes )
{
DLOG("KeyLargoATA setConfig DMA mode not supported\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() > 0x0000
&& configRequest->getUltraMode() > 0x0000 )
{
DLOG("KeylargoATA err, only one DMA class allowed in config select\n");
return kATAModeNotSupported;
}
_devInfo[unitNumber].packetSend = configRequest->getPacketConfig();
DLOG("KeyLargoATA setConfig packetConfig = %ld\n", _devInfo[unitNumber].packetSend );
return selectIOTimerValue(configRequest, unitNumber);
}
IOReturn
KeyLargoATA::selectIOTimerValue( IOATADevConfig* configRequest, UInt32 unitNumber)
{
static const UInt16 MinPIOCycle[kATAMaxPIOMode + 1] =
{
600, 383, 240, 180, 120 };
static const UInt16 MinMultiDMACycle[kATAMaxMultiDMAMode + 1] =
{
480, 150, 120 };
static const UInt32 PIOCycleValue33[kKeyLargoPIOCycleEntries] =
{
0x00000400, 0x00000526, 0x00000085, 0x00000046, 0x00000045, 0x00000025, 0x00000025, 0x00000025, 0x00000025, 0x00000025, 0x00000025 };
UInt32 PIOCycleValue66[kKeyLargoPIOCycleEntries] =
{
0x000003FF, 0x0000038C, 0x0000020A, 0x0000018C, 0x0000018A, 0x00000156, 0x00000148, 0x00000127, 0x00000108, 0x000000C6, 0x00000065 };
static const UInt16 PIOCycleTime[ kKeyLargoPIOCycleEntries ]=
{
2070, 600, 383, 360, 330, 300, 270, 240, 239, 180, 120, };
static const UInt32 MultiDMACycleValue33[kKeyLargoMultiDMACycleEntries] =
{
0x00000000, 0x00084000, 0x00063000, 0x00052800, 0x00042000, 0x00041800, 0x00031800, 0x00021800, 0x00011800 };
static const UInt32 MultiDMACycleValue66[kKeyLargoMultiDMACycleEntries] =
{
0x000FFC00, 0x00084000, 0x00063000, 0x0004A400, 0x00042000, 0x00039C00, 0x00031800, 0x00029800, 0x00019400 };
static const UInt16 MultiDMACycleTime[kKeyLargoMultiDMACycleEntries] =
{
1950, 480, 360, 270, 240, 210, 180, 150, 120 };
static const UInt16 AllowUltraDMACycle[kATAMaxUltraDMAMode + 1] =
{
120, 90, 60, 45, 30 };
static const UInt32 UltraDMACycleValue66[kKeyLargoUltraDMACycleEntries] =
{
0x19100000, 0x14D00000, 0x10900000, 0x0C700000, 0x0C500000 };
static const UInt16 UltraDMACycleTime[kKeyLargoUltraDMACycleEntries] =
{
120, 90, 60, 45, 30 };
UInt32 pioConfigBits = isUltraCell? PIOCycleValue66[0] : PIOCycleValue33[0];
UInt32 pioModeNumber = bitSigToNumeric( configRequest->getPIOMode());
if( pioModeNumber > kATAMaxPIOMode )
{
DLOG("KeyLargoATA pio mode out of range\n");
return kATAModeNotSupported;
}
UInt32 pioCycleTime = configRequest->getPIOCycleTime();
if( pioCycleTime < MinPIOCycle[ pioModeNumber ] )
{
pioCycleTime = MinPIOCycle[ pioModeNumber ];
}
for( int i = kKeyLargoPIOCycleEntries - 1; i >= 0; i--)
{
if( pioCycleTime <= PIOCycleTime[ i ] )
{
pioConfigBits = isUltraCell? PIOCycleValue66[i] : PIOCycleValue33[i];
break;
}
}
UInt32 dmaConfigBits = isUltraCell? MultiDMACycleValue66[0] : MultiDMACycleValue33[0];
UInt32 dmaModeNumber = 0;
UInt32 dmaCycleTime = 0;
if( configRequest->getDMAMode() )
{
dmaModeNumber = bitSigToNumeric( configRequest->getDMAMode() );
if( dmaModeNumber > kATAMaxMultiDMAMode )
{
dmaModeNumber = 0;
}
dmaCycleTime = configRequest->getDMACycleTime();
if( dmaCycleTime < MinMultiDMACycle[ dmaModeNumber ] )
{
dmaCycleTime = MinMultiDMACycle[ dmaModeNumber ];
}
for( int i = kKeyLargoMultiDMACycleEntries - 1; i >= 0; i--)
{
if( dmaCycleTime <= MultiDMACycleTime[ i ] )
{
dmaConfigBits = isUltraCell? MultiDMACycleValue66[i] : MultiDMACycleValue33[i];
break;
}
}
}
UInt32 ultraModeNumber = 0;
if( configRequest->getUltraMode() )
{
ultraModeNumber = bitSigToNumeric( configRequest->getUltraMode() );
if( ultraModeNumber > kATAMaxUltraDMAMode )
{
ultraModeNumber = cableIs80Conductor ? kATAMaxUltraDMAMode : 2;
}
dmaConfigBits = UltraDMACycleValue66[ ultraModeNumber ];
}
if( isUltraCell )
{
}
busTimings[unitNumber].cycleRegValue = dmaConfigBits | pioConfigBits;
busTimings[unitNumber].ataPIOSpeedMode = configRequest->getPIOMode();
busTimings[unitNumber].ataPIOCycleTime = pioCycleTime;
busTimings[unitNumber].ataMultiDMASpeed = configRequest->getDMAMode();
busTimings[unitNumber].ataMultiCycleTime = dmaCycleTime;
busTimings[unitNumber].ataUltraDMASpeedMode = configRequest->getUltraMode();
DLOG("KeyLargoATA PIO mode %x at %ld ns selected for device: %x\n", (int)pioModeNumber, pioCycleTime, (int)unitNumber);
DLOG("KeyLargoATA DMA mode %x at %ld ns selected for device: %x\n", (int)dmaModeNumber, dmaCycleTime, (int)unitNumber);
DLOG("KeyLargoATA Ultra mode %x at %ld ns selected for device: %x\n", (int)ultraModeNumber, UltraDMACycleTime[ultraModeNumber], (int)unitNumber);
DLOG("KeyLargo cycle value = %x for unit: %x\n", busTimings[unitNumber].cycleRegValue, unitNumber);
return getConfig( configRequest, unitNumber);
}
void
KeyLargoATA::selectIOTiming( ataUnitID unit )
{
if( isUltraCell == true
&& ( ( _currentCommand->getFlags() & (mATAFlagUseDMA | mATAFlagIORead) ) == (mATAFlagUseDMA | mATAFlagIORead))
&& (( busTimings[unit].cycleRegValue & 0x00100000 ) == 0x00100000 ) )
{
OSWriteSwapInt32(_timingConfigReg, 0, busTimings[unit].cycleRegValue + 0x00800000 );
} else {
OSWriteSwapInt32(_timingConfigReg, 0, busTimings[unit].cycleRegValue );
}
}
IOReturn
KeyLargoATA::handleDeviceInterrupt(void)
{
if( !_currentCommand )
{
volatile UInt8 status = *_tfStatusCmdReg;
OSSynchronizeIO();
status++; return 0;
}
UInt32 intStatus = OSReadLittleInt32( (void*) _tfDataReg, 0x300);
OSSynchronizeIO();
if( !(intStatus & 0x40000000)
&& !_needsResync )
{
volatile UInt8 status = *_tfStatusCmdReg;
OSSynchronizeIO();
status++;
return 0;
}
return super::handleDeviceInterrupt();
}
IOReturn
KeyLargoATA::selectDevice( ataUnitID unit )
{
_devIntSrc->disable();
IOReturn result = super::selectDevice( unit);
_devIntSrc->enable();
return result;
}
IOReturn
KeyLargoATA::synchronousIO(void)
{
_devIntSrc->disable();
IOReturn result = super::synchronousIO();
UInt32 intStatus = OSReadLittleInt32( (void*) _tfDataReg, 0x300);
OSSynchronizeIO();
while( (intStatus & 0x40000000) )
{
volatile UInt8 status = *_tfStatusCmdReg;
OSSynchronizeIO();
status++;
intStatus = OSReadLittleInt32( (void*) _tfDataReg, 0x300);
OSSynchronizeIO();
}
_devIntSrc->enable();
return result;
}
IOReturn
KeyLargoATA::handleBusReset(void)
{
bool isATAPIReset = ((_currentCommand->getFlags() & mATAFlagProtocolATAPI) != 0);
bool doATAPI[2];
IOReturn err = kATANoErr;
UInt8 index;
UInt8 statCheck;
DLOG("IOATA bus reset start.\n");
doATAPI[0] = doATAPI[1] = false;
if(isATAPIReset)
{
doATAPI[_currentCommand->getUnit()] = true;
}else if (!isUltraCell) {
doATAPI[0] = doATAPI[1] = true;
}
for(index=0;index<2;index++)
{
if( doATAPI[index] && _devInfo[index].type == kATAPIDeviceType)
{
OSSynchronizeIO();
*_tfSDHReg = mATASectorSize + (index << 4);
OSSynchronizeIO();
statCheck = *_tfAltSDevCReg;
err = softResetBus(true);
}
}
if(isATAPIReset)
{
executeEventCallouts( kATAPIResetEvent, _currentCommand->getUnit() );
}
if(!isATAPIReset)
{
err = softResetBus();
executeEventCallouts( kATAResetEvent, kATAInvalidDeviceID );
}
_currentCommand->state = IOATAController::kATAComplete;
DLOG("IOATA bus reset done.\n");
completeIO( err );
return err;
}
IOATAController::transState
KeyLargoATA::determineATAPIState(void)
{
IOATAController::transState drivePhase = super::determineATAPIState();
if( _currentCommand->state > drivePhase
|| _currentCommand->state == kATAStarted )
{
return (IOATAController::transState) _currentCommand->state;
}
return drivePhase;
}
void
KeyLargoATA::processDMAInterrupt(void)
{
_needsResync = _resyncInterrupts;
super::processDMAInterrupt();
_needsResync = false;
}