/* $Id: xemacps_bdring.c,v 1.1.2.1 2011/01/20 03:39:02 sadanan Exp $ */ /****************************************************************************** * * 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 xemacps_bdring.c * * This file implements buffer descriptor ring related functions. * *
* MODIFICATION HISTORY: * * Ver Who Date Changes * ----- ---- -------- ------------------------------------------------------- * 1.00a wsy 01/10/10 First release * 1.00a asa 11/21/11 The function XEmacPs_BdRingFromHwTx is modified. * Earlier it used to search in "BdLimit" number of BDs to * know which BDs are processed. Now one more check is * added. It looks for BDs till the current BD pointer * reaches HwTail. By doing this processing time is saved. * 1.00a asa 01/24/12 The function XEmacPs_BdRingFromHwTx in file * xemacps_bdring.c is modified. Now start of packet is * searched for returning the number of BDs processed. * 1.05a asa 09/23/13 Cache operations on BDs are not required and hence * removed. It is expected that all BDs are allocated in * from uncached area. Fix for CR #663885. * 2.1 srt 07/15/14 Add support for Zynq Ultrascale Mp architecture. * *******************************************************************************/ /***************************** Include Files *********************************/ #include "xstatus.h" #include "xil_cache.h" #include "xemacps_hw.h" #include "xemacps_bd.h" #include "xemacps_bdring.h" /************************** Constant Definitions *****************************/ /**************************** Type Definitions *******************************/ /***************** Macros (Inline Functions) Definitions *********************/ /**************************************************************************** * Compute the virtual address of a descriptor from its physical address * * @param BdPtr is the physical address of the BD * * @returns Virtual address of BdPtr * * @note Assume BdPtr is always a valid BD in the ring ****************************************************************************/ #define XEMACPS_PHYS_TO_VIRT(BdPtr) \ ((UINTPTR)(BdPtr) + (RingPtr->BaseBdAddr - RingPtr->PhysBaseAddr)) /**************************************************************************** * Compute the physical address of a descriptor from its virtual address * * @param BdPtr is the physical address of the BD * * @returns Physical address of BdPtr * * @note Assume BdPtr is always a valid BD in the ring ****************************************************************************/ #define XEMACPS_VIRT_TO_PHYS(BdPtr) \ ((UINTPTR)(BdPtr) - (RingPtr->BaseBdAddr - RingPtr->PhysBaseAddr)) /**************************************************************************** * Move the BdPtr argument ahead an arbitrary number of BDs wrapping around * to the beginning of the ring if needed. * * We know if a wrapaound should occur if the new BdPtr is greater than * the high address in the ring OR if the new BdPtr crosses over the * 0xFFFFFFFF to 0 boundary. The latter test is a valid one since we do not * allow a BD space to span this 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 * ****************************************************************************/ #define XEMACPS_RING_SEEKAHEAD(RingPtr, BdPtr, NumBd) \ { \ UINTPTR Addr = (UINTPTR)(void *)(BdPtr); \ \ Addr += ((RingPtr)->Separation * (NumBd)); \ if ((Addr > (RingPtr)->HighBdAddr) || ((UINTPTR)(void *)(BdPtr) > Addr)) \ { \ Addr -= (RingPtr)->Length; \ } \ \ (BdPtr) = (XEmacPs_Bd*)(void *)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 wrapaound should occur if the new BdPtr is less than * the base address in the ring OR if the new BdPtr crosses over the * 0xFFFFFFFF to 0 boundary. The latter test is a valid one since we do not * allow a BD space to span this 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 * ****************************************************************************/ #define XEMACPS_RING_SEEKBACK(RingPtr, BdPtr, NumBd) \ { \ UINTPTR Addr = (UINTPTR)(void *)(BdPtr); \ \ Addr -= ((RingPtr)->Separation * (NumBd)); \ if ((Addr < (RingPtr)->BaseBdAddr) || ((UINTPTR)(void*)(BdPtr) < Addr)) \ { \ Addr += (RingPtr)->Length; \ } \ \ (BdPtr) = (XEmacPs_Bd*)(void*)Addr; \ } /************************** Function Prototypes ******************************/ static void XEmacPs_BdSetRxWrap(UINTPTR BdPtr); static void XEmacPs_BdSetTxWrap(UINTPTR BdPtr); /************************** Variable Definitions *****************************/ /*****************************************************************************/ /** * Using a memory segment allocated by the caller, create and setup the BD list * for the given DMA channel. * * @param RingPtr is the instance to be worked on. * @param PhysAddr is the physical base address of user memory region. * @param VirtAddr is the virtual base address of the user 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 4 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 user memory region. It * is assumed the region is large enough to contain the BDs. * * @return * * - XST_SUCCESS if initialization was successful * - XST_NO_FEATURE if the provided instance is a non DMA type * channel. * - XST_INVALID_PARAM under any of the following conditions: * 1) PhysAddr and/or VirtAddr are not aligned to the given Alignment * parameter. * 2) Alignment parameter does not meet minimum requirements or is not a * power of 2 value. * 3) BdCount is 0. * - XST_DMA_SG_LIST_ERROR if the memory segment containing the list spans * over address 0x00000000 in virtual address space. * * @note * Make sure to pass in the right alignment value. *****************************************************************************/ LONG XEmacPs_BdRingCreate(XEmacPs_BdRing * RingPtr, UINTPTR PhysAddr, UINTPTR VirtAddr, u32 Alignment, u32 BdCount) { u32 i; UINTPTR BdVirtAddr; UINTPTR BdPhyAddr; UINTPTR VirtAddrLoc = VirtAddr; /* In case there is a failure prior to creating list, make sure the * following attributes are 0 to prevent calls to other functions * from doing anything. */ RingPtr->AllCnt = 0U; RingPtr->FreeCnt = 0U; RingPtr->HwCnt = 0U; RingPtr->PreCnt = 0U; RingPtr->PostCnt = 0U; /* Make sure Alignment parameter meets minimum requirements */ if (Alignment < (u32)XEMACPS_DMABD_MINIMUM_ALIGNMENT) { return (LONG)(XST_INVALID_PARAM); } /* Make sure Alignment is a power of 2 */ if (((Alignment - 0x00000001U) & Alignment)!=0x00000000U) { return (LONG)(XST_INVALID_PARAM); } /* Make sure PhysAddr and VirtAddr are on same Alignment */ if (((PhysAddr % Alignment)!=(u32)0) || ((VirtAddrLoc % Alignment)!=(u32)0)) { return (LONG)(XST_INVALID_PARAM); } /* Is BdCount reasonable? */ if (BdCount == 0x00000000U) { return (LONG)(XST_INVALID_PARAM); } /* Figure out how many bytes will be between the start of adjacent BDs */ RingPtr->Separation = ((u32)sizeof(XEmacPs_Bd) + (Alignment - (u32)1)) & ~(Alignment - (u32)1); /* Must make sure the ring doesn't span address 0x00000000. If it does, * then the next/prev BD traversal macros will fail. */ if (VirtAddrLoc > ((VirtAddrLoc + (RingPtr->Separation * BdCount)) - (u32)1)) { return (LONG)(XST_DMA_SG_LIST_ERROR); } /* Initial ring setup: * - Clear the entire space * - Setup each BD's BDA field with the physical address of the next BD */ (void)memset((void *) VirtAddrLoc, 0, (RingPtr->Separation * BdCount)); BdVirtAddr = VirtAddrLoc; BdPhyAddr = PhysAddr + RingPtr->Separation; for (i = 1U; i < BdCount; i++) { BdVirtAddr += RingPtr->Separation; BdPhyAddr += RingPtr->Separation; } /* Setup and initialize pointers and counters */ RingPtr->RunState = (u32)(XST_DMA_SG_IS_STOPPED); RingPtr->BaseBdAddr = VirtAddrLoc; RingPtr->PhysBaseAddr = PhysAddr; RingPtr->HighBdAddr = BdVirtAddr; RingPtr->Length = ((RingPtr->HighBdAddr - RingPtr->BaseBdAddr) + RingPtr->Separation); RingPtr->AllCnt = (u32)BdCount; RingPtr->FreeCnt = (u32)BdCount; RingPtr->FreeHead = (XEmacPs_Bd *)(void *)VirtAddrLoc; RingPtr->PreHead = (XEmacPs_Bd *)VirtAddrLoc; RingPtr->HwHead = (XEmacPs_Bd *)VirtAddrLoc; RingPtr->HwTail = (XEmacPs_Bd *)VirtAddrLoc; RingPtr->PostHead = (XEmacPs_Bd *)VirtAddrLoc; RingPtr->BdaRestart = (XEmacPs_Bd *)(void *)PhysAddr; return (LONG)(XST_SUCCESS); } /*****************************************************************************/ /** * Clone the given BD into every BD in the list. * every field of the source BD is replicated in every BD of the list. * * This function can be called only when all BDs are in the free group such as * they are immediately after initialization with XEmacPs_BdRingCreate(). * This prevents modification of BDs while they are in use by hardware or the * user. * * @param RingPtr is the pointer of BD ring instance to be worked on. * @param SrcBdPtr is the source BD template to be cloned into the list. This * BD will be modified. * @param Direction is either XEMACPS_SEND or XEMACPS_RECV that indicates * which direction. * * @return * - XST_SUCCESS if the list was modified. * - XST_DMA_SG_NO_LIST if a list has not been created. * - XST_DMA_SG_LIST_ERROR if some of the BDs in this channel are under * hardware or user control. * - XST_DEVICE_IS_STARTED if the DMA channel has not been stopped. * *****************************************************************************/ LONG XEmacPs_BdRingClone(XEmacPs_BdRing * RingPtr, XEmacPs_Bd * SrcBdPtr, u8 Direction) { u32 i; UINTPTR CurBd; /* Can't do this function if there isn't a ring */ if (RingPtr->AllCnt == 0x00000000U) { return (LONG)(XST_DMA_SG_NO_LIST); } /* Can't do this function with the channel running */ if (RingPtr->RunState == (u32)XST_DMA_SG_IS_STARTED) { return (LONG)(XST_DEVICE_IS_STARTED); } /* Can't do this function with some of the BDs in use */ if (RingPtr->FreeCnt != RingPtr->AllCnt) { return (LONG)(XST_DMA_SG_LIST_ERROR); } if ((Direction != (u8)XEMACPS_SEND) && (Direction != (u8)XEMACPS_RECV)) { return (LONG)(XST_INVALID_PARAM); } /* Starting from the top of the ring, save BD.Next, overwrite the entire * BD with the template, then restore BD.Next */ CurBd = RingPtr->BaseBdAddr; for (i = 0U; i < RingPtr->AllCnt; i++) { memcpy((void *)CurBd, SrcBdPtr, sizeof(XEmacPs_Bd)); CurBd += RingPtr->Separation; } CurBd -= RingPtr->Separation; if (Direction == XEMACPS_RECV) { XEmacPs_BdSetRxWrap(CurBd); } else { XEmacPs_BdSetTxWrap(CurBd); } return (LONG)(XST_SUCCESS); } /*****************************************************************************/ /** * Reserve locations in the BD list. The set of returned BDs may be modified * in preparation for future DMA transaction(s). Once the BDs are ready to be * submitted to hardware, the user must call XEmacPs_BdRingToHw() in the same * order which they were allocated here. Example: * *
* NumBd = 2, * Status = XEmacPs_BdRingAlloc(MyRingPtr, NumBd, &MyBdSet), * * if (Status != XST_SUCCESS) * { * *Not enough BDs available for the request* * } * * CurBd = MyBdSet, * for (i=0; i* * 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: * * * Legal * * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1), * * * Legal * * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2), * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1), * XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2), * * * Not legal * * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2), * XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2), * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1), ** * Use the API defined in xemacps_bd.h to modify individual BDs. Traversal * of the BD set can be done using XEmacPs_BdRingNext() and * XEmacPs_BdRingPrev(). * * @param RingPtr is a pointer to the BD 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 was returned in the BdSetPtr * parameter. * - XST_FAILURE if there were not enough free BDs to satisfy the request. * * @note This function should not be preempted by another XEmacPs_Bd function * call that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * * @note Do not modify more BDs than the number requested with the NumBd * parameter. Doing so will lead to data corruption and system * instability. * *****************************************************************************/ LONG XEmacPs_BdRingAlloc(XEmacPs_BdRing * RingPtr, u32 NumBd, XEmacPs_Bd ** BdSetPtr) { LONG Status; /* Enough free BDs available for the request? */ if (RingPtr->FreeCnt < NumBd) { Status = (LONG)(XST_FAILURE); } else { /* Set the return argument and move FreeHead forward */ *BdSetPtr = RingPtr->FreeHead; XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->FreeHead, NumBd); RingPtr->FreeCnt -= NumBd; RingPtr->PreCnt += NumBd; Status = (LONG)(XST_SUCCESS); } return Status; } /*****************************************************************************/ /** * Fully or partially undo an XEmacPs_BdRingAlloc() operation. Use this * function if all the BDs allocated by XEmacPs_BdRingAlloc() could not be * transferred to hardware with XEmacPs_BdRingToHw(). * * This function helps out in situations when an unrelated error occurs after * BDs have been allocated but before they have been given to hardware. * An example of this type of error would be an OS running out of resources. * * This function is not the same as XEmacPs_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: * ** Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr), * ... * if (Error) * { * Status = XEmacPs_BdRingUnAlloc(MyRingPtr, 10, &BdPtr), * } ** * A partial UnAlloc means some of the BDs Alloc'd will be returned: * ** Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr), * BdsLeft = 10, * CurBdPtr = BdPtr, * * while (BdsLeft) * { * if (Error) * { * Status = XEmacPs_BdRingUnAlloc(MyRingPtr, BdsLeft, CurBdPtr), * } * * CurBdPtr = XEmacPs_BdRingNext(MyRingPtr, CurBdPtr), * BdsLeft--, * } ** * A partial UnAlloc must include the last BD in the list that was Alloc'd. * * @param RingPtr is a pointer to the 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 BDs were unallocated. * - 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 XEmacPs_Bd function * call that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * *****************************************************************************/ LONG XEmacPs_BdRingUnAlloc(XEmacPs_BdRing * RingPtr, u32 NumBd, XEmacPs_Bd * BdSetPtr) { LONG Status; (void *)BdSetPtr; Xil_AssertNonvoid(RingPtr != NULL); Xil_AssertNonvoid(BdSetPtr != NULL); /* Enough BDs in the free state for the request? */ if (RingPtr->PreCnt < NumBd) { Status = (LONG)(XST_FAILURE); } else { /* Set the return argument and move FreeHead backward */ XEMACPS_RING_SEEKBACK(RingPtr, (RingPtr->FreeHead), NumBd); RingPtr->FreeCnt += NumBd; RingPtr->PreCnt -= NumBd; Status = (LONG)(XST_SUCCESS); } return Status; } /*****************************************************************************/ /** * Enqueue a set of BDs to hardware that were previously allocated by * XEmacPs_BdRingAlloc(). Once this function returns, the argument BD set goes * under hardware control. Any changes made to these BDs after this point will * corrupt the BD list leading to data corruption and system instability. * * The set will be rejected if the last BD of the set does not mark the end of * a packet (see XEmacPs_BdSetLast()). * * @param RingPtr is a pointer to the 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_FAILURE if the set of BDs was rejected because the last BD of the set * did not have its "last" bit set. * - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with * XEmacPs_BdRingAlloc(). * * @note This function should not be preempted by another XEmacPs_Bd function * call that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * *****************************************************************************/ LONG XEmacPs_BdRingToHw(XEmacPs_BdRing * RingPtr, u32 NumBd, XEmacPs_Bd * BdSetPtr) { XEmacPs_Bd *CurBdPtr; u32 i; LONG Status; /* if no bds to process, simply return. */ if (0U == NumBd){ Status = (LONG)(XST_SUCCESS); } else { /* Make sure we are in sync with XEmacPs_BdRingAlloc() */ if ((RingPtr->PreCnt < NumBd) || (RingPtr->PreHead != BdSetPtr)) { Status = (LONG)(XST_DMA_SG_LIST_ERROR); } else { CurBdPtr = BdSetPtr; for (i = 0U; i < NumBd; i++) { CurBdPtr = (XEmacPs_Bd *)((void *)XEmacPs_BdRingNext(RingPtr, CurBdPtr)); } /* Adjust ring pointers & counters */ XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->PreHead, NumBd); RingPtr->PreCnt -= NumBd; RingPtr->HwTail = CurBdPtr; RingPtr->HwCnt += NumBd; Status = (LONG)(XST_SUCCESS); } } return Status; } /*****************************************************************************/ /** * Returns a set of BD(s) that have been processed by hardware. The returned * BDs may be examined to determine the outcome of the DMA transaction(s). * Once the BDs have been examined, the user must call XEmacPs_BdRingFree() * in the same order which they were retrieved here. Example: * ** NumBd = XEmacPs_BdRingFromHwTx(MyRingPtr, MaxBd, &MyBdSet), * if (NumBd == 0) * { * * hardware has nothing ready for us yet* * } * * CurBd = MyBdSet, * for (i=0; i* * 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: * * * Legal * * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1), * * * Legal * * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2), * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1), * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2), * * * Not legal * * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2), * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2), * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1), ** * If hardware has only 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 instance to be worked on. * @param BdLimit is the maximum number of BDs to return in the set. * @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. * * @note This function should not be preempted by another XEmacPs_Bd function * call that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * *****************************************************************************/ u32 XEmacPs_BdRingFromHwTx(XEmacPs_BdRing * RingPtr, u32 BdLimit, XEmacPs_Bd ** BdSetPtr) { XEmacPs_Bd *CurBdPtr; u32 BdStr = 0U; u32 BdCount; u32 BdPartialCount; u32 Sop = 0U; u32 Status; u32 BdLimitLoc = BdLimit; CurBdPtr = RingPtr->HwHead; BdCount = 0U; BdPartialCount = 0U; /* If no BDs in work group, then there's nothing to search */ if (RingPtr->HwCnt == 0x00000000U) { *BdSetPtr = NULL; Status = 0U; } else { if (BdLimitLoc > RingPtr->HwCnt){ BdLimitLoc = RingPtr->HwCnt; } /* Starting at HwHead, keep moving forward in the list until: * - A BD is encountered with its new/used bit set which means * hardware has not completed processing of that BD. * - RingPtr->HwTail is reached and RingPtr->HwCnt is reached. * - The number of requested BDs has been processed */ while (BdCount < BdLimitLoc) { /* Read the status */ if(CurBdPtr != NULL){ BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET); } if ((Sop == 0x00000000U) && ((BdStr & XEMACPS_TXBUF_USED_MASK)!=0x00000000U)){ Sop = 1U; } if (Sop == 0x00000001U) { BdCount++; BdPartialCount++; } /* 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. */ if ((Sop == 0x00000001U) && ((BdStr & XEMACPS_TXBUF_LAST_MASK)!=0x00000000U)) { Sop = 0U; BdPartialCount = 0U; } /* Move on to next BD in work group */ CurBdPtr = XEmacPs_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 > 0x00000000U) { *BdSetPtr = RingPtr->HwHead; RingPtr->HwCnt -= BdCount; RingPtr->PostCnt += BdCount; XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount); Status = (BdCount); } else { *BdSetPtr = NULL; Status = 0U; } } return Status; } /*****************************************************************************/ /** * Returns a set of BD(s) that have been processed by hardware. The returned * BDs may be examined to determine the outcome of the DMA transaction(s). * Once the BDs have been examined, the user must call XEmacPs_BdRingFree() * in the same order which they were retrieved here. Example: * ** NumBd = XEmacPs_BdRingFromHwRx(MyRingPtr, MaxBd, &MyBdSet), * * if (NumBd == 0) * { * *hardware has nothing ready for us yet* * } * * CurBd = MyBdSet, * for (i=0; i* * 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: * * * Legal * * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1), * * * Legal * * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2), * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1), * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2), * * * Not legal * * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1), * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2), * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2), * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1), ** * If hardware has only 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 instance to be worked on. * @param BdLimit is the maximum number of BDs to return in the set. * @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. * * @note This function should not be preempted by another XEmacPs_Bd function * call that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * *****************************************************************************/ u32 XEmacPs_BdRingFromHwRx(XEmacPs_BdRing * RingPtr, u32 BdLimit, XEmacPs_Bd ** BdSetPtr) { XEmacPs_Bd *CurBdPtr; u32 BdStr = 0U; u32 BdCount; u32 BdPartialCount; u32 Status; CurBdPtr = RingPtr->HwHead; BdCount = 0U; BdPartialCount = 0U; /* If no BDs in work group, then there's nothing to search */ if (RingPtr->HwCnt == 0x00000000U) { *BdSetPtr = NULL; Status = 0U; } else { /* Starting at HwHead, keep moving forward in the list until: * - A BD is encountered with its new/used bit set which means * hardware has completed processing of that BD. * - RingPtr->HwTail is reached and RingPtr->HwCnt is reached. * - The number of requested BDs has been processed */ while (BdCount < BdLimit) { /* Read the status */ if(CurBdPtr!=NULL){ BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET); } if ((!(XEmacPs_BdIsRxNew(CurBdPtr)))==TRUE) { 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. */ if ((BdStr & XEMACPS_RXBUF_EOF_MASK)!=0x00000000U) { BdPartialCount = 0U; } else { BdPartialCount++; } /* Move on to next BD in work group */ CurBdPtr = XEmacPs_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 > 0x00000000U) { *BdSetPtr = RingPtr->HwHead; RingPtr->HwCnt -= BdCount; RingPtr->PostCnt += BdCount; XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount); Status = (BdCount); } else { *BdSetPtr = NULL; Status = 0U; } } return Status; } /*****************************************************************************/ /** * Frees a set of BDs that had been previously retrieved with * XEmacPs_BdRingFromHw(). * * @param RingPtr is a pointer to the 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 * XEmacPs_BdRingFromHw(). * * @return * - XST_SUCCESS if the set of BDs was freed. * - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with * XEmacPs_BdRingFromHw(). * * @note This function should not be preempted by another XEmacPs_Bd function * call that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * *****************************************************************************/ LONG XEmacPs_BdRingFree(XEmacPs_BdRing * RingPtr, u32 NumBd, XEmacPs_Bd * BdSetPtr) { LONG Status; /* if no bds to process, simply return. */ if (0x00000000U == NumBd){ Status = (LONG)(XST_SUCCESS); } else { /* Make sure we are in sync with XEmacPs_BdRingFromHw() */ if ((RingPtr->PostCnt < NumBd) || (RingPtr->PostHead != BdSetPtr)) { Status = (LONG)(XST_DMA_SG_LIST_ERROR); } else { /* Update pointers and counters */ RingPtr->FreeCnt += NumBd; RingPtr->PostCnt -= NumBd; XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->PostHead, NumBd); Status = (LONG)(XST_SUCCESS); } } return Status; } /*****************************************************************************/ /** * Check the internal data structures of the BD ring for the provided channel. * The following checks are made: * * - Is the BD ring linked correctly in physical address space. * - Do the internal pointers point to BDs in the ring. * - Do the internal counters add up. * * The channel should be stopped prior to calling this function. * * @param RingPtr is a pointer to the instance to be worked on. * @param Direction is either XEMACPS_SEND or XEMACPS_RECV that indicates * which direction. * * @return * - XST_SUCCESS if the set of BDs was freed. * - XST_DMA_SG_NO_LIST if the list 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 to * avoid data corruption or system instability. * * @note This function should not be preempted by another XEmacPs_Bd function * call that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * *****************************************************************************/ LONG XEmacPs_BdRingCheck(XEmacPs_BdRing * RingPtr, u8 Direction) { UINTPTR AddrV, AddrP; u32 i; if ((Direction != (u8)XEMACPS_SEND) && (Direction != (u8)XEMACPS_RECV)) { return (LONG)(XST_INVALID_PARAM); } /* Is the list created */ if (RingPtr->AllCnt == 0x00000000U) { return (LONG)(XST_DMA_SG_NO_LIST); } /* Can't check if channel is running */ if (RingPtr->RunState == (u32)XST_DMA_SG_IS_STARTED) { return (LONG)(XST_IS_STARTED); } /* RunState doesn't make sense */ if (RingPtr->RunState != (u32)XST_DMA_SG_IS_STOPPED) { return (LONG)(XST_DMA_SG_LIST_ERROR); } /* Verify internal pointers point to correct memory space */ AddrV = (UINTPTR) RingPtr->FreeHead; if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) { return (LONG)(XST_DMA_SG_LIST_ERROR); } AddrV = (UINTPTR) RingPtr->PreHead; if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) { return (LONG)(XST_DMA_SG_LIST_ERROR); } AddrV = (UINTPTR) RingPtr->HwHead; if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) { return (LONG)(XST_DMA_SG_LIST_ERROR); } AddrV = (UINTPTR) RingPtr->HwTail; if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) { return (LONG)(XST_DMA_SG_LIST_ERROR); } AddrV = (UINTPTR) RingPtr->PostHead; if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) { return (LONG)(XST_DMA_SG_LIST_ERROR); } /* Verify internal counters add up */ if ((RingPtr->HwCnt + RingPtr->PreCnt + RingPtr->FreeCnt + RingPtr->PostCnt) != RingPtr->AllCnt) { return (LONG)(XST_DMA_SG_LIST_ERROR); } /* Verify BDs are linked correctly */ AddrV = RingPtr->BaseBdAddr; AddrP = RingPtr->PhysBaseAddr + RingPtr->Separation; for (i = 1U; i < RingPtr->AllCnt; i++) { /* Check BDA for this BD. It should point to next physical addr */ if (XEmacPs_BdRead(AddrV, XEMACPS_BD_ADDR_OFFSET) != AddrP) { return (LONG)(XST_DMA_SG_LIST_ERROR); } /* Move on to next BD */ AddrV += RingPtr->Separation; AddrP += RingPtr->Separation; } /* Last BD should have wrap bit set */ if (XEMACPS_SEND == Direction) { if ((!XEmacPs_BdIsTxWrap(AddrV))==TRUE) { return (LONG)(XST_DMA_SG_LIST_ERROR); } } else { /* XEMACPS_RECV */ if ((!XEmacPs_BdIsRxWrap(AddrV))==TRUE) { return (LONG)(XST_DMA_SG_LIST_ERROR); } } /* No problems found */ return (LONG)(XST_SUCCESS); } /*****************************************************************************/ /** * Set this bit to mark the last descriptor in the receive buffer descriptor * list. * * @param BdPtr is the BD pointer to operate on * * @note * C-style signature: * void XEmacPs_BdSetRxWrap(XEmacPs_Bd* BdPtr) * *****************************************************************************/ static void XEmacPs_BdSetRxWrap(UINTPTR BdPtr) { u32 DataValueRx; u32 *TempPtr; BdPtr += (u32)(XEMACPS_BD_ADDR_OFFSET); TempPtr = (u32 *)BdPtr; if(TempPtr != NULL) { DataValueRx = *TempPtr; DataValueRx |= XEMACPS_RXBUF_WRAP_MASK; *TempPtr = DataValueRx; } } /*****************************************************************************/ /** * Sets this bit to mark the last descriptor in the transmit buffer * descriptor list. * * @param BdPtr is the BD pointer to operate on * * @note * C-style signature: * void XEmacPs_BdSetTxWrap(XEmacPs_Bd* BdPtr) * *****************************************************************************/ static void XEmacPs_BdSetTxWrap(UINTPTR BdPtr) { u32 DataValueTx; u32 *TempPtr; BdPtr += (u32)(XEMACPS_BD_STAT_OFFSET); TempPtr = (u32 *)BdPtr; if(TempPtr != NULL) { DataValueTx = *TempPtr; DataValueTx |= XEMACPS_TXBUF_WRAP_MASK; *TempPtr = DataValueTx; } }