// Copyright (c) 2004-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:
// bsptemplate/asspvariant/template_assp/dmapsl.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);
pD->iDestAddr = (dst.iFlags & KDmaPhysAddr) ? dst.iAddr : Epoc::LinearToPhysical(dst.iAddr);
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();
}