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