IOPCIConfigurator.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/pci/IOPCIConfigurator.h>
#include <IOKit/acpi/IOACPIPlatformDevice.h>
#include <IOKit/pci/IOPCIPrivate.h>
#include <libkern/sysctl.h>
__BEGIN_DECLS
#if __i386__
#include <i386/cpuid.h>
#include <i386/cpu_number.h>
extern void mp_rendezvous_no_intrs(
void (*action_func)(void *),
void *arg);
#else
#define NO_RENDEZVOUS_KERNEL 1
#define cpu_number() (0)
#endif
__END_DECLS
#define DLOGC(configurator, fmt, args...) \
do { \
if (configurator->fFlags & kIOPCIConfiguratorIOLog) \
IOLog(fmt, ## args); \
if (configurator->fFlags & kIOPCIConfiguratorKPrintf) \
kprintf(fmt, ## args); \
} while(0)
#define DLOG(fmt, args...) DLOGC(this, fmt, ## args);
static const char * gPCIResourceTypeName[kIOPCIResourceTypeCount] =
{
"MEM", "PFM", "I/O", "BUS"
};
#define CLASS IOPCIConfigurator
#define super IOService
OSDefineMetaClassAndStructors( IOPCIConfigurator, IOService )
bool CLASS::start( IOService * provider )
{
uint64_t cacheLineSize;
size_t siz;
fRootBridge = OSDynamicCast(IOPCIBridge, provider);
if (!fRootBridge) return false;
super::start(provider);
if (!createRoot(fRootBridge))
return false;
fFlags = kIOPCIConfiguratorEnable;
if (!fPCIBridgeList[0]->supportsHotPlug)
{
siz = sizeof(cacheLineSize);
if (0 != sysctlbyname("hw.cachelinesize", &cacheLineSize, &siz, 0, 0))
cacheLineSize = 32;
fCacheLineSize = cacheLineSize >> 2;
}
else
{
fFlags |= 0
| kIOPCIConfiguratorAllocate
;
fCacheLineSize = 0x40;
}
DLOG("PCI cache line size = %u bytes\n", fCacheLineSize);
DLOG("[ PCI configuration begin ]\n");
checkPCIConfiguration();
DLOG("[ PCI configuration end ]\n");
if (fBridgeConfigCount || fDeviceConfigCount || fYentaConfigCount)
{
IOLog("PCI configuration changed (bridge=%d device=%d yenta=%d)\n",
fBridgeConfigCount, fDeviceConfigCount, fYentaConfigCount);
}
provider->setProperty(kIOPCIConfiguredKey, kOSBooleanTrue);
return false; }
void CLASS::free( void )
{
pci_dev_t bridge;
pci_dev_t child;
DLOG("ApplePCIConfigurator::free(%p)\n", this);
for (int i = 0; i < kIOPCIResourceTypeCount; i++)
{
IOPCIRange *
range = fRootBridge->reserved->rangeLists[i];
if (range)
range->nextSubRange = NULL;
}
for (int i = 0; i < kPCIBridgeMaxCount; i++)
{
if ((bridge = fPCIBridgeList[i]))
{
for (child = bridge->child; child; )
{
pci_dev_t lastChild = child;
child = child->peer;
if (!lastChild->isBridge)
{
IODelete(lastChild, pci_dev, 1);
}
}
IODelete(bridge, pci_dev, 1);
fPCIBridgeList[i] = 0;
}
}
super::free();
}
void CLASS::checkPCIConfiguration( void )
{
pci_dev_t bridge;
UInt8 busNum;
DLOG("[ PCI configuration Phase-1 ]\n");
bridge = fPCIBridgeList[0];
busNum = bridge->secBusNum;
pciBridgeScanBus(bridge, busNum, &busNum, bridge->subBusNum);
DLOG("[ PCI configuration Phase-1b ]\n");
fPCIBridgeIndex = 0;
fPCIBridgeTailIndex = 0;
do
{
bridge = fPCIBridgeList[fPCIBridgeIndex++];
FOREACH_CHILD(bridge, child)
{
if (child->isBridge &&
(fPCIBridgeTailIndex < (kPCIBridgeMaxCount-1)))
{
fPCIBridgeList[++fPCIBridgeTailIndex] = child;
DLOG(" added bridge %p bus %u:%u to index %d\n",
child, child->secBusNum, child->subBusNum,
fPCIBridgeTailIndex);
}
}
}
while (fPCIBridgeIndex <= fPCIBridgeTailIndex);
if (fFlags & kIOPCIConfiguratorAllocate)
{
DLOG("[ PCI configuration Phase-2 ]\n");
for (int bridgeIndex = fPCIBridgeTailIndex; bridgeIndex; bridgeIndex--)
{
pciBridgeCheckConfiguration(fPCIBridgeList[bridgeIndex]);
}
DLOG("[ PCI configuration Phase-3 ]\n");
for (int bridgeIndex = 0; bridgeIndex <= fPCIBridgeTailIndex; bridgeIndex++)
{
bridge = fPCIBridgeList[bridgeIndex];
pciBridgeAllocateResource(bridge);
pciBridgeDistributeResource(bridge);
}
}
DLOG("[ PCI configuration Phase-4 ]\n");
for (int bridgeIndex = 0; bridgeIndex <= fPCIBridgeTailIndex; bridgeIndex++)
{
bridge = fPCIBridgeList[bridgeIndex];
pciBridgeConstructDeviceTree(bridge);
}
}
void CLASS::constructAddressingProperties( pci_dev_t device, OSDictionary * propTable )
{
IOPCIRange * range;
IOPCIPhysicalAddress regData;
OSData * prop;
OSData * regProp;
OSData * assignedProp;
OSData * rangeProp;
assignedProp = OSData::withCapacity(32);
regProp = OSData::withCapacity(32);
rangeProp = OSData::withCapacity(32);
if (!assignedProp || !regProp || !rangeProp)
return;
regData.physHi = device->space;
regData.physMid = 0;
regData.physLo = 0;
regData.lengthHi = 0;
regData.lengthLo = 0;
regProp->appendBytes(®Data, sizeof(regData));
for (uint32_t i = 0; i < kIOPCIRangeCount; i++)
{
static const uint8_t barRegisters[kIOPCIRangeExpansionROM + 1] = { 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x30 };
range = &device->ranges[i];
if (range->size == 0)
continue;
if (kIOPCIResourceTypeBusNumber == range->type)
continue;
regData.physHi = device->space;
switch (range->type)
{
case kIOPCIResourceTypeMemory:
regData.physHi.s.space = kIOPCI32BitMemorySpace;
break;
case kIOPCIResourceTypePrefetchMemory:
regData.physHi.s.space = kIOPCI32BitMemorySpace;
regData.physHi.s.prefetch = 1;
break;
case kIOPCIResourceTypeIO:
regData.physHi.s.space = kIOPCIIOSpace;
break;
}
regData.physMid = 0;
regData.physLo = 0;
regData.lengthHi = (range->size >> 32ULL);
regData.lengthLo = range->size;
if (i <= kIOPCIRangeExpansionROM)
{
regData.physHi.s.registerNum = barRegisters[i];
regProp->appendBytes(®Data, sizeof(regData));
if (range->start)
{
regData.physHi.s.reloc = 1;
regData.physMid = (range->start >> 32ULL);
regData.physLo = range->start;
assignedProp->appendBytes(®Data, sizeof(regData));
}
}
else
{
regData.physHi.s.reloc = 1;
regData.physHi.s.reloc = 1;
regData.physMid = (range->start >> 32ULL);
regData.physLo = range->start;
regData.physHi.s.busNum = 0;
regData.physHi.s.deviceNum = 0;
regData.physHi.s.functionNum = 0;
regData.physHi.s.registerNum = 0;
rangeProp->appendBytes(®Data, sizeof(regData.physHi) + sizeof(regData.physMid) + sizeof(regData.physLo));
rangeProp->appendBytes(®Data, sizeof(regData.physHi) + sizeof(regData.physMid) + sizeof(regData.physLo));
rangeProp->appendBytes(®Data.lengthHi, sizeof(regData.lengthHi) + sizeof(regData.lengthLo));
}
}
propTable->setObject("reg", regProp);
regProp->release();
if (assignedProp->getLength())
propTable->setObject("assigned-addresses", assignedProp);
assignedProp->release();
if (rangeProp->getLength())
{
propTable->setObject("ranges", rangeProp);
regData.lengthLo = 3;
prop = OSData::withBytes( ®Data.lengthLo, sizeof(regData.lengthLo) );
if (prop)
{
propTable->setObject("#address-cells", prop );
prop->release();
}
regData.lengthLo = 2;
prop = OSData::withBytes( ®Data.lengthLo, sizeof(regData.lengthLo) );
if (prop)
{
propTable->setObject("#size-cells", prop );
prop->release();
}
}
rangeProp->release();
}
OSDictionary * CLASS::constructProperties( pci_dev_t device )
{
IOPCIAddressSpace space = device->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)
return (NULL);
constructAddressingProperties(device, propTable);
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();
}
if (device->supportsHotPlug)
propTable->setObject(kIOPCIHotPlugKey, kOSBooleanTrue);
return (propTable);
}
static IOACPIPlatformDevice * IOPCICopyACPIDevice( IORegistryEntry * device )
{
IOACPIPlatformDevice * acpiDevice = 0;
OSString * acpiPath;
if (device)
{
acpiPath = (OSString *) device->copyProperty(kACPIDevicePathKey);
if (acpiPath && !OSDynamicCast(OSString, acpiPath))
{
acpiPath->release();
acpiPath = 0;
}
if (acpiPath)
{
IORegistryEntry * entry;
entry = IORegistryEntry::fromPath(acpiPath->getCStringNoCopy());
acpiPath->release();
if (entry && entry->metaCast("IOACPIPlatformDevice"))
acpiDevice = (IOACPIPlatformDevice *) entry;
else if (entry)
entry->release();
}
}
return (acpiDevice);
}
static bool IOPCIIsHotplugPort(IORegistryEntry * bridgeDevice)
{
IOACPIPlatformDevice * rp;
IOACPIPlatformDevice * child;
UInt32 result32 = 0;
const IORegistryPlane * plane = IORegistryEntry::getPlane("IOACPIPlane");
rp = IOPCICopyACPIDevice(bridgeDevice);
if (rp && plane)
{
child = (IOACPIPlatformDevice *) rp->getChildEntry(plane);
if (child)
{
IOReturn
ret = child->evaluateInteger("_RMV", &result32);
if (kIOReturnSuccess != ret)
result32 = 0;
}
}
if (rp)
rp->release();
return (result32 != 0);
}
struct MatchDTEntryContext
{
CLASS * me;
pci_dev_t bridge;
};
void CLASS::matchDTEntry( IORegistryEntry * dtEntry, void * _context )
{
MatchDTEntryContext * context = (MatchDTEntryContext *) _context;
pci_dev_t bridge = context->bridge;
pci_dev_t match = 0;
const OSSymbol * location;
assert(bridge);
assert(dtEntry);
location = dtEntry->copyLocation();
if (!location)
return;
UInt32 devfn = strtoul(location->getCStringNoCopy(), NULL, 16);
UInt32 deviceNum = ((devfn >> 16) & 0x1f);
UInt32 functionNum = (devfn & 0x7);
bool functionAll = ((devfn & 0xffff) == 0xffff);
FOREACH_CHILD( bridge, child )
{
if (child->space.s.deviceNum == deviceNum &&
(functionAll || (child->space.s.functionNum == functionNum)))
{
match = child;
break;
}
}
if (match)
{
match->dtNub = dtEntry;
DLOGC(context->me, "Found PCI device for DT entry [%s] %x:%x\n",
dtEntry->getName(), match->space.s.deviceNum,
match->space.s.functionNum);
}
else
{
DLOGC(context->me, "NOT FOUND: PCI device for DT entry [%s] %lu:%lu\n",
dtEntry->getName(), deviceNum, functionNum);
}
if (location)
location->release();
}
void CLASS::matchACPIEntry( IORegistryEntry * dtEntry, void * _context )
{
MatchDTEntryContext * context = (MatchDTEntryContext *) _context;
pci_dev_t bridge = context->bridge;
pci_dev_t match = 0;
OSNumber * adr;
assert(bridge);
assert(dtEntry);
adr = OSDynamicCast(OSNumber, dtEntry->getProperty("_ADR"));
if (!adr)
return;
UInt32 devfn = adr->unsigned32BitValue();
UInt32 deviceNum = ((devfn >> 16) & 0x1f);
UInt32 functionNum = (devfn & 0x7);
bool functionAll = ((devfn & 0xffff) == 0xffff);
FOREACH_CHILD( bridge, child )
{
if (child->space.s.deviceNum == deviceNum &&
(functionAll || (child->space.s.functionNum == functionNum)))
{
match = child;
if (!functionAll)
break;
}
}
if (match)
{
match->acpiDevice = dtEntry;
DLOGC(context->me, "Found PCI device for ACPI entry [%s] %x:%x\n",
dtEntry->getName(), match->space.s.deviceNum,
match->space.s.functionNum);
}
else
{
DLOGC(context->me, "NOT FOUND: PCI device for ACPI entry [%s] %lu:%lu\n",
dtEntry->getName(), deviceNum, functionNum);
}
}
void CLASS::pciBridgeConnectDeviceTree( pci_dev_t bridge )
{
IORegistryEntry * dtBridge = bridge->dtNub;
MatchDTEntryContext context;
if (dtBridge)
{
context.me = this;
context.bridge = bridge;
dtBridge->applyToChildren(&matchDTEntry, &context, gIODTPlane);
if (gIOPCIACPIPlane)
{
IORegistryEntry *
acpiBridgeDevice = IOPCICopyACPIDevice(dtBridge);
if (acpiBridgeDevice)
{
acpiBridgeDevice->applyToChildren(&matchACPIEntry, &context, gIOPCIACPIPlane);
acpiBridgeDevice->release();
}
}
}
FOREACH_CHILD(bridge, child)
{
if (!child->isBridge)
continue;
child->supportsHotPlug = ((child->headerType == kPCIHeaderType2)
|| (child->dtNub && IOPCIIsHotplugPort(child->dtNub)));
}
}
void CLASS::pciBridgeConstructDeviceTree( pci_dev_t bridge )
{
IORegistryEntry * dtBridge = bridge->dtNub;
uint32_t int32;
if (dtBridge)
{
int32 = 3;
dtBridge->setProperty("#address-cells", &int32, sizeof(int32));
int32 = 2;
dtBridge->setProperty("#size-cells", &int32, sizeof(int32));
FOREACH_CHILD( bridge, child )
{
OSDictionary * propTable;
OSObject * obj;
const OSSymbol * sym;
propTable = constructProperties(child);
if (!propTable)
continue;
if (!child->dtNub)
{
IOService * nub = OSTypeAlloc(IOService);
if (nub &&
(!nub->init(propTable) || (!nub->attachToParent(bridge->dtNub, gIODTPlane))))
{
nub->release();
nub = 0;
}
child->dtNub = nub;
if ((sym = OSDynamicCast(OSSymbol, propTable->getObject(gIONameKey))))
child->dtNub->setName(sym);
if (child->acpiDevice)
{
OSObject * obj;
const OSSymbol * sym;
char acpiPath[512];
int pathSize = 512;
if ((sym = child->acpiDevice->copyName()))
{
nub->setName(sym);
sym->release();
}
if ((sym = child->acpiDevice->copyLocation()))
{
nub->setLocation(sym);
sym->release();
}
if (child->acpiDevice->getPath(acpiPath, &pathSize, gIOPCIACPIPlane))
nub->setProperty(kACPIDevicePathKey, acpiPath);
obj = child->acpiDevice->getProperty(kPCIInterruptRoutingTableKey);
if (obj)
nub->setProperty(kPCIInterruptRoutingTableKey, obj);
}
}
else
{
OSCollectionIterator * propIter =
OSCollectionIterator::withCollection(propTable);
if (propIter)
{
const OSSymbol * propKey;
while ((propKey = (const OSSymbol *)propIter->getNextObject()))
{
if (child->dtNub->getProperty(propKey))
continue;
obj = propTable->getObject(propKey);
child->dtNub->setProperty(propKey, obj);
}
propIter->release();
}
}
propTable->release();
}
if ((kIOPCIConfiguratorAllocate & fFlags)
|| !(bridge->supportsHotPlug))
{
dtBridge->setProperty(kIOPCIConfiguredKey, kOSBooleanTrue);
}
}
}
void CLASS::pciBridgeScanBus( pci_dev_t bridge,
UInt8 busNum, UInt8 * nextBusNum, UInt8 lastBusNum )
{
UInt8 scanDevice, scanFunction, lastFunction;
IOPCIAddressSpace space;
UInt32 vendor;
UInt8 nextChildBusNum;
DLOG("Scanning PCI bridge %p, bus %u (%u:%u)\n", bridge, busNum, *nextBusNum, lastBusNum);
assert(bridge->headerType == kPCIHeaderType1);
assert(bridge->deviceState == kPCIDeviceStateBIOSConfig);
space.bits = 0;
space.s.busNum = busNum;
for (scanDevice = 0; scanDevice <= 31; scanDevice++)
{
lastFunction = 0;
for (scanFunction = 0; scanFunction <= lastFunction; scanFunction++)
{
space.s.deviceNum = scanDevice;
space.s.functionNum = scanFunction;
vendor = configRead32(space, kIOPCIConfigVendorID);
vendor &= 0x0000ffff;
if ((0 == vendor) || (0xffff == vendor))
{
continue;
}
pciBridgeProbeChild(bridge, space);
if ((0 == scanFunction)
&& (0x00800000 & configRead32(space, kIOPCIConfigCacheLineSize)))
lastFunction = 7;
}
}
pciBridgeConnectDeviceTree(bridge);
nextChildBusNum = busNum;
lastBusNum = bridge->subBusNum;
FOREACH_CHILD(bridge, child)
{
UInt8 childBusNum;
bool didAllocateBus;
if (child->headerType != kPCIHeaderType1)
continue;
if (child->supportsHotPlug && !(kIOPCIConfiguratorAllocate & fFlags))
continue;
didAllocateBus = false;
childBusNum = child->secBusNum;
if (childBusNum <= busNum)
{
childBusNum = nextChildBusNum;
if (childBusNum >= lastBusNum)
{
DLOG("bridge %p ran out of bus numbers bus %u:%u\n",
child, child->secBusNum, child->subBusNum);
continue;
}
childBusNum++;
DLOG("bridge %p allocated bus %u\n", child, childBusNum);
nextChildBusNum = childBusNum;
child->secBusNum = childBusNum;
child->subBusNum = lastBusNum;
uint32_t reg32 = configRead32(child->space, kPCI2PCIPrimaryBus);
reg32 &= ~0x00ffffff;
reg32 |= busNum | (child->secBusNum << 8) | (child->subBusNum << 16);
configWrite32(child->space, kPCI2PCIPrimaryBus, reg32);
didAllocateBus = true;
}
pciBridgeScanBus(child, childBusNum, &nextChildBusNum, lastBusNum);
if (didAllocateBus)
{
child->subBusNum = nextChildBusNum;
configWrite8( child->space, kPCI2PCISubordinateBus, child->subBusNum );
}
DLOG("bridge %p scan final bus range %u:%u\n",
child, child->secBusNum, child->subBusNum);
}
}
void CLASS::pciRangeAppendSubRange( IOPCIRange * headRange,
IOPCIRange * newRange )
{
IOPCIRange * * prevRange;
IOPCIRange * nextRange;
assert(newRange->nextSubRange == 0);
prevRange = &headRange->subRange;
nextRange = *prevRange;
while (nextRange && (newRange->alignment < nextRange->alignment))
{
prevRange = &nextRange->nextSubRange;
nextRange = *prevRange;
}
*prevRange = newRange;
newRange->nextSubRange = nextRange;
}
void CLASS::pciBridgeCheckConfiguration( pci_dev_t bridge )
{
bool reconfig = false;
IOPCIRange * newRanges;
IOPCIRange * childRange;
IOPCIRange * range;
DLOG("pciBridgeCheckConfiguration(bus %u, state %d)\n", bridge->secBusNum, bridge->deviceState);
assert(bridge != fPCIBridgeList[0]);
assert(bridge->isBridge);
assert(bridge->deviceState == kPCIDeviceStateBIOSConfig);
if (bridge->headerType != kPCIHeaderType1)
return;
if ((bridge->classCode & 0xFFFFFF) != 0x060400)
return;
DLOG("Checking PCI bus %u\n", bridge->secBusNum);
newRanges = IONew(IOPCIRange, kIOPCIResourceTypeCount);
if (!newRanges)
{
DLOG(" ERROR: memory allocation failed\n");
return;
}
memset(newRanges, 0, sizeof(IOPCIRange) * kIOPCIResourceTypeCount);
for (int i = 0; i < kIOPCIResourceTypeCount; i++)
{
static const IOPCIScalar alignments[kIOPCIResourceTypeCount] = {
kPCIBridgeMemoryAlignment, kPCIBridgeMemoryAlignment,
kPCIBridgeIOAlignment, kPCIBridgeBusNumberAlignment
};
newRanges[i].type = i;
newRanges[i].alignment = alignments[i];
}
FOREACH_CHILD(bridge, child)
{
for (int i = 0; i < kIOPCIRangeCount; i++)
{
childRange = &child->ranges[i];
if (childRange->size == 0)
continue;
range = &newRanges[childRange->type];
range->flags |= childRange->flags;
range->size += childRange->size;
range->alignment = max(childRange->alignment, range->alignment);
assert(childRange->size >= childRange->alignment);
if (kIOPCIRangeFlagMaximizeSize & range->flags)
reconfig = true;
}
}
if (bridge->ranges[kIOPCIRangeBridgeIO].size == 0 &&
newRanges[kIOPCIResourceTypeIO].size)
{
reconfig = true;
}
if (bridge->ranges[kIOPCIRangeBridgeMemory].size == 0 &&
newRanges[kIOPCIResourceTypeMemory].size)
{
reconfig = true;
}
if (reconfig)
{
for (int i = 0; i < kIOPCIResourceTypeCount; i++)
{
newRanges[i].size = IORound(newRanges[i].size,
newRanges[i].alignment);
if ((kIOPCIResourceTypeBusNumber == i) && !newRanges[i].size)
newRanges[i].size = 1;
memcpy(&bridge->ranges[BRIDGE_RANGE_NUM(i)],
&newRanges[i], sizeof(IOPCIRange));
DLOG(" %s: new range size %llx align %llx flags %lx\n",
gPCIResourceTypeName[i],
newRanges[i].size, newRanges[i].alignment, newRanges[i].flags);
}
bridge->deviceState = kPCIDeviceStateResourceWait;
}
else
{
DLOG(" BIOS config retained\n");
}
IODelete(newRanges, IOPCIRange, kIOPCIResourceTypeCount);
}
void CLASS::pciBridgeClipRanges( IOPCIRange * rangeList,
IOPCIScalar start, IOPCIScalar size )
{
IOPCIRange * thisRange;
IOPCIScalar end;
IOPCIScalar overlap;
IOPCIScalar rangeEnd;
if (!rangeList || !size)
return;
for (thisRange = rangeList; thisRange; thisRange = thisRange->next)
{
if (thisRange->size == 0)
continue;
end = start + size - 1;
if (thisRange->start > end)
continue;
rangeEnd = thisRange->start + thisRange->size - 1;
if (thisRange->start >= start)
{
overlap = end - thisRange->start + 1;
if (thisRange->size <= overlap)
{
thisRange->size = 0;
}
else
{
thisRange->size -= overlap;
thisRange->start += overlap;
}
DLOG(" clipped bridge %s range head to %llx:%llx for %llx:%llx overlap %llx\n",
gPCIResourceTypeName[thisRange->type],
thisRange->start, thisRange->size, start, size, overlap);
}
else if (rangeEnd >= start && rangeEnd < end)
{
overlap = rangeEnd - start + 1;
thisRange->size -= overlap;
DLOG(" clipped bridge %s range tail to %llx:%llx for %llx:%llx overlap %llx\n",
gPCIResourceTypeName[thisRange->type],
thisRange->start, thisRange->size, start, size, overlap);
}
}
}
void CLASS::pciBridgeAllocateResource( pci_dev_t bridge )
{
bool allocNeeded = false;
IORangeAllocator * allocators[kIOPCIResourceTypeCount];
IOPCIScalar allocLimit[kIOPCIResourceTypeCount];
int waitCounts[kIOPCIResourceTypeCount];
IOPCIRange * bridgeRangeList[kIOPCIResourceTypeCount];
bool ok;
DLOG("pciBridgeAllocateResource(bus %u, state %d)\n", bridge->secBusNum, bridge->deviceState);
if ((bridge->deviceState != kPCIDeviceStateBIOSConfig)
&& (bridge->deviceState != kPCIDeviceStateConfigurationDone))
return;
FOREACH_CHILD(bridge, child)
{
if (child->deviceState == kPCIDeviceStateResourceWait)
{
allocNeeded = true;
break;
}
}
if (!allocNeeded) return;
DLOG("Allocating resources on bus %u\n", bridge->secBusNum);
memset(allocators, 0, sizeof(allocators));
memset(allocLimit, 0, sizeof(allocLimit));
memset(waitCounts, 0, sizeof(waitCounts));
memset(bridgeRangeList, 0, sizeof(bridgeRangeList));
if (bridge == fPCIBridgeList[0])
{
bridgeRangeList[kIOPCIResourceTypeMemory]
= fRootBridge->reserved->rangeLists[kIOPCIResourceTypeMemory];
bridgeRangeList[kIOPCIResourceTypePrefetchMemory]
= fRootBridge->reserved->rangeLists[kIOPCIResourceTypePrefetchMemory];
bridgeRangeList[kIOPCIResourceTypeIO]
= fRootBridge->reserved->rangeLists[kIOPCIResourceTypeIO];
bridgeRangeList[kIOPCIResourceTypeBusNumber]
= &bridge->ranges[kIOPCIRangeBridgeBusNumber];
for (int i = 0; i < kIOPCIResourceTypeCount; i++)
{
IOPCIRange * thisRange;
for (thisRange = bridgeRangeList[i]; thisRange; thisRange = thisRange->next)
{
DLOG("root bridge resource %s %llx len %llx\n", gPCIResourceTypeName[i], thisRange->start, thisRange->size);
}
}
pciBridgeClipRanges(bridgeRangeList[kIOPCIResourceTypeIO],
0, 0x400);
pciBridgeClipRanges(bridgeRangeList[kIOPCIResourceTypeMemory],
0, 0x100000);
}
else
{
for (int i = 0; i < kIOPCIResourceTypeCount; i++)
{
bridgeRangeList[i] = &bridge->ranges[BRIDGE_RANGE_NUM(i)];
}
}
FOREACH_CHILD(bridge, child)
{
IOPCIRange * childRange;
if (child->deviceState != kPCIDeviceStateBIOSConfig)
continue;
for (int i = 0; i < kIOPCIRangeCount; i++)
{
childRange = &child->ranges[i];
if (childRange->size == 0)
continue;
pciBridgeClipRanges(bridgeRangeList[childRange->type],
childRange->start, childRange->size);
if (childRange->type == kIOPCIResourceTypePrefetchMemory)
{
pciBridgeClipRanges(bridgeRangeList[kIOPCIResourceTypeMemory],
childRange->start, childRange->size);
}
else if (childRange->type == kIOPCIResourceTypeMemory)
{
pciBridgeClipRanges(bridgeRangeList[kIOPCIResourceTypePrefetchMemory],
childRange->start, childRange->size);
}
}
}
for (int i = 0; i < kIOPCIResourceTypeCount; i++)
{
IOPCIRange * range;
allocators[i] = IORangeAllocator::withRange(
0, 1, 8);
if (allocators[i] == 0)
goto fail;
for (range = bridgeRangeList[i]; range; range = range->next)
{
if (range->size)
{
allocators[i]->deallocate((IORangeScalar) range->start,
(IORangeScalar) range->size);
}
}
if (kIOPCIResourceTypeBusNumber == i)
allocators[i]->allocateRange(bridge->secBusNum, 1);
}
FOREACH_CHILD(bridge, child)
{
IOPCIRange * childRange;
assert(child->deviceState == kPCIDeviceStateBIOSConfig ||
child->deviceState == kPCIDeviceStateResourceWait);
for (int i = 0; i < kIOPCIRangeCount; i++)
{
childRange = &child->ranges[i];
if (childRange->size == 0)
continue;
pciRangeAppendSubRange(bridgeRangeList[childRange->type],
childRange);
if (child->deviceState == kPCIDeviceStateBIOSConfig)
{
ok = allocators[childRange->type]->allocateRange(
(IORangeScalar) childRange->start,
(IORangeScalar) childRange->size);
if (!ok)
{
if (childRange->type == kIOPCIResourceTypePrefetchMemory)
{
ok = allocators[kIOPCIResourceTypeMemory]->allocateRange(
(IORangeScalar) childRange->start,
(IORangeScalar) childRange->size);
}
if (!ok)
{
DLOG(" %s: sub-range outside parent range: "
"0x%llx:0x%llx\n",
gPCIResourceTypeName[childRange->type],
childRange->start, childRange->size);
}
}
}
else if (child->deviceState == kPCIDeviceStateResourceWait)
{
waitCounts[childRange->type]++;
}
}
}
for (int i = 0; i < kIOPCIResourceTypeCount; i++)
{
if (waitCounts[i] == 0)
continue;
allocLimit[i] = allocators[i]->getFreeCount() / waitCounts[i];
DLOG(" %s: %u sub-range limited to %llx each\n",
gPCIResourceTypeName[i], waitCounts[i], allocLimit[i]);
}
if (allocLimit[kIOPCIResourceTypePrefetchMemory] == 0 &&
waitCounts[kIOPCIResourceTypePrefetchMemory])
{
IOPCIRange * to;
IOPCIRange * from;
IOPCIRange * subRange;
FOREACH_CHILD(bridge, child)
{
if (child->deviceState != kPCIDeviceStateResourceWait ||
child->ranges[kIOPCIRangeBridgePFMemory].size == 0)
continue;
to = &child->ranges[kIOPCIRangeBridgeMemory];
from = &child->ranges[kIOPCIRangeBridgePFMemory];
assert(from->subRange != 0);
subRange = from->subRange;
while (subRange)
{
IOPCIRange * tmpRange = subRange->nextSubRange;
subRange->nextSubRange = 0;
pciRangeAppendSubRange(to, subRange);
subRange = tmpRange;
}
to->alignment = max(to->alignment, from->alignment);
to->size += from->size;
to->size = IORound(to->size, to->alignment);
memset(from, 0, sizeof(*from));
DLOG(" substitute non-prefetchable memory for bus %u %llx:%llx\n",
child->secBusNum, to->size, to->alignment);
}
}
FOREACH_CHILD(bridge, child)
{
IOPCIRange * childRange;
IOPCIScalar rangeStart;
IOPCIScalar rangeLimit;
bool ok = false;
if (child->deviceState != kPCIDeviceStateResourceWait)
continue;
for (int i = 0; i < kIOPCIRangeCount; i++)
{
childRange = &child->ranges[i];
if (childRange->size == 0)
continue;
if (childRange->flags & kIOPCIRangeFlagMaximizeSize)
{
rangeLimit = allocLimit[childRange->type];
if (i <= kIOPCIRangeBridgeMemory)
for (UInt32 limit = 0x80000000; limit > 0; limit >>= 1)
{
if (allocLimit[i] >= limit)
{
allocLimit[i] = limit;
break;
}
}
}
else
rangeLimit = childRange->size;
ok = false;
for (IOPCIScalar rangeSize = rangeLimit;
rangeSize >= childRange->size;
rangeSize >>= 1)
{
IORangeScalar allocResult;
ok = allocators[childRange->type]->allocate(
rangeSize, &allocResult, childRange->alignment);
if (!ok && childRange->type == kIOPCIResourceTypePrefetchMemory)
{
ok = allocators[kIOPCIResourceTypeMemory]->allocate(
rangeSize, &allocResult, childRange->alignment);
}
if (ok)
{
rangeStart = allocResult;
childRange->start = rangeStart;
childRange->size = rangeSize;
DLOG(" %s: allocated block %llx:%llx\n",
gPCIResourceTypeName[childRange->type],
childRange->start, childRange->size);
break;
}
}
if (!ok) break;
}
child->deviceState = kPCIDeviceStateResourceAssigned;
}
bridge->deviceState = kPCIDeviceStateResourceAssigned;
fail:
for (int i = 0; i < kIOPCIResourceTypeCount; i++)
{
if (allocators[i])
allocators[i]->release();
}
}
void CLASS::pciBridgeDistributeResource( pci_dev_t bridge )
{
DLOG("pciBridgeDistributeResource(bus %u, state %d)\n", bridge->secBusNum, bridge->deviceState);
if (bridge->deviceState != kPCIDeviceStateResourceAssigned)
return;
if (bridge == fPCIBridgeList[0])
pciApplyConfiguration(bridge);
DLOG("Distribute resources for bus %u\n", bridge->secBusNum);
for (int type = 0; type < kIOPCIResourceTypeCount; type++)
{
pciBridgeDistributeResourceType(bridge, type);
}
FOREACH_CHILD(bridge, child)
{
pciApplyConfiguration(child);
}
}
void CLASS::pciBridgeDistributeResourceType( pci_dev_t bridge, UInt32 type )
{
IOPCIRange * bridgeRange = &bridge->ranges[BRIDGE_RANGE_NUM(type)];
IOPCIRange * nextRange;
IOPCIScalar nextStart;
IOPCIScalar allocSize;
IOPCIScalar extraSize = 0;
IOPCIScalar requiredSize = 0;
int maximizeCount = 0;
allocSize = bridgeRange->size;
if (allocSize == 0)
return;
assert(bridgeRange->subRange != 0);
if (kIOPCIResourceTypeBusNumber == type)
allocSize--;
for (nextRange = bridgeRange->subRange; nextRange;
nextRange = nextRange->nextSubRange)
{
assert(nextRange->size > 0);
assert(nextRange->type == type);
requiredSize += nextRange->size;
if (nextRange->flags & kIOPCIRangeFlagMaximizeSize)
maximizeCount++;
}
assert(allocSize >= requiredSize);
if (maximizeCount && (allocSize > requiredSize))
{
extraSize = allocSize - requiredSize;
extraSize /= maximizeCount;
}
DLOG(" %s: total size %llx, required size %llx, maximize count %u\n",
gPCIResourceTypeName[type],
allocSize, requiredSize, maximizeCount);
nextStart = bridgeRange->start;
if (kIOPCIResourceTypeBusNumber == type)
nextStart++;
for (nextRange = bridgeRange->subRange; nextRange;
nextRange = nextRange->nextSubRange)
{
assert((nextStart & (nextRange->alignment - 1)) == 0);
nextRange->start = nextStart;
if (nextRange->flags & kIOPCIRangeFlagMaximizeSize)
nextRange->size += IOTrunc(extraSize, nextRange->alignment);
nextStart += nextRange->size;
DLOG(" %s: assigned block %llx:%llx\n",
gPCIResourceTypeName[type], nextRange->start, nextRange->size);
}
assert(nextStart - bridgeRange->start <= bridgeRange->size);
}
UInt16 CLASS::pciDisableAccess( pci_dev_t device )
{
UInt16 command;
command = configRead16(device->space, kIOPCIConfigCommand);
configWrite16(device->space, kIOPCIConfigCommand,
command & ~(kIOPCICommandIOSpace | kIOPCICommandMemorySpace));
return command;
}
void CLASS::pciRestoreAccess( pci_dev_t device, UInt16 command )
{
configWrite16(device->space, kIOPCIConfigCommand, command);
}
void CLASS::pciApplyConfiguration( pci_dev_t device )
{
switch (device->headerType)
{
case kPCIHeaderType0:
pciDeviceApplyConfiguration( device );
break;
case kPCIHeaderType1:
case kPCIHeaderType2:
pciBridgeApplyConfiguration( device );
break;
}
pciWriteLatencyTimer( device );
device->deviceState = kPCIDeviceStateConfigurationDone;
}
void CLASS::pciDeviceApplyConfiguration( pci_dev_t device )
{
IOPCIRange * range;
UInt16 reg16;
DLOG("Applying config for device %u:%u:%u\n", PCI_ADDRESS_TUPLE(device));
fDeviceConfigCount++;
reg16 = pciDisableAccess(device);
for (int i = kIOPCIRangeBAR0; i <= kIOPCIRangeBAR5; i++)
{
range = &device->ranges[i];
if (range->size == 0)
continue;
if (range->start == 0)
continue;
DLOG(" bar 0x%x = %llx\n", 0x10 + i * 4, range->start);
configWrite32(device->space, 0x10 + i * 4, range->start);
}
range = &device->ranges[kIOPCIRangeExpansionROM];
if (range->size && range->start)
{
DLOG(" rom 0x%x = %llx\n", kIOPCIConfigExpansionROMBase, range->start);
configWrite32(device->space, kIOPCIConfigExpansionROMBase, range->start);
}
reg16 &= ~(kIOPCICommandIOSpace | kIOPCICommandMemorySpace |
kIOPCICommandBusMaster | kIOPCICommandMemWrInvalidate);
pciRestoreAccess(device, reg16);
DLOG(" Device Command = %08lx\n",
configRead32(device->space, kIOPCIConfigCommand));
}
void CLASS::pciBridgeApplyConfiguration( pci_dev_t bridge )
{
UInt32 start;
UInt32 end;
IOPCIRange * range;
UInt16 reg16;
UInt32 baselim32;
UInt16 baselim16;
enum {
kBridgeCommand = (kIOPCICommandIOSpace | kIOPCICommandMemorySpace | kIOPCICommandBusMaster)
};
if ((bridge == fPCIBridgeList[0])
|| (bridge->deviceState != kPCIDeviceStateResourceAssigned))
reg16 = configRead16(bridge->space, kIOPCIConfigCommand);
else do
{
bridge->secBusNum = bridge->ranges[kIOPCIRangeBridgeBusNumber].start;
bridge->subBusNum = bridge->secBusNum + bridge->ranges[kIOPCIRangeBridgeBusNumber].size - 1;
DLOG("Applying config for bridge serving bus %u\n", bridge->secBusNum);
DLOG(" MEM: start/size = %08llx:%08llx\n",
bridge->ranges[kIOPCIRangeBridgeMemory].start,
bridge->ranges[kIOPCIRangeBridgeMemory].size);
DLOG(" I/O: start/size = %08llx:%08llx\n",
bridge->ranges[kIOPCIRangeBridgeIO].start,
bridge->ranges[kIOPCIRangeBridgeIO].size);
DLOG(" BUS: start/size = %08llx:%08llx\n",
bridge->ranges[kIOPCIRangeBridgeBusNumber].start,
bridge->ranges[kIOPCIRangeBridgeBusNumber].size);
reg16 = pciDisableAccess(bridge);
FOREACH_CHILD(bridge, child)
{
child->space.s.busNum = bridge->secBusNum;
}
for (int i = kIOPCIRangeBAR0; i <= kIOPCIRangeBAR1; i++)
{
range = &bridge->ranges[i];
if (range->size == 0)
continue;
assert(range->start);
configWrite32(bridge->space, 0x10 + i * 4, range->start);
}
uint32_t reg32 = configRead32(bridge->space, kPCI2PCIPrimaryBus);
reg32 &= ~0x00ffffff;
reg32 |= bridge->space.s.busNum | (bridge->secBusNum << 8) | (bridge->subBusNum << 16);
configWrite32(bridge->space, kPCI2PCIPrimaryBus, reg32);
DLOG(" Regs:\n BUS: prim/sec/sub = %02x:%02x:%02x\n",
configRead8(bridge->space, kPCI2PCIPrimaryBus),
configRead8(bridge->space, kPCI2PCISecondaryBus),
configRead8(bridge->space, kPCI2PCISubordinateBus));
if (kPCIHeaderType2 == bridge->headerType)
{
fYentaConfigCount++;
break;
}
baselim16 = 0x00f0; range = &bridge->ranges[kIOPCIRangeBridgeIO];
if (range->size)
{
assert(range->start);
assert((range->size & (4096-1)) == 0);
assert((range->start & (4096-1)) == 0);
assert((range->start & 0xffff0000) == 0);
start = range->start;
end = start + range->size - 1;
baselim16 = ((start >> 8) & 0xf0) | (end & 0xf000);
}
configWrite16(bridge->space, kPCI2PCIIORange, baselim16);
configWrite32(bridge->space, kPCI2PCIUpperIORange, 0);
baselim32 = 0x0000FFF0; range = &bridge->ranges[kIOPCIRangeBridgeMemory];
if (range->size)
{
assert(range->start);
assert((range->size & (0x100000-1)) == 0);
assert((range->start & (0x100000-1)) == 0);
start = range->start;
end = range->start + range->size - 1;
baselim32 = ((start >> 16) & 0xFFF0) | (end & 0xFFF00000);
}
configWrite32(bridge->space, kPCI2PCIMemoryRange, baselim32);
baselim32 = 0x0000FFF0; range = &bridge->ranges[kIOPCIRangeBridgePFMemory];
if (range->size)
{
assert(range->start);
assert((range->size & (0x100000-1)) == 0);
assert((range->start & (0x100000-1)) == 0);
start = range->start;
end = range->start + range->size - 1;
baselim32 = ((start >> 16) & 0xFFF0) | (end & 0xFFF00000);
}
configWrite32(bridge->space, kPCI2PCIPrefetchMemoryRange, baselim32);
configWrite32(bridge->space, kPCI2PCIPrefetchUpperBase, 0);
configWrite32(bridge->space, kPCI2PCIPrefetchUpperLimit, 0);
DLOG(" MEM: base/limit = %08lx\n",
configRead32(bridge->space, kPCI2PCIMemoryRange));
DLOG(" PFM: base/limit = %08lx\n",
configRead32(bridge->space, kPCI2PCIPrefetchMemoryRange));
DLOG(" I/O: base/limit = %04x\n",
configRead16(bridge->space, kPCI2PCIIORange));
fBridgeConfigCount++;
} while (false);
if (bridge->deviceState == kPCIDeviceStateResourceAssigned)
{
DLOG("Enabling bridge serving bus %u\n", bridge->secBusNum);
if (kPCIHeaderType2 == bridge->headerType)
{
reg16 &= ~(kIOPCICommandIOSpace | kIOPCICommandMemorySpace |
kIOPCICommandBusMaster | kIOPCICommandMemWrInvalidate);
}
else
{
uint16_t bridgeControl;
reg16 |= (kIOPCICommandIOSpace | kIOPCICommandMemorySpace |
kIOPCICommandBusMaster);
bridgeControl = configRead16(bridge->space, kPCI2PCIBridgeControl);
if (bridgeControl & 0x0004)
{
bridgeControl &= ~0x0004;
configWrite16(bridge->space, kPCI2PCIBridgeControl, bridgeControl);
}
DLOG(" Bridge Control = %04x\n",
configRead16(bridge->space, kPCI2PCIBridgeControl));
}
pciRestoreAccess(bridge, reg16);
DLOG(" Bridge Command = %08lx\n",
configRead32(bridge->space, kIOPCIConfigCommand));
}
}
void CLASS::pciBridgeAddChild( pci_dev_t bridge, pci_dev_t child )
{
pci_dev_t old_child = bridge->child;
bridge->child = child;
child->peer = old_child;
}
void CLASS::pciBridgeProbeChild( pci_dev_t bridge, IOPCIAddressSpace space )
{
pci_dev_t child;
bool ok = true;
child = IONew(pci_dev, 1);
if (!child) return;
memset(child, 0, sizeof(*child));
child->headerType = configRead8(space, kIOPCIConfigHeaderType) & 0x7f;
child->classCode = configRead32(space, kIOPCIConfigRevisionID) >> 8;
child->space = space;
DLOG("Probing type %u device class-code %06lx at %u:%u:%u [cpu %x]\n",
child->headerType, child->classCode,
PCI_ADDRESS_TUPLE(child),
cpu_number());
switch (child->headerType)
{
case kPCIHeaderType0:
if ((child->classCode & 0xFFFFFF) == 0x060000)
break;
pciDeviceProbeRanges(child);
break;
case kPCIHeaderType1:
child->isBridge = true;
pciBridgeProbeRanges(child);
break;
case kPCIHeaderType2:
child->isBridge = true;
pciYentaProbeRanges(child);
break;
default:
DLOG(" bad PCI header type %x\n", child->headerType);
ok = false;
break;
}
pciCheckCacheLineSize(child);
if (ok)
pciBridgeAddChild(bridge, child);
else
IODelete(child, pci_dev, 1);
}
struct BARProbeParam {
CLASS * target;
pci_dev_t device;
UInt32 lastBarNum;
};
void CLASS::safeProbeCallback( void * refcon )
{
BARProbeParam * param = (BARProbeParam *) refcon;
assert(param);
if (cpu_number() == 0)
{
param->target->pciProbeBaseAddressRegister(
param->device, param->lastBarNum );
}
if (cpu_number() == 99)
{
IOLog("safeProbeCallback() gcc workaround\n");
}
}
void CLASS::pciSafeProbeBaseAddressRegister( pci_dev_t device, UInt32 lastBarNum )
{
{
#if NO_RENDEZVOUS_KERNEL
boolean_t istate;
istate = ml_set_interrupts_enabled(FALSE);
pciProbeBaseAddressRegister(device, lastBarNum);
ml_set_interrupts_enabled(istate);
#else
BARProbeParam param;
param.target = this;
param.device = device;
param.lastBarNum = lastBarNum;
mp_rendezvous_no_intrs(&safeProbeCallback, ¶m);
#endif
}
}
void CLASS::pciProbeBaseAddressRegister( pci_dev_t device, UInt32 lastBarNum )
{
IOPCIRange * range;
UInt32 saved;
UInt32 value;
UInt32 barMask;
UInt8 barOffset;
if (kIOPCIRangeExpansionROM == lastBarNum) do
{
lastBarNum--;
barOffset = kIOPCIConfigExpansionROMBase;
barMask = 0x7FF;
range = &device->ranges[kIOPCIRangeExpansionROM];
saved = configRead32(device->space, barOffset);
configWrite32(device->space, barOffset, 0xFFFFFFFF & ~barMask);
value = configRead32(device->space, barOffset);
configWrite32(device->space, barOffset, saved);
if (value == 0)
continue;
range->type = kIOPCIResourceTypeMemory;
value &= ~barMask;
range->start = IOPhysical32( 0, saved & value );
range->size = IOPhysical32( 0, -value );
range->alignment = range->size;
if (!range->start)
device->deviceState = kPCIDeviceStateResourceWait;
}
while (false);
for (UInt32 barNum = 0; barNum <= lastBarNum; barNum++)
{
barOffset = 0x10 + barNum * 4;
range = &device->ranges[barNum];
saved = configRead32(device->space, barOffset);
configWrite32(device->space, barOffset, 0xFFFFFFFF);
value = configRead32(device->space, barOffset);
configWrite32(device->space, barOffset, saved);
if (value == 0)
continue;
if (value & 1)
{
barMask = 0x3;
range->type = kIOPCIResourceTypeIO;
if ((value & 0xFFFF0000) == 0)
{
value |= 0xFFFF0000;
}
}
else
{
barMask = 0xf;
if (value & 0x8)
range->type = kIOPCIResourceTypePrefetchMemory;
else
range->type = kIOPCIResourceTypeMemory;
switch (value & 6)
{
case 2:
case 0:
case 6:
break;
case 4:
barNum++;
break;
}
}
value &= ~barMask;
range->start = IOPhysical32( 0, saved & value );
range->size = IOPhysical32( 0, -value );
range->alignment = range->size;
if (!range->start)
device->deviceState = kPCIDeviceStateResourceWait;
}
}
void CLASS::pciDeviceProbeRanges( pci_dev_t device )
{
pciSafeProbeBaseAddressRegister(device, kIOPCIRangeExpansionROM);
}
void CLASS::pciBridgeProbeBusRange( pci_dev_t bridge )
{
IOPCIRange * range;
bridge->secBusNum = configRead8(bridge->space, kPCI2PCISecondaryBus);
bridge->subBusNum = configRead8(bridge->space, kPCI2PCISubordinateBus);
range = &bridge->ranges[kIOPCIRangeBridgeBusNumber];
range->start = bridge->secBusNum;
range->size = bridge->subBusNum - bridge->secBusNum + 1;
range->alignment = kPCIBridgeBusNumberAlignment;
range->type = kIOPCIResourceTypeBusNumber;
}
void CLASS::pciBridgeProbeRanges( pci_dev_t bridge )
{
IOPCIRange * range;
UInt32 end;
UInt32 start;
UInt32 bar0;
UInt32 bar1;
pciBridgeProbeBusRange(bridge);
bar0 = configRead32(bridge->space, kIOPCIConfigBaseAddress0);
bar1 = configRead32(bridge->space, kIOPCIConfigBaseAddress1);
if (bar0 || bar1)
{
pciSafeProbeBaseAddressRegister(bridge, kIOPCIRangeBAR1);
DLOG(" bridge BAR sizes %llx, %llx\n",
bridge->ranges[0].size, bridge->ranges[1].size);
}
end = configRead32(bridge->space, kPCI2PCIMemoryRange);
if (end)
{
start = (end & 0xfff0) << 16;
end |= 0x000fffff;
if (end > start)
{
range = &bridge->ranges[kIOPCIRangeBridgeMemory];
range->start = start;
range->size = end - start + 1;
range->alignment = kPCIBridgeMemoryAlignment;
range->type = kIOPCIResourceTypeMemory;
}
}
end = configRead32(bridge->space, kPCI2PCIPrefetchMemoryRange);
if (end)
{
start = (end & 0xfff0) << 16;
end |= 0x000fffff;
if (end > start)
{
range = &bridge->ranges[kIOPCIRangeBridgePFMemory];
range->start = start;
range->size = end - start + 1;
range->alignment = kPCIBridgeMemoryAlignment;
range->type = kIOPCIResourceTypePrefetchMemory;
}
}
end = configRead32(bridge->space, kPCI2PCIIORange);
if (end && ((end & (0x0f0f)) == 0))
{
start = (end & 0xf0) << 8;
end = (end & 0xffff) | 0xfff;
if (end > start)
{
range = &bridge->ranges[kIOPCIRangeBridgeIO];
range->start = start;
range->size = end - start + 1;
range->alignment = kPCIBridgeIOAlignment;
range->type = kIOPCIResourceTypeIO;
}
}
DLOG(" bridge bus %x:%x I/O %04llx:%04llx MEM %08llx:%08llx PFM %08llx:%08llx\n",
bridge->secBusNum, bridge->subBusNum,
bridge->ranges[kIOPCIRangeBridgeIO].start,
bridge->ranges[kIOPCIRangeBridgeIO].size,
bridge->ranges[kIOPCIRangeBridgeMemory].start,
bridge->ranges[kIOPCIRangeBridgeMemory].size,
bridge->ranges[kIOPCIRangeBridgePFMemory].start,
bridge->ranges[kIOPCIRangeBridgePFMemory].size);
}
void CLASS::pciYentaProbeRanges( pci_dev_t bridge )
{
IOPCIRange * range;
pciBridgeProbeBusRange(bridge);
range = &bridge->ranges[kIOPCIRangeBridgeBusNumber];
range->flags |= kIOPCIRangeFlagMaximizeSize;
range = &bridge->ranges[kIOPCIRangeBAR0];
range->start = 0;
range->size = 4096;
range->alignment = 4096;
range->type = kIOPCIResourceTypeMemory;
range->flags = 0;
range = &bridge->ranges[kIOPCIRangeBridgeIO];
range->start = 0;
range->size = kPCIBridgeIOAlignment;
range->alignment = kPCIBridgeIOAlignment;
range->type = kIOPCIResourceTypeIO;
range->flags = kIOPCIRangeFlagMaximizeSize;
range = &bridge->ranges[kIOPCIRangeBridgeMemory];
range->start = 0;
range->size = kPCIBridgeMemoryAlignment;
range->alignment = kPCIBridgeMemoryAlignment;
range->type = kIOPCIResourceTypeMemory;
range->flags = kIOPCIRangeFlagMaximizeSize;
bridge->deviceState = kPCIDeviceStateResourceWait;
}
#ifndef ExtractLSB(x)
#define ExtractLSB(x) ((x) & (~((x) - 1)))
#endif
void CLASS::pciCheckCacheLineSize( pci_dev_t device )
{
UInt8 cls, was;
if (!(fFlags & kIOPCIConfiguratorAllocate))
return;
cls = configRead8(device->space, kIOPCIConfigCacheLineSize);
was = cls;
if (fCacheLineSize)
{
if ((cls >= fCacheLineSize) && ((cls % fCacheLineSize) == 0))
return;
}
else
{
if (ExtractLSB(cls) == cls)
return;
}
configWrite8(device->space, kIOPCIConfigCacheLineSize, fCacheLineSize);
cls = configRead8(device->space, kIOPCIConfigCacheLineSize);
if (cls != fCacheLineSize)
{
configWrite8(device->space, kIOPCIConfigCacheLineSize, 0);
}
else
{
DLOG(" changed CLS from %u to %u dwords\n", was, cls);
}
}
void CLASS::pciWriteLatencyTimer( pci_dev_t device )
{
const UInt8 defaultLT = 0x40;
if (device == fPCIBridgeList[0])
return;
configWrite8(device->space, kIOPCIConfigLatencyTimer, defaultLT);
DLOG(" changed LT to %u PCI clocks\n",
configRead8(device->space, kIOPCIConfigLatencyTimer));
if (device->isBridge)
{
configWrite8(device->space, kPCI2PCISecondaryLT, defaultLT);
DLOG(" changed SEC-LT to %u PCI clocks\n",
configRead8(device->space, kPCI2PCISecondaryLT));
}
}
bool CLASS::createRoot( IOService * provider )
{
pci_dev_t bridge;
bridge = IONew(pci_dev, 1);
if (!bridge) return (false);
memset(bridge, 0, sizeof(*bridge));
bridge->classCode = 0x060000;
bridge->headerType = kPCIHeaderType1;
bridge->secBusNum = fRootBridge->firstBusNum();
bridge->subBusNum = fRootBridge->lastBusNum();
bridge->space = fRootBridge->getBridgeSpace();
bridge->ranges[kIOPCIRangeBridgeBusNumber].start = bridge->secBusNum;
bridge->ranges[kIOPCIRangeBridgeBusNumber].size = bridge->subBusNum - bridge->secBusNum + 1;
bridge->ranges[kIOPCIRangeBridgeBusNumber].alignment = 1;
bridge->ranges[kIOPCIRangeBridgeBusNumber].type = kIOPCIResourceTypeBusNumber;
bridge->ranges[kIOPCIRangeBridgeBusNumber].flags = 0;
bridge->dtNub = fRootBridge->getProvider();
bridge->acpiDevice = IOPCICopyACPIDevice(bridge->dtNub);
bridge->isHostBridge = (!OSDynamicCast(IOPCIDevice, bridge->dtNub));
bridge->isBridge = true;
bridge->supportsHotPlug = IOPCIIsHotplugPort(bridge->dtNub);
fPCIBridgeList[0] = bridge;
return true;
}
UInt32 CLASS::configRead32( IOPCIAddressSpace space, UInt32 offset )
{
return (fRootBridge->configRead32(space, offset));
}
void CLASS::configWrite32( IOPCIAddressSpace space,
UInt32 offset, UInt32 data )
{
fRootBridge->configWrite32(space, offset, data);
}
UInt16 CLASS::configRead16( IOPCIAddressSpace space, UInt32 offset )
{
return (fRootBridge->configRead16(space, offset));
}
void CLASS::configWrite16( IOPCIAddressSpace space,
UInt32 offset, UInt16 data )
{
fRootBridge->configWrite16(space, offset, data);
}
UInt8 CLASS::configRead8( IOPCIAddressSpace space, UInt32 offset )
{
return (fRootBridge->configRead8(space, offset));
}
void CLASS::configWrite8( IOPCIAddressSpace space,
UInt32 offset, UInt8 data )
{
fRootBridge->configWrite8(space, offset, data);
}