diff options
Diffstat (limited to 'embeddedsw/ThirdParty/sw_services/lwip211/src')
11 files changed, 4024 insertions, 0 deletions
diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xadapter.h b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xadapter.h new file mode 100644 index 0000000..4d32b7f --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xadapter.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 - 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. + * + */ + +#ifndef __XADAPTER_H_ +#define __XADAPTER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwipopts.h" + +#if !NO_SYS +#include "lwip/sys.h" +#endif + +#include "lwip/netif.h" +#include "lwip/ip.h" + +#include "netif/xtopology.h" + +struct xemac_s { + enum xemac_types type; + int topology_index; + void *state; +#if !NO_SYS + sys_sem_t sem_rx_data_available; +#if defined(__arm__) && !defined(ARMR5) + TimerHandle_t xTimer; +#endif +#endif +}; + +enum ethernet_link_status { + ETH_LINK_UNDEFINED = 0, + ETH_LINK_UP, + ETH_LINK_DOWN, + ETH_LINK_NEGOTIATING +}; + +void eth_link_detect(struct netif *netif); +void lwip_raw_init(); +int xemacif_input(struct netif *netif); +void xemacif_input_thread(struct netif *netif); +struct netif * xemac_add(struct netif *netif, + ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw, + unsigned char *mac_ethernet_address, + UINTPTR mac_baseaddr); +#if defined (__arm__) || defined (__aarch64__) +void xemacpsif_resetrx_on_no_rxdata(struct netif *netif); +#endif + +/* global lwip debug variable used for debugging */ +extern int lwip_runtime_debug; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xemacpsif.h b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xemacpsif.h new file mode 100644 index 0000000..f108920 --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xemacpsif.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2010 - 2019 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. + * + */ + +#ifndef __NETIF_XEMACPSIF_H__ +#define __NETIF_XEMACPSIF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xlwipconfig.h" +#include "lwip/netif.h" +#include "netif/etharp.h" +#include "lwip/sys.h" +#include "netif/xadapter.h" + +#include "xstatus.h" +#include "sleep.h" +#include "xparameters.h" +#include "xparameters_ps.h" /* defines XPAR values */ +#include "xil_types.h" +#include "xil_assert.h" +#include "xil_io.h" +#include "xil_exception.h" +#include "xpseudo_asm.h" +#include "xil_cache.h" +#include "xil_printf.h" +#include "xscugic.h" +#include "xemacps.h" /* defines XEmacPs API */ + +#include "netif/xpqueue.h" +#include "xlwipconfig.h" + +#if defined (__aarch64__) && (EL1_NONSECURE == 1) +#include "xil_smc.h" +#endif + +#define ZYNQ_EMACPS_0_BASEADDR 0xE000B000 +#define ZYNQ_EMACPS_1_BASEADDR 0xE000C000 + +#define ZYNQMP_EMACPS_0_BASEADDR 0xFF0B0000 +#define ZYNQMP_EMACPS_1_BASEADDR 0xFF0C0000 +#define ZYNQMP_EMACPS_2_BASEADDR 0xFF0D0000 +#define ZYNQMP_EMACPS_3_BASEADDR 0xFF0E0000 + +#define CRL_APB_GEM0_REF_CTRL 0xFF5E0050 +#define CRL_APB_GEM1_REF_CTRL 0xFF5E0054 +#define CRL_APB_GEM2_REF_CTRL 0xFF5E0058 +#define CRL_APB_GEM3_REF_CTRL 0xFF5E005C + +#define CRL_APB_GEM_DIV0_MASK 0x00003F00 +#define CRL_APB_GEM_DIV0_SHIFT 8 +#define CRL_APB_GEM_DIV1_MASK 0x003F0000 +#define CRL_APB_GEM_DIV1_SHIFT 16 + +#define VERSAL_EMACPS_0_BASEADDR 0xFF0C0000 +#define VERSAL_EMACPS_1_BASEADDR 0xFF0D0000 + +#define VERSAL_CRL_GEM0_REF_CTRL 0xFF5E0118 +#define VERSAL_CRL_GEM1_REF_CTRL 0xFF5E011C + +#define VERSAL_CRL_GEM_DIV_MASK 0x0003FF00 +#define VERSAL_CRL_APB_GEM_DIV_SHIFT 8 + +#if defined (ARMR5) || (__aarch64__) || (ARMA53_32) || (__MICROBLAZE__) +#if defined (USE_JUMBO_FRAMES) +#define ZYNQMP_USE_JUMBO +#endif +#endif + +#define GEM_VERSION_ZYNQMP 7 +#define GEM_VERSION_VERSAL 0x107 + +#define MAX_FRAME_SIZE_JUMBO (XEMACPS_MTU_JUMBO + XEMACPS_HDR_SIZE + XEMACPS_TRL_SIZE) + +void xemacpsif_setmac(u32_t index, u8_t *addr); +u8_t* xemacpsif_getmac(u32_t index); +err_t xemacpsif_init(struct netif *netif); +s32_t xemacpsif_input(struct netif *netif); + +/* xaxiemacif_hw.c */ +void xemacps_error_handler(XEmacPs * Temac); + +/* structure within each netif, encapsulating all information required for + * using a particular temac instance + */ +typedef struct { + XEmacPs emacps; + + /* queue to store overflow packets */ + pq_queue_t *recv_q; + pq_queue_t *send_q; + + /* pointers to memory holding buffer descriptors (used only with SDMA) */ + void *rx_bdspace; + void *tx_bdspace; + + unsigned int last_rx_frms_cntr; + +} xemacpsif_s; + +extern xemacpsif_s xemacpsif; + +s32_t is_tx_space_available(xemacpsif_s *emac); + +/* xemacpsif_dma.c */ + +void process_sent_bds(xemacpsif_s *xemacpsif, XEmacPs_BdRing *txring); +u32_t phy_setup_emacps (XEmacPs *xemacpsp, u32_t phy_addr); +void detect_phy(XEmacPs *xemacpsp); +void emacps_send_handler(void *arg); +#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 +void emacps_recv_handler(void *arg); +void emacps_error_handler(void *arg,u8 Direction, u32 ErrorWord); +void setup_rx_bds(xemacpsif_s *xemacpsif, XEmacPs_BdRing *rxring); +void HandleTxErrors(struct xemac_s *xemac); +void HandleEmacPsError(struct xemac_s *xemac); +XEmacPs_Config *xemacps_lookup_config(unsigned mac_base); +void init_emacps(xemacpsif_s *xemacps, struct netif *netif); +void setup_isr (struct xemac_s *xemac); +XStatus init_dma(struct xemac_s *xemac); +void start_emacps (xemacpsif_s *xemacps); +void free_txrx_pbufs(xemacpsif_s *xemacpsif); +void free_onlytx_pbufs(xemacpsif_s *xemacpsif); +void init_emacps_on_error (xemacpsif_s *xemacps, struct netif *netif); +void clean_dma_txdescs(struct xemac_s *xemac); +void resetrx_on_no_rxdata(xemacpsif_s *xemacpsif); +void reset_dma(struct xemac_s *xemac); + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_XAXIEMACIF_H__ */ diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xpqueue.h b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xpqueue.h new file mode 100644 index 0000000..132f02f --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xpqueue.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007 - 2019 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. + * + */ + +#ifndef __LWIP_PBUF_QUEUE_H_ +#define __LWIP_PBUF_QUEUE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define PQ_QUEUE_SIZE 4096 + +typedef struct { + void *data[PQ_QUEUE_SIZE]; + int head, tail, len; +} pq_queue_t; + +pq_queue_t* pq_create_queue(); +int pq_enqueue(pq_queue_t *q, void *p); +void* pq_dequeue(pq_queue_t *q); +int pq_qlength(pq_queue_t *q); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xtopology.h b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xtopology.h new file mode 100644 index 0000000..51718f1 --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/include/netif/xtopology.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 - 2019 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. + * + */ + +#ifndef __XTOPOLOGY_H_ +#define __XTOPOLOGY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xil_types.h" + +enum xemac_types { xemac_type_unknown = -1, xemac_type_xps_emaclite, xemac_type_xps_ll_temac, xemac_type_axi_ethernet, xemac_type_emacps }; + +struct xtopology_t { + UINTPTR emac_baseaddr; + enum xemac_types emac_type; + UINTPTR intc_baseaddr; + unsigned intc_emac_intr; /* valid only for xemac_type_xps_emaclite */ + UINTPTR scugic_baseaddr; /* valid only for Zynq */ + unsigned scugic_emac_intr; /* valid only for GEM */ +}; + +extern int xtopology_n_emacs; +extern struct xtopology_t xtopology[]; + +int xtopology_find_index(unsigned base); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xadapter.c b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xadapter.c new file mode 100644 index 0000000..9594ff5 --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xadapter.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2007 - 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 "xlwipconfig.h" +#include "xemac_ieee_reg.h" + +#if !NO_SYS +#endif + +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/ip.h" +#include "lwip/tcp.h" +#include "lwip/udp.h" +#include "lwip/priv/tcp_priv.h" + +#include "netif/etharp.h" +#include "netif/xadapter.h" + +#ifdef XLWIP_CONFIG_INCLUDE_EMACLITE +#include "netif/xemacliteif.h" +#endif + +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET +#include "netif/xaxiemacif.h" +#endif + +#ifdef XLWIP_CONFIG_INCLUDE_GEM +#include "netif/xemacpsif.h" +#endif + +#if !NO_SYS +#include "lwip/tcpip.h" + +#define THREAD_STACKSIZE 256 +#define LINK_DETECT_THREAD_INTERVAL 1000 /* one second */ + +void link_detect_thread(void *p); +#endif + +/* global lwip debug variable used for debugging */ +int lwip_runtime_debug = 0; + +enum ethernet_link_status eth_link_status = ETH_LINK_UNDEFINED; +u32_t phyaddrforemac; + +void +lwip_raw_init() +{ + ip_init(); /* Doesn't do much, it should be called to handle future changes. */ +#if LWIP_UDP + udp_init(); /* Clears the UDP PCB list. */ +#endif +#if LWIP_TCP + tcp_init(); /* Clears the TCP PCB list and clears some internal TCP timers. */ + /* Note: you must call tcp_fasttmr() and tcp_slowtmr() at the */ + /* predefined regular intervals after this initialization. */ +#endif +} + +static enum xemac_types +find_mac_type(unsigned base) +{ + int i; + + for (i = 0; i < xtopology_n_emacs; i++) { + if (xtopology[i].emac_baseaddr == base) + return xtopology[i].emac_type; + } + + return xemac_type_unknown; +} + +int +xtopology_find_index(unsigned base) +{ + int i; + + for (i = 0; i < xtopology_n_emacs; i++) { + if (xtopology[i].emac_baseaddr == base) + return i; + } + + return -1; +} + +/* + * xemac_add: this is a wrapper around lwIP's netif_add function. + * The objective is to provide portability between the different Xilinx MAC's + * This function can be used to add both xps_ethernetlite and xps_ll_temac + * based interfaces + */ +struct netif * +xemac_add(struct netif *netif, + ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw, + unsigned char *mac_ethernet_address, + UINTPTR mac_baseaddr) +{ + int i; + +#if !NO_SYS + /* Start thread to detect link periodically for Hot Plug autodetect */ + sys_thread_new("link_detect_thread", link_detect_thread, netif, + THREAD_STACKSIZE, tskIDLE_PRIORITY); +#endif + + /* set mac address */ + netif->hwaddr_len = 6; + for (i = 0; i < 6; i++) + netif->hwaddr[i] = mac_ethernet_address[i]; + + /* initialize based on MAC type */ + switch (find_mac_type(mac_baseaddr)) { + case xemac_type_xps_emaclite: +#ifdef XLWIP_CONFIG_INCLUDE_EMACLITE + return netif_add(netif, ipaddr, netmask, gw, + (void*)mac_baseaddr, + xemacliteif_init, +#if NO_SYS + ethernet_input +#else + tcpip_input +#endif + ); +#else + return NULL; +#endif + case xemac_type_axi_ethernet: +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET + return netif_add(netif, ipaddr, netmask, gw, + (void*)mac_baseaddr, + xaxiemacif_init, +#if NO_SYS + ethernet_input +#else + tcpip_input +#endif + ); +#else + return NULL; +#endif +#if defined (__arm__) || defined (__aarch64__) + case xemac_type_emacps: +#ifdef XLWIP_CONFIG_INCLUDE_GEM + return netif_add(netif, ipaddr, netmask, gw, + (void*)mac_baseaddr, + xemacpsif_init, +#if NO_SYS + ethernet_input +#else + tcpip_input +#endif + + ); +#endif +#endif + default: + xil_printf("unable to determine type of EMAC with baseaddress 0x%08x\r\n", + mac_baseaddr); + return NULL; + } +} + +#if !NO_SYS +/* + * The input thread calls lwIP to process any received packets. + * This thread waits until a packet is received (sem_rx_data_available), + * and then calls xemacif_input which processes 1 packet at a time. + */ +void +xemacif_input_thread(struct netif *netif) +{ + struct xemac_s *emac = (struct xemac_s *)netif->state; + while (1) { + /* sleep until there are packets to process + * This semaphore is set by the packet receive interrupt + * routine. + */ + sys_sem_wait(&emac->sem_rx_data_available); + + /* move all received packets to lwIP */ + xemacif_input(netif); + } +} +#endif + +int +xemacif_input(struct netif *netif) +{ + struct xemac_s *emac = (struct xemac_s *)netif->state; + + int n_packets = 0; + + switch (emac->type) { + case xemac_type_xps_emaclite: +#ifdef XLWIP_CONFIG_INCLUDE_EMACLITE + n_packets = xemacliteif_input(netif); + break; +#else + print("incorrect configuration: xps_ethernetlite drivers not present?"); + while(1); + return 0; +#endif + case xemac_type_axi_ethernet: +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET + n_packets = xaxiemacif_input(netif); + break; +#else + print("incorrect configuration: axi_ethernet drivers not present?"); + while(1); + return 0; +#endif +#if defined (__arm__) || defined (__aarch64__) + case xemac_type_emacps: +#ifdef XLWIP_CONFIG_INCLUDE_GEM + n_packets = xemacpsif_input(netif); + break; +#else + xil_printf("incorrect configuration: ps7_ethernet drivers not present?\r\n"); + while(1); + return 0; +#endif +#endif + default: + print("incorrect configuration: unknown temac type"); + while(1); + return 0; + } + + return n_packets; +} + +#if defined(XLWIP_CONFIG_INCLUDE_GEM) +static u32_t phy_link_detect(XEmacPs *xemacp, u32_t phy_addr) +{ + u16_t status; + + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XEmacPs_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + XEmacPs_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + if (status & IEEE_STAT_LINK_STATUS) + return 1; + return 0; +} +#elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) +static u32_t phy_link_detect(XAxiEthernet *xemacp, u32_t phy_addr) +{ + u16_t status; + + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XAxiEthernet_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + XAxiEthernet_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + if (status & IEEE_STAT_LINK_STATUS) + return 1; + return 0; +} +#elif defined(XLWIP_CONFIG_INCLUDE_EMACLITE) +static u32_t phy_link_detect(XEmacLite *xemacp, u32_t phy_addr) +{ + u16_t status; + + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XEmacLite_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + XEmacLite_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + if (status & IEEE_STAT_LINK_STATUS) + return 1; + return 0; +} +#endif + +#if defined(XLWIP_CONFIG_INCLUDE_GEM) +static u32_t phy_autoneg_status(XEmacPs *xemacp, u32_t phy_addr) +{ + u16_t status; + + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XEmacPs_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + XEmacPs_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) + return 1; + return 0; +} +#elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) +static u32_t phy_autoneg_status(XAxiEthernet *xemacp, u32_t phy_addr) +{ + u16_t status; + + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XAxiEthernet_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + XAxiEthernet_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) + return 1; + return 0; +} +#elif defined(XLWIP_CONFIG_INCLUDE_EMACLITE) +static u32_t phy_autoneg_status(XEmacLite *xemacp, u32_t phy_addr) +{ + u16_t status; + + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XEmacLite_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + XEmacLite_PhyRead(xemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) + return 1; + return 0; +} +#endif + +void eth_link_detect(struct netif *netif) +{ + u32_t link_speed, phy_link_status; + struct xemac_s *xemac = (struct xemac_s *)(netif->state); + +#if defined(XLWIP_CONFIG_INCLUDE_GEM) + xemacpsif_s *xemacs = (xemacpsif_s *)(xemac->state); + XEmacPs *xemacp = &xemacs->emacps; +#elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) + xaxiemacif_s *xemacs = (xaxiemacif_s *)(xemac->state); + XAxiEthernet *xemacp = &xemacs->axi_ethernet; +#elif defined(XLWIP_CONFIG_INCLUDE_EMACLITE) + xemacliteif_s *xemacs = (xemacliteif_s *)(xemac->state); + XEmacLite *xemacp = xemacs->instance; +#endif + + if ((xemacp->IsReady != (u32)XIL_COMPONENT_IS_READY) || + (eth_link_status == ETH_LINK_UNDEFINED)) + return; + + phy_link_status = phy_link_detect(xemacp, phyaddrforemac); + + if ((eth_link_status == ETH_LINK_UP) && (!phy_link_status)) + eth_link_status = ETH_LINK_DOWN; + + switch (eth_link_status) { + case ETH_LINK_UNDEFINED: + case ETH_LINK_UP: + return; + case ETH_LINK_DOWN: + netif_set_link_down(netif); + eth_link_status = ETH_LINK_NEGOTIATING; + xil_printf("Ethernet Link down\r\n"); + break; + case ETH_LINK_NEGOTIATING: + if (phy_link_status && + phy_autoneg_status(xemacp, phyaddrforemac)) { + + /* Initiate Phy setup to get link speed */ +#if defined(XLWIP_CONFIG_INCLUDE_GEM) + link_speed = phy_setup_emacps(xemacp, + phyaddrforemac); + XEmacPs_SetOperatingSpeed(xemacp, link_speed); +#elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) + link_speed = phy_setup_axiemac(xemacp); + XAxiEthernet_SetOperatingSpeed(xemacp, + link_speed); +#endif + netif_set_link_up(netif); + eth_link_status = ETH_LINK_UP; + xil_printf("Ethernet Link up\r\n"); + } + break; + } +} + +#if !NO_SYS +void link_detect_thread(void *p) +{ + struct netif *netif = (struct netif *) p; + + while (1) { + /* Call eth_link_detect() every second to detect Ethernet link + * change. + */ + eth_link_detect(netif); + vTaskDelay(LINK_DETECT_THREAD_INTERVAL / portTICK_RATE_MS); + } +} +#endif diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemac_ieee_reg.h b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemac_ieee_reg.h new file mode 100644 index 0000000..4c240a9 --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemac_ieee_reg.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018 - 2019 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. + * + */ + +#ifndef __XEMAC_IEEE_REGS_H_ +#define __XEMAC_IEEE_REGS_H_ + +/* Advertisement control register. */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ + + +#define ADVERTISE_100_AND_10 (ADVERTISE_10FULL | ADVERTISE_100FULL | \ + ADVERTISE_10HALF | ADVERTISE_100HALF) +#define ADVERTISE_100 (ADVERTISE_100FULL | ADVERTISE_100HALF) +#define ADVERTISE_10 (ADVERTISE_10FULL | ADVERTISE_10HALF) + +#define ADVERTISE_1000 0x0300 + + +#define IEEE_CONTROL_REG_OFFSET 0 +#define IEEE_STATUS_REG_OFFSET 1 +#define IEEE_AUTONEGO_ADVERTISE_REG 4 +#define IEEE_PARTNER_ABILITIES_1_REG_OFFSET 5 +#define IEEE_PARTNER_ABILITIES_2_REG_OFFSET 8 +#define IEEE_PARTNER_ABILITIES_3_REG_OFFSET 10 +#define IEEE_1000_ADVERTISE_REG_OFFSET 9 +#define IEEE_MMD_ACCESS_CONTROL_REG 13 +#define IEEE_MMD_ACCESS_ADDRESS_DATA_REG 14 +#define IEEE_COPPER_SPECIFIC_CONTROL_REG 16 +#define IEEE_SPECIFIC_STATUS_REG 17 +#define IEEE_COPPER_SPECIFIC_STATUS_REG_2 19 +#define IEEE_EXT_PHY_SPECIFIC_CONTROL_REG 20 +#define IEEE_CONTROL_REG_MAC 21 +#define IEEE_PAGE_ADDRESS_REGISTER 22 + +#define IEEE_CTRL_1GBPS_LINKSPEED_MASK 0x2040 +#define IEEE_CTRL_LINKSPEED_MASK 0x0040 +#define IEEE_CTRL_LINKSPEED_1000M 0x0040 +#define IEEE_CTRL_LINKSPEED_100M 0x2000 +#define IEEE_CTRL_LINKSPEED_10M 0x0000 +#define IEEE_CTRL_FULL_DUPLEX 0x100 +#define IEEE_CTRL_RESET_MASK 0x8000 +#define IEEE_CTRL_AUTONEGOTIATE_ENABLE 0x1000 +#define IEEE_STAT_AUTONEGOTIATE_CAPABLE 0x0008 +#define IEEE_STAT_AUTONEGOTIATE_COMPLETE 0x0020 +#define IEEE_STAT_AUTONEGOTIATE_RESTART 0x0200 +#define IEEE_STAT_LINK_STATUS 0x0004 +#define IEEE_STAT_1GBPS_EXTENSIONS 0x0100 +#define IEEE_AN1_ABILITY_MASK 0x1FE0 +#define IEEE_AN3_ABILITY_MASK_1GBPS 0x0C00 +#define IEEE_AN1_ABILITY_MASK_100MBPS 0x0380 +#define IEEE_AN1_ABILITY_MASK_10MBPS 0x0060 +#define IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK 0x0030 + +#define IEEE_SPEED_MASK 0xC000 +#define IEEE_SPEED_1000 0x8000 +#define IEEE_SPEED_100 0x4000 + +#define IEEE_ASYMMETRIC_PAUSE_MASK 0x0800 +#define IEEE_PAUSE_MASK 0x0400 +#define IEEE_AUTONEG_ERROR_MASK 0x8000 + +#define IEEE_MMD_ACCESS_CTRL_DEVAD_MASK 0x1F +#define IEEE_MMD_ACCESS_CTRL_PIDEVAD_MASK 0x801F +#define IEEE_MMD_ACCESS_CTRL_NOPIDEVAD_MASK 0x401F + +#endif /* __XEMAC_IEEE_REGS_H_ */ diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif.c b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif.c new file mode 100644 index 0000000..1bf3abb --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif.c @@ -0,0 +1,795 @@ +/* + * 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 <stdio.h> +#include <string.h> + +#include <xparameters.h> +#include "lwipopts.h" +#include "xlwipconfig.h" +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/igmp.h" + +#include "netif/etharp.h" +#include "netif/xemacpsif.h" +#include "netif/xadapter.h" +#include "netif/xpqueue.h" +#include "xparameters.h" +#include "xscugic.h" +#include "xemacps.h" + +#if LWIP_IPV6 +#include "lwip/ethip6.h" +#endif + + +/* Define those to better describe your network interface. */ +#define IFNAME0 't' +#define IFNAME1 'e' + +#if LWIP_IGMP +static err_t xemacpsif_mac_filter_update (struct netif *netif, + ip_addr_t *group, u8_t action); + +static u8_t xemacps_mcast_entry_mask = 0; +#endif + +#if LWIP_IPV6 && LWIP_IPV6_MLD +static err_t xemacpsif_mld6_mac_filter_update (struct netif *netif, + ip_addr_t *group, u8_t action); + +static u8_t xemacps_mld6_mcast_entry_mask; +#endif + +XEmacPs_Config *mac_config; +struct netif *NetIf; + +#if !NO_SYS +#if defined(__arm__) && !defined(ARMR5) +int32_t lExpireCounter = 0; +#define RESETRXTIMEOUT 10 +#endif +#endif + +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +extern volatile u32_t notifyinfo[4*XLWIP_CONFIG_N_TX_DESC]; +#endif + +/* + * this function is always called with interrupts off + * this function also assumes that there are available BD's + */ +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +static err_t _unbuffered_low_level_output(xemacpsif_s *xemacpsif, + struct pbuf *p, u32_t block_till_tx_complete, u32_t *to_block_index ) +#else +static err_t _unbuffered_low_level_output(xemacpsif_s *xemacpsif, + struct pbuf *p) +#endif +{ + XStatus status = 0; + err_t err = ERR_MEM; + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + if (block_till_tx_complete == 1) { + status = emacps_sgsend(xemacpsif, p, 1, to_block_index); + } else { + status = emacps_sgsend(xemacpsif, p, 0, to_block_index); + } +#else + status = emacps_sgsend(xemacpsif, p); +#endif + if (status != XST_SUCCESS) { +#if LINK_STATS + lwip_stats.link.drop++; +#endif + } else { + err = ERR_OK; + } + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + +#if LINK_STATS + lwip_stats.link.xmit++; +#endif /* LINK_STATS */ + + return err; + +} + +/* + * low_level_output(): + * + * Should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + */ + +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ + err_t err = ERR_MEM; + s32_t freecnt; + XEmacPs_BdRing *txring; +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + u32_t notfifyblocksleepcntr; + u32_t to_block_index; +#endif + + SYS_ARCH_DECL_PROTECT(lev); + struct xemac_s *xemac = (struct xemac_s *)(netif->state); + xemacpsif_s *xemacpsif = (xemacpsif_s *)(xemac->state); + + SYS_ARCH_PROTECT(lev); + /* check if space is available to send */ + freecnt = is_tx_space_available(xemacpsif); + if (freecnt <= 5) { + txring = &(XEmacPs_GetTxRing(&xemacpsif->emacps)); + process_sent_bds(xemacpsif, txring); + } + + if (is_tx_space_available(xemacpsif)) { +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + if (netif_is_opt_block_tx_set(netif, NETIF_ENABLE_BLOCKING_TX_FOR_PACKET)) { + err = _unbuffered_low_level_output(xemacpsif, p, 1, &to_block_index); + } else { + err = _unbuffered_low_level_output(xemacpsif, p, 0, &to_block_index); + } +#else + err = _unbuffered_low_level_output(xemacpsif, p); +#endif + } else { +#if LINK_STATS + lwip_stats.link.drop++; +#endif + printf("pack dropped, no space\r\n"); + SYS_ARCH_UNPROTECT(lev); + goto return_pack_dropped; + } + SYS_ARCH_UNPROTECT(lev); + +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + if (netif_is_opt_block_tx_set(netif, NETIF_ENABLE_BLOCKING_TX_FOR_PACKET)) { + /* Wait for approx 1 second before timing out */ + notfifyblocksleepcntr = 900000; + while(notifyinfo[to_block_index] == 1) { + usleep(1); + notfifyblocksleepcntr--; + if (notfifyblocksleepcntr <= 0) { + err = ERR_TIMEOUT; + break; + } + } + } + netif_clear_opt_block_tx(netif, NETIF_ENABLE_BLOCKING_TX_FOR_PACKET); +#endif +return_pack_dropped: + return err; +} + +/* + * low_level_input(): + * + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + */ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct xemac_s *xemac = (struct xemac_s *)(netif->state); + xemacpsif_s *xemacpsif = (xemacpsif_s *)(xemac->state); + struct pbuf *p; + + /* see if there is data to process */ + if (pq_qlength(xemacpsif->recv_q) == 0) + return NULL; + + /* return one packet from receive q */ + p = (struct pbuf *)pq_dequeue(xemacpsif->recv_q); + return p; +} + +/* + * xemacpsif_output(): + * + * This function is called by the TCP/IP stack when an IP packet + * should be sent. It calls the function called low_level_output() to + * do the actual transmission of the packet. + * + */ + +static err_t xemacpsif_output(struct netif *netif, struct pbuf *p, + const ip_addr_t *ipaddr) +{ + /* resolve hardware address, then send (or queue) packet */ + return etharp_output(netif, p, ipaddr); +} + +/* + * xemacpsif_input(): + * + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. + * + * Returns the number of packets read (max 1 packet on success, + * 0 if there are no packets) + * + */ + +s32_t xemacpsif_input(struct netif *netif) +{ + struct eth_hdr *ethhdr; + struct pbuf *p; + SYS_ARCH_DECL_PROTECT(lev); + +#if !NO_SYS + while (1) +#endif + { + /* move received packet into a new pbuf */ + SYS_ARCH_PROTECT(lev); + p = low_level_input(netif); + SYS_ARCH_UNPROTECT(lev); + + /* no packet could be read, silently ignore this */ + if (p == NULL) { + return 0; + } + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + #if LINK_STATS + lwip_stats.link.recv++; + #endif /* LINK_STATS */ + + switch (htons(ethhdr->type)) { + /* IP or ARP packet? */ + case ETHTYPE_IP: + case ETHTYPE_ARP: + #if LWIP_IPV6 + /*IPv6 Packet?*/ + case ETHTYPE_IPV6: + #endif + #if PPPOE_SUPPORT + /* PPPoE packet? */ + case ETHTYPE_PPPOEDISC: + case ETHTYPE_PPPOE: + #endif /* PPPOE_SUPPORT */ + /* full packet send to tcpip_thread to process */ + if (netif->input(p, netif) != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("xemacpsif_input: IP input error\r\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } + } + + return 1; +} + +#if !NO_SYS +#if defined(__arm__) && !defined(ARMR5) +void vTimerCallback( TimerHandle_t pxTimer ) +{ + /* Do something if the pxTimer parameter is NULL */ + configASSERT(pxTimer); + + lExpireCounter++; + /* If the timer has expired 100 times then reset RX */ + if(lExpireCounter >= RESETRXTIMEOUT) { + lExpireCounter = 0; + xemacpsif_resetrx_on_no_rxdata(NetIf); + } +} +#endif +#endif + +static err_t low_level_init(struct netif *netif) +{ + UINTPTR mac_address = (UINTPTR)(netif->state); + struct xemac_s *xemac; + xemacpsif_s *xemacpsif; + u32 dmacrreg; + + s32_t status = XST_SUCCESS; + + NetIf = netif; + + xemacpsif = mem_malloc(sizeof *xemacpsif); + if (xemacpsif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("xemacpsif_init: out of memory\r\n")); + return ERR_MEM; + } + + xemac = mem_malloc(sizeof *xemac); + if (xemac == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("xemacpsif_init: out of memory\r\n")); + return ERR_MEM; + } + + xemac->state = (void *)xemacpsif; + xemac->topology_index = xtopology_find_index(mac_address); + xemac->type = xemac_type_emacps; + + xemacpsif->send_q = NULL; + xemacpsif->recv_q = pq_create_queue(); + if (!xemacpsif->recv_q) + return ERR_MEM; + + /* maximum transfer unit */ +#ifdef ZYNQMP_USE_JUMBO + netif->mtu = XEMACPS_MTU_JUMBO - XEMACPS_HDR_SIZE; +#else + netif->mtu = XEMACPS_MTU - XEMACPS_HDR_SIZE; +#endif + +#if LWIP_IGMP + netif->igmp_mac_filter = xemacpsif_mac_filter_update; +#endif + +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif->mld_mac_filter = xemacpsif_mld6_mac_filter_update; +#endif + + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | + NETIF_FLAG_LINK_UP; + +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif->flags |= NETIF_FLAG_MLD6; +#endif + +#if LWIP_IGMP + netif->flags |= NETIF_FLAG_IGMP; +#endif + +#if !NO_SYS + sys_sem_new(&xemac->sem_rx_data_available, 0); +#endif + /* obtain config of this emac */ + mac_config = (XEmacPs_Config *)xemacps_lookup_config((unsigned)(UINTPTR)netif->state); + +#if defined (__aarch64__) && (EL1_NONSECURE == 1) + /* Request device to indicate that this library is using it */ + if (mac_config->BaseAddress == VERSAL_EMACPS_0_BASEADDR) { + Xil_Smc(PM_REQUEST_DEVICE_SMC_FID, DEV_GEM_0, 1, 0, 100, 1, 0, 0); + } + if (mac_config->BaseAddress == VERSAL_EMACPS_0_BASEADDR) { + Xil_Smc(PM_REQUEST_DEVICE_SMC_FID, DEV_GEM_1, 1, 0, 100, 1, 0, 0); + } +#endif + + status = XEmacPs_CfgInitialize(&xemacpsif->emacps, mac_config, + mac_config->BaseAddress); + if (status != XST_SUCCESS) { + xil_printf("In %s:EmacPs Configuration Failed....\r\n", __func__); + } + + /* initialize the mac */ + init_emacps(xemacpsif, netif); + + dmacrreg = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_DMACR_OFFSET); + dmacrreg = dmacrreg | (0x00000010); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_DMACR_OFFSET, dmacrreg); +#if !NO_SYS +#if defined(__arm__) && !defined(ARMR5) + /* Freertos tick is 10ms by default; set period to the same */ + xemac->xTimer = xTimerCreate("Timer", 10, pdTRUE, ( void * ) 1, vTimerCallback); + if (xemac->xTimer == NULL) { + xil_printf("In %s:Timer creation failed....\r\n", __func__); + } else { + if(xTimerStart(xemac->xTimer, 0) != pdPASS) { + xil_printf("In %s:Timer start failed....\r\n", __func__); + } + } +#endif +#endif + setup_isr(xemac); + init_dma(xemac); + start_emacps(xemacpsif); + + /* replace the state in netif (currently the emac baseaddress) + * with the mac instance pointer. + */ + netif->state = (void *)xemac; + + return ERR_OK; +} + +void HandleEmacPsError(struct xemac_s *xemac) +{ + xemacpsif_s *xemacpsif; + s32_t status = XST_SUCCESS; + u32 dmacrreg; + + SYS_ARCH_DECL_PROTECT(lev); + SYS_ARCH_PROTECT(lev); + + xemacpsif = (xemacpsif_s *)(xemac->state); + free_txrx_pbufs(xemacpsif); + status = XEmacPs_CfgInitialize(&xemacpsif->emacps, mac_config, + mac_config->BaseAddress); + if (status != XST_SUCCESS) { + xil_printf("In %s:EmacPs Configuration Failed....\r\n", __func__); + } + /* initialize the mac */ + init_emacps_on_error(xemacpsif, NetIf); + dmacrreg = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_DMACR_OFFSET); + dmacrreg = dmacrreg | (0x01000000); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_DMACR_OFFSET, dmacrreg); + setup_isr(xemac); + init_dma(xemac); + start_emacps(xemacpsif); + + SYS_ARCH_UNPROTECT(lev); +} + +void HandleTxErrors(struct xemac_s *xemac) +{ + xemacpsif_s *xemacpsif; + u32 netctrlreg; + + SYS_ARCH_DECL_PROTECT(lev); + SYS_ARCH_PROTECT(lev); + xemacpsif = (xemacpsif_s *)(xemac->state); + netctrlreg = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET); + netctrlreg = netctrlreg & (~XEMACPS_NWCTRL_TXEN_MASK); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, netctrlreg); + free_onlytx_pbufs(xemacpsif); + + clean_dma_txdescs(xemac); + netctrlreg = XEmacPs_ReadReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET); + netctrlreg = netctrlreg | (XEMACPS_NWCTRL_TXEN_MASK); + XEmacPs_WriteReg(xemacpsif->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, netctrlreg); + SYS_ARCH_UNPROTECT(lev); +} + +#if LWIP_IPV6 && LWIP_IPV6_MLD +static u8_t xemacpsif_ip6_addr_ismulticast(ip6_addr_t* ip_addr) +{ + if(ip6_addr_ismulticast_linklocal(ip_addr)|| + ip6_addr_ismulticast_iflocal(ip_addr) || + ip6_addr_ismulticast_adminlocal(ip_addr)|| + ip6_addr_ismulticast_sitelocal(ip_addr) || + ip6_addr_ismulticast_orglocal(ip_addr) || + ip6_addr_ismulticast_global(ip_addr)) { + /*Return TRUE if IPv6 is Multicast type*/ + return TRUE; + } else { + return FALSE; + } +} + +static void xemacpsif_mld6_mac_hash_update (struct netif *netif, u8_t *ip_addr, + u8_t action) +{ + u8_t multicast_mac_addr[6]; + struct xemac_s *xemac = (struct xemac_s *) (netif->state); + xemacpsif_s *xemacpsif = (xemacpsif_s *) (xemac->state); + XEmacPs_BdRing *txring; + txring = &(XEmacPs_GetTxRing(&xemacpsif->emacps)); + + multicast_mac_addr[0] = LL_IP6_MULTICAST_ADDR_0; + multicast_mac_addr[1] = LL_IP6_MULTICAST_ADDR_1; + multicast_mac_addr[2] = ip_addr[12]; + multicast_mac_addr[3] = ip_addr[13]; + multicast_mac_addr[4] = ip_addr[14]; + multicast_mac_addr[5] = ip_addr[15]; + + /* Wait till all sent packets are acknowledged from HW */ + while(txring->HwCnt); + + SYS_ARCH_DECL_PROTECT(lev); + + SYS_ARCH_PROTECT(lev); + + /* Stop Ethernet */ + XEmacPs_Stop(&xemacpsif->emacps); + + if (action == NETIF_ADD_MAC_FILTER) { + /* Set Mulitcast mac address in hash table */ + XEmacPs_SetHash(&xemacpsif->emacps, multicast_mac_addr); + + } else if (action == NETIF_DEL_MAC_FILTER) { + /* Remove Mulitcast mac address in hash table */ + XEmacPs_DeleteHash(&xemacpsif->emacps, multicast_mac_addr); + } + + /* Reset DMA */ + reset_dma(xemac); + + /* Start Ethernet */ + XEmacPs_Start(&xemacpsif->emacps); + + SYS_ARCH_UNPROTECT(lev); +} + +static err_t xemacpsif_mld6_mac_filter_update (struct netif *netif, ip_addr_t *group, + u8_t action) +{ + u8_t temp_mask; + unsigned int i; + u8_t * ip_addr = (u8_t *) group; + + if(!(xemacpsif_ip6_addr_ismulticast((ip6_addr_t*) ip_addr))) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: The requested MAC address is not a multicast address.\r\n", __func__)); LWIP_DEBUGF(NETIF_DEBUG, + ("Multicast address add operation failure !!\r\n")); + return ERR_ARG; + } + if (action == NETIF_ADD_MAC_FILTER) { + for (i = 0; i < XEMACPS_MAX_MAC_ADDR; i++) { + temp_mask = (0x01) << i; + if ((xemacps_mld6_mcast_entry_mask & temp_mask) == temp_mask) { + continue; + } + xemacps_mld6_mcast_entry_mask |= temp_mask; + + /* Update mac address in hash table */ + xemacpsif_mld6_mac_hash_update(netif, ip_addr, action); + + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: Multicast MAC address successfully added.\r\n", __func__)); + + return ERR_OK; + } + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: No multicast address registers left.\r\n", __func__)); + LWIP_DEBUGF(NETIF_DEBUG, + ("Multicast MAC address add operation failure !!\r\n")); + return ERR_MEM; + } else if (action == NETIF_DEL_MAC_FILTER) { + for (i = 0; i < XEMACPS_MAX_MAC_ADDR; i++) { + temp_mask = (0x01) << i; + if ((xemacps_mld6_mcast_entry_mask & temp_mask) != temp_mask) { + continue; + } + xemacps_mld6_mcast_entry_mask &= (~temp_mask); + + /* Update mac address in hash table */ + xemacpsif_mld6_mac_hash_update(netif, ip_addr, action); + + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: Multicast MAC address successfully removed.\r\n", __func__)); + + return ERR_OK; + } + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: No multicast address registers present with\r\n", __func__)); + LWIP_DEBUGF(NETIF_DEBUG, + ("the requested Multicast MAC address.\r\n")); + LWIP_DEBUGF(NETIF_DEBUG, + ("Multicast MAC address removal failure!!.\r\n")); + return ERR_MEM; + } + return ERR_ARG; +} +#endif + +#if LWIP_IGMP +static void xemacpsif_mac_hash_update (struct netif *netif, u8_t *ip_addr, + u8_t action) +{ + u8_t multicast_mac_addr[6]; + struct xemac_s *xemac = (struct xemac_s *) (netif->state); + xemacpsif_s *xemacpsif = (xemacpsif_s *) (xemac->state); + XEmacPs_BdRing *txring; + txring = &(XEmacPs_GetTxRing(&xemacpsif->emacps)); + + multicast_mac_addr[0] = 0x01; + multicast_mac_addr[1] = 0x00; + multicast_mac_addr[2] = 0x5E; + multicast_mac_addr[3] = ip_addr[1] & 0x7F; + multicast_mac_addr[4] = ip_addr[2]; + multicast_mac_addr[5] = ip_addr[3]; + + /* Wait till all sent packets are acknowledged from HW */ + while(txring->HwCnt); + + SYS_ARCH_DECL_PROTECT(lev); + + SYS_ARCH_PROTECT(lev); + + /* Stop Ethernet */ + XEmacPs_Stop(&xemacpsif->emacps); + + if (action == IGMP_ADD_MAC_FILTER) { + /* Set Mulitcast mac address in hash table */ + XEmacPs_SetHash(&xemacpsif->emacps, multicast_mac_addr); + + } else if (action == IGMP_DEL_MAC_FILTER) { + /* Remove Mulitcast mac address in hash table */ + XEmacPs_DeleteHash(&xemacpsif->emacps, multicast_mac_addr); + } + + /* Reset DMA */ + reset_dma(xemac); + + /* Start Ethernet */ + XEmacPs_Start(&xemacpsif->emacps); + + SYS_ARCH_UNPROTECT(lev); +} + +static err_t xemacpsif_mac_filter_update (struct netif *netif, ip_addr_t *group, + u8_t action) +{ + u8_t temp_mask; + unsigned int i; + u8_t * ip_addr = (u8_t *) group; + + if ((ip_addr[0] < 224) && (ip_addr[0] > 239)) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: The requested MAC address is not a multicast address.\r\n", __func__)); + LWIP_DEBUGF(NETIF_DEBUG, + ("Multicast address add operation failure !!\r\n")); + + return ERR_ARG; + } + + if (action == IGMP_ADD_MAC_FILTER) { + + for (i = 0; i < XEMACPS_MAX_MAC_ADDR; i++) { + temp_mask = (0x01) << i; + if ((xemacps_mcast_entry_mask & temp_mask) == temp_mask) { + continue; + } + xemacps_mcast_entry_mask |= temp_mask; + + /* Update mac address in hash table */ + xemacpsif_mac_hash_update(netif, ip_addr, action); + + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: Multicast MAC address successfully added.\r\n", __func__)); + + return ERR_OK; + } + if (i == XEMACPS_MAX_MAC_ADDR) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: No multicast address registers left.\r\n", __func__)); + LWIP_DEBUGF(NETIF_DEBUG, + ("Multicast MAC address add operation failure !!\r\n")); + + return ERR_MEM; + } + } else if (action == IGMP_DEL_MAC_FILTER) { + for (i = 0; i < XEMACPS_MAX_MAC_ADDR; i++) { + temp_mask = (0x01) << i; + if ((xemacps_mcast_entry_mask & temp_mask) != temp_mask) { + continue; + } + xemacps_mcast_entry_mask &= (~temp_mask); + + /* Update mac address in hash table */ + xemacpsif_mac_hash_update(netif, ip_addr, action); + + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: Multicast MAC address successfully removed.\r\n", __func__)); + + return ERR_OK; + } + if (i == XEMACPS_MAX_MAC_ADDR) { + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: No multicast address registers present with\r\n", __func__)); + LWIP_DEBUGF(NETIF_DEBUG, + ("the requested Multicast MAC address.\r\n")); + LWIP_DEBUGF(NETIF_DEBUG, + ("Multicast MAC address removal failure!!.\r\n")); + + return ERR_MEM; + } + } + return ERR_OK; +} +#endif + +/* + * xemacpsif_init(): + * + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + */ + +err_t xemacpsif_init(struct netif *netif) +{ +#if LWIP_SNMP + /* ifType ethernetCsmacd(6) @see RFC1213 */ + netif->link_type = 6; + /* your link speed here */ + netif->link_speed = ; + netif->ts = 0; + netif->ifinoctets = 0; + netif->ifinucastpkts = 0; + netif->ifinnucastpkts = 0; + netif->ifindiscards = 0; + netif->ifoutoctets = 0; + netif->ifoutucastpkts = 0; + netif->ifoutnucastpkts = 0; + netif->ifoutdiscards = 0; +#endif + + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + netif->output = xemacpsif_output; + netif->linkoutput = low_level_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif + + low_level_init(netif); + return ERR_OK; +} + +/* + * xemacpsif_resetrx_on_no_rxdata(): + * + * Should be called by the user at regular intervals, typically + * from a timer (100 msecond). This is to provide a SW workaround + * for the HW bug (SI #692601). Please refer to the function header + * for the function resetrx_on_no_rxdata in xemacpsif_dma.c to + * know more about the SI. + * + */ + +void xemacpsif_resetrx_on_no_rxdata(struct netif *netif) +{ + struct xemac_s *xemac = (struct xemac_s *)(netif->state); + xemacpsif_s *xemacpsif = (xemacpsif_s *)(xemac->state); + + resetrx_on_no_rxdata(xemacpsif); +} 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); +} diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_hw.c b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_hw.c new file mode 100644 index 0000000..a1fdeda --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_hw.c @@ -0,0 +1,276 @@ +/* + * 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 "netif/xemacpsif.h" +#include "lwipopts.h" + +#if XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1 || \ + XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 +#define PCM_PMA_CORE_PRESENT +#else +#undef PCM_PMA_CORE_PRESENT +#endif + +u32_t link_speed = 100; +extern XEmacPs_Config XEmacPs_ConfigTable[]; +extern u32_t phymapemac0[32]; +extern u32_t phymapemac1[32]; +extern u32_t phyaddrforemac; +extern enum ethernet_link_status eth_link_status; + +#if !NO_SYS +extern long xInsideISR; +#endif + +XEmacPs_Config *xemacps_lookup_config(unsigned mac_base) +{ + XEmacPs_Config *cfgptr = NULL; + s32_t i; + + for (i = 0; i < XPAR_XEMACPS_NUM_INSTANCES; i++) { + if (XEmacPs_ConfigTable[i].BaseAddress == mac_base) { + cfgptr = &XEmacPs_ConfigTable[i]; + break; + } + } + + return (cfgptr); +} + +void init_emacps(xemacpsif_s *xemacps, struct netif *netif) +{ + XEmacPs *xemacpsp; + s32_t status = XST_SUCCESS; + u32_t i; + u32_t phyfoundforemac0 = FALSE; + u32_t phyfoundforemac1 = FALSE; + + xemacpsp = &xemacps->emacps; + +#ifdef ZYNQMP_USE_JUMBO + XEmacPs_SetOptions(xemacpsp, XEMACPS_JUMBO_ENABLE_OPTION); +#endif + +#ifdef LWIP_IGMP + XEmacPs_SetOptions(xemacpsp, XEMACPS_MULTICAST_OPTION); +#endif + + /* set mac address */ + status = XEmacPs_SetMacAddress(xemacpsp, (void*)(netif->hwaddr), 1); + if (status != XST_SUCCESS) { + xil_printf("In %s:Emac Mac Address set failed...\r\n",__func__); + } + + XEmacPs_SetMdioDivisor(xemacpsp, MDC_DIV_224); + +/* Please refer to file header comments for the file xemacpsif_physpeed.c + * to know more about the PHY programming sequence. + * For PCS PMA core, phy_setup_emacps is called with the predefined PHY address + * exposed through xaparemeters.h + * For RGMII case, assuming multiple PHYs can be present on the MDIO bus, + * detect_phy is called to get the addresses of the PHY present on + * a particular MDIO bus (emac0 or emac1). This address map is populated + * in phymapemac0 or phymapemac1. + * phy_setup_emacps is then called for each PHY present on the MDIO bus. + */ +#ifdef PCM_PMA_CORE_PRESENT +#ifdef XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT + link_speed = phy_setup_emacps(xemacpsp, XPAR_PCSPMA_1000BASEX_PHYADDR); +#elif XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT + link_speed = phy_setup_emacps(xemacpsp, XPAR_PCSPMA_SGMII_PHYADDR); +#endif +#else + detect_phy(xemacpsp); + for (i = 31; i > 0; i--) { + if (xemacpsp->Config.BaseAddress == XPAR_XEMACPS_0_BASEADDR) { + if (phymapemac0[i] == TRUE) { + link_speed = phy_setup_emacps(xemacpsp, i); + phyfoundforemac0 = TRUE; + phyaddrforemac = i; + } + } else { + if (phymapemac1[i] == TRUE) { + link_speed = phy_setup_emacps(xemacpsp, i); + phyfoundforemac1 = TRUE; + phyaddrforemac = i; + } + } + } + /* If no PHY was detected, use broadcast PHY address of 0 */ + if (xemacpsp->Config.BaseAddress == XPAR_XEMACPS_0_BASEADDR) { + if (phyfoundforemac0 == FALSE) + link_speed = phy_setup_emacps(xemacpsp, 0); + } else { + if (phyfoundforemac1 == FALSE) + link_speed = phy_setup_emacps(xemacpsp, 0); + } +#endif + + if (link_speed == XST_FAILURE) { + eth_link_status = ETH_LINK_DOWN; + xil_printf("Phy setup failure %s \n\r",__func__); + return; + } else { + eth_link_status = ETH_LINK_UP; + } + + XEmacPs_SetOperatingSpeed(xemacpsp, link_speed); + /* Setting the operating speed of the MAC needs a delay. */ + { + volatile s32_t wait; + for (wait=0; wait < 20000; wait++); + } +} + +void init_emacps_on_error (xemacpsif_s *xemacps, struct netif *netif) +{ + XEmacPs *xemacpsp; + s32_t status = XST_SUCCESS; + + xemacpsp = &xemacps->emacps; + + /* set mac address */ + status = XEmacPs_SetMacAddress(xemacpsp, (void*)(netif->hwaddr), 1); + if (status != XST_SUCCESS) { + xil_printf("In %s:Emac Mac Address set failed...\r\n",__func__); + } + + XEmacPs_SetOperatingSpeed(xemacpsp, link_speed); + + /* Setting the operating speed of the MAC needs a delay. */ + { + volatile s32_t wait; + for (wait=0; wait < 20000; wait++); + } +} + +void setup_isr (struct xemac_s *xemac) +{ + xemacpsif_s *xemacpsif; + + xemacpsif = (xemacpsif_s *)(xemac->state); + /* + * Setup callbacks + */ + XEmacPs_SetHandler(&xemacpsif->emacps, XEMACPS_HANDLER_DMASEND, + (void *) emacps_send_handler, + (void *) xemac); + + XEmacPs_SetHandler(&xemacpsif->emacps, XEMACPS_HANDLER_DMARECV, + (void *) emacps_recv_handler, + (void *) xemac); + + XEmacPs_SetHandler(&xemacpsif->emacps, XEMACPS_HANDLER_ERROR, + (void *) emacps_error_handler, + (void *) xemac); +} + +void start_emacps (xemacpsif_s *xemacps) +{ + /* start the temac */ + XEmacPs_Start(&xemacps->emacps); +} + +void restart_emacps_transmitter (xemacpsif_s *xemacps) { + u32_t Reg; + Reg = XEmacPs_ReadReg(xemacps->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET); + Reg = Reg & (~XEMACPS_NWCTRL_TXEN_MASK); + XEmacPs_WriteReg(xemacps->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, Reg); + + Reg = XEmacPs_ReadReg(xemacps->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET); + Reg = Reg | (XEMACPS_NWCTRL_TXEN_MASK); + XEmacPs_WriteReg(xemacps->emacps.Config.BaseAddress, + XEMACPS_NWCTRL_OFFSET, Reg); +} + +void emacps_error_handler(void *arg,u8 Direction, u32 ErrorWord) +{ + struct xemac_s *xemac; + xemacpsif_s *xemacpsif; + XEmacPs_BdRing *rxring; + XEmacPs_BdRing *txring; +#if !NO_SYS + xInsideISR++; +#endif + + xemac = (struct xemac_s *)(arg); + xemacpsif = (xemacpsif_s *)(xemac->state); + rxring = &XEmacPs_GetRxRing(&xemacpsif->emacps); + txring = &XEmacPs_GetTxRing(&xemacpsif->emacps); + + if (ErrorWord != 0) { + switch (Direction) { + case XEMACPS_RECV: + if (ErrorWord & XEMACPS_RXSR_HRESPNOK_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("Receive DMA error\r\n")); + HandleEmacPsError(xemac); + } + if (ErrorWord & XEMACPS_RXSR_RXOVR_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("Receive over run\r\n")); + emacps_recv_handler(arg); + setup_rx_bds(xemacpsif, rxring); + } + if (ErrorWord & XEMACPS_RXSR_BUFFNA_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("Receive buffer not available\r\n")); + emacps_recv_handler(arg); + setup_rx_bds(xemacpsif, rxring); + } + break; + case XEMACPS_SEND: + if (ErrorWord & XEMACPS_TXSR_HRESPNOK_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("Transmit DMA error\r\n")); + HandleEmacPsError(xemac); + } + if (ErrorWord & XEMACPS_TXSR_URUN_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("Transmit under run\r\n")); + HandleTxErrors(xemac); + } + if (ErrorWord & XEMACPS_TXSR_BUFEXH_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("Transmit buffer exhausted\r\n")); + HandleTxErrors(xemac); + } + if (ErrorWord & XEMACPS_TXSR_RXOVR_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("Transmit retry excessed limits\r\n")); + HandleTxErrors(xemac); + } + if (ErrorWord & XEMACPS_TXSR_FRAMERX_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("Transmit collision\r\n")); + process_sent_bds(xemacpsif, txring); + } + break; + } + } +#if !NO_SYS + xInsideISR--; +#endif +} diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_physpeed.c b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_physpeed.c new file mode 100644 index 0000000..069020a --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xemacpsif_physpeed.c @@ -0,0 +1,1037 @@ +/* + * Copyright (C) 2010 - 2019 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. + * + */ + +/***************************************************************************** +* This file xemacpsif_physpeed.c implements functionalities to: +* - Detect the available PHYs connected to a MAC +* - Negotiate speed +* - Configure speed +* - Configure the SLCR registers for the negotiated speed +* +* In a typical use case, users of the APIs implemented in this file need to +* do the following. +* - Call the API detect_phy. It probes for the available PHYs connected to a MAC. +* The MACs can be Emac0 (XPAR_XEMACPS_0_BASEADDR, 0xE000B000) or Emac1 +* (XPAR_XEMACPS_0_BASEADDR, 0xE000C000). It populates an array to notify +* about the detected PHYs. The array phymapemac0 is used for Emac0 and +* phymapemac1 is for Emac1. +* - The users need to parse the corresponding arrays, phymapemac0 or phymapemac1 +* to know the available PHYs for a MAC. The users then need to call +* phy_setup_emacps to setup the PHYs for proper speed setting. The API +* phy_setup_emacps should be called with the PHY address for which the speed +* needs to be negotiated or configured. In a specific use case, if 2 PHYs are +* connected to Emac0 with addresses of 7 and 11, then users get these address +* details from phymapemac0 (after calling detect_phy) and then call +* phy_setup_emacps twice, with ab address of 7 and 11. +* - Points to note: The MAC can operate at only one speed. If a MAC is connected +* to multiple PHYs, then all PHYs must negotiate and configured for the same +* speed. +* - This file implements static functions to set proper SLCR clocks. As stated +* above, all PHYs connected to a PHY must operate at same speed and the SLCR +* clock will be setup accordingly. +* +* This file implements the following PHY types. +* - The standard RGMII. +* - It provides support for GMII to RGMII converter Xilinx IP. This Xilinx IP +* sits on the MDIO bus with a predefined PHY address. This IP exposes register +* that needs to be programmed with the negotiated speed. +* For example, in a typical design, the Emac0 or Emac1 exposes GMII interface. +* The user can then use the Xilinx IP that converts GMII to RGMII. +* The external PHY (most typically Marvell 88E1116R) negotiates for speed +* with the remote PHY. The implementation in this file then programs the +* Xilinx IP with this negotiated speed. The Xilinx IP has a predefined IP +* address exposed through xparameters.h +* - The SGMII and 1000 BaseX PHY interfaces. +* If the PHY interface is SGMII or 1000 BaseX a separate "get_IEEE_phy_speed" +* is used which is different from standard RGMII "get_IEEE_phy_speed". +* The 1000 BaseX always operates at 1000 Mbps. The SGMII interface can +* negotiate speed accordingly. +* For SGMII or 1000 BaseX interfaces, the detect_phy should not be called. +* The phy addresses for these interfaces are fixed at the design time. +* +* Point to note: +* A MAC can not be connected to PHYs where there is a mix between +* SGMII or 1000 Basex or GMII/MII/RGMII. +* In a typical multiple PHY designs, it is expected that the PHYs connected +* will be RGMII or GMII. +* +* The users can choose not to negotiate speed from lwip settings GUI. +* If they opt to choose a particular PHY speed, then the PHY will hard code +* the speed to operate only at the corresponding speed. It will not advertise +* any other speeds. It is users responsibility to ensure that the remote PHY +* supports the speed programmed through the lwip gui. +* +* The following combination of MDIO/PHY are supported: +* - Multiple PHYs connected to the MDIO bus of a MAC. If Emac0 MDIO is connected +* to single/multiple PHYs, it is supported. Similarly Emac1 MDIO connected to +* single/multiple PHYs is supported. +* - A design where both the interfaces are present and are connected to their own +* MDIO bus is supported. +* +* The following MDIO/PHY setup is not supported: +* - A design has both the MACs present. MDIO bus is available only for one MAC +* (Emac0 or Emac1). This MDIO bus has multiple PHYs available for both the +* MACs. The negotiated speed for PHYs sitting on the MDIO bus of one MAC will +* not be see for the other MAC and hence the speed/SLCR settings of the other +* MAC cannot be programmed. Hence this kind of design will not work for +* this implementation. +* +********************************************************************************/ + +#include "netif/xemacpsif.h" +#include "lwipopts.h" +#include "xparameters_ps.h" +#include "xparameters.h" +#include "xemac_ieee_reg.h" + +#if defined (__aarch64__) +#include "bspconfig.h" +#include "xil_smc.h" +#endif + +#define PHY_DETECT_REG 1 +#define PHY_IDENTIFIER_1_REG 2 +#define PHY_IDENTIFIER_2_REG 3 +#define PHY_DETECT_MASK 0x1808 +#define PHY_MARVELL_IDENTIFIER 0x0141 +#define PHY_TI_IDENTIFIER 0x2000 +#define PHY_REALTEK_IDENTIFIER 0x001c +#define PHY_XILINX_PCS_PMA_ID1 0x0174 +#define PHY_XILINX_PCS_PMA_ID2 0x0C00 + +#define XEMACPS_GMII2RGMII_SPEED1000_FD 0x140 +#define XEMACPS_GMII2RGMII_SPEED100_FD 0x2100 +#define XEMACPS_GMII2RGMII_SPEED10_FD 0x100 +#define XEMACPS_GMII2RGMII_REG_NUM 0x10 + +#define PHY_REGCR 0x0D +#define PHY_ADDAR 0x0E +#define PHY_RGMIIDCTL 0x86 +#define PHY_RGMIICTL 0x32 +#define PHY_STS 0x11 +#define PHY_TI_CR 0x10 +#define PHY_TI_CFG4 0x31 + +#define PHY_REGCR_ADDR 0x001F +#define PHY_REGCR_DATA 0x401F +#define PHY_TI_CRVAL 0x5048 +#define PHY_TI_CFG4RESVDBIT7 0x80 + +/* Frequency setting */ +#define SLCR_LOCK_ADDR (XPS_SYS_CTRL_BASEADDR + 0x4) +#define SLCR_UNLOCK_ADDR (XPS_SYS_CTRL_BASEADDR + 0x8) +#define SLCR_GEM0_CLK_CTRL_ADDR (XPS_SYS_CTRL_BASEADDR + 0x140) +#define SLCR_GEM1_CLK_CTRL_ADDR (XPS_SYS_CTRL_BASEADDR + 0x144) +#define SLCR_GEM_SRCSEL_EMIO 0x40 +#define SLCR_LOCK_KEY_VALUE 0x767B +#define SLCR_UNLOCK_KEY_VALUE 0xDF0D +#define SLCR_ADDR_GEM_RST_CTRL (XPS_SYS_CTRL_BASEADDR + 0x214) +#define EMACPS_SLCR_DIV_MASK 0xFC0FC0FF + +#if XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1 || \ + XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 +#define PCM_PMA_CORE_PRESENT +#else +#undef PCM_PMA_CORE_PRESENT +#endif + +#ifdef PCM_PMA_CORE_PRESENT +#define IEEE_CTRL_RESET 0x9140 +#define IEEE_CTRL_ISOLATE_DISABLE 0xFBFF +#endif + +u32_t phymapemac0[32]; +u32_t phymapemac1[32]; + +#if defined (PCM_PMA_CORE_PRESENT) || defined (CONFIG_LINKSPEED_AUTODETECT) +static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr); +#endif +static void SetUpSLCRDivisors(UINTPTR mac_baseaddr, s32_t speed); +#if defined (CONFIG_LINKSPEED1000) || defined (CONFIG_LINKSPEED100) \ + || defined (CONFIG_LINKSPEED10) +static u32_t configure_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr, u32_t speed); +#endif + +#ifdef PCM_PMA_CORE_PRESENT +u32_t phy_setup_emacps (XEmacPs *xemacpsp, u32_t phy_addr) +{ + u32_t link_speed; + u16_t regval; + u16_t phy_id; + + if(phy_addr == 0) { + for (phy_addr = 31; phy_addr > 0; phy_addr--) { + XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG, + &phy_id); + + if (phy_id == PHY_XILINX_PCS_PMA_ID1) { + XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_2_REG, + &phy_id); + if (phy_id == PHY_XILINX_PCS_PMA_ID2) { + /* Found a valid PHY address */ + LWIP_DEBUGF(NETIF_DEBUG, ("XEmacPs detect_phy: PHY detected at address %d.\r\n", + phy_addr)); + break; + } + } + } + } + + link_speed = get_IEEE_phy_speed(xemacpsp, phy_addr); + if (link_speed == 1000) + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,1000); + else if (link_speed == 100) + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,100); + else + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,10); + + xil_printf("link speed for phy address %d: %d\r\n", phy_addr, link_speed); + return link_speed; +} + +static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr) +{ + u16_t temp; + u16_t control; + u16_t status; + u16_t partner_capabilities; + + xil_printf("Start PHY autonegotiation \r\n"); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + control &= IEEE_CTRL_ISOLATE_DISABLE; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); + + xil_printf("Waiting for PHY to complete autonegotiation.\r\n"); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { + sleep(1); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, + &status); + } + xil_printf("autonegotiation complete \r\n"); + +#if XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1 + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 1); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp); + if ((temp & 0x0020) == 0x0020) { + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + return 1000; + } + else { + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + xil_printf("Link error, temp = %x\r\n", temp); + return 0; + } +#elif XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 + xil_printf("Waiting for Link to be up; Polling for SGMII core Reg \r\n"); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp); + while(!(temp & 0x8000)) { + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp); + } + if((temp & 0x0C00) == 0x0800) { + return 1000; + } + else if((temp & 0x0C00) == 0x0400) { + return 100; + } + else if((temp & 0x0C00) == 0x0000) { + return 10; + } else { + xil_printf("get_IEEE_phy_speed(): Invalid speed bit value, Defaulting to Speed = 10 Mbps\r\n"); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &temp); + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, 0x0100); + return 10; + } +#endif + +} + +#else /*PCM_PMA_CORE_PRESENT not defined, GMII/RGMII case*/ +void detect_phy(XEmacPs *xemacpsp) +{ + u16_t phy_reg; + u32_t phy_addr; + u32_t emacnum; + + if (xemacpsp->Config.BaseAddress == XPAR_XEMACPS_0_BASEADDR) + emacnum = 0; + else + emacnum = 1; + for (phy_addr = 31; phy_addr > 0; phy_addr--) { + XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_DETECT_REG, + &phy_reg); + + if ((phy_reg != 0xFFFF) && + ((phy_reg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + LWIP_DEBUGF(NETIF_DEBUG, ("XEmacPs detect_phy: PHY detected at address %d.\r\n", + phy_addr)); + if (emacnum == 0) + phymapemac0[phy_addr] = TRUE; + else + phymapemac1[phy_addr] = TRUE; + + XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG, + &phy_reg); + if ((phy_reg != PHY_MARVELL_IDENTIFIER) && + (phy_reg != PHY_TI_IDENTIFIER) && + (phy_reg != PHY_REALTEK_IDENTIFIER)) { + xil_printf("WARNING: Not a Marvell or TI or Realtek Ethernet PHY. Please verify the initialization sequence\r\n"); + } + } + } +} + +u32_t phy_setup_emacps (XEmacPs *xemacpsp, u32_t phy_addr) +{ + u32_t link_speed; + u32_t conv_present = 0; + u32_t convspeeddupsetting = 0; + u32_t convphyaddr = 0; + +#ifdef XPAR_GMII2RGMIICON_0N_ETH0_ADDR + convphyaddr = XPAR_GMII2RGMIICON_0N_ETH0_ADDR; + conv_present = 1; +#endif +#ifdef XPAR_GMII2RGMIICON_0N_ETH1_ADDR + convphyaddr = XPAR_GMII2RGMIICON_0N_ETH1_ADDR; + conv_present = 1; +#endif + +#ifdef CONFIG_LINKSPEED_AUTODETECT + link_speed = get_IEEE_phy_speed(xemacpsp, phy_addr); + if (link_speed == 1000) { + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,1000); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED1000_FD; + } else if (link_speed == 100) { + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,100); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED100_FD; + } else if (link_speed != XST_FAILURE){ + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,10); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED10_FD; + } else { + xil_printf("Phy setup error \r\n"); + return XST_FAILURE; + } +#elif defined(CONFIG_LINKSPEED1000) + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,1000); + link_speed = 1000; + configure_IEEE_phy_speed(xemacpsp, phy_addr, link_speed); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED1000_FD; + sleep(1); +#elif defined(CONFIG_LINKSPEED100) + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,100); + link_speed = 100; + configure_IEEE_phy_speed(xemacpsp, phy_addr, link_speed); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED100_FD; + sleep(1); +#elif defined(CONFIG_LINKSPEED10) + SetUpSLCRDivisors(xemacpsp->Config.BaseAddress,10); + link_speed = 10; + configure_IEEE_phy_speed(xemacpsp, phy_addr, link_speed); + convspeeddupsetting = XEMACPS_GMII2RGMII_SPEED10_FD; + sleep(1); +#endif + if (conv_present) { + XEmacPs_PhyWrite(xemacpsp, convphyaddr, + XEMACPS_GMII2RGMII_REG_NUM, convspeeddupsetting); + } + + xil_printf("link speed for phy address %d: %d\r\n", phy_addr, link_speed); + return link_speed; +} + +#if defined CONFIG_LINKSPEED_AUTODETECT +static u32_t get_TI_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr) +{ + u16_t control; + u16_t status; + u16_t status_speed; + u32_t timeout_counter = 0; + u32_t phyregtemp; + u32_t RetStatus; + + xil_printf("Start PHY autonegotiation \r\n"); + + XEmacPs_PhyRead(xemacpsp, phy_addr, 0x1F, (u16_t *)&phyregtemp); + phyregtemp |= 0x4000; + XEmacPs_PhyWrite(xemacpsp, phy_addr, 0x1F, phyregtemp); + RetStatus = XEmacPs_PhyRead(xemacpsp, phy_addr, 0x1F, (u16_t *)&phyregtemp); + if (RetStatus != XST_SUCCESS) { + xil_printf("Error during sw reset \n\r"); + return XST_FAILURE; + } + + XEmacPs_PhyRead(xemacpsp, phy_addr, 0, (u16_t *)&phyregtemp); + phyregtemp |= 0x8000; + XEmacPs_PhyWrite(xemacpsp, phy_addr, 0, phyregtemp); + + /* + * Delay + */ + sleep(1); + + RetStatus = XEmacPs_PhyRead(xemacpsp, phy_addr, 0, (u16_t *)&phyregtemp); + if (RetStatus != XST_SUCCESS) { + xil_printf("Error during reset \n\r"); + return XST_FAILURE; + } + + /* FIFO depth */ + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_TI_CR, PHY_TI_CRVAL); + RetStatus = XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_TI_CR, (u16_t *)&phyregtemp); + if (RetStatus != XST_SUCCESS) { + xil_printf("Error writing to 0x10 \n\r"); + return XST_FAILURE; + } + + /* TX/RX tuning */ + /* Write to PHY_RGMIIDCTL */ + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, PHY_RGMIIDCTL); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA); + RetStatus = XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, 0xA8); + if (RetStatus != XST_SUCCESS) { + xil_printf("Error in tuning"); + return XST_FAILURE; + } + + /* Read PHY_RGMIIDCTL */ + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, PHY_RGMIIDCTL); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA); + RetStatus = XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_ADDAR, (u16_t *)&phyregtemp); + if (RetStatus != XST_SUCCESS) { + xil_printf("Error in tuning"); + return XST_FAILURE; + } + + /* Write PHY_RGMIICTL */ + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, PHY_RGMIICTL); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA); + RetStatus = XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, 0xD3); + if (RetStatus != XST_SUCCESS) { + xil_printf("Error in tuning"); + return XST_FAILURE; + } + + /* Read PHY_RGMIICTL */ + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, PHY_RGMIICTL); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA); + RetStatus = XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_ADDAR, (u16_t *)&phyregtemp); + if (RetStatus != XST_SUCCESS) { + xil_printf("Error in tuning"); + return XST_FAILURE; + } + + /* SW workaround for unstable link when RX_CTRL is not STRAP MODE 3 or 4 */ + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, PHY_TI_CFG4); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA); + RetStatus = XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_ADDAR, (u16_t *)&phyregtemp); + phyregtemp &= ~(PHY_TI_CFG4RESVDBIT7); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_ADDR); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, PHY_TI_CFG4); + XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_REGCR, PHY_REGCR_DATA); + RetStatus = XEmacPs_PhyWrite(xemacpsp, phy_addr, PHY_ADDAR, phyregtemp); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + &control); + control |= ADVERTISE_1000; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + control); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + xil_printf("Waiting for PHY to complete autonegotiation.\r\n"); + + while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { + sleep(1); + timeout_counter++; + + if (timeout_counter == 30) { + xil_printf("Auto negotiation error \r\n"); + return XST_FAILURE; + } + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + } + xil_printf("autonegotiation complete \r\n"); + + XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_STS, &status_speed); + if ((status_speed & 0xC000) == 0x8000) { + return 1000; + } else if ((status_speed & 0xC000) == 0x4000) { + return 100; + } else { + return 10; + } + + return XST_SUCCESS; +} + +static u32_t get_Marvell_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr) +{ + u16_t temp; + u16_t control; + u16_t status; + u16_t status_speed; + u32_t timeout_counter = 0; + u32_t temp_speed; + + xil_printf("Start PHY autonegotiation \r\n"); + + XEmacPs_PhyWrite(xemacpsp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control); + control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control); + + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + &control); + control |= ADVERTISE_1000; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + control); + + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, + &control); + control |= (7 << 12); /* max number of gigabit attempts */ + control |= (1 << 11); /* enable downshift */ + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, + control); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control |= IEEE_CTRL_RESET_MASK; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); + + while (1) { + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + if (control & IEEE_CTRL_RESET_MASK) + continue; + else + break; + } + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + xil_printf("Waiting for PHY to complete autonegotiation.\r\n"); + + while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { + sleep(1); + XEmacPs_PhyRead(xemacpsp, phy_addr, + IEEE_COPPER_SPECIFIC_STATUS_REG_2, &temp); + timeout_counter++; + + if (timeout_counter == 30) { + xil_printf("Auto negotiation error \r\n"); + return XST_FAILURE; + } + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + } + xil_printf("autonegotiation complete \r\n"); + + XEmacPs_PhyRead(xemacpsp, phy_addr,IEEE_SPECIFIC_STATUS_REG, + &status_speed); + if (status_speed & 0x400) { + temp_speed = status_speed & IEEE_SPEED_MASK; + + if (temp_speed == IEEE_SPEED_1000) + return 1000; + else if(temp_speed == IEEE_SPEED_100) + return 100; + else + return 10; + } + + return XST_SUCCESS; +} + +static u32_t get_Realtek_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr) +{ + u16_t control; + u16_t status; + u16_t status_speed; + u32_t timeout_counter = 0; + u32_t temp_speed; + + xil_printf("Start PHY autonegotiation \r\n"); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + &control); + control |= ADVERTISE_1000; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + control); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control |= IEEE_CTRL_RESET_MASK; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); + + while (1) { + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + if (control & IEEE_CTRL_RESET_MASK) + continue; + else + break; + } + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + + xil_printf("Waiting for PHY to complete autonegotiation.\r\n"); + + while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { + sleep(1); + timeout_counter++; + + if (timeout_counter == 30) { + xil_printf("Auto negotiation error \r\n"); + return XST_FAILURE; + } + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + } + xil_printf("autonegotiation complete \r\n"); + + XEmacPs_PhyRead(xemacpsp, phy_addr,IEEE_SPECIFIC_STATUS_REG, + &status_speed); + if (status_speed & 0x400) { + temp_speed = status_speed & IEEE_SPEED_MASK; + + if (temp_speed == IEEE_SPEED_1000) + return 1000; + else if(temp_speed == IEEE_SPEED_100) + return 100; + else + return 10; + } + + return XST_FAILURE; +} + +static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr) +{ + u16_t phy_identity; + u32_t RetStatus; + + XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG, + &phy_identity); + if (phy_identity == PHY_TI_IDENTIFIER) { + RetStatus = get_TI_phy_speed(xemacpsp, phy_addr); + } else if (phy_identity == PHY_REALTEK_IDENTIFIER) { + RetStatus = get_Realtek_phy_speed(xemacpsp, phy_addr); + } else { + RetStatus = get_Marvell_phy_speed(xemacpsp, phy_addr); + } + + return RetStatus; +} +#endif + +#if defined (CONFIG_LINKSPEED1000) || defined (CONFIG_LINKSPEED100) \ + || defined (CONFIG_LINKSPEED10) +static u32_t configure_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr, u32_t speed) +{ + u16_t control; + u16_t autonereg; + + XEmacPs_PhyWrite(xemacpsp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2); + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, &control); + control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_MAC, control); + + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); + autonereg |= IEEE_ASYMMETRIC_PAUSE_MASK; + autonereg |= IEEE_PAUSE_MASK; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); + + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control &= ~IEEE_CTRL_LINKSPEED_1000M; + control &= ~IEEE_CTRL_LINKSPEED_100M; + control &= ~IEEE_CTRL_LINKSPEED_10M; + + if (speed == 1000) { + control |= IEEE_CTRL_LINKSPEED_1000M; + + /* Don't advertise PHY speed of 100 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); + autonereg &= (~ADVERTISE_100); + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); + + /* Don't advertise PHY speed of 10 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); + autonereg &= (~ADVERTISE_10); + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); + + /* Advertise PHY speed of 1000 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &autonereg); + autonereg |= ADVERTISE_1000; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, autonereg); + } + + else if (speed == 100) { + control |= IEEE_CTRL_LINKSPEED_100M; + + /* Don't advertise PHY speed of 1000 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &autonereg); + autonereg &= (~ADVERTISE_1000); + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, autonereg); + + /* Don't advertise PHY speed of 10 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); + autonereg &= (~ADVERTISE_10); + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); + + /* Advertise PHY speed of 100 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); + autonereg |= ADVERTISE_100; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); + } + + else if (speed == 10) { + control |= IEEE_CTRL_LINKSPEED_10M; + + /* Don't advertise PHY speed of 1000 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &autonereg); + autonereg &= (~ADVERTISE_1000); + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, autonereg); + + /* Don't advertise PHY speed of 100 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); + autonereg &= (~ADVERTISE_100); + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); + + /* Advertise PHY speed of 10 Mbps */ + XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &autonereg); + autonereg |= ADVERTISE_10; + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, autonereg); + } + + XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, + control | IEEE_CTRL_RESET_MASK); + { + volatile s32_t wait; + for (wait=0; wait < 100000; wait++); + } + return 0; +} +#endif +#endif /*PCM_PMA_CORE_PRESENT*/ + +static void SetUpSLCRDivisors(UINTPTR mac_baseaddr, s32_t speed) +{ + volatile UINTPTR slcrBaseAddress; + u32_t SlcrDiv0 = 0; + u32_t SlcrDiv1 = 0; + u32_t SlcrTxClkCntrl; + u32_t gigeversion; + volatile UINTPTR CrlApbBaseAddr; + u32_t CrlApbDiv0 = 0; + u32_t CrlApbDiv1 = 0; + u32_t CrlApbGemCtrl; +#if defined (__aarch64__) && (EL1_NONSECURE == 1) + u32_t ClkId; +#endif + + gigeversion = ((Xil_In32(mac_baseaddr + 0xFC)) >> 16) & 0xFFF; + if (gigeversion == 2) { + + Xil_Out32(SLCR_UNLOCK_ADDR, SLCR_UNLOCK_KEY_VALUE); + + if (mac_baseaddr == ZYNQ_EMACPS_0_BASEADDR) { + slcrBaseAddress = SLCR_GEM0_CLK_CTRL_ADDR; + } else { + slcrBaseAddress = SLCR_GEM1_CLK_CTRL_ADDR; + } + + if(Xil_In32(slcrBaseAddress) & + SLCR_GEM_SRCSEL_EMIO) { + return; + } + + if (speed == 1000) { + if (mac_baseaddr == ZYNQ_EMACPS_0_BASEADDR) { +#ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV1; +#endif + } else { +#ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_1000MBPS_DIV1; +#endif + } + } else if (speed == 100) { + if (mac_baseaddr == ZYNQ_EMACPS_0_BASEADDR) { +#ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV1; +#endif + } else { +#ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_100MBPS_DIV1; +#endif + } + } else { + if (mac_baseaddr == ZYNQ_EMACPS_0_BASEADDR) { +#ifdef XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV1; +#endif + } else { +#ifdef XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV0 + SlcrDiv0 = XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV0; + SlcrDiv1 = XPAR_PS7_ETHERNET_1_ENET_SLCR_10MBPS_DIV1; +#endif + } + } + + if (SlcrDiv0 != 0 && SlcrDiv1 != 0) { + SlcrTxClkCntrl = Xil_In32(slcrBaseAddress); + SlcrTxClkCntrl &= EMACPS_SLCR_DIV_MASK; + SlcrTxClkCntrl |= (SlcrDiv1 << 20); + SlcrTxClkCntrl |= (SlcrDiv0 << 8); + Xil_Out32(slcrBaseAddress, SlcrTxClkCntrl); + Xil_Out32(SLCR_LOCK_ADDR, SLCR_LOCK_KEY_VALUE); + } else { + xil_printf("Clock Divisors incorrect - Please check\r\n"); + } + } else if (gigeversion == GEM_VERSION_ZYNQMP) { + /* Setup divisors in CRL_APB for Zynq Ultrascale+ MPSoC */ + if (mac_baseaddr == ZYNQMP_EMACPS_0_BASEADDR) { + CrlApbBaseAddr = CRL_APB_GEM0_REF_CTRL; + } else if (mac_baseaddr == ZYNQMP_EMACPS_1_BASEADDR) { + CrlApbBaseAddr = CRL_APB_GEM1_REF_CTRL; + } else if (mac_baseaddr == ZYNQMP_EMACPS_2_BASEADDR) { + CrlApbBaseAddr = CRL_APB_GEM2_REF_CTRL; + } else if (mac_baseaddr == ZYNQMP_EMACPS_3_BASEADDR) { + CrlApbBaseAddr = CRL_APB_GEM3_REF_CTRL; + } + + if (speed == 1000) { + if (mac_baseaddr == ZYNQMP_EMACPS_0_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_0_ENET_SLCR_1000MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_1_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_1_ENET_SLCR_1000MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_2_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_2_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_2_ENET_SLCR_1000MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_2_ENET_SLCR_1000MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_3_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_3_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_3_ENET_SLCR_1000MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_3_ENET_SLCR_1000MBPS_DIV1; +#endif + } + } else if (speed == 100) { + if (mac_baseaddr == ZYNQMP_EMACPS_0_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_0_ENET_SLCR_100MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_0_ENET_SLCR_100MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_1_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_1_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_1_ENET_SLCR_100MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_1_ENET_SLCR_100MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_2_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_2_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_2_ENET_SLCR_100MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_2_ENET_SLCR_100MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_3_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_3_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_3_ENET_SLCR_100MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_3_ENET_SLCR_100MBPS_DIV1; +#endif + } + } else { + if (mac_baseaddr == ZYNQMP_EMACPS_0_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_0_ENET_SLCR_10MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_0_ENET_SLCR_10MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_1_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_1_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_1_ENET_SLCR_10MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_1_ENET_SLCR_10MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_2_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_2_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_2_ENET_SLCR_10MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_2_ENET_SLCR_10MBPS_DIV1; +#endif + } else if (mac_baseaddr == ZYNQMP_EMACPS_3_BASEADDR) { +#ifdef XPAR_PSU_ETHERNET_3_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSU_ETHERNET_3_ENET_SLCR_10MBPS_DIV0; + CrlApbDiv1 = XPAR_PSU_ETHERNET_3_ENET_SLCR_10MBPS_DIV1; +#endif + } + } + + if (CrlApbDiv0 != 0 && CrlApbDiv1 != 0) { + #if defined (__aarch64__) && (EL1_NONSECURE == 1) + XSmc_OutVar RegRead; + RegRead = Xil_Smc(MMIO_READ_SMC_FID, (u64)(CrlApbBaseAddr), + 0, 0, 0, 0, 0, 0); + CrlApbGemCtrl = RegRead.Arg0 >> 32; + #else + CrlApbGemCtrl = Xil_In32(CrlApbBaseAddr); + #endif + CrlApbGemCtrl &= ~CRL_APB_GEM_DIV0_MASK; + CrlApbGemCtrl |= CrlApbDiv0 << CRL_APB_GEM_DIV0_SHIFT; + CrlApbGemCtrl &= ~CRL_APB_GEM_DIV1_MASK; + CrlApbGemCtrl |= CrlApbDiv1 << CRL_APB_GEM_DIV1_SHIFT; + #if defined (__aarch64__) && (EL1_NONSECURE == 1) + Xil_Smc(MMIO_WRITE_SMC_FID, (u64)(CrlApbBaseAddr) | ((u64)(0xFFFFFFFF) << 32), + (u64)CrlApbGemCtrl, 0, 0, 0, 0, 0); + do { + RegRead = Xil_Smc(MMIO_READ_SMC_FID, (u64)(CrlApbBaseAddr), + 0, 0, 0, 0, 0, 0); + } while((RegRead.Arg0 >> 32) != CrlApbGemCtrl); + #else + Xil_Out32(CrlApbBaseAddr, CrlApbGemCtrl); + #endif + } else { + xil_printf("Clock Divisors incorrect - Please check\r\n"); + } + } else if (gigeversion == GEM_VERSION_VERSAL) { + /* Setup divisors in CRL for Versal */ + if (mac_baseaddr == VERSAL_EMACPS_0_BASEADDR) { + CrlApbBaseAddr = VERSAL_CRL_GEM0_REF_CTRL; +#if defined (__aarch64__) && (EL1_NONSECURE == 1) + ClkId = CLK_GEM0_REF; +#endif + } else if (mac_baseaddr == VERSAL_EMACPS_1_BASEADDR) { + CrlApbBaseAddr = VERSAL_CRL_GEM1_REF_CTRL; +#if defined (__aarch64__) && (EL1_NONSECURE == 1) + ClkId = CLK_GEM1_REF; +#endif + } + + if (speed == 1000) { + if (mac_baseaddr == VERSAL_EMACPS_0_BASEADDR) { +#ifdef XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0; +#endif + } else if (mac_baseaddr == VERSAL_EMACPS_1_BASEADDR) { +#ifdef XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0; +#endif + } + } else if (speed == 100) { + if (mac_baseaddr == VERSAL_EMACPS_0_BASEADDR) { +#ifdef XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_0_ENET_SLCR_100MBPS_DIV0; +#endif + } else if (mac_baseaddr == VERSAL_EMACPS_1_BASEADDR) { +#ifdef XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_1_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_1_ENET_SLCR_100MBPS_DIV0; +#endif + } + } else { + if (mac_baseaddr == VERSAL_EMACPS_0_BASEADDR) { +#ifdef XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_0_ENET_SLCR_10MBPS_DIV0; +#endif + } else if (mac_baseaddr == VERSAL_EMACPS_1_BASEADDR) { +#ifdef XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_1_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_ETHERNET_1_ENET_SLCR_10MBPS_DIV0; +#endif + } + } + + if (CrlApbDiv0 != 0) { +#if defined (__aarch64__) && (EL1_NONSECURE == 1) + Xil_Smc(PM_SET_DIVIDER_SMC_FID, (((u64)CrlApbDiv0 << 32) | ClkId), 0, 0, 0, 0, 0, 0); +#else + CrlApbGemCtrl = Xil_In32(CrlApbBaseAddr); + CrlApbGemCtrl &= ~VERSAL_CRL_GEM_DIV_MASK; + CrlApbGemCtrl |= CrlApbDiv0 << VERSAL_CRL_APB_GEM_DIV_SHIFT; + + Xil_Out32(CrlApbBaseAddr, CrlApbGemCtrl); +#endif + } else { + xil_printf("Clock Divisors incorrect - Please check\r\n"); + } + } + + return; +} diff --git a/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xpqueue.c b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xpqueue.c new file mode 100644 index 0000000..b5a773e --- /dev/null +++ b/embeddedsw/ThirdParty/sw_services/lwip211/src/contrib/ports/xilinx/netif/xpqueue.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 - 2019 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 <stdlib.h> + +#include "netif/xpqueue.h" +#include "xil_printf.h" + +#define NUM_QUEUES 2 + +pq_queue_t pq_queue[NUM_QUEUES]; + +pq_queue_t * +pq_create_queue() +{ + static int i; + pq_queue_t *q = NULL; + + if (i >= NUM_QUEUES) { + xil_printf("ERR: Max Queues allocated\n\r"); + return q; + } + + q = &pq_queue[i++]; + + if (!q) + return q; + + q->head = q->tail = q->len = 0; + + return q; +} + +int +pq_enqueue(pq_queue_t *q, void *p) +{ + if (q->len == PQ_QUEUE_SIZE) + return -1; + + q->data[q->head] = p; + q->head = (q->head + 1)%PQ_QUEUE_SIZE; + q->len++; + + return 0; +} + +void* +pq_dequeue(pq_queue_t *q) +{ + int ptail; + + if (q->len == 0) + return NULL; + + ptail = q->tail; + q->tail = (q->tail + 1)%PQ_QUEUE_SIZE; + q->len--; + + return q->data[ptail]; +} + +int +pq_qlength(pq_queue_t *q) +{ + return q->len; +} |