#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/IOMessage.h>
#include <mach/clock_types.h>
#include "AppleFan.h"
static fan_speed_table_t gDefaultSpeedTable =
{ 0x3900, 0x3A4A, 0x3Ad3, 0x3B3C,
0x3B94, 0x3BE3, 0x3C29, 0x3C6A,
0x3CA6, 0x3CD7, 0x3D15, 0x3D48,
0x3D78, 0x3DA7, 0x3DD4, 0x3E00 };
static SInt16 gDefaultHysteresisTemp = 0x3700;
static UInt64 gDefaultPollingPeriod = 8;
static UInt64 gDefaultSpeedupDelay = 8;
static UInt64 gDefaultSlowdownDelay = 48;
#define super IOService
OSDefineMetaClassAndStructors(AppleFan, IOService)
bool AppleFan::init(OSDictionary *dict)
{
if (!super::init(dict)) return(false);
I2C_iface = 0;
cpu_thermo = 0;
fI2CAddr = 0;
timerCallout = NULL;
fCurrentPowerState = kPowerOn;
pollingPeriodKey = OSSymbol::withCString(kFanPollingPeriodKey);
speedTableKey = OSSymbol::withCString(kFanSpeedTableKey);
speedupDelayKey = OSSymbol::withCString(kSpeedupDelayKey);
slowdownDelayKey = OSSymbol::withCString(kSlowdownDelayKey);
hysteresisTempKey = OSSymbol::withCString(kHysteresisTempKey);
getTempSymbol = OSSymbol::withCString(kGetTempSymbol);
#ifdef APPLEFAN_DEBUG
currentSpeedKey = OSSymbol::withCString(kFanCurrentSpeedKey);
currentCPUTempKey = OSSymbol::withCString(kCPUCurrentTempKey);
forceUpdateKey = OSSymbol::withCString(kForceUpdateSymbol);
#endif
AbsoluteTime_to_scalar(&fLastTransition) = 0;
fLastFanSpeed = 0;
fLastRmtTemp = 0;
AbsoluteTime_to_scalar(&fWakeTime) = 0;
return(true);
}
void AppleFan::free(void)
{
if (pollingPeriodKey) pollingPeriodKey->release();
if (speedTableKey) speedTableKey->release();
if (speedupDelayKey) speedupDelayKey->release();
if (slowdownDelayKey) slowdownDelayKey->release();
if (hysteresisTempKey) hysteresisTempKey->release();
if (getTempSymbol) getTempSymbol->release();
#ifdef APPLEFAN_DEBUG
if (currentSpeedKey) currentSpeedKey->release();
if (currentCPUTempKey) currentCPUTempKey->release();
if (forceUpdateKey) forceUpdateKey->release();
#endif
super::free();
}
IOService *AppleFan::probe(IOService *provider, SInt32 *score)
{
OSData *tmp_osdata, *thermo;
const char *compat;
tmp_osdata = OSDynamicCast(OSData, provider->getProperty("compatible"));
thermo = OSDynamicCast(OSData, provider->getProperty(kGetTempSymbol));
if (tmp_osdata && thermo)
{
compat = (const char *)tmp_osdata->getBytesNoCopy();
if (strcmp(compat, kADM1030Compatible) == 0)
{
*score = 10000;
return(this);
}
}
*score = 0;
return(0);
}
bool AppleFan::start(IOService *provider)
{
OSData *tmp_osdata;
UInt32 *tmp_uint32;
IOService *tmp_svc;
const OSSymbol *uninI2C;
mach_timespec_t WaitTimeOut;
DLOG("+AppleFan::start\n");
static const IOPMPowerState powerStates[2] = {
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, IOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
WaitTimeOut.tv_sec = 30;
WaitTimeOut.tv_nsec = 0;
if (!super::start(provider)) return(false);
tmp_osdata = OSDynamicCast(OSData, provider->getProperty("reg"));
if (!tmp_osdata)
{
IOLog("AppleFan::start failed to fetch provider's reg property!\n");
DLOG("-AppleFan::start\n");
return(false);
}
tmp_uint32 = (UInt32 *)tmp_osdata->getBytesNoCopy();
fI2CBus = (UInt8)(*tmp_uint32 >> 8);
fI2CAddr = (UInt8)*tmp_uint32;
fI2CAddr >>= 1;
DLOG("@AppleFan::start fI2CBus=%02x fI2CAddr=%02x\n",
fI2CBus, fI2CAddr);
uninI2C = OSSymbol::withCStringNoCopy("PPCI2CInterface.i2c-uni-n");
tmp_svc = waitForService(resourceMatching(uninI2C), &WaitTimeOut);
if (tmp_svc)
{
I2C_iface = (PPCI2CInterface *)tmp_svc->getProperty(uninI2C);
}
if (uninI2C) uninI2C->release();
if (!I2C_iface)
{
IOLog("AppleFan::start failed to find UniN I2C interface!\n");
DLOG("-AppleFan::start\n");
return(false);
}
DLOG("@AppleFan::start I2C_iface=%08x\n", (unsigned int)I2C_iface);
cpu_thermo = waitForService( serviceMatching( "AppleCPUThermo" ), &WaitTimeOut );
if ( cpu_thermo == NULL )
{
IOLog("AppleFan::start failed to find CPU thermistor driver!\n");
DLOG("-AppleFan::start\n");
return(false);
}
DLOG("@AppleFan::start cpu_thermo=%08x\n", (unsigned int)cpu_thermo);
tmp_osdata = OSDynamicCast(OSData, provider->getProperty(kGetTempSymbol));
if (tmp_osdata)
{
tmp_uint32 = (UInt32 *)tmp_osdata->getBytesNoCopy();
fThermoPHandle = *tmp_uint32;
tmp_svc = cpu_thermo->getProvider();
tmp_osdata = OSDynamicCast(OSData, tmp_svc->getProperty("AAPL,phandle"));
tmp_uint32 = (UInt32 *)tmp_osdata->getBytesNoCopy();
if (fThermoPHandle != *tmp_uint32)
{
IOLog("AppleFan::start AppleCPUThermo is attached to wrong thermo!!\n");
DLOG("-AppleFan::start\n");
return(false);
}
}
if (!initParms(provider))
{
IOLog("AppleFan::start failed to initialize operating parameters!\n");
return(false);
}
timerCallout = thread_call_allocate( (thread_call_func_t) AppleFan::timerEventOccured,
(thread_call_param_t) this );
if (!timerCallout)
{
IOLog("IOPlatformPlugin::start failed to allocate thread callout\n");
return(false);
}
if (!initHW(provider))
{
IOLog("AppleFan::start failed to initialize ADM1030!\n");
thread_call_free( timerCallout );
return(false);
}
DLOG("@AppleFan setting up PM notifications\n");
PMinit();
provider->joinPMtree(this);
registerPowerDriver(this, (IOPMPowerState *)powerStates, 2);
registerPrioritySleepWakeInterest(sPMNotify, this, NULL);
registerService();
doUpdate(true);
DLOG("-AppleFan::start\n");
return(true);
}
void AppleFan::stop(IOService *provider)
{
restoreADM1030State(&fSavedRegs);
if (timerCallout)
{
thread_call_cancel( timerCallout );
thread_call_free( timerCallout );
timerCallout = NULL;
}
super::stop(provider);
}
bool AppleFan::initHW(IOService *provider)
{
UInt8 myByte;
if (!saveADM1030State(&fSavedRegs))
{
IOLog("AppleFan::initHW unable to save ADM1030State!!\n");
return(false);
}
if (!doI2COpen())
{
IOLog("AppleFan::initHW failed to open I2C bus\n");
DLOG("-AppleFan::initHW\n");
return(false);
}
myByte = kFilterEnable | (kSampleRateMask & kSampleRate1_4KHz) | (kRampRateMask & kRampRate1) | kSpinUpDisable;
if (!doI2CWrite(kFanFilterReg, &myByte, 1))
{
doI2CClose();
DLOG("-AppleFan::initHw failed to write fan filter reg\n");
return(false);
}
myByte = kPWMOutputEnable;
if (!doI2CWrite(kConfigReg2, &myByte, 1))
{
doI2CClose();
DLOG("-AppleFan::initHW failed to write config reg 2\n");
return(false);
}
myByte = kMonitorEnable | kTACHModeSelect | kFanFaultEnable | (kPWMModeSelectMask & kPWMModeRemote) | kAutoEnable;
if (!doI2CWrite(kConfigReg1, &myByte, 1))
{
doI2CClose();
DLOG("-AppleFan::initHW failed to write config reg 1\n");
return(false);
}
doI2CClose();
return(true);
}
bool AppleFan::initParms(IOService *provider)
{
int i;
OSObject *value;
OSDictionary *personality;
OSDictionary *defaults = OSDictionary::withCapacity(5);
for (i=0; i<kNumFanSpeeds; i++)
fSpeedTable[i] = gDefaultSpeedTable[i];
fPollingPeriod = gDefaultPollingPeriod * NSEC_PER_SEC;
fSpeedupDelay = gDefaultSpeedupDelay * NSEC_PER_SEC;
fSlowdownDelay = gDefaultSlowdownDelay * NSEC_PER_SEC;
fHysteresisTemp = gDefaultHysteresisTemp;
personality = OSDynamicCast(OSDictionary, getProperty(kDefaultParamsKey));
if (personality == NULL || defaults == NULL) return true;
if (value = personality->getObject(speedTableKey))
{
DLOG("@AppleFan::initParams using personality's speed table\n");
defaults->setObject(speedTableKey, value);
}
if (value = personality->getObject(pollingPeriodKey))
{
DLOG("@AppleFan::initParams using personality's polling period\n");
defaults->setObject(pollingPeriodKey, value);
}
if (value = personality->getObject(speedupDelayKey))
{
DLOG("@AppleFan::initParams using personality's speedup delay\n");
defaults->setObject(speedupDelayKey, value);
}
if (value = personality->getObject(slowdownDelayKey))
{
DLOG("@AppleFan::initParams using personality's slowdown delay\n");
defaults->setObject(slowdownDelayKey, value);
}
if (value = personality->getObject(hysteresisTempKey))
{
DLOG("@AppleFan::initParams using personality's hysteresis temp\n");
defaults->setObject(hysteresisTempKey, value);
}
parseDict(defaults);
return(true);
}
void AppleFan::parseDict(OSDictionary *props)
{
unsigned int count, index;
SInt16 temperature;
OSNumber *number;
OSArray *speeds;
if ((number = OSDynamicCast(OSNumber, props->getObject(pollingPeriodKey))) != 0)
{
fPollingPeriod = number->unsigned64BitValue();
fPollingPeriod *= NSEC_PER_SEC;
}
if ((number = OSDynamicCast(OSNumber, props->getObject(speedupDelayKey))) != 0)
{
fSpeedupDelay = number->unsigned64BitValue();
fSpeedupDelay *= NSEC_PER_SEC;
}
if ((number = OSDynamicCast(OSNumber, props->getObject(slowdownDelayKey))) != 0)
{
fSlowdownDelay = number->unsigned64BitValue();
fSlowdownDelay *= NSEC_PER_SEC;
}
if ((number = OSDynamicCast(OSNumber, props->getObject(hysteresisTempKey))) != 0)
{
fHysteresisTemp = (SInt16)number->unsigned16BitValue();
}
if ((speeds = OSDynamicCast(OSArray, props->getObject(speedTableKey))) != 0)
{
count = speeds->getCount();
if (count == kNumFanSpeeds)
{
for (index=0; index<count; index++)
{
number = OSDynamicCast(OSNumber, speeds->getObject(index));
if (number == NULL)
{
IOLog("AppleFan::setProperties SOMETHING IS VERY WRONG!!!");
break;
}
temperature = (SInt16)number->unsigned16BitValue();
fSpeedTable[index] = temperature;
}
}
}
}
void AppleFan::timerEventOccured( void *self )
{
AppleFan * me = OSDynamicCast(AppleFan, (OSMetaClassBase *) self);
if (me) me->doUpdate(false);
}
void AppleFan::doUpdate(bool first)
{
AbsoluteTime interval;
UInt8 newSpeed;
SInt16 cpu_temp;
DLOG("+AppleFan::doUpdate\n");
if (!getCPUTemp(&cpu_temp))
{
IOLog("AppleFan::doUpdate ERROR FETCHING CPU TEMP!!!\n");
restoreADM1030State(&fSavedRegs);
terminate();
return;
}
newSpeed = 0;
while ((cpu_temp >= fSpeedTable[newSpeed]) && (newSpeed < (kNumFanSpeeds - 1)))
newSpeed++;
setFanSpeed(newSpeed, cpu_temp, first);
if (first) clock_get_uptime(&fWakeTime);
nanoseconds_to_absolutetime(fPollingPeriod, &interval);
ADD_ABSOLUTETIME(&fWakeTime, &interval);
thread_call_enter_delayed( timerCallout, fWakeTime );
DLOG("-AppleFan::doUpdate\n");
}
void AppleFan::setFanSpeed(UInt8 speed, SInt16 cpu_temp, bool first)
{
UInt8 desiredSpeed;
SInt16 rmt_temp;
AbsoluteTime ticksPassed;
UInt64 nsecPassed;
if (!getRemoteTemp(&rmt_temp))
{
IOLog("AppleFan::setFanSpeed FATAL ERROR FETCHING REMOTE CHANNEL TEMP!!!\n");
restoreADM1030State(&fSavedRegs);
terminate();
return;
}
if (first)
{
DLOG("@AppleFan::setFanSpeed initial speed is %u\n", speed);
setADM1030SpeedMagically(speed, rmt_temp);
clock_get_uptime(&fLastTransition);
}
else
{
if (speed == fLastFanSpeed)
{
if (rmt_temp != fLastRmtTemp)
{
DLOG("@AppleFan::setFanSpeed environmental update\n");
setADM1030SpeedMagically(speed, rmt_temp);
return;
}
DLOG("@AppleFan::setFanSpeed no update needed\n");
}
else
{
clock_get_uptime(&ticksPassed);
SUB_ABSOLUTETIME(&ticksPassed, &fLastTransition);
absolutetime_to_nanoseconds(ticksPassed, &nsecPassed);
if (speed < fLastFanSpeed)
{
if (speed == kDutyCycleOff && fLastFanSpeed == kDutyCycle07)
{
DLOG("@AppleFan::setFanSpeed hysteresis check cpu_temp 0x%04x fHysteresisTemp %04x\n",
cpu_temp, fHysteresisTemp);
if (cpu_temp > fHysteresisTemp)
{
DLOG("@AppleFan::setFanSpeed hysteresis active\n");
if (rmt_temp != fLastRmtTemp)
setADM1030SpeedMagically(fLastFanSpeed, rmt_temp);
return;
}
}
DLOG("@AppleFan::setFanSpeed downward check nsecPassed 0x%llX fSlowdownDelay 0x%llX\n",
nsecPassed, fSlowdownDelay);
if (nsecPassed > fSlowdownDelay)
{
desiredSpeed = fLastFanSpeed - 1;
DLOG("@AppleFan::setFanSpeed slowdown to %u\n", desiredSpeed);
setADM1030SpeedMagically(desiredSpeed, rmt_temp);
clock_get_uptime(&fLastTransition);
}
else
{
DLOG("@AppleFan::setFanSpeed slowdown delay active\n");
if (rmt_temp != fLastRmtTemp)
setADM1030SpeedMagically(fLastFanSpeed, rmt_temp);
}
}
else if (speed > fLastFanSpeed)
{
DLOG("@AppleFan::setFanSpeed upward check nsecPassed 0x%llX fSpeedupDelay 0x%llX\n",
nsecPassed, fSpeedupDelay);
if (nsecPassed > fSpeedupDelay)
{
desiredSpeed = fLastFanSpeed + 1;
DLOG("@AppleFan::setFanSpeed speedup to %u\n", desiredSpeed);
setADM1030SpeedMagically(desiredSpeed, rmt_temp);
clock_get_uptime(&fLastTransition);
}
else
{
DLOG("@AppleFan::setFanSpeed speedup delay active\n");
if (rmt_temp != fLastRmtTemp)
setADM1030SpeedMagically(fLastFanSpeed, rmt_temp);
}
}
else { }
}
}
}
void AppleFan::setADM1030SpeedMagically(UInt8 desiredSpeed, SInt16 rmt_temp)
{
UInt8 TminTrange, speed;
TminTrange = (UInt8)(rmt_temp >> 7);
TminTrange &= ~kTrangeMask; TminTrange |= 0x7;
if (desiredSpeed == 0)
{
TminTrange += 0x10; speed = desiredSpeed;
fLastFanSpeed = desiredSpeed;
}
else
{
TminTrange -= 0x08;
speed = desiredSpeed - 1;
fLastFanSpeed = desiredSpeed;
}
fLastRmtTemp = rmt_temp;
#ifdef APPLEFAN_DEBUG
char debug[16];
temp2str(rmt_temp, debug);
#endif
DLOG("@AppleFan::setADM1030SpeedMagically speed=%u rmt_temp=%x (%sC) TminTrange=%x\n",
speed, rmt_temp, debug, TminTrange);
if (!doI2COpen())
{
IOLog("AppleFan failed to open bus for setting fan speed\n");
return;
}
if (!doI2CWrite(kRmtTminTrange, &TminTrange, 1))
{
doI2CClose();
IOLog("AppleFan failed to write to T_min/T_range register!\n");
return;
}
if (!doI2CWrite(kSpeedCfgReg, &speed, 1))
{
doI2CClose();
IOLog("AppleFan failed to write to fan speed register\n");
return;
}
doI2CClose();
}
bool AppleFan::getRemoteTemp(SInt16 *rmt_temp)
{
SInt16 scratch;
UInt8 ext_res_reg, remote_reg;
bool failed;
if (rmt_temp == NULL)
{
IOLog("AppleFan::getRemoteTemp bad arguments\n");
return(false);
}
if (!doI2COpen())
{
IOLog("AppleFan::getRemoteTemp cannot open I2C!!\n");
return(false);
}
failed = false;
if (!doI2CRead(kExtTempReg, &ext_res_reg, 1) ||
!doI2CRead(kRemoteTempReg, &remote_reg, 1))
{
IOLog("AppleFan::getRemoteTemp failed to read temperature reg!\n");
failed = true;
}
doI2CClose();
if (failed) return(false);
scratch = (SInt16)(ext_res_reg & kRemoteExtMask);
scratch <<= kRemoteExtShift;
*rmt_temp = (SInt16)(remote_reg << 8);
*rmt_temp |= scratch;
return(true);
}
bool AppleFan::getCPUTemp(SInt16 *cpu_temp)
{
if (!cpu_temp || !cpu_thermo)
{
IOLog("AppleFan::getCPUTemp bad params!!\n");
return(false);
}
if (cpu_thermo->callPlatformFunction(getTempSymbol, false,
(void *) cpu_temp, 0, 0, 0) != kIOReturnSuccess)
{
IOLog("AppleFan::getCPUTemp failed to retreive CPU temp!!\n");
return(false);
}
return(true);
}
void AppleFan::setRestartMode(void)
{
UInt8 regval;
#ifdef APPLEFAN_DEBUG
bool success = false;
#endif
DLOG("+AppleFan::setRestartMode\n");
if (!doI2COpen())
{
IOLog("AppleFan::setRestartMode failed to open bus\n");
return;
}
do {
regval = 0x41;
if (!doI2CWrite(kLocTminTrange, ®val, 1)) break;
if (!doI2CWrite(kRmtTminTrange, ®val, 1)) break;
regval = kSpinUp2000MS |
kPWMFreq31Hz |
kSpeedRange1324;
if (!doI2CWrite(kFanCharReg, ®val, 1)) break;
regval = kDutyCycleOff;
if (!doI2CWrite(kSpeedCfgReg, ®val, 1)) break;
regval = kSampleRate1_4KHz |
kRampRate1 |
kSpinUpDisable;
if (!doI2CWrite(kFanFilterReg, ®val, 1)) break;
regval = kPWMOutputEnable |
kLocTempINTEnable |
kRmtTempINTEnable;
if (!doI2CWrite(kConfigReg2, ®val, 1)) break;
regval = kFanFaultEnable;
if (!doI2CWrite(kConfigReg1, ®val, 1)) break;
#ifdef APPLEFAN_DEBUG
success = true;
#endif
} while (false);
doI2CClose();
DLOG("-AppleFan::setRestartMode success = %s\n", success ? "TRUE" : "FALSE");
}
IOReturn AppleFan::powerStateWillChangeTo(IOPMPowerFlags flags,
unsigned long stateNumber, IOService *whatDevice)
{
return(IOPMAckImplied);
}
IOReturn AppleFan::setPowerState(unsigned long powerStateOrdinal,
IOService *whatDevice)
{
DLOG("@AppleFan setPowerState %lu\n", powerStateOrdinal);
if (fCurrentPowerState == kPowerOff && powerStateOrdinal == kPowerOn)
{
doWake();
}
else if (fCurrentPowerState == kPowerOn && powerStateOrdinal == kPowerOff)
{
doSleep();
}
fCurrentPowerState = powerStateOrdinal;
return(IOPMAckImplied);
}
IOReturn AppleFan::powerStateDidChangeTo(IOPMPowerFlags flags,
unsigned long stateNumber, IOService *whatDevice)
{
return(IOPMAckImplied);
}
IOReturn AppleFan::sPMNotify(void *target, void *refCon,
long unsigned int messageType, IOService *provider,
void *messageArg, vm_size_t argSize)
{
AppleFan *self;
IOService *svc_target;
DLOG("+AppleFan::sPMNotify\n");
svc_target = (IOService *)target;
if (OSDynamicCast(AppleFan, svc_target) != 0)
{
self = (AppleFan *)target;
}
else
{
IOLog("AppleFan::sPMNotify invalid target\n");
return(kIOReturnBadArgument);
}
switch (messageType)
{
case kIOMessageSystemWillRestart:
DLOG("@AppleFan::sPMNotify kIOMessageSystemWillRestart\n");
self->doRestart();
break;
case kIOMessageSystemWillPowerOff:
DLOG("@AppleFan::sPMNotify kIOMessageSystemWillPowerOff\n");
default:
break;
}
return(kIOReturnSuccess);
}
void AppleFan::doSleep(void)
{
DLOG("+AppleFan::doSleep\n");
thread_call_cancel( timerCallout );
if (fLastFanSpeed != kDutyCycleOff)
setADM1030SpeedMagically( kDutyCycleOff, fLastRmtTemp );
DLOG("-AppleFan::doSleep\n");
}
void AppleFan::doWake(void)
{
DLOG("+AppleFan::doWake\n");
doUpdate(true);
DLOG("-AppleFan::doWake\n");
}
void AppleFan::doRestart(void)
{
DLOG("+AppleFan::doRestart\n");
thread_call_cancel( timerCallout );
setRestartMode();
DLOG("-AppleFan::doRestart\n");
}
bool AppleFan::saveADM1030State(adm1030_regs_t *regs)
{
bool success = false;
if (!doI2COpen())
{
IOLog("AppleFan::saveADM1030State failed to open bus!!\n");
return false;
}
do
{
if (!doI2CRead(kConfigReg1, ®s->config1, 1)) break;
if (!doI2CRead(kConfigReg2, ®s->config2, 1)) break;
if (!doI2CRead(kFanCharReg, ®s->fan_char, 1)) break;
if (!doI2CRead(kSpeedCfgReg, ®s->speed_cfg, 1)) break;
if (!doI2CRead(kFanFilterReg, ®s->fan_filter, 1)) break;
if (!doI2CRead(kLocTminTrange, ®s->loc_tmin_trange, 1)) break;
if (!doI2CRead(kRmtTminTrange, ®s->rmt_tmin_trange, 1)) break;
success = true;
} while (false);
doI2CClose();
return success;
}
void AppleFan::restoreADM1030State(adm1030_regs_t *regs)
{
bool success = false;
if (!doI2COpen())
{
IOLog("AppleFan::restoreADM1030State failed to open bus!!\n");
return;
}
do
{
if (!doI2CWrite(kRmtTminTrange, ®s->rmt_tmin_trange, 1)) break;
if (!doI2CWrite(kLocTminTrange, ®s->loc_tmin_trange, 1)) break;
if (!doI2CWrite(kFanFilterReg, ®s->fan_filter, 1)) break;
if (!doI2CWrite(kSpeedCfgReg, ®s->speed_cfg, 1)) break;
if (!doI2CWrite(kFanCharReg, ®s->fan_char, 1)) break;
if (!doI2CWrite(kConfigReg2, ®s->config2, 1)) break;
if (!doI2CWrite(kConfigReg1, ®s->config1, 1)) break;
success = true;
} while (false);
doI2CClose();
}
bool AppleFan::doI2COpen(void)
{
DLOG("@AppleFan::doI2COpen bus=%02x\n", fI2CBus);
return(I2C_iface->openI2CBus(fI2CBus));
}
void AppleFan::doI2CClose(void)
{
DLOG("@AppleFan::doI2CClose\n");
I2C_iface->closeI2CBus();
}
bool AppleFan::doI2CRead(UInt8 sub, UInt8 *bytes, UInt16 len)
{
UInt8 retries;
#ifdef APPLEFAN_DEBUG
char debugStr[128];
sprintf(debugStr, "@AppleFan::doI2CRead addr=%02x sub=%02x bytes=%08x len=%04x",
fI2CAddr, sub, (unsigned int)bytes, len);
#endif
I2C_iface->setCombinedMode();
retries = kNumRetries;
while (!I2C_iface->readI2CBus(fI2CAddr, sub, bytes, len))
{
if (retries > 0)
{
IOLog("AppleFan::doI2CRead read failed, retrying...\n");
retries--;
}
else
{
IOLog("AppleFan::doI2CRead cannot read from I2C!!\n");
return(false);
}
}
DLOG("%s (first byte %02x)\n", debugStr, bytes[0]);
return(true);
}
bool AppleFan::doI2CWrite(UInt8 sub, UInt8 *bytes, UInt16 len)
{
UInt8 retries;
DLOG("@AppleFan::doI2CWrite addr=%02x sub=%02x bytes=%08x len=%04x (first byte %02x)\n",
fI2CAddr, sub, (unsigned int)bytes, len, bytes[0]);
I2C_iface->setStandardSubMode();
retries = kNumRetries;
while (!I2C_iface->writeI2CBus(fI2CAddr, sub, bytes, len))
{
if (retries > 0)
{
IOLog("AppleFan::doI2CWrite write failed, retrying...\n");
retries--;
}
else
{
IOLog("AppleFan::doI2CWrite cannot write to I2C!!\n");
return(false);
}
}
return(true);
}
#ifdef APPLEFAN_DEBUG
IOReturn AppleFan::setProperties(OSObject *properties)
{
OSDictionary *props = OSDynamicCast(OSDictionary, properties);
if (props == NULL) return kIOReturnBadArgument;
if (props->getObject(forceUpdateKey) != NULL)
{
publishSpeedTable();
publishPollingPeriod();
publishDelays();
publishHysteresisTemp();
publishCurrentSpeed();
publishCurrentCPUTemp();
return kIOReturnSuccess;
}
thread_call_cancel( timerCallout );
parseDict(props);
doUpdate(true);
return kIOReturnSuccess;
}
void AppleFan::publishSpeedTable(void)
{
int i;
SInt64 mylonglong;
OSNumber *entries[kNumFanSpeeds];
OSArray *entryArray;
for (i=0; i<kNumFanSpeeds; i++)
{
mylonglong = (SInt64)fSpeedTable[i];
entries[i] = OSNumber::withNumber(mylonglong, sizeof(SInt64) * 8);
}
entryArray = OSArray::withObjects((const OSObject **)entries, kNumFanSpeeds, 0);
for (i=0; i<kNumFanSpeeds; i++)
entries[i]->release();
setProperty(speedTableKey, OSDynamicCast(OSObject, entryArray));
entryArray->release();
}
void AppleFan::publishPollingPeriod(void)
{
OSNumber *period = OSNumber::withNumber(fPollingPeriod / NSEC_PER_SEC,
sizeof(fPollingPeriod) * 8);
if (period)
{
setProperty(pollingPeriodKey, period);
period->release();
}
}
void AppleFan::publishDelays(void)
{
OSNumber *speedupDelay = OSNumber::withNumber(fSpeedupDelay / NSEC_PER_SEC,
sizeof(fSpeedupDelay) * 8);
OSNumber *slowdownDelay = OSNumber::withNumber(fSlowdownDelay / NSEC_PER_SEC,
sizeof(fSlowdownDelay) * 8);
if (speedupDelay)
{
setProperty(speedupDelayKey, speedupDelay);
speedupDelay->release();
}
if (slowdownDelay)
{
setProperty(slowdownDelayKey, slowdownDelay);
slowdownDelay->release();
}
}
void AppleFan::publishHysteresisTemp(void)
{
OSNumber *hysteresisTemp = OSNumber::withNumber(fHysteresisTemp, sizeof(fHysteresisTemp) * 8);
if (hysteresisTemp)
{
setProperty(hysteresisTempKey, hysteresisTemp);
hysteresisTemp->release();
}
}
void AppleFan::publishCurrentSpeed(void)
{
UInt64 mylonglong = (UInt64)fLastFanSpeed;
OSNumber *curSpeed = OSNumber::withNumber(mylonglong, sizeof(UInt64) * 8);
if (curSpeed)
{
setProperty(currentSpeedKey, curSpeed);
curSpeed->release();
}
}
void AppleFan::publishCurrentCPUTemp(void)
{
OSNumber *cpuTempNum;
SInt64 mylonglong;
SInt16 cpu_temp;
if (getCPUTemp(&cpu_temp))
{
mylonglong = (SInt64)cpu_temp;
cpuTempNum = OSNumber::withNumber(mylonglong, sizeof(SInt64) * 8);
if (cpuTempNum)
{
setProperty(currentCPUTempKey, cpuTempNum);
cpuTempNum->release();
}
}
}
#endif