<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>AppleFan.cpp</title>
<style type="text/css">
.enscript-comment { font-style: italic; color: rgb(178,34,34); }
.enscript-function-name { font-weight: bold; color: rgb(0,0,255); }
.enscript-variable-name { font-weight: bold; color: rgb(184,134,11); }
.enscript-keyword { font-weight: bold; color: rgb(160,32,240); }
.enscript-reference { font-weight: bold; color: rgb(95,158,160); }
.enscript-string { font-weight: bold; color: rgb(188,143,143); }
.enscript-builtin { font-weight: bold; color: rgb(218,112,214); }
.enscript-type { font-weight: bold; color: rgb(34,139,34); }
.enscript-highlight { text-decoration: underline; color: 0; }
</style>
</head>
<body id="top">
<h1 style="margin:8px;" id="f1">AppleFan.cpp&nbsp;&nbsp;&nbsp;<span style="font-weight: normal; font-size: 0.5em;">[<a href="?txt">plain text</a>]</span></h1>
<hr/>
<div></div>
<pre>
<span class="enscript-comment">/*
 * 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
 * &quot;License&quot;).  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * <a href="http://www.apple.com/publicsource">http://www.apple.com/publicsource</a> and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an &quot;AS IS&quot; 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@
 */</span>
<span class="enscript-comment">/*
 * Copyright (c) 2002 Apple Computer, Inc.  All rights reserved.
 *
 */</span>

#<span class="enscript-reference">include</span> <span class="enscript-string">&lt;IOKit/pwr_mgt/RootDomain.h&gt;</span>
#<span class="enscript-reference">include</span> <span class="enscript-string">&lt;IOKit/IOMessage.h&gt;</span>
#<span class="enscript-reference">include</span> <span class="enscript-string">&lt;mach/clock_types.h&gt;</span>
#<span class="enscript-reference">include</span> <span class="enscript-string">&quot;AppleFan.h&quot;</span>

<span class="enscript-comment">/*
 * Default Parameters
 *
 * First look for defaults in the personality, otherwise we fall back to these
 * hard coded ones.
 *
 * Speed Table: Linear Ramp, Minimum 57C, Maximum 62C
 *
 * Note: These temperatures are expressed in 8.8 fixed point values.
 */</span>
<span class="enscript-type">static</span> fan_speed_table_t gDefaultSpeedTable =
	{ 0x3900, 0x3A4A, 0x3Ad3, 0x3B3C,
	  0x3B94, 0x3BE3, 0x3C29, 0x3C6A,
	  0x3CA6, 0x3CD7, 0x3D15, 0x3D48,
	  0x3D78, 0x3DA7, 0x3DD4, 0x3E00 };

<span class="enscript-comment">/*
 * Hysteresis Temperature 55 C
 */</span>
<span class="enscript-type">static</span> SInt16 gDefaultHysteresisTemp = 0x3700;

<span class="enscript-comment">/*
 * Polling Period (seconds)
 */</span>
<span class="enscript-type">static</span> UInt64 gDefaultPollingPeriod = 8;

<span class="enscript-comment">/*
 * Speedup Delay (seconds)
 */</span>
<span class="enscript-type">static</span> UInt64 gDefaultSpeedupDelay = 8;

<span class="enscript-comment">/*
 * Slowdown Delay (seconds)
 */</span>
<span class="enscript-type">static</span> UInt64 gDefaultSlowdownDelay = 48;


#<span class="enscript-reference">define</span> <span class="enscript-variable-name">super</span> IOService

<span class="enscript-function-name">OSDefineMetaClassAndStructors</span>(AppleFan, IOService)

<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::init</span>(OSDictionary *dict)
{
	<span class="enscript-keyword">if</span> (!super::init(dict)) <span class="enscript-keyword">return</span>(false);

	I2C_iface = 0;
	cpu_thermo = 0;

	fI2CAddr = 0;

	timerCallout = NULL;

	fCurrentPowerState = kPowerOn;	<span class="enscript-comment">// good guess, i suppose
</span>
	pollingPeriodKey = OSSymbol::withCString(kFanPollingPeriodKey);
	speedTableKey = OSSymbol::withCString(kFanSpeedTableKey);
	speedupDelayKey = OSSymbol::withCString(kSpeedupDelayKey);
	slowdownDelayKey = OSSymbol::withCString(kSlowdownDelayKey);
	hysteresisTempKey = OSSymbol::withCString(kHysteresisTempKey);
	getTempSymbol = OSSymbol::withCString(kGetTempSymbol);

#<span class="enscript-reference">ifdef</span> <span class="enscript-variable-name">APPLEFAN_DEBUG</span>
	currentSpeedKey = OSSymbol::withCString(kFanCurrentSpeedKey);
	currentCPUTempKey = OSSymbol::withCString(kCPUCurrentTempKey);
	forceUpdateKey = OSSymbol::withCString(kForceUpdateSymbol);
#<span class="enscript-reference">endif</span>

	AbsoluteTime_to_scalar(&amp;fLastTransition) = 0;
	fLastFanSpeed = 0;
	fLastRmtTemp = 0;
	AbsoluteTime_to_scalar(&amp;fWakeTime) = 0;

	<span class="enscript-keyword">return</span>(true);
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::free</span>(<span class="enscript-type">void</span>)
{
	<span class="enscript-keyword">if</span> (pollingPeriodKey) pollingPeriodKey-&gt;release();
	<span class="enscript-keyword">if</span> (speedTableKey) speedTableKey-&gt;release();
	<span class="enscript-keyword">if</span> (speedupDelayKey) speedupDelayKey-&gt;release();
	<span class="enscript-keyword">if</span> (slowdownDelayKey) slowdownDelayKey-&gt;release();
	<span class="enscript-keyword">if</span> (hysteresisTempKey) hysteresisTempKey-&gt;release();
	<span class="enscript-keyword">if</span> (getTempSymbol) getTempSymbol-&gt;release();

#<span class="enscript-reference">ifdef</span> <span class="enscript-variable-name">APPLEFAN_DEBUG</span>
	<span class="enscript-keyword">if</span> (currentSpeedKey) currentSpeedKey-&gt;release();
	<span class="enscript-keyword">if</span> (currentCPUTempKey) currentCPUTempKey-&gt;release();
	<span class="enscript-keyword">if</span> (forceUpdateKey) forceUpdateKey-&gt;release();
#<span class="enscript-reference">endif</span>

	<span class="enscript-reference">super</span>::free();
}

IOService *<span class="enscript-function-name">AppleFan::probe</span>(IOService *provider, SInt32 *score)
{
	OSData *tmp_osdata, *thermo;
	<span class="enscript-type">const</span> <span class="enscript-type">char</span> *compat;

	<span class="enscript-comment">// Check compatible property and presence of platform-getTemp
</span>	tmp_osdata = OSDynamicCast(OSData, provider-&gt;getProperty(<span class="enscript-string">&quot;compatible&quot;</span>));
	thermo = OSDynamicCast(OSData, provider-&gt;getProperty(kGetTempSymbol));
	<span class="enscript-keyword">if</span> (tmp_osdata &amp;&amp; thermo)
	{
		compat = (<span class="enscript-type">const</span> <span class="enscript-type">char</span> *)tmp_osdata-&gt;getBytesNoCopy();
		<span class="enscript-keyword">if</span> (strcmp(compat, kADM1030Compatible) == 0)
		{
			*score = 10000;
			<span class="enscript-keyword">return</span>(<span class="enscript-keyword">this</span>);
		}
	}

	<span class="enscript-comment">// I can't drive this fan controller
</span>	*score = 0;
	<span class="enscript-keyword">return</span>(0);
}

<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::start</span>(IOService *provider)
{
	OSData			*tmp_osdata;
	UInt32			*tmp_uint32;
	IOService		*tmp_svc;
	<span class="enscript-type">const</span> OSSymbol	*uninI2C;
    mach_timespec_t WaitTimeOut;

	DLOG(<span class="enscript-string">&quot;+AppleFan::start\n&quot;</span>);

	<span class="enscript-comment">// We have two power states - off and on
</span>	<span class="enscript-type">static</span> <span class="enscript-type">const</span> 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 }
    };

	<span class="enscript-comment">// Use a 30 second timeout when calling waitForService()
