#include <ppc/proc_reg.h>
#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IODeviceMemory.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/platform/AppleNMI.h>
#include "GrandCentral.h"
#define super AppleMacIO
OSDefineMetaClassAndStructors(GrandCentral, AppleMacIO);
bool GrandCentral::start(IOService *provider)
{
IOInterruptAction handler;
IOPhysicalAddress base;
OSData * data;
AppleNMI *appleNMI;
IOService *sixty6;
long nmiSource;
OSData *nmiData;
IOReturn error;
if (!super::start(provider))
return false;
base = fMemory->getPhysicalAddress();
data = OSData::withBytes(&base, sizeof(base));
if (data != 0) provider->setProperty("AAPL,address", data);
if (provider->childFromPath("sixty6", gIODTPlane) == 0) {
sixty6 = new IOService;
if(sixty6->init()) {
sixty6->setName("sixty6");
sixty6->attachToParent(provider, gIODTPlane);
sixty6->registerService();
}
}
publishBelow( provider );
grandCentralBaseAddress = fMemory->getVirtualAddress();
getPlatform()->setCPUInterruptProperties(provider);
interruptController = new GrandCentralInterruptController;
if (interruptController == NULL) return false;
error = interruptController->initInterruptController(provider, grandCentralBaseAddress);
if (error != kIOReturnSuccess) return false;
handler = interruptController->getInterruptHandlerAddress();
provider->registerInterrupt(0, interruptController, handler, 0);
provider->enableInterrupt(0);
getPlatform()->registerInterruptController(gIODTDefaultInterruptController,
interruptController);
nmiSource = 20;
nmiData = OSData::withBytes(&nmiSource, sizeof(long));
appleNMI = new AppleNMI;
if ((nmiData != 0) && (appleNMI != 0)) {
appleNMI->initNMI(interruptController, nmiData);
}
return true;
}
#undef super
#define super IOInterruptController
OSDefineMetaClassAndStructors(GrandCentralInterruptController, IOInterruptController);
IOReturn GrandCentralInterruptController::initInterruptController(IOService *provider, IOLogicalAddress interruptControllerBase)
{
int cnt;
parentNub = provider;
taskLock = IOLockAlloc();
if (taskLock == 0) return kIOReturnNoResources;
vectors = (IOInterruptVector *)IOMalloc(kNumVectors * sizeof(IOInterruptVector));
if (vectors == NULL) {
IOLockFree(taskLock);
return kIOReturnNoMemory;
}
bzero(vectors, kNumVectors * sizeof(IOInterruptVector));
for (cnt = 0; cnt < kNumVectors; cnt++) {
vectors[cnt].interruptLock = IOLockAlloc();
if (vectors[cnt].interruptLock == NULL) {
for (cnt = 0; cnt < kNumVectors; cnt++) {
IOLockFree(taskLock);
if (vectors[cnt].interruptLock != NULL)
IOLockFree(vectors[cnt].interruptLock);
}
return kIOReturnNoResources;
}
}
eventsReg = (unsigned long)(interruptControllerBase + kEventsOffset);
maskReg = (unsigned long)(interruptControllerBase + kMaskOffset);
clearReg = (unsigned long)(interruptControllerBase + kClearOffset);
levelsReg = (unsigned long)(interruptControllerBase + kLevelsOffset);
stwbrx(0x00000000, maskReg);
eieio();
stwbrx(0xFFFFFFFF, clearReg);
eieio();
stwbrx(0x00000000, maskReg);
eieio();
return kIOReturnSuccess;
}
IOInterruptAction GrandCentralInterruptController::getInterruptHandlerAddress(void)
{
return (IOInterruptAction)&GrandCentralInterruptController::handleInterrupt;
}
IOReturn GrandCentralInterruptController::handleInterrupt(void * ,
IOService * ,
int )
{
int done;
long events, vectorNumber;
IOInterruptVector *vector;
unsigned long maskTmp;
do {
done = 1;
maskTmp = lwbrx(maskReg);
events = lwbrx(eventsReg) & ~kTypeLevelMask;
events |= lwbrx(levelsReg) & maskTmp & kTypeLevelMask;
events |= pendingEvents & maskTmp;
pendingEvents = 0;
eieio();
stwbrx(kTypeLevelMask | events, clearReg);
eieio();
if (events) done = 0;
while (events) {
vectorNumber = 31 - cntlzw(events);
events ^= (1 << vectorNumber);
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);
}
} else {
vector->interruptDisabledHard = 1;
disableVectorHard(vectorNumber, vector);
}
vector->interruptActive = 0;
}
} while (!done);
return kIOReturnSuccess;
}
bool GrandCentralInterruptController::vectorCanBeShared(long , IOInterruptVector *)
{
return true;
}
int GrandCentralInterruptController::getVectorType(long vectorNumber, IOInterruptVector *)
{
int interruptType;
if (kTypeLevelMask & (1 << vectorNumber)) {
interruptType = kIOInterruptTypeLevel;
} else {
interruptType = kIOInterruptTypeEdge;
}
return interruptType;
}
void GrandCentralInterruptController::disableVectorHard(long vectorNumber, IOInterruptVector *)
{
unsigned long maskTmp;
maskTmp = lwbrx(maskReg);
maskTmp &= ~(1 << vectorNumber);
stwbrx(maskTmp, maskReg);
eieio();
}
void GrandCentralInterruptController::enableVector(long vectorNumber,
IOInterruptVector *vector)
{
unsigned long maskTmp;
maskTmp = lwbrx(maskReg);
maskTmp |= (1 << vectorNumber);
stwbrx(maskTmp, maskReg);
eieio();
if (lwbrx(levelsReg) & (1 << vectorNumber)) {
causeVector(vectorNumber, vector);
}
}
void GrandCentralInterruptController::causeVector(long vectorNumber, IOInterruptVector *)
{
pendingEvents |= 1 << vectorNumber;
parentNub->causeInterrupt(0);
}