+/*
+ * For machines where the transfers on an I/O bus can stall because
+ * the CPU is in an idle mode, These APIs allow a driver to specify
+ * the maximum bus stall that they can handle. 0 indicates no limit.
+ */
+void IOService::
+setCPUSnoopDelay(UInt32 __unused ns)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ ml_set_maxsnoop(ns);
+#endif /* defined(__i386__) || defined(__x86_64__) */
+}
+
+UInt32 IOService::
+getCPUSnoopDelay()
+{
+#if defined(__i386__) || defined(__x86_64__)
+ return ml_get_maxsnoop();
+#else
+ return 0;
+#endif /* defined(__i386__) || defined(__x86_64__) */
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+static void
+requireMaxCpuDelay(IOService * service, UInt32 ns, UInt32 delayType)
+{
+ static const UInt kNoReplace = -1U; // Must be an illegal index
+ UInt replace = kNoReplace;
+ bool setCpuDelay = false;
+
+ IORecursiveLockLock(sCpuDelayLock);
+
+ UInt count = sCpuDelayData->getLength() / sizeof(CpuDelayEntry);
+ CpuDelayEntry *entries = (CpuDelayEntry *) sCpuDelayData->getBytesNoCopy();
+ IOService * holder = NULL;
+
+ if (ns) {
+ const CpuDelayEntry ne = {service, ns, delayType};
+ holder = service;
+ // Set maximum delay.
+ for (UInt i = 0; i < count; i++) {
+ IOService *thisService = entries[i].fService;
+ bool sameType = (delayType == entries[i].fDelayType);
+ if ((service == thisService) && sameType)
+ replace = i;
+ else if (!thisService) {
+ if (kNoReplace == replace)
+ replace = i;
+ }
+ else if (sameType) {
+ const UInt32 thisMax = entries[i].fMaxDelay;
+ if (thisMax < ns)
+ {
+ ns = thisMax;
+ holder = thisService;
+ }
+ }
+ }
+
+ setCpuDelay = true;
+ if (kNoReplace == replace)
+ sCpuDelayData->appendBytes(&ne, sizeof(ne));
+ else
+ entries[replace] = ne;
+ }
+ else {
+ ns = -1U; // Set to max unsigned, i.e. no restriction
+
+ for (UInt i = 0; i < count; i++) {
+ // Clear a maximum delay.
+ IOService *thisService = entries[i].fService;
+ if (thisService && (delayType == entries[i].fDelayType)) {
+ UInt32 thisMax = entries[i].fMaxDelay;
+ if (service == thisService)
+ replace = i;
+ else if (thisMax < ns) {
+ ns = thisMax;
+ holder = thisService;
+ }
+ }
+ }
+
+ // Check if entry found
+ if (kNoReplace != replace) {
+ entries[replace].fService = 0; // Null the entry
+ setCpuDelay = true;
+ }
+ }
+
+ if (setCpuDelay)
+ {
+ // Must be safe to call from locked context
+ if (delayType == kCpuDelayBusStall)
+ {
+ ml_set_maxbusdelay(ns);
+ }
+ else if (delayType == kCpuDelayInterrupt)
+ {
+ ml_set_maxintdelay(ns);
+ }
+
+ OSArray * handlers = sCpuLatencyHandlers[delayType];
+ IOService * target;
+ if (handlers) for (unsigned int idx = 0;
+ (target = (IOService *) handlers->getObject(idx));
+ idx++)
+ {
+ target->callPlatformFunction(sCPULatencyFunctionName[delayType], false,
+ (void *) (uintptr_t) ns, holder,
+ NULL, NULL);
+ }
+ }
+
+ IORecursiveLockUnlock(sCpuDelayLock);
+}
+
+static IOReturn
+setLatencyHandler(UInt32 delayType, IOService * target, bool enable)
+{
+ IOReturn result = kIOReturnNotFound;
+ OSArray * array;
+ unsigned int idx;
+
+ IORecursiveLockLock(sCpuDelayLock);
+
+ do
+ {
+ if (enable && !sCpuLatencyHandlers[delayType])
+ sCpuLatencyHandlers[delayType] = OSArray::withCapacity(4);
+ array = sCpuLatencyHandlers[delayType];
+ if (!array)
+ break;
+ idx = array->getNextIndexOfObject(target, 0);
+ if (!enable)
+ {
+ if (-1U != idx)
+ {
+ array->removeObject(idx);
+ result = kIOReturnSuccess;
+ }
+ }
+ else
+ {
+ if (-1U != idx) {
+ result = kIOReturnExclusiveAccess;
+ break;
+ }
+ array->setObject(target);
+
+ UInt count = sCpuDelayData->getLength() / sizeof(CpuDelayEntry);
+ CpuDelayEntry *entries = (CpuDelayEntry *) sCpuDelayData->getBytesNoCopy();
+ UInt32 ns = -1U; // Set to max unsigned, i.e. no restriction
+ IOService * holder = NULL;
+
+ for (UInt i = 0; i < count; i++) {
+ if (entries[i].fService
+ && (delayType == entries[i].fDelayType)
+ && (entries[i].fMaxDelay < ns)) {
+ ns = entries[i].fMaxDelay;
+ holder = entries[i].fService;
+ }
+ }
+ target->callPlatformFunction(sCPULatencyFunctionName[delayType], false,
+ (void *) (uintptr_t) ns, holder,
+ NULL, NULL);
+ result = kIOReturnSuccess;
+ }
+ }
+ while (false);
+
+ IORecursiveLockUnlock(sCpuDelayLock);
+
+ return (result);
+}
+
+#endif /* defined(__i386__) || defined(__x86_64__) */
+
+void IOService::
+requireMaxBusStall(UInt32 __unused ns)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ requireMaxCpuDelay(this, ns, kCpuDelayBusStall);
+#endif
+}
+
+void IOService::
+requireMaxInterruptDelay(uint32_t __unused ns)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ requireMaxCpuDelay(this, ns, kCpuDelayInterrupt);
+#endif
+}
+