</span>	WaitTimeOut.tv_sec = 30;
	WaitTimeOut.tv_nsec = 0;

	<span class="enscript-keyword">if</span> (!super::start(provider)) <span class="enscript-keyword">return</span>(false);

	<span class="enscript-comment">// get my I2C address from the device tree
</span>	tmp_osdata = OSDynamicCast(OSData, provider-&gt;getProperty(<span class="enscript-string">&quot;reg&quot;</span>));
	<span class="enscript-keyword">if</span> (!tmp_osdata)
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::start failed to fetch provider's reg property!\n&quot;</span>);
		DLOG(<span class="enscript-string">&quot;-AppleFan::start\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	tmp_uint32 = (UInt32 *)tmp_osdata-&gt;getBytesNoCopy();
	fI2CBus = (UInt8)(*tmp_uint32 &gt;&gt; 8);
	fI2CAddr = (UInt8)*tmp_uint32;
	fI2CAddr &gt;&gt;= 1;		<span class="enscript-comment">// right shift by one to make a 7-bit address
</span>
	DLOG(<span class="enscript-string">&quot;@AppleFan::start fI2CBus=%02x fI2CAddr=%02x\n&quot;</span>,
			fI2CBus, fI2CAddr);

	<span class="enscript-comment">// find the UniN I2C driver
</span>	uninI2C = OSSymbol::withCStringNoCopy(<span class="enscript-string">&quot;PPCI2CInterface.i2c-uni-n&quot;</span>);
	tmp_svc = waitForService(resourceMatching(uninI2C), &amp;WaitTimeOut);
	<span class="enscript-keyword">if</span> (tmp_svc)
	{
		I2C_iface = (PPCI2CInterface *)tmp_svc-&gt;getProperty(uninI2C);
	}

	<span class="enscript-keyword">if</span> (uninI2C) uninI2C-&gt;release();

	<span class="enscript-comment">// I2C_iface is initialized to 0 so if it is not set here, we didn't find I2C
</span>	<span class="enscript-keyword">if</span> (!I2C_iface)
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::start failed to find UniN I2C interface!\n&quot;</span>);
		DLOG(<span class="enscript-string">&quot;-AppleFan::start\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	DLOG(<span class="enscript-string">&quot;@AppleFan::start I2C_iface=%08x\n&quot;</span>, (<span class="enscript-type">unsigned</span> <span class="enscript-type">int</span>)I2C_iface);

	<span class="enscript-comment">// find the AppleCPUThermo driver
</span>	cpu_thermo = waitForService( serviceMatching( <span class="enscript-string">&quot;AppleCPUThermo&quot;</span> ), &amp;WaitTimeOut );
	
	<span class="enscript-keyword">if</span> ( cpu_thermo == NULL )
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::start failed to find CPU thermistor driver!\n&quot;</span>);
		DLOG(<span class="enscript-string">&quot;-AppleFan::start\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	DLOG(<span class="enscript-string">&quot;@AppleFan::start cpu_thermo=%08x\n&quot;</span>, (<span class="enscript-type">unsigned</span> <span class="enscript-type">int</span>)cpu_thermo);

	<span class="enscript-comment">// the platform-getTemp property in our provider's property table holds
</span>	<span class="enscript-comment">// the phandle of the temp-monitor node.  Check to see if this matches
</span>	<span class="enscript-comment">// cpu_thermo's provider's phandle.
</span>	tmp_osdata = OSDynamicCast(OSData, provider-&gt;getProperty(kGetTempSymbol));
	<span class="enscript-keyword">if</span> (tmp_osdata)
	{
		tmp_uint32 = (UInt32 *)tmp_osdata-&gt;getBytesNoCopy();
		fThermoPHandle = *tmp_uint32;

		tmp_svc = cpu_thermo-&gt;getProvider();
		tmp_osdata = OSDynamicCast(OSData, tmp_svc-&gt;getProperty(<span class="enscript-string">&quot;AAPL,phandle&quot;</span>));
		tmp_uint32 = (UInt32 *)tmp_osdata-&gt;getBytesNoCopy();

		<span class="enscript-keyword">if</span> (fThermoPHandle != *tmp_uint32)
		{
			IOLog(<span class="enscript-string">&quot;AppleFan::start AppleCPUThermo is attached to wrong thermo!!\n&quot;</span>);
			DLOG(<span class="enscript-string">&quot;-AppleFan::start\n&quot;</span>);
			<span class="enscript-keyword">return</span>(false);
		}
	}

	<span class="enscript-comment">// Set up operating parameters, checking for overrides in device tree
</span>	<span class="enscript-keyword">if</span> (!initParms(provider))
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::start failed to initialize operating parameters!\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-comment">// Set up the timer
</span>	timerCallout = thread_call_allocate( (thread_call_func_t) AppleFan::timerEventOccured,
			(thread_call_param_t) <span class="enscript-keyword">this</span> );

	<span class="enscript-keyword">if</span> (!timerCallout)
	{
		IOLog(<span class="enscript-string">&quot;IOPlatformPlugin::start failed to allocate thread callout\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}


	<span class="enscript-comment">// Program the chip's registers as the last operation that can fail
</span>	<span class="enscript-keyword">if</span> (!initHW(provider))
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::start failed to initialize ADM1030!\n&quot;</span>);
		<span class="enscript-comment">// don't need to thread_call_cancel because we haven't scheduled the callout
</span>		thread_call_free( timerCallout );
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-comment">// register interest in power state changes
</span>	DLOG(<span class="enscript-string">&quot;@AppleFan setting up PM notifications\n&quot;</span>);

	PMinit();
	provider-&gt;joinPMtree(<span class="enscript-keyword">this</span>);
	registerPowerDriver(<span class="enscript-keyword">this</span>, (IOPMPowerState *)powerStates, 2);

	<span class="enscript-comment">// Get restart and shutdown events too
</span>    registerPrioritySleepWakeInterest(sPMNotify, <span class="enscript-keyword">this</span>, NULL);

	<span class="enscript-comment">// Register with I/O Kit matching engine
</span>	registerService();

	<span class="enscript-comment">// do the initial update.  this sets a timeout as its last step and
</span>	<span class="enscript-comment">// begins the fan control in motion.
</span>	doUpdate(true);

	DLOG(<span class="enscript-string">&quot;-AppleFan::start\n&quot;</span>);
	<span class="enscript-keyword">return</span>(true);
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::stop</span>(IOService *provider)
{
	<span class="enscript-comment">// driver is going away, so put the chip back in the state OF left it in
</span>	restoreADM1030State(&amp;fSavedRegs);

	<span class="enscript-keyword">if</span> (timerCallout)
	{
		thread_call_cancel( timerCallout );
		thread_call_free( timerCallout );
		timerCallout = NULL;
	}

	<span class="enscript-reference">super</span>::stop(provider);
}

<span class="enscript-comment">/*************************************************************************************
	Put the ADM1030 in filtered automatic control mode to prepare it for operation.
	
*************************************************************************************/</span>
<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::initHW</span>(IOService *provider)
{
	UInt8 myByte;

	<span class="enscript-comment">// Save initial state of the biznatch
</span>	<span class="enscript-keyword">if</span> (!saveADM1030State(&amp;fSavedRegs))
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::initHW unable to save ADM1030State!!\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-comment">// Open the I2C bus
</span>	<span class="enscript-keyword">if</span> (!doI2COpen())
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::initHW failed to open I2C bus\n&quot;</span>);
		DLOG(<span class="enscript-string">&quot;-AppleFan::initHW\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-comment">// Fan Filter Register - 0x23
</span>	myByte = kFilterEnable							|	<span class="enscript-comment">// Enable auto-mode filtering
</span>			 (kSampleRateMask &amp; kSampleRate1_4KHz)	|	<span class="enscript-comment">// 1.4 KHz ADC sample rate
</span>			 (kRampRateMask &amp; kRampRate1)			|	<span class="enscript-comment">// Ramp Rate factor 1
</span>			 kSpinUpDisable;							<span class="enscript-comment">// disable fan spin-up
</span>
	<span class="enscript-keyword">if</span> (!doI2CWrite(kFanFilterReg, &amp;myByte, 1))
	{
		doI2CClose();
		DLOG(<span class="enscript-string">&quot;-AppleFan::initHw failed to write fan filter reg\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-comment">// Configuration Register 2 - 0x01
</span>	<span class="enscript-comment">//DLOG(&quot;@AppleFan::initHW setting config register 2\n&quot;);
</span>	myByte = kPWMOutputEnable;		<span class="enscript-comment">// Drive the PWM output
</span>
	<span class="enscript-keyword">if</span> (!doI2CWrite(kConfigReg2, &amp;myByte, 1))
	{
		doI2CClose();
		DLOG(<span class="enscript-string">&quot;-AppleFan::initHW failed to write config reg 2\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-comment">// Configuration Register 1 - 0x00
</span>	<span class="enscript-comment">//DLOG(&quot;@AppleFan::initHW setting config register 1\n&quot;);
</span>	myByte = kMonitorEnable							|	<span class="enscript-comment">// Enable temp monitoring
</span>	         kTACHModeSelect						|	<span class="enscript-comment">// Select analog TACH
</span>	         kFanFaultEnable						|	<span class="enscript-comment">// Enable FAN_FAULT output
</span>	         (kPWMModeSelectMask &amp; kPWMModeRemote)	|	<span class="enscript-comment">// Use remote temp
</span>	         kAutoEnable;								<span class="enscript-comment">// Use automatic control mode
</span>
	<span class="enscript-keyword">if</span> (!doI2CWrite(kConfigReg1, &amp;myByte, 1))
	{
		doI2CClose();
		DLOG(<span class="enscript-string">&quot;-AppleFan::initHW failed to write config reg 1\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	doI2CClose();

	<span class="enscript-keyword">return</span>(true);
}

<span class="enscript-comment">/*************************************************************************************
	initParms() and parseDict() are responsible for setting the initial values for
		polling period
		hysteresis temperature
		speed lookup table
		speedup delay time
		slowdown delay time

	There should be defaults for these provided in the I/O Kit personality.  If those
	are not found, then we revert to the hardcoded defaults declared statically in
	this code.
	
	parseDict is also called from setProperties when we're in debug mode.
*************************************************************************************/</span>
<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::initParms</span>(IOService *provider)
{
	<span class="enscript-type">int</span>		i;
	OSObject *value;
	OSDictionary *personality;
	OSDictionary *defaults = OSDictionary::withCapacity(5);

	<span class="enscript-keyword">for</span> (i=0; i&lt;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));

	<span class="enscript-keyword">if</span> (personality == NULL || defaults == NULL) <span class="enscript-keyword">return</span> true;

	<span class="enscript-comment">// Look for speed table
</span>	<span class="enscript-keyword">if</span> (value = personality-&gt;getObject(speedTableKey))
	{
		DLOG(<span class="enscript-string">&quot;@AppleFan::initParams using personality's speed table\n&quot;</span>);
		defaults-&gt;setObject(speedTableKey, value);
	}

	<span class="enscript-comment">// Set the polling period
</span>	<span class="enscript-keyword">if</span> (value = personality-&gt;getObject(pollingPeriodKey))
	{
		DLOG(<span class="enscript-string">&quot;@AppleFan::initParams using personality's polling period\n&quot;</span>);
		defaults-&gt;setObject(pollingPeriodKey, value);
	}

	<span class="enscript-comment">// Set up delays
</span>	<span class="enscript-keyword">if</span> (value = personality-&gt;getObject(speedupDelayKey))
	{
		DLOG(<span class="enscript-string">&quot;@AppleFan::initParams using personality's speedup delay\n&quot;</span>);
		defaults-&gt;setObject(speedupDelayKey, value);
	}

	<span class="enscript-keyword">if</span> (value = personality-&gt;getObject(slowdownDelayKey))
	{
		DLOG(<span class="enscript-string">&quot;@AppleFan::initParams using personality's slowdown delay\n&quot;</span>);
		defaults-&gt;setObject(slowdownDelayKey, value);
	}

	<span class="enscript-keyword">if</span> (value = personality-&gt;getObject(hysteresisTempKey))
	{
		DLOG(<span class="enscript-string">&quot;@AppleFan::initParams using personality's hysteresis temp\n&quot;</span>);
		defaults-&gt;setObject(hysteresisTempKey, value);
	}

	parseDict(defaults);

	<span class="enscript-keyword">return</span>(true);
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::parseDict</span>(OSDictionary *props)
{
	<span class="enscript-type">unsigned</span> <span class="enscript-type">int</span> count, index;
	SInt16 temperature;
	OSNumber *number;
	OSArray *speeds;

	<span class="enscript-keyword">if</span> ((number = OSDynamicCast(OSNumber, props-&gt;getObject(pollingPeriodKey))) != 0)
	{
		fPollingPeriod = number-&gt;unsigned64BitValue();

		<span class="enscript-comment">// Convert to nanoseconds
</span>		fPollingPeriod *= NSEC_PER_SEC;
	}

	<span class="enscript-keyword">if</span> ((number = OSDynamicCast(OSNumber, props-&gt;getObject(speedupDelayKey))) != 0)
	{
		fSpeedupDelay = number-&gt;unsigned64BitValue();

		<span class="enscript-comment">// Convert to nanoseconds
</span>		fSpeedupDelay *= NSEC_PER_SEC;
	}

	<span class="enscript-keyword">if</span> ((number = OSDynamicCast(OSNumber, props-&gt;getObject(slowdownDelayKey))) != 0)
	{
		fSlowdownDelay = number-&gt;unsigned64BitValue();

		<span class="enscript-comment">// Convert to nanoseconds
</span>		fSlowdownDelay *= NSEC_PER_SEC;
	}

	<span class="enscript-keyword">if</span> ((number = OSDynamicCast(OSNumber, props-&gt;getObject(hysteresisTempKey))) != 0)
	{
		fHysteresisTemp = (SInt16)number-&gt;unsigned16BitValue();
	}

	<span class="enscript-keyword">if</span> ((speeds = OSDynamicCast(OSArray, props-&gt;getObject(speedTableKey))) != 0)
	{
		count = speeds-&gt;getCount();
		<span class="enscript-keyword">if</span> (count == kNumFanSpeeds)
		{
			<span class="enscript-keyword">for</span> (index=0; index&lt;count; index++)
			{
				number = OSDynamicCast(OSNumber, speeds-&gt;getObject(index));
				<span class="enscript-keyword">if</span> (number == NULL)
				{
					IOLog(<span class="enscript-string">&quot;AppleFan::setProperties SOMETHING IS VERY WRONG!!!&quot;</span>);
					<span class="enscript-keyword">break</span>;
				}
			
				temperature = (SInt16)number-&gt;unsigned16BitValue();
				fSpeedTable[index] = temperature;
			}
		}
	}
}

<span class="enscript-comment">/*************************************************************************************
	doUpdate() is where we read the CPU temp and look it up in the speed table to
	choose a fan speed.  Also here is the timer callback routine which repeatedly
	calls doUpdate().
*************************************************************************************/</span>

<span class="enscript-comment">/* static */</span>
<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::timerEventOccured</span>( <span class="enscript-type">void</span> *self )
{
	AppleFan * me = OSDynamicCast(AppleFan, (OSMetaClassBase *) self);

	<span class="enscript-keyword">if</span> (me)	me-&gt;doUpdate(false);
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::doUpdate</span>(<span class="enscript-type">bool</span> first)
{
	AbsoluteTime interval;
	UInt8 newSpeed;
	SInt16 cpu_temp;

	DLOG(<span class="enscript-string">&quot;+AppleFan::doUpdate\n&quot;</span>);

	<span class="enscript-comment">// Get temperature data
</span>	<span class="enscript-keyword">if</span> (!getCPUTemp(&amp;cpu_temp))
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::doUpdate ERROR FETCHING CPU TEMP!!!\n&quot;</span>);
		restoreADM1030State(&amp;fSavedRegs);
		terminate();
		<span class="enscript-keyword">return</span>;
	}

	<span class="enscript-comment">// look up the fan speed
</span>	newSpeed = 0;
	<span class="enscript-keyword">while</span> ((cpu_temp &gt;= fSpeedTable[newSpeed]) &amp;&amp; (newSpeed &lt; (kNumFanSpeeds - 1)))
		newSpeed++;

	<span class="enscript-comment">// set fan speed cfg register
</span>	setFanSpeed(newSpeed, cpu_temp, first);

	<span class="enscript-comment">// implement a periodic timer
</span>	<span class="enscript-keyword">if</span> (first) clock_get_uptime(&amp;fWakeTime);

	nanoseconds_to_absolutetime(fPollingPeriod, &amp;interval);
	ADD_ABSOLUTETIME(&amp;fWakeTime, &amp;interval);
	
	thread_call_enter_delayed( timerCallout, fWakeTime );

	DLOG(<span class="enscript-string">&quot;-AppleFan::doUpdate\n&quot;</span>);
}

<span class="enscript-comment">/*************************************************************************************
	Routines which take a fan speed as input and program the ADM1030 to run at the
	desired speed.  Some nasty tricks are in this code, but it is pretty well
	encapsulated and explained in comments...
	
	The routines are setFanSpeed and setADM1030SpeedMagically (lots of comments in
	the latter for obvious reasons...)
*************************************************************************************/</span>
<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::setFanSpeed</span>(UInt8 speed, SInt16 cpu_temp, <span class="enscript-type">bool</span> first)
{
	UInt8 desiredSpeed;
	SInt16 rmt_temp;
	AbsoluteTime ticksPassed;
	UInt64 nsecPassed;

	<span class="enscript-keyword">if</span> (!getRemoteTemp(&amp;rmt_temp))
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::setFanSpeed FATAL ERROR FETCHING REMOTE CHANNEL TEMP!!!\n&quot;</span>);
		restoreADM1030State(&amp;fSavedRegs);
		terminate();
		<span class="enscript-keyword">return</span>;
	}

	<span class="enscript-keyword">if</span> (first)
	{
		<span class="enscript-comment">// If this is the first run, don't apply any of the hysteresis mechanisms,
</span>		<span class="enscript-comment">// just program the chip with the speed that was produced from the table
</span>		<span class="enscript-comment">// lookup
</span>		DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed initial speed is %u\n&quot;</span>, speed);
		setADM1030SpeedMagically(speed, rmt_temp);
		clock_get_uptime(&amp;fLastTransition);
	}
	<span class="enscript-keyword">else</span>
	{
		<span class="enscript-keyword">if</span> (speed == fLastFanSpeed)
		{
			<span class="enscript-keyword">if</span> (rmt_temp != fLastRmtTemp)
			{
				<span class="enscript-comment">// need to update the remote temp limit register
</span>				DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed environmental update\n&quot;</span>);
				setADM1030SpeedMagically(speed, rmt_temp);
				<span class="enscript-keyword">return</span>;
			}

			DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed no update needed\n&quot;</span>);
		}
		<span class="enscript-keyword">else</span> 
		{
			<span class="enscript-comment">// calculate nanoseconds since last speed change
</span>			clock_get_uptime(&amp;ticksPassed);
			SUB_ABSOLUTETIME(&amp;ticksPassed, &amp;fLastTransition);
			absolutetime_to_nanoseconds(ticksPassed, &amp;nsecPassed);
			
			<span class="enscript-keyword">if</span> (speed &lt; fLastFanSpeed)
			{
				<span class="enscript-comment">// Hysteresis mechanism - don't turn off the fan unless we've reached
</span>				<span class="enscript-comment">// the hysteresis temp
</span>				<span class="enscript-keyword">if</span> (speed == kDutyCycleOff &amp;&amp; fLastFanSpeed == kDutyCycle07)
				{
					DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed hysteresis check cpu_temp 0x%04x fHysteresisTemp %04x\n&quot;</span>,
							cpu_temp, fHysteresisTemp);

					<span class="enscript-keyword">if</span> (cpu_temp &gt; fHysteresisTemp)
					{
						DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed hysteresis active\n&quot;</span>);

						<span class="enscript-comment">// do an environmental update if needed
</span>						<span class="enscript-keyword">if</span> (rmt_temp != fLastRmtTemp)
							setADM1030SpeedMagically(fLastFanSpeed, rmt_temp);

						<span class="enscript-keyword">return</span>;
					}
				}

				DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed downward check nsecPassed 0x%llX fSlowdownDelay 0x%llX\n&quot;</span>,
						nsecPassed, fSlowdownDelay);

				<span class="enscript-comment">// apply downward delay
</span>				<span class="enscript-keyword">if</span> (nsecPassed &gt; fSlowdownDelay)
				{
					desiredSpeed = fLastFanSpeed - 1;
					DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed slowdown to %u\n&quot;</span>, desiredSpeed);
					setADM1030SpeedMagically(desiredSpeed, rmt_temp);
					clock_get_uptime(&amp;fLastTransition);
				}
				<span class="enscript-keyword">else</span>
				{
					DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed slowdown delay active\n&quot;</span>);

					<span class="enscript-comment">// do an environmental update if needed
</span>					<span class="enscript-keyword">if</span> (rmt_temp != fLastRmtTemp)
						setADM1030SpeedMagically(fLastFanSpeed, rmt_temp);
				}
			}
			<span class="enscript-keyword">else</span> <span class="enscript-keyword">if</span> (speed &gt; fLastFanSpeed)
			{
				DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed upward check nsecPassed 0x%llX fSpeedupDelay 0x%llX\n&quot;</span>,
						nsecPassed, fSpeedupDelay);

				<span class="enscript-comment">// apply upward hysteresis
</span>				<span class="enscript-keyword">if</span> (nsecPassed &gt; fSpeedupDelay)
				{
					desiredSpeed = fLastFanSpeed + 1;
					DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed speedup to %u\n&quot;</span>, desiredSpeed);
					setADM1030SpeedMagically(desiredSpeed, rmt_temp);
					clock_get_uptime(&amp;fLastTransition);
				}
				<span class="enscript-keyword">else</span>
				{
					DLOG(<span class="enscript-string">&quot;@AppleFan::setFanSpeed speedup delay active\n&quot;</span>);

					<span class="enscript-comment">// do an environmental update if needed
</span>					<span class="enscript-keyword">if</span> (rmt_temp != fLastRmtTemp)
						setADM1030SpeedMagically(fLastFanSpeed, rmt_temp);
				}
			}
			<span class="enscript-keyword">else</span> { <span class="enscript-comment">/* not reached */</span> }
		}
	}
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::setADM1030SpeedMagically</span>(UInt8 desiredSpeed, SInt16 rmt_temp)
{
	UInt8 TminTrange, speed;

	<span class="enscript-comment">// shift rmt_temp(14:10) into TminTrange(7:3)
</span>	TminTrange = (UInt8)(rmt_temp &gt;&gt; 7);
	TminTrange &amp;= ~kTrangeMask;	<span class="enscript-comment">// clear out the 3 LSBs
</span>	TminTrange |= 0x7;			<span class="enscript-comment">// T_range = highest possible
</span>
	<span class="enscript-comment">/*
	 * setFanSpeed() calculates a speed between 0x0 and 0xF and passes it
	 * into this routine (in the variable named &quot;speed&quot;).  setFanSpeed
	 * is responsible for setting the remote T_min/T_range and the speed
	 * config register to make the PWM match the requested speed.
	 *
	 * If we want the PWM to be completely inactive, we have to set
	 * Tmin ABOVE the current remote temp.  We set the speed config
	 * register to zero in this case.
	 *
	 * For other PWM values, we program Tmin to a value just below the
	 * current remote temp.  This will instruct the ADM1030 to operate
	 * the PWM at a speed just above whatever speed is programmed into
	 * the speed config register (which, in automatic control mode,
	 * sets the minimum speed at which the fan runs when the current
	 * remote temp exceeds Tmin).  Then, we program the speed config
	 * register with speed - 1 ; that is, one less than the value that
	 * was passed in by doUpdate().  It may seem less than intuitive
	 * to program the speed config register with 0 when we want speed
	 * 1, just remember that the difference is made by Tmin -- is the
	 * fan operating below the linear range (PWM completely inactive),
	 * or just inside the linear range (PWM active)?
	 */</span>

	<span class="enscript-comment">// The first &quot;if&quot; clause handles two cases:
</span>	<span class="enscript-comment">//
</span>	<span class="enscript-comment">// 1.  The fan is already set below the linear range.  This is
</span>	<span class="enscript-comment">//     when speed=0 and fLastFanSpeed=0.  We program Tmin 8 degrees
</span>	<span class="enscript-comment">//     above rmt_temp, and preserve the speed config reg at 0.
</span>	<span class="enscript-comment">//
</span>	<span class="enscript-comment">// 2.  The fan is currently in the linear range, but we are about
</span>	<span class="enscript-comment">//     to shift below the linear range and shut off PWM entirely.
</span>	<span class="enscript-comment">//     This is denoted by speed=0 and fLastFanSpeed=1.
</span>	<span class="enscript-keyword">if</span> (desiredSpeed == 0)
	{
		<span class="enscript-comment">// Transition from 1 to 0 takes fan out of linear range
</span>		TminTrange += 0x10;		<span class="enscript-comment">// raise Tmin above current rmt_temp
</span>		speed = desiredSpeed;
		fLastFanSpeed = desiredSpeed;
	}
	<span class="enscript-comment">// Put the hw control loop into the linear range
</span>	<span class="enscript-keyword">else</span>
	{
		TminTrange -= 0x08;
		speed = desiredSpeed - 1;	
		fLastFanSpeed = desiredSpeed;
	}

	<span class="enscript-comment">// Recored the rmt_temp at the time of this update
</span>	fLastRmtTemp = rmt_temp;

#<span class="enscript-reference">ifdef</span> <span class="enscript-variable-name">APPLEFAN_DEBUG</span>
	<span class="enscript-type">char</span> debug[16];
	temp2str(rmt_temp, debug);
#<span class="enscript-reference">endif</span>

	DLOG(<span class="enscript-string">&quot;@AppleFan::setADM1030SpeedMagically speed=%u rmt_temp=%x (%sC) TminTrange=%x\n&quot;</span>,
			speed, rmt_temp, debug, TminTrange);

	<span class="enscript-keyword">if</span> (!doI2COpen())
	{
		IOLog(<span class="enscript-string">&quot;AppleFan failed to open bus for setting fan speed\n&quot;</span>);
		<span class="enscript-keyword">return</span>;
	}

	<span class="enscript-keyword">if</span> (!doI2CWrite(kRmtTminTrange, &amp;TminTrange, 1))
	{
		doI2CClose();
		IOLog(<span class="enscript-string">&quot;AppleFan failed to write to T_min/T_range register!\n&quot;</span>);
		<span class="enscript-keyword">return</span>;
	}		

	<span class="enscript-keyword">if</span> (!doI2CWrite(kSpeedCfgReg, &amp;speed, 1))
	{
		doI2CClose();
		IOLog(<span class="enscript-string">&quot;AppleFan failed to write to fan speed register\n&quot;</span>);
		<span class="enscript-keyword">return</span>;
	}

	doI2CClose();
}

