#include <ppc/proc_reg.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IODeviceMemory.h>
#include <IOKit/IOPlatformExpert.h>
#include "AppleMPIC.h"
#undef super
#define super IOInterruptController
OSDefineMetaClassAndStructors(AppleMPICInterruptController, IOInterruptController);
bool AppleMPICInterruptController::start(IOService *provider)
{
long cnt, regTemp;
OSObject *tmpObject;
IOInterruptAction handler;
mpic_getProvider = OSSymbol::withCString("mpic_getProvider");
mpic_getIPIVector= OSSymbol::withCString("mpic_getIPIVector");
mpic_setCurrentTaskPriority = OSSymbol::withCString("mpic_setCurrentTaskPriority");
mpic_setUpForSleep = OSSymbol::withCString("mpic_setUpForSleep");
mpic_dispatchIPI = OSSymbol::withCString("mpic_dispatchIPI");
if (!super::start(provider))
return false;
tmpObject = provider->getProperty("InterruptControllerName");
interruptControllerName = OSDynamicCast(OSSymbol, tmpObject);
if (interruptControllerName == 0) return false;
mpicMemoryMap = provider->mapDeviceMemoryWithIndex(0);
if (mpicMemoryMap == 0) return false;
mpicBaseAddress = mpicMemoryMap->getVirtualAddress();
regTemp = lwbrx(mpicBaseAddress + kFeatureOffset);
numCPUs = ((regTemp & kFRRNumCPUMask) >> kFRRNumCPUShift) + 1;
numVectors = ((regTemp & kFRRNumIRQsMask) >> kFRRNumIRQsShift) + 1;
senses = (long *)IOMalloc(((numVectors + numCPUs + 31) / 32)*sizeof(long));
if (senses == NULL) return false;
for (cnt = 0; cnt < ((numVectors + numCPUs + 31) / 32); cnt++)
senses[cnt] = 0;
vectors = (IOInterruptVector *)IOMalloc((numVectors + numCPUs) *
sizeof(IOInterruptVector));
if (vectors == NULL) return false;
bzero(vectors, (numVectors + numCPUs) * sizeof(IOInterruptVector));
for (cnt = 0; cnt < (numVectors + numCPUs) ; cnt++) {
vectors[cnt].interruptLock = IOLockAlloc();
if (vectors[cnt].interruptLock == NULL) {
for (cnt = 0; cnt < (numVectors + numCPUs); cnt++) {
if (vectors[cnt].interruptLock != NULL)
IOLockFree(vectors[cnt].interruptLock);
}
return false;
}
}
stwbrx(kGCR0Reset, mpicBaseAddress + kGlobal0Offset);
eieio();
do {
regTemp = lwbrx(mpicBaseAddress + kGlobal0Offset);
eieio();
} while (regTemp & kGCR0Reset);
for (cnt = 0; cnt < (numVectors + numCPUs); cnt++) {
stwbrx(kIntnVPRMask, mpicBaseAddress+kIntnVecPriOffset+kIntnStride*cnt);
}
eieio();
stwbrx(kSpuriousVectorNumber, mpicBaseAddress + kSpurVectOffset);
stwbrx(kGCR0Cascade, mpicBaseAddress + kGlobal0Offset);
for(cnt = 0; cnt < numCPUs; cnt++) {
stwbrx(0, mpicBaseAddress + kPnCurrTskPriOffset + (kPnStride * cnt));
eieio();
}
originalIpivecPriOffsets = (UInt32*)IOMalloc(sizeof(UInt32) * numCPUs);
if (originalIpivecPriOffsets == NULL) {
return false;
}
originalCurrentTaskPris = (UInt32*)IOMalloc(sizeof(UInt32) * numCPUs);
if (originalCurrentTaskPris == NULL) {
return false;
}
registerService();
getPlatform()->setCPUInterruptProperties(provider);
handler = getInterruptHandlerAddress();
for (cnt = 0; cnt < numCPUs; cnt++) {
provider->registerInterrupt(cnt, this, handler, 0);
provider->enableInterrupt(cnt);
}
getPlatform()->registerInterruptController(interruptControllerName, this);
return true;
}
IOReturn AppleMPICInterruptController::callPlatformFunction(const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4)
{
if (functionName == mpic_getProvider)
{
IORegistryEntry **tmpIORegistryEntry = (IORegistryEntry**)param1;
*tmpIORegistryEntry = getProvider();
return kIOReturnSuccess;
}
if (functionName == mpic_getIPIVector)
{
OSData **tmpOSData = (OSData**)param2;
*tmpOSData = getIPIVector(*(long *)param1);
return kIOReturnSuccess;
}
if (functionName == mpic_setCurrentTaskPriority)
{
setCurrentTaskPriority(*(long *)param1);
return kIOReturnSuccess;
}
if (functionName == mpic_setUpForSleep)
{
setUpForSleep((bool)param1, (int)param2);
return kIOReturnSuccess;
}
if (functionName == mpic_dispatchIPI)
{
dispatchIPI(*(long *)param1, (long)param2);
return kIOReturnSuccess;
}
return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
}
IOReturn AppleMPICInterruptController::getInterruptType(IOService *nub,
int source,
int *interruptType)
{
IOInterruptSource *interruptSources;
OSData *vectorData;
long vectorNumber;
if (interruptType == 0) return kIOReturnBadArgument;
interruptSources = nub->_interruptSources;
vectorData = interruptSources[source].vectorData;
vectorNumber = *(long *)vectorData->getBytesNoCopy();
*interruptType = ((long *)vectorData->getBytesNoCopy())[1];
return kIOReturnSuccess;
}
IOInterruptAction AppleMPICInterruptController::getInterruptHandlerAddress(void)
{
return (IOInterruptAction)&AppleMPICInterruptController::handleInterrupt;
}
IOReturn AppleMPICInterruptController::handleInterrupt(void *,
IOService *,
int source)
{
long vectorNumber, level;
IOInterruptVector *vector;
do {
vectorNumber = lwbrx(mpicBaseAddress + source*kPnStride + kPnIntAckOffset);
eieio();
if (vectorNumber == kSpuriousVectorNumber) break;
level = senses[vectorNumber / 32] & (1 << (vectorNumber & 31));
if (!level) {
stwbrx(0, mpicBaseAddress + source * kPnStride + kPnEOIOffset);
eieio();
}
vector = &vectors[vectorNumber];
vector->interruptActive = 1;
sync();
isync();
if (!vector->interruptDisabledSoft) {
isync();
if (vector->interruptRegistered) {
vector->handler(vector->target, vector->refCon,
vector->nub, vector->source);
if (level && vector->interruptDisabledSoft) {
vector->interruptDisabledHard = 1;
disableVectorHard(vectorNumber, vector);
}
}
} else {
vector->interruptDisabledHard = 1;
disableVectorHard(vectorNumber, vector);
}
if (level) {
stwbrx(0, mpicBaseAddress + source * kPnStride + kPnEOIOffset);
eieio();
}
vector->interruptActive = 0;
} while (1);
return kIOReturnSuccess;
}
bool AppleMPICInterruptController::vectorCanBeShared(long , IOInterruptVector *)
{
return true;
}
void AppleMPICInterruptController::initVector(long vectorNumber, IOInterruptVector *vector)
{
IOInterruptSource *interruptSources;
long vectorType;
OSData *vectorData;
long regTemp, vectorBase;
interruptSources = vector->nub->_interruptSources;
vectorData = interruptSources[vector->source].vectorData;
vectorType = ((long *)vectorData->getBytesNoCopy())[1];
if (vectorType == kIOInterruptTypeEdge) {
senses[vectorNumber / 32] &= ~(1 << (vectorNumber & 31));
} else {
senses[vectorNumber / 32] |= (1 << (vectorNumber & 31));
}
if (vectorNumber < numVectors) {
vectorBase = mpicBaseAddress + kIntnStride * vectorNumber;
regTemp = 0x1; stwbrx(regTemp, vectorBase + kIntnDestOffset);
eieio();
regTemp = kIntnVPRMask | (8 << kIntnVPRPriorityShift);
regTemp |= (vectorType == kIOInterruptTypeLevel) ? kIntnVPRSense : 0;
regTemp |= vectorNumber << kIntnVPRVectorShift;
stwbrx(regTemp, vectorBase + kIntnVecPriOffset);
eieio();
} else {
vectorBase = mpicBaseAddress + kIPInVecPriStride*(vectorNumber-numVectors);
regTemp = kIntnVPRMask | (14 << kIntnVPRPriorityShift);
regTemp |= vectorNumber << kIntnVPRVectorShift;
stwbrx(regTemp, vectorBase + kIPInVecPriOffset);
eieio();
}
}
void AppleMPICInterruptController::disableVectorHard(long vectorNumber, IOInterruptVector *)
{
long regTemp, vectorBase;
if (vectorNumber < numVectors) {
vectorBase = mpicBaseAddress + kIntnVecPriOffset +
kIntnStride * vectorNumber;
} else {
vectorBase = mpicBaseAddress + kIPInVecPriOffset +
kIPInVecPriStride * (vectorNumber - numVectors);
}
regTemp = lwbrx(vectorBase);
regTemp |= kIntnVPRMask;
stwbrx(regTemp, vectorBase);
eieio();
}
void AppleMPICInterruptController::enableVector(long vectorNumber,
IOInterruptVector *)
{
long regTemp, vectorBase;
if (vectorNumber < numVectors) {
vectorBase = mpicBaseAddress + kIntnVecPriOffset +
kIntnStride * vectorNumber;
} else {
vectorBase = mpicBaseAddress + kIPInVecPriOffset +
kIPInVecPriStride * (vectorNumber - numVectors);
}
regTemp = lwbrx(vectorBase);
regTemp &= ~kIntnVPRMask;
stwbrx(regTemp, vectorBase);
eieio();
}
OSData *AppleMPICInterruptController::getIPIVector(long physCPU)
{
long tmpLongs[2];
OSData *tmpData;
if ((physCPU < 0) && (physCPU >= numCPUs)) return 0;
tmpLongs[0] = numVectors + physCPU;
tmpLongs[1] = kIOInterruptTypeEdge;
tmpData = OSData::withBytes(tmpLongs, 2 * sizeof(long));
return tmpData;
}
void AppleMPICInterruptController::dispatchIPI(long source, long targetMask)
{
long ipiBase, cnt;
ipiBase = mpicBaseAddress + kPnIPImDispOffset + kPnStride * source;
for (cnt = 0; cnt < numCPUs; cnt++) {
if (targetMask & (1 << cnt)) {
stwbrx((1 << cnt), ipiBase + kPnIPImDispStride * cnt);
eieio();
}
}
}
void AppleMPICInterruptController::setCurrentTaskPriority(long priority)
{
long cnt;
for(cnt = 0; cnt < numCPUs; cnt++) {
stwbrx(priority, mpicBaseAddress + kPnCurrTskPriOffset + (kPnStride*cnt));
eieio();
}
}
void AppleMPICInterruptController::setUpForSleep(bool goingToSleep, int cpu)
{
volatile UInt32 *ipivecPriOffset = (UInt32*)(mpicBaseAddress + kIPInVecPriOffset + (cpu << 4));
volatile UInt32 *currentTaskPri = (UInt32*)(mpicBaseAddress + kPnCurrTskPriOffset + (kPnStride * cpu));
kprintf("\nAppleMPICInterruptController::setUpForSleep(%s, %d)\n",(goingToSleep ? "true" : "false"), cpu);
if (goingToSleep) {
kprintf("AppleMPICInterruptController::setUpForSleep ipivecPriOffset(0x%08lx) = 0x%08lx\n",(UInt32)ipivecPriOffset, 0x00000080);
kprintf("AppleMPICInterruptController::setUpForSleep currentTaskPri(0x%08lx) = 0x%08lx\n",(UInt32)currentTaskPri, (0x0000000F << 24));
originalIpivecPriOffsets[cpu] = *ipivecPriOffset;
eieio();
originalCurrentTaskPris[cpu] = *currentTaskPri;
eieio();
if (numCPUs > 1) {
*ipivecPriOffset = 0x00000080;
eieio();
}
*currentTaskPri = (0x0000000F << 24);
eieio();
}
else {
kprintf("AppleMPICInterruptController::setUpForSleep ipivecPriOffset(0x%08lx) = 0x%08lx\n",(UInt32)ipivecPriOffset, originalIpivecPriOffsets[cpu]);
kprintf("AppleMPICInterruptController::setUpForSleep currentTaskPri(0x%08lx) = 0x%08lx\n",(UInt32)currentTaskPri, originalCurrentTaskPris[cpu]);
if (numCPUs > 1) {
*ipivecPriOffset = originalIpivecPriOffsets[cpu];
eieio();
}
*currentTaskPri = (0x00000000 << 24); eieio();
}
}