#include "CMD646ATA.h"
#include "CMD646Root.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 "cmd646-ata"
#define kModelPropertyKey "name"
#define kModel4String "ata-4"
#define kATASupportedPIOModes 0x001F // modes 4, 3, 2, 1, and 0
#define kATASupportedMultiDMAModes 0x0007 // modes 2, 1, and 0
#define kATASupportedUltraDMAModes 0x0007 // modes 2, 1, and 0
#define kPIOCycleEntries 12
#define kMultiDMACycleEntries 9
#define kUltraDMACycleEntries 3
#define kATAMaxPIOMode 4
#define kATAMaxMultiDMAMode 2
#define kATAMaxUltraDMAMode 2
#pragma mark -IOService Overrides -
#define super IOPCIATA
OSDefineMetaClassAndStructors ( CMD646ATA, IOPCIATA )
bool
CMD646ATA::init(OSDictionary* properties)
{
DLOG("CMD646ATA init start\n");
for( int i = 0; i < 3; i++)
{
ioBaseAddrMap[ i ] = 0;
}
if (super::init(properties) == false)
{
DLOG("CMD646ATA: super::init() failed\n");
return false;
}
DLOG("CMD646ATA init done\n");
return true;
}
IOService*
CMD646ATA::probe(IOService* provider, SInt32* score)
{
OSData *compatibleEntry;
DLOG("CMD646ATA starting probe\n");
compatibleEntry = OSDynamicCast( OSData, provider->getProperty( "compatible" ) );
if ( compatibleEntry == 0 )
{
DLOG("CMD646ATA failed getting compatible property\n");
return 0;
}
if ( compatibleEntry->isEqualTo( kCompatibleString, sizeof(kCompatibleString)-1 ) == false )
{
DLOG("CMD646ATA compatible property doesn't match\n");
return 0;
}
OSData *registryEntry;
registryEntry = OSDynamicCast( OSData, provider->getProperty( kModelPropertyKey ) );
if( registryEntry == 0)
{
DLOG("CMD646ATA unknown model property.\n");
return 0;
}
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;
busTimings[0].pioActiveRecoveryValue = busTimings[1].pioActiveRecoveryValue = 0x6d;
busTimings[0].dmaActiveRecoveryValue = busTimings[1].dmaActiveRecoveryValue = 0x88;
busTimings[0].ultraTimingValue = busTimings[1].ultraTimingValue = 0x00;
return this;
}
bool
CMD646ATA::start(IOService *provider)
{
DLOG("CMD646ATA::start() begin\n");
CMD646Device* myProvider = OSDynamicCast( CMD646Device, provider);
if( myProvider == 0)
{
DLOG("CMD646 provider not CMD646 Device nub\n");
return 0;
}
_cmdRoot = myProvider->getRootCMD();
_pciNub = (IOPCIDevice*) myProvider->getPCINub();
if( ! provider->open(this) )
{
DLOG("CMD646ATA provider did not open\n");
return false;
}
ATADeviceNub* newNub=0L;
if (!super::start( provider))
{
DLOG("CMD646ATA: super::start() failed\n");
provider->close(this);
return false;
}
if( ! createDeviceInterrupt() )
{
DLOG("CMD646ATA: createDeviceInterrupts failed\n");
return false;
}
DLOG("CMD646ATA::start() done\n");
for( UInt32 i = 0; i < 2; i++)
{
if( _devInfo[i].type != kUnknownATADeviceType )
{
DLOG("CMD646ATA creating nub\n");
newNub = ATADeviceNub::ataDeviceNub( (IOATAController*)this, (ataUnitID) i, _devInfo[i].type );
if( newNub )
{
DLOG("CMD646ATA attach nub\n");
newNub->attach(this);
_nub[i] = (IOATADevice*) newNub;
DLOG("CMD646ATA register nub\n");
newNub->registerService();
newNub = 0L;
}
}
}
return true;
}
void
CMD646ATA::free()
{
for( int i = 0; i < 3; i++)
{
if( ioBaseAddrMap[i] )
ioBaseAddrMap[ i ]->release();
}
super::free();
}
bool
CMD646ATA::createDeviceInterrupt(void)
{
DLOG("CMD646ATA createDeviceInterrupt started\n");
_devIntSrc = IOFilterInterruptEventSource::filterInterruptEventSource(
(OSObject *)this,
(IOInterruptEventSource::Action) &CMD646ATA::sDeviceInterruptOccurred,
(IOFilterInterruptEventSource::Filter) &CMD646ATA::sFilterInterrupt,
_pciNub,
0);
DLOG("CMD646ATA createdDeviceInterruptsource = %x\n", _devIntSrc);
DLOG("_workLoop = %x\n", _workLoop);
if( !_devIntSrc || getWorkLoop()->addEventSource(_devIntSrc) )
{
DLOG("CMD646ATA failed create dev intrpt source\n");
return false;
}
_devIntSrc->enable();
DLOG("CMD646ATA createDeviceInterrupt done\n");
return true;
}
void
CMD646ATA::sDeviceInterruptOccurred(OSObject * owner, IOInterruptEventSource *evtSrc, int count)
{
CMD646ATA* self = (CMD646ATA*) owner;
self->handleDeviceInterrupt();
}
bool
CMD646ATA::sFilterInterrupt(OSObject *owner, IOFilterInterruptEventSource *src)
{
CMD646ATA* self = (CMD646ATA*) owner;
return self->interruptIsValid( src );
}
bool
CMD646ATA::interruptIsValid( IOFilterInterruptEventSource* )
{
UInt8 modeBits = *_mrdModeReg;
if( ( modeBits & 0x04 ) )
{
*_mrdModeReg = 0x3C; OSSynchronizeIO();
return true;
}
return false;
}
IOReturn
CMD646ATA::handleDeviceInterrupt(void)
{
IOReturn result = super::handleDeviceInterrupt();
*_mrdModeReg = 0x20; OSSynchronizeIO();
return result;
}
IOWorkLoop*
CMD646ATA::getWorkLoop() const
{
DLOG("CMD646ATA::getWorkLoop\n");
IOWorkLoop* wl = _workLoop;
if (!wl)
{
wl = IOWorkLoop::workLoop();
if (!wl)
return 0;
}
return wl;
}
void
CMD646ATA::handleTimeout( void )
{
*_mrdModeReg = 0x3C; OSSynchronizeIO();
volatile UInt8 statusByte = *_tfStatusCmdReg;
OSSynchronizeIO();
statusByte++;
*_mrdModeReg = 0x20; OSSynchronizeIO();
super::handleTimeout();
}
bool
CMD646ATA::configureTFPointers(void)
{
DLOG("CMD646ATA config TF Pointers \n");
ioBaseAddrMap[0] = _pciNub->mapDeviceMemoryWithRegister( kPrimaryCmd );
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("CMD646ATA base address 0 = %lX \n", baseAddress);
ioBaseAddrMap[1] = _pciNub->mapDeviceMemoryWithRegister( kPrimaryCntrl );
if ( ioBaseAddrMap[1] == NULL )
{
return false;
}
baseAddress = (volatile UInt8 *)ioBaseAddrMap[1]->getVirtualAddress();
_tfAltSDevCReg = baseAddress + 2;
DLOG("CMD646ATA base address 1 = %lX altStatus at %lx \n", baseAddress, _tfAltSDevCReg);
ioBaseAddrMap[2] = _pciNub->mapDeviceMemoryWithRegister( kBusMaster );
if ( ioBaseAddrMap[2] == NULL )
{
return false;
}
volatile UInt8* bmAddress = (volatile UInt8*)ioBaseAddrMap[2]->getVirtualAddress();
DLOG("CMD646ATA base address 2 = %lX \n", bmAddress);
_bmCommandReg = bmAddress;
_bmStatusReg = bmAddress + 2;
_bmPRDAddresReg = (volatile UInt32*) (bmAddress + 4);
_mrdModeReg = bmAddress + 1;
_udideTCR0 = bmAddress + 3;
UInt8 scratchByte = _pciNub->configRead8( kARTTIM0);
_pciNub->configWrite8( kARTTIM0, scratchByte & 0x3f | 0x40);
busTimings[0].pioAddrSetupValue = _pciNub->configRead8( 0x53);
_pciNub->configWrite8( kDRWTIM0, busTimings[0].pioActiveRecoveryValue );
scratchByte = _pciNub->configRead8( kARTTIM1);
_pciNub->configWrite8( kARTTIM1, scratchByte & 0x3f | 0x40);
busTimings[1].pioAddrSetupValue = _pciNub->configRead8( kARTTIM1);
_pciNub->configWrite8( kDRWTIM1, busTimings[1].pioActiveRecoveryValue );
currentActiveRecoveryValue[0] = busTimings[0].pioActiveRecoveryValue;
currentActiveRecoveryValue[1] = busTimings[1].pioActiveRecoveryValue;
DLOG("CMD646ATA configTFPointers done\n");
return true;
}
IOReturn
CMD646ATA::provideBusInfo( IOATABusInfo* infoOut)
{
if( infoOut == 0)
{
DLOG("CMD646ATA nil pointer in provideBusInfo\n");
return -1;
}
infoOut->zeroData();
infoOut->setSocketType( kInternalATASocket );
infoOut->setPIOModes( kATASupportedPIOModes);
infoOut->setDMAModes( kATASupportedMultiDMAModes );
infoOut->setUltraModes( kATASupportedUltraDMAModes );
UInt8 units = 0;
if( _devInfo[0].type != kUnknownATADeviceType )
units++;
if( _devInfo[1].type != kUnknownATADeviceType )
units++;
infoOut->setUnits( units);
if( units > 1
&& _pciNub->configRead8( 0x08 ) == 0x05 )
{
infoOut->setUltraModes( 0x00 );
}
return kATANoErr;
}
IOReturn
CMD646ATA::getConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("CMD646ATA 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
CMD646ATA::selectConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("CMD646ATA bad param in setConfig\n");
return -1;
}
if( ( configRequest->getPIOMode() & kATASupportedPIOModes ) == 0x00 )
{
DLOG("CMD646ATA setConfig PIO mode not supported\n");
return kATAModeNotSupported;
}
UInt16 ultraSupported = kATASupportedUltraDMAModes;
if( configRequest->getUltraMode() & ~ultraSupported )
{
DLOG("CMD646ATA setConfig no ultra\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() & ~kATASupportedMultiDMAModes )
{
DLOG("CMD646ATA setConfig DMA mode not supported\n");
return kATAModeNotSupported;
}
if( configRequest->getDMAMode() > 0x0000
&& configRequest->getUltraMode() > 0x0000 )
{
DLOG("CMD646ATA err, only one DMA class allowed in config select\n");
return kATAModeNotSupported;
}
_devInfo[unitNumber].packetSend = configRequest->getPacketConfig();
DLOG("CMD646ATA setConfig packetConfig = %ld\n", _devInfo[unitNumber].packetSend );
return selectIOTimerValue(configRequest, unitNumber);
}
IOReturn
CMD646ATA::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 UInt8 PIOCycleValue[kPIOCycleEntries] =
{
0x00, 0x6d, 0x57, 0x65, 0x55, 0x54, 0x44, 0x44, 0x33, 0x32, 0x31, 0x3F };
static const UInt16 PIOCycleTime[ kPIOCycleEntries ]=
{
960, 600, 390, 360, 330, 300, 270, 240, 210, 180, 150, 120, };
static const UInt8 MultiDMACycleValue[kMultiDMACycleEntries] =
{
0x00, 0x88, 0x65, 0x44, 0x43, 0x42, 0x32, 0x31, 0x3F };
static const UInt16 MultiDMACycleTime[kMultiDMACycleEntries] =
{
960, 480, 360, 270, 240, 210, 180, 150, 120 };
static const UInt8 UltraDMADev0CycleValue[kUltraDMACycleEntries] =
{
0x31, 0x21, 0x11 };
static const UInt8 UltraDMADev1CycleValue[kUltraDMACycleEntries] =
{
0xC2, 0x82, 0x42 };
static const UInt16 UltraDMACycleTime[kUltraDMACycleEntries] =
{
120, 90, 60, };
UInt8 pioConfigBits = PIOCycleValue[0];
UInt32 pioModeNumber = bitSigToNumeric( configRequest->getPIOMode());
if( pioModeNumber > kATAMaxPIOMode )
{
DLOG("CMD646ATA pio mode out of range\n");
return kATAModeNotSupported;
}
UInt32 pioCycleTime = configRequest->getPIOCycleTime();
if( pioCycleTime < MinPIOCycle[ pioModeNumber ] )
{
pioCycleTime = MinPIOCycle[ pioModeNumber ];
}
for( int i = kPIOCycleEntries - 1; i >= 0; i--)
{
if( pioCycleTime <= PIOCycleTime[ i ] )
{
pioConfigBits = PIOCycleValue[i];;
break;
}
}
UInt32 dmaConfigBits = MultiDMACycleValue[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 = kMultiDMACycleEntries - 1; i >= 0; i--)
{
if( dmaCycleTime <= MultiDMACycleTime[ i ] )
{
dmaConfigBits = MultiDMACycleValue[i];
break;
}
}
}
UInt8 ultraConfigBits = 0;
UInt32 ultraModeNumber = 0;
if( configRequest->getUltraMode() )
{
ultraModeNumber = bitSigToNumeric( configRequest->getUltraMode() );
ultraConfigBits = unitNumber == 0 ? UltraDMADev0CycleValue[ ultraModeNumber ] : UltraDMADev1CycleValue[ ultraModeNumber ];
}
busTimings[unitNumber].pioActiveRecoveryValue = pioConfigBits;
busTimings[unitNumber].dmaActiveRecoveryValue = dmaConfigBits;
busTimings[unitNumber].ultraTimingValue = ultraConfigBits;
busTimings[unitNumber].ataPIOSpeedMode = configRequest->getPIOMode();
busTimings[unitNumber].ataPIOCycleTime = pioCycleTime;
busTimings[unitNumber].ataMultiDMASpeed = configRequest->getDMAMode();
busTimings[unitNumber].ataMultiCycleTime = dmaCycleTime;
busTimings[unitNumber].ataUltraDMASpeedMode = configRequest->getUltraMode();
DLOG("CMD646ATA PIO mode %x at %ld ns hex= %x selected for device: %x\n", (int)pioModeNumber, pioCycleTime, busTimings[unitNumber].pioActiveRecoveryValue, (int)unitNumber);
DLOG("CMD646ATA DMA mode %x at %ld ns hex= %x selected for device: %x\n", (int)dmaModeNumber, dmaCycleTime,busTimings[unitNumber].dmaActiveRecoveryValue, (int)unitNumber);
DLOG("CMD646ATA Ultra mode %x at %ld ns selected for device: %x\n", (int)ultraModeNumber, UltraDMACycleTime[ultraModeNumber], (int)unitNumber);
DLOG("CMD646ATA Ultra cycle value = %x for unit: %x\n", busTimings[unitNumber].ultraTimingValue, unitNumber);
_devIntSrc->disable();
*_udideTCR0 = busTimings[0].ultraTimingValue | busTimings[1].ultraTimingValue;
OSSynchronizeIO();
_devIntSrc->enable();
return getConfig( configRequest, unitNumber);
}
void
CMD646ATA::selectIOTiming( ataUnitID unit )
{
_devIntSrc->disable();
_devIntSrc->enable();
if( _currentCommand->getFlags() & mATAFlagUseDMA
&& busTimings[unit].ataUltraDMASpeedMode != 0 )
{
OSSynchronizeIO();
return;
}
UInt8 regOffset = unit == 0 ? kDRWTIM0 : kDRWTIM1;
UInt8 value = busTimings[unit].pioActiveRecoveryValue;
if( _currentCommand->getFlags() & mATAFlagUseDMA)
{
value = busTimings[unit].dmaActiveRecoveryValue;
}
if( value == currentActiveRecoveryValue[ unit ] )
{
return;
}
_devIntSrc->disable();
_pciNub->configWrite8( regOffset , value );
currentActiveRecoveryValue[ unit ] = value;
_devIntSrc->enable();
}