diff options
Diffstat (limited to 'embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_dma.c')
-rw-r--r-- | embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_dma.c | 930 |
1 files changed, 930 insertions, 0 deletions
diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_dma.c b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_dma.c new file mode 100644 index 0000000..2da3566 --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_dma.c @@ -0,0 +1,930 @@ +/* + * Copyright (C) 2010 - 2021 Xilinx, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwipopts.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/inet_chksum.h" + +#include "netif/xadapter.h" +#include "netif/xemacpsif.h" +#include "xstatus.h" + +#include "xlwipconfig.h" +#include "xparameters.h" +#include "xparameters_ps.h" +#include "xil_exception.h" +#include "xil_mmu.h" +#if defined (ARMR5) +#include "xreg_cortexr5.h" +#endif +#ifdef CONFIG_XTRACE +#include "xtrace.h" +#endif +#if !NO_SYS +#include "FreeRTOS.h" +#include "semphr.h" +#include "timers.h" +#endif + + +#define INTC_BASE_ADDR XPAR_SCUGIC_0_CPU_BASEADDR +#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_0_DIST_BASEADDR + +/* Byte alignment of BDs */ +#define BD_ALIGNMENT (XEMACPS_DMABD_MINIMUM_ALIGNMENT*2) + +/* A max of 4 different ethernet interfaces are supported */ +static UINTPTR tx_pbufs_storage[4*XLWIP_CONFIG_N_TX_DESC]; +static UINTPTR rx_pbufs_storage[4*XLWIP_CONFIG_N_RX_DESC]; + +static s32_t emac_intr_num; +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +volatile u32_t notifyinfo[4*XLWIP_CONFIG_N_TX_DESC]; +#endif + +/****************************************************************************** + * Each BD is of 8 bytes of size and the BDs (BD chain) need to be put + * at uncached memory location. If they are not put at uncached + * locations, the user needs to flush or invalidate for each BD/packet. + * However, the flush or invalidate can happen over a cache line which can + * span multiple BDs. This means a flush or invalidate of one BD can actually + * flush/invalidate multiple BDs adjacent to the targeted BD.Assuming that + * the user and hardware both update the BD fields, this operation from user + * can potentially overwrite the updates done by hardware or user. + * To avoid this, it is always safe to put the BD chains for Rx and tx side + * at uncached memory location. + * + * The Xilinx standalone BSP for Cortex A9 implements only primary page tables. + * Each table entry corresponds to 1 MB of address map. This means, if a memory + * region has to be made uncached, the minimum granularity will be of 1 MB. + * + * The implementation below allocates a 1 MB of u8 array aligned to 1 MB. + * This ensures that this array is put at 1 MB aligned memory (e.g. 0x1200000) + * and accupies memory of 1 MB. The init_dma function then changes 1 MB of this + * region to make it uncached (strongly ordered). + * This increases the bss section of the program significantly and can be a + * wastage of memory. The reason beings, BDs will hardly occupy few KBs of + * memory and the rest of 1 MB of memory will be unused. + * + * If a program uses other peripherals that have DMAs/bus masters and need + * uncached memory, they may also end of following the same approach. This + * definitely aggravates the memory wastage issue. To avoid all this, the user + * can create a new 1 MB section in the linker script and reserve it for such + * use cases that need uncached memory location. They can then have their own + * memory allocation logic in their application that allocates uncached memory + * from this 1 MB location. For such a case, changes need to be done in this + * file and appropriate uncached memory allocated through other means can be + * used. + * + * The present implementation here allocates 1 MB of uncached memory. It + * reserves of 64 KB of memory for each BD chain. 64 KB of memory means 8192 of + * BDs for each BD chain which is more than enough for any application. + * Assuming that both emac0 and emac1 are present, 256 KB of memory is allocated + * for BDs. The rest 768 KB of memory is just unused. + *********************************************************************************/ + +#if defined __aarch64__ +u8_t bd_space[0x200000] __attribute__ ((aligned (0x200000))); +#else +u8_t bd_space[0x100000] __attribute__ ((aligned (0x100000))); +#endif +static volatile u32_t bd_space_index = 0; +static volatile u32_t bd_space_attr_set = 0; + +#if !NO_SYS +long xInsideISR = 0; +#endif + +#define XEMACPS_BD_TO_INDEX(ringptr, bdptr) \ + (((UINTPTR)bdptr - (UINTPTR)(ringptr)->BaseBdAddr) / (ringptr)->Separation) + + +s32_t is_tx_space_available(xemacpsif_s *emac) +{ + XEmacPs_BdRing *txring; + s32_t freecnt = 0; + + txring = &(XEmacPs_GetTxRing(&emac->emacps)); + + /* tx space is available as long as there are valid BD's */ + freecnt = XEmacPs_BdRingGetFreeCnt(txring); + return freecnt; +} + + +static inline +u32_t get_base_index_txpbufsstorage (xemacpsif_s *xemacpsif) +{ + u32_t index; +#ifdef XPAR_XEMACPS_0_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_0_BASEADDR) { + index = 0; + } +#endif +#ifdef XPAR_XEMACPS_1_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_1_BASEADDR) { + index = XLWIP_CONFIG_N_TX_DESC; + } +#endif +#ifdef XPAR_XEMACPS_2_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_2_BASEADDR) { + index = 2 * XLWIP_CONFIG_N_TX_DESC; + } +#endif +#ifdef XPAR_XEMACPS_3_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_3_BASEADDR) { + index = 3 * XLWIP_CONFIG_N_TX_DESC; + } +#endif + return index; +} + +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +static inline +u32_t get_base_index_tasknotifyinfo (xemacpsif_s *xemacpsif) +{ + u32_t index; +#ifdef XPAR_XEMACPS_0_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_0_BASEADDR) { + index = 0; + } +#endif +#ifdef XPAR_XEMACPS_1_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_1_BASEADDR) { + index = XLWIP_CONFIG_N_TX_DESC; + } +#endif +#ifdef XPAR_XEMACPS_2_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_2_BASEADDR) { + index = 2 * XLWIP_CONFIG_N_TX_DESC; + } +#endif +#ifdef XPAR_XEMACPS_3_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_3_BASEADDR) { + index = 3 * XLWIP_CONFIG_N_TX_DESC; + } +#endif + return index; +} +#endif + +static inline +u32_t get_base_index_rxpbufsstorage (xemacpsif_s *xemacpsif) +{ + u32_t index; +#ifdef XPAR_XEMACPS_0_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_0_BASEADDR) { + index = 0; + } +#endif +#ifdef XPAR_XEMACPS_1_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_1_BASEADDR) { + index = XLWIP_CONFIG_N_RX_DESC; + } +#endif +#ifdef XPAR_XEMACPS_2_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_2_BASEADDR) { + index = 2 * XLWIP_CONFIG_N_RX_DESC; + } +#endif +#ifdef XPAR_XEMACPS_3_BASEADDR + if (xemacpsif->emacps.Config.BaseAddress == XPAR_XEMACPS_3_BASEADDR) { + index = 3 * XLWIP_CONFIG_N_RX_DESC; + } +#endif + return index; +} + +void process_sent_bds(xemacpsif_s *xemacpsif, XEmacPs_BdRing *txring) +{ + XEmacPs_Bd *txbdset; + XEmacPs_Bd *curbdpntr; + s32_t n_bds; + XStatus status; + s32_t n_pbufs_freed = 0; + u32_t bdindex; + struct pbuf *p; + u32 *temp; + u32_t index; +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + u32_t tx_task_notifier_index; +#endif + + index = get_base_index_txpbufsstorage (xemacpsif); +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + tx_task_notifier_index = get_base_index_tasknotifyinfo (xemacpsif); +#endif + + while (1) { + /* obtain processed BD's */ + n_bds = XEmacPs_BdRingFromHwTx(txring, + XLWIP_CONFIG_N_TX_DESC, &txbdset); + if (n_bds == 0) { + return; + } + /* free the processed BD's */ + n_pbufs_freed = n_bds; + curbdpntr = txbdset; + while (n_pbufs_freed > 0) { + bdindex = XEMACPS_BD_TO_INDEX(txring, curbdpntr); + temp = (u32 *)curbdpntr; + *temp = 0; + temp++; + if (bdindex == (XLWIP_CONFIG_N_TX_DESC - 1)) { + *temp = 0xC0000000; + } else { + *temp = 0x80000000; + } + dsb(); + p = (struct pbuf *)tx_pbufs_storage[index + bdindex]; + if (p != NULL) { + pbuf_free(p); + } +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + notifyinfo[tx_task_notifier_index + bdindex] = 0; +#endif + tx_pbufs_storage[index + bdindex] = 0; + curbdpntr = XEmacPs_BdRingNext(txring, curbdpntr); + n_pbufs_freed--; + dsb(); + } + + status = XEmacPs_BdRingFree(txring, n_bds, txbdset); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Failure while freeing in Tx Done ISR\r\n")); + } + } + return; +} + +void emacps_send_handler(void *arg) +{ + struct xemac_s *xemac; + xemacpsif_s *xemacpsif; + XEmacPs_BdRing *txringptr; + u32_t regval; +#if !NO_SYS + xInsideISR++; +#endif + xemac = (struct xemac_s *)(arg); + xemacpsif = (xemacpsif_s *)(xemac->state); + txringptr = &(XEmacPs_GetTxRing(&xemacpsif->emacps)); + regval = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_TXSR_OFFSET); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress,XEMACPS_TXSR_OFFSET, regval); + + /* If Transmit done interrupt is asserted, process completed BD's */ + process_sent_bds(xemacpsif, txringptr); +#if !NO_SYS + xInsideISR--; +#endif +} +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +XStatus emacps_sgsend(xemacpsif_s *xemacpsif, struct pbuf *p, + u32_t block_till_tx_complete, u32_t *to_block_index) +#else +XStatus emacps_sgsend(xemacpsif_s *xemacpsif, struct pbuf *p) +#endif +{ + struct pbuf *q; + s32_t n_pbufs; + XEmacPs_Bd *txbdset, *txbd, *last_txbd = NULL; + XEmacPs_Bd *temp_txbd; + XStatus status; + XEmacPs_BdRing *txring; + u32_t bdindex = 0; + u32_t index; + u32_t max_fr_size; +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + u32_t tx_task_notifier_index; +#endif + + txring = &(XEmacPs_GetTxRing(&xemacpsif->emacps)); + + index = get_base_index_txpbufsstorage (xemacpsif); +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + tx_task_notifier_index = get_base_index_tasknotifyinfo (xemacpsif); +#endif + + /* first count the number of pbufs */ + for (q = p, n_pbufs = 0; q != NULL; q = q->next) + n_pbufs++; + + /* obtain as many BD's */ + status = XEmacPs_BdRingAlloc(txring, n_pbufs, &txbdset); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("sgsend: Error allocating TxBD\r\n")); + return XST_FAILURE; + } + + for(q = p, txbd = txbdset; q != NULL; q = q->next) { + bdindex = XEMACPS_BD_TO_INDEX(txring, txbd); + if (tx_pbufs_storage[index + bdindex] != 0) { + LWIP_DEBUGF(NETIF_DEBUG, ("PBUFS not available\r\n")); + return XST_FAILURE; + } + + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + if (xemacpsif->emacps.Config.IsCacheCoherent == 0) { + Xil_DCacheFlushRange((UINTPTR)q->payload, (UINTPTR)q->len); + } + + XEmacPs_BdSetAddressTx(txbd, (UINTPTR)q->payload); + +#ifdef ZYNQMP_USE_JUMBO + max_fr_size = MAX_FRAME_SIZE_JUMBO - 18; +#else + max_fr_size = XEMACPS_MAX_FRAME_SIZE - 18; +#endif + if (q->len > max_fr_size) + XEmacPs_BdSetLength(txbd, max_fr_size & 0x3FFF); + else + XEmacPs_BdSetLength(txbd, q->len & 0x3FFF); + + tx_pbufs_storage[index + bdindex] = (UINTPTR)q; + + pbuf_ref(q); + last_txbd = txbd; + XEmacPs_BdClearLast(txbd); + txbd = XEmacPs_BdRingNext(txring, txbd); + } +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + if (block_till_tx_complete == 1) { + notifyinfo[tx_task_notifier_index + bdindex] = 1; + *to_block_index = tx_task_notifier_index + bdindex; + } +#endif + XEmacPs_BdSetLast(last_txbd); + /* For fragmented packets, remember the 1st BD allocated for the 1st + packet fragment. The used bit for this BD should be cleared at the end + after clearing out used bits for other fragments. For packets without + just remember the allocated BD. */ + temp_txbd = txbdset; + txbd = txbdset; + txbd = XEmacPs_BdRingNext(txring, txbd); + q = p->next; + for(; q != NULL; q = q->next) { + XEmacPs_BdClearTxUsed(txbd); + txbd = XEmacPs_BdRingNext(txring, txbd); + } + XEmacPs_BdClearTxUsed(temp_txbd); + dsb(); + + status = XEmacPs_BdRingToHw(txring, n_pbufs, txbdset); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("sgsend: Error submitting TxBD\r\n")); + return XST_FAILURE; + } + /* Start transmit */ + XEmacPs_WriteReg((xemacpsif->emacps).Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, + (XEmacPs_ReadReg((xemacpsif->emacps).Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET) | XEMACPS_NWCTRL_STARTTX_MASK)); + return status; +} + +void setup_rx_bds(xemacpsif_s *xemacpsif, XEmacPs_BdRing *rxring) +{ + XEmacPs_Bd *rxbd; + XStatus status; + struct pbuf *p; + u32_t freebds; + u32_t bdindex; + u32 *temp; + u32_t index; + + index = get_base_index_rxpbufsstorage (xemacpsif); + + freebds = XEmacPs_BdRingGetFreeCnt (rxring); + while (freebds > 0) { + freebds--; +#ifdef ZYNQMP_USE_JUMBO + p = pbuf_alloc(PBUF_RAW, MAX_FRAME_SIZE_JUMBO, PBUF_POOL); +#else + p = pbuf_alloc(PBUF_RAW, XEMACPS_MAX_FRAME_SIZE, PBUF_POOL); +#endif + if (!p) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + printf("unable to alloc pbuf in recv_handler\r\n"); + return; + } + status = XEmacPs_BdRingAlloc(rxring, 1, &rxbd); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("setup_rx_bds: Error allocating RxBD\r\n")); + pbuf_free(p); + return; + } + status = XEmacPs_BdRingToHw(rxring, 1, rxbd); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error committing RxBD to hardware: ")); + if (status == XST_DMA_SG_LIST_ERROR) { + LWIP_DEBUGF(NETIF_DEBUG, ("XST_DMA_SG_LIST_ERROR: this function was called out of sequence with XEmacPs_BdRingAlloc()\r\n")); + } + else { + LWIP_DEBUGF(NETIF_DEBUG, ("set of BDs was rejected because the first BD did not have its start-of-packet bit set, or the last BD did not have its end-of-packet bit set, or any one of the BD set has 0 as length value\r\n")); + } + + pbuf_free(p); + XEmacPs_BdRingUnAlloc(rxring, 1, rxbd); + return; + } +#ifdef ZYNQMP_USE_JUMBO + if (xemacpsif->emacps.Config.IsCacheCoherent == 0) { + Xil_DCacheInvalidateRange((UINTPTR)p->payload, (UINTPTR)MAX_FRAME_SIZE_JUMBO); + } +#else + if (xemacpsif->emacps.Config.IsCacheCoherent == 0) { + Xil_DCacheInvalidateRange((UINTPTR)p->payload, (UINTPTR)XEMACPS_MAX_FRAME_SIZE); + } +#endif + bdindex = XEMACPS_BD_TO_INDEX(rxring, rxbd); + temp = (u32 *)rxbd; + temp++; + /* Status field should be cleared first to avoid drops */ + *temp = 0; + dsb(); + + /* Set high address when required */ +#ifdef __aarch64__ + XEmacPs_BdWrite(rxbd, XEMACPS_BD_ADDR_HI_OFFSET, + (((UINTPTR)p->payload) & ULONG64_HI_MASK) >> 32U); +#endif + /* Set address field; add WRAP bit on last descriptor */ + if (bdindex == (XLWIP_CONFIG_N_RX_DESC - 1)) { + XEmacPs_BdWrite(rxbd, XEMACPS_BD_ADDR_OFFSET, ((UINTPTR)p->payload | XEMACPS_RXBUF_WRAP_MASK)); + } else { + XEmacPs_BdWrite(rxbd, XEMACPS_BD_ADDR_OFFSET, (UINTPTR)p->payload); + } + + rx_pbufs_storage[index + bdindex] = (UINTPTR)p; + } +} + +void emacps_recv_handler(void *arg) +{ + struct pbuf *p; + XEmacPs_Bd *rxbdset, *curbdptr; + struct xemac_s *xemac; + xemacpsif_s *xemacpsif; + XEmacPs_BdRing *rxring; + volatile s32_t bd_processed; + s32_t rx_bytes, k; + u32_t bdindex; + u32_t regval; + u32_t index; + u32_t gigeversion; + + xemac = (struct xemac_s *)(arg); + xemacpsif = (xemacpsif_s *)(xemac->state); + rxring = &XEmacPs_GetRxRing(&xemacpsif->emacps); + +#if !NO_SYS + xInsideISR++; +#endif + + gigeversion = ((Xil_In32(xemacpsif->emacps.Config.BaseAddress + 0xFC)) >> 16) & 0xFFF; + index = get_base_index_rxpbufsstorage (xemacpsif); + /* + * If Reception done interrupt is asserted, call RX call back function + * to handle the processed BDs and then raise the according flag. + */ + regval = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXSR_OFFSET, regval); + if (gigeversion <= 2) { + resetrx_on_no_rxdata(xemacpsif); + } + + while(1) { + + bd_processed = XEmacPs_BdRingFromHwRx(rxring, XLWIP_CONFIG_N_RX_DESC, &rxbdset); + if (bd_processed <= 0) { + break; + } + + for (k = 0, curbdptr=rxbdset; k < bd_processed; k++) { + + bdindex = XEMACPS_BD_TO_INDEX(rxring, curbdptr); + p = (struct pbuf *)rx_pbufs_storage[index + bdindex]; + + /* + * Adjust the buffer size to the actual number of bytes received. + */ +#ifdef ZYNQMP_USE_JUMBO + rx_bytes = XEmacPs_GetRxFrameSize(&xemacpsif->emacps, curbdptr); +#else + rx_bytes = XEmacPs_BdGetLength(curbdptr); +#endif + pbuf_realloc(p, rx_bytes); + + /* Invalidate RX frame before queuing to handle + * L1 cache prefetch conditions on any architecture. + */ + if (xemacpsif->emacps.Config.IsCacheCoherent == 0) { + Xil_DCacheInvalidateRange((UINTPTR)p->payload, rx_bytes); + } + + /* store it in the receive queue, + * where it'll be processed by a different handler + */ + if (pq_enqueue(xemacpsif->recv_q, (void*)p) < 0) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + pbuf_free(p); + } + curbdptr = XEmacPs_BdRingNext( rxring, curbdptr); + } + /* free up the BD's */ + XEmacPs_BdRingFree(rxring, bd_processed, rxbdset); + setup_rx_bds(xemacpsif, rxring); + } +#if !NO_SYS + sys_sem_signal(&xemac->sem_rx_data_available); + xInsideISR--; +#endif + + return; +} + +void clean_dma_txdescs(struct xemac_s *xemac) +{ + XEmacPs_Bd bdtemplate; + XEmacPs_BdRing *txringptr; + xemacpsif_s *xemacpsif = (xemacpsif_s *)(xemac->state); + + txringptr = &XEmacPs_GetTxRing(&xemacpsif->emacps); + + XEmacPs_BdClear(&bdtemplate); + XEmacPs_BdSetStatus(&bdtemplate, XEMACPS_TXBUF_USED_MASK); + + /* + * Create the TxBD ring + */ + XEmacPs_BdRingCreate(txringptr, (UINTPTR) xemacpsif->tx_bdspace, + (UINTPTR) xemacpsif->tx_bdspace, BD_ALIGNMENT, + XLWIP_CONFIG_N_TX_DESC); + XEmacPs_BdRingClone(txringptr, &bdtemplate, XEMACPS_SEND); +} + +XStatus init_dma(struct xemac_s *xemac) +{ + XEmacPs_Bd bdtemplate; + XEmacPs_BdRing *rxringptr, *txringptr; + XEmacPs_Bd *rxbd; + struct pbuf *p; + XStatus status; + s32_t i; + u32_t bdindex; + volatile UINTPTR tempaddress; + u32_t index; + u32_t gigeversion; + XEmacPs_Bd *bdtxterminate = NULL; + XEmacPs_Bd *bdrxterminate = NULL; + u32 *temp; + + xemacpsif_s *xemacpsif = (xemacpsif_s *)(xemac->state); + struct xtopology_t *xtopologyp = &xtopology[xemac->topology_index]; + + index = get_base_index_rxpbufsstorage (xemacpsif); + gigeversion = ((Xil_In32(xemacpsif->emacps.Config.BaseAddress + 0xFC)) >> 16) & 0xFFF; + /* + * The BDs need to be allocated in uncached memory. Hence the 1 MB + * address range allocated for Bd_Space is made uncached + * by setting appropriate attributes in the translation table. + * The Bd_Space is aligned to 1MB and has a size of 1 MB. This ensures + * a reserved uncached area used only for BDs. + */ + if (bd_space_attr_set == 0) { +#if defined (ARMR5) + Xil_SetTlbAttributes((s32_t)bd_space, STRONG_ORDERD_SHARED | PRIV_RW_USER_RW); // addr, attr +#else +#if defined __aarch64__ + Xil_SetTlbAttributes((u64)bd_space, NORM_NONCACHE | INNER_SHAREABLE); +#else + Xil_SetTlbAttributes((s32_t)bd_space, DEVICE_MEMORY); // addr, attr +#endif +#endif + bd_space_attr_set = 1; + } + + rxringptr = &XEmacPs_GetRxRing(&xemacpsif->emacps); + txringptr = &XEmacPs_GetTxRing(&xemacpsif->emacps); + LWIP_DEBUGF(NETIF_DEBUG, ("rxringptr: 0x%08x\r\n", rxringptr)); + LWIP_DEBUGF(NETIF_DEBUG, ("txringptr: 0x%08x\r\n", txringptr)); + + /* Allocate 64k for Rx and Tx bds each to take care of extreme cases */ + tempaddress = (UINTPTR)&(bd_space[bd_space_index]); + xemacpsif->rx_bdspace = (void *)tempaddress; + bd_space_index += 0x10000; + tempaddress = (UINTPTR)&(bd_space[bd_space_index]); + xemacpsif->tx_bdspace = (void *)tempaddress; + bd_space_index += 0x10000; + if (gigeversion > 2) { + tempaddress = (UINTPTR)&(bd_space[bd_space_index]); + bdrxterminate = (XEmacPs_Bd *)tempaddress; + bd_space_index += 0x10000; + tempaddress = (UINTPTR)&(bd_space[bd_space_index]); + bdtxterminate = (XEmacPs_Bd *)tempaddress; + bd_space_index += 0x10000; + } + + LWIP_DEBUGF(NETIF_DEBUG, ("rx_bdspace: %p \r\n", xemacpsif->rx_bdspace)); + LWIP_DEBUGF(NETIF_DEBUG, ("tx_bdspace: %p \r\n", xemacpsif->tx_bdspace)); + + if (!xemacpsif->rx_bdspace || !xemacpsif->tx_bdspace) { + xil_printf("%s@%d: Error: Unable to allocate memory for TX/RX buffer descriptors", + __FILE__, __LINE__); + return ERR_IF; + } + + /* + * Setup RxBD space. + * + * Setup a BD template for the Rx channel. This template will be copied to + * every RxBD. We will not have to explicitly set these again. + */ + XEmacPs_BdClear(&bdtemplate); + + /* + * Create the RxBD ring + */ + + status = XEmacPs_BdRingCreate(rxringptr, (UINTPTR) xemacpsif->rx_bdspace, + (UINTPTR) xemacpsif->rx_bdspace, BD_ALIGNMENT, + XLWIP_CONFIG_N_RX_DESC); + + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error setting up RxBD space\r\n")); + return ERR_IF; + } + + status = XEmacPs_BdRingClone(rxringptr, &bdtemplate, XEMACPS_RECV); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error initializing RxBD space\r\n")); + return ERR_IF; + } + + XEmacPs_BdClear(&bdtemplate); + XEmacPs_BdSetStatus(&bdtemplate, XEMACPS_TXBUF_USED_MASK); + /* + * Create the TxBD ring + */ + status = XEmacPs_BdRingCreate(txringptr, (UINTPTR) xemacpsif->tx_bdspace, + (UINTPTR) xemacpsif->tx_bdspace, BD_ALIGNMENT, + XLWIP_CONFIG_N_TX_DESC); + + if (status != XST_SUCCESS) { + return ERR_IF; + } + + /* We reuse the bd template, as the same one will work for both rx and tx. */ + status = XEmacPs_BdRingClone(txringptr, &bdtemplate, XEMACPS_SEND); + if (status != XST_SUCCESS) { + return ERR_IF; + } + + /* + * Allocate RX descriptors, 1 RxBD at a time. + */ + for (i = 0; i < XLWIP_CONFIG_N_RX_DESC; i++) { +#ifdef ZYNQMP_USE_JUMBO + p = pbuf_alloc(PBUF_RAW, MAX_FRAME_SIZE_JUMBO, PBUF_POOL); +#else + p = pbuf_alloc(PBUF_RAW, XEMACPS_MAX_FRAME_SIZE, PBUF_POOL); +#endif + if (!p) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + printf("unable to alloc pbuf in init_dma\r\n"); + return ERR_IF; + } + status = XEmacPs_BdRingAlloc(rxringptr, 1, &rxbd); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("init_dma: Error allocating RxBD\r\n")); + pbuf_free(p); + return ERR_IF; + } + /* Enqueue to HW */ + status = XEmacPs_BdRingToHw(rxringptr, 1, rxbd); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error: committing RxBD to HW\r\n")); + pbuf_free(p); + XEmacPs_BdRingUnAlloc(rxringptr, 1, rxbd); + return ERR_IF; + } + + bdindex = XEMACPS_BD_TO_INDEX(rxringptr, rxbd); + temp = (u32 *)rxbd; + *temp = 0; + if (bdindex == (XLWIP_CONFIG_N_RX_DESC - 1)) { + *temp = 0x00000002; + } + temp++; + *temp = 0; + dsb(); +#ifdef ZYNQMP_USE_JUMBO + if (xemacpsif->emacps.Config.IsCacheCoherent == 0) { + Xil_DCacheInvalidateRange((UINTPTR)p->payload, (UINTPTR)MAX_FRAME_SIZE_JUMBO); + } +#else + if (xemacpsif->emacps.Config.IsCacheCoherent == 0) { + Xil_DCacheInvalidateRange((UINTPTR)p->payload, (UINTPTR)XEMACPS_MAX_FRAME_SIZE); + } +#endif + XEmacPs_BdSetAddressRx(rxbd, (UINTPTR)p->payload); + + rx_pbufs_storage[index + bdindex] = (UINTPTR)p; + } + XEmacPs_SetQueuePtr(&(xemacpsif->emacps), xemacpsif->emacps.RxBdRing.BaseBdAddr, 0, XEMACPS_RECV); + if (gigeversion > 2) { + XEmacPs_SetQueuePtr(&(xemacpsif->emacps), xemacpsif->emacps.TxBdRing.BaseBdAddr, 1, XEMACPS_SEND); + }else { + XEmacPs_SetQueuePtr(&(xemacpsif->emacps), xemacpsif->emacps.TxBdRing.BaseBdAddr, 0, XEMACPS_SEND); + } + if (gigeversion > 2) + { + /* + * This version of GEM supports priority queuing and the current + * driver is using tx priority queue 1 and normal rx queue for + * packet transmit and receive. The below code ensure that the + * other queue pointers are parked to known state for avoiding + * the controller to malfunction by fetching the descriptors + * from these queues. + */ + XEmacPs_BdClear(bdrxterminate); + XEmacPs_BdSetAddressRx(bdrxterminate, (XEMACPS_RXBUF_NEW_MASK | + XEMACPS_RXBUF_WRAP_MASK)); + XEmacPs_Out32((xemacpsif->emacps.Config.BaseAddress + XEMACPS_RXQ1BASE_OFFSET), + (UINTPTR)bdrxterminate); + XEmacPs_BdClear(bdtxterminate); + XEmacPs_BdSetStatus(bdtxterminate, (XEMACPS_TXBUF_USED_MASK | + XEMACPS_TXBUF_WRAP_MASK)); + XEmacPs_Out32((xemacpsif->emacps.Config.BaseAddress + XEMACPS_TXQBASE_OFFSET), + (UINTPTR)bdtxterminate); + } +#if !NO_SYS + xPortInstallInterruptHandler(xtopologyp->scugic_emac_intr, + ( Xil_InterruptHandler ) XEmacPs_IntrHandler, + (void *)&xemacpsif->emacps); +#else + /* + * Connect the device driver handler that will be called when an + * interrupt for the device occurs, the handler defined above performs + * the specific interrupt processing for the device. + */ + XScuGic_RegisterHandler(INTC_BASE_ADDR, xtopologyp->scugic_emac_intr, + (Xil_ExceptionHandler)XEmacPs_IntrHandler, + (void *)&xemacpsif->emacps); +#endif + /* + * Enable the interrupt for emacps. + */ + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, (u32) xtopologyp->scugic_emac_intr); + emac_intr_num = (u32) xtopologyp->scugic_emac_intr; + return 0; +} + +/* + * resetrx_on_no_rxdata(): + * + * It is called at regular intervals through the API xemacpsif_resetrx_on_no_rxdata + * called by the user. + * The EmacPs has a HW bug (SI# 692601) on the Rx path for heavy Rx traffic. + * Under heavy Rx traffic because of the HW bug there are times when the Rx path + * becomes unresponsive. The workaround for it is to check for the Rx path for + * traffic (by reading the stats registers regularly). If the stats register + * does not increment for sometime (proving no Rx traffic), the function resets + * the Rx data path. + * + */ + +void resetrx_on_no_rxdata(xemacpsif_s *xemacpsif) +{ + u32_t regctrl; + u32_t tempcntr; + u32_t gigeversion; + + gigeversion = ((Xil_In32(xemacpsif->emacps.Config.BaseAddress + 0xFC)) >> 16) & 0xFFF; + if (gigeversion == 2) { + tempcntr = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_RXCNT_OFFSET); + if ((!tempcntr) && (!(xemacpsif->last_rx_frms_cntr))) { + regctrl = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET); + regctrl &= (~XEMACPS_NWCTRL_RXEN_MASK); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, regctrl); + regctrl = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET); + regctrl |= (XEMACPS_NWCTRL_RXEN_MASK); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, XEMACPS_NWCTRL_OFFSET, regctrl); + } + xemacpsif->last_rx_frms_cntr = tempcntr; + } +} + +void free_txrx_pbufs(xemacpsif_s *xemacpsif) +{ + s32_t index; + s32_t index1; + struct pbuf *p; + + index1 = get_base_index_txpbufsstorage (xemacpsif); + + for (index = index1; index < (index1 + XLWIP_CONFIG_N_TX_DESC); index++) { + if (tx_pbufs_storage[index] != 0) { + p = (struct pbuf *)tx_pbufs_storage[index]; + pbuf_free(p); + tx_pbufs_storage[index] = 0; + } + } + + index1 = get_base_index_rxpbufsstorage(xemacpsif); + for (index = index1; index < (index1 + XLWIP_CONFIG_N_RX_DESC); index++) { + p = (struct pbuf *)rx_pbufs_storage[index]; + pbuf_free(p); + + } +} + +void free_onlytx_pbufs(xemacpsif_s *xemacpsif) +{ + s32_t index; + s32_t index1; + struct pbuf *p; + + index1 = get_base_index_txpbufsstorage (xemacpsif); + for (index = index1; index < (index1 + XLWIP_CONFIG_N_TX_DESC); index++) { + if (tx_pbufs_storage[index] != 0) { + p = (struct pbuf *)tx_pbufs_storage[index]; + pbuf_free(p); + tx_pbufs_storage[index] = 0; + } + } +} + +/* reset Tx and Rx DMA pointers after XEmacPs_Stop */ +void reset_dma(struct xemac_s *xemac) +{ + u8 txqueuenum; + u32_t gigeversion; + xemacpsif_s *xemacpsif = (xemacpsif_s *)(xemac->state); + XEmacPs_BdRing *txringptr = &XEmacPs_GetTxRing(&xemacpsif->emacps); + XEmacPs_BdRing *rxringptr = &XEmacPs_GetRxRing(&xemacpsif->emacps); + + XEmacPs_BdRingPtrReset(txringptr, xemacpsif->tx_bdspace); + XEmacPs_BdRingPtrReset(rxringptr, xemacpsif->rx_bdspace); + + gigeversion = ((Xil_In32(xemacpsif->emacps.Config.BaseAddress + 0xFC)) >> 16) & 0xFFF; + if (gigeversion > 2) { + txqueuenum = 1; + } else { + txqueuenum = 0; + } + + XEmacPs_SetQueuePtr(&(xemacpsif->emacps), xemacpsif->emacps.RxBdRing.BaseBdAddr, 0, XEMACPS_RECV); + XEmacPs_SetQueuePtr(&(xemacpsif->emacps), xemacpsif->emacps.TxBdRing.BaseBdAddr, txqueuenum, XEMACPS_SEND); +} + +void emac_disable_intr(void) +{ + XScuGic_DisableIntr(INTC_DIST_BASE_ADDR, emac_intr_num); +} + +void emac_enable_intr(void) +{ + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, emac_intr_num); +} |