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