rt-thread/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.c

958 lines
31 KiB
C
Raw Normal View History

/*
* 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 implements buffer descriptor ring related functions.
*
* Modify History:
* Ver   Who        Date         Changes
* ----- ------     --------    --------------------------------------
* 1.0 huanghe 2022/06/16 first release
*/
#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:
*
* <pre>
* status = FXmacBdRingAlloc(Myring_ptr, 10, &bdptr),
* ...
* if (Error)
* {
* status = FXmacBdRingUnAlloc(Myring_ptr, 10, &bdptr),
* }
* </pre>
*
* A partial UnAlloc means some of the BDs Alloc'd will be returned:
*
* <pre>
* 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--,
* }
* </pre>
*
* 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:
*
* <pre>
* 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<num_bd; i++)
* {
* * Examine cur_bd for post processing *.....
*
* * Onto next BD *
* cur_bd = FXMAC_BD_RING_NEXT(Myring_ptr, cur_bd),
* }
*
* FXmacBdRingFree(Myring_ptr, num_bd, MyBdSet), *Return list*
* }
* </pre>
*
* 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:
* <pre>
* * 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),
* </pre>
*
* 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:
*
* <pre>
* 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<num_bd; i++)
* {
* * Examine cur_bd for post processing *.....
*
* * Onto next BD *
* cur_bd = FXMAC_BD_RING_NEXT(Myring_ptr, cur_bd),
* }
*
* FXmacBdRingFree(Myring_ptr, num_bd, MyBdSet), * Return list *
* }
* </pre>
*
* 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:
* <pre>
* * 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),
* </pre>
*
* 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);
}
/* 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;
}