Source to iokit/Drivers/platform/drvAppleHeathrow/Heathrow.cpp


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

/*
 * Copyright (c) 1998-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 Apple Computer, Inc.  All rights reserved.
 *
 *  DRI: Josh de Cesare
 *
 */


#include <ppc/proc_reg.h>

#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IODeviceMemory.h>
#include <IOKit/IOPlatformExpert.h>

#include "../drvAppleNMI/AppleNMI.h"

#include "Heathrow.h"

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define super AppleMacIO

OSDefineMetaClassAndStructors(Heathrow, AppleMacIO);

bool Heathrow::start(IOService *provider)
{
  bool		     ret;
  
  // Call MacIO's start.
  if (!super::start(provider))
    return false;
  
  // Figure out which heathrow this is.
  if (IODTMatchNubWithKeys(provider, "heathrow"))
    heathrowNum = kPrimaryHeathrow;
  else if (IODTMatchNubWithKeys(provider, "gatwick"))
    heathrowNum = kSecondaryHeathrow;
  else return false; // This should not happen.
  
  if (heathrowNum == kPrimaryHeathrow) {
    if (getPlatform()->getFamily() != 3)
      getPlatform()->setCPUInterruptProperties(provider);
  }
  
  // get the base address of the this heathrow.
  heathrowBaseAddress = fMemory->getVirtualAddress();
  
  // Make nubs for the children.
  publishBelow( provider );

  ret = installInterrupts(provider);

  return ret;
}

bool Heathrow::installInterrupts(IOService *provider)
{
  IORegistryEntry    *regEntry;
  OSSymbol           *interruptControllerName;
  IOInterruptAction  handler;
  AppleNMI           *appleNMI;
  long               nmiSource;
  OSData             *nmiData;
  IOReturn           error;

  // Everything below here is for interrupts, return true if
  // interrupts are not needed.
  if (getPlatform()->getFamily() == 3) return true;
  
  // get the name of the interrupt controller
  if( (regEntry = provider->childFromPath("interrupt-controller",
	gIODTPlane))) {
    interruptControllerName = (OSSymbol *)IODTInterruptControllerName(regEntry);
    regEntry->release();
  } else
    interruptControllerName = getInterruptControllerName();
  
  // Allocate the interruptController instance.
  interruptController = new HeathrowInterruptController;
  if (interruptController == NULL) return false;
  
  // call the interruptController's init method.
  error = interruptController->initInterruptController(provider, heathrowBaseAddress);
  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.
  getPlatform()->registerInterruptController(interruptControllerName,
					     interruptController);
  
  if (heathrowNum != kPrimaryHeathrow) return true;
  
  // Create the NMI Driver.
  nmiSource = 20;
  nmiData = OSData::withBytes(&nmiSource, sizeof(long));
  appleNMI = new AppleNMI;
  if ((nmiData != 0) && (appleNMI != 0)) {
    appleNMI->initNMI(interruptController, nmiData);
  } 
  
  return true;
}

OSSymbol *Heathrow::getInterruptControllerName(void)
{
  OSSymbol *interruptControllerName;
  
  switch (heathrowNum) {
  case kPrimaryHeathrow :
    interruptControllerName = (OSSymbol *)gIODTDefaultInterruptController;
    break;
    
  case kSecondaryHeathrow :
    interruptControllerName = (OSSymbol *)OSSymbol::withCStringNoCopy("SecondaryInterruptController");
    break;
    
  default:
    interruptControllerName = (OSSymbol *)OSSymbol::withCStringNoCopy("UnknownInterruptController");
    break;
  }
  
  return interruptControllerName;
}

