/* * 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; }