text: /******************************************************************************
  * Copyright (C) 2009 - 2020 Xilinx, Inc.  All rights reserved.
  * SPDX-License-Identifier: MIT
  ******************************************************************************/
  
  /****************************************************************************/
  /**
  *
  * @file xdmaps.c
  * @addtogroup dmaps_v2_7
  * @{
  *
  * 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
  * 2.2  mus    12/08/16   Remove definition of INLINE macro to avoid re-definition,
  *                         since it is being defined in xil_io.h
  * 2.3 kpc     14/10/16   Fixed the compiler error when optimization O0 is used.
  * 2.5 hk      08/16/19   Add a memory barrier before DMASEV as per specification.
  * 2.6 hk      02/14/20   Correct boundary check for Channel.
  *
  * </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 ******************************/
  
  /***************** 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.
  *
  *****************************************************************************/
  static 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;
  }
  
  static 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
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  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.
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  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.
  *
  *****************************************************************************/
  static 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.
  *
  *****************************************************************************/
  static 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);
  
  		}
  	}
  
  	/* Add a memory barrier before DMASSEV as recommended by spec */
  	DmaProgBuf += XDmaPs_Instr_DMAWMB(DmaProgBuf);
  	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 XST_FAILURE;
  
  	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);
  	}
  }
  
  
  /** @} */
  