]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOInterruptController.cpp
xnu-792.6.56.tar.gz
[apple/xnu.git] / iokit / Kernel / IOInterruptController.cpp
index 3b7d49b5a622b817656b696b14c0389cd1a4c491..7defbe15220c666d9bd703c6cdff5fcccf3ef5c4 100644 (file)
@@ -3,19 +3,20 @@
  *
  * @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 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.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * 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 OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * 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@
  */
@@ -34,6 +35,7 @@
 #include <IOKit/IOLib.h>
 #include <IOKit/IOService.h>
 #include <IOKit/IOPlatformExpert.h>
+#include <IOKit/IODeviceTreeSupport.h>
 #include <IOKit/IOInterrupts.h>
 #include <IOKit/IOInterruptController.h>
 
@@ -66,6 +68,8 @@ IOReturn IOInterruptController::registerInterrupt(IOService *nub, int source,
   OSData            *vectorData;
   IOService         *originalNub;
   int               originalSource;
+  IOOptionBits      options;
+  bool              canBeShared, shouldBeShared, wasAlreadyRegisterd;
   
   interruptSources = nub->_interruptSources;
   vectorData = interruptSources[source].vectorData;
@@ -75,14 +79,22 @@ IOReturn IOInterruptController::registerInterrupt(IOService *nub, int source,
   // Get the lock for this vector.
   IOTakeLock(vector->interruptLock);
   
-  // If this vector is already in use, and can be shared,
+  // Check if the interrupt source can/should be shared.
+  canBeShared = vectorCanBeShared(vectorNumber, vector);
+  IODTGetInterruptOptions(nub, source, &options);
+  shouldBeShared = canBeShared && (options & kIODTInterruptShared);
+  wasAlreadyRegisterd = vector->interruptRegistered;
+  
+  // If the vector is registered and can not be shared return error.
+  if (wasAlreadyRegisterd && !canBeShared) {
+    IOUnlock(vector->interruptLock);
+    return kIOReturnNoResources;
+  }
+  
+  // If this vector is already in use, and can be shared (implied),
+  // or it is not registered and should be shared,
   // register as a shared interrupt.
-  if (vector->interruptRegistered) {
-    if (!vectorCanBeShared(vectorNumber, vector)) {
-      IOUnlock(vector->interruptLock);
-      return kIOReturnNoResources;
-    }
-    
+  if (wasAlreadyRegisterd || shouldBeShared) {
     // If this vector is not already shared, break it out.
     if (vector->sharedController == 0) {
       // Make the IOShareInterruptController instance
@@ -92,44 +104,57 @@ IOReturn IOInterruptController::registerInterrupt(IOService *nub, int source,
         return kIOReturnNoMemory;
       }
       
-      // Save the nub and source for the original consumer.
-      originalNub = vector->nub;
-      originalSource = vector->source;
-      
-      // Save the dis/enable state for the original consumer's interrupt.
-      // Then disable the source
-      wasDisabledSoft = vector->interruptDisabledSoft;
-      disableInterrupt(originalNub, originalSource);
+      if (wasAlreadyRegisterd) {
+       // Save the nub and source for the original consumer.
+       originalNub = vector->nub;
+       originalSource = vector->source;
+       
+       // Physically disable the interrupt, but mark it as being enabled in the hardware.
+       // The interruptDisabledSoft now indicates the driver's request for enablement.
+       disableVectorHard(vectorNumber, vector);
+       vector->interruptDisabledHard = 0;
+      }
       
       // Initialize the new shared interrupt controller.
-      error = vector->sharedController->initInterruptController(this,
-                                                                vectorData);
+      error = vector->sharedController->initInterruptController(this, vectorData);
       // If the IOSharedInterruptController could not be initalized,
-      // put the original consumor's interrupt back to normal and
+      // if needed, put the original consumer's interrupt back to normal and
       // get rid of whats left of the shared controller.
       if (error != kIOReturnSuccess) {
-        enableInterrupt(originalNub, originalSource);
+       if (wasAlreadyRegisterd) enableInterrupt(originalNub, originalSource);
         vector->sharedController->release();
         vector->sharedController = 0;
         IOUnlock(vector->interruptLock);
         return error;
       }
       
-      // Try to register the original consumer on the shared controller.
-      error = vector->sharedController->registerInterrupt(originalNub,
-                                                          originalSource,
-                                                          vector->target,
-                                                          vector->handler,
-                                                          vector->refCon);
-      // If the original consumer could not be moved to the shared controller,
-      // put the original consumor's interrupt back to normal and
-      // get rid of whats left of the shared controller.
-      if (error != kIOReturnSuccess) {
-        enableInterrupt(originalNub, originalSource);
-        vector->sharedController->release();
-        vector->sharedController = 0;
-        IOUnlock(vector->interruptLock);
-        return error;
+      // If there was an original consumer try to register it on the shared controller.
+      if (wasAlreadyRegisterd) {
+       error = vector->sharedController->registerInterrupt(originalNub,
+                                                           originalSource,
+                                                           vector->target,
+                                                           vector->handler,
+                                                           vector->refCon);
+       // If the original consumer could not be moved to the shared controller,
+       // put the original consumor's interrupt back to normal and
+       // get rid of whats left of the shared controller.
+       if (error != kIOReturnSuccess) {
+         // Save the driver's interrupt enablement state.
+         wasDisabledSoft = vector->interruptDisabledSoft;
+         
+         // Make the interrupt really hard disabled.
+         vector->interruptDisabledSoft = 1;
+         vector->interruptDisabledHard = 1;
+         
+         // Enable the original consumer's interrupt if needed.
+         if (!wasDisabledSoft) originalNub->enableInterrupt(originalSource);
+         enableInterrupt(originalNub, originalSource);
+         
+         vector->sharedController->release();
+         vector->sharedController = 0;
+         IOUnlock(vector->interruptLock);
+         return error;
+       }
       }
       
       // Fill in vector with the shared controller's info.
@@ -139,6 +164,19 @@ IOReturn IOInterruptController::registerInterrupt(IOService *nub, int source,
       vector->target  = vector->sharedController;
       vector->refCon  = 0;
       
+      // If the interrupt was already registered,
+      // save the driver's interrupt enablement state.
+      if (wasAlreadyRegisterd) wasDisabledSoft = vector->interruptDisabledSoft;
+      else wasDisabledSoft = true;
+      
+      // Do any specific initalization for this vector if it has not yet been used.
+      if (!wasAlreadyRegisterd) initVector(vectorNumber, vector);
+      
+      // Make the interrupt really hard disabled.
+      vector->interruptDisabledSoft = 1;
+      vector->interruptDisabledHard = 1;
+      vector->interruptRegistered   = 1;
+      
       // Enable the original consumer's interrupt if needed.
       if (!wasDisabledSoft) originalNub->enableInterrupt(originalSource);
     }
@@ -371,6 +409,8 @@ OSMetaClassDefineReservedUnused(IOSharedInterruptController, 3);
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
+#define kIOSharedInterruptControllerDefaultVectors (128)
+
 IOReturn IOSharedInterruptController::initInterruptController(IOInterruptController *parentController, OSData *parentSource)
 {
   int      cnt, interruptType;
@@ -399,7 +439,7 @@ IOReturn IOSharedInterruptController::initInterruptController(IOInterruptControl
   }
   
   // Allocate the memory for the vectors
-  numVectors = 8; // For now a constant number.
+  numVectors = kIOSharedInterruptControllerDefaultVectors; // For now a constant number.
   vectors = (IOInterruptVector *)IOMalloc(numVectors * sizeof(IOInterruptVector));
   if (vectors == NULL) {
     IOFree(_interruptSources, sizeof(IOInterruptSource));
@@ -423,6 +463,7 @@ IOReturn IOSharedInterruptController::initInterruptController(IOInterruptControl
     }
   }
   
+  numVectors = 0; // reset the high water mark for used vectors
   vectorsRegistered = 0;
   vectorsEnabled = 0;
   controllerDisabled = 1;
@@ -445,9 +486,9 @@ IOReturn IOSharedInterruptController::registerInterrupt(IOService *nub,
   interruptSources = nub->_interruptSources;
   
   // Find a free vector.
-  vectorNumber = numVectors;
-  while (vectorsRegistered != numVectors) {
-    for (vectorNumber = 0; vectorNumber < numVectors; vectorNumber++) {
+  vectorNumber = kIOSharedInterruptControllerDefaultVectors;
+  while (vectorsRegistered != kIOSharedInterruptControllerDefaultVectors) {
+    for (vectorNumber = 0; vectorNumber < kIOSharedInterruptControllerDefaultVectors; vectorNumber++) {
       vector = &vectors[vectorNumber];
       
       // Get the lock for this vector.
@@ -460,11 +501,11 @@ IOReturn IOSharedInterruptController::registerInterrupt(IOService *nub,
       IOUnlock(vector->interruptLock);
     }
     
-    if (vectorNumber != numVectors) break;
+    if (vectorNumber != kIOSharedInterruptControllerDefaultVectors) break;
   }
   
   // Could not find a free one, so give up.
-  if (vectorNumber == numVectors) {
+  if (vectorNumber == kIOSharedInterruptControllerDefaultVectors) {
     return kIOReturnNoResources;
   }
   
@@ -485,12 +526,13 @@ IOReturn IOSharedInterruptController::registerInterrupt(IOService *nub,
   vector->target  = target;
   vector->refCon  = refCon;
   
-  // Get the vector ready.  It start soft disabled.
+  // Get the vector ready.  It starts off soft disabled.
   vector->interruptDisabledSoft = 1;
   vector->interruptRegistered   = 1;
   
   interruptState = IOSimpleLockLockDisableInterrupt(controllerLock);
-  vectorsRegistered++;
+  // Move the high water mark if needed
+  if (++vectorsRegistered > numVectors) numVectors = vectorsRegistered;
   IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
   
   IOUnlock(vector->interruptLock);
@@ -504,7 +546,7 @@ IOReturn IOSharedInterruptController::unregisterInterrupt(IOService *nub,
   long              vectorNumber;
   IOInterruptVector *vector;
   OSData            *vectorData;
-  IOInterruptState  interruptState;;
+  IOInterruptState  interruptState;
   
   interruptSources = nub->_interruptSources;
   vectorData = interruptSources[source].vectorData;
@@ -520,7 +562,7 @@ IOReturn IOSharedInterruptController::unregisterInterrupt(IOService *nub,
     return kIOReturnSuccess;
   }
   
-  // Soft disable the source.
+  // Soft disable the source and the controller too.
   disableInterrupt(nub, source);
   
   // Clear all the storage for the vector except for interruptLock.
@@ -539,6 +581,13 @@ IOReturn IOSharedInterruptController::unregisterInterrupt(IOService *nub,
   IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
   
   IOUnlock(vector->interruptLock);
+  
+  // Re-enable the controller if all vectors are enabled.
+  if (vectorsEnabled == vectorsRegistered) {
+    controllerDisabled = 0;
+    provider->enableInterrupt(0);
+  }
+  
   return kIOReturnSuccess;
 }
 
@@ -556,23 +605,26 @@ IOReturn IOSharedInterruptController::enableInterrupt(IOService *nub,
   long              vectorNumber;
   IOInterruptVector *vector;
   OSData            *vectorData;
-  IOInterruptState  interruptState;;
+  IOInterruptState  interruptState;
   
   interruptSources = nub->_interruptSources;
   vectorData = interruptSources[source].vectorData;
   vectorNumber = *(long *)vectorData->getBytesNoCopy();
   vector = &vectors[vectorNumber];
   
-  if (vector->interruptDisabledSoft) {
-    interruptState = IOSimpleLockLockDisableInterrupt(controllerLock);
-    vector->interruptDisabledSoft = 0;
-    vectorsEnabled++;
+  interruptState = IOSimpleLockLockDisableInterrupt(controllerLock);
+  if (!vector->interruptDisabledSoft) {
     IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
-    
-    if (controllerDisabled && (vectorsEnabled == vectorsRegistered)) {
-      controllerDisabled = 0;
-      provider->enableInterrupt(0);
-    }
+    return kIOReturnSuccess;
+  }
+  
+  vector->interruptDisabledSoft = 0;
+  vectorsEnabled++;
+  IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
+  
+  if (controllerDisabled && (vectorsEnabled == vectorsRegistered)) {
+    controllerDisabled = 0;
+    provider->enableInterrupt(0);
   }
   
   return kIOReturnSuccess;
@@ -585,23 +637,23 @@ IOReturn IOSharedInterruptController::disableInterrupt(IOService *nub,
   long              vectorNumber;
   IOInterruptVector *vector;
   OSData            *vectorData;
-  IOInterruptState  interruptState;;
+  IOInterruptState  interruptState;
   
   interruptSources = nub->_interruptSources;
   vectorData = interruptSources[source].vectorData;
   vectorNumber = *(long *)vectorData->getBytesNoCopy();
   vector = &vectors[vectorNumber];
   
+  interruptState = IOSimpleLockLockDisableInterrupt(controllerLock); 
   if (!vector->interruptDisabledSoft) {
-    interruptState = IOSimpleLockLockDisableInterrupt(controllerLock); 
     vector->interruptDisabledSoft = 1;
 #if __ppc__
     sync();
     isync();
 #endif
     vectorsEnabled--;
-    IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
   }
+  IOSimpleLockUnlockEnableInterrupt(controllerLock, interruptState);
   
   if (!getPlatform()->atInterruptLevel()) {
     while (vector->interruptActive);