/* * Copyright : (C) 2022 Phytium Information Technology, Inc. * All Rights Reserved. * * This program is OPEN SOURCE software: you can redistribute it and/or modify it * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, * either version 1.0 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Phytium Public License for more details. * * * FilePath: fxmac_bdring.c * Date: 2022-04-06 14:46:52 * LastEditTime: 2022-04-06 14:46:58 * Description: This file is for * * Modify History: * Ver Who Date Changes * ----- ------ -------- -------------------------------------- */ #include "fxmac_hw.h" #include "fxmac.h" #include "fxmac_bdring.h" #include "fxmac_bd.h" #include "ftypes.h" #include "fxmac.h" #include "string.h" #include "fprintk.h" #include "fdebug.h" static void FXmacBdSetRxWrap(uintptr bdptr); static void FXmacBdSetTxWrap(uintptr bdptr); /************************** Variable Definitions *****************************/ /***************** Macros (Inline Functions) Definitions *********************/ /** * @name: FXMAC_RING_SEEKAHEAD * @msg: Move the bdptr argument ahead an arbitrary number of BDs wrapping around * to the beginning of the ring if needed. * @param ring_ptr is the ring bdptr appears in * @param bdptr on input is the starting BD position and on output is the final BD position * @param num_bd is the number of BD spaces to increment */ #define FXMAC_RING_SEEKAHEAD(ring_ptr, bdptr, num_bd) \ { \ uintptr addr = (uintptr)(void *)(bdptr); \ \ addr += ((ring_ptr)->separation * (num_bd)); \ if ((addr > (ring_ptr)->high_bd_addr) || ((uintptr)(void *)(bdptr) > addr)) \ { \ addr -= (ring_ptr)->length; \ } \ \ (bdptr) = (FXmacBd *)(void *)addr; \ } /** * @name: FXMAC_RING_SEEKBACK * @msg: Move the bdptr argument backwards an arbitrary number of BDs wrapping * around to the end of the ring if needed. * @param ring_ptr is the ring bdptr appears in * @param bdptr on input is the starting BD position and on output is the * final BD position * @param num_bd is the number of BD spaces to increment * @return {*} */ #define FXMAC_RING_SEEKBACK(ring_ptr, bdptr, num_bd) \ { \ uintptr addr = (uintptr)(void *)(bdptr); \ \ addr -= ((ring_ptr)->separation * (num_bd)); \ if ((addr < (ring_ptr)->base_bd_addr) || ((uintptr)(void *)(bdptr) < addr)) \ { \ addr += (ring_ptr)->length; \ } \ \ (bdptr) = (FXmacBd *)(void *)addr; \ } /** * @name: FXmacBdRingCreate * @msg: Using a memory segment allocated by the caller, create and setup the BD list * for the given DMA channel. * @param ring_ptr is the instance to be worked on. * @param Physaddr is the physical base address of user memory region. * @param Virtaddr is the virtual base address of the user memory region. If * address translation is not being utilized, then Virtaddr should be * equivalent to Physaddr. * @param Alignment governs the byte alignment of individual BDs. This function * will enforce a minimum alignment of 4 bytes with no maximum as long * as it is specified as a power of 2. * @param bd_count is the number of BDs to setup in the user memory region. It * is assumed the region is large enough to contain the BDs. * @return * FT_SUCCESS if initialization was successful * FXMAC_ERR_INVALID_PARAM under any of the following conditions: * 1) Physaddr and/or Virtaddr are not aligned to the given Alignment * parameter. * 2) Alignment parameter does not meet minimum requirements or is not a * power of 2 value. * 3) bd_count is 0. */ FError FXmacBdRingCreate(FXmacBdRing *ring_ptr, uintptr phys_addr, uintptr virt_addr, u32 alignment, u32 bd_count) { u32 i; uintptr bd_virt_addr; uintptr bd_phy_addr; uintptr virt_addr_loc = virt_addr; /* In case there is a failure prior to creating list, make sure the * following attributes are 0 to prevent calls to other functions * from doing anything. */ ring_ptr->all_cnt = 0U; ring_ptr->free_cnt = 0U; ring_ptr->hw_cnt = 0U; ring_ptr->pre_cnt = 0U; ring_ptr->post_cnt = 0U; /* Make sure alignment parameter meets minimum requirements */ if (alignment < (u32)FXMAC_DMABD_MINIMUM_ALIGNMENT) { return (FError)(FXMAC_ERR_INVALID_PARAM); } /* Make sure alignment is a power of 2 */ if (((alignment - 0x00000001U) & alignment) != 0x00000000U) { return (FError)(FXMAC_ERR_INVALID_PARAM); } /* Make sure phys_addr and virt_addr are on same alignment */ if (((phys_addr % alignment) != (u32)0) || ((virt_addr_loc % alignment) != (u32)0)) { return (FError)(FXMAC_ERR_INVALID_PARAM); } /* Is bd_count reasonable? */ if (bd_count == 0x00000000U) { return (FError)(FXMAC_ERR_INVALID_PARAM); } /* Figure out how many bytes will be between the start of adjacent BDs */ ring_ptr->separation = ((u32)sizeof(FXmacBd)); /* Must make sure the ring doesn't span address 0x00000000. If it does, * then the next/prev BD traversal macros will fail. */ if (virt_addr_loc > ((virt_addr_loc + (ring_ptr->separation * bd_count)) - (u32)1)) { return (FError)(FXMAC_ERR_SG_LIST); } /* Initial ring setup: * - Clear the entire space * - Setup each BD's BDA field with the physical address of the next BD */ (void)memset((void *)virt_addr_loc, 0, (ring_ptr->separation * bd_count)); bd_virt_addr = virt_addr_loc; bd_phy_addr = phys_addr + ring_ptr->separation; for (i = 1U; i < bd_count; i++) { bd_virt_addr += ring_ptr->separation; bd_phy_addr += ring_ptr->separation; } /* Setup and initialize pointers and counters */ ring_ptr->run_state = (u32)(FXMAC_DMA_SG_IS_STOPED); ring_ptr->base_bd_addr = virt_addr_loc; ring_ptr->phys_base_addr = phys_addr; ring_ptr->high_bd_addr = bd_virt_addr; ring_ptr->length = ((ring_ptr->high_bd_addr - ring_ptr->base_bd_addr) + ring_ptr->separation); ring_ptr->all_cnt = (u32)bd_count; ring_ptr->free_cnt = (u32)bd_count; ring_ptr->free_head = (FXmacBd *)(void *)virt_addr_loc; ring_ptr->pre_head = (FXmacBd *)virt_addr_loc; ring_ptr->hw_head = (FXmacBd *)virt_addr_loc; ring_ptr->hw_tail = (FXmacBd *)virt_addr_loc; ring_ptr->post_head = (FXmacBd *)virt_addr_loc; ring_ptr->bda_restart = (FXmacBd *)(void *)phys_addr; return (FError)(FT_SUCCESS); } /** * @name: FXmacBdRingClone * @msg: Clone the given BD into every BD in the list. * every field of the source BD is replicated in every BD of the list. * @param ring_ptr is the instance to be worked on. * @param src_bd_ptr is the source BD template to be cloned into the list. This * BD will be modified. * @param direction is either FXMAC_SEND or FXMAC_RECV that indicates * which direction. * @return {*} */ FError FXmacBdRingClone(FXmacBdRing *ring_ptr, FXmacBd *src_bd_ptr, u8 direction) { u32 i; uintptr cur_bd; /* Can't do this function if there isn't a ring */ if (ring_ptr->all_cnt == 0x00000000U) { return (FError)(FXMAC_ERR_SG_NO_LIST); } /* Can't do this function with the channel running */ if (ring_ptr->run_state == (u32)FXMAC_DMA_SG_IS_STARTED) { return (FError)(FT_COMPONENT_IS_STARTED); } /* Can't do this function with some of the BDs in use */ if (ring_ptr->free_cnt != ring_ptr->all_cnt) { return (FError)(FXMAC_ERR_SG_LIST); } if ((direction != (u8)FXMAC_SEND) && (direction != (u8)FXMAC_RECV)) { return (FError)(FXMAC_ERR_INVALID_PARAM); } /* Starting from the top of the ring, save bd.next, overwrite the entire * BD with the template, then restore bd.next */ cur_bd = ring_ptr->base_bd_addr; for (i = 0U; i < ring_ptr->all_cnt; i++) { memcpy((void *)cur_bd, src_bd_ptr, sizeof(FXmacBd)); cur_bd += ring_ptr->separation; } cur_bd -= ring_ptr->separation; if (direction == FXMAC_RECV) { FXmacBdSetRxWrap(cur_bd); } else { FXmacBdSetTxWrap(cur_bd); } return (FError)(FT_SUCCESS); } /** * @name: FXmacBdRingAlloc * @msg: Reserve locations in the BD list. The set of returned BDs may be modified * in preparation for future DMA transaction(s). Once the BDs are ready to be * submitted to hardware, the user must call FXmacBdRingToHw() in the same * order which they were allocated here. * @param ring_ptr is a pointer to the BD ring instance to be worked on. * @param num_bd is the number of BDs to allocate * @param bd_set_ptr is an output parameter, it points to the first BD available * for modification. * @return FT_SUCCESS if the requested number of BDs was returned in the bd_set_ptr * parameter. * - FXMAC_ERR_GENERAL if there were not enough free BDs to satisfy the request. */ FError FXmacBdRingAlloc(FXmacBdRing *ring_ptr, u32 num_bd, FXmacBd **bd_set_ptr) { FError status; /* Enough free BDs available for the request? */ if (ring_ptr->free_cnt < num_bd) { status = (FError)(FXMAC_ERR_GENERAL); } else { /* Set the return argument and move free_head forward */ *bd_set_ptr = ring_ptr->free_head; FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->free_head, num_bd); ring_ptr->free_cnt -= num_bd; ring_ptr->pre_cnt += num_bd; status = (FError)(FT_SUCCESS); } return status; } /** * @name: FXmacBdRingUnAlloc * @msg: * Fully or partially undo an FXmacBdRingAlloc() operation. Use this * function if all the BDs allocated by FXmacBdRingAlloc() could not be * transferred to hardware with FXmacBdRingToHw(). * * This function helps out in situations when an unrelated error occurs after * BDs have been allocated but before they have been given to hardware. * An example of this type of error would be an OS running out of resources. * * This function is not the same as FXmacBdRingFree(). The Free function * returns BDs to the free list after they have been processed by hardware, * while UnAlloc returns them before being processed by hardware. * * There are two scenarios where this function can be used. Full UnAlloc or * Partial UnAlloc. A Full UnAlloc means all the BDs Alloc'd will be returned: * *
* status = FXmacBdRingAlloc(Myring_ptr, 10, &bdptr),
* ...
* if (Error)
* {
* status = FXmacBdRingUnAlloc(Myring_ptr, 10, &bdptr),
* }
*
*
* A partial UnAlloc means some of the BDs Alloc'd will be returned:
*
*
* status = FXmacBdRingAlloc(Myring_ptr, 10, &bdptr),
* BdsLeft = 10,
* cur_bd_ptr = bdptr,
*
* while (BdsLeft)
* {
* if (Error)
* {
* status = FXmacBdRingUnAlloc(Myring_ptr, BdsLeft, cur_bd_ptr),
* }
*
* cur_bd_ptr = FXMAC_BD_RING_NEXT(Myring_ptr, cur_bd_ptr),
* BdsLeft--,
* }
*
*
* A partial UnAlloc must include the last BD in the list that was Alloc'd.
*
* @param ring_ptr is a pointer to the instance to be worked on.
* @param num_bd is the number of BDs to allocate
* @param bd_set_ptr is an output parameter, it points to the first BD available
* for modification.
*
* @return
* - FT_SUCCESS if the BDs were unallocated.
* - FXMAC_ERR_GENERAL if num_bd parameter was greater that the number of BDs in
* the preprocessing state.
*
* @return {*}
*/
FError FXmacBdRingUnAlloc(FXmacBdRing *ring_ptr, u32 num_bd,
FXmacBd *bd_set_ptr)
{
FError status;
(void)bd_set_ptr;
FASSERT(ring_ptr != NULL);
FASSERT(bd_set_ptr != NULL);
/* Enough BDs in the free state for the request? */
if (ring_ptr->pre_cnt < num_bd)
{
status = (FError)(FXMAC_ERR_GENERAL);
}
else
{
/* Set the return argument and move free_head backward */
FXMAC_RING_SEEKBACK(ring_ptr, (ring_ptr->free_head), num_bd);
ring_ptr->free_cnt += num_bd;
ring_ptr->pre_cnt -= num_bd;
status = (FError)(FT_SUCCESS);
}
return status;
}
/**
* @name: FXmacBdRingToHw
* @msg: Enqueue a set of BDs to hardware that were previously allocated by
* FXmacBdRingAlloc(). Once this function returns, the argument BD set goes
* under hardware control. Any changes made to these BDs after this point will
* corrupt the BD list leading to data corruption and system instability.
*
* @param ring_ptr is a pointer to the instance to be worked on.
* @param num_bd is the number of BDs in the set.
* @param bd_set_ptr is the first BD of the set to commit to hardware.
* @return FT_SUCCESS if the set of BDs was accepted and enqueued to hardware.
* XST_FAILURE if the set of BDs was rejected because the last BD of the set
* did not have its "last" bit set.
* FXMAC_ERR_SG_LIST if this function was called out of sequence with
* FXmacBdRingAlloc().
*/
FError FXmacBdRingToHw(FXmacBdRing *ring_ptr, u32 num_bd,
FXmacBd *bd_set_ptr)
{
FXmacBd *cur_bd_ptr;
u32 i;
FError status;
/* if no bds to process, simply return. */
if (0U == num_bd)
{
status = (FError)(FT_SUCCESS);
}
else
{
/* Make sure we are in sync with FXmacBdRingAlloc() */
if ((ring_ptr->pre_cnt < num_bd) || (ring_ptr->pre_head != bd_set_ptr))
{
status = (FError)(FXMAC_ERR_SG_LIST);
}
else
{
cur_bd_ptr = bd_set_ptr;
for (i = 0U; i < num_bd; i++)
{
cur_bd_ptr = (FXmacBd *)((void *)FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr));
}
/* Adjust ring pointers & counters */
FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->pre_head, num_bd);
ring_ptr->pre_cnt -= num_bd;
ring_ptr->hw_tail = cur_bd_ptr;
ring_ptr->hw_cnt += num_bd;
status = (FError)(FT_SUCCESS);
}
}
return status;
}
/**
* @name: FXmacBdRingFromHwTx
* @msg: Returns a set of BD(s) that have been processed by hardware. 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 FXmacBdRingFree()
* in the same order which they were retrieved here. Example:
*
*
* num_bd = FXmacBdRingFromHwTx(Myring_ptr, MaxBd, &MyBdSet),
* if (num_bd == 0)
* {
* * hardware has nothing ready for us yet*
* }
*
* cur_bd = MyBdSet,
* for (i=0; i
*
* A more advanced use of this function may allocate multiple sets of BDs.
* They must be retrieved from hardware and freed in the correct sequence:
*
* * Legal *
* FXmacBdRingFromHwTx(Myring_ptr, num_bd1, &MySet1),
* FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
*
* * Legal *
* FXmacBdRingFromHwTx(Myring_ptr, num_bd1, &MySet1),
* FXmacBdRingFromHwTx(Myring_ptr, num_bd2, &MySet2),
* FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
* FXmacBdRingFree(Myring_ptr, num_bd2, MySet2),
*
* * Not legal *
* FXmacBdRingFromHwTx(Myring_ptr, num_bd1, &MySet1),
* FXmacBdRingFromHwTx(Myring_ptr, num_bd2, &MySet2),
* FXmacBdRingFree(Myring_ptr, num_bd2, MySet2),
* FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
*
*
* If hardware has only partially completed a packet spanning multiple BDs,
* then none of the BDs for that packet will be included in the results.
*
* @param ring_ptr is a pointer to the instance to be worked on.
* @param bd_limit is the maximum number of BDs to return in the set.
* @param bd_set_ptr is an output parameter, it points to the first BD available
* for examination.
*
* @return
* The number of BDs processed by hardware. A value of 0 indicates that no
* data is available. No more than bd_limit BDs will be returned.
*
* @return
* The number of BDs processed by hardware. A value of 0 indicates that no
* data is available. No more than bd_limit BDs will be returned.
*/
u32 FXmacBdRingFromHwTx(FXmacBdRing *ring_ptr, u32 bd_limit,
FXmacBd **bd_set_ptr)
{
FXmacBd *cur_bd_ptr;
u32 bd_str = 0U;
u32 bd_count;
u32 bd_partial_count;
u32 Sop = 0U;
u32 status;
u32 bd_limitLoc = bd_limit;
cur_bd_ptr = ring_ptr->hw_head;
bd_count = 0U;
bd_partial_count = 0U;
/* If no BDs in work group, then there's nothing to search */
if (ring_ptr->hw_cnt == 0x00000000U)
{
*bd_set_ptr = NULL;
status = 0U;
}
else
{
if (bd_limitLoc > ring_ptr->hw_cnt)
{
bd_limitLoc = ring_ptr->hw_cnt;
}
/* Starting at hw_head, keep moving forward in the list until:
* - A BD is encountered with its new/used bit set which means
* hardware has not completed processing of that BD.
* - ring_ptr->hw_tail is reached and ring_ptr->hw_cnt is reached.
* - The number of requested BDs has been processed
*/
while (bd_count < bd_limitLoc)
{
/* Read the status */
if (cur_bd_ptr != NULL)
{
bd_str = FXMAC_BD_READ(cur_bd_ptr, FXMAC_BD_STAT_OFFSET);
}
if ((Sop == 0x00000000U) && ((bd_str & FXMAC_TXBUF_USED_MASK) != 0x00000000U))
{
Sop = 1U;
}
if (Sop == 0x00000001U)
{
bd_count++;
bd_partial_count++;
}
/* hardware 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 ((Sop == 0x00000001U) && ((bd_str & FXMAC_TXBUF_LAST_MASK) != 0x00000000U))
{
Sop = 0U;
bd_partial_count = 0U;
}
/* Move on to next BD in work group */
cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr);
}
/* Subtract off any partial packet BDs found */
bd_count -= bd_partial_count;
/* If bd_count is non-zero then BDs were found to return. Set return
* parameters, update pointers and counters, return success
*/
if (bd_count > 0x00000000U)
{
*bd_set_ptr = ring_ptr->hw_head;
ring_ptr->hw_cnt -= bd_count;
ring_ptr->post_cnt += bd_count;
FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->hw_head, bd_count);
status = (bd_count);
}
else
{
*bd_set_ptr = NULL;
status = 0U;
}
}
return status;
}
/**
* @name: FXmacBdRingFromHwRx
* @msg: Returns a set of BD(s) that have been processed by hardware. 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 FXmacBdRingFree()
* in the same order which they were retrieved here. Example:
*
*
* num_bd = FXmacBdRingFromHwRx(Myring_ptr, MaxBd, &MyBdSet),
*
* if (num_bd == 0)
* {
* *hardware has nothing ready for us yet*
* }
*
* cur_bd = MyBdSet,
* for (i=0; i
*
* A more advanced use of this function may allocate multiple sets of BDs.
* They must be retrieved from hardware and freed in the correct sequence:
*
* * Legal *
* FXmacBdRingFromHwRx(Myring_ptr, num_bd1, &MySet1),
* FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
*
* * Legal *
* FXmacBdRingFromHwRx(Myring_ptr, num_bd1, &MySet1),
* FXmacBdRingFromHwRx(Myring_ptr, num_bd2, &MySet2),
* FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
* FXmacBdRingFree(Myring_ptr, num_bd2, MySet2),
*
* * Not legal *
* FXmacBdRingFromHwRx(Myring_ptr, num_bd1, &MySet1),
* FXmacBdRingFromHwRx(Myring_ptr, num_bd2, &MySet2),
* FXmacBdRingFree(Myring_ptr, num_bd2, MySet2),
* FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
*
*
* If hardware has only partially completed a packet spanning multiple BDs,
* then none of the BDs for that packet will be included in the results.
*
* @param ring_ptr is a pointer to the instance to be worked on.
* @param bd_limit is the maximum number of BDs to return in the set.
* @param bd_set_ptr is an output parameter, it points to the first BD available
* for examination.
*
* @return
* The number of BDs processed by hardware. A value of 0 indicates that no
* data is available. No more than bd_limit BDs will be returned.
*/
u32 FXmacBdRingFromHwRx(FXmacBdRing *ring_ptr, u32 bd_limit,
FXmacBd **bd_set_ptr)
{
FXmacBd *cur_bd_ptr;
u32 bd_str = 0U;
u32 bd_count;
u32 bd_partial_count;
u32 status;
cur_bd_ptr = ring_ptr->hw_head;
bd_count = 0U;
bd_partial_count = 0U;
/* If no BDs in work group, then there's nothing to search */
if (ring_ptr->hw_cnt == 0x00000000U)
{
*bd_set_ptr = NULL;
status = 0U;
}
else
{
/* Starting at hw_head, keep moving forward in the list until:
* - A BD is encountered with its new/used bit set which means
* hardware has completed processing of that BD.
* - ring_ptr->hw_tail is reached and ring_ptr->hw_cnt is reached.
* - The number of requested BDs has been processed
*/
while (bd_count < bd_limit)
{
/* Read the status */
if (cur_bd_ptr != NULL)
{
bd_str = FXMAC_BD_READ(cur_bd_ptr, FXMAC_BD_STAT_OFFSET);
}
if ((!(FXMAC_BD_IS_RX_NEW(cur_bd_ptr))) == TRUE)
{
break;
}
bd_count++;
/* hardware 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 ((bd_str & FXMAC_RXBUF_EOF_MASK) != 0x00000000U)
{
bd_partial_count = 0U;
}
else
{
bd_partial_count++;
}
/* Move on to next BD in work group */
cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr);
// if((bd_str & FXMAC_RXBUF_EOF_MASK) != 0x00000000U)
// {
// if(bd_str &FXMAC_RXBUF_FCS_STATUS_MASK)
// {
// f_printk("********** error fcs data is appear ************* \r\n");
// FtDumpHexWord(FXMAC_BD_READ(cur_bd_ptr,0) &(0xfffffff8),bd_str&FXMAC_RXBUF_LEN_MASK);
// f_printk("********** end ************* \r\n");
// }
// }
}
/* Subtract off any partial packet BDs found */
bd_count -= bd_partial_count;
/* If bd_count is non-zero then BDs were found to return. Set return
* parameters, update pointers and counters, return success
*/
if (bd_count > 0x00000000U)
{
*bd_set_ptr = ring_ptr->hw_head;
ring_ptr->hw_cnt -= bd_count;
ring_ptr->post_cnt += bd_count;
FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->hw_head, bd_count);
status = (bd_count);
}
else
{
*bd_set_ptr = NULL;
status = 0U;
}
}
return status;
}
/**
* @name: FXmacBdRingFree
* @msg: Frees a set of BDs that had been previously retrieved with
* FXmacBdRingFromHw().
*
* @param ring_ptr is a pointer to the instance to be worked on.
* @param num_bd is the number of BDs to free.
* @param bd_set_ptr is the head of a list of BDs returned by
* FXmacBdRingFromHw().
*
* @return
* FT_SUCCESS if the set of BDs was freed.
* FXMAC_ERR_SG_LIST if this function was called out of sequence with
* FXmacBdRingFromHw().
*/
FError FXmacBdRingFree(FXmacBdRing *ring_ptr, u32 num_bd,
FXmacBd *bd_set_ptr)
{
FError status;
/* if no bds to process, simply return. */
if (0x00000000U == num_bd)
{
status = (FError)(FT_SUCCESS);
}
else
{
/* Make sure we are in sync with FXmacBdRingFromHw() */
if ((ring_ptr->post_cnt < num_bd) || (ring_ptr->post_head != bd_set_ptr))
{
status = (FError)(FXMAC_ERR_SG_LIST);
}
else
{
/* Update pointers and counters */
ring_ptr->free_cnt += num_bd;
ring_ptr->post_cnt -= num_bd;
FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->post_head, num_bd);
status = (FError)(FT_SUCCESS);
}
}
return status;
}
/**
* @name: FXmacBdRingCheck
* @msg: Check the internal data structures of the BD ring for the provided channel.
* The following checks are made:
*
* - Is the BD ring linked correctly in physical address space.
* - Do the internal pointers point to BDs in the ring.
* - Do the internal counters add up.
*
* The channel should be stopped prior to calling this function.
*
* @param {FXmacBdRing} ring_ptr is a pointer to the instance to be worked on.
* @param {u8} direction is either FXMAC_SEND or FXMAC_RECV that indicates
* which direction.
* @return {*}
* FT_SUCCESS if the set of BDs was freed.
* XST_DMA_SG_NO_LIST if the list has not been created.
* FT_COMPONENT_IS_STARTED if the channel is not stopped.
* FXMAC_ERR_SG_LIST if a problem is found with the internal data
* structures. If this value is returned, the channel should be reset to
* avoid data corruption or system instability.
*/
FError FXmacBdRingCheck(FXmacBdRing *ring_ptr, u8 direction)
{
uintptr addr_v, addr_p;
u32 i;
if ((direction != (u8)FXMAC_SEND) && (direction != (u8)FXMAC_RECV))
{
return (FError)(FXMAC_ERR_INVALID_PARAM);
}
/* Is the list created */
if (ring_ptr->all_cnt == 0x00000000U)
{
return (FError)(FXMAC_ERR_SG_NO_LIST);
}
/* Can't check if channel is running */
if (ring_ptr->run_state == (u32)FXMAC_DMA_SG_IS_STARTED)
{
return (FError)(FT_COMPONENT_IS_STARTED);
}
/* run_state doesn't make sense */
if (ring_ptr->run_state != (u32)FXMAC_DMA_SG_IS_STOPED)
{
return (FError)(FXMAC_ERR_SG_LIST);
}
/* Verify internal pointers point to correct memory space */
addr_v = (uintptr)ring_ptr->free_head;
if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr))
{
return (FError)(FXMAC_ERR_SG_LIST);
}
addr_v = (uintptr)ring_ptr->pre_head;
if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr))
{
return (FError)(FXMAC_ERR_SG_LIST);
}
addr_v = (uintptr)ring_ptr->hw_head;
if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr))
{
return (FError)(FXMAC_ERR_SG_LIST);
}
addr_v = (uintptr)ring_ptr->hw_tail;
if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr))
{
return (FError)(FXMAC_ERR_SG_LIST);
}
addr_v = (uintptr)ring_ptr->post_head;
if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr))
{
return (FError)(FXMAC_ERR_SG_LIST);
}
/* Verify internal counters add up */
if ((ring_ptr->hw_cnt + ring_ptr->pre_cnt + ring_ptr->free_cnt +
ring_ptr->post_cnt) != ring_ptr->all_cnt)
{
return (FError)(FXMAC_ERR_SG_LIST);
}
/* Verify BDs are linked correctly */
addr_v = ring_ptr->base_bd_addr;
addr_p = ring_ptr->phys_base_addr + ring_ptr->separation;
for (i = 1U; i < ring_ptr->all_cnt; i++)
{
/* Check BDA for this BD. It should point to next physical addr */
if (FXMAC_BD_READ(addr_v, FXMAC_BD_ADDR_OFFSET) != addr_p)
{
return (FError)(FXMAC_ERR_SG_LIST);
}
/* Move on to next BD */
addr_v += ring_ptr->separation;
addr_p += ring_ptr->separation;
}
/* Last BD should have wrap bit set */
if (FXMAC_SEND == direction)
{
if ((!FXMAC_BD_IS_TX_WRAP(addr_v)) == TRUE)
{
return (FError)(FXMAC_ERR_SG_LIST);
}
}
else
{
/* FXMAC_RECV */
if ((!FXMAC_BD_IS_RX_WRAP(addr_v)) == TRUE)
{
return (FError)(FXMAC_ERR_SG_LIST);
}
}
/* No problems found */
return (FError)(FT_SUCCESS);
}
/**
* @name: FXmacBdSetRxWrap
* @msg: Set this bit to mark the last descriptor in the receive buffer descriptor
* list.
* @param {uintptr} bdptr is the BD pointer to operate on
*/
static void FXmacBdSetRxWrap(uintptr bdptr)
{
u32 data_value_rx;
u32 *temp_ptr;
bdptr += (u32)(FXMAC_BD_ADDR_OFFSET);
temp_ptr = (u32 *)bdptr;
if (temp_ptr != NULL)
{
data_value_rx = *temp_ptr;
data_value_rx |= FXMAC_RXBUF_WRAP_MASK;
*temp_ptr = data_value_rx;
}
}
/**
* @name: FXmacBdSetTxWrap
* @msg: Sets this bit to mark the last descriptor in the transmit buffer
* descriptor list.
* @param {uintptr} bdptr is the BD pointer to operate on
*/
static void FXmacBdSetTxWrap(uintptr bdptr)
{
u32 data_value_tx;
u32 *temp_ptr;
bdptr += (u32)(FXMAC_BD_STAT_OFFSET);
temp_ptr = (u32 *)bdptr;
if (temp_ptr != NULL)
{
data_value_tx = *temp_ptr;
data_value_tx |= FXMAC_TXBUF_WRAP_MASK;
*temp_ptr = data_value_tx;
}
}
/**
* @name: FXmacBdringPtrReset
* @msg: Reset BD ring head and tail pointers.
* @return {*}
* @param {FXmacBdRing} *ring_ptr is the instance to be worked on.
* @param {void} *virtaddrloc is the virtual base address of the user memory region.
*/
void FXmacBdringPtrReset(FXmacBdRing *ring_ptr, void *virtaddrloc)
{
ring_ptr->free_head = virtaddrloc;
ring_ptr->pre_head = virtaddrloc;
ring_ptr->hw_head = virtaddrloc;
ring_ptr->hw_tail = virtaddrloc;
ring_ptr->post_head = virtaddrloc;
}