<span class="enscript-comment">/*************************************************************************************
	Read temperature registers -- cpu temp (via AppleCPUThermo) and remote channel
	
*************************************************************************************/</span>

<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::getRemoteTemp</span>(SInt16 *rmt_temp)
{
	SInt16	scratch;
	UInt8	ext_res_reg, remote_reg;
	<span class="enscript-type">bool</span>	failed;

	<span class="enscript-comment">// sanity check
</span>	<span class="enscript-keyword">if</span> (rmt_temp == NULL)
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::getRemoteTemp bad arguments\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-comment">// get local and remote temperatures from ADM1030
</span>	<span class="enscript-keyword">if</span> (!doI2COpen())
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::getRemoteTemp cannot open I2C!!\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	failed = false;
	<span class="enscript-keyword">if</span> (!doI2CRead(kExtTempReg, &amp;ext_res_reg, 1) ||
	    !doI2CRead(kRemoteTempReg, &amp;remote_reg, 1))
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::getRemoteTemp failed to read temperature reg!\n&quot;</span>);
		failed = true;
	}

	doI2CClose();

	<span class="enscript-keyword">if</span> (failed) <span class="enscript-keyword">return</span>(false);

	scratch = (SInt16)(ext_res_reg &amp; kRemoteExtMask);
	scratch &lt;&lt;= kRemoteExtShift;
	*rmt_temp = (SInt16)(remote_reg &lt;&lt; 8);
	*rmt_temp |= scratch;

	<span class="enscript-keyword">return</span>(true);
}

