/** * Clone the given BD into every BD in the list. Except for XDMAV2_BD_BDA_OFFSET, * every field of the source BD is replicated in every BD of the list. * * This function can be called only when all BDs are in the free group such as * they are immediately after initialization with XDmaV2_SgListCreate(). This * prevents modification of BDs while they are in use by HW or the user. * * @param InstancePtr is the instance to be worked on. * @param SrcBdPtr is the source BD to be cloned into the list. * * @return * - XST_SUCCESS if the list was modified. * - XST_DMA_SG_NO_LIST if a list has not been created. * - XST_DMA_SG_LIST_ERROR if some of the BDs in this channel are under HW * or user control. * - XST_DEVICE_IS_STARTED if the DMA channel has not been stopped. * ******************************************************************************/ XStatus XDmaV2_SgListClone(XDmaV2 * InstancePtr, XDmaBdV2 * SrcBdPtr) { u32 CurBd; u32 BdaSave; XDmaV2_BdRing *Ring = &InstancePtr->BdRing; /* Can't do this function if there isn't a ring */ if (Ring->AllCnt == 0) { return (XST_DMA_SG_NO_LIST); } /* Can't do this function with the channel running */ if (Ring->RunState == XST_DMA_SG_IS_STARTED) { return (XST_DEVICE_IS_STARTED); } /* Can't do this function with some of the BDs in use */ if (Ring->FreeCnt != Ring->AllCnt) { return (XST_DMA_SG_LIST_ERROR); } /* Starting from the top, save BD.Next, overwrite the entire BD with the * template, then restore BD.Next */ for (CurBd = Ring->BaseAddr; CurBd <= Ring->HighAddr; CurBd += Ring->Separation) { BdaSave = XDmaV2_mReadBd(CurBd, XDMAV2_BD_BDA_OFFSET); memcpy((void *)CurBd, SrcBdPtr, sizeof(XDmaBdV2)); XDmaV2_mWriteBd(CurBd, XDMAV2_BD_BDA_OFFSET, BdaSave); } return (XST_SUCCESS); }
/** * Verify the consistency of the SGDMA BD ring. While the check occurs, the * device is stopped. If any problems are found the device is left stopped. * * Use this function to troubleshoot SGDMA problems. * * @param InstancePtr is a pointer to the instance to be worked on. * @param Direction is the channel to check (XTE_SEND or XTE_RECV) * * @return * - XST_SUCCESS if no problems are found. * - XST_INVALID_PARAM if Direction is not XTE_SEND or XTE_RECV. * - XST_DMA_SG_NO_LIST if the SG list has not yet been setup. * - XST_DMA_BD_ERROR if a BD has been corrupted. * - XST_DMA_SG_LIST_ERROR if the internal data structures of the BD ring are * inconsistent. * * @note * This function is not thread-safe. The user must provide mutually exclusive * access to this function if there are to be multiple threads that can call it. * ******************************************************************************/ XStatus XTemac_SgCheck(XTemac * InstancePtr, u32 Direction) { XDmaV2 *DmaPtr; XDmaBdV2 *BdPtr; int i; int Restart = 0; u32 Dmacr; XStatus Rc = XST_SUCCESS; XASSERT_NONVOID(InstancePtr != NULL); /* Select channel to check */ if (Direction == XTE_SEND) { DmaPtr = &InstancePtr->SendDma; } else if (Direction == XTE_RECV) { DmaPtr = &InstancePtr->RecvDma; } else { return (XST_INVALID_PARAM); } /* Stop the device if it is running */ if (InstancePtr->IsStarted == XST_DEVICE_IS_STARTED) { XTemac_Stop(InstancePtr); Restart = 1; } /* Perform check of ring structure using DMA driver routine */ Rc = XDmaV2_SgCheck(DmaPtr); if (Rc == XST_SUCCESS) { /* Check the BDs for consistency as used by TEMAC */ if (Direction == XTE_SEND) { /* Starting from the beginning of the ring and going to the end: * 1. Verify the destination address is the packet fifo * 2. Verify DMACR is setup for the transmit direction */ BdPtr = (XDmaBdV2 *) DmaPtr->BdRing.BaseAddr; for (i = 0; i < DmaPtr->BdRing.AllCnt; i++) { /* Dest address should be address of packet fifo */ if (XDmaBdV2_mGetDestAddr(BdPtr) != InstancePtr->Config->BaseAddress + XTE_PFIFO_TXDATA_OFFSET) { Rc = XST_DMA_BD_ERROR; break; } Dmacr = XDmaV2_mReadBd(BdPtr, XDMAV2_BD_DMACR_OFFSET); /* DMACR field should have these bits set */ if (Dmacr != (Dmacr & (XDMAV2_DMACR_DLOCAL_MASK | XDMAV2_DMACR_SINC_MASK))) { Rc = XST_DMA_BD_ERROR; break; } /* DMACR field should have these bits clear */ if (Dmacr & (XDMAV2_DMACR_SLOCAL_MASK | XDMAV2_DMACR_DINC_MASK)) { Rc = XST_DMA_BD_ERROR; break; } /* Next BD */ BdPtr = XDmaV2_mSgBdNext(DmaPtr, BdPtr); } } else { /* XTE_RECV */ /* Starting from the beginning of the ring and going to the end: * 1. Verify the source address is the packet fifo * 2. Verify DMACR is setup for the receive direction */ BdPtr = (XDmaBdV2 *) DmaPtr->BdRing.BaseAddr; for (i = 0; i < DmaPtr->BdRing.AllCnt; i++) { /* Src address should be address of packet fifo */ if (XDmaBdV2_mGetSrcAddr(BdPtr) != InstancePtr->Config->BaseAddress + XTE_PFIFO_RXDATA_OFFSET) { Rc = XST_DMA_BD_ERROR; break; } Dmacr = XDmaV2_mReadBd(BdPtr, XDMAV2_BD_DMACR_OFFSET); /* DMACR field should have these bits set */ if (Dmacr != (Dmacr & (XDMAV2_DMACR_SLOCAL_MASK | XDMAV2_DMACR_DINC_MASK))) { Rc = XST_DMA_BD_ERROR; break; } /* DMACR field should have these bits clear */ if (Dmacr & (XDMAV2_DMACR_DLOCAL_MASK | XDMAV2_DMACR_SINC_MASK)) { Rc = XST_DMA_BD_ERROR; break; } /* Next BD */ BdPtr = XDmaV2_mSgBdNext(DmaPtr, BdPtr); } } } /* Restart the device if it was stopped by this function */ if ((Rc == XST_SUCCESS) && Restart) { XTemac_Start(InstancePtr); } return (Rc); }
/** * Returns a set of BD(s) that have been processed by HW. The returned BDs may * be examined to determine the outcome of the DMA transaction(s). Once the BDs * have been examined, the user must call XDmaV2_SgBdFree() in the same order * which they were retrieved here. Example: * * <pre> * NumBd = 0xFFFFFFFF; // Ensure we get all that are ready * * Status = XDmaV2_SgBdFromHw(MyDmaInstPtr, &NumBd, &MyBdSet); * * if (Status != XST_SUCCESS) * { * // HW has nothing ready for us yet * } * * CurBd = MyBdSet; * for (i=0; i<NumBd; i++) * { * // Examine CurBd for post processing..... * * // Onto next BD * CurBd = XDmaV2_mSgBdNext(MyDmaInstPtr, CurBd); * } * * XDmaV2_SgBdFree(MyDmaInstPtr, NumBd, MyBdSet); // Return the list * } * </pre> * * A more advanced use of this function may allocate multiple sets of BDs. * They must be retrieved from HW and freed in the correct sequence: * <pre> * // Legal * XDmaV2_SgBdFromHw(MyDmaInstPtr, NumBd1, &MySet1); * XDmaV2_SgBdFree(MyDmaInstPtr, NumBd1, MySet1); * * // Legal * XDmaV2_SgBdFromHw(MyDmaInstPtr, NumBd1, &MySet1); * XDmaV2_SgBdFromHw(MyDmaInstPtr, NumBd2, &MySet2); * XDmaV2_SgBdFree(MyDmaInstPtr, NumBd1, MySet1); * XDmaV2_SgBdFree(MyDmaInstPtr, NumBd2, MySet2); * * // Not legal * XDmaV2_SgBdFromHw(MyDmaInstPtr, NumBd1, &MySet1); * XDmaV2_SgBdFromHw(MyDmaInstPtr, NumBd2, &MySet2); * XDmaV2_SgBdFree(MyDmaInstPtr, NumBd2, MySet2); * XDmaV2_SgBdFree(MyDmaInstPtr, NumBd1, MySet1); * </pre> * * If HW has only partially completed a packet spanning multiple BDs, then none * of the BDs for that packet will be included in the results. * * @param InstancePtr is a pointer to the instance to be worked on. * @param NumBd is the maximum number of BDs to return in the set. * @param BdSetPtr is an output parameter, it points to the first BD available * for examination. * * @return * The number of BDs processed by HW. A value of 0 indicates that no data * is available. * * @note Treat BDs returned by this function as read-only. * * @note This function should not be preempted by another XDmaV2 function call * that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * ******************************************************************************/ unsigned XDmaV2_SgBdFromHw(XDmaV2 * InstancePtr, unsigned NumBd, XDmaBdV2 ** BdSetPtr) { XDmaV2_BdRing *Ring = &InstancePtr->BdRing; XDmaBdV2 *CurBd; unsigned BdCount; unsigned PktCount; unsigned BdPartialCount; unsigned BdLimit = NumBd; u32 Dmasr; u32 Upc; CurBd = Ring->HwHead; PktCount = 0; BdCount = 0; BdPartialCount = 0; /* If no BDs in work group, then there's nothing to search */ if (Ring->HwCnt == 0) { *BdSetPtr = NULL; return (0); } /* Get the number of packets HW is reporting completed */ Upc = XDmaV2_mReadReg(InstancePtr->RegBase, XDMAV2_UPC_OFFSET); /* Starting at HwHead, keep moving forward in the list until: * - A BD is encountered with its DMASR.DMABSY bit set which means HW has * not completed processing of that BD. * - We've processed the number of packets HW has reported completed * - Ring->HwTail is reached * - The number of requested BDs has been processed */ while (BdCount < BdLimit) { /* Read the status */ XDMAV2_CACHE_INVALIDATE(CurBd); Dmasr = XDmaV2_mReadBd(CurBd, XDMAV2_BD_DMASR_OFFSET); /* If the HW still hasn't processed this BD then we are done */ if (Dmasr & XDMAV2_DMASR_DMABSY_MASK) { break; } BdCount++; /* HW has processed this BD so check the "last" bit. If it is clear, * then there are more BDs for the current packet. Keep a count of * these partial packet BDs. */ if (Dmasr & XDMAV2_DMASR_L_MASK) { /* Tell HW a packet has been serviced */ XDmaV2_mWriteReg(InstancePtr->RegBase, XDMAV2_UPC_OFFSET, 1); BdPartialCount = 0; /* We are done if we have serviced the number of packets HW has * reported completed. */ if (++PktCount == Upc) { break; } } else { BdPartialCount++; } /* Reached the end of the work group */ if (CurBd == Ring->HwTail) { break; } /* Move on to next BD in work group */ CurBd = XDmaV2_mSgBdNext(InstancePtr, CurBd); } /* Subtract off any partial packet BDs found */ BdCount -= BdPartialCount; /* If BdCount is non-zero then BDs were found to return. Set return * parameters, update pointers and counters, return success */ if (BdCount) { *BdSetPtr = Ring->HwHead; Ring->HwCnt -= BdCount; Ring->PostCnt += BdCount; XDMAV2_RING_SEEKAHEAD(Ring, Ring->HwHead, BdCount); return (BdCount); } else { *BdSetPtr = NULL; return (0); } }
/** * Enqueue a set of BDs to HW that were previously allocated by * XDmaV2_SgBdAlloc(). Once this function returns, the argument BD set goes * under HW control. Any changes made to these BDs after this point will corrupt * the BD list leading to data corruption and system instability. * * The set will be rejected if the last BD of the set does not mark the end of * a packet (see XDmaBdV2_mSetLast()). * * @param InstancePtr is a pointer to the instance to be worked on. * @param NumBd is the number of BDs in the set. * @param BdSetPtr is the first BD of the set to commit to HW. * * @return * - XST_SUCCESS if the set of BDs was accepted and enqueued to HW. * - XST_FAILURE if the set of BDs was rejected because the last BD of the set * did not have its "last" bit set. * - XST_DMA_SG_LIST_ERROR if this function was called out of sequence with * XDmaV2_SgBdAlloc(). * * @note This function should not be preempted by another XDmaV2 function call * that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * ******************************************************************************/ XStatus XDmaV2_SgBdToHw(XDmaV2 * InstancePtr, unsigned NumBd, XDmaBdV2 * BdSetPtr) { XDmaV2_BdRing *Ring = &InstancePtr->BdRing; XDmaBdV2 *LastBdPtr; int i; u32 Dmacr; u32 Swcr; /* Make sure we are in sync with XDmaV2_SgBdAlloc() */ if ((Ring->PreCnt < NumBd) || (Ring->PreHead != BdSetPtr)) { return (XST_DMA_SG_LIST_ERROR); } /* For all BDs in this set (except the last one) * - Clear DMASR except for DMASR.DMABSY * - Clear DMACR.SGS * * For the last BD in this set * - Clear DMASR except for DMASR.DMABSY * - Set DMACR.SGS */ LastBdPtr = BdSetPtr; for (i = 1; i < NumBd; i++) { XDmaV2_mWriteBd(LastBdPtr, XDMAV2_BD_DMASR_OFFSET, XDMAV2_DMASR_DMABSY_MASK); Dmacr = XDmaV2_mReadBd(LastBdPtr, XDMAV2_BD_DMACR_OFFSET); XDmaV2_mWriteBd(LastBdPtr, XDMAV2_BD_DMACR_OFFSET, /* DMACR.SGS = 0 */ Dmacr & ~XDMAV2_DMACR_SGS_MASK); XDMAV2_CACHE_FLUSH(LastBdPtr); LastBdPtr = XDmaV2_mSgBdNext(InstancePtr, LastBdPtr); } /* Last BD */ XDmaV2_mWriteBd(LastBdPtr, XDMAV2_BD_DMASR_OFFSET, XDMAV2_DMASR_DMABSY_MASK); Dmacr = XDmaV2_mReadBd(LastBdPtr, XDMAV2_BD_DMACR_OFFSET); XDmaV2_mWriteBd(LastBdPtr, XDMAV2_BD_DMACR_OFFSET, /* DMACR.SGS = 1 */ Dmacr | XDMAV2_DMACR_SGS_MASK); XDMAV2_CACHE_FLUSH(LastBdPtr); /* The last BD should have DMACR.LAST set */ if (!(Dmacr & XDMAV2_DMACR_L_MASK)) { return (XST_FAILURE); } /* This set has completed pre-processing, adjust ring pointers & counters */ XDMAV2_RING_SEEKAHEAD(Ring, Ring->PreHead, NumBd); Ring->PreCnt -= NumBd; /* This set is now ready to be added to the work group. * * Case 1: If there are no BDs in the list, then we know HW is idle, simply * reset the list to begin and end on the current BD set */ if (Ring->HwCnt == 0) { /* Update pointers and counters. XDMAV2_RING_SEEKAHEAD could be used to * advance HwTail, but it will always evaluate to LastBdPtr */ Ring->HwTail = LastBdPtr; Ring->HwCnt += NumBd; /* HW DMACR.SGS = 0 */ XDMAV2_HW_SGS_CLEAR; } /* Case 2: There are BDs in the work group so we extend it in such a way * that the channel doesn't need to be stopped */ else { /* Extend the work list: HwTail->DMACR.SGS = 0 */ Dmacr = XDmaV2_mReadBd(Ring->HwTail, XDMAV2_BD_DMACR_OFFSET); XDmaV2_mWriteBd(Ring->HwTail, XDMAV2_BD_DMACR_OFFSET, Dmacr & ~XDMAV2_DMACR_SGS_MASK); XDMAV2_CACHE_FLUSH(Ring->HwTail); /* Update pointers and counters. HwTail now points to a new BD. */ Ring->HwTail = LastBdPtr; Ring->HwCnt += NumBd; /* HW DMACR.SGS = 0 (with conditions) * * Must be careful here, the HW SGS may be set because it encountered * the end of the work list before we extended it. If that is the * situation, then clear it as we did in Case 1 so HW will see the * BDs just added. * * The HW SGS bit may also be set because it has finished processing * the set of BDs we just added! If that is the case then do nothing. * * If the HW SGS is clear, then HW is actively processing BDs so it will * continue on getting to the list we just added. */ Dmacr = XDmaV2_mReadReg(InstancePtr->RegBase, XDMAV2_DMACR_OFFSET); if (Dmacr & XDMAV2_DMACR_SGS_MASK) { /* Did HW just finish processing what we added? This is checked by * comparing the BDA register with HwTail. If they are the same * then the channel has loaded the HwTail BD so we shouldn't enable * the engine by setting SGS = 0. */ XDMAV2_CACHE_INVALIDATE(Ring->HwTail); if (XDMAV2_VIRT_TO_PHYS(Ring, Ring->HwTail) != XDmaV2_mReadReg(InstancePtr->RegBase, XDMAV2_BDA_OFFSET)) { /* No, SGS = 0 */ XDmaV2_mWriteReg(InstancePtr->RegBase, XDMAV2_DMACR_OFFSET, Dmacr & ~XDMAV2_DMACR_SGS_MASK); } } } /* If the channel was in a running state, then keep it that way. It may have * stopped because DMACR.SGS got set for any reason. */ if (Ring->RunState == XST_DMA_SG_IS_STARTED) { /* If SWCR.SGE was 0, then set it to 1 */ Swcr = XDmaV2_mReadReg(InstancePtr->RegBase, XDMAV2_SWCR_OFFSET); if (!(Swcr & XDMAV2_SWCR_SGE_MASK)) { XDmaV2_mWriteReg(InstancePtr->RegBase, XDMAV2_SWCR_OFFSET, Swcr | XDMAV2_SWCR_SGE_MASK); } } return (XST_SUCCESS); }
/** * Start the SGDMA channel. * * @param InstancePtr is a pointer to the instance to be started. * * @return * - XST_SUCCESS if channel was started. * - XST_DMA_SG_NO_LIST if buffer descriptor space has not been assigned to * the channel. See XDmaV2_SgListCreate(). * ******************************************************************************/ XStatus XDmaV2_SgStart(XDmaV2 * InstancePtr) { XDmaV2_BdRing *Ring = &InstancePtr->BdRing; u32 BdaV; int i; /* BD list has yet to be created for this channel */ if (Ring->AllCnt == 0) { return (XST_DMA_SG_NO_LIST); } /* Do nothing if already started */ if (Ring->RunState == XST_DMA_SG_IS_STARTED) { return (XST_SUCCESS); } /* Note as started */ Ring->RunState = XST_DMA_SG_IS_STARTED; /* Sync HW.BDA with the driver and start the engine if unprocessed BDs * are present. This process is quite complex since we have to assume * that HW may have been reset since it was stopped. Additionally, we * have to account for calls made to XDmaV2_SgBdToHw() and * XDmaV2_SgBdFromHw() while stopped. Several cases are handled below. * * Wherever HW.BDA is set, that will be the 1st BD processed once * the engine starts. */ /* Case 1: Virgin BD ring that hasn't changed state since it was * created. No BDs have ever been enqueued. */ if (!(XDmaV2_mReadBd(Ring->HwTail, XDMAV2_BD_DMACR_OFFSET) & XDMAV2_DMACR_SGS_MASK)) { XDmaV2_mWriteReg(InstancePtr->RegBase, XDMAV2_BDA_OFFSET, Ring->PhysBaseAddr); } /* Case 2: There are no active BDs. In this case, HwHead has overtaken * HwTail and points one BD past the last one HW has processed. This is * the BD to set HW to start from. Any new BDs will be enqueued at this * point. */ else if (Ring->HwCnt == 0) { XDmaV2_mWriteReg(InstancePtr->RegBase, XDMAV2_BDA_OFFSET, XDMAV2_VIRT_TO_PHYS(Ring, Ring->HwHead)); } /* Case 3: There are 1 or more BDs between HwHead and HwTail. * To find the restart point, look for the first BD between HwHead and * HwTail where the DMABSY bit is set. This will be the 1st unprocessed * BD. Set HW here then tell the engine to begin processing straight away. * * If the end of the list is reached before a DMABSY set bit is found, then * there are no BDs unprocessed by HW. In this case set HW to HwTail.BDA. * Any new BDs will be enqueued at this point. */ else { BdaV = (u32) Ring->HwHead; for (i = 0; i < Ring->HwCnt; i++) { /* Found a BD with DMABSY set? */ if (XDmaV2_mReadBd(BdaV, XDMAV2_BD_DMASR_OFFSET) & XDMAV2_DMASR_DMABSY_MASK) { /* Yes, this is where to point HW */ XDmaV2_mWriteReg(InstancePtr->RegBase, XDMAV2_BDA_OFFSET, XDMAV2_VIRT_TO_PHYS(Ring, BdaV)); /* Since this BD is unprocessed by HW, enable processing */ XDMAV2_HW_SGS_CLEAR; break; } /* Onto next BD */ BdaV = (u32) XDmaV2_mSgBdNext(InstancePtr, BdaV); } /* Made it through loop without finding a DMABSY? */ if (i == Ring->HwCnt) { /* Point HW to the next BD location that will be read once * new BDs are enqueued. This position is at HwTail.BDA which * is where BdaV should be after completing the loop above. */ XDmaV2_mWriteReg(InstancePtr->RegBase, XDMAV2_BDA_OFFSET, XDMAV2_VIRT_TO_PHYS(Ring, BdaV)); } } /* Enable the engine */ XDmaV2_mWriteReg(InstancePtr->RegBase, XDMAV2_SWCR_OFFSET, XDMAV2_SWCR_SGE_MASK); /* Note: If while the channel was XDmaV2_SgStop'd and new BDs were enqueued * to HW, XDmaV2_SgBdToHw() will have cleared DMACR.SGS. Once we set * SWCR.SGE, then processing will begin. */ return (XST_SUCCESS); }
/** * Check the internal data structures of the BD list for the provided channel. * The following checks are made: * * - Is the BD list linked correctly in physical address space. * - Do the internal pointers point to BDs in the list. * - Do the internal counters add up. * * The channel should be stopped prior to calling this function. * * @param InstancePtr is a pointer to the instance to be worked on. * * @return * - XST_SUCCESS if the set of BDs was freed. * - XST_DMA_SG_NO_LIST if the list has not been created. * - XST_IS_STARTED if the channel is not stopped. * - XST_DMA_SG_LIST_ERROR if a problem is found with the internal data * structures. If this value is returned, the channel should be reset and * the list recreated to avoid data corruption or system instability. * * @note This function should not be preempted by another XDmaV2 function call * that modifies the BD space. It is the caller's responsibility to * provide a mutual exclusion mechanism. * ******************************************************************************/ XStatus XDmaV2_SgCheck(XDmaV2 * InstancePtr) { XDmaV2_BdRing *RingPtr = &InstancePtr->BdRing; u32 AddrV, AddrP; int i; /* Is the list created */ if (RingPtr->AllCnt == 0) { return (XST_DMA_SG_NO_LIST); } /* Can't check if channel is running */ if (RingPtr->RunState == XST_DMA_SG_IS_STARTED) { return (XST_IS_STARTED); } /* RunState doesn't make sense */ else if (RingPtr->RunState != XST_DMA_SG_IS_STOPPED) { return (XST_DMA_SG_LIST_ERROR); } /* Verify internal pointers point to correct memory space */ AddrV = (u32) RingPtr->FreeHead; if ((AddrV < RingPtr->BaseAddr) || (AddrV > RingPtr->HighAddr)) { return (XST_DMA_SG_LIST_ERROR); } AddrV = (u32) RingPtr->PreHead; if ((AddrV < RingPtr->BaseAddr) || (AddrV > RingPtr->HighAddr)) { return (XST_DMA_SG_LIST_ERROR); } AddrV = (u32) RingPtr->HwHead; if ((AddrV < RingPtr->BaseAddr) || (AddrV > RingPtr->HighAddr)) { return (XST_DMA_SG_LIST_ERROR); } AddrV = (u32) RingPtr->HwTail; if ((AddrV < RingPtr->BaseAddr) || (AddrV > RingPtr->HighAddr)) { return (XST_DMA_SG_LIST_ERROR); } AddrV = (u32) RingPtr->PostHead; if ((AddrV < RingPtr->BaseAddr) || (AddrV > RingPtr->HighAddr)) { return (XST_DMA_SG_LIST_ERROR); } /* Verify internal counters add up */ if ((RingPtr->HwCnt + RingPtr->PreCnt + RingPtr->FreeCnt + RingPtr->PostCnt) != RingPtr->AllCnt) { return (XST_DMA_SG_LIST_ERROR); } /* Verify BDs are linked correctly */ AddrV = RingPtr->BaseAddr; AddrP = RingPtr->PhysBaseAddr + RingPtr->Separation; for (i = 1; i < RingPtr->AllCnt; i++) { /* Check BDA for this BD. It should point to next physical addr */ if (XDmaV2_mReadBd(AddrV, XDMAV2_BD_BDA_OFFSET) != AddrP) { return (XST_DMA_SG_LIST_ERROR); } /* Move on to next BD */ AddrV += RingPtr->Separation; AddrP += RingPtr->Separation; } /* Last BD should point back to the beginning of ring */ if (XDmaV2_mReadBd(AddrV, XDMAV2_BD_BDA_OFFSET) != RingPtr->PhysBaseAddr) { return (XST_DMA_SG_LIST_ERROR); } /* No problems found */ return (XST_SUCCESS); }