/*
 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 2004 Apple Computer, Inc.  All rights reserved.
 *
 *  File: $Id: PowerMac12_1_SystemFansCtrlLoop.cpp,v 1.3 2005/09/09 18:35:56 mpontil Exp $
 *
 */



#include "IOPlatformPluginSymbols.h"
#include "IOPlatformPlugin.h"
#include "PowerMac12_1_SystemFansCtrlLoop.h"
#include "PowerMac12_1VirtualControl.h"

#define super SMU_Neo2_PIDCtrlLoop
OSDefineMetaClassAndStructors( PowerMac12_1_SystemFansCtrlLoop, SMU_Neo2_PIDCtrlLoop )

SensorValue PowerMac12_1_SystemFansCtrlLoop::getAggregateSensorValue( void )
{
	SensorValue aggValue;

	aggValue = inputSensor->forceAndFetchCurrentValue();
	inputSensor->setCurrentValue( aggValue );

	// If the value is not acceptable use the old value:
	if ( aggValue.sensValue < 0 )
	{
		unsigned int previousIndex;

		if (latestSample == historyLen - 1)
			previousIndex = 0;
		else
			previousIndex = latestSample + 1;

		IOLog("PowerMac12_1_SystemFansCtrlLoop::getAggregateSensorValue using previous value (ci=%d pi=%d cv=%ld pv=%ld\n",
			latestSample, previousIndex, aggValue.sensValue>>16 , historyArray[ previousIndex ].sample.sensValue>>16);

		aggValue = historyArray[ previousIndex ].sample;
	}

	return( aggValue );
}

/*
	IMPORTANT NOTE:
	the following PowerMac12_1_SystemFansCtrlLoop::calculateNewTarget is a cut & paste
	of the SMU_Neo2_PIDCtrlLoop::calculateNewTarget(), no code changed except for:
	
	result = ( SInt32 ) accum + ( SInt32 ) targetValue;
	
	which became 
	
	result = ( SInt32 ) accum + outputControl->getTargetValue();
*/

ControlValue PowerMac12_1_SystemFansCtrlLoop::calculateNewTarget( void ) const
	{
	SInt64										accum;
	SInt64										dProd;
	SInt64										rProd;
	SInt64										pProd;
	ControlValue								newTarget;
	SInt32										dRaw;
	SInt32										rRaw;
	SInt32										result;
	samplePoint*								latest = NULL;

	// If there is an output override, use it.

	if ( overrideActive )
		{
		CTRLLOOP_DLOG( "*** PID *** Override Active\n" );

		newTarget = outputOverride->unsigned32BitValue();

		return( newTarget );
		}

	// Apply the PID algorithm to choose a new control target value.

	if ( ctrlloopState == kIOPCtrlLoopFirstAdjustment )
		{
		result = 0;
		}
	else
		{
		// Calculate the derivative term.

		dRaw = calculateDerivativeTerm().sensValue;

		// Apply the derivative gain.
		// This is 12.20 * 16.16 => 28.36.

		accum = dProd = ( SInt64 ) G_d * ( SInt64 ) dRaw;

		// Calculate the reset term and 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 and apply the proportional gain.
		// This is 12.20 * 16.16 => 28.36.

		latest = sampleAtIndex( 0 );
		pProd = ( SInt64 ) G_p * ( SInt64 ) latest->error.sensValue;
		accum += pProd;

		// Truncate the fractional part.

		accum >>= 36;

		// Direct controlled fans 

		if ( isDirectControlType )
			{
			result = ( SInt32 ) accum;
			}
		else
			{
			//result = ( SInt32 ) accum + ( SInt32 ) targetValue; // BEFORE
			result = ( SInt32 ) accum +  ( SInt32 ) outputControl->getTargetValue(); // AFTER
			}
		}

	newTarget = ( ControlValue )( ( result > 0 ) ? result : 0 );

	newTarget = min( newTarget, maxMaximum );
	newTarget = max( newTarget, minMinimum );

#if 0
#if 1 // #ifdef CTRLLOOP_DEBUG
	{
		const OSString*				tempDesc = OSDynamicCast( OSString, infoDict->getObject( kIOPPluginThermalGenericDescKey ) );

		if ( tempDesc && latest )
		{
		IOLog( "*** TARGET *** "
					"%s"
					" G_p=%08lX"
					" G_d=%08lX"
					" G_r=%08lX"
					" Ttgt=%08lX (%ld)"
					" T_cur=%08lX (%ld)"
					" Pres=%lu Res=%lu"
					" Out=%lu"
					" Omin=%lu"
					" Omax=%lu"
					" T_err=%08lX (%ld)"
					" pProd=%016llX"
					" dRaw=%08lX"
					" dProd=%016llX"
					" rRaw=%08lX"
					" rProd=%016llX"
					" ctrlloopstate=%d\n",
					tempDesc->getCStringNoCopy(),
					G_p,
					G_d,
					G_r,
					inputTarget.sensValue, inputTarget.sensValue >> 16,
					latest->sample.sensValue, latest->sample.sensValue >> 16,
					outputControl->getTargetValue(), result,
					newTarget,
					outputMin,
					outputMax,
					latest->error.sensValue, latest->error.sensValue >> 16,
					pProd,
					dRaw,
					dProd,
					rRaw,
					rProd,
					ctrlloopState );
			}
	}
#endif // CTRLLOOP_DEBUG
#endif

	return( newTarget );
	}


void PowerMac12_1_SystemFansCtrlLoop::sendNewTarget( ControlValue newTarget )
	{
	UInt32						mainNewTarget = newTarget;
	PowerMac12_1VirtualControl *tmpControl = OSDynamicCast( PowerMac12_1VirtualControl, outputControl);
	
	targetValue = newTarget;

	// const OSString*tempDesc; IOLog("%s sendNewTarget( %ld ) - %ld \n", ( tempDesc = OSDynamicCast( OSString, infoDict->getObject( kIOPPluginThermalGenericDescKey ) ) ) != NULL ? tempDesc->getCStringNoCopy() : "Unknown CtrlLoop", newTarget , outputControl->getTargetValue() );
	
	// Apply any hard limits.

	mainNewTarget = min( ( SInt16 ) mainNewTarget, ( SInt16 ) outputMax );
	mainNewTarget = max( ( SInt16 ) mainNewTarget, ( SInt16 ) outputMin );

	tmpControl->setTargetValue( mainNewTarget , this , mainNewTarget);
	ctrlloopState = kIOPCtrlLoopAllRegistered;
	}
	
void PowerMac12_1_SystemFansCtrlLoop::controlRegistered( IOPlatformControl* aControl )
	{
	PowerMac12_1VirtualControl *tmpControl = OSDynamicCast( PowerMac12_1VirtualControl, outputControl);
	
	if  ( ( tmpControl != NULL ) && ( aControl == tmpControl->targetControl() ) )
		super::controlRegistered( outputControl );
	else
		super::controlRegistered( aControl );
	}
