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