// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32test\realtime\d_latncy.cpp
// 
//

#include "platform.h"

#if defined(__MEIG__)
#include <cl7211.h>
#elif defined(__MAWD__)
#include <windermere.h>
#elif defined(__MISA__)
#include <sa1100.h>
#elif defined(__MCOT__)
#include <cotulla.h>
#elif defined(__MI920__) || defined(__NI1136__)
#include <integratorap.h>
//#define FREE_RUNNING_MODE			// runs the millisecond timer in free running mode
#elif defined(__IS_OMAP1610__)
#include <omap_timer.h>
#include <omap_plat.h>
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
#include <omap_hw.h>
#include <shared_instrtimer.h>
#elif defined(__EPOC32__) && defined(__CPU_X86)
#include <x86.h>
#include <x86pc.h>
#elif defined(__RVEMUBOARD__)
#include <rvemuboard.h>
#elif defined(__NE1_TB__)
#include <upd35001_timer.h>
#endif

#ifdef __CPU_ARM
#include <arm.h>
#endif

#include <kernel/kern_priv.h>		//temporary
#include "d_latncy.h"

_LIT(KLddName,"Latency");
_LIT(KThreadName,"LatencyThreadK");

#if defined(__MEIG__)
const TInt KTickPeriodMs=2;
const TInt KTicksPerMillisecond=512;
#elif defined(__MAWD__)
const TInt KTickPeriodMs=1;
const TInt KTicksPerMillisecond=512;
#elif defined(__MISA__) || defined(__MCOT__)
const TInt KTicksPerMillisecond=3686;
const TInt KOstTicks=3685;	// not quite 1ms, so it goes in and out of phase with ms timer
TUint TriggerTime;
#elif defined(__MI920__) || defined(__NI1136__)
const TInt KTickPeriodMs=1;
#if defined(__MI920__) || defined(__NI1136__)
#ifdef FREE_RUNNING_MODE	
const TInt KTicksPerMillisecond=1500;
#else
const TInt KTicksPerMillisecond=24000;
#endif
#endif
#elif defined(__IS_OMAP1610__)
const TInt KTickPeriodMs=1;
TInt KTicksPerMillisecond = TOmapPlat::GetInputClk()/32000;
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
const TInt KTickPeriodMs=1;					// defined for compatibility but not used (ignored)
const TInt KTicksPerMillisecond = 12000; 	// Hard coded (12Mhz)
#elif defined(__EPOC32__) && defined(__CPU_X86)
const TInt KTickPeriodMs=1;
const TInt KTicksPerMillisecond=1193;
#elif defined(__RVEMUBOARD__)
const TInt KTickPeriodMs=1;
const TInt KTicksPerMillisecond=1000;
#elif defined(__NE1_TB__)
const TInt KTickPeriodMs=1;
const TInt KTicksPerMillisecond=66667;
#endif

#ifdef _DEBUG
const TInt KFudgeFactor=1;
#else
const TInt KFudgeFactor=1;
#endif

class DDeviceLatency : public DLogicalDevice
	{
public:
	DDeviceLatency();
	virtual TInt Install();
	virtual void GetCaps(TDes8& aDes) const;
	virtual TInt Create(DLogicalChannelBase*& aChannel);
	};

class DLatencyPowerHandler : public DPowerHandler
	{
public: // from DPOwerHandler
	void PowerUp();
	void PowerDown(TPowerState);
public:
	DLatencyPowerHandler(DLatency* aChannel);
public:
	DLatency* iChannel;
	};



