| /****************************************************************************** |
| * |
| * 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 |
| * XILINX 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 |
| * @addtogroup emacps_v3_7 |
| * @{ |
| * |
| * This file implements buffer descriptor ring related functions. |
| * |
| * <pre> |
| * 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. |
| * 3.0 kvn 02/13/15 Modified code for MISRA-C:2012 compliance. |
| * 3.6 rb 09/08/17 Add XEmacPs_BdRingPtrReset() API to reset BD ring |
| * pointers |
| * |
| * </pre> |
| ******************************************************************************/ |
| |
| /***************************** 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)); |
| |
| /* 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: |
| * |
| * <pre> |
| * 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<NumBd; i++) |
| * { |
| * * Prepare CurBd *..... |
| * |
| * * Onto next BD * |
| * CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd), |
| * } |
| * |
| * * Give list to hardware * |
| * Status = XEmacPs_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 * |
| * 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), |
| * </pre> |
| * |
| * 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: |
| * |
| * <pre> |
| * Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr), |
| * ... |
| * if (Error) |
| * { |
| * Status = XEmacPs_BdRingUnAlloc(MyRingPtr, 10, &BdPtr), |
| * } |
| * </pre> |
| * |
| * A partial UnAlloc means some of the BDs Alloc'd will be returned: |
| * |
| * <pre> |
| * 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--, |
| * } |
| * </pre> |
| * |
| * 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: |
| * |
| * <pre> |
| * NumBd = XEmacPs_BdRingFromHwTx(MyRingPtr, MaxBd, &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 = XEmacPs_BdRingNext(MyRingPtr, CurBd), |
| * } |
| * |
| * XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet), *Return 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 * |
| * 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), |
| * </pre> |
| * |
| * 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: |
| * |
| * <pre> |
| * NumBd = XEmacPs_BdRingFromHwRx(MyRingPtr, MaxBd, &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 = XEmacPs_BdRingNext(MyRingPtr, CurBd), |
| * } |
| * |
| * XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet), * Return 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 * |
| * 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), |
| * </pre> |
| * |
| * 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; |
| } |
| } |
| |
| /*****************************************************************************/ |
| /** |
| * Reset BD ring head and tail pointers. |
| * |
| * @param RingPtr is the instance to be worked on. |
| * @param VirtAddr is the virtual base address of the user memory region. |
| * |
| * @note |
| * Should be called after XEmacPs_Stop() |
| * |
| * @note |
| * C-style signature: |
| * void XEmacPs_BdRingPtrReset(XEmacPs_BdRing * RingPtr, void *virtaddrloc) |
| * |
| *****************************************************************************/ |
| void XEmacPs_BdRingPtrReset(XEmacPs_BdRing * RingPtr, void *virtaddrloc) |
| { |
| RingPtr->FreeHead = virtaddrloc; |
| RingPtr->PreHead = virtaddrloc; |
| RingPtr->HwHead = virtaddrloc; |
| RingPtr->HwTail = virtaddrloc; |
| RingPtr->PostHead = virtaddrloc; |
| } |
| |
| /** @} */ |