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
37 * This file implements buffer descriptor ring related functions.
40 * MODIFICATION HISTORY:
42 * Ver Who Date Changes
43 * ----- ---- -------- -------------------------------------------------------
44 * 1.00a wsy 01/10/10 First release
45 * 1.00a asa 11/21/11 The function XEmacPs_BdRingFromHwTx is modified.
46 * Earlier it used to search in "BdLimit" number of BDs to
47 * know which BDs are processed. Now one more check is
48 * added. It looks for BDs till the current BD pointer
49 * reaches HwTail. By doing this processing time is saved.
50 * 1.00a asa 01/24/12 The function XEmacPs_BdRingFromHwTx in file
51 * xemacps_bdring.c is modified. Now start of packet is
52 * searched for returning the number of BDs processed.
53 * 1.05a asa 09/23/13 Cache operations on BDs are not required and hence
54 * removed. It is expected that all BDs are allocated in
55 * from uncached area. Fix for CR #663885.
56 * 2.1 srt 07/15/14 Add support for Zynq Ultrascale Mp architecture.
57 * 3.0 kvn 02/13/15 Modified code for MISRA-C:2012 compliance.
60 ******************************************************************************/
62 /***************************** Include Files *********************************/
65 #include "xil_cache.h"
66 #include "xemacps_hw.h"
67 #include "xemacps_bd.h"
68 #include "xemacps_bdring.h"
70 /************************** Constant Definitions *****************************/
72 /**************************** Type Definitions *******************************/
75 /***************** Macros (Inline Functions) Definitions *********************/
77 /****************************************************************************
78 * Compute the virtual address of a descriptor from its physical address
80 * @param BdPtr is the physical address of the BD
82 * @returns Virtual address of BdPtr
84 * @note Assume BdPtr is always a valid BD in the ring
85 ****************************************************************************/
86 #define XEMACPS_PHYS_TO_VIRT(BdPtr) \
87 ((UINTPTR)(BdPtr) + (RingPtr->BaseBdAddr - RingPtr->PhysBaseAddr))
89 /****************************************************************************
90 * Compute the physical address of a descriptor from its virtual address
92 * @param BdPtr is the physical address of the BD
94 * @returns Physical address of BdPtr
96 * @note Assume BdPtr is always a valid BD in the ring
97 ****************************************************************************/
98 #define XEMACPS_VIRT_TO_PHYS(BdPtr) \
99 ((UINTPTR)(BdPtr) - (RingPtr->BaseBdAddr - RingPtr->PhysBaseAddr))
101 /****************************************************************************
102 * Move the BdPtr argument ahead an arbitrary number of BDs wrapping around
103 * to the beginning of the ring if needed.
105 * We know if a wrapaound should occur if the new BdPtr is greater than
106 * the high address in the ring OR if the new BdPtr crosses over the
107 * 0xFFFFFFFF to 0 boundary. The latter test is a valid one since we do not
108 * allow a BD space to span this boundary.
110 * @param RingPtr is the ring BdPtr appears in
111 * @param BdPtr on input is the starting BD position and on output is the
113 * @param NumBd is the number of BD spaces to increment
115 ****************************************************************************/
116 #define XEMACPS_RING_SEEKAHEAD(RingPtr, BdPtr, NumBd) \
118 UINTPTR Addr = (UINTPTR)(void *)(BdPtr); \
120 Addr += ((RingPtr)->Separation * (NumBd)); \
121 if ((Addr > (RingPtr)->HighBdAddr) || ((UINTPTR)(void *)(BdPtr) > Addr)) \
123 Addr -= (RingPtr)->Length; \
126 (BdPtr) = (XEmacPs_Bd*)(void *)Addr; \
129 /****************************************************************************
130 * Move the BdPtr argument backwards an arbitrary number of BDs wrapping
131 * around to the end of the ring if needed.
133 * We know if a wrapaound should occur if the new BdPtr is less than
134 * the base address in the ring OR if the new BdPtr crosses over the
135 * 0xFFFFFFFF to 0 boundary. The latter test is a valid one since we do not
136 * allow a BD space to span this boundary.
138 * @param RingPtr is the ring BdPtr appears in
139 * @param BdPtr on input is the starting BD position and on output is the
141 * @param NumBd is the number of BD spaces to increment
143 ****************************************************************************/
144 #define XEMACPS_RING_SEEKBACK(RingPtr, BdPtr, NumBd) \
146 UINTPTR Addr = (UINTPTR)(void *)(BdPtr); \
148 Addr -= ((RingPtr)->Separation * (NumBd)); \
149 if ((Addr < (RingPtr)->BaseBdAddr) || ((UINTPTR)(void*)(BdPtr) < Addr)) \
151 Addr += (RingPtr)->Length; \
154 (BdPtr) = (XEmacPs_Bd*)(void*)Addr; \
158 /************************** Function Prototypes ******************************/
160 static void XEmacPs_BdSetRxWrap(UINTPTR BdPtr);
161 static void XEmacPs_BdSetTxWrap(UINTPTR BdPtr);
163 /************************** Variable Definitions *****************************/
165 /*****************************************************************************/
167 * Using a memory segment allocated by the caller, create and setup the BD list
168 * for the given DMA channel.
170 * @param RingPtr is the instance to be worked on.
171 * @param PhysAddr is the physical base address of user memory region.
172 * @param VirtAddr is the virtual base address of the user memory region. If
173 * address translation is not being utilized, then VirtAddr should be
174 * equivalent to PhysAddr.
175 * @param Alignment governs the byte alignment of individual BDs. This function
176 * will enforce a minimum alignment of 4 bytes with no maximum as long
177 * as it is specified as a power of 2.
178 * @param BdCount is the number of BDs to setup in the user memory region. It
179 * is assumed the region is large enough to contain the BDs.
183 * - XST_SUCCESS if initialization was successful
184 * - XST_NO_FEATURE if the provided instance is a non DMA type
186 * - XST_INVALID_PARAM under any of the following conditions:
187 * 1) PhysAddr and/or VirtAddr are not aligned to the given Alignment
189 * 2) Alignment parameter does not meet minimum requirements or is not a
192 * - XST_DMA_SG_LIST_ERROR if the memory segment containing the list spans
193 * over address 0x00000000 in virtual address space.
196 * Make sure to pass in the right alignment value.
197 *****************************************************************************/
198 LONG XEmacPs_BdRingCreate(XEmacPs_BdRing * RingPtr, UINTPTR PhysAddr,
199 UINTPTR VirtAddr, u32 Alignment, u32 BdCount)
204 UINTPTR VirtAddrLoc = VirtAddr;
206 /* In case there is a failure prior to creating list, make sure the
207 * following attributes are 0 to prevent calls to other functions
208 * from doing anything.
210 RingPtr->AllCnt = 0U;
211 RingPtr->FreeCnt = 0U;
213 RingPtr->PreCnt = 0U;
214 RingPtr->PostCnt = 0U;
216 /* Make sure Alignment parameter meets minimum requirements */
217 if (Alignment < (u32)XEMACPS_DMABD_MINIMUM_ALIGNMENT) {
218 return (LONG)(XST_INVALID_PARAM);
221 /* Make sure Alignment is a power of 2 */
222 if (((Alignment - 0x00000001U) & Alignment)!=0x00000000U) {
223 return (LONG)(XST_INVALID_PARAM);
226 /* Make sure PhysAddr and VirtAddr are on same Alignment */
227 if (((PhysAddr % Alignment)!=(u32)0) || ((VirtAddrLoc % Alignment)!=(u32)0)) {
228 return (LONG)(XST_INVALID_PARAM);
231 /* Is BdCount reasonable? */
232 if (BdCount == 0x00000000U) {
233 return (LONG)(XST_INVALID_PARAM);
236 /* Figure out how many bytes will be between the start of adjacent BDs */
237 RingPtr->Separation = ((u32)sizeof(XEmacPs_Bd));
239 /* Must make sure the ring doesn't span address 0x00000000. If it does,
240 * then the next/prev BD traversal macros will fail.
242 if (VirtAddrLoc > ((VirtAddrLoc + (RingPtr->Separation * BdCount)) - (u32)1)) {
243 return (LONG)(XST_DMA_SG_LIST_ERROR);
246 /* Initial ring setup:
247 * - Clear the entire space
248 * - Setup each BD's BDA field with the physical address of the next BD
250 (void)memset((void *) VirtAddrLoc, 0, (RingPtr->Separation * BdCount));
252 BdVirtAddr = VirtAddrLoc;
253 BdPhyAddr = PhysAddr + RingPtr->Separation;
254 for (i = 1U; i < BdCount; i++) {
255 BdVirtAddr += RingPtr->Separation;
256 BdPhyAddr += RingPtr->Separation;
259 /* Setup and initialize pointers and counters */
260 RingPtr->RunState = (u32)(XST_DMA_SG_IS_STOPPED);
261 RingPtr->BaseBdAddr = VirtAddrLoc;
262 RingPtr->PhysBaseAddr = PhysAddr;
263 RingPtr->HighBdAddr = BdVirtAddr;
265 ((RingPtr->HighBdAddr - RingPtr->BaseBdAddr) + RingPtr->Separation);
266 RingPtr->AllCnt = (u32)BdCount;
267 RingPtr->FreeCnt = (u32)BdCount;
268 RingPtr->FreeHead = (XEmacPs_Bd *)(void *)VirtAddrLoc;
269 RingPtr->PreHead = (XEmacPs_Bd *)VirtAddrLoc;
270 RingPtr->HwHead = (XEmacPs_Bd *)VirtAddrLoc;
271 RingPtr->HwTail = (XEmacPs_Bd *)VirtAddrLoc;
272 RingPtr->PostHead = (XEmacPs_Bd *)VirtAddrLoc;
273 RingPtr->BdaRestart = (XEmacPs_Bd *)(void *)PhysAddr;
275 return (LONG)(XST_SUCCESS);
279 /*****************************************************************************/
281 * Clone the given BD into every BD in the list.
282 * every field of the source BD is replicated in every BD of the list.
284 * This function can be called only when all BDs are in the free group such as
285 * they are immediately after initialization with XEmacPs_BdRingCreate().
286 * This prevents modification of BDs while they are in use by hardware or the
289 * @param RingPtr is the pointer of BD ring instance to be worked on.
290 * @param SrcBdPtr is the source BD template to be cloned into the list. This
291 * BD will be modified.
292 * @param Direction is either XEMACPS_SEND or XEMACPS_RECV that indicates
296 * - XST_SUCCESS if the list was modified.
297 * - XST_DMA_SG_NO_LIST if a list has not been created.
298 * - XST_DMA_SG_LIST_ERROR if some of the BDs in this channel are under
299 * hardware or user control.
300 * - XST_DEVICE_IS_STARTED if the DMA channel has not been stopped.
302 *****************************************************************************/
303 LONG XEmacPs_BdRingClone(XEmacPs_BdRing * RingPtr, XEmacPs_Bd * SrcBdPtr,
309 /* Can't do this function if there isn't a ring */
310 if (RingPtr->AllCnt == 0x00000000U) {
311 return (LONG)(XST_DMA_SG_NO_LIST);
314 /* Can't do this function with the channel running */
315 if (RingPtr->RunState == (u32)XST_DMA_SG_IS_STARTED) {
316 return (LONG)(XST_DEVICE_IS_STARTED);
319 /* Can't do this function with some of the BDs in use */
320 if (RingPtr->FreeCnt != RingPtr->AllCnt) {
321 return (LONG)(XST_DMA_SG_LIST_ERROR);
324 if ((Direction != (u8)XEMACPS_SEND) && (Direction != (u8)XEMACPS_RECV)) {
325 return (LONG)(XST_INVALID_PARAM);
328 /* Starting from the top of the ring, save BD.Next, overwrite the entire
329 * BD with the template, then restore BD.Next
331 CurBd = RingPtr->BaseBdAddr;
332 for (i = 0U; i < RingPtr->AllCnt; i++) {
333 memcpy((void *)CurBd, SrcBdPtr, sizeof(XEmacPs_Bd));
334 CurBd += RingPtr->Separation;
337 CurBd -= RingPtr->Separation;
339 if (Direction == XEMACPS_RECV) {
340 XEmacPs_BdSetRxWrap(CurBd);
343 XEmacPs_BdSetTxWrap(CurBd);
346 return (LONG)(XST_SUCCESS);
350 /*****************************************************************************/
352 * Reserve locations in the BD list. The set of returned BDs may be modified
353 * in preparation for future DMA transaction(s). Once the BDs are ready to be
354 * submitted to hardware, the user must call XEmacPs_BdRingToHw() in the same
355 * order which they were allocated here. Example:
359 * Status = XEmacPs_BdRingAlloc(MyRingPtr, NumBd, &MyBdSet),
361 * if (Status != XST_SUCCESS)
363 * *Not enough BDs available for the request*
367 * for (i=0; i<NumBd; i++)
369 * * Prepare CurBd *.....
372 * CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
375 * * Give list to hardware *
376 * Status = XEmacPs_BdRingToHw(MyRingPtr, NumBd, MyBdSet),
379 * A more advanced use of this function may allocate multiple sets of BDs.
380 * They must be allocated and given to hardware in the correct sequence:
383 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
384 * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
387 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
388 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2),
389 * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
390 * XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2),
393 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
394 * XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2),
395 * XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2),
396 * XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
399 * Use the API defined in xemacps_bd.h to modify individual BDs. Traversal
400 * of the BD set can be done using XEmacPs_BdRingNext() and
401 * XEmacPs_BdRingPrev().
403 * @param RingPtr is a pointer to the BD ring instance to be worked on.
404 * @param NumBd is the number of BDs to allocate
405 * @param BdSetPtr is an output parameter, it points to the first BD available
409 * - XST_SUCCESS if the requested number of BDs was returned in the BdSetPtr
411 * - XST_FAILURE if there were not enough free BDs to satisfy the request.
413 * @note This function should not be preempted by another XEmacPs_Bd function
414 * call that modifies the BD space. It is the caller's responsibility to
415 * provide a mutual exclusion mechanism.
417 * @note Do not modify more BDs than the number requested with the NumBd
418 * parameter. Doing so will lead to data corruption and system
421 *****************************************************************************/
422 LONG XEmacPs_BdRingAlloc(XEmacPs_BdRing * RingPtr, u32 NumBd,
423 XEmacPs_Bd ** BdSetPtr)
426 /* Enough free BDs available for the request? */
427 if (RingPtr->FreeCnt < NumBd) {
428 Status = (LONG)(XST_FAILURE);
430 /* Set the return argument and move FreeHead forward */
431 *BdSetPtr = RingPtr->FreeHead;
432 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->FreeHead, NumBd);
433 RingPtr->FreeCnt -= NumBd;
434 RingPtr->PreCnt += NumBd;
435 Status = (LONG)(XST_SUCCESS);
440 /*****************************************************************************/
442 * Fully or partially undo an XEmacPs_BdRingAlloc() operation. Use this
443 * function if all the BDs allocated by XEmacPs_BdRingAlloc() could not be
444 * transferred to hardware with XEmacPs_BdRingToHw().
446 * This function helps out in situations when an unrelated error occurs after
447 * BDs have been allocated but before they have been given to hardware.
448 * An example of this type of error would be an OS running out of resources.
450 * This function is not the same as XEmacPs_BdRingFree(). The Free function
451 * returns BDs to the free list after they have been processed by hardware,
452 * while UnAlloc returns them before being processed by hardware.
454 * There are two scenarios where this function can be used. Full UnAlloc or
455 * Partial UnAlloc. A Full UnAlloc means all the BDs Alloc'd will be returned:
458 * Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr),
462 * Status = XEmacPs_BdRingUnAlloc(MyRingPtr, 10, &BdPtr),
466 * A partial UnAlloc means some of the BDs Alloc'd will be returned:
469 * Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr),
477 * Status = XEmacPs_BdRingUnAlloc(MyRingPtr, BdsLeft, CurBdPtr),
480 * CurBdPtr = XEmacPs_BdRingNext(MyRingPtr, CurBdPtr),
485 * A partial UnAlloc must include the last BD in the list that was Alloc'd.
487 * @param RingPtr is a pointer to the instance to be worked on.
488 * @param NumBd is the number of BDs to allocate
489 * @param BdSetPtr is an output parameter, it points to the first BD available
493 * - XST_SUCCESS if the BDs were unallocated.
494 * - XST_FAILURE if NumBd parameter was greater that the number of BDs in
495 * the preprocessing state.
497 * @note This function should not be preempted by another XEmacPs_Bd function
498 * call that modifies the BD space. It is the caller's responsibility to
499 * provide a mutual exclusion mechanism.
501 *****************************************************************************/
502 LONG XEmacPs_BdRingUnAlloc(XEmacPs_BdRing * RingPtr, u32 NumBd,
503 XEmacPs_Bd * BdSetPtr)
507 Xil_AssertNonvoid(RingPtr != NULL);
508 Xil_AssertNonvoid(BdSetPtr != NULL);
510 /* Enough BDs in the free state for the request? */
511 if (RingPtr->PreCnt < NumBd) {
512 Status = (LONG)(XST_FAILURE);
514 /* Set the return argument and move FreeHead backward */
515 XEMACPS_RING_SEEKBACK(RingPtr, (RingPtr->FreeHead), NumBd);
516 RingPtr->FreeCnt += NumBd;
517 RingPtr->PreCnt -= NumBd;
518 Status = (LONG)(XST_SUCCESS);
524 /*****************************************************************************/
526 * Enqueue a set of BDs to hardware that were previously allocated by
527 * XEmacPs_BdRingAlloc(). Once this function returns, the argument BD set goes
528 * under hardware control. Any changes made to these BDs after this point will
529 * corrupt the BD list leading to data corruption and system instability.
531 * The set will be rejected if the last BD of the set does not mark the end of
532 * a packet (see XEmacPs_BdSetLast()).
534 * @param RingPtr is a pointer to the instance to be worked on.
535 * @param NumBd is the number of BDs in the set.
536 * @param BdSetPtr is the first BD of the set to commit to hardware.
539 * - XST_SUCCESS if the set of BDs was accepted and enqueued to hardware.
540 * - XST_FAILURE if the set of BDs was rejected because the last BD of the set
541 * did not have its "last" bit set.
542 * - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with
543 * XEmacPs_BdRingAlloc().
545 * @note This function should not be preempted by another XEmacPs_Bd function
546 * call that modifies the BD space. It is the caller's responsibility to
547 * provide a mutual exclusion mechanism.
549 *****************************************************************************/
550 LONG XEmacPs_BdRingToHw(XEmacPs_BdRing * RingPtr, u32 NumBd,
551 XEmacPs_Bd * BdSetPtr)
553 XEmacPs_Bd *CurBdPtr;
556 /* if no bds to process, simply return. */
558 Status = (LONG)(XST_SUCCESS);
560 /* Make sure we are in sync with XEmacPs_BdRingAlloc() */
561 if ((RingPtr->PreCnt < NumBd) || (RingPtr->PreHead != BdSetPtr)) {
562 Status = (LONG)(XST_DMA_SG_LIST_ERROR);
565 for (i = 0U; i < NumBd; i++) {
566 CurBdPtr = (XEmacPs_Bd *)((void *)XEmacPs_BdRingNext(RingPtr, CurBdPtr));
568 /* Adjust ring pointers & counters */
569 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->PreHead, NumBd);
570 RingPtr->PreCnt -= NumBd;
571 RingPtr->HwTail = CurBdPtr;
572 RingPtr->HwCnt += NumBd;
574 Status = (LONG)(XST_SUCCESS);
581 /*****************************************************************************/
583 * Returns a set of BD(s) that have been processed by hardware. The returned
584 * BDs may be examined to determine the outcome of the DMA transaction(s).
585 * Once the BDs have been examined, the user must call XEmacPs_BdRingFree()
586 * in the same order which they were retrieved here. Example:
589 * NumBd = XEmacPs_BdRingFromHwTx(MyRingPtr, MaxBd, &MyBdSet),
592 * * hardware has nothing ready for us yet*
596 * for (i=0; i<NumBd; i++)
598 * * Examine CurBd for post processing *.....
601 * CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
604 * XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet), *Return list*
608 * A more advanced use of this function may allocate multiple sets of BDs.
609 * They must be retrieved from hardware and freed in the correct sequence:
612 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
613 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
616 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
617 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2),
618 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
619 * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
622 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
623 * XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2),
624 * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
625 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
628 * If hardware has only partially completed a packet spanning multiple BDs,
629 * then none of the BDs for that packet will be included in the results.
631 * @param RingPtr is a pointer to the instance to be worked on.
632 * @param BdLimit is the maximum number of BDs to return in the set.
633 * @param BdSetPtr is an output parameter, it points to the first BD available
637 * The number of BDs processed by hardware. A value of 0 indicates that no
638 * data is available. No more than BdLimit BDs will be returned.
640 * @note Treat BDs returned by this function as read-only.
642 * @note This function should not be preempted by another XEmacPs_Bd function
643 * call that modifies the BD space. It is the caller's responsibility to
644 * provide a mutual exclusion mechanism.
646 *****************************************************************************/
647 u32 XEmacPs_BdRingFromHwTx(XEmacPs_BdRing * RingPtr, u32 BdLimit,
648 XEmacPs_Bd ** BdSetPtr)
650 XEmacPs_Bd *CurBdPtr;
656 u32 BdLimitLoc = BdLimit;
657 CurBdPtr = RingPtr->HwHead;
661 /* If no BDs in work group, then there's nothing to search */
662 if (RingPtr->HwCnt == 0x00000000U) {
667 if (BdLimitLoc > RingPtr->HwCnt){
668 BdLimitLoc = RingPtr->HwCnt;
670 /* Starting at HwHead, keep moving forward in the list until:
671 * - A BD is encountered with its new/used bit set which means
672 * hardware has not completed processing of that BD.
673 * - RingPtr->HwTail is reached and RingPtr->HwCnt is reached.
674 * - The number of requested BDs has been processed
676 while (BdCount < BdLimitLoc) {
677 /* Read the status */
678 if(CurBdPtr != NULL){
679 BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET);
682 if ((Sop == 0x00000000U) && ((BdStr & XEMACPS_TXBUF_USED_MASK)!=0x00000000U)){
685 if (Sop == 0x00000001U) {
690 /* hardware has processed this BD so check the "last" bit.
691 * If it is clear, then there are more BDs for the current
692 * packet. Keep a count of these partial packet BDs.
694 if ((Sop == 0x00000001U) && ((BdStr & XEMACPS_TXBUF_LAST_MASK)!=0x00000000U)) {
699 /* Move on to next BD in work group */
700 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
703 /* Subtract off any partial packet BDs found */
704 BdCount -= BdPartialCount;
706 /* If BdCount is non-zero then BDs were found to return. Set return
707 * parameters, update pointers and counters, return success
709 if (BdCount > 0x00000000U) {
710 *BdSetPtr = RingPtr->HwHead;
711 RingPtr->HwCnt -= BdCount;
712 RingPtr->PostCnt += BdCount;
713 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
724 /*****************************************************************************/
726 * Returns a set of BD(s) that have been processed by hardware. The returned
727 * BDs may be examined to determine the outcome of the DMA transaction(s).
728 * Once the BDs have been examined, the user must call XEmacPs_BdRingFree()
729 * in the same order which they were retrieved here. Example:
732 * NumBd = XEmacPs_BdRingFromHwRx(MyRingPtr, MaxBd, &MyBdSet),
736 * *hardware has nothing ready for us yet*
740 * for (i=0; i<NumBd; i++)
742 * * Examine CurBd for post processing *.....
745 * CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
748 * XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet), * Return list *
752 * A more advanced use of this function may allocate multiple sets of BDs.
753 * They must be retrieved from hardware and freed in the correct sequence:
756 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
757 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
760 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
761 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2),
762 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
763 * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
766 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
767 * XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2),
768 * XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
769 * XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
772 * If hardware has only partially completed a packet spanning multiple BDs,
773 * then none of the BDs for that packet will be included in the results.
775 * @param RingPtr is a pointer to the instance to be worked on.
776 * @param BdLimit is the maximum number of BDs to return in the set.
777 * @param BdSetPtr is an output parameter, it points to the first BD available
781 * The number of BDs processed by hardware. A value of 0 indicates that no
782 * data is available. No more than BdLimit BDs will be returned.
784 * @note Treat BDs returned by this function as read-only.
786 * @note This function should not be preempted by another XEmacPs_Bd function
787 * call that modifies the BD space. It is the caller's responsibility to
788 * provide a mutual exclusion mechanism.
790 *****************************************************************************/
791 u32 XEmacPs_BdRingFromHwRx(XEmacPs_BdRing * RingPtr, u32 BdLimit,
792 XEmacPs_Bd ** BdSetPtr)
794 XEmacPs_Bd *CurBdPtr;
800 CurBdPtr = RingPtr->HwHead;
804 /* If no BDs in work group, then there's nothing to search */
805 if (RingPtr->HwCnt == 0x00000000U) {
810 /* Starting at HwHead, keep moving forward in the list until:
811 * - A BD is encountered with its new/used bit set which means
812 * hardware has completed processing of that BD.
813 * - RingPtr->HwTail is reached and RingPtr->HwCnt is reached.
814 * - The number of requested BDs has been processed
816 while (BdCount < BdLimit) {
818 /* Read the status */
820 BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET);
822 if ((!(XEmacPs_BdIsRxNew(CurBdPtr)))==TRUE) {
828 /* hardware has processed this BD so check the "last" bit. If
829 * it is clear, then there are more BDs for the current packet.
830 * Keep a count of these partial packet BDs.
832 if ((BdStr & XEMACPS_RXBUF_EOF_MASK)!=0x00000000U) {
838 /* Move on to next BD in work group */
839 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
842 /* Subtract off any partial packet BDs found */
843 BdCount -= BdPartialCount;
845 /* If BdCount is non-zero then BDs were found to return. Set return
846 * parameters, update pointers and counters, return success
848 if (BdCount > 0x00000000U) {
849 *BdSetPtr = RingPtr->HwHead;
850 RingPtr->HwCnt -= BdCount;
851 RingPtr->PostCnt += BdCount;
852 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
864 /*****************************************************************************/
866 * Frees a set of BDs that had been previously retrieved with
867 * XEmacPs_BdRingFromHw().
869 * @param RingPtr is a pointer to the instance to be worked on.
870 * @param NumBd is the number of BDs to free.
871 * @param BdSetPtr is the head of a list of BDs returned by
872 * XEmacPs_BdRingFromHw().
875 * - XST_SUCCESS if the set of BDs was freed.
876 * - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with
877 * XEmacPs_BdRingFromHw().
879 * @note This function should not be preempted by another XEmacPs_Bd function
880 * call that modifies the BD space. It is the caller's responsibility to
881 * provide a mutual exclusion mechanism.
883 *****************************************************************************/
884 LONG XEmacPs_BdRingFree(XEmacPs_BdRing * RingPtr, u32 NumBd,
885 XEmacPs_Bd * BdSetPtr)
888 /* if no bds to process, simply return. */
889 if (0x00000000U == NumBd){
890 Status = (LONG)(XST_SUCCESS);
892 /* Make sure we are in sync with XEmacPs_BdRingFromHw() */
893 if ((RingPtr->PostCnt < NumBd) || (RingPtr->PostHead != BdSetPtr)) {
894 Status = (LONG)(XST_DMA_SG_LIST_ERROR);
896 /* Update pointers and counters */
897 RingPtr->FreeCnt += NumBd;
898 RingPtr->PostCnt -= NumBd;
899 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->PostHead, NumBd);
900 Status = (LONG)(XST_SUCCESS);
907 /*****************************************************************************/
909 * Check the internal data structures of the BD ring for the provided channel.
910 * The following checks are made:
912 * - Is the BD ring linked correctly in physical address space.
913 * - Do the internal pointers point to BDs in the ring.
914 * - Do the internal counters add up.
916 * The channel should be stopped prior to calling this function.
918 * @param RingPtr is a pointer to the instance to be worked on.
919 * @param Direction is either XEMACPS_SEND or XEMACPS_RECV that indicates
923 * - XST_SUCCESS if the set of BDs was freed.
924 * - XST_DMA_SG_NO_LIST if the list has not been created.
925 * - XST_IS_STARTED if the channel is not stopped.
926 * - XST_DMA_SG_LIST_ERROR if a problem is found with the internal data
927 * structures. If this value is returned, the channel should be reset to
928 * avoid data corruption or system instability.
930 * @note This function should not be preempted by another XEmacPs_Bd function
931 * call that modifies the BD space. It is the caller's responsibility to
932 * provide a mutual exclusion mechanism.
934 *****************************************************************************/
935 LONG XEmacPs_BdRingCheck(XEmacPs_BdRing * RingPtr, u8 Direction)
937 UINTPTR AddrV, AddrP;
940 if ((Direction != (u8)XEMACPS_SEND) && (Direction != (u8)XEMACPS_RECV)) {
941 return (LONG)(XST_INVALID_PARAM);
944 /* Is the list created */
945 if (RingPtr->AllCnt == 0x00000000U) {
946 return (LONG)(XST_DMA_SG_NO_LIST);
949 /* Can't check if channel is running */
950 if (RingPtr->RunState == (u32)XST_DMA_SG_IS_STARTED) {
951 return (LONG)(XST_IS_STARTED);
954 /* RunState doesn't make sense */
955 if (RingPtr->RunState != (u32)XST_DMA_SG_IS_STOPPED) {
956 return (LONG)(XST_DMA_SG_LIST_ERROR);
959 /* Verify internal pointers point to correct memory space */
960 AddrV = (UINTPTR) RingPtr->FreeHead;
961 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
962 return (LONG)(XST_DMA_SG_LIST_ERROR);
965 AddrV = (UINTPTR) RingPtr->PreHead;
966 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
967 return (LONG)(XST_DMA_SG_LIST_ERROR);
970 AddrV = (UINTPTR) RingPtr->HwHead;
971 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
972 return (LONG)(XST_DMA_SG_LIST_ERROR);
975 AddrV = (UINTPTR) RingPtr->HwTail;
976 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
977 return (LONG)(XST_DMA_SG_LIST_ERROR);
980 AddrV = (UINTPTR) RingPtr->PostHead;
981 if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
982 return (LONG)(XST_DMA_SG_LIST_ERROR);
985 /* Verify internal counters add up */
986 if ((RingPtr->HwCnt + RingPtr->PreCnt + RingPtr->FreeCnt +
987 RingPtr->PostCnt) != RingPtr->AllCnt) {
988 return (LONG)(XST_DMA_SG_LIST_ERROR);
991 /* Verify BDs are linked correctly */
992 AddrV = RingPtr->BaseBdAddr;
993 AddrP = RingPtr->PhysBaseAddr + RingPtr->Separation;
995 for (i = 1U; i < RingPtr->AllCnt; i++) {
996 /* Check BDA for this BD. It should point to next physical addr */
997 if (XEmacPs_BdRead(AddrV, XEMACPS_BD_ADDR_OFFSET) != AddrP) {
998 return (LONG)(XST_DMA_SG_LIST_ERROR);
1001 /* Move on to next BD */
1002 AddrV += RingPtr->Separation;
1003 AddrP += RingPtr->Separation;
1006 /* Last BD should have wrap bit set */
1007 if (XEMACPS_SEND == Direction) {
1008 if ((!XEmacPs_BdIsTxWrap(AddrV))==TRUE) {
1009 return (LONG)(XST_DMA_SG_LIST_ERROR);
1012 else { /* XEMACPS_RECV */
1013 if ((!XEmacPs_BdIsRxWrap(AddrV))==TRUE) {
1014 return (LONG)(XST_DMA_SG_LIST_ERROR);
1018 /* No problems found */
1019 return (LONG)(XST_SUCCESS);
1022 /*****************************************************************************/
1024 * Set this bit to mark the last descriptor in the receive buffer descriptor
1027 * @param BdPtr is the BD pointer to operate on
1030 * C-style signature:
1031 * void XEmacPs_BdSetRxWrap(XEmacPs_Bd* BdPtr)
1033 *****************************************************************************/
1034 static void XEmacPs_BdSetRxWrap(UINTPTR BdPtr)
1039 BdPtr += (u32)(XEMACPS_BD_ADDR_OFFSET);
1040 TempPtr = (u32 *)BdPtr;
1041 if(TempPtr != NULL) {
1042 DataValueRx = *TempPtr;
1043 DataValueRx |= XEMACPS_RXBUF_WRAP_MASK;
1044 *TempPtr = DataValueRx;
1048 /*****************************************************************************/
1050 * Sets this bit to mark the last descriptor in the transmit buffer
1053 * @param BdPtr is the BD pointer to operate on
1056 * C-style signature:
1057 * void XEmacPs_BdSetTxWrap(XEmacPs_Bd* BdPtr)
1059 *****************************************************************************/
1060 static void XEmacPs_BdSetTxWrap(UINTPTR BdPtr)
1065 BdPtr += (u32)(XEMACPS_BD_STAT_OFFSET);
1066 TempPtr = (u32 *)BdPtr;
1067 if(TempPtr != NULL) {
1068 DataValueTx = *TempPtr;
1069 DataValueTx |= XEMACPS_TXBUF_WRAP_MASK;
1070 *TempPtr = DataValueTx;