#include "HeathrowATA.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 "heathrow-ata"
#define kATASupportedPIOModes 0x001F // modes 4, 3, 2, 1, and 0
#define kATASupportedMultiDMAModes 0x0007 // modes 2, 1, and 0
#define kHeathrowPIOCycleEntries 11
#define kHeathrowMultiDMACycleEntries 9
#define kATAMaxPIOMode 4
#define kATAMaxMultiDMAMode 2
#pragma mark -IOService Overrides -
#define super MacIOATA
OSDefineMetaClassAndStructors( HeathrowATA, MacIOATA )
bool
HeathrowATA::init(OSDictionary* properties)
{
DLOG("HeathrowATA init start\n");
if (super::init(properties) == false)
{
DLOG("HeathrowATA: super::init() failed\n");
return false;
}
busTimings[0].cycleRegValue = busTimings[1].cycleRegValue = 0x00000526 | 0x00074000;
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;
DLOG("HeathrowATA init done\n");
return true;
}
IOService*
HeathrowATA::probe(IOService* provider, SInt32* score)
{
OSData *compatibleEntry;
DLOG("HeathrowATA starting probe\n");
compatibleEntry = OSDynamicCast( OSData, provider->getProperty( "compatible" ) );
if ( compatibleEntry == 0 )
{
DLOG("HeathrowATA failed getting compatible property\n");
return 0;
}
if ( compatibleEntry->isEqualTo( kCompatibleString, sizeof(kCompatibleString)-1 ) == false )
{
DLOG("HeathrowATA compatible property doesn't match\n");
return 0;
}
return this;
}
bool
HeathrowATA::start(IOService *provider)
{
DLOG("HeathrowATA::start() begin\n");
if( ! OSDynamicCast(AppleMacIODevice, provider ) )
{
DLOG("HeathrowATA provider not AppleMacIODevice!\n");
return false;
}
ATADeviceNub* newNub=0L;
if (!super::start( provider))
{
DLOG("HeathrowATA: super::start() failed\n");
return false;
}
DLOG("HeathrowATA::start() done\n");
for( UInt32 i = 0; i < 2; i++)
{
if( _devInfo[i].type != kUnknownATADeviceType )
{
DLOG("HeathrowATA creating nub\n");
newNub = ATADeviceNub::ataDeviceNub( (IOATAController*)this, (ataUnitID) i, _devInfo[i].type );
if( newNub )
{
DLOG("HeathrowATA attach nub\n");
newNub->attach(this);
_nub[i] = (IOATADevice*) newNub;
DLOG("HeathrowATA register nub\n");
newNub->registerService();
newNub = 0L;
}
}
}
return true;
}
void
HeathrowATA::free()
{
super::free();
}
IOWorkLoop*
HeathrowATA::getWorkLoop() const
{
DLOG("HeathrowATA::getWorkLoop\n");
IOWorkLoop* wl = _workLoop;
if (!wl)
{
wl = IOWorkLoop::workLoop();
if (!wl)
return 0;
}
return wl;
}
bool
HeathrowATA::configureTFPointers(void)
{
DLOG("HeathrowATA config TF Pointers \n");
if( ! super::configureTFPointers() )
{
return false;
}
DLOG("HeathrowATA setting default timing \n");
OSWriteSwapInt32(_timingConfigReg, 0, busTimings[0].cycleRegValue );
DLOG("HeathrowATA configTFPointers done\n");
return true;
}
IOReturn
HeathrowATA::provideBusInfo( IOATABusInfo* infoOut)
{
if( infoOut == 0)
{
DLOG("HeathrowATA nil pointer in provideBusInfo\n");
return -1;
}
infoOut->zeroData();
if( isMediaBay )
{
infoOut->setSocketType( kMediaBaySocket );
} else {
infoOut->setSocketType( kInternalATASocket ); }
infoOut->setPIOModes( kATASupportedPIOModes);
infoOut->setDMAModes( kATASupportedMultiDMAModes );
infoOut->setUltraModes( 0x00 );
UInt8 units = 0;
if( _devInfo[0].type != kUnknownATADeviceType )
units++;
if( _devInfo[1].type != kUnknownATADeviceType )
units++;
infoOut->setUnits( units);
return kATANoErr;
}
IOReturn
HeathrowATA::getConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("HeathrowATA bad param in getConfig\n");
return -1;
}
configRequest->setUltraMode(0x00);
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 );
return kATANoErr;
}
IOReturn
HeathrowATA::selectConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("HeathrowATA bad param in setConfig\n");
return -1;
}
if( configRequest->getUltraMode() )
{
DLOG("HeathrowATA setConfig no ultra\n");
return kATAModeNotSupported;
}
if( ( configRequest->getPIOMode() & kATASupportedPIOModes ) == 0x00 )
{
DLOG("HeathrowATA setConfig PIO mode not supported\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() & ~kATASupportedMultiDMAModes )
{
DLOG("HeathrowATA setConfig DMA mode not supported\n");
return kATAModeNotSupported;
}
_devInfo[unitNumber].packetSend = configRequest->getPacketConfig();
DLOG("HeathrowATA setConfig packetConfig = %ld\n", _devInfo[unitNumber].packetSend );
return selectIOTimerValue(configRequest, unitNumber);
}
IOReturn
HeathrowATA::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 PIOCycleValue[kHeathrowPIOCycleEntries] =
{
0x00000400, 0x00000526, 0x00000085, 0x00000046, 0x00000045, 0x00000025, 0x00000025, 0x00000025, 0x00000025, 0x00000025, 0x00000025 };
static const UInt16 PIOCycleTime[ kHeathrowPIOCycleEntries ]=
{
2070, 600, 383, 360, 330, 300, 270, 240, 239, 180, 120, };
static const UInt32 MultiDMACycleValue[kHeathrowMultiDMACycleEntries] =
{
0x00000000, 0x00074000, 0x00053000, 0x00242000, 0x00032000, 0x00231800, 0x00021800, 0x00221000, 0x00211000 };
static const UInt16 MultiDMACycleTime[kHeathrowMultiDMACycleEntries] =
{
1950, 480, 360, 270, 240, 210, 180, 150, 120 };
UInt32 pioConfigBits = PIOCycleValue[0];
UInt32 pioModeNumber = bitSigToNumeric( configRequest->getPIOMode());
if( pioModeNumber > kATAMaxPIOMode )
{
DLOG("HeathrowATA pio mode out of range\n");
return kATAModeNotSupported;
}
UInt32 pioCycleTime = configRequest->getPIOCycleTime();
if( pioCycleTime < MinPIOCycle[ pioModeNumber ] )
{
pioCycleTime = MinPIOCycle[ pioModeNumber ];
}
for( int i = kHeathrowPIOCycleEntries - 1; i >= 0; i--)
{
if( pioCycleTime <= PIOCycleTime[ i ] )
{
pioConfigBits = PIOCycleValue[i];
break;
}
}
UInt32 dmaConfigBits = MultiDMACycleValue[0];
UInt32 dmaModeNumber = bitSigToNumeric( configRequest->getDMAMode() );
if( dmaModeNumber > kATAMaxMultiDMAMode )
{
dmaModeNumber = 0;
}
UInt32 dmaCycleTime = configRequest->getDMACycleTime();
if( dmaCycleTime < MinMultiDMACycle[ dmaModeNumber ] )
{
dmaCycleTime = MinMultiDMACycle[ dmaModeNumber ];
}
for( int i = kHeathrowMultiDMACycleEntries - 1; i >= 0; i--)
{
if( dmaCycleTime <= MultiDMACycleTime[ i ] )
{
dmaConfigBits = MultiDMACycleValue[i];
break;
}
}
busTimings[unitNumber].cycleRegValue = dmaConfigBits | pioConfigBits;
busTimings[unitNumber].ataPIOSpeedMode = configRequest->getPIOMode();
busTimings[unitNumber].ataPIOCycleTime = pioCycleTime;
busTimings[unitNumber].ataMultiDMASpeed = configRequest->getDMAMode();
busTimings[unitNumber].ataMultiCycleTime = dmaCycleTime;
DLOG("HeathrowATA PIO mode %x at %ld ns selected for device: %x\n", (int)pioModeNumber, pioCycleTime, (int)unitNumber);
DLOG("HeathrowATA DMA mode %x at %ld ns selected for device: %x\n", (int)dmaModeNumber, dmaCycleTime, (int)unitNumber);
return getConfig( configRequest, unitNumber);
}
void
HeathrowATA::selectIOTiming( ataUnitID unit )
{
DLOG("heathrowATA setting timing config %lx on unit %d\n", busTimings[unit].cycleRegValue, unit);
OSWriteSwapInt32(_timingConfigReg, 0, busTimings[unit].cycleRegValue );
}
UInt32
HeathrowATA::scanForDrives( void )
{
IOReturn err = kATANoErr;
err = softResetBus( false );
return super::scanForDrives();
}
IOReturn
HeathrowATA::synchronousIO(void)
{
_devIntSrc->disable();
IOReturn result = super::synchronousIO();
_devIntSrc->enable();
return result;
}
IOReturn
HeathrowATA::handleDeviceInterrupt(void)
{
if( _dmaIntExpected != true )
{
volatile UInt8 status = *_tfStatusCmdReg;
OSSynchronizeIO();
status++; }
return super::handleDeviceInterrupt();
}
IOReturn
HeathrowATA::selectDevice( ataUnitID unit )
{
_devIntSrc->disable();
IOReturn result = super::selectDevice( unit);
_devIntSrc->enable();
return result;
}