AppleIntelPIIXATARoot.cpp [plain text]
#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include "AppleIntelPIIXATARoot.h"
#include "AppleIntelPIIXATAChannel.h"
#include "AppleIntelPIIXATAKeys.h"
#include "AppleIntelPIIXATAHW.h"
#define super IOService
OSDefineMetaClassAndStructors( AppleIntelPIIXATARoot, IOService )
static const UInt8 gICH5ChannelModeMap[8 ][2 ] =
{
{ kChannelModeSATAPort0, kChannelModeSATAPort1 },
{ kChannelModeSATAPort1, kChannelModeSATAPort0 },
{ kChannelModeDisabled, kChannelModeDisabled },
{ kChannelModeDisabled, kChannelModeDisabled },
{ kChannelModeSATAPort01, kChannelModePATA },
{ kChannelModeSATAPort10, kChannelModePATA },
{ kChannelModePATA, kChannelModeSATAPort01 },
{ kChannelModePATA, kChannelModeSATAPort10 }
};
static const UInt8 gICH6ChannelModeMap[4 ][2 ] =
{
{ kChannelModeSATAPort02, kChannelModeSATAPort13 },
{ kChannelModePATA, kChannelModeSATAPort13 },
{ kChannelModeSATAPort02, kChannelModePATA },
{ kChannelModeDisabled, kChannelModeDisabled }
};
static const UInt8 gICH6MChannelModeMap[4 ][2 ] =
{
{ kChannelModeSATAPort02, kChannelModeDisabled },
{ kChannelModePATA, kChannelModeDisabled },
{ kChannelModeSATAPort02, kChannelModePATA },
{ kChannelModeDisabled, kChannelModeDisabled }
};
static const UInt8 gICH7ChannelModeMap[4 ][2 ] =
{
{ kChannelModeSATAPort02, kChannelModeSATAPort13 },
{ kChannelModePATA, kChannelModeSATAPort13 },
{ kChannelModeSATAPort02, kChannelModePATA },
{ kChannelModeDisabled, kChannelModeDisabled }
};
static const UInt8 gICH7MChannelModeMap[4 ][2 ] =
{
{ kChannelModeSATAPort02, kChannelModeDisabled },
{ kChannelModePATA, kChannelModeDisabled },
{ kChannelModeSATAPort02, kChannelModePATA },
{ kChannelModeDisabled, kChannelModeDisabled }
};
IOService * AppleIntelPIIXATARoot::probe( IOService * provider,
SInt32 * score )
{
IOPCIDevice * pciDevice;
if ( super::probe( provider, score ) == 0 )
{
return 0;
}
pciDevice = OSDynamicCast( IOPCIDevice, provider );
if ( pciDevice == 0 )
{
return 0;
}
if ( (pciDevice->configRead16( kIOPCIConfigCommand ) &
kIOPCICommandIOSpace) == 0 )
{
return 0;
}
return this;
}
static void registerClientApplier( IOService * service, void * context )
{
if ( service ) service->registerService();
}
bool AppleIntelPIIXATARoot::start( IOService * provider )
{
if ( super::start(provider) != true )
return false;
_pciConfigLock = IOLockAlloc();
if ( _pciConfigLock == 0 )
return false;
_provider = OSDynamicCast( IOPCIDevice, provider );
if ( _provider == 0 )
return false;
_provider->retain();
_nubs = createATAChannelNubs();
if ( _nubs == 0 )
return false;
_openNubs = OSSet::withCapacity( _nubs->getCount() );
if ( _openNubs == 0 )
return false;
applyToClients( registerClientApplier, 0 );
return true;
}
void AppleIntelPIIXATARoot::free( void )
{
if ( _nubs )
{
_nubs->release();
_nubs = 0;
}
if ( _openNubs )
{
_openNubs->release();
_openNubs = 0;
}
if ( _provider )
{
_provider->release();
_provider = 0;
}
if ( _pciConfigLock )
{
IOLockFree( _pciConfigLock );
_pciConfigLock = 0;
}
super::free();
}
IORegistryEntry * AppleIntelPIIXATARoot::getDTChannelEntry( int channelID )
{
IORegistryEntry * entry = 0;
const char * location;
OSIterator * iter = _provider->getChildIterator( gIODTPlane );
if (iter == 0) return 0;
while (( entry = (IORegistryEntry *) iter->getNextObject() ))
{
location = entry->getLocation();
if ( location && strtol(location, 0, 10) == channelID )
{
entry->retain();
break;
}
}
iter->release();
return entry; }
OSSet * AppleIntelPIIXATARoot::createATAChannelNubs( void )
{
OSSet * nubSet;
OSDictionary * channelInfo;
IORegistryEntry * dtEntry;
UInt32 priChannelMode;
UInt32 secChannelMode;
UInt8 mapValue = 0;
do {
nubSet = OSSet::withCapacity(2);
if ( nubSet == 0 )
break;
if ( _provider->open( this ) != true )
break;
priChannelMode = kChannelModePATA;
secChannelMode = kChannelModePATA;
if ( getProperty( kSerialATAKey ) == kOSBooleanTrue )
{
OSString * hwName;
hwName = OSDynamicCast(OSString, getProperty(kControllerNameKey));
mapValue = _provider->configRead8(kPIIX_PCI_MAP);
setProperty( kPortMappingKey, mapValue, 8 );
priChannelMode = kChannelModeDisabled;
secChannelMode = kChannelModeDisabled;
if (hwName)
{
if (hwName->isEqualTo("ICH7-M SATA"))
{
mapValue &= 0x3;
priChannelMode = gICH7MChannelModeMap[mapValue][0];
secChannelMode = gICH7MChannelModeMap[mapValue][1];
}
else if (hwName->isEqualTo("ICH6 SATA") || hwName->isEqualTo("ESB2 SATA"))
{
mapValue &= 0x3;
priChannelMode = gICH6ChannelModeMap[mapValue][0];
secChannelMode = gICH6ChannelModeMap[mapValue][1];
}
else if (hwName->isEqualTo("ICH6-M SATA"))
{
mapValue &= 0x3;
priChannelMode = gICH6MChannelModeMap[mapValue][0];
secChannelMode = gICH6MChannelModeMap[mapValue][1];
}
else if (hwName->isEqualTo("ICH5 SATA"))
{
mapValue &= 0x7;
priChannelMode = gICH5ChannelModeMap[mapValue][0];
secChannelMode = gICH5ChannelModeMap[mapValue][1];
}
else
{
mapValue &= 0x3;
priChannelMode = gICH7ChannelModeMap[mapValue][0];
secChannelMode = gICH7ChannelModeMap[mapValue][1];
}
}
}
if ( priChannelMode == kChannelModeDisabled &&
secChannelMode == kChannelModeDisabled )
{
IOLog("%s: bad value (%x) in Port Mapping register",
getName(), mapValue);
_provider->close( this );
break;
}
for ( UInt32 channelID = 0; channelID < 2; channelID++ )
{
UInt32 channelMode = (channelID ? secChannelMode : priChannelMode);
channelInfo = createNativeModeChannelInfo( channelID, channelMode );
if (channelInfo == 0)
channelInfo = createLegacyModeChannelInfo( channelID, channelMode );
if (channelInfo == 0)
continue;
AppleIntelPIIXATAChannel * nub = new AppleIntelPIIXATAChannel;
if ( nub )
{
dtEntry = getDTChannelEntry( channelID );
if ( nub->init( this, channelInfo, dtEntry ) &&
nub->attach( this ) )
{
nubSet->setObject( nub );
}
if ( dtEntry )
{
dtEntry->release();
}
else
{
char channelName[5] = {'C','H','N','_','\0'};
channelName[3] = '0' + channelID;
nub->setName( channelName );
if ( _provider->inPlane(gIODTPlane) )
{
nub->attachToParent( _provider, gIODTPlane );
}
}
nub->release();
}
channelInfo->release();
}
_provider->close( this );
}
while ( false );
if ( nubSet && (nubSet->getCount() == 0) )
{
nubSet->release();
nubSet = 0;
}
return nubSet;
}
OSDictionary *
AppleIntelPIIXATARoot::createNativeModeChannelInfo( UInt32 ataChannel,
UInt32 channelMode )
{
UInt8 pi = _provider->configRead8( kPIIX_PCI_PI );
UInt16 cmdPort = 0;
UInt16 ctrPort = 0;
switch ( ataChannel )
{
case kPIIX_CHANNEL_PRIMARY:
if ((pi & kPIIX_PCI_PRI_NATIVE_MASK) == kPIIX_PCI_PRI_NATIVE_MASK)
{
cmdPort = _provider->configRead16( kIOPCIConfigBaseAddress0 );
ctrPort = _provider->configRead16( kIOPCIConfigBaseAddress1 );
cmdPort &= ~0x1; ctrPort &= ~0x1;
if ( cmdPort == kPIIX_P_CMD_ADDR &&
ctrPort == kPIIX_P_CTL_ADDR )
{
cmdPort = ctrPort = 0;
}
}
break;
case kPIIX_CHANNEL_SECONDARY:
if ((pi & kPIIX_PCI_SEC_NATIVE_MASK) == kPIIX_PCI_SEC_NATIVE_MASK)
{
cmdPort = _provider->configRead16( kIOPCIConfigBaseAddress2 );
ctrPort = _provider->configRead16( kIOPCIConfigBaseAddress3 );
cmdPort &= ~0x1; ctrPort &= ~0x1;
if ( cmdPort == kPIIX_S_CMD_ADDR &&
ctrPort == kPIIX_S_CTL_ADDR )
{
cmdPort = ctrPort = 0;
}
}
break;
}
if ( cmdPort && ctrPort )
return createChannelInfo( ataChannel, channelMode, cmdPort, ctrPort,
_provider->configRead8( kIOPCIConfigInterruptLine ) );
else
return 0;
}
OSDictionary *
AppleIntelPIIXATARoot::createLegacyModeChannelInfo( UInt32 ataChannel,
UInt32 channelMode )
{
UInt16 cmdPort = 0;
UInt16 ctrPort = 0;
UInt8 irq = 0;
switch ( ataChannel )
{
case kPIIX_CHANNEL_PRIMARY:
cmdPort = kPIIX_P_CMD_ADDR;
ctrPort = kPIIX_P_CTL_ADDR;
irq = kPIIX_P_IRQ;
break;
case kPIIX_CHANNEL_SECONDARY:
cmdPort = kPIIX_S_CMD_ADDR;
ctrPort = kPIIX_S_CTL_ADDR;
irq = kPIIX_S_IRQ;
break;
}
return createChannelInfo( ataChannel, channelMode,
cmdPort, ctrPort, irq );
}
OSDictionary *
AppleIntelPIIXATARoot::createChannelInfo( UInt32 ataChannel,
UInt32 channelMode,
UInt16 commandPort,
UInt16 controlPort,
UInt8 interruptVector )
{
OSDictionary * dict = OSDictionary::withCapacity( 4 );
OSNumber * num;
if ( dict == 0 || commandPort == 0 || controlPort == 0 ||
interruptVector == 0 || interruptVector == 0xFF )
{
if ( dict ) dict->release();
return 0;
}
num = OSNumber::withNumber( ataChannel, 32 );
if (num)
{
dict->setObject( kChannelNumberKey, num );
num->release();
}
num = OSNumber::withNumber( commandPort, 16 );
if (num)
{
dict->setObject( kCommandBlockAddressKey, num );
num->release();
}
num = OSNumber::withNumber( controlPort, 16 );
if (num)
{
dict->setObject( kControlBlockAddressKey, num );
num->release();
}
num = OSNumber::withNumber( interruptVector, 8 );
if (num)
{
dict->setObject( kInterruptVectorKey, num );
num->release();
}
num = OSNumber::withNumber( channelMode, 32 );
if (num)
{
dict->setObject( kChannelModeKey, num );
num->release();
}
return dict;
}
bool AppleIntelPIIXATARoot::handleOpen( IOService * client,
IOOptionBits options,
void * arg )
{
bool ret = true;
if ( ( _nubs->containsObject( client ) == false ) ||
( _openNubs->containsObject( client ) == true ) )
return false;
if ( _openNubs->getCount() == 0 )
ret = _provider->open( this );
if ( ret )
{
_openNubs->setObject( client );
if ( arg ) *((IOService **) arg) = _provider;
}
return ret;
}
void AppleIntelPIIXATARoot::handleClose( IOService * client,
IOOptionBits options )
{
if ( _openNubs->containsObject( client ) == false ) return;
_openNubs->removeObject( client );
if ( _openNubs->getCount() == 0 )
_provider->close( this );
}
bool AppleIntelPIIXATARoot::handleIsOpen( const IOService * client ) const
{
if ( client )
return _openNubs->containsObject( client );
else
return ( _openNubs->getCount() != 0 );
}
void AppleIntelPIIXATARoot::pciConfigWrite8( UInt8 offset,
UInt8 data,
UInt8 mask )
{
UInt8 u8;
IOLockLock( _pciConfigLock );
u8 = _provider->configRead8( offset );
u8 &= ~mask;
u8 |= (mask & data);
_provider->configWrite8( offset, u8 );
IOLockUnlock( _pciConfigLock );
}
void AppleIntelPIIXATARoot::pciConfigWrite16( UInt8 offset,
UInt16 data,
UInt16 mask )
{
UInt16 u16;
IOLockLock( _pciConfigLock );
u16 = _provider->configRead16( offset );
u16 &= ~mask;
u16 |= (mask & data);
_provider->configWrite16( offset, u16 );
IOLockUnlock( _pciConfigLock );
}
bool AppleIntelPIIXATARoot::serializeProperties( OSSerialize * s ) const
{
AppleIntelPIIXATARoot * self;
char timingString[80];
if ( _provider )
{
sprintf( timingString, "0x40=%08lx 0x44=%08lx 0x48=%08lx 0x54=%04x",
_provider->configRead32( 0x40 ),
_provider->configRead32( 0x44 ),
_provider->configRead32( 0x48 ),
_provider->configRead16( 0x54 ) );
self = (AppleIntelPIIXATARoot *) this;
self->setProperty( "PCI Timing Registers", timingString );
}
return super::serializeProperties(s);
}
struct PCSPortMap
{
UInt8 enableOffset;
UInt8 enableMask;
UInt8 presenceOffset;
UInt8 presenceMask;
};
static const PCSPortMap gDefaultPortMap[4] =
{
{ kPIIX_PCI_PCS, kPIIX_PCI_PCS_P0E, kPIIX_PCI_PCS, kPIIX_PCI_PCS_P0P },
{ kPIIX_PCI_PCS, kPIIX_PCI_PCS_P1E, kPIIX_PCI_PCS, kPIIX_PCI_PCS_P1P },
{ kPIIX_PCI_PCS, kPIIX_PCI_PCS_P2E, kPIIX_PCI_PCS, kPIIX_PCI_PCS_P2P },
{ kPIIX_PCI_PCS, kPIIX_PCI_PCS_P3E, kPIIX_PCI_PCS, kPIIX_PCI_PCS_P3P }
};
static bool
getPCSPortMapping(
IORegistryEntry * root,
UInt32 portNum,
UInt8 * enableOffset,
UInt8 * enableMask,
UInt8 * presenceOffset,
UInt8 * presenceMask )
{
OSObject * prop;
OSData * data;
const PCSPortMap * portMap = gDefaultPortMap;
if (!root || (portNum > kSerialATAPort3) ||
root->getProperty( kSerialATAKey ) != kOSBooleanTrue)
return false;
prop = root->copyProperty( kPCSPortMapKey );
data = OSDynamicCast(OSData, prop);
if (data && (data->getLength() == sizeof(gDefaultPortMap)))
{
portMap = (const PCSPortMap *) data->getBytesNoCopy();
}
if (enableOffset)
*enableOffset = portMap[portNum].enableOffset;
if (enableMask)
*enableMask = portMap[portNum].enableMask;
if (presenceOffset)
*presenceOffset = portMap[portNum].presenceOffset;
if (presenceMask)
*presenceMask = portMap[portNum].presenceMask;
if (prop) prop->release();
return true;
}
void AppleIntelPIIXATARoot::setSerialATAPortEnable( UInt32 port, bool enable )
{
UInt8 offset;
UInt8 mask;
if (getPCSPortMapping( this, port, &offset, &mask, NULL, NULL ))
{
pciConfigWrite8( offset, enable ? mask : 0, mask );
}
}
bool AppleIntelPIIXATARoot::getSerialATAPortPresentStatus( UInt32 port )
{
UInt8 offset;
UInt8 mask = 0;
UInt8 pcs = 0;
if (getPCSPortMapping( this, port, NULL, NULL, &offset, &mask ))
{
pcs = _provider->configRead8( offset );
}
return ( pcs & mask );
}