inline TUint DLatency::Ticks()
	{
#if defined(__MEIG__)
	return KTicksPerMillisecond-(*(volatile TUint*)(KEigerTimer2Data16+KEigerBaseAddress)&0xffff);
#elif defined(__MAWD__)
	return KTicksPerMillisecond-(*(volatile TUint*)(KWindTimer2Value16+KWindBaseAddress)&0xffff);
#elif defined(__MISA__) || defined(__MCOT__)
	return *(volatile TUint*)KHwRwOstOscr-iTriggerTime;
#elif defined(__MI920__) || defined(__NI1136__)
	return KTicksPerMillisecond-(*(volatile TUint*)(KHwCounterTimer2+KHoTimerValue)&0xffff);
#elif defined(__IS_OMAP1610__)
	return KTicksPerMillisecond - *(volatile TUint*)(KHwBaseOSTimer1Reg+KHoOSTimer_READ_TIM);
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
	return (*(volatile TUint*)(iTimerInfo.iAddress + KHoGpTimer_TCRR)) - iTimerLoadValue;
#elif defined(__X86PC__)
	return 1194 - __HwTimer();
#elif defined(__RVEMUBOARD__)
	return KTicksPerMillisecond-(*(volatile TUint*)(KHwCounterTimer1+KHoTimerValue)&0xffff);
#elif defined(__NE1_TB__)
	return NETimer::Timer(0).iTimerCount;	// counts up, reset timer + interrupt on match
#endif
	}

#if !defined(__SMP__)
#if !defined(__EPOC32__) || !defined(__CPU_X86)
extern TUint IntStackPtr();
#endif
#endif

DECLARE_STANDARD_LDD()
	{
	return new DDeviceLatency;
	}

DDeviceLatency::DDeviceLatency()
//
// Constructor
//
	{
	//iParseMask=0;
	//iUnitsMask=0;
	iVersion=TVersion(1,0,1);
	}

TInt DDeviceLatency::Install()
//
// Install the device driver.
//
	{
	TInt r=SetName(&KLddName);
	return r;
	}

void DDeviceLatency::GetCaps(TDes8& aDes) const
//
// Return the Comm capabilities.
//
	{
	}

TInt DDeviceLatency::Create(DLogicalChannelBase*& aChannel)
//
// Create a channel on the device.
//
	{
	aChannel=new DLatency;
	return aChannel?KErrNone:KErrNoMemory;
	}

DLatency::DLatency()
	:	iMsCallBack(MsCallBack,this),
		iMsDfc(MsDfc,this,NULL,1)
//
// Constructor
//
	{
#if !defined(__SMP__)
#if !defined(__EPOC32__) || !defined(__CPU_X86)
	iIntStackTop=(TUint*)IntStackPtr();
#endif
#endif
#if defined(__MISA__) || defined(__MCOT__)
	iTickIncrement=KOstTicks*KFudgeFactor;
#endif
#if defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
	iTimerInfo.iAddress = 0;
#endif
	}

DLatency::~DLatency()
//
// Destructor
//
	{
	iOff = (TUint8)ETrue;
	StopTimer();
	iMsDfc.Cancel();

	if (iRtDfcQ)
		iRtDfcQ->Destroy();

	if (iPowerHandler)
		{
		iPowerHandler->Remove();
		delete iPowerHandler;
		}

	Kern::SafeClose((DObject*&)iClient, NULL);
	}

TInt DLatency::DoCreate(TInt /*aUnit*/, const TDesC8* /*anInfo*/, const TVersion& aVer)
//
// Create the channel from the passed info.
//
	{
	if (!Kern::QueryVersionSupported(TVersion(1,0,1),aVer))
		return KErrNotSupported;

	// create the power handler
	iPowerHandler = new DLatencyPowerHandler(this);
	if (!iPowerHandler)
		return KErrNoMemory;
	iPowerHandler->Add();

	// Allocate a kernel thread to run the DFC 
	TInt r = Kern::DynamicDfcQCreate(iRtDfcQ, KNumPriorities-1,KThreadName);

	if (r != KErrNone)
		return r;
	
#ifdef CPU_AFFINITY_ANY
	NKern::ThreadSetCpuAffinity((NThread*)(iRtDfcQ->iThread), KCpuAffinityAny);			
#endif

	iMsDfc.SetDfcQ(iRtDfcQ);
	iClient=&Kern::CurrentThread();
	iClient->Open();
	Kern::SetThreadPriority(KNumPriorities-2);
	return KErrNone;
	}

