kernel/eka/drivers/pbus/mmc/sdcard/sdcard3c/sdio/interrupt.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/pbus/mmc/sdcard/sdcard3c/sdio/interrupt.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,380 @@
+/*
+* Copyright (c) 2003-2007 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:
+*
+*/
+
+
+#include <kernel/kernel.h>
+
+#include <drivers/sdio/sdiocard.h>
+#include <drivers/sdio/interrupt.h>
+#include <drivers/sdio/regifc.h>
+#include <drivers/sdio/function.h>
+#include <drivers/sdio/callback.h>
+#include "utraceepbussdio.h"
+
+
+TSDIOInterruptController::TSDIOInterruptController() :
+/**
+Contructs the TSDIOInterruptController
+*/
+	iCardP(NULL),
+	iInterruptSessionP(NULL),
+    iSessionEndCallBack(TSDIOInterruptController::SessionEndCallBack, this),
+	iPending(0)
+	{
+	SYMBIAN_TRACE_SDIO_VERBOSE_ONLY(Printf(TTraceContext(EInternals), "TSDIOInterruptController::TSDIOInterruptController, construct")); // @SymTraceDataInternalTechnology
+	}
+
+TSDIOInterruptController::~TSDIOInterruptController()
+/**
+Destroys the TSDIOInterruptControllerinstance.
+*/
+	{
+	SYMBIAN_TRACE_SDIO_VERBOSE_ONLY(Printf(TTraceContext(EInternals), "TSDIOInterruptController::~TSDIOInterruptController, destruct")); // @SymTraceDataInternalTechnology
+	}
+
+TInt TSDIOInterruptController::Create(DSDIOStack* aStackP, TSDIOCard* aCardP)
+/**
+Contructs the TSDIOInterruptController
+*/
+	{
+	__ASSERT_ALWAYS((aStackP != NULL) && (aCardP != NULL), Panic(EBadParameter));
+
+	SYMBIAN_TRACE_SDIO_VERBOSE_ONLY(Printf(TTraceContext(EInternals), "TSDIOInterruptController::Create")); // @SymTraceDataInternalTechnology
+	
+	TInt err = KErrNone;
+
+	iCardP = aCardP;
+
+	if(!iInterruptSessionP)
+		{
+		iInterruptSessionP = static_cast<DSDIOSession*>(aStackP->AllocSession(iSessionEndCallBack));
+		if (iInterruptSessionP)
+			{
+			iInterruptSessionP->SetStack(aStackP);
+			iInterruptSessionP->SetCard((TMMCard*)aCardP);
+			}
+		else
+			{
+			err = KErrNoMemory;
+			}
+		}
+
+	return(err);
+	}
+
+
+TInt TSDIOInterruptController::EnableInterrupt(TUint8 aFunctionNumber)
+/**
+Enables an SDIO interrupt
+*/
+	{
+	__ASSERT_DEBUG(aFunctionNumber > 0 && aFunctionNumber <= KMaxSDIOFunctions, 
+		TSDIOInterruptController::Panic(TSDIOInterruptController::EParameterOutOfRange));
+
+	SYMBIAN_TRACE_SDIO_VERBOSE_ONLY(Printf(TTraceContext(EInternals), "TSDIOInterruptController::EnableInterrupt for function %d", aFunctionNumber)); // @SymTraceDataInternalTechnology
+	
+	TInt err = KErrNone;
+
+	if((err = Start()) == KErrNone)
+		{	
+		DSDIORegisterInterface* pRegIfc = iCardP->CommonRegisterInterface();
+		err = pRegIfc->Modify8(KSDIOCCCRIntEnable, (TUint8)(0x01 << aFunctionNumber), 0);
+		}
+
+	return(err);
+	}
+
+
+TInt TSDIOInterruptController::DisableInterrupt(TUint8 aFunctionNumber)
+/**
+Enables an SDIO interrupt.  If no interrupts remain enabled, this DOES NOT
+abort the Interrupt Handler session.  This shall be aborted when the stack times out.
+*/
+	{
+	__ASSERT_DEBUG(aFunctionNumber > 0 && aFunctionNumber <= KMaxSDIOFunctions, 
+		TSDIOInterruptController::Panic(TSDIOInterruptController::EParameterOutOfRange));
+
+	SYMBIAN_TRACE_SDIO_VERBOSE_ONLY(Printf(TTraceContext(EInternals), "TSDIOInterruptController::DisableInterrupt for function %d", aFunctionNumber)); // @SymTraceDataInternalTechnology
+	
+	TInt err = KErrNone;
+
+	DSDIORegisterInterface* pRegIfc = iCardP->CommonRegisterInterface();
+	err = pRegIfc->Modify8(KSDIOCCCRIntEnable, 0x00, (TUint8)(0x01 << aFunctionNumber));
+
+	return(err);
+	}
+
+
+TInt TSDIOInterruptController::Start()
+/**
+Starts the interrupt handler session. (This is a long-running
+session that blocks until the interrupt occurs to improve latency)
+*/
+	{
+	SYMBIAN_TRACE_SDIO_VERBOSE_ONLY(Printf(TTraceContext(EInternals), "TSDIOInterruptController::Start")); // @SymTraceDataInternalTechnology
+		
+	TInt err = KErrNone;
+	if(!iInterruptSessionP->IsEngaged())
+		{
+		iInterruptSessionP->SetupCIMIoInterruptHandler(&iPending);
+		err = iInterruptSessionP->Engage();
+		}
+	
+	return(err);
+	}
+
+TInt TSDIOInterruptController::Stop()
+/**
+Stops the TSDIOInterruptController (aborts the session if engaged)
+and clears the Master Interrupt Enable bit in the CCCR
+
+This should not block
+
+@todo Clearing IENM should happen in the session end callback (after abort)
+*/
+	{
+	SYMBIAN_TRACE_SDIO_VERBOSE_ONLY(Printf(TTraceContext(EInternals), "TSDIOInterruptController::Stop")); // @SymTraceDataInternalTechnology
+	
+	if(iInterruptSessionP->IsEngaged())
+		{
+		iInterruptSessionP->Abort();
+		}	
+		
+	return(KErrNone);
+	}
+
+TInt TSDIOInterruptController::Schedule()
+/**
+Service the SDIO interrupt.  Called from ISR context.
+*/
+	{
+	TInt err = KErrNone;
+
+	if(iInterruptSessionP->IsEngaged())
+		{
+		iInterruptSessionP->UnblockInterrupt(KMMCErrNone);
+		}
+	else
+		{
+		err = KErrNotReady;
+		}
+
+	return(err);
+	}
+
+void TSDIOInterruptController::Service()
+/**
+Called by then ISR state machine when a pending interrupt is detected
+*/
+	{
+	TInt shiftVal = iPending;
+	TUint8 functionNo = 1;
+	while(shiftVal)
+		{
+		shiftVal >>= 1;
+		if(shiftVal & 0x01)
+			{
+			TSDIOFunction* functionP = iCardP->IoFunction(functionNo);
+			if(functionP)
+				{
+				functionP->Interrupt().Service();
+				}
+			}
+		functionNo++;
+		}
+	}
+
+#ifndef __DISABLE_SDIO_DEBUG
+void TSDIOInterruptController::SessionEndCallBack(TAny* aSelfP)
+#else
+void TSDIOInterruptController::SessionEndCallBack(TAny* /*aSelfP*/)
+#endif
+/**
+Called when the interrupt controller session terminates with an 
+error or is stopped (not aborted, which is the usual exit case).
+@param aSelfP 'this' pointer.
+*/
+	{
+	// The interrupt session has completed (in response to either a stop request,
+	// or some other problem.  In any case, disable interrupts completely
+#if defined(SYMBIAN_TRACE_SDIO_VERBOSE) 
+	TSDIOInterruptController& self = *static_cast<TSDIOInterruptController*>(aSelfP);
+	Printf(TTraceContext(EInternals), "ISR::Abort(%d)", self.iInterruptSessionP->EpocErrorCode()); // @SymTraceDataInternalTechnology
+#endif
+	}
+
+
+void TSDIOInterruptController::Panic(TSDIOInterruptController::TPanic aPanic)
+/**
+Panic the interrupt controller
+*/
+	{
+	Kern::Fault("SDIO_IC", aPanic);
+	}
+
+EXPORT_C TSDIOInterrupt::TSDIOInterrupt(TSDIOInterruptController* aControllerP, TUint8 aFunctionNumber)
+/**
+Contructs a TSDIOInterrupt for the specified function.
+
+@param aFunctionP A pointer to the associated TSDIOFunction class.
+*/
+  : iControllerP(aControllerP),
+	iFunctionNumber(aFunctionNumber),
+	iPtr(this)
+	{
+	TRACE2(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptConstructor, reinterpret_cast<TUint32>(this), aFunctionNumber); // @SymTraceDataPublishedTvk	
+
+	iIsr = TSDIOInterrupt::UnhookedIsr;
+
+	TRACE1(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptConstructorReturning, reinterpret_cast<TUint32>(this)); // @SymTraceDataPublishedTvk	
+	}
+
+EXPORT_C TSDIOInterrupt::~TSDIOInterrupt()
+/**
+Destroys the TSDIOInterrupt instance.
+*/
+	{
+	TRACE1(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptDestructor, reinterpret_cast<TUint32>(this)); // @SymTraceDataPublishedTvk	
+	TRACE1(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptDestructorReturning, reinterpret_cast<TUint32>(this)); // @SymTraceDataPublishedTvk	
+	}
+
+EXPORT_C TInt TSDIOInterrupt::Bind(TSDIOIsr aIsr, TAny* aPtr)
+/**
+@publishedPartner
+@released 
+
+Binds the TSDIOIsr callback to the functions interrupt through the Interrupt Controller.
+
+Should the functionality of the DSDIORegInterface class be used within the ISR,
+then the callback should queue a DFC within which the interrupt processing can occur.
+This will ensure that the interrupt is serviced as soon as any active requests have completed.
+
+Alternatively, if it is a requirement that the client thread does not block while servicing 
+the interrupt, then a seperate DSDIORegInterface (or a raw DSIOSession) may be used to handle 
+the interrupt asynchronously.
+
+Once bound, interrupts should be enabled for the function by calling Enable().  
+It may also be necessary to enable interrupts in a function-defined manner by writing to the 
+appropriate register within the function. 
+
+Bind shall fail with the error KErrAlreadyExists if an ISR is already bound to the function interrupt.
+
+@param aIsr The ISR used to handle the functions interrupt
+@param aPtr User defined data for use within the ISR
+
+@return KErrNone if successful, KErrAlreadyExists if already bound, or a standard Symbian OS error code.
+
+@see TSDIOInterrupt::Unbind
+*/
+	{
+	TRACE2(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptBind, reinterpret_cast<TUint32>(this), reinterpret_cast<TUint32>(aIsr)); // @SymTraceDataPublishedTvk	
+
+	if(iIsr != TSDIOInterrupt::UnhookedIsr)
+		{
+		return(KErrAlreadyExists);
+		}
+
+	iIsr = aIsr;
+	iPtr = aPtr;
+
+	TRACE2(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptBind, reinterpret_cast<TUint32>(this), KErrNone); // @SymTraceDataPublishedTvk	
+	return(KErrNone);
+	}
+
+EXPORT_C TInt TSDIOInterrupt::Unbind()
+/**
+@publishedPartner
+@released 
+
+Unbinds the callback from the interrupt controller, replacing the callback with a dummy handler.
+
+@return KErrNone if successful, otherwise a standard Symbian OS error code.
+
+@see TSDIOInterrupt::Bind
+*/
+	{
+	TRACE1(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptUnbind, reinterpret_cast<TUint32>(this)); // @SymTraceDataPublishedTvk	
+
+	if(iIsr == TSDIOInterrupt::UnhookedIsr)
+		{
+		return(KErrNotSupported);
+		}
+
+	iIsr = UnhookedIsr;
+	iPtr = NULL;
+
+	TRACE2(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptUnbindReturning, reinterpret_cast<TUint32>(this), KErrNone); // @SymTraceDataPublishedTvk	
+	return(KErrNone);
+	}
+
+EXPORT_C TInt TSDIOInterrupt::Enable()
+/**
+@publishedPartner
+@released 
+
+Enables the functions interrupt by setting to the appropriate IEN bit in the CCCR.
+
+Note that this only unmasks the global function interrupt - it is the responsibility 
+of the client to perform any function-specific interrupt enabling that may be required.
+
+@return KErrNone if successful, otherwise a standard Symbian OS error code.
+
+@see TSDIOInterrupt::Disable
+*/
+	{
+	TRACE1(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptEnable, reinterpret_cast<TUint32>(this)); // @SymTraceDataPublishedTvk
+	TInt ret = iControllerP->EnableInterrupt(iFunctionNumber);
+	TRACE2(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptEnableReturning, reinterpret_cast<TUint32>(this), ret); // @SymTraceDataPublishedTvk
+	return(ret);
+	}
+
+EXPORT_C TInt TSDIOInterrupt::Disable()
+/**
+@publishedPartner
+@released 
+
+Disables the functions interrupt by clearing to the appropriate IEN bit in the CCCR.
+
+Note that this only masks the global function interrupt - it is the responsibility 
+of the client to perform any function-specific interrupt disabling that may be required.
+
+@return KErrNone if successful, otherwise a standard Symbian OS error code.
+
+@see TSDIOInterrupt::Enable
+*/
+	{
+	TRACE1(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptDisable, reinterpret_cast<TUint32>(this)); // @SymTraceDataPublishedTvk
+	TInt ret = iControllerP->DisableInterrupt(iFunctionNumber);
+	TRACE2(TTraceContext(EBorder), UTraceModuleEPBusSDIO::ESDIOTSDIOInterruptDisableReturning, reinterpret_cast<TUint32>(this), ret); // @SymTraceDataPublishedTvk
+	return(ret);
+	}
+
+void TSDIOInterrupt::UnhookedIsr(TAny* /*aPtr*/)
+/**
+Dummy ISR for unhooked interrupts.
+*/
+	{
+	SYMBIAN_TRACE_SDIO_VERBOSE_ONLY(Printf(TTraceContext(EInternals), "TSDIOInterrupt::UnhookedIsr")); // @SymTraceDataInternalTechnology
+	}
+
+void TSDIOInterrupt::Service()
+/**
+Services the interrupt
+*/
+	{
+	iIsr(iPtr);
+	}