1 /******************************************************************************
3 * Copyright (C) 2010 - 2015 Xilinx, Inc. All rights reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * Use of the Software is limited solely to applications:
16 * (a) running on a Xilinx device, or
17 * (b) that interact with a Xilinx device through a bus or interconnect.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
24 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * Except as contained in this notice, the name of the Xilinx shall not be used
28 * in advertising or otherwise to promote the sale, use or other dealings in
29 * this Software without prior written authorization from Xilinx.
31 ******************************************************************************/
32 /*****************************************************************************/
35 * @file xemacps_bdring.c
36 * @addtogroup emacps_v3_7
39 * This file implements buffer descriptor ring related functions.
42 * MODIFICATION HISTORY:
44 * Ver Who Date Changes
45 * ----- ---- -------- -------------------------------------------------------
46 * 1.00a wsy 01/10/10 First release
47 * 1.00a asa 11/21/11 The function XEmacPs_BdRingFromHwTx is modified.
48 * Earlier it used to search in "BdLimit" number of BDs to
49 * know which BDs are processed. Now one more check is
50 * added. It looks for BDs till the current BD pointer
51 * reaches HwTail. By doing this processing time is saved.
52 * 1.00a asa 01/24/12 The function XEmacPs_BdRingFromHwTx in file
53 * xemacps_bdring.c is modified. Now start of packet is
54 * searched for returning the number of BDs processed.
55 * 1.05a asa 09/23/13 Cache operations on BDs are not required and hence
56 * removed. It is expected that all BDs are allocated in
57 * from uncached area. Fix for CR #663885.
58 * 2.1 srt 07/15/14 Add support for Zynq Ultrascale Mp architecture.
59 * 3.0 kvn 02/13/15 Modified code for MISRA-C:2012 compliance.
60 * 3.6 rb 09/08/17 Add XEmacPs_BdRingPtrReset() API to reset BD ring
64 ******************************************************************************/
66 /***************************** Include Files *********************************/
69 #include "xil_cache.h"
70 #include "xemacps_hw.h"
71 #include "xemacps_bd.h"
72 #include "xemacps_bdring.h"
74 /************************** Constant Definitions *****************************/
76 /**************************** Type Definitions *******************************/
79 /***************** Macros (Inline Functions) Definitions *********************/
81 /****************************************************************************
82 * Compute the virtual address of a descriptor from its physical address
84 * @param BdPtr is the physical address of the BD
86 * @returns Virtual address of BdPtr
88 * @note Assume BdPtr is always a valid BD in the ring
89 ****************************************************************************/
90 #define XEMACPS_PHYS_TO_VIRT(BdPtr) \
91 ((UINTPTR)(BdPtr) + (RingPtr->BaseBdAddr - RingPtr->PhysBaseAddr))
93 /****************************************************************************
94 * Compute the physical address of a descriptor from its virtual address
96 * @param BdPtr is the physical address of the BD
98 * @returns Physical address of BdPtr
100 * @note Assume BdPtr is always a valid BD in the ring
101 ****************************************************************************/
102 #define XEMACPS_VIRT_TO_PHYS(BdPtr) \
103 ((UINTPTR)(BdPtr) - (RingPtr->BaseBdAddr - RingPtr->PhysBaseAddr))
105 /****************************************************************************
106 * Move the BdPtr argument ahead an arbitrary number of BDs wrapping around
107 * to the beginning of the ring if needed.
109 * We know if a wrapaound should occur if the new BdPtr is greater than
110 * the high address in the ring OR if the new BdPtr crosses over the
111 * 0xFFFFFFFF to 0 boundary. The latter test is a valid one since we do not
112 * allow a BD space to span this boundary.
114 * @param RingPtr is the ring BdPtr appears in
115 * @param BdPtr on input is the starting BD position and on output is the
117 * @param NumBd is the number of BD spaces to increment
119 ****************************************************************************/
120 #define XEMACPS_RING_SEEKAHEAD(RingPtr, BdPtr, NumBd) \
122 UINTPTR Addr = (UINTPTR)(void *)(BdPtr); \
124 Addr += ((RingPtr)->Separation * (NumBd)); \
125 if ((Addr > (RingPtr)->HighBdAddr) || ((UINTPTR)(void *)(BdPtr) > Addr)) \
127 Addr -= (RingPtr)->Length; \
130 (BdPtr) = (XEmacPs_Bd*)(void *)Addr; \
133 /****************************************************************************
134 * Move the BdPtr argument backwards an arbitrary number of BDs wrapping
135 * around to the end of the ring if needed.
137 * We know if a wrapaound should occur if the new BdPtr is less than
138 * the base address in the ring OR if the new BdPtr crosses over the
139 * 0xFFFFFFFF to 0 boundary. The latter test is a valid one since we do not
140 * allow a BD space to span this boundary.
142 * @param RingPtr is the ring BdPtr appears in
143 * @param BdPtr on input is the starting BD position and on output is the
145 * @param NumBd is the number of BD spaces to increment
147 ****************************************************************************/
148 #define XEMACPS_RING_SEEKBACK(RingPtr, BdPtr, NumBd) \
150 UINTPTR Addr = (UINTPTR)(void *)(BdPtr); \
152 Addr -= ((RingPtr)->Separation * (NumBd)); \
153 if ((Addr < (RingPtr)->BaseBdAddr) || ((UINTPTR)(void*)(BdPtr) < Addr)) \
155 Addr += (RingPtr)->Length; \
158 (BdPtr) = (XEmacPs_Bd*)(void*)Addr; \
162 /************************** Function Prototypes ******************************/
164 static void XEmacPs_BdSetRxWrap(UINTPTR BdPtr);
165 static void XEmacPs_BdSetTxWrap(UINTPTR BdPtr);
167 /************************** Variable Definitions *****************************/
169 /*****************************************************************************/
171 * Using a memory segment allocated by the caller, create and setup the BD list
172 * for the given DMA channel.
174 * @param RingPtr is the instance to be worked on.
175 * @param PhysAddr is the physical base address of user memory region.
176 * @param VirtAddr is the virtual base address of the user memory region. If
177 * address translation is not being utilized, then VirtAddr should be
178 * equivalent to PhysAddr.
179 * @param Alignment governs the byte alignment of individual BDs. This function
180 * will enforce a minimum alignment of 4 bytes with no maximum as long
181 * as it is specified as a power of 2.
182 * @param BdCount is the number of BDs to setup in the user memory region. It
183 * is assumed the region is large enough to contain the BDs.
187 * - XST_SUCCESS if initialization was successful
188 * - XST_NO_FEATURE if the provided instance is a non DMA type
190 * - XST_INVALID_PARAM under any of the following conditions:
191 * 1) PhysAddr and/or VirtAddr are not aligned to the given Alignment
193 * 2) Alignment parameter does not meet minimum requirements or is not a
196 * - XST_DMA_SG_LIST_ERROR if the memory segment containing the list spans
197 * over address 0x00000000 in virtual address space.
200 * Make sure to pass in the right alignment value.
201 *****************************************************************************/
202 LONG XEmacPs_BdRingCreate(XEmacPs_BdRing * RingPtr, UINTPTR PhysAddr,
203 UINTPTR VirtAddr, u32 Alignment, u32 BdCount)
208 UINTPTR VirtAddrLoc = VirtAddr;
210 /* In case there is a failure prior to creating list, make sure the
211 * following attributes are 0 to prevent calls to other functions
212 * from doing anything.
214 RingPtr->AllCnt = 0U;
215 RingPtr->FreeCnt = 0U;
217 RingPtr->PreCnt = 0U;
218 RingPtr->PostCnt = 0U;
220 /* Make sure Alignment parameter meets minimum requirements */
221 if (Alignment < (u32)XEMACPS_DMABD_MINIMUM_ALIGNMENT) {
222 return (LONG)(XST_INVALID_PARAM);
225 /* Make sure Alignment is a power of 2 */
226 if (((Alignment - 0x00000001U) & Alignment)!=0x00000000U) {
227 return (LONG)(XST_INVALID_PARAM);
230 /* Make sure PhysAddr and VirtAddr are on same Alignment */
231 if (((PhysAddr % Alignment)!=(u32)0) || ((VirtAddrLoc % Alignment)!=(u32)0)) {
232 return (LONG)(XST_INVALID_PARAM);
235 /* Is BdCount reasonable? */
236 if (BdCount == 0x00000000U) {
237 return (LONG)(XST_INVALID_PARAM);
240 /* Figure out how many bytes will be between the start of adjacent BDs */
241 RingPtr->Separation = ((u32)sizeof(XEmacPs_Bd));
243 /* Must make sure the ring doesn't span address 0x00000000. If it does,
244 * then the next/prev BD traversal macros will fail.
246 if (VirtAddrLoc > ((VirtAddrLoc + (RingPtr->Separation * BdCount)) - (u32)1)) {
247 return (LONG)(XST_DMA_SG_LIST_ERROR);
250 /* Initial ring setup:
251 * - Clear the entire space
252 * - Setup each BD's BDA field with the physical address of the next BD
254 (void)memset((void *) VirtAddrLoc, 0, (RingPtr->Separation * BdCount));
256 BdVirtAddr = VirtAddrLoc;
257 BdPhyAddr = PhysAddr + RingPtr->Separation;
258 for (i = 1U; i < BdCount; i++) {
259 BdVirtAddr += RingPtr->Separation;
260 BdPhyAddr += RingPtr->Separation;
263 /* Setup and initialize pointers and counters */
264 RingPtr->RunState = (u32)(XST_DMA_SG_IS_STOPPED);
265 RingPtr->BaseBdAddr = VirtAddrLoc;
266 RingPtr->PhysBaseAddr = PhysAddr;
267 RingPtr->HighBdAddr = BdVirtAddr;
269 ((RingPtr->HighBdAddr - RingPtr->BaseBdAddr) + RingPtr->Separation);
270 RingPtr->AllCnt = (u32)BdCount;
271 RingPtr->FreeCnt = (u32)BdCount;
272 RingPtr->FreeHead = (XEmacPs_Bd *)(void *)VirtAddrLoc;
273 RingPtr->PreHead = (XEmacPs_Bd *)VirtAddrLoc;
274 RingPtr->HwHead = (XEmacPs_Bd *)VirtAddrLoc;
275 RingPtr->HwTail = (XEmacPs_Bd *)VirtAddrLoc;
276 RingPtr->PostHead = (XEmacPs_Bd *)VirtAddrLoc;
277 RingPtr->BdaRestart = (XEmacPs_Bd *)(void *)PhysAddr;
279 return (LONG)(XST_SUCCESS);
283 /*****************************************************************************/
285 * Clone the given BD into every BD in the list.
286 * every field of the source BD is replicated in every BD of the list.
288 * This function can be called only when all BDs are in the free group such as
289 * they are immediately after initialization with XEmacPs_BdRingCreate().
290 * This prevents modification of BDs while they are in use by hardware or the
293 * @param RingPtr is the pointer of BD ring instance to be worked on.
294 * @param SrcBdPtr is the source BD template to be cloned into the list. This
295 * BD will be modified.
296 * @param Direction is either XEMACPS_SEND or XEMACPS_RECV that indicates
300 * - XST_SUCCESS if the list was modified.
301 * - XST_DMA_SG_NO_LIST if a list has not been created.
302 * - XST_DMA_SG_LIST_ERROR if some of the BDs in this channel are under
303 * hardware or user control.
304 * - XST_DEVICE_IS_STARTED if the DMA channel has not been stopped.
306 *****************************************************************************/
307 LONG XEmacPs_BdRingClone(XEmacPs_BdRing * RingPtr, XEmacPs_Bd * SrcBdPtr,
313 /* Can't do this function if there isn't a ring */
314 if (RingPtr->AllCnt == 0x00000000U) {
315 return (LONG)(XST_DMA_SG_NO_LIST);
318 /* Can't do this function with the channel running */
319 if (RingPtr->RunState == (u32)XST_DMA_SG_IS_STARTED) {
320 return (LONG)(XST_DEVICE_IS_STARTED);
323 /* Can't do this function with some of the BDs in use */
324 if (RingPtr->FreeCnt != RingPtr->AllCnt) {
325 return (LONG)(XST_DMA_SG_LIST_ERROR);
328 if ((Direction != (u8)XEMACPS_SEND) && (Direction != (u8)XEMACPS_RECV)) {
329 return (LONG)(XST_INVALID_PARAM);
332 /* Starting from the top of the ring, save BD.Next, overwrite the entire
333 * BD with the template, then restore BD.Next
335 CurBd = RingPtr->BaseBdAddr;
336 for (i = 0U; i < RingPtr->AllCnt; i++) {
337 memcpy((void *)CurBd, SrcBdPtr, sizeof(XEmacPs_Bd));
338 CurBd += RingPtr->Separation;
341 CurBd -= RingPtr->Separation;
343 if (Direction == XEMACPS_RECV) {
344 XEmacPs_BdSetRxWrap(CurBd);
347 XEmacPs_BdSetTxWrap(CurBd);
350 return (LONG)(XST_SUCCESS);
354 /*****************************************************************************/
356 * Reserve locations in the BD list. The set of returned BDs may be modified
357 * in preparation for future DMA transaction(s). Once the BDs are ready to be
358 * submitted to hardware, the user must call XEmacPs_BdRingToHw() in the same
359 * order which they were allocated here. Example:
363 * Status = XEmacPs_BdRingAlloc(MyRingPtr, NumBd, &MyBdSet),
365 * if (Status != XST_SUCCESS)
367 * *Not enough BDs available for the request*
371 * for (i=0; i<NumBd; i++)
373 * * Prepare CurBd *.....
376 * CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
379 * * Give list to hardware *
380 * Status = XEmacPs_BdRingToHw(MyRingPtr, NumBd, MyBdSet),
383 * A more advanced use of this function may allocate multiple sets of BDs.
384 * They must be allocated and given to hardware in the correct sequence:
387 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
388 * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
391 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
392 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2),
393 * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
394 * XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2),
397 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
398 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2),
399 * XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2),
400 * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
403 * Use the API defined in xemacps_bd.h to modify individual BDs. Traversal
404 * of the BD set can be done using XEmacPs_BdRingNext() and
405 * XEmacPs_BdRingPrev().
407 * @param RingPtr is a pointer to the BD ring instance to be worked on.
408 * @param NumBd is the number of BDs to allocate
409 * @param BdSetPtr is an output parameter, it points to the first BD available
413 * - XST_SUCCESS if the requested number of BDs was returned in the BdSetPtr
415 * - XST_FAILURE if there were not enough free BDs to satisfy the request.
417 * @note This function should not be preempted by another XEmacPs_Bd function
418 * call that modifies the BD space. It is the caller's responsibility to
419 * provide a mutual exclusion mechanism.
421 * @note Do not modify more BDs than the number requested with the NumBd
422 * parameter. Doing so will lead to data corruption and system
425 *****************************************************************************/
426 LONG XEmacPs_BdRingAlloc(XEmacPs_BdRing * RingPtr, u32 NumBd,
427 XEmacPs_Bd ** BdSetPtr)
430 /* Enough free BDs available for the request? */
431 if (RingPtr->FreeCnt < NumBd) {
432 Status = (LONG)(XST_FAILURE);
434 /* Set the return argument and move FreeHead forward */
435 *BdSetPtr = RingPtr->FreeHead;
436 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->FreeHead, NumBd);
437 RingPtr->FreeCnt -= NumBd;
438 RingPtr->PreCnt += NumBd;
439 Status = (LONG)(XST_SUCCESS);
444 /*****************************************************************************/
446 * Fully or partially undo an XEmacPs_BdRingAlloc() operation. Use this
447 * function if all the BDs allocated by XEmacPs_BdRingAlloc() could not be
448 * transferred to hardware with XEmacPs_BdRingToHw().
450 * This function helps out in situations when an unrelated error occurs after
451 * BDs have been allocated but before they have been given to hardware.
452 * An example of this type of error would be an OS running out of resources.
454 * This function is not the same as XEmacPs_BdRingFree(). The Free function
455 * returns BDs to the free list after they have been processed by hardware,
456 * while UnAlloc returns them before being processed by hardware.
458 * There are two scenarios where this function can be used. Full UnAlloc or
459 * Partial UnAlloc. A Full UnAlloc means all the BDs Alloc'd will be returned:
462 * Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr),
466 * Status = XEmacPs_BdRingUnAlloc(MyRingPtr, 10, &BdPtr),
470 * A partial UnAlloc means some of the BDs Alloc'd will be returned:
473 * Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr),
481 * Status = XEmacPs_BdRingUnAlloc(MyRingPtr, BdsLeft, CurBdPtr),
484 * CurBdPtr = XEmacPs_BdRingNext(MyRingPtr, CurBdPtr),
489 * A partial UnAlloc must include the last BD in the list that was Alloc'd.
491 * @param RingPtr is a pointer to the instance to be worked on.
492 * @param NumBd is the number of BDs to allocate
493 * @param BdSetPtr is an output parameter, it points to the first BD available
497 * - XST_SUCCESS if the BDs were unallocated.
498 * - XST_FAILURE if NumBd parameter was greater that the number of BDs in
499 * the preprocessing state.
501 * @note This function should not be preempted by another XEmacPs_Bd function
502 * call that modifies the BD space. It is the caller's responsibility to
503 * provide a mutual exclusion mechanism.
505 *****************************************************************************/
506 LONG XEmacPs_BdRingUnAlloc(XEmacPs_BdRing * RingPtr, u32 NumBd,
507 XEmacPs_Bd * BdSetPtr)
511 Xil_AssertNonvoid(RingPtr != NULL);
512 Xil_AssertNonvoid(BdSetPtr != NULL);
514 /* Enough BDs in the free state for the request? */
515 if (RingPtr->PreCnt < NumBd) {
516 Status = (LONG)(XST_FAILURE);
518 /* Set the return argument and move FreeHead backward */
519 XEMACPS_RING_SEEKBACK(RingPtr, (RingPtr->FreeHead), NumBd);
520 RingPtr->FreeCnt += NumBd;
521 RingPtr->PreCnt -= NumBd;
522 Status = (LONG)(XST_SUCCESS);
528 /*****************************************************************************/
530 * Enqueue a set of BDs to hardware that were previously allocated by
531 * XEmacPs_BdRingAlloc(). Once this function returns, the argument BD set goes
532 * under hardware control. Any changes made to these BDs after this point will
533 * corrupt the BD list leading to data corruption and system instability.
535 * The set will be rejected if the last BD of the set does not mark the end of
536 * a packet (see XEmacPs_BdSetLast()).
538 * @param RingPtr is a pointer to the instance to be worked on.
539 * @param NumBd is the number of BDs in the set.
540 * @param BdSetPtr is the first BD of the set to commit to hardware.
543 * - XST_SUCCESS if the set of BDs was accepted and enqueued to hardware.
544 * - XST_FAILURE if the set of BDs was rejected because the last BD of the set
545 * did not have its "last" bit set.
546 * - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with
547 * XEmacPs_BdRingAlloc().
549 * @note This function should not be preempted by another XEmacPs_Bd function
550 * call that modifies the BD space. It is the caller's responsibility to
551 * provide a mutual exclusion mechanism.
553 *****************************************************************************/
554 LONG XEmacPs_BdRingToHw(XEmacPs_BdRing * RingPtr, u32 NumBd,
555 XEmacPs_Bd * BdSetPtr)
557 XEmacPs_Bd *CurBdPtr;
560 /* if no bds to process, simply return. */
562 Status = (LONG)(XST_SUCCESS);
564 /* Make sure we are in sync with XEmacPs_BdRingAlloc() */
565 if ((RingPtr->PreCnt < NumBd) || (RingPtr->PreHead != BdSetPtr)) {
566 Status = (LONG)(XST_DMA_SG_LIST_ERROR);
569 for (i = 0U; i < NumBd; i++) {
570 CurBdPtr = (XEmacPs_Bd *)((void *)XEmacPs_BdRingNext(RingPtr, CurBdPtr));
572 /* Adjust ring pointers & counters */
573 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->PreHead, NumBd);
574 RingPtr->PreCnt -= NumBd;
575 RingPtr->HwTail = CurBdPtr;
576 RingPtr->HwCnt += NumBd;
578 Status = (LONG)(XST_SUCCESS);
585 /*****************************************************************************/
587 * Returns a set of BD(s) that have been processed by hardware. The returned
588 * BDs may be examined to determine the outcome of the DMA transaction(s).
589 * Once the BDs have been examined, the user must call XEmacPs_BdRingFree()
590 * in the same order which they were retrieved here. Example:
593 * NumBd = XEmacPs_BdRingFromHwTx(MyRingPtr, MaxBd, &MyBdSet),
596 * * hardware has nothing ready for us yet*
600 * for (i=0; i<NumBd; i++)
602 * * Examine CurBd for post processing *.....
605 * CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
608 * XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet), *Return list*
612 * A more advanced use of this function may allocate multiple sets of BDs.
613 * They must be retrieved from hardware and freed in the correct sequence:
616 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
617 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
620 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
621 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2),
622 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
623 * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
626 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
627 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2),
628 * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
629 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
632 * If hardware has only partially completed a packet spanning multiple BDs,
633 * then none of the BDs for that packet will be included in the results.
635 * @param RingPtr is a pointer to the instance to be worked on.
636 * @param BdLimit is the maximum number of BDs to return in the set.
637 * @param BdSetPtr is an output parameter, it points to the first BD available
641 * The number of BDs processed by hardware. A value of 0 indicates that no
642 * data is available. No more than BdLimit BDs will be returned.
644 * @note Treat BDs returned by this function as read-only.
646 * @note This function should not be preempted by another XEmacPs_Bd function
647 * call that modifies the BD space. It is the caller's responsibility to
648 * provide a mutual exclusion mechanism.
650 *****************************************************************************/
651 u32 XEmacPs_BdRingFromHwTx(XEmacPs_BdRing * RingPtr, u32 BdLimit,
652 XEmacPs_Bd ** BdSetPtr)
654 XEmacPs_Bd *CurBdPtr;
660 u32 BdLimitLoc = BdLimit;
661 CurBdPtr = RingPtr->HwHead;
665 /* If no BDs in work group, then there's nothing to search */
666 if (RingPtr->HwCnt == 0x00000000U) {
671 if (BdLimitLoc > RingPtr->HwCnt){
672 BdLimitLoc = RingPtr->HwCnt;
674 /* Starting at HwHead, keep moving forward in the list until:
675 * - A BD is encountered with its new/used bit set which means
676 * hardware has not completed processing of that BD.
677 * - RingPtr->HwTail is reached and RingPtr->HwCnt is reached.
678 * - The number of requested BDs has been processed
680 while (BdCount < BdLimitLoc) {
681 /* Read the status */
682 if(CurBdPtr != NULL){
683 BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET);
686 if ((Sop == 0x00000000U) && ((BdStr & XEMACPS_TXBUF_USED_MASK)!=0x00000000U)){
689 if (Sop == 0x00000001U) {
694 /* hardware has processed this BD so check the "last" bit.
695 * If it is clear, then there are more BDs for the current
696 * packet. Keep a count of these partial packet BDs.
698 if ((Sop == 0x00000001U) && ((BdStr & XEMACPS_TXBUF_LAST_MASK)!=0x00000000U)) {
703 /* Move on to next BD in work group */
704 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
707 /* Subtract off any partial packet BDs found */
708 BdCount -= BdPartialCount;
710 /* If BdCount is non-zero then BDs were found to return. Set return
711 * parameters, update pointers and counters, return success
713 if (BdCount > 0x00000000U) {
714 *BdSetPtr = RingPtr->HwHead;
715 RingPtr->HwCnt -= BdCount;
716 RingPtr->PostCnt += BdCount;
717 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
728 /*****************************************************************************/
730 * Returns a set of BD(s) that have been processed by hardware. The returned
731 * BDs may be examined to determine the outcome of the DMA transaction(s).
732 * Once the BDs have been examined, the user must call XEmacPs_BdRingFree()
733 * in the same order which they were retrieved here. Example:
736 * NumBd = XEmacPs_BdRingFromHwRx(MyRingPtr, MaxBd, &MyBdSet),
740 * *hardware has nothing ready for us yet*
744 * for (i=0; i<NumBd; i++)
746 * * Examine CurBd for post processing *.....
749 * CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
752 * XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet), * Return list *
756 * A more advanced use of this function may allocate multiple sets of BDs.
757 * They must be retrieved from hardware and freed in the correct sequence:
760 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
761 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
764 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
765 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2),
766 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
767 * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
770 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
771 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2),
772 * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
773 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
776 * If hardware has only partially completed a packet spanning multiple BDs,
777 * then none of the BDs for that packet will be included in the results.
779 * @param RingPtr is a pointer to the instance to be worked on.
780 * @param BdLimit is the maximum number of BDs to return in the set.
781 * @param BdSetPtr is an output parameter, it points to the first BD available
785 * The number of BDs processed by hardware. A value of 0 indicates that no
786 * data is available. No more than BdLimit BDs will be returned.
788 * @note Treat BDs returned by this function as read-only.
790 * @note This function should not be preempted by another XEmacPs_Bd function
791 * call that modifies the BD space. It is the caller's responsibility to
792 * provide a mutual exclusion mechanism.
794 *****************************************************************************/
795 u32 XEmacPs_BdRingFromHwRx(XEmacPs_BdRing * RingPtr, u32 BdLimit,
796 XEmacPs_Bd ** BdSetPtr)
798 XEmacPs_Bd *CurBdPtr;
804 CurBdPtr = RingPtr->HwHead;
808 /* If no BDs in work group, then there's nothing to search */
809 if (RingPtr->HwCnt == 0x00000000U) {
814 /* Starting at HwHead, keep moving forward in the list until:
815 * - A BD is encountered with its new/used bit set which means
816 * hardware has completed processing of that BD.
817 * - RingPtr->HwTail is reached and RingPtr->HwCnt is reached.
818 * - The number of requested BDs has been processed
820 while (BdCount < BdLimit) {
822 /* Read the status */
824 BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET);
826 if ((!(XEmacPs_BdIsRxNew(CurBdPtr)))==TRUE) {
832 /* hardware has processed this BD so check the "last" bit. If
833 * it is clear, then there are more BDs for the current packet.
834 * Keep a count of these partial packet BDs.
836 if ((BdStr & XEMACPS_RXBUF_EOF_MASK)!=0x00000000U) {
842 /* Move on to next BD in work group */
843 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
846 /* Subtract off any partial packet BDs found */
847 BdCount -= BdPartialCount;
849 /* If BdCount is non-zero then BDs were found to return. Set return
850 * parameters, update pointers and counters, return success
852 if (BdCount > 0x00000000U) {
853 *BdSetPtr = RingPtr->HwHead;
854 RingPtr->HwCnt -= BdCount;
855 RingPtr->PostCnt += BdCount;
856 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
868 /*****************************************************************************/
870 * Frees a set of BDs that had been previously retrieved with
871 * XEmacPs_BdRingFromHw().
873 * @param RingPtr is a pointer to the instance to be worked on.
874 * @param NumBd is the number of BDs to free.
875 * @param BdSetPtr is the head of a list of BDs returned by
876 * XEmacPs_BdRingFromHw().
879 * - XST_SUCCESS if the set of BDs was freed.
880 * - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with
881 * XEmacPs_BdRingFromHw().
883 * @note This function should not be preempted by another XEmacPs_Bd function
884 * call that modifies the BD space. It is the caller's responsibility to
885 * provide a mutual exclusion mechanism.
887 *****************************************************************************/
888 LONG XEmacPs_BdRingFree(XEmacPs_BdRing * RingPtr, u32 NumBd,
889 XEmacPs_Bd * BdSetPtr)
892 /* if no bds to process, simply return. */
893 if (0x00000000U == NumBd){
894 Status = (LONG)(XST_SUCCESS);
896 /* Make sure we are in sync with XEmacPs_BdRingFromHw() */
897 if ((RingPtr->PostCnt < NumBd) || (RingPtr->PostHead != BdSetPtr)) {
898 Status = (LONG)(XST_DMA_SG_LIST_ERROR);
900 /* Update pointers and counters */
901 RingPtr->FreeCnt += NumBd;
902 RingPtr->PostCnt -= NumBd;
903 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->PostHead, NumBd);
904 Status = (LONG)(XST_SUCCESS);
911 /*****************************************************************************/
913 * Check the internal data structures of the BD ring for the provided channel.
914 * The following checks are made:
916 * - Is the BD ring linked correctly in physical address space.
917 * - Do the internal pointers point to BDs in the ring.
918 * - Do the internal counters add up.
920 * The channel should be stopped prior to calling this function.
922 * @param RingPtr is a pointer to the instance to be worked on.
923 * @param Direction is either XEMACPS_SEND or XEMACPS_RECV that indicates
927 * - XST_SUCCESS if the set of BDs was freed.
928 * - XST_DMA_SG_NO_LIST if the list has not been created.
929 * - XST_IS_STARTED if the channel is not stopped.
930 * - XST_DMA_SG_LIST_ERROR if a problem is found with the internal data
931 * structures. If this value is returned, the channel should be reset to
932 * avoid data corruption or system instability.
934 * @note This function should not be preempted by another XEmacPs_Bd function
935 * call that modifies the BD space. It is the caller's responsibility to
936 * provide a mutual exclusion mechanism.
938 *****************************************************************************/
939 LONG XEmacPs_BdRingCheck(XEmacPs_BdRing * RingPtr, u8 Direction)
941 UINTPTR AddrV, AddrP;
944 if ((Direction != (u8)XEMACPS_SEND) && (Direction != (u8)XEMACPS_RECV)) {
945 return (LONG)(XST_INVALID_PARAM);
948 /* Is the list created */
949 if (RingPtr->AllCnt == 0x00000000U) {
950 return (LONG)(XST_DMA_SG_NO_LIST);
953 /* Can't check if channel is running */
954 if (RingPtr->RunState == (u32)XST_DMA_SG_IS_STARTED) {
955 return (LONG)(XST_IS_STARTED);
958 /* RunState doesn't make sense */
959 if (RingPtr->RunState != (u32)XST_DMA_SG_IS_STOPPED) {
960 return (LONG)(XST_DMA_SG_LIST_ERROR);
963 /* Verify internal pointers point to correct memory space */
964 AddrV = (UINTPTR) RingPtr->FreeHead;
965 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
966 return (LONG)(XST_DMA_SG_LIST_ERROR);
969 AddrV = (UINTPTR) RingPtr->PreHead;
970 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
971 return (LONG)(XST_DMA_SG_LIST_ERROR);
974 AddrV = (UINTPTR) RingPtr->HwHead;
975 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
976 return (LONG)(XST_DMA_SG_LIST_ERROR);
979 AddrV = (UINTPTR) RingPtr->HwTail;
980 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
981 return (LONG)(XST_DMA_SG_LIST_ERROR);
984 AddrV = (UINTPTR) RingPtr->PostHead;
985 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
986 return (LONG)(XST_DMA_SG_LIST_ERROR);
989 /* Verify internal counters add up */
990 if ((RingPtr->HwCnt + RingPtr->PreCnt + RingPtr->FreeCnt +
991 RingPtr->PostCnt) != RingPtr->AllCnt) {
992 return (LONG)(XST_DMA_SG_LIST_ERROR);
995 /* Verify BDs are linked correctly */
996 AddrV = RingPtr->BaseBdAddr;
997 AddrP = RingPtr->PhysBaseAddr + RingPtr->Separation;
999 for (i = 1U; i < RingPtr->AllCnt; i++) {
1000 /* Check BDA for this BD. It should point to next physical addr */
1001 if (XEmacPs_BdRead(AddrV, XEMACPS_BD_ADDR_OFFSET) != AddrP) {
1002 return (LONG)(XST_DMA_SG_LIST_ERROR);
1005 /* Move on to next BD */
1006 AddrV += RingPtr->Separation;
1007 AddrP += RingPtr->Separation;
1010 /* Last BD should have wrap bit set */
1011 if (XEMACPS_SEND == Direction) {
1012 if ((!XEmacPs_BdIsTxWrap(AddrV))==TRUE) {
1013 return (LONG)(XST_DMA_SG_LIST_ERROR);
1016 else { /* XEMACPS_RECV */
1017 if ((!XEmacPs_BdIsRxWrap(AddrV))==TRUE) {
1018 return (LONG)(XST_DMA_SG_LIST_ERROR);
1022 /* No problems found */
1023 return (LONG)(XST_SUCCESS);
1026 /*****************************************************************************/
1028 * Set this bit to mark the last descriptor in the receive buffer descriptor
1031 * @param BdPtr is the BD pointer to operate on
1034 * C-style signature:
1035 * void XEmacPs_BdSetRxWrap(XEmacPs_Bd* BdPtr)
1037 *****************************************************************************/
1038 static void XEmacPs_BdSetRxWrap(UINTPTR BdPtr)
1043 BdPtr += (u32)(XEMACPS_BD_ADDR_OFFSET);
1044 TempPtr = (u32 *)BdPtr;
1045 if(TempPtr != NULL) {
1046 DataValueRx = *TempPtr;
1047 DataValueRx |= XEMACPS_RXBUF_WRAP_MASK;
1048 *TempPtr = DataValueRx;
1052 /*****************************************************************************/
1054 * Sets this bit to mark the last descriptor in the transmit buffer
1057 * @param BdPtr is the BD pointer to operate on
1060 * C-style signature:
1061 * void XEmacPs_BdSetTxWrap(XEmacPs_Bd* BdPtr)
1063 *****************************************************************************/
1064 static void XEmacPs_BdSetTxWrap(UINTPTR BdPtr)
1069 BdPtr += (u32)(XEMACPS_BD_STAT_OFFSET);
1070 TempPtr = (u32 *)BdPtr;
1071 if(TempPtr != NULL) {
1072 DataValueTx = *TempPtr;
1073 DataValueTx |= XEMACPS_TXBUF_WRAP_MASK;
1074 *TempPtr = DataValueTx;
1078 /*****************************************************************************/
1080 * Reset BD ring head and tail pointers.
1082 * @param RingPtr is the instance to be worked on.
1083 * @param VirtAddr is the virtual base address of the user memory region.
1086 * Should be called after XEmacPs_Stop()
1089 * C-style signature:
1090 * void XEmacPs_BdRingPtrReset(XEmacPs_BdRing * RingPtr, void *virtaddrloc)
1092 *****************************************************************************/
1093 void XEmacPs_BdRingPtrReset(XEmacPs_BdRing * RingPtr, void *virtaddrloc)
1095 RingPtr->FreeHead = virtaddrloc;
1096 RingPtr->PreHead = virtaddrloc;
1097 RingPtr->HwHead = virtaddrloc;
1098 RingPtr->HwTail = virtaddrloc;
1099 RingPtr->PostHead = virtaddrloc;