#if defined(__MISA__) 
// For SA1100/SA1110 use a separate timer on a FIQ interrupt (OST match 0)
TInt DLatency::StartTimer()
	{
	TInt r=Interrupt::Bind(KIntIdOstMatchGeneral,MsCallBack,this);
	if (r==KErrNone)
		{
		TSa1100::ModifyIntLevels(0,KHtIntsOstMatchGeneral);	// route new timer interrupt to FIQ
		TSa1100::SetOstMatchEOI(KHwOstMatchGeneral);
		TUint oscr=TSa1100::OstData();
		iTriggerTime=oscr+KOstTicks*KFudgeFactor;
		TSa1100::SetOstMatch(KHwOstMatchGeneral,iTriggerTime);
		TSa1100::EnableOstInterrupt(KHwOstMatchGeneral);
		Interrupt::Enable(KIntIdOstMatchGeneral);
		}
	return r;
	}
#elif defined(__MCOT__)
// For Cotulla use a separate timer on a FIQ interrupt (OST match 0)
TInt DLatency::StartTimer()
	{
	TInt r=Interrupt::Bind(KIntIdOstMatchGeneral,MsCallBack,this);
	if (r==KErrNone)
		{
		TCotulla::ModifyIntLevels(0,KHtIntsOstMatchGeneral);	// route new timer interrupt to FIQ
		TCotulla::SetOstMatchEOI(KHwOstMatchGeneral);
		TUint oscr=TCotulla::OstData();
		iTriggerTime=oscr+KOstTicks*KFudgeFactor;
		TCotulla::SetOstMatch(iTriggerTime,KHwOstMatchGeneral);
		TCotulla::EnableOstInterrupt(KHwOstMatchGeneral);
		Interrupt::Enable(KIntIdOstMatchGeneral);
		}
	return r;
	}
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
TInt DLatency::StartTimer()
/* 
 *  For OMAP2420 initialise a new timer to generate an interrupt every 1ms
 */
	{
	__ASSERT_ALWAYS(!iTimerInfo.iAddress, Kern::Fault("D_Latncy: timer allocated twice.",
													 iTimerInfo.iAddress));

	// Get an available Timer from the system
    TInt r = OmapTimerMgr::GetTimer(iGPTimerId, iTimerInfo);
    if (KErrNone != r)
    	{
    	return r;
    	}
    	
    // Configure the timer
    r = ConfigureTimer();
    if (KErrNone != r)
    	{
    	DisableTimer();
    	return r;
    	}
    
    // Bind to timer interrupt
    r = Interrupt::Bind(iTimerInfo.iInterruptId, MsCallBack, this);
    if (KErrNone != r)
        {
        DisableTimer();
        return r;
        }
              
    // Unmask timer IT in interrupt controller
    r = Interrupt::Enable(iTimerInfo.iInterruptId);
    if (KErrNone != r)
		{
		Interrupt::Unbind(iTimerInfo.iInterruptId);
		DisableTimer();
    	return r;
    	}
    
    // Start timer
    TOmap::ModifyRegister32(iTimerInfo.iAddress + KHoGpTimer_TCLR, KClear32,
                        KHtGpTimer_TCLR_St);
        
    return KErrNone;
	}

void DLatency::DisableTimer()
/*
 *	Disable the interface and functional clock and mark the timer as available 
 */
	{
	  // Stop timer
    TOmap::ModifyRegister32(iTimerInfo.iAddress + KHoGpTimer_TCLR,
                        KHtGpTimer_TCLR_St, KClear32);

#if defined(__WAKEUP_3430__)
    // Disable Timer clocks using Timer framework instead of using TPRcm direct calls for 3430
    TInt r = OmapTimerMgr::DisableClocks(iGPTimerId);
    if (r != KErrNone)
        __ASSERT_ALWAYS(r, Kern::Fault("Timer clocks disable failed", 0)) ;
#else
	// Disable timer interface clock in PRCM
	TPrcm::InterfaceClkCtrl(iTimerInfo.iPrcmDeviceId, EFalse);
	
	// Disable timer functional clock in PRCM
	TPrcm::FunctionalClkCtrl(iTimerInfo.iPrcmDeviceId, EFalse);
#endif

	// Release the timer
	OmapTimerMgr::ReleaseTimer(iGPTimerId);
	
	iTimerInfo.iAddress = 0;
	}


