summaryrefslogtreecommitdiff
path: root/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_dma.c
diff options
context:
space:
mode:
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.c930
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);
+}