void Heathrow::processNub(IOService *nub)
{
  int           cnt, numSources;
  OSArray       *controllerNames, *controllerSources;
  OSSymbol      *interruptControllerName;
  char          *nubName;
  unsigned long heathrowIDs, heathrowFCR;
  
  nubName = (char *)nub->getName();
  
  // Turn on the media bay if this a PowerBook 101.
  if (IODTMatchNubWithKeys(getPlatform()->getProvider(), "'PowerBook1,1'")) {
    if (!strcmp(nubName, "media-bay")) {
      heathrowIDs = lwbrx(heathrowBaseAddress + 0x34);
      if ((heathrowIDs & 0x0000FF00) == 0x00003000) {
	heathrowFCR = lwbrx(heathrowBaseAddress + 0x38);
	heathrowFCR |= 0x00800000;
	stwbrx(heathrowFCR, heathrowBaseAddress + 0x38);
	IOSleep(500);
      }
    }
  }
  
  // change the interrupt controller name for this nub
  // if it is on the secondary heathrow.
  if (heathrowNum == kPrimaryHeathrow) return;
  
  interruptControllerName = getInterruptControllerName();
  
  if (!strcmp(nubName, "ch-a")) {
    controllerSources = OSDynamicCast(OSArray, getProperty("vectors-escc-ch-a"));
  } else if (!strcmp(nubName, "floppy")) {
      controllerSources = OSDynamicCast(OSArray, getProperty("vectors-floppy"));
  } else if (!strcmp(nubName, "ata4")) {
      controllerSources = OSDynamicCast(OSArray, getProperty("vectors-ata4"));
  } else return;
  
  numSources = controllerSources->getCount();
  
  controllerNames = OSArray::withCapacity(numSources);
  for (cnt = 0; cnt < numSources; cnt++) {
    controllerNames->setObject(interruptControllerName);
  }

  nub->setProperty(gIOInterruptControllersKey, controllerNames);
  nub->setProperty(gIOInterruptSpecifiersKey, controllerSources);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#undef  super
#define super IOInterruptController

OSDefineMetaClassAndStructors(HeathrowInterruptController, IOInterruptController);

IOReturn HeathrowInterruptController::initInterruptController(IOService *provider, IOLogicalAddress interruptControllerBase)
{
  int cnt;
  
  parentNub = provider;
  
  // Allocate the task lock.
  taskLock = IOLockAlloc();
  if (taskLock == 0) return kIOReturnNoResources;
  
  // Allocate the memory for the vectors
  vectors = (IOInterruptVector *)IOMalloc(kNumVectors * sizeof(IOInterruptVector));
  if (vectors == NULL) {
    IOLockFree(taskLock);
    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) {
      IOLockFree(taskLock);
      for (cnt = 0; cnt < kNumVectors; cnt++) {
	if (vectors[cnt].interruptLock != NULL)
	  IOLockFree(vectors[cnt].interruptLock);
      }
      return kIOReturnNoResources;
    }
  }
  
  // Setup the registers accessors
  events1Reg = (unsigned long)(interruptControllerBase + kEvents1Offset);
  events2Reg = (unsigned long)(interruptControllerBase + kEvents2Offset);
  mask1Reg   = (unsigned long)(interruptControllerBase + kMask1Offset);
  mask2Reg   = (unsigned long)(interruptControllerBase + kMask2Offset);
  clear1Reg  = (unsigned long)(interruptControllerBase + kClear1Offset);
  clear2Reg  = (unsigned long)(interruptControllerBase + kClear2Offset);
  levels1Reg = (unsigned long)(interruptControllerBase + kLevels1Offset);
  levels2Reg = (unsigned long)(interruptControllerBase + kLevels2Offset);
  
  // Initialize the registers.
  
  // Disable all interrupts.
  stwbrx(0x00000000, mask1Reg);
  stwbrx(0x00000000, mask2Reg);
  eieio();
  
  // Clear all pending interrupts.
  stwbrx(0xFFFFFFFF, clear1Reg);
  stwbrx(0xFFFFFFFF, clear2Reg);
  eieio();
  
  // Disable all interrupts. (again?)
  stwbrx(0x00000000, mask1Reg);
  stwbrx(0x00000000, mask2Reg);
  eieio();
  
  return kIOReturnSuccess;
}

IOInterruptAction HeathrowInterruptController::getInterruptHandlerAddress(void)
{
  return (IOInterruptAction)&HeathrowInterruptController::handleInterrupt;
}