TInt DLatency::ConfigureTimer()
/*
 *	This method will configure a timer to:
 *		-	run at the system clock (12Mhz)
 *		-	no prescaler (disable TCLR[PRE])
 *		-   autoreload and overflow interrupt enabled (TLDR will contain a
 *			value to generate an interrupt every 1000microsec)
 */
	{

#if defined(__WAKEUP_3430__)
	// Enable Timer clocks using timer framework instead of TPrcm direct calls for 3430
    TInt r = OmapTimerMgr::EnableClocks(iGPTimerId);
    if (r != KErrNone)
        __ASSERT_ALWAYS(r, Kern::Fault("Timer Clocks enable failed", 0)) ;

	// Select the input clock to be system clock 
    r = OmapTimerMgr::SetTimerClkSrc(iGPTimerId, ESysClk);
#else
	// Enable timer interface clock in PRCM  
	TPrcm::InterfaceClkCtrl(iTimerInfo.iPrcmDeviceId, ETrue, ETrue);
	// Enable timer functional clock in PRCM
	TPrcm::FunctionalClkCtrl(iTimerInfo.iPrcmDeviceId, ETrue, ETrue);

	// Select the input clock to be system clock 
    TInt r = OmapTimerMgr::SetTimerClkSrc(iGPTimerId, ESysClk);
#endif

    if (KErrNone != r)	
    	return r;

    // Timer OCP configuration: - software reset
    TOmap::SetRegister32( iTimerInfo.iAddress + KHoGpTimerTIOCP_CFG,
                          KHtGpTimer_TIOCP_CFG_SoftReset);

    // Wait for reset to be complete
    TUint16 timeOut = 1000;
    while ( !(TOmap::Register32(iTimerInfo.iAddress + KHoGpTimer_TISTAT) & 
    			KHtGpTimer_TISTAT_ResetComplete)
    			&& --timeOut);
    
   // Check if the timer has been reset or we hit the timeout
   __ASSERT_ALWAYS((TOmap::Register32(iTimerInfo.iAddress + KHoGpTimer_TISTAT) & 
    			KHtGpTimer_TISTAT_ResetComplete), Kern::Fault("D_Latncy: failed to reset timer.",
													 iGPTimerId));
	
    // Set PRE to be 0, PTV value is ignored, AutoReload is enabled
    TOmap::SetRegister32(iTimerInfo.iAddress + KHoGpTimer_TCLR, KHtGpTimer_TCLR_AR );

	//PTV argument is 0 because of TCLR[PRE] = 0 (prescaling disabled)
	TInt timerPTV = 0;
	
	// Calculate clock frequence from the ticks per ms
	TInt timerClkSrcFreq = KTicksPerMillisecond * 1000;
    
    iTimerLoadValue = OmapTimerMgr::TimerLoadValue(/*microsecs*/1000, timerClkSrcFreq, timerPTV);                          

	// First, load value in TCRR and TLDR registers
    TOmap::SetRegister32(iTimerInfo.iAddress + KHoGpTimer_TCRR, iTimerLoadValue);
    TOmap::SetRegister32(iTimerInfo.iAddress + KHoGpTimer_TLDR, iTimerLoadValue);

    // Enable overflow interrupt
    TOmap::SetRegister32(iTimerInfo.iAddress + KHoGpTimer_TIER,
                         KHtGpTimer_TIER_OverFlow);

    return KErrNone;
	}
#else
TInt DLatency::StartTimer()
	{
	iMsCallBack.OneShot(KTickPeriodMs*KFudgeFactor);
	return KErrNone;
	}
#endif

#if defined(__MISA__) 
// For SA1100/SA1110 use a separate timer on a FIQ interrupt (OST match 0)
void DLatency::StopTimer()
	{
	TSa1100::ModifyIntLevels(KHtIntsOstMatchGeneral,0);
	TSa1100::DisableOstInterrupt(KHwOstMatchGeneral);
	Interrupt::Disable(KIntIdOstMatchGeneral);
	Interrupt::Unbind(KIntIdOstMatchGeneral);
	TSa1100::SetOstMatchEOI(KHwOstMatchGeneral);
	}
