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