#include <IOKit/platform/ApplePlatformExpert.h>
#include "U3.h"
#include "MacRISC4PE.h"
#include <sys/cdefs.h>
__BEGIN_DECLS
#include <mach/mach_types.h>
__END_DECLS
static const OSSymbol *symsafeReadRegUInt32;
static const OSSymbol *symsafeWriteRegUInt32;
static const OSSymbol *symUniNSetPowerState;
static const OSSymbol *symUniNPrepareForSleep;
#define super IOService
OSDefineMetaClassAndStructors(AppleU3,ApplePlatformExpert)
bool AppleU3::start ( IOService * nub )
{
IOInterruptState intState;
IOPlatformFunction *func;
const OSSymbol *functionSymbol = OSSymbol::withCString(kInstantiatePlatformFunctions);
SInt32 retval;
if (!OSDynamicCast (MacRISC4PE, getPlatform())) return false;
provider = nub;
uniNMemory = provider->mapDeviceMemoryWithIndex( 0 );
if (!uniNMemory) {
kprintf ("AppleU3::start - no memory\n");
return false;
}
uniNBaseAddress = (UInt32 *)uniNMemory->getVirtualAddress();
mutex = IOSimpleLockAlloc();
if (mutex != NULL)
IOSimpleLockInit( mutex );
if ( mutex != NULL )
intState = IOSimpleLockLockDisableInterrupt(mutex);
uniNVersion = readUniNReg(kUniNVersion);
if (uniNVersion < kUniNVersion3) {
kprintf ("AppleU3::start - UniN version 0x%x not supported\n", uniNVersion);
return false;
}
if ( mutex != NULL )
IOSimpleLockUnlockEnableInterrupt(mutex, intState);
if (callPlatformFunction ("PlatformIsPortable", true, (void *) &hostIsMobile, (void *)0,
(void *)0, (void *)0) != kIOReturnSuccess)
hostIsMobile = false;
symsafeReadRegUInt32 = OSSymbol::withCString("safeReadRegUInt32");
symsafeWriteRegUInt32 = OSSymbol::withCString("safeWriteRegUInt32");
symUniNSetPowerState = OSSymbol::withCString("UniNSetPowerState");
symUniNPrepareForSleep = OSSymbol::withCString("UniNPrepareForSleep");
symGetHTLinkFrequency = OSSymbol::withCString("getHTLinkFrequency");
symSetHTLinkFrequency = OSSymbol::withCString("setHTLinkFrequency");
symGetHTLinkWidth = OSSymbol::withCString("getHTLinkWidth");
symSetHTLinkWidth = OSSymbol::withCString("setHTLinkWidth");
symSetSPUSleep = OSSymbol::withCString("setSPUsleep");
symSetPMUSleep = OSSymbol::withCString("sleepNow");
symU3APIPhyDisableProcessor1 = OSSymbol::withCString("u3APIPhyDisableProcessor1");
retval = callPlatformFunction (functionSymbol, true, (void *)provider,
(void *)&platformFuncArray, (void *)0, (void *)0);
if (retval == kIOReturnSuccess && (platformFuncArray != NULL)) {
unsigned int i;
for (i = 0; i < platformFuncArray->getCount(); i++)
if (func = OSDynamicCast (IOPlatformFunction, platformFuncArray->getObject(i)))
if (func->getCommandFlags() & kIOPFFlagOnDemand)
func->publishPlatformFunction (this);
}
if (mpicRegEntry = fromPath ("mpic", gIODTPlane, NULL, NULL, provider)) {
safeWriteRegUInt32(kU3ToggleRegister, kU3MPICEnableOutputs | kU3MPICReset,
kU3MPICEnableOutputs | kU3MPICReset);
}
createNubs(this, provider->getChildIterator( gIODTPlane ));
registerService();
if (kIOReturnSuccess == installChipFaultHandler( provider ))
{
setupDARTExcp();
setupECC();
}
return super::start(provider);
}
void AppleU3::free ()
{
if (platformFuncArray) {
platformFuncArray->flushCollection();
platformFuncArray->release();
}
if (mutex != NULL)
IOSimpleLockFree( mutex );
if (dimmErrors)
IOFree( dimmErrors, sizeof(u3_parity_error_record_t) * dimmCount );
if (dimmLock)
IOSimpleLockFree( dimmLock );
super::free();
return;
}
IOReturn AppleU3::callPlatformFunction(const OSSymbol *functionName, bool waitForFunction,
void *param1, void *param2, void *param3, void *param4)
{
if (functionName == symsafeReadRegUInt32)
{
UInt32 *returnval = (UInt32 *)param2;
*returnval = safeReadRegUInt32((UInt32)param1);
return kIOReturnSuccess;
}
if (functionName == symsafeWriteRegUInt32)
{
safeWriteRegUInt32((UInt32)param1, (UInt32)param2, (UInt32)param3);
return kIOReturnSuccess;
}
if (functionName == symUniNSetPowerState)
{
uniNSetPowerState((UInt32)param1);
return kIOReturnSuccess;
}
if (functionName == symUniNPrepareForSleep)
{
prepareForSleep();
return kIOReturnSuccess;
}
if (functionName == symGetHTLinkFrequency) {
if (getHTLinkFrequency ((UInt32 *)param1))
return kIOReturnSuccess;
return kIOReturnError;
}
if (functionName == symSetHTLinkFrequency) {
if (setHTLinkFrequency ((UInt32)param1))
return kIOReturnSuccess;
return kIOReturnError;
}
if (functionName == symGetHTLinkWidth) {
if (getHTLinkWidth ((UInt32 *)param1, (UInt32 *)param2))
return kIOReturnSuccess;
return kIOReturnError;
}
if (functionName == symSetHTLinkWidth) {
if (setHTLinkWidth ((UInt32)param1, (UInt32)param2))
return kIOReturnSuccess;
return kIOReturnError;
}
if (functionName == symU3APIPhyDisableProcessor1) {
u3APIPhyDisableProcessor1 ();
return kIOReturnSuccess;
}
if (platformFuncArray) {
UInt32 i;
IOPlatformFunction *pfFunc;
for (i = 0; i < platformFuncArray->getCount(); i++)
if (pfFunc = OSDynamicCast (IOPlatformFunction, platformFuncArray->getObject(i)))
if (pfFunc->platformFunctionMatch (functionName, kIOPFFlagOnDemand, NULL))
return (performFunction (pfFunc, param1, param2, param3, param4) ? kIOReturnSuccess : kIOReturnBadArgument);
}
return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
}
IOReturn AppleU3::callPlatformFunction(const char *functionName, bool waitForFunction,
void *param1, void *param2, void *param3, void *param4)
{
IOReturn result = kIOReturnNoMemory;
const OSSymbol *functionSymbol = OSSymbol::withCString(functionName);
if (functionSymbol != 0) {
result = callPlatformFunction(functionSymbol, waitForFunction,
param1, param2, param3, param4);
functionSymbol->release();
}
return result;
}
UInt32 AppleU3::readUniNReg(UInt32 offset)
{
return uniNBaseAddress[offset >> 2];
}
void AppleU3::writeUniNReg(UInt32 offset, UInt32 data)
{
uniNBaseAddress[offset >> 2] = data;
OSSynchronizeIO();
return;
}
UInt32 AppleU3::safeReadRegUInt32(UInt32 offset)
{
IOInterruptState intState;
if ( mutex != NULL )
intState = IOSimpleLockLockDisableInterrupt(mutex);
UInt32 currentReg = readUniNReg(offset);
if ( mutex != NULL )
IOSimpleLockUnlockEnableInterrupt(mutex, intState);
return (currentReg);
}
void AppleU3::safeWriteRegUInt32(UInt32 offset, UInt32 mask, UInt32 data)
{
IOInterruptState intState;
UInt32 currentReg;
if ( mutex != NULL )
intState = IOSimpleLockLockDisableInterrupt(mutex);
if (mask == ~0UL) currentReg = data;
else {
currentReg = readUniNReg(offset);
currentReg = (currentReg & ~mask) | (data & mask);
}
writeUniNReg (offset, currentReg);
if ( mutex != NULL )
IOSimpleLockUnlockEnableInterrupt(mutex, intState);
return;
}
void AppleU3::uniNSetPowerState (UInt32 state)
{
UInt32 data;
if (state == kUniNNormal) { if (mpicRegEntry)
safeWriteRegUInt32(kU3ToggleRegister, kU3MPICEnableOutputs,
kU3MPICEnableOutputs);
safeWriteRegUInt32(kUniNHWInitState, ~0UL, kUniNHWInitStateRunning);
} else if (state == kUniNIdle2) {
IOLog ("AppleU3: Idle 2 state not supported\n");
} else if (state == kUniNSleep) { if (spu) spu->callPlatformFunction (symSetSPUSleep, false, (void *)false, (void *)0, (void *)0, (void *)0);
safeWriteRegUInt32(kUniNHWInitState, ~0UL, kUniNHWInitStateSleeping);
if (mpicRegEntry)
safeWriteRegUInt32(kU3ToggleRegister, kU3MPICEnableOutputs, 0);
setHTLinkFrequency (0);
if (k2)
k2->callPlatformFunction (symSetHTLinkFrequency, false, (void *)0, (void *)0, (void *)0, (void *)0);
setHTLinkWidth (0, 0);
}
return;
}
bool AppleU3::performFunction(const IOPlatformFunction *func, void *cpfParam1,
void *cpfParam2, void *cpfParam3, void *cpfParam4)
{
bool ret;
IOPlatformFunctionIterator *iter;
UInt32 offset, value, valueLen, mask, maskLen, data, writeLen,
cmd, cmdLen, result, pHandle, lastCmd,
param1, param2, param3, param4, param5,
param6, param7, param8, param9, param10;
IOPCIDevice *nub;
if (func == 0) return(false);
if (!(iter = ((IOPlatformFunction *)func)->getCommandIterator()))
return false;
ret = true;
while (iter->getNextCommand (&cmd, &cmdLen, ¶m1, ¶m2, ¶m3, ¶m4,
¶m5, ¶m6, ¶m7, ¶m8, ¶m9, ¶m10, &result) && ret) {
if (result != kIOPFNoError)
ret = false;
else
switch (cmd) {
case kCommandWriteReg32:
offset = param1;
value = param2;
mask = param3;
if (mask != 0xFFFFFFFF) {
data = readUniNReg (offset);
data &= mask;
data |= value;
} else data = value;
writeUniNReg(offset, data);
break;
case kCommandReadConfig:
offset = param1;
valueLen = param2;
if (valueLen != 4) {
IOLog ("AppleU3::performFunction config reads cannot handle anything other than 4 bytes, found length %ld\n", valueLen);
ret = false;
}
if (!nub) {
if (!pHandle) {
IOLog ("AppleU3::performFunction config read requires pHandle to locate nub\n");
ret = false;
}
nub = findNubForPHandle (pHandle);
if (!nub) {
IOLog ("AppleU3::performFunction config read cannot find nub for pHandle 0x%08lx\n", pHandle);
ret = false;
}
}
data = nub->configRead32 (offset);
if (cpfParam1)
*(UInt32 *)cpfParam1 = data;
lastCmd = kCommandReadConfig;
break;
case kCommandRMWConfig:
if (lastCmd != kCommandReadConfig) {
IOLog ("AppleU3::performFunction - config modify/write requires prior read\n");
ret = false;
}
offset = param1;
maskLen = param2;
valueLen = param3;
writeLen = param4;
if (writeLen != 4) {
IOLog ("AppleU3::performFunction config read/modify/write cannot handle anything other than 4 bytes, found length %ld\n", writeLen);
ret = false;
}
mask = *(UInt32 *)param5;
value = *(UInt32 *)param6;
if (!nub) {
if (!pHandle) {
IOLog ("AppleU3::performFunction config read/modify/write requires pHandle to locate nub\n");
ret = false;
}
nub = findNubForPHandle (pHandle);
if (!nub) {
IOLog ("AppleU3::performFunction config read/modify/write cannot find nub for pHandle 0x%08lx\n", pHandle);
ret = false;
}
}
data &= mask;
data |= value;
nub->configWrite32 (offset, data);
break;
default:
IOLog("AppleU3::performFunction got unsupported command %08lx\n", cmd);
ret = false;
break;
}
}
iter->release();
return(ret);
}
IOPCIDevice* AppleU3::findNubForPHandle( UInt32 pHandleValue )
{
IORegistryIterator* iterator;
IORegistryEntry* matchingEntry = NULL;
iterator = IORegistryIterator::iterateOver( gIODTPlane, kIORegistryIterateRecursively );
if ( iterator == NULL )
return( NULL );
while ( ( matchingEntry = iterator->getNextObject() ) != NULL ) {
OSData* property;
if ( ( property = OSDynamicCast( OSData, matchingEntry->getProperty( "AAPL,phandle" ) ) ) != NULL )
if ( pHandleValue == *( ( UInt32 * ) property->getBytesNoCopy() ) )
break;
}
iterator->release();
return( OSDynamicCast (IOPCIDevice, matchingEntry));
}
void AppleU3::prepareForSleep ( void )
{
IOService *service;
static bool noGolem, noSPU;
if (!k2)
k2 = waitForService(serviceMatching("KeyLargo"));
if (!(spu || noSPU)) {
if (fromPath("/spu", gIODTPlane)) spu = waitForService(serviceMatching("AppleSPU"));
noSPU = (spu == NULL); }
if (!pmu) {
service = waitForService(resourceMatching("IOPMU"));
if (service)
pmu = OSDynamicCast (IOService, service->getProperty("IOPMU"));
}
if (!(golem || noGolem)) {
golem = OSDynamicCast (IOPCIDevice, provider->fromPath("/ht@0,F2000000/pci@1", gIODTPlane));
if (golem) {
if (!(IODTMatchNubWithKeys(golem, "pci-x"))) {
noGolem = true; golem = NULL; }
} else
noGolem = true; }
return;
}
bool AppleU3::getHTLinkFrequency (UInt32 *freqResult)
{
UInt32 freq;
freq = safeReadRegUInt32 (kU3HTLinkFreqRegister);
freq = (freq >> 8) & 0xF;
*freqResult = freq;
return true;
}
bool AppleU3::setHTLinkFrequency (UInt32 newFreq)
{
UInt32 freq;
freq = safeReadRegUInt32 (kU3HTLinkFreqRegister);
freq = (freq & 0xFFFFF0FF) | (newFreq << 8);
safeWriteRegUInt32 (kU3HTLinkFreqRegister, ~0UL, freq);
return true;
}
bool AppleU3::getHTLinkWidth (UInt32 *linkOutWidthResult, UInt32 *linkInWidthResult)
{
UInt32 width;
width = safeReadRegUInt32 (kU3HTLinkConfigRegister);
*linkOutWidthResult = (width >> 28) & 0x7;
*linkInWidthResult = (width >> 24) & 0x7;
return true;
}
bool AppleU3::setHTLinkWidth (UInt32 newLinkOutWidth, UInt32 newLinkInWidth)
{
UInt32 width;
width = safeReadRegUInt32 (kU3HTLinkConfigRegister);
width = (width & 0x88FFFFFF) | (newLinkOutWidth << 28) | (newLinkInWidth << 24);
safeWriteRegUInt32 (kU3HTLinkConfigRegister, ~0UL, width);
return true;
}
void AppleU3::u3APIPhyDisableProcessor1 ( void )
{
safeWriteRegUInt32 (kU3APIPhyConfigRegister1, ~0UL, 0);
return;
}
IOReturn AppleU3::installChipFaultHandler ( IOService * provider )
{
const OSData *provider_phandle;
char stringBuf[256];
UInt32 pHandle;
symChipFaultFunc = symPFIntRegister = symPFIntEnable = symPFIntDisable = NULL;
if (provider->getProperty(kChipFaultFuncName) == NULL)
{
if (IS_U3_TWINS(uniNVersion))
IOLog("AppleU3: WARNING: platform-chip-fault expected, but not found\n");
return kIOReturnUnsupported;
}
if ((provider_phandle = OSDynamicCast( OSData, provider->getProperty("AAPL,phandle") )) == NULL)
return kIOReturnNotFound;
symPFIntRegister = OSSymbol::withCString(kIOPFInterruptRegister);
symPFIntEnable = OSSymbol::withCString(kIOPFInterruptEnable);
symPFIntDisable = OSSymbol::withCString(kIOPFInterruptDisable);
pHandle = *((UInt32 *)provider_phandle->getBytesNoCopy());
sprintf(stringBuf, "%s-%08lx", kChipFaultFuncName, pHandle);
symChipFaultFunc = OSSymbol::withCString(stringBuf);
safeWriteRegUInt32 ( kU3ChipFaultMaskRegister, ~0UL, 0 );
return callPlatformFunction(symChipFaultFunc, TRUE,
(void *) AppleU3::sHandleChipFault, this, NULL, (void *) symPFIntRegister);
}
void AppleU3::sHandleChipFault( void * vSelf, void * vRefCon, void * , void * )
{
UInt32 apiexcp;
AppleU3 * me = OSDynamicCast( AppleU3, (OSMetaClassBase *) vSelf );
if (!me) return;
apiexcp = me->safeReadRegUInt32( kU3APIExceptionRegister );
if (apiexcp & kU3API_DARTExcp)
{
char errstr[128];
UInt32 dartexcp = me->safeReadRegUInt32( kU3DARTExceptionRegister );
sprintf( errstr, "DART %s%s%s %s logical page 0x%05lX\n",
( dartexcp & kU3DARTExcpXBEMask ) ? "out-of-bounds exception: " : "",
( dartexcp & kU3DARTExcpXEEMask ) ? "entry exception: " : "",
( dartexcp & kU3DARTExcpRQSRCMask ) ? "HyperTransport" : "PCI0",
( dartexcp & kU3DARTExcpRQOPMask ) ? "write" : "read",
( dartexcp & kU3DARTExcpLogAdrsMask ) >> kU3DARTExcpLogAdrsShift );
panic( errstr );
}
if (IS_U3_HEAVY(me->uniNVersion) &&
(apiexcp & (kU3API_ECC_UE_H | kU3API_ECC_UE_L | kU3API_ECC_CE_H | kU3API_ECC_CE_L)))
{
UInt32 mear, mesr, rank, dimmloc;
mear = me->safeReadRegUInt32( kU3MemErrorAddressRegister );
mesr = me->safeReadRegUInt32( kU3MemErrorSyndromeRegister );
rank = (mear & kU3MEAR_RNK_A_mask) >> kU3MEAR_RNK_A_shift;
if (apiexcp & (kU3API_ECC_UE_H | kU3API_ECC_UE_L))
{
dimmloc = rank - (rank%2) + (apiexcp & kU3API_ECC_UE_H ? 0 : 1);
panic("Uncorrectable parity error detected in %s (MEAR=0x%08lX MESR=0x%08lX)\n",
me->dimmErrors[dimmloc].slotName, mear, mesr);
}
else {
dimmloc = rank - (rank%2) + (apiexcp & kU3API_ECC_CE_H ? 0 : 1);
IOSimpleLockLock( me->dimmLock );
me->dimmErrors[dimmloc].count++;
IOSimpleLockUnlock( me->dimmLock );
if (thread_call_is_delayed( me->eccErrorCallout, NULL ) == FALSE)
{
AbsoluteTime deadline;
clock_interval_to_deadline( kU3ECCNotificationIntervalMS, kMillisecondScale, &deadline );
thread_call_enter1_delayed( me->eccErrorCallout, vRefCon, deadline);
}
}
}
}
void AppleU3::sDispatchECCNotifier( void *self, void *refcon )
{
AppleU3 * me = OSDynamicCast( AppleU3, (OSMetaClassBase *) self );
if (me) me->eccNotifier( refcon );
}
void AppleU3::eccNotifier( void * refcon )
{
UInt32 slot;
UInt32 * dimmErrorCounts;
u3_parity_error_msg_t msg;
dimmErrorCounts = (UInt32 *) IOMalloc( dimmCount * sizeof(UInt32) );
if (!dimmErrorCounts)
{
IOLog("ECC Notifier -- failed to allocate memory!\n");
return;
}
IOSimpleLockLock( dimmLock );
for (slot=0; slot<dimmCount; slot++)
{
dimmErrorCounts[slot] = dimmErrors[slot].count;
dimmErrors[slot].count = 0;
}
IOSimpleLockUnlock( dimmLock );
for (slot=0; slot<dimmCount; slot++)
{
if (dimmErrorCounts[slot] > 0)
{
IOLog("WARNING: %lu parity errors corrected in %s\n",
dimmErrorCounts[slot], dimmErrors[slot].slotName);
msg.version = 0x1;
msg.slotIndex = slot;
strncpy(msg.slotName, dimmErrors[slot].slotName, 32);
msg.count = dimmErrorCounts[slot];
messageClients( kIOPlatformMessageParityError,
(void *) &msg, sizeof(u3_parity_error_msg_t) );
}
}
IOFree( dimmErrorCounts, dimmCount * sizeof(UInt32) );
}
void AppleU3::setupECC( void )
{
IORegistryEntry * memoryNode;
const OSData * slotNamesData;
const char * slotNames;
UInt32 bits, i, slotBitField;
if (!IS_U3_HEAVY(uniNVersion)) return;
if ( ! ( kU3MCCR_ECC_EN & safeReadRegUInt32( kU3MemCheckCtrlRegister ) )) return;
if ((memoryNode = fromPath("/memory", gIODTPlane, 0, 0, 0)) == NULL) return;
slotNamesData = OSDynamicCast(OSData, memoryNode->getProperty("slot-names"));
memoryNode->release();
if (!slotNamesData) return;
slotNames = (const char *) slotNamesData->getBytesNoCopy();
slotBitField = *(const UInt32 *) slotNames; slotNames += sizeof(UInt32);
dimmCount = 0;
for (i=0; i<32; i++)
{
if (!(slotBitField & (1 << i))) break;
dimmCount++;
}
if (dimmCount > kU3MaxDIMMSlots) return;
eccErrorCallout = thread_call_allocate((thread_call_func_t) AppleU3::sDispatchECCNotifier,
(thread_call_param_t) this);
if (!eccErrorCallout) return;
if ((dimmLock = IOSimpleLockAlloc()) == NULL) return;
IOSimpleLockInit( dimmLock );
dimmErrors = (u3_parity_error_record_t *) IOMalloc( sizeof(u3_parity_error_record_t) * dimmCount );
if (!dimmErrors)
{
IOSimpleLockFree( dimmLock );
dimmLock = NULL;
return;
}
for (i=0; i<dimmCount; i++)
{
strncpy( dimmErrors[i].slotName, slotNames, 31 ); dimmErrors[i].slotName[31] = '\0'; dimmErrors[i].count = 0;
slotNames += strlen(slotNames) + 1; }
IOLog( "Enabling ECC Error Notifications\n" );
setProperty("ecc-supported", "true" );
bits = kU3MCCR_ECC_UE_MASK_H | kU3MCCR_ECC_CE_MASK_H | kU3MCCR_ECC_UE_MASK_L | kU3MCCR_ECC_CE_MASK_L;
safeWriteRegUInt32( kU3MemCheckCtrlRegister, bits, ~bits );
bits = kU3API_ECC_UE_H | kU3API_ECC_CE_H | kU3API_ECC_UE_L | kU3API_ECC_CE_L;
safeWriteRegUInt32( kU3ChipFaultMaskRegister, bits, bits );
}
void AppleU3::setupDARTExcp( void )
{
safeWriteRegUInt32( kU3ChipFaultMaskRegister, kU3API_DARTExcp, kU3API_DARTExcp );
}