#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 "AppleVIA.h"
extern "C" {
extern void PE_Determine_Clock_Speeds(unsigned int via_addr,
int num_speeds,
unsigned long *speed_list);
}
#define super IOService
OSDefineMetaClassAndStructors(AppleVIA, IOService);
bool AppleVIA::start(IOService *provider)
{
AppleVIADevice *nub;
IOInterruptAction handler;
IOReturn error;
IOMemoryMap *viaMemoryMap;
OSSymbol *interruptControllerName;
int numSpeeds = 0;
unsigned long *speedList = 0;
if (!super::start(provider))
return false;
if (provider->getProperty("preserveIODeviceTree") != 0)
provider->callPlatformFunction ("mac-io-publishChildren", 0, (void*)this, (void*)0, (void*)0, (void*)0);
if (IODTMatchNubWithKeys(provider, "'via-cuda'"))
viaDeviceType = kVIADeviceTypeCuda;
else if (IODTMatchNubWithKeys(provider, "'via-pmu'"))
viaDeviceType = kVIADeviceTypePMU;
else viaDeviceType = -1;
viaMemoryMap = provider->mapDeviceMemoryWithIndex(0);
if (viaMemoryMap == 0) return false;
viaBaseAddress = viaMemoryMap->getVirtualAddress();
viaMemoryMap->release();
if (provider->getProperty("BusSpeedCorrect") == 0) {
callPlatformFunction("GetDefaultBusSpeeds", false,
&numSpeeds, &speedList, 0, 0);
PE_Determine_Clock_Speeds(viaBaseAddress, numSpeeds, speedList);
}
interruptController = new AppleVIAInterruptController;
if (interruptController == NULL) return false;
error = interruptController->initInterruptController(provider,
viaBaseAddress);
if (error != kIOReturnSuccess) return false;
handler = interruptController->getInterruptHandlerAddress();
provider->registerInterrupt(0, interruptController, handler, 0);
provider->enableInterrupt(0);
interruptControllerName = (OSSymbol *)OSSymbol::withCStringNoCopy(kInterruptControllerName);
getPlatform()->registerInterruptController(interruptControllerName,
interruptController);
nub = createNub();
if (nub == 0) return false;
nub->attach(this);
nub->registerService();
return true;
}
AppleVIADevice *AppleVIA::createNub(void)
{
int cnt;
bool err;
OSSymbol *cName;
OSSymbol *name = 0;
OSArray *array = 0, *vecArray = 0, *deviceMemoryArray;
OSDictionary *dict = 0;
AppleVIADevice *nub = 0;
do {
cName = (OSSymbol *)OSSymbol::withCStringNoCopy(kInterruptControllerName);
if (cName == 0) continue;
vecArray = OSDynamicCast(OSArray, getProperty("vectors"));
if ((vecArray == 0) || (vecArray->getCount() != kNumVectors)) continue;
array = OSArray::withCapacity(kNumVectors);
if (array == 0) continue;
err = false;
for (cnt = 0; cnt < kNumVectors; cnt++) {
if (!array->setObject(cName)) {
err = true;
break;
}
}
if (err) continue;
deviceMemoryArray = getProvider()->getDeviceMemory();
if (deviceMemoryArray == 0) continue;
if (viaDeviceType == kVIADeviceTypeCuda)
name = (OSSymbol *)OSSymbol::withCStringNoCopy("cuda");
else if (viaDeviceType == kVIADeviceTypePMU)
name = (OSSymbol *)OSSymbol::withCStringNoCopy("pmu");
else name = 0;
if (name == 0) continue;
dict = OSDictionary::withCapacity(1);
if (dict == 0) continue;
err = !dict->setObject("name", name);
err |= !dict->setObject(gIOInterruptSpecifiersKey, vecArray);
err |= !dict->setObject(gIOInterruptControllersKey, array);
if (err) continue;
nub = new AppleVIADevice;
if (nub == 0) continue;
if (!nub->init(dict)) {
nub->release();
nub = 0;
continue;
}
nub->setName(name);
nub->setDeviceMemory(deviceMemoryArray);
} while(false);
if(name) name->release();
if(cName) cName->release();
if(array) array->release();
if(dict) dict->release();
return nub;
}
#undef super
#define super IOService
OSDefineMetaClassAndStructors(AppleVIADevice, IOService);
#undef super
#define super IOInterruptController
OSDefineMetaClassAndStructors(AppleVIAInterruptController, IOInterruptController);
IOReturn AppleVIAInterruptController::initInterruptController(IOService *provider, IOLogicalAddress interruptControllerBase)
{
int cnt;
parentNub = provider;
vectors = (IOInterruptVector *)IOMalloc(kNumVectors *
sizeof(IOInterruptVector));
if (vectors == NULL) 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++) {
if (vectors[cnt].interruptLock != NULL)
IOLockFree(vectors[cnt].interruptLock);
}
return kIOReturnNoResources;
}
}
IEReg = (volatile unsigned char *)(interruptControllerBase + kIEOffset);
IFReg = (volatile unsigned char *)(interruptControllerBase + kIFOffset);
PCReg = (volatile unsigned char *)(interruptControllerBase + kPCOffset);
*PCReg = 0x00; eieio();
*IEReg = 0x00; eieio();
*IFReg = 0x7F; eieio();
return kIOReturnSuccess;
}
IOInterruptAction AppleVIAInterruptController::getInterruptHandlerAddress(void)
{
return (IOInterruptAction)&AppleVIAInterruptController::handleInterrupt;
}
IOReturn AppleVIAInterruptController::handleInterrupt(void *,
IOService *,
int )
{
int done;
long events, vectorNumber;
IOInterruptVector *vector;
do {
done = 1;
events = *IFReg; eieio();
events |= pendingEvents;
pendingEvents = 0;
events &= *IEReg & 0x7F; eieio();
if (events) {
done = 0;
*IFReg = events; eieio();
}
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;
}
void AppleVIAInterruptController::disableVectorHard(long vectorNumber, IOInterruptVector *)
{
*IEReg &= ~(1 << vectorNumber);
eieio();
}
void AppleVIAInterruptController::enableVector(long vectorNumber,
IOInterruptVector *vector)
{
*IEReg |= (1 << vectorNumber);
eieio();
}
void AppleVIAInterruptController::causeVector(long vectorNumber,
IOInterruptVector *)
{
pendingEvents |= 1 << vectorNumber;
parentNub->causeInterrupt(0);
}