text: /*
   * Copyright (C) 2010 - 2019 Xilinx, Inc.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice,
   *    this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright notice,
   *    this list of conditions and the following disclaimer in the documentation
   *    and/or other materials provided with the distribution.
   * 3. The name of the author may not be used to endorse or promote products
   *    derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
   * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
   * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
   * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
   * OF SUCH DAMAGE.
   *
   * This file is part of the lwIP TCP/IP stack.
   *
   */
  
  #include "netif/xemacpsif.h"
  #include "lwipopts.h"
  
  #if XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1 || \
  	XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1
  #define PCM_PMA_CORE_PRESENT
  #else
  #undef PCM_PMA_CORE_PRESENT
  #endif
  
  u32_t link_speed = 100;
  extern XEmacPs_Config XEmacPs_ConfigTable[];
  extern u32_t phymapemac0[32];
  extern u32_t phymapemac1[32];
  extern u32_t phyaddrforemac;
  extern enum ethernet_link_status eth_link_status;
  
  #ifdef OS_IS_FREERTOS
  extern long xInsideISR;
  #endif
  
  XEmacPs_Config *xemacps_lookup_config(unsigned mac_base)
  {
  	XEmacPs_Config *cfgptr = NULL;
  	s32_t i;
  
  	for (i = 0; i < XPAR_XEMACPS_NUM_INSTANCES; i++) {
  		if (XEmacPs_ConfigTable[i].BaseAddress == mac_base) {
  			cfgptr = &XEmacPs_ConfigTable[i];
  			break;
  		}
  	}
  
  	return (cfgptr);
  }
  
  void init_emacps(xemacpsif_s *xemacps, struct netif *netif)
  {
  	XEmacPs *xemacpsp;
  	s32_t status = XST_SUCCESS;
  	u32_t i;
  	u32_t phyfoundforemac0 = FALSE;
  	u32_t phyfoundforemac1 = FALSE;
  
  	xemacpsp = &xemacps->emacps;
  
  #ifdef ZYNQMP_USE_JUMBO
  	XEmacPs_SetOptions(xemacpsp, XEMACPS_JUMBO_ENABLE_OPTION);
  #endif
  
  #ifdef LWIP_IGMP
  	XEmacPs_SetOptions(xemacpsp, XEMACPS_MULTICAST_OPTION);
  #endif
  
  	/* set mac address */
  	status = XEmacPs_SetMacAddress(xemacpsp, (void*)(netif->hwaddr), 1);
  	if (status != XST_SUCCESS) {
  		xil_printf("In %s:Emac Mac Address set failed...\r\n",__func__);
  	}
  
  	XEmacPs_SetMdioDivisor(xemacpsp, MDC_DIV_224);
  
  /*  Please refer to file header comments for the file xemacpsif_physpeed.c
   *  to know more about the PHY programming sequence.
   *  For PCS PMA core, phy_setup_emacps is called with the predefined PHY address
   *  exposed through xaparemeters.h
   *  For RGMII case, assuming multiple PHYs can be present on the MDIO bus,
   *  detect_phy is called to get the addresses of the PHY present on
   *  a particular MDIO bus (emac0 or emac1). This address map is populated
   *  in phymapemac0 or phymapemac1.
   *  phy_setup_emacps is then called for each PHY present on the MDIO bus.
   */
  #ifdef PCM_PMA_CORE_PRESENT
  #ifdef  XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT
  	link_speed = phy_setup_emacps(xemacpsp, XPAR_PCSPMA_1000BASEX_PHYADDR);
  #elif XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT
  	link_speed = phy_setup_emacps(xemacpsp, XPAR_PCSPMA_SGMII_PHYADDR);
  #endif
  #else
  	detect_phy(xemacpsp);
  	for (i = 31; i > 0; i--) {
  		if (xemacpsp->Config.BaseAddress == XPAR_XEMACPS_0_BASEADDR) {
  			if (phymapemac0[i] == TRUE) {
  				link_speed = phy_setup_emacps(xemacpsp, i);
  				phyfoundforemac0 = TRUE;
  				phyaddrforemac = i;
  			}
  		} else {
  			if (phymapemac1[i] == TRUE) {
  				link_speed = phy_setup_emacps(xemacpsp, i);
  				phyfoundforemac1 = TRUE;
  				phyaddrforemac = i;
  			}
  		}
  	}
  	/* If no PHY was detected, use broadcast PHY address of 0 */
  	if (xemacpsp->Config.BaseAddress == XPAR_XEMACPS_0_BASEADDR) {
  		if (phyfoundforemac0 == FALSE)
  			link_speed = phy_setup_emacps(xemacpsp, 0);
  	} else {
  		if (phyfoundforemac1 == FALSE)
  			link_speed = phy_setup_emacps(xemacpsp, 0);
  	}
  #endif
  
  	if (link_speed == XST_FAILURE) {
  		eth_link_status = ETH_LINK_DOWN;
  		xil_printf("Phy setup failure %s \n\r",__func__);
  		return;
  	} else {
  		eth_link_status = ETH_LINK_UP;
  	}
  
  	XEmacPs_SetOperatingSpeed(xemacpsp, link_speed);
  	/* Setting the operating speed of the MAC needs a delay. */
  	{
  		volatile s32_t wait;
  		for (wait=0; wait < 20000; wait++);
  	}
  }
  
  void init_emacps_on_error (xemacpsif_s *xemacps, struct netif *netif)
  {
  	XEmacPs *xemacpsp;
  	s32_t status = XST_SUCCESS;
  
  	xemacpsp = &xemacps->emacps;
  
  	/* set mac address */
  	status = XEmacPs_SetMacAddress(xemacpsp, (void*)(netif->hwaddr), 1);
  	if (status != XST_SUCCESS) {
  		xil_printf("In %s:Emac Mac Address set failed...\r\n",__func__);
  	}
  
  	XEmacPs_SetOperatingSpeed(xemacpsp, link_speed);
  
  	/* Setting the operating speed of the MAC needs a delay. */
  	{
  		volatile s32_t wait;
  		for (wait=0; wait < 20000; wait++);
  	}
  }
  
  void setup_isr (struct xemac_s *xemac)
  {
  	xemacpsif_s   *xemacpsif;
  
  	xemacpsif = (xemacpsif_s *)(xemac->state);
  	/*
  	 * Setup callbacks
  	 */
  	XEmacPs_SetHandler(&xemacpsif->emacps, XEMACPS_HANDLER_DMASEND,
  				     (void *) emacps_send_handler,
  				     (void *) xemac);
  
  	XEmacPs_SetHandler(&xemacpsif->emacps, XEMACPS_HANDLER_DMARECV,
  				    (void *) emacps_recv_handler,
  				    (void *) xemac);
  
  	XEmacPs_SetHandler(&xemacpsif->emacps, XEMACPS_HANDLER_ERROR,
  				    (void *) emacps_error_handler,
  				    (void *) xemac);
  }
  
  void start_emacps (xemacpsif_s *xemacps)
  {
  	/* start the temac */
  	XEmacPs_Start(&xemacps->emacps);
  }
  
  void restart_emacps_transmitter (xemacpsif_s *xemacps) {
  	u32_t Reg;
  	Reg = XEmacPs_ReadReg(xemacps->emacps.Config.BaseAddress,
  					XEMACPS_NWCTRL_OFFSET);
  	Reg = Reg & (~XEMACPS_NWCTRL_TXEN_MASK);
  	XEmacPs_WriteReg(xemacps->emacps.Config.BaseAddress,
  										XEMACPS_NWCTRL_OFFSET, Reg);
  
  	Reg = XEmacPs_ReadReg(xemacps->emacps.Config.BaseAddress,
  						XEMACPS_NWCTRL_OFFSET);
  	Reg = Reg | (XEMACPS_NWCTRL_TXEN_MASK);
  	XEmacPs_WriteReg(xemacps->emacps.Config.BaseAddress,
  										XEMACPS_NWCTRL_OFFSET, Reg);
  }
  
  void emacps_error_handler(void *arg,u8 Direction, u32 ErrorWord)
  {
  	struct xemac_s *xemac;
  	xemacpsif_s   *xemacpsif;
  	XEmacPs_BdRing *rxring;
  	XEmacPs_BdRing *txring;
  #ifdef OS_IS_FREERTOS
  	xInsideISR++;
  #endif
  
  	xemac = (struct xemac_s *)(arg);
  	xemacpsif = (xemacpsif_s *)(xemac->state);
  	rxring = &XEmacPs_GetRxRing(&xemacpsif->emacps);
  	txring = &XEmacPs_GetTxRing(&xemacpsif->emacps);
  
  	if (ErrorWord != 0) {
  		switch (Direction) {
  			case XEMACPS_RECV:
  			if (ErrorWord & XEMACPS_RXSR_HRESPNOK_MASK) {
  				LWIP_DEBUGF(NETIF_DEBUG, ("Receive DMA error\r\n"));
  				HandleEmacPsError(xemac);
  			}
  			if (ErrorWord & XEMACPS_RXSR_RXOVR_MASK) {
  				LWIP_DEBUGF(NETIF_DEBUG, ("Receive over run\r\n"));
  				emacps_recv_handler(arg);
  				setup_rx_bds(xemacpsif, rxring);
  			}
  			if (ErrorWord & XEMACPS_RXSR_BUFFNA_MASK) {
  				LWIP_DEBUGF(NETIF_DEBUG, ("Receive buffer not available\r\n"));
  				emacps_recv_handler(arg);
  				setup_rx_bds(xemacpsif, rxring);
  			}
  			break;
  			case XEMACPS_SEND:
  			if (ErrorWord & XEMACPS_TXSR_HRESPNOK_MASK) {
  				LWIP_DEBUGF(NETIF_DEBUG, ("Transmit DMA error\r\n"));
  				HandleEmacPsError(xemac);
  			}
  			if (ErrorWord & XEMACPS_TXSR_URUN_MASK) {
  				LWIP_DEBUGF(NETIF_DEBUG, ("Transmit under run\r\n"));
  				HandleTxErrors(xemac);
  			}
  			if (ErrorWord & XEMACPS_TXSR_BUFEXH_MASK) {
  				LWIP_DEBUGF(NETIF_DEBUG, ("Transmit buffer exhausted\r\n"));
  				HandleTxErrors(xemac);
  			}
  			if (ErrorWord & XEMACPS_TXSR_RXOVR_MASK) {
  				LWIP_DEBUGF(NETIF_DEBUG, ("Transmit retry excessed limits\r\n"));
  				HandleTxErrors(xemac);
  			}
  			if (ErrorWord & XEMACPS_TXSR_FRAMERX_MASK) {
  				LWIP_DEBUGF(NETIF_DEBUG, ("Transmit collision\r\n"));
  				process_sent_bds(xemacpsif, txring);
  			}
  			break;
  		}
  	}
  #ifdef OS_IS_FREERTOS
  	xInsideISR--;
  #endif
  }
  