diff --git a/CMakeLists.txt b/CMakeLists.txt index 4beaf4b..0addf44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,15 +46,16 @@ add_executable(${TARGET_NAME}) # lwip set (LWIP_INCLUDE_DIRS "${LWIP_DIR}/src/include" - "bsp_z7/lwip" + "bsp_z7/lwip/include" ) -#include(${LWIP_DIR}/src/Filelists.cmake) +include(${LWIP_DIR}/src/Filelists.cmake) set(lwip_SRCS ${lwipcore_SRCS} ${lwipcore4_SRCS} ${lwipcore6_SRCS} - ${LWIP_DIR}/src/netif/slipif.c - ${LWIP_DIR}/src/apps/tftp/tftp.c + ${lwipnetif_SRCS} + #${LWIP_DIR}/src/netif/slipif.c + #${LWIP_DIR}/src/apps/tftp/tftp.c ) if(${CMAKE_CROSSCOMPILING}) add_library(lwip ${lwip_SRCS}) diff --git a/bsp_z7/CMakeLists.txt b/bsp_z7/CMakeLists.txt index c3ab00e..596ab69 100644 --- a/bsp_z7/CMakeLists.txt +++ b/bsp_z7/CMakeLists.txt @@ -1,6 +1,8 @@ add_subdirectory(freeRTOS) add_subdirectory(ps7_cortexa9_0) +add_subdirectory(lwip) add_subdirectory(newlib) add_subdirectory(hardware) -target_sources(${TARGET_NAME} PRIVATE main.c) +target_sources(${TARGET_NAME} PRIVATE main.c testEth.c) + diff --git a/bsp_z7/lwip/CMakeLists.txt b/bsp_z7/lwip/CMakeLists.txt new file mode 100644 index 0000000..a642046 --- /dev/null +++ b/bsp_z7/lwip/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(netif) \ No newline at end of file diff --git a/bsp_z7/lwip/arch/cc.h b/bsp_z7/lwip/include/arch/cc.h similarity index 100% rename from bsp_z7/lwip/arch/cc.h rename to bsp_z7/lwip/include/arch/cc.h diff --git a/bsp_z7/lwip/lwipopts.h b/bsp_z7/lwip/include/lwipopts.h similarity index 99% rename from bsp_z7/lwip/lwipopts.h rename to bsp_z7/lwip/include/lwipopts.h index 64ea35b..b40fe12 100644 --- a/bsp_z7/lwip/lwipopts.h +++ b/bsp_z7/lwip/include/lwipopts.h @@ -219,7 +219,7 @@ a lot of data that needs to be copied, this should be set high. */ #define TCPIP_MBOX_SIZE 5 /* ---------- ARP options ---------- */ -#define LWIP_ARP 0 +#define LWIP_ARP 1 #define ARP_TABLE_SIZE 10 #define ARP_QUEUEING 1 #define ARP_QUEUE_LEN 10 diff --git a/bsp_z7/lwip/lwippools.h b/bsp_z7/lwip/include/lwippools.h similarity index 100% rename from bsp_z7/lwip/lwippools.h rename to bsp_z7/lwip/include/lwippools.h diff --git a/bsp_z7/lwip/include/netif/xadapter.h b/bsp_z7/lwip/include/netif/xadapter.h new file mode 100644 index 0000000..552a64f --- /dev/null +++ b/bsp_z7/lwip/include/netif/xadapter.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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/bsp_z7/lwip/include/netif/xaxiemacif.h b/bsp_z7/lwip/include/netif/xaxiemacif.h new file mode 100644 index 0000000..2b72689 --- /dev/null +++ b/bsp_z7/lwip/include/netif/xaxiemacif.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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_XAXIEMACIF_H__ +#define __NETIF_XAXIEMACIF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "xlwipconfig.h" +#include "lwip/netif.h" +#include "netif/etharp.h" +#include "netif/xadapter.h" + +#include "xparameters.h" +#include "xstatus.h" + +#include "xaxiethernet.h" +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_FIFO +#include "xllfifo.h" +#elif XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_MCDMA +#include "xmcdma.h" +#else +#include "xaxidma.h" +#include "xaxidma_hw.h" +#endif + +#include "netif/xpqueue.h" +#include "xlwipconfig.h" + +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 +#define AXIDMA_TX_INTR_PRIORITY_SET_IN_GIC 0xA0 +#define AXIDMA_RX_INTR_PRIORITY_SET_IN_GIC 0xA0 +#define AXIETH_INTR_PRIORITY_SET_IN_GIC 0xA0 +#define TRIG_TYPE_RISING_EDGE_SENSITIVE 0x3 + +#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_0_DIST_BASEADDR +#endif + +void xaxiemacif_setmac(u32_t index, u8_t *addr); +u8_t* xaxiemacif_getmac(u32_t index); +err_t xaxiemacif_init(struct netif *netif); +int xaxiemacif_input(struct netif *netif); + +unsigned get_IEEE_phy_speed(XAxiEthernet *xaxiemacp); +void enable_sgmii_clock(XAxiEthernet *xaxiemacp); +unsigned configure_IEEE_phy_speed(XAxiEthernet *xaxiemacp, unsigned speed); +unsigned phy_setup_axiemac (XAxiEthernet *xaxiemacp); + +/* xaxiemacif_hw.c */ +void xaxiemac_error_handler(XAxiEthernet * Temac); + +/* structure within each netif, encapsulating all information required for + * using a particular temac instance + */ +typedef struct { +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_FIFO + XLlFifo axififo; +#elif defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_MCDMA) + XMcdma aximcdma; +#else + XAxiDma axidma; +#endif + XAxiEthernet axi_ethernet; + + /* 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; + + enum ethernet_link_status eth_link_status; +} xaxiemacif_s; + +extern xaxiemacif_s xaxiemacif; + +#ifndef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_FIFO +s32_t xaxiemac_is_tx_space_available(xaxiemacif_s *emac); +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_MCDMA +s32_t process_sent_bds(XMcdma_ChanCtrl *Tx_Chan); +#else +s32_t process_sent_bds(XAxiDma_BdRing *txring); +#endif +#endif + +/* xaxiemacif_dma.c/xaxiemacif_mcdma.c */ +#ifndef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_FIFO +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_MCDMA +XStatus init_axi_mcdma(struct xemac_s *xemac); +XStatus axi_mcdma_sgsend(xaxiemacif_s *xaxiemacif, struct pbuf *p); +#else +XStatus init_axi_dma(struct xemac_s *xemac); +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +XStatus axidma_sgsend(xaxiemacif_s *xaxiemacif, struct pbuf *p, + u32_t block_till_tx_complete, u32_t *to_block_index); +#else +XStatus axidma_sgsend(xaxiemacif_s *xaxiemacif, struct pbuf *p); +#endif +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_XAXIEMACIF_H__ */ diff --git a/bsp_z7/lwip/include/netif/xemacliteif.h b/bsp_z7/lwip/include/netif/xemacliteif.h new file mode 100644 index 0000000..3da418d --- /dev/null +++ b/bsp_z7/lwip/include/netif/xemacliteif.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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_XEMACLITEIF_H__ +#define __NETIF_XEMACLITEIF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/netif.h" +#include "netif/etharp.h" +#include "netif/xpqueue.h" +#include "xemaclite.h" +#include "xemaclite_i.h" +#include "xstatus.h" + +/* structure within each netif, encapsulating all information required for + * using a particular emaclite instance + */ +typedef struct { + XEmacLite *instance; + + /* queue to store overflow packets */ + pq_queue_t *recv_q; + pq_queue_t *send_q; + + enum ethernet_link_status eth_link_status; +} xemacliteif_s; + +void xemacliteif_setmac(u32_t index, u8_t *addr); +u8_t* xemacliteif_getmac(u32_t index); +err_t xemacliteif_init(struct netif *netif); +int xemacliteif_input(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_XEMACLITEIF_H__ */ diff --git a/bsp_z7/lwip/include/netif/xemacpsif.h b/bsp_z7/lwip/include/netif/xemacpsif.h new file mode 100644 index 0000000..2b6a653 --- /dev/null +++ b/bsp_z7/lwip/include/netif/xemacpsif.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 + +#define VERSAL_NET_EMACPS_0_BASEADDR 0xF19E0000 +#define VERSAL_NET_EMACPS_1_BASEADDR 0xF19F0000 + +#ifdef XPAR_PSX_CRL_0_S_AXI_BASEADDR +#define VERSAL_NET_CRL_GEM0_REF_CTRL ( XPAR_PSX_CRL_0_S_AXI_BASEADDR + 0x118) +#define VERSAL_NET_CRL_GEM1_REF_CTRL ( XPAR_PSX_CRL_0_S_AXI_BASEADDR + 0x11C) +#endif + +#define VERSAL_NET_CRL_GEM_DIV_MASK VERSAL_CRL_GEM_DIV_MASK +#define VERSAL_NET_CRL_APB_GEM_DIV_SHIFT VERSAL_CRL_APB_GEM_DIV_SHIFT + +#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; + enum ethernet_link_status eth_link_status; +} xemacpsif_s; + +extern xemacpsif_s xemacpsif; + +s32_t xemacps_is_tx_space_available(xemacpsif_s *emac); + +/* xemacpsif_dma.c */ + +void xemacps_process_sent_bds(xemacpsif_s *xemacpsif, XEmacPs_BdRing *txring); +u32_t phy_setup_emacps (XEmacPs *xemacpsp, u32_t phy_addr); +#ifdef SGMII_FIXED_LINK +u32_t pcs_setup_emacps (XEmacPs *xemacps); +#endif +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/bsp_z7/lwip/include/netif/xlltemacif.h b/bsp_z7/lwip/include/netif/xlltemacif.h new file mode 100644 index 0000000..4c5ae99 --- /dev/null +++ b/bsp_z7/lwip/include/netif/xlltemacif.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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_XLLTEMACIF_H__ +#define __NETIF_XLLTEMACIF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwip/netif.h" +#include "netif/etharp.h" +#include "netif/xadapter.h" + +#include "xparameters.h" +#include "xstatus.h" +#include "xlltemac.h" +#include "xlldma.h" +#include "xllfifo.h" +#include "xlldma_bdring.h" + +#include "netif/xpqueue.h" +#include "xlwipconfig.h" + +void xlltemacif_setmac(u32_t index, u8_t *addr); +u8_t* xlltemacif_getmac(u32_t index); +err_t xlltemacif_init(struct netif *netif); +int xlltemacif_input(struct netif *netif); +unsigned get_IEEE_phy_speed(XLlTemac *xlltemacp); +unsigned Phy_Setup (XLlTemac *xlltemacp); +unsigned configure_IEEE_phy_speed(XLlTemac *xlltemacp, unsigned speed); + +/* xlltemacif_hw.c */ +void xlltemac_error_handler(XLlTemac * Temac); + +/* structure within each netif, encapsulating all information required for + * using a particular temac instance + */ +typedef struct { + XLlDma lldma; + XLlFifo llfifo; + XLlTemac lltemac; + + /* 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; + + enum ethernet_link_status eth_link_status; +} xlltemacif_s; + +extern xlltemacif_s xlltemacif; + +/* xlltemacif_sdma.c */ +XStatus init_sdma(struct xemac_s *xemac); +int process_sent_bds(XLlDma_BdRing *txring); +void lldma_send_handler(void *arg); +XStatus lldma_sgsend(xlltemacif_s *xlltemacif, struct pbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_XLLTEMACIF_H__ */ diff --git a/bsp_z7/lwip/include/netif/xpqueue.h b/bsp_z7/lwip/include/netif/xpqueue.h new file mode 100644 index 0000000..ba5c917 --- /dev/null +++ b/bsp_z7/lwip/include/netif/xpqueue.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 + +#include "lwip/debug.h" + +#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/bsp_z7/lwip/include/netif/xtopology.h b/bsp_z7/lwip/include/netif/xtopology.h new file mode 100644 index 0000000..77f73b1 --- /dev/null +++ b/bsp_z7/lwip/include/netif/xtopology.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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; +#ifndef SDT + 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 */ +#endif +}; + +extern int xtopology_n_emacs; +extern struct xtopology_t xtopology[]; + +int xtopology_find_index(unsigned base); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp_z7/lwip/onrre.h b/bsp_z7/lwip/include/onrre.h similarity index 100% rename from bsp_z7/lwip/onrre.h rename to bsp_z7/lwip/include/onrre.h diff --git a/bsp_z7/lwip/include/xlwipconfig.h b/bsp_z7/lwip/include/xlwipconfig.h new file mode 100644 index 0000000..e7691b1 --- /dev/null +++ b/bsp_z7/lwip/include/xlwipconfig.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 __XLWIPCONFIG_H_ +#define __XLWIPCONFIG_H_ + +#define XLWIP_CONFIG_INCLUDE_GEM +#define XLWIP_CONFIG_N_TX_DESC 64 +#define XLWIP_CONFIG_N_RX_DESC 64 + +//#define CONFIG_LINKSPEED100 +#define CONFIG_LINKSPEED_AUTODETECT + +//TODO defines +/* +#cmakedefine XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ @XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ@ +#cmakedefine XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ @XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ@ +#cmakedefine XLWIP_CONFIG_INCLUDE_EMACLITE @XLWIP_CONFIG_INCLUDE_EMACLITE@ +#cmakedefine XLWIP_CONFIG_INCLUDE_AXI_ETHERNET @XLWIP_CONFIG_INCLUDE_AXI_ETHERNET@ +#cmakedefine XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_DMA @XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_DMA@ +#cmakedefine XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_FIFO @XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_FIFO@ +#cmakedefine XLWIP_CONFIG_AXI_ETHERNET_ENABLE_1588 @XLWIP_CONFIG_AXI_ETHERNET_ENABLE_1588@ +#cmakedefine XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_MCDMA @XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_MCDMA@ +#cmakedefine XLWIP_CONFIG_INCLUDE_GEM @XLWIP_CONFIG_INCLUDE_GEM@ +#cmakedefine XLWIP_CONFIG_N_TX_DESC @XLWIP_CONFIG_N_TX_DESC@ +#cmakedefine XLWIP_CONFIG_N_RX_DESC @XLWIP_CONFIG_N_RX_DESC@ +#cmakedefine XLWIP_CONFIG_N_TX_COALESCE @XLWIP_CONFIG_N_TX_COALESCE@ +#cmakedefine XLWIP_CONFIG_N_RX_COALESCE @XLWIP_CONFIG_N_RX_COALESCE@ +#cmakedefine XLWIP_CONFIG_EMAC_NUMBER @XLWIP_CONFIG_EMAC_NUMBER@ +#cmakedefine XLWIP_CONFIG_PCS_PMA_1000BASEX_CORE_PRESENT @XLWIP_CONFIG_PCS_PMA_1000BASEX_CORE_PRESENT@ +#cmakedefine XLWIP_CONFIG_PCS_PMA_SGMII_CORE_PRESENT @XLWIP_CONFIG_PCS_PMA_SGMII_CORE_PRESENT@ +*/ + +#endif diff --git a/bsp_z7/lwip/netif/CMakeLists.txt b/bsp_z7/lwip/netif/CMakeLists.txt new file mode 100644 index 0000000..a0b386d --- /dev/null +++ b/bsp_z7/lwip/netif/CMakeLists.txt @@ -0,0 +1,12 @@ +target_sources(lwip PRIVATE + xadapter.c + xpqueue.c + xemacpsif_dma.c + xemacpsif_physpeed.c + xemacpsif_hw.c + xemacpsif.c + topology.c +) + +target_include_directories(lwip PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + diff --git a/bsp_z7/lwip/netif/topology.c b/bsp_z7/lwip/netif/topology.c new file mode 100644 index 0000000..ad8393c --- /dev/null +++ b/bsp_z7/lwip/netif/topology.c @@ -0,0 +1,37 @@ +#include "netif/xtopology.h" + +// struct xtopology_t { +// UINTPTR emac_baseaddr; +// enum xemac_types emac_type; +// #ifndef SDT +// 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 */ +// #endif +// }; + + +int xtopology_n_emacs = 2; +struct xtopology_t xtopology[] = { + { + 0xE000B000, + xemac_type_emacps, +#ifndef SDT + 0, + 0, + 0xF8F00100, + 0x36 +#endif + }, + { + 0xE000C000, + xemac_type_emacps, +#ifndef SDT + 0, + 0, + 0xF8F00100, + 0x4D +#endif + } +}; \ No newline at end of file diff --git a/bsp_z7/lwip/netif/xadapter.c b/bsp_z7/lwip/netif/xadapter.c new file mode 100644 index 0000000..2c48d74 --- /dev/null +++ b/bsp_z7/lwip/netif/xadapter.c @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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; + +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; + +#ifdef SDT + for (i = 0; xtopology[i].emac_baseaddr != NULL; i++) { +#else + for (i = 0; i < xtopology_n_emacs; i++) { +#endif + if (xtopology[i].emac_baseaddr == base) + return xtopology[i].emac_type; + } + + return xemac_type_unknown; +} + +int +xtopology_find_index(unsigned base) +{ + int i; +#ifdef SDT + for (i = 0; xtopology[i].emac_baseaddr != NULL; i++) { +#else + for (i = 0; i < xtopology_n_emacs; i++) { +#endif + 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; + struct netif * nif = NULL; + + /* 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 + nif = netif_add(netif, ipaddr, netmask, gw, + (void*)(UINTPTR)mac_baseaddr, + xemacliteif_init, +#if NO_SYS + ethernet_input +#else + tcpip_input +#endif + ); +#else + nif = NULL; +#endif + break; + + case xemac_type_axi_ethernet: +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET + nif = netif_add(netif, ipaddr, netmask, gw, + (void*)(UINTPTR)mac_baseaddr, + xaxiemacif_init, +#if NO_SYS + ethernet_input +#else + tcpip_input +#endif + ); +#else + nif = NULL; +#endif + break; + +#if defined (__arm__) || defined (__aarch64__) + case xemac_type_emacps: +#ifdef XLWIP_CONFIG_INCLUDE_GEM + nif = netif_add(netif, ipaddr, netmask, gw, + (void*)(UINTPTR)mac_baseaddr, + xemacpsif_init, +#if NO_SYS + ethernet_input +#else + tcpip_input +#endif + + ); +#endif + break; +#endif + default: + xil_printf("unable to determine type of EMAC with baseaddress 0x%08lx\r\n", + (UINTPTR)mac_baseaddr); + } + + #ifdef OS_IS_FREERTOS + /* 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 + + return nif; +} + +#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 + xil_printf("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 + xil_printf("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: + xil_printf("incorrect configuration: unknown temac type"); + while(1); + return 0; + } + + return n_packets; +} + +#ifdef SGMII_FIXED_LINK +static u32_t pcs_link_detect(XEmacPs *xemacp) +{ + u16_t status; + + status = XEmacPs_ReadReg(xemacp->Config.BaseAddress, XEMACPS_PCS_STATUS_OFFSET); + status = XEmacPs_ReadReg(xemacp->Config.BaseAddress, XEMACPS_PCS_STATUS_OFFSET); + status &= XEMACPS_PCS_STATUS_LINK_STATUS_MASK; + if (status) + return 1; + + return 0; +} +#endif + +#if defined(XLWIP_CONFIG_INCLUDE_GEM) +void emacps_link_status(struct netif *netif, xemacpsif_s *xemacs, XEmacPs *xemacp) +{ + u32_t link_speed, phy_link_status, phy_autoneg_status; + u16_t status; + + if ((xemacp->IsReady != (u32)XIL_COMPONENT_IS_READY) || + (xemacs->eth_link_status == ETH_LINK_UNDEFINED)) + return; +#ifndef SGMII_FIXED_LINK + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XEmacPs_PhyRead(xemacp, phyaddrforemac, IEEE_STATUS_REG_OFFSET, &status); + XEmacPs_PhyRead(xemacp, phyaddrforemac, IEEE_STATUS_REG_OFFSET, &status); + if (status & IEEE_STAT_LINK_STATUS) + phy_link_status = 1; + else + phy_link_status = 0; + + if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) + phy_autoneg_status = 1; + else + phy_autoneg_status = 0; + +#else + phy_link_status = pcs_link_detect(xemacp); +#endif + + if ((xemacs->eth_link_status == ETH_LINK_UP) && (!phy_link_status)) + xemacs->eth_link_status = ETH_LINK_DOWN; + + switch (xemacs->eth_link_status) { + case ETH_LINK_UNDEFINED: + case ETH_LINK_UP: + return; + case ETH_LINK_DOWN: + netif_set_link_down(netif); + xemacs->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) { + + link_speed = phy_setup_emacps(xemacp, + phyaddrforemac); + XEmacPs_SetOperatingSpeed(xemacp, link_speed); + netif_set_link_up(netif); + xemacs->eth_link_status = ETH_LINK_UP; + xil_printf("Ethernet Link up\r\n"); + } + break; + } + return; +} +#endif + +#if defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) +void axieth_link_status(struct netif *netif, xaxiemacif_s *xemacs, XAxiEthernet *xemacp) +{ + u32_t link_speed, phy_link_status, phy_autoneg_status; + u16_t status; + + if ((xemacp->IsReady != (u32)XIL_COMPONENT_IS_READY) || + (xemacs->eth_link_status == ETH_LINK_UNDEFINED)) + return; +#ifndef SGMII_FIXED_LINK + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XAxiEthernet_PhyRead(xemacp, phyaddrforemac, IEEE_STATUS_REG_OFFSET, &status); + XAxiEthernet_PhyRead(xemacp, phyaddrforemac, IEEE_STATUS_REG_OFFSET, &status); + if (status & IEEE_STAT_LINK_STATUS) + phy_link_status = 1; + else + phy_link_status = 0; + + if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) + phy_autoneg_status = 1; + else + phy_autoneg_status = 0; +#else + phy_link_status = pcs_link_detect(xemacp); +#endif + + if ((xemacs->eth_link_status == ETH_LINK_UP) && (!phy_link_status)) + xemacs->eth_link_status = ETH_LINK_DOWN; + + switch (xemacs->eth_link_status) { + case ETH_LINK_UNDEFINED: + case ETH_LINK_UP: + return; + case ETH_LINK_DOWN: + netif_set_link_down(netif); + xemacs->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) { + + link_speed = phy_setup_axiemac(xemacp); + XAxiEthernet_SetOperatingSpeed(xemacp,link_speed); + netif_set_link_up(netif); + xemacs->eth_link_status = ETH_LINK_UP; + xil_printf("Ethernet Link up\r\n"); + } + break; + } + return; +} +#endif +#if defined(XLWIP_CONFIG_INCLUDE_EMACLITE) +void emaclite_link_status(struct netif *netif, xemacliteif_s *xemacs, XEmacLite *xemacp) +{ + u32_t phy_link_status, status, phy_autoneg_status; + + if ((xemacp->IsReady != (u32)XIL_COMPONENT_IS_READY) || + (xemacs->eth_link_status == ETH_LINK_UNDEFINED)) + return; +#ifndef SGMII_FIXED_LINK + /* Read Phy Status register twice to get the confirmation of the current + * link status. + */ + XEmacLite_PhyRead(xemacp, phyaddrforemac, IEEE_STATUS_REG_OFFSET, &status); + XEmacLite_PhyRead(xemacp, phyaddrforemac, IEEE_STATUS_REG_OFFSET, &status); + if (status & IEEE_STAT_LINK_STATUS) + phy_link_status = 1; + else + phy_link_status = 0; + + if (status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) + phy_autoneg_status = 1; + else + phy_autoneg_status = 0; +#else + phy_link_status = pcs_link_detect(xemacp); +#endif + + if ((xemacs->eth_link_status == ETH_LINK_UP) && (!phy_link_status)) + xemacs->eth_link_status = ETH_LINK_DOWN; + + switch (xemacs->eth_link_status) { + case ETH_LINK_UNDEFINED: + case ETH_LINK_UP: + return; + case ETH_LINK_DOWN: + netif_set_link_down(netif); + xemacs->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) { + netif_set_link_up(netif); + xemacs->eth_link_status = ETH_LINK_UP; + xil_printf("Ethernet Link up\r\n"); + } + break; + } + return; +} +#endif + +void eth_link_detect(struct netif *netif) +{ + struct xemac_s *xemac = (struct xemac_s *)(netif->state); + +#if defined(XLWIP_CONFIG_INCLUDE_GEM) + xemacpsif_s *xemacps = (xemacpsif_s *)(xemac->state); + XEmacPs *xemacpsp = &xemacps->emacps; +#endif +#if defined(XLWIP_CONFIG_INCLUDE_AXI_ETHERNET) + xaxiemacif_s *xaxiemac = (xaxiemacif_s *)(xemac->state); + XAxiEthernet *xaxiemacp = &xaxiemac->axi_ethernet; +#endif +#if defined(XLWIP_CONFIG_INCLUDE_EMACLITE) + xemacliteif_s *xemaclite = (xemacliteif_s *)(xemac->state); + XEmacLite *xemaclitep = xemaclite->instance; +#endif + + switch (xemac->type) { + case xemac_type_emacps: +#if defined(XLWIP_CONFIG_INCLUDE_GEM) + emacps_link_status(netif, xemacps, xemacpsp); +#endif + break; + case xemac_type_xps_emaclite: +#if defined(XLWIP_CONFIG_INCLUDE_EMACLITE) + emaclite_link_status(netif, xemaclite, xemaclitep); +#endif + break; + case xemac_type_axi_ethernet: +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET + axieth_link_status(netif, xaxiemac, xaxiemacp); +#endif + 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/bsp_z7/lwip/netif/xaxiemacif.c b/bsp_z7/lwip/netif/xaxiemacif.c new file mode 100644 index 0000000..d3ed2fa --- /dev/null +++ b/bsp_z7/lwip/netif/xaxiemacif.c @@ -0,0 +1,752 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 +#include + +#include + +#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/xaxiemacif.h" +#include "netif/xadapter.h" +#include "netif/xpqueue.h" + +#include "xaxiemacif_fifo.h" +#include "xaxiemacif_hw.h" + +#include "xparameters.h" +#ifndef SDT +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 +#include "xscugic.h" +#else +#include "xintc.h" +#endif +#else +#include "xinterrupt_wrap.h" +#endif + +#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 xaxiemacif_mac_filter_update (struct netif *netif, + ip_addr_t *group, u8_t action); + +static u8_t xaxiemac_mcast_entry_mask = 0; +#endif + +#if LWIP_IPV6 && LWIP_IPV6_MLD +#define XAXIEMAC_MAX_MAC_ADDR 4 +static err_t xaxiemacif_mld6_mac_filter_update (struct netif *netif, + ip_addr_t *group, u8_t action); + +static u8_t xaxiemac_mld6_mcast_entry_mask; +#endif + +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +extern volatile u32_t notifyinfo[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(xaxiemacif_s *xaxiemacif, + struct pbuf *p, u32_t block_till_tx_complete, u32_t *to_block_index ) +#else +static err_t _unbuffered_low_level_output(xaxiemacif_s *xaxiemacif, + 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 (XAxiEthernet_IsDma(&xaxiemacif->axi_ethernet)) { +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_DMA +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + if (block_till_tx_complete == 1) { + status = axidma_sgsend(xaxiemacif, p, 1, to_block_index); + } else { + status = axidma_sgsend(xaxiemacif, p, 0, to_block_index); + } +#else + status = axidma_sgsend(xaxiemacif, p); +#endif +#endif + } else if (XAxiEthernet_IsMcDma(&xaxiemacif->axi_ethernet)) { +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_MCDMA + xil_printf("lwip support with mcdma is deprecated\n"); + status = axi_mcdma_sgsend(xaxiemacif, p); +#endif + } else { +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_FIFO + status = axififo_send(xaxiemacif, 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; +#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); + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_DMA + /* + * With AXI Ethernet on Zynq, we observed unexplained delays for + * BD Status update. As a result, we are hitting a condition where + * there are no BDs free to transmit packets. So, we have added + * this logic where we look for the status update in a definite + * loop. + */ + XAxiDma_BdRing *txring = XAxiDma_GetTxRing(&xaxiemacif->axidma); +#endif + int count = 100; + + SYS_ARCH_PROTECT(lev); + + while (count) { + + /* check if space is available to send */ + if (xaxiemac_is_tx_space_available(xaxiemacif)) { +#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(xaxiemacif, p, 1, &to_block_index); + break; + } else { + err = _unbuffered_low_level_output(xaxiemacif, p, 0, &to_block_index); + break; + } +#else + err = _unbuffered_low_level_output(xaxiemacif, p); + break; +#endif + } else { +#if LINK_STATS + lwip_stats.link.drop++; +#endif +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_DMA + process_sent_bds(txring); +#endif + count--; + } + } + + if (count == 0) { + xil_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); + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + struct pbuf *p; + + /* see if there is data to process */ + if (pq_qlength(xaxiemacif->recv_q) == 0) + return NULL; + + /* return one packet from receive q */ + p = (struct pbuf *)pq_dequeue(xaxiemacif->recv_q); + return p; +} + +/* + * xaxiemacif_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 xaxiemacif_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); +} + +/* + * xaxiemacif_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) + * + */ + +int xaxiemacif_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, ("xaxiemacif_input: IP input error\r\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } + } + return 1; +} + +static err_t low_level_init(struct netif *netif) +{ + unsigned mac_address = (unsigned)(UINTPTR)(netif->state); + struct xemac_s *xemac; + xaxiemacif_s *xaxiemacif; + XAxiEthernet_Config *mac_config; + + xaxiemacif = mem_malloc(sizeof *xaxiemacif); + if (xaxiemacif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("xaxiemacif_init: out of memory\r\n")); + return ERR_MEM; + } + + xemac = mem_malloc(sizeof *xemac); + if (xemac == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("xaxiemacif_init: out of memory\r\n")); + return ERR_MEM; + } + + xemac->state = (void *)xaxiemacif; + xemac->topology_index = xtopology_find_index(mac_address); + xemac->type = xemac_type_axi_ethernet; + + xaxiemacif->send_q = NULL; + xaxiemacif->recv_q = pq_create_queue(); + if (!xaxiemacif->recv_q) + return ERR_MEM; + + /* maximum transfer unit */ +#ifdef USE_JUMBO_FRAMES + netif->mtu = XAE_JUMBO_MTU - XAE_HDR_SIZE; +#else + netif->mtu = XAE_MTU - XAE_HDR_SIZE; +#endif + +#if LWIP_IGMP + netif->igmp_mac_filter = xaxiemacif_mac_filter_update; +#endif + +#if LWIP_IPV6 && LWIP_IPV6_MLD + netif->mld_mac_filter = xaxiemacif_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 = xaxiemac_lookup_config((unsigned)(UINTPTR)netif->state); + + XAxiEthernet_Initialize(&xaxiemacif->axi_ethernet, mac_config, + mac_config->BaseAddress); + +#ifdef XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT + enable_sgmii_clock(&xaxiemacif->axi_ethernet); +#endif + + /* figure out if the system has DMA */ + if (XAxiEthernet_IsDma(&xaxiemacif->axi_ethernet)) { +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_DMA + /* initialize the DMA engine */ + init_axi_dma(xemac); +#endif + } else if (XAxiEthernet_IsFifo(&xaxiemacif->axi_ethernet)) { +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_FIFO + /* initialize the locallink FIFOs */ + init_axi_fifo(xemac); +#endif + } else if (XAxiEthernet_IsMcDma(&xaxiemacif->axi_ethernet)) { +#ifdef XLWIP_CONFIG_INCLUDE_AXI_ETHERNET_MCDMA + /* Initialize MCDMA engine */ + init_axi_mcdma(xemac); +#endif + } else { + /* should not occur */ + LWIP_DEBUGF(NETIF_DEBUG, ("xaxiemacif_init: mac is not configured with DMA, MCDMA or FIFO\r\n")); + return ERR_IF; + } + + /* initialize the mac */ + init_axiemac(xaxiemacif, netif); + + /* replace the state in netif (currently the emac baseaddress) + * with the mac instance pointer. + */ + netif->state = (void *)xemac; + + return ERR_OK; +} +#if LWIP_IPV6 && LWIP_IPV6_MLD +static u8_t xaxiemacif_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 xaxiemacif_mld6_mac_hash_update (struct netif *netif, u8_t *ip_addr, + u8_t action,u8_t entry) +{ + u8_t multicast_mac_addr[6]; + u8_t multicast_mac_addr_to_clr[6]; + struct xemac_s *xemac = (struct xemac_s *)(netif->state); + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + + + if (action == NETIF_ADD_MAC_FILTER) { + /* Set Mulitcast mac address in hash table */ + 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]; + + XAxiEthernet_Stop(&xaxiemacif->axi_ethernet); + + XAxiEthernet_MulticastAdd(&xaxiemacif->axi_ethernet,multicast_mac_addr, entry); + + XAxiEthernet_Start(&xaxiemacif->axi_ethernet); + + } else if (action == NETIF_DEL_MAC_FILTER) { + /* Remove Mulitcast mac address in hash table */ + + XAxiEthernet_MulticastGet(&xaxiemacif->axi_ethernet,multicast_mac_addr_to_clr, entry); + + XAxiEthernet_Stop(&xaxiemacif->axi_ethernet); + + XAxiEthernet_MulticastClear(&xaxiemacif->axi_ethernet, entry); + + XAxiEthernet_Start(&xaxiemacif->axi_ethernet); + } +} +static err_t +xaxiemacif_mld6_mac_filter_update (struct netif *netif, ip_addr_t *group, + u8_t action) +{ + u8_t temp_mask; + unsigned int i; + u8_t entry; + u8_t * ip_addr = (u8_t *) group; + + if(!(xaxiemacif_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 < XAXIEMAC_MAX_MAC_ADDR; i++) { + temp_mask = (0x01) << i; + if ((xaxiemac_mld6_mcast_entry_mask & temp_mask) == temp_mask) { + continue; + } + entry = i; + xaxiemac_mld6_mcast_entry_mask |= temp_mask; + + /* Update mac address in hash table */ + xaxiemacif_mld6_mac_hash_update(netif, ip_addr, action,entry); + + 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 < XAXIEMAC_MAX_MAC_ADDR; i++) { + temp_mask = (0x01) << i; + if ((xaxiemac_mld6_mcast_entry_mask & temp_mask) == temp_mask) { + entry = i; + xaxiemacif_mld6_mac_hash_update(netif, ip_addr,action, entry); + + xaxiemac_mld6_mcast_entry_mask &= (~temp_mask); + + LWIP_DEBUGF(NETIF_DEBUG, + ("%s: Multicast MAC address successfully removed.\r\n", __func__)); + + return ERR_OK; + } else { + continue; + } + } + 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 err_t +xaxiemacif_mac_filter_update (struct netif *netif, ip_addr_t *group, + u8_t action) +{ + err_t return_val = ERR_OK; + u8_t multicast_mac_addr[6]; + u8_t multicast_mac_addr_to_clr[6]; + u8_t temp_mask; + int entry; + int i; + u8_t * ip_addr_temp = (u8_t *)group; + struct xemac_s *xemac = (struct xemac_s *)(netif->state); + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + + if (action == IGMP_ADD_MAC_FILTER) { + if ((ip_addr_temp[0] >= 224) && (ip_addr_temp[0] <= 239)) { + if (xaxiemac_mcast_entry_mask >= 0x0F) { + LWIP_DEBUGF(NETIF_DEBUG, + ("xaxiemacif_mac_filter_update: No multicast address registers left.\r\n")); + LWIP_DEBUGF(NETIF_DEBUG, + (" Multicast MAC address add operation failure !!\r\n")); + + return_val = ERR_MEM; + } else { + for (i = 0; i < 4; i++) { + temp_mask = (0x01) << i; + if ((xaxiemac_mcast_entry_mask & + temp_mask) == temp_mask) { + + continue; + + } else { + entry = i; + xaxiemac_mcast_entry_mask + |= temp_mask; + multicast_mac_addr[0] = 0x01; + multicast_mac_addr[1] = 0x00; + multicast_mac_addr[2] = 0x5E; + + multicast_mac_addr[3] = + ip_addr_temp[1] & 0x7F; + multicast_mac_addr[4] = + ip_addr_temp[2]; + multicast_mac_addr[5] = + ip_addr_temp[3]; + + XAxiEthernet_Stop + (&xaxiemacif->axi_ethernet); + + XAxiEthernet_MulticastAdd + (&xaxiemacif->axi_ethernet, + multicast_mac_addr,entry); + + XAxiEthernet_Start + (&xaxiemacif->axi_ethernet); + + LWIP_DEBUGF(NETIF_DEBUG, + ("xaxiemacif_mac_filter_update: Multicast MAC address successfully added.\r\n")); + + return_val = ERR_OK; + break; + } + } + if (i == 4) { + LWIP_DEBUGF(NETIF_DEBUG, + ("xaxiemacif_mac_filter_update: No multicast address registers left.\r\n")); + LWIP_DEBUGF(NETIF_DEBUG, + (" Multicast MAC address add operation failure !!\r\n")); + + return_val = ERR_MEM; + } + } + } else { + LWIP_DEBUGF(NETIF_DEBUG, + ("xaxiemacif_mac_filter_update: The requested MAC address is not a multicast address.\r\n")); + LWIP_DEBUGF(NETIF_DEBUG, + (" Multicast address add operation failure !!\r\n")); + + return_val = ERR_ARG; + } + } else if (action == IGMP_DEL_MAC_FILTER) { + if ((ip_addr_temp[0] < 224) || (ip_addr_temp[0] > 239)) { + + LWIP_DEBUGF(NETIF_DEBUG, + ("xaxiemacif_mac_filter_update: The requested MAC address is not a multicast address.\r\n")); + LWIP_DEBUGF(NETIF_DEBUG, + (" Multicast address add operation failure !!\r\n")); + + return_val = ERR_ARG; + } else { + for (i = 0; i < 4; i++) { + temp_mask = (0x01) << i; + if ((xaxiemac_mcast_entry_mask & temp_mask) + == temp_mask) { + XAxiEthernet_MulticastGet + (&xaxiemacif->axi_ethernet, + multicast_mac_addr_to_clr, i); + + if ((ip_addr_temp[3] == + multicast_mac_addr_to_clr[5]) && + (ip_addr_temp[2] == + multicast_mac_addr_to_clr[4]) && + ((ip_addr_temp[1] & 0x7f) == + multicast_mac_addr_to_clr[3])) { + + XAxiEthernet_Stop + (&xaxiemacif->axi_ethernet); + + XAxiEthernet_MulticastClear + (&xaxiemacif->axi_ethernet, i); + + XAxiEthernet_Start + (&xaxiemacif->axi_ethernet); + + LWIP_DEBUGF(NETIF_DEBUG, + ("xaxiemacif_mac_filter_update: Multicast MAC address successfully removed.\r\n")); + + return_val = ERR_OK; + xaxiemac_mcast_entry_mask &= + (~temp_mask); + break; + } else { + continue; + } + } else { + continue; + } + } + if (i == 4) { + LWIP_DEBUGF(NETIF_DEBUG, + ("xaxiemacif_mac_filter_update: No multicast address registers present with\r\n")); + LWIP_DEBUGF(NETIF_DEBUG, + (" the requested Multicast MAC address.\r\n")); + LWIP_DEBUGF(NETIF_DEBUG, + (" Multicast MAC address removal failure!!.\r\n")); + + return_val = ERR_MEM; + } + } + } + return return_val; +} +#endif + +/* + * xaxiemacif_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 +xaxiemacif_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 = xaxiemacif_output; + netif->linkoutput = low_level_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif + + low_level_init(netif); + + return ERR_OK; +} diff --git a/bsp_z7/lwip/netif/xaxiemacif_dma.c b/bsp_z7/lwip/netif/xaxiemacif_dma.c new file mode 100644 index 0000000..2169d09 --- /dev/null +++ b/bsp_z7/lwip/netif/xaxiemacif_dma.c @@ -0,0 +1,994 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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" + +#if !NO_SYS +#include "FreeRTOS.h" +#include "semphr.h" +#include "timers.h" +#include "lwip/sys.h" +#endif + +#include "lwip/stats.h" +#include "lwip/inet_chksum.h" + +#include "netif/xadapter.h" +#include "netif/xaxiemacif.h" + +#ifndef SDT +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 +#include "xscugic.h" +#else +#include "xintc_l.h" +#endif +#else +#include "xinterrupt_wrap.h" +#ifndef XCACHE_FLUSH_DCACHE_RANGE +#define XCACHE_FLUSH_DCACHE_RANGE(data, length) \ + Xil_DCacheFlushRange((UINTPTR)data, length) +#endif +#ifndef XCACHE_INVALIDATE_DCACHE_RANGE +#define XCACHE_INVALIDATE_DCACHE_RANGE(data, length) \ + Xil_DCacheInvalidateRange((u32)data, length) +#endif +#endif + +#include "xstatus.h" + +#include "xlwipconfig.h" +#include "xparameters.h" + +#ifdef CONFIG_XTRACE +#include "xtrace.h" +#endif + +#if defined __aarch64__ || defined (__arm__) +#include "xil_mmu.h" +#elif defined (ARMR5) +#include "xreg_cortexr5.h" +#endif + +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 +#ifndef XCACHE_FLUSH_DCACHE_RANGE +#define XCACHE_FLUSH_DCACHE_RANGE(data, length) \ + Xil_DCacheFlushRange((UINTPTR)data, length) +#endif +#ifndef XCACHE_INVALIDATE_DCACHE_RANGE +#define XCACHE_INVALIDATE_DCACHE_RANGE(data, length) \ + Xil_DCacheInvalidateRange((u32)data, length) +#endif + +#endif + +/* Byte alignment of BDs */ +#define BD_ALIGNMENT (XAXIDMA_BD_MINIMUM_ALIGNMENT*2) + +#if XPAR_INTC_0_HAS_FAST == 1 +/*********** Function Prototypes *********************************************/ +/* + * Function prototypes of the functions used for registering Fast + * Interrupt Handlers + */ +static void axidma_sendfast_handler(void) __attribute__ ((fast_interrupt)); +static void axidma_recvfast_handler(void) __attribute__ ((fast_interrupt)); +static void xaxiemac_errorfast_handler(void) __attribute__ ((fast_interrupt)); + +/**************** Variable Declarations **************************************/ +/** Variables for Fast Interrupt handlers ***/ +struct xemac_s *xemac_fast; +xaxiemacif_s *xaxiemacif_fast; +#endif + +#if !NO_SYS +extern u32 xInsideISR; +#endif + +#define BD_SIZE_2MB 0x200000 +#define BD_SIZE_1MB 0x100000 +#define BD_SIZE_64KB 0x10000 + +#if defined (__aarch64__) +u8_t bd_space[BD_SIZE_2MB] __attribute__ ((aligned (BD_SIZE_2MB))); +#elif defined (__arm__) || defined (ARMR5) +u8_t bd_space[BD_SIZE_1MB] __attribute__ ((aligned (BD_SIZE_1MB))); +#endif + +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +volatile u32_t notifyinfo[XLWIP_CONFIG_N_TX_DESC]; +#endif + +#define XAxiDma_BD_TO_INDEX(ringptr, bdptr) \ + (((UINTPTR)bdptr - (UINTPTR)(ringptr)->FirstBdAddr) / (ringptr)->Separation) + +static inline void bd_csum_enable(XAxiDma_Bd *bd) +{ + XAxiDma_BdWrite((bd), XAXIDMA_BD_USR0_OFFSET, + (XAxiDma_BdRead((bd), XAXIDMA_BD_USR0_OFFSET) + | 1)); +} + +static inline void bd_csum_disable(XAxiDma_Bd *bd) +{ + XAxiDma_BdWrite((bd), XAXIDMA_BD_USR0_OFFSET, + (XAxiDma_BdRead((bd), XAXIDMA_BD_USR0_OFFSET) + & ~1)); +} + +static inline void bd_fullcsum_disable(XAxiDma_Bd *bd) +{ + XAxiDma_BdWrite((bd), XAXIDMA_BD_USR0_OFFSET, + (XAxiDma_BdRead((bd), XAXIDMA_BD_USR0_OFFSET) + & ~3)); +} + +static inline void bd_fullcsum_enable(XAxiDma_Bd *bd) +{ + XAxiDma_BdWrite((bd), XAXIDMA_BD_USR0_OFFSET, + (XAxiDma_BdRead((bd), XAXIDMA_BD_USR0_OFFSET) + | 2)); +} + +static inline void bd_csum_set(XAxiDma_Bd *bd, u16_t tx_csbegin, u16_t tx_csinsert, + u16_t tx_csinit) +{ + u32_t app1; + + bd_csum_enable(bd); + + /* write start offset and insert offset into BD */ + app1 = ((u32_t)tx_csbegin << 16) | (u32_t) tx_csinsert; + XAxiDma_BdWrite(bd, XAXIDMA_BD_USR1_OFFSET, app1); + + /* insert init value */ + XAxiDma_BdWrite(bd, XAXIDMA_BD_USR2_OFFSET, tx_csinit); +} + +static inline u16_t extract_packet_len(XAxiDma_Bd *rxbd) { + u16_t packet_len = XAxiDma_BdRead(rxbd, XAXIDMA_BD_USR4_OFFSET) & 0x3fff; + return packet_len; +} + +static inline u16_t extract_csum(XAxiDma_Bd *rxbd) { + u16_t csum = XAxiDma_BdRead(rxbd, XAXIDMA_BD_USR3_OFFSET) & 0xffff; + return csum; +} + +static inline u32_t csum_sub(u32_t csum, u16_t v) +{ + csum += (u32_t)v; + return csum + (csum < (u32_t)v); +} + +/* + * compare if the h/w computed checksum (stored in the rxbd) + * equals the TCP checksum value in the packet + */ +s32_t is_checksum_valid(XAxiDma_Bd *rxbd, struct pbuf *p) { + struct ethip_hdr *ehdr = p->payload; + u8_t proto = IPH_PROTO(&ehdr->ip); + + /* check if it is a TCP packet */ + if (htons(ehdr->eth.type) == ETHTYPE_IP && proto == IP_PROTO_TCP) { + u32_t iphdr_len; + u16_t csum_in_rxbd, pseudo_csum, iphdr_csum, padding_csum; + u16_t tcp_payload_offset; + u32_t computed_csum; + u16_t padding_len, tcp_payload_len, packet_len; + u16_t csum; + + /* determine length of IP header */ + iphdr_len = (IPH_HL(&ehdr->ip) * 4); + tcp_payload_offset = XAE_HDR_SIZE + iphdr_len; + tcp_payload_len = htons(IPH_LEN(&ehdr->ip)) - IPH_HL(&ehdr->ip) * 4; + packet_len = extract_packet_len(rxbd); + padding_len = packet_len - tcp_payload_offset - tcp_payload_len; + + csum_in_rxbd = extract_csum(rxbd); + pseudo_csum = htons(inet_chksum_pseudo(NULL, + proto, tcp_payload_len, (ip_addr_t *)&ehdr->ip.src, + (ip_addr_t *)&ehdr->ip.dest)); + + /* xps_ll_temac computes the checksum of the packet starting at byte 14 + * we need to subtract the values of the ethernet & IP headers + */ + iphdr_csum = inet_chksum(p->payload + 14, tcp_payload_offset - 14); + + /* compute csum of padding bytes, if any */ + padding_csum = inet_chksum(p->payload + p->tot_len - padding_len, + padding_len); + + /* get the h/w checksum value */ + computed_csum = (u32_t)csum_in_rxbd; + + /* remove the effect of csumming the iphdr */ + computed_csum = csum_sub(computed_csum, ~iphdr_csum); + + /* add in the pseudo csum */ + computed_csum = csum_sub(computed_csum, ~pseudo_csum); + + /* remove any padding effect */ + computed_csum = csum_sub(computed_csum, ~padding_csum); + + /* normalize computed csum */ + while (computed_csum >> 16) { + computed_csum = (computed_csum & 0xffff) + (computed_csum >> 16); + } + + /* convert to 16 bits and take 1's complement */ + csum = (u16_t)computed_csum; + csum = ~csum; + + /* chksum is valid if: computed csum over the packet is 0 */ + return !csum; + } else { + /* just say yes to all other packets */ + /* the upper layers in the stack will compute and verify the checksum */ + return 1; + } +} + +static inline void *alloc_bdspace(int n_desc) +{ + int space = XAxiDma_BdRingMemCalc(BD_ALIGNMENT, n_desc); + int padding = BD_ALIGNMENT*2; + void *unaligned_mem = mem_malloc(space + padding*4); + void *aligned_mem = + (void *)(((UINTPTR)(unaligned_mem + BD_ALIGNMENT)) & ~(BD_ALIGNMENT - 1)); + +#if DEBUG + assert(aligned_mem > unaligned_mem); + assert(aligned_mem + space < unaligned_mem + space + padding); +#endif + return aligned_mem; +} + +static void axidma_send_handler(void *arg) +{ + unsigned irq_status; + struct xemac_s *xemac; + xaxiemacif_s *xaxiemacif; + XAxiDma_BdRing *txringptr; + +#if !NO_SYS + xInsideISR++; +#endif + xemac = (struct xemac_s *)(arg); + xaxiemacif = (xaxiemacif_s *)(xemac->state); + txringptr = XAxiDma_GetTxRing(&xaxiemacif->axidma); + + XAxiDma_BdRingIntDisable(txringptr, XAXIDMA_IRQ_ALL_MASK); + + /* Read pending interrupts */ + irq_status = XAxiDma_BdRingGetIrq(txringptr); + + /* Acknowledge pending interrupts */ + XAxiDma_BdRingAckIrq(txringptr, irq_status); + + /* If error interrupt is asserted, raise error flag, reset the + * hardware to recover from the error, and return with no further + * processing. + */ + if (irq_status & XAXIDMA_IRQ_ERROR_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s: Error: axidma error interrupt is asserted\r\n", + __FUNCTION__)); + XAxiDma_Reset(&xaxiemacif->axidma); +#if !NO_SYS + xInsideISR--; +#endif + return; + } + /* If Transmit done interrupt is asserted, process completed BD's */ + if (irq_status & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK)) { + process_sent_bds(txringptr); + } + + XAxiDma_BdRingIntEnable(txringptr, XAXIDMA_IRQ_ALL_MASK); + +#if !NO_SYS + xInsideISR--; +#endif +} + +static void setup_rx_bds(XAxiDma_BdRing *rxring) +{ + XAxiDma_Bd *rxbd; + s32_t n_bds; + XStatus status; + struct pbuf *p; + u32 bdsts; + + n_bds = XAxiDma_BdRingGetFreeCnt(rxring); + while (n_bds > 0) { + n_bds--; +#ifdef USE_JUMBO_FRAMES + p = pbuf_alloc(PBUF_RAW, XAE_MAX_JUMBO_FRAME_SIZE, PBUF_POOL); +#else + p = pbuf_alloc(PBUF_RAW, XAE_MAX_FRAME_SIZE, PBUF_POOL); +#endif + if (!p) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + xil_printf("unable to alloc pbuf in recv_handler\r\n"); + return; + } + status = XAxiDma_BdRingAlloc(rxring, 1, &rxbd); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("setup_rx_bds: Error allocating RxBD\r\n")); + pbuf_free(p); + return; + } + /* Setup the BD. */ + XAxiDma_BdSetBufAddr(rxbd, (UINTPTR)p->payload); + /* Clear everything but the COMPLETE bit, which is cleared when + * committed to hardware. + */ + bdsts = XAxiDma_BdGetSts(rxbd); + bdsts &= XAXIDMA_BD_STS_COMPLETE_MASK; + XAxiDma_BdWrite(rxbd, XAXIDMA_BD_STS_OFFSET, bdsts); + XAxiDma_BdSetLength(rxbd, p->len, rxring->MaxTransferLen); + XAxiDma_BdSetCtrl(rxbd, 0); + XAxiDma_BdSetId(rxbd, p); +#if !defined (__MICROBLAZE__) && !defined (__riscv) + dsb(); +#endif +#ifdef USE_JUMBO_FRAMES + XCACHE_FLUSH_DCACHE_RANGE((UINTPTR)p->payload, (UINTPTR)XAE_MAX_JUMBO_FRAME_SIZE); +#else + XCACHE_FLUSH_DCACHE_RANGE((UINTPTR)p->payload, (UINTPTR)XAE_MAX_FRAME_SIZE); +#endif +#if !defined(__aarch64__) + XCACHE_FLUSH_DCACHE_RANGE(rxbd, sizeof *rxbd); +#endif + + /* Enqueue to HW */ + status = XAxiDma_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 XAxiDma_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); + XAxiDma_BdRingUnAlloc(rxring, 1, rxbd); + return; + } + } +} + +static void axidma_recv_handler(void *arg) +{ + struct pbuf *p; + u32 irq_status, i, timeOut; + XAxiDma_Bd *rxbd, *rxbdset; + struct xemac_s *xemac; + xaxiemacif_s *xaxiemacif; + XAxiDma_BdRing *rxring; + +#if !NO_SYS + xInsideISR++; +#endif + + xemac = (struct xemac_s *)(arg); + xaxiemacif = (xaxiemacif_s *)(xemac->state); + rxring = XAxiDma_GetRxRing(&xaxiemacif->axidma); + + XAxiDma_BdRingIntDisable(rxring, XAXIDMA_IRQ_ALL_MASK); + + /* Read pending interrupts */ + irq_status = XAxiDma_BdRingGetIrq(rxring); + + /* Acknowledge pending interrupts */ + XAxiDma_BdRingAckIrq(rxring, irq_status); + + /* If error interrupt is asserted, raise error flag, reset the + * hardware to recover from the error, and return with no further + * processing. + */ + if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) { + setup_rx_bds(rxring); + LWIP_DEBUGF(NETIF_DEBUG, ("%s: Error: axidma error interrupt is asserted\r\n", + __FUNCTION__)); + XAxiDma_Reset(&xaxiemacif->axidma); + timeOut = 10000; + while (timeOut) { + if (XAxiDma_ResetIsDone(&xaxiemacif->axidma)) { + break; + } + timeOut -= 1; + } + XAxiDma_BdRingIntEnable(rxring, XAXIDMA_IRQ_ALL_MASK); + XAxiDma_Resume(&xaxiemacif->axidma); +#if !NO_SYS + xInsideISR--; +#endif + return; + } + /* If Reception done interrupt is asserted, call RX call back function + * to handle the processed BDs and then raise the according flag. + */ + if (irq_status & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK)) { + u32 bd_processed; + u32 rx_bytes; + + bd_processed = XAxiDma_BdRingFromHw(rxring, XAXIDMA_ALL_BDS, &rxbdset); + + for (i = 0, rxbd = rxbdset; i < bd_processed; i++) { + p = (struct pbuf *)(UINTPTR)XAxiDma_BdGetId(rxbd); + /* Adjust the buffer size to the actual number of bytes received.*/ + rx_bytes = extract_packet_len(rxbd); + pbuf_realloc(p, rx_bytes); + +#if defined(__aarch64__) +#ifdef USE_JUMBO_FRAMES + XCACHE_INVALIDATE_DCACHE_RANGE(p->payload, + XAE_MAX_JUMBO_FRAME_SIZE); +#else + XCACHE_INVALIDATE_DCACHE_RANGE(p->payload, XAE_MAX_FRAME_SIZE); +#endif +#endif + +#if LWIP_PARTIAL_CSUM_OFFLOAD_RX==1 + /* Verify for partial checksum offload case */ + if (!is_checksum_valid(rxbd, p)) { + LWIP_DEBUGF(NETIF_DEBUG, ("Incorrect csum as calculated by the hw\r\n")); + } +#endif + /* store it in the receive queue, + * where it'll be processed by a different handler + */ + if (pq_enqueue(xaxiemacif->recv_q, (void*)p) < 0) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + pbuf_free(p); + } + rxbd = (XAxiDma_Bd *)XAxiDma_BdRingNext(rxring, rxbd); + } + /* free up the BD's */ + XAxiDma_BdRingFree(rxring, bd_processed, rxbdset); + /* return all the processed bd's back to the stack */ + /* setup_rx_bds -> use XAxiDma_BdRingGetFreeCnt */ + setup_rx_bds(rxring); + } + XAxiDma_BdRingIntEnable(rxring, XAXIDMA_IRQ_ALL_MASK); +#if !NO_SYS + sys_sem_signal(&xemac->sem_rx_data_available); + xInsideISR--; +#endif + +} + +s32_t xaxiemac_is_tx_space_available(xaxiemacif_s *emac) +{ + XAxiDma_BdRing *txring; + + txring = XAxiDma_GetTxRing(&emac->axidma); + /* tx space is available as long as there are valid BD's */ + return XAxiDma_BdRingGetFreeCnt(txring); +} + +s32_t process_sent_bds(XAxiDma_BdRing *txring) +{ + XAxiDma_Bd *txbdset, *txbd; + int n_bds, i; + u32_t bdindex; + + /* obtain a list of processed BD's */ + n_bds = XAxiDma_BdRingFromHw(txring, XAXIDMA_ALL_BDS, &txbdset); + if (n_bds == 0) { + return XST_FAILURE; + } + /* free the pbuf associated with each BD */ + for (i = 0, txbd = txbdset; i < n_bds; i++) { + bdindex = XAxiDma_BD_TO_INDEX(txring, txbd); + struct pbuf *p = (struct pbuf *)(UINTPTR)XAxiDma_BdGetId(txbd); + pbuf_free(p); +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + notifyinfo[bdindex] = 0; +#endif + txbd = (XAxiDma_Bd *)XAxiDma_BdRingNext(txring, txbd); + } + /* free the processed BD's */ + return (XAxiDma_BdRingFree(txring, n_bds, txbdset)); +} + +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE +XStatus axidma_sgsend(xaxiemacif_s *xaxiemacif, struct pbuf *p, + u32_t block_till_tx_complete, u32_t *to_block_index) +#else +XStatus axidma_sgsend(xaxiemacif_s *xaxiemacif, struct pbuf *p) +#endif +{ + struct pbuf *q; + s32_t n_pbufs; + XAxiDma_Bd *txbdset, *txbd, *last_txbd = NULL; + XStatus status; + XAxiDma_BdRing *txring; + u32_t max_frame_size; + u32_t bdindex = 0; + +#ifdef USE_JUMBO_FRAMES + max_frame_size = XAE_MAX_JUMBO_FRAME_SIZE - 18; +#else + max_frame_size = XAE_MAX_FRAME_SIZE - 18; +#endif + txring = XAxiDma_GetTxRing(&xaxiemacif->axidma); + + /* 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 = XAxiDma_BdRingAlloc(txring, n_pbufs, &txbdset); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("sgsend: Error allocating TxBD\r\n")); + return ERR_IF; + } + + for(q = p, txbd = txbdset; q != NULL; q = q->next) { + bdindex = XAxiDma_BD_TO_INDEX(txring, txbd); + /* 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. + */ + XAxiDma_BdSetBufAddr(txbd, (UINTPTR)q->payload); + if (q->len > max_frame_size) { + XAxiDma_BdSetLength(txbd, max_frame_size, + txring->MaxTransferLen); + } + else { + XAxiDma_BdSetLength(txbd, q->len, txring->MaxTransferLen); + } + XAxiDma_BdSetId(txbd, (void *)q); + XAxiDma_BdSetCtrl(txbd, 0); + XCACHE_FLUSH_DCACHE_RANGE(q->payload, q->len); + + pbuf_ref(q); + + last_txbd = txbd; + txbd = (XAxiDma_Bd *)XAxiDma_BdRingNext(txring, txbd); + } + + if (n_pbufs == 1) { + XAxiDma_BdSetCtrl(txbdset, XAXIDMA_BD_CTRL_TXSOF_MASK + | XAXIDMA_BD_CTRL_TXEOF_MASK); + } else { + /* in the first packet, set the SOP */ + XAxiDma_BdSetCtrl(txbdset, XAXIDMA_BD_CTRL_TXSOF_MASK); + /* in the last packet, set the EOP */ + XAxiDma_BdSetCtrl(last_txbd, XAXIDMA_BD_CTRL_TXEOF_MASK); + } + +#if LWIP_FULL_CSUM_OFFLOAD_TX==1 + bd_fullcsum_disable(txbdset); + if (p->len > sizeof(struct ethip_hdr)) { + bd_fullcsum_enable(txbdset); + } +#endif +#if LWIP_PARTIAL_CSUM_OFFLOAD_TX==1 + bd_csum_disable(txbdset); + if (p->len > sizeof(struct ethip_hdr)) { + struct ethip_hdr *ehdr = p->payload; + u8_t proto = IPH_PROTO(&ehdr->ip); + + /* check if it is a TCP packet */ + if (htons(ehdr->eth.type) == ETHTYPE_IP && proto == + IP_PROTO_TCP) { + u32_t iphdr_len, csum_insert_offset; + u16_t tcp_len; /* TCP header length + data length in bytes */ + u16_t csum_init = 0; + u16_t tcp_payload_offset; + + /* determine length of IP header */ + iphdr_len = (IPH_HL(&ehdr->ip) * 4); + + tcp_payload_offset = XAE_HDR_SIZE + iphdr_len; + tcp_len = p->tot_len - tcp_payload_offset; + + /* insert checksum at offset 16 for TCP, 6 for UDP */ + if (proto == IP_PROTO_TCP) + csum_insert_offset = tcp_payload_offset + 16; + else if (proto == IP_PROTO_UDP) + csum_insert_offset = tcp_payload_offset + 6; + else + csum_insert_offset = 0; + /* compute pseudo header checksum value */ + csum_init = inet_chksum_pseudo(NULL, + (ip_addr_t *)&ehdr->ip.src, + (ip_addr_t *)&ehdr->ip.dest, proto, tcp_len); + /* init buffer descriptor */ + bd_csum_set(txbdset, tcp_payload_offset, + csum_insert_offset, htons(~csum_init)); + } + } +#endif + +#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETE + if (block_till_tx_complete == 1) { + notifyinfo[bdindex] = 1; + *to_block_index = bdindex; + } +#endif + + /* enq to h/w */ + return XAxiDma_BdRingToHw(txring, n_pbufs, txbdset); +} + +XStatus init_axi_dma(struct xemac_s *xemac) +{ + XAxiDma_Config *dmaconfig; + XAxiDma_Bd bdtemplate; + XAxiDma_BdRing *rxringptr, *txringptr; + XAxiDma_Bd *rxbd; + struct pbuf *p; + XStatus status; + u32_t i; + u32_t bd_space_index = 0; + UINTPTR baseaddr; + + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + +#if XPAR_INTC_0_HAS_FAST == 1 + xaxiemacif_fast = xaxiemacif; + xemac_fast = xemac; +#endif +#if NO_SYS + struct xtopology_t *xtopologyp = &xtopology[xemac->topology_index]; +#endif +#if !NO_SYS + struct xtopology_t *xtopologyp = &xtopology[xemac->topology_index]; +#endif + + /* FIXME: On ZyqnMP Multiple Axi Ethernet are not supported */ +#if defined (__aarch64__) + Xil_SetTlbAttributes((u64)bd_space, NORM_NONCACHE | INNER_SHAREABLE); +#elif defined (ARMR5) + Xil_SetTlbAttributes((s32_t)bd_space, STRONG_ORDERD_SHARED | PRIV_RW_USER_RW); +#elif defined (__arm__) + Xil_SetTlbAttributes((s32_t)bd_space, DEVICE_MEMORY); +#endif + +#if defined (__MICROBLAZE__) || defined (__riscv) + xaxiemacif->rx_bdspace = alloc_bdspace(XLWIP_CONFIG_N_RX_DESC); + xaxiemacif->tx_bdspace = alloc_bdspace(XLWIP_CONFIG_N_TX_DESC); +#else + xaxiemacif->rx_bdspace = (void *)(UINTPTR)&(bd_space[bd_space_index]); + bd_space_index += BD_SIZE_64KB; + xaxiemacif->tx_bdspace = (void *)(UINTPTR)&(bd_space[bd_space_index]); +#endif + + LWIP_DEBUGF(NETIF_DEBUG, ("rx_bdspace: 0x%08x\r\n", + xaxiemacif->rx_bdspace)); + LWIP_DEBUGF(NETIF_DEBUG, ("tx_bdspace: 0x%08x\r\n", + xaxiemacif->tx_bdspace)); + + if (!xaxiemacif->rx_bdspace || !xaxiemacif->tx_bdspace) { + xil_printf("%s@%d: Error: Unable to allocate memory for RX buffer descriptors", + __FILE__, __LINE__); + return ERR_IF; + } + /* initialize DMA */ +#ifndef SDT + baseaddr = xaxiemacif->axi_ethernet.Config.AxiDevBaseAddress; + dmaconfig = XAxiDma_LookupConfigBaseAddr(baseaddr); +#else + baseaddr = xaxiemacif->axi_ethernet.AxiDevBaseAddress; + dmaconfig = XAxiDma_LookupConfig(baseaddr); +#endif + XAxiDma_CfgInitialize(&xaxiemacif->axidma, dmaconfig); + + rxringptr = XAxiDma_GetRxRing(&xaxiemacif->axidma); + txringptr = XAxiDma_GetTxRing(&xaxiemacif->axidma); + LWIP_DEBUGF(NETIF_DEBUG, ("rxringptr: 0x%08x\r\n", rxringptr)); + LWIP_DEBUGF(NETIF_DEBUG, ("txringptr: 0x%08x\r\n", txringptr)); + + /* 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. + */ + XAxiDma_BdClear(&bdtemplate); + /* Create the RxBD ring */ + status = XAxiDma_BdRingCreate(rxringptr, (UINTPTR) xaxiemacif->rx_bdspace, + (UINTPTR) xaxiemacif->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; + } + XAxiDma_BdClear(&bdtemplate); + status = XAxiDma_BdRingClone(rxringptr, &bdtemplate); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error initializing RxBD space\r\n")); + return ERR_IF; + } + /* Create the TxBD ring */ + status = XAxiDma_BdRingCreate(txringptr, (UINTPTR) xaxiemacif->tx_bdspace, + (UINTPTR) xaxiemacif->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 = XAxiDma_BdRingClone(txringptr, &bdtemplate); + 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++) { + status = XAxiDma_BdRingAlloc(rxringptr, 1, &rxbd); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("init_axi_dma: Error allocating RxBD\r\n")); + return ERR_IF; + } +#ifdef USE_JUMBO_FRAMES + p = pbuf_alloc(PBUF_RAW, XAE_MAX_JUMBO_FRAME_SIZE, PBUF_POOL); +#else + p = pbuf_alloc(PBUF_RAW, XAE_MAX_FRAME_SIZE, PBUF_POOL); +#endif + if (!p) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + LWIP_DEBUGF(NETIF_DEBUG, ("unable to alloc pbuf in recv_handler\r\n")); + return ERR_IF; + } + /* Setup the BD. The BD template used in the call to + * XAxiEthernet_SgSetSpace() set the "last" field of all RxBDs. + * Therefore we are not required to issue a XAxiDma_Bd_SetLast(rxbd) + * here. + */ + XAxiDma_BdSetBufAddr(rxbd, (UINTPTR)p->payload); + XAxiDma_BdSetLength(rxbd, p->len, rxringptr->MaxTransferLen); + XAxiDma_BdSetCtrl(rxbd, 0); + XAxiDma_BdSetId(rxbd, p); +#ifdef USE_JUMBO_FRAMES + XCACHE_FLUSH_DCACHE_RANGE((UINTPTR)p->payload, (UINTPTR)XAE_MAX_JUMBO_FRAME_SIZE); +#else + XCACHE_FLUSH_DCACHE_RANGE((UINTPTR)p->payload, (UINTPTR)XAE_MAX_FRAME_SIZE); +#endif +#if !defined(__aarch64__) + XCACHE_FLUSH_DCACHE_RANGE(rxbd, sizeof *rxbd); +#endif + + /* Enqueue to HW */ + status = XAxiDma_BdRingToHw(rxringptr, 1, rxbd); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error: committing RxBD to HW\r\n")); + return ERR_IF; + } + } + + status = XAxiDma_BdRingSetCoalesce(txringptr, XLWIP_CONFIG_N_TX_COALESCE, + 0x1); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error setting coalescing settings\r\n")); + return ERR_IF; + } + status = XAxiDma_BdRingSetCoalesce(rxringptr, XLWIP_CONFIG_N_RX_COALESCE, + 0x1); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error setting coalescing settings\r\n")); + return ERR_IF; + } + /* start DMA */ + status = XAxiDma_BdRingStart(txringptr); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error: failed to start TX BD ring\r\n")); + return ERR_IF; + } + status = XAxiDma_BdRingStart(rxringptr); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error: failed to start RX BD ring\r\n")); + return ERR_IF; + } + /* enable DMA interrupts */ + XAxiDma_BdRingIntEnable(txringptr, XAXIDMA_IRQ_ALL_MASK); + XAxiDma_BdRingIntEnable(rxringptr, XAXIDMA_IRQ_ALL_MASK); + +#ifndef SDT +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (Xil_ExceptionHandler)xaxiemac_error_handler, + &xaxiemacif->axi_ethernet); + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr, + (Xil_ExceptionHandler)axidma_send_handler, + xemac); + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr, + (Xil_ExceptionHandler)axidma_recv_handler, + xemac); + + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.TemacIntr, + AXIETH_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr, + AXIDMA_TX_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr, + AXIDMA_RX_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.TemacIntr); + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr); + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr); +#else +#if NO_SYS +#if XPAR_INTC_0_HAS_FAST == 1 + + /* Register axiethernet interrupt with interrupt controller as Fast + Interrupts */ + + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XFastInterruptHandler)xaxiemac_errorfast_handler); + + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr, + (XFastInterruptHandler)axidma_sendfast_handler); + + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr, + (XFastInterruptHandler)axidma_recvfast_handler); +#else + /* Register axiethernet interrupt with interrupt controller */ + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XInterruptHandler)xaxiemac_error_handler, + &xaxiemacif->axi_ethernet); + /* connect & enable DMA interrupts */ + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr, + (XInterruptHandler)axidma_send_handler, + xemac); + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr, + (XInterruptHandler)axidma_recv_handler, + xemac); +#endif + /* Enable EMAC interrupts in the interrupt controller */ + do { + /* read current interrupt enable mask */ + unsigned int cur_mask = XIntc_In32(xtopologyp->intc_baseaddr + + XIN_IER_OFFSET); + + /* form new mask enabling AXIDMA & axiethernet interrupts */ + cur_mask = cur_mask + | (1 << xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr) + | (1 << xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr) + | (1 << xaxiemacif->axi_ethernet.Config.TemacIntr); + + /* set new mask */ + XIntc_EnableIntr(xtopologyp->intc_baseaddr, cur_mask); + } while (0); +#else +#if XPAR_INTC_0_HAS_FAST == 1 + + /* Register axiethernet interrupt with interrupt controller as Fast + Interrupts */ + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XFastInterruptHandler)xaxiemac_errorfast_handler); + + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr, + (XFastInterruptHandler)axidma_sendfast_handler); + + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr, + (XFastInterruptHandler)axidma_recvfast_handler); +#else + /* Register axiethernet interrupt with interrupt controller */ + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XInterruptHandler)xaxiemac_error_handler, + &xaxiemacif->axi_ethernet); + /* connect & enable DMA interrupts */ + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr, + (XInterruptHandler)axidma_send_handler, + xemac); + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr, + (XInterruptHandler)axidma_recv_handler, + xemac); +#endif + /* Enable EMAC interrupts in the interrupt controller */ + do { + /* read current interrupt enable mask */ + unsigned int cur_mask = XIntc_In32(xtopologyp->intc_baseaddr + + XIN_IER_OFFSET); + + /* form new mask enabling AXIDMA & axiethernet interrupts */ + cur_mask = cur_mask + | (1 << xaxiemacif->axi_ethernet.Config.AxiDmaTxIntr) + | (1 << xaxiemacif->axi_ethernet.Config.AxiDmaRxIntr) + | (1 << xaxiemacif->axi_ethernet.Config.TemacIntr); + + /* set new mask */ + XIntc_EnableIntr(xtopologyp->intc_baseaddr, cur_mask); + } while (0); +#endif +#endif +#else + XSetupInterruptSystem(&xaxiemacif->axi_ethernet, &xaxiemac_error_handler, + xaxiemacif->axi_ethernet.Config.IntrId, + xaxiemacif->axi_ethernet.Config.IntrParent, + XINTERRUPT_DEFAULT_PRIORITY); + XSetupInterruptSystem(xemac, &axidma_send_handler, + dmaconfig->IntrId[0], + dmaconfig->IntrParent, + XINTERRUPT_DEFAULT_PRIORITY); + XSetupInterruptSystem(xemac, &axidma_recv_handler, + dmaconfig->IntrId[1], + dmaconfig->IntrParent, + XINTERRUPT_DEFAULT_PRIORITY); + +#endif + return 0; +} + +#ifndef SDT +#if XPAR_INTC_0_HAS_FAST == 1 +/****************************** Fast receive Handler *************************/ +static void axidma_recvfast_handler(void) +{ + axidma_recv_handler((void *)xemac_fast); +} + +/****************************** Fast Send Handler ****************************/ +static void axidma_sendfast_handler(void) +{ + axidma_send_handler((void *)xemac_fast); +} + +/****************************** Fast Error Handler ***************************/ +static void xaxiemac_errorfast_handler(void) +{ + xaxiemac_error_handler(&xaxiemacif_fast->axi_ethernet); +} +#endif +#endif diff --git a/bsp_z7/lwip/netif/xaxiemacif_fifo.c b/bsp_z7/lwip/netif/xaxiemacif_fifo.c new file mode 100644 index 0000000..3a6dd16 --- /dev/null +++ b/bsp_z7/lwip/netif/xaxiemacif_fifo.c @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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" + +#if !NO_SYS +#include "FreeRTOS.h" +#include "semphr.h" +#include "timers.h" +#include "lwip/sys.h" +#endif + +#include "lwip/stats.h" +#include "netif/xadapter.h" +#include "netif/xaxiemacif.h" + +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 +#include "xscugic.h" +#else +#include "xintc_l.h" +#endif + +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 +#define AXIFIFO_INTR_PRIORITY_SET_IN_GIC 0xA0 +#define AXIETH_INTR_PRIORITY_SET_IN_GIC 0xA0 +#define TRIG_TYPE_RISING_EDGE_SENSITIVE 0x3 +#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_DIST_BASEADDR +#endif + +#include "xstatus.h" + +#include "xaxiemacif_fifo.h" + +#include "xlwipconfig.h" + +#if XPAR_INTC_0_HAS_FAST == 1 +/*********** Function Prototypes *********************************************/ + +/* + * Function prototypes of the functions used for registering Fast + * Interrupt Handlers + */ +static void xllfifo_fastintr_handler(void) __attribute__ ((fast_interrupt)); +static void xaxiemac_fasterror_handler(void) __attribute__ ((fast_interrupt)); + +/**************** Variable Declarations **************************************/ + +/** Variables for Fast Interrupt handlers ***/ +struct xemac_s *xemac_fast; +xaxiemacif_s *xaxiemacif_fast; +#endif + +#if !NO_SYS +extern u32 xInsideISR; +#endif + +int xaxiemac_is_tx_space_available(xaxiemacif_s *emac) +{ + return ((XLlFifo_TxVacancy(&emac->axififo) * 4) > XAE_MAX_FRAME_SIZE); +} + +static void +xllfifo_recv_handler(struct xemac_s *xemac) +{ + u32_t frame_length; + struct pbuf *p; + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + XLlFifo *llfifo = &xaxiemacif->axififo; + + /* While there is data in the fifo ... */ + while (XLlFifo_RxOccupancy(llfifo)) { + /* find packet length */ + frame_length = XLlFifo_RxGetLen(llfifo); + + /* allocate a pbuf */ + p = pbuf_alloc(PBUF_RAW, frame_length, PBUF_POOL); + if (!p) { + char tmp_frame[XAE_MAX_FRAME_SIZE]; +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + /* receive and drop packet to keep data & len registers in sync */ + XLlFifo_Read(llfifo, tmp_frame, frame_length); + + continue; + } + + /* receive packet */ + XLlFifo_Read(llfifo, p->payload, frame_length); + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* store it in the receive queue, where it'll be processed by xemacif input thread */ + if (pq_enqueue(xaxiemacif->recv_q, (void*)p) < 0) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + pbuf_free(p); + continue; + } + +#if !NO_SYS + sys_sem_signal(&xemac->sem_rx_data_available); +#endif + +#if LINK_STATS + lwip_stats.link.recv++; +#endif + } +} + +static void +fifo_error_handler(xaxiemacif_s *xaxiemacif, u32_t pending_intr) +{ + XLlFifo *llfifo = &xaxiemacif->axififo; + + if (pending_intr & XLLF_INT_RPURE_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("llfifo: Rx under-read error")); + } + if (pending_intr & XLLF_INT_RPORE_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("llfifo: Rx over-read error")); + } + if (pending_intr & XLLF_INT_RPUE_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("llfifo: Rx fifo empty")); + } + if (pending_intr & XLLF_INT_TPOE_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("llfifo: Tx fifo overrun")); + } + if (pending_intr & XLLF_INT_TSE_MASK) { + LWIP_DEBUGF(NETIF_DEBUG, ("llfifo: Tx length mismatch")); + } + + /* Reset the tx or rx side of the fifo as needed */ + if (pending_intr & XLLF_INT_RXERROR_MASK) { + XLlFifo_IntClear(llfifo, XLLF_INT_RRC_MASK); + XLlFifo_RxReset(llfifo); + } + + if (pending_intr & XLLF_INT_TXERROR_MASK) { + XLlFifo_IntClear(llfifo, XLLF_INT_TRC_MASK); + XLlFifo_TxReset(llfifo); + } +} + +static void +xllfifo_intr_handler(struct xemac_s *xemac) +{ + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + XLlFifo *llfifo = &xaxiemacif->axififo; + + u32_t pending_fifo_intr = XLlFifo_IntPending(llfifo); + +#if !NO_SYS + xInsideISR++; +#endif + + while (pending_fifo_intr) { + if (pending_fifo_intr & XLLF_INT_RC_MASK) { + /* receive interrupt */ + XLlFifo_IntClear(llfifo, XLLF_INT_RC_MASK); + xllfifo_recv_handler(xemac); + } else if (pending_fifo_intr & XLLF_INT_TC_MASK) { + /* tx intr */ + XLlFifo_IntClear(llfifo, XLLF_INT_TC_MASK); + } else { + XLlFifo_IntClear(llfifo, XLLF_INT_ALL_MASK & + ~(XLLF_INT_RC_MASK | + XLLF_INT_TC_MASK)); + fifo_error_handler(xaxiemacif, pending_fifo_intr); + } + pending_fifo_intr = XLlFifo_IntPending(llfifo); + } + +#if !NO_SYS + xInsideISR--; +#endif + +} + +XStatus init_axi_fifo(struct xemac_s *xemac) +{ + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); +#if XPAR_INTC_0_HAS_FAST == 1 + xaxiemacif_fast = xaxiemacif; + xemac_fast = xemac; +#endif + struct xtopology_t *xtopologyp = &xtopology[xemac->topology_index]; + + /* initialize ll fifo */ + XLlFifo_Initialize(&xaxiemacif->axififo, + XAxiEthernet_AxiDevBaseAddress(&xaxiemacif->axi_ethernet)); + + /* Clear any pending FIFO interrupts */ + XLlFifo_IntClear(&xaxiemacif->axififo, XLLF_INT_ALL_MASK); + + /* enable fifo interrupts */ + XLlFifo_IntEnable(&xaxiemacif->axififo, XLLF_INT_ALL_MASK); + +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XInterruptHandler)xaxiemac_error_handler, + &xaxiemacif->axi_ethernet); + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiFifoIntr, + (XInterruptHandler)xllfifo_intr_handler, + xemac); + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.TemacIntr, + AXIETH_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiFifoIntr, + AXIFIFO_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.TemacIntr); + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiFifoIntr); +#else +#if NO_SYS +#if XPAR_INTC_0_HAS_FAST == 1 + /* Register temac interrupt with interrupt controller */ + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XFastInterruptHandler)xaxiemac_fasterror_handler); + + /* connect & enable FIFO interrupt */ + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiFifoIntr, + (XFastInterruptHandler)xllfifo_fastintr_handler); +#else + /* Register temac interrupt with interrupt controller */ + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XInterruptHandler)xaxiemac_error_handler, + &xaxiemacif->axi_ethernet); + + /* connect & enable FIFO interrupt */ + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiFifoIntr, + (XInterruptHandler)xllfifo_intr_handler, + xemac); + +#endif + /* Enable EMAC interrupts in the interrupt controller */ + do { + /* read current interrupt enable mask */ + unsigned int cur_mask = XIntc_In32(xtopologyp->intc_baseaddr + XIN_IER_OFFSET); + + /* form new mask enabling SDMA & ll_temac interrupts */ + cur_mask = cur_mask + | (1 << xaxiemacif->axi_ethernet.Config.AxiFifoIntr) + | (1 << xaxiemacif->axi_ethernet.Config.TemacIntr); + + /* set new mask */ + XIntc_EnableIntr(xtopologyp->intc_baseaddr, cur_mask); + } while (0); +#else +#if XPAR_INTC_0_HAS_FAST == 1 + /* Register temac interrupt with interrupt controller */ + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XFastInterruptHandler)xaxiemac_fasterror_handler); + + /* connect & enable FIFO interrupt */ + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiFifoIntr, + (XFastInterruptHandler)xllfifo_fastintr_handler); +#else + /* Register temac interrupt with interrupt controller */ + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (XInterruptHandler)xaxiemac_error_handler, + &xaxiemacif->axi_ethernet); + + /* connect & enable FIFO interrupt */ + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiFifoIntr, + (XInterruptHandler)xllfifo_intr_handler, + xemac); + +#endif + /* Enable EMAC interrupts in the interrupt controller */ + do { + /* read current interrupt enable mask */ + unsigned int cur_mask = XIntc_In32(xtopologyp->intc_baseaddr + XIN_IER_OFFSET); + + /* form new mask enabling SDMA & ll_temac interrupts */ + cur_mask = cur_mask + | (1 << xaxiemacif->axi_ethernet.Config.AxiFifoIntr) + | (1 << xaxiemacif->axi_ethernet.Config.TemacIntr); + + /* set new mask */ + XIntc_EnableIntr(xtopologyp->intc_baseaddr, cur_mask); + } while (0); +#endif +#endif + + return 0; +} + +XStatus axififo_send(xaxiemacif_s *xaxiemacif, struct pbuf *p) +{ + XLlFifo *llfifo = &xaxiemacif->axififo; + u32_t l = 0; + struct pbuf *q; + + for(q = p; q != NULL; q = q->next) { + /* write frame data to FIFO */ + XLlFifo_Write(llfifo, q->payload, q->len); + l += q->len; + } + + /* initiate transmit */ + XLlFifo_TxSetLen(llfifo, l); + + return 0; +} + +#if XPAR_INTC_0_HAS_FAST == 1 +/*********** Fast Error Handler ********************************************/ +void xaxiemac_fasterror_handler(void) +{ + xaxiemac_error_handler(&xaxiemacif_fast->axi_ethernet); +} + +/********** Fast Interrupt handler *****************************************/ +void xllfifo_fastintr_handler(void) +{ + xllfifo_intr_handler(xemac_fast); +} +#endif diff --git a/bsp_z7/lwip/netif/xaxiemacif_fifo.h b/bsp_z7/lwip/netif/xaxiemacif_fifo.h new file mode 100644 index 0000000..7aaacc6 --- /dev/null +++ b/bsp_z7/lwip/netif/xaxiemacif_fifo.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 __XAXIEMACIF_FIFO_H_ +#define __XAXIEMACIF_FIFO_H_ + +#include "xparameters.h" +#include "netif/xaxiemacif.h" +#include "xlwipconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +XStatus init_axi_fifo(struct xemac_s *xemac); +XStatus axififo_send(xaxiemacif_s *xaxiemacif, struct pbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp_z7/lwip/netif/xaxiemacif_hw.c b/bsp_z7/lwip/netif/xaxiemacif_hw.c new file mode 100644 index 0000000..392ed1b --- /dev/null +++ b/bsp_z7/lwip/netif/xaxiemacif_hw.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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/xaxiemacif.h" +#include "lwipopts.h" + +XAxiEthernet_Config *xaxiemac_lookup_config(unsigned mac_base) +{ + extern XAxiEthernet_Config XAxiEthernet_ConfigTable[]; + XAxiEthernet_Config *CfgPtr = NULL; + unsigned int i; + + for (i = 0; i < XPAR_XAXIETHERNET_NUM_INSTANCES; i++) { + if (XAxiEthernet_ConfigTable[i].BaseAddress == mac_base) { + CfgPtr = &XAxiEthernet_ConfigTable[i]; + break; + } + } + + return (CfgPtr); +} + +void init_axiemac(xaxiemacif_s *xaxiemac, struct netif *netif) +{ + unsigned link_speed = 1000; + unsigned options; + XAxiEthernet *xaxiemacp; + + xaxiemacp = &xaxiemac->axi_ethernet; + + XAxiEthernet_Reset(xaxiemacp); + + options = XAxiEthernet_GetOptions(xaxiemacp); + options |= XAE_FLOW_CONTROL_OPTION; +#ifdef USE_JUMBO_FRAMES + options |= XAE_JUMBO_OPTION; +#endif + options |= XAE_TRANSMITTER_ENABLE_OPTION; + options |= XAE_RECEIVER_ENABLE_OPTION; + options |= XAE_FCS_STRIP_OPTION; + options |= XAE_MULTICAST_OPTION; + XAxiEthernet_SetOptions(xaxiemacp, options); + XAxiEthernet_ClearOptions(xaxiemacp, ~options); + + /* set mac address */ + XAxiEthernet_SetMacAddress(xaxiemacp, (unsigned char*)(netif->hwaddr)); + link_speed = phy_setup_axiemac(xaxiemacp); + XAxiEthernet_SetOperatingSpeed(xaxiemacp, link_speed); + + if (link_speed == 0) + xaxiemac->eth_link_status = ETH_LINK_DOWN; + else + xaxiemac->eth_link_status = ETH_LINK_UP; + + /* Setting the operating speed of the MAC needs a delay. */ + { + volatile int wait; + for (wait=0; wait < 100000; wait++); + for (wait=0; wait < 100000; wait++); + } + +#ifdef NOTNOW + /* in a soft temac implementation, we need to explicitly make sure that + * the RX DCM has been locked. See xps_ll_temac manual for details. + * This bit is guaranteed to be 1 for hard temac's + */ + lock_message_printed = 0; + while (!(XAxiEthernet_ReadReg(xaxiemacp->Config.BaseAddress, XAE_IS_OFFSET) + & XAE_INT_RXDCMLOCK_MASK)) { + int first = 1; + if (first) { + LWIP_DEBUGF(NETIF_DEBUGF, ("Waiting for RX DCM to lock..")); + first = 0; + lock_message_printed = 1; + } + } + + if (lock_message_printed) + LWIP_DEBUGF(NETIF_DEBUGF, ("RX DCM locked.\r\n")); +#endif + + /* start the temac */ + XAxiEthernet_Start(xaxiemacp); + + /* enable MAC interrupts */ + XAxiEthernet_IntEnable(xaxiemacp, XAE_INT_RECV_ERROR_MASK); +} + +void xaxiemac_error_handler(XAxiEthernet * Temac) +{ + unsigned Pending; + + Pending = XAxiEthernet_IntPending(Temac); + XAxiEthernet_IntClear(Temac, Pending); +} diff --git a/bsp_z7/lwip/netif/xaxiemacif_hw.h b/bsp_z7/lwip/netif/xaxiemacif_hw.h new file mode 100644 index 0000000..f790ba0 --- /dev/null +++ b/bsp_z7/lwip/netif/xaxiemacif_hw.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 __XAXIEMACIF_HW_H_ +#define __XAXIEMACIF_HW_H_ + +#include "netif/xaxiemacif.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +XAxiEthernet_Config * xaxiemac_lookup_config(unsigned mac_base); + +void init_axiemac(xaxiemacif_s *xaxiemacif, struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp_z7/lwip/netif/xaxiemacif_mcdma.c b/bsp_z7/lwip/netif/xaxiemacif_mcdma.c new file mode 100644 index 0000000..d8b3be1 --- /dev/null +++ b/bsp_z7/lwip/netif/xaxiemacif_mcdma.c @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2018 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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" + +#if !NO_SYS +#include "FreeRTOS.h" +#include "lwip/sys.h" +#endif + +#include "lwip/stats.h" +#include "lwip/inet_chksum.h" + +#include "netif/xadapter.h" +#include "netif/xaxiemacif.h" + +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 +#include "xscugic.h" +#endif + +#include "xstatus.h" + +#include "xlwipconfig.h" +#include "xparameters.h" + +#if defined __aarch64__ +#include "xil_mmu.h" +#endif + +#if defined ARMR5 +#include "xil_mpu.h" +#endif + +#define PARTIAL_CSUM_ENABLE 0x00000001 /* Option for partial csum enable */ +#define FULL_CSUM_ENABLE 0x00000002 /* Option for full csum enable */ + +#define BD_USR0_OFFSET 0 /* AXI4-Stream Control Word offset from + * the start of app user words in BD. + * Offset 0 means, Control Word 0, used + * for enabling checksum offloading. + */ +#define BD_USR1_OFFSET 1 /* AXI4-Stream Control Word offset from + * the start of app user words in BD. + * Offset means, Control Word 1, used + * for mentioning checksum begin and + * checksum insert points + */ +#define BD_USR2_OFFSET 2 /* AXI4-Stream Control Word offset from + * the start of app user words in BD. + * Offset 2 means, Control Word 2, used + * for mentioning checksum seed. + */ + +#define XMCDMA_ALL_BDS 0xFFFF +#define XMCDMA_BD_LENGTH_MASK 0x007FFFFF +#define XMCDMA_COALESCEDELAY 0x1 + +#define RESET_TIMEOUT_COUNT 10000 +#define BLOCK_SIZE_2MB 0x200000 +#define BLOCK_SIZE_1MB 0x100000 + +#if defined (__aarch64__) +#define BD_SIZE BLOCK_SIZE_2MB +static u8_t bd_space[BD_SIZE] __attribute__ ((aligned (BLOCK_SIZE_2MB))); +#else +#define BD_SIZE BLOCK_SIZE_1MB +static u8_t bd_space[BD_SIZE] __attribute__ ((aligned (BLOCK_SIZE_1MB))); +#endif + +static u8_t *bd_mem_ptr = bd_space; + +#if !NO_SYS +extern u32 xInsideISR; +#endif + +static inline void bd_csum_enable(XMcdma_Bd *bd) +{ + XMcDma_BdSetAppWord(bd, BD_USR0_OFFSET, PARTIAL_CSUM_ENABLE); +} + +static inline void bd_csum_disable(XMcdma_Bd *bd) +{ + XMcDma_BdSetAppWord(bd, BD_USR0_OFFSET, ~PARTIAL_CSUM_ENABLE); +} + +static inline void bd_fullcsum_disable(XMcdma_Bd *bd) +{ + XMcDma_BdSetAppWord(bd, BD_USR0_OFFSET, ~FULL_CSUM_ENABLE); +} + +static inline void bd_fullcsum_enable(XMcdma_Bd *bd) +{ + XMcDma_BdSetAppWord(bd, BD_USR0_OFFSET, FULL_CSUM_ENABLE); +} + +static inline void bd_csum_set(XMcdma_Bd *bd, u16_t tx_csbegin, + u16_t tx_csinsert, u16_t tx_csinit) +{ + u32_t app1; + + bd_csum_enable(bd); + + /* write start offset and insert offset into BD */ + app1 = ((u32_t)tx_csbegin << 16) | tx_csinsert; + XMcDma_BdSetAppWord(bd, BD_USR1_OFFSET, app1); + + /* insert init value */ + XMcDma_BdSetAppWord(bd, BD_USR2_OFFSET, tx_csinit); +} + +static inline u32_t extract_packet_len(XMcdma_Bd *rxbd) { + return XMcDma_BdGetActualLength(rxbd, XMCDMA_BD_LENGTH_MASK); +} + +static inline u16_t extract_csum(XMcdma_Bd *rxbd) { + return XMcdma_BdRead64(rxbd, XMCDMA_BD_USR3_OFFSET) & 0xffff; +} + +static inline u32_t csum_sub(u32_t csum, u16_t v) +{ + csum += (u32_t)v; + return csum + (csum < (u32_t)v); +} + +/* + * compare if the h/w computed checksum (stored in the rxbd) + * equals the TCP checksum value in the packet + */ +s32_t is_checksum_valid(XMcdma_Bd *rxbd, struct pbuf *p) +{ + struct ethip_hdr *ehdr = p->payload; + u8_t proto = IPH_PROTO(&ehdr->ip); + + /* check if it is a TCP packet */ + if (htons(ehdr->eth.type) == ETHTYPE_IP && proto == IP_PROTO_TCP) { + u32_t iphdr_len; + u16_t csum_in_rxbd, pseudo_csum, iphdr_csum, padding_csum; + u16_t tcp_payload_offset; + u32_t computed_csum; + u16_t padding_len, tcp_payload_len, packet_len; + u16_t csum; + + /* determine length of IP header */ + iphdr_len = (IPH_HL(&ehdr->ip) * 4); + tcp_payload_offset = XAE_HDR_SIZE + iphdr_len; + tcp_payload_len = htons(IPH_LEN(&ehdr->ip)) - + IPH_HL(&ehdr->ip) * 4; + packet_len = extract_packet_len(rxbd); + padding_len = packet_len - tcp_payload_offset - tcp_payload_len; + + csum_in_rxbd = extract_csum(rxbd); + pseudo_csum = htons(ip_chksum_pseudo(NULL, proto, + tcp_payload_len, + (ip_addr_t *)&ehdr->ip.src, + (ip_addr_t *)&ehdr->ip.dest)); + + /* xps_ll_temac computes the checksum of the packet starting + * at byte XAE_HDR_SIZE we need to subtract the values of + * the ethernet & IP headers + */ + iphdr_csum = inet_chksum(p->payload + XAE_HDR_SIZE, iphdr_len); + + /* compute csum of padding bytes, if any */ + padding_csum = inet_chksum(p->payload + p->tot_len - + padding_len, padding_len); + + /* get the h/w checksum value */ + computed_csum = (u32_t)csum_in_rxbd; + + /* remove the effect of csumming the iphdr */ + computed_csum = csum_sub(computed_csum, ~iphdr_csum); + + /* add in the pseudo csum */ + computed_csum = csum_sub(computed_csum, ~pseudo_csum); + + /* remove any padding effect */ + computed_csum = csum_sub(computed_csum, ~padding_csum); + + /* normalize computed csum */ + while (computed_csum >> 16) { + computed_csum = (computed_csum & 0xffff) + + (computed_csum >> 16); + } + + /* convert to 16 bits and take 1's complement */ + csum = (u16_t)computed_csum; + csum = ~csum; + + /* chksum is valid if: computed csum over the packet is 0 */ + return !csum; + } else { + /* just say yes to all other packets */ + /* the upper layers in the stack will compute and + * verify the checksum */ + return 1; + } +} + +#define XMcdma_BdMemCalc(Alignment, NumBd) \ + (int)((sizeof(XMcdma_Bd)+((Alignment)-1)) & ~((Alignment)-1))*(NumBd) + +static inline void *alloc_bdspace(int n_desc, u32 alignment) +{ + int space = XMcdma_BdMemCalc(alignment, n_desc); + void *unaligned_mem = bd_mem_ptr; + void *aligned_mem = + (void *)(((UINTPTR)(unaligned_mem + alignment - 1)) & ~(alignment - 1)); + + if (aligned_mem + space > (void *)(bd_space + BD_SIZE)) { + LWIP_DEBUGF(NETIF_DEBUG, ("Unable to allocate BD space\r\n")); + return NULL; + } + + bd_mem_ptr = aligned_mem + space; + + return aligned_mem; +} + +static void axi_mcdma_send_error_handler(void *CallBackRef, u32 ChanId, u32 Mask) +{ + u32 timeOut; + XMcdma *McDmaInstPtr = (XMcdma *)((void *)CallBackRef); + +#if !NO_SYS + xInsideISR++; +#endif + LWIP_DEBUGF(NETIF_DEBUG, ("%s: Error: aximcdma error interrupt is asserted, Chan_id = " + "%d, Mask = %d\r\n", __FUNCTION__, ChanId, Mask)); + + XMcDma_Reset(McDmaInstPtr); + timeOut = RESET_TIMEOUT_COUNT; + while (timeOut) { + if (XMcdma_ResetIsDone(McDmaInstPtr)) + break; + timeOut -= 1; + } + + if (!timeOut) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s: Error: aximcdma reset timed out\r\n", __func__)); + } + +#if !NO_SYS + xInsideISR--; +#endif +} + +static void axi_mcdma_send_handler(void *CallBackRef, u32 ChanId) +{ + XMcdma *McDmaInstPtr = (XMcdma *)((void *)CallBackRef); + XMcdma_ChanCtrl *Tx_Chan = XMcdma_GetMcdmaTxChan(McDmaInstPtr, ChanId); + +#if !NO_SYS + xInsideISR++; +#endif + + process_sent_bds(Tx_Chan); + +#if !NO_SYS + xInsideISR--; +#endif +} + +static void setup_rx_bds(XMcdma_ChanCtrl *Rx_Chan, u32_t n_bds) +{ + XMcdma_Bd *rxbd; + u32_t i = 0; + XStatus status; + struct pbuf *p; + u32 bdsts; +#ifdef USE_JUMBO_FRAMES + u32 max_frame_size = XAE_MAX_JUMBO_FRAME_SIZE + IEEE_1588_PAD_SIZE; +#else + u32 max_frame_size = XAE_MAX_FRAME_SIZE + IEEE_1588_PAD_SIZE; +#endif + + for (i = 0; i < n_bds; i++) { + p = pbuf_alloc(PBUF_RAW, max_frame_size, PBUF_POOL); + if (!p) { + LWIP_DEBUGF(NETIF_DEBUG, ("unable to alloc pbuf in recv_handler\r\n")); + return; + } + + rxbd = (XMcdma_Bd *)XMcdma_GetChanCurBd(Rx_Chan); + status = XMcDma_ChanSubmit(Rx_Chan, (UINTPTR)p->payload, + p->len); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("setup_rx_bds: Error allocating RxBD\r\n")); + pbuf_free(p); + return; + } + /* Clear everything but the COMPLETE bit, which is cleared when + * committed to hardware. + */ + bdsts = XMcDma_BdGetSts(rxbd); + bdsts &= XMCDMA_BD_STS_COMPLETE_MASK; + XMcdma_BdWrite(rxbd, XMCDMA_BD_STS_OFFSET, bdsts); + XMcDma_BdSetCtrl(rxbd, 0); + XMcdma_BdSetSwId(rxbd, p); + +#if defined(__aarch64__) + Xil_DCacheInvalidateRange((UINTPTR)p->payload, + (UINTPTR)max_frame_size); +#else + Xil_DCacheFlushRange((UINTPTR)p->payload, + (UINTPTR)max_frame_size); +#endif + } + +#if !defined (__MICROBLAZE__) && !defined (__riscv) + dsb(); +#endif + + if (n_bds) { + /* Enqueue to HW */ + status = XMcDma_ChanToHw(Rx_Chan); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error committing RxBD to hardware\n\r")); + } + } +} + +static void axi_mcdma_recv_error_handler(void *CallBackRef, u32 ChanId) +{ + u32 timeOut; + XMcdma_ChanCtrl *Rx_Chan; + struct xemac_s *xemac = (struct xemac_s *)(CallBackRef); + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + XMcdma *McDmaInstPtr = &xaxiemacif->aximcdma; + +#if !NO_SYS + xInsideISR++; +#endif + LWIP_DEBUGF(NETIF_DEBUG, ("%s: Error: aximcdma error interrupt is asserted\r\n", + __FUNCTION__)); + Rx_Chan = XMcdma_GetMcdmaRxChan(McDmaInstPtr, ChanId); + + setup_rx_bds(Rx_Chan, Rx_Chan->BdCnt); + + XMcDma_Reset(McDmaInstPtr); + timeOut = RESET_TIMEOUT_COUNT; + while (timeOut) { + if (XMcdma_ResetIsDone(McDmaInstPtr)) + break; + timeOut -= 1; + } + + if (!timeOut) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s: Error: aximcdma reset timed out\r\n", __func__)); + } + + XMcDma_ChanToHw(Rx_Chan); + +#if !NO_SYS + xInsideISR--; +#endif + return; +} + +static void axi_mcdma_recv_handler(void *CallBackRef, u32 ChanId) +{ + struct pbuf *p; + u32 i, rx_bytes, ProcessedBdCnt; + XMcdma_Bd *rxbd, *rxbdset; + struct xemac_s *xemac = (struct xemac_s *)(CallBackRef); + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + XMcdma *McDmaInstPtr = &xaxiemacif->aximcdma; + XMcdma_ChanCtrl *Rx_Chan; + +#if !NO_SYS + xInsideISR++; +#endif + + Rx_Chan = XMcdma_GetMcdmaRxChan(McDmaInstPtr, ChanId); + + ProcessedBdCnt = XMcdma_BdChainFromHW(Rx_Chan, XMCDMA_ALL_BDS, &rxbdset); + + for (i = 0, rxbd = rxbdset; i < ProcessedBdCnt; i++) { + + p = (struct pbuf *)(UINTPTR)XMcdma_BdGetSwId(rxbd); + + /* Adjust the buffer size to actual number of bytes received.*/ + rx_bytes = extract_packet_len(rxbd); +#ifndef __aarch64__ + Xil_DCacheInvalidateRange((UINTPTR)p->payload, + (UINTPTR)rx_bytes); +#endif + pbuf_realloc(p, rx_bytes); + +#if LWIP_PARTIAL_CSUM_OFFLOAD_RX==1 + /* Verify for partial checksum offload case */ + if (!is_checksum_valid(rxbd, p)) { + LWIP_DEBUGF(NETIF_DEBUG, ("Incorrect csum as calculated by the hw\r\n")); + } +#endif + /* store it in the receive queue, + * where it'll be processed by a different handler + */ + if (pq_enqueue(xaxiemacif->recv_q, (void*)p) < 0) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + pbuf_free(p); + } + rxbd = (XMcdma_Bd *)XMcdma_BdChainNextBd(Rx_Chan, rxbd); + } + + /* free up the BD's */ + XMcdma_BdChainFree(Rx_Chan, ProcessedBdCnt, rxbdset); + + /* return all the processed bd's back to the stack */ + setup_rx_bds(Rx_Chan, Rx_Chan->BdCnt); +#if !NO_SYS + sys_sem_signal(&xemac->sem_rx_data_available); + xInsideISR--; +#endif +} + +s32_t xaxiemac_is_tx_space_available(xaxiemacif_s *xaxiemacif) +{ + XMcdma_ChanCtrl *Tx_Chan; + u8_t ChanId; + for (ChanId = 1; + ChanId <= xaxiemacif->axi_ethernet.Config.AxiMcDmaChan_Cnt; + ChanId++) { + Tx_Chan = XMcdma_GetMcdmaTxChan(&xaxiemacif->aximcdma, ChanId); + + if (Tx_Chan->BdCnt) { + return Tx_Chan->BdCnt; + } + } + return 0; +} + +s32_t process_sent_bds(XMcdma_ChanCtrl *Tx_Chan) +{ + int ProcessedBdCnt, i; + XStatus status; + XMcdma_Bd *txbdset, *txbd; + + ProcessedBdCnt = XMcdma_BdChainFromHW(Tx_Chan, XMCDMA_ALL_BDS, + &txbdset); + if (ProcessedBdCnt == 0) { + return XST_FAILURE; + } + + /* free the pbuf associated with each BD */ + for (i = 0, txbd = txbdset; i < ProcessedBdCnt; i++) { + struct pbuf *p = (struct pbuf *)(UINTPTR)XMcdma_BdGetSwId(txbd); + pbuf_free(p); + txbd = (XMcdma_Bd *)XMcdma_BdChainNextBd(Tx_Chan, txbd); + } + + /* free the processed BD's */ + status = XMcdma_BdChainFree(Tx_Chan, ProcessedBdCnt, txbdset); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error freeing up TxBDs")); + return XST_FAILURE; + } + return XST_SUCCESS; +} + +#if LWIP_PARTIAL_CSUM_OFFLOAD_TX==1 +static void update_partial_cksum_offload(XMcdma_Bd *txbdset, struct pbuf *p) +{ + if (p->len > sizeof(struct ethip_hdr)) { + struct ethip_hdr *ehdr = p->payload; + u8_t proto = IPH_PROTO(&ehdr->ip); + + /* check if it is a TCP packet */ + if (htons(ehdr->eth.type) == ETHTYPE_IP && proto == + IP_PROTO_TCP) { + u32_t iphdr_len, csum_insert_offset; + u16_t tcp_len; /* TCP header length + data length in bytes */ + u16_t csum_init = 0; + u16_t tcp_payload_offset; + + /* determine length of IP header */ + iphdr_len = (IPH_HL(&ehdr->ip) * 4); + + tcp_payload_offset = XAE_HDR_SIZE + iphdr_len; + tcp_len = p->tot_len - tcp_payload_offset; + + /* insert checksum at offset 16 for TCP, 6 for UDP */ + if (proto == IP_PROTO_TCP) + csum_insert_offset = tcp_payload_offset + 16; + else if (proto == IP_PROTO_UDP) + csum_insert_offset = tcp_payload_offset + 6; + else + csum_insert_offset = 0; + /* compute pseudo header checksum value */ + csum_init = ip_chksum_pseudo(NULL, proto, tcp_len, + (ip_addr_t *)&ehdr->ip.src, + (ip_addr_t *)&ehdr->ip.dest); + /* init buffer descriptor */ + bd_csum_set(txbdset, tcp_payload_offset, + csum_insert_offset, htons(~csum_init)); + } + } +} +#endif + +XStatus axi_mcdma_sgsend(xaxiemacif_s *xaxiemacif, struct pbuf *p) +{ + struct pbuf *q; + u32_t n_pbufs = 0; + XMcdma_Bd *txbdset, *txbd, *last_txbd = NULL; + XMcdma_ChanCtrl *Tx_Chan; + XStatus status; + static u8_t ChanId = 1; + u8_t next_ChanId = ChanId; + + /* first count the number of pbufs */ + for (q = p; q != NULL; q = q->next) + n_pbufs++; + + /* Transfer packets to TX DMA Channels in round-robin manner */ + do { + Tx_Chan = XMcdma_GetMcdmaTxChan(&xaxiemacif->aximcdma, ChanId); + + if (++ChanId > xaxiemacif->axi_ethernet.Config.AxiMcDmaChan_Cnt) + ChanId = 1; + + if ((next_ChanId == ChanId) && (n_pbufs > Tx_Chan->BdCnt)) { + LWIP_DEBUGF(NETIF_DEBUG, ("sgsend: Error, not enough BD space in All Chans\r\n")); + return ERR_IF; + } + + } while (n_pbufs > Tx_Chan->BdCnt); + + txbdset = (XMcdma_Bd *)XMcdma_GetChanCurBd(Tx_Chan); + + for (q = p, txbd = txbdset; q != NULL; q = q->next) { + /* 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. + */ + XMcDma_BdSetCtrl(txbd, 0); + XMcdma_BdSetSwId(txbd, (void *)q); + + Xil_DCacheFlushRange((UINTPTR)q->payload, q->len); + + status = XMcDma_ChanSubmit(Tx_Chan, (UINTPTR)q->payload, + q->len); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("ChanSubmit failed\n\r")); + return XST_FAILURE; + } + + pbuf_ref(q); + + last_txbd = txbd; + txbd = (XMcdma_Bd *)XMcdma_BdChainNextBd(Tx_Chan, txbd); + } + + if (n_pbufs == 1) { + XMcDma_BdSetCtrl(txbdset, XMCDMA_BD_CTRL_SOF_MASK + | XMCDMA_BD_CTRL_EOF_MASK); + } else { + /* in the first packet, set the SOP */ + XMcDma_BdSetCtrl(txbdset, XMCDMA_BD_CTRL_SOF_MASK); + /* in the last packet, set the EOP */ + XMcDma_BdSetCtrl(last_txbd, XMCDMA_BD_CTRL_EOF_MASK); + } + +#if LWIP_FULL_CSUM_OFFLOAD_TX==1 + bd_fullcsum_disable(txbdset); + if (p->len > sizeof(struct ethip_hdr)) { + bd_fullcsum_enable(txbdset); + } +#endif + +#if LWIP_PARTIAL_CSUM_OFFLOAD_TX==1 + bd_csum_disable(txbdset); + update_partial_cksum_offload(txbdset, p); +#endif + DATA_SYNC; + /* enq to h/w */ + return XMcDma_ChanToHw(Tx_Chan); +} + +void axi_mcdma_register_handlers(struct xemac_s *xemac, u8 ChanId) +{ + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + XMcdma *McDmaInstPtr = &xaxiemacif->aximcdma; + + struct xtopology_t *xtopologyp = &xtopology[xemac->topology_index]; + +#if XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ == 1 + + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xaxiemacif->axi_ethernet.Config.TemacIntr, + (Xil_InterruptHandler)xaxiemac_error_handler, + &xaxiemacif->axi_ethernet); + + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiMcDmaRxIntr[ChanId - 1], + (Xil_InterruptHandler)XMcdma_IntrHandler, + McDmaInstPtr); + + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xaxiemacif->axi_ethernet.Config.AxiMcDmaTxIntr[ChanId - 1], + (Xil_InterruptHandler)XMcdma_TxIntrHandler, + McDmaInstPtr); + + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.TemacIntr, + AXIETH_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiMcDmaTxIntr[ChanId - 1], + AXIDMA_TX_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiMcDmaRxIntr[ChanId - 1], + AXIDMA_RX_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.TemacIntr); + + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiMcDmaTxIntr[ChanId - 1]); + + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xaxiemacif->axi_ethernet.Config.AxiMcDmaRxIntr[ChanId - 1]); + +#endif /* XLWIP_CONFIG_INCLUDE_AXIETH_ON_ZYNQ */ +} + +XStatus axi_mcdma_setup_rx_chan(struct xemac_s *xemac, u32_t ChanId) +{ + XMcdma_ChanCtrl *Rx_Chan; + XStatus status; + + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + + /* RX chan configurations */ + Rx_Chan = XMcdma_GetMcdmaRxChan(&xaxiemacif->aximcdma, ChanId); + + /* Disable all interrupts */ + XMcdma_IntrDisable(Rx_Chan, XMCDMA_IRQ_ALL_MASK); + + status = XMcDma_ChanBdCreate(Rx_Chan, (UINTPTR) xaxiemacif->rx_bdspace, + XLWIP_CONFIG_N_RX_DESC); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Rx bd create failed with %d\r\n", status)); + return XST_FAILURE; + } + + xaxiemacif->rx_bdspace += (XLWIP_CONFIG_N_RX_DESC * sizeof(XMcdma_Bd)); + + /* Setup Interrupt System and register callbacks */ + XMcdma_SetCallBack(&xaxiemacif->aximcdma, XMCDMA_HANDLER_DONE, + (void *)axi_mcdma_recv_handler, xemac); + XMcdma_SetCallBack(&xaxiemacif->aximcdma, XMCDMA_HANDLER_ERROR, + (void *)axi_mcdma_recv_error_handler, xemac); + + status = XMcdma_SetChanCoalesceDelay(Rx_Chan, + XLWIP_CONFIG_N_RX_COALESCE, + XMCDMA_COALESCEDELAY); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error setting coalescing settings\r\n")); + return ERR_IF; + } + + setup_rx_bds(Rx_Chan, XLWIP_CONFIG_N_RX_DESC); + + /* enable DMA interrupts */ + XMcdma_IntrEnable(Rx_Chan, XMCDMA_IRQ_ALL_MASK); + + return XST_SUCCESS; +} + +XStatus axi_mcdma_setup_tx_chan(struct xemac_s *xemac, u8 ChanId) +{ + XStatus status; + XMcdma_ChanCtrl *Tx_Chan; + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + + /* TX chan configurations */ + Tx_Chan = XMcdma_GetMcdmaTxChan(&xaxiemacif->aximcdma, ChanId); + + XMcdma_IntrDisable(Tx_Chan, XMCDMA_IRQ_ALL_MASK); + + status = XMcDma_ChanBdCreate(Tx_Chan, (UINTPTR) xaxiemacif->tx_bdspace, + XLWIP_CONFIG_N_TX_DESC); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("TX bd create failed with %d\r\n", status)); + return XST_FAILURE; + } + + xaxiemacif->tx_bdspace += (XLWIP_CONFIG_N_TX_DESC * sizeof(XMcdma_Bd)); + + /* Setup Interrupt System and register callbacks */ + XMcdma_SetCallBack(&xaxiemacif->aximcdma, XMCDMA_TX_HANDLER_DONE, + (void *)axi_mcdma_send_handler, &xaxiemacif->aximcdma); + XMcdma_SetCallBack(&xaxiemacif->aximcdma, XMCDMA_TX_HANDLER_ERROR, + (void *)axi_mcdma_send_error_handler, + &xaxiemacif->aximcdma); + + status = XMcdma_SetChanCoalesceDelay(Tx_Chan, + XLWIP_CONFIG_N_TX_COALESCE, + XMCDMA_COALESCEDELAY); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("Error setting coalescing settings\r\n")); + return ERR_IF; + } + + XMcdma_IntrEnable(Tx_Chan, XMCDMA_IRQ_ALL_MASK); + + return XST_SUCCESS; +} + +XStatus init_axi_mcdma(struct xemac_s *xemac) +{ + XMcdma_Config *dmaconfig; + XStatus status; + u32_t ChanId; + xaxiemacif_s *xaxiemacif = (xaxiemacif_s *)(xemac->state); + UINTPTR baseaddr; + + /* + * Disable L1 prefetch if the processor type is Cortex A53. It is + * observed that the L1 prefetching for ARMv8 can cause issues while + * dealing with cache memory on Rx path. On Rx path, the lwIP adapter + * does a clean and invalidation of buffers (pbuf payload) before + * allocating them to Rx BDs. However, there are chances that the + * the same cache line may get prefetched by the time Rx data is + * DMAed to the same buffer. In such cases, CPU fetches stale data from + * cache memory instead of getting them from memory. To avoid such + * scenarios L1 prefetch is being disabled for ARMv8. That can cause + * a performance degradation in the range of 3-5%. In tests, it is + * generally observed that this performance degradation is quite + * insignificant to be really visible. + */ +#if defined __aarch64__ + Xil_ConfigureL1Prefetch(0); +#endif + + xaxiemacif->rx_bdspace = alloc_bdspace(XLWIP_CONFIG_N_RX_DESC * + (XMCDMA_MAX_CHAN_PER_DEVICE / 2), + XMCDMA_BD_MINIMUM_ALIGNMENT); + if (!xaxiemacif->rx_bdspace) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s@%d: Error: Unable to allocate memory for " + "RX buffer descriptors", __FILE__, __LINE__)); + return ERR_IF; + } + + xaxiemacif->tx_bdspace = alloc_bdspace(XLWIP_CONFIG_N_TX_DESC * + (XMCDMA_MAX_CHAN_PER_DEVICE / 2), + XMCDMA_BD_MINIMUM_ALIGNMENT); + if (!xaxiemacif->tx_bdspace) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s@%d: Error: Unable to allocate memory for " + "TX buffer descriptors", __FILE__, __LINE__)); + return ERR_IF; + } + + /* Mark the BD Region as uncacheable */ +#if defined(__aarch64__) + Xil_SetTlbAttributes((UINTPTR)bd_space, + NORM_NONCACHE | INNER_SHAREABLE); +#elif defined (ARMR5) + Xil_SetTlbAttributes((INTPTR)bd_space, + DEVICE_SHARED | PRIV_RW_USER_RW); +#else + Xil_SetTlbAttributes((INTPTR)bd_space, DEVICE_MEMORY); +#endif + + LWIP_DEBUGF(NETIF_DEBUG, ("rx_bdspace: 0x%08x\r\n", + xaxiemacif->rx_bdspace)); + LWIP_DEBUGF(NETIF_DEBUG, ("tx_bdspace: 0x%08x\r\n", + xaxiemacif->tx_bdspace)); + + /* Initialize MCDMA */ + baseaddr = xaxiemacif->axi_ethernet.Config.AxiDevBaseAddress; + dmaconfig = XMcdma_LookupConfigBaseAddr(baseaddr); + if (!baseaddr) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s@%d: Error: Lookup Config failed\r\n", __FILE__, + __LINE__)); + } + status = XMcDma_CfgInitialize(&xaxiemacif->aximcdma, dmaconfig); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s@%d: Error: MCDMA config initialization failed\r\n", __FILE__, __LINE__)); + return XST_FAILURE; + } + + /* Setup Rx/Tx chan and Interrupts */ + for (ChanId = 1; + ChanId <= xaxiemacif->axi_ethernet.Config.AxiMcDmaChan_Cnt; + ChanId++) { + + status = axi_mcdma_setup_rx_chan(xemac, ChanId); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s@%d: Error: MCDMA Rx chan setup failed\r\n", __FILE__, __LINE__)); + return XST_FAILURE; + } + + status = axi_mcdma_setup_tx_chan(xemac, ChanId); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("%s@%d: Error: MCDMA Tx chan setup failed\r\n", __FILE__, __LINE__)); + return XST_FAILURE; + } + + axi_mcdma_register_handlers(xemac, ChanId); + + } + return XST_SUCCESS; +} diff --git a/bsp_z7/lwip/netif/xaxiemacif_physpeed.c b/bsp_z7/lwip/netif/xaxiemacif_physpeed.c new file mode 100644 index 0000000..523a6ad --- /dev/null +++ b/bsp_z7/lwip/netif/xaxiemacif_physpeed.c @@ -0,0 +1,834 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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/xaxiemacif.h" +#include "lwipopts.h" +#include "sleep.h" +#include "xemac_ieee_reg.h" + +#define PHY_R0_ISOLATE 0x0400 +#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 + +/* Marvel PHY flags */ +#define MARVEL_PHY_IDENTIFIER 0x141 +#define MARVEL_PHY_MODEL_NUM_MASK 0x3F0 +#define MARVEL_PHY_88E1111_MODEL 0xC0 +#define MARVEL_PHY_88E1116R_MODEL 0x240 +#define PHY_88E1111_RGMII_RX_CLOCK_DELAYED_MASK 0x0080 + +/* TI PHY Flags */ +#define TI_PHY_DETECT_MASK 0x796D +#define TI_PHY_IDENTIFIER 0x2000 +#define TI_PHY_DP83867_MODEL 0xA231 +#define DP83867_RGMII_CLOCK_DELAY_CTRL_MASK 0x0003 +#define DP83867_RGMII_TX_CLOCK_DELAY_MASK 0x0030 +#define DP83867_RGMII_RX_CLOCK_DELAY_MASK 0x0003 + +/* TI DP83867 PHY Registers */ +#define DP83867_R32_RGMIICTL1 0x32 +#define DP83867_R86_RGMIIDCTL 0x86 + +#define TI_PHY_REGCR 0xD +#define TI_PHY_ADDDR 0xE +#define TI_PHY_PHYCTRL 0x10 +#define TI_PHY_CFGR2 0x14 +#define TI_PHY_SGMIITYPE 0xD3 +#define TI_PHY_CFGR2_SGMII_AUTONEG_EN 0x0080 +#define TI_PHY_SGMIICLK_EN 0x4000 +#define TI_PHY_REGCR_DEVAD_EN 0x001F +#define TI_PHY_REGCR_DEVAD_DATAEN 0x4000 +#define TI_PHY_CFGR2_MASK 0x003F +#define TI_PHY_REGCFG4 0x31 +#define TI_PHY_REGCR_DATA 0x401F +#define TI_PHY_CFG4RESVDBIT7 0x80 +#define TI_PHY_CFG4RESVDBIT8 0x100 +#define TI_PHY_CFG4_AUTONEG_TIMER 0x60 + +#define TI_PHY_CFG2_SPEEDOPT_10EN 0x0040 +#define TI_PHY_CFG2_SGMII_AUTONEGEN 0x0080 +#define TI_PHY_CFG2_SPEEDOPT_ENH 0x0100 +#define TI_PHY_CFG2_SPEEDOPT_CNT 0x0800 +#define TI_PHY_CFG2_SPEEDOPT_INTLOW 0x2000 + +#define TI_PHY_CR_SGMII_EN 0x0800 + +/* Loop counters to check for reset done + */ +#define RESET_TIMEOUT 0xFFFF +#define AUTO_NEG_TIMEOUT 0x00FFFFFF + +#define IEEE_CTRL_RESET 0x9140 +#define IEEE_CTRL_ISOLATE_DISABLE 0xFBFF + +#define PHY_XILINX_PCS_PMA_ID1 0x0174 +#define PHY_XILINX_PCS_PMA_ID2 0x0C00 + +#ifdef SDT +#define XPAR_AXIETHERNET_0_PHYADDR XPAR_XAXIETHERNET_0_PHYADDR +#define XPAR_AXIETHERNET_0_BASEADDR XPAR_XAXIETHERNET_0_BASEADDR +#endif + +extern u32_t phyaddrforemac; + +static void __attribute__ ((noinline)) AxiEthernetUtilPhyDelay(unsigned int Seconds); + +static int detect_phy(XAxiEthernet *xaxiemacp) +{ + u16 phy_reg; + u16 phy_id; + u32 phy_addr; + + for (phy_addr = 31; phy_addr > 0; phy_addr--) { + XAxiEthernet_PhyRead(xaxiemacp, 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, ("XAxiEthernet detect_phy: PHY detected at address %d.\r\n", phy_addr)); + LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet detect_phy: PHY detected.\r\n")); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, + &phy_reg); + if ((phy_reg != PHY_MARVELL_IDENTIFIER) && + (phy_reg != TI_PHY_IDENTIFIER)){ + xil_printf("WARNING: Not a Marvell or TI Ethernet PHY. Please verify the initialization sequence\r\n"); + } + phyaddrforemac = phy_addr; + return phy_addr; + } + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, + &phy_id); + + if (phy_id == PHY_XILINX_PCS_PMA_ID1) { + XAxiEthernet_PhyRead(xaxiemacp, 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, ("XAxiEthernet detect_phy: PHY detected at address %d.\r\n", + phy_addr)); + phyaddrforemac = phy_addr; + return phy_addr; + } + } + } + + LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet detect_phy: No PHY detected. Assuming a PHY at address 0\r\n")); + + /* default to zero */ + return 0; +} + +static int isphy_pcspma(XAxiEthernet *xaxiemacp, u32 phy_addr) +{ + u16 phy_id; + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, + &phy_id); + if (phy_id == PHY_XILINX_PCS_PMA_ID1) { + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG, + &phy_id); + if (phy_id == PHY_XILINX_PCS_PMA_ID2) { + return 1; + } + } + + return 0; +} + +void XAxiEthernet_PhyReadExtended(XAxiEthernet *InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 *PhyDataPtr) +{ + XAxiEthernet_PhyWrite(InstancePtr, PhyAddress, + IEEE_MMD_ACCESS_CONTROL_REG, IEEE_MMD_ACCESS_CTRL_DEVAD_MASK); + + XAxiEthernet_PhyWrite(InstancePtr, PhyAddress, + IEEE_MMD_ACCESS_ADDRESS_DATA_REG, RegisterNum); + + XAxiEthernet_PhyWrite(InstancePtr, PhyAddress, + IEEE_MMD_ACCESS_CONTROL_REG, IEEE_MMD_ACCESS_CTRL_NOPIDEVAD_MASK); + + XAxiEthernet_PhyRead(InstancePtr, PhyAddress, + IEEE_MMD_ACCESS_ADDRESS_DATA_REG, PhyDataPtr); + +} + +void XAxiEthernet_PhyWriteExtended(XAxiEthernet *InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 PhyDataPtr) +{ + XAxiEthernet_PhyWrite(InstancePtr, PhyAddress, + IEEE_MMD_ACCESS_CONTROL_REG, IEEE_MMD_ACCESS_CTRL_DEVAD_MASK); + + XAxiEthernet_PhyWrite(InstancePtr, PhyAddress, + IEEE_MMD_ACCESS_ADDRESS_DATA_REG, RegisterNum); + + XAxiEthernet_PhyWrite(InstancePtr, PhyAddress, + IEEE_MMD_ACCESS_CONTROL_REG, IEEE_MMD_ACCESS_CTRL_NOPIDEVAD_MASK); + + XAxiEthernet_PhyWrite(InstancePtr, PhyAddress, + IEEE_MMD_ACCESS_ADDRESS_DATA_REG, PhyDataPtr); + +} + +unsigned int get_phy_negotiated_speed (XAxiEthernet *xaxiemacp, u32 phy_addr) +{ + u16 control; + u16 status; + u16 partner_capabilities; + u16 partner_capabilities_1000; + u16 phylinkspeed; + u16 temp; + + + phy_addr = detect_phy(xaxiemacp); + + xil_printf("Start PHY autonegotiation \r\n"); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, + &control); + + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + + if (isphy_pcspma(xaxiemacp, phy_addr)) { + control &= IEEE_CTRL_ISOLATE_DISABLE; + } + + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, + control); + if (isphy_pcspma(xaxiemacp, phy_addr)) { + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + xil_printf("Waiting for PHY to complete autonegotiation \r\n"); + while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { + AxiEthernetUtilPhyDelay(1); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, + &status); + + } + + xil_printf("Autonegotiation complete \r\n"); + + if (xaxiemacp->Config.Speed == XAE_SPEED_2500_MBPS) + return XAE_SPEED_2500_MBPS; + +#ifndef SDT + if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == XAE_PHY_TYPE_1000BASE_X) { +#else + if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == XAE_PHY_TYPE_1000BASE_X) { +#endif + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 1); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp); + if ((temp & 0x0020) == 0x0020) { + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + return 1000; + } + else { + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + xil_printf("Link error, temp = %x\r\n", temp); + return 0; + } +#ifndef SDT + } else if(XAxiEthernet_GetPhysicalInterface(xaxiemacp) == XAE_PHY_TYPE_SGMII) { +#else + } else if(XAxiEthernet_Get_Phy_Interface(xaxiemacp) == XAE_PHY_TYPE_SGMII) { + +#endif + xil_printf("Waiting for Link to be up; Polling for SGMII core Reg \r\n"); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp); + while(!(temp & 0x8000)) { + XAxiEthernet_PhyRead(xaxiemacp, 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"); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &temp); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, 0x0100); + return 10; + } + } + } + + /* Read PHY control and status registers is successful. */ + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, + &control); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, + &status); + if ((control & IEEE_CTRL_AUTONEGOTIATE_ENABLE) && (status & + IEEE_STAT_AUTONEGOTIATE_CAPABLE)) { + xil_printf("Waiting for PHY to complete autonegotiation.\r\n"); + while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, + IEEE_STATUS_REG_OFFSET, + &status); + } + + xil_printf("autonegotiation complete \r\n"); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, + IEEE_PARTNER_ABILITIES_1_REG_OFFSET, + &partner_capabilities); + if (status & IEEE_STAT_1GBPS_EXTENSIONS) { + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, + IEEE_PARTNER_ABILITIES_3_REG_OFFSET, + &partner_capabilities_1000); + if (partner_capabilities_1000 & + IEEE_AN3_ABILITY_MASK_1GBPS) + return 1000; + } + + if (partner_capabilities & IEEE_AN1_ABILITY_MASK_100MBPS) + return 100; + if (partner_capabilities & IEEE_AN1_ABILITY_MASK_10MBPS) + return 10; + + xil_printf("%s: unknown PHY link speed, setting TEMAC speed to be 10 Mbps\r\n", + __FUNCTION__); + return 10; + } else { + /* Update TEMAC speed accordingly */ + if (status & IEEE_STAT_1GBPS_EXTENSIONS) { + + /* Get commanded link speed */ + phylinkspeed = control & + IEEE_CTRL_1GBPS_LINKSPEED_MASK; + + switch (phylinkspeed) { + case (IEEE_CTRL_LINKSPEED_1000M): + return 1000; + case (IEEE_CTRL_LINKSPEED_100M): + return 100; + case (IEEE_CTRL_LINKSPEED_10M): + return 10; + default: + xil_printf("%s: unknown PHY link speed (%d), setting TEMAC speed to be 10 Mbps\r\n", + __FUNCTION__, phylinkspeed); + return 10; + } + } else { + return (control & IEEE_CTRL_LINKSPEED_MASK) ? 100 : 10; + } + } +} + +unsigned int get_phy_speed_TI_DP83867(XAxiEthernet *xaxiemacp, u32 phy_addr) +{ + u16 phy_val; + u16 control; + + xil_printf("Start PHY autonegotiation \r\n"); + + /* Changing the PHY RX and TX DELAY settings. */ + XAxiEthernet_PhyReadExtended(xaxiemacp, phy_addr, DP83867_R32_RGMIICTL1, &phy_val); + phy_val |= DP83867_RGMII_CLOCK_DELAY_CTRL_MASK; + XAxiEthernet_PhyWriteExtended(xaxiemacp, phy_addr, DP83867_R32_RGMIICTL1, phy_val); + + XAxiEthernet_PhyReadExtended(xaxiemacp, phy_addr, DP83867_R86_RGMIIDCTL, &phy_val); + phy_val &= 0xFF00; + phy_val |= DP83867_RGMII_TX_CLOCK_DELAY_MASK; + phy_val |= DP83867_RGMII_RX_CLOCK_DELAY_MASK; + XAxiEthernet_PhyWriteExtended(xaxiemacp, phy_addr, DP83867_R86_RGMIIDCTL, phy_val); + + /* Set advertised speeds for 10/100/1000Mbps modes. */ + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, &control); + control |= ADVERTISE_1000; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, control); + + return get_phy_negotiated_speed(xaxiemacp, phy_addr); +} + +unsigned int get_phy_speed_TI_DP83867_SGMII(XAxiEthernet *xaxiemacp, u32 phy_addr) +{ + u16 control; + u16 temp; + u16 phyregtemp; + + xil_printf("Start TI PHY autonegotiation \r\n"); + + /* Enable SGMII Clock */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, + TI_PHY_REGCR_DEVAD_EN); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, + TI_PHY_SGMIITYPE); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, + TI_PHY_REGCR_DEVAD_EN | TI_PHY_REGCR_DEVAD_DATAEN); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, + TI_PHY_SGMIICLK_EN); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, + &control); + control |= (IEEE_CTRL_AUTONEGOTIATE_ENABLE | IEEE_CTRL_LINKSPEED_1000M | + IEEE_CTRL_FULL_DUPLEX); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, + control); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, TI_PHY_CFGR2, &control); + control &= TI_PHY_CFGR2_MASK; + control |= (TI_PHY_CFG2_SPEEDOPT_10EN | + TI_PHY_CFG2_SGMII_AUTONEGEN | + TI_PHY_CFG2_SPEEDOPT_ENH | + TI_PHY_CFG2_SPEEDOPT_CNT | + TI_PHY_CFG2_SPEEDOPT_INTLOW); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_CFGR2, control); + + /* Disable RGMII */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, + TI_PHY_REGCR_DEVAD_EN); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, + DP83867_R32_RGMIICTL1); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, + TI_PHY_REGCR_DEVAD_EN | TI_PHY_REGCR_DEVAD_DATAEN); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, 0); + + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_PHYCTRL, + TI_PHY_CR_SGMII_EN); + + xil_printf("Waiting for Link to be up \r\n"); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, + IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp); + while(!(temp & 0x4000)) { + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, + IEEE_PARTNER_ABILITIES_1_REG_OFFSET, &temp); + } + xil_printf("Auto negotiation completed for TI PHY\n\r"); + + /* SW workaround for unstable link when RX_CTRL is not STRAP MODE 3 or 4 */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, TI_PHY_REGCR_DEVAD_EN); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, TI_PHY_REGCFG4); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, TI_PHY_REGCR_DATA); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, TI_PHY_ADDDR, (u16_t *)&phyregtemp); + phyregtemp &= ~(TI_PHY_CFG4RESVDBIT7); + phyregtemp |= TI_PHY_CFG4RESVDBIT8; + phyregtemp &= ~(TI_PHY_CFG4_AUTONEG_TIMER); + phyregtemp |= TI_PHY_CFG4_AUTONEG_TIMER; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, TI_PHY_REGCR_DEVAD_EN); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, TI_PHY_REGCFG4); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, TI_PHY_REGCR_DATA); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, phyregtemp); + + return get_phy_negotiated_speed(xaxiemacp, phy_addr); +} + +unsigned int get_phy_speed_88E1116R(XAxiEthernet *xaxiemacp, u32 phy_addr) +{ + u16 phy_val; + u16 control; + u16 status; + u16 partner_capabilities; + + xil_printf("Start PHY autonegotiation \r\n"); + + XAxiEthernet_PhyWrite(xaxiemacp,phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 2); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, &control); + control |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, control); + + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control); + control |= IEEE_ASYMMETRIC_PAUSE_MASK; + control |= IEEE_PAUSE_MASK; + control |= ADVERTISE_100; + control |= ADVERTISE_10; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + &control); + control |= ADVERTISE_1000; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + control); + + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, + &control); + control |= (7 << 12); /* max number of gigabit atphy_valts */ + control |= (1 << 11); /* enable downshift */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_CONTROL_REG, + control); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE; + control |= IEEE_STAT_AUTONEGOTIATE_RESTART; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + control |= IEEE_CTRL_RESET_MASK; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, control); + while (1) { + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control); + if (control & IEEE_CTRL_RESET_MASK) + continue; + else + break; + } + + xil_printf("Waiting for PHY to complete autonegotiation.\r\n"); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, &status); + while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { + AxiEthernetUtilPhyDelay(1); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_COPPER_SPECIFIC_STATUS_REG_2, + &phy_val); + if (phy_val & IEEE_AUTONEG_ERROR_MASK) { + xil_printf("Auto negotiation error \r\n"); + } + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_STATUS_REG_OFFSET, + &status); + } + + xil_printf("autonegotiation complete \r\n"); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_SPECIFIC_STATUS_REG, + &partner_capabilities); + if ( ((partner_capabilities >> 14) & 3) == 2)/* 1000Mbps */ + return 1000; + else if ( ((partner_capabilities >> 14) & 3) == 1)/* 100Mbps */ + return 100; + else /* 10Mbps */ + return 10; +} + + +unsigned int get_phy_speed_88E1111 (XAxiEthernet *xaxiemacp, u32 phy_addr) +{ + u16 control; + int TimeOut; + u16 phy_val; + +#ifndef SDT + if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == +#else + if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == +#endif + XAE_PHY_TYPE_RGMII_2_0) { + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, + IEEE_EXT_PHY_SPECIFIC_CONTROL_REG, &phy_val); + phy_val |= PHY_88E1111_RGMII_RX_CLOCK_DELAYED_MASK; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, + IEEE_EXT_PHY_SPECIFIC_CONTROL_REG, phy_val); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, + &control); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_OFFSET, + control | IEEE_CTRL_RESET_MASK); + + TimeOut = RESET_TIMEOUT; + while (TimeOut) { + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, + IEEE_CONTROL_REG_OFFSET, &control); + if (!(control & IEEE_CTRL_RESET_MASK)) + break; + TimeOut -= 1; + } + + if (!TimeOut) { + xil_printf("%s: Phy Reset failed\n\r", __FUNCTION__); + return 0; + } + } + + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET, + ADVERTISE_1000); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_100_AND_10); + + return get_phy_negotiated_speed(xaxiemacp, phy_addr); +} + +unsigned get_IEEE_phy_speed(XAxiEthernet *xaxiemacp) +{ + u16 phy_identifier; + u16 phy_model; + u8 phytype; + +#ifdef XPAR_AXIETHERNET_0_BASEADDR + u32 phy_addr = detect_phy(xaxiemacp); + + /* Get the PHY Identifier and Model number */ + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, &phy_identifier); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG, &phy_model); + +/* Depending upon what manufacturer PHY is connected, a different mask is + * needed to determine the specific model number of the PHY. */ + if (phy_identifier == MARVEL_PHY_IDENTIFIER) { + phy_model = phy_model & MARVEL_PHY_MODEL_NUM_MASK; + + if (phy_model == MARVEL_PHY_88E1116R_MODEL) { + return get_phy_speed_88E1116R(xaxiemacp, phy_addr); + } else if (phy_model == MARVEL_PHY_88E1111_MODEL) { + return get_phy_speed_88E1111(xaxiemacp, phy_addr); + } + } else if (phy_identifier == TI_PHY_IDENTIFIER) { + phy_model = phy_model & TI_PHY_DP83867_MODEL; +#ifndef SDT + phytype = XAxiEthernet_GetPhysicalInterface(xaxiemacp); +#else + phytype = XAxiEthernet_Get_Phy_Interface(xaxiemacp); +#endif + if (phy_model == TI_PHY_DP83867_MODEL && phytype == XAE_PHY_TYPE_SGMII) { + return get_phy_speed_TI_DP83867_SGMII(xaxiemacp, phy_addr); + } + + if (phy_model == TI_PHY_DP83867_MODEL) { + return get_phy_speed_TI_DP83867(xaxiemacp, phy_addr); + } + } + else { + LWIP_DEBUGF(NETIF_DEBUG, ("XAxiEthernet get_IEEE_phy_speed: Detected PHY with unknown identifier/model.\r\n")); + } +#endif + if (isphy_pcspma(xaxiemacp, phy_addr)) { + return get_phy_negotiated_speed(xaxiemacp, phy_addr); + } +} + +unsigned configure_IEEE_phy_speed(XAxiEthernet *xaxiemacp, unsigned speed) +{ + u16 control; + u32 phy_addr = detect_phy(xaxiemacp); + u16 phy_val; + +#ifndef SDT + if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == +#else + if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == +#endif + XAE_PHY_TYPE_RGMII_2_0) { + /* Setting Tx and Rx Delays for RGMII mode */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0x2); + + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, &phy_val); + phy_val |= IEEE_RGMII_TXRX_CLOCK_DELAYED_MASK; + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_CONTROL_REG_MAC, phy_val); + + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, IEEE_PAGE_ADDRESS_REGISTER, 0x0); + } + + XAxiEthernet_PhyRead(xaxiemacp, 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; + } + + else if (speed == 100) { + control |= IEEE_CTRL_LINKSPEED_100M; + /* Don't advertise PHY speed of 1000 Mbps */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, + IEEE_1000_ADVERTISE_REG_OFFSET, + 0); + /* Don't advertise PHY speed of 10 Mbps */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, + IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_100); + + } + else if (speed == 10) { + control |= IEEE_CTRL_LINKSPEED_10M; + /* Don't advertise PHY speed of 1000 Mbps */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, + IEEE_1000_ADVERTISE_REG_OFFSET, + 0); + /* Don't advertise PHY speed of 100 Mbps */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, + IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_10); + } + + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, + IEEE_CONTROL_REG_OFFSET, + control | IEEE_CTRL_RESET_MASK); + +#ifndef SDT + if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == +#else + if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == +#endif + XAE_PHY_TYPE_SGMII) { + control &= (~PHY_R0_ISOLATE); + XAxiEthernet_PhyWrite(xaxiemacp, + XPAR_AXIETHERNET_0_PHYADDR, + IEEE_CONTROL_REG_OFFSET, + control | IEEE_CTRL_AUTONEGOTIATE_ENABLE); + } + + { + volatile int wait; + for (wait=0; wait < 100000; wait++); + for (wait=0; wait < 100000; wait++); + } + return 0; +} + +unsigned phy_setup_axiemac (XAxiEthernet *xaxiemacp) +{ + unsigned link_speed = 1000; + +#ifndef SDT + if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == +#else + if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == +#endif + XAE_PHY_TYPE_RGMII_1_3) { + ; /* Add PHY initialization code for RGMII 1.3 */ +#ifndef SDT + } else if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == +#else + } else if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == +#endif + XAE_PHY_TYPE_RGMII_2_0) { + ; /* Add PHY initialization code for RGMII 2.0 */ +#ifndef SDT + } else if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == +#else + } else if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == +#endif + XAE_PHY_TYPE_SGMII) { +#ifdef CONFIG_LINKSPEED_AUTODETECT + u32 phy_wr_data = IEEE_CTRL_AUTONEGOTIATE_ENABLE | + IEEE_CTRL_LINKSPEED_1000M; + phy_wr_data &= (~PHY_R0_ISOLATE); + + XAxiEthernet_PhyWrite(xaxiemacp, + XPAR_AXIETHERNET_0_PHYADDR, + IEEE_CONTROL_REG_OFFSET, + phy_wr_data); +#endif +#ifndef SDT + } else if (XAxiEthernet_GetPhysicalInterface(xaxiemacp) == +#else + } else if (XAxiEthernet_Get_Phy_Interface(xaxiemacp) == +#endif + XAE_PHY_TYPE_1000BASE_X) { + ; /* Add PHY initialization code for 1000 Base-X */ + } +/* set PHY <--> MAC data clock */ +#ifdef CONFIG_LINKSPEED_AUTODETECT + link_speed = get_IEEE_phy_speed(xaxiemacp); + xil_printf("auto-negotiated link speed: %d\r\n", link_speed); +#elif defined(CONFIG_LINKSPEED1000) + link_speed = 1000; + configure_IEEE_phy_speed(xaxiemacp, link_speed); + xil_printf("link speed: %d\r\n", link_speed); +#elif defined(CONFIG_LINKSPEED100) + link_speed = 100; + configure_IEEE_phy_speed(xaxiemacp, link_speed); + xil_printf("link speed: %d\r\n", link_speed); +#elif defined(CONFIG_LINKSPEED10) + link_speed = 10; + configure_IEEE_phy_speed(xaxiemacp, link_speed); + xil_printf("link speed: %d\r\n", link_speed); +#endif + return link_speed; +} + +static void __attribute__ ((noinline)) AxiEthernetUtilPhyDelay(unsigned int Seconds) +{ +#if defined (__MICROBLAZE__) + static int WarningFlag = 0; + + /* If MB caches are disabled or do not exist, this delay loop could + * take minutes instead of seconds (e.g., 30x longer). Print a warning + * message for the user (once). If only MB had a built-in timer! + */ + if (((mfmsr() & 0x20) == 0) && (!WarningFlag)) { + WarningFlag = 1; + } + +#define ITERS_PER_SEC (XPAR_CPU_CORE_CLOCK_FREQ_HZ / 6) + __asm volatile ("\n" + "1: \n\t" + "addik r7, r0, %0 \n\t" + "2: \n\t" + "addik r7, r7, -1 \n\t" + "bneid r7, 2b \n\t" + "or r0, r0, r0 \n\t" + "bneid %1, 1b \n\t" + "addik %1, %1, -1 \n\t" + :: "i"(ITERS_PER_SEC), "d" (Seconds)); +#else + sleep(Seconds); +#endif +} + +void enable_sgmii_clock(XAxiEthernet *xaxiemacp) +{ + u16 phy_identifier; + u16 phy_model; + u8 phytype; + + XAxiEthernet_PhySetMdioDivisor(xaxiemacp, XAE_MDIO_DIV_DFT); + u32 phy_addr = detect_phy(xaxiemacp); + /* Get the PHY Identifier and Model number */ + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_1_REG, &phy_identifier); + XAxiEthernet_PhyRead(xaxiemacp, phy_addr, PHY_IDENTIFIER_2_REG, &phy_model); + + if (phy_identifier == TI_PHY_IDENTIFIER) { + phy_model = phy_model & TI_PHY_DP83867_MODEL; +#ifndef SDT + phytype = XAxiEthernet_GetPhysicalInterface(xaxiemacp); +#else + phytype = XAxiEthernet_Get_Phy_Interface(xaxiemacp); +#endif + if (phy_model == TI_PHY_DP83867_MODEL && phytype == XAE_PHY_TYPE_SGMII) { + /* Enable SGMII Clock by switching to 6-wire mode */ + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, + TI_PHY_REGCR_DEVAD_EN); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, + TI_PHY_SGMIITYPE); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_REGCR, + TI_PHY_REGCR_DEVAD_EN | TI_PHY_REGCR_DEVAD_DATAEN); + XAxiEthernet_PhyWrite(xaxiemacp, phy_addr, TI_PHY_ADDDR, + TI_PHY_SGMIICLK_EN); + } + } +} diff --git a/bsp_z7/lwip/netif/xemac_ieee_reg.h b/bsp_z7/lwip/netif/xemac_ieee_reg.h new file mode 100644 index 0000000..4305850 --- /dev/null +++ b/bsp_z7/lwip/netif/xemac_ieee_reg.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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/bsp_z7/lwip/netif/xemacliteif.c b/bsp_z7/lwip/netif/xemacliteif.c new file mode 100644 index 0000000..5a61482 --- /dev/null +++ b/bsp_z7/lwip/netif/xemacliteif.c @@ -0,0 +1,873 @@ +/* + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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" + +#if !NO_SYS +#include "FreeRTOS.h" +#include "semphr.h" +#include "timers.h" +#include "lwip/timeouts.h" +#endif + +#include +#include + +#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 "netif/etharp.h" +#include "netif/xadapter.h" +#include "netif/xemacliteif.h" +#include "xstatus.h" + +#include "netif/xpqueue.h" + +#include "xlwipconfig.h" +#include "xparameters.h" +#ifndef SDT +#if XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ == 1 +#include "xscugic.h" +#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_DIST_BASEADDR +#else +#include "xintc.h" +#endif +#else +#include "xinterrupt_wrap.h" +#endif + +/* Define those to better describe your network interface. */ +#define IFNAME0 'x' +#define IFNAME1 'e' + + +/* 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) + +#if XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ == 1 +#define EMACLITE_INTR_PRIORITY_SET_IN_GIC 0xA0 +#define TRIG_TYPE_RISING_EDGE_SENSITIVE 0x3 +#endif + +#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_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_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_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 PHY_DETECT_REG 1 +#define PHY_DETECT_MASK 0x1808 + +/* Forward declarations. */ +static err_t xemacliteif_output(struct netif *netif, struct pbuf *p, + const ip_addr_t *ipaddr); +unsigned get_IEEE_phy_speed_emaclite(XEmacLite *xemaclitep); +unsigned configure_IEEE_phy_speed_emaclite(XEmacLite *xemaclitep, unsigned speed); + +/* The payload from multiple pbufs is assembled into a single contiguous + * area for transmission. Currently this is a global variable (it should really + * belong in the per netif structure), but that is ok since this can be used + * only in a protected context + */ +unsigned char xemac_tx_frame[XEL_MAX_FRAME_SIZE] __attribute__((aligned(64))); + +#if !NO_SYS +extern u32 xInsideISR; +#endif + +#ifndef XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ +#if XPAR_INTC_0_HAS_FAST == 1 +/*********** Function Prototypes *********************************************/ +/* + * Function prototypes of the functions used for registering Fast + * Interrupt Handlers + */ +static void XEmacLite_FastInterruptHandler(void) + __attribute__ ((fast_interrupt)); + + +/**************** Variable Declarations **************************************/ + +/** Variables for Fast Interrupt handlers ***/ +XEmacLite *xemaclitep_fast; + +#endif +#endif + +static void +xemacif_recv_handler(void *arg) { + struct xemac_s *xemac = (struct xemac_s *)(arg); + xemacliteif_s *xemacliteif = (xemacliteif_s *)(xemac->state); + XEmacLite *instance = xemacliteif->instance; + struct pbuf *p; + int len = 0; + struct xtopology_t *xtopologyp = &xtopology[xemac->topology_index]; + +#if !NO_SYS + xInsideISR++; +#endif + +#if XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ == 1 +#else +#ifndef SDT + XIntc_AckIntr(xtopologyp->intc_baseaddr, 1 << xtopologyp->intc_emac_intr); +#endif +#endif + p = pbuf_alloc(PBUF_RAW, XEL_MAX_FRAME_SIZE, PBUF_POOL); + if (!p) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + /* receive and just ignore the frame. + * we need to receive the frame because otherwise emaclite will + * not generate any other interrupts since it cannot receive, + * and we do not actively poll the emaclite + */ + XEmacLite_Recv(instance, xemac_tx_frame); +#if !NO_SYS + xInsideISR--; +#endif + return; + } + + /* receive the packet */ + len = XEmacLite_Recv(instance, p->payload); + + if (len == 0) { +#if LINK_STATS + lwip_stats.link.drop++; +#endif + pbuf_free(p); +#if !NO_SYS + xInsideISR--; +#endif + return; + } + + /* store it in the receive queue, where it'll be processed by xemacif input thread */ + if (pq_enqueue(xemacliteif->recv_q, (void*)p) < 0) { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + pbuf_free(p); +#if !NO_SYS + xInsideISR--; +#endif + return; + } + +#if !NO_SYS + sys_sem_signal(&xemac->sem_rx_data_available); + xInsideISR--; +#endif +} + +int transmit_packet(XEmacLite *instancep, void *packet, unsigned len) +{ + XStatus result = 0; + + /* there is space for a buffer, so transfer */ + result = XEmacLite_Send(instancep, packet, len); + + if (result != XST_SUCCESS) { + return -1; + } + + return 0; +} + +/* + * this function is always called with interrupts off + * this function also assumes that there is space to send in the Emaclite buffer + */ +static err_t +_unbuffered_low_level_output(XEmacLite *instancep, struct pbuf *p) +{ + struct pbuf *q; + int total_len = 0; + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + for(q = p, total_len = 0; q != NULL; q = q->next) { + /* 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. */ + memcpy(xemac_tx_frame + total_len, q->payload, q->len); + total_len += q->len; + } + + if (transmit_packet(instancep, xemac_tx_frame, total_len) < 0) { +#if LINK_STATS + lwip_stats.link.drop++; +#endif + } + +#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_OK; +} + +/* + * 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) +{ + SYS_ARCH_DECL_PROTECT(lev); + struct xemac_s *xemac = (struct xemac_s *)(netif->state); + xemacliteif_s *xemacliteif = (xemacliteif_s *)(xemac->state); + XEmacLite *instance = xemacliteif->instance; + struct pbuf *q; + + SYS_ARCH_PROTECT(lev); + + /* check if space is available to send */ + if (XEmacLite_TxBufferAvailable(instance) == TRUE) { + if (pq_qlength(xemacliteif->send_q)) { /* send backlog */ + _unbuffered_low_level_output(instance, (struct pbuf *)pq_dequeue(xemacliteif->send_q)); + } else { /* send current */ + _unbuffered_low_level_output(instance, p); + SYS_ARCH_UNPROTECT(lev); + return ERR_OK; + } + } + + /* if we cannot send the packet immediately, then make a copy of the whole packet + * into a separate pbuf and store it in send_q. We cannot enqueue the pbuf as is + * since parts of the pbuf may be modified inside lwIP. + */ + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_POOL); + if (!q) { +#if LINK_STATS + lwip_stats.link.drop++; +#endif + SYS_ARCH_UNPROTECT(lev); + return ERR_MEM; + } + + for (q->len = 0; p; p = p->next) { + memcpy(q->payload + q->len, p->payload, p->len); + q->len += p->len; + } + if (pq_enqueue(xemacliteif->send_q, (void *)q) < 0) { +#if LINK_STATS + lwip_stats.link.drop++; +#endif + SYS_ARCH_UNPROTECT(lev); + return ERR_MEM; + } + + SYS_ARCH_UNPROTECT(lev); + + return ERR_OK; +} + +static void +xemacif_send_handler(void *arg) { + struct xemac_s *xemac = (struct xemac_s *)(arg); + xemacliteif_s *xemacliteif = (xemacliteif_s *)(xemac->state); + XEmacLite *instance = xemacliteif->instance; + struct xtopology_t *xtopologyp = &xtopology[xemac->topology_index]; + +#if !NO_SYS + xInsideISR++; +#endif +#if XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ == 1 +#else +#ifndef SDT + XIntc_AckIntr(xtopologyp->intc_baseaddr, 1 << xtopologyp->intc_emac_intr); +#endif +#endif + + if (pq_qlength(xemacliteif->send_q) && (XEmacLite_TxBufferAvailable(instance) == TRUE)) { + struct pbuf *p = pq_dequeue(xemacliteif->send_q); + _unbuffered_low_level_output(instance, p); + pbuf_free(p); + } +#if !NO_SYS + xInsideISR--; +#endif +} + +/* + * 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); + xemacliteif_s *xemacliteif = (xemacliteif_s *)(xemac->state); + + /* see if there is data to process */ + if (pq_qlength(xemacliteif->recv_q) == 0) + return NULL; + + /* return one packet from receive q */ + return (struct pbuf *)pq_dequeue(xemacliteif->recv_q); +} + +/* + * xemacliteif_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. + * + */ + +err_t +xemacliteif_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); +} + +/* + * xemacliteif_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) + * + */ +int +xemacliteif_input(struct netif *netif) +{ + struct eth_hdr *ethhdr; + struct pbuf *p; + SYS_ARCH_DECL_PROTECT(lev); + +#if !NO_SYS + while (1) +#endif + { + SYS_ARCH_PROTECT(lev); + /* move received packet into a new pbuf */ + 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 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, ("xlltemacif_input: IP input error\r\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } + } + + return 1; +} + +#if !NO_SYS +static void +arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} +#endif + +static XEmacLite_Config * +xemaclite_lookup_config(unsigned base) +{ + XEmacLite_Config *CfgPtr = NULL; + int i; + + for (i = 0; i < XPAR_XEMACLITE_NUM_INSTANCES; i++) + if (XEmacLite_ConfigTable[i].BaseAddress == base) { + CfgPtr = &XEmacLite_ConfigTable[i]; + break; + } + + return CfgPtr; +} + +static err_t low_level_init(struct netif *netif) +{ + struct xemac_s *xemac; + XEmacLite_Config *config; + XEmacLite *xemaclitep; + struct xtopology_t *xtopologyp; + xemacliteif_s *xemacliteif; + unsigned link_speed = 1000; + + xemaclitep = mem_malloc(sizeof *xemaclitep); +#ifndef XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ +#if XPAR_INTC_0_HAS_FAST == 1 + xemaclitep_fast = xemaclitep; +#endif +#endif + if (xemaclitep == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("xemacliteif_init: out of memory\r\n")); + return ERR_MEM; + } + + xemac = mem_malloc(sizeof *xemac); + if (xemac == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("xemacliteif_init: out of memory\r\n")); + return ERR_MEM; + } + + xemacliteif = mem_malloc(sizeof *xemacliteif); + if (xemacliteif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("xemacliteif_init: out of memory\r\n")); + return ERR_MEM; + } + + /* obtain pointer to topology structure for this emac */ + xemac->topology_index = xtopology_find_index((unsigned)(netif->state)); + xtopologyp = &xtopology[xemac->topology_index]; + + /* obtain config of this emaclite */ + config = xemaclite_lookup_config((unsigned)(netif->state)); + + /* maximum transfer unit */ + netif->mtu = XEL_MTU_SIZE; + + /* broadcast capability */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* initialize the mac */ +#ifndef SDT + XEmacLite_Initialize(xemaclitep, config->DeviceId); +#else + XEmacLite_Initialize(xemaclitep, config->BaseAddress); +#endif + xemaclitep->NextRxBufferToUse = 0; +#ifndef SDT +#if XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ == 1 + XScuGic_RegisterHandler(xtopologyp->scugic_baseaddr, + xtopologyp->intc_emac_intr, + (Xil_ExceptionHandler)XEmacLite_InterruptHandler, + xemaclitep); + + XScuGic_SetPriTrigTypeByDistAddr(INTC_DIST_BASE_ADDR, + xtopologyp->intc_emac_intr, + EMACLITE_INTR_PRIORITY_SET_IN_GIC, + TRIG_TYPE_RISING_EDGE_SENSITIVE); + + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, + xtopologyp->intc_emac_intr); +#else +#if NO_SYS +#if XPAR_INTC_0_HAS_FAST == 1 + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xtopologyp->intc_emac_intr, + (XFastInterruptHandler)XEmacLite_FastInterruptHandler); +#else + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xtopologyp->intc_emac_intr, + (XInterruptHandler)XEmacLite_InterruptHandler, + xemaclitep); +#endif +#else +#if XPAR_INTC_0_HAS_FAST == 1 + XIntc_RegisterFastHandler(xtopologyp->intc_baseaddr, + xtopologyp->intc_emac_intr, + (XFastInterruptHandler)XEmacLite_FastInterruptHandler); + + XIntc_EnableIntr(xtopologyp->intc_baseaddr, XIntc_In32(xtopologyp->intc_baseaddr + + XIN_IER_OFFSET) | (1 << xtopologyp->intc_emac_intr)); +#else + + XIntc_RegisterHandler(xtopologyp->intc_baseaddr, + xtopologyp->intc_emac_intr, + (XInterruptHandler)XEmacLite_InterruptHandler, + xemaclitep); + XIntc_EnableIntr(xtopologyp->intc_baseaddr, XIntc_In32(xtopologyp->intc_baseaddr + + XIN_IER_OFFSET) | (1 << xtopologyp->intc_emac_intr)); +#endif +#endif +#endif +#else + XSetupInterruptSystem(xemaclitep, &XEmacLite_InterruptHandler, + config->IntrId, + config->IntrParent, + XINTERRUPT_DEFAULT_PRIORITY); +#endif + + /* set mac address */ + XEmacLite_SetMacAddress(xemaclitep, (unsigned char*)(netif->hwaddr)); + + /* flush any frames already received */ + XEmacLite_FlushReceive(xemaclitep); + + /* set Rx, Tx interrupt handlers */ + XEmacLite_SetRecvHandler(xemaclitep, (void *)(xemac), xemacif_recv_handler); + XEmacLite_SetSendHandler(xemaclitep, (void *)(xemac), xemacif_send_handler); + + /* enable Rx, Tx interrupts */ + XEmacLite_EnableInterrupts(xemaclitep); + +#if !NO_SYS + sys_sem_new(&xemac->sem_rx_data_available, 0); +#endif + + /* replace the state in netif (currently the base address of emaclite) + * with the xemacliteif instance pointer. + * this contains a pointer to the config table entry + */ + xemac->type = xemac_type_xps_emaclite; + xemac->state = (void *)xemacliteif; + netif->state = (void *)xemac; + + xemacliteif->instance = xemaclitep; + xemacliteif->recv_q = pq_create_queue(); + if (!xemacliteif->recv_q) + return ERR_MEM; + + xemacliteif->send_q = pq_create_queue(); + if (!xemacliteif->send_q) + return ERR_MEM; + + /* Initialize PHY */ + + +/* set PHY <--> MAC data clock */ +#ifdef CONFIG_LINKSPEED_AUTODETECT + link_speed = get_IEEE_phy_speed_emaclite(xemaclitep); + xil_printf("auto-negotiated link speed: %d\r\n", link_speed); +#elif defined(CONFIG_LINKSPEED1000) + xil_printf("Link speed of 1000 Mbps not possible\r\n"); +#elif defined(CONFIG_LINKSPEED100) + link_speed = 100; + configure_IEEE_phy_speed_emaclite(xemaclitep, link_speed); + xil_printf("link speed: %d\r\n", link_speed); +#elif defined(CONFIG_LINKSPEED10) + link_speed = 10; + configure_IEEE_phy_speed_emaclite(xemaclitep, link_speed); + xil_printf("link speed: %d\r\n", link_speed); +#endif + + return ERR_OK; +} + + +static int detect_phy_emaclite(XEmacLite *xemaclitep) +{ + u16 phy_reg; + u32 phy_addr; + + for (phy_addr = 31; phy_addr > 0; phy_addr--) { + XEmacLite_PhyRead(xemaclitep, 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, ("XEMacLite detect_phy: PHY detected at address %d.\r\n", phy_addr)); + LWIP_DEBUGF(NETIF_DEBUG, ("XEMacLite detect_phy: PHY detected.\r\n")); + return phy_addr; + } + } + + LWIP_DEBUGF(NETIF_DEBUG, ("XEMacLite detect_phy: No PHY detected. Assuming a PHY at address 0\r\n")); + + /* default to zero */ + return 0; +} + +unsigned get_IEEE_phy_speed_emaclite(XEmacLite *xemaclitep) +{ + u16 control; + u16 status; + u16 partner_capabilities; + u16 partner_capabilities_1000; + u16 phylinkspeed; + u32 phy_addr = detect_phy_emaclite(xemaclitep); + + /* Don't advertise PHY speed of 1000 Mbps */ + XEmacLite_PhyWrite(xemaclitep, phy_addr, + IEEE_1000_ADVERTISE_REG_OFFSET, + 0); + /* Advertise PHY speed of 100 and 10 Mbps */ + XEmacLite_PhyWrite(xemaclitep, phy_addr, + IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_100_AND_10); + + XEmacLite_PhyRead(xemaclitep, phy_addr, + IEEE_CONTROL_REG_OFFSET, + &control); + control |= (IEEE_CTRL_AUTONEGOTIATE_ENABLE | + IEEE_STAT_AUTONEGOTIATE_RESTART); + + XEmacLite_PhyWrite(xemaclitep, phy_addr, + IEEE_CONTROL_REG_OFFSET, + control); + + /* Read PHY control and status registers is successful. */ + XEmacLite_PhyRead(xemaclitep, phy_addr, + IEEE_CONTROL_REG_OFFSET, + &control); + XEmacLite_PhyRead(xemaclitep, phy_addr, + IEEE_STATUS_REG_OFFSET, + &status); + + if ((control & IEEE_CTRL_AUTONEGOTIATE_ENABLE) && + (status & IEEE_STAT_AUTONEGOTIATE_CAPABLE)) { + + while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) { + XEmacLite_PhyRead(xemaclitep, phy_addr, + IEEE_STATUS_REG_OFFSET, + &status); + } + + XEmacLite_PhyRead(xemaclitep, phy_addr, + IEEE_PARTNER_ABILITIES_1_REG_OFFSET, + &partner_capabilities); + + if (status & IEEE_STAT_1GBPS_EXTENSIONS) { + XEmacLite_PhyRead(xemaclitep, phy_addr, + IEEE_PARTNER_ABILITIES_3_REG_OFFSET, + &partner_capabilities_1000); + if (partner_capabilities_1000 & IEEE_AN3_ABILITY_MASK_1GBPS) return 1000; + } + + if (partner_capabilities & IEEE_AN1_ABILITY_MASK_100MBPS) return 100; + if (partner_capabilities & IEEE_AN1_ABILITY_MASK_10MBPS) return 10; + + xil_printf("%s: unknown PHY link speed, setting TEMAC speed to be 10 Mbps\r\n", + __FUNCTION__); + return 10; + + + } else { + + /* Update TEMAC speed accordingly */ + if (status & IEEE_STAT_1GBPS_EXTENSIONS) { + + /* Get commanded link speed */ + phylinkspeed = control & IEEE_CTRL_1GBPS_LINKSPEED_MASK; + + switch (phylinkspeed) { + case (IEEE_CTRL_LINKSPEED_1000M): + return 1000; + case (IEEE_CTRL_LINKSPEED_100M): + return 100; + case (IEEE_CTRL_LINKSPEED_10M): + return 10; + default: + xil_printf("%s: unknown PHY link speed (%d), setting TEMAC speed to be 10 Mbps\r\n", + __FUNCTION__, phylinkspeed); + return 10; + } + + } else { + + return (control & IEEE_CTRL_LINKSPEED_MASK) ? 100 : 10; + + } + + } +} + +unsigned configure_IEEE_phy_speed_emaclite(XEmacLite *xemaclitep, unsigned speed) +{ + u16 control; + u32 phy_addr = detect_phy_emaclite(xemaclitep); + + XEmacLite_PhyRead(xemaclitep, phy_addr, + IEEE_CONTROL_REG_OFFSET, + &control); + control &= ~IEEE_CTRL_LINKSPEED_100M; + control &= ~IEEE_CTRL_LINKSPEED_10M; + if (speed == 100) { + control |= IEEE_CTRL_LINKSPEED_100M; + /* Don't advertise PHY speed of 1000 Mbps */ + XEmacLite_PhyWrite(xemaclitep, phy_addr, + IEEE_1000_ADVERTISE_REG_OFFSET, + 0); + /* Don't advertise PHY speed of 10 Mbps */ + XEmacLite_PhyWrite(xemaclitep, phy_addr, + IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_100); + + } + else if (speed == 10) { + control |= IEEE_CTRL_LINKSPEED_10M; + /* Don't advertise PHY speed of 1000 Mbps */ + XEmacLite_PhyWrite(xemaclitep, phy_addr, + IEEE_1000_ADVERTISE_REG_OFFSET, + 0); + /* Don't advertise PHY speed of 100 Mbps */ + XEmacLite_PhyWrite(xemaclitep, phy_addr, + IEEE_AUTONEGO_ADVERTISE_REG, + ADVERTISE_10); + } + + XEmacLite_PhyWrite(xemaclitep, phy_addr, + IEEE_CONTROL_REG_OFFSET, + control | IEEE_CTRL_RESET_MASK); + { + volatile int wait; + for (wait=0; wait < 100000; wait++); + for (wait=0; wait < 100000; wait++); + } + return 0; +} + +/* + * xemacliteif_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 +xemacliteif_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 = xemacliteif_output; + netif->linkoutput = low_level_output; + + low_level_init(netif); + +#if !NO_SYS + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +#endif + + return ERR_OK; +} + +#ifndef XLWIP_CONFIG_INCLUDE_EMACLITE_ON_ZYNQ +#if XPAR_INTC_0_HAS_FAST == 1 +/****************** Fast Interrupt Handler **********************************/ + +void XEmacLite_FastInterruptHandler (void) +{ + XEmacLite_InterruptHandler((void *)xemaclitep_fast); +} +#endif +#endif diff --git a/bsp_z7/lwip/netif/xemacpsif.c b/bsp_z7/lwip/netif/xemacpsif.c new file mode 100644 index 0000000..e9abf70 --- /dev/null +++ b/bsp_z7/lwip/netif/xemacpsif.c @@ -0,0 +1,796 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 +#include + +#include +#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 = xemacps_is_tx_space_available(xemacpsif); + if (freecnt <= 5) { + txring = &(XEmacPs_GetTxRing(&xemacpsif->emacps)); + xemacps_process_sent_bds(xemacpsif, txring); + } + + if (xemacps_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 + xil_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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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) { + LWIP_DEBUGF(NETIF_DEBUG, ("In %s:Timer creation failed....\r\n", __func__)); + } else { + if(xTimerStart(xemac->xTimer, 0) != pdPASS) { + LWIP_DEBUGF(NETIF_DEBUG, ("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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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/bsp_z7/lwip/netif/xemacpsif_dma.c b/bsp_z7/lwip/netif/xemacpsif_dma.c new file mode 100644 index 0000000..6bde0d1 --- /dev/null +++ b/bsp_z7/lwip/netif/xemacpsif_dma.c @@ -0,0 +1,951 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 + +#ifdef SDT +#include "xinterrupt_wrap.h" +#else +#define INTC_BASE_ADDR XPAR_SCUGIC_0_CPU_BASEADDR +#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_0_DIST_BASEADDR +#endif + +/* 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 emac_bd_space[0x200000] __attribute__ ((aligned (0x200000))); +#else +u8_t emac_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 +extern u32 xInsideISR; +#endif + +#define XEMACPS_BD_TO_INDEX(ringptr, bdptr) \ + (((UINTPTR)bdptr - (UINTPTR)(ringptr)->BaseBdAddr) / (ringptr)->Separation) + + +s32_t xemacps_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 xemacps_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 */ + xemacps_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 + xil_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)emac_bd_space, STRONG_ORDERD_SHARED | PRIV_RW_USER_RW); // addr, attr +#else +#if defined __aarch64__ + Xil_SetTlbAttributes((u64)emac_bd_space, NORM_NONCACHE | INNER_SHAREABLE); +#else + Xil_SetTlbAttributes((s32_t)emac_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)&(emac_bd_space[bd_space_index]); + xemacpsif->rx_bdspace = (void *)tempaddress; + bd_space_index += 0x10000; + tempaddress = (UINTPTR)&(emac_bd_space[bd_space_index]); + xemacpsif->tx_bdspace = (void *)tempaddress; + bd_space_index += 0x10000; + if (gigeversion > 2) { + tempaddress = (UINTPTR)&(emac_bd_space[bd_space_index]); + bdrxterminate = (XEmacPs_Bd *)tempaddress; + bd_space_index += 0x10000; + tempaddress = (UINTPTR)&(emac_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 + xil_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 +#ifdef SDT + xPortInstallInterruptHandler(xemacpsif->emacps.Config.IntrId, + ( Xil_InterruptHandler ) XEmacPs_IntrHandler, + (void *)&xemacpsif->emacps); +#else + xPortInstallInterruptHandler(xtopologyp->scugic_emac_intr, + ( Xil_InterruptHandler ) XEmacPs_IntrHandler, + (void *)&xemacpsif->emacps); + +#endif +#else +#ifndef SDT + /* + * 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 +#endif + /* + * Enable the interrupt for emacps. + */ +#ifdef SDT + XSetupInterruptSystem(&xemacpsif->emacps, &XEmacPs_IntrHandler, + xemacpsif->emacps.Config.IntrId, xemacpsif->emacps.Config.IntrParent, + XINTERRUPT_DEFAULT_PRIORITY); +#else + XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, (u32) xtopologyp->scugic_emac_intr); + emac_intr_num = (u32) xtopologyp->scugic_emac_intr; +#endif + 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); +} + +#ifndef SDT +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); +} +#endif diff --git a/bsp_z7/lwip/netif/xemacpsif_hw.c b/bsp_z7/lwip/netif/xemacpsif_hw.c new file mode 100644 index 0000000..4bb30a1 --- /dev/null +++ b/bsp_z7/lwip/netif/xemacpsif_hw.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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" + + +u32_t link_speed = 100; +extern XEmacPs_Config XEmacPs_ConfigTable[]; +extern u32_t phymapemac0[32]; +extern u32_t phymapemac1[32]; +extern u32_t phyaddrforemac; + +#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 + +#ifdef SGMII_FIXED_LINK + XEmacPs_SetOptions(xemacpsp, XEMACPS_SGMII_ENABLE_OPTION); + status = XEmacPs_ReadReg(xemacpsp->Config.BaseAddress, XEMACPS_PCS_CONTROL_OFFSET); + status &= ~XEMACPS_PCS_CON_AUTO_NEG_MASK; + XEmacPs_WriteReg(xemacps->emacps.Config.BaseAddress, XEMACPS_PCS_CONTROL_OFFSET, status); +#endif + + /* set mac address */ + status = XEmacPs_SetMacAddress(xemacpsp, (void*)(netif->hwaddr), 1); + if (status != XST_SUCCESS) { + LWIP_DEBUGF(NETIF_DEBUG, ("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. + */ +#ifndef SGMII_FIXED_LINK + 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); + } +#else + link_speed = pcs_setup_emacps(xemacpsp); +#endif + + if (link_speed == XST_FAILURE) { + xemacps->eth_link_status = ETH_LINK_DOWN; + xil_printf("Phy setup failure %s \n\r",__func__); + return; + } else { + xemacps->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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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")); + xemacps_process_sent_bds(xemacpsif, txring); + } + break; + } + } +#if !NO_SYS + xInsideISR--; +#endif +} diff --git a/bsp_z7/lwip/netif/xemacpsif_hw.h b/bsp_z7/lwip/netif/xemacpsif_hw.h new file mode 100644 index 0000000..cd7f3f7 --- /dev/null +++ b/bsp_z7/lwip/netif/xemacpsif_hw.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 __XEMACPSIF_HW_H_ +#define __XEMACPSIF_HW_H_ + +#include "netif/xemacpsif.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +XEmacPs_Config * lookup_config(unsigned mac_base); + +void init_emacps(xemacpsif_s *xemacpsif, struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp_z7/lwip/netif/xemacpsif_physpeed.c b/bsp_z7/lwip/netif/xemacpsif_physpeed.c new file mode 100644 index 0000000..ef6fe28 --- /dev/null +++ b/bsp_z7/lwip/netif/xemacpsif_physpeed.c @@ -0,0 +1,1251 @@ +/* + * Copyright (C) 2010 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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_ADI_IDENTIFIER 0x0283 +#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 + +#define IEEE_CTRL_ISOLATE_DISABLE 0xFBFF + +u32_t phymapemac0[32]; +u32_t phymapemac1[32]; + +#if 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 + +static u32_t get_Xilinx_pcs_pma_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr) +{ + +#if XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1 || \ + XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 || defined(SDT) + u16_t temp; +#endif + + u16_t control; + u16_t status; + + 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"); +#ifndef SDT +#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 + if (strcmp(xemacpsp->Config.PhyType, "1000base-x") == 0) { + 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; + } + } + if (strcmp(xemacpsp->Config.PhyType, "sgmii") == 0) { + 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 + return 0; +} + +void detect_phy(XEmacPs *xemacpsp) +{ + u16_t phy_reg; + u32_t phy_addr; + u32_t emacnum; + u16_t phy_id; + + 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); + XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG, + &phy_id); + + if (((phy_reg != 0xFFFF) && + ((phy_reg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) || + (phy_id == PHY_XILINX_PCS_PMA_ID1)) { + /* 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) && + (phy_reg != PHY_ADI_IDENTIFIER)) { + xil_printf("WARNING: Not a Marvell or TI or Realtek or Xilinx PCS PMA Ethernet PHY or ADI Ethernet PHY. Please verify the initialization sequence\r\n"); + } + } + } +} + +#ifdef SGMII_FIXED_LINK +u32_t pcs_setup_emacps (XEmacPs *xemacps) +{ + u32_t link_speed; + + SetUpSLCRDivisors(xemacps->Config.BaseAddress,1000); + link_speed = 1000; + sleep(1); + return link_speed; +} +#endif + +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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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 == 5) { + 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 == 5) { + 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 == 5) { + 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; +} + +#define ADIN1300_PHY_CTRL1 0x0012 +#define ADIN1300_PHY_CTRL2 0x0016 +#define ADIN1300_PHY_CTRL3 0x0017 +#define ADIN1300_EXT_ADDR 0x0010 +#define ADIN1300_EXT_DATA 0x0011 +#define ADIN1300_PHY_STS1 0x001A + +#define ADIN1300_RGMII_CFG 0xFF23 +#define ADIN1300_RMII_CFG 0xFF24 + +#define ADIN1300_AUTO_MDI_EN 0x400 +#define ADIN1300_MAN_MDIX_EN 0x200 +#define ADIN1300_DIAG_CLK_EN 0x4 + +#define ADIN1300_LINKING_EN 0x2000 + +#define ADIN1300_RGMII_EN 0x0001 +#define ADIN1300_RGMII_TXRX_TUNING_EN 0x0006 +#define ADIN1300_RGMII_RX_DELAY_MASK 0x01C0 +#define ADIN1300_RGMII_TX_DELAY_MASK 0x0038 +#define ADIN1300_RGMII_RX_DELAY_VAL_2000PS 0x0 +#define ADIN1300_RGMII_TX_DELAY_VAL_2000PS 0x0 + +#define ADIN1300_SPEED_RETRY_MASK 0x1C00 +#define ADIN1300_SPEED_RETRY_FOUR 0x1000 +#define ADIN1300_DOWNSPEED_EN 0x0C00 + +#define ADIN1300_AN_DONE 0x1000 +#define ADIN1300_SPEED_MASK 0x0380 +#define ADIN1300_SPEED_1G 0x0280 +#define ADIN1300_SPEED_100M 0x0180 +#define ADIN1300_SPEED_10M 0x0080 + +static u32_t get_Adi_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr) +{ + u16_t temp; + u16_t control; + u16_t status; + u16_t status_speed; + u16_t phyreg; + u32_t timeout_counter = 0; + u32_t temp_speed; + + xil_printf("Start PHY autonegotiation \r\n"); + + /* PHY soft reset */ + 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); + + /* Delay for PHY to be accessible */ + sleep(1); + + /* RGMII TX/RX tuning */ + XEmacPs_PhyWrite(xemacpsp, phy_addr, ADIN1300_EXT_ADDR, ADIN1300_RGMII_CFG); + XEmacPs_PhyRead(xemacpsp, phy_addr, ADIN1300_EXT_DATA, &phyreg); + phyreg |= (ADIN1300_RGMII_EN | ADIN1300_RGMII_TXRX_TUNING_EN); + phyreg &= ~(ADIN1300_RGMII_RX_DELAY_MASK | ADIN1300_RGMII_TX_DELAY_MASK); + phyreg |= ((ADIN1300_RGMII_RX_DELAY_VAL_2000PS << 6) | (ADIN1300_RGMII_TX_DELAY_VAL_2000PS << 3)); + XEmacPs_PhyWrite(xemacpsp, phy_addr, ADIN1300_EXT_ADDR, ADIN1300_RGMII_CFG); + XEmacPs_PhyWrite(xemacpsp, phy_addr, ADIN1300_EXT_DATA, phyreg); + + /* Downspeed */ + XEmacPs_PhyRead(xemacpsp, phy_addr, ADIN1300_PHY_CTRL3, &phyreg); + phyreg &= ~(ADIN1300_SPEED_RETRY_MASK); + phyreg |= ADIN1300_SPEED_RETRY_FOUR; + XEmacPs_PhyWrite(xemacpsp, phy_addr, ADIN1300_PHY_CTRL3, phyreg); + XEmacPs_PhyRead(xemacpsp, phy_addr, ADIN1300_PHY_CTRL2, &phyreg); + phyreg |= ADIN1300_DOWNSPEED_EN; + XEmacPs_PhyWrite(xemacpsp, phy_addr, ADIN1300_PHY_CTRL2, phyreg); + + /* Diag clock disable */ + XEmacPs_PhyRead(xemacpsp, phy_addr, ADIN1300_PHY_CTRL1, &phyreg); + phyreg &= ~ADIN1300_DIAG_CLK_EN; + XEmacPs_PhyWrite(xemacpsp, phy_addr, ADIN1300_PHY_CTRL1, phyreg); + /* Linking Enable */ + XEmacPs_PhyRead(xemacpsp, phy_addr, ADIN1300_PHY_CTRL3, &phyreg); + phyreg |= ADIN1300_LINKING_EN; + XEmacPs_PhyWrite(xemacpsp, phy_addr, ADIN1300_PHY_CTRL3, phyreg); + /* Auto MDIX by default */ + XEmacPs_PhyRead(xemacpsp, phy_addr, ADIN1300_PHY_CTRL1, &phyreg); + phyreg &= ~ADIN1300_MAN_MDIX_EN; + phyreg |= ADIN1300_AUTO_MDI_EN; + XEmacPs_PhyWrite(xemacpsp, phy_addr, ADIN1300_PHY_CTRL1, phyreg); + + 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); + + 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,ADIN1300_PHY_STS1, + &status_speed); + if (status_speed & ADIN1300_AN_DONE) { + temp_speed = status_speed & ADIN1300_SPEED_MASK; + + if (temp_speed == ADIN1300_SPEED_1G) + return 1000; + else if(temp_speed == ADIN1300_SPEED_100M) + return 100; + else + return 10; + } + + return XST_SUCCESS; +} + +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 if (phy_identity == PHY_XILINX_PCS_PMA_ID1) { + RetStatus = get_Xilinx_pcs_pma_phy_speed(xemacpsp, phy_addr); + } else if (phy_identity == PHY_ADI_IDENTIFIER) { + RetStatus = get_Adi_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 + +static void SetUpSLCRDivisors(UINTPTR mac_baseaddr, s32_t speed) +{ +#ifndef SDT + 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 + } else if (mac_baseaddr == VERSAL_NET_EMACPS_0_BASEADDR) { +#ifdef VERSAL_NET_CRL_GEM0_REF_CTRL + CrlApbBaseAddr = VERSAL_NET_CRL_GEM0_REF_CTRL; +#endif +#if defined (__aarch64__) && (EL1_NONSECURE == 1) + ClkId = CLK_GEM0_REF; +#endif + } else if (mac_baseaddr == VERSAL_NET_EMACPS_1_BASEADDR) { +#ifdef VERSAL_NET_CRL_GEM1_REF_CTRL + CrlApbBaseAddr = VERSAL_NET_CRL_GEM1_REF_CTRL; +#endif +#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 (mac_baseaddr == VERSAL_NET_EMACPS_0_BASEADDR) { +#ifdef XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0; +#endif + } else if (mac_baseaddr == VERSAL_NET_EMACPS_1_BASEADDR) { +#ifdef XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_1_ENET_SLCR_1000MBPS_DIV0 + CrlApbDiv0 = XPAR_PSX_WIZARD_0_PSXL_0_PSX_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_NET_EMACPS_0_BASEADDR) { +#ifdef XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_0_ENET_SLCR_100MBPS_DIV0; +#endif + } else if (mac_baseaddr == VERSAL_NET_EMACPS_1_BASEADDR) { +#ifdef XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_1_ENET_SLCR_100MBPS_DIV0 + CrlApbDiv0 = XPAR_PSX_WIZARD_0_PSXL_0_PSX_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 + } else if (mac_baseaddr == VERSAL_NET_EMACPS_0_BASEADDR) { +#ifdef XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_0_ENET_SLCR_10MBPS_DIV0; +#endif + } else if (mac_baseaddr == VERSAL_NET_EMACPS_1_BASEADDR) { +#ifdef XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_1_ENET_SLCR_10MBPS_DIV0 + CrlApbDiv0 = XPAR_PSX_WIZARD_0_PSXL_0_PSX_ETHERNET_1_ENET_SLCR_10MBPS_DIV0; +#endif + } + } + + if (CrlApbDiv0 != 0) { + if ((mac_baseaddr == VERSAL_EMACPS_0_BASEADDR) || + (mac_baseaddr == VERSAL_EMACPS_1_BASEADDR)) { +#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 if((mac_baseaddr == VERSAL_NET_EMACPS_0_BASEADDR) || + (mac_baseaddr == VERSAL_NET_EMACPS_1_BASEADDR)) { +#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_NET_CRL_GEM_DIV_MASK; + CrlApbGemCtrl |= CrlApbDiv0 << VERSAL_NET_CRL_APB_GEM_DIV_SHIFT; + + Xil_Out32(CrlApbBaseAddr, CrlApbGemCtrl); +#endif + } + } else { + xil_printf("Clock Divisors incorrect - Please check\r\n"); + } + } +#else + xil_printf("Using default Speed from design\r\n"); +#endif + return; +} diff --git a/bsp_z7/lwip/netif/xpqueue.c b/bsp_z7/lwip/netif/xpqueue.c new file mode 100644 index 0000000..09b1e13 --- /dev/null +++ b/bsp_z7/lwip/netif/xpqueue.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2024 Advanced Micro Devices, 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 + +#include "netif/xpqueue.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) { + LWIP_DEBUGF(NETIF_DEBUG, ("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; +} diff --git a/bsp_z7/main.c b/bsp_z7/main.c index 39cf8d6..6bca562 100644 --- a/bsp_z7/main.c +++ b/bsp_z7/main.c @@ -56,6 +56,8 @@ void mission(void); void initFreeRTOSHelper(); +void testEth(); + int main(void) { /* Configure the hardware ready to run the demo. */ @@ -63,6 +65,8 @@ int main(void) { // printf("Booting Software\n"); + testEth(); + mission(); } diff --git a/bsp_z7/ps7_cortexa9_0/CMakeLists.txt b/bsp_z7/ps7_cortexa9_0/CMakeLists.txt index 28e307c..f817dec 100644 --- a/bsp_z7/ps7_cortexa9_0/CMakeLists.txt +++ b/bsp_z7/ps7_cortexa9_0/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(libsrc/emacps) add_subdirectory(libsrc/gpiops) add_subdirectory(libsrc/scugic) add_subdirectory(libsrc/scutimer) diff --git a/bsp_z7/ps7_cortexa9_0/include/xparameters.h b/bsp_z7/ps7_cortexa9_0/include/xparameters.h index 485bbc9..f032659 100644 --- a/bsp_z7/ps7_cortexa9_0/include/xparameters.h +++ b/bsp_z7/ps7_cortexa9_0/include/xparameters.h @@ -7,7 +7,7 @@ /* Definition for CPU ID */ #define XPAR_CPU_ID 0U -#define USE_AMP 0U +//#define USE_AMP 0U TODO having this to 0 breaks xil_cache /* Definitions for peripheral PS7_CORTEXA9_0 */ #define XPAR_PS7_CORTEXA9_0_CPU_CLK_FREQ_HZ 666666687 diff --git a/bsp_z7/ps7_cortexa9_0/libsrc/emacps/CMakeLists.txt b/bsp_z7/ps7_cortexa9_0/libsrc/emacps/CMakeLists.txt new file mode 100644 index 0000000..972ee75 --- /dev/null +++ b/bsp_z7/ps7_cortexa9_0/libsrc/emacps/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${TARGET_NAME} PUBLIC + src/xemacps_bdring.c + src/xemacps.c + src/xemacps_control.c + src/xemacps_g.c + src/xemacps_hw.c + src/xemacps_intr.c + src/xemacps_sinit.c +) \ No newline at end of file diff --git a/bsp_z7/testEth.c b/bsp_z7/testEth.c new file mode 100644 index 0000000..3798b4e --- /dev/null +++ b/bsp_z7/testEth.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2017 - 2022 Xilinx, Inc. + * Copyright (C) 2022 - 2023 Advanced Micro Devices, 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. + * + */ + +#include "lwip/inet.h" +#include "lwip/init.h" +#include "lwip/udp.h" +#include "lwip/timeouts.h" +#include "netif/xadapter.h" +#include "xil_printf.h" +#include "xparameters.h" +#include +#include +#include + +#define PLATFORM_EMAC_BASEADDR 0xE000B000 + +#if LWIP_DHCP == 1 +#include "lwip/dhcp.h" +extern volatile int dhcp_timoutcntr; +err_t dhcp_start(struct netif *netif); +#endif + +#ifdef XPS_BOARD_ZCU102 +#if defined(XPAR_XIICPS_0_DEVICE_ID) || defined(XPAR_XIICPS_0_BASEADDR) +int IicPhyReset(void); +#endif +#endif + +void print_app_header(); +void start_application(); + +#define THREAD_STACKSIZE 1024 + +#define DEFAULT_IP_ADDRESS "192.168.1.10" +#define DEFAULT_IP_MASK "255.255.255.0" +#define DEFAULT_GW_ADDRESS "192.168.1.1" + +struct netif server_netif; + +u32_t sys_now(void) { return xTaskGetTickCount() * portTICK_PERIOD_MS; } + +static void print_ip(char *msg, ip_addr_t *ip) { + xil_printf(msg); + xil_printf("%d.%d.%d.%d\n\r", ip4_addr1(ip), ip4_addr2(ip), ip4_addr3(ip), + ip4_addr4(ip)); +} + +static void print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw) { + print_ip("Board IP: ", ip); + print_ip("Netmask : ", mask); + print_ip("Gateway : ", gw); +} + +static void assign_default_ip(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw) { + int err; + + xil_printf("Configuring default IP %s \r\n", DEFAULT_IP_ADDRESS); + + err = inet_aton(DEFAULT_IP_ADDRESS, ip); + if (!err) + xil_printf("Invalid default IP address: %d\r\n", err); + + err = inet_aton(DEFAULT_IP_MASK, mask); + if (!err) + xil_printf("Invalid default IP MASK: %d\r\n", err); + + err = inet_aton(DEFAULT_GW_ADDRESS, gw); + if (!err) + xil_printf("Invalid default gateway address: %d\r\n", err); +} + +void network_thread() { +#if LWIP_DHCP == 1 + int mscnt = 0; +#endif + /* the mac address of the board. this should be unique per board */ + u8_t mac_ethernet_address[] = {0x00, 0x0a, 0x35, 0x00, 0x01, 0x02}; + + /* Add network interface to the netif_list, and set it as default */ + if (!xemac_add(&server_netif, NULL, NULL, NULL, mac_ethernet_address, + PLATFORM_EMAC_BASEADDR)) { + xil_printf("Error adding N/W interface\r\n"); + return; + } + + netif_set_default(&server_netif); + + /* specify that the network if is up */ + netif_set_up(&server_netif); + + // /* start packet receive thread - required for lwIP operation */ + // sys_thread_new("xemacif_input_thread", + // (void(*)(void*))xemacif_input_thread, &server_netif, + // THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); + +#if LWIP_DHCP == 1 + dhcp_start(&server_netif); + while (1) { + vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS); + dhcp_fine_tmr(); + mscnt += DHCP_FINE_TIMER_MSECS; + if (mscnt >= DHCP_COARSE_TIMER_SECS * 1000) { + dhcp_coarse_tmr(); + mscnt = 0; + } + } +#else + // vTaskDelete(NULL); +#endif +} + +int testEth() { + +#if LWIP_DHCP == 1 + int mscnt = 0; +#endif + +#ifdef XPS_BOARD_ZCU102 + IicPhyReset(); +#endif + xil_printf("\n\r\n\r"); + xil_printf("-----lwIP Socket Mode UDP Client Application------\r\n"); + + /* initialize lwIP before calling sys_thread_new */ + lwip_init(); + + /* any thread using lwIP should be created using sys_thread_new */ + network_thread(); + +#if LWIP_DHCP == 1 + while (1) { + vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS); + if (server_netif.ip_addr.addr) { + xil_printf("DHCP request success\r\n"); + break; + } + mscnt += DHCP_FINE_TIMER_MSECS; + if (mscnt >= 10000) { + xil_printf("ERROR: DHCP request timed out\r\n"); + assign_default_ip(&(server_netif.ip_addr), &(server_netif.netmask), + &(server_netif.gw)); + break; + } + } + +#else + assign_default_ip(&(server_netif.ip_addr), &(server_netif.netmask), + &(server_netif.gw)); +#endif + + print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask), + &(server_netif.gw)); + xil_printf("\r\n"); + + /* print all application headers */ + // print_app_header(); + xil_printf("\r\n"); + + + uint8_t data[] = {'1','2','3','4','5'}; + + struct pbuf* tx = pbuf_alloc_reference(data, sizeof(data), PBUF_REF); + + ip_addr_t addr = IPADDR4_INIT_BYTES(192,168,1,5); + + struct udp_pcb *udpecho_raw_pcb = udp_new(); + + xil_printf("pcb: %p\r\n", udpecho_raw_pcb); + + udp_sendto(udpecho_raw_pcb, tx, &addr, 1177); + + while (1) { + // slipif_rxbyte_input() is private, so we use slipif_poll and implement + // sio_tryread() + // sio_tryread() will do a blocking read with a timeout, so we get to check + // the timeouts even if no data is incoming + //slipif_poll(&netif); + sys_check_timeouts(); + } + + pbuf_free(tx); + + /* start the application*/ + // start_application(); + return 0; +} \ No newline at end of file