<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::getCPUTemp</span>(SInt16 *cpu_temp)
{
	<span class="enscript-keyword">if</span> (!cpu_temp || !cpu_thermo)
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::getCPUTemp bad params!!\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-comment">// Get CPU temp
</span>	<span class="enscript-keyword">if</span> (cpu_thermo-&gt;callPlatformFunction(getTempSymbol, false,
			(<span class="enscript-type">void</span> *) cpu_temp, 0, 0, 0) != kIOReturnSuccess)
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::getCPUTemp failed to retreive CPU temp!!\n&quot;</span>);
		<span class="enscript-keyword">return</span>(false);
	}

	<span class="enscript-keyword">return</span>(true);
}

<span class="enscript-comment">/*************************************************************************************
// 3056480:
// When the system is restarted, we have to put the ADM1030 in S/W
// control mode with PWM output set to 0% duty cycle.  This prevents
// any nasty spin-up noises when HWInit/OF re-program the chip into
// (filtered) automatic control mode.  This is a workaround for an
// apparent problem with the ADM1030.
*************************************************************************************/</span>

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::setRestartMode</span>(<span class="enscript-type">void</span>)
{
	UInt8	regval;

#<span class="enscript-reference">ifdef</span> <span class="enscript-variable-name">APPLEFAN_DEBUG</span>
	<span class="enscript-type">bool</span>	success	= false;
#<span class="enscript-reference">endif</span>

	DLOG(<span class="enscript-string">&quot;+AppleFan::setRestartMode\n&quot;</span>);

	<span class="enscript-keyword">if</span> (!doI2COpen())
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::setRestartMode failed to open bus\n&quot;</span>);
		<span class="enscript-keyword">return</span>;
	}

	<span class="enscript-keyword">do</span> {
		<span class="enscript-comment">// Restore sane values to TminTrange registers
</span>		<span class="enscript-comment">// Tmin = 32
</span>		<span class="enscript-comment">// Trange = 10
</span>		regval = 0x41;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kLocTminTrange, &amp;regval, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kRmtTminTrange, &amp;regval, 1)) <span class="enscript-keyword">break</span>;

		<span class="enscript-comment">// Fan Characteristics gets power on defaults