#elif defined(__MCOT__)
// For Cotulla use a separate timer on a FIQ interrupt (OST match 0)
void DLatency::StopTimer()
	{
	TCotulla::ModifyIntLevels(KHtIntsOstMatchGeneral,0);
	TCotulla::DisableOstInterrupt(KHwOstMatchGeneral);
	Interrupt::Disable(KIntIdOstMatchGeneral);
	Interrupt::Unbind(KIntIdOstMatchGeneral);
	TCotulla::SetOstMatchEOI(KHwOstMatchGeneral);
	}
#elif defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
void DLatency::StopTimer()
	{
	Interrupt::Disable(iTimerInfo.iInterruptId);
	Interrupt::Unbind(iTimerInfo.iInterruptId);
	DisableTimer();
	}
#else
void DLatency::StopTimer()
	{
	iMsCallBack.Cancel();
	}
#endif

TInt DLatency::Request(TInt aFunction, TAny* a1, TAny* a2)
//
// Client requests
//
	{
	// Kern::Printf("DLatency::Request() 0x%x)\n", aFunction);
	TInt r=KErrNone;
	switch (aFunction)
		{
		case RLatency::EControlStart:
			iStarted = (TUint8)ETrue;
			StartTimer();
			break;
		case RLatency::EControlTicksPerMs:
			r=KTicksPerMillisecond;
			break;
		case RLatency::EControlGetResults:
			iResults.iUserThreadTicks = Ticks();
			kumemput32(a1, &iResults, sizeof(SLatencyResults));
			break;
		default:
			r = KErrNotSupported;
			break;
		}
	return(r);
	}

#ifdef __CAPTURE_EXTRAS
extern void CaptureExtras(SLatencyResults&);
#endif

#if !defined(__MISA__) && !defined(__MCOT__)
void DLatency::MsCallBack(TAny* aPtr)
	{
	DLatency* pL = (DLatency*)aPtr;
#if defined(__IS_OMAP2420__) || defined(__WAKEUP_3430__)
	pL->iResults.iIntTicks = pL->Ticks();
	TOmap::SetRegister32(pL->iTimerInfo.iAddress + KHoGpTimer_TISR, KHtGpTimer_TISR_OverFlow);
#else
	pL->iResults.iIntTicks = Ticks();
#endif
#ifdef __CAPTURE_EXTRAS
	CaptureExtras(pL->iResults);
#endif
#if defined(__EPOC32__) && defined(__CPU_X86)
	pL->iResults.iIntRetAddr = X86::IrqReturnAddress();
#elif defined(__CPU_ARM) && defined(__SMP__)
	pL->iResults.iIntRetAddr = Arm::IrqReturnAddress();
#else
	pL->iResults.iIntRetAddr=(pL->iIntStackTop)[-1];
#endif
	if (!pL->iOff)
		{
		pL->iMsCallBack.Again(KTickPeriodMs*KFudgeFactor);
		pL->iMsDfc.Add();
		}
	}
#endif

void DLatency::MsDfc(TAny* aPtr)
	{
	DLatency* pL = (DLatency*)aPtr;
	pL->iResults.iKernThreadTicks=pL->Ticks();
	NKern::ThreadRequestSignal(&pL->iClient->iNThread);
	}

DLatencyPowerHandler::DLatencyPowerHandler(DLatency* aChannel)
	:	DPowerHandler(KLddName), 
		iChannel(aChannel)
	{
	}

void DLatencyPowerHandler::PowerUp()
	{
	iChannel->iOff = (TUint8)EFalse;
	if (iChannel->iStarted)
		iChannel->StartTimer();
	PowerUpDone();
	}

void DLatencyPowerHandler::PowerDown(TPowerState)
	{
	iChannel->iOff = (TUint8)ETrue;
	iChannel->StopTimer();
	PowerDownDone();
	}



