
This patch updates the copy right to 2015. Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
1533 lines
48 KiB
C
Executable file
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");
|
|
}
|