/* * Copyright (c) 2002-2004 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) 2002-2004 Apple Computer, Inc. All rights reserved. * * */ #include "IOPlatformPluginDefs.h" #include "IOPlatformPluginSymbols.h" #include "PowerMac7_2_PlatformPlugin.h" #include "IOPlatformSensor.h" #include "PowerMac7_3_CPUCoolingCtrlLoop.h" #define super IOPlatformPIDCtrlLoop OSDefineMetaClassAndStructors(PowerMac7_3_CPUCoolingCtrlLoop, IOPlatformPIDCtrlLoop) //extern const OSSymbol * gPM72EnvShroudRemoved; //extern const OSSymbol * gPM72EnvAllowNapping; extern PowerMac7_2_PlatformPlugin * PM72Plugin; extern const OSSymbol * gPM72EnvSystemUncalibrated; bool PowerMac7_3_CPUCoolingCtrlLoop::init( void ) { if (!super::init()) return(false); secOutputControl = NULL; tempHistory[0].sensValue = tempHistory[1].sensValue = 0; tempIndex = 0; return(true); } void PowerMac7_3_CPUCoolingCtrlLoop::free( void ) { if (secOutputControl) { secOutputControl->release(); secOutputControl = NULL; } super::free(); } IOReturn PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop( const OSDictionary *dict) { IOReturn status; const OSArray * array; const OSNumber * num; //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop ENTERED\n"); // the cpu-id property tells me which cpu I'm related to num = OSDynamicCast(OSNumber, dict->getObject("cpu-id")); if (!num) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no cpu-id\n"); return(kIOReturnError); } procID = num->unsigned32BitValue(); // Choose a PID dataset if (!choosePIDDataset( dict )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop failed to choose PID dataset\n"); return(kIOReturnError); } status = super::initPlatformCtrlLoop(dict); // the second to sixth control are slave controls (index 1 thru 5) if ((array = OSDynamicCast(OSArray, dict->getObject(kIOPPluginThermalControlIDsKey))) == NULL || (secOutputControl = platformPlugin->lookupControlByID( OSDynamicCast(OSNumber, array->getObject(1)) )) == NULL) //cpu1 intake { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no sec control ID!!\n"); return(kIOReturnError); } secOutputControl->retain(); addControl( secOutputControl ); if ((array = OSDynamicCast(OSArray, dict->getObject(kIOPPluginThermalControlIDsKey))) == NULL || (thiOutputControl = platformPlugin->lookupControlByID( OSDynamicCast(OSNumber, array->getObject(2)) )) == NULL) //cpu2 exhaust { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no third control ID!!\n"); return(kIOReturnError); } thiOutputControl->retain(); addControl( thiOutputControl ); if ((array = OSDynamicCast(OSArray, dict->getObject(kIOPPluginThermalControlIDsKey))) == NULL || (fouOutputControl = platformPlugin->lookupControlByID( OSDynamicCast(OSNumber, array->getObject(3)) )) == NULL) //cpu2 intake { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no fourth control ID!!\n"); return(kIOReturnError); } fouOutputControl->retain(); addControl( fouOutputControl ); if ((array = OSDynamicCast(OSArray, dict->getObject(kIOPPluginThermalControlIDsKey))) == NULL || (fifOutputControl = platformPlugin->lookupControlByID( OSDynamicCast(OSNumber, array->getObject(4)) )) == NULL) //cpu1 pump { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no fifth control ID!!\n"); return(kIOReturnError); } fifOutputControl->retain(); addControl( fifOutputControl ); if ((array = OSDynamicCast(OSArray, dict->getObject(kIOPPluginThermalControlIDsKey))) == NULL || (sixOutputControl = platformPlugin->lookupControlByID( OSDynamicCast(OSNumber, array->getObject(5)) )) == NULL) //cpu2 pump { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no sixth control ID!!\n"); return(kIOReturnError); } sixOutputControl->retain(); addControl( sixOutputControl ); // the secondary output control (a.k.a. the intake fan) is programmed with a scaled version of the // value from the primary. This plist variable sets the scaling factor. It is in 16.16 fixed point // format. num = OSDynamicCast(OSNumber, dict->getObject("intake-scaling-factor")); if (!num) { intakeScaling = 0x00010000; // 1.0 in 16.16 fixed point CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no intake-scaling-factor, assuming 1.0\n"); } else { intakeScaling = num->unsigned32BitValue(); CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop(%d) intake-scaling-factor is 0x%08lX\n", procID, intakeScaling); } // voltage sensor for proc1 is at index 1 in the sensor-id array if ((array = OSDynamicCast(OSArray, dict->getObject(kIOPPluginThermalSensorIDsKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no SensorIDArray\n"); return(kIOReturnError); } if ((voltageSensor1 = OSDynamicCast(IOPlatformSensor, platformPlugin->lookupSensorByID( OSDynamicCast(OSNumber, array->getObject(1)) ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no voltage sensor ID for 1st processor!!\n"); return(kIOReturnError); } voltageSensor1->retain(); addSensor( voltageSensor1 ); // current sensor for proc1 is at index 2 in the sensor-id array if ((currentSensor1 = OSDynamicCast(IOPlatformSensor, platformPlugin->lookupSensorByID( OSDynamicCast(OSNumber, array->getObject(2)) ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no current sensor ID for 1st processor!!\n"); return(kIOReturnError); } currentSensor1->retain(); addSensor( currentSensor1 ); // power sensor for proc1 is at index 3 in the sensor-id array if ((powerSensor1 = OSDynamicCast(IOPlatformSensor, platformPlugin->lookupSensorByID( OSDynamicCast(OSNumber, array->getObject(3)) ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no power sensor ID for 1st processor!!\n"); return(kIOReturnError); } powerSensor1->retain(); addSensor( powerSensor1 ); // temp sensor for proc2 is at index 4 in the sensor-id array if ((inputSensor2 = OSDynamicCast(IOPlatformSensor, platformPlugin->lookupSensorByID( OSDynamicCast(OSNumber, array->getObject(4)) ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no temp sensor ID for 2nd processor!!\n"); return(kIOReturnError); } inputSensor2->retain(); addSensor( inputSensor2 ); // voltage sensor for proc2 is at index 5 in the sensor-id array if ((voltageSensor2 = OSDynamicCast(IOPlatformSensor, platformPlugin->lookupSensorByID( OSDynamicCast(OSNumber, array->getObject(5)) ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no voltage sensor ID for 2nd processor!!\n"); return(kIOReturnError); } voltageSensor2->retain(); addSensor( voltageSensor2 ); // current sensor for proc2 is at index 6 in the sensor-id array if ((currentSensor2 = OSDynamicCast(IOPlatformSensor, platformPlugin->lookupSensorByID( OSDynamicCast(OSNumber, array->getObject(6)) ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no current sensor ID for 2nd processor!!\n"); return(kIOReturnError); } currentSensor2->retain(); addSensor( currentSensor2 ); // power sensor for proc2 is at index 7 in the sensor-id array if ((powerSensor2 = OSDynamicCast(IOPlatformSensor, platformPlugin->lookupSensorByID( OSDynamicCast(OSNumber, array->getObject(7)) ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no power sensor ID for 2nd processor!!\n"); return(kIOReturnError); } powerSensor2->retain(); addSensor( powerSensor2 ); // [3751599, 3751602] Setup CPU pump min/max values from processor ROM... #define MAX(a,b) (((a)>(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b)) #define kDefaultOutputMinForPump 1250 #define kDefaultOutputMaxForPump 3200 UInt16 tmp_buf[4]; // Fetch pump1 and pump2 min/max values. if (false == PM72Plugin->readProcROM( procID, 0x54, 8, (UInt8*)tmp_buf )) { // Can't read ROM?... hard-code pump min/max. CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop failed to fetch pump min/max from ROM\n"); outputMinForPump = kDefaultOutputMinForPump; outputMaxForPump = kDefaultOutputMaxForPump; } else if ((tmp_buf[0] != 0xffff) && (tmp_buf[1] != 0xffff) && (tmp_buf[2] != 0xffff) && (tmp_buf[3] != 0xffff)) { // Both pumps are present... use the larger min and the smaller max between pump 1 and 2. outputMinForPump = MAX(tmp_buf[0], tmp_buf[2]); outputMaxForPump = MIN(tmp_buf[1], tmp_buf[3]); } else // Find a pump with a good min/max pair... if ((tmp_buf[0] != 0xffff) && (tmp_buf[1] != 0xffff)) { // Using min/max from pump1 outputMinForPump = tmp_buf[0]; outputMaxForPump = tmp_buf[1]; } else if ((tmp_buf[2] != 0xffff) && (tmp_buf[3] != 0xffff)) { // Using min/max from pump2 outputMinForPump = tmp_buf[2]; outputMaxForPump = tmp_buf[3]; } else { // Otherwise hard-code them thar puppies... outputMinForPump = kDefaultOutputMinForPump; outputMaxForPump = kDefaultOutputMaxForPump; } // Make sure min/max are NOT the same... if (outputMinForPump == outputMaxForPump) { outputMinForPump = kDefaultOutputMinForPump; outputMaxForPump = kDefaultOutputMaxForPump; } // IOLog("*******************************************************************************\n"); // IOLog("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop using pump min=%d max=%d\n", outputMinForPump, outputMaxForPump); // IOLog("procID:%d min0:%d min1:%d max0:%d max1:%d\n", procID, tmp_buf[0],tmp_buf[2],tmp_buf[1],tmp_buf[3]); #ifdef CTRLLOOP_DEBUG const OSNumber * slewID = OSNumber::withNumber( 0x10, 32 ); if ((slewControl = platformPlugin->lookupControlByID( slewID )) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop no slew control!!\n"); return(kIOReturnError); } slewID->release(); #endif tMaxAverageSampleCount = kPM73CPU_DEFAULT_slewAverageSampleCount; //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::initPlatformCtrlLoop ENTERED\n"); return(status); } bool PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset( const OSDictionary * ctrlLoopDict ) { int i, count, cmp; const OSArray * tmp_array; OSArray * metaStateArray; OSDictionary * eepromDataset, * plistDataset = NULL, * chosenDataset; const OSData * binID, * plistBinID; if (!ctrlLoopDict) return(false); // get a pointer to the meta state array if ((metaStateArray = OSDynamicCast(OSArray, ctrlLoopDict->getObject(gIOPPluginThermalMetaStatesKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset failed to find meta state array\n"); return(false); } //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset proc %u ctrlLoopDict %08lX metaStateArray %08lX\n", procID, // (UInt32) ctrlLoopDict, (UInt32) metaStateArray); // extract meta state data from IIC ROM if ((eepromDataset = fetchPIDDatasetFromROM()) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset failed to fetch EEPROM dataset\n"); return(false); } if ((binID = OSDynamicCast(OSData, eepromDataset->getObject( kPM72ProcessorBinKey ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset failed to fetch processor bin id\n"); return(false); } // Search the plist for a dataset that matches this CPU's bin ID if ((tmp_array = OSDynamicCast(OSArray, ctrlLoopDict->getObject( kPM72CPUPIDDatasetsKey ))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset failed to fetch plist datasets\n"); chosenDataset = eepromDataset; goto installChosenDataset; } count = tmp_array->getCount(); //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset proc %u count %d\n", procID, count); for (i=0; igetObject(i))) == NULL) continue; if ((plistBinID = OSDynamicCast(OSData, plistDataset->getObject( kPM72ProcessorBinKey ))) == NULL) continue; if (binID->isEqualTo(plistBinID)) break; plistDataset = NULL; } if (plistDataset == NULL) { // since we don't have a valid parameter list in our plist, use the data fetched from the EEPROM CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset plist datasets don't match processor\n"); chosenDataset = eepromDataset; goto installChosenDataset; } plistDataset->retain(); // Compare versions of the eeprom and plist datasets. Choose the one that's later, or use the // eeprom dataset if the versions are equal. cmp = comparePIDDatasetVersions( OSDynamicCast(OSData, eepromDataset->getObject(kPM72PIDDatasetVersionKey)), OSDynamicCast(OSData, plistDataset->getObject(kPM72PIDDatasetVersionKey)) ); if (cmp == -2) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset dataset version comparison failed\n"); return(false); } else if (cmp == -1) { chosenDataset = plistDataset; goto installChosenDataset; } else // cmp == 0 || cmp == 1 { chosenDataset = eepromDataset; goto installChosenDataset; } installChosenDataset: CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::choosePIDDataset using %s\n", chosenDataset == eepromDataset ? "EEPROM" : "PLIST"); metaStateArray->replaceObject(0, chosenDataset); // // if (plistDataset) plistDataset->release(); eepromDataset->release(); return(true); } // Compares two PID dataset versions. These are four bytes. The format is as follows: // 0xuuuabcld // u = unused // a = major // b = minor // c = very minor // l = level (d, a, b, f) // d = rev // // so 0x000100d9 is version 1.0.0d9. // // comparison procedure is to compare a, then b, then c, then l, then d // // this routine returns: // -1 if v1 is less than v2 // 0 if v1 is equal to v2 // 1 if v1 is greater than v2 // -2 for error #define MAJOR(x) (((x) & 0x000F0000) >> 16) #define MINOR(x) (((x) & 0x0000F000) >> 12) #define VERY_MINOR(x) (((x) & 0x00000F00) >> 8) #define LEVEL(x) (((x) & 0x000000F0) >> 4) #define REVISION(x) (((x) & 0x0000000F) >> 0) int PowerMac7_3_CPUCoolingCtrlLoop::comparePIDDatasetVersions( const OSData * v1, const OSData * v2 ) const { UInt32 lv1, lv2, t1, t2; UInt8 levelLookup[] = { /* 0 */ 0xFF, /* 1 */ 0xFF, /* 2 */ 0xFF, /* 3 */ 0xFF, /* 4 */ 0xFF, /* 5 */ 0xFF, /* 6 */ 0xFF, /* 7 */ 0xFF, /* 8 */ 0xFF, /* 9 */ 0xFF, /* A */ 1, /* B */ 2, /* C */ 0xFF, /* D */ 0, /* E */ 4, /* F */ 3 }; if (v1 == NULL || v2 == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::comparePIDDatasetVersions bad args\n"); return(-2); } lv1 = (*((UInt32 *) v1->getBytesNoCopy()) & 0x000FFFFF); lv2 = (*((UInt32 *) v2->getBytesNoCopy()) & 0x000FFFFF); //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::comparePIDDatasetVersions %08lX %08lX\n", lv1, lv2); // check for equal if (lv1 == lv2) return(0); // compare major t1 = MAJOR(lv1); t2 = MAJOR(lv2); if (t1 < t2) return(-1); else if (t1 > t2) return(1); // compare minor t1 = MINOR(lv1); t2 = MINOR(lv2); if (t1 < t2) return(-1); else if (t1 > t2) return(1); // compare very minor t1 = VERY_MINOR(lv1); t2 = VERY_MINOR(lv2); if (t1 < t2) return(-1); else if (t1 > t2) return(1); // compare level : d=0, a=1, b=2, f=3 t1 = (UInt32) levelLookup[LEVEL(lv1)]; t2 = (UInt32) levelLookup[LEVEL(lv2)]; if (t1 == 0xFF || t2 == 0xFF) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::comparePIDDatasetVersions invalid version\n"); return(-2); } if (t1 < t2) return(-1); else if (t1 > t2) return(1); t1 = REVISION(lv1); t2 = REVISION(lv2); if (t1 < t2) return(-1); else if (t1 > t2) return(1); // if we're here, something is wrong. The versions are not equal, but they're not greater // than or less than either?!?! CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::comparePIDDatasetVersions THIS SHOULDN'T HAPPEN\n"); return(-2); } OSDictionary *PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM( void ) const { OSDictionary * dataset; const OSSymbol * tmp_symbol; const OSData * tmp_osdata; const OSNumber * tmp_number; UInt8 tmp_bytebuf[4]; if ((dataset = OSDictionary::withCapacity(14)) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to allocate dictionary\n"); return(NULL); } // Flag the source of this dataset tmp_symbol = OSSymbol::withCString( kPM72PIDDatasetSourceIICROM ); dataset->setObject( kPM72PIDDatasetSourceKey, tmp_symbol ); tmp_symbol->release(); // Get processor bin identifier if (!PM72Plugin->readProcROM( procID, 0x08, 3, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch processor bin ID\n"); goto failReleaseDataset; } tmp_osdata = OSData::withBytes( tmp_bytebuf, 3 ); dataset->setObject( kPM72ProcessorBinKey, tmp_osdata ); tmp_osdata->release(); // Get dataset version if (!PM72Plugin->readProcROM( procID, 0x04, 4, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch processor ROM version\n"); goto failReleaseDataset; } tmp_osdata = OSData::withBytes( tmp_bytebuf, 4 ); dataset->setObject( kPM72PIDDatasetVersionKey, tmp_osdata ); tmp_osdata->release(); // Proportional gain (G_p) if (!PM72Plugin->readProcROM( procID, 0x2C, 4, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch G_p\n"); goto failReleaseDataset; } tmp_osdata = OSData::withBytes( tmp_bytebuf, 4 ); dataset->setObject( kIOPPIDCtrlLoopProportionalGainKey, tmp_osdata ); tmp_osdata->release(); // Reset gain (G_r) -- this is actually used as the power integral gain if (!PM72Plugin->readProcROM( procID, 0x30, 4, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch G_r\n"); goto failReleaseDataset; } tmp_osdata = OSData::withBytes( tmp_bytebuf, 4 ); dataset->setObject( kIOPPIDCtrlLoopResetGainKey, tmp_osdata ); tmp_osdata->release(); // Derivative gain (G_d) if (!PM72Plugin->readProcROM( procID, 0x34, 4, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch G_d\n"); goto failReleaseDataset; } tmp_osdata = OSData::withBytes( tmp_bytebuf, 4 ); dataset->setObject( kIOPPIDCtrlLoopDerivativeGainKey, tmp_osdata ); tmp_osdata->release(); // Min output (RPM/PWM) if (!PM72Plugin->readProcROM( procID, 0x50, 2, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch min output\n"); goto failReleaseDataset; } tmp_number = OSNumber::withNumber( *((UInt16 *) tmp_bytebuf), 16 ); dataset->setObject( kIOPPIDCtrlLoopOutputMinKey, tmp_number ); tmp_number->release(); // Max output (RPM/PWM) if (!PM72Plugin->readProcROM( procID, 0x52, 2, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch max output\n"); goto failReleaseDataset; } tmp_number = OSNumber::withNumber( *((UInt16 *) tmp_bytebuf), 16 ); dataset->setObject( kIOPPIDCtrlLoopOutputMaxKey, tmp_number ); tmp_number->release(); // history length if (!PM72Plugin->readProcROM( procID, 0x2B, 1, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch history length\n"); goto failReleaseDataset; } tmp_number = OSNumber::withNumber( tmp_bytebuf[0], 8 ); dataset->setObject( kIOPPIDCtrlLoopHistoryLenKey, tmp_number ); tmp_number->release(); // Target temperature if (!PM72Plugin->readProcROM( procID, 0x28, 1, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch target temp\n"); goto failReleaseDataset; } tmp_number = OSNumber::withNumber( ((UInt32)tmp_bytebuf[0]) << 16, 32 ); dataset->setObject( kIOPPIDCtrlLoopInputTargetKey, tmp_number ); tmp_number->release(); // Max temperature if (!PM72Plugin->readProcROM( procID, 0x29, 1, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch max temp\n"); goto failReleaseDataset; } tmp_number = OSNumber::withNumber( ((UInt32)tmp_bytebuf[0]) << 16, 32 ); dataset->setObject( kIOPPIDCtrlLoopInputMaxKey, tmp_number ); tmp_number->release(); // Max power if (!PM72Plugin->readProcROM( procID, 0x2A, 1, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch max power\n"); goto failReleaseDataset; } tmp_number = OSNumber::withNumber( ((UInt32)tmp_bytebuf[0]) << 16, 32 ); dataset->setObject( kPM72MaxPowerKey, tmp_number ); tmp_number->release(); // Power adjustment if (!PM72Plugin->readProcROM( procID, 0x27, 1, tmp_bytebuf )) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::fetchPIDDatasetFromROM failed to fetch max power adjustment\n"); goto failReleaseDataset; } tmp_number = OSNumber::withNumber( (*(SInt8 *)&tmp_bytebuf[0]) << 16, 32 ); dataset->setObject( kPM72MaxPowerAdjustmentKey, tmp_number ); tmp_number->release(); // The iteration interval is not stored in the ROM, it is always set to 1 second dataset->setObject( kIOPPIDCtrlLoopIntervalKey, gIOPPluginOne ); return(dataset); failReleaseDataset: dataset->release(); return(NULL); } SensorValue PowerMac7_3_CPUCoolingCtrlLoop::getAggregateSensorValue( void ) { SensorValue curValue, curValue1, curValue2; curValue1 = inputSensor->forceAndFetchCurrentValue(); inputSensor->setCurrentValue( curValue1 ); curValue2 = inputSensor2->forceAndFetchCurrentValue(); inputSensor2->setCurrentValue( curValue2 ); curValue = (curValue1.sensValue >= curValue2.sensValue) ? curValue1 : curValue2; return( curValue ); } bool PowerMac7_3_CPUCoolingCtrlLoop::updateMetaState( void ) { const OSArray * metaStateArray; const OSDictionary * metaStateDict; const OSNumber * newMetaState; // else if there is an overtemp condition, use meta-state 1 // else if there is a forced meta state, use it // else, use meta-state 0 if ((metaStateArray = OSDynamicCast(OSArray, infoDict->getObject(gIOPPluginThermalMetaStatesKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::updateMetaState no meta state array\n"); return(false); } // Check for overtemp condition if ((platformPlugin->envArrayCondIsTrue(gIOPPluginEnvInternalOvertemp)) || (platformPlugin->envArrayCondIsTrue(gIOPPluginEnvExternalOvertemp)) || (platformPlugin->getEnv(gPM72EnvSystemUncalibrated) != NULL)) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::updateMetaState Entering Overtemp Mode\n"); if ((metaStateDict = OSDynamicCast(OSDictionary, metaStateArray->getObject(1))) != NULL && (cacheMetaState( metaStateDict ) == true)) { // successfully entered overtemp mode setMetaState( gIOPPluginOne ); return(true); } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::updateMetaState Overtemp Mode Failed!\n"); } } // Look for forced meta state if ((metaStateDict = OSDynamicCast(OSDictionary, infoDict->getObject(gIOPPluginForceCtrlLoopMetaStateKey))) != NULL) { if (cacheMetaState( metaStateDict ) == true) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::updateMetaState using forced meta state\n"); newMetaState = OSNumber::withNumber( 0xFFFFFFFF, 32 ); setMetaState( newMetaState ); newMetaState->release(); return(true); } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::updateMetaState forced meta state is invalid, removing...\n"); infoDict->removeObject(gIOPPluginForceCtrlLoopMetaStateKey); } } // Use default "Normal" meta state if ((metaStateDict = OSDynamicCast(OSDictionary, metaStateArray->getObject(0))) != NULL && (cacheMetaState( metaStateDict ) == true)) { //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::updateMetaState use meta state zero\n"); setMetaState( gIOPPluginZero ); return(true); } else { // can't find a valid meta state, nothing we can really do except log an error CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::updateMetaState no valid meta states!\n"); return(false); } } bool PowerMac7_3_CPUCoolingCtrlLoop::acquireSample( void ) { samplePoint * latest; SensorValue curValue, curValue1, curValue2; // Set up the temperature history if (tempIndex == 1) tempIndex = 0; else tempIndex = 1; // fetch the temperature reading curValue = getAggregateSensorValue(); tempHistory[tempIndex].sensValue = curValue.sensValue; int i; if (tMaxAverageHistory) // Get another sample and calculate new average... { tMaxAverageHistory[tMaxAverageIndex] = curValue.sensValue; tMaxAverage = 0; if (tMaxAverageSampleCount > 0) { for (i = 0; i < tMaxAverageSampleCount; i++) tMaxAverage += tMaxAverageHistory[i]; tMaxAverage /= tMaxAverageSampleCount; } #define fract16(x) ((((UInt32)(x)>>13)&7)*128) /* #ifdef kLOG_ENABLED IOLog("PM73: N:%ld Avg:%ld.%03lu Temp[%ld]=%ld.%03lu\n", tMaxAverageSampleCount, tMaxAverage>>16, fract16(tMaxAverage), tMaxAverageIndex, tMaxAverageSampleCount?tMaxAverageHistory[tMaxAverageIndex]>>16:0, tMaxAverageSampleCount?fract16(tMaxAverageHistory[tMaxAverageIndex]):0); #endif // kLOG_ENABLED */ if (++tMaxAverageIndex >= tMaxAverageSampleCount) tMaxAverageIndex = 0; } else if (tMaxAverageSampleCount >= 2) // Re-Initialize the sample history. { tMaxAverageHistory = (SInt32 *)IOMalloc(tMaxAverageSampleCount * sizeof(SInt32)); // Initialize all samples to the current value. for (i = 0; i < tMaxAverageSampleCount; i++) tMaxAverageHistory[i] = curValue.sensValue; tMaxAverage = curValue.sensValue; tMaxAverageIndex = 1; #ifdef kLOG_ENABLED IOLog("Changing number of samples:%ld tMaxAverage:%ld.%03lu\n", tMaxAverageSampleCount, tMaxAverage>>16, fract16(tMaxAverage)); #endif // kLOG_ENABLED } else // No sample history? and Number of samples is less than two? { // Disable averaging... average = current value. tMaxAverage = curValue.sensValue; } // move the top of the power array to the next spot -- it's circular if (latestSample == 0) latestSample = historyLen - 1; else latestSample -= 1; // get a pointer to the array element where we'll store this sample point latest = &historyArray[latestSample]; // fetch the power reading // the power sensor is a "fake" logical sensor. In order to get a good reading, // we have to update the current and power sensors first. // for processor 1 voltageSensor1->setCurrentValue( voltageSensor1->forceAndFetchCurrentValue() ); currentSensor1->setCurrentValue( currentSensor1->forceAndFetchCurrentValue() ); // the cpu power sensor doesn't need a force update, its fetchCurrentValue() does all the // work (there's no IOHWSensor instance to send a message to since it's a fake sensor) curValue1 = powerSensor1->fetchCurrentValue(); powerSensor1->setCurrentValue( curValue1 ); // for processor 2 voltageSensor2->setCurrentValue( voltageSensor2->forceAndFetchCurrentValue() ); currentSensor2->setCurrentValue( currentSensor2->forceAndFetchCurrentValue() ); // the cpu power sensor doesn't need a force update, its fetchCurrentValue() does all the // work (there's no IOHWSensor instance to send a message to since it's a fake sensor) curValue2 = powerSensor2->fetchCurrentValue(); powerSensor2->setCurrentValue( curValue2 ); curValue = ( curValue1.sensValue >= curValue2.sensValue ) ? curValue1 : curValue2; // store the sample in the history latest->sample.sensValue = curValue.sensValue; // calculate the error term and store it latest->error.sensValue = powerMaxAdj.sensValue - latest->sample.sensValue; //CTRLLOOP_DLOG("*** SAMPLE *** InT: 0x%08lX Cur: 0x%08lX Error: 0x%08lX\n", // inputTarget.sensValue, latest->sample.sensValue, latest->error.sensValue); return(true); } bool PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState( const OSDictionary * metaState ) { const OSData * dataG_p, * dataG_d, * dataG_r; const OSNumber * numInterval, * numOverride, * numInputTarget, * numInputMax; const OSNumber * numOutputMin, * numOutputMax, * numHistLen, * numPowerMax, * numPowerAdj; //const OSNumber * tmpNumber; samplePoint * sample, * newHistoryArray; unsigned int i; UInt32 tempInterval, newHistoryLen; // cache the interval. it is listed in seconds. if ((numInterval = OSDynamicCast(OSNumber, metaState->getObject("interval"))) != NULL) { tempInterval = numInterval->unsigned32BitValue(); if ((tempInterval == 0) || (tempInterval > 300)) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state interval is out of bounds\n"); goto failNoInterval; } } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state interval is absent\n"); goto failNoInterval; } // if there is an output-override key, flag it. Otherwise, look for the full // set of coefficients, setpoints and output bounds if ((numOverride = OSDynamicCast(OSNumber, metaState->getObject(kIOPPIDCtrlLoopOutputOverrideKey))) != NULL) { overrideActive = true; outputOverride = numOverride; outputOverride->retain(); //CTRLLOOP_DLOG("*** PID CACHE *** Override: 0x%08lX\n", outputOverride->unsigned32BitValue()); } else { // look for G_p, G_d, G_r, input-target, output-max, output-min if ((dataG_p = OSDynamicCast(OSData, metaState->getObject(kIOPPIDCtrlLoopProportionalGainKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no G_p\n"); goto failFullSet; } if ((dataG_d = OSDynamicCast(OSData, metaState->getObject(kIOPPIDCtrlLoopDerivativeGainKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no G_d\n"); goto failFullSet; } if ((dataG_r = OSDynamicCast(OSData, metaState->getObject(kIOPPIDCtrlLoopResetGainKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no G_r\n"); goto failFullSet; } if ((numInputTarget = OSDynamicCast(OSNumber, metaState->getObject(kIOPPIDCtrlLoopInputTargetKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no intput-target\n"); goto failFullSet; } if ((numInputMax = OSDynamicCast(OSNumber, metaState->getObject(kIOPPIDCtrlLoopInputMaxKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no input-max\n"); goto failFullSet; } if ((numPowerMax = OSDynamicCast(OSNumber, metaState->getObject(kPM72MaxPowerKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no power-max\n"); goto failFullSet; } if ((numPowerAdj = OSDynamicCast(OSNumber, metaState->getObject(kPM72MaxPowerAdjustmentKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no power-max-adjustment\n"); goto failFullSet; } if ((numOutputMin = OSDynamicCast(OSNumber, metaState->getObject(kIOPPIDCtrlLoopOutputMinKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no output-min\n"); goto failFullSet; } if ((numOutputMax = OSDynamicCast(OSNumber, metaState->getObject(kIOPPIDCtrlLoopOutputMaxKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no output-max\n"); goto failFullSet; } if ((numHistLen = OSDynamicCast(OSNumber, metaState->getObject(kIOPPIDCtrlLoopHistoryLenKey))) == NULL) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState meta state has no history-length\n"); goto failFullSet; } overrideActive = false; if (outputOverride) { outputOverride->release(); outputOverride = NULL; } G_p = *((SInt32 *) dataG_p->getBytesNoCopy()); G_d = *((SInt32 *) dataG_d->getBytesNoCopy()); G_r = *((SInt32 *) dataG_r->getBytesNoCopy()); inputTarget.sensValue = (SInt32)numInputTarget->unsigned32BitValue(); inputMax.sensValue = (SInt32)numInputMax->unsigned32BitValue(); powerMaxAdj.sensValue = ((SInt32) numPowerMax->unsigned32BitValue()) - ((SInt32) numPowerAdj->unsigned32BitValue()); CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::cacheMetaState powerMaxAdj: %ld\n", powerMaxAdj.sensValue >> 16); outputMin = numOutputMin->unsigned32BitValue(); outputMax = numOutputMax->unsigned32BitValue(); // resize the history array if necessary newHistoryLen = numHistLen->unsigned32BitValue(); if (newHistoryLen == 0) newHistoryLen = 2; // must be two or more in order to have a valid // derivative term if (newHistoryLen != historyLen) { newHistoryArray = (samplePoint *) IOMalloc( sizeof(samplePoint) * newHistoryLen ); bzero( newHistoryArray, sizeof(samplePoint) * newHistoryLen ); // copy samples from the old array into the new for (i=0; isample.sensValue = sample->sample.sensValue; (&(newHistoryArray[i]))->error.sensValue = sample->error.sensValue; } IOFree( historyArray, sizeof(samplePoint) * historyLen ); historyArray = newHistoryArray; historyLen = newHistoryLen; latestSample = 0; } // CTRLLOOP_DLOG("*** PID CACHE *** G_p: 0x%08lX G_d: 0x%08lX G_r: 0x%08lX\n" // "***************** inT: 0x%08lX oMi: 0x%08lX oMa: 0x%08lX\n", // G_p, G_d, G_r, inputTarget, outputMin, outputMax); } // set the interval intervalSec = tempInterval; clock_interval_to_absolutetime_interval(intervalSec, NSEC_PER_SEC, &interval); // CTRLLOOP_DLOG("***************** Interval: %u\n", intervalSec); return(true); failFullSet: failNoInterval: return(false); } ControlValue PowerMac7_3_CPUCoolingCtrlLoop::calculateNewTarget( void ) const { SInt32 pRaw, dRaw, rRaw; SInt64 accum, dProd, rProd, pProd; //UInt32 result, prevResult, scratch; SInt32 result; ControlValue newTarget; SensorValue adjInputTarget, sVal; // if there is an output override, use it if (overrideActive) { CTRLLOOP_DLOG("*** PID *** Override Active\n"); newTarget = outputOverride->unsigned32BitValue(); } // apply the PID algorithm to choose a new control target value else { if (ctrlloopState == kIOPCtrlLoopFirstAdjustment) { result = 0; } else { // Calculate the adjusted target rRaw = calculateIntegralTerm().sensValue; // this is 12.20 * 16.16 => 28.36 rProd = (SInt64)G_r * (SInt64)rRaw; sVal.sensValue = (SInt32)((rProd >> 20) & 0xFFFFFFFF); sVal.sensValue = inputMax.sensValue - sVal.sensValue; adjInputTarget.sensValue = inputTarget.sensValue < sVal.sensValue ? inputTarget.sensValue : sVal.sensValue; // do the PID iteration result = (SInt32)outputControl->getTargetValue(); // calculate the derivative term // apply the derivative gain // this is 12.20 * 16.16 => 28.36 dRaw = calculateDerivativeTerm().sensValue; accum = dProd = (SInt64)G_d * (SInt64)dRaw; // calculate the reset term // apply the reset gain // this is 12.20 * 16.16 => 28.36 //rRaw = calculateIntegralTerm().sensValue; //rProd = (SInt64)G_r * (SInt64)rRaw; //accum += rProd; // calculate the proportional term // apply the proportional gain // this is 12.20 * 16.16 => 28.36 pRaw = tempHistory[tempIndex].sensValue - adjInputTarget.sensValue; pProd = (SInt64)G_p * (SInt64)pRaw; accum += pProd; // truncate the fractional part accum >>= 36; //result = (UInt32)(accum < 0 ? 0 : (accum & 0xFFFFFFFF)); result += (SInt32)accum; } newTarget = (UInt32)(result > 0) ? result : 0; // apply the hard limits if (newTarget < outputMin) newTarget = outputMin; else if (newTarget > outputMax) newTarget = outputMax; #if 0 #ifdef CTRLLOOP_DEBUG if (timerCallbackActive) { const OSString * tempDesc; if (slewControl) slewControl->setCurrentValue( slewControl->fetchCurrentValue() ); #endif CTRLLOOP_DLOG("%s" " powerSum=%08lX (%ld)" " G_power=%08lX" " powerProd=%016llX (%lld)" " Ttgt=%08lX (%ld)" " Tmax=%08lX (%ld)" " TtgtAdj=%08lX (%ld)" " T_cur=%08lX (%ld)" " T_err=%08lX (%ld)" " G_p=%08lX" " pProd=%016llX (%lld)" " dRaw=%08lX (%ld)" " G_d=%08lX" " dProd=%016llX (%lld)" " Res=%016llX" " Out=%ld" " Spd=%0lu\n", (tempDesc = OSDynamicCast( OSString, infoDict->getObject(kIOPPluginThermalGenericDescKey))) != NULL ? tempDesc->getCStringNoCopy() : "Unknown CtrlLoop", rRaw, rRaw >> 16, G_r, rProd, rProd >> 36, inputTarget.sensValue, inputTarget.sensValue >> 16, inputMax.sensValue, inputMax.sensValue >> 16, adjInputTarget.sensValue, adjInputTarget.sensValue >> 16, tempHistory[tempIndex].sensValue, tempHistory[tempIndex].sensValue >> 16, pRaw, pRaw >> 16, G_p, pProd, pProd >> 36, dRaw, dRaw >> 16, G_d, dProd, dProd >> 36, accum, result, slewControl ? slewControl->getCurrentValue() : 0xFFFFFFFF ); #ifdef CTRLLOOP_DEBUG } #endif #endif } return(newTarget); } SensorValue PowerMac7_3_CPUCoolingCtrlLoop::calculateDerivativeTerm( void ) const { int latest, previous; SensorValue result; latest = tempIndex; previous = tempIndex == 0 ? 1 : 0; // get the change in the error term over the latest inteval result.sensValue = tempHistory[latest].sensValue - tempHistory[previous].sensValue; // divide by the elapsed time to get the slope result.sensValue /= (SInt32)intervalSec; return(result); } void PowerMac7_3_CPUCoolingCtrlLoop::deadlinePassed( void ) { bool deadlineAbsolute; bool didSetEnv = false; SInt32 slewOffset = kPM73CPU_DEFAULT_slewOffset; SInt32 sleepOffset = kPM73CPU_DEFAULT_sleepOffset; SInt32 slewAverageOffset = kPM73CPU_DEFAULT_slewAverageOffset; SInt32 latestTemperature; bool isSlewing; #ifdef kLOG_ENABLED char log[4096], *plog = log; #endif // kLOG_ENABLED deadlineAbsolute = (ctrlloopState == kIOPCtrlLoopFirstAdjustment); timerCallbackActive = true; // sample the input if (!acquireSample()) { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::deadlinePassed FAILED TO ACQUIRE INPUT SAMPLE!!!\n"); } // Check condition for slew... latestTemperature = tempHistory[tempIndex].sensValue; isSlewing = platformPlugin->envArrayCondIsTrueForObject(this, gIOPPluginEnvInternalOvertemp); // convert offset to 16.16 fixed point bool overtempAverage = (tMaxAverage >= (inputMax.sensValue + (slewAverageOffset<<16)))?true:false; bool overtempImmediate = (latestTemperature >= (inputMax.sensValue + (slewOffset<<16)))?true:false; #ifdef kLOG_ENABLED sprintf(plog, "PM73: tgt:%ld %c %c Tavg:%ld.%03lu %c %ld, Tcur:%ld.%03lu %c %ld", outputControl->getTargetValue(), isSlewing?'S':' ', fOvertempAverage?'O':' ', tMaxAverage>>16, fract16(tMaxAverage), overtempAverage?'*':' ', (inputMax.sensValue>>16) + slewAverageOffset, latestTemperature>>16, fract16(latestTemperature), overtempImmediate?'*':' ', (inputMax.sensValue>>16) + slewOffset); plog += strlen(plog); #endif // kLOG_ENABLED if ((fOvertempAverage == false) && (overtempAverage || overtempImmediate)) { if (isSlewing == false) { if (overtempAverage) fOvertempAverage = true; #ifdef kLOG_ENABLED sprintf(plog, " ->low"); plog += strlen(plog); #endif // kLOG_ENABLED platformPlugin->setEnvArray(gIOPPluginEnvInternalOvertemp, this, true); didSetEnv = true; } } else { if (isSlewing == true) { // If previously slewing low because the overtempAverage limit was excceded... // then slew high only after the immediate temperature falls below the average limit. // otherwise slew high when (average >= aveOffset) and (immed >= immOffset). if ((fOvertempAverage == false) || (latestTemperature < (inputMax.sensValue + (slewAverageOffset<<16)))) { #ifdef kLOG_ENABLED sprintf(plog, " ->high"); plog += strlen(plog); #endif // kLOG_ENABLED fOvertempAverage = false; platformPlugin->setEnvArray(gIOPPluginEnvInternalOvertemp, this, false); didSetEnv = true; delayToReleaseFanAfterSlew = 1; // intervals to force fans on full after slewing } } } #ifdef kLOG_ENABLED sprintf(plog, "\n"); IOLog(log); #endif // kLOG_ENABLED // Check condition for sleep... if (latestTemperature >= (inputMax.sensValue + (sleepOffset<<16))) // convert offset to 16.16 fixed point { IOLog("PowerMac7,3 Thermal Manager: Thermal Runaway Detected: System Will Sleep\n"); IOLog("PM73 T_cur=%ld >= (T_max:%ld + sleepOffset:%ld)\n", latestTemperature >> 16, inputMax.sensValue >> 16, sleepOffset); platformPlugin->coreDump(); platformPlugin->sleepSystem(); } // If we changed the environment, the platform plugin will invoke updateMetaState() // and adjustControls(). If not, then we just need to call adjustControls() if (!didSetEnv) { adjustControls(); } // set the deadline if (deadlineAbsolute) { // this is the first time we're setting the deadline. In order to better stagger // timer callbacks, offset the deadline by 100us * ctrlloopID. AbsoluteTime adjustedInterval; const OSNumber * id = getCtrlLoopID(); // 100 * ctrlLoopID -> absolute time format clock_interval_to_absolutetime_interval(100 * id->unsigned32BitValue(), NSEC_PER_USEC, &adjustedInterval); // Add standard interval to produce adjusted interval ADD_ABSOLUTETIME( &adjustedInterval, &interval ); clock_absolutetime_interval_to_deadline(adjustedInterval, &deadline); } else { ADD_ABSOLUTETIME(&deadline, &interval); } // timerCallbackActive = false; // force adjustControls() to call calculateNewTarget + sendNewTarget always! } void PowerMac7_3_CPUCoolingCtrlLoop::sendNewTarget( ControlValue newTarget ) { // newTarget is the exhaust fan speeds // intakeTarget is the intake fan speeds // pumpTarget is the pump speeds ControlValue intakeTarget, pumpTarget; /* // If the fans are at minimum speed, don't let the processors nap. We need to // keep the heat sink warm or it loses efficiency. bool allowNapping = (newTarget->unsigned32BitValue() != outputMin); // Sync the internal napping state with the platform plugin's environment dict if (ctrlloopState == kIOPCtrlLoopFirstAdjustment || allowNapping != platformPlugin->envArrayCondIsTrueForObject(this, gPM72EnvAllowNapping)) { platformPlugin->setEnvArray(gPM72EnvAllowNapping, this, allowNapping); } */ // If the new target value is different, send it to the control if (ctrlloopState == kIOPCtrlLoopFirstAdjustment || ctrlloopState == kIOPCtrlLoopDidWake || newTarget != outputControl->getTargetValue() ) { if (outputControl->sendTargetValue( newTarget )) //outputControl is still cpu1 exhaust fan { outputControl->setTargetValue(newTarget); ctrlloopState = kIOPCtrlLoopAllRegistered; } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sendNewTarget failed to send target value\n"); } if (thiOutputControl->sendTargetValue( newTarget )) { thiOutputControl->setTargetValue( newTarget ); } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sendNewTarget failed to send target value (third)\n"); } // the second and fifth controls (intake fans) are programmed with a scaled version // of the primary control (exhaust fans) value. Now I need to apply the 16.16 // scaling factor to the exhaust fan's target in order to calculate the // intake fan's target. This requires some shifting since the scaling // factor is a 16.16 fixed point value. intakeTarget = (newTarget * intakeScaling) >> 16; //cpu1 intake // The scaled value is again clipped to the min/max output rpm. if (intakeTarget < outputMin) intakeTarget = outputMin; else if (intakeTarget > outputMax) intakeTarget = outputMax; // program the intake fans, secOutputControl and fouOutputControl if (secOutputControl->sendTargetValue( intakeTarget )) { secOutputControl->setTargetValue( intakeTarget ); } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sendNewTarget failed to send target value (secondary)\n"); } if (fouOutputControl->sendTargetValue( intakeTarget )) { fouOutputControl->setTargetValue( intakeTarget ); } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sendNewTarget failed to send target value (fourth)\n"); } // The fifth and sixth control are the pumps. // The pumps get programmed with a scaled version of the exhaust speed. The // scaling factor is simply a proportion between the max fan RPM and the max // pump RPM, the objective being that if the fans are at max, the pumps will // also be at max. // [3751602] // NOTE: The order of the math here is critical. The mutiplication must be done first to // avoid rounding errors which will keep the pump speed at min, or not allow it // to go above the fan speed. This math makes sure that the fan speed does not // exceed the outputMaxForPump value, hence keeping the value at or below the max. pumpTarget = (newTarget * outputMaxForPump)/outputMax; // Now we need to check to make sure we do not go below the MIN value for the pump. pumpTarget = MAX(pumpTarget, outputMinForPump); if (fifOutputControl->sendTargetValue( pumpTarget )) { fifOutputControl->setTargetValue( pumpTarget ); } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sendNewTarget failed to send target value (fifth)\n"); } if (sixOutputControl->sendTargetValue( pumpTarget )) { sixOutputControl->setTargetValue( pumpTarget ); } else { CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sendNewTarget failed to send target value (sixth)\n"); } } } void PowerMac7_3_CPUCoolingCtrlLoop::sensorRegistered( IOPlatformSensor * aSensor ) { //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sensorRegistered - entered\n"); if (inputSensor->isRegistered() == kOSBooleanTrue && inputSensor2->isRegistered() == kOSBooleanTrue && voltageSensor1->isRegistered() == kOSBooleanTrue && voltageSensor2->isRegistered() == kOSBooleanTrue && currentSensor1->isRegistered() == kOSBooleanTrue && currentSensor2->isRegistered() == kOSBooleanTrue && outputControl->isRegistered() == kOSBooleanTrue && secOutputControl->isRegistered() == kOSBooleanTrue && thiOutputControl->isRegistered() == kOSBooleanTrue && fouOutputControl->isRegistered() == kOSBooleanTrue && // fifth is RPM0, but it might not exist on single pump systems sixOutputControl->isRegistered() == kOSBooleanTrue) { //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sensorRegistered allRegistered!\n"); ctrlloopState = kIOPCtrlLoopFirstAdjustment; // set the deadline deadlinePassed(); } } void PowerMac7_3_CPUCoolingCtrlLoop::controlRegistered( IOPlatformControl * aControl ) { //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::controlRegistered - entered\n"); if (inputSensor->isRegistered() == kOSBooleanTrue && inputSensor2->isRegistered() == kOSBooleanTrue && voltageSensor1->isRegistered() == kOSBooleanTrue && voltageSensor2->isRegistered() == kOSBooleanTrue && currentSensor1->isRegistered() == kOSBooleanTrue && currentSensor2->isRegistered() == kOSBooleanTrue && outputControl->isRegistered() == kOSBooleanTrue && secOutputControl->isRegistered() == kOSBooleanTrue && thiOutputControl->isRegistered() == kOSBooleanTrue && fouOutputControl->isRegistered() == kOSBooleanTrue && // fifth is RPM0, but it might not exist on single pump systems sixOutputControl->isRegistered() == kOSBooleanTrue) { //CTRLLOOP_DLOG("PowerMac7_3_CPUCoolingCtrlLoop::sensorRegistered allRegistered!\n"); ctrlloopState = kIOPCtrlLoopFirstAdjustment; // set the deadline deadlinePassed(); } }