#include <IOKit/system.h>
#include <IOKit/pci/IOPCIBridge.h>
#include <IOKit/pci/IOPCIPrivate.h>
#include <IOKit/pci/IOAGPDevice.h>
#include <IOKit/pci/IOPCIConfigurator.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IORangeAllocator.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOMessage.h>
#include <IOKit/assert.h>
#include <IOKit/IOCatalogue.h>
#include <IOKit/IOFilterInterruptEventSource.h>
#include <IOKit/acpi/IOACPIPlatformDevice.h>
#include <libkern/c++/OSContainers.h>
extern "C"
{
#include <machine/machine_routines.h>
};
#if 0
#define LOG(fmt, args...) \
do { kprintf(fmt, ## args); IOLog(fmt, ## args); } while (false);
#else
#define LOG(fmt, args...)
#endif
const IORegistryPlane * gIOPCIACPIPlane;
static IOPCIMessagedInterruptController * gIOPCIMessagedInterruptController;
enum
{
kMSIX = 0x01
};
enum
{
kIOPCIClassBridge = 0x06
};
#ifndef kBaseVectorNumberKey
#define kBaseVectorNumberKey "Base Vector Number"
#endif
#ifndef kInterruptControllerNameKey
#define kVectorCountKey "Vector Count"
#endif
#ifndef kInterruptControllerNameKey
#define kInterruptControllerNameKey "InterruptControllerName"
#endif
class IOPCIMessagedInterruptController : public IOInterruptController
{
OSDeclareDefaultStructors( IOPCIMessagedInterruptController )
protected:
SInt32 _vectorBase;
UInt32 _vectorCount;
IORangeAllocator * _messagedInterruptsAllocator;
long _destinationAddress;
virtual void free( void );
public:
bool init( UInt32 numVectors );
virtual IOReturn getInterruptType( IOService * nub,
int source,
int * interruptType );
virtual IOReturn registerInterrupt( IOService * nub,
int source,
void * target,
IOInterruptHandler handler,
void * refCon );
virtual IOReturn unregisterInterrupt( IOService * nub,
int source);
virtual void initVector( long vectorNumber,
IOInterruptVector * vector );
virtual int getVectorType(long vectorNumber, IOInterruptVector *vector);
virtual bool vectorCanBeShared( long vectorNumber,
IOInterruptVector * vector );
virtual void enableVector( long vectorNumber,
IOInterruptVector * vector );
virtual void disableVectorHard( long vectorNumber,
IOInterruptVector * vector );
virtual IOReturn handleInterrupt( void * savedState,
IOService * nub,
int source );
virtual IOInterruptAction getInterruptHandlerAddress( void );
bool addDeviceInterruptProperties(
IORegistryEntry * device,
UInt32 controllerIndex,
UInt32 interruptFlags,
SInt32 * deviceIndex);
IOReturn allocateDeviceInterrupts( IOPCIBridge * bridge,
IOPCIDevice * device, UInt32 numVectors);
IOReturn deallocateDeviceInterrupts(
IOPCIBridge * bridge, IOPCIDevice * device);
};
#undef super
#define super IOInterruptController
OSDefineMetaClassAndStructors( IOPCIMessagedInterruptController,
IOInterruptController )
bool IOPCIMessagedInterruptController::init( UInt32 numVectors )
{
OSNumber * num;
const OSSymbol * sym = 0;
if (!super::init())
return (false);
_vectorCount = numVectors;
setProperty(kVectorCountKey, _vectorCount, 32);
vectors = IONew( IOInterruptVector, _vectorCount );
if ( 0 == vectors )
goto fail;
bzero( vectors, sizeof(IOInterruptVector) * _vectorCount );
for (uint32_t i = 0; i < _vectorCount; i++)
{
vectors[i].interruptLock = IOLockAlloc();
if ( vectors[i].interruptLock == 0 )
goto fail;
}
attach(getPlatform());
sym = copyName();
setProperty(kInterruptControllerNameKey, (OSObject *) sym);
getPlatform()->registerInterruptController( (OSSymbol *) sym, this );
sym->release();
num = OSDynamicCast( OSNumber,
getProperty( kBaseVectorNumberKey ) );
if ( num ) _vectorBase = num->unsigned32BitValue();
_messagedInterruptsAllocator = IORangeAllocator::withRange(0, 0, 4, IORangeAllocator::kLocking);
_messagedInterruptsAllocator->deallocate(_vectorBase, _vectorCount);
registerService();
LOG("%s: start success\n", getName());
return (true);
fail:
return false;
}
bool IOPCIMessagedInterruptController::addDeviceInterruptProperties(
IORegistryEntry * device,
UInt32 controllerIndex,
UInt32 interruptFlags,
SInt32 * deviceIndex)
{
OSArray * controllers;
OSArray * specifiers;
OSArray * liveCtrls;
OSArray * liveSpecs;
const OSSymbol * symName;
OSData * specData;
bool success = false;
if (!device)
return false;
liveCtrls = OSDynamicCast(OSArray,
device->getProperty(gIOInterruptControllersKey));
liveSpecs = OSDynamicCast(OSArray,
device->getProperty(gIOInterruptSpecifiersKey));
if (liveCtrls && liveSpecs)
{
controllers = OSArray::withArray(liveCtrls, liveCtrls->getCount() + 1);
specifiers = OSArray::withArray(liveSpecs, liveSpecs->getCount() + 1);
}
else
{
controllers = OSArray::withCapacity(1);
specifiers = OSArray::withCapacity(1);
}
specData = OSData::withCapacity(2 * sizeof(UInt32));
symName = copyName();
if (!controllers || !specifiers || !specData || !symName)
return (false);
specData->appendBytes(&controllerIndex, sizeof(controllerIndex));
specData->appendBytes(&interruptFlags, sizeof(interruptFlags));
if (deviceIndex)
*deviceIndex = specifiers->getCount() - 1;
success = specifiers->setObject(specData)
&& controllers->setObject(symName);
if (success)
{
device->setProperty(gIOInterruptControllersKey, controllers);
device->setProperty(gIOInterruptSpecifiersKey, specifiers);
}
specifiers->release();
controllers->release();
symName->release();
specData->release();
return success;
}
IOReturn IOPCIMessagedInterruptController::allocateDeviceInterrupts(
IOPCIBridge * bridge, IOPCIDevice * device, UInt32 numVectors)
{
IOReturn ret;
IOByteCount msi = device->reserved->msiConfig;
IOByteCount msiBlockSize;
uint32_t vector, firstVector = 0;
uint16_t control = device->configRead16(msi + 2);
IORangeScalar rangeStart;
uint32_t message[3];
if (!_messagedInterruptsAllocator->allocate(numVectors, &rangeStart, numVectors))
return (kIOReturnNoSpace);
firstVector = rangeStart;
ret = bridge->callPlatformFunction( "GetMessagedInterruptAddress",
false,
device,
(void *) 0,
(void *) firstVector,
(void *) &message[0]);
if (kIOReturnSuccess == ret)
{
for (vector = firstVector; vector < (firstVector + numVectors); vector++)
{
addDeviceInterruptProperties(device,
vector - _vectorBase,
kIOInterruptTypeEdge | kIOInterruptTypePCIMessaged, NULL);
}
if (kMSIX & device->reserved->msiMode)
{
IOByteCount msiTable;
UInt8 bar;
IODeviceMemory * memory;
IOPhysicalAddress phys;
control &= ~(1 << 15);
msiBlockSize = 1;
msiTable = device->configRead32(msi + 4);
bar = kIOPCIConfigBaseAddress0 + ((msiTable & 7) << 2);
msiTable &= ~7;
memory = device->getDeviceMemoryWithRegister(bar);
if (memory && (phys = memory->getPhysicalAddress()))
{
control = device->configRead16(kIOPCIConfigCommand);
device->configWrite16(kIOPCIConfigCommand, control | 4);
for (vector = 0; vector < numVectors; vector++)
{
IOMappedWrite32(phys + msiTable + vector * 16 + 0, message[0]);
IOMappedWrite32(phys + msiTable + vector * 16 + 4, message[1]);
IOMappedWrite32(phys + msiTable + vector * 16 + 8, message[2]);
IOMappedWrite32(phys + msiTable + vector * 16 + 0, 0);
}
device->configWrite16(kIOPCIConfigCommand, control);
}
}
else
{
control &= ~1; numVectors = (31 - __builtin_clz(numVectors)); control |= (numVectors << 4);
msiBlockSize = 3; if (0x0080 & control)
{
device->configWrite32(msi + 4, message[0]);
device->configWrite32(msi + 8, message[1]);
device->configWrite16(msi + 12, message[2]);
device->configWrite16(msi + 2, control);
msiBlockSize += 1;
}
else
{
device->configWrite32(msi + 4, message[0]);
device->configWrite16(msi + 8, message[2]);
device->configWrite16(msi + 2, control);
}
if (0x0100 & control)
msiBlockSize += 2;
}
device->reserved->msiBlockSize = msiBlockSize;
}
return (ret);
}
IOReturn IOPCIMessagedInterruptController::deallocateDeviceInterrupts(
IOPCIBridge * bridge, IOPCIDevice * device)
{
const OSSymbol * myName;
OSArray * controllers;
OSObject * controller;
OSArray * specs;
OSData * spec;
uint32_t index = 0;
myName = copyName();
controllers = OSDynamicCast(OSArray,
device->getProperty(gIOInterruptControllersKey));
specs = OSDynamicCast(OSArray,
device->getProperty(gIOInterruptSpecifiersKey));
if (!myName || !controllers || !specs)
return (kIOReturnBadArgument);
while( (spec = OSDynamicCast(OSData, specs->getObject(index)))
&& (controller = controllers->getObject(index)))
{
if (controller->isEqualTo(myName))
{
IORangeScalar rangeStart = _vectorBase + *((uint32_t *) spec->getBytesNoCopy());
_messagedInterruptsAllocator->deallocate(rangeStart, 1);
}
index++;
}
myName->release();
return (kIOReturnSuccess);
}
IOReturn IOPCIMessagedInterruptController::registerInterrupt(
IOService * nub,
int source,
void * target,
IOInterruptHandler handler,
void * refCon )
{
IOReturn ret;
IOPCIDevice * device = OSDynamicCast(IOPCIDevice, nub);
ret = super::registerInterrupt(nub, source, target, handler, refCon);
if ((kIOReturnSuccess == ret) && device)
{
if (!device->reserved->msiEnable)
{
IOByteCount msi = device->reserved->msiConfig;
uint16_t control;
control = device->configRead16(msi + 2);
if (kMSIX & device->reserved->msiMode)
{
control |= (1 << 15);
}
else
{
control |= 1;
}
device->configWrite16(msi + 2, control);
control = device->configRead16(kIOPCIConfigCommand);
control |= (1 << 10) | (1 << 2);
device->configWrite16(kIOPCIConfigCommand, control);
device->setProperty("IOPCIMSIMode", kOSBooleanTrue);
}
device->reserved->msiEnable++;
}
return (ret);
}
IOReturn IOPCIMessagedInterruptController::unregisterInterrupt(
IOService * nub,
int source)
{
IOReturn ret;
IOPCIDevice * device = OSDynamicCast(IOPCIDevice, nub);
ret = super::unregisterInterrupt(nub, source);
if (device && device->reserved->msiEnable && !(--device->reserved->msiEnable))
{
IOByteCount msi = device->reserved->msiConfig;
uint16_t control;
control = device->configRead16(msi + 2);
control &= ~((1 << 15) | (7 << 4) | 1);
device->configWrite16(msi + 2, control);
device->removeProperty("IOPCIMSIMode");
}
return (ret);
}
IOReturn
IOPCIMessagedInterruptController::handleInterrupt( void * state,
IOService * nub,
int source)
{
IOInterruptVector * vector;
source -= _vectorBase;
if ((source < 0) || (source > (int) _vectorCount))
return kIOReturnSuccess;
vector = &vectors[source];
if (!vector->interruptRegistered)
return kIOReturnInvalid;
vector->handler(vector->target, vector->refCon,
vector->nub, vector->source);
return kIOReturnSuccess;
}
bool IOPCIMessagedInterruptController::vectorCanBeShared(long vectorNumber,
IOInterruptVector * vector)
{
return (false);
}
void IOPCIMessagedInterruptController::initVector(long vectorNumber,
IOInterruptVector * vector)
{
}
int IOPCIMessagedInterruptController::getVectorType(long vectorNumber,
IOInterruptVector * vector)
{
return (kIOInterruptTypeEdge | kIOInterruptTypePCIMessaged);
}
void IOPCIMessagedInterruptController::disableVectorHard(long vectorNumber,
IOInterruptVector * vector)
{
}
void IOPCIMessagedInterruptController::enableVector(long vectorNumber,
IOInterruptVector * vector)
{
}
#undef super
#define super IOService
OSDefineMetaClassAndAbstractStructorsWithInit( IOPCIBridge, IOService, IOPCIBridge::initialize() )
OSMetaClassDefineReservedUsed(IOPCIBridge, 0);
OSMetaClassDefineReservedUsed(IOPCIBridge, 1);
OSMetaClassDefineReservedUnused(IOPCIBridge, 2);
OSMetaClassDefineReservedUnused(IOPCIBridge, 3);
OSMetaClassDefineReservedUnused(IOPCIBridge, 4);
OSMetaClassDefineReservedUnused(IOPCIBridge, 5);
OSMetaClassDefineReservedUnused(IOPCIBridge, 6);
OSMetaClassDefineReservedUnused(IOPCIBridge, 7);
OSMetaClassDefineReservedUnused(IOPCIBridge, 8);
OSMetaClassDefineReservedUnused(IOPCIBridge, 9);
OSMetaClassDefineReservedUnused(IOPCIBridge, 10);
OSMetaClassDefineReservedUnused(IOPCIBridge, 11);
OSMetaClassDefineReservedUnused(IOPCIBridge, 12);
OSMetaClassDefineReservedUnused(IOPCIBridge, 13);
OSMetaClassDefineReservedUnused(IOPCIBridge, 14);
OSMetaClassDefineReservedUnused(IOPCIBridge, 15);
OSMetaClassDefineReservedUnused(IOPCIBridge, 16);
OSMetaClassDefineReservedUnused(IOPCIBridge, 17);
OSMetaClassDefineReservedUnused(IOPCIBridge, 18);
OSMetaClassDefineReservedUnused(IOPCIBridge, 19);
OSMetaClassDefineReservedUnused(IOPCIBridge, 20);
OSMetaClassDefineReservedUnused(IOPCIBridge, 21);
OSMetaClassDefineReservedUnused(IOPCIBridge, 22);
OSMetaClassDefineReservedUnused(IOPCIBridge, 23);
OSMetaClassDefineReservedUnused(IOPCIBridge, 24);
OSMetaClassDefineReservedUnused(IOPCIBridge, 25);
OSMetaClassDefineReservedUnused(IOPCIBridge, 26);
OSMetaClassDefineReservedUnused(IOPCIBridge, 27);
OSMetaClassDefineReservedUnused(IOPCIBridge, 28);
OSMetaClassDefineReservedUnused(IOPCIBridge, 29);
OSMetaClassDefineReservedUnused(IOPCIBridge, 30);
OSMetaClassDefineReservedUnused(IOPCIBridge, 31);
int gIOPCIDebug = 0;
#ifdef __i386__
__BEGIN_DECLS
#include <i386/cpu_number.h>
extern void mp_rendezvous_no_intrs(
void (*action_func)(void *),
void *arg);
__END_DECLS
#endif
#if 0
enum {
kPCI2PCIPrimaryBus = 0x18, kPCI2PCISecondaryBus = 0x19, kPCI2PCISubordinateBus = 0x1a, kPCI2PCIIORange = 0x1c,
kPCI2PCIMemoryRange = 0x20,
kPCI2PCIPrefetchMemoryRange = 0x24,
kPCI2PCIUpperIORange = 0x30
};
#endif
#ifndef kIOPlatformDeviceMessageKey
#define kIOPlatformDeviceMessageKey "IOPlatformDeviceMessage"
#endif
static IOSimpleLock * gIOAllPCI2PCIBridgesLock;
UInt32 gIOAllPCI2PCIBridgeState;
static queue_head_t gIOAllPCIDeviceRestoreQ;
static IOLock * gIOPCIMessagedInterruptControllerLock;
const OSSymbol * gIOPlatformDeviceMessageKey;
static IOWorkLoop * gCommonWorkLoop;
#define fXpressCapability reserved->xpressCapability
#define fBridgeInterruptSource reserved->bridgeInterruptSource
#define fWorkLoop reserved->workLoop
#define fHotplugCount reserved->hotplugCount
#define fPresence reserved->presence
#define fWaitingLinkEnable reserved->waitingLinkEnable
#define fBridgeInterruptEnablePending reserved->interruptEnablePending
#define fLinkChangeOnly reserved->linkChangeOnly
enum { kIOPCIBridgePowerStateCount = 3 };
void IOPCIBridge::initialize(void)
{
if (!gIOAllPCI2PCIBridgesLock)
{
gIOAllPCI2PCIBridgesLock = IOSimpleLockAlloc();
gIOPCIMessagedInterruptControllerLock = IOLockAlloc();
queue_init(&gIOAllPCIDeviceRestoreQ);
gIOPlatformDeviceMessageKey = OSSymbol::withCStringNoCopy(kIOPlatformDeviceMessageKey);
}
}
bool IOPCIBridge::start( IOService * provider )
{
static const IOPMPowerState powerStates[ kIOPCIBridgePowerStateCount ] = {
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 0, IOPMSoftSleep, IOPMSoftSleep, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, IOPMPowerOn, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
if (!gIOPCIACPIPlane)
gIOPCIACPIPlane = IORegistryEntry::getPlane("IOACPIPlane");
if (!super::start(provider))
return (false);
reserved = IONew(ExpansionData, 1);
if (reserved == 0) return (false);
bzero(reserved, sizeof(ExpansionData));
bridgeMemoryRanges = IORangeAllocator::withRange( 0, 1, 8,
IORangeAllocator::kLocking );
assert( bridgeMemoryRanges );
setProperty( "Bridge Memory Ranges", bridgeMemoryRanges );
bridgeIORanges = IORangeAllocator::withRange( 0, 1, 8,
IORangeAllocator::kLocking );
assert( bridgeIORanges );
setProperty( "Bridge IO Ranges", bridgeIORanges );
if (!configure(provider))
return (false);
PMinit();
registerPowerDriver( this, (IOPMPowerState *) powerStates,
kIOPCIBridgePowerStateCount);
provider->joinPMtree( this);
temporaryPowerClampOn();
probeBus( provider, firstBusNum() );
registerService();
return (true);
}
void IOPCIBridge::stop( IOService * provider )
{
PMstop();
super::stop( provider);
}
void IOPCIBridge::free( void )
{
if (bridgeMemoryRanges)
{
bridgeMemoryRanges->release();
bridgeMemoryRanges = 0;
}
if (bridgeIORanges)
{
bridgeIORanges->release();
bridgeIORanges = 0;
}
if (reserved)
{
IOPCIRange * range;
IOPCIRange * nextRange;
uint32_t resourceType;
for (resourceType = 0; resourceType < kIOPCIResourceTypeCount; resourceType++)
{
range = reserved->rangeLists[resourceType];
while (range)
{
nextRange = range->next;
IODelete(range, IOPCIRange, 1);
range = nextRange;
}
}
IODelete(reserved, ExpansionData, 1);
}
super::free();
}
IOReturn IOPCIBridge::setDevicePowerState( IOPCIDevice * device,
unsigned long whatToDo )
{
if ((kSaveDeviceState == whatToDo) || (kRestoreDeviceState == whatToDo))
{
IOReturn ret = kIOReturnSuccess;
if ((device->savedConfig && configShadow(device)->bridge)
|| (kOSBooleanFalse != device->getProperty(kIOPMPCIConfigSpaceVolatileKey)))
{
if (kRestoreDeviceState == whatToDo)
ret = restoreDeviceState(device);
else
ret = saveDeviceState(device);
}
return (ret);
}
#if 0
if (16 == whatToDo)
{
OSIterator * iter = getClientIterator();
if (iter)
{
OSObject * child;
while ((child = iter->getNextObject()))
{
IOPCIDevice * pciDevice;
if ((pciDevice = OSDynamicCast(IOPCIDevice, child)))
pciDevice->setBusMasterEnable(false);
}
iter->release();
}
}
#endif
if (kRestoreBridgeState == whatToDo)
restoreMachineState(0);
gIOAllPCI2PCIBridgeState = whatToDo;
return (kIOReturnSuccess);
}
IOReturn IOPCIBridge::saveDeviceState( IOPCIDevice * device,
IOOptionBits options )
{
UInt32 flags;
void * p3 = (void *) 3;
int i;
if (!device->savedConfig)
return (kIOReturnNotReady);
flags = configShadow(device)->flags;
if (kIOPCIConfigShadowValid & flags)
return (kIOReturnSuccess);
flags |= kIOPCIConfigShadowValid;
configShadow(device)->flags = flags;
device->callPlatformFunction(gIOPlatformDeviceMessageKey, false,
(void *) kIOMessageDeviceWillPowerOff, device, p3, (void *) 0);
if (kIOPCIConfigShadowBridge & flags)
{
if (configShadow(device)->bridge)
configShadow(device)->bridge->saveBridgeState();
}
else
{
for (i = 0; i < kIOPCIConfigShadowRegs; i++)
{
if (kIOPCIVolatileRegsMask & (1 << i))
device->savedConfig[i] = device->configRead32( i * 4 );
}
}
if (device->reserved->expressConfig)
{
device->savedConfig[kIOPCIConfigShadowXPress + 0] = device->configRead16( device->reserved->expressConfig + 0x08 );
device->savedConfig[kIOPCIConfigShadowXPress + 1] = device->configRead16( device->reserved->expressConfig + 0x10 );
if (0x100 & device->reserved->expressCapabilities)
{ device->savedConfig[kIOPCIConfigShadowXPress + 2]
= device->configRead16( device->reserved->expressConfig + 0x18 );
}
}
for (i = 0; i < device->reserved->msiBlockSize; i++)
device->savedConfig[kIOPCIConfigShadowMSI + i]
= device->configRead32( device->reserved->msiConfig + i * 4 );
device->callPlatformFunction(gIOPlatformDeviceMessageKey, false,
(void *) kIOMessageDeviceHasPoweredOn, device, p3, (void *) 0);
IOSimpleLockLock(gIOAllPCI2PCIBridgesLock);
queue_enter_first( &gIOAllPCIDeviceRestoreQ,
configShadow(device),
IOPCIConfigShadow *,
link );
IOSimpleLockUnlock(gIOAllPCI2PCIBridgesLock);
return (kIOReturnSuccess);
}
IOReturn IOPCIBridge::_restoreDeviceState( IOPCIDevice * device,
IOOptionBits options )
{
UInt32 flags;
void * p3 = (void *) 0;
int i;
flags = configShadow(device)->flags;
if (!(kIOPCIConfigShadowValid & flags))
return (kIOReturnNotReady);
flags &= ~kIOPCIConfigShadowValid;
configShadow(device)->flags = flags;
device->callPlatformFunction(gIOPlatformDeviceMessageKey, false,
(void *) kIOMessageDeviceWillPowerOff, device, p3, (void *) 0);
if (kIOPCIConfigShadowBridge & flags)
{
if (configShadow(device)->bridge)
configShadow(device)->bridge->restoreBridgeState();
}
else
{
for (i = (kIOPCIConfigRevisionID >> 2); i < kIOPCIConfigShadowRegs; i++)
{
if (kIOPCIVolatileRegsMask & (1 << i))
device->configWrite32( i * 4, device->savedConfig[ i ]);
}
device->configWrite32(kIOPCIConfigCommand, device->savedConfig[1]);
}
if (device->reserved->expressConfig)
{
device->configWrite16( device->reserved->expressConfig + 0x08, device->savedConfig[kIOPCIConfigShadowXPress + 0]);
device->configWrite16( device->reserved->expressConfig + 0x10, device->savedConfig[kIOPCIConfigShadowXPress + 1] );
if (0x100 & device->reserved->expressCapabilities)
{ device->configWrite16( device->reserved->expressConfig + 0x18,
device->savedConfig[kIOPCIConfigShadowXPress + 2] );
}
}
for (i = 0; i < device->reserved->msiBlockSize; i++)
device->configWrite32( device->reserved->msiConfig + i * 4,
device->savedConfig[kIOPCIConfigShadowMSI + i]);
device->callPlatformFunction(gIOPlatformDeviceMessageKey, false,
(void *) kIOMessageDeviceHasPoweredOn, device, p3, (void *) 0);
return (kIOReturnSuccess);
}
IOReturn IOPCIBridge::restoreMachineState( IOOptionBits options )
{
IOSimpleLockLock(gIOAllPCI2PCIBridgesLock);
if (kSaveBridgeState != gIOAllPCI2PCIBridgeState)
{
IOSimpleLockUnlock(gIOAllPCI2PCIBridgesLock);
return (kIOReturnNotFound);
}
gIOAllPCI2PCIBridgeState = kRestoreBridgeState;
IOPCIConfigShadow * shadow;
IOPCIConfigShadow * next;
UInt32 bridgesOnly = true;
do
{
next = (IOPCIConfigShadow *) queue_first(&gIOAllPCIDeviceRestoreQ);
while (!queue_end(&gIOAllPCIDeviceRestoreQ, (queue_entry_t) next))
{
shadow = next;
next = (IOPCIConfigShadow *) queue_next(&shadow->link);
if (bridgesOnly && !(kIOPCIConfigShadowBridge & shadow->flags))
continue;
queue_remove( &gIOAllPCIDeviceRestoreQ,
shadow,
IOPCIConfigShadow *,
link );
shadow->link.next = shadow->link.prev = NULL;
IOSimpleLockUnlock(gIOAllPCI2PCIBridgesLock);
_restoreDeviceState(shadow->device, false);
IOSimpleLockLock(gIOAllPCI2PCIBridgesLock);
}
}
#ifndef __ppc__
while (bridgesOnly--);
#else
while (false);
#endif
IOSimpleLockUnlock(gIOAllPCI2PCIBridgesLock);
return (kIOReturnSuccess);
}
IOReturn IOPCIBridge::restoreDeviceState( IOPCIDevice * device, IOOptionBits options )
{
IOReturn ret;
if (!device->savedConfig)
return (kIOReturnNotReady);
#ifndef __ppc__
ret = restoreMachineState(0);
if (kIOReturnSuccess != ret)
#endif
{
if (configShadow(device)->link.next)
{
IOSimpleLockLock(gIOAllPCI2PCIBridgesLock);
queue_remove( &gIOAllPCIDeviceRestoreQ,
configShadow(device),
IOPCIConfigShadow *,
link );
configShadow(device)->link.next = configShadow(device)->link.prev = NULL;
IOSimpleLockUnlock(gIOAllPCI2PCIBridgesLock);
}
ret = _restoreDeviceState(device, true);
}
return (kIOReturnSuccess);
}
bool IOPCIBridge::configure( IOService * provider )
{
return (true);
}
SInt32 IOPCIBridge::compareAddressCell( UInt32 , UInt32 cleft[], UInt32 cright[] )
{
IOPCIPhysicalAddress * left = (IOPCIPhysicalAddress *) cleft;
IOPCIPhysicalAddress * right = (IOPCIPhysicalAddress *) cright;
static const UInt8 spacesEq[] = { 0, 1, 2, 2 };
if (spacesEq[ left->physHi.s.space ] != spacesEq[ right->physHi.s.space ])
return (-1);
return (left->physLo - right->physLo);
}
void IOPCIBridge::nvLocation( IORegistryEntry * entry,
UInt8 * busNum, UInt8 * deviceNum, UInt8 * functionNum )
{
IOPCIDevice * nub;
nub = OSDynamicCast( IOPCIDevice, entry );
assert( nub );
*busNum = nub->space.s.busNum;
*deviceNum = nub->space.s.deviceNum;
*functionNum = nub->space.s.functionNum;
}
void IOPCIBridge::spaceFromProperties( OSDictionary * propTable,
IOPCIAddressSpace * space )
{
OSData * regProp;
IOPCIAddressSpace * inSpace;
space->bits = 0;
if ((regProp = (OSData *) propTable->getObject("reg")))
{
inSpace = (IOPCIAddressSpace *) regProp->getBytesNoCopy();
space->s.busNum = inSpace->s.busNum;
space->s.deviceNum = inSpace->s.deviceNum;
space->s.functionNum = inSpace->s.functionNum;
}
}
IORegistryEntry * IOPCIBridge::findMatching( OSIterator * kids,
IOPCIAddressSpace space )
{
IORegistryEntry * found = 0;
IOPCIAddressSpace regSpace;
if (kids)
{
kids->reset();
while ((0 == found)
&& (found = (IORegistryEntry *) kids->getNextObject()))
{
spaceFromProperties( found->getPropertyTable(), ®Space);
if (space.bits != regSpace.bits)
found = 0;
}
}
return (found);
}
bool IOPCIBridge::checkProperties( IOPCIDevice * entry )
{
UInt32 vendor, product, classCode, revID;
UInt32 subVendor = 0, subProduct = 0;
IOByteCount offset;
OSData * data;
OSData * nameData;
char compatBuf[128];
char * out;
if ((data = OSDynamicCast(OSData, entry->getProperty("vendor-id"))))
vendor = *((UInt32 *) data->getBytesNoCopy());
else
return (false);
if ((data = OSDynamicCast(OSData, entry->getProperty("device-id"))))
product = *((UInt32 *) data->getBytesNoCopy());
else
return (false);
if ((data = OSDynamicCast(OSData, entry->getProperty("class-code"))))
classCode = *((UInt32 *) data->getBytesNoCopy());
else
return (false);
if ((data = OSDynamicCast(OSData, entry->getProperty("revision-id"))))
revID = *((UInt32 *) data->getBytesNoCopy());
else
return (false);
if ((data = OSDynamicCast(OSData, entry->getProperty("subsystem-vendor-id"))))
subVendor = *((UInt32 *) data->getBytesNoCopy());
if ((data = OSDynamicCast(OSData, entry->getProperty("subsystem-id"))))
subProduct = *((UInt32 *) data->getBytesNoCopy());
if (entry->savedConfig)
{
entry->savedConfig[kIOPCIConfigVendorID >> 2] = (product << 16) | vendor;
entry->savedConfig[kIOPCIConfigRevisionID >> 2] = (classCode << 8) | revID;
if (subVendor && subProduct)
entry->savedConfig[kIOPCIConfigSubSystemVendorID >> 2] = (subProduct << 16) | subVendor;
}
if (!(data = OSDynamicCast(OSData, entry->getProperty("compatible")))
|| !(nameData = OSDynamicCast(OSData, entry->getProperty("name")))
|| data->isEqualTo(nameData))
{
out = compatBuf;
if ((subVendor || subProduct)
&& ((subVendor != vendor) || (subProduct != product)))
out += sprintf(out, "pci%lx,%lx", subVendor, subProduct) + 1;
out += sprintf(out, "pci%lx,%lx", vendor, product) + 1;
out += sprintf(out, "pciclass,%06lx", classCode) + 1;
entry->setProperty("compatible", compatBuf, out - compatBuf);
}
offset = 0;
if (entry->extendedFindPCICapability(kIOPCIPCIExpressCapability, &offset))
{
UInt32
value = entry->configRead16(offset + 0x12);
entry->setProperty(kIOPCIExpressLinkStatusKey, value, 32);
value = entry->configRead32(offset + 0x0c);
entry->setProperty(kIOPCIExpressLinkCapabilitiesKey, value, 32);
}
return (true);
}
OSDictionary * IOPCIBridge::constructProperties( IOPCIAddressSpace space )
{
OSDictionary * propTable;
UInt32 value;
UInt32 vendor, product, classCode, revID;
UInt32 subVendor = 0, subProduct = 0;
OSData * prop;
const char * name;
const OSSymbol * nameProp;
char compatBuf[128];
char * out;
struct IOPCIGenericNames
{
const char * name;
UInt32 mask;
UInt32 classCode;
};
static const IOPCIGenericNames genericNames[] = {
{ "display", 0xffffff, 0x000100 },
{ "scsi", 0xffff00, 0x010000 },
{ "ethernet", 0xffff00, 0x020000 },
{ "display", 0xff0000, 0x030000 },
{ "pci-bridge", 0xffff00, 0x060400 },
{ 0, 0, 0 }
};
const IOPCIGenericNames * nextName;
propTable = OSDictionary::withCapacity( 8 );
if (propTable)
{
prop = OSData::withBytes( &space, sizeof( space) );
if (prop)
{
propTable->setObject("reg", prop );
prop->release();
}
value = configRead32( space, kIOPCIConfigVendorID );
vendor = value & 0xffff;
product = value >> 16;
prop = OSData::withBytes( &vendor, sizeof(vendor) );
if (prop)
{
propTable->setObject("vendor-id", prop );
prop->release();
}
prop = OSData::withBytes( &product, sizeof(product) );
if (prop)
{
propTable->setObject("device-id", prop );
prop->release();
}
value = configRead32( space, kIOPCIConfigRevisionID );
revID = value & 0xff;
prop = OSData::withBytes( &revID, sizeof(revID) );
if (prop)
{
propTable->setObject("revision-id", prop );
prop->release();
}
classCode = value >> 8;
prop = OSData::withBytes( &classCode, sizeof(classCode) );
if (prop)
{
propTable->setObject("class-code", prop );
prop->release();
}
name = 0;
for (nextName = genericNames;
(0 == name) && nextName->name;
nextName++)
{
if ((classCode & nextName->mask) == nextName->classCode)
name = nextName->name;
}
value = configRead32( space, kIOPCIConfigSubSystemVendorID );
if (value)
{
subVendor = value & 0xffff;
subProduct = value >> 16;
prop = OSData::withBytes( &subVendor, sizeof(subVendor) );
if (prop)
{
propTable->setObject("subsystem-vendor-id", prop );
prop->release();
}
prop = OSData::withBytes( &subProduct, sizeof(subProduct) );
if (prop)
{
propTable->setObject("subsystem-id", prop );
prop->release();
}
}
out = compatBuf;
if ((subVendor || subProduct)
&& ((subVendor != vendor) || (subProduct != product)))
out += sprintf(out, "pci%lx,%lx", subVendor, subProduct) + 1;
if (0 == name)
name = out;
out += sprintf(out, "pci%lx,%lx", vendor, product) + 1;
out += sprintf(out, "pciclass,%06lx", classCode) + 1;
prop = OSData::withBytes( compatBuf, out - compatBuf );
if (prop)
{
propTable->setObject("compatible", prop );
prop->release();
}
nameProp = OSSymbol::withCString( name );
if (nameProp)
{
propTable->setObject( "name", (OSSymbol *) nameProp);
propTable->setObject( gIONameKey, (OSSymbol *) nameProp);
nameProp->release();
}
}
return (propTable);
}
IOPCIDevice * IOPCIBridge::createNub( OSDictionary * from )
{
return (new IOPCIDevice);
}
bool IOPCIBridge::initializeNub( IOPCIDevice * nub,
OSDictionary * from )
{
spaceFromProperties( from, &nub->space);
nub->parent = this;
if (ioDeviceMemory())
nub->ioMap = ioDeviceMemory()->map();
return (true);
}
void IOPCIBridge::removeDevice( IOPCIDevice * device, IOOptionBits options )
{
IOReturn ret = kIOReturnSuccess;
if (device->reserved->msiConfig && gIOPCIMessagedInterruptController)
ret = gIOPCIMessagedInterruptController->deallocateDeviceInterrupts(this, device);
IOSimpleLockLock(gIOAllPCI2PCIBridgesLock);
if (configShadow(device)->link.next)
{
queue_remove( &gIOAllPCIDeviceRestoreQ,
configShadow(device),
IOPCIConfigShadow *,
link );
configShadow(device)->link.next = configShadow(device)->link.prev = NULL;
}
IOSimpleLockUnlock(gIOAllPCI2PCIBridgesLock);
}
bool IOPCIBridge::publishNub( IOPCIDevice * nub, UInt32 )
{
char location[ 24 ];
bool ok;
OSData * data;
OSData * driverData;
UInt32 *regData, expRomReg;
IOMemoryMap * memoryMap;
IOVirtualAddress virtAddr;
if (nub)
{
if (nub->space.s.functionNum)
sprintf( location, "%X,%X", nub->space.s.deviceNum,
nub->space.s.functionNum );
else
sprintf( location, "%X", nub->space.s.deviceNum );
nub->setLocation( location );
IODTFindSlotName( nub, nub->space.s.deviceNum );
IOPCIConfigShadow * shadow = IONew(IOPCIConfigShadow, 1);
if (shadow)
{
bzero(shadow, sizeof(IOPCIConfigShadow));
shadow->device = nub;
nub->savedConfig = &shadow->savedConfig[0];
for (int i = 0; i < kIOPCIConfigShadowRegs; i++)
if (!(kIOPCIVolatileRegsMask & (1 << i)))
nub->savedConfig[i] = nub->configRead32( i << 2 );
}
checkProperties( nub );
if (shadow && (kIOPCIClassBridge == nub->savedConfig[kIOPCIConfigRevisionID >> 2] >> 24))
shadow->flags |= kIOPCIConfigShadowBridge;
if ((data = (OSData *)nub->getProperty("driver-reg,AAPL,MacOSX,PowerPC")))
{
if (data->getLength() == (2 * sizeof(UInt32)))
{
regData = (UInt32 *)data->getBytesNoCopy();
getNubResources(nub);
memoryMap = nub->mapDeviceMemoryWithRegister(kIOPCIConfigExpansionROMBase);
if (memoryMap != 0)
{
virtAddr = memoryMap->getVirtualAddress();
virtAddr += regData[0];
nub->setMemoryEnable(true);
expRomReg = nub->configRead32(kIOPCIConfigExpansionROMBase);
nub->configWrite32(kIOPCIConfigExpansionROMBase, expRomReg | 1);
driverData = OSData::withBytesNoCopy((void *)virtAddr, regData[1]);
if (driverData != 0)
{
gIOCatalogue->addExtensionsFromArchive(driverData);
driverData->release();
}
nub->configWrite32(kIOPCIConfigExpansionROMBase, expRomReg);
nub->setMemoryEnable(false);
memoryMap->release();
}
}
}
ok = nub->attach( this );
if (ok)
{
nub->callPlatformFunction(gIOPlatformDeviceMessageKey, false,
(void *) kIOMessageDeviceWillPowerOff, nub, (void *) 0, (void *) 0);
nub->callPlatformFunction(gIOPlatformDeviceMessageKey, false,
(void *) kIOMessageDeviceHasPoweredOn, nub, (void *) 0, (void *) 0);
nub->registerService();
}
}
else
ok = false;
return (ok);
}
UInt8 IOPCIBridge::firstBusNum( void )
{
return (0);
}
UInt8 IOPCIBridge::lastBusNum( void )
{
return (255);
}
void IOPCIBridge::probeBus( IOService * provider, UInt8 busNum )
{
IORegistryEntry * found;
IOService * service;
OSDictionary * propTable;
IOPCIDevice * nub = 0;
OSIterator * kidsIter;
UInt32 index = 0;
UInt32 idx = 0;
bool hotplugBus;
hotplugBus = (0 != getProperty(kIOPCIHotPlugKey));
if (hotplugBus && !provider->getProperty(kIOPCIOnlineKey))
{
LOG("offline\n");
return;
}
#if __i386__
bool configured;
configured = (0 != provider->getProperty(kIOPCIConfiguredKey));
if (!configured)
{
IOPCIConfigurator * pciConfig = new IOPCIConfigurator;
if (pciConfig && pciConfig->init())
{
pciConfig->attach(this);
pciConfig->start(this);
pciConfig->detach(this);
pciConfig->release();
}
}
#endif
IODTSetResolving(provider, &compareAddressCell, &nvLocation);
if (2 & gIOPCIDebug)
kidsIter = 0;
else
kidsIter = provider->getChildIterator( gIODTPlane );
OSArray * nubs = OSArray::withCapacity(0x10);
assert(nubs);
if (kidsIter) {
kidsIter->reset();
while ((found = (IORegistryEntry *) kidsIter->getNextObject()))
{
if (!found->getProperty("vendor-id"))
continue;
if ((service = OSDynamicCast(IOService, found)) && service->isInactive())
continue;
propTable = found->getPropertyTable();
nub = createNub( propTable );
if ( nub
&& initializeNub(nub, propTable)
&& nub->init(found, gIODTPlane) )
{
IOByteCount capa;
nubs->setObject(index++, nub);
capa = 0;
if (nub->extendedFindPCICapability(kIOPCIPCIExpressCapability, &capa))
{
nub->reserved->expressConfig = capa;
nub->reserved->expressCapabilities = nub->configRead16(capa + 0x02);
}
capa = 0;
#if 0
if (nub->extendedFindPCICapability(kIOPCIMSIXCapability, &capa))
{
nub->reserved->msiConfig = capa;
nub->reserved->msiMode |= kMSIX;
}
else
#endif
if (nub->extendedFindPCICapability(kIOPCIMSICapability, &capa))
nub->reserved->msiConfig = capa;
}
if (nub)
nub->release();
}
}
idx = 0;
while (nub = (IOPCIDevice *)nubs->getObject(idx++))
{
if (hotplugBus || provider->getProperty(kIOPCIEjectableKey))
nub->setProperty(kIOPCIEjectableKey, kOSBooleanTrue);
publishNub(nub , idx);
if (1 & gIOPCIDebug)
IOLog("%08lx = 0:%08lx 4:%08lx ", nub->space.bits,
nub->configRead32(kIOPCIConfigVendorID),
nub->configRead32(kIOPCIConfigCommand) );
}
nubs->release();
if (kidsIter)
kidsIter->release();
}
bool IOPCIBridge::addBridgeMemoryRange( IOPhysicalAddress start,
IOPhysicalLength length, bool host )
{
IORangeAllocator * platformRanges;
bool ok = true;
if (host)
{
platformRanges = getPlatform()->getPhysicalRangeAllocator();
assert( platformRanges );
ok = platformRanges->allocateRange( start, length );
if (!ok)
kprintf("%s: didn't get host range (%08lx:%08lx)\n", getName(),
start, length);
}
bridgeMemoryRanges->deallocate( start, length );
IOPCIRange * newRange = IONew(IOPCIRange, 1);
if (newRange)
{
bzero(newRange, sizeof(IOPCIRange));
newRange->start = start;
newRange->size = length;
newRange->alignment = 0;
newRange->type = kIOPCIResourceTypeMemory;
newRange->flags = 0;
newRange->next = reserved->rangeLists[kIOPCIResourceTypeMemory];
reserved->rangeLists[kIOPCIResourceTypeMemory] = newRange;
}
return (ok);
}
bool IOPCIBridge::addBridgePrefetchableMemoryRange( IOPhysicalAddress start,
IOPhysicalLength length,
bool host )
{
IORangeAllocator * platformRanges;
bool ok = true;
if (host)
{
platformRanges = getPlatform()->getPhysicalRangeAllocator();
assert( platformRanges );
ok = platformRanges->allocateRange( start, length );
if (!ok)
kprintf("%s: didn't get host range (%08lx:%08lx)\n", getName(),
start, length);
}
bridgeMemoryRanges->deallocate( start, length );
IOPCIRange * newRange = IONew(IOPCIRange, 1);
if (newRange)
{
bzero(newRange, sizeof(IOPCIRange));
newRange->start = start;
newRange->size = length;
newRange->alignment = 0;
newRange->type = kIOPCIResourceTypePrefetchMemory;
newRange->flags = 0;
newRange->next = reserved->rangeLists[kIOPCIResourceTypePrefetchMemory];
reserved->rangeLists[kIOPCIResourceTypePrefetchMemory] = newRange;
}
return (ok);
}
bool IOPCIBridge::addBridgeIORange( IOByteCount start, IOByteCount length )
{
bool ok = true;
bridgeIORanges->deallocate( start, length );
IOPCIRange * newRange = IONew(IOPCIRange, 1);
if (newRange)
{
bzero(newRange, sizeof(IOPCIRange));
newRange->start = start;
newRange->size = length;
newRange->alignment = 0;
newRange->type = kIOPCIResourceTypeIO;
newRange->flags = 0;
newRange->next = reserved->rangeLists[kIOPCIResourceTypeIO];
reserved->rangeLists[kIOPCIResourceTypeIO] = newRange;
}
return (ok);
}
bool IOPCIBridge::constructRange( IOPCIAddressSpace * flags,
IOPhysicalAddress phys,
IOPhysicalLength len,
OSArray * array )
{
IODeviceMemory * range;
IODeviceMemory * ioMemory;
IORangeAllocator * bridgeRanges;
bool ok;
if (!array)
return (false);
if (kIOPCIIOSpace == flags->s.space)
{
bridgeRanges = bridgeIORanges;
if ((ioMemory = ioDeviceMemory()))
{
phys &= 0x00ffffff; range = IODeviceMemory::withSubRange( ioMemory, phys, len );
if (range == 0)
range = IODeviceMemory::withRange(
phys + ioMemory->getPhysicalAddress(), len );
}
else
range = 0;
}
else
{
bridgeRanges = bridgeMemoryRanges;
range = IODeviceMemory::withRange( phys, len );
}
if (range)
{
ok = bridgeRanges->allocateRange( phys, len );
#ifdef __ppc__
if (!ok)
IOLog("%s: bad range %d(%08lx:%08lx)\n", getName(), flags->s.space,
phys, len);
#endif
range->setTag( flags->bits );
ok = array->setObject( range );
range->release();
}
else
ok = false;
return (ok);
}
IOReturn IOPCIBridge::getDTNubAddressing( IOPCIDevice * regEntry )
{
OSArray * array;
IORegistryEntry * parentEntry;
OSData * addressProperty;
IOPhysicalAddress phys;
IOPhysicalLength len;
UInt32 cells = 5;
int i, num;
UInt32 * reg;
addressProperty = (OSData *) regEntry->getProperty( "assigned-addresses" );
if (0 == addressProperty)
return (kIOReturnSuccess);
parentEntry = regEntry->getParentEntry( gIODTPlane );
if (0 == parentEntry)
return (kIOReturnBadArgument);
array = OSArray::withCapacity( 1 );
if (0 == array)
return (kIOReturnNoMemory);
reg = (UInt32 *) addressProperty->getBytesNoCopy();
num = addressProperty->getLength() / (4 * cells);
for (i = 0; i < num; i++)
{
if (IODTResolveAddressCell(parentEntry, reg, &phys, &len))
constructRange( (IOPCIAddressSpace *) reg, phys, len, array );
reg += cells;
}
if (array->getCount())
regEntry->setProperty( gIODeviceMemoryKey, array);
array->release();
return (kIOReturnSuccess);
}
struct SafeProbeParam {
IOPCIDevice * nub;
UInt8 regNum;
UInt32 value;
UInt32 save;
};
static void probeBAR( void * refcon )
{
SafeProbeParam * param = (SafeProbeParam *)refcon;
IOPCIDevice * nub;
nub = param->nub;
param->save = nub->configRead32( param->regNum );
nub->configWrite32( param->regNum, 0xffffffff );
param->value = nub->configRead32( param->regNum );
nub->configWrite32( param->regNum, param->save );
}
#ifdef __i386__
static void safeProbeBAR( void * refcon )
{
assert(refcon);
if (cpu_number() == 0)
probeBAR(refcon);
}
#else
static void safeProbeBAR( void * refcon )
{
SafeProbeParam * param = (SafeProbeParam *)refcon;
IOPCIDevice * nub;
bool memEna, ioEna;
boolean_t s;
nub = param->nub;
s = ml_set_interrupts_enabled(FALSE);
memEna = nub->setMemoryEnable( false );
ioEna = nub->setIOEnable( false );
probeBAR(refcon);
nub->setMemoryEnable( memEna );
nub->setIOEnable( ioEna );
ml_set_interrupts_enabled( s );
}
#endif
IOReturn IOPCIBridge::getNubAddressing( IOPCIDevice * nub )
{
OSArray * array;
OSData * assignedProp;
IOPhysicalAddress phys;
IOPhysicalLength len;
UInt32 save, value;
IOPCIAddressSpace reg;
UInt8 regNum;
UInt8 headerType;
SafeProbeParam probeParam;
value = nub->configRead32( kIOPCIConfigRevisionID );
if ((value >> 8) == 0x060000) return (kIOReturnSuccess);
value = nub->configRead32( kIOPCIConfigVendorID );
if (0x0003106b == value) return (kIOReturnSuccess);
headerType = nub->configRead8( kIOPCIConfigHeaderType ) & 0x7f;
if (headerType != 0 && headerType != 2)
return (kIOReturnSuccess);
array = OSArray::withCapacity( 1 );
if (0 == array)
return (kIOReturnNoMemory);
assignedProp = OSData::withCapacity( 3 * sizeof(IOPCIPhysicalAddress) );
if (0 == assignedProp)
return (kIOReturnNoMemory);
for (regNum = 0x10; regNum < 0x28; regNum += 4)
{
if ( (2 == headerType) && (regNum > 0x10) )
break;
probeParam.nub = nub;
probeParam.regNum = regNum;
#ifdef __i386__
mp_rendezvous_no_intrs(&safeProbeBAR, &probeParam);
#else
safeProbeBAR(&probeParam);
#endif
value = probeParam.value;
save = probeParam.save;
if (0 == value)
continue;
reg = nub->space;
reg.s.registerNum = regNum;
if (value & 1)
{
reg.s.space = kIOPCIIOSpace;
if ((value & 0xFFFF0000) == 0)
{
value = value | 0xFFFF0000;
}
}
else
{
reg.s.prefetch = (0 != (value & 8));
switch (value & 6)
{
case 2:
reg.s.t = 1;
case 0:
case 6:
reg.s.space = kIOPCI32BitMemorySpace;
break;
case 4:
reg.s.space = kIOPCI64BitMemorySpace;
regNum += 4;
break;
}
}
value &= 0xfffffff0;
phys = IOPhysical32( 0, save & value );
len = IOPhysical32( 0, -value );
if (assignedProp)
{
IOPCIPhysicalAddress assigned;
assigned.physHi = reg;
assigned.physMid = 0;
assigned.physLo = phys;
assigned.lengthHi = 0;
assigned.lengthLo = len;
assignedProp->appendBytes( &assigned, sizeof(assigned) );
}
if (1 & gIOPCIDebug)
IOLog("Space %08lx : %08lx, %08lx\n", reg.bits, phys, len);
constructRange( ®, phys, len, array );
}
if (array->getCount())
nub->setProperty( gIODeviceMemoryKey, array);
array->release();
if (assignedProp->getLength())
nub->setProperty( "assigned-addresses", assignedProp );
assignedProp->release();
return (kIOReturnSuccess);
}
bool IOPCIBridge::isDTNub( IOPCIDevice * nub )
{
return (0 != nub->getParentEntry(gIODTPlane));
}
IOReturn IOPCIBridge::getNubResources( IOService * service )
{
IOPCIDevice * nub = (IOPCIDevice *) service;
IOReturn err;
if (service->getProperty(kIOPCIResourcedKey))
return (kIOReturnSuccess);
service->setProperty(kIOPCIResourcedKey, kOSBooleanTrue);
#ifdef __i386__
err = getDTNubAddressing( nub );
#else
if (isDTNub(nub))
err = getDTNubAddressing( nub );
else
err = getNubAddressing( nub );
#endif
bool
msiDefault = (false
#if 0
|| (0 == strcmp("display", nub->getName()))
|| (0 == strcmp("GFX0", nub->getName()))
|| (0 == strcmp("PXS1", nub->getName())) || (0 == strcmp("HDEF", nub->getName()))
|| (0 == strcmp("SATA", nub->getName()))
|| (0 == strcmp("LAN0", nub->getName()))
|| (0 == strcmp("LAN1", nub->getName()))
|| (0 == strcmp("PXS2", nub->getName())) || (0 == strcmp("PXS3", nub->getName())) #endif
);
IOService * provider = getProvider();
if (msiDefault)
resolveMSIInterrupts( provider, nub );
resolveLegacyInterrupts( provider, nub );
if (!msiDefault)
resolveMSIInterrupts( provider, nub );
return (err);
}
bool IOPCIBridge::matchKeys( IOPCIDevice * nub, const char * keys,
UInt32 defaultMask, UInt8 regNum )
{
const char * next;
UInt32 mask, value, reg;
bool found = false;
do
{
value = strtoul( keys, (char **) &next, 16);
if (next == keys)
break;
while ((*next) == ' ')
next++;
if ((*next) == '&')
mask = strtoul( next + 1, (char **) &next, 16);
else
mask = defaultMask;
reg = nub->savedConfig[ regNum >> 2 ];
found = ((value & mask) == (reg & mask));
keys = next;
}
while (!found);
return (found);
}
bool IOPCIBridge::pciMatchNub( IOPCIDevice * nub,
OSDictionary * table,
SInt32 * score )
{
OSString * prop;
const char * keys;
bool match = true;
UInt8 regNum;
int i;
struct IOPCIMatchingKeys
{
const char * propName;
UInt8 regs[ 4 ];
UInt32 defaultMask;
};
const IOPCIMatchingKeys * look;
static const IOPCIMatchingKeys matching[] = {
{ kIOPCIMatchKey,
{ 0x00 + 1, 0x2c }, 0xffffffff },
{ kIOPCIPrimaryMatchKey,
{ 0x00 }, 0xffffffff },
{ kIOPCISecondaryMatchKey,
{ 0x2c }, 0xffffffff },
{ kIOPCIClassMatchKey,
{ 0x08 }, 0xffffff00 }};
for (look = matching;
(match && (look < &matching[4]));
look++)
{
prop = (OSString *) table->getObject( look->propName );
if (prop)
{
keys = prop->getCStringNoCopy();
match = false;
for (i = 0;
((false == match) && (i < 4));
i++)
{
regNum = look->regs[ i ];
match = matchKeys( nub, keys,
look->defaultMask, regNum & 0xfc );
if (0 == (1 & regNum))
break;
}
}
}
return (match);
}
bool IOPCIBridge::matchNubWithPropertyTable( IOService * nub,
OSDictionary * table,
SInt32 * score )
{
bool matches;
matches = pciMatchNub( (IOPCIDevice *) nub, table, score);
return (matches);
}
bool IOPCIBridge::compareNubName( const IOService * nub,
OSString * name, OSString ** matched ) const
{
return (IODTCompareNubName(nub, name, matched));
}
UInt32 IOPCIBridge::findPCICapability( IOPCIAddressSpace space,
UInt8 capabilityID, UInt8 * found )
{
UInt32 data = 0;
UInt8 offset;
if (found)
*found = 0;
if (0 == ((kIOPCIStatusCapabilities << 16)
& (configRead32(space, kIOPCIConfigCommand))))
return (0);
offset = (0xff & configRead32(space, kIOPCIConfigCapabilitiesPtr));
if (offset & 3)
offset = 0;
while (offset)
{
data = configRead32( space, offset );
if (capabilityID == (data & 0xff))
{
if (found)
*found = offset;
break;
}
offset = (data >> 8) & 0xff;
if (offset & 3)
offset = 0;
}
return (offset ? data : 0);
}
UInt32 IOPCIBridge::extendedFindPCICapability( IOPCIAddressSpace space,
UInt32 capabilityID, IOByteCount * found )
{
UInt32 data = 0;
IOByteCount offset, firstOffset = 0;
if (found)
{
firstOffset = *found;
*found = 0;
}
if (0 == ((kIOPCIStatusCapabilities << 16)
& (configRead32(space, kIOPCIConfigCommand))))
return (0);
if (capabilityID >= 0x100)
{
capabilityID =- capabilityID;
offset = 0x100;
while (offset)
{
space.es.registerNumExtended = (offset >> 8);
data = configRead32( space, offset );
if ((offset > firstOffset) && (capabilityID == (data & 0xffff)))
{
if (found)
*found = offset;
break;
}
offset = (data >> 20) & 0xfff;
if ((offset < 0x100) || (offset & 3))
offset = 0;
}
}
else
{
offset = (0xff & configRead32(space, kIOPCIConfigCapabilitiesPtr));
if (offset & 3)
offset = 0;
while (offset)
{
data = configRead32( space, offset );
if ((offset > firstOffset) && (capabilityID == (data & 0xff)))
{
if (found)
*found = offset;
break;
}
offset = (data >> 8) & 0xff;
if (offset & 3)
offset = 0;
}
}
return (offset ? data : 0);
}
IOReturn IOPCIBridge::createAGPSpace( IOAGPDevice * master,
IOOptionBits options,
IOPhysicalAddress * address,
IOPhysicalLength * length )
{
return (kIOReturnUnsupported);
}
IOReturn IOPCIBridge::destroyAGPSpace( IOAGPDevice * master )
{
return (kIOReturnUnsupported);
}
IORangeAllocator * IOPCIBridge::getAGPRangeAllocator( IOAGPDevice * master )
{
return (0);
}
IOOptionBits IOPCIBridge::getAGPStatus( IOAGPDevice * master,
IOOptionBits options )
{
return (0);
}
IOReturn IOPCIBridge::commitAGPMemory( IOAGPDevice * master,
IOMemoryDescriptor * memory,
IOByteCount agpOffset,
IOOptionBits options )
{
return (kIOReturnUnsupported);
}
IOReturn IOPCIBridge::releaseAGPMemory( IOAGPDevice * master,
IOMemoryDescriptor * memory,
IOByteCount agpOffset,
IOOptionBits options )
{
return (kIOReturnUnsupported);
}
IOReturn IOPCIBridge::resetAGPDevice( IOAGPDevice * master,
IOOptionBits options )
{
return (kIOReturnUnsupported);
}
IOReturn IOPCIBridge::getAGPSpace( IOAGPDevice * master,
IOPhysicalAddress * address,
IOPhysicalLength * length )
{
return (kIOReturnUnsupported);
}
#undef super
#define super IOPCIBridge
OSDefineMetaClassAndStructors(IOPCI2PCIBridge, IOPCIBridge)
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 0);
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 1);
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 2);
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 3);
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 4);
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 5);
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 6);
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 7);
OSMetaClassDefineReservedUnused(IOPCI2PCIBridge, 8);
IOService * IOPCI2PCIBridge::probe( IOService * provider,
SInt32 * score )
{
if (0 == (bridgeDevice = OSDynamicCast(IOPCIDevice, provider)))
return (0);
*score -= 100;
return (this);
}
bool IOPCI2PCIBridge::serializeProperties( OSSerialize * serialize ) const
{
return (super::serializeProperties(serialize));
}
bool IOPCI2PCIBridge::filterInterrupt( IOFilterInterruptEventSource * source)
{
enum { kNeedMask = ((1 << 8) | (1 << 3)) };
uint16_t slotStatus = bridgeDevice->configRead16( fXpressCapability + 0x1a );
if (kNeedMask & slotStatus)
bridgeDevice->configWrite16( fXpressCapability + 0x1a, slotStatus );
return (0 != (kNeedMask & slotStatus));
}
void IOPCI2PCIBridge::handleInterrupt( IOInterruptEventSource * source,
int count )
{
bool present;
fHotplugCount++;
uint16_t slotStatus = bridgeDevice->configRead16( fXpressCapability + 0x1a );
uint16_t linkStatus = bridgeDevice->configRead16( fXpressCapability + 0x12 );
uint16_t linkControl = bridgeDevice->configRead16( fXpressCapability + 0x10 );
LOG("hotpInt (%d), slotStatus %x, linkStatus %x, linkControl %x\n",
fHotplugCount, slotStatus, linkStatus, linkControl);
present = (0 != ((1 << 6) & slotStatus));
if (present && ((1 << 4) & linkControl))
{
LOG("enabling link\n");
linkControl &= ~(1 << 4);
bridgeDevice->configWrite16( fXpressCapability + 0x10, linkControl );
fWaitingLinkEnable = true;
present = false;
}
else if (!present && !((1 << 4) & linkControl))
{
if (fWaitingLinkEnable)
fWaitingLinkEnable = false;
else
{
LOG("disabling link\n");
bridgeDevice->configWrite16( fXpressCapability + 0x10, linkControl | (1 << 4) );
}
}
if (fLinkChangeOnly)
return;
present &= (0 != ((1 << 13) & linkStatus));
if (fPresence == present)
{
if (present)
{
IOService * dev = OSDynamicCast(IOService, bridgeDevice->getChildEntry(gIODTPlane));
if (dev && kOSBooleanFalse == dev->getProperty(kIOPCIOnlineKey))
{
LOG("eject-terminate %p\n", dev);
dev->terminate();
LOG("did terminate %p\n", dev);
}
}
return;
}
bridgeDevice->removeProperty(kIOPCIConfiguredKey);
fPresence = present;
if (!present)
{
bridgeDevice->removeProperty(kIOPCIOnlineKey);
IOService * dev = OSDynamicCast(IOService, bridgeDevice->getChildEntry(gIODTPlane));
if (dev)
{
LOG("hotp-terminate %p\n", dev);
dev->terminate();
LOG("did terminate %p\n", dev);
}
}
else
{
bridgeDevice->setProperty(kIOPCIOnlineKey, true);
#if 0
if (fHotplugLastBus <= fHotplugFirstBus)
{
fHotplugFirstBus = 36;
fHotplugLastBus = 40;
uint32_t reg32 = bridgeDevice->configRead32(kPCI2PCIPrimaryBus);
reg32 &= ~0x00ffff00;
bridgeDevice->configWrite32(kPCI2PCIPrimaryBus, reg32 | (0xff << 8) | (0x00 << 16));
reg32 |= (fHotplugFirstBus << 8) | (fHotplugLastBus << 16);
bridgeDevice->configWrite32(kPCI2PCIPrimaryBus, reg32);
fHotplugFirstBus = bridgeDevice->configRead8( kPCI2PCISecondaryBus );
fHotplugLastBus = bridgeDevice->configRead8( kPCI2PCISubordinateBus );
LOG("making first bus %d, last bus %d\n", fHotplugFirstBus, fHotplugLastBus);
}
#endif
IOSleep(1* 1000);
LOG("probeBus()\n");
setProperty(kIOPCIResetKey, kOSBooleanTrue);
probeBus( bridgeDevice, firstBusNum() );
}
}
bool IOPCI2PCIBridge::start( IOService * provider )
{
bool ok;
reserved = IONew(ExpansionData, 1);
if (reserved == 0) return (false);
bzero(reserved, sizeof(ExpansionData));
ok = super::start(provider);
if (ok && fBridgeInterruptSource)
changePowerStateTo(2);
return (ok);
}
bool IOPCI2PCIBridge::configure( IOService * provider )
{
UInt32 end;
UInt32 start;
bool ok;
IOByteCount offset = 0;
if (bridgeDevice->extendedFindPCICapability(kIOPCIPCIExpressCapability, &offset))
fXpressCapability = offset;
if (fXpressCapability)
do
{
IOReturn ret;
if (bridgeDevice->getProperty(kIOPCIHotPlugKey))
setProperty(kIOPCIHotPlugKey, kOSBooleanTrue);
else if (bridgeDevice->getProperty(kIOPCILinkChangeKey))
{
setProperty(kIOPCILinkChangeKey, kOSBooleanTrue);
fLinkChangeOnly = true;
}
else
break;
if (!gCommonWorkLoop)
gCommonWorkLoop = IOWorkLoop::workLoop();
fWorkLoop = gCommonWorkLoop;
fBridgeInterruptSource = IOFilterInterruptEventSource::filterInterruptEventSource(
this,
OSMemberFunctionCast(IOInterruptEventSource::Action,
this, &IOPCI2PCIBridge::handleInterrupt),
OSMemberFunctionCast(IOFilterInterruptEventSource::Filter,
this, &IOPCI2PCIBridge::filterInterrupt),
provider, 0);
if (!fBridgeInterruptSource)
break;
ret = fWorkLoop->addEventSource( fBridgeInterruptSource );
if (kIOReturnSuccess != ret)
break;
uint16_t slotStatus = bridgeDevice->configRead16( fXpressCapability + 0x1a );
uint16_t linkStatus = bridgeDevice->configRead16( fXpressCapability + 0x12 );
uint16_t linkControl = bridgeDevice->configRead16( fXpressCapability + 0x10 );
LOG("hotp configure slotStatus %x, linkStatus %x, linkControl %x\n",
slotStatus, linkStatus, linkControl);
fPresence = (0 != ((1 << 13) & linkStatus));
fPresence &= (0 != ((1 << 6) & slotStatus));
if (fPresence)
{
if (!fLinkChangeOnly)
bridgeDevice->setProperty(kIOPCIOnlineKey, kOSBooleanTrue);
}
else if (!((1 << 4) & linkControl))
{
LOG("disable link\n");
linkControl |= (1 << 4);
bridgeDevice->configWrite16( fXpressCapability + 0x10, linkControl );
}
fBridgeInterruptEnablePending = true;
}
while(false);
uint8_t secBus = bridgeDevice->configRead8( kPCI2PCISecondaryBus );
if (!secBus)
{
LOG("Bridge has secBus 0\n");
return (false);
}
end = bridgeDevice->configRead32( kPCI2PCIMemoryRange );
if (end)
{
start = (end & 0xfff0) << 16;
end |= 0x000fffff;
ok = addBridgeMemoryRange( start, end - start + 1, false );
}
end = bridgeDevice->configRead32( kPCI2PCIPrefetchMemoryRange );
if (end)
{
start = (end & 0xfff0) << 16;
end |= 0x000fffff;
ok = addBridgePrefetchableMemoryRange( start, end - start + 1, false );
}
end = bridgeDevice->configRead32( kPCI2PCIIORange );
if (end)
{
start = (end & 0xf0) << 8;
end = (end & 0xffff) | 0xfff;
ok = addBridgeIORange( start, end - start + 1 );
}
saveBridgeState();
if (bridgeDevice->savedConfig)
{
configShadow(bridgeDevice)->flags |= kIOPCIConfigShadowBridge;
configShadow(bridgeDevice)->bridge = this;
}
return (super::configure(provider));
}
void IOPCI2PCIBridge::probeBus( IOService * provider, UInt8 busNum )
{
super::probeBus(provider, busNum);
if (fBridgeInterruptEnablePending)
{
uint16_t slotControl = bridgeDevice->configRead16( fXpressCapability + 0x18 );
bridgeDevice->configWrite16( fXpressCapability + 0x1a, 1 << 3 );
slotControl |= (1 << 12) | (1 << 5) | (1 << 3);
bridgeDevice->configWrite16( fXpressCapability + 0x18, slotControl );
fBridgeInterruptSource->enable();
fBridgeInterruptEnablePending = false;
}
}
IOReturn IOPCI2PCIBridge::requestProbe( IOOptionBits options )
{
if (kIOPCIProbeOptionEject != options)
return (super::requestProbe(options));
if (!fBridgeInterruptSource || fBridgeInterruptEnablePending)
return (kIOReturnNotReady);
fBridgeInterruptSource->signalInterrupt();
return (kIOReturnSuccess);
}
IOReturn IOPCI2PCIBridge::setPowerState( unsigned long powerState,
IOService * whatDevice )
{
if (powerState && reserved && fBridgeInterruptSource && !fBridgeInterruptEnablePending)
{
fBridgeInterruptSource->signalInterrupt();
}
return (super::setPowerState(powerState, whatDevice));
}
void IOPCI2PCIBridge::stop( IOService * provider )
{
if (reserved && fBridgeInterruptSource)
{
fBridgeInterruptSource->disable();
IOWorkLoop * tempWL = fBridgeInterruptSource->getWorkLoop();
if (tempWL)
tempWL->removeEventSource(fBridgeInterruptSource);
fBridgeInterruptSource->release();
fBridgeInterruptSource = 0;
}
super::stop( provider);
}
void IOPCI2PCIBridge::free()
{
super::free();
}
void IOPCI2PCIBridge::saveBridgeState( void )
{
long cnt;
for (cnt = 0; cnt < kIOPCIBridgeRegs; cnt++)
{
bridgeState[cnt] = bridgeDevice->configRead32(cnt * 4);
}
}
void IOPCI2PCIBridge::restoreBridgeState( void )
{
long cnt;
for (cnt = (kIOPCIConfigCommand >> 2) + 1; cnt < kIOPCIBridgeRegs; cnt++)
{
bridgeDevice->configWrite32(cnt * 4, bridgeState[cnt]);
}
bridgeDevice->configWrite32(kIOPCIConfigCommand,
bridgeState[kIOPCIConfigCommand >> 2]);
}
UInt8 IOPCI2PCIBridge::firstBusNum( void )
{
return bridgeDevice->configRead8( kPCI2PCISecondaryBus );
}
UInt8 IOPCI2PCIBridge::lastBusNum( void )
{
return bridgeDevice->configRead8( kPCI2PCISubordinateBus );
}
IOPCIAddressSpace IOPCI2PCIBridge::getBridgeSpace( void )
{
return (bridgeDevice->space);
}
UInt32 IOPCI2PCIBridge::configRead32( IOPCIAddressSpace space,
UInt8 offset )
{
return (bridgeDevice->configRead32(space, offset));
}
void IOPCI2PCIBridge::configWrite32( IOPCIAddressSpace space,
UInt8 offset, UInt32 data )
{
bridgeDevice->configWrite32( space, offset, data );
}
UInt16 IOPCI2PCIBridge::configRead16( IOPCIAddressSpace space,
UInt8 offset )
{
return (bridgeDevice->configRead16(space, offset));
}
void IOPCI2PCIBridge::configWrite16( IOPCIAddressSpace space,
UInt8 offset, UInt16 data )
{
bridgeDevice->configWrite16( space, offset, data );
}
UInt8 IOPCI2PCIBridge::configRead8( IOPCIAddressSpace space,
UInt8 offset )
{
return (bridgeDevice->configRead8(space, offset));
}
void IOPCI2PCIBridge::configWrite8( IOPCIAddressSpace space,
UInt8 offset, UInt8 data )
{
bridgeDevice->configWrite8( space, offset, data );
}
IODeviceMemory * IOPCI2PCIBridge::ioDeviceMemory( void )
{
return (bridgeDevice->ioDeviceMemory());
}
bool IOPCI2PCIBridge::publishNub( IOPCIDevice * nub, UInt32 index )
{
if (nub)
nub->setProperty( "IOChildIndex" , index, 32 );
return (super::publishNub(nub, index));
}
IOReturn IOPCIBridge::resolveMSIInterrupts( IOService * provider, IOPCIDevice * nub )
{
IOReturn ret = kIOReturnUnsupported;
#ifdef __i386__
IOByteCount msi = nub->reserved->msiConfig;
if (msi) do
{
uint16_t control = nub->configRead16(msi + 2);
uint32_t numMessages;
if (kMSIX & nub->reserved->msiMode)
numMessages = 1 + (0x7ff & control);
else
numMessages = 1 << (0x7 & (control >> 1));
IOLockLock(gIOPCIMessagedInterruptControllerLock);
if (!gIOPCIMessagedInterruptController)
{
enum {
kNumMessagedInterruptVectors = 0xD0 - 0x90
};
IOPCIMessagedInterruptController *
ic = new IOPCIMessagedInterruptController;
if (ic
&& !ic->init(kNumMessagedInterruptVectors))
{
ic->release();
ic = 0;
}
gIOPCIMessagedInterruptController = ic;
}
IOLockUnlock(gIOPCIMessagedInterruptControllerLock);
if (!gIOPCIMessagedInterruptController)
continue;
ret = gIOPCIMessagedInterruptController->allocateDeviceInterrupts(this, nub, numMessages);
}
while (false);
#endif
return (ret);
}
IOReturn IOPCIBridge::resolveLegacyInterrupts( IOService * provider, IOPCIDevice * nub )
{
#ifdef __i386__
UInt32 pin;
UInt32 irq = 0;
pin = (UInt8)(nub->configRead32( kIOPCIConfigInterruptLine ) >> 8);
if ( pin == 0 || pin > 4 )
return (kIOReturnUnsupported);
pin--;
if ( kIOReturnSuccess == provider->callPlatformFunction( "ResolvePCIInterrupt",
false,
provider,
(void *) nub->space.s.deviceNum,
(void *) pin,
&irq ))
{
if (1 & gIOPCIDebug)
IOLog("%s: Resolved interrupt %ld (%d) for %s\n",
provider->getName(),
irq, nub->configRead8(kIOPCIConfigInterruptLine),
nub->getName());
nub->configWrite8( kIOPCIConfigInterruptLine, irq & 0xff );
}
else
{
irq = nub->configRead8( kIOPCIConfigInterruptLine );
if ( 0 == irq || 0xff == irq ) return (kIOReturnUnsupported);
irq &= 0xf; }
provider->callPlatformFunction( "SetDeviceInterrupts",
false,
nub,
(void *) &irq,
(void *) 1,
(void *) false );
#endif
return (kIOReturnSuccess);
}