#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 "OHare.h"
#define super AppleMacIO
OSDefineMetaClassAndStructors(OHare, AppleMacIO);
bool OHare::start(IOService *provider)
{
IOInterruptAction handler;
OSSymbol *interruptControllerName;
AppleNMI *appleNMI;
long nmiSource;
OSData *nmiData;
IOReturn error;
if (!super::start(provider))
return false;
if (IODTMatchNubWithKeys(provider, "ohare"))
ohareNum = kPrimaryOHare;
else if (IODTMatchNubWithKeys(provider, "'pci106b,7'"))
ohareNum = kSecondaryOHare;
else return false;
if (ohareNum == kPrimaryOHare) {
getPlatform()->setCPUInterruptProperties(provider);
}
publishBelow( provider );
ohareBaseAddress = fMemory->getVirtualAddress();
interruptControllerName = getInterruptControllerName();
interruptController = new OHareInterruptController;
if (interruptController == NULL) return false;
error = interruptController->initInterruptController(provider, ohareBaseAddress);
if (error != kIOReturnSuccess) return false;
handler = interruptController->getInterruptHandlerAddress();
provider->registerInterrupt(0, interruptController, handler, 0);
provider->enableInterrupt(0);
getPlatform()->registerInterruptController(interruptControllerName,
interruptController);
if (ohareNum != kPrimaryOHare) return true;
nmiSource = 20;
nmiData = OSData::withBytes(&nmiSource, sizeof(long));
appleNMI = new AppleNMI;
if ((nmiData != 0) && (appleNMI != 0)) {
appleNMI->initNMI(interruptController, nmiData);
}
return true;
}
OSSymbol *OHare::getInterruptControllerName(void)
{
OSSymbol *interruptControllerName;
switch (ohareNum) {
case kPrimaryOHare :
interruptControllerName = gIODTDefaultInterruptController;
break;
case kSecondaryOHare :
interruptControllerName = OSSymbol::withCStringNoCopy("SecondaryInterruptController");
break;
default:
interruptControllerName = OSSymbol::withCStringNoCopy("UnknownInterruptController");
break;
}
return interruptControllerName;
}
#undef super
#define super IOInterruptController
OSDefineMetaClassAndStructors(OHareInterruptController, IOInterruptController);
IOReturn OHareInterruptController::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 OHareInterruptController::getInterruptHandlerAddress(void)
{
return (IOInterruptAction)&OHareInterruptController::handleInterrupt;
}
IOReturn OHareInterruptController::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 OHareInterruptController::vectorCanBeShared(long , IOInterruptVector *)
{
return true;
}
int OHareInterruptController::getVectorType(long vectorNumber, IOInterruptVector *)
{
int interruptType;
if (kTypeLevelMask & (1 << vectorNumber)) {
interruptType = kIOInterruptTypeLevel;
} else {
interruptType = kIOInterruptTypeEdge;
}
return interruptType;
}
void OHareInterruptController::disableVectorHard(long vectorNumber, IOInterruptVector *)
{
unsigned long maskTmp;
maskTmp = lwbrx(maskReg);
maskTmp &= ~(1 << vectorNumber);
stwbrx(maskTmp, maskReg);
eieio();
}
void OHareInterruptController::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 OHareInterruptController::causeVector(long vectorNumber,
IOInterruptVector *)
{
pendingEvents |= 1 << vectorNumber;
parentNub->causeInterrupt(0);
}