Source to iokit/Drivers/platform/drvAppleMPIC/AppleMPIC.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This 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 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-2000 Apple Computer, Inc.  All rights reserved.
 *
 *  DRI: Josh de Cesare
 */


#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;
  IOInterruptAction handler;
  
  if (!super::start(provider))
    return false;
  
  // If this is a PowerExpress get a different name.
  if (getPlatform()->getFamily() == 3) {
    interruptControllerName = gIODTDefaultInterruptController;
  } else {
    interruptControllerName = IODTInterruptControllerName(provider);
  }
  
  // Map the MPIC's memory.
  mpicMemoryMap = provider->mapDeviceMemoryWithIndex(0);
  
  if (mpicMemoryMap == 0) return false;
  
  // get the base address of the MPIC.
  mpicBaseAddress = mpicMemoryMap->getVirtualAddress();
  
  // Read the Feature Reporting Register
  regTemp = lwbrx(mpicBaseAddress + kFeatureOffset);
  numCPUs =    ((regTemp & kFRRNumCPUMask) >> kFRRNumCPUShift) + 1;
  numVectors = ((regTemp & kFRRNumIRQsMask) >> kFRRNumIRQsShift) + 1;
  
  // Allocate the memory for the senses.
  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;
  
  // Allocate the memory for the vectors.
  vectors = (IOInterruptVector *)IOMalloc((numVectors + numCPUs) *
					  sizeof(IOInterruptVector));
  if (vectors == NULL) return false;
  bzero(vectors, (numVectors + numCPUs) * sizeof(IOInterruptVector));
  
  // Allocate locks for the vectors.
  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;
    }
  }
  
  // Reset the MPIC.
  stwbrx(kGCR0Reset, mpicBaseAddress + kGlobal0Offset);
  eieio();
  
  // Wait for the reset to complete.
  do {
    regTemp = lwbrx(mpicBaseAddress + kGlobal0Offset);
    eieio();
  }  while (regTemp & kGCR0Reset);
  
  // Clear and mask all the interrupt vectors.
  for (cnt = 0; cnt < (numVectors + numCPUs); cnt++) {
    stwbrx(kIntnVPRMask, mpicBaseAddress+kIntnVecPriOffset+kIntnStride*cnt);
  }
  eieio();
  
  // Set the Spurious Vector Register.
  stwbrx(kSpuriousVectorNumber, mpicBaseAddress + kSpurVectOffset);
  
  // Set 8259 Cascade Mode.
  stwbrx(kGCR0Cascade, mpicBaseAddress + kGlobal0Offset);
  
  // Set the all CPUs Current Task Priority to zero.
  for(cnt = 0; cnt < numCPUs; cnt++) {		/* Do 'em all */
    stwbrx(0, mpicBaseAddress + kPnCurrTskPriOffset + (kPnStride * cnt));
    eieio();
  }
  
  registerService();  
  
  getPlatform()->setCPUInterruptProperties(provider);
  
  // register the interrupt handler so it can receive interrupts.
  handler = getInterruptHandlerAddress();
  for (cnt = 0; cnt < numCPUs; cnt++) {
    provider->registerInterrupt(cnt, this, handler, 0);
    provider->enableInterrupt(cnt);
  }
  
  // Register this interrupt controller so clients can find it.
  getPlatform()->registerInterruptController(interruptControllerName, this);
  
  return true;
}

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 */*refCon*/,
						       IOService */*nub*/,
						       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();
      
      // Call the handler if it exists.
      if (vector->interruptRegistered) {
	vector->handler(vector->target, vector->refCon,
			vector->nub, vector->source);
	
	if (level && vector->interruptDisabledSoft) {
	  // Hard disable the source.
	  vector->interruptDisabledHard = 1;
	  disableVectorHard(vectorNumber, vector);
	}
      }
    } else {
      // Hard disable the source.
      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 /*vectorNumber*/, IOInterruptVector */*vector*/)
{
  return true;
}

void AppleMPICInterruptController::initVector(long vectorNumber, IOInterruptVector *vector)
{
  IOInterruptSource *interruptSources;
  long              vectorType;
  OSData            *vectorData;
  long              regTemp, vectorBase;
  
  // Get the vector's type.
  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));
  }
  
  // Set the Vector/Priority and Destination Registers
  if (vectorNumber < numVectors) {
    vectorBase = mpicBaseAddress + kIntnStride * vectorNumber;
    
    regTemp = 0x1; // CPU 0 only.
    stwbrx(regTemp, vectorBase + kIntnDestOffset);
    eieio();
    
    // Vectors start masked with priority 8.
    regTemp  = kIntnVPRMask | (8 << kIntnVPRPriorityShift);
    regTemp |= (vectorType == kIOInterruptTypeLevel) ? kIntnVPRSense : 0;
    regTemp |= vectorNumber << kIntnVPRVectorShift;
    stwbrx(regTemp, vectorBase + kIntnVecPriOffset);
    eieio();
  } else {
    vectorBase = mpicBaseAddress + kIPInVecPriStride*(vectorNumber-numVectors);
    
    // IPI Vectors start masked with priority 14.
    regTemp  = kIntnVPRMask | (14 << kIntnVPRPriorityShift);
    regTemp |= vectorNumber << kIntnVPRVectorShift;
    stwbrx(regTemp, vectorBase + kIPInVecPriOffset);
    eieio();
  }
}

void AppleMPICInterruptController::disableVectorHard(long vectorNumber, IOInterruptVector */*vector*/)
{
  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 */*vector*/)
{
  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();
    }
  }
}