| /****************************************************************************** |
| * |
| * Copyright (C) 2009 - 2015 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 |
| * XILINX 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 |
| * @addtogroup dmaps_v2_1 |
| * @{ |
| * |
| * This file contains the implementation of the interface functions for XDmaPs |
| * driver. Refer to the header file xdmaps.h for more detailed information. |
| * |
| * <pre> |
| * 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 |
| * </pre> |
| * |
| *****************************************************************************/ |
| |
| /***************************** Include Files ********************************/ |
| |
| #include <string.h> |
| |
| #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);*/ |
| |
| |
| DmaCmd = ChanData->DmaCmdToHw; |
| if (DmaCmd) { |
| 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); |
| } |
| } |
| |
| |
| /** @} */ |