bsptemplate/asspandvariant/template_assp/dmapsl_v2.cpp
changeset 130 c30940f6d922
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsptemplate/asspandvariant/template_assp/dmapsl_v2.cpp	Tue May 04 09:44:26 2010 +0100
@@ -0,0 +1,480 @@
+// Copyright (c) 2004-2010 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:
+// bsptemplate/asspvariant/template_assp/dmapsl_v2.cpp
+// Template DMA Platform Specific Layer (PSL).
+//
+//
+
+
+#include <kernel/kern_priv.h>
+#include <template_assp.h>									// /assp/template_assp/
+
+#include <drivers/dma.h>
+#include <drivers/dma_hai.h>
+
+
+// Debug support
+static const char KDmaPanicCat[] = "DMA PSL - " __FILE__;
+
+static const TInt KMaxTransferLen = 0x1FE0;	// max transfer length for this DMAC
+static const TInt KMemAlignMask = 7; // memory addresses passed to DMAC must be multiple of 8
+static const TInt KChannelCount = 16;			// we got 16 channels
+static const TInt KDesCount = 160;				// Initial DMA descriptor count
+
+
+class TDmaDesc
+//
+// Hardware DMA descriptor
+//
+	{
+public:
+	enum {KStopBitMask = 1};
+public:
+	TPhysAddr iDescAddr;
+	TPhysAddr iSrcAddr;
+	TPhysAddr iDestAddr;
+	TUint32 iCmd;
+	};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Test Support
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+TO DO: Fill in to provide information to the V1 test harness (t_dma.exe)
+*/
+TDmaTestInfo TestInfo =
+	{
+	0,
+	0,
+	0,
+	0,
+	NULL,
+	0,
+	NULL,
+	0,
+	NULL
+	};
+
+
+EXPORT_C const TDmaTestInfo& DmaTestInfo()
+//
+//
+//
+	{
+	return TestInfo;
+	}
+
+/**
+TO DO: Fill in to provide information to the V2 test harness (t_dma2.exe)
+*/
+TDmaV2TestInfo TestInfov2 =
+	{
+	0,
+	0,
+	0,
+	0,
+	{0},
+	0,
+	{0},
+	0,
+	{0}
+	};
+
+EXPORT_C const TDmaV2TestInfo& DmaTestInfoV2()
+	{
+	return TestInfov2;
+	}
+
+//////////////////////////////////////////////////////////////////////////////
+// Helper Functions
+//////////////////////////////////////////////////////////////////////////////
+
+inline TBool IsHwDesAligned(TAny* aDes)
+//
+// Checks whether given hardware descriptor is 16-bytes aligned.
+//
+	{
+	return ((TLinAddr)aDes & 0xF) == 0;
+	}
+
+
+static TUint32 DmaCmdReg(TUint aCount, TUint aFlags, TUint32 aSrcPslInfo, TUint32 aDstPslInfo)
+//
+// Returns value to set in DMA command register or in descriptor command field.
+//
+	{
+	// TO DO: Construct CMD word from input values.
+	// The return value should reflect the actual control word.
+	return (aCount | aFlags | aSrcPslInfo | aDstPslInfo);
+	}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Derived Channel (Scatter/Gather)
+//////////////////////////////////////////////////////////////////////////////
+
+class TTemplateSgChannel : public TDmaSgChannel
+	{
+public:
+	TDmaDesc* iTmpDes;
+	TPhysAddr iTmpDesPhysAddr;
+	};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Derived Controller Class
+//////////////////////////////////////////////////////////////////////////////
+
+class TTemplateDmac : public TDmac
+	{
+public:
+	TTemplateDmac();
+	TInt Create();
+private:
+	// from TDmac (PIL pure virtual)
+	virtual void StopTransfer(const TDmaChannel& aChannel);
+	virtual TBool IsIdle(const TDmaChannel& aChannel);
+	virtual TUint MaxTransferLength(TDmaChannel& aChannel, TUint aSrcFlags,
+									TUint aDstFlags, TUint32 aPslInfo);
+	virtual TUint AddressAlignMask(TDmaChannel& aChannel, TUint aSrcFlags,
+								   TUint aDstFlags, TUint32 aPslInfo);
+	// from TDmac (PIL virtual)
+	virtual void Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr);
+	virtual TInt InitHwDes(const SDmaDesHdr& aHdr, const TDmaTransferArgs& aTransferArgs);
+	virtual void ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr);
+	virtual void AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr,
+							 const SDmaDesHdr& aNewHdr);
+	virtual void UnlinkHwDes(const TDmaChannel& aChannel, SDmaDesHdr& aHdr);
+	// other
+	static void Isr(TAny* aThis);
+	inline TDmaDesc* HdrToHwDes(const SDmaDesHdr& aHdr);
+private:
+	static const SCreateInfo KInfo;
+public:
+	TTemplateSgChannel iChannels[KChannelCount];
+	};
+
+
+static TTemplateDmac Controller;
+
+
+const TDmac::SCreateInfo TTemplateDmac::KInfo =
+	{
+	ETrue,													// iCapsHwDes
+	KDesCount,												// iDesCount
+	sizeof(TDmaDesc),										// iDesSize
+	EMapAttrSupRw | EMapAttrFullyBlocking					// iDesChunkAttribs
+	};
+
+
+TTemplateDmac::TTemplateDmac()
+//
+// Constructor.
+//
+	: TDmac(KInfo)
+	{}
+
+
+TInt TTemplateDmac::Create()
+//
+// Second phase construction.
+//
+	{
+	TInt r = TDmac::Create(KInfo);							// Base class Create()
+	if (r == KErrNone)
+		{
+		__DMA_ASSERTA(ReserveSetOfDes(KChannelCount) == KErrNone);
+		for (TInt i=0; i < KChannelCount; ++i)
+			{
+			TDmaDesc* pD = HdrToHwDes(*iFreeHdr);
+			iChannels[i].iTmpDes = pD;
+			iChannels[i].iTmpDesPhysAddr = HwDesLinToPhys(pD);
+			iFreeHdr = iFreeHdr->iNext;
+			}
+		r = Interrupt::Bind(EAsspIntIdDma, Isr, this);
+		if (r == KErrNone)
+			{
+			// TO DO: Map DMA clients (requests) to DMA channels here.
+
+			r = Interrupt::Enable(EAsspIntIdDma);
+			}
+		}
+	return r;
+	}
+
+
+void TTemplateDmac::Transfer(const TDmaChannel& aChannel, const SDmaDesHdr& aHdr)
+//
+// Initiates a (previously constructed) request on a specific channel.
+//
+	{
+	const TUint8 i = static_cast<TUint8>(aChannel.PslId());
+	TDmaDesc* pD = HdrToHwDes(aHdr);
+
+	__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::Transfer channel=%d des=0x%08X", i, pD));
+
+	// TO DO (for instance): Load the first descriptor address into the DMAC and start it
+	// by setting the RUN bit.
+	(void) *pD, (void) i;
+
+	}
+
+
+void TTemplateDmac::StopTransfer(const TDmaChannel& aChannel)
+//
+// Stops a running channel.
+//
+	{
+	const TUint8 i = static_cast<TUint8>(aChannel.PslId());
+
+	__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::StopTransfer channel=%d", i));
+
+	// TO DO (for instance): Clear the RUN bit of the channel.
+	(void) i;
+
+	}
+
+
+TBool TTemplateDmac::IsIdle(const TDmaChannel& aChannel)
+//
+// Returns the state of a given channel.
+//
+	{
+	const TUint8 i = static_cast<TUint8>(aChannel.PslId());
+
+	__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::IsIdle channel=%d", i));
+
+	// TO DO (for instance): Return the state of the RUN bit of the channel.
+	// The return value should reflect the actual state.
+	(void) i;
+
+	return ETrue;
+	}
+
+
+TUint TTemplateDmac::MaxTransferLength(TDmaChannel& /*aChannel*/, TUint /*aSrcFlags*/,
+									   TUint /*aDstFlags*/, TUint32 /*aPslInfo*/)
+//
+// Returns the maximum transfer length in bytes for a given transfer.
+//
+	{
+	// TO DO: Determine the proper return value, based on the arguments.
+
+	// For instance:
+	return KMaxTransferLen;
+	}
+
+
+TUint TTemplateDmac::AddressAlignMask(TDmaChannel& aChannel, TUint /*aSrcFlags*/,
+									  TUint /*aDstFlags*/, TUint32 /*aPslInfo*/)
+//
+// Returns the memory buffer alignment restrictions mask for a given transfer.
+//
+	{
+	// TO DO: Determine the proper return value, based on the arguments.
+
+	// For instance:
+	return KMemAlignMask;
+	}
+
+
+TInt TTemplateDmac::InitHwDes(const SDmaDesHdr& aHdr, const TDmaTransferArgs& aTransferArgs)
+//
+// Sets up (from a passed in request) the descriptor with that fragment's
+// source and destination address, the fragment size, and the (driver/DMA
+// controller) specific transfer parameters (mem/peripheral, burst size,
+// transfer width).
+//
+	{
+	TDmaDesc* pD = HdrToHwDes(aHdr);
+
+	__KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::InitHwDes 0x%08X", pD));
+
+	// Unaligned descriptor? Bug in generic layer!
+	__DMA_ASSERTD(IsHwDesAligned(pD));
+
+	const TDmaTransferConfig& src = aTransferArgs.iSrcConfig;
+	const TDmaTransferConfig& dst = aTransferArgs.iDstConfig;
+	pD->iSrcAddr  = (src.iFlags & KDmaPhysAddr) ? src.iAddr : Epoc::LinearToPhysical(src.iAddr);
+	__DMA_ASSERTD(pD->iSrcAddr != KPhysAddrInvalid);
+	pD->iDestAddr = (dst.iFlags & KDmaPhysAddr) ? dst.iAddr : Epoc::LinearToPhysical(dst.iAddr);
+	__DMA_ASSERTD(pD->iDestAddr != KPhysAddrInvalid);
+	pD->iCmd = DmaCmdReg(aTransferArgs.iTransferCount, aTransferArgs.iFlags,
+					   src.iPslTargetInfo, dst.iPslTargetInfo);
+	pD->iDescAddr = TDmaDesc::KStopBitMask;
+
+	return KErrNone;
+	}
+
+
+void TTemplateDmac::ChainHwDes(const SDmaDesHdr& aHdr, const SDmaDesHdr& aNextHdr)
+//
+// Chains hardware descriptors together by setting the next pointer of the original descriptor
+// to the physical address of the descriptor to be chained.
+//
+	{
+	TDmaDesc* pD = HdrToHwDes(aHdr);
+	TDmaDesc* pN = HdrToHwDes(aNextHdr);
+
+	__KTRACE_OPT(KDMA, Kern::Printf("TTemplateDmac::ChainHwDes des=0x%08X next des=0x%08X", pD, pN));
+
+	// Unaligned descriptor? Bug in generic layer!
+	__DMA_ASSERTD(IsHwDesAligned(pD) && IsHwDesAligned(pN));
+
+	// TO DO: Modify pD->iCmd so that no end-of-transfer interrupt gets raised any longer.
+
+	pD->iDescAddr = HwDesLinToPhys(pN);
+	}
+
+
+void TTemplateDmac::AppendHwDes(const TDmaChannel& aChannel, const SDmaDesHdr& aLastHdr,
+								const SDmaDesHdr& aNewHdr)
+//
+// Appends a descriptor to the chain while the channel is running.
+//
+	{
+	const TUint8 i = static_cast<TUint8>(aChannel.PslId());
+
+	TDmaDesc* pL = HdrToHwDes(aLastHdr);
+	TDmaDesc* pN = HdrToHwDes(aNewHdr);
+
+	__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::AppendHwDes channel=%d last des=0x%08X new des=0x%08X",
+									i, pL, pN));
+	// Unaligned descriptor? Bug in generic layer!
+	__DMA_ASSERTD(IsHwDesAligned(pL) && IsHwDesAligned(pN));
+
+	TPhysAddr newPhys = HwDesLinToPhys(pN);
+
+	const TInt irq = NKern::DisableAllInterrupts();
+	StopTransfer(aChannel);
+
+	pL->iDescAddr = newPhys;
+	const TTemplateSgChannel& channel = static_cast<const TTemplateSgChannel&>(aChannel);
+	TDmaDesc* pD = channel.iTmpDes;
+
+	// TO DO: Implement the appropriate algorithm for appending a descriptor here.
+	(void) *pD, (void) i;
+
+	NKern::RestoreInterrupts(irq);
+
+	__KTRACE_OPT(KDMA, Kern::Printf("<TTemplateDmac::AppendHwDes"));
+	}
+
+
+void TTemplateDmac::UnlinkHwDes(const TDmaChannel& /*aChannel*/, SDmaDesHdr& aHdr)
+//
+// Unlink the last item in the h/w descriptor chain from a subsequent chain that it was
+// possibly linked to.
+//
+	{
+ 	__KTRACE_OPT(KDMA, Kern::Printf(">TTemplateDmac::UnlinkHwDes"));
+  	TDmaDesc* pD = HdrToHwDes(aHdr);
+  	pD->iDescAddr = TDmaDesc::KStopBitMask;
+
+	// TO DO: Modify pD->iCmd so that an end-of-transfer interrupt will get raised.
+
+	}
+
+
+void TTemplateDmac::Isr(TAny* aThis)
+//
+// This ISR reads the interrupt identification and calls back into the base class
+// interrupt service handler with the channel identifier and an indication whether the
+// transfer completed correctly or with an error.
+//
+	{
+	TTemplateDmac& me = *static_cast<TTemplateDmac*>(aThis);
+
+	// TO DO: Implement the behaviour described above, call HandleIsr().
+
+	HandleIsr(me.iChannels[5], EDmaCallbackRequestCompletion, ETrue); // Example
+
+	}
+
+
+inline TDmaDesc* TTemplateDmac::HdrToHwDes(const SDmaDesHdr& aHdr)
+//
+// Changes return type of base class call.
+//
+	{
+	return static_cast<TDmaDesc*>(TDmac::HdrToHwDes(aHdr));
+	}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Channel Opening/Closing (Channel Allocator)
+//////////////////////////////////////////////////////////////////////////////
+
+TDmaChannel* DmaChannelMgr::Open(TUint32 aOpenId, TBool /*aDynChannel*/, TUint /*aPriority*/)
+//
+//
+//
+	{
+	__KTRACE_OPT(KDMA, Kern::Printf(">DmaChannelMgr::Open aOpenId=%d", aOpenId));
+
+	__DMA_ASSERTA(aOpenId < static_cast<TUint32>(KChannelCount));
+
+	TDmaChannel* pC = Controller.iChannels + aOpenId;
+	if (pC->IsOpened())
+		{
+		pC = NULL;
+		}
+	else
+		{
+		pC->iController = &Controller;
+		pC->iPslId = aOpenId;
+		}
+
+	return pC;
+	}
+
+
+void DmaChannelMgr::Close(TDmaChannel* /*aChannel*/)
+//
+//
+//
+	{
+	// NOP
+	}
+
+
+TInt DmaChannelMgr::StaticExtension(TInt /*aCmd*/, TAny* /*aArg*/)
+//
+//
+//
+	{
+	return KErrNotSupported;
+	}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// DLL Exported Function
+//////////////////////////////////////////////////////////////////////////////
+
+DECLARE_STANDARD_EXTENSION()
+//
+// Creates and initializes a new DMA controller object on the kernel heap.
+//
+	{
+	__KTRACE_OPT2(KBOOT, KDMA, Kern::Printf("Starting DMA Extension"));
+
+	const TInt r = DmaChannelMgr::Initialise();
+	if (r != KErrNone)
+		{
+		return r;
+		}
+	return Controller.Create();
+	}