IOReturn HeathrowInterruptController::handleInterrupt(void * /*refCon*/,
						      IOService * /*nub*/,
						      int /*source*/)
{
  int               done;
  long              events, vectorNumber;
  IOInterruptVector *vector;
  unsigned long     maskTmp;
  
  do {
    done = 1;
    
    // Do all the sources for events1, plus any pending interrupts.
    // Also add in the "level" sensitive sources
    maskTmp = lwbrx(mask1Reg);
    events = lwbrx(events1Reg) & ~kTypeLevelMask;
    events |= lwbrx(levels1Reg) & maskTmp & kTypeLevelMask;
    events |= pendingEvents1 & maskTmp;
    pendingEvents1 = 0;
    eieio();
    
    // Since we have to clear the level'd one clear the current edge's too.
    stwbrx(kTypeLevelMask | events, clear1Reg);
    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();
	
	// 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;
    }
    
    // Do all the sources for events2, plus any pending interrupts.
    maskTmp = lwbrx(mask2Reg);
    events = lwbrx(events2Reg);
    events |= pendingEvents1 & maskTmp;
    pendingEvents2 = 0;
    eieio();
    
    if (events) {
      done = 0;
      stwbrx(events, clear2Reg);
      eieio();
    }
    
    while (events) {
      vectorNumber = 31 - cntlzw(events);
      events ^= (1 << vectorNumber);
      vector = &vectors[vectorNumber + kVectorsPerReg];
      
      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 + kVectorsPerReg, vector);
      }
      
      vector->interruptActive = 0;
    }
  } while (!done);
  
  return kIOReturnSuccess;
}

bool HeathrowInterruptController::vectorCanBeShared(long /*vectorNumber*/, IOInterruptVector */*vector*/)
{
  return true;
}

int HeathrowInterruptController::getVectorType(long vectorNumber, IOInterruptVector */*vector*/)
{
  int interruptType;
  
  if (kTypeLevelMask & (1 << vectorNumber)) {
    interruptType = kIOInterruptTypeLevel;
  } else {
    interruptType = kIOInterruptTypeEdge;
  }
  
  return interruptType;
}

void HeathrowInterruptController::disableVectorHard(long vectorNumber, IOInterruptVector */*vector*/)
{
  unsigned long     maskTmp;
  
  // Turn the source off at hardware.
  if (vectorNumber < kVectorsPerReg) {
    maskTmp = lwbrx(mask1Reg);
    maskTmp &= ~(1 << vectorNumber);
    stwbrx(maskTmp, mask1Reg);
    eieio();
  } else {
    vectorNumber -= kVectorsPerReg;
    maskTmp = lwbrx(mask2Reg);
    maskTmp &= ~(1 << vectorNumber);
    stwbrx(maskTmp, mask2Reg);
    eieio();
  }
}

void HeathrowInterruptController::enableVector(long vectorNumber,
					       IOInterruptVector *vector)
{
  unsigned long     maskTmp;
  
  if (vectorNumber < kVectorsPerReg) {
    maskTmp = lwbrx(mask1Reg);
    maskTmp |= (1 << vectorNumber);
    stwbrx(maskTmp, mask1Reg);
    eieio();
    if (lwbrx(levels1Reg) & (1 << vectorNumber)) {
      // lost the interrupt
      causeVector(vectorNumber, vector);
    }
  } else {
    vectorNumber -= kVectorsPerReg;
    maskTmp = lwbrx(mask2Reg);
    maskTmp |= (1 << vectorNumber);
    stwbrx(maskTmp, mask2Reg);
    eieio();
    if (lwbrx(levels1Reg) & (1 << vectorNumber)) {
      // lost the interrupt
      causeVector(vectorNumber + kVectorsPerReg, vector);
    }
  }
}

void HeathrowInterruptController::causeVector(long vectorNumber,
					      IOInterruptVector */*vector*/)
{
  if (vectorNumber < kVectorsPerReg) {
    pendingEvents1 |= 1 << vectorNumber;
  } else {
    vectorNumber -= kVectorsPerReg;
    pendingEvents2 |= 1 << vectorNumber;
  }

  parentNub->causeInterrupt(0);
}