omap3530/shared/tps65950/tps65950.cpp
changeset 0 6663340f3fc9
child 51 254b9435d75e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omap3530/shared/tps65950/tps65950.cpp	Thu Oct 15 12:59:54 2009 +0100
@@ -0,0 +1,760 @@
+// Copyright (c) 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:
+// \omap3530\omap3530_assp\shared\tps65950\tps65950.cpp
+// Access driver for TPS65950
+// This file is part of the Beagle Base port
+//
+
+#include <e32cmn.h>
+#include <kernel.h>
+#include <nk_priv.h>
+//#include <e32atomics.h>
+#include <omap3530_i2c.h>
+#include <omap3530_i2creg.h>
+#include "tps65950.h"
+
+
+GLREF_C TInt InitInterrupts();
+
+
+const TUint KGroupCount = 5;
+
+// One handle per group on TPS65950
+static I2c::THandle	I2cHandle[ KGroupCount ];
+
+// One DCB per group
+static I2c::TConfigPb TheDcb[ KGroupCount ];
+
+// I2C transfer object
+enum TPhase
+	{
+	EAddressPb,
+	EDataPb
+	};
+static I2c::TTransferPb TheTransferPb[2];
+
+// Group index to Group number
+static const TUint8 KGroupIndexToGroupNumber[ KGroupCount ] =
+	{ 0x12, 0x48, 0x49, 0x4a, 0x4b };
+
+// Queue of requests
+static SDblQue TheQueue;
+
+// Current state
+enum TState
+	{
+	EIdle,
+	EPending,
+	EReading,
+	EWriting,
+	EUnprotectPhase0
+	};
+static TState CurrentState;
+TPS65950::TReq* CurrentPhaseReq;
+TPS65950::TReq* PreviousPhaseReq;
+
+LOCAL_D TUint8 IsInitialized;	// auto-cleared to EFalse
+
+const TUint8	KUnprotectPhase0Data	= 0xCE;
+const TUint8	KUnprotectPhase1Data	= 0xEC;
+
+static const TUint8 KUnprotectData[4] =
+	{
+	TPS65950::Register::PROTECT_KEY bitand TPS65950::Register::KRegisterMask, KUnprotectPhase0Data,
+	TPS65950::Register::PROTECT_KEY bitand TPS65950::Register::KRegisterMask, KUnprotectPhase1Data,
+	};
+
+static const TUint8 KProtectData[2] =
+	{
+	TPS65950::Register::PROTECT_KEY bitand TPS65950::Register::KRegisterMask, 0
+	};
+
+static TUint8 TempWriteBuf[2];
+
+// Spinlock to protect queue when adding or removing items
+//static TSpinLock QueueLock(TSpinLock::EOrderGenericIrqLow1+1);
+static TSpinLock QueueLock();
+
+GLDEF_D TDfcQue*	TheDfcQue;
+
+const TInt KDfcQuePriority	= 27;
+_LIT( KDriverNameDes, "tps65950" );
+
+TInt TheProtectionUsageCount = 0;
+
+
+
+LOCAL_C void InternalPanic( TInt aLine )
+	{
+	Kern::Fault( "tps65950", aLine );
+	}
+
+LOCAL_C void PanicClient( TPS65950::TPanic aPanic )
+	{
+	Kern::PanicCurrentThread( KDriverNameDes, aPanic );
+	}
+
+namespace TPS65950
+{
+void CompletionDfcFunction( TAny* aParam );
+void SyncDfcFunction( TAny* aParam );
+void DummyDfcFunction( TAny* aParam );
+static TDfc CompletionDfc( CompletionDfcFunction, NULL, 1 );
+static TDfc DummyDfc( CompletionDfcFunction, NULL, 1 );
+
+FORCE_INLINE TReq& ReqFromLink( SDblQueLink* aLink )
+	{
+	return *_LOFF( aLink, TReq, iLink );
+	}
+
+inline TBool AtomicSetPendingWasIdle()
+	{
+	// atomic if (CurrentState == idleState) {CurrentState=EPending, returns TRUE} else {idleState=CurrentState, CurrentState unchanged, return FALSE}
+	TState idleState = EIdle;	// required for atomic comparison
+	//TBool wasIdle = __e32_atomic_cas_ord32( (TUint32*)&CurrentState, (TUint32*)&idleState, EPending );
+	//return wasIdle;
+	
+	if (CurrentState == idleState)
+		{
+		CurrentState = EPending;
+		return ETrue;
+		}
+	else
+		{
+		idleState = CurrentState;
+		return EFalse;
+		}
+	
+	}
+
+
+void StartRead( TReq& aReq )
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "+TPS65950:StartRead(@%x) [%x:%x]",
+												&aReq,
+												KGroupIndexToGroupNumber[ aReq.iRegister >> Register::KGroupShift ],
+												aReq.iRegister bitand Register::KRegisterMask ) );
+
+	//__e32_atomic_store_ord32( &CurrentState, EReading );
+	CurrentState = EReading;
+	
+	TheTransferPb[ EAddressPb ].iData = (TUint8*)&aReq.iRegister;	// low byte is register address
+	TheTransferPb[ EDataPb ].iType = I2c::TTransferPb::ERead;
+	TheTransferPb[ EDataPb ].iData = &aReq.iReadValue;
+	__DEBUG_ONLY( aReq.iReadValue = 0xEE );
+	TheTransferPb[ EDataPb ].iLength = 1;
+	TheTransferPb[ EAddressPb ].iResult = KErrNone;
+	TheTransferPb[ EDataPb ].iResult = KErrNone;
+
+	TUint groupIndex = aReq.iRegister >> Register::KGroupShift;
+	I2c::TransferA( I2cHandle[ groupIndex ], TheTransferPb[ EAddressPb ] );
+
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "-TPS65950:StartRead(@%x)", &aReq ) );
+	}
+
+void StartWrite( TReq& aReq )
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "+TPS65950:StartWrite(@%x) [%x:%x]<-%x",
+												&aReq,
+												KGroupIndexToGroupNumber[ aReq.iRegister >> Register::KGroupShift ],
+												aReq.iRegister bitand Register::KRegisterMask,
+												aReq.iWriteValue ) );
+
+	//__e32_atomic_store_ord32( &CurrentState, EWriting );
+	CurrentState = EWriting;
+	
+	TempWriteBuf[0] = aReq.iRegister bitand Register::KRegisterMask;
+	TempWriteBuf[1] = aReq.iWriteValue;
+	TheTransferPb[ EDataPb ].iType = I2c::TTransferPb::EWrite;
+	TheTransferPb[ EDataPb ].iData = &TempWriteBuf[0];
+	TheTransferPb[ EDataPb ].iLength = 2;
+	TheTransferPb[ EDataPb ].iResult = KErrNone;
+
+	TUint groupIndex = aReq.iRegister >> Register::KGroupShift;
+	I2c::TransferA( I2cHandle[ groupIndex ], TheTransferPb[ EDataPb ] );
+
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "-TPS65950:StartWrite(@%x)", &aReq ) );
+	}
+
+void StartUnprotectPhase0( TReq& aReq )
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "+TPS65950:StartUnprotectPhase0(@%x)", &aReq ) );
+
+	//__e32_atomic_store_ord32( &CurrentState, EUnprotectPhase0 );
+	CurrentState = EUnprotectPhase0;
+	
+	TheTransferPb[ EDataPb ].iType = I2c::TTransferPb::EWrite;
+	TheTransferPb[ EDataPb ].iData = &KUnprotectData[0];
+	TheTransferPb[ EDataPb ].iLength = 2;
+	TheTransferPb[ EDataPb ].iResult = KErrNone;
+
+	const TUint groupIndex = Register::PROTECT_KEY >> Register::KGroupShift;
+	I2c::TransferA( I2cHandle[ groupIndex ], TheTransferPb[ EDataPb ] );
+
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "-TPS65950:StartUnprotectPhase0(@%x)", &aReq ) );
+	}
+
+void StartUnprotectPhase1( TReq& aReq )
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "+TPS65950:StartUnprotectPhase1(@%x)", &aReq ) );
+
+	// Set state to writing so that it will complete as a normal write
+	//__e32_atomic_store_ord32( &CurrentState, EWriting );
+	CurrentState = EWriting;
+	
+	TheTransferPb[ EDataPb ].iData = &KUnprotectData[2];
+	TheTransferPb[ EDataPb ].iResult = KErrNone;
+
+	const TUint groupIndex = Register::PROTECT_KEY >> Register::KGroupShift;
+	I2c::TransferA( I2cHandle[ groupIndex ], TheTransferPb[ EDataPb ] );
+
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "-TPS65950:StartUnprotectPhase1(@%x)", &aReq ) );
+	}
+
+void StartProtect( TReq& aReq )
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "+TPS65950:StartProtect(@%x)", &aReq ) );
+
+	//__e32_atomic_store_ord32( &CurrentState, EWriting );
+	CurrentState = EWriting;
+	
+	TheTransferPb[ EDataPb ].iType = I2c::TTransferPb::EWrite;
+	TheTransferPb[ EDataPb ].iData = &KProtectData[0];
+	TheTransferPb[ EDataPb ].iLength = 2;
+	TheTransferPb[ EDataPb ].iResult = KErrNone;
+
+	const TUint groupIndex = Register::PROTECT_KEY >> Register::KGroupShift;
+	I2c::TransferA( I2cHandle[ groupIndex ], TheTransferPb[ EDataPb ] );
+
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "-TPS65950:StartWrite(@%x)", &aReq ) );
+	}
+
+
+void StartRequest()
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "+TPS65950:StartRequest(%d)", CurrentState ) );
+	__ASSERT_DEBUG( EPending == CurrentState, InternalPanic( __LINE__ ) );
+
+	// We don't need to take lock here because we're currently idle so it's not possible
+	// for there to be a change in which item is queue head. The link pointers of the
+	// head item could change if another thread is queueing a new request, but that doesn't
+	// affect any of the fields we care about here
+	__ASSERT_DEBUG( !TheQueue.IsEmpty(), InternalPanic( __LINE__ ) );
+	
+	if( !CurrentPhaseReq )
+		{
+		PreviousPhaseReq = NULL;
+		CurrentPhaseReq = &ReqFromLink( TheQueue.First() );
+		}
+
+	FOREVER
+		{
+		if( !CurrentPhaseReq )
+			{
+			__ASSERT_DEBUG( PreviousPhaseReq, InternalPanic( __LINE__ ) );
+
+			// we didn't find any phases to execute, so complete request
+			// by faking a write completion on the previous phase
+			CurrentPhaseReq = PreviousPhaseReq;
+			//__e32_atomic_store_ord32( &CurrentState, EWriting );
+			CurrentState = EWriting;
+			
+			// Queue DFC instead of calling directly to avoid recursion if multiple items on queue
+			// complete without any action
+			CompletionDfc.Enque();
+			break;
+			}
+		else
+			{
+			TReq::TAction action = CurrentPhaseReq->iAction;
+
+			__KTRACE_OPT( KTPS65950, Kern::Printf( "=TPS65950:StartRequest:req@%x:a=%x", CurrentPhaseReq, action ) );
+
+			if( (TReq::ERead == action) || (TReq::EClearSet == action) )
+				{
+				StartRead( *CurrentPhaseReq );
+				break;
+				}
+			else if( TReq::EWrite == action )
+				{
+				StartWrite( *CurrentPhaseReq );
+				break;
+				}
+			else if( TReq::EDisableProtect == action )
+				{
+				if( ++TheProtectionUsageCount == 1 )
+					{
+					// Currently protected, start an unprotect sequence
+					StartUnprotectPhase0( *CurrentPhaseReq );
+					break;
+					}
+				else 
+					{
+					goto move_to_next_phase;
+					}
+				}
+			else if( TReq::ERestoreProtect == action )
+				{
+				if( --TheProtectionUsageCount == 0 )
+					{
+					StartProtect( *CurrentPhaseReq );
+					break;
+					}
+				else 
+					{
+	move_to_next_phase:
+					// already unprotected, skip to next phase
+					CurrentPhaseReq->iResult = KErrNone;
+					PreviousPhaseReq = CurrentPhaseReq;
+					CurrentPhaseReq = CurrentPhaseReq->iNextPhase;
+					}
+				}
+			else
+				{
+				InternalPanic( __LINE__ );
+				}
+			}
+		}
+
+
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "-TPS65950:StartRequest(%d)", CurrentState ) );
+	}
+
+void DummyDfcFunction( TAny* /*aParam*/ )
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "TPS65950:DummyDFC(%d)", CurrentState ) );
+	}
+
+void CompletionDfcFunction( TAny* /*aParam*/ )
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "+TPS65950:DFC(%d)", CurrentState ) );
+	__ASSERT_DEBUG( EIdle != CurrentState, InternalPanic( __LINE__ ) );
+	__ASSERT_DEBUG( CurrentPhaseReq, InternalPanic( __LINE__ ) );
+
+	TInt result = TheTransferPb[ EDataPb ].iResult;
+	if( KErrNone != TheTransferPb[ EAddressPb ].iResult )
+		{
+		result = TheTransferPb[ EAddressPb ].iResult;
+		}
+
+	TReq& req = *CurrentPhaseReq;
+	TBool completed = ETrue;
+
+	if( KErrNone == result)
+		{
+		if( EReading == CurrentState )
+			{
+			__KTRACE_OPT( KTPS65950, Kern::Printf( "=TPS65950:DFC:Read [%x:%x]=%x",
+													KGroupIndexToGroupNumber[ req.iRegister >> Register::KGroupShift ],
+													req.iRegister bitand Register::KRegisterMask,
+													req.iReadValue ) );
+
+			if( TReq::EClearSet == req.iAction)
+				{
+				// Start write phase of a ClearSet
+				req.iWriteValue = (req.iReadValue bitand ~req.iClearMask) bitor req.iSetMask;
+				StartWrite( req );
+				completed = EFalse;
+				}
+			}
+		else if( EUnprotectPhase0 == CurrentState )
+			{
+			StartUnprotectPhase1( req );
+			completed = EFalse;
+			}
+		}
+
+	if( completed || (KErrNone != result) )
+		{
+		// Read or write, protect has completed, or final write stage of a ClearSet or unprotect, or error
+		PreviousPhaseReq = CurrentPhaseReq;
+		CurrentPhaseReq = req.iNextPhase;
+
+		if( CurrentPhaseReq )
+			{
+			// start next phase
+			//__e32_atomic_store_ord32( &CurrentState, EPending );
+			CurrentState = EPending;
+			StartRequest();
+			}
+		else
+			{
+			//__e32_atomic_store_ord32( &CurrentState, EIdle );
+			CurrentState = EIdle;
+			// From now a concurrent ExecAsync() can start a new request if it adds an item to the queue
+
+			// remove item from queue and complete
+			TUint irq = __SPIN_LOCK_IRQSAVE( QueueLock );
+			ReqFromLink( TheQueue.First() ).iLink.Deque();
+			TBool queueEmpty = TheQueue.IsEmpty();
+			__SPIN_UNLOCK_IRQRESTORE( QueueLock, irq );
+
+			// If queue was empty inside spinlock but an ExecAsync() adds an item before the if statement below,
+			// the ExecAsync() will start the new request
+			if( !queueEmpty )
+				{
+				if( AtomicSetPendingWasIdle() )
+					{
+					// ExecAsync didn't start a request
+					StartRequest();
+					}
+				}
+
+			// Notify client of completion
+			req.iResult = result;
+			if( req.iCompletionDfc )
+				{
+				req.iCompletionDfc->Enque();
+				}
+			}
+		}
+
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "-TPS65950:DFC(%d)", CurrentState ) );
+	}
+
+// Used to complete synchronous operations
+void SyncDfcFunction( TAny* aParam )
+	{
+	NKern::FSSignal( reinterpret_cast<NFastSemaphore*>( aParam ) );
+	}
+
+
+EXPORT_C void ExecAsync( TReq& aRequest )
+	{
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "+TPS65950:ExecAsync(@%x)", &aRequest ) );
+
+	__ASSERT_DEBUG( (TUint)aRequest.iAction <= TReq::ERestoreProtect, PanicClient( EBadAction ) );
+	__ASSERT_DEBUG( (TReq::EDisableProtect == aRequest.iAction) 
+					|| (TReq::ERestoreProtect == aRequest.iAction)
+					|| (((TUint)aRequest.iRegister >> Register::KGroupShift) < KGroupCount), PanicClient( EBadGroup ) );
+
+	TUint irq = __SPIN_LOCK_IRQSAVE( QueueLock );
+	TheQueue.Add( &aRequest.iLink );
+	__SPIN_UNLOCK_IRQRESTORE( QueueLock, irq );
+
+	if( AtomicSetPendingWasIdle() )
+		{
+		StartRequest();
+		}
+
+	__KTRACE_OPT( KTPS65950, Kern::Printf( "-TPS65950:ExecAsync" ) );
+	}
+
+EXPORT_C TInt WriteSync( TUint16 aRegister, TUint8 aValue )
+	{
+	__ASSERT_NO_FAST_MUTEX;
+
+	NFastSemaphore sem;
+	TDfc dfc( SyncDfcFunction, &sem, TheDfcQue, 2 );
+	TReq req;
+	req.iRegister = aRegister;
+	req.iAction = TReq::EWrite;
+	req.iCompletionDfc = &dfc;
+	req.iWriteValue = aValue;
+	req.iNextPhase = NULL;
+
+	NKern::FSSetOwner( &sem, NULL );
+	ExecAsync( req );
+	NKern::FSWait( &sem );
+
+	return req.iResult;
+	}
+
+EXPORT_C TInt ReadSync( TUint16 aRegister, TUint8& aValue )
+	{
+	__ASSERT_NO_FAST_MUTEX;
+
+	NFastSemaphore sem;
+	TDfc dfc( SyncDfcFunction, &sem, TheDfcQue, 2 );
+	TReq req;
+	req.iRegister = aRegister;
+	req.iAction = TReq::ERead;
+	req.iCompletionDfc = &dfc;
+	req.iNextPhase = NULL;
+
+	NKern::FSSetOwner( &sem, NULL );
+	ExecAsync( req );
+	NKern::FSWait( &sem );
+
+	aValue = req.iReadValue;
+	return req.iResult;
+	}
+
+EXPORT_C TInt ClearSetSync( TUint16 aRegister, TUint8 aClearMask, TUint8 aSetMask )
+	{
+	__ASSERT_NO_FAST_MUTEX;
+
+	NFastSemaphore sem;
+	TDfc dfc( SyncDfcFunction, &sem, TheDfcQue, 2 );
+	TReq req;
+	req.iRegister = aRegister;
+	req.iAction = TReq::EClearSet;
+	req.iCompletionDfc = &dfc;
+	req.iClearMask = aClearMask;
+	req.iSetMask = aSetMask;
+	req.iNextPhase = NULL;
+
+	NKern::FSSetOwner( &sem, NULL );
+	ExecAsync( req );
+	NKern::FSWait( &sem );
+
+	return req.iResult;
+	}
+
+EXPORT_C TInt DisableProtect()
+	{
+	__ASSERT_NO_FAST_MUTEX;
+
+	NFastSemaphore sem;
+	TDfc dfc( SyncDfcFunction, &sem, TheDfcQue, 2 );
+	TReq req;
+	req.iAction = TReq::EDisableProtect;
+	req.iCompletionDfc = &dfc;
+	req.iNextPhase = NULL;
+
+	NKern::FSSetOwner( &sem, NULL );
+	ExecAsync( req );
+	NKern::FSWait( &sem );
+
+	return req.iResult;
+	}
+
+EXPORT_C TInt RestoreProtect()
+	{
+	__ASSERT_NO_FAST_MUTEX;
+
+	NFastSemaphore sem;
+	TDfc dfc( SyncDfcFunction, &sem, TheDfcQue, 2 );
+	TReq req;
+	req.iAction = TReq::ERestoreProtect;
+	req.iCompletionDfc = &dfc;
+	req.iNextPhase = NULL;
+
+	NKern::FSSetOwner( &sem, NULL );
+	ExecAsync( req );
+	NKern::FSWait( &sem );
+
+	return req.iResult;
+	}
+
+
+TInt Init()
+	{
+	// Create DFC queue
+	TInt r = Kern::DfcQCreate( TheDfcQue, KDfcQuePriority, &KDriverNameDes );
+	if( KErrNone != r )
+		{
+		return r;
+		}
+
+	TPS65950::CompletionDfc.SetDfcQ( TheDfcQue );
+	TPS65950::DummyDfc.SetDfcQ( TheDfcQue );
+
+	// Open I2c handles
+	for( TInt i = 0; i < KGroupCount; ++i )
+		{
+		TheDcb[i].iUnit = I2c::E1; // Master / slave
+		TheDcb[i].iRole = I2c::EMaster;
+		TheDcb[i].iMode = I2c::E7Bit;
+		TheDcb[i].iExclusiveClient = NULL;
+		TheDcb[i].iRate = I2c::E400K;
+		TheDcb[i].iOwnAddress = 0x01;
+		TheDcb[i].iDfcQueue = TheDfcQue;
+		TheDcb[i].iDeviceAddress = KGroupIndexToGroupNumber[i];
+
+		I2cHandle[i] = I2c::Open( TheDcb[i] );
+		if( I2cHandle[i] < 0 )
+			{
+			return I2cHandle[i];
+			}
+		}
+
+	// Setup transfer linked list
+	TheTransferPb[ EAddressPb ].iType = I2c::TTransferPb::EWrite;	// address write
+	TheTransferPb[ EAddressPb ].iLength = 1;
+	TheTransferPb[ EAddressPb ].iCompletionDfc = &TPS65950::DummyDfc;
+	TheTransferPb[ EAddressPb ].iNextPhase = &TheTransferPb[ EDataPb ];
+	TheTransferPb[ EDataPb ].iCompletionDfc = &TPS65950::CompletionDfc;
+	TheTransferPb[ EDataPb ].iNextPhase = NULL;
+
+	return r;
+	}
+
+inline TInt BcdToDecimal( TUint8 aBcd )
+	{
+	return ( aBcd bitand 0xF ) + ( (aBcd >> 4) * 10);
+	}
+
+inline TUint8 DecimalToBcd( TInt aDecimal )
+	{
+	TUint tens = (aDecimal / 10);
+	return ( tens << 4 ) + ( aDecimal - tens );
+	}
+
+EXPORT_C TInt GetRtcData( TRtcTime& aTime )
+	{
+	__ASSERT_NO_FAST_MUTEX;
+
+	NFastSemaphore sem;
+	TDfc dfc( SyncDfcFunction, &sem, TheDfcQue, 2 );
+	TReq req[8];	// 9 stages to the operation
+	req[0].iRegister = RTC_CTRL_REG::Addr;
+	req[0].iAction = TReq::EClearSet;
+	req[0].iSetMask = RTC_CTRL_REG::GET_TIME;
+	req[0].iClearMask = 0;
+	req[0].iCompletionDfc = NULL;
+	req[0].iNextPhase = &req[1];
+
+	req[1].iRegister = Register::SECONDS_REG;
+	req[1].iAction = TReq::ERead;
+	req[1].iCompletionDfc = NULL;
+	req[1].iNextPhase = &req[2];
+
+	req[2].iRegister = Register::MINUTES_REG;
+	req[2].iAction = TReq::ERead;
+	req[2].iCompletionDfc = NULL;
+	req[2].iNextPhase = &req[3];
+	
+	req[3].iRegister = Register::HOURS_REG;
+	req[3].iAction = TReq::ERead;
+	req[3].iCompletionDfc = NULL;
+	req[3].iNextPhase = &req[4];
+
+	req[4].iRegister = Register::DAYS_REG;
+	req[4].iAction = TReq::ERead;
+	req[4].iCompletionDfc = NULL;
+	req[4].iNextPhase = &req[5];
+
+	req[5].iRegister = Register::MONTHS_REG;
+	req[5].iAction = TReq::ERead;
+	req[5].iCompletionDfc = NULL;
+	req[5].iNextPhase = &req[6];
+
+	req[6].iRegister = Register::YEARS_REG;
+	req[6].iAction = TReq::ERead;
+	req[6].iCompletionDfc = NULL;
+	req[6].iNextPhase = &req[7];
+
+	req[7].iRegister = RTC_CTRL_REG::Addr;
+	req[7].iAction = TReq::EClearSet;
+	req[7].iSetMask = 0;
+	req[7].iClearMask = RTC_CTRL_REG::GET_TIME;
+	req[7].iCompletionDfc = &dfc;
+	req[7].iNextPhase = NULL;
+
+	NKern::FSSetOwner( &sem, NULL );
+	ExecAsync( req[0] );
+	NKern::FSWait( &sem );
+
+	aTime.iSecond = BcdToDecimal( req[1].iReadValue );
+	aTime.iMinute = BcdToDecimal( req[2].iReadValue );
+	aTime.iHour = BcdToDecimal( req[3].iReadValue );
+	aTime.iDay = BcdToDecimal( req[4].iReadValue );
+	aTime.iMonth = BcdToDecimal( req[5].iReadValue );
+	aTime.iYear = BcdToDecimal( req[6].iReadValue );
+
+	return KErrNone;
+	}
+
+#define BCD0(a) ((a)%10)
+#define BCD1(a) (((a)/10)<<4)
+#define TOBCD(i) (BCD1(i)|BCD0(i))
+
+EXPORT_C TInt SetRtcData( const TRtcTime& aTime )
+	{	
+	__ASSERT_NO_FAST_MUTEX;
+
+	NFastSemaphore sem;
+	TDfc dfc( SyncDfcFunction, &sem, TheDfcQue, 2 );
+	TReq req[8];	// 9 stages to the operation
+	req[0].iRegister = RTC_CTRL_REG::Addr;
+	req[0].iAction = TReq::EClearSet;
+	req[0].iSetMask = 0;
+	req[0].iClearMask = RTC_CTRL_REG::STOP_RTC;
+	req[0].iCompletionDfc = NULL;
+	req[0].iNextPhase = &req[1];
+
+	req[1].iRegister = Register::SECONDS_REG;
+	req[1].iAction = TReq::EWrite;
+	req[1].iWriteValue = DecimalToBcd( aTime.iSecond );
+	req[1].iCompletionDfc = NULL;
+	req[1].iNextPhase = &req[2];
+
+	req[2].iRegister = Register::MINUTES_REG;
+	req[2].iAction = TReq::EWrite;
+	req[2].iWriteValue = DecimalToBcd( aTime.iMinute );
+	req[2].iCompletionDfc = NULL;
+	req[2].iNextPhase = &req[3];
+	
+	req[3].iRegister = Register::HOURS_REG;
+	req[3].iAction = TReq::EWrite;
+	req[3].iWriteValue = DecimalToBcd( aTime.iHour );
+	req[3].iCompletionDfc = NULL;
+	req[3].iNextPhase = &req[4];
+
+	req[4].iRegister = Register::DAYS_REG;
+	req[4].iAction = TReq::EWrite;
+	req[4].iWriteValue = DecimalToBcd( aTime.iDay );
+	req[4].iCompletionDfc = NULL;
+	req[4].iNextPhase = &req[5];
+
+	req[5].iRegister = Register::MONTHS_REG;
+	req[5].iAction = TReq::EWrite;
+	req[5].iWriteValue = DecimalToBcd( aTime.iMonth );
+	req[5].iCompletionDfc = NULL;
+	req[5].iNextPhase = &req[6];
+
+	req[6].iRegister = Register::YEARS_REG;
+	req[6].iAction = TReq::EWrite;
+	req[6].iWriteValue = DecimalToBcd( aTime.iYear );
+	req[6].iCompletionDfc = NULL;
+	req[6].iNextPhase = &req[7];
+
+	req[7].iRegister = RTC_CTRL_REG::Addr;
+	req[7].iAction = TReq::EClearSet;
+	req[7].iSetMask = RTC_CTRL_REG::STOP_RTC;
+	req[7].iClearMask = 0;
+	req[7].iCompletionDfc = &dfc;
+	req[7].iNextPhase = NULL;
+
+	NKern::FSSetOwner( &sem, NULL );
+	ExecAsync( req[0] );
+	NKern::FSWait( &sem );
+
+	return KErrNone;
+	}
+
+EXPORT_C TBool Initialized()
+	{
+	return IsInitialized;
+	}
+
+} // namespace TPS65950
+
+
+
+
+DECLARE_STANDARD_EXTENSION()
+	{
+	TInt r = TPS65950::Init();
+	if( KErrNone == r )
+		{
+		r = InitInterrupts();
+		}
+
+	IsInitialized = ( KErrNone == r );
+
+	return r;
+	}
+