]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_R5_UltraScale_MPSoC/RTOSDemo_R5_bsp/psu_cortexr5_0/libsrc/emacps_v3_7/src/xemacps_bdring.c
Update Zynq, MPSoc Cortex-A53 and MPSoc Cortex-R5 demo projects to build with the...
[freertos] / FreeRTOS / Demo / CORTEX_R5_UltraScale_MPSoC / RTOSDemo_R5_bsp / psu_cortexr5_0 / libsrc / emacps_v3_7 / src / xemacps_bdring.c
1 /******************************************************************************
2 *
3 * Copyright (C) 2010 - 2015 Xilinx, Inc.  All rights reserved.
4 *
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:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
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.
18 *
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
25 * SOFTWARE.
26 *
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.
30 *
31 ******************************************************************************/
32 /*****************************************************************************/
33 /**
34 *
35 * @file xemacps_bdring.c
36 * @addtogroup emacps_v3_7
37 * @{
38 *
39 * This file implements buffer descriptor ring related functions.
40 *
41 * <pre>
42 * MODIFICATION HISTORY:
43 *
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
61 *                     pointers
62 *
63 * </pre>
64 ******************************************************************************/
65
66 /***************************** Include Files *********************************/
67
68 #include "xstatus.h"
69 #include "xil_cache.h"
70 #include "xemacps_hw.h"
71 #include "xemacps_bd.h"
72 #include "xemacps_bdring.h"
73
74 /************************** Constant Definitions *****************************/
75
76 /**************************** Type Definitions *******************************/
77
78
79 /***************** Macros (Inline Functions) Definitions *********************/
80
81 /****************************************************************************
82  * Compute the virtual address of a descriptor from its physical address
83  *
84  * @param BdPtr is the physical address of the BD
85  *
86  * @returns Virtual address of BdPtr
87  *
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))
92
93 /****************************************************************************
94  * Compute the physical address of a descriptor from its virtual address
95  *
96  * @param BdPtr is the physical address of the BD
97  *
98  * @returns Physical address of BdPtr
99  *
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))
104
105 /****************************************************************************
106  * Move the BdPtr argument ahead an arbitrary number of BDs wrapping around
107  * to the beginning of the ring if needed.
108  *
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.
113  *
114  * @param RingPtr is the ring BdPtr appears in
115  * @param BdPtr on input is the starting BD position and on output is the
116  *        final BD position
117  * @param NumBd is the number of BD spaces to increment
118  *
119  ****************************************************************************/
120 #define XEMACPS_RING_SEEKAHEAD(RingPtr, BdPtr, NumBd)                  \
121     {                                                                   \
122         UINTPTR Addr = (UINTPTR)(void *)(BdPtr);                        \
123                                                                         \
124         Addr += ((RingPtr)->Separation * (NumBd));                        \
125         if ((Addr > (RingPtr)->HighBdAddr) || ((UINTPTR)(void *)(BdPtr) > Addr))  \
126         {                                                               \
127             Addr -= (RingPtr)->Length;                                  \
128         }                                                               \
129                                                                         \
130         (BdPtr) = (XEmacPs_Bd*)(void *)Addr;                                     \
131     }
132
133 /****************************************************************************
134  * Move the BdPtr argument backwards an arbitrary number of BDs wrapping
135  * around to the end of the ring if needed.
136  *
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.
141  *
142  * @param RingPtr is the ring BdPtr appears in
143  * @param BdPtr on input is the starting BD position and on output is the
144  *        final BD position
145  * @param NumBd is the number of BD spaces to increment
146  *
147  ****************************************************************************/
148 #define XEMACPS_RING_SEEKBACK(RingPtr, BdPtr, NumBd)                   \
149     {                                                                   \
150         UINTPTR Addr = (UINTPTR)(void *)(BdPtr);                                  \
151                                                                         \
152         Addr -= ((RingPtr)->Separation * (NumBd));                        \
153         if ((Addr < (RingPtr)->BaseBdAddr) || ((UINTPTR)(void*)(BdPtr) < Addr))  \
154         {                                                               \
155             Addr += (RingPtr)->Length;                                  \
156         }                                                               \
157                                                                         \
158         (BdPtr) = (XEmacPs_Bd*)(void*)Addr;                                     \
159     }
160
161
162 /************************** Function Prototypes ******************************/
163
164 static void XEmacPs_BdSetRxWrap(UINTPTR BdPtr);
165 static void XEmacPs_BdSetTxWrap(UINTPTR BdPtr);
166
167 /************************** Variable Definitions *****************************/
168
169 /*****************************************************************************/
170 /**
171  * Using a memory segment allocated by the caller, create and setup the BD list
172  * for the given DMA channel.
173  *
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.
184  *
185  * @return
186  *
187  * - XST_SUCCESS if initialization was successful
188  * - XST_NO_FEATURE if the provided instance is a non DMA type
189  *   channel.
190  * - XST_INVALID_PARAM under any of the following conditions:
191  *   1) PhysAddr and/or VirtAddr are not aligned to the given Alignment
192  *      parameter.
193  *   2) Alignment parameter does not meet minimum requirements or is not a
194  *      power of 2 value.
195  *   3) BdCount is 0.
196  * - XST_DMA_SG_LIST_ERROR if the memory segment containing the list spans
197  *   over address 0x00000000 in virtual address space.
198  *
199  * @note
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)
204 {
205         u32 i;
206         UINTPTR BdVirtAddr;
207         UINTPTR BdPhyAddr;
208         UINTPTR VirtAddrLoc = VirtAddr;
209
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.
213          */
214         RingPtr->AllCnt = 0U;
215         RingPtr->FreeCnt = 0U;
216         RingPtr->HwCnt = 0U;
217         RingPtr->PreCnt = 0U;
218         RingPtr->PostCnt = 0U;
219
220         /* Make sure Alignment parameter meets minimum requirements */
221         if (Alignment < (u32)XEMACPS_DMABD_MINIMUM_ALIGNMENT) {
222                 return (LONG)(XST_INVALID_PARAM);
223         }
224
225         /* Make sure Alignment is a power of 2 */
226         if (((Alignment - 0x00000001U) & Alignment)!=0x00000000U) {
227                 return (LONG)(XST_INVALID_PARAM);
228         }
229
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);
233         }
234
235         /* Is BdCount reasonable? */
236         if (BdCount == 0x00000000U) {
237                 return (LONG)(XST_INVALID_PARAM);
238         }
239
240         /* Figure out how many bytes will be between the start of adjacent BDs */
241         RingPtr->Separation = ((u32)sizeof(XEmacPs_Bd));
242
243         /* Must make sure the ring doesn't span address 0x00000000. If it does,
244          * then the next/prev BD traversal macros will fail.
245          */
246         if (VirtAddrLoc > ((VirtAddrLoc + (RingPtr->Separation * BdCount)) - (u32)1)) {
247                 return (LONG)(XST_DMA_SG_LIST_ERROR);
248         }
249
250         /* Initial ring setup:
251          *  - Clear the entire space
252          *  - Setup each BD's BDA field with the physical address of the next BD
253          */
254         (void)memset((void *) VirtAddrLoc, 0, (RingPtr->Separation * BdCount));
255
256         BdVirtAddr = VirtAddrLoc;
257         BdPhyAddr = PhysAddr + RingPtr->Separation;
258         for (i = 1U; i < BdCount; i++) {
259                 BdVirtAddr += RingPtr->Separation;
260                 BdPhyAddr += RingPtr->Separation;
261         }
262
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;
268         RingPtr->Length =
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;
278
279         return (LONG)(XST_SUCCESS);
280 }
281
282
283 /*****************************************************************************/
284 /**
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.
287  *
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
291  * user.
292  *
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
297  *        which direction.
298  *
299  * @return
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.
305  *
306  *****************************************************************************/
307 LONG XEmacPs_BdRingClone(XEmacPs_BdRing * RingPtr, XEmacPs_Bd * SrcBdPtr,
308                          u8 Direction)
309 {
310         u32 i;
311         UINTPTR CurBd;
312
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);
316         }
317
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);
321         }
322
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);
326         }
327
328         if ((Direction != (u8)XEMACPS_SEND) && (Direction != (u8)XEMACPS_RECV)) {
329                 return (LONG)(XST_INVALID_PARAM);
330         }
331
332         /* Starting from the top of the ring, save BD.Next, overwrite the entire
333          * BD with the template, then restore BD.Next
334          */
335         CurBd = RingPtr->BaseBdAddr;
336         for (i = 0U; i < RingPtr->AllCnt; i++) {
337                 memcpy((void *)CurBd, SrcBdPtr, sizeof(XEmacPs_Bd));
338         CurBd += RingPtr->Separation;
339         }
340
341         CurBd -= RingPtr->Separation;
342
343         if (Direction == XEMACPS_RECV) {
344                 XEmacPs_BdSetRxWrap(CurBd);
345         }
346         else {
347                 XEmacPs_BdSetTxWrap(CurBd);
348         }
349
350         return (LONG)(XST_SUCCESS);
351 }
352
353
354 /*****************************************************************************/
355 /**
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:
360  *
361  * <pre>
362  *        NumBd = 2,
363  *        Status = XEmacPs_BdRingAlloc(MyRingPtr, NumBd, &MyBdSet),
364  *
365  *        if (Status != XST_SUCCESS)
366  *        {
367  *            *Not enough BDs available for the request*
368  *        }
369  *
370  *        CurBd = MyBdSet,
371  *        for (i=0; i<NumBd; i++)
372  *        {
373  *            * Prepare CurBd *.....
374  *
375  *            * Onto next BD *
376  *            CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
377  *        }
378  *
379  *        * Give list to hardware *
380  *        Status = XEmacPs_BdRingToHw(MyRingPtr, NumBd, MyBdSet),
381  * </pre>
382  *
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:
385  * <pre>
386  *        * Legal *
387  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
388  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
389  *
390  *        * Legal *
391  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
392  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2),
393  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
394  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2),
395  *
396  *        * Not legal *
397  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1),
398  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2),
399  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2),
400  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1),
401  * </pre>
402  *
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().
406  *
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
410  *        for modification.
411  *
412  * @return
413  *   - XST_SUCCESS if the requested number of BDs was returned in the BdSetPtr
414  *     parameter.
415  *   - XST_FAILURE if there were not enough free BDs to satisfy the request.
416  *
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.
420  *
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
423  *       instability.
424  *
425  *****************************************************************************/
426 LONG XEmacPs_BdRingAlloc(XEmacPs_BdRing * RingPtr, u32 NumBd,
427                          XEmacPs_Bd ** BdSetPtr)
428 {
429         LONG Status;
430         /* Enough free BDs available for the request? */
431         if (RingPtr->FreeCnt < NumBd) {
432                 Status = (LONG)(XST_FAILURE);
433         } else {
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);
440         }
441         return Status;
442 }
443
444 /*****************************************************************************/
445 /**
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().
449  *
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.
453  *
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.
457  *
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:
460  *
461  * <pre>
462  *    Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr),
463  *        ...
464  *    if (Error)
465  *    {
466  *        Status = XEmacPs_BdRingUnAlloc(MyRingPtr, 10, &BdPtr),
467  *    }
468  * </pre>
469  *
470  * A partial UnAlloc means some of the BDs Alloc'd will be returned:
471  *
472  * <pre>
473  *    Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr),
474  *    BdsLeft = 10,
475  *    CurBdPtr = BdPtr,
476  *
477  *    while (BdsLeft)
478  *    {
479  *       if (Error)
480  *       {
481  *          Status = XEmacPs_BdRingUnAlloc(MyRingPtr, BdsLeft, CurBdPtr),
482  *       }
483  *
484  *       CurBdPtr = XEmacPs_BdRingNext(MyRingPtr, CurBdPtr),
485  *       BdsLeft--,
486  *    }
487  * </pre>
488  *
489  * A partial UnAlloc must include the last BD in the list that was Alloc'd.
490  *
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
494  *        for modification.
495  *
496  * @return
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.
500  *
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.
504  *
505  *****************************************************************************/
506 LONG XEmacPs_BdRingUnAlloc(XEmacPs_BdRing * RingPtr, u32 NumBd,
507                            XEmacPs_Bd * BdSetPtr)
508 {
509         LONG Status;
510         (void) BdSetPtr;
511         Xil_AssertNonvoid(RingPtr != NULL);
512         Xil_AssertNonvoid(BdSetPtr != NULL);
513
514         /* Enough BDs in the free state for the request? */
515         if (RingPtr->PreCnt < NumBd) {
516                 Status = (LONG)(XST_FAILURE);
517         } else {
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);
523         }
524         return Status;
525 }
526
527
528 /*****************************************************************************/
529 /**
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.
534  *
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()).
537  *
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.
541  *
542  * @return
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().
548  *
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.
552  *
553  *****************************************************************************/
554 LONG XEmacPs_BdRingToHw(XEmacPs_BdRing * RingPtr, u32 NumBd,
555                         XEmacPs_Bd * BdSetPtr)
556 {
557         XEmacPs_Bd *CurBdPtr;
558         u32 i;
559         LONG Status;
560         /* if no bds to process, simply return. */
561         if (0U == NumBd){
562                 Status = (LONG)(XST_SUCCESS);
563         } else {
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);
567                 } else {
568         CurBdPtr = BdSetPtr;
569                         for (i = 0U; i < NumBd; i++) {
570                                 CurBdPtr = (XEmacPs_Bd *)((void *)XEmacPs_BdRingNext(RingPtr, CurBdPtr));
571         }
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;
577
578                         Status = (LONG)(XST_SUCCESS);
579                 }
580         }
581         return Status;
582 }
583
584
585 /*****************************************************************************/
586 /**
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:
591  *
592  * <pre>
593  *        NumBd = XEmacPs_BdRingFromHwTx(MyRingPtr, MaxBd, &MyBdSet),
594  *        if (NumBd == 0)
595  *        {
596  *           * hardware has nothing ready for us yet*
597  *        }
598  *
599  *        CurBd = MyBdSet,
600  *        for (i=0; i<NumBd; i++)
601  *        {
602  *           * Examine CurBd for post processing *.....
603  *
604  *           * Onto next BD *
605  *           CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
606  *           }
607  *
608  *           XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet),  *Return list*
609  *        }
610  * </pre>
611  *
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:
614  * <pre>
615  *        * Legal *
616  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
617  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
618  *
619  *        * Legal *
620  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
621  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2),
622  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
623  *        XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
624  *
625  *        * Not legal *
626  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1),
627  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2),
628  *        XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
629  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
630  * </pre>
631  *
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.
634  *
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
638  *        for examination.
639  *
640  * @return
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.
643  *
644  * @note Treat BDs returned by this function as read-only.
645  *
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.
649  *
650  *****************************************************************************/
651 u32 XEmacPs_BdRingFromHwTx(XEmacPs_BdRing * RingPtr, u32 BdLimit,
652                                  XEmacPs_Bd ** BdSetPtr)
653 {
654         XEmacPs_Bd *CurBdPtr;
655         u32 BdStr = 0U;
656         u32 BdCount;
657         u32 BdPartialCount;
658         u32 Sop = 0U;
659         u32 Status;
660         u32 BdLimitLoc = BdLimit;
661         CurBdPtr = RingPtr->HwHead;
662         BdCount = 0U;
663         BdPartialCount = 0U;
664
665         /* If no BDs in work group, then there's nothing to search */
666         if (RingPtr->HwCnt == 0x00000000U) {
667                 *BdSetPtr = NULL;
668                 Status = 0U;
669         } else {
670
671                 if (BdLimitLoc > RingPtr->HwCnt){
672                         BdLimitLoc = RingPtr->HwCnt;
673         }
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
679          */
680                 while (BdCount < BdLimitLoc) {
681                 /* Read the status */
682                         if(CurBdPtr != NULL){
683                 BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET);
684                         }
685
686                         if ((Sop == 0x00000000U) && ((BdStr & XEMACPS_TXBUF_USED_MASK)!=0x00000000U)){
687                                 Sop = 1U;
688                         }
689                         if (Sop == 0x00000001U) {
690                         BdCount++;
691                         BdPartialCount++;
692                 }
693
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.
697                  */
698                         if ((Sop == 0x00000001U) && ((BdStr & XEMACPS_TXBUF_LAST_MASK)!=0x00000000U)) {
699                                 Sop = 0U;
700                                 BdPartialCount = 0U;
701                 }
702
703                 /* Move on to next BD in work group */
704                 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
705         }
706
707         /* Subtract off any partial packet BDs found */
708         BdCount -= BdPartialCount;
709
710         /* If BdCount is non-zero then BDs were found to return. Set return
711          * parameters, update pointers and counters, return success
712          */
713                 if (BdCount > 0x00000000U) {
714                 *BdSetPtr = RingPtr->HwHead;
715                 RingPtr->HwCnt -= BdCount;
716                 RingPtr->PostCnt += BdCount;
717                 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
718                         Status = (BdCount);
719                 } else {
720                         *BdSetPtr = NULL;
721                         Status = 0U;
722         }
723         }
724         return Status;
725 }
726
727
728 /*****************************************************************************/
729 /**
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:
734  *
735  * <pre>
736  *        NumBd = XEmacPs_BdRingFromHwRx(MyRingPtr, MaxBd, &MyBdSet),
737  *
738  *        if (NumBd == 0)
739  *        {
740  *           *hardware has nothing ready for us yet*
741  *        }
742  *
743  *        CurBd = MyBdSet,
744  *        for (i=0; i<NumBd; i++)
745  *        {
746  *           * Examine CurBd for post processing *.....
747  *
748  *           * Onto next BD *
749  *           CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd),
750  *           }
751  *
752  *           XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet),  * Return list *
753  *        }
754  * </pre>
755  *
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:
758  * <pre>
759  *        * Legal *
760  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
761  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
762  *
763  *        * Legal *
764  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
765  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2),
766  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
767  *        XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
768  *
769  *        * Not legal *
770  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1),
771  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2),
772  *        XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2),
773  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1),
774  * </pre>
775  *
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.
778  *
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
782  *        for examination.
783  *
784  * @return
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.
787  *
788  * @note Treat BDs returned by this function as read-only.
789  *
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.
793  *
794  *****************************************************************************/
795 u32 XEmacPs_BdRingFromHwRx(XEmacPs_BdRing * RingPtr, u32 BdLimit,
796                                  XEmacPs_Bd ** BdSetPtr)
797 {
798         XEmacPs_Bd *CurBdPtr;
799         u32 BdStr = 0U;
800         u32 BdCount;
801         u32 BdPartialCount;
802         u32 Status;
803
804         CurBdPtr = RingPtr->HwHead;
805         BdCount = 0U;
806         BdPartialCount = 0U;
807
808         /* If no BDs in work group, then there's nothing to search */
809         if (RingPtr->HwCnt == 0x00000000U) {
810                 *BdSetPtr = NULL;
811                 Status = 0U;
812         } else {
813
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
819          */
820         while (BdCount < BdLimit) {
821
822                 /* Read the status */
823                         if(CurBdPtr!=NULL){
824                 BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET);
825                         }
826                         if ((!(XEmacPs_BdIsRxNew(CurBdPtr)))==TRUE) {
827                         break;
828                 }
829
830                 BdCount++;
831
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.
835                  */
836                         if ((BdStr & XEMACPS_RXBUF_EOF_MASK)!=0x00000000U) {
837                                 BdPartialCount = 0U;
838                         } else {
839                         BdPartialCount++;
840                 }
841
842                 /* Move on to next BD in work group */
843                 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
844         }
845
846         /* Subtract off any partial packet BDs found */
847         BdCount -= BdPartialCount;
848
849         /* If BdCount is non-zero then BDs were found to return. Set return
850          * parameters, update pointers and counters, return success
851          */
852                 if (BdCount > 0x00000000U) {
853                 *BdSetPtr = RingPtr->HwHead;
854                 RingPtr->HwCnt -= BdCount;
855                 RingPtr->PostCnt += BdCount;
856                 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
857                         Status = (BdCount);
858         }
859         else {
860                 *BdSetPtr = NULL;
861                         Status = 0U;
862         }
863 }
864         return Status;
865 }
866
867
868 /*****************************************************************************/
869 /**
870  * Frees a set of BDs that had been previously retrieved with
871  * XEmacPs_BdRingFromHw().
872  *
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().
877  *
878  * @return
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().
882  *
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.
886  *
887  *****************************************************************************/
888 LONG XEmacPs_BdRingFree(XEmacPs_BdRing * RingPtr, u32 NumBd,
889                         XEmacPs_Bd * BdSetPtr)
890 {
891         LONG Status;
892         /* if no bds to process, simply return. */
893         if (0x00000000U == NumBd){
894                 Status = (LONG)(XST_SUCCESS);
895         } else {
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);
899                 } else {
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);
905                 }
906         }
907         return Status;
908 }
909
910
911 /*****************************************************************************/
912 /**
913  * Check the internal data structures of the BD ring for the provided channel.
914  * The following checks are made:
915  *
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.
919  *
920  * The channel should be stopped prior to calling this function.
921  *
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
924  *        which direction.
925  *
926  * @return
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.
933  *
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.
937  *
938  *****************************************************************************/
939 LONG XEmacPs_BdRingCheck(XEmacPs_BdRing * RingPtr, u8 Direction)
940 {
941         UINTPTR AddrV, AddrP;
942         u32 i;
943
944         if ((Direction != (u8)XEMACPS_SEND) && (Direction != (u8)XEMACPS_RECV)) {
945                 return (LONG)(XST_INVALID_PARAM);
946         }
947
948         /* Is the list created */
949         if (RingPtr->AllCnt == 0x00000000U) {
950                 return (LONG)(XST_DMA_SG_NO_LIST);
951         }
952
953         /* Can't check if channel is running */
954         if (RingPtr->RunState == (u32)XST_DMA_SG_IS_STARTED) {
955                 return (LONG)(XST_IS_STARTED);
956         }
957
958         /* RunState doesn't make sense */
959         if (RingPtr->RunState != (u32)XST_DMA_SG_IS_STOPPED) {
960                 return (LONG)(XST_DMA_SG_LIST_ERROR);
961         }
962
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);
967         }
968
969         AddrV = (UINTPTR) RingPtr->PreHead;
970         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
971                 return (LONG)(XST_DMA_SG_LIST_ERROR);
972         }
973
974         AddrV = (UINTPTR) RingPtr->HwHead;
975         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
976                 return (LONG)(XST_DMA_SG_LIST_ERROR);
977         }
978
979         AddrV = (UINTPTR) RingPtr->HwTail;
980         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
981                 return (LONG)(XST_DMA_SG_LIST_ERROR);
982         }
983
984         AddrV = (UINTPTR) RingPtr->PostHead;
985         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
986                 return (LONG)(XST_DMA_SG_LIST_ERROR);
987         }
988
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);
993         }
994
995         /* Verify BDs are linked correctly */
996         AddrV = RingPtr->BaseBdAddr;
997         AddrP = RingPtr->PhysBaseAddr + RingPtr->Separation;
998
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);
1003                 }
1004
1005                 /* Move on to next BD */
1006                 AddrV += RingPtr->Separation;
1007                 AddrP += RingPtr->Separation;
1008         }
1009
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);
1014                 }
1015         }
1016         else {                  /* XEMACPS_RECV */
1017                 if ((!XEmacPs_BdIsRxWrap(AddrV))==TRUE) {
1018                         return (LONG)(XST_DMA_SG_LIST_ERROR);
1019                 }
1020         }
1021
1022         /* No problems found */
1023         return (LONG)(XST_SUCCESS);
1024 }
1025
1026 /*****************************************************************************/
1027 /**
1028  * Set this bit to mark the last descriptor in the receive buffer descriptor
1029  * list.
1030  *
1031  * @param  BdPtr is the BD pointer to operate on
1032  *
1033  * @note
1034  * C-style signature:
1035  *    void XEmacPs_BdSetRxWrap(XEmacPs_Bd* BdPtr)
1036  *
1037  *****************************************************************************/
1038 static void XEmacPs_BdSetRxWrap(UINTPTR BdPtr)
1039 {
1040     u32 DataValueRx;
1041         u32 *TempPtr;
1042
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;
1049         }
1050 }
1051
1052 /*****************************************************************************/
1053 /**
1054  * Sets this bit to mark the last descriptor in the transmit buffer
1055  * descriptor list.
1056  *
1057  * @param  BdPtr is the BD pointer to operate on
1058  *
1059  * @note
1060  * C-style signature:
1061  *    void XEmacPs_BdSetTxWrap(XEmacPs_Bd* BdPtr)
1062  *
1063  *****************************************************************************/
1064 static void XEmacPs_BdSetTxWrap(UINTPTR BdPtr)
1065 {
1066     u32 DataValueTx;
1067         u32 *TempPtr;
1068
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;
1075         }
1076 }
1077
1078 /*****************************************************************************/
1079 /**
1080  * Reset BD ring head and tail pointers.
1081  *
1082  * @param RingPtr is the instance to be worked on.
1083  * @param VirtAddr is the virtual base address of the user memory region.
1084  *
1085  * @note
1086  * Should be called after XEmacPs_Stop()
1087  *
1088  * @note
1089  * C-style signature:
1090  *    void XEmacPs_BdRingPtrReset(XEmacPs_BdRing * RingPtr, void *virtaddrloc)
1091  *
1092  *****************************************************************************/
1093 void XEmacPs_BdRingPtrReset(XEmacPs_BdRing * RingPtr, void *virtaddrloc)
1094 {
1095         RingPtr->FreeHead = virtaddrloc;
1096         RingPtr->PreHead = virtaddrloc;
1097         RingPtr->HwHead = virtaddrloc;
1098         RingPtr->HwTail = virtaddrloc;
1099         RingPtr->PostHead = virtaddrloc;
1100 }
1101
1102 /** @} */