embeddedsw/XilinxProcessorIPLib/drivers/axicdma/src/xaxicdma_bdring.c
Jagannadha Sutradharudu Teki 2c8f92039d embeddesw: Add initial code support
Added initial support Xilinx Embedded Software.

Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
2014-06-24 16:45:01 +05:30

1054 lines
32 KiB
C
Executable file

/******************************************************************************
*
* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
* @file xaxicdma_bdring.c
*
* Implementation for support on Scatter Gather (SG) transfers.
* It includes the implementation of the BD ring API. There is only one BD ring
* per DMA engine.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- -------- -------------------------------------------------------
* 1.00a jz 04/18/10 First release
* 2.01a rkv 01/25/11 Replaced with "\r\n" in place on "\n\r" in printf statements
* </pre>
*
*****************************************************************************/
#include "xaxicdma.h"
#include "xaxicdma_i.h"
/***************** Macros (Inline Functions) Definitions *********************/
/* The following macros are helper functions inside this file.
*/
/******************************************************************************
* Move the BdPtr argument ahead an arbitrary number of BDs. Wrapping around
* to the beginning of the ring if needed.
*
* We know a wraparound should occur if the new BdPtr is greater than
* the high address in the ring.
*
* @param InstancePtr 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
*
* @note The BdPtr will be changed if NumBd not zero.
*
*****************************************************************************/
#define XAXICDMA_RING_SEEKAHEAD(InstancePtr, BdPtr, NumBd) \
{ \
u32 Addr = (u32)(BdPtr); \
\
Addr += ((InstancePtr)->BdSeparation * (NumBd)); \
if (Addr > (InstancePtr)->LastBdAddr) { \
Addr -= (InstancePtr)->BdRingTotalLen; \
} \
\
(BdPtr) = (XAxiCdma_Bd*)Addr; \
}
/******************************************************************************
* Move the BdPtr argument backwards an arbitrary number of BDs. Wrapping
* around to the end of the ring if needed.
*
* We know a wraparound should occur if the new BdPtr is less than
* the base address in the ring.
*
* @param InstancePtr 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
*
* @note The BdPtr will be changed if NumBd not zero.
*
*****************************************************************************/
#define XAXICDMA_RING_SEEKBACK(InstancePtr, BdPtr, NumBd) \
{ \
u32 Addr = (u32)(BdPtr); \
\
Addr -= ((InstancePtr)->BdSeparation * (NumBd)); \
if (Addr < (InstancePtr)->FirstBdAddr) { \
Addr += (InstancePtr)->BdRingTotalLen; \
} \
\
(BdPtr) = (XAxiCdma_Bd*)Addr; \
}
/*****************************************************************************/
/**
* This function calculates how many BDs can be built using given number of
* bytes of memory, according to alignment provided.
*
* @param Alignment is the preferred alignment for the BDs
* @param Bytes is the number of bytes of memory to build BDs with
* @param BdBuffAddr is the buffer address allocated for the BDs. This
* is to check the alignment of the buffer to make sure the the
* buffer is aligned to the BD alignment. An invalid buffer
* address results in 0.
*
* @return The number of BDs can be built. 0 means buffer address
* is not valid.
*
* @note The application is responsible to align the buffer before pass
* it to this function.
*
*****************************************************************************/
int XAxiCdma_BdRingCntCalc(u32 Alignment, u32 Bytes, u32 BdBuffAddr)
{
/* The buffer alignment has to be taken account of. An unaligned buffer
* is invalid.
*/
if (BdBuffAddr & Alignment) {
xdbg_printf(XDBG_DEBUG_ERROR, "Invalid buffer addr %x\r\n",
(unsigned int)BdBuffAddr);
return 0;
}
return ((int)(Bytes) / ((sizeof(XAxiCdma_Bd) + (Alignment - 1)) &
~(Alignment - 1)));
}
/*****************************************************************************/
/**
* This function calculates how much memory is needed to build requested number
* of BDs.
*
* @param Alignment is the preferred alignment for the BDs
* @param NumBd is the number of BDs to be built
*
* @return The number of bytes of memory needed to build the BDs
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingMemCalc(u32 Alignment, int NumBd)
{
return (int)(((sizeof(XAxiCdma_Bd) + (Alignment - 1)) &
~(Alignment - 1)) * NumBd);
}
/*****************************************************************************/
/**
* This function gets the total number of BDs in the BD ring.
*
* @param InstancePtr is the driver instance we are working on
*
* @return The total number of BDs for this instance
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingGetCnt(XAxiCdma *InstancePtr)
{
return InstancePtr->AllBdCnt;
}
/*****************************************************************************/
/**
* This function gets the number of free BDs.
*
* @param InstancePtr is the driver instance we are working on
*
* @return The total number of free BDs for this instance
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingGetFreeCnt(XAxiCdma *InstancePtr)
{
return InstancePtr->FreeBdCnt;
}
/*****************************************************************************/
/**
* This function grabs a copy of the current BD pointer from the hardware.
* It is normally used to prepare for the hardware reset. The snapshot of the
* current BD pointer should be reloaded once the reset is done.
*
* @param InstancePtr is the driver instance we are working on
*
* @return None
*
* @note None.
*
*****************************************************************************/
void XAxiCdma_BdRingSnapShotCurrBd(XAxiCdma *InstancePtr)
{
XAxiCdma_Bd *BdPtr;
BdPtr = (XAxiCdma_Bd *)XAxiCdma_ReadReg(InstancePtr->BaseAddr,
XAXICDMA_CDESC_OFFSET);
InstancePtr->BdaRestart = XAxiCdma_BdRingNext(InstancePtr, BdPtr);
return;
}
/*****************************************************************************/
/**
* This function grabs a copy of the current BD pointer from the hardware.
*
* @param InstancePtr is the driver instance we are working on
*
* @return The BD pointer in CDESC register
*
* @note None.
*
*****************************************************************************/
XAxiCdma_Bd *XAxiCdma_BdRingGetCurrBd(XAxiCdma *InstancePtr)
{
return (XAxiCdma_Bd *)XAxiCdma_ReadReg(InstancePtr->BaseAddr,
XAXICDMA_CDESC_OFFSET);
}
/*****************************************************************************/
/**
* This function gets the next BD of the current BD on the BD ring.
*
* @param InstancePtr is the driver instance we are working on
* @param BdPtr is the current BD
*
* @return The next BD on the ring from the current BD, NULL if passed
* in BdPtr not valid.
*
* @note None.
*
*****************************************************************************/
XAxiCdma_Bd *XAxiCdma_BdRingNext(XAxiCdma *InstancePtr, XAxiCdma_Bd *BdPtr)
{
u32 ReturnBd;
/* Check whether the BD ptr is valid
* A BD ptr is not valid if:
* - It is outside of the BD memory range
* - It has invalid alignment
*/
if (((u32)BdPtr > InstancePtr->LastBdAddr) ||
((u32)BdPtr < InstancePtr->FirstBdAddr) ||
((u32)BdPtr & (InstancePtr->BdSeparation - 1))) {
xdbg_printf(XDBG_DEBUG_ERROR, "Invalid BdPtr %x: %x/%x/%x\r\n",
(unsigned int)BdPtr, (unsigned int)InstancePtr->FirstBdAddr,
(unsigned int)InstancePtr->LastBdAddr,
(unsigned int)XAXICDMA_BD_MINIMUM_ALIGNMENT);
ReturnBd = 0x0;
}
/* If the current BD is the last BD in the ring, return the first BD
*/
else if ((u32)BdPtr == InstancePtr->LastBdAddr) {
ReturnBd = InstancePtr->FirstBdAddr;
}
else {
ReturnBd = (u32)BdPtr + InstancePtr->BdSeparation;
}
return (XAxiCdma_Bd *)ReturnBd;
}
/*****************************************************************************/
/**
* This function gets the previous BD of the current BD on the BD ring.
*
* @param InstancePtr is the driver instance we are working on
* @param BdPtr is the current BD
*
* @return The previous BD on the ring from the current BD
*
* @note None.
*
*****************************************************************************/
XAxiCdma_Bd *XAxiCdma_BdRingPrev(XAxiCdma *InstancePtr, XAxiCdma_Bd *BdPtr)
{
u32 ReturnBd;
/* Check whether the BD ptr is valid
* A BD ptr is not valid if:
* - It is outside of the BD memory range
* - It has invalid alignment
*/
if (((u32)BdPtr > InstancePtr->LastBdAddr) ||
((u32)BdPtr < InstancePtr->FirstBdAddr) ||
((u32)BdPtr & (InstancePtr->BdSeparation - 1))) {
xdbg_printf(XDBG_DEBUG_ERROR, "Invalid BdPtr %x: %x/%x/%x\r\n",
(unsigned int)BdPtr, (unsigned int)InstancePtr->FirstBdAddr,
(unsigned int)InstancePtr->LastBdAddr,
(unsigned int)XAXICDMA_BD_MINIMUM_ALIGNMENT);
ReturnBd = 0x0;
}
/* If the current BD is the first BD in the ring, return the last BD
*/
else if ((u32)BdPtr == InstancePtr->FirstBdAddr) {
ReturnBd = InstancePtr->LastBdAddr;
}
else {
ReturnBd = (u32)BdPtr - InstancePtr->BdSeparation;
}
return (XAxiCdma_Bd *)ReturnBd;
}
/*****************************************************************************/
/**
* This function creates the BD ring for the driver instance. If a BD ring
* pre-exist of this ring, the previous ring is lost.
*
* @param InstancePtr is the driver instance we are working on
* @param PhysAddr is the physical address of the memory for the BD ring
* @param VirtAddr is the virtual address of the memory for the BD ring
* @param Alignment is the alignment for the BDs
* @param BdCount is the number of BDs to create in the ring
*
* @return
* - XST_SUCCESS for success
* - XST_INVALID_PARAM for invalid parameter
* - XST_DMA_SG_LIST_ERROR for invalid memory region
* - XST_FAILURE if the hardware build is simple mode only
*
* @note For a system that has flat memory layout, then the PhysAddr
* and the VirtAddr are the same.
*
*****************************************************************************/
int XAxiCdma_BdRingCreate(XAxiCdma *InstancePtr, u32 PhysAddr,
u32 VirtAddr, u32 Alignment, int BdCount)
{
u32 BdVirtAddr;
u32 BdNxtPhysAddr;
u32 BdPhysAddr;
int Index;
if (InstancePtr->SimpleOnlyBuild) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCreate: hardware simple "
"mode only\r\n");
return XST_FAILURE;
}
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
*/
InstancePtr->AllBdCnt = 0;
InstancePtr->FreeBdCnt = 0;
InstancePtr->HwBdCnt = 0;
InstancePtr->PreBdCnt = 0;
InstancePtr->PostBdCnt = 0;
/* Make sure Alignment parameter meets minimum requirements */
if (Alignment < XAXICDMA_BD_MINIMUM_ALIGNMENT) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCreate: alignment too small "
"%d, need to be at least %d\r\n", (int)Alignment,
(int)XAXICDMA_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 have valid Alignment */
if ((PhysAddr & (Alignment - 1)) || (VirtAddr & (Alignment - 1))) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingCreate: Physical address %x "
"and/or virtual address %x have invalid 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 */
InstancePtr->BdSeparation =
(sizeof(XAxiCdma_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 + (InstancePtr->BdSeparation * 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
*/
memset((void *) VirtAddr, 0, (InstancePtr->BdSeparation * BdCount));
/* The virtual address is used to reference/flush cache for the BD
* The physical address is to be put in the BD for hardware reference
*/
BdVirtAddr = VirtAddr;
BdNxtPhysAddr = PhysAddr + InstancePtr->BdSeparation;
BdPhysAddr = PhysAddr;
for (Index = 1; Index < BdCount; Index++) {
XAxiCdma_BdSetNextPtr((XAxiCdma_Bd *)BdVirtAddr, BdNxtPhysAddr);
XAxiCdma_BdSetPhysAddr((XAxiCdma_Bd *)BdVirtAddr, BdPhysAddr);
XAxiCdma_BdSetIsLite((XAxiCdma_Bd *)BdVirtAddr,
InstancePtr->IsLite);
XAxiCdma_BdSetHasDRE((XAxiCdma_Bd *)BdVirtAddr,
InstancePtr->HasDRE);
XAxiCdma_BdSetWordLen((XAxiCdma_Bd *)BdVirtAddr,
InstancePtr->WordLength);
XAxiCdma_BdSetMaxLen((XAxiCdma_Bd *)BdVirtAddr,
InstancePtr->MaxTransLen);
XAXICDMA_CACHE_FLUSH(BdVirtAddr);
BdVirtAddr += InstancePtr->BdSeparation;
BdNxtPhysAddr += InstancePtr->BdSeparation;
BdPhysAddr += InstancePtr->BdSeparation;
}
/* At the end of the ring, link the last BD back to the top,
* and set its fields
*/
XAxiCdma_BdSetNextPtr((XAxiCdma_Bd *)BdVirtAddr, PhysAddr);
XAxiCdma_BdSetPhysAddr((XAxiCdma_Bd *)BdVirtAddr, BdPhysAddr);
XAxiCdma_BdSetIsLite((XAxiCdma_Bd *)BdVirtAddr,
InstancePtr->IsLite);
XAxiCdma_BdSetHasDRE((XAxiCdma_Bd *)BdVirtAddr,
InstancePtr->HasDRE);
XAxiCdma_BdSetWordLen((XAxiCdma_Bd *)BdVirtAddr,
InstancePtr->WordLength);
XAxiCdma_BdSetMaxLen((XAxiCdma_Bd *)BdVirtAddr,
InstancePtr->MaxTransLen);
/* Setup and initialize pointers and counters */
InstancePtr->FirstBdAddr = VirtAddr;
InstancePtr->FirstBdPhysAddr = PhysAddr;
InstancePtr->LastBdAddr = BdVirtAddr;
InstancePtr->BdRingTotalLen = InstancePtr->LastBdAddr -
InstancePtr->FirstBdAddr + InstancePtr->BdSeparation;
InstancePtr->AllBdCnt = BdCount;
InstancePtr->FreeBdCnt = BdCount;
InstancePtr->FreeBdHead = (XAxiCdma_Bd *) VirtAddr;
InstancePtr->PreBdHead = (XAxiCdma_Bd *) VirtAddr;
InstancePtr->HwBdHead = (XAxiCdma_Bd *) VirtAddr;
InstancePtr->HwBdTail = (XAxiCdma_Bd *) VirtAddr;
InstancePtr->PostBdHead = (XAxiCdma_Bd *) VirtAddr;
InstancePtr->BdaRestart = (XAxiCdma_Bd *) PhysAddr;
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function clones all BDs in the BD ring to be the same as the given
* BD.
*
* @param InstancePtr is the driver instance we are working on
* @param TemplateBdPtr is the BD to be copied from
*
* @return
* - XST_SUCCESS for success
* - XST_DMA_SG_NO_LIST if there is no BD ring
* - XST_DEVICE_IS_STARTED if the hardware is running
* - XST_DMA_SG_LIST_ERROR is the BD ring is still in use
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingClone(XAxiCdma *InstancePtr, XAxiCdma_Bd * TemplateBdPtr)
{
int Index;
u32 CurBd;
XAxiCdma_Bd TmpBd;
/* Can't do this function if there isn't a ring */
if (InstancePtr->AllBdCnt == 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 (XAxiCdma_IsBusy(InstancePtr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingClone: engine is running, "
"cannot do\r\n");
return XST_DEVICE_IS_STARTED;
}
/* Can't do this function with some of the BDs in use */
if (InstancePtr->FreeBdCnt != InstancePtr->AllBdCnt) {
xdbg_printf(XDBG_DEBUG_ERROR,
"BdRingClone: some bds already in use %d/%d\r\n",
InstancePtr->FreeBdCnt, InstancePtr->AllBdCnt);
return XST_DMA_SG_LIST_ERROR;
}
/* Make a copy of the template then clear the status bits
*/
memcpy(&TmpBd, TemplateBdPtr, sizeof(XAxiCdma_Bd));
XAxiCdma_BdClearSts(&TmpBd);
/* Starting from the top of the ring, save BD.Next, BD.PhysAddr
* overwrite the entire BD with the template, then restore BD.Next
* and BD.PhysAddr
*/
for (Index = 0, CurBd = InstancePtr->FirstBdAddr;
Index < InstancePtr->AllBdCnt;
Index++, CurBd += InstancePtr->BdSeparation) {
XAxiCdma_BdClone((XAxiCdma_Bd *)CurBd, &TmpBd);
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function requests number of BDs from the BD ring.
*
* @param InstancePtr is the driver instance we are working on
* @param NumBd is the number of BDs to request
* @param BdSetPtr is the pointer to the set of BDs returned
*
* @return
* - XST_SUCCESS for success
* - XST_INVALID_PARAM if requests non-positive number of BDs
* - XST_FAILURE if not enough free BDs available
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingAlloc(XAxiCdma *InstancePtr, int NumBd,
XAxiCdma_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 (InstancePtr->FreeBdCnt < NumBd) {
return XST_FAILURE;
}
/* Set the return argument and move FreeBdHead forward */
*BdSetPtr = InstancePtr->FreeBdHead;
XAXICDMA_RING_SEEKAHEAD(InstancePtr, InstancePtr->FreeBdHead, NumBd);
InstancePtr->FreeBdCnt -= NumBd;
InstancePtr->PreBdCnt += NumBd;
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function tries to free the number of BDs back to the ring.
*
* @param InstancePtr is the driver instance we are working on
* @param NumBd is the number of BDs to return
* @param BdSetPtr is the set of BDs to be returned
*
* @return
* - XST_SUCCESS for success
* - XST_INVALID_PARAM if to free non-positive number of BDs
* - XST_FAILURE if BD ring management shows an error
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingUnAlloc(XAxiCdma *InstancePtr, int NumBd,
XAxiCdma_Bd * BdSetPtr)
{
XAxiCdma_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 (InstancePtr->PreBdCnt < NumBd) {
return XST_FAILURE;
}
/* The last BD in the BD set must has the FreeBdHead as its next BD.
* Otherwise, this is not a valid operation.
*/
TmpBd = BdSetPtr;
XAXICDMA_RING_SEEKAHEAD(InstancePtr, TmpBd, NumBd);
if (TmpBd != InstancePtr->FreeBdHead) {
return XST_FAILURE;
}
/* Set the return argument and move FreeBdHead backward */
XAXICDMA_RING_SEEKBACK(InstancePtr, InstancePtr->FreeBdHead, NumBd);
InstancePtr->FreeBdCnt += NumBd;
InstancePtr->PreBdCnt -= NumBd;
return XST_SUCCESS;
}
/*****************************************************************************/
/*
* Stub call back function.
*
* This is used in case the user does not provide a callback function.
* This function only releases the resources.
*
* @param CallBackRef is the call back reference passed in by
* application,A NULL pointer is acceptable.
* @param IrqMask is the interrupt mask regarding this completion
* @param NumBdPtr is the pointer to number of BDs this handler should
* handle
*
* @return None
*
* @note None.
*
*****************************************************************************/
static void StubCallBackFn(void *CallBackRef, u32 IrqMask, int *NumBdPtr)
{
XAxiCdma *InstancePtr;
XAxiCdma_Bd *BdPtr;
int TargetNum;
int BdCount;
int Status;
InstancePtr = (XAxiCdma *)CallBackRef;
TargetNum = *NumBdPtr;
(void) (IrqMask);
BdCount = XAxiCdma_BdRingFromHw(InstancePtr, TargetNum, &BdPtr);
if (BdCount > 0) {
Status = XAxiCdma_BdRingFree(InstancePtr, BdCount, BdPtr);
if (Status != XST_SUCCESS) {
xdbg_printf(XDBG_DEBUG_ERROR, "CallBack: BdRingFree()"
"failed with %08x", Status);
}
}
*NumBdPtr = TargetNum - BdCount;
return;
}
/*****************************************************************************/
/**
* This function tries to enqueue the number of BDs to the hardware.
*
* @param InstancePtr is the driver instance we are working on
* @param NumBd is the number of BDs to enqueue
* @param BdSetPtr is the set of BDs to be enqueued
* @param CallBackFn is the callback function for this transfer, NULL is fine
* @param CallBackRef is the callback reference pointer
*
* @return
* - XST_SUCCESS for success
* - XST_INVALID_PARAM if enqueues negative number of BDs or zero
* transfer len
* - XST_DMA_SG_LIST_ERROR if BD ring management has a problem
* - XST_FIFO_NO_ROOM if the interrupt handler array is full
* - XST_FAILURE for:
* Hardware is in invalid state, for example, reset failed
* Or, the hardware build is simple mode only
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingToHw(XAxiCdma *InstancePtr, int NumBd,
XAxiCdma_Bd *BdSetPtr, XAxiCdma_CallBackFn CallBackFn,
void *CallBackRef)
{
XAxiCdma_Bd *CurBdPtr;
int Index;
if ((!InstancePtr->Initialized) ||
(InstancePtr->SimpleOnlyBuild)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingToHw: driver instance not "
"in valid state or simple only build");
return XST_FAILURE;
}
if (NumBd < 0) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingToHw: enqueue negative "
"number of BDs %d", 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 XAxiCdma_BdRingAlloc() */
if ((InstancePtr->PreBdCnt < NumBd) ||
(InstancePtr->PreBdHead != BdSetPtr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "Bd ring has problems\r\n");
return XST_DMA_SG_LIST_ERROR;
}
/* Check whether the driver can manage another interrupt handler
*/
Index = InstancePtr->SgHandlerTail + 1;
if (Index == XAXICDMA_MAXIMUM_MAX_HANDLER) {
Index = 0;
}
/* If the interrupt handler array is full, we cannot submit this
* transfer
*/
if (InstancePtr->SgHandlerHead == Index) {
return XST_FIFO_NO_ROOM;
}
CurBdPtr = BdSetPtr;
/* Clear the status bits, also check BD length is positive
*
* Checking BD length can be removed if user check return status for
* BD set length call
*/
for (Index = 0; Index < NumBd; Index++) {
/* Make sure the length value in the BD is non-zero. */
if (XAxiCdma_BdGetLength(CurBdPtr) == 0) {
xdbg_printf(XDBG_DEBUG_ERROR,
"0 length bd %x\r\n", (unsigned int)CurBdPtr);
return XST_INVALID_PARAM;
}
XAxiCdma_BdClearSts(CurBdPtr);
/* Flush the current BD so DMA core could see the updates */
XAXICDMA_CACHE_FLUSH((unsigned int)CurBdPtr);
CurBdPtr = XAxiCdma_BdRingNext(InstancePtr, CurBdPtr);
}
/* Rewind the current BD ptr, because the loop over steps one
*/
CurBdPtr = XAxiCdma_BdRingPrev(InstancePtr, CurBdPtr);
/* This set has completed pre-processing, adjust ring pointers and
* counters
*/
XAXICDMA_RING_SEEKAHEAD(InstancePtr, InstancePtr->PreBdHead, NumBd);
InstancePtr->PreBdCnt -= NumBd;
InstancePtr->HwBdTail = CurBdPtr;
InstancePtr->HwBdCnt += NumBd;
/* Put the interrupt handler into the interrupt handler array
*/
Index = InstancePtr->SgHandlerTail;
if (CallBackFn != NULL) {
InstancePtr->Handlers[Index].CallBackFn = CallBackFn;
InstancePtr->Handlers[Index].CallBackRef = CallBackRef;
}
else { /* Use the default handler */
InstancePtr->Handlers[Index].CallBackFn = StubCallBackFn;
InstancePtr->Handlers[Index].CallBackRef = InstancePtr;
}
InstancePtr->Handlers[Index].NumBds = NumBd;
Index += 1;
if (Index == XAXICDMA_MAXIMUM_MAX_HANDLER) {
Index = 0;
}
InstancePtr->SgHandlerTail = Index;
/* Now check whether hardware is in SG mode
* If a simple transfer is still going on or cannot switch to SG
* mode, mark that there are SG transfers waiting
*/
if (XAxiCdma_IsSimpleMode(InstancePtr)) {
if ((InstancePtr->SimpleNotDone) ||
(XAxiCdma_SwitchMode(InstancePtr, XAXICDMA_SG_MODE)
!= XST_SUCCESS)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingToHw: Cannot "
"switch to SG or simple in progress\r\n");
InstancePtr->SGWaiting = 1;
return XST_SUCCESS;
}
}
/* Signal that SG transfer has been satisfied
*/
InstancePtr->SGWaiting = 0;
/* The hardware is in SG mode, update the tail BD pointer register
* so that hardware will process the transfer
*/
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_TDESC_OFFSET,
XAxiCdma_BdGetPhysAddr((XAxiCdma_Bd *)InstancePtr->HwBdTail));
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function tries to retrieve completed BDs from the hardware.
*
* @param InstancePtr is the driver instance we are working on
* @param BdLimit is the maximum number of completed BDs to retrieve
* @param BdSetPtr is the set of completed BDs
*
* @return The number of completed BDs.
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingFromHw(XAxiCdma *InstancePtr, int BdLimit,
XAxiCdma_Bd ** BdSetPtr)
{
XAxiCdma_Bd *CurBdPtr;
int BdCount;
u32 BdSts;
CurBdPtr = InstancePtr->HwBdHead;
BdCount = 0;
BdSts = 0;
/* If no BDs in work group, then there's nothing to search */
if (InstancePtr->HwBdCnt == 0) {
*BdSetPtr = (XAxiCdma_Bd *)NULL;
return 0;
}
if (BdLimit > InstancePtr->HwBdCnt) {
BdLimit = InstancePtr->HwBdCnt;
}
/* Starting at HwBdHead, 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.
* - InstancePtr->HwBdTail is reached
* - The number of requested BDs has been processed
*/
while (BdCount < BdLimit) {
/* Read the status */
XAXICDMA_CACHE_INVALIDATE((unsigned int)CurBdPtr);
BdSts = XAxiCdma_BdGetSts(CurBdPtr);
/* If the hardware still hasn't processed this BD then we are
* done
*/
if (!(BdSts & XAXICDMA_BD_STS_COMPLETE_MASK)) {
break;
}
BdCount++;
/* Reached the end of the work group */
if (CurBdPtr == InstancePtr->HwBdTail) {
break;
}
/* Move on to the next BD in work group */
CurBdPtr = XAxiCdma_BdRingNext(InstancePtr, CurBdPtr);
}
/* If BdCount is non-zero then BDs were found to return. Set return
* parameters, update pointers and counters, return success
*/
if (BdCount) {
*BdSetPtr = InstancePtr->HwBdHead;
InstancePtr->HwBdCnt -= BdCount;
InstancePtr->PostBdCnt += BdCount;
XAXICDMA_RING_SEEKAHEAD(InstancePtr,
InstancePtr->HwBdHead, BdCount);
return BdCount;
}
else {
*BdSetPtr = (XAxiCdma_Bd *)NULL;
return 0;
}
return 0;
}
/*****************************************************************************/
/**
* This function returns the BDs back to the free pool of the BD ring.
*
* @param InstancePtr is the driver instance we are working on
* @param NumBd is the number of BDs to free
* @param BdSetPtr is the set of BDs to be freed
*
* @return
* - XST_SUCCESS for success
* - XST_INVALID_PARAM if number of BDs is negative
* - XST_DMA_SG_LIST_ERROR if the BD ring management has a problem
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingFree(XAxiCdma *InstancePtr, int NumBd,
XAxiCdma_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 XAxiCdma_BdRingFromHw() */
if ((InstancePtr->PostBdCnt < NumBd) ||
(InstancePtr->PostBdHead != BdSetPtr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "BdRingFree: Error free BDs: "
"post count %d to free %d, PostBdHead %x to free ptr %x\r\n",
InstancePtr->PostBdCnt, NumBd,
(unsigned int)InstancePtr->PostBdHead,
(unsigned int)BdSetPtr);
return XST_DMA_SG_LIST_ERROR;
}
/* Update pointers and counters */
InstancePtr->FreeBdCnt += NumBd;
InstancePtr->PostBdCnt -= NumBd;
XAXICDMA_RING_SEEKAHEAD(InstancePtr, InstancePtr->PostBdHead, NumBd);
return XST_SUCCESS;
}
/*****************************************************************************/
/*
* This function tries to start the SG transfer that has been submitted to
* the driver, however, not to the hardware yet, because the hardware was
* doing a simple transfer at the time of submit.
*
* This function should be called when the simple transfer is done.
*
* @param InstancePtr is the driver instance we are working on
*
* @return
* - XST_SUCCESS if BD ring has been successfully started,
* SGWaiting will be 0
* - XST_FAILURE if BD ring cannot be be started, either because
* the hardware is simple only build or cannot switch to SG mode.
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_BdRingStartTransfer(XAxiCdma *InstancePtr)
{
if (InstancePtr->SimpleOnlyBuild) {
return XST_FAILURE;
}
/* If no BDs need to be transfered, we are done
*/
if (InstancePtr->HwBdCnt == 0) {
return XST_SUCCESS;
}
/* See whether hardware is in simple mode
*/
if (XAxiCdma_IsSimpleMode(InstancePtr)) {
/* Cannot switch to SG mode
*/
if ((InstancePtr->SimpleNotDone) ||
(XAxiCdma_SwitchMode(InstancePtr, XAXICDMA_SG_MODE)
!= XST_SUCCESS)) {
return XST_FAILURE;
}
}
/* Now it is in SG mode, update the tail pointer to start the
* SG transfer
*/
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_TDESC_OFFSET,
XAxiCdma_BdGetPhysAddr((XAxiCdma_Bd *)InstancePtr->HwBdTail));
InstancePtr->SGWaiting = 0;
return XST_SUCCESS;
}