</span>		regval = kSpinUp2000MS		|
		         kPWMFreq31Hz		|
		         kSpeedRange1324;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kFanCharReg, &amp;regval, 1)) <span class="enscript-keyword">break</span>;

		<span class="enscript-comment">// Set the PWM output to 7% duty cycle
</span>		regval = kDutyCycleOff;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kSpeedCfgReg, &amp;regval, 1)) <span class="enscript-keyword">break</span>;

		<span class="enscript-comment">// Disable fan spin-up, disable filter mode
</span>		regval = kSampleRate1_4KHz	|
		         kRampRate1			|
		         kSpinUpDisable;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kFanFilterReg, &amp;regval, 1)) <span class="enscript-keyword">break</span>;

		<span class="enscript-comment">// config reg 2 gets power-on defaults, except disable TACH input
</span>		regval = kPWMOutputEnable	|
		         kLocTempINTEnable	|
		         kRmtTempINTEnable;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kConfigReg2, &amp;regval, 1)) <span class="enscript-keyword">break</span>;

		<span class="enscript-comment">// config reg 1 - use s/w control mode, fan fault.  nothing else.
</span>		regval = kFanFaultEnable;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kConfigReg1, &amp;regval, 1)) <span class="enscript-keyword">break</span>;

#<span class="enscript-reference">ifdef</span> <span class="enscript-variable-name">APPLEFAN_DEBUG</span>
		success = true;
#<span class="enscript-reference">endif</span>
	} <span class="enscript-keyword">while</span> (false);

	doI2CClose();

	DLOG(<span class="enscript-string">&quot;-AppleFan::setRestartMode success = %s\n&quot;</span>, success ? <span class="enscript-string">&quot;TRUE&quot;</span> : <span class="enscript-string">&quot;FALSE&quot;</span>);
}

<span class="enscript-comment">/*************************************************************************************
	Power management-related functions

*************************************************************************************/</span>

<span class="enscript-comment">// Program the ADM1030 in preparation for a power state change
</span>IOReturn <span class="enscript-function-name">AppleFan::powerStateWillChangeTo</span>(IOPMPowerFlags flags,
				<span class="enscript-type">unsigned</span> <span class="enscript-type">long</span> stateNumber, IOService *whatDevice)
{
	<span class="enscript-comment">//DLOG(&quot;@AppleFan powerStateWillChangeTo %lu\n&quot;, stateNumber);
</span>	<span class="enscript-keyword">return</span>(IOPMAckImplied);
}

IOReturn <span class="enscript-function-name">AppleFan::setPowerState</span>(<span class="enscript-type">unsigned</span> <span class="enscript-type">long</span> powerStateOrdinal,
				IOService *whatDevice)
{
	DLOG(<span class="enscript-string">&quot;@AppleFan setPowerState %lu\n&quot;</span>, powerStateOrdinal);

	<span class="enscript-keyword">if</span> (fCurrentPowerState == kPowerOff &amp;&amp; powerStateOrdinal == kPowerOn)
	{
		doWake();
	}
	<span class="enscript-keyword">else</span> <span class="enscript-keyword">if</span> (fCurrentPowerState == kPowerOn &amp;&amp; powerStateOrdinal == kPowerOff)
	{
		doSleep();
	}

	fCurrentPowerState = powerStateOrdinal;

	<span class="enscript-keyword">return</span>(IOPMAckImplied);
}

IOReturn <span class="enscript-function-name">AppleFan::powerStateDidChangeTo</span>(IOPMPowerFlags flags,
				<span class="enscript-type">unsigned</span> <span class="enscript-type">long</span> stateNumber, IOService *whatDevice)
{
	<span class="enscript-comment">//DLOG(&quot;@AppleFan powerStateDidChangeTo %lu\n&quot;, stateNumber);
</span>	<span class="enscript-keyword">return</span>(IOPMAckImplied);
}

IOReturn <span class="enscript-function-name">AppleFan::sPMNotify</span>(<span class="enscript-type">void</span> *target, <span class="enscript-type">void</span> *refCon,
		<span class="enscript-type">long</span> <span class="enscript-type">unsigned</span> <span class="enscript-type">int</span> messageType, IOService *provider,
		<span class="enscript-type">void</span> *messageArg, vm_size_t argSize)
{	
	AppleFan	*self;
	IOService	*svc_target;

	DLOG(<span class="enscript-string">&quot;+AppleFan::sPMNotify\n&quot;</span>);

	svc_target = (IOService *)target;

    <span class="enscript-keyword">if</span> (OSDynamicCast(AppleFan, svc_target) != 0)
    {
        self = (AppleFan *)target;
    }
	<span class="enscript-keyword">else</span>
    {
		IOLog(<span class="enscript-string">&quot;AppleFan::sPMNotify invalid target\n&quot;</span>);
		<span class="enscript-keyword">return</span>(kIOReturnBadArgument);
	}

	<span class="enscript-keyword">switch</span> (messageType)
	{
		<span class="enscript-keyword">case</span> <span class="enscript-reference">kIOMessageSystemWillRestart</span>:
			DLOG(<span class="enscript-string">&quot;@AppleFan::sPMNotify kIOMessageSystemWillRestart\n&quot;</span>);
			self-&gt;doRestart();
			<span class="enscript-keyword">break</span>;

		<span class="enscript-keyword">case</span> <span class="enscript-reference">kIOMessageSystemWillPowerOff</span>:
			DLOG(<span class="enscript-string">&quot;@AppleFan::sPMNotify kIOMessageSystemWillPowerOff\n&quot;</span>);
		<span class="enscript-reference">default</span>:
			<span class="enscript-keyword">break</span>;
	}

	<span class="enscript-keyword">return</span>(kIOReturnSuccess);
}

<span class="enscript-comment">// Transition from kPowerOn to kPowerOff
</span><span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::doSleep</span>(<span class="enscript-type">void</span>)
{
	DLOG(<span class="enscript-string">&quot;+AppleFan::doSleep\n&quot;</span>);

	<span class="enscript-comment">// Cancel any outstanding timer events
</span>	thread_call_cancel( timerCallout );

	<span class="enscript-comment">// Set the fan to speed zero so it doesn't spin up unnecessarily coming out
</span>	<span class="enscript-comment">// of sleep.  Should be good enough to use the last remote temp rather than
</span>	<span class="enscript-comment">// doing extra I2C cycles here.
</span>	<span class="enscript-keyword">if</span> (fLastFanSpeed != kDutyCycleOff)
		setADM1030SpeedMagically( kDutyCycleOff, fLastRmtTemp );

	DLOG(<span class="enscript-string">&quot;-AppleFan::doSleep\n&quot;</span>);
}

<span class="enscript-comment">// Transition from kPowerOff to kPowerOn
</span><span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::doWake</span>(<span class="enscript-type">void</span>)
{
	DLOG(<span class="enscript-string">&quot;+AppleFan::doWake\n&quot;</span>);

	<span class="enscript-comment">// Force an update, this will sync up Tmin with the current
</span>	<span class="enscript-comment">// remote temp reading and restart the timer
</span>	doUpdate(true);

	DLOG(<span class="enscript-string">&quot;-AppleFan::doWake\n&quot;</span>);
}

<span class="enscript-comment">// Handle restart
</span><span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::doRestart</span>(<span class="enscript-type">void</span>)
{
	DLOG(<span class="enscript-string">&quot;+AppleFan::doRestart\n&quot;</span>);

	<span class="enscript-comment">// Disable updates
</span>	thread_call_cancel( timerCallout );

	setRestartMode();

	DLOG(<span class="enscript-string">&quot;-AppleFan::doRestart\n&quot;</span>);
}

<span class="enscript-comment">/*************************************************************************************
	Save and Restore ADM1030 register state
		saveADM1030State
		restoreADM1030State
		
	These functions save and restore only the registers that are modified by this
	driver, NOT THE ENTIRE REGISTER SET.

	restoreADM1030State's final write is to config reg 1 as per the ADM1030 data
	sheet.
*************************************************************************************/</span>

<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::saveADM1030State</span>(adm1030_regs_t *regs)
{
	<span class="enscript-type">bool</span> success = false;

	<span class="enscript-keyword">if</span> (!doI2COpen())
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::saveADM1030State failed to open bus!!\n&quot;</span>);
		<span class="enscript-keyword">return</span> false;
	}

	<span class="enscript-keyword">do</span>
	{
		<span class="enscript-keyword">if</span> (!doI2CRead(kConfigReg1, &amp;regs-&gt;config1, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CRead(kConfigReg2, &amp;regs-&gt;config2, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CRead(kFanCharReg, &amp;regs-&gt;fan_char, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CRead(kSpeedCfgReg, &amp;regs-&gt;speed_cfg, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CRead(kFanFilterReg, &amp;regs-&gt;fan_filter, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CRead(kLocTminTrange, &amp;regs-&gt;loc_tmin_trange, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CRead(kRmtTminTrange, &amp;regs-&gt;rmt_tmin_trange, 1)) <span class="enscript-keyword">break</span>;

		success = true;
	} <span class="enscript-keyword">while</span> (false);

	doI2CClose();

	<span class="enscript-keyword">return</span> success;
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::restoreADM1030State</span>(adm1030_regs_t *regs)
{
	<span class="enscript-type">bool</span> success = false;

	<span class="enscript-keyword">if</span> (!doI2COpen())
	{
		IOLog(<span class="enscript-string">&quot;AppleFan::restoreADM1030State failed to open bus!!\n&quot;</span>);
		<span class="enscript-keyword">return</span>;
	}

	<span class="enscript-keyword">do</span>
	{
		<span class="enscript-keyword">if</span> (!doI2CWrite(kRmtTminTrange, &amp;regs-&gt;rmt_tmin_trange, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kLocTminTrange, &amp;regs-&gt;loc_tmin_trange, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kFanFilterReg, &amp;regs-&gt;fan_filter, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kSpeedCfgReg, &amp;regs-&gt;speed_cfg, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kFanCharReg, &amp;regs-&gt;fan_char, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kConfigReg2, &amp;regs-&gt;config2, 1)) <span class="enscript-keyword">break</span>;
		<span class="enscript-keyword">if</span> (!doI2CWrite(kConfigReg1, &amp;regs-&gt;config1, 1)) <span class="enscript-keyword">break</span>;

		success = true;
	} <span class="enscript-keyword">while</span> (false);

	doI2CClose();
}

<span class="enscript-comment">/*************************************************************************************
	I2C Wrappers -- these functions use fI2CBus, fI2CAddr instance vars, so they
	cannot be called until after AppleFan::start initializes these vars.
*************************************************************************************/</span>

<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::doI2COpen</span>(<span class="enscript-type">void</span>)
{
	DLOG(<span class="enscript-string">&quot;@AppleFan::doI2COpen bus=%02x\n&quot;</span>, fI2CBus);
	<span class="enscript-keyword">return</span>(I2C_iface-&gt;openI2CBus(fI2CBus));
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::doI2CClose</span>(<span class="enscript-type">void</span>)
{
	DLOG(<span class="enscript-string">&quot;@AppleFan::doI2CClose\n&quot;</span>);
	I2C_iface-&gt;closeI2CBus();
}

<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::doI2CRead</span>(UInt8 sub, UInt8 *bytes, UInt16 len)
{
	UInt8 retries;

#<span class="enscript-reference">ifdef</span> <span class="enscript-variable-name">APPLEFAN_DEBUG</span>
	<span class="enscript-type">char</span>	debugStr[128];
	sprintf(debugStr, <span class="enscript-string">&quot;@AppleFan::doI2CRead addr=%02x sub=%02x bytes=%08x len=%04x&quot;</span>,
			fI2CAddr, sub, (<span class="enscript-type">unsigned</span> <span class="enscript-type">int</span>)bytes, len);
#<span class="enscript-reference">endif</span>

	I2C_iface-&gt;setCombinedMode();

	retries = kNumRetries;

	<span class="enscript-keyword">while</span> (!I2C_iface-&gt;readI2CBus(fI2CAddr, sub, bytes, len))
	{
		<span class="enscript-keyword">if</span> (retries &gt; 0)
		{
			IOLog(<span class="enscript-string">&quot;AppleFan::doI2CRead read failed, retrying...\n&quot;</span>);
			retries--;
		}
		<span class="enscript-keyword">else</span>
		{
			IOLog(<span class="enscript-string">&quot;AppleFan::doI2CRead cannot read from I2C!!\n&quot;</span>);
			<span class="enscript-keyword">return</span>(false);
		}
	}

	DLOG(<span class="enscript-string">&quot;%s (first byte %02x)\n&quot;</span>, debugStr, bytes[0]);

	<span class="enscript-keyword">return</span>(true);
}

<span class="enscript-type">bool</span> <span class="enscript-function-name">AppleFan::doI2CWrite</span>(UInt8 sub, UInt8 *bytes, UInt16 len)
{
	UInt8 retries;

	DLOG(<span class="enscript-string">&quot;@AppleFan::doI2CWrite addr=%02x sub=%02x bytes=%08x len=%04x (first byte %02x)\n&quot;</span>,
			fI2CAddr, sub, (<span class="enscript-type">unsigned</span> <span class="enscript-type">int</span>)bytes, len, bytes[0]);

	I2C_iface-&gt;setStandardSubMode();

	retries = kNumRetries;

	<span class="enscript-keyword">while</span> (!I2C_iface-&gt;writeI2CBus(fI2CAddr, sub, bytes, len))
	{
		<span class="enscript-keyword">if</span> (retries &gt; 0)
		{
			IOLog(<span class="enscript-string">&quot;AppleFan::doI2CWrite write failed, retrying...\n&quot;</span>);
			retries--;
		}
		<span class="enscript-keyword">else</span>
		{
			IOLog(<span class="enscript-string">&quot;AppleFan::doI2CWrite cannot write to I2C!!\n&quot;</span>);
			<span class="enscript-keyword">return</span>(false);
		}
	}

	<span class="enscript-keyword">return</span>(true);
}

<span class="enscript-comment">//###################################################################################
</span><span class="enscript-comment">// Routines to publish internal variables to I/O Registry
</span><span class="enscript-comment">// Only enabled for APPLEFAN_DEBUG builds
</span><span class="enscript-comment">//
</span>
#<span class="enscript-reference">ifdef</span> <span class="enscript-variable-name">APPLEFAN_DEBUG</span>

<span class="enscript-comment">// User-land clients can call into this to force an update of the I/O Registry or
</span><span class="enscript-comment">// or set parameters at run-time.
</span>IOReturn <span class="enscript-function-name">AppleFan::setProperties</span>(OSObject *properties)
{
	OSDictionary *props = OSDynamicCast(OSDictionary, properties);
	<span class="enscript-keyword">if</span> (props == NULL) <span class="enscript-keyword">return</span> kIOReturnBadArgument;

	<span class="enscript-keyword">if</span> (props-&gt;getObject(forceUpdateKey) != NULL)
	{
		<span class="enscript-comment">// refresh the I/O registry
</span>		publishSpeedTable();
		publishPollingPeriod();
		publishDelays();
		publishHysteresisTemp();
		publishCurrentSpeed();
		publishCurrentCPUTemp();
		<span class="enscript-keyword">return</span> kIOReturnSuccess;
	}

	<span class="enscript-comment">// The ground is about to move beneath us, so disable the timer while
</span>	<span class="enscript-comment">// we are changing things up
</span>	thread_call_cancel( timerCallout );

	parseDict(props);

	<span class="enscript-comment">// start up the timer, and pass first==true to force a write using the new
</span>	<span class="enscript-comment">// parameters
</span>	doUpdate(true);

	<span class="enscript-keyword">return</span> kIOReturnSuccess;
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::publishSpeedTable</span>(<span class="enscript-type">void</span>)
{
	<span class="enscript-type">int</span> i;
	SInt64 mylonglong;
	OSNumber *entries[kNumFanSpeeds];
	OSArray *entryArray;

	<span class="enscript-comment">// encapsulate each array element into an OSData
</span>	<span class="enscript-keyword">for</span> (i=0; i&lt;kNumFanSpeeds; i++)
	{
		mylonglong = (SInt64)fSpeedTable[i];
		entries[i] = OSNumber::withNumber(mylonglong, <span class="enscript-keyword">sizeof</span>(SInt64) * 8);
	}

	<span class="enscript-comment">// stash the OSData's into an OSArray
</span>	entryArray = OSArray::withObjects((<span class="enscript-type">const</span> OSObject **)entries, kNumFanSpeeds, 0);

	<span class="enscript-comment">// release my reference on the OSData's
</span>	<span class="enscript-keyword">for</span> (i=0; i&lt;kNumFanSpeeds; i++)
		entries[i]-&gt;release();

	<span class="enscript-comment">// set the OSData as the property value
</span>	setProperty(speedTableKey, OSDynamicCast(OSObject, entryArray));

	<span class="enscript-comment">// release my reference to the array
</span>	entryArray-&gt;release();
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::publishPollingPeriod</span>(<span class="enscript-type">void</span>)
{
	OSNumber *period = OSNumber::withNumber(fPollingPeriod / NSEC_PER_SEC,
			<span class="enscript-keyword">sizeof</span>(fPollingPeriod) * 8);

	<span class="enscript-keyword">if</span> (period)
	{
		setProperty(pollingPeriodKey, period);
		period-&gt;release();
	}
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::publishDelays</span>(<span class="enscript-type">void</span>)
{
	OSNumber *speedupDelay = OSNumber::withNumber(fSpeedupDelay / NSEC_PER_SEC,
			<span class="enscript-keyword">sizeof</span>(fSpeedupDelay) * 8);
	OSNumber *slowdownDelay = OSNumber::withNumber(fSlowdownDelay / NSEC_PER_SEC,
			<span class="enscript-keyword">sizeof</span>(fSlowdownDelay) * 8);

	<span class="enscript-keyword">if</span> (speedupDelay)
	{
		setProperty(speedupDelayKey, speedupDelay);
		speedupDelay-&gt;release();
	}

	<span class="enscript-keyword">if</span> (slowdownDelay)
	{
		setProperty(slowdownDelayKey, slowdownDelay);
		slowdownDelay-&gt;release();
	}
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::publishHysteresisTemp</span>(<span class="enscript-type">void</span>)
{
	OSNumber *hysteresisTemp = OSNumber::withNumber(fHysteresisTemp, <span class="enscript-keyword">sizeof</span>(fHysteresisTemp) * 8);

	<span class="enscript-keyword">if</span> (hysteresisTemp)
	{
		setProperty(hysteresisTempKey, hysteresisTemp);
		hysteresisTemp-&gt;release();
	}
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::publishCurrentSpeed</span>(<span class="enscript-type">void</span>)
{
	UInt64 mylonglong = (UInt64)fLastFanSpeed;
	OSNumber *curSpeed = OSNumber::withNumber(mylonglong, <span class="enscript-keyword">sizeof</span>(UInt64) * 8);

	<span class="enscript-keyword">if</span> (curSpeed)
	{
		setProperty(currentSpeedKey, curSpeed);
		curSpeed-&gt;release();
	}
}

<span class="enscript-type">void</span> <span class="enscript-function-name">AppleFan::publishCurrentCPUTemp</span>(<span class="enscript-type">void</span>)
{
	OSNumber *cpuTempNum;
	SInt64 mylonglong;
	SInt16 cpu_temp;

	<span class="enscript-keyword">if</span> (getCPUTemp(&amp;cpu_temp))
	{
		mylonglong = (SInt64)cpu_temp;
		cpuTempNum = OSNumber::withNumber(mylonglong, <span class="enscript-keyword">sizeof</span>(SInt64) * 8);

		<span class="enscript-keyword">if</span> (cpuTempNum)
		{
			setProperty(currentCPUTempKey, cpuTempNum);
			cpuTempNum-&gt;release();
		}
	}
}
#<span class="enscript-reference">endif</span>
<span class="enscript-comment">//
</span><span class="enscript-comment">//###################################################################################
</span></pre>
<hr />
</body></html>