]> git.sur5r.net Git - freertos/blob
13ef305d149f4cd2d702934d86a07174259371a5
[freertos] /
1 /* $Id: xemacps_bdring.c,v 1.1.2.1 2011/01/20 03:39:02 sadanan Exp $ */
2 /******************************************************************************
3 *
4 * Copyright (C) 2010 - 2014 Xilinx, Inc.  All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal 
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * Use of the Software is limited solely to applications:
17 * (a) running on a Xilinx device, or
18 * (b) that interact with a Xilinx device through a bus or interconnect.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 
25 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 *
28 * Except as contained in this notice, the name of the Xilinx shall not be used
29 * in advertising or otherwise to promote the sale, use or other dealings in
30 * this Software without prior written authorization from Xilinx.
31 *
32 ******************************************************************************/
33 /*****************************************************************************/
34 /**
35 *
36 * @file xemacps_bdring.c
37 * @addtogroup emacps_v2_0
38 * @{
39 *
40 * This file implements buffer descriptor ring related functions.
41 *
42 * <pre>
43 * MODIFICATION HISTORY:
44 *
45 * Ver   Who  Date     Changes
46 * ----- ---- -------- -------------------------------------------------------
47 * 1.00a wsy  01/10/10 First release
48 * 1.00a asa  11/21/11 The function XEmacPs_BdRingFromHwTx is modified.
49 *                     Earlier it used to search in "BdLimit" number of BDs to
50 *                     know which BDs are processed. Now one more check is
51 *                     added. It looks for BDs till the current BD pointer
52 *                     reaches HwTail. By doing this processing time is saved.
53 * 1.00a asa  01/24/12 The function XEmacPs_BdRingFromHwTx in file
54 *                     xemacps_bdring.c is modified. Now start of packet is
55 *                     searched for returning the number of BDs processed.
56 * 1.05a asa  09/23/13 Cache operations on BDs are not required and hence
57 *                     removed. It is expected that all BDs are allocated in
58 *                     from uncached area. Fix for CR #663885.
59 * </pre>
60 ******************************************************************************/
61
62 /***************************** Include Files *********************************/
63
64 #include "xstatus.h"
65 #include "xil_cache.h"
66 #include "xemacps_hw.h"
67 #include "xemacps_bd.h"
68 #include "xemacps_bdring.h"
69
70 /************************** Constant Definitions *****************************/
71
72 /**************************** Type Definitions *******************************/
73
74
75 /***************** Macros (Inline Functions) Definitions *********************/
76
77 /****************************************************************************
78  * Compute the virtual address of a descriptor from its physical address
79  *
80  * @param BdPtr is the physical address of the BD
81  *
82  * @returns Virtual address of BdPtr
83  *
84  * @note Assume BdPtr is always a valid BD in the ring
85  ****************************************************************************/
86 #define XEMACPS_PHYS_TO_VIRT(BdPtr) \
87     ((u32)BdPtr + (RingPtr->BaseBdAddr - RingPtr->PhysBaseAddr))
88
89 /****************************************************************************
90  * Compute the physical address of a descriptor from its virtual address
91  *
92  * @param BdPtr is the physical address of the BD
93  *
94  * @returns Physical address of BdPtr
95  *
96  * @note Assume BdPtr is always a valid BD in the ring
97  ****************************************************************************/
98 #define XEMACPS_VIRT_TO_PHYS(BdPtr) \
99     ((u32)BdPtr - (RingPtr->BaseBdAddr - RingPtr->PhysBaseAddr))
100
101 /****************************************************************************
102  * Move the BdPtr argument ahead an arbitrary number of BDs wrapping around
103  * to the beginning of the ring if needed.
104  *
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.
109  *
110  * @param RingPtr is the ring BdPtr appears in
111  * @param BdPtr on input is the starting BD position and on output is the
112  *        final BD position
113  * @param NumBd is the number of BD spaces to increment
114  *
115  ****************************************************************************/
116 #define XEMACPS_RING_SEEKAHEAD(RingPtr, BdPtr, NumBd)                  \
117     {                                                                   \
118         u32 Addr = (u32)BdPtr;                                  \
119                                                                         \
120         Addr += ((RingPtr)->Separation * NumBd);                        \
121         if ((Addr > (RingPtr)->HighBdAddr) || ((u32)BdPtr > Addr))  \
122         {                                                               \
123             Addr -= (RingPtr)->Length;                                  \
124         }                                                               \
125                                                                         \
126         BdPtr = (XEmacPs_Bd*)Addr;                                     \
127     }
128
129 /****************************************************************************
130  * Move the BdPtr argument backwards an arbitrary number of BDs wrapping
131  * around to the end of the ring if needed.
132  *
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.
137  *
138  * @param RingPtr is the ring BdPtr appears in
139  * @param BdPtr on input is the starting BD position and on output is the
140  *        final BD position
141  * @param NumBd is the number of BD spaces to increment
142  *
143  ****************************************************************************/
144 #define XEMACPS_RING_SEEKBACK(RingPtr, BdPtr, NumBd)                   \
145     {                                                                   \
146         u32 Addr = (u32)BdPtr;                                  \
147                                                                         \
148         Addr -= ((RingPtr)->Separation * NumBd);                        \
149         if ((Addr < (RingPtr)->BaseBdAddr) || ((u32)BdPtr < Addr))  \
150         {                                                               \
151             Addr += (RingPtr)->Length;                                  \
152         }                                                               \
153                                                                         \
154         BdPtr = (XEmacPs_Bd*)Addr;                                     \
155     }
156
157
158 /************************** Function Prototypes ******************************/
159
160
161 /************************** Variable Definitions *****************************/
162
163 /*****************************************************************************/
164 /**
165  * Using a memory segment allocated by the caller, create and setup the BD list
166  * for the given DMA channel.
167  *
168  * @param RingPtr is the instance to be worked on.
169  * @param PhysAddr is the physical base address of user memory region.
170  * @param VirtAddr is the virtual base address of the user memory region. If
171  *        address translation is not being utilized, then VirtAddr should be
172  *        equivalent to PhysAddr.
173  * @param Alignment governs the byte alignment of individual BDs. This function
174  *        will enforce a minimum alignment of 4 bytes with no maximum as long
175  *        as it is specified as a power of 2.
176  * @param BdCount is the number of BDs to setup in the user memory region. It
177  *        is assumed the region is large enough to contain the BDs.
178  *
179  * @return
180  *
181  * - XST_SUCCESS if initialization was successful
182  * - XST_NO_FEATURE if the provided instance is a non DMA type
183  *   channel.
184  * - XST_INVALID_PARAM under any of the following conditions:
185  *   1) PhysAddr and/or VirtAddr are not aligned to the given Alignment
186  *      parameter;
187  *   2) Alignment parameter does not meet minimum requirements or is not a
188  *      power of 2 value;
189  *   3) BdCount is 0.
190  * - XST_DMA_SG_LIST_ERROR if the memory segment containing the list spans
191  *   over address 0x00000000 in virtual address space.
192  *
193  * @note
194  * Make sure to pass in the right alignment value.
195  *****************************************************************************/
196 int XEmacPs_BdRingCreate(XEmacPs_BdRing * RingPtr, u32 PhysAddr,
197                           u32 VirtAddr, u32 Alignment, unsigned BdCount)
198 {
199         unsigned i;
200         u32 BdVirtAddr;
201         u32 BdPhyAddr;
202
203         /* In case there is a failure prior to creating list, make sure the
204          * following attributes are 0 to prevent calls to other functions
205          * from doing anything.
206          */
207         RingPtr->AllCnt = 0;
208         RingPtr->FreeCnt = 0;
209         RingPtr->HwCnt = 0;
210         RingPtr->PreCnt = 0;
211         RingPtr->PostCnt = 0;
212
213         /* Make sure Alignment parameter meets minimum requirements */
214         if (Alignment < XEMACPS_DMABD_MINIMUM_ALIGNMENT) {
215                 return (XST_INVALID_PARAM);
216         }
217
218         /* Make sure Alignment is a power of 2 */
219         if ((Alignment - 1) & Alignment) {
220                 return (XST_INVALID_PARAM);
221         }
222
223         /* Make sure PhysAddr and VirtAddr are on same Alignment */
224         if ((PhysAddr % Alignment) || (VirtAddr % Alignment)) {
225                 return (XST_INVALID_PARAM);
226         }
227
228         /* Is BdCount reasonable? */
229         if (BdCount == 0) {
230                 return (XST_INVALID_PARAM);
231         }
232
233         /* Figure out how many bytes will be between the start of adjacent BDs */
234         RingPtr->Separation =
235                 (sizeof(XEmacPs_Bd) + (Alignment - 1)) & ~(Alignment - 1);
236
237         /* Must make sure the ring doesn't span address 0x00000000. If it does,
238          * then the next/prev BD traversal macros will fail.
239          */
240         if (VirtAddr > (VirtAddr + (RingPtr->Separation * BdCount) - 1)) {
241                 return (XST_DMA_SG_LIST_ERROR);
242         }
243
244         /* Initial ring setup:
245          *  - Clear the entire space
246          *  - Setup each BD's BDA field with the physical address of the next BD
247          */
248         memset((void *) VirtAddr, 0, (RingPtr->Separation * BdCount));
249
250         BdVirtAddr = VirtAddr;
251         BdPhyAddr = PhysAddr + RingPtr->Separation;
252         for (i = 1; i < BdCount; i++) {
253                 BdVirtAddr += RingPtr->Separation;
254                 BdPhyAddr += RingPtr->Separation;
255         }
256
257         /* Setup and initialize pointers and counters */
258         RingPtr->RunState = XST_DMA_SG_IS_STOPPED;
259         RingPtr->BaseBdAddr = VirtAddr;
260         RingPtr->PhysBaseAddr = PhysAddr;
261         RingPtr->HighBdAddr = BdVirtAddr;
262         RingPtr->Length =
263                 RingPtr->HighBdAddr - RingPtr->BaseBdAddr + RingPtr->Separation;
264         RingPtr->AllCnt = BdCount;
265         RingPtr->FreeCnt = BdCount;
266         RingPtr->FreeHead = (XEmacPs_Bd *) VirtAddr;
267         RingPtr->PreHead = (XEmacPs_Bd *) VirtAddr;
268         RingPtr->HwHead = (XEmacPs_Bd *) VirtAddr;
269         RingPtr->HwTail = (XEmacPs_Bd *) VirtAddr;
270         RingPtr->PostHead = (XEmacPs_Bd *) VirtAddr;
271         RingPtr->BdaRestart = (XEmacPs_Bd *) PhysAddr;
272
273         return (XST_SUCCESS);
274 }
275
276
277 /*****************************************************************************/
278 /**
279  * Clone the given BD into every BD in the list.
280  * every field of the source BD is replicated in every BD of the list.
281  *
282  * This function can be called only when all BDs are in the free group such as
283  * they are immediately after initialization with XEmacPs_BdRingCreate().
284  * This prevents modification of BDs while they are in use by hardware or the
285  * user.
286  *
287  * @param RingPtr is the pointer of BD ring instance to be worked on.
288  * @param SrcBdPtr is the source BD template to be cloned into the list. This
289  *        BD will be modified.
290  * @param Direction is either XEMACPS_SEND or XEMACPS_RECV that indicates
291  *        which direction.
292  *
293  * @return
294  *   - XST_SUCCESS if the list was modified.
295  *   - XST_DMA_SG_NO_LIST if a list has not been created.
296  *   - XST_DMA_SG_LIST_ERROR if some of the BDs in this channel are under
297  *     hardware or user control.
298  *   - XST_DEVICE_IS_STARTED if the DMA channel has not been stopped.
299  *
300  *****************************************************************************/
301 int XEmacPs_BdRingClone(XEmacPs_BdRing * RingPtr, XEmacPs_Bd * SrcBdPtr,
302                          u8 Direction)
303 {
304         unsigned i;
305         u32 CurBd;
306
307         /* Can't do this function if there isn't a ring */
308         if (RingPtr->AllCnt == 0) {
309                 return (XST_DMA_SG_NO_LIST);
310         }
311
312         /* Can't do this function with the channel running */
313         if (RingPtr->RunState == XST_DMA_SG_IS_STARTED) {
314                 return (XST_DEVICE_IS_STARTED);
315         }
316
317         /* Can't do this function with some of the BDs in use */
318         if (RingPtr->FreeCnt != RingPtr->AllCnt) {
319                 return (XST_DMA_SG_LIST_ERROR);
320         }
321
322         if ((Direction != XEMACPS_SEND) && (Direction != XEMACPS_RECV)) {
323                 return (XST_INVALID_PARAM);
324         }
325
326         /* Starting from the top of the ring, save BD.Next, overwrite the entire
327          * BD with the template, then restore BD.Next
328          */
329         for (i = 0, CurBd = (u32) RingPtr->BaseBdAddr;
330              i < RingPtr->AllCnt; i++, CurBd += RingPtr->Separation) {
331                 memcpy((void *)CurBd, SrcBdPtr, sizeof(XEmacPs_Bd));
332         }
333
334         CurBd -= RingPtr->Separation;
335
336         if (Direction == XEMACPS_RECV) {
337                 XEmacPs_BdSetRxWrap(CurBd);
338         }
339         else {
340                 XEmacPs_BdSetTxWrap(CurBd);
341         }
342
343         return (XST_SUCCESS);
344 }
345
346
347 /*****************************************************************************/
348 /**
349  * Reserve locations in the BD list. The set of returned BDs may be modified
350  * in preparation for future DMA transaction(s). Once the BDs are ready to be
351  * submitted to hardware, the user must call XEmacPs_BdRingToHw() in the same
352  * order which they were allocated here. Example:
353  *
354  * <pre>
355  *        NumBd = 2;
356  *        Status = XEmacPs_BdRingAlloc(MyRingPtr, NumBd, &MyBdSet);
357  *
358  *        if (Status != XST_SUCCESS)
359  *        {
360  *            // Not enough BDs available for the request
361  *        }
362  *
363  *        CurBd = MyBdSet;
364  *        for (i=0; i<NumBd; i++)
365  *        {
366  *            // Prepare CurBd.....
367  *
368  *            // Onto next BD
369  *            CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd);
370  *        }
371  *
372  *        // Give list to hardware
373  *        Status = XEmacPs_BdRingToHw(MyRingPtr, NumBd, MyBdSet);
374  * </pre>
375  *
376  * A more advanced use of this function may allocate multiple sets of BDs.
377  * They must be allocated and given to hardware in the correct sequence:
378  * <pre>
379  *        // Legal
380  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1);
381  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1);
382  *
383  *        // Legal
384  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1);
385  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2);
386  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1);
387  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2);
388  *
389  *        // Not legal
390  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd1, &MySet1);
391  *        XEmacPs_BdRingAlloc(MyRingPtr, NumBd2, &MySet2);
392  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd2, MySet2);
393  *        XEmacPs_BdRingToHw(MyRingPtr, NumBd1, MySet1);
394  * </pre>
395  *
396  * Use the API defined in xemacps_bd.h to modify individual BDs. Traversal
397  * of the BD set can be done using XEmacPs_BdRingNext() and
398  * XEmacPs_BdRingPrev().
399  *
400  * @param RingPtr is a pointer to the BD ring instance to be worked on.
401  * @param NumBd is the number of BDs to allocate
402  * @param BdSetPtr is an output parameter, it points to the first BD available
403  *        for modification.
404  *
405  * @return
406  *   - XST_SUCCESS if the requested number of BDs was returned in the BdSetPtr
407  *     parameter.
408  *   - XST_FAILURE if there were not enough free BDs to satisfy the request.
409  *
410  * @note This function should not be preempted by another XEmacPs_Bd function
411  *       call that modifies the BD space. It is the caller's responsibility to
412  *       provide a mutual exclusion mechanism.
413  *
414  * @note Do not modify more BDs than the number requested with the NumBd
415  *       parameter. Doing so will lead to data corruption and system
416  *       instability.
417  *
418  *****************************************************************************/
419 int XEmacPs_BdRingAlloc(XEmacPs_BdRing * RingPtr, unsigned NumBd,
420                          XEmacPs_Bd ** BdSetPtr)
421 {
422         /* Enough free BDs available for the request? */
423         if (RingPtr->FreeCnt < NumBd) {
424                 return (XST_FAILURE);
425         }
426
427         /* Set the return argument and move FreeHead forward */
428         *BdSetPtr = RingPtr->FreeHead;
429         XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->FreeHead, NumBd);
430         RingPtr->FreeCnt -= NumBd;
431         RingPtr->PreCnt += NumBd;
432         return (XST_SUCCESS);
433 }
434
435 /*****************************************************************************/
436 /**
437  * Fully or partially undo an XEmacPs_BdRingAlloc() operation. Use this
438  * function if all the BDs allocated by XEmacPs_BdRingAlloc() could not be
439  * transferred to hardware with XEmacPs_BdRingToHw().
440  *
441  * This function helps out in situations when an unrelated error occurs after
442  * BDs have been allocated but before they have been given to hardware.
443  * An example of this type of error would be an OS running out of resources.
444  *
445  * This function is not the same as XEmacPs_BdRingFree(). The Free function
446  * returns BDs to the free list after they have been processed by hardware,
447  * while UnAlloc returns them before being processed by hardware.
448  *
449  * There are two scenarios where this function can be used. Full UnAlloc or
450  * Partial UnAlloc. A Full UnAlloc means all the BDs Alloc'd will be returned:
451  *
452  * <pre>
453  *    Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr);
454  *        ...
455  *    if (Error)
456  *    {
457  *        Status = XEmacPs_BdRingUnAlloc(MyRingPtr, 10, &BdPtr);
458  *    }
459  * </pre>
460  *
461  * A partial UnAlloc means some of the BDs Alloc'd will be returned:
462  *
463  * <pre>
464  *    Status = XEmacPs_BdRingAlloc(MyRingPtr, 10, &BdPtr);
465  *    BdsLeft = 10;
466  *    CurBdPtr = BdPtr;
467  *
468  *    while (BdsLeft)
469  *    {
470  *       if (Error)
471  *       {
472  *          Status = XEmacPs_BdRingUnAlloc(MyRingPtr, BdsLeft, CurBdPtr);
473  *       }
474  *
475  *       CurBdPtr = XEmacPs_BdRingNext(MyRingPtr, CurBdPtr);
476  *       BdsLeft--;
477  *    }
478  * </pre>
479  *
480  * A partial UnAlloc must include the last BD in the list that was Alloc'd.
481  *
482  * @param RingPtr is a pointer to the instance to be worked on.
483  * @param NumBd is the number of BDs to allocate
484  * @param BdSetPtr is an output parameter, it points to the first BD available
485  *        for modification.
486  *
487  * @return
488  *   - XST_SUCCESS if the BDs were unallocated.
489  *   - XST_FAILURE if NumBd parameter was greater that the number of BDs in
490  *     the preprocessing state.
491  *
492  * @note This function should not be preempted by another XEmacPs_Bd function
493  *       call that modifies the BD space. It is the caller's responsibility to
494  *       provide a mutual exclusion mechanism.
495  *
496  *****************************************************************************/
497 int XEmacPs_BdRingUnAlloc(XEmacPs_BdRing * RingPtr, unsigned NumBd,
498                            XEmacPs_Bd * BdSetPtr)
499 {
500         (void)BdSetPtr;
501
502         /* Enough BDs in the free state for the request? */
503         if (RingPtr->PreCnt < NumBd) {
504                 return (XST_FAILURE);
505         }
506
507         /* Set the return argument and move FreeHead backward */
508         XEMACPS_RING_SEEKBACK(RingPtr, RingPtr->FreeHead, NumBd);
509         RingPtr->FreeCnt += NumBd;
510         RingPtr->PreCnt -= NumBd;
511         return (XST_SUCCESS);
512 }
513
514
515 /*****************************************************************************/
516 /**
517  * Enqueue a set of BDs to hardware that were previously allocated by
518  * XEmacPs_BdRingAlloc(). Once this function returns, the argument BD set goes
519  * under hardware control. Any changes made to these BDs after this point will
520  * corrupt the BD list leading to data corruption and system instability.
521  *
522  * The set will be rejected if the last BD of the set does not mark the end of
523  * a packet (see XEmacPs_BdSetLast()).
524  *
525  * @param RingPtr is a pointer to the instance to be worked on.
526  * @param NumBd is the number of BDs in the set.
527  * @param BdSetPtr is the first BD of the set to commit to hardware.
528  *
529  * @return
530  *   - XST_SUCCESS if the set of BDs was accepted and enqueued to hardware.
531  *   - XST_FAILURE if the set of BDs was rejected because the last BD of the set
532  *     did not have its "last" bit set.
533  *   - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with
534  *     XEmacPs_BdRingAlloc().
535  *
536  * @note This function should not be preempted by another XEmacPs_Bd function
537  *       call that modifies the BD space. It is the caller's responsibility to
538  *       provide a mutual exclusion mechanism.
539  *
540  *****************************************************************************/
541 int XEmacPs_BdRingToHw(XEmacPs_BdRing * RingPtr, unsigned NumBd,
542                         XEmacPs_Bd * BdSetPtr)
543 {
544         XEmacPs_Bd *CurBdPtr;
545         unsigned i;
546
547         /* if no bds to process, simply return. */
548         if (0 == NumBd)
549                 return (XST_SUCCESS);
550
551         /* Make sure we are in sync with XEmacPs_BdRingAlloc() */
552         if ((RingPtr->PreCnt < NumBd) || (RingPtr->PreHead != BdSetPtr)) {
553                 return (XST_DMA_SG_LIST_ERROR);
554         }
555
556         CurBdPtr = BdSetPtr;
557         for (i = 0; i < NumBd; i++) {
558                 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
559         }
560
561         /* Adjust ring pointers & counters */
562         XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->PreHead, NumBd);
563         RingPtr->PreCnt -= NumBd;
564
565         RingPtr->HwTail = CurBdPtr;
566         RingPtr->HwCnt += NumBd;
567
568         return (XST_SUCCESS);
569 }
570
571
572 /*****************************************************************************/
573 /**
574  * Returns a set of BD(s) that have been processed by hardware. The returned
575  * BDs may be examined to determine the outcome of the DMA transaction(s).
576  * Once the BDs have been examined, the user must call XEmacPs_BdRingFree()
577  * in the same order which they were retrieved here. Example:
578  *
579  * <pre>
580  *        NumBd = XEmacPs_BdRingFromHwTx(MyRingPtr, MaxBd, &MyBdSet);
581  *
582  *        if (NumBd == 0)
583  *        {
584  *           // hardware has nothing ready for us yet
585  *        }
586  *
587  *        CurBd = MyBdSet;
588  *        for (i=0; i<NumBd; i++)
589  *        {
590  *           // Examine CurBd for post processing.....
591  *
592  *           // Onto next BD
593  *           CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd);
594  *           }
595  *
596  *           XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet); // Return list
597  *        }
598  * </pre>
599  *
600  * A more advanced use of this function may allocate multiple sets of BDs.
601  * They must be retrieved from hardware and freed in the correct sequence:
602  * <pre>
603  *        // Legal
604  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1);
605  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1);
606  *
607  *        // Legal
608  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1);
609  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2);
610  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1);
611  *        XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2);
612  *
613  *        // Not legal
614  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd1, &MySet1);
615  *        XEmacPs_BdRingFromHwTx(MyRingPtr, NumBd2, &MySet2);
616  *        XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2);
617  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1);
618  * </pre>
619  *
620  * If hardware has only partially completed a packet spanning multiple BDs,
621  * then none of the BDs for that packet will be included in the results.
622  *
623  * @param RingPtr is a pointer to the instance to be worked on.
624  * @param BdLimit is the maximum number of BDs to return in the set.
625  * @param BdSetPtr is an output parameter, it points to the first BD available
626  *        for examination.
627  *
628  * @return
629  *   The number of BDs processed by hardware. A value of 0 indicates that no
630  *   data is available. No more than BdLimit BDs will be returned.
631  *
632  * @note Treat BDs returned by this function as read-only.
633  *
634  * @note This function should not be preempted by another XEmacPs_Bd function
635  *       call that modifies the BD space. It is the caller's responsibility to
636  *       provide a mutual exclusion mechanism.
637  *
638  *****************************************************************************/
639 unsigned XEmacPs_BdRingFromHwTx(XEmacPs_BdRing * RingPtr, unsigned BdLimit,
640                                  XEmacPs_Bd ** BdSetPtr)
641 {
642         XEmacPs_Bd *CurBdPtr;
643         u32 BdStr = 0;
644         unsigned BdCount;
645         unsigned BdPartialCount;
646         unsigned int Sop = 0;
647
648
649         CurBdPtr = RingPtr->HwHead;
650         BdCount = 0;
651         BdPartialCount = 0;
652
653         /* If no BDs in work group, then there's nothing to search */
654         if (RingPtr->HwCnt == 0) {
655                 *BdSetPtr = NULL;
656                 return (0);
657         }
658
659         if (BdLimit > RingPtr->HwCnt)
660                 BdLimit = RingPtr->HwCnt;
661
662         /* Starting at HwHead, keep moving forward in the list until:
663          *  - A BD is encountered with its new/used bit set which means
664          *    hardware has not completed processing of that BD.
665          *  - RingPtr->HwTail is reached and RingPtr->HwCnt is reached.
666          *  - The number of requested BDs has been processed
667          */
668         while (BdCount < BdLimit) {
669                 /* Read the status */
670                 BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET);
671
672                 if ((Sop == 0) && (BdStr & XEMACPS_TXBUF_USED_MASK))
673                         Sop = 1;
674
675                 if (Sop == 1) {
676                         BdCount++;
677                         BdPartialCount++;
678                 }
679
680                 /* hardware has processed this BD so check the "last" bit.
681                  * If it is clear, then there are more BDs for the current
682                  * packet. Keep a count of these partial packet BDs.
683                  */
684                 if ((Sop == 1) && (BdStr & XEMACPS_TXBUF_LAST_MASK)) {
685                         Sop = 0;
686                         BdPartialCount = 0;
687                 }
688
689                 /* Move on to next BD in work group */
690                 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
691         }
692
693         /* Subtract off any partial packet BDs found */
694         BdCount -= BdPartialCount;
695
696         /* If BdCount is non-zero then BDs were found to return. Set return
697          * parameters, update pointers and counters, return success
698          */
699         if (BdCount > 0) {
700                 *BdSetPtr = RingPtr->HwHead;
701                 RingPtr->HwCnt -= BdCount;
702                 RingPtr->PostCnt += BdCount;
703                 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
704                 return (BdCount);
705         }
706         else {
707                 *BdSetPtr = NULL;
708                 return (0);
709         }
710 }
711
712
713 /*****************************************************************************/
714 /**
715  * Returns a set of BD(s) that have been processed by hardware. The returned
716  * BDs may be examined to determine the outcome of the DMA transaction(s).
717  * Once the BDs have been examined, the user must call XEmacPs_BdRingFree()
718  * in the same order which they were retrieved here. Example:
719  *
720  * <pre>
721  *        NumBd = XEmacPs_BdRingFromHwRx(MyRingPtr, MaxBd, &MyBdSet);
722  *
723  *        if (NumBd == 0)
724  *        {
725  *           // hardware has nothing ready for us yet
726  *        }
727  *
728  *        CurBd = MyBdSet;
729  *        for (i=0; i<NumBd; i++)
730  *        {
731  *           // Examine CurBd for post processing.....
732  *
733  *           // Onto next BD
734  *           CurBd = XEmacPs_BdRingNext(MyRingPtr, CurBd);
735  *           }
736  *
737  *           XEmacPs_BdRingFree(MyRingPtr, NumBd, MyBdSet); // Return list
738  *        }
739  * </pre>
740  *
741  * A more advanced use of this function may allocate multiple sets of BDs.
742  * They must be retrieved from hardware and freed in the correct sequence:
743  * <pre>
744  *        // Legal
745  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1);
746  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1);
747  *
748  *        // Legal
749  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1);
750  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2);
751  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1);
752  *        XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2);
753  *
754  *        // Not legal
755  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd1, &MySet1);
756  *        XEmacPs_BdRingFromHwRx(MyRingPtr, NumBd2, &MySet2);
757  *        XEmacPs_BdRingFree(MyRingPtr, NumBd2, MySet2);
758  *        XEmacPs_BdRingFree(MyRingPtr, NumBd1, MySet1);
759  * </pre>
760  *
761  * If hardware has only partially completed a packet spanning multiple BDs,
762  * then none of the BDs for that packet will be included in the results.
763  *
764  * @param RingPtr is a pointer to the instance to be worked on.
765  * @param BdLimit is the maximum number of BDs to return in the set.
766  * @param BdSetPtr is an output parameter, it points to the first BD available
767  *        for examination.
768  *
769  * @return
770  *   The number of BDs processed by hardware. A value of 0 indicates that no
771  *   data is available. No more than BdLimit BDs will be returned.
772  *
773  * @note Treat BDs returned by this function as read-only.
774  *
775  * @note This function should not be preempted by another XEmacPs_Bd function
776  *       call that modifies the BD space. It is the caller's responsibility to
777  *       provide a mutual exclusion mechanism.
778  *
779  *****************************************************************************/
780 unsigned XEmacPs_BdRingFromHwRx(XEmacPs_BdRing * RingPtr, unsigned BdLimit,
781                                  XEmacPs_Bd ** BdSetPtr)
782 {
783         XEmacPs_Bd *CurBdPtr;
784         u32 BdStr = 0;
785         unsigned BdCount;
786         unsigned BdPartialCount;
787
788         CurBdPtr = RingPtr->HwHead;
789         BdCount = 0;
790         BdPartialCount = 0;
791
792         /* If no BDs in work group, then there's nothing to search */
793         if (RingPtr->HwCnt == 0) {
794                 *BdSetPtr = NULL;
795                 return (0);
796         }
797
798         /* Starting at HwHead, keep moving forward in the list until:
799          *  - A BD is encountered with its new/used bit set which means
800          *    hardware has completed processing of that BD.
801          *  - RingPtr->HwTail is reached and RingPtr->HwCnt is reached.
802          *  - The number of requested BDs has been processed
803          */
804         while (BdCount < BdLimit) {
805
806                 /* Read the status */
807                 BdStr = XEmacPs_BdRead(CurBdPtr, XEMACPS_BD_STAT_OFFSET);
808
809                 if (!(XEmacPs_BdIsRxNew(CurBdPtr))) {
810                         break;
811                 }
812
813                 BdCount++;
814
815                 /* hardware has processed this BD so check the "last" bit. If
816                  * it is clear, then there are more BDs for the current packet.
817                  * Keep a count of these partial packet BDs.
818                  */
819                 if (BdStr & XEMACPS_RXBUF_EOF_MASK) {
820                         BdPartialCount = 0;
821                 }
822                 else {
823                         BdPartialCount++;
824                 }
825
826                 /* Move on to next BD in work group */
827                 CurBdPtr = XEmacPs_BdRingNext(RingPtr, CurBdPtr);
828         }
829
830         /* Subtract off any partial packet BDs found */
831         BdCount -= BdPartialCount;
832
833         /* If BdCount is non-zero then BDs were found to return. Set return
834          * parameters, update pointers and counters, return success
835          */
836         if (BdCount > 0) {
837                 *BdSetPtr = RingPtr->HwHead;
838                 RingPtr->HwCnt -= BdCount;
839                 RingPtr->PostCnt += BdCount;
840                 XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->HwHead, BdCount);
841                 return (BdCount);
842         }
843         else {
844                 *BdSetPtr = NULL;
845                 return (0);
846         }
847 }
848
849
850 /*****************************************************************************/
851 /**
852  * Frees a set of BDs that had been previously retrieved with
853  * XEmacPs_BdRingFromHw().
854  *
855  * @param RingPtr is a pointer to the instance to be worked on.
856  * @param NumBd is the number of BDs to free.
857  * @param BdSetPtr is the head of a list of BDs returned by
858  * XEmacPs_BdRingFromHw().
859  *
860  * @return
861  *   - XST_SUCCESS if the set of BDs was freed.
862  *   - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with
863  *     XEmacPs_BdRingFromHw().
864  *
865  * @note This function should not be preempted by another XEmacPs_Bd function
866  *       call that modifies the BD space. It is the caller's responsibility to
867  *       provide a mutual exclusion mechanism.
868  *
869  *****************************************************************************/
870 int XEmacPs_BdRingFree(XEmacPs_BdRing * RingPtr, unsigned NumBd,
871                         XEmacPs_Bd * BdSetPtr)
872 {
873         /* if no bds to process, simply return. */
874         if (0 == NumBd)
875                 return (XST_SUCCESS);
876
877         /* Make sure we are in sync with XEmacPs_BdRingFromHw() */
878         if ((RingPtr->PostCnt < NumBd) || (RingPtr->PostHead != BdSetPtr)) {
879                 return (XST_DMA_SG_LIST_ERROR);
880         }
881
882         /* Update pointers and counters */
883         RingPtr->FreeCnt += NumBd;
884         RingPtr->PostCnt -= NumBd;
885         XEMACPS_RING_SEEKAHEAD(RingPtr, RingPtr->PostHead, NumBd);
886         return (XST_SUCCESS);
887 }
888
889
890 /*****************************************************************************/
891 /**
892  * Check the internal data structures of the BD ring for the provided channel.
893  * The following checks are made:
894  *
895  *   - Is the BD ring linked correctly in physical address space.
896  *   - Do the internal pointers point to BDs in the ring.
897  *   - Do the internal counters add up.
898  *
899  * The channel should be stopped prior to calling this function.
900  *
901  * @param RingPtr is a pointer to the instance to be worked on.
902  * @param Direction is either XEMACPS_SEND or XEMACPS_RECV that indicates
903  *        which direction.
904  *
905  * @return
906  *   - XST_SUCCESS if the set of BDs was freed.
907  *   - XST_DMA_SG_NO_LIST if the list has not been created.
908  *   - XST_IS_STARTED if the channel is not stopped.
909  *   - XST_DMA_SG_LIST_ERROR if a problem is found with the internal data
910  *     structures. If this value is returned, the channel should be reset to
911  *     avoid data corruption or system instability.
912  *
913  * @note This function should not be preempted by another XEmacPs_Bd function
914  *       call that modifies the BD space. It is the caller's responsibility to
915  *       provide a mutual exclusion mechanism.
916  *
917  *****************************************************************************/
918 int XEmacPs_BdRingCheck(XEmacPs_BdRing * RingPtr, u8 Direction)
919 {
920         u32 AddrV, AddrP;
921         unsigned i;
922
923         if ((Direction != XEMACPS_SEND) && (Direction != XEMACPS_RECV)) {
924                 return (XST_INVALID_PARAM);
925         }
926
927         /* Is the list created */
928         if (RingPtr->AllCnt == 0) {
929                 return (XST_DMA_SG_NO_LIST);
930         }
931
932         /* Can't check if channel is running */
933         if (RingPtr->RunState == XST_DMA_SG_IS_STARTED) {
934                 return (XST_IS_STARTED);
935         }
936
937         /* RunState doesn't make sense */
938         else if (RingPtr->RunState != XST_DMA_SG_IS_STOPPED) {
939                 return (XST_DMA_SG_LIST_ERROR);
940         }
941
942         /* Verify internal pointers point to correct memory space */
943         AddrV = (u32) RingPtr->FreeHead;
944         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
945                 return (XST_DMA_SG_LIST_ERROR);
946         }
947
948         AddrV = (u32) RingPtr->PreHead;
949         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
950                 return (XST_DMA_SG_LIST_ERROR);
951         }
952
953         AddrV = (u32) RingPtr->HwHead;
954         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
955                 return (XST_DMA_SG_LIST_ERROR);
956         }
957
958         AddrV = (u32) RingPtr->HwTail;
959         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
960                 return (XST_DMA_SG_LIST_ERROR);
961         }
962
963         AddrV = (u32) RingPtr->PostHead;
964         if ((AddrV < RingPtr->BaseBdAddr) || (AddrV > RingPtr->HighBdAddr)) {
965                 return (XST_DMA_SG_LIST_ERROR);
966         }
967
968         /* Verify internal counters add up */
969         if ((RingPtr->HwCnt + RingPtr->PreCnt + RingPtr->FreeCnt +
970              RingPtr->PostCnt) != RingPtr->AllCnt) {
971                 return (XST_DMA_SG_LIST_ERROR);
972         }
973
974         /* Verify BDs are linked correctly */
975         AddrV = RingPtr->BaseBdAddr;
976         AddrP = RingPtr->PhysBaseAddr + RingPtr->Separation;
977
978         for (i = 1; i < RingPtr->AllCnt; i++) {
979                 /* Check BDA for this BD. It should point to next physical addr */
980                 if (XEmacPs_BdRead(AddrV, XEMACPS_BD_ADDR_OFFSET) != AddrP) {
981                         return (XST_DMA_SG_LIST_ERROR);
982                 }
983
984                 /* Move on to next BD */
985                 AddrV += RingPtr->Separation;
986                 AddrP += RingPtr->Separation;
987         }
988
989         /* Last BD should have wrap bit set */
990         if (XEMACPS_SEND == Direction) {
991                 if (!XEmacPs_BdIsTxWrap(AddrV)) {
992                         return (XST_DMA_SG_LIST_ERROR);
993                 }
994         }
995         else {                  /* XEMACPS_RECV */
996                 if (!XEmacPs_BdIsRxWrap(AddrV)) {
997                         return (XST_DMA_SG_LIST_ERROR);
998                 }
999         }
1000
1001         /* No problems found */
1002         return (XST_SUCCESS);
1003 }
1004 /** @} */