/****************************************************************************** * * Copyright (C) 2009 - 2014 Xilinx, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Use of the Software is limited solely to applications: * (a) running on a Xilinx device, or * (b) that interact with a Xilinx device through a bus or interconnect. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of the Xilinx shall not be used * in advertising or otherwise to promote the sale, use or other dealings in * this Software without prior written authorization from Xilinx. * ******************************************************************************/ /****************************************************************************/ /** * * @file xdmaps.c * * This file contains the implementation of the interface functions for XDmaPs * driver. Refer to the header file xdmaps.h for more detailed information. * *
* MODIFICATION HISTORY:
*
* Ver   Who  	Date     Changes
* ----- ------ -------- ----------------------------------------------
* 1.00	hbm    08/19/2010 First Release
* 1.00  nm     05/25/2011 Updated for minor doxygen corrections
* 1.02a sg     05/16/2012 Made changes for doxygen and moved some function
*			  header from the xdmaps.h file to xdmaps.c file
*			  Other cleanup for coding guidelines and CR 657109
*			  and CR 657898
* 1.03a sg     07/16/2012 changed inline to __inline for CR665681
* 1.04a nm     10/22/2012 Fixed CR# 681671.
* 1.05a nm     04/15/2013 Fixed CR# 704396. Removed warnings when compiled
*			  with -Wall and -Wextra option in bsp.
*	       05/01/2013 Fixed CR# 700189. Changed XDmaPs_BuildDmaProg()
*			  function description.
*			  Fixed CR# 704396. Removed unused variables
*			  UseM2MByte & MemBurstLen from XDmaPs_BuildDmaProg()
*			  function.
* 1.07a asa    11/02/13. Made changes to fix compilation issues for iarcc.
*			   Removed the PDBG prints. By default they were always
*			   defined out and never used. The PDBG is non-standard for
*			   Xilinx drivers and no other driver does something similar.
*			   Since there is no easy way to fix compilation issues with
*			   the IARCC compiler around PDBG, it is better to remove it.
*			   Users can always use xil_printfs if they want to debug.
* 2.01 kpc    08/23/14   Fixed the IAR compiler reported errors
* 
* *****************************************************************************/ /***************************** Include Files ********************************/ #include #include "xstatus.h" #include "xdmaps.h" #include "xil_io.h" #include "xil_cache.h" #include "xil_printf.h" /************************** Constant Definitions ****************************/ /* The following constant defines the amount of error that is allowed for * a specified baud rate. This error is the difference between the actual * baud rate that will be generated using the specified clock and the * desired baud rate. */ /**************************** Type Definitions ******************************/ #ifdef __ICCARM__ #define INLINE #else #define INLINE __inline #endif /***************** Macros (Inline Functions) Definitions ********************/ /************************** Function Prototypes *****************************/ static int XDmaPs_Exec_DMAKILL(u32 BaseAddr, unsigned int Channel, unsigned int Thread); static void XDmaPs_BufPool_Free(XDmaPs_ProgBuf *Pool, void *Buf); static int XDmaPs_Exec_DMAGO(u32 BaseAddr, unsigned int Channel, u32 DmaProg); static void XDmaPs_DoneISR_n(XDmaPs *InstPtr, unsigned Channel); static void *XDmaPs_BufPool_Allocate(XDmaPs_ProgBuf *Pool); static int XDmaPs_BuildDmaProg(unsigned Channel, XDmaPs_Cmd *Cmd, unsigned CacheLength); static void XDmaPs_Print_DmaProgBuf(char *Buf, int Length); /************************** Variable Definitions ****************************/ /****************************************************************************/ /** * * Initializes a specific XDmaPs instance such that it is ready to be used. * The data format of the device is setup for 8 data bits, 1 stop bit, and no * parity by default. The baud rate is set to a default value specified by * Config->DefaultBaudRate if set, otherwise it is set to 19.2K baud. The * receive FIFO threshold is set for 8 bytes. The default operating mode of the * driver is polled mode. * * @param InstPtr is a pointer to the XDmaPs instance. * @param Config is a reference to a structure containing information * about a specific XDmaPs driver. * @param EffectiveAddr is the device base address in the virtual memory * address space. The caller is responsible for keeping the * address mapping from EffectiveAddr to the device physical base * address unchanged once this function is invoked. Unexpected * errors may occur if the address mapping changes after this * function is called. If address translation is not used, pass in * the physical address instead. * * @return * * - XST_SUCCESS on initialization completion * * @note None. * *****************************************************************************/ int XDmaPs_CfgInitialize(XDmaPs *InstPtr, XDmaPs_Config *Config, u32 EffectiveAddr) { int Status = XST_SUCCESS; unsigned int CacheLength = 0; u32 CfgReg; unsigned Channel; XDmaPs_ChannelData *ChanData; /* * Assert validates the input arguments */ Xil_AssertNonvoid(InstPtr != NULL); Xil_AssertNonvoid(Config != NULL); /* * Setup the driver instance using passed in parameters */ InstPtr->Config.DeviceId = Config->DeviceId; InstPtr->Config.BaseAddress = EffectiveAddr; CfgReg = XDmaPs_ReadReg(EffectiveAddr, XDMAPS_CR1_OFFSET); CacheLength = CfgReg & XDMAPS_CR1_I_CACHE_LEN_MASK; if (CacheLength < 2 || CacheLength > 5) CacheLength = 0; else CacheLength = 1 << CacheLength; InstPtr->CacheLength = CacheLength; memset(InstPtr->Chans, 0, sizeof(XDmaPs_ChannelData[XDMAPS_CHANNELS_PER_DEV])); for (Channel = 0; Channel < XDMAPS_CHANNELS_PER_DEV; Channel++) { ChanData = InstPtr->Chans + Channel; ChanData->ChanId = Channel; ChanData->DevId = Config->DeviceId; } InstPtr->IsReady = 1; return Status; } /****************************************************************************/ /** * * Reset the DMA Manager. * * @param InstPtr is the DMA instance. * * @return 0 on success, -1 on time out * * @note None. * *****************************************************************************/ int XDmaPs_ResetManager(XDmaPs *InstPtr) { int Status; Status = XDmaPs_Exec_DMAKILL(InstPtr->Config.BaseAddress, 0, 0); return Status; } /****************************************************************************/ /** * * Reset the specified DMA Channel. * * @param InstPtr is the DMA instance. * @param Channel is the channel to be reset. * * @return 0 on success, -1 on time out * * @note None. * *****************************************************************************/ int XDmaPs_ResetChannel(XDmaPs *InstPtr, unsigned int Channel) { int Status; Status = XDmaPs_Exec_DMAKILL(InstPtr->Config.BaseAddress, Channel, 1); return Status; } /*****************************************************************************/ /** * * Driver fault interrupt service routine * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_FaultISR(XDmaPs *InstPtr) { void *DmaProgBuf; u32 Fsm; /* Fault status DMA manager register value */ u32 Fsc; /* Fault status DMA channel register value */ u32 FaultType; /* Fault type DMA manager register value */ u32 BaseAddr = InstPtr->Config.BaseAddress; u32 Pc; /* DMA Pc or channel Pc */ XDmaPs_ChannelData *ChanData; unsigned Chan; unsigned DevId; XDmaPs_Cmd *DmaCmd; Fsm = XDmaPs_ReadReg(BaseAddr, XDMAPS_FSM_OFFSET) & 0x01; Fsc = XDmaPs_ReadReg(BaseAddr, XDMAPS_FSC_OFFSET) & 0xFF; DevId = InstPtr->Config.DeviceId; if (Fsm) { /* * if DMA manager is fault */ FaultType = XDmaPs_ReadReg(BaseAddr, XDMAPS_FTM_OFFSET); Pc = XDmaPs_ReadReg(BaseAddr, XDMAPS_DPC_OFFSET); xil_printf("PL330 device %d fault with type: %x at Pc %x\n", DevId, FaultType, Pc); /* kill the DMA manager thread */ /* Should we disable interrupt?*/ XDmaPs_Exec_DMAKILL(BaseAddr, 0, 0); } /* * check which channel faults and kill the channel thread */ for (Chan = 0; Chan < XDMAPS_CHANNELS_PER_DEV; Chan++) { if (Fsc & (0x01 << Chan)) { FaultType = XDmaPs_ReadReg(BaseAddr, XDmaPs_FTCn_OFFSET(Chan)); Pc = XDmaPs_ReadReg(BaseAddr, XDmaPs_CPCn_OFFSET(Chan)); /* kill the channel thread */ /* Should we disable interrupt? */ XDmaPs_Exec_DMAKILL(BaseAddr, Chan, 1); /* * get the fault type and fault Pc and invoke the * fault callback. */ ChanData = InstPtr->Chans + Chan; DmaCmd = ChanData->DmaCmdToHw; /* Should we check DmaCmd is not null */ DmaCmd->DmaStatus = -1; DmaCmd->ChanFaultType = FaultType; DmaCmd->ChanFaultPCAddr = Pc; ChanData->DmaCmdFromHw = DmaCmd; ChanData->DmaCmdToHw = NULL; if (!ChanData->HoldDmaProg) { DmaProgBuf = (void *)DmaCmd->GeneratedDmaProg; if (DmaProgBuf) XDmaPs_BufPool_Free(ChanData->ProgBufPool, DmaProgBuf); DmaCmd->GeneratedDmaProg = NULL; } if (InstPtr->FaultHandler) InstPtr->FaultHandler(Chan, DmaCmd, InstPtr->FaultRef); } } } /*****************************************************************************/ /** * * Set the done handler for a channel. * * @param InstPtr is the DMA instance. * @param Channel is the channel number. * @param DoneHandler is the done interrupt handler. * @param CallbackRef is the callback reference data. * * @return None. * * @note None. * ******************************************************************************/ int XDmaPs_SetDoneHandler(XDmaPs *InstPtr, unsigned Channel, XDmaPsDoneHandler DoneHandler, void *CallbackRef) { XDmaPs_ChannelData *ChanData; Xil_AssertNonvoid(InstPtr != NULL); if (Channel >= XDMAPS_CHANNELS_PER_DEV) return XST_FAILURE; ChanData = InstPtr->Chans + Channel; ChanData->DoneHandler = DoneHandler; ChanData->DoneRef = CallbackRef; return 0; } /*****************************************************************************/ /** * * Set the fault handler for a channel. * * @param InstPtr is the DMA instance. * @param FaultHandler is the fault interrupt handler. * @param CallbackRef is the callback reference data. * * @return None. * * @note None. * ******************************************************************************/ int XDmaPs_SetFaultHandler(XDmaPs *InstPtr, XDmaPsFaultHandler FaultHandler, void *CallbackRef) { Xil_AssertNonvoid(InstPtr != NULL); InstPtr->FaultHandler = FaultHandler; InstPtr->FaultRef = CallbackRef; return XST_SUCCESS; } /****************************************************************************/ /** * Construction function for DMAEND instruction. This function fills the program * buffer with the constructed instruction. * * @param DmaProg the DMA program buffer, it's the starting address for * the instruction being constructed * * @return The number of bytes for this instruction which is 1. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMAEND(char *DmaProg) { /* * DMAEND encoding: * 7 6 5 4 3 2 1 0 * 0 0 0 0 0 0 0 0 */ *DmaProg = 0x0; return 1; } INLINE void XDmaPs_Memcpy4(char *Dst, char *Src) { *Dst = *Src; *(Dst + 1) = *(Src + 1); *(Dst + 2) = *(Src + 2); *(Dst + 3) = *(Src + 3); } /****************************************************************************/ /** * * Construction function for DMAGO instruction. This function fills the program * buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * @param Cn is the Channel number, 0 - 7 * @param Imm is 32-bit immediate number written to the Channel Program * Counter. * @param Ns is Non-secure flag. If Ns is 1, the DMA channel operates in * the Non-secure state. If Ns is 0, the execution depends on the * security state of the DMA manager: * DMA manager is in the Secure state, DMA channel operates in the * Secure state. * DMA manager is in the Non-secure state, DMAC aborts. * * @return The number of bytes for this instruction which is 6. * * @note None * *****************************************************************************/ INLINE int XDmaPs_Instr_DMAGO(char *DmaProg, unsigned int Cn, u32 Imm, unsigned int Ns) { /* * DMAGO encoding: * 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 * 0 0 0 0 0 |cn[2:0]| 1 0 1 0 0 0 ns 0 * * 47 ... 16 * imm[32:0] */ *DmaProg = 0xA0 | ((Ns << 1) & 0x02); *(DmaProg + 1) = (u8)(Cn & 0x07); // *((u32 *)(DmaProg + 2)) = Imm; XDmaPs_Memcpy4(DmaProg + 2, (char *)&Imm); /* success */ return 6; } /****************************************************************************/ /** * * Construction function for DMALD instruction. This function fills the program * buffer with the constructed instruction. * * @param DmaProg the DMA program buffer, it's the starting address for the * instruction being constructed * * @return The number of bytes for this instruction which is 1. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMALD(char *DmaProg) { /* * DMALD encoding * 7 6 5 4 3 2 1 0 * 0 0 0 0 0 1 bs x * * Note: this driver doesn't support conditional load or store, * so the bs bit is 0 and x bit is 0. */ *DmaProg = 0x04; return 1; } /****************************************************************************/ /** * * Construction function for DMALP instruction. This function fills the program * buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * @param Lc is the Loop counter register, can either be 0 or 1. * @param LoopIterations: the number of interations, LoopInterations - 1 * will be encoded in the DMALP instruction. * * @return The number of bytes for this instruction which is 2. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMALP(char *DmaProg, unsigned Lc, unsigned LoopIterations) { /* * DMALP encoding * 15 ... 8 7 6 5 4 3 2 1 0 * | iter[7:0] |0 0 1 0 0 0 lc 0 */ *DmaProg = (u8)(0x20 | ((Lc & 1) << 1)); *(DmaProg + 1) = (u8)(LoopIterations - 1); return 2; } /****************************************************************************/ /** * * Construction function for DMALPEND instruction. This function fills the * program buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * @param BodyStart is the starting address of the loop body. It is used * to calculate the bytes of backward jump. * @param Lc is the Loop counter register, can either be 0 or 1. * * @return The number of bytes for this instruction which is 2. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMALPEND(char *DmaProg, char *BodyStart, unsigned Lc) { /* * DMALPEND encoding * 15 ... 8 7 6 5 4 3 2 1 0 * | backward_jump[7:0] |0 0 1 nf 1 lc bs x * * lc: loop counter * nf is for loop forever. The driver does not support loop forever, * so nf is 1. * The driver does not support conditional LPEND, so bs is 0, x is 0. */ *DmaProg = 0x38 | ((Lc & 1) << 2); *(DmaProg + 1) = (u8)(DmaProg - BodyStart); return 2; } /* * Register number for the DMAMOV instruction */ #define XDMAPS_MOV_SAR 0x0 #define XDMAPS_MOV_CCR 0x1 #define XDMAPS_MOV_DAR 0x2 /****************************************************************************/ /** * * Construction function for DMAMOV instruction. This function fills the * program buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * @param Rd is the register id, 0 for SAR, 1 for CCR, and 2 for DAR * @param Imm is the 32-bit immediate number * * @return The number of bytes for this instruction which is 6. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMAMOV(char *DmaProg, unsigned Rd, u32 Imm) { /* * DMAMOV encoding * 15 4 3 2 1 10 ... 8 7 6 5 4 3 2 1 0 * 0 0 0 0 0 |rd[2:0]|1 0 1 1 1 1 0 0 * * 47 ... 16 * imm[32:0] * * rd: b000 for SAR, b001 CCR, b010 DAR */ *DmaProg = 0xBC; *(DmaProg + 1) = Rd & 0x7; // *((u32 *)(DmaProg + 2)) = Imm; XDmaPs_Memcpy4(DmaProg + 2, (char *)&Imm); return 6; } /****************************************************************************/ /** * * Construction function for DMANOP instruction. This function fills the * program buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * @return The number of bytes for this instruction which is 1. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMANOP(char *DmaProg) { /* * DMANOP encoding * 7 6 5 4 3 2 1 0 * 0 0 0 1 1 0 0 0 */ *DmaProg = 0x18; return 1; } /****************************************************************************/ /** * * Construction function for DMARMB instruction. This function fills the * program buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * * @return The number of bytes for this instruction which is 1. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMARMB(char *DmaProg) { /* * DMARMB encoding * 7 6 5 4 3 2 1 0 * 0 0 0 1 0 0 1 0 */ *DmaProg = 0x12; return 1; } /****************************************************************************/ /** * * Construction function for DMASEV instruction. This function fills the * program buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * @param EventNumber is the Event number to signal. * * @return The number of bytes for this instruction which is 2. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMASEV(char *DmaProg, unsigned int EventNumber) { /* * DMASEV encoding * 15 4 3 2 1 10 9 8 7 6 5 4 3 2 1 0 * |event[4:0]| 0 0 0 0 0 1 1 0 1 0 0 */ *DmaProg = 0x34; *(DmaProg + 1) = (u8)(EventNumber << 3); return 2; } /****************************************************************************/ /** * * Construction function for DMAST instruction. This function fills the * program buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * * @return The number of bytes for this instruction which is 1. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMAST(char *DmaProg) { /* * DMAST encoding * 7 6 5 4 3 2 1 0 * 0 0 0 0 1 0 bs x * * Note: this driver doesn't support conditional load or store, * so the bs bit is 0 and x bit is 0. */ *DmaProg = 0x08; return 1; } /****************************************************************************/ /** * * Construction function for DMAWMB instruction. This function fills the * program buffer with the constructed instruction. * * @param DmaProg is the DMA program buffer, it's the starting address * for the instruction being constructed * * @return The number of bytes for this instruction which is 1. * * @note None. * *****************************************************************************/ INLINE int XDmaPs_Instr_DMAWMB(char *DmaProg) { /* * DMAWMB encoding * 7 6 5 4 3 2 1 0 * 0 0 0 1 0 0 1 0 */ *DmaProg = 0x13; return 1; } /****************************************************************************/ /** * * Conversion function from the endian swap size to the bit encoding of the CCR * * @param EndianSwapSize is the endian swap size, in terms of bits, it * could be 8, 16, 32, 64, or 128(We are using DMA assembly syntax) * * @return The endian swap size bit encoding for the CCR. * * @note None. * *****************************************************************************/ INLINE unsigned XDmaPs_ToEndianSwapSizeBits(unsigned int EndianSwapSize) { switch (EndianSwapSize) { case 0: case 8: return 0; case 16: return 1; case 32: return 2; case 64: return 3; case 128: return 4; default: return 0; } } /****************************************************************************/ /** * * Conversion function from the burst size to the bit encoding of the CCR * * @param BurstSize is the burst size. It's the data width. * In terms of bytes, it could be 1, 2, 4, 8, 16, 32, 64, or 128. * It must be no larger than the bus width. * (We are using DMA assembly syntax.) * * @note None. * *****************************************************************************/ INLINE unsigned XDmaPs_ToBurstSizeBits(unsigned BurstSize) { switch (BurstSize) { case 1: return 0; case 2: return 1; case 4: return 2; case 8: return 3; case 16: return 4; case 32: return 5; case 64: return 6; case 128: return 7; default: return 0; } } /****************************************************************************/ /** * * Conversion function from PL330 bus transfer descriptors to CCR value. All the * values passed to the functions are in terms of assembly languages, not in * terms of the register bit encoding. * * @param ChanCtrl is the Instance of XDmaPs_ChanCtrl. * * @return The 32-bit CCR value. * * @note None. * *****************************************************************************/ u32 XDmaPs_ToCCRValue(XDmaPs_ChanCtrl *ChanCtrl) { /* * Channel Control Register encoding * [31:28] - endian_swap_size * [27:25] - dst_cache_ctrl * [24:22] - dst_prot_ctrl * [21:18] - dst_burst_len * [17:15] - dst_burst_size * [14] - dst_inc * [13:11] - src_cache_ctrl * [10:8] - src_prot_ctrl * [7:4] - src_burst_len * [3:1] - src_burst_size * [0] - src_inc */ unsigned es = XDmaPs_ToEndianSwapSizeBits(ChanCtrl->EndianSwapSize); unsigned dst_burst_size = XDmaPs_ToBurstSizeBits(ChanCtrl->DstBurstSize); unsigned dst_burst_len = (ChanCtrl->DstBurstLen - 1) & 0x0F; unsigned dst_cache_ctrl = (ChanCtrl->DstCacheCtrl & 0x03) | ((ChanCtrl->DstCacheCtrl & 0x08) >> 1); unsigned dst_prot_ctrl = ChanCtrl->DstProtCtrl & 0x07; unsigned dst_inc_bit = ChanCtrl->DstInc & 1; unsigned src_burst_size = XDmaPs_ToBurstSizeBits(ChanCtrl->SrcBurstSize); unsigned src_burst_len = (ChanCtrl->SrcBurstLen - 1) & 0x0F; unsigned src_cache_ctrl = (ChanCtrl->SrcCacheCtrl & 0x03) | ((ChanCtrl->SrcCacheCtrl & 0x08) >> 1); unsigned src_prot_ctrl = ChanCtrl->SrcProtCtrl & 0x07; unsigned src_inc_bit = ChanCtrl->SrcInc & 1; u32 ccr_value = (es << 28) | (dst_cache_ctrl << 25) | (dst_prot_ctrl << 22) | (dst_burst_len << 18) | (dst_burst_size << 15) | (dst_inc_bit << 14) | (src_cache_ctrl << 11) | (src_prot_ctrl << 8) | (src_burst_len << 4) | (src_burst_size << 1) | (src_inc_bit); return ccr_value; } /****************************************************************************/ /** * Construct a loop with only DMALD and DMAST as the body using loop counter 0. * The function also makes sure the loop body and the lpend is in the same * cache line. * * @param DmaProgStart is the very start address of the DMA program. * This is used to calculate whether the loop is in a cache line. * @param CacheLength is the icache line length, in terms of bytes. * If it's zero, the performance enhancement feature will be * turned off. * @param DmaProgLoopStart The starting address of the loop (DMALP). * @param LoopCount The inner loop count. Loop count - 1 will be used to * initialize the loop counter. * * @return The number of bytes the loop has. * * @note None. * *****************************************************************************/ int XDmaPs_ConstructSingleLoop(char *DmaProgStart, int CacheLength, char *DmaProgLoopStart, int LoopCount) { int CacheStartOffset; int CacheEndOffset; int NumNops; char *DmaProgBuf = DmaProgLoopStart; DmaProgBuf += XDmaPs_Instr_DMALP(DmaProgBuf, 0, LoopCount); if (CacheLength > 0) { /* * the CacheLength > 0 switch is ued to turn on/off nop * insertion */ CacheStartOffset = DmaProgBuf - DmaProgStart; CacheEndOffset = CacheStartOffset + 3; /* * check whether the body and lpend fit in one cache line */ if (CacheStartOffset / CacheLength != CacheEndOffset / CacheLength) { /* insert the nops */ NumNops = CacheLength - CacheStartOffset % CacheLength; while (NumNops--) { DmaProgBuf += XDmaPs_Instr_DMANOP(DmaProgBuf); } } } DmaProgBuf += XDmaPs_Instr_DMALD(DmaProgBuf); DmaProgBuf += XDmaPs_Instr_DMAST(DmaProgBuf); DmaProgBuf += XDmaPs_Instr_DMALPEND(DmaProgBuf, DmaProgBuf - 2, 0); return DmaProgBuf - DmaProgLoopStart; } /****************************************************************************/ /** * Construct a nested loop with only DMALD and DMAST in the inner loop body. * It uses loop counter 1 for the outer loop and loop counter 0 for the * inner loop. * * @param DmaProgStart is the very start address of the DMA program. * This is used to calculate whether the loop is in a cache line. * @param CacheLength is the icache line length, in terms of bytes. * If it's zero, the performance enhancement feature will be * turned off. * @param DmaProgLoopStart The starting address of the loop (DMALP). * @param LoopCountOuter The outer loop count. Loop count - 1 will be * used to initialize the loop counter. * @param LoopCountInner The inner loop count. Loop count - 1 will be * used to initialize the loop counter. * * @return The number byes the nested loop program has. * * @note None. * *****************************************************************************/ int XDmaPs_ConstructNestedLoop(char *DmaProgStart, int CacheLength, char *DmaProgLoopStart, unsigned int LoopCountOuter, unsigned int LoopCountInner) { int CacheStartOffset; int CacheEndOffset; int NumNops; char *InnerLoopStart; char *DmaProgBuf = DmaProgLoopStart; DmaProgBuf += XDmaPs_Instr_DMALP(DmaProgBuf, 1, LoopCountOuter); InnerLoopStart = DmaProgBuf; if (CacheLength > 0) { /* * the CacheLength > 0 switch is ued to turn on/off nop * insertion */ if (CacheLength < 8) { /* * if the cache line is too small to fit both loops * just align the inner loop */ DmaProgBuf += XDmaPs_ConstructSingleLoop(DmaProgStart, CacheLength, DmaProgBuf, LoopCountInner); /* outer loop end */ DmaProgBuf += XDmaPs_Instr_DMALPEND(DmaProgBuf, InnerLoopStart, 1); /* * the nested loop is constructed for * smaller cache line */ return DmaProgBuf - DmaProgLoopStart; } /* * Now let's handle the case where a cache line can * fit the nested loops. */ CacheStartOffset = DmaProgBuf - DmaProgStart; CacheEndOffset = CacheStartOffset + 7; /* * check whether the body and lpend fit in one cache line */ if (CacheStartOffset / CacheLength != CacheEndOffset / CacheLength) { /* insert the nops */ NumNops = CacheLength - CacheStartOffset % CacheLength; while (NumNops--) { DmaProgBuf += XDmaPs_Instr_DMANOP(DmaProgBuf); } } } /* insert the inner DMALP */ DmaProgBuf += XDmaPs_Instr_DMALP(DmaProgBuf, 0, LoopCountInner); /* DMALD and DMAST instructions */ DmaProgBuf += XDmaPs_Instr_DMALD(DmaProgBuf); DmaProgBuf += XDmaPs_Instr_DMAST(DmaProgBuf); /* inner DMALPEND */ DmaProgBuf += XDmaPs_Instr_DMALPEND(DmaProgBuf, DmaProgBuf - 2, 0); /* outer DMALPEND */ DmaProgBuf += XDmaPs_Instr_DMALPEND(DmaProgBuf, InnerLoopStart, 1); /* return the number of bytes */ return DmaProgBuf - DmaProgLoopStart; } /* * [31:28] endian_swap_size b0000 * [27:25] dst_cache_ctrl b000 * [24:22] dst_prot_ctrl b000 * [21:18] dst_burst_len b0000 * [17:15] dst_burst_size b000 * [14] dst_inc b0 * [27:25] src_cache_ctrl b000 * [24:22] src_prot_ctrl b000 * [21:18] src_burst_len b0000 * [17:15] src_burst_size b000 * [14] src_inc b0 */ #define XDMAPS_CCR_SINGLE_BYTE (0x0) #define XDMAPS_CCR_M2M_SINGLE_BYTE ((0x1 << 14) | 0x1) /****************************************************************************/ /** * * Construct the DMA program based on the descriptions of the DMA transfer. * The function handles memory to memory DMA transfers. * It also handles unalgined head and small amount of residue tail. * * @param Channel DMA channel number * @param Cmd is the DMA command. * @param CacheLength is the icache line length, in terms of bytes. * If it's zero, the performance enhancement feature will be * turned off. * * @returns The number of bytes for the program. * * @note None. * *****************************************************************************/ static int XDmaPs_BuildDmaProg(unsigned Channel, XDmaPs_Cmd *Cmd, unsigned CacheLength) { /* * unpack arguments */ char *DmaProgBuf = (char *)Cmd->GeneratedDmaProg; unsigned DevChan = Channel; unsigned long DmaLength = Cmd->BD.Length; u32 SrcAddr = Cmd->BD.SrcAddr; unsigned SrcInc = Cmd->ChanCtrl.SrcInc; u32 DstAddr = Cmd->BD.DstAddr; unsigned DstInc = Cmd->ChanCtrl.DstInc; char *DmaProgStart = DmaProgBuf; unsigned int BurstBytes; unsigned int LoopCount; unsigned int LoopCount1 = 0; unsigned int LoopResidue = 0; unsigned int TailBytes; unsigned int TailWords; int DmaProgBytes; u32 CCRValue; unsigned int Unaligned; unsigned int UnalignedCount; unsigned int MemBurstSize = 1; u32 MemAddr = 0; unsigned int Index; unsigned int SrcUnaligned = 0; unsigned int DstUnaligned = 0; XDmaPs_ChanCtrl *ChanCtrl; XDmaPs_ChanCtrl WordChanCtrl; static XDmaPs_ChanCtrl Mem2MemByteCC; Mem2MemByteCC.EndianSwapSize = 0; Mem2MemByteCC.DstCacheCtrl = 0; Mem2MemByteCC.DstProtCtrl = 0; Mem2MemByteCC.DstBurstLen = 1; Mem2MemByteCC.DstBurstSize = 1; Mem2MemByteCC.DstInc = 1; Mem2MemByteCC.SrcCacheCtrl = 0; Mem2MemByteCC.SrcProtCtrl = 0; Mem2MemByteCC.SrcBurstLen = 1; Mem2MemByteCC.SrcBurstSize = 1; Mem2MemByteCC.SrcInc = 1; ChanCtrl = &Cmd->ChanCtrl; /* insert DMAMOV for SAR and DAR */ DmaProgBuf += XDmaPs_Instr_DMAMOV(DmaProgBuf, XDMAPS_MOV_SAR, SrcAddr); DmaProgBuf += XDmaPs_Instr_DMAMOV(DmaProgBuf, XDMAPS_MOV_DAR, DstAddr); if (ChanCtrl->SrcInc) SrcUnaligned = SrcAddr % ChanCtrl->SrcBurstSize; if (ChanCtrl->DstInc) DstUnaligned = DstAddr % ChanCtrl->DstBurstSize; if ((SrcUnaligned && DstInc) || (DstUnaligned && SrcInc)) { ChanCtrl = &Mem2MemByteCC; } if (ChanCtrl->SrcInc) { MemBurstSize = ChanCtrl->SrcBurstSize; MemAddr = SrcAddr; } else if (ChanCtrl->DstInc) { MemBurstSize = ChanCtrl->DstBurstSize; MemAddr = DstAddr; } /* check whether the head is aligned or not */ Unaligned = MemAddr % MemBurstSize; if (Unaligned) { /* if head is unaligned, transfer head in bytes */ UnalignedCount = MemBurstSize - Unaligned; CCRValue = XDMAPS_CCR_SINGLE_BYTE | (SrcInc & 1) | ((DstInc & 1) << 14); DmaProgBuf += XDmaPs_Instr_DMAMOV(DmaProgBuf, XDMAPS_MOV_CCR, CCRValue); for (Index = 0; Index < UnalignedCount; Index++) { DmaProgBuf += XDmaPs_Instr_DMALD(DmaProgBuf); DmaProgBuf += XDmaPs_Instr_DMAST(DmaProgBuf); } DmaLength -= UnalignedCount; } /* now the burst transfer part */ CCRValue = XDmaPs_ToCCRValue(ChanCtrl); DmaProgBuf += XDmaPs_Instr_DMAMOV(DmaProgBuf, XDMAPS_MOV_CCR, CCRValue); BurstBytes = ChanCtrl->SrcBurstSize * ChanCtrl->SrcBurstLen; LoopCount = DmaLength / BurstBytes; TailBytes = DmaLength % BurstBytes; /* * the loop count register is 8-bit wide, so if we need * a larger loop, we need to have nested loops */ if (LoopCount > 256) { LoopCount1 = LoopCount / 256; if (LoopCount1 > 256) { xil_printf("DMA operation cannot fit in a 2-level " "loop for channel %d, please reduce the " "DMA length or increase the burst size or " "length", Channel); return 0; } LoopResidue = LoopCount % 256; if (LoopCount1 > 1) DmaProgBuf += XDmaPs_ConstructNestedLoop(DmaProgStart, CacheLength, DmaProgBuf, LoopCount1, 256); else DmaProgBuf += XDmaPs_ConstructSingleLoop(DmaProgStart, CacheLength, DmaProgBuf, 256); /* there will be some that cannot be covered by * nested loops */ LoopCount = LoopResidue; } if (LoopCount > 0) { DmaProgBuf += XDmaPs_ConstructSingleLoop(DmaProgStart, CacheLength, DmaProgBuf, LoopCount); } if (TailBytes) { /* handle the tail */ TailWords = TailBytes / MemBurstSize; TailBytes = TailBytes % MemBurstSize; if (TailWords) { WordChanCtrl = *ChanCtrl; /* * if we can transfer the tail in words, we will * transfer words as much as possible */ WordChanCtrl.SrcBurstSize = MemBurstSize; WordChanCtrl.SrcBurstLen = 1; WordChanCtrl.DstBurstSize = MemBurstSize; WordChanCtrl.DstBurstLen = 1; /* * the burst length is 1 */ CCRValue = XDmaPs_ToCCRValue(&WordChanCtrl); DmaProgBuf += XDmaPs_Instr_DMAMOV(DmaProgBuf, XDMAPS_MOV_CCR, CCRValue); DmaProgBuf += XDmaPs_ConstructSingleLoop(DmaProgStart, CacheLength, DmaProgBuf, TailWords); } if (TailBytes) { /* * for the rest, we'll tranfer in bytes */ /* * So far just to be safe, the tail bytes * are transfered in a loop. We can optimize a little * to perform a burst. */ CCRValue = XDMAPS_CCR_SINGLE_BYTE | (SrcInc & 1) | ((DstInc & 1) << 14); DmaProgBuf += XDmaPs_Instr_DMAMOV(DmaProgBuf, XDMAPS_MOV_CCR, CCRValue); DmaProgBuf += XDmaPs_ConstructSingleLoop(DmaProgStart, CacheLength, DmaProgBuf, TailBytes); } } DmaProgBuf += XDmaPs_Instr_DMASEV(DmaProgBuf, DevChan); DmaProgBuf += XDmaPs_Instr_DMAEND(DmaProgBuf); DmaProgBytes = DmaProgBuf - DmaProgStart; Xil_DCacheFlushRange((u32)DmaProgStart, DmaProgBytes); return DmaProgBytes; } /****************************************************************************/ /** * * Generate a DMA program based for the DMA command, the buffer will be pointed * by the GeneratedDmaProg field of the command. * * @param InstPtr is then DMA instance. * @param Channel is the DMA channel number. * @param Cmd is the DMA command. * * @return - XST_SUCCESS on success. * - XST_FAILURE if it fails * * @note None. * *****************************************************************************/ int XDmaPs_GenDmaProg(XDmaPs *InstPtr, unsigned int Channel, XDmaPs_Cmd *Cmd) { void *Buf; int ProgLen; XDmaPs_ChannelData *ChanData; XDmaPs_ChanCtrl *ChanCtrl; Xil_AssertNonvoid(InstPtr != NULL); Xil_AssertNonvoid(Cmd != NULL); if (Channel > XDMAPS_CHANNELS_PER_DEV) return XST_FAILURE; ChanData = InstPtr->Chans + Channel; ChanCtrl = &Cmd->ChanCtrl; if (ChanCtrl->SrcBurstSize * ChanCtrl->SrcBurstLen != ChanCtrl->DstBurstSize * ChanCtrl->DstBurstLen) { return XST_FAILURE; } /* * unaligned fixed address is not supported */ if (!ChanCtrl->SrcInc && Cmd->BD.SrcAddr % ChanCtrl->SrcBurstSize) { return XST_FAILURE; } if (!ChanCtrl->DstInc && Cmd->BD.DstAddr % ChanCtrl->DstBurstSize) { return XST_FAILURE; } Buf = XDmaPs_BufPool_Allocate(ChanData->ProgBufPool); if (Buf == NULL) { return XST_FAILURE; } Cmd->GeneratedDmaProg = Buf; ProgLen = XDmaPs_BuildDmaProg(Channel, Cmd, InstPtr->CacheLength); Cmd->GeneratedDmaProgLength = ProgLen; #ifdef XDMAPS_DEBUG XDmaPs_Print_DmaProg(Cmd); #endif if (ProgLen <= 0) { /* something wrong, release the buffer */ XDmaPs_BufPool_Free(ChanData->ProgBufPool, Buf); Cmd->GeneratedDmaProgLength = 0; Cmd->GeneratedDmaProg = NULL; return XST_FAILURE; } return XST_SUCCESS; } /****************************************************************************/ /** * Free the DMA program buffer that is pointed by the GeneratedDmaProg field * of the command. * * @param InstPtr is then DMA instance. * @param Channel is the DMA channel number. * @param Cmd is the DMA command. * * @return XST_SUCCESS on success. * XST_FAILURE if there is any error. * * @note None. * ****************************************************************************/ int XDmaPs_FreeDmaProg(XDmaPs *InstPtr, unsigned int Channel, XDmaPs_Cmd *Cmd) { void *Buf; XDmaPs_ChannelData *ChanData; Xil_AssertNonvoid(InstPtr != NULL); Xil_AssertNonvoid(Cmd != NULL); if (Channel > XDMAPS_CHANNELS_PER_DEV) return XST_FAILURE; Buf = (void *)Cmd->GeneratedDmaProg; ChanData = InstPtr->Chans + Channel; if (Buf) { XDmaPs_BufPool_Free(ChanData->ProgBufPool, Buf); Cmd->GeneratedDmaProg = 0; Cmd->GeneratedDmaProgLength = 0; } return XST_SUCCESS; } /****************************************************************************/ /** * * Start a DMA command. The command can only be invoked when the channel * is idle. The driver takes the command, generates DMA program if needed, * then pass the program to DMAC to execute. * * @param InstPtr is then DMA instance. * @param Channel is the DMA channel number. * @param Cmd is the DMA command. * @param HoldDmaProg is tag indicating whether the driver can release * the allocated DMA buffer or not. If a user wants to examine the * generated DMA program, the flag should be set to 1. After the * DMA program is finished, a user needs to explicity free the * buffer. * * @return * - XST_SUCCESS on success * - XST_DEVICE_BUSY if DMA is busy * - XST_FAILURE on other failures * * @note None. * ****************************************************************************/ int XDmaPs_Start(XDmaPs *InstPtr, unsigned int Channel, XDmaPs_Cmd *Cmd, int HoldDmaProg) { int Status; u32 DmaProg = 0; u32 Inten; Xil_AssertNonvoid(InstPtr != NULL); Xil_AssertNonvoid(Cmd != NULL); Cmd->DmaStatus = XST_FAILURE; if (XDmaPs_IsActive(InstPtr, Channel)) return XST_DEVICE_BUSY; if (!Cmd->UserDmaProg && !Cmd->GeneratedDmaProg) { Status = XDmaPs_GenDmaProg(InstPtr, Channel, Cmd); if (Status) return XST_FAILURE; } InstPtr->Chans[Channel].HoldDmaProg = HoldDmaProg; if (Cmd->UserDmaProg) DmaProg = (u32)Cmd->UserDmaProg; else if (Cmd->GeneratedDmaProg) DmaProg = (u32)Cmd->GeneratedDmaProg; if (DmaProg) { /* enable the interrupt */ Inten = XDmaPs_ReadReg(InstPtr->Config.BaseAddress, XDMAPS_INTEN_OFFSET); Inten |= 0x01 << Channel; /* set the correpsonding bit */ XDmaPs_WriteReg(InstPtr->Config.BaseAddress, XDMAPS_INTEN_OFFSET, Inten); Inten = XDmaPs_ReadReg(InstPtr->Config.BaseAddress, XDMAPS_INTEN_OFFSET); InstPtr->Chans[Channel].DmaCmdToHw = Cmd; if (Cmd->ChanCtrl.SrcInc) { Xil_DCacheFlushRange(Cmd->BD.SrcAddr, Cmd->BD.Length); } if (Cmd->ChanCtrl.DstInc) { Xil_DCacheInvalidateRange(Cmd->BD.DstAddr, Cmd->BD.Length); } Status = XDmaPs_Exec_DMAGO(InstPtr->Config.BaseAddress, Channel, DmaProg); } else { InstPtr->Chans[Channel].DmaCmdToHw = NULL; Status = XST_FAILURE; } return Status; } /****************************************************************************/ /** * * Checks whether the DMA channel is active or idle. * * @param InstPtr is the DMA instance. * @param Channel is the DMA channel number. * * @return 0: if the channel is idle * 1: otherwise * * @note None. * *****************************************************************************/ int XDmaPs_IsActive(XDmaPs *InstPtr, unsigned int Channel) { Xil_AssertNonvoid(InstPtr != NULL); /* Need to assert Channel is in range */ if (Channel > XDMAPS_CHANNELS_PER_DEV) return 0; return InstPtr->Chans[Channel].DmaCmdToHw != NULL; } /****************************************************************************/ /** * * Allocate a buffer of the DMA program buffer from the pool. * * @param Pool the DMA program pool. * * @return The allocated buffer, NULL if there is any error. * * @note None. * *****************************************************************************/ static void *XDmaPs_BufPool_Allocate(XDmaPs_ProgBuf *Pool) { int Index; Xil_AssertNonvoid(Pool != NULL); for (Index = 0; Index < XDMAPS_MAX_CHAN_BUFS; Index++) { if (!Pool[Index].Allocated) { Pool[Index].Allocated = 1; return Pool[Index].Buf; } } return NULL; } /*****************************************************************************/ /** * * Driver done interrupt service routine for channel 0. We need this done ISR * mainly because the driver needs to release the DMA program buffer. * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_DoneISR_0(XDmaPs *InstPtr) { XDmaPs_DoneISR_n(InstPtr, 0); } /*****************************************************************************/ /** * * Driver done interrupt service routine for channel 1. We need this done ISR * mainly because the driver needs to release the DMA program buffer. * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_DoneISR_1(XDmaPs *InstPtr) { XDmaPs_DoneISR_n(InstPtr, 1); } /*****************************************************************************/ /** * * Driver done interrupt service routine for channel 2. We need this done ISR * mainly because the driver needs to release the DMA program buffer. * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_DoneISR_2(XDmaPs *InstPtr) { XDmaPs_DoneISR_n(InstPtr, 2); } /*****************************************************************************/ /** * * Driver done interrupt service routine for channel 3. We need this done ISR * mainly because the driver needs to release the DMA program buffer. * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_DoneISR_3(XDmaPs *InstPtr) { XDmaPs_DoneISR_n(InstPtr, 3); } /*****************************************************************************/ /** * * Driver done interrupt service routine for channel 4. We need this done ISR * mainly because the driver needs to release the DMA program buffer. * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_DoneISR_4(XDmaPs *InstPtr) { XDmaPs_DoneISR_n(InstPtr, 4); } /*****************************************************************************/ /** * * Driver done interrupt service routine for channel 5. We need this done ISR * mainly because the driver needs to release the DMA program buffer. * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_DoneISR_5(XDmaPs *InstPtr) { XDmaPs_DoneISR_n(InstPtr, 5); } /*****************************************************************************/ /** * * Driver done interrupt service routine for channel 6. We need this done ISR * mainly because the driver needs to release the DMA program buffer. * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_DoneISR_6(XDmaPs *InstPtr) { XDmaPs_DoneISR_n(InstPtr, 6); } /*****************************************************************************/ /** * * Driver done interrupt service routine for channel 7. We need this done ISR * mainly because the driver needs to release the DMA program buffer. * This is the one that connects the GIC * * @param InstPtr is the DMA instance. * * @return None. * * @note None. * ******************************************************************************/ void XDmaPs_DoneISR_7(XDmaPs *InstPtr) { XDmaPs_DoneISR_n(InstPtr, 7); } #ifndef XDMAPS_MAX_WAIT #define XDMAPS_MAX_WAIT 4000 #endif /****************************************************************************/ /** * Use the debug registers to kill the DMA thread. * * @param BaseAddr is DMA device base address. * @param Channel is the DMA channel number. * @param Thread is Debug thread encoding. * 0: DMA manager thread, 1: DMA channel. * * @return 0 on success, -1 on time out * * @note None. * *****************************************************************************/ static int XDmaPs_Exec_DMAKILL(u32 BaseAddr, unsigned int Channel, unsigned int Thread) { u32 DbgInst0; int WaitCount; DbgInst0 = XDmaPs_DBGINST0(0, 0x01, Channel, Thread); /* wait while debug status is busy */ WaitCount = 0; while ((XDmaPs_ReadReg(BaseAddr, XDMAPS_DBGSTATUS_OFFSET) & XDMAPS_DBGSTATUS_BUSY) && (WaitCount < XDMAPS_MAX_WAIT)) WaitCount++; if (WaitCount >= XDMAPS_MAX_WAIT) { /* wait time out */ xil_printf("PL330 device at %x debug status busy time out\n", BaseAddr); return -1; } /* write debug instruction 0 */ XDmaPs_WriteReg(BaseAddr, XDMAPS_DBGINST0_OFFSET, DbgInst0); XDmaPs_WriteReg(BaseAddr, XDMAPS_DBGINST1_OFFSET, 0); /* run the command in DbgInst0 and DbgInst1 */ XDmaPs_WriteReg(BaseAddr, XDMAPS_DBGCMD_OFFSET, 0); return 0; } /****************************************************************************/ /** * * * Free a buffer of the DMA program buffer. * @param Pool the DMA program pool. * @param Buf the DMA program buffer to be release. * * @return None * * @note None. * *****************************************************************************/ static void XDmaPs_BufPool_Free(XDmaPs_ProgBuf *Pool, void *Buf) { int Index; Xil_AssertVoid(Pool != NULL); for (Index = 0; Index < XDMAPS_MAX_CHAN_BUFS; Index++) { if (Pool[Index].Buf == Buf) { if (Pool[Index].Allocated) { Pool[Index].Allocated = 0; } } } } /*****************************************************************************/ /** * XDmaPs_Exec_DMAGO - Execute the DMAGO to start a channel. * * @param BaseAddr PL330 device base address * @param Channel Channel number for the device * @param DmaProg DMA program starting address, this should be DMA address * * @return 0 on success, -1 on time out * * @note None. * ****************************************************************************/ static int XDmaPs_Exec_DMAGO(u32 BaseAddr, unsigned int Channel, u32 DmaProg) { char DmaGoProg[8]; u32 DbgInst0; u32 DbgInst1; int WaitCount; XDmaPs_Instr_DMAGO(DmaGoProg, Channel, DmaProg, 0); DbgInst0 = XDmaPs_DBGINST0(*(DmaGoProg + 1), *DmaGoProg, 0, 0); DbgInst1 = (u32)DmaProg; /* wait while debug status is busy */ WaitCount = 0; while ((XDmaPs_ReadReg(BaseAddr, XDMAPS_DBGSTATUS_OFFSET) & XDMAPS_DBGSTATUS_BUSY) && (WaitCount < XDMAPS_MAX_WAIT)) { WaitCount++; } if (WaitCount >= XDMAPS_MAX_WAIT) { xil_printf("PL330 device at %x debug status busy time out\r\n", BaseAddr); return -1; } /* write debug instruction 0 */ XDmaPs_WriteReg(BaseAddr, XDMAPS_DBGINST0_OFFSET, DbgInst0); /* write debug instruction 1 */ XDmaPs_WriteReg(BaseAddr, XDMAPS_DBGINST1_OFFSET, DbgInst1); /* wait while the DMA Manager is busy */ WaitCount = 0; while ((XDmaPs_ReadReg(BaseAddr, XDMAPS_DS_OFFSET) & XDMAPS_DS_DMA_STATUS) != XDMAPS_DS_DMA_STATUS_STOPPED && WaitCount <= XDMAPS_MAX_WAIT) { WaitCount++; } if (WaitCount >= XDMAPS_MAX_WAIT) { xil_printf("PL330 device at %x debug status busy time out\r\n", BaseAddr); return -1; } /* run the command in DbgInst0 and DbgInst1 */ XDmaPs_WriteReg(BaseAddr, XDMAPS_DBGCMD_OFFSET, 0); return 0; } /****************************************************************************/ /** * * It's the generic Done ISR. * @param InstPtr is the DMA instance. * @param Channel is the DMA channel numer. * * @return None.* * * @note None. * *****************************************************************************/ static void XDmaPs_DoneISR_n(XDmaPs *InstPtr, unsigned Channel) { void *DmaProgBuf; XDmaPs_ChannelData *ChanData; XDmaPs_Cmd *DmaCmd; //u32 Value; ChanData = InstPtr->Chans + Channel; /*Value = XDmaPs_ReadReg(InstPtr->Config.BaseAddress, XDMAPS_INTSTATUS_OFFSET);*/ /* clear the interrupt status */ XDmaPs_WriteReg(InstPtr->Config.BaseAddress, XDMAPS_INTCLR_OFFSET, 1 << ChanData->ChanId); /*Value = XDmaPs_ReadReg(InstPtr->Config.BaseAddress, XDMAPS_INTSTATUS_OFFSET);*/ if ((DmaCmd == ChanData->DmaCmdToHw)) { if (!ChanData->HoldDmaProg) { DmaProgBuf = (void *)DmaCmd->GeneratedDmaProg; if (DmaProgBuf) XDmaPs_BufPool_Free(ChanData->ProgBufPool, DmaProgBuf); DmaCmd->GeneratedDmaProg = NULL; } DmaCmd->DmaStatus = 0; ChanData->DmaCmdToHw = NULL; ChanData->DmaCmdFromHw = DmaCmd; if (ChanData->DoneHandler) ChanData->DoneHandler(Channel, DmaCmd, ChanData->DoneRef); } } /****************************************************************************/ /** * Prints the content of the buffer in bytes * @param Buf is the buffer. * @param Length is the length of the DMA program. * * @return None. * * @note None. ****************************************************************************/ static void XDmaPs_Print_DmaProgBuf(char *Buf, int Length) { int Index; for (Index = 0; Index < Length; Index++) xil_printf("[%x] %x\r\n", Index, Buf[Index]); } /****************************************************************************/ /** * Print the Dma Prog Contents. * * @param Cmd is the command buffer. * * @return None. * * @note None. * *****************************************************************************/ void XDmaPs_Print_DmaProg(XDmaPs_Cmd *Cmd) { if (Cmd->GeneratedDmaProg && Cmd->GeneratedDmaProgLength) { xil_printf("Generated DMA program (%d):\r\n", Cmd->GeneratedDmaProgLength); XDmaPs_Print_DmaProgBuf((char *)Cmd->GeneratedDmaProg, Cmd->GeneratedDmaProgLength); } if (Cmd->UserDmaProg && Cmd->UserDmaProgLength) { xil_printf("User defined DMA program (%d):\r\n", Cmd->UserDmaProgLength); XDmaPs_Print_DmaProgBuf((char *)Cmd->UserDmaProg, Cmd->UserDmaProgLength); } }