/* * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * DRI: Josh de Cesare */ #include #include #include #include #include #include #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; // Call super's start. if (!super::start(provider)) return false; // Figure out what kind of via device nub to make. if (IODTMatchNubWithKeys(provider, "'via-cuda'")) viaDeviceType = kVIADeviceTypeCuda; else if (IODTMatchNubWithKeys(provider, "'via-pmu'")) viaDeviceType = kVIADeviceTypePMU; else viaDeviceType = -1; // This should not happen. // get the via's base address viaMemoryMap = provider->mapDeviceMemoryWithIndex(0); if (viaMemoryMap == 0) return false; viaBaseAddress = viaMemoryMap->getVirtualAddress(); viaMemoryMap->release(); // Calculate the bus and cpu speeds if needed. if (provider->getProperty("BusSpeedCorrect") == 0) { callPlatformFunction("GetDefaultBusSpeeds", false, &numSpeeds, &speedList, 0, 0); PE_Determine_Clock_Speeds(viaBaseAddress, numSpeeds, speedList); } // Allocate the interruptController instance. interruptController = new AppleVIAInterruptController; if (interruptController == NULL) return false; // call the interruptController's init method. error = interruptController->initInterruptController(provider, viaBaseAddress); if (error != kIOReturnSuccess) return false; handler = interruptController->getInterruptHandlerAddress(); provider->registerInterrupt(0, interruptController, handler, 0); provider->enableInterrupt(0); // Register the interrupt controller so clients can find it. 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 { // create the name symbol for the interrupt controller. cName = (OSSymbol *)OSSymbol::withCStringNoCopy(kInterruptControllerName); if (cName == 0) continue; // create the vector array. vecArray = OSDynamicCast(OSArray, getProperty("vectors")); if ((vecArray == 0) || (vecArray->getCount() != kNumVectors)) continue; // create the controller array. array = OSArray::withCapacity(kNumVectors); if (array == 0) continue; // populate the names array err = false; for (cnt = 0; cnt < kNumVectors; cnt++) { if (!array->setObject(cName)) { err = true; break; } } if (err) continue; // create the deviceMemory array. deviceMemoryArray = getProvider()->getDeviceMemory(); if (deviceMemoryArray == 0) continue; // create the name for the viaDevice nub if (viaDeviceType == kVIADeviceTypeCuda) name = (OSSymbol *)OSSymbol::withCStringNoCopy("cuda"); else if (viaDeviceType == kVIADeviceTypePMU) name = (OSSymbol *)OSSymbol::withCStringNoCopy("pmu"); else name = 0; if (name == 0) continue; // Create the dictionary for the viaDevice nub. dict = OSDictionary::withCapacity(1); if (dict == 0) continue; // add the interrupt numbers and parents to the dictionary. err = !dict->setObject("name", name); err |= !dict->setObject(gIOInterruptSpecifiersKey, vecArray); err |= !dict->setObject(gIOInterruptControllersKey, array); if (err) continue; // Create the viaDevice nub nub = new AppleVIADevice; if (nub == 0) continue; if (!nub->init(dict)) { nub->release(); nub = 0; continue; } // set the nub's name. nub->setName(name); // set the nub's deviceMemory. 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; // Allocate the memory for the vectors vectors = (IOInterruptVector *)IOMalloc(kNumVectors * sizeof(IOInterruptVector)); if (vectors == NULL) return kIOReturnNoMemory; bzero(vectors, kNumVectors * sizeof(IOInterruptVector)); // Allocate locks for the 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; } } // Initialize the registers. IEReg = (volatile unsigned char *)(interruptControllerBase + kIEOffset); IFReg = (volatile unsigned char *)(interruptControllerBase + kIFOffset); PCReg = (volatile unsigned char *)(interruptControllerBase + kPCOffset); // Disable all interrupts. *PCReg = 0x00; eieio(); *IEReg = 0x00; eieio(); *IFReg = 0x7F; eieio(); return kIOReturnSuccess; } IOInterruptAction AppleVIAInterruptController::getInterruptHandlerAddress(void) { return (IOInterruptAction)&AppleVIAInterruptController::handleInterrupt; } IOReturn AppleVIAInterruptController::handleInterrupt(void */*refCon*/, IOService */*nub*/, int /*source*/) { int done; long events, vectorNumber; IOInterruptVector *vector; do { done = 1; // Do all the sources for events. 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(); // Call the handler if it exists. if (vector->interruptRegistered) { vector->handler(vector->target, vector->refCon, vector->nub, vector->source); } } else { // Hard disable the source. vector->interruptDisabledHard = 1; disableVectorHard(vectorNumber, vector); } vector->interruptActive = 0; } } while (!done); return kIOReturnSuccess; } void AppleVIAInterruptController::disableVectorHard(long vectorNumber, IOInterruptVector */*vector*/) { *IEReg &= ~(1 << vectorNumber); eieio(); } void AppleVIAInterruptController::enableVector(long vectorNumber, IOInterruptVector *vector) { *IEReg |= (1 << vectorNumber); eieio(); } void AppleVIAInterruptController::causeVector(long vectorNumber, IOInterruptVector */*vector*/) { pendingEvents |= 1 << vectorNumber; parentNub->causeInterrupt(0); }