embeddedsw/XilinxProcessorIPLib/drivers/axidma/src/xaxidma_bdring.c
Kedareswara rao Appana eb0a7a4ae0 dma: update copy right information
This patch updates the copy right to 2015.

Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
2015-04-26 10:31:25 +05:30

1533 lines
48 KiB
C
Executable file

/******************************************************************************
*
* Copyright (C) 2010 - 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 THE
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xaxidma_bdring.c
*
* This file implements buffer descriptor ring related functions. For more
* information on how to manage the BD ring, please see xaxidma.h.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- -------- -------------------------------------------------------
* 1.00a jz 05/18/10 First release
* 2.00a jz 08/10/10 Second release, added in xaxidma_g.c, xaxidma_sinit.c,
* updated tcl file, added xaxidma_porting_guide.h
* 3.00a jz 11/22/10 Support IP core parameters change
* 5.00a srt 08/25/11 Added support for memory barrier.
* 6.00a srt 01/24/12 Added support for Multi-Channel DMA.
* - New API
* * XAxiDma_UpdateBdRingCDesc(XAxiDma_BdRing * RingPtr,
* int RingIndex)
* - Changed APIs
* * XAxiDma_StartBdRingHw(XAxiDma_BdRing * RingPtr,
* int RingIndex)
* * XAxiDma_BdRingStart(XAxiDma_BdRing * RingPtr,
* int RingIndex)
* * XAxiDma_BdRingToHw(XAxiDma_BdRing * RingPtr,
* int NumBd, XAxiDma_Bd * BdSetPtr, int RingIndex)
* * XAxiDma_BdRingDumpRegs(XAxiDma_BdRing * RingPtr,
* int RingIndex)
* * XAxiDma_BdRingSnapShotCurrBd(XAxiDma_BdRing * RingPtr,
* int RingIndex)
* 7.00a srt 06/18/12 All the APIs changed in v6_00_a are reverted back for
* backward compatibility.
*
*
* </pre>
******************************************************************************/
/***************************** Include Files *********************************/
#include "xaxidma_bdring.h"
/************************** Constant Definitions *****************************/
/* Use 100 milliseconds for 100 MHz
* This interval is sufficient for hardware to finish 40MB transfer with
* 32-bit bus.
*/
#define XAXIDMA_STOP_TIMEOUT 500000 /* about 100 milliseconds on 100MHz */
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/* The following macros are helper functions inside this file.
*/
/******************************************************************************
* Compute the physical address of a descriptor from its virtual address
*
* @param BdPtr is the virtual address of the BD
*
* @returns Physical address of BdPtr
*
* @note Assume virtual and physical mapping is flat.
* RingPtr is an implicit parameter
*
*****************************************************************************/
#define XAXIDMA_VIRT_TO_PHYS(BdPtr) \
((u32)(BdPtr) + (RingPtr->FirstBdPhysAddr - RingPtr->FirstBdAddr))
/******************************************************************************
* Move the BdPtr argument ahead an arbitrary number of BDs wrapping around
* to the beginning of the ring if needed.
*
* We know if a wraparound should occur if the new BdPtr is greater than
* the high address in the ring OR if the new BdPtr crosses the 0xFFFFFFFF
* to 0 boundary.
*
* @param RingPtr is the ring BdPtr appears in
* @param BdPtr on input is the starting BD position and on output is the
* final BD position
* @param NumBd is the number of BD spaces to increment
*
* @returns None
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
#define XAXIDMA_RING_SEEKAHEAD(RingPtr, BdPtr, NumBd) \
{ \
u32 Addr = (u32)(BdPtr); \
\
Addr += ((RingPtr)->Separation * (NumBd)); \
if ((Addr > (RingPtr)->LastBdAddr) || ((u32)(BdPtr) > Addr)) \
{ \
Addr -= (RingPtr)->Length; \
} \
\
(BdPtr) = (XAxiDma_Bd*)Addr; \
}
/******************************************************************************
* Move the BdPtr argument backwards an arbitrary number of BDs wrapping
* around to the end of the ring if needed.
*
* We know if a wraparound should occur if the new BdPtr is less than
* the base address in the ring OR if the new BdPtr crosses the 0xFFFFFFFF
* to 0 boundary.
*
* @param RingPtr is the ring BdPtr appears in
* @param BdPtr on input is the starting BD position and on output is the
* final BD position
* @param NumBd is the number of BD spaces to increment
*
* @returns None
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
#define XAXIDMA_RING_SEEKBACK(RingPtr, BdPtr, NumBd) \
{ \
u32 Addr = (u32)(BdPtr); \
\
Addr -= ((RingPtr)->Separation * (NumBd)); \
if ((Addr < (RingPtr)->FirstBdAddr) || ((u32)(BdPtr) < Addr)) \
{ \
Addr += (RingPtr)->Length; \
} \
\
(BdPtr) = (XAxiDma_Bd*)Addr; \
}
/************************** Function Prototypes ******************************/
/************************** Variable Definitions *****************************/
/*****************************************************************************/
/**
* Update Current Descriptor
*
* @param RingPtr is the Channel instance to be worked on
*
* @return
* - XST_SUCCESS upon success
* - XST_DMA_ERROR if no valid BD available to put into current
* BD register
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_UpdateBdRingCDesc(XAxiDma_BdRing* RingPtr)
{
u32 RegBase;
XAxiDma_Bd *BdPtr;
int RingIndex = RingPtr->RingIndex;
/* BD list has yet to be created for this channel */
if (RingPtr->AllCnt == 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingStart: no bds\r\n");
return XST_DMA_SG_NO_LIST;
}
/* Do nothing if already started */
if (RingPtr->RunState == AXIDMA_CHANNEL_NOT_HALTED) {
/* Need to update tail pointer if needed (Engine is not
* transferring)
*/
return XST_SUCCESS;
}
if (!XAxiDma_BdRingHwIsStarted(RingPtr)) {
/* If hardware is not running, then we need to put a valid current
* BD pointer to the current BD register before start the hardware
*/
RegBase = RingPtr->ChanBase;
/* Put a valid BD pointer in the current BD pointer register
* So, the hardware is ready to go when tail BD pointer is updated
*/
BdPtr = (XAxiDma_Bd *)RingPtr->BdaRestart;
if (!XAxiDma_BdHwCompleted(BdPtr)) {
if (RingPtr->IsRxChannel) {
if (!RingIndex) {
XAxiDma_WriteReg(RegBase,
XAXIDMA_CDESC_OFFSET, (u32)BdPtr);
}
else {
XAxiDma_WriteReg(RegBase,
(XAXIDMA_RX_CDESC0_OFFSET +
(RingIndex - 1) * XAXIDMA_RX_NDESC_OFFSET),
(u32)BdPtr);
}
}
else {
XAxiDma_WriteReg(RegBase, XAXIDMA_CDESC_OFFSET,
(u32)BdPtr);
}
}
else {
/* Look for an uncompleted BD
*/
while (XAxiDma_BdHwCompleted(BdPtr)) {
BdPtr = XAxiDma_BdRingNext(RingPtr, BdPtr);
if ((u32)BdPtr == (u32) RingPtr->BdaRestart) {
xdbg_printf(XDBG_DEBUG_ERROR,
"StartBdRingHw: Cannot find valid cdesc\r\n");
return XST_DMA_ERROR;
}
if (!XAxiDma_BdHwCompleted(BdPtr)) {
if (RingPtr->IsRxChannel) {
if (!RingIndex) {
XAxiDma_WriteReg(RegBase,
XAXIDMA_CDESC_OFFSET, (u32)BdPtr);
}
else {
XAxiDma_WriteReg(RegBase,
(XAXIDMA_RX_CDESC0_OFFSET +
(RingIndex - 1) *
XAXIDMA_RX_NDESC_OFFSET),
(u32)BdPtr);
}
}
else {
XAxiDma_WriteReg(RegBase,
XAXIDMA_CDESC_OFFSET, (u32)BdPtr);
}
break;
}
}
}
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Using a memory segment allocated by the caller, This fundtion creates and
* setup the BD ring.
*
* @param RingPtr is the BD ring instance to be worked on.
* @param PhysAddr is the physical base address of application memory
* region.
* @param VirtAddr is the virtual base address of the application memory
* region.If address translation is not being utilized, then
* VirtAddr should be equivalent to PhysAddr.
* @param Alignment governs the byte alignment of individual BDs. This
* function will enforce a minimum alignment of
* XAXIDMA_BD_MINIMUM_ALIGNMENT bytes with no maximum as long as
* it is specified as a power of 2.
* @param BdCount is the number of BDs to setup in the application memory
* region. It is assumed the region is large enough to contain the
* BDs.Refer to the "SGDMA Ring Creation" section in xaxidma.h
* for more information. The minimum valid value for this
* parameter is 1.
*
* @return
* - XST_SUCCESS if initialization was successful
* - XST_NO_FEATURE if the provided instance is a non SGDMA type
* of DMA channel.
* - XST_INVALID_PARAM under any of the following conditions:
* 1) BdCount is not positive
*
* 2) PhysAddr and/or VirtAddr are not aligned to the given
* Alignment parameter;
*
* 3) Alignment parameter does not meet minimum requirements or
* is not a power of 2 value.
*
* - XST_DMA_SG_LIST_ERROR if the memory segment containing the
* list spans over address 0x00000000 in virtual address space.
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingCreate(XAxiDma_BdRing *RingPtr, u32 PhysAddr,
u32 VirtAddr, u32 Alignment, int BdCount)
{
int i;
u32 BdVirtAddr;
u32 BdPhysAddr;
if (BdCount <= 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCreate: non-positive BD"
" number %d\r\n", BdCount);
return XST_INVALID_PARAM;
}
/* In case there is a failure prior to creating list, make sure the
* following attributes are 0 to prevent calls to other SG functions
* from doing anything
*/
RingPtr->AllCnt = 0;
RingPtr->FreeCnt = 0;
RingPtr->HwCnt = 0;
RingPtr->PreCnt = 0;
RingPtr->PostCnt = 0;
/* Make sure Alignment parameter meets minimum requirements */
if (Alignment < XAXIDMA_BD_MINIMUM_ALIGNMENT) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCreate: alignment too "
"small %d, need to be at least %d\r\n", (int)Alignment,
XAXIDMA_BD_MINIMUM_ALIGNMENT);
return XST_INVALID_PARAM;
}
/* Make sure Alignment is a power of 2 */
if ((Alignment - 1) & Alignment) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCreate: alignment not"
" valid %d\r\n", (int)Alignment);
return XST_INVALID_PARAM;
}
/* Make sure PhysAddr and VirtAddr are on same Alignment */
if ((PhysAddr % Alignment) || (VirtAddr % Alignment)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCreate: Physical address"
" %x and virtual address %x have different alignment\r\n",
(unsigned int)PhysAddr, (unsigned int)VirtAddr);
return XST_INVALID_PARAM;
}
/* Compute how many bytes will be between the start of adjacent BDs */
RingPtr->Separation =
(sizeof(XAxiDma_Bd) + (Alignment - 1)) & ~(Alignment - 1);
/* Must make sure the ring doesn't span address 0x00000000. If it does,
* then the next/prev BD traversal macros will fail.
*/
if (VirtAddr > (VirtAddr + (RingPtr->Separation * BdCount) - 1)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCreate: BD space cross "
"0x0\r\n");
return XST_DMA_SG_LIST_ERROR;
}
/* Initial ring setup:
* - Clear the entire space
* - Setup each BD's next pointer with the physical address of the
* next BD
* - Put hardware information in each BD
*/
memset((void *) VirtAddr, 0, (RingPtr->Separation * BdCount));
BdVirtAddr = VirtAddr;
BdPhysAddr = PhysAddr + RingPtr->Separation;
for (i = 1; i < BdCount; i++) {
XAxiDma_BdWrite(BdVirtAddr, XAXIDMA_BD_NDESC_OFFSET,
BdPhysAddr);
/* Put hardware information in the BDs
*/
XAxiDma_BdWrite(BdVirtAddr, XAXIDMA_BD_HAS_STSCNTRL_OFFSET,
(u32)RingPtr->HasStsCntrlStrm);
XAxiDma_BdWrite(BdVirtAddr, XAXIDMA_BD_HAS_DRE_OFFSET,
(((u32)(RingPtr->HasDRE)) << XAXIDMA_BD_HAS_DRE_SHIFT) |
RingPtr->DataWidth);
XAXIDMA_CACHE_FLUSH(BdVirtAddr);
BdVirtAddr += RingPtr->Separation;
BdPhysAddr += RingPtr->Separation;
}
/* At the end of the ring, link the last BD back to the top */
XAxiDma_BdWrite(BdVirtAddr, XAXIDMA_BD_NDESC_OFFSET, PhysAddr);
/* Setup the last BD's hardware information */
XAxiDma_BdWrite(BdVirtAddr, XAXIDMA_BD_HAS_STSCNTRL_OFFSET,
(u32)RingPtr->HasStsCntrlStrm);
XAxiDma_BdWrite(BdVirtAddr, XAXIDMA_BD_HAS_DRE_OFFSET,
(((u32)(RingPtr->HasDRE)) << XAXIDMA_BD_HAS_DRE_SHIFT) |
RingPtr->DataWidth);
/* Setup and initialize pointers and counters */
RingPtr->RunState = AXIDMA_CHANNEL_HALTED;
RingPtr->FirstBdAddr = VirtAddr;
RingPtr->FirstBdPhysAddr = PhysAddr;
RingPtr->LastBdAddr = BdVirtAddr;
RingPtr->Length = RingPtr->LastBdAddr - RingPtr->FirstBdAddr +
RingPtr->Separation;
RingPtr->AllCnt = BdCount;
RingPtr->FreeCnt = BdCount;
RingPtr->FreeHead = (XAxiDma_Bd *) VirtAddr;
RingPtr->PreHead = (XAxiDma_Bd *) VirtAddr;
RingPtr->HwHead = (XAxiDma_Bd *) VirtAddr;
RingPtr->HwTail = (XAxiDma_Bd *) VirtAddr;
RingPtr->PostHead = (XAxiDma_Bd *) VirtAddr;
RingPtr->BdaRestart = (XAxiDma_Bd *) PhysAddr;
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Clone the given BD into every BD in the ring. Only the fields offset from
* XAXIDMA_BD_START_CLEAR are copied, for XAXIDMA_BD_BYTES_TO_CLEAR bytes.
* This covers: BufferAddr, Control/Buffer length, status, APP words 0 - 4,
* and software ID fields.
*
* This function can be called only when all BDs are in the free group such as
* immediately after creation of the ring. This prevents modification
* of BDs while they are in use by hardware or the application.
*
* @param RingPtr is the BD ring instance to be worked on.
* @param SrcBdPtr is the source BD template to be cloned into the list.
*
* @return
* - XST_SUCCESS if the list was modified.
* - XST_DMA_SG_NO_LIST if a list has not been created.
* - XST_DEVICE_IS_STARTED if the DMA channel has not been stopped.
* - XST_DMA_SG_LIST_ERROR if some of the BDs in this channel are
* under hardware or application control.
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingClone(XAxiDma_BdRing * RingPtr, XAxiDma_Bd * SrcBdPtr)
{
int i;
u32 CurBd;
u32 Save;
XAxiDma_Bd TmpBd;
/* Can't do this function if there isn't a ring */
if (RingPtr->AllCnt == 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingClone: no bds\r\n");
return XST_DMA_SG_NO_LIST;
}
/* Can't do this function with the channel running */
if (RingPtr->RunState == AXIDMA_CHANNEL_NOT_HALTED) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingClone: bd ring started "
"already, cannot do\r\n");
return XST_DEVICE_IS_STARTED;
}
/* Can't do this function with some of the BDs in use */
if (RingPtr->FreeCnt != RingPtr->AllCnt) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingClone: some bds already "
"in use %d/%d\r\n",RingPtr->FreeCnt, RingPtr->AllCnt);
return XST_DMA_SG_LIST_ERROR;
}
/* Make a copy of the template then modify it by clearing
* the complete bit in status/control field
*/
memcpy(&TmpBd, SrcBdPtr, sizeof(XAxiDma_Bd));
Save = XAxiDma_BdRead(&TmpBd, XAXIDMA_BD_STS_OFFSET);
Save &= ~XAXIDMA_BD_STS_COMPLETE_MASK;
XAxiDma_BdWrite(&TmpBd, XAXIDMA_BD_STS_OFFSET, Save);
for (i = 0, CurBd = RingPtr->FirstBdAddr;
i < RingPtr->AllCnt; i++, CurBd += RingPtr->Separation) {
memcpy((void *)((u32)CurBd + XAXIDMA_BD_START_CLEAR),
(void *)((u32)(&TmpBd) + XAXIDMA_BD_START_CLEAR),
XAXIDMA_BD_BYTES_TO_CLEAR);
XAXIDMA_CACHE_FLUSH(CurBd);
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Start a DMA channel and
* Allow DMA transactions to commence on a given channel if descriptors are
* ready to be processed.
*
* After a DMA channel is started, it is not halted, and it is idle (no active
* DMA transfers).
*
* @param RingPtr is the Channel instance to be worked on
*
* @return
* - XST_SUCCESS upon success
* - XST_DMA_ERROR if no valid BD available to put into current
* BD register
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_StartBdRingHw(XAxiDma_BdRing * RingPtr)
{
u32 RegBase;
int RingIndex = RingPtr->RingIndex;
if (!XAxiDma_BdRingHwIsStarted(RingPtr)) {
/* Start the hardware
*/
RegBase = RingPtr->ChanBase;
XAxiDma_WriteReg(RegBase, XAXIDMA_CR_OFFSET,
XAxiDma_ReadReg(RegBase, XAXIDMA_CR_OFFSET)
| XAXIDMA_CR_RUNSTOP_MASK);
}
if (XAxiDma_BdRingHwIsStarted(RingPtr)) {
/* Note as started */
RingPtr->RunState = AXIDMA_CHANNEL_NOT_HALTED;
/* If there are unprocessed BDs then we want the channel to begin
* processing right away
*/
if (RingPtr->HwCnt > 0) {
XAXIDMA_CACHE_INVALIDATE(RingPtr->HwTail);
if ((XAxiDma_BdRead(RingPtr->HwTail,
XAXIDMA_BD_STS_OFFSET) &
XAXIDMA_BD_STS_COMPLETE_MASK) == 0) {
if (RingPtr->IsRxChannel) {
if (!RingIndex) {
XAxiDma_WriteReg(RingPtr->ChanBase,
XAXIDMA_TDESC_OFFSET,
XAXIDMA_VIRT_TO_PHYS(RingPtr->HwTail));
}
else {
XAxiDma_WriteReg(RingPtr->ChanBase,
(XAXIDMA_RX_TDESC0_OFFSET +
(RingIndex - 1) * XAXIDMA_RX_NDESC_OFFSET),
XAXIDMA_VIRT_TO_PHYS(RingPtr->HwTail));
}
}
else {
XAxiDma_WriteReg(RingPtr->ChanBase,
XAXIDMA_TDESC_OFFSET,
XAXIDMA_VIRT_TO_PHYS(RingPtr->HwTail));
}
}
}
return XST_SUCCESS;
}
return XST_DMA_ERROR;
}
/*****************************************************************************/
/**
* Start a DMA channel, updates current descriptors and
* Allow DMA transactions to commence on a given channel if descriptors are
* ready to be processed.
*
* After a DMA channel is started, it is not halted, and it is idle (no active
* DMA transfers).
*
* @param RingPtr is the Channel instance to be worked on
*
* @return
* - XST_SUCCESS upon success
* - XST_DMA_ERROR if no valid BD available to put into current
* BD register
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingStart(XAxiDma_BdRing * RingPtr)
{
int Status;
Status = XAxiDma_UpdateBdRingCDesc(RingPtr);
if (Status != XST_SUCCESS) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingStart: "
"Updating Current Descriptor Failed\n\r");
return Status;
}
Status = XAxiDma_StartBdRingHw(RingPtr);
if (Status != XST_SUCCESS) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingStart: "
"Starting Hardware Failed\n\r");
return Status;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Set interrupt coalescing parameters for the given descriptor ring channel.
*
* @param RingPtr is a pointer to the descriptor ring instance to be
* worked on.
* @param Counter sets the packet counter on the channel. Valid range is
* - 1..255.
* - XAXIDMA_NO_CHANGE to leave this setting unchanged.
* @param Timer sets the waitbound timer on the channel. Valid range is
* - 0..255.
* - XAXIDMA_NO_CHANGE to leave this setting unchanged.
* Each unit depend on hardware building parameter
* C_DLYTMR_RESOLUTION,which is in the range from 0 to 100,000
* clock cycles. A value of 0 disables the delay interrupt.
*
* @return
* - XST_SUCCESS if interrupt coalescing settings updated
* - XST_FAILURE if Counter or Timer parameters are out of range
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingSetCoalesce(XAxiDma_BdRing *RingPtr, u32 Counter, u32 Timer)
{
u32 Cr;
Cr = XAxiDma_ReadReg(RingPtr->ChanBase, XAXIDMA_CR_OFFSET);
if (Counter != XAXIDMA_NO_CHANGE) {
if ((Counter == 0) || (Counter > 0xFF)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingSetCoalesce: "
"invalid coalescing threshold %d", (int)Counter);
return XST_FAILURE;
}
Cr = (Cr & ~XAXIDMA_COALESCE_MASK) |
(Counter << XAXIDMA_COALESCE_SHIFT);
}
if (Timer != XAXIDMA_NO_CHANGE) {
if (Timer > 0xFF) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingSetCoalesce: "
"invalid delay counter %d", (int)Timer);
return XST_FAILURE;
}
Cr = (Cr & ~XAXIDMA_DELAY_MASK) |
(Timer << XAXIDMA_DELAY_SHIFT);
}
XAxiDma_WriteReg(RingPtr->ChanBase, XAXIDMA_CR_OFFSET, Cr);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Retrieve current interrupt coalescing parameters from the given descriptor
* ring channel.
*
* @param RingPtr is a pointer to the descriptor ring instance to be
* worked on.
* @param CounterPtr points to a memory location where the current packet
* counter will be written.
* @param TimerPtr points to a memory location where the current
* waitbound timer will be written.
*
* @return The passed in parameters, CounterPtr and TimerPtr, holds the
* references to the return values.
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
void XAxiDma_BdRingGetCoalesce(XAxiDma_BdRing * RingPtr,
u32 *CounterPtr, u32 *TimerPtr)
{
u32 Cr;
Cr = XAxiDma_ReadReg(RingPtr->ChanBase, XAXIDMA_CR_OFFSET);
*CounterPtr = ((Cr & XAXIDMA_COALESCE_MASK) >> XAXIDMA_COALESCE_SHIFT);
*TimerPtr = ((Cr & XAXIDMA_DELAY_MASK) >> XAXIDMA_DELAY_SHIFT);
}
/*****************************************************************************/
/**
* Reserve locations in the BD ring. The set of returned BDs may be modified in
* preparation for future DMA transactions. Once the BDs are ready to be
* submitted to hardware, the application must call XAxiDma_BdRingToHw() in the
* same order which they were allocated here. Example:
*
* <pre>
* NumBd = 2;
* Status = XDsma_RingBdAlloc(MyRingPtr, NumBd, &MyBdSet);
*
* if (Status != XST_SUCCESS)
* {
* // Not enough BDs available for the request
* }
*
* CurBd = MyBdSet;
* for (i=0; i<NumBd; i++)
* {
* // Prepare CurBd.....
*
* // Onto next BD
* CurBd = XAxiDma_BdRingNext(MyRingPtr, CurBd);
* }
*
* // Give list to hardware
* Status = XAxiDma_BdRingToHw(MyRingPtr, NumBd, MyBdSet);
* </pre>
*
* A more advanced use of this function may allocate multiple sets of BDs.
* They must be allocated and given to hardware in the correct sequence:
* <pre>
* // Legal
* XAxiDma_BdRingAlloc(MyRingPtr, NumBd1, &MySet1);
* XAxiDma_BdRingToHw(MyRingPtr, NumBd1, MySet1);
*
* // Legal
* XAxiDma_BdRingAlloc(MyRingPtr, NumBd1, &MySet1);
* XAxiDma_BdRingAlloc(MyRingPtr, NumBd2, &MySet2);
* XAxiDma_BdRingToHw(MyRingPtr, NumBd1, MySet1);
* XAxiDma_BdRingToHw(MyRingPtr, NumBd2, MySet2);
*
* // Not legal
* XAxiDma_BdRingAlloc(MyRingPtr, NumBd1, &MySet1);
* XAxiDma_BdRingAlloc(MyRingPtr, NumBd2, &MySet2);
* XAxiDma_BdRingToHw(MyRingPtr, NumBd2, MySet2);
* XAxiDma_BdRingToHw(MyRingPtr, NumBd1, MySet1);
* </pre>
*
* Use the API defined in xaxidmabd.h to modify individual BDs. Traversal of
* the BD set can be done using XAxiDma_BdRingNext() and XAxiDma_BdRingPrev().
*
* @param RingPtr is a pointer to the descriptor ring instance to be
* worked on.
* @param NumBd is the number of BDs to allocate
* @param BdSetPtr is an output parameter, it points to the first BD
* available for modification.
*
* @return
* - XST_SUCCESS if the requested number of BDs were returned in
* the BdSetPtr parameter.
* - XST_INVALID_PARAM if passed in NumBd is not positive
* - XST_FAILURE if there were not enough free BDs to satisfy
* the request.
*
* @note This function should not be preempted by another XAxiDma_BdRing
* function call that modifies the BD space. It is the caller's
* responsibility to provide a mutual exclusion mechanism.
*
* Do not modify more BDs than the number requested with the NumBd
* parameter. Doing so will lead to data corruption and system
* instability.
*
* This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingAlloc(XAxiDma_BdRing * RingPtr, int NumBd,
XAxiDma_Bd ** BdSetPtr)
{
if (NumBd <= 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingAlloc: negative BD "
"number %d\r\n", NumBd);
return XST_INVALID_PARAM;
}
/* Enough free BDs available for the request? */
if (RingPtr->FreeCnt < NumBd) {
xdbg_printf(XDBG_DEBUG_ERROR,
"Not enough BDs to alloc %d/%d\r\n", NumBd, RingPtr->FreeCnt);
return XST_FAILURE;
}
/* Set the return argument and move FreeHead forward */
*BdSetPtr = RingPtr->FreeHead;
XAXIDMA_RING_SEEKAHEAD(RingPtr, RingPtr->FreeHead, NumBd);
RingPtr->FreeCnt -= NumBd;
RingPtr->PreCnt += NumBd;
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Fully or partially undo an XAxiDma_BdRingAlloc() operation. Use this
* function if all the BDs allocated by XAxiDma_BdRingAlloc() could not be
* transferred to hardware with XAxiDma_BdRingToHw().
*
* This function releases the BDs after they have been allocated but before
* they have been given to hardware.
*
* This function is not the same as XAxiDma_BdRingFree(). The Free function
* returns BDs to the free list after they have been processed by hardware,
* while UnAlloc returns them before being processed by hardware.
*
* There are two scenarios where this function can be used. Full UnAlloc or
* Partial UnAlloc. A Full UnAlloc means all the BDs Alloc'd will be returned:
*
* <pre>
* Status = XAxiDma_BdRingAlloc(MyRingPtr, 10, &BdPtr);
* ...
* ...
* if (Error)
* {
* Status = XAxiDma_BdRingUnAlloc(MyRingPtr, 10, &BdPtr);
* }
* </pre>
*
* A partial UnAlloc means some of the BDs Alloc'd will be returned:
*
* <pre>
* Status = XAxiDma_BdRingAlloc(MyRingPtr, 10, &BdPtr);
* BdsLeft = 10;
* CurBdPtr = BdPtr;
*
* while (BdsLeft)
* {
* if (Error)
* {
* Status = XAxiDma_BdRingUnAlloc(MyRingPtr, BdsLeft, CurBdPtr);
* }
*
* CurBdPtr = XAxiDma_BdRingNext(MyRingPtr, CurBdPtr);
* BdsLeft--;
* }
* </pre>
*
* A partial UnAlloc must include the last BD in the list that was Alloc'd.
*
* @param RingPtr is a pointer to the descriptor ring instance to be
* worked on.
* @param NumBd is the number of BDs to unallocate
* @param BdSetPtr points to the first of the BDs to be returned.
*
* @return
* - XST_SUCCESS if the BDs were unallocated.
* - XST_INVALID_PARAM if passed in NumBd is negative
* - XST_FAILURE if NumBd parameter was greater that the number of
* BDs in the preprocessing state.
*
* @note This function should not be preempted by another XAxiDma ring
* function call that modifies the BD space. It is the caller's
* responsibility to provide a mutual exclusion mechanism.
*
* This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingUnAlloc(XAxiDma_BdRing * RingPtr, int NumBd,
XAxiDma_Bd * BdSetPtr)
{
XAxiDma_Bd *TmpBd;
if (NumBd <= 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingUnAlloc: negative BD"
" number %d\r\n", NumBd);
return XST_INVALID_PARAM;
}
/* Enough BDs in the preprocessing state for the request? */
if (RingPtr->PreCnt < NumBd) {
xdbg_printf(XDBG_DEBUG_ERROR,
"Pre-allocated BDs less than requested %d/%d\r\n",
RingPtr->PreCnt, NumBd);
return XST_FAILURE;
}
/* The last BD in the BD set must has the FreeHead as its next BD.
* Otherwise, this is not a valid operation.
*/
TmpBd = BdSetPtr;
XAXIDMA_RING_SEEKAHEAD(RingPtr, TmpBd, NumBd);
if (TmpBd != RingPtr->FreeHead) {
xdbg_printf(XDBG_DEBUG_ERROR,
"Unalloc does not go back to free head\r\n");
return XST_FAILURE;
}
/* Set the return argument and move FreeHead backward */
XAXIDMA_RING_SEEKBACK(RingPtr, RingPtr->FreeHead, NumBd);
RingPtr->FreeCnt += NumBd;
RingPtr->PreCnt -= NumBd;
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Enqueue a set of BDs to hardware that were previously allocated by
* XAxiDma_BdRingAlloc(). Once this function returns, the argument BD set goes
* under hardware control. Changes to these BDs should be held until they are
* finished by hardware to avoid data corruption and system instability.
*
* For transmit, the set will be rejected if the last BD of the set does not
* mark the end of a packet or the first BD does not mark the start of a packet.
*
* @param RingPtr is a pointer to the descriptor ring instance to be
* worked on.
* @param NumBd is the number of BDs in the set.
* @param BdSetPtr is the first BD of the set to commit to hardware.
*
* @return
* - XST_SUCCESS if the set of BDs was accepted and enqueued to
* hardware
* - XST_INVALID_PARAM if passed in NumBd is negative
* - XST_FAILURE if the set of BDs was rejected because the first
* BD does not have its start-of-packet bit set, or the last BD
* does not have its end-of-packet bit set, or any one of the BDs
* has 0 length.
* - XST_DMA_SG_LIST_ERROR if this function was called out of
* sequence with XAxiDma_BdRingAlloc()
*
* @note This function should not be preempted by another XAxiDma ring
* function call that modifies the BD space. It is the caller's
* responsibility to provide a mutual exclusion mechanism.
*
* This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingToHw(XAxiDma_BdRing * RingPtr, int NumBd,
XAxiDma_Bd * BdSetPtr)
{
XAxiDma_Bd *CurBdPtr;
int i;
u32 BdCr;
u32 BdSts;
int RingIndex = RingPtr->RingIndex;
if (NumBd < 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingToHw: negative BD number "
"%d\r\n", NumBd);
return XST_INVALID_PARAM;
}
/* If the commit set is empty, do nothing */
if (NumBd == 0) {
return XST_SUCCESS;
}
/* Make sure we are in sync with XAxiDma_BdRingAlloc() */
if ((RingPtr->PreCnt < NumBd) || (RingPtr->PreHead != BdSetPtr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "Bd ring has problems\r\n");
return XST_DMA_SG_LIST_ERROR;
}
CurBdPtr = BdSetPtr;
BdCr = XAxiDma_BdGetCtrl(CurBdPtr);
BdSts = XAxiDma_BdGetSts(CurBdPtr);
/* In case of Tx channel, the first BD should have been marked
* as start-of-frame
*/
if (!(RingPtr->IsRxChannel) && !(BdCr & XAXIDMA_BD_CTRL_TXSOF_MASK)) {
xdbg_printf(XDBG_DEBUG_ERROR, "Tx first BD does not have "
"SOF\r\n");
return XST_FAILURE;
}
/* Clear the completed status bit
*/
for (i = 0; i < NumBd - 1; i++) {
/* Make sure the length value in the BD is non-zero. */
if (XAxiDma_BdGetLength(CurBdPtr,
RingPtr->MaxTransferLen) == 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "0 length bd\r\n");
return XST_FAILURE;
}
BdSts &= ~XAXIDMA_BD_STS_COMPLETE_MASK;
XAxiDma_BdWrite(CurBdPtr, XAXIDMA_BD_STS_OFFSET, BdSts);
/* Flush the current BD so DMA core could see the updates */
XAXIDMA_CACHE_FLUSH(CurBdPtr);
CurBdPtr = XAxiDma_BdRingNext(RingPtr, CurBdPtr);
BdCr = XAxiDma_BdRead(CurBdPtr, XAXIDMA_BD_CTRL_LEN_OFFSET);
BdSts = XAxiDma_BdRead(CurBdPtr, XAXIDMA_BD_STS_OFFSET);
}
/* In case of Tx channel, the last BD should have EOF bit set */
if (!(RingPtr->IsRxChannel) && !(BdCr & XAXIDMA_BD_CTRL_TXEOF_MASK)) {
xdbg_printf(XDBG_DEBUG_ERROR, "Tx last BD does not have "
"EOF\r\n");
return XST_FAILURE;
}
/* Make sure the length value in the last BD is non-zero. */
if (XAxiDma_BdGetLength(CurBdPtr,
RingPtr->MaxTransferLen) == 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "0 length bd\r\n");
return XST_FAILURE;
}
/* The last BD should also have the completed status bit cleared
*/
BdSts &= ~XAXIDMA_BD_STS_COMPLETE_MASK;
XAxiDma_BdWrite(CurBdPtr, XAXIDMA_BD_STS_OFFSET, BdSts);
/* Flush the last BD so DMA core could see the updates */
XAXIDMA_CACHE_FLUSH(CurBdPtr);
DATA_SYNC;
/* This set has completed pre-processing, adjust ring pointers and
* counters
*/
XAXIDMA_RING_SEEKAHEAD(RingPtr, RingPtr->PreHead, NumBd);
RingPtr->PreCnt -= NumBd;
RingPtr->HwTail = CurBdPtr;
RingPtr->HwCnt += NumBd;
/* If it is running, signal the engine to begin processing */
if (RingPtr->RunState == AXIDMA_CHANNEL_NOT_HALTED) {
if (RingPtr->IsRxChannel) {
if (!RingIndex) {
XAxiDma_WriteReg(RingPtr->ChanBase,
XAXIDMA_TDESC_OFFSET,
XAXIDMA_VIRT_TO_PHYS(RingPtr->HwTail));
}
else {
XAxiDma_WriteReg(RingPtr->ChanBase,
(XAXIDMA_RX_TDESC0_OFFSET +
(RingIndex - 1) *
XAXIDMA_RX_NDESC_OFFSET),
XAXIDMA_VIRT_TO_PHYS(RingPtr->HwTail));
}
}
else {
XAxiDma_WriteReg(RingPtr->ChanBase,
XAXIDMA_TDESC_OFFSET,
XAXIDMA_VIRT_TO_PHYS(RingPtr->HwTail));
}
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Returns a set of BD(s) that have been processed by hardware. The returned
* BDs may be examined by the application to determine the outcome of the DMA
* transactions. Once the BDs have been examined, the application must call
* XAxiDma_BdRingFree() in the same order which they were retrieved here.
*
* Example:
*
* <pre>
* NumBd = XAxiDma_BdRingFromHw(MyRingPtr, XAXIDMA_ALL_BDS, &MyBdSet);
*
* if (NumBd == 0)
* {
* // hardware has nothing ready for us yet
* }
*
* CurBd = MyBdSet;
* for (i=0; i<NumBd; i++)
* {
* // Examine CurBd for post processing.....
*
* // Onto next BD
* CurBd = XAxiDma_BdRingNext(MyRingPtr, CurBd);
* }
*
* XAxiDma_BdRingFree(MyRingPtr, NumBd, MyBdSet); // Return the list
* </pre>
*
* A more advanced use of this function may allocate multiple sets of BDs.
* They must be retrieved from hardware and freed in the correct sequence:
* <pre>
* // Legal
* XAxiDma_BdRingFromHw(MyRingPtr, NumBd1, &MySet1);
* XAxiDma_BdRingFree(MyRingPtr, NumBd1, MySet1);
*
* // Legal
* XAxiDma_BdRingFromHw(MyRingPtr, NumBd1, &MySet1);
* XAxiDma_BdRingFromHw(MyRingPtr, NumBd2, &MySet2);
* XAxiDma_BdRingFree(MyRingPtr, NumBd1, MySet1);
* XAxiDma_BdRingFree(MyRingPtr, NumBd2, MySet2);
*
* // Not legal
* XAxiDma_BdRingFromHw(MyRingPtr, NumBd1, &MySet1);
* XAxiDma_BdRingFromHw(MyRingPtr, NumBd2, &MySet2);
* XAxiDma_BdRingFree(MyRingPtr, NumBd2, MySet2);
* XAxiDma_BdRingFree(MyRingPtr, NumBd1, MySet1);
* </pre>
*
* If hardware has partially completed a packet spanning multiple BDs, then
* none of the BDs for that packet will be included in the results.
*
* @param RingPtr is a pointer to the descriptor ring instance to be
* worked on.
* @param BdLimit is the maximum number of BDs to return in the set. Use
* XAXIDMA_ALL_BDS to return all BDs that have been processed.
* @param BdSetPtr is an output parameter, it points to the first BD
* available for examination.
*
* @return The number of BDs processed by hardware. A value of 0 indicates
* that no data is available. No more than BdLimit BDs will be
* returned.
*
* @note Treat BDs returned by this function as read-only.
*
* This function should not be preempted by another XAxiDma ring
* function call that modifies the BD space. It is the caller's
* responsibility to provide a mutual exclusion mechanism.
*
* This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingFromHw(XAxiDma_BdRing * RingPtr, int BdLimit,
XAxiDma_Bd ** BdSetPtr)
{
XAxiDma_Bd *CurBdPtr;
int BdCount;
int BdPartialCount;
u32 BdSts;
u32 BdCr;
CurBdPtr = RingPtr->HwHead;
BdCount = 0;
BdPartialCount = 0;
BdSts = 0;
BdCr = 0;
/* If no BDs in work group, then there's nothing to search */
if (RingPtr->HwCnt == 0) {
*BdSetPtr = (XAxiDma_Bd *)NULL;
return 0;
}
if (BdLimit > RingPtr->HwCnt) {
BdLimit = RingPtr->HwCnt;
}
/* Starting at HwHead, keep moving forward in the list until:
* - A BD is encountered with its completed bit clear in the status
* word which means hardware has not completed processing of that
* BD.
* - RingPtr->HwTail is reached
* - The number of requested BDs has been processed
*/
while (BdCount < BdLimit) {
/* Read the status */
XAXIDMA_CACHE_INVALIDATE(CurBdPtr);
BdSts = XAxiDma_BdRead(CurBdPtr, XAXIDMA_BD_STS_OFFSET);
BdCr = XAxiDma_BdRead(CurBdPtr, XAXIDMA_BD_CTRL_LEN_OFFSET);
/* If the hardware still hasn't processed this BD then we are
* done
*/
if (!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK)) {
break;
}
BdCount++;
/* Hardware has processed this BD so check the "last" bit. If
* it is clear, then there are more BDs for the current packet.
* Keep a count of these partial packet BDs.
*
* For tx BDs, EOF bit is in the control word
* For rx BDs, EOF bit is in the status word
*/
if (((!(RingPtr->IsRxChannel) &&
(BdCr & XAXIDMA_BD_CTRL_TXEOF_MASK)) ||
((RingPtr->IsRxChannel) && (BdSts &
XAXIDMA_BD_STS_RXEOF_MASK)))) {
BdPartialCount = 0;
}
else {
BdPartialCount++;
}
/* Reached the end of the work group */
if (CurBdPtr == RingPtr->HwTail) {
break;
}
/* Move on to the next BD in work group */
CurBdPtr = XAxiDma_BdRingNext(RingPtr, CurBdPtr);
}
/* Subtract off any partial packet BDs found */
BdCount -= BdPartialCount;
/* If BdCount is non-zero then BDs were found to return. Set return
* parameters, update pointers and counters, return success
*/
if (BdCount) {
*BdSetPtr = RingPtr->HwHead;
RingPtr->HwCnt -= BdCount;
RingPtr->PostCnt += BdCount;
XAXIDMA_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
return BdCount;
}
else {
*BdSetPtr = (XAxiDma_Bd *)NULL;
return 0;
}
}
/*****************************************************************************/
/**
* Frees a set of BDs that had been previously retrieved with
* XAxiDma_BdRingFromHw().
*
* @param RingPtr is a pointer to the descriptor ring instance to be
* worked on.
* @param NumBd is the number of BDs to free.
* @param BdSetPtr is the head of a list of BDs returned by
* XAxiDma_BdRingFromHw().
*
* @return
* - XST_SUCCESS if the set of BDs was freed.
* - XST_INVALID_PARAM if NumBd is negative
* - XST_DMA_SG_LIST_ERROR if this function was called out of
* sequence with XAxiDma_BdRingFromHw().
*
* @note This function should not be preempted by another XAxiDma
* function call that modifies the BD space. It is the caller's
* responsibility to ensure mutual exclusion.
*
* This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingFree(XAxiDma_BdRing * RingPtr, int NumBd,
XAxiDma_Bd * BdSetPtr)
{
if (NumBd < 0) {
xdbg_printf(XDBG_DEBUG_ERROR,
"BdRingFree: negative BDs %d\r\n", NumBd);
return XST_INVALID_PARAM;
}
/* If the BD Set to free is empty, do nothing
*/
if (NumBd == 0) {
return XST_SUCCESS;
}
/* Make sure we are in sync with XAxiDma_BdRingFromHw() */
if ((RingPtr->PostCnt < NumBd) || (RingPtr->PostHead != BdSetPtr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingFree: Error free BDs: "
"post count %d to free %d, PostHead %x to free ptr %x\r\n",
RingPtr->PostCnt, NumBd,
(unsigned int)RingPtr->PostHead,
(unsigned int)BdSetPtr);
return XST_DMA_SG_LIST_ERROR;
}
/* Update pointers and counters */
RingPtr->FreeCnt += NumBd;
RingPtr->PostCnt -= NumBd;
XAXIDMA_RING_SEEKAHEAD(RingPtr, RingPtr->PostHead, NumBd);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Check the internal data structures of the BD ring for the provided channel.
* The following checks are made:
*
* - The BD ring is linked correctly in physical address space.
* - The internal pointers point to BDs in the ring.
* - The internal counters add up.
*
* The channel should be stopped (through XAxiDma_Pause() or XAxiDma_Reset())
* prior to calling this function.
*
* @param RingPtr is a pointer to the descriptor ring to be worked on.
*
* @return
* - XST_SUCCESS if no errors were found.
* - XST_DMA_SG_NO_LIST if the ring has not been created.
* - XST_IS_STARTED if the channel is not stopped.
* - XST_DMA_SG_LIST_ERROR if a problem is found with the internal
* data structures. If this value is returned, the channel should
* be reset,and the BD ring should be recreated through
* XAxiDma_BdRingCreate() to avoid data corruption or system
* instability.
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
int XAxiDma_BdRingCheck(XAxiDma_BdRing * RingPtr)
{
u32 AddrV;
u32 AddrP;
int i;
/* Is the list created */
if (RingPtr->AllCnt == 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: no BDs\r\n");
return XST_DMA_SG_NO_LIST;
}
/* Can't check if channel is running */
if (RingPtr->RunState == AXIDMA_CHANNEL_NOT_HALTED) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: Bd ring is "
"running, cannot check it\r\n");
return XST_IS_STARTED;
}
/* RunState doesn't make sense */
else if (RingPtr->RunState != AXIDMA_CHANNEL_HALTED) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: unknown BD ring "
"state %d ", RingPtr->RunState);
return XST_DMA_SG_LIST_ERROR;
}
/* Verify internal pointers point to correct memory space */
AddrV = (u32) RingPtr->FreeHead;
if ((AddrV < RingPtr->FirstBdAddr) || (AddrV > RingPtr->LastBdAddr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: FreeHead wrong "
"%x, should be in range of %x/%x\r\n",
(unsigned int)AddrV,
(unsigned int)RingPtr->FirstBdAddr,
(unsigned int)RingPtr->LastBdAddr);
return XST_DMA_SG_LIST_ERROR;
}
AddrV = (u32) RingPtr->PreHead;
if ((AddrV < RingPtr->FirstBdAddr) || (AddrV > RingPtr->LastBdAddr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: PreHead wrong %x, "
"should be in range of %x/%x\r\n",
(unsigned int)AddrV,
(unsigned int)RingPtr->FirstBdAddr,
(unsigned int)RingPtr->LastBdAddr);
return XST_DMA_SG_LIST_ERROR;
}
AddrV = (u32) RingPtr->HwHead;
if ((AddrV < RingPtr->FirstBdAddr) || (AddrV > RingPtr->LastBdAddr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: HwHead wrong %x, "
"should be in range of %x/%x\r\n",
(unsigned int)AddrV,
(unsigned int)RingPtr->FirstBdAddr,
(unsigned int)RingPtr->LastBdAddr);
return XST_DMA_SG_LIST_ERROR;
}
AddrV = (u32) RingPtr->HwTail;
if ((AddrV < RingPtr->FirstBdAddr) || (AddrV > RingPtr->LastBdAddr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: HwTail wrong %x, "
"should be in range of %x/%x\r\n",
(unsigned int)AddrV,
(unsigned int)RingPtr->FirstBdAddr,
(unsigned int)RingPtr->LastBdAddr);
return XST_DMA_SG_LIST_ERROR;
}
AddrV = (u32) RingPtr->PostHead;
if ((AddrV < RingPtr->FirstBdAddr) || (AddrV > RingPtr->LastBdAddr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: PostHead wrong "
"%x, should be in range of %x/%x\r\n",
(unsigned int)AddrV,
(unsigned int)RingPtr->FirstBdAddr,
(unsigned int)RingPtr->LastBdAddr);
return XST_DMA_SG_LIST_ERROR;
}
/* Verify internal counters add up */
if ((RingPtr->HwCnt + RingPtr->PreCnt + RingPtr->FreeCnt +
RingPtr->PostCnt) != RingPtr->AllCnt) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: internal counter "
"error\r\n");
return XST_DMA_SG_LIST_ERROR;
}
/* Verify BDs are linked correctly */
AddrV = RingPtr->FirstBdAddr;
AddrP = RingPtr->FirstBdPhysAddr + RingPtr->Separation;
for (i = 1; i < RingPtr->AllCnt; i++) {
XAXIDMA_CACHE_INVALIDATE(AddrV);
/* Check next pointer for this BD. It should equal to the
* physical address of next BD
*/
if (XAxiDma_BdRead(AddrV, XAXIDMA_BD_NDESC_OFFSET) != AddrP) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: Next Bd "
"ptr %x wrong, expect %x\r\n",
(unsigned int)XAxiDma_BdRead(AddrV,
XAXIDMA_BD_NDESC_OFFSET),
(unsigned int)AddrP);
return XST_DMA_SG_LIST_ERROR;
}
/* Move on to next BD */
AddrV += RingPtr->Separation;
AddrP += RingPtr->Separation;
}
XAXIDMA_CACHE_INVALIDATE(AddrV);
/* Last BD should point back to the beginning of ring */
if (XAxiDma_BdRead(AddrV, XAXIDMA_BD_NDESC_OFFSET) !=
RingPtr->FirstBdPhysAddr) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCheck: last Bd Next BD "
"ptr %x wrong, expect %x\r\n",
(unsigned int)XAxiDma_BdRead(AddrV,
XAXIDMA_BD_NDESC_OFFSET),
(unsigned int)RingPtr->FirstBdPhysAddr);
return XST_DMA_SG_LIST_ERROR;
}
/* No problems found */
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* Dump the registers for a channel.
*
* @param RingPtr is a pointer to the descriptor ring to be worked on.
*
* @return None
*
* @note This function can be used only when DMA is in SG mode
*
*****************************************************************************/
void XAxiDma_BdRingDumpRegs(XAxiDma_BdRing *RingPtr) {
u32 RegBase = RingPtr->ChanBase;
int RingIndex = RingPtr->RingIndex;
xil_printf("Dump registers %x:\r\n", (unsigned int)RegBase);
xil_printf("Control REG: %08x\r\n",
(unsigned int)XAxiDma_ReadReg(RegBase, XAXIDMA_CR_OFFSET));
xil_printf("Status REG: %08x\r\n",
(unsigned int)XAxiDma_ReadReg(RegBase, XAXIDMA_SR_OFFSET));
if (RingIndex) {
xil_printf("Cur BD REG: %08x\r\n",
(unsigned int)XAxiDma_ReadReg(RegBase,
XAXIDMA_RX_CDESC0_OFFSET + ((RingIndex - 1) *
XAXIDMA_RX_NDESC_OFFSET)));
xil_printf("Tail BD REG: %08x\r\n",
(unsigned int)XAxiDma_ReadReg(RegBase,
XAXIDMA_RX_TDESC0_OFFSET + ((RingIndex - 1) *
XAXIDMA_RX_NDESC_OFFSET)));
}
else {
xil_printf("Cur BD REG: %08x\r\n",
(unsigned int)XAxiDma_ReadReg(RegBase, XAXIDMA_CDESC_OFFSET));
xil_printf("Tail BD REG: %08x\r\n",
(unsigned int)XAxiDma_ReadReg(RegBase, XAXIDMA_TDESC_OFFSET));
}
xil_printf("\r\n");
}