/******************************************************************************* * Copyright 2020 Microchip Corporation. * * SPDX-License-Identifier: MIT * * PolarFire SoC 10/100/1000 Mbps Ethernet MAC bare metal software driver implementation. * */ #include #include #include #include #include "mpfs_hal/mss_hal.h" #if defined(MSS_MAC_PHY_HW_RESET) || defined(MSS_MAC_PHY_HW_SRESET) #include "drivers/mss_gpio/mss_gpio.h" #endif #include "drivers/mss_ethernet_mac/mss_ethernet_registers.h" #include "drivers/mss_ethernet_mac/mss_ethernet_mac_regs.h" #include "drivers/mss_ethernet_mac/mss_ethernet_mac_sw_cfg.h" #if defined(USING_FREERTOS) #include "FreeRTOS.h" #endif #include "drivers/mss_ethernet_mac/mss_ethernet_mac.h" #include "drivers/mss_ethernet_mac/phy.h" #include "hal/hal.h" #ifdef __cplusplus extern "C" { #endif #if (defined(MSS_MAC_VSC8662_NWC_25) || defined(MSS_MAC_VSC8662_NWC_125)) extern PLL_TypeDef *MSS_SCB_MSS_PLL; extern PLL_TypeDef *MSS_SCB_DDR_PLL; extern PLL_TypeDef *MSS_SCB_SGMII_PLL; extern IOSCB_CFM_MSS *MSS_SCB_CFM_MSS_MUX; extern IOSCB_CFM_SGMII *MSS_SCB_CFM_SGMII_MUX; extern IOSCB_IO_CALIB_STRUCT *IOSCB_IO_CALIB_SGMII; extern IOSCB_IO_CALIB_STRUCT *IOSCB_IO_CALIB_DDR; #endif /**************************************************************************/ /* Preprocessor Macros */ /**************************************************************************/ #define NULL_POINTER (void *)0U #if !defined(NDEBUG) #define IS_STATE(x) ( ((x) == MSS_MAC_ENABLE) || ((x) == MSS_MAC_DISABLE) ) #endif /* NDEBUG */ #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) #define IS_WORD_ALIGNED(x) ((uint64_t)0U == ((uint64_t)(x) & (uint64_t)3U)) #else #define IS_WORD_ALIGNED(x) ((uint32_t)0U == ((uint32_t)(x) & (uint32_t)3U)) #endif #define BMSR_AUTO_NEGOTIATION_COMPLETE (0x0020U) #define INVALID_INDEX (0xFFFFFFFFU) #define PHY_ADDRESS_MIN (0U) #define PHY_ADDRESS_MAX (31U) /* * Defines for determining DMA descriptor sizes */ #if 0 #if !defined(MSS_MAC_TIME_STAMPED_MODE) #define MSS_MAC_TIME_STAMPED_MODE 0 /* Default to non time stamped descriptors */ #endif #endif #if defined(TARGET_ALOE) #define MSS_MAC0_BASE (0x10090000U); #elif defined(TARGET_G5_SOC) #define MSS_MAC0_BASE (0x20110000U) #define MSS_EMAC0_BASE (0x20111000U) #define MSS_MAC1_BASE (0x20112000U) #define MSS_EMAC1_BASE (0x20113000U) #define MSS_MAC0_BASE_HI (0x28110000U) #define MSS_EMAC0_BASE_HI (0x28111000U) #define MSS_MAC1_BASE_HI (0x28112000U) #define MSS_EMAC1_BASE_HI (0x28113000U) #define MSS_MAC_GEM0_ABP_BIT (0x00010000U) #define MSS_MAC_GEM1_ABP_BIT (0x00020000U) #else #warning "No target platform defined for MSS Ethernet MAC" #endif /**************************************************************************/ /* Private variables */ /**************************************************************************/ #if defined(TARGET_ALOE) static volatile uint32_t *GEMGXL_tx_clk_sel = (volatile uint32_t *)0x100A0000UL; static volatile uint32_t *GEMGXL_speed_mode = (volatile uint32_t *)0x100A0020UL; #endif /************************************************************************** * Global variables * * * * Note: there are two instances of the GMAC for G5 SOC and each has a * * primary MAC and a secondary eMAC for time sensative traffic. The FU540 * * device on the Aloe board has a single primary MAC. * **************************************************************************/ #if defined(TARGET_G5_SOC) mss_mac_instance_t g_mac0; mss_mac_instance_t g_mac1; mss_mac_instance_t g_emac0; mss_mac_instance_t g_emac1; #endif #if defined(MSS_MAC_USE_DDR) #if MSS_MAC_USE_DDR == MSS_MAC_MEM_DDR uint8_t *g_mss_mac_ddr_ptr = (uint8_t *)0xC0000000LLU; #elif MSS_MAC_USE_DDR == MSS_MAC_MEM_FIC0 uint8_t *g_mss_mac_ddr_ptr = (uint8_t *)0x60000000LLU; #elif MSS_MAC_USE_DDR == MSS_MAC_MEM_FIC1 uint8_t *g_mss_mac_ddr_ptr = (uint8_t *)0xE0000000LLU; #elif MSS_MAC_USE_DDR == MSS_MAC_MEM_CRYPTO uint8_t *g_mss_mac_ddr_ptr = (uint8_t *)0x22002000LLU; #else #error "bad memory region defined" #endif #endif #if defined(TARGET_ALOE) mss_mac_instance_t g_mac0; #endif /**************************************************************************/ /* Private Functions */ /**************************************************************************/ static void mac_reset(void); static void config_mac_hw(mss_mac_instance_t *this_mac, const mss_mac_cfg_t * cfg); static void tx_desc_ring_init(mss_mac_instance_t *this_mac); static void rx_desc_ring_init(mss_mac_instance_t *this_mac); static void assign_station_addr(mss_mac_instance_t *this_mac, const uint8_t mac_addr[MSS_MAC_MAC_LEN]); static void generic_mac_irq_handler(mss_mac_instance_t *this_mac, uint64_t queue_no); static void rxpkt_handler(mss_mac_instance_t *this_mac, uint64_t queue_no); static void txpkt_handler(mss_mac_instance_t *this_mac, uint64_t queue_no); static void update_mac_cfg(const mss_mac_instance_t *this_mac); static uint8_t probe_phy(const mss_mac_instance_t *this_mac); static void instances_init(mss_mac_instance_t *this_mac, mss_mac_cfg_t *cfg); static void msgmii_init(const mss_mac_instance_t *this_mac); /* PMCS: Made non static for now for test program use... */ void msgmii_autonegotiate(const mss_mac_instance_t *this_mac); /**************************************************************************/ /* Public Functions */ /**************************************************************************/ /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_init ( mss_mac_instance_t *this_mac, mss_mac_cfg_t *cfg ) { int32_t queue_no; ASSERT(cfg != NULL_POINTER); #if defined(TARGET_ALOE) ASSERT(this_mac == &g_mac0); instances_init(this_mac, cfg); if((cfg != NULL_POINTER) && (this_mac == &g_mac0)) #endif #if defined(TARGET_G5_SOC) ASSERT((this_mac == &g_mac0) || (this_mac == &g_mac1) || (this_mac == &g_emac0) || (this_mac == &g_emac1)); instances_init(this_mac, cfg); /* * Always reset GEM if the pMAC is selected for init but not if eMAC * The pMAC should always be initialised first followed by the eMAC so this * is ok... */ if(this_mac == &g_mac0) { SYSREG->SUBBLK_CLOCK_CR |= (uint32_t)2U; /* Reset MAC */ SYSREG->SOFT_RESET_CR |= (uint32_t)2U; { volatile int64_t index; index = 0; while(1000 != index) /* Don't know if necessary, but delay a bit before de-asserting reset... */ { index++; } } /* Take MAC out of reset. */ SYSREG->SOFT_RESET_CR &= (uint32_t)~2U; } if(this_mac == &g_mac1) { SYSREG->SUBBLK_CLOCK_CR |= (uint32_t)4U; /* Reset MAC */ SYSREG->SOFT_RESET_CR |= (uint32_t)4U; { volatile int64_t index; index = 0; while(1000 != index) /* Don't know if necessary, but delay a bit before de-asserting reset... */ { index++; } } /* Take MAC out of reset. */ SYSREG->SOFT_RESET_CR &= (uint32_t)~4U; } /* * Disable all queues. * * In theory you only need to write 1UL to the queue pointers but in * practice doing this results in spurious accesses to addresses in the * lower 4K on the AXI bus when enabling a queue which can trigger hresp * errors. To stop this we use real addresses with the lsb set instead. */ if(0U == this_mac->is_emac) { *this_mac->queue[0].receive_q_ptr = (uint32_t)((uint64_t)this_mac->queue[0].rx_desc_tab) | 1U; *this_mac->queue[1].receive_q_ptr = (uint32_t)((uint64_t)this_mac->queue[1].rx_desc_tab) | 1U; *this_mac->queue[2].receive_q_ptr = (uint32_t)((uint64_t)this_mac->queue[2].rx_desc_tab) | 1U; *this_mac->queue[3].receive_q_ptr = (uint32_t)((uint64_t)this_mac->queue[3].rx_desc_tab) | 1U; *this_mac->queue[0].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[0].tx_desc_tab) | 1U; *this_mac->queue[1].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[1].tx_desc_tab) | 1U; *this_mac->queue[2].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[2].tx_desc_tab) | 1U; *this_mac->queue[3].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[3].tx_desc_tab) | 1U; } else { *this_mac->queue[0].receive_q_ptr = (uint32_t)((uint64_t)this_mac->queue[0].rx_desc_tab) | 1U; *this_mac->queue[0].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[0].tx_desc_tab) | 1U; } if(0U == this_mac->is_emac) { this_mac->mac_base->USER_IO = cfg->tsu_clock_select & 1U; } if((cfg != NULL_POINTER) && ((this_mac == &g_mac0) || (this_mac == &g_mac1) || (this_mac == &g_emac0) || (this_mac == &g_emac1))) #endif /* defined(TARGET_G5_SOC) */ { this_mac->phy_addr = cfg->phy_addr; this_mac->pcs_phy_addr = cfg->pcs_phy_addr; this_mac->interface_type = cfg->interface_type; this_mac->jumbo_frame_enable = cfg->jumbo_frame_enable; this_mac->phy_type = cfg->phy_type; this_mac->phy_autonegotiate = cfg->phy_autonegotiate; this_mac->phy_mac_autonegotiate = cfg->phy_mac_autonegotiate; this_mac->phy_get_link_status = cfg->phy_get_link_status; this_mac->phy_init = cfg->phy_init; this_mac->phy_set_link_speed = cfg->phy_set_link_speed; this_mac->append_CRC = cfg->append_CRC; #if defined(MSS_MAC_PHY_HW_RESET) || defined(MSS_MAC_PHY_HW_SRESET) this_mac->phy_soft_reset_gpio = cfg->phy_soft_reset_gpio; this_mac->phy_soft_reset_pin = cfg->phy_soft_reset_pin; this_mac->phy_hard_reset_gpio = cfg->phy_hard_reset_gpio; this_mac->phy_hard_reset_pin = cfg->phy_hard_reset_pin; #endif this_mac->phy_controller = cfg->phy_controller; this_mac->speed_mode = cfg->speed_mode; this_mac->speed_duplex_select = cfg->speed_duplex_select; #if MSS_MAC_USE_PHY_DP83867 this_mac->phy_extended_read = cfg->phy_extended_read; this_mac->phy_extended_write = cfg->phy_extended_write; #endif #if defined(TARGET_G5_SOC) if(0U != cfg->use_local_ints) { __disable_local_irq(this_mac->mac_q_int[0]); if(0U == this_mac->is_emac) { __disable_local_irq(this_mac->mac_q_int[1]); __disable_local_irq(this_mac->mac_q_int[2]); __disable_local_irq(this_mac->mac_q_int[3]); __disable_local_irq(this_mac->mmsl_int); } } else #endif { /* * In the following if an interrupt is set to NoInterrupt_IRQn then * the PLIC will ignore it as interrupt 0 is a dummy one. */ PLIC_DisableIRQ(this_mac->mac_q_int[0]); PLIC_SetPriority(this_mac->mac_q_int[0], cfg->queue0_int_priority); #if defined(TARGET_G5_SOC) PLIC_DisableIRQ(this_mac->mac_q_int[1]); PLIC_SetPriority(this_mac->mac_q_int[1], cfg->queue1_int_priority); PLIC_DisableIRQ(this_mac->mac_q_int[2]); PLIC_SetPriority(this_mac->mac_q_int[2], cfg->queue2_int_priority); PLIC_DisableIRQ(this_mac->mac_q_int[3]); PLIC_SetPriority(this_mac->mac_q_int[3], cfg->queue3_int_priority); PLIC_DisableIRQ(this_mac->mmsl_int); PLIC_SetPriority(this_mac->mmsl_int, cfg->mmsl_int_priority); #endif } mac_reset(); config_mac_hw(this_mac, cfg); /* Assign MAC station address */ assign_station_addr(this_mac, cfg->mac_addr); /* Intialize Tx & Rx descriptor rings */ tx_desc_ring_init(this_mac); rx_desc_ring_init(this_mac); this_mac->rx_discard = 0U; /* Ensure normal RX operation */ for(queue_no = 0; queue_no < MSS_MAC_QUEUE_COUNT; queue_no++) { /* Initialize Tx descriptors related variables. */ this_mac->queue[queue_no].nb_available_tx_desc = MSS_MAC_TX_RING_SIZE; /* Initialize Rx descriptors related variables. */ this_mac->queue[queue_no].nb_available_rx_desc = MSS_MAC_RX_RING_SIZE; this_mac->queue[queue_no].next_free_rx_desc_index = 0U; this_mac->queue[queue_no].first_rx_desc_index = 0U; /* initialize default interrupt handlers */ this_mac->queue[queue_no].pckt_tx_callback = (mss_mac_transmit_callback_t)NULL_POINTER; this_mac->queue[queue_no].pckt_rx_callback = (mss_mac_receive_callback_t)NULL_POINTER; /* Added these to MAC structure to make them MAC and queue specific... */ this_mac->queue[queue_no].ingress = 0U; this_mac->queue[queue_no].egress = 0U; this_mac->queue[queue_no].rx_overflow = 0U; this_mac->queue[queue_no].hresp_error = 0U; this_mac->queue[queue_no].rx_restart = 0U; this_mac->queue[queue_no].tx_restart = 0U; this_mac->queue[queue_no].tx_reenable = 0U; } #if 0 /* Initialize PHY interface */ if(MSS_MAC_AUTO_DETECT_PHY_ADDRESS == cfg->phy_addr) { cfg->phy_addr = probe_phy(); } #endif if(0U == this_mac->is_emac) /* Only do the PHY stuff for primary MAC */ { if(TBI == this_mac->interface_type) { msgmii_init(this_mac); } #if defined(TARGET_ALOE) this_mac->phy_addr = 0U; #endif this_mac->phy_init(this_mac, (uint8_t)this_mac->phy_addr); #if (defined(MSS_MAC_VSC8662_NWC_25) || defined(MSS_MAC_VSC8662_NWC_125)) /* 0U => configure using scb, 1U => NVMAP reset */ pre_configure_sgmii_and_ddr_pll_via_scb(1U); { volatile uint32_t timer_out=0x00000FFFU; while(((MSS_SCB_SGMII_PLL->PLL_CTRL & ((0x01U) << 25U))) == 0U) { #ifdef RENODE_DEBUG break; #endif if (timer_out == 0U) { timer_out--; } } } #endif this_mac->phy_set_link_speed(this_mac, this_mac->speed_duplex_select, this_mac->speed_mode); this_mac->phy_autonegotiate(this_mac); if(TBI == this_mac->interface_type) { msgmii_autonegotiate(this_mac); } } update_mac_cfg(this_mac); /* * Enable TX Packet and TX Packet Bus Error interrupts. * We don't enable the tx underrun interrupt as we only send on demand * and don't need to explicitly note an underrun as that is the "normal" * state for the interface to be in. */ /* * Enable RX Packet, RX Packet Overflow and RX Packet Bus Error * interrupts. */ if(0U != this_mac->is_emac) { this_mac->emac_base->INT_ENABLE = GEM_RECEIVE_OVERRUN_INT | GEM_TRANSMIT_COMPLETE | GEM_RX_USED_BIT_READ | GEM_RECEIVE_COMPLETE | GEM_RESP_NOT_OK_INT; this_mac->emac_base->NETWORK_CONTROL |= GEM_ENABLE_TRANSMIT; this_mac->emac_base->NETWORK_CONTROL |= GEM_TRANSMIT_START; } else { for(queue_no = 0; queue_no < MSS_MAC_QUEUE_COUNT; queue_no++) { /* Enable pause related interrupts if pause control is selected */ if((MSS_MAC_ENABLE == cfg->rx_flow_ctrl) || (MSS_MAC_ENABLE == cfg->tx_flow_ctrl)) { *this_mac->queue[queue_no].int_enable = GEM_RECEIVE_OVERRUN_INT | GEM_TRANSMIT_COMPLETE | GEM_RX_USED_BIT_READ | GEM_RECEIVE_COMPLETE | GEM_RESP_NOT_OK_INT | GEM_PAUSE_FRAME_TRANSMITTED | GEM_PAUSE_TIME_ELAPSED | GEM_PAUSE_FRAME_WITH_NON_0_PAUSE_QUANTUM_RX | GEM_AMBA_ERROR; } else { *this_mac->queue[queue_no].int_enable = GEM_RECEIVE_OVERRUN_INT | GEM_TRANSMIT_COMPLETE | GEM_RX_USED_BIT_READ | GEM_RECEIVE_COMPLETE | GEM_RESP_NOT_OK_INT; } #if 0 /* PMCS - set this to 1 if you want to check for un-handled interrupt conditions */ *this_mac->queue[queue_no].int_enable |= GEM_PAUSE_FRAME_TRANSMITTED | GEM_PAUSE_TIME_ELAPSED | GEM_PAUSE_FRAME_WITH_NON_0_PAUSE_QUANTUM_RX | GEM_LINK_CHANGE | GEM_TX_LOCKUP_DETECTED | GEM_RX_LOCKUP_DETECTED | GEM_AMBA_ERROR | GEM_RETRY_LIMIT_EXCEEDED_OR_LATE_COLLISION | GEM_TRANSMIT_UNDER_RUN; #endif } this_mac->mac_base->NETWORK_CONTROL |= GEM_ENABLE_TRANSMIT; this_mac->mac_base->NETWORK_CONTROL |= GEM_TRANSMIT_START; } /* * At this stage, the MSS MAC interrupts are disabled and won't be enabled * until at least one of the FIFOs is configured with a buffer(s) for * data transfer. */ } this_mac->mac_available = MSS_MAC_AVAILABLE; } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_update_hw_address ( mss_mac_instance_t *this_mac, const mss_mac_cfg_t * cfg ) { ASSERT(cfg != NULL_POINTER); if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(cfg != NULL_POINTER) { /* Assign MAC station address */ assign_station_addr(this_mac, cfg->mac_addr); } } } /****************************************************************************** * */ static void update_mac_cfg(const mss_mac_instance_t *this_mac) { mss_mac_speed_t speed; uint8_t fullduplex; uint8_t link_up; uint32_t temp_cr; link_up = this_mac->phy_get_link_status(this_mac, &speed, &fullduplex); if(link_up != MSS_MAC_LINK_DOWN) { if(0U != this_mac->is_emac) { temp_cr = this_mac->emac_base->NETWORK_CONFIG; } else { temp_cr = this_mac->mac_base->NETWORK_CONFIG; } temp_cr &= ~(GEM_GIGABIT_MODE_ENABLE | GEM_SPEED | GEM_FULL_DUPLEX); if(MSS_MAC_1000MBPS == speed) { #if defined(TARGET_ALOE) *GEMGXL_tx_clk_sel = (uint32_t)0U; *GEMGXL_speed_mode = (uint32_t)2U; #endif temp_cr |= GEM_GIGABIT_MODE_ENABLE; } else { if(MSS_MAC_100MBPS == speed) { #if defined(TARGET_ALOE) *GEMGXL_tx_clk_sel = (uint32_t)1U; *GEMGXL_speed_mode = (uint32_t)1U; #endif temp_cr |= (uint32_t)GEM_SPEED; } #if defined(TARGET_ALOE) else { *GEMGXL_tx_clk_sel = (uint32_t)1U; *GEMGXL_speed_mode = (uint32_t)0U; } #endif } /* Configure duplex mode */ if(MSS_MAC_FULL_DUPLEX == fullduplex) { temp_cr |= GEM_FULL_DUPLEX; } if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONFIG = temp_cr; } else { this_mac->mac_base->NETWORK_CONFIG = temp_cr; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint8_t MSS_MAC_get_link_status ( const mss_mac_instance_t *this_mac, mss_mac_speed_t * speed, uint8_t * fullduplex ) { /* Todo: These statics will only work for the single MAC Aloe case... */ static mss_mac_speed_t previous_speed = INVALID_SPEED; static uint8_t previous_duplex = 0xAAU; mss_mac_speed_t link_speed; uint8_t link_fullduplex; uint8_t link_up; link_up = MSS_MAC_LINK_DOWN; /* Default condition in case we are not active yet */ if(MSS_MAC_AVAILABLE == this_mac->mac_available) { link_up = this_mac->phy_get_link_status(this_mac, &link_speed, &link_fullduplex); if(link_up != MSS_MAC_LINK_DOWN) { /*---------------------------------------------------------------------- * Update MAC configuration if link characteristics changed. */ if((link_speed != previous_speed) || (link_fullduplex != previous_duplex)) { uint32_t temp_cr; if(0U != this_mac->is_emac) { temp_cr = this_mac->emac_base->NETWORK_CONFIG; } else { temp_cr = this_mac->mac_base->NETWORK_CONFIG; } temp_cr &= ~(GEM_GIGABIT_MODE_ENABLE | GEM_SPEED | GEM_FULL_DUPLEX); if(MSS_MAC_1000MBPS == link_speed) { #if defined(TARGET_ALOE) *GEMGXL_tx_clk_sel = (uint32_t)0U; *GEMGXL_speed_mode = (uint32_t)2U; #endif temp_cr |= GEM_GIGABIT_MODE_ENABLE; } else { if(MSS_MAC_100MBPS == link_speed) { #if defined(TARGET_ALOE) *GEMGXL_tx_clk_sel = (uint32_t)1U; *GEMGXL_speed_mode = (uint32_t)1U; #endif temp_cr |= (uint32_t)GEM_SPEED; } #if defined(TARGET_ALOE) else { *GEMGXL_tx_clk_sel = (uint32_t)1U; *GEMGXL_speed_mode = (uint32_t)0U; } #endif } /* Configure duplex mode */ if(MSS_MAC_FULL_DUPLEX == link_fullduplex) { temp_cr |= GEM_FULL_DUPLEX; } if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONFIG = temp_cr; } else { this_mac->mac_base->NETWORK_CONFIG = temp_cr; } } previous_speed = link_speed; previous_duplex = link_fullduplex; /*---------------------------------------------------------------------- * Return current link speed and duplex mode. */ if(speed != NULL_POINTER) { *speed = link_speed; } if(fullduplex != NULL_POINTER) { *fullduplex = link_fullduplex; } if(GMII_SGMII == this_mac->interface_type) /* Emulation platform with embedded SGMII link for PHY connection and GMII for MAC connection */ { uint16_t phy_reg; uint16_t sgmii_link_up; /* * Find out if link is up on SGMII link between GMII core and * external PHY. * * The link status bit latches 0 state until read to record fact * link has failed since last read so you need to read it twice to * get the current status... */ phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->pcs_phy_addr, MII_BMSR); phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->pcs_phy_addr, MII_BMSR); sgmii_link_up = phy_reg & BMSR_LSTATUS; if(0U == sgmii_link_up) { /* Initiate auto-negotiation on the SGMII link. */ phy_reg = MSS_MAC_read_phy_reg(this_mac, (uint8_t)this_mac->pcs_phy_addr, MII_BMCR); phy_reg |= BMCR_ANENABLE; MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->pcs_phy_addr, MII_BMCR, phy_reg); phy_reg |= BMCR_ANRESTART; MSS_MAC_write_phy_reg(this_mac, (uint8_t)this_mac->pcs_phy_addr, MII_BMCR, phy_reg); } } #if 0 /* TBD old SF2 stuff keep here for now for guidance... */ #if ((MSS_MAC_PHY_INTERFACE == SGMII) || (MSS_MAC_PHY_INTERFACE == TBI)) #if (MSS_MAC_PHY_INTERFACE == TBI) /*---------------------------------------------------------------------- * Make sure SGMII interface link is up. if interface is TBI */ #define MDIO_PHY_ADDR SF2_MSGMII_PHY_ADDR #endif /* #if (MSS_MAC_PHY_INTERFACE == TBI) */ #if (MSS_MAC_PHY_INTERFACE == SGMII) /*---------------------------------------------------------------------- * Make sure SGMII/1000baseX interface link is up. if interface is * SGMII/1000baseX */ #define MDIO_PHY_ADDR MSS_MAC_INTERFACE_MDIO_ADDR #endif /* #if ((MSS_MAC_PHY_INTERFACE == SGMII) || (MSS_MAC_PHY_INTERFACE == BASEX1000)) */ { uint16_t phy_reg; uint16_t sgmii_link_up; /* Find out if link is up on SGMII link between MAC and external PHY. */ phy_reg = MSS_MAC_read_phy_reg(MDIO_PHY_ADDR, MII_BMSR); sgmii_link_up = phy_reg & BMSR_LSTATUS; if(0U == sgmii_link_up) { /* Initiate auto-negotiation on the SGMII link. */ phy_reg = MSS_MAC_read_phy_reg(MDIO_PHY_ADDR, MII_BMCR); phy_reg |= BMCR_ANENABLE; MSS_MAC_write_phy_reg(MDIO_PHY_ADDR, MII_BMCR, phy_reg); phy_reg |= BMCR_ANRESTART; MSS_MAC_write_phy_reg(MDIO_PHY_ADDR, MII_BMCR, phy_reg); } } #endif #endif } } return link_up; } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_cfg_struct_def_init ( mss_mac_cfg_t * cfg ) { ASSERT(NULL_POINTER != cfg); if(NULL_POINTER != cfg) { (void)memset(cfg, 0, sizeof(mss_mac_cfg_t)); /* Start with clean slate */ cfg->speed_duplex_select = MSS_MAC_ANEG_ALL_SPEEDS; cfg->speed_mode = MSS_MAC_SPEED_AN; #if defined(TARGET_ALOE) cfg->phy_addr = 0U; #endif #if defined(MSS_MAC_PHY_HW_RESET) || defined(MSS_MAC_PHY_HW_SRESET) cfg->phy_addr = PHY_NULL_MDIO_ADDR; cfg->phy_soft_reset_gpio = (GPIO_TypeDef *)0UL; cfg->phy_soft_reset_pin = MSS_GPIO_0; cfg->phy_hard_reset_gpio = (GPIO_TypeDef *)0UL; cfg->phy_hard_reset_pin = MSS_GPIO_0; #endif cfg->phy_controller = (mss_mac_instance_t *)0UL; cfg->tx_edc_enable = MSS_MAC_ERR_DET_CORR_DISABLE; cfg->rx_edc_enable = MSS_MAC_ERR_DET_CORR_DISABLE; cfg->jumbo_frame_enable = MSS_MAC_JUMBO_FRAME_DISABLE; cfg->jumbo_frame_default = MSS_MAC_MAX_PACKET_SIZE; cfg->length_field_check = MSS_MAC_LENGTH_FIELD_CHECK_ENABLE; cfg->append_CRC = MSS_MAC_CRC_ENABLE; cfg->loopback = MSS_MAC_LOOPBACK_DISABLE; cfg->rx_flow_ctrl = MSS_MAC_RX_FLOW_CTRL_ENABLE; cfg->tx_flow_ctrl = MSS_MAC_TX_FLOW_CTRL_ENABLE; cfg->ipg_multiplier = MSS_MAC_IPG_DEFVAL; cfg->ipg_divisor = MSS_MAC_IPG_DEFVAL; cfg->phyclk = MSS_MAC_DEF_PHY_CLK; cfg->mac_addr[0] = 0x00U; cfg->mac_addr[1] = 0x00U; cfg->mac_addr[2] = 0x00U; cfg->mac_addr[3] = 0x00U; cfg->mac_addr[4] = 0x00U; cfg->mac_addr[5] = 0x00U; cfg->queue_enable[0] = MSS_MAC_QUEUE_DISABLE; cfg->queue_enable[1] = MSS_MAC_QUEUE_DISABLE; cfg->queue_enable[2] = MSS_MAC_QUEUE_DISABLE; cfg->queue_enable[3] = MSS_MAC_QUEUE_DISABLE; cfg->phy_type = MSS_MAC_DEV_PHY_NULL; cfg->interface_type = NULL_PHY; cfg->phy_autonegotiate = MSS_MAC_NULL_phy_autonegotiate; cfg->phy_mac_autonegotiate = MSS_MAC_NULL_phy_mac_autonegotiate; cfg->phy_get_link_status = MSS_MAC_NULL_phy_get_link_status; cfg->phy_init = MSS_MAC_NULL_phy_init; cfg->phy_set_link_speed = MSS_MAC_NULL_phy_set_link_speed; cfg->use_hi_address = MSS_MAC_DISABLE; cfg->use_local_ints = MSS_MAC_DISABLE; cfg->queue0_int_priority = 7U; /* Give them highest priority by default */ cfg->queue1_int_priority = 7U; cfg->queue2_int_priority = 7U; cfg->queue3_int_priority = 7U; cfg->mmsl_int_priority = 7U; /* * PMCS: Note for the Emulation platform we need to select the non * default TSU clock the moment or TX won't work */ cfg->tsu_clock_select = 0U; cfg->amba_burst_length = MSS_MAC_AMBA_BURST_16; #if MSS_MAC_USE_PHY_DP83867 cfg->phy_extended_read = NULL_ti_read_extended_regs; cfg->phy_extended_write = NULL_ti_write_extended_regs; #endif } } /****************************************************************************** * */ static void mac_reset ( void ) { } #if defined(TARGET_ALOE) /****************************************************************************** * PLL and Reset registers after reset in "wait for debug mode" * 0x10000000 : 0x10000000 * Address 0 - 3 4 - 7 8 - B C - F * 10000000 C0000000 030187C1 00000000 030187C1 * 10000010 00000000 00000000 00000000 030187C1 * 10000020 00000000 00000001 00000000 00000004 * * PLL and Reset registers after Linux boot. * * 0x10000000 : 0x10000000 * Address 0 - 3 4 - 7 8 - B C - F * 10000000 C0000000 82110EC0 00000000 82110DC0 * 10000010 80000000 00000000 00000000 82128EC0 * 10000020 80000000 00000000 0000002F 00000004 * */ /****************************************************************************** * */ #define __I const volatile #define __IO volatile #define __O volatile typedef struct { __IO uint32_t HFXOSCCFG; /* 0x0000 */ __IO uint32_t COREPLLCFG0; /* 0x0004 */ __IO uint32_t reserved0; /* 0x0008 */ __IO uint32_t DDRPLLCFG0; /* 0x000C */ __IO uint32_t DDRPLLCFG1; /* 0x0010 */ __IO uint32_t reserved1; /* 0x0014 */ __IO uint32_t reserved2; /* 0x0018 */ __IO uint32_t GEMGXLPLLCFG0; /* 0x001C */ __IO uint32_t GEMGXLPLLCFG1; /* 0x0020 */ __IO uint32_t CORECLKSEL; /* 0x0024 */ __IO uint32_t DEVICERESETREG; /* 0x0028 */ } AloePRCI_TypeDef; AloePRCI_TypeDef *g_aloe_prci = (AloePRCI_TypeDef *)0x10000000UL; typedef struct { __IO uint32_t PWMCFG; /* 0x0000 */ __IO uint32_t reserved0; /* 0x0004 */ __IO uint32_t PWMCOUNT; /* 0x0008 */ __IO uint32_t reserved1; /* 0x000C */ __IO uint32_t PWMS; /* 0x0010 */ __IO uint32_t reserved2; /* 0x0014 */ __IO uint32_t reserved3; /* 0x0018 */ __IO uint32_t reserved4; /* 0x001C */ __IO uint32_t PWMCMP0; /* 0x0020 */ __IO uint32_t PWMCMP1; /* 0x0024 */ __IO uint32_t PWMCMP2; /* 0x0028 */ __IO uint32_t PWMCMP3; /* 0x002C */ } AloePWM_TypeDef; AloePWM_TypeDef *g_aloe_pwm0 = (AloePWM_TypeDef *)0x10020000UL; static void config_mac_pll_and_reset(void); static void config_mac_pll_and_reset(void) { volatile int64_t ix; volatile int64_t counter; volatile int64_t loops = 0; /* * COREPLLCFG0 reset value = 0x030187C1 * divr = 1 * divf = 1F * divq = 3 * range = 0 * bypass = 1 * fse = 1 * lock = 0 * * Desired value = 82110EC0 * divr = 0 * divf = 1D * divq = 2 * range = 4 * bypass = 0 * fse = 1 * lock = 1 */ #if 0 /* Test code for proving Core clock speed switching works */ g_aloe_pwm0->PWMCFG = 0x0000020EU; g_aloe_pwm0->PWMCMP0 = 0x0000FFFFU; g_aloe_pwm0->PWMCMP1 = 0x0000FFFFU; g_aloe_pwm0->PWMCMP2 = 0x0000FFFFU; g_aloe_pwm0->PWMCMP3 = 0x0000FFFFU; while(loops < 16) { if(ix & 1) { g_aloe_pwm0->PWMCMP0 = 0x00000000U; } else { g_aloe_pwm0->PWMCMP0 = 0x0000FFFFU; } if(ix & 2) { g_aloe_pwm0->PWMCMP1 = 0x00000000U; } else { g_aloe_pwm0->PWMCMP1 = 0x0000FFFFU; } if(ix & 4) { g_aloe_pwm0->PWMCMP2 = 0x00000000U; } else { g_aloe_pwm0->PWMCMP2 = 0x0000FFFFU; } if(ix & 8) { g_aloe_pwm0->PWMCMP3 = 0x00000000U; } else { g_aloe_pwm0->PWMCMP3 = 0x0000FFFFU; } ix++; for(counter = 0; counter != 100000; counter++) ; loops++; } #endif g_aloe_prci->COREPLLCFG0 = 0x03110EC0U; /* Configure Core Clock PLL */ while(g_aloe_prci->COREPLLCFG0 & 0x80000000U) /* Wait for lock with PLL bypassed */ ix++; g_aloe_prci->COREPLLCFG0 = 0x02110EC0U; /* Take PLL out of bypass */ g_aloe_prci->CORECLKSEL = 0x00000000U; /* Switch to PLL as clock source */ #if 0 /* Test code for proving Core clock speed switching works */ loops = 0; while(loops < 20) { if(ix & 1) { g_aloe_pwm0->PWMCMP0 = 0x0000FFFFU; } else { g_aloe_pwm0->PWMCMP0 = 0x00000000U; } ix++; for(counter = 0; counter != 3000000; counter++) ; loops++; } #endif /* * GEMGXLPLLCFG0 reset value = 0x030187C1 * divr = 1 * divf = 1F * divq = 3 * range = 0 * bypass = 1 * fse = 1 * lock = 0 * * Desired value = 82128EC0 * divr = 0 * divf = 3B * divq = 5 * range = 4 * bypass = 0 * fse = 1 * lock = 1 */ g_aloe_prci->GEMGXLPLLCFG0 = 0x03128EC0U; /* Configure GEM Clock PLL */ while(g_aloe_prci->GEMGXLPLLCFG0 & 0x80000000U) /* Wait for lock with PLL bypassed */ ix++; g_aloe_prci->GEMGXLPLLCFG0 = 0x02128EC0U; /* Take PLL out of bypass */ g_aloe_prci->GEMGXLPLLCFG1 = 0x80000000U; /* Switch to PLL as clock source */ g_aloe_prci->DEVICERESETREG |= 0x00000020U; /* Release MAC from reset */ } #endif /****************************************************************************** * */ static void config_mac_hw(mss_mac_instance_t *this_mac, const mss_mac_cfg_t * cfg) { uint32_t temp_net_config = 0U; uint32_t temp_net_control = 0U; uint32_t temp_dma_config = 0U; uint32_t temp_length; /* Check for validity of configuration parameters */ ASSERT( IS_STATE(cfg->tx_edc_enable) ); ASSERT( IS_STATE(cfg->rx_edc_enable) ); ASSERT( IS_STATE(cfg->jumbo_frame_enable) ); ASSERT( IS_STATE(cfg->length_field_check) ); ASSERT( IS_STATE(cfg->append_CRC) ); ASSERT( IS_STATE(cfg->loopback) ); ASSERT( IS_STATE(cfg->rx_flow_ctrl) ); ASSERT( IS_STATE(cfg->tx_flow_ctrl) ); #if defined(TARGET_ALOE) config_mac_pll_and_reset(); #endif /*-------------------------------------------------------------------------- * Configure MAC Network Control register */ /* temp_net_control = GEM_MAN_PORT_EN | GEM_CLEAR_ALL_STATS_REGS | GEM_PFC_ENABLE; */ temp_net_control = GEM_MAN_PORT_EN | GEM_CLEAR_ALL_STATS_REGS | GEM_PFC_ENABLE | GEM_ALT_SGMII_MODE; #if 0 temp_net_control |= GEM_LOOPBACK_LOCAL; /* PMCS: Enable this to force local loop back */ #endif if(MSS_MAC_ENABLE == cfg->loopback) { temp_net_control |= GEM_LOOPBACK_LOCAL; } #if defined(MSS_MAC_TIME_STAMPED_MODE) temp_net_control |= GEM_PTP_UNICAST_ENA; #endif /* * eMAC has to be configured as external TSU although it is actually using * the pMAC TSU. There is only really 1 TSU per GEM instance and all * adjustments etc should really be done via the pMAC. */ if(0U != this_mac->is_emac) { temp_net_control |= GEM_EXT_TSU_PORT_ENABLE; } /*-------------------------------------------------------------------------- * Configure MAC Network Config and Network Control registers */ #if defined(TARGET_G5_SOC) if(TBI == this_mac->interface_type) { temp_net_config = (((uint32_t)(1UL)) << GEM_DATA_BUS_WIDTH_SHIFT) | ((cfg->phyclk & GEM_MDC_CLOCK_DIVISOR_MASK) << GEM_MDC_CLOCK_DIVISOR_SHIFT) | GEM_PCS_SELECT | GEM_SGMII_MODE_ENABLE; } else { /* Actually for the G5 SoC Emulation Platform the interface is GMII... */ temp_net_config = (((uint32_t)(1UL)) << GEM_DATA_BUS_WIDTH_SHIFT) | ((cfg->phyclk & GEM_MDC_CLOCK_DIVISOR_MASK) << GEM_MDC_CLOCK_DIVISOR_SHIFT); } #endif #if defined(TARGET_ALOE) /* No pause frames received in memory, divide PCLK by 224 for MDC */ temp_net_config = (cfg->phyclk & GEM_MDC_CLOCK_DIVISOR_MASK) << GEM_MDC_CLOCK_DIVISOR_SHIFT; #endif if((MSS_MAC_ENABLE == cfg->rx_flow_ctrl) || (MSS_MAC_ENABLE == cfg->tx_flow_ctrl)) { temp_net_config |= GEM_FCS_REMOVE | GEM_DISABLE_COPY_OF_PAUSE_FRAMES | GEM_RECEIVE_1536_BYTE_FRAMES | GEM_PAUSE_ENABLE | GEM_FULL_DUPLEX | GEM_GIGABIT_MODE_ENABLE; } else { temp_net_config |= GEM_FCS_REMOVE | GEM_DISABLE_COPY_OF_PAUSE_FRAMES | GEM_RECEIVE_1536_BYTE_FRAMES | GEM_FULL_DUPLEX | GEM_GIGABIT_MODE_ENABLE; } if(MSS_MAC_ENABLE == cfg->length_field_check) { temp_net_config |= GEM_LENGTH_FIELD_ERROR_FRAME_DISCARD; } if(MSS_MAC_IPG_DEFVAL != cfg->ipg_multiplier) /* If we have a non zero value here then enable IPG stretching */ { uint32_t stretch; temp_net_config |= GEM_IPG_STRETCH_ENABLE; stretch = cfg->ipg_multiplier & GEM_IPG_STRETCH_MUL_MASK; stretch |= (cfg->ipg_divisor & GEM_IPG_STRETCH_DIV_MASK) << GEM_IPG_STRETCH_DIV_SHIFT; if(0U != this_mac->is_emac) { this_mac->emac_base->STRETCH_RATIO = stretch; } else { this_mac->mac_base->STRETCH_RATIO = stretch; } } if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONTROL = temp_net_control; this_mac->emac_base->NETWORK_CONFIG = temp_net_config; this_mac->mac_base->NETWORK_CONFIG = temp_net_config; } else { this_mac->mac_base->NETWORK_CONTROL = temp_net_control; this_mac->mac_base->NETWORK_CONFIG = temp_net_config; #if defined(TARGET_G5_SOC) this_mac->emac_base->NETWORK_CONFIG = temp_net_config; #endif } /*-------------------------------------------------------------------------- * Reset PCS */ if(0U == this_mac->is_emac) { this_mac->mac_base->PCS_CONTROL |= (uint32_t)0x8000UL; } /*-------------------------------------------------------------------------- * Configure MAC Network DMA Config register */ temp_dma_config = (MSS_MAC_RX_BUF_VALUE << GEM_RX_BUF_SIZE_SHIFT) | GEM_TX_PBUF_SIZE | (((uint32_t)(0x3UL)) << GEM_RX_PBUF_SIZE_SHIFT) | ((uint32_t)(cfg->amba_burst_length & MSS_MAC_AMBA_BURST_MASK)); #if defined(MSS_MAC_TIME_STAMPED_MODE) temp_dma_config |= GEM_TX_BD_EXTENDED_MODE_EN | GEM_RX_BD_EXTENDED_MODE_EN; #endif #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) temp_dma_config |= GEM_DMA_ADDR_BUS_WIDTH_1; #endif if(0U != this_mac->is_emac) { this_mac->emac_base->DMA_CONFIG = temp_dma_config; #if defined(MSS_MAC_TIME_STAMPED_MODE) /* Record TS for all packets by default */ this_mac->emac_base->TX_BD_CONTROL = GEM_BD_TS_MODE; this_mac->emac_base->RX_BD_CONTROL = GEM_BD_TS_MODE; #endif } else { int32_t queue_index; this_mac->mac_base->DMA_CONFIG = temp_dma_config; #if defined(MSS_MAC_TIME_STAMPED_MODE) /* Record TS for all packets by default */ this_mac->mac_base->TX_BD_CONTROL = GEM_BD_TS_MODE; this_mac->mac_base->RX_BD_CONTROL = GEM_BD_TS_MODE; #endif #if (MSS_MAC_QUEUE_COUNT > 1) for(queue_index = 1; queue_index < MSS_MAC_QUEUE_COUNT; queue_index++) { *(this_mac->queue[queue_index].dma_rxbuf_size) = ((uint32_t)MSS_MAC_RX_BUF_VALUE); } #endif } /* * Disable the other queues as the GEM reset leaves them enabled with an * address pointer of 0 for some unfathomable reason... This screws things * up when we enable transmission or reception. * * Setting b0 of the queue pointer disables a queue. */ #if (MSS_MAC_QUEUE_COUNT > 1) if(0U == this_mac->is_emac) { /* Added these to MAC structure to make them MAC specific... */ this_mac->mac_base->TRANSMIT_Q1_PTR = ((uint32_t)(uint64_t)this_mac->queue[0].tx_desc_tab) | 1U; /* Use address of valid descriptor but set b0 to disable */ this_mac->mac_base->TRANSMIT_Q2_PTR = ((uint32_t)(uint64_t)this_mac->queue[0].tx_desc_tab) | 1U; this_mac->mac_base->TRANSMIT_Q3_PTR = ((uint32_t)(uint64_t)this_mac->queue[0].tx_desc_tab) | 1U; this_mac->mac_base->RECEIVE_Q1_PTR = ((uint32_t)(uint64_t)this_mac->queue[0].rx_desc_tab) | 1U; this_mac->mac_base->RECEIVE_Q2_PTR = ((uint32_t)(uint64_t)this_mac->queue[0].rx_desc_tab) | 1U; this_mac->mac_base->RECEIVE_Q3_PTR = ((uint32_t)(uint64_t)this_mac->queue[0].rx_desc_tab) | 1U; } #endif /* Set up maximum initial jumbo frame size - but bounds check first */ if(cfg->jumbo_frame_default > MSS_MAC_JUMBO_MAX) { temp_length = MSS_MAC_JUMBO_MAX; } else { temp_length = cfg->jumbo_frame_default; } if(0U != this_mac->is_emac) { this_mac->emac_base->JUMBO_MAX_LENGTH = temp_length; } else { this_mac->mac_base->JUMBO_MAX_LENGTH = temp_length; } /*-------------------------------------------------------------------------- * Disable all ints for now */ if(0U != this_mac->is_emac) { this_mac->emac_base->INT_DISABLE = ((uint32_t)0xFFFFFFFFUL); /* Only one queue here... */ } else { int32_t queue_no; for(queue_no = 0; queue_no < MSS_MAC_QUEUE_COUNT; queue_no++) { *(this_mac->queue[queue_no].int_disable) = ((uint32_t)0xFFFFFFFFUL); } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_write_phy_reg ( const mss_mac_instance_t *this_mac_in, uint8_t phyaddr, uint8_t regaddr, uint16_t regval ) { volatile uint32_t phy_op; psr_t lev; const mss_mac_instance_t *this_mac; ASSERT(MSS_MAC_PHYADDR_MAXVAL >= phyaddr); ASSERT(MSS_MAC_PHYREGADDR_MAXVAL >= regaddr); /* Check to see which MAC is actually in charge of PHY interface */ if((struct mss_mac_instance *)0UL != this_mac_in->phy_controller) { this_mac = (const mss_mac_instance_t *)this_mac_in->phy_controller; } else { this_mac = this_mac_in; } /* * Write PHY address in MII Mgmt address register. * Makes previous register address 0 & invalid. * * Don't check mac_available flag here as we may be called in the MAC init * phase before everything else is in place... */ if((MSS_MAC_PHYADDR_MAXVAL >= phyaddr) && (MSS_MAC_PHYREGADDR_MAXVAL >= regaddr)) { phy_op = GEM_WRITE1 | (GEM_PHY_OP_CL22_WRITE << GEM_OPERATION_SHIFT) | (((uint32_t)(2UL)) << GEM_WRITE10_SHIFT) | (uint32_t)regval; phy_op |= ((uint32_t)phyaddr << GEM_PHY_ADDRESS_SHIFT) & GEM_PHY_ADDRESS; phy_op |= ((uint32_t)regaddr << GEM_REGISTER_ADDRESS_SHIFT) & GEM_REGISTER_ADDRESS; lev = HAL_disable_interrupts(); /* * Always use the pMAC for this as the eMAC MDIO interface is not * connected to the outside world... */ /* Wait for MII Mgmt interface to complete previous operation. */ do { volatile int32_t ix; ix++; } while(0U == (this_mac->mac_base->NETWORK_STATUS & GEM_MAN_DONE)); this_mac->mac_base->PHY_MANAGEMENT = phy_op; HAL_restore_interrupts(lev); } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint16_t MSS_MAC_read_phy_reg ( const mss_mac_instance_t *this_mac_in, uint8_t phyaddr, uint8_t regaddr ) { volatile uint32_t phy_op; psr_t lev; const mss_mac_instance_t *this_mac; ASSERT(MSS_MAC_PHYADDR_MAXVAL >= phyaddr); ASSERT(MSS_MAC_PHYREGADDR_MAXVAL >= regaddr); /* Check to see which MAC is actually in charge of PHY interface */ if((struct mss_mac_instance *)0UL != this_mac_in->phy_controller) { this_mac = (const mss_mac_instance_t *)this_mac_in->phy_controller; } else { this_mac = this_mac_in; } /* * Write PHY address in MII Mgmt address register. * Makes previous register address 0 & invalid. * * Don't check mac_available flag here as we may be called in the MAC init * phase before everything else is in place... */ if((MSS_MAC_PHYADDR_MAXVAL >= phyaddr) && (MSS_MAC_PHYREGADDR_MAXVAL >= regaddr)) { phy_op = GEM_WRITE1 | (GEM_PHY_OP_CL22_READ << GEM_OPERATION_SHIFT) | (((uint32_t)(2UL)) << GEM_WRITE10_SHIFT); phy_op |= ((uint32_t)phyaddr << GEM_PHY_ADDRESS_SHIFT) & GEM_PHY_ADDRESS; phy_op |= ((uint32_t)regaddr << GEM_REGISTER_ADDRESS_SHIFT) & GEM_REGISTER_ADDRESS; /* * Always use the pMAC for this as the eMAC MDIO interface is not * connected to the outside world... */ lev = HAL_disable_interrupts(); /* Wait for MII Mgmt interface to complete previous operation. */ do { volatile int32_t ix; ix++; } while(0U == (this_mac->mac_base->NETWORK_STATUS & GEM_MAN_DONE)); this_mac->mac_base->PHY_MANAGEMENT = phy_op; do { volatile int32_t ix; ix++; } while(0U == (this_mac->mac_base->NETWORK_STATUS & GEM_MAN_DONE)); phy_op = this_mac->mac_base->PHY_MANAGEMENT; HAL_restore_interrupts(lev); } else { phy_op = 0U; } return((uint16_t)phy_op); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_phy_reset ( const mss_mac_instance_t *this_mac, mss_mac_phy_reset_t reset_type, bool reset_state ) { #if defined(MSS_MAC_PHY_HW_RESET) || defined(MSS_MAC_PHY_HW_SRESET) /* Reset pointer to base MAC structure */ if(&g_emac0 == this_mac) { this_mac = &g_mac0; } if(&g_emac1 == this_mac) { this_mac = &g_mac1; } if(MSS_MAC_SOFT_RESET == reset_type) { if((GPIO_TypeDef *)0UL != this_mac->phy_soft_reset_gpio) { MSS_GPIO_set_output(this_mac->phy_soft_reset_gpio, this_mac->phy_soft_reset_pin, reset_state); } } else { if((GPIO_TypeDef *)0UL != this_mac->phy_hard_reset_gpio) { MSS_GPIO_set_output(this_mac->phy_hard_reset_gpio, this_mac->phy_hard_reset_pin, reset_state); } } #else (void)this_mac; (void)reset_type; (void)reset_state; #endif } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ /* Divide by 4 as offset is in bytes but pointer is 4 bytes */ #define GEM_REG_OFFSET(x) (offsetof(MAC_TypeDef, x) / 4) uint32_t MSS_MAC_read_stat ( const mss_mac_instance_t *this_mac, mss_mac_stat_t stat ) { uint32_t stat_val = 0u; static uint64_t const stat_regs_lut[] = { GEM_REG_OFFSET(OCTETS_TXED_BOTTOM), GEM_REG_OFFSET(OCTETS_TXED_TOP), GEM_REG_OFFSET(FRAMES_TXED_OK), GEM_REG_OFFSET(BROADCAST_TXED), GEM_REG_OFFSET(MULTICAST_TXED), GEM_REG_OFFSET(PAUSE_FRAMES_TXED), GEM_REG_OFFSET(FRAMES_TXED_64), GEM_REG_OFFSET(FRAMES_TXED_65), GEM_REG_OFFSET(FRAMES_TXED_128), GEM_REG_OFFSET(FRAMES_TXED_256), GEM_REG_OFFSET(FRAMES_TXED_512), GEM_REG_OFFSET(FRAMES_TXED_1024), GEM_REG_OFFSET(FRAMES_TXED_1519), GEM_REG_OFFSET(TX_UNDERRUNS), GEM_REG_OFFSET(SINGLE_COLLISIONS), GEM_REG_OFFSET(MULTIPLE_COLLISIONS), GEM_REG_OFFSET(EXCESSIVE_COLLISIONS), GEM_REG_OFFSET(LATE_COLLISIONS), GEM_REG_OFFSET(DEFERRED_FRAMES), GEM_REG_OFFSET(CRS_ERRORS), GEM_REG_OFFSET(OCTETS_RXED_BOTTOM), GEM_REG_OFFSET(OCTETS_RXED_TOP), GEM_REG_OFFSET(FRAMES_RXED_OK), GEM_REG_OFFSET(BROADCAST_RXED), GEM_REG_OFFSET(MULTICAST_RXED), GEM_REG_OFFSET(PAUSE_FRAMES_RXED), GEM_REG_OFFSET(FRAMES_RXED_64), GEM_REG_OFFSET(FRAMES_RXED_65), GEM_REG_OFFSET(FRAMES_RXED_128), GEM_REG_OFFSET(FRAMES_RXED_256), GEM_REG_OFFSET(FRAMES_RXED_512), GEM_REG_OFFSET(FRAMES_RXED_1024), GEM_REG_OFFSET(FRAMES_RXED_1519), GEM_REG_OFFSET(UNDERSIZE_FRAMES), GEM_REG_OFFSET(EXCESSIVE_RX_LENGTH), GEM_REG_OFFSET(RX_JABBERS), GEM_REG_OFFSET(FCS_ERRORS), GEM_REG_OFFSET(RX_LENGTH_ERRORS), GEM_REG_OFFSET(RX_SYMBOL_ERRORS), GEM_REG_OFFSET(ALIGNMENT_ERRORS), GEM_REG_OFFSET(RX_RESOURCE_ERRORS), GEM_REG_OFFSET(RX_OVERRUNS), GEM_REG_OFFSET(RX_IP_CK_ERRORS), GEM_REG_OFFSET(RX_TCP_CK_ERRORS), GEM_REG_OFFSET(RX_UDP_CK_ERRORS), GEM_REG_OFFSET(AUTO_FLUSHED_PKTS) }; ASSERT(MSS_MAC_LAST_STAT > stat); if((MSS_MAC_LAST_STAT > stat) && (MSS_MAC_AVAILABLE == this_mac->mac_available)) { if(0U != this_mac->is_emac) { stat_val = *(((uint32_t *)this_mac->emac_base) + stat_regs_lut[stat]); } else { stat_val = *(((uint32_t *)this_mac->mac_base) + stat_regs_lut[stat]); } } return stat_val; } /******************************************************************************* See mss_ethernet_mac.h for details of how to use this function */ void MSS_MAC_clear_statistics ( const mss_mac_instance_t *this_mac ) { if((0U != this_mac->is_emac) && (MSS_MAC_AVAILABLE == this_mac->mac_available)) { this_mac->emac_base->NETWORK_CONTROL |= GEM_CLEAR_ALL_STATS_REGS; } else { this_mac->mac_base->NETWORK_CONTROL |= GEM_CLEAR_ALL_STATS_REGS; } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint8_t MSS_MAC_receive_pkt ( mss_mac_instance_t *this_mac, uint32_t queue_no, uint8_t * rx_pkt_buffer, void * p_user_data, mss_mac_rx_int_ctrl_t enable ) { uint8_t status = MSS_MAC_FAILED; /* Make this function atomic w.r.to EMAC interrupt */ /* PLIC_DisableIRQ() et al should not be called from the associated interrupt... */ if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U == this_mac->queue[queue_no].in_isr) { if(0U != this_mac->use_local_ints) { __disable_local_irq(this_mac->mac_q_int[queue_no]); } else { PLIC_DisableIRQ(this_mac->mac_q_int[queue_no]); /* Single interrupt from GEM? */ } } ASSERT(NULL_POINTER != rx_pkt_buffer); ASSERT(IS_WORD_ALIGNED(rx_pkt_buffer)); if(this_mac->queue[queue_no].nb_available_rx_desc > 0U) { uint32_t next_rx_desc_index; if(MSS_MAC_INT_DISABLE == enable) { /* * When setting up the chain of buffers, we don't want the DMA * engine active so shut down reception. */ if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONTROL &= ~GEM_ENABLE_RECEIVE; } else { this_mac->mac_base->NETWORK_CONTROL &= ~GEM_ENABLE_RECEIVE; } } --this_mac->queue[queue_no].nb_available_rx_desc; next_rx_desc_index = this_mac->queue[queue_no].next_free_rx_desc_index; if((MSS_MAC_RX_RING_SIZE - 1U) == next_rx_desc_index) { this_mac->queue[queue_no].rx_desc_tab[next_rx_desc_index].addr_low = (uint32_t)((uint64_t)rx_pkt_buffer | 2UL); /* Set wrap bit */ } else { this_mac->queue[queue_no].rx_desc_tab[next_rx_desc_index].addr_low = (uint32_t)((uint64_t)rx_pkt_buffer); } #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) this_mac->queue[queue_no].rx_desc_tab[next_rx_desc_index].addr_high = (uint32_t)((uint64_t)rx_pkt_buffer >> 32); #endif this_mac->queue[queue_no].rx_caller_info[next_rx_desc_index] = p_user_data; /* If the RX is found disabled, then it might be because this is the first time a packet is scheduled for reception or the RX ENABLE is made zero by RX overflow or RX bus error. In either case, this function tries to schedule the current packet for reception. Don't bother if we are not enabling interrupt at the end of all this... */ if(MSS_MAC_INT_ARM == enable) { /* * Starting receive operations off with a chain of buffers set up. */ if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONTROL &= ~GEM_ENABLE_RECEIVE; this_mac->emac_base->RECEIVE_Q_PTR = (uint32_t)((uint64_t)this_mac->queue[queue_no].rx_desc_tab); #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) this_mac->emac_base->UPPER_RX_Q_BASE_ADDR = (uint32_t)((uint64_t)this_mac->queue[queue_no].rx_desc_tab >> 32); #endif this_mac->emac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } else { this_mac->mac_base->NETWORK_CONTROL &= ~GEM_ENABLE_RECEIVE; *(this_mac->queue[queue_no].receive_q_ptr) = (uint32_t)((uint64_t)this_mac->queue[queue_no].rx_desc_tab); #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) this_mac->mac_base->UPPER_RX_Q_BASE_ADDR = (uint32_t)((uint64_t)this_mac->queue[queue_no].rx_desc_tab >> 32); #endif this_mac->mac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } } else { if(MSS_MAC_INT_DISABLE != enable) { uint32_t temp_cr; if(0U != this_mac->is_emac) { temp_cr = this_mac->emac_base->NETWORK_CONTROL; } else { temp_cr = this_mac->mac_base->NETWORK_CONTROL; } if(0U == (temp_cr & GEM_ENABLE_RECEIVE)) { /* RX disabled so restart it... */ if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } else { this_mac->mac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } } } } /* Point the next_rx_desc to next free descriptor in the ring */ /* Wrap around in case next descriptor is pointing to last in the ring */ ++this_mac->queue[queue_no].next_free_rx_desc_index; this_mac->queue[queue_no].next_free_rx_desc_index %= MSS_MAC_RX_RING_SIZE; } /* * Only call Ethernet Interrupt Enable function if the user says so. * See note above for disable... */ if((MSS_MAC_INT_ARM == enable) && (0U == this_mac->queue[queue_no].in_isr)) { if(0U != this_mac->use_local_ints) { __enable_local_irq(this_mac->mac_q_int[queue_no]); } else { PLIC_EnableIRQ(this_mac->mac_q_int[queue_no]); /* Single interrupt from GEM? */ } } } return status; } #if defined(MSS_MAC_UNH_TEST) /****************************************************************************** * We take a lot of short cuts here as we "know" we are called from ISR and * are only supposed to be called for pMAC queue 0, we assume the packet buffer * for this descriptor has not changed, we ignore caller_info etc... */ void MSS_MAC_receive_pkt_isr ( mss_mac_instance_t *this_mac ) { mss_mac_queue_t *p_queue = &this_mac->queue[0]; --p_queue->nb_available_rx_desc; /* clear used bit and time stamp present bit */ p_queue->rx_desc_tab[p_queue->next_free_rx_desc_index].addr_low &= 0xFFFFFFFA; /* Point the next_rx_desc to next free descriptor in the ring */ /* Wrap around in case next descriptor is pointing to last in the ring */ ++p_queue->next_free_rx_desc_index; p_queue->next_free_rx_desc_index %= MSS_MAC_RX_RING_SIZE; } #endif /******************************************************************************* * See mss_ethernet_mac.h for details of how to use this function. */ int32_t MSS_MAC_send_pkt ( mss_mac_instance_t *this_mac, uint32_t queue_no, uint8_t const * tx_buffer, uint32_t length, void * p_user_data ) { /* * Simplified transmit operation which depends on the following assumptions: * * 1. The TX DMA buffer size is big enough to contain a full packet. * 2. We will only transmit one packet at a time. * 3. We wait for any outstanding transmits on other queues to complete * because we need to ALWAYS write to the queue 0 pointer to trigger * internal restart of the DMA process. * * We do transmission by using two buffer descriptors. The first contains the * packet to transmit and the second is a dummy one with the USED bit set. This * halts transmission once the first packet is transmitted. We always reset the * TX DMA to point to the first packet when we send a packet so we don't have * to juggle buffer positions or worry about wrap. */ uint32_t tx_length = length; int32_t status = MSS_MAC_ERR_NOT_DONE; /* * Hack for system testing. If b31 of the tx_length is 1 then we want to * send without a CRC appended. This is used for loopback testing where we * can get the GEM to receive packets with CRC appended and send them * straight back. */ int32_t no_crc = 0; volatile int delay; volatile uint32_t *p_nw_control; volatile uint32_t *p_tx_status; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { /* Is config option for disabling CRC set? */ if(MSS_MAC_CRC_DISABLE == this_mac->append_CRC) { no_crc = 1; } /* Or has the user requested just this packet? */ if(0U != (tx_length & 0x80000000U)) { no_crc = 1; } tx_length &= 0x7FFFFFFFU; /* Make sure high bit is now clear */ /* Make this function atomic w.r.to EMAC interrupt */ /* PLIC_DisableIRQ() et al should not be called from the associated interrupt... */ if(0U == this_mac->queue[queue_no].in_isr) { if(0U != this_mac->use_local_ints) { __disable_local_irq(this_mac->mac_q_int[queue_no]); } else { PLIC_DisableIRQ(this_mac->mac_q_int[queue_no]); /* Single interrupt from GEM? */ } } ASSERT(NULL_POINTER != tx_buffer); ASSERT(0U != tx_length); ASSERT(IS_WORD_ALIGNED(tx_buffer)); /* We use these a lot and this cuts down on conditional code later on */ if(0U != this_mac->is_emac) { p_nw_control = &this_mac->emac_base->NETWORK_CONTROL; p_tx_status = &this_mac->emac_base->TRANSMIT_STATUS; } else { p_nw_control = &this_mac->mac_base->NETWORK_CONTROL; p_tx_status = &this_mac->mac_base->TRANSMIT_STATUS; } #if defined(MSS_MAC_SIMPLE_TX_QUEUE) if(this_mac->queue[queue_no].nb_available_tx_desc == (uint32_t)MSS_MAC_TX_RING_SIZE) { /* Queue is fully available for transmit so clear retry count */ this_mac->queue[queue_no].tries = 0UL; /* Make sure transmit is enabled */ if(0 == (*p_nw_control & GEM_ENABLE_TRANSMIT)) { *p_nw_control = *p_nw_control | GEM_ENABLE_TRANSMIT; } /* * Wait for pending transmits to complete as you cannot alter * tx queue pointers while transmit is active... */ while(0 != (*p_tx_status & GEM_TRANSMIT_GO)) { delay++; /* Empty loop will cause debug issues... */ } /* Make sure queue is currently disabled */ *this_mac->queue[queue_no].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[queue_no].tx_desc_tab) | 1UL; /* Set up tx descriptor for this packet */ this_mac->queue[queue_no].nb_available_tx_desc--; this_mac->queue[queue_no].current_tx_desc = 0; this_mac->queue[queue_no].tx_desc_tab[0].addr_low = (uint32_t)((uint64_t)tx_buffer); /* Mark as last buffer for frame */ this_mac->queue[queue_no].tx_desc_tab[0].status = (tx_length & GEM_TX_DMA_BUFF_LEN) | GEM_TX_DMA_LAST; if(0 != no_crc) { this_mac->queue[queue_no].tx_desc_tab[0].status |= GEM_TX_DMA_NO_CRC; } #if 0 this_mac->tx_desc_tab[0].status = (tx_length & GEM_TX_DMA_BUFF_LEN) | GEM_TX_DMA_LAST | GEM_TX_DMA_USED; /* PMCS deliberate error ! */ #endif #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) this_mac->queue[queue_no].tx_desc_tab[0].addr_high = (uint32_t)((uint64_t)tx_buffer >> 32); this_mac->queue[queue_no].tx_desc_tab[0].unused = 0U; #endif this_mac->queue[queue_no].tx_desc_tab[1].status = GEM_TX_DMA_WRAP | GEM_TX_DMA_LAST | GEM_TX_DMA_USED ; this_mac->queue[queue_no].tx_caller_info[0] = p_user_data; *this_mac->queue[queue_no].transmit_q_ptr = (uint32_t)((uint64_t)&this_mac->queue[queue_no].tx_desc_tab[0]); /* * If not queue 0 then we need to write disabled value to queue 0 to * get the DMA engine reloaded... */ if(0 != queue_no) { *this_mac->queue[0].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[0].tx_desc_tab) | 1U; } /* When transmitting at 10M, this delay is needed, 625MHz cpu clock - YMMV */ for(delay = 0; delay != 8; delay++) { } *p_nw_control = *p_nw_control | GEM_TRANSMIT_START; this_mac->queue[queue_no].egress += tx_length; status = MSS_MAC_ERR_OK; } else { /* * Queue not available so lets check some things... */ if(0 == (*p_nw_control & GEM_ENABLE_TRANSMIT)) { /* * TX is currently disabled so re-enable it and restart the last * operation on this queue to see if that gets us a completion. */ this_mac->queue[queue_no].tx_reenable++; *p_nw_control = *p_nw_control | GEM_ENABLE_TRANSMIT; *this_mac->queue[queue_no].transmit_q_ptr = (uint32_t)((uint64_t)&this_mac->queue[queue_no].tx_desc_tab[0]); if(0 != queue_no) { *this_mac->queue[0].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[0].tx_desc_tab) | 1U; } /* When transmitting at 10M, this delay is needed, 625MHz cpu clock - YMMV */ for(delay = 0; delay != 8; delay++) { } *p_nw_control = *p_nw_control | GEM_TRANSMIT_START; } else { /* Kick the tx start bit in case we are stalled... */ this_mac->queue[queue_no].tx_restart++; *p_nw_control = *p_nw_control | GEM_TRANSMIT_START; } /* * TX might have completed since we entered the function but if we * seem to be spinning on this and buffers haven't been returned to * the queue then just give up and reset queue. */ if(this_mac->queue[queue_no].tx_desc_tab[0].status & GEM_TX_DMA_USED) { this_mac->queue[queue_no].tries++; if(this_mac->queue[queue_no].tries > 3) /* Been here too often? */ { /* Give up and reset FW queue count */ this_mac->queue[queue_no].nb_available_tx_desc = (uint32_t)MSS_MAC_TX_RING_SIZE; status = MSS_MAC_ERR_TX_TIMEOUT; } } if(this_mac->queue[queue_no].tx_desc_tab[0].status & (GEM_TX_DMA_RETRY_ERROR | GEM_TX_DMA_UNDERRUN | GEM_TX_DMA_BUS_ERROR | GEM_TX_DMA_LATE_COL_ERROR | GEM_TX_DMA_OFFLOAD_ERRORS)) { /* Give up and reset FW queue count */ this_mac->queue[queue_no].nb_available_tx_desc = (uint32_t)MSS_MAC_TX_RING_SIZE; status = MSS_MAC_ERR_TX_FAIL; } } #else /* TBD PMCS need to implement multi packet queuing... */ #warning "Nothing implemented for multi packet tx yet" #endif /* Ethernet Interrupt Enable function. */ /* PLIC_DisableIRQ() et al should not be called from the associated interrupt... */ if(0U == this_mac->queue[queue_no].in_isr) { if(0U != this_mac->use_local_ints) { __enable_local_irq(this_mac->mac_q_int[queue_no]); } else { PLIC_EnableIRQ(this_mac->mac_q_int[queue_no]); /* Single interrupt from GEM? */ } } } return status; } /******************************************************************************* * See mss_ethernet_mac.h for details of how to use this function. */ int32_t MSS_MAC_send_pkts ( mss_mac_instance_t *this_mac, uint32_t queue_mask, mss_mac_tx_pkt_info_t *p_packets ) { /* * Multi packet, multi queue transmit operation which depends on the * following assumptions: * * 1. queue_mask indicates the queues which may be involved so that we can * properly handle ISR vs normal calling. * Set the mask to 0x0000000F to indicate all queues if there is any * uncertainty. * Set the mask to 0xFFFFFFF0 to indicate we are being called from a GEM * ISR. * 2. We wait for any outstanding transmits on other queues to complete * because we need to ALWAYS write to the queue 0 pointer to trigger * internal restart of the DMA process. * 3. We accept at most MSS_MAC_TX_RING_SIZE-1 packets as the final packet * in the chain is a dummy one to get the TX DMA engine to stop. Any * packets beyond that are silently dropped. * * We do transmission by using multiple buffer descriptors. The initial * descriptors are for the packet(s) to transmit and the last is a dummy one * with the USED bit set. This halts transmission once the first packet is * transmitted. We always reset the TX DMA to point to the first packet when * we send packets so we don't have to juggle buffer positions or worry * about wrap. */ uint32_t mask_bit; uint32_t counter; int32_t status = MSS_MAC_ERR_NOT_DONE; bool tx_done; uint32_t queues; mss_mac_tx_pkt_info_t *tx_rover; /* */ int32_t no_crc = 0; volatile int delay; volatile uint32_t *p_nw_control; volatile uint32_t *p_tx_status; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { /* We use these a lot and this cuts down on conditional code later on */ if(0U != this_mac->is_emac) { p_nw_control = &this_mac->emac_base->NETWORK_CONTROL; p_tx_status = &this_mac->emac_base->TRANSMIT_STATUS; queues = 1; } else { p_nw_control = &this_mac->mac_base->NETWORK_CONTROL; p_tx_status = &this_mac->mac_base->TRANSMIT_STATUS; queues = 4; } /* PLIC_DisableIRQ() et al should not be called from the associated interrupt... */ if(0xFFFFFFF0U != queue_mask) /* Not called from ISR */ { /* * Make this function atomic w.r.to EMAC interrupt for the queues we * are using and queue 0. */ mask_bit = 1; queue_mask |= 1U; /* Force queue 0 interrupt disable */ for(counter = 0; counter != 4; counter++) { if(0 != (mask_bit & queue_mask)) { if(0U != this_mac->use_local_ints) { __disable_local_irq(this_mac->mac_q_int[counter]); } else { PLIC_DisableIRQ(this_mac->mac_q_int[counter]); /* Single interrupt from GEM? */ } } mask_bit <<= 1; } } tx_done = true; for(counter = 0; counter != queues; counter++) { if(this_mac->queue[counter].nb_available_tx_desc != (uint32_t)MSS_MAC_TX_RING_SIZE) { tx_done = false; /* Not done if any queue still active */ } } if(tx_done) { /* Queues are fully available for transmit so clear retry counts */ for(counter = 0; counter != queues; counter++) { /* Make sure queue is currently disabled */ *this_mac->queue[counter].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[counter].tx_desc_tab) | 1UL; this_mac->queue[counter].tries = 0UL; } /* Make sure transmit is enabled */ if(0 == (*p_nw_control & GEM_ENABLE_TRANSMIT)) { *p_nw_control = *p_nw_control | GEM_ENABLE_TRANSMIT; } /* * Wait for pending transmits to complete as you cannot alter * tx queue pointers while transmit is active... */ while(0 != (*p_tx_status & GEM_TRANSMIT_GO)) { delay++; /* Empty loop will cause debug issues... */ } /* All is quiet now so let's construct the TX queue(s) */ tx_rover = p_packets; while(0 != tx_rover->length) { uint32_t tx_length; mss_mac_queue_t *p_queue; tx_length = tx_rover->length; /* Is config option for disabling CRC set? */ if(MSS_MAC_CRC_DISABLE == this_mac->append_CRC) { no_crc = 1; } /* Or has the user requested just this packet? */ if(0U != (tx_length & 0x80000000U)) { no_crc = 1; tx_length &= 0x7FFFFFFFU; /* Make sure high bit is now clear */ } /* * We need to leave one spare at the end to halt the DMA when we * are done... */ p_queue = &this_mac->queue[tx_rover->queue_no]; if(p_queue->nb_available_tx_desc > 1) { uint32_t position; mss_mac_tx_desc_t *p_desc; /* Set up tx descriptor for this packet */ position = MSS_MAC_TX_RING_SIZE - p_queue->nb_available_tx_desc; p_desc = &p_queue->tx_desc_tab[position]; p_queue->nb_available_tx_desc--; p_queue->current_tx_desc = 0; p_desc->addr_low = (uint32_t)((uint64_t)tx_rover->tx_buffer); /* Mark as last buffer for frame */ p_desc->status = (tx_length & GEM_TX_DMA_BUFF_LEN) | GEM_TX_DMA_LAST; if(0 != no_crc) { p_desc->status |= GEM_TX_DMA_NO_CRC; } #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) p_desc->addr_high = (uint32_t)((uint64_t)tx_rover->tx_buffer >> 32); p_desc->unused = 0U; #endif (p_desc + 1)->status |= GEM_TX_DMA_WRAP | GEM_TX_DMA_USED; p_queue->tx_caller_info[position] = tx_rover->p_user_data; p_queue->egress += tx_length; } tx_rover++; } counter = queues; while(0 != counter) { counter--; if(this_mac->queue[counter].nb_available_tx_desc != (uint32_t)MSS_MAC_TX_RING_SIZE) { *this_mac->queue[counter].transmit_q_ptr = (uint32_t)((uint64_t)&this_mac->queue[counter].tx_desc_tab[0]); } } /* If queue 0 not used we still need a dummy write... */ if(this_mac->queue[0].nb_available_tx_desc == (uint32_t)MSS_MAC_TX_RING_SIZE) { *this_mac->queue[0].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[0].tx_desc_tab) | 1U; } /* When transmitting at 10M, this delay is needed, 625MHz cpu clock - YMMV */ for(delay = 0; delay != 8; delay++) { } *p_nw_control = *p_nw_control | GEM_TRANSMIT_START; status = MSS_MAC_ERR_OK; } else /* tx_done */ { /* * Queues not available so lets check some things... */ if(0 == (*p_nw_control & GEM_ENABLE_TRANSMIT)) { /* * TX is currently disabled so re-enable it and restart the last * operation on the queues to see if that gets us a completion. */ counter = queues; while(0 != counter) { counter--; if(this_mac->queue[counter].nb_available_tx_desc != (uint32_t)MSS_MAC_TX_RING_SIZE) { bool test_done = false; mss_mac_tx_desc_t *p_descriptor; /* Scan descriptor lists to see where they are at */ p_descriptor = &this_mac->queue[counter].tx_desc_tab[0]; while(!test_done) { if((p_descriptor->status & GEM_TX_DMA_USED) && (p_descriptor->status & GEM_TX_DMA_WRAP)) { /* * End of chain and no unsent packets... * Mark as all sent and disable queue. */ *this_mac->queue[counter].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[counter].tx_desc_tab) | 1U; this_mac->queue[counter].nb_available_tx_desc = (uint32_t)MSS_MAC_TX_RING_SIZE; test_done = true; } else if(p_descriptor->status & GEM_TX_DMA_USED) { /* Used but not last so try next one */ p_descriptor++; } else /* Not used - restart from here */ { this_mac->queue[counter].tx_reenable++; *this_mac->queue[counter].transmit_q_ptr = (uint32_t)((uint64_t)p_descriptor); test_done = true; } } } } *p_nw_control = *p_nw_control | GEM_ENABLE_TRANSMIT; /* When transmitting at 10M, this delay is needed, 625MHz cpu clock - YMMV */ for(delay = 0; delay != 8; delay++) { } *p_nw_control = *p_nw_control | GEM_TRANSMIT_START; } else { /* Kick the tx start bit in case we are stalled... */ this_mac->queue[0].tx_restart++; *p_nw_control = *p_nw_control | GEM_TRANSMIT_START; } /* * TX might have completed since we entered the function but if we * seem to be spinning on this and buffers haven't been returned to * the queue then just give up and reset queue. */ for(counter = 0; counter != queues; counter++) { bool test_done = false; bool all_sent = false; mss_mac_tx_desc_t *p_descriptor; /* Scan descriptor lists to see where they are at */ p_descriptor = &this_mac->queue[counter].tx_desc_tab[0]; while(!test_done) { if((p_descriptor->status & GEM_TX_DMA_USED) && (p_descriptor->status & GEM_TX_DMA_WRAP)) { /* * End of chain and no unsent packets... * Mark as all sent and disable queue. */ all_sent = true; test_done = true; } else if(p_descriptor->status & GEM_TX_DMA_USED) { /* Used but not last so try next one */ p_descriptor++; } else /* Not used - not all sent yet */ { test_done = true; } } if(true == all_sent) { this_mac->queue[counter].tries++; if(this_mac->queue[counter].tries > 3) /* Been here too often? */ { /* Give up and reset FW queue count */ this_mac->queue[counter].nb_available_tx_desc = (uint32_t)MSS_MAC_TX_RING_SIZE; status = MSS_MAC_ERR_TX_TIMEOUT; } } } for(counter = 0; counter != queues; counter++) /* Scan for TX fails */ { bool test_done = false; mss_mac_tx_desc_t *p_descriptor; /* Scan descriptor lists to see where they are at */ p_descriptor = &this_mac->queue[counter].tx_desc_tab[0]; while(!test_done) { if(p_descriptor->status & (GEM_TX_DMA_RETRY_ERROR | GEM_TX_DMA_UNDERRUN | GEM_TX_DMA_BUS_ERROR | GEM_TX_DMA_LATE_COL_ERROR | GEM_TX_DMA_OFFLOAD_ERRORS)) { /* Give up and reset FW queue count on first fail */ this_mac->queue[counter].nb_available_tx_desc = (uint32_t)MSS_MAC_TX_RING_SIZE; status = MSS_MAC_ERR_TX_FAIL; test_done = true; } if(p_descriptor->status & GEM_TX_DMA_WRAP) { /* End of chain so bail */ test_done = true; } p_descriptor++; } } } /* Ethernet Interrupt Enable function. */ /* PLIC_DisableIRQ() et al should not be called from the associated interrupt... */ if(0xFFFFFFF0U != queue_mask) /* Not called from ISR */ { mask_bit = 1; for(counter = 0; counter != 4; counter++) { if(0 != (mask_bit & queue_mask)) { if(0U != this_mac->use_local_ints) { __enable_local_irq(this_mac->mac_q_int[counter]); } else { PLIC_EnableIRQ(this_mac->mac_q_int[counter]); /* Single interrupt from GEM? */ } } mask_bit <<= 1; } } } return status; } #if defined(MSS_MAC_SPEED_TEST) /* Very stripped down queue 0 packet blaster - assumes full tx queue0 is being * used on pMAC and does little or no checking. Don't call from GEM interrupts! * Don't use with local interrupts or eMAC either... */ void copy8b(uint64_t *dest, uint64_t *source, uint32_t count) { while(count--) *dest++ = *source++; } int32_t MSS_MAC_send_pkts_fast ( mss_mac_instance_t *this_mac, mss_mac_tx_desc_t *descriptors, uint32_t tx_count ) { /* * Multi packet, multi queue transmit operation which depends on the * following assumptions: * * 1. queue_mask indicates the queues which may be involved so that we can * properly handle ISR vs normal calling. * Set the mask to 0x0000000F to indicate all queues if there is any * uncertainty. * Set the mask to 0xFFFFFFF0 to indicate we are being called from a GEM * ISR. * 2. We wait for any outstanding transmits on other queues to complete * because we need to ALWAYS write to the queue 0 pointer to trigger * internal restart of the DMA process. * 3. We accept at most MSS_MAC_TX_RING_SIZE-1 packets as the final packet * in the chain is a dummy one to get the TX DMA engine to stop. Any * packets beyond that are silently dropped. * * We do transmission by using multiple buffer descriptors. The initial * descriptors are for the packet(s) to transmit and the last is a dummy one * with the USED bit set. This halts transmission once the first packet is * transmitted. We always reset the TX DMA to point to the first packet when * we send packets so we don't have to juggle buffer positions or worry * about wrap. */ volatile int delay; volatile uint32_t *p_nw_control; volatile uint32_t *p_tx_status; p_nw_control = &this_mac->mac_base->NETWORK_CONTROL; p_tx_status = &this_mac->mac_base->TRANSMIT_STATUS; /* Wait for queue to empty */ while(this_mac->queue[0].nb_available_tx_desc != (uint32_t)MSS_MAC_TX_RING_SIZE) { /* Make sure transmit stays enabled */ if(0 == (*p_nw_control & GEM_ENABLE_TRANSMIT)) { *p_nw_control = *p_nw_control | GEM_ENABLE_TRANSMIT; } } PLIC_DisableIRQ(this_mac->mac_q_int[0]); /* Single interrupt from GEM? */ /* * Wait for pending transmits to complete as you cannot alter * tx queue pointers while transmit is active... */ while(0 != (*p_tx_status & GEM_TRANSMIT_GO)) { delay++; /* Empty loop will cause debug issues... */ } *this_mac->queue[0].transmit_q_ptr = (uint32_t)((uint64_t)this_mac->queue[0].tx_desc_tab) | 1UL; this_mac->queue[0].tries = 0UL; /* All is quiet now so let's construct the TX queue(s) */ copy8b((uint64_t *)this_mac->queue[0].tx_desc_tab, (uint64_t *)descriptors, sizeof(this_mac->queue[0].tx_desc_tab) / 8); this_mac->queue[0].egress += tx_count; this_mac->queue[0].nb_available_tx_desc = 1; this_mac->queue[0].current_tx_desc = 0; *this_mac->queue[0].transmit_q_ptr = (uint32_t)((uint64_t)&this_mac->queue[0].tx_desc_tab[0]); *p_nw_control = *p_nw_control | GEM_TRANSMIT_START; PLIC_EnableIRQ(this_mac->mac_q_int[0]); /* Single interrupt from GEM? */ return(MSS_MAC_ERR_OK); } #endif /* defined(MSS_MAC_SPEED_TEST) */ /****************************************************************************** * */ #if defined(USING_LWIP) extern BaseType_t g_mac_context_switch; #endif #if defined(USING_FREERTOS) extern UBaseType_t uxCriticalNesting; #endif #if defined(TARGET_ALOE) uint8_t MAC0_plic_53_IRQHandler(void); uint8_t MAC0_plic_53_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 0U); #endif return(EXT_IRQ_KEEP_ENABLED); } #else /****************************************************************************** * */ uint8_t mac0_int_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 0U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac0_queue1_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 1U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 1U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac0_queue2_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 2U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 2U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac0_queue3_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 3U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 3U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac0_emac_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_emac0, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_emac0, 0U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac0_mmsl_plic_IRQHandler(void) { g_mac0.mac_base->MMSL_INT_DISABLE = 0x3F; /* Acknowledge ints */ return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac1_int_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 0U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac1_queue1_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 1U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 1U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac1_queue2_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 2U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 2U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac1_queue3_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 3U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 3U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac1_emac_plic_IRQHandler(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_emac1, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_emac1, 0U); #endif return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ uint8_t mac1_mmsl_plic_IRQHandler(void) { g_mac1.mac_base->MMSL_INT_DISABLE = 0x3F; /* Acknowledge ints */ return(EXT_IRQ_KEEP_ENABLED); } /****************************************************************************** * */ /* U54 1 */ void mac_mmsl_u54_1_local_IRQHandler_3(void) { g_mac0.mac_base->MMSL_INT_DISABLE = 0x3F; /* Acknowledge ints */ } /****************************************************************************** * */ void mac_emac_u54_1_local_IRQHandler_4(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_emac0, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_emac0, 0U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue3_u54_1_local_IRQHandler_5(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 3U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 3U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue2_u54_1_local_IRQHandler_6(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 2U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 2U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue1_u54_1_local_IRQHandler_7(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 1U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 1U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_int_u54_1_local_IRQHandler_8(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 0U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ /* U54 2 */ void mac_mmsl_u54_2_local_IRQHandler_3(void) { g_mac0.mac_base->MMSL_INT_DISABLE = 0x3F; /* Acknowledge ints */ } /****************************************************************************** * */ void mac_emac_u54_2_local_IRQHandler_4(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_emac0, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_emac0, 0U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue3_u54_2_local_IRQHandler_5(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 3U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 3U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue2_u54_2_local_IRQHandler_6(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 2U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 2U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue1_u54_2_local_IRQHandler_7(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 1U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 1U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_int_u54_2_local_IRQHandler_8(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac0, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac0, 0U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ /* U54 3 */ void mac_mmsl_u54_3_local_IRQHandler_3(void) { g_mac1.mac_base->MMSL_INT_DISABLE = 0x3F; /* Acknowledge ints */ } /****************************************************************************** * */ void mac_emac_u54_3_local_IRQHandler_4(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_emac1, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_emac1, 0U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue3_u54_3_local_IRQHandler_5(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 3U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 3U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue2_u54_3_local_IRQHandler_6(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 2U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 2U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue1_u54_3_local_IRQHandler_7(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 1U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 1U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_int_u54_3_local_IRQHandler_8(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 0U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ /* U54 4 */ void mac_mmsl_u54_4_local_IRQHandler_3(void) { g_mac1.mac_base->MMSL_INT_DISABLE = 0x3F; /* Acknowledge ints */ } /****************************************************************************** * */ void mac_emac_u54_4_local_IRQHandler_4(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_emac1, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_emac1, 0U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue3_u54_4_local_IRQHandler_5(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 3U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 3U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue2_u54_4_local_IRQHandler_6(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 2U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 2U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_queue1_u54_4_local_IRQHandler_7(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 1U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 1U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } /****************************************************************************** * */ void mac_int_u54_4_local_IRQHandler_8(void) { #if defined(USING_FREERTOS) uxCriticalNesting++; generic_mac_irq_handler(&g_mac1, 0U); uxCriticalNesting--; #else generic_mac_irq_handler(&g_mac1, 0U); #endif #if defined(USING_LWIP) if(0 != g_mac_context_switch) { g_mac_context_switch = 0; vPortYieldISR(); } #endif } #endif /* Define the following if your GEM is configured to clear on read for int flags * In this case you should not write to the int status reg ... */ /* #define GEM_FLAGS_CLR_ON_RD */ static void generic_mac_irq_handler(mss_mac_instance_t *this_mac, uint64_t queue_no) { volatile uint32_t int_pending; /* We read and hold a working copy as many * of the bits can be clear on read if * the GEM is configured that way... */ uint32_t volatile *rx_status; /* Address of receive status register */ uint32_t volatile *tx_status; /* Address of transmit status register */ uint32_t volatile *int_status; /* Address of interrupt status register */ mss_mac_queue_t *p_queue; p_queue = &this_mac->queue[queue_no]; p_queue->in_isr = 1U; int_status = p_queue->int_status; int_pending = *int_status; if(0U != this_mac->is_emac) { rx_status = &this_mac->emac_base->RECEIVE_STATUS; tx_status = &this_mac->emac_base->TRANSMIT_STATUS; } else { rx_status = &this_mac->mac_base->RECEIVE_STATUS; tx_status = &this_mac->mac_base->TRANSMIT_STATUS; } /* * Note, in the following code we generally clear any flags first and then * handle the condition as this allows any new events which occur in the * course of the ISR to be picked up later. */ /* * Packet received interrupt - first in line as most time critical. Followed * by transmit interrupt and then the less frequent ones. */ if((int_pending & GEM_RECEIVE_COMPLETE) != 0U) { *rx_status = GEM_FRAME_RECEIVED; #if !defined(GEM_FLAGS_CLR_ON_RD) #if defined(TARGET_ALOE) *int_status = (uint32_t)3U; /* PMCS: Should be 2 but that does not work on Aloe... */ #else *int_status = (uint32_t)2U; #endif rxpkt_handler(this_mac, queue_no); p_queue->overflow_counter = 0U; /* Reset counter as we have received something */ #endif } /* Transmit packet sent interrupt */ if((int_pending & GEM_TRANSMIT_COMPLETE) != 0U) { if((*tx_status & GEM_STAT_TRANSMIT_COMPLETE) != 0U) /* If loopback test or other hasn't taken care of this... */ { *tx_status = GEM_STAT_TRANSMIT_COMPLETE; *int_status = GEM_TRANSMIT_COMPLETE; #if !defined(GEM_FLAGS_CLR_ON_RD) #endif txpkt_handler(this_mac, queue_no); } } /* * RX and TX are the majority cases so see if all done rather than testing * remaining one by one. Move on to one by one testing if not finished. Cuts * down on interrupt processing impact for 'normal' case... */ if((int_pending & ~(GEM_RECEIVE_COMPLETE | GEM_TRANSMIT_COMPLETE)) != 0U) { if((int_pending & GEM_RECEIVE_OVERRUN_INT) != 0U) { *rx_status = GEM_RECEIVE_OVERRUN; #if !defined(GEM_FLAGS_CLR_ON_RD) *int_status = GEM_RECEIVE_OVERRUN_INT; #endif p_queue->overflow_counter++; p_queue->rx_overflow++; } if((int_pending & GEM_RX_USED_BIT_READ) != 0U) { *rx_status = GEM_BUFFER_NOT_AVAILABLE; #if !defined(GEM_FLAGS_CLR_ON_RD) *int_status = GEM_RX_USED_BIT_READ; #endif rxpkt_handler(this_mac, queue_no); p_queue->rx_overflow++; p_queue->overflow_counter++; } if((int_pending & GEM_RESP_NOT_OK_INT) != 0U) /* Hope this is transient and restart rx... */ { *rx_status = GEM_RX_RESP_NOT_OK; /* One of them did it... */ *tx_status = GEM_TX_RESP_NOT_OK; #if !defined(GEM_FLAGS_CLR_ON_RD) *int_status = GEM_RESP_NOT_OK_INT; #endif if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } else { this_mac->mac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } p_queue->hresp_error++; } /* * This may cause more problems than it cures so disabling for now until we * figure out a better strategy... */ #if 0 if(p_queue->overflow_counter > 4U) /* looks like we are stuck in a rut here... */ { uint32_t descriptor; /* Restart receive operation from scratch */ p_queue->overflow_counter = 0U; p_queue->rx_restart++; if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONTROL &= ~GEM_ENABLE_RECEIVE; } else { this_mac->mac_base->NETWORK_CONTROL &= ~GEM_ENABLE_RECEIVE; } p_queue->nb_available_rx_desc = MSS_MAC_RX_RING_SIZE; p_queue->next_free_rx_desc_index = 0U; p_queue->first_rx_desc_index = 0U; for(descriptor = 0U; descriptor < MSS_MAC_RX_RING_SIZE; descriptor++) /* Discard everything */ { p_queue->rx_desc_tab[descriptor].addr_low &= ~GEM_RX_DMA_USED; } if(0U != this_mac->is_emac) { this_mac->emac_base->RECEIVE_Q_PTR = (uint32_t)((uint64_t)p_queue->rx_desc_tab); #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) this_mac->emac_base->UPPER_RX_Q_BASE_ADDR = (uint32_t)((uint64_t)p_queue->rx_desc_tab >> 32); #endif this_mac->emac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } else { this_mac->mac_base->RECEIVE_Q_PTR = (uint32_t)((uint64_t)p_queue->rx_desc_tab); #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) this_mac->mac_base->UPPER_RX_Q_BASE_ADDR = (uint32_t)((uint64_t)p_queue->rx_desc_tab >> 32); #endif this_mac->mac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } } #endif if((int_pending & GEM_PAUSE_FRAME_TRANSMITTED) != 0U) { *int_status = GEM_PAUSE_FRAME_TRANSMITTED; this_mac->tx_pause++; } if((int_pending & GEM_PAUSE_TIME_ELAPSED) != 0U) { *int_status = GEM_PAUSE_TIME_ELAPSED; this_mac->pause_elapsed++; } if((int_pending & GEM_PAUSE_FRAME_WITH_NON_0_PAUSE_QUANTUM_RX) != 0U) { *int_status = GEM_PAUSE_FRAME_WITH_NON_0_PAUSE_QUANTUM_RX; this_mac->rx_pause++; } /* Mask off checked ints and see if any left pending */ int_pending &= ~(GEM_RECEIVE_OVERRUN_INT | GEM_RX_USED_BIT_READ | GEM_RECEIVE_COMPLETE | GEM_TRANSMIT_COMPLETE | GEM_RESP_NOT_OK_INT | GEM_PAUSE_FRAME_TRANSMITTED | GEM_PAUSE_TIME_ELAPSED | GEM_PAUSE_FRAME_WITH_NON_0_PAUSE_QUANTUM_RX); if(0U != int_pending) { if((int_pending & GEM_AMBA_ERROR) != 0U) { /* *int_status = (uint32_t)0x60U; */ /* Should be 0x40 but that doesn't clear it... */ *int_status = (uint32_t)0x40U; *tx_status = GEM_STAT_AMBA_ERROR; *rx_status = GEM_AMBA_ERROR; p_queue->tx_amba_errors++; p_queue->nb_available_tx_desc = MSS_MAC_TX_RING_SIZE; } else { volatile int32_t index; ASSERT(0); /* Need to think about how to deal with this... */ while(1) { index++; } } } } p_queue->in_isr = 0U; } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_tx_callback ( mss_mac_instance_t *this_mac, uint32_t queue_no, mss_mac_transmit_callback_t tx_complete_handler ) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { this_mac->queue[queue_no].pckt_tx_callback = tx_complete_handler; } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_rx_callback ( mss_mac_instance_t *this_mac, uint32_t queue_no, mss_mac_receive_callback_t rx_callback ) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { this_mac->queue[queue_no].pckt_rx_callback = rx_callback; } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_change_speed ( mss_mac_instance_t *this_mac, uint32_t speed_duplex_select, mss_mac_speed_mode_t speed_mode ) { uint32_t temp_cr; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { /* Only change HW settings if there is a change in status */ if((this_mac->speed_duplex_select |= speed_duplex_select) || (this_mac->speed_mode != speed_mode)) { this_mac->speed_duplex_select = speed_duplex_select; this_mac->speed_mode = speed_mode; /* Apply settings to the PHY */ this_mac->phy_set_link_speed(this_mac, this_mac->speed_duplex_select, speed_mode); this_mac->phy_autonegotiate(this_mac); /* Do this in case it is needed */ if(MSS_MAC_SPEED_AN == speed_mode) /* Apply results of AN to MAC */ { update_mac_cfg(this_mac); } else { if(0U != this_mac->is_emac) { temp_cr = this_mac->emac_base->NETWORK_CONFIG; } else { temp_cr = this_mac->mac_base->NETWORK_CONFIG; } temp_cr &= ~(GEM_GIGABIT_MODE_ENABLE | GEM_SPEED | GEM_FULL_DUPLEX); if((MSS_MAC_1000_HDX == this_mac->speed_mode) || (MSS_MAC_1000_FDX == this_mac->speed_mode)) { #if defined(TARGET_ALOE) *GEMGXL_tx_clk_sel = (uint32_t)0U; *GEMGXL_speed_mode = (uint32_t)2U; #endif temp_cr |= GEM_GIGABIT_MODE_ENABLE; } else { if((MSS_MAC_100_HDX == this_mac->speed_mode) || (MSS_MAC_100_FDX == this_mac->speed_mode)) { #if defined(TARGET_ALOE) *GEMGXL_tx_clk_sel = (uint32_t)1U; *GEMGXL_speed_mode = (uint32_t)1U; #endif temp_cr |= (uint32_t)GEM_SPEED; } #if defined(TARGET_ALOE) else { *GEMGXL_tx_clk_sel = (uint32_t)1U; *GEMGXL_speed_mode = (uint32_t)0U; } #endif } /* Configure duplex mode */ if((MSS_MAC_10_FDX == this_mac->speed_mode) || (MSS_MAC_100_FDX == this_mac->speed_mode) || (MSS_MAC_1000_FDX == this_mac->speed_mode)) { temp_cr |= GEM_FULL_DUPLEX; } if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONFIG = temp_cr; } else { this_mac->mac_base->NETWORK_CONFIG = temp_cr; } } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_init_TSU(const mss_mac_instance_t *this_mac, const mss_mac_tsu_config_t *tsu_cfg) { uint32_t temp; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { temp = (tsu_cfg->sub_ns_inc & 0xFFU) << 24; temp |= (tsu_cfg->sub_ns_inc >> 8) & 0xFFFFU; if(0U != this_mac->is_emac) { #if 0 /* Shouldn't really allow setting of tsu through eMAC as it is slaved to pMAC TSU */ this_mac->emac_base->TSU_TIMER_INCR_SUB_NSEC = temp; this_mac->emac_base->TSU_TIMER_INCR = tsu_cfg->ns_inc; /* PMCS: I'm not 100% sure about the sequencing here... */ this_mac->emac_base->TSU_TIMER_MSB_SEC = tsu_cfg->secs_msb; this_mac->emac_base->TSU_TIMER_SEC = tsu_cfg->secs_lsb; this_mac->emac_base->TSU_TIMER_NSEC = tsu_cfg->nanoseconds; #endif } else { this_mac->mac_base->TSU_TIMER_INCR_SUB_NSEC = temp; this_mac->mac_base->TSU_TIMER_INCR = tsu_cfg->ns_inc; this_mac->mac_base->TSU_TIMER_MSB_SEC = tsu_cfg->secs_msb; this_mac->mac_base->TSU_TIMER_SEC = tsu_cfg->secs_lsb; this_mac->mac_base->TSU_TIMER_NSEC = tsu_cfg->nanoseconds; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_read_TSU(const mss_mac_instance_t *this_mac, mss_mac_tsu_time_t *tsu_time) { int32_t got_time = 0; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { do { if(0U != this_mac->is_emac) { tsu_time->secs_lsb = this_mac->emac_base->TSU_TIMER_SEC; tsu_time->secs_msb = this_mac->emac_base->TSU_TIMER_MSB_SEC; tsu_time->nanoseconds = this_mac->emac_base->TSU_TIMER_NSEC; /* Check for nanoseconds roll over and exit loop if none otherwise do again */ if(tsu_time->secs_lsb == this_mac->emac_base->TSU_TIMER_SEC) { got_time = 1; } } else { tsu_time->secs_lsb = this_mac->mac_base->TSU_TIMER_SEC; tsu_time->secs_msb = this_mac->mac_base->TSU_TIMER_MSB_SEC; tsu_time->nanoseconds = this_mac->mac_base->TSU_TIMER_NSEC; /* Check for nanoseconds roll over and exit loop if none otherwise do again */ if(tsu_time->secs_lsb == this_mac->mac_base->TSU_TIMER_SEC) { got_time = 1; } } } while(0 == got_time); } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_TSU_rx_mode(const mss_mac_instance_t *this_mac, mss_mac_tsu_mode_t tsu_mode) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { this_mac->emac_base->RX_BD_CONTROL = ((uint32_t)tsu_mode << GEM_BD_TS_MODE_SHIFT) & GEM_BD_TS_MODE; } else { this_mac->mac_base->RX_BD_CONTROL = ((uint32_t)tsu_mode << GEM_BD_TS_MODE_SHIFT) & GEM_BD_TS_MODE; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_TSU_tx_mode(const mss_mac_instance_t *this_mac, mss_mac_tsu_mode_t tsu_mode) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { this_mac->emac_base->TX_BD_CONTROL = ((uint32_t)tsu_mode << GEM_BD_TS_MODE_SHIFT) & GEM_BD_TS_MODE; } else { this_mac->mac_base->TX_BD_CONTROL = ((uint32_t)tsu_mode << GEM_BD_TS_MODE_SHIFT) & GEM_BD_TS_MODE; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ mss_mac_tsu_mode_t MSS_MAC_get_TSU_rx_mode(const mss_mac_instance_t *this_mac) { mss_mac_tsu_mode_t ret_val = MSS_MAC_TSU_MODE_DISABLED; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { ret_val = (mss_mac_tsu_mode_t)((this_mac->emac_base->RX_BD_CONTROL & GEM_BD_TS_MODE) >> GEM_BD_TS_MODE_SHIFT); } else { ret_val = (mss_mac_tsu_mode_t)((this_mac->mac_base->RX_BD_CONTROL & GEM_BD_TS_MODE) >> GEM_BD_TS_MODE_SHIFT); } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ mss_mac_tsu_mode_t MSS_MAC_get_TSU_tx_mode(const mss_mac_instance_t *this_mac) { mss_mac_tsu_mode_t ret_val= MSS_MAC_TSU_MODE_DISABLED; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { ret_val = (mss_mac_tsu_mode_t)((this_mac->emac_base->TX_BD_CONTROL & GEM_BD_TS_MODE) >> GEM_BD_TS_MODE_SHIFT); } else { ret_val = (mss_mac_tsu_mode_t)((this_mac->mac_base->TX_BD_CONTROL & GEM_BD_TS_MODE) >> GEM_BD_TS_MODE_SHIFT); } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_TSU_oss_mode(const mss_mac_instance_t *this_mac, mss_mac_oss_mode_t oss_mode) { volatile uint32_t temp_control; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { temp_control = this_mac->emac_base->NETWORK_CONTROL; } else { temp_control = this_mac->mac_base->NETWORK_CONTROL; } /* * Note. The docs don't say these are mutually exclusive but I don't think * it makes sense to allow both modes at once... */ if(MSS_MAC_OSS_MODE_DISABLED == oss_mode) { temp_control &= ~(GEM_OSS_CORRECTION_FIELD | GEM_ONE_STEP_SYNC_MODE); } else if(MSS_MAC_OSS_MODE_REPLACE == oss_mode) { temp_control &= ~GEM_OSS_CORRECTION_FIELD; temp_control |= GEM_ONE_STEP_SYNC_MODE; } else { if(MSS_MAC_OSS_MODE_ADJUST == oss_mode) { temp_control |= GEM_OSS_CORRECTION_FIELD; temp_control &= ~GEM_ONE_STEP_SYNC_MODE; } } if(0U != this_mac->is_emac) { this_mac->emac_base->NETWORK_CONTROL = temp_control; } else { this_mac->mac_base->NETWORK_CONTROL = temp_control; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ mss_mac_oss_mode_t MSS_MAC_get_TSU_oss_mode(const mss_mac_instance_t *this_mac) { mss_mac_oss_mode_t ret_val = MSS_MAC_OSS_MODE_DISABLED; volatile uint32_t temp_control; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { temp_control = this_mac->emac_base->NETWORK_CONTROL; } else { temp_control = this_mac->mac_base->NETWORK_CONTROL; } /* * Note. The docs don't say these are mutually exclusive but I don't think * it makes sense to allow both modes at once so report this as invalid... */ if((GEM_OSS_CORRECTION_FIELD | GEM_ONE_STEP_SYNC_MODE) == (temp_control & (GEM_OSS_CORRECTION_FIELD | GEM_ONE_STEP_SYNC_MODE))) { ret_val = MSS_MAC_OSS_MODE_INVALID; } else if(GEM_OSS_CORRECTION_FIELD == (temp_control & GEM_OSS_CORRECTION_FIELD)) { ret_val = MSS_MAC_OSS_MODE_ADJUST; } else if(GEM_ONE_STEP_SYNC_MODE == (temp_control & GEM_ONE_STEP_SYNC_MODE)) { ret_val = MSS_MAC_OSS_MODE_REPLACE; } else { ret_val = MSS_MAC_OSS_MODE_DISABLED; } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_TSU_unicast_addr(const mss_mac_instance_t *this_mac, mss_mac_tsu_addr_t select, uint32_t ip_address) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { if(MSS_MAC_TSU_UNICAST_RX == select) { this_mac->emac_base->RX_PTP_UNICAST = ip_address; } else { this_mac->emac_base->TX_PTP_UNICAST = ip_address; } } else { if(MSS_MAC_TSU_UNICAST_RX == select) { this_mac->mac_base->RX_PTP_UNICAST = ip_address; } else { this_mac->mac_base->TX_PTP_UNICAST = ip_address; } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint32_t MSS_MAC_get_TSU_unicast_addr(const mss_mac_instance_t *this_mac, mss_mac_tsu_addr_t select) { uint32_t ret_val = 0U; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { if(MSS_MAC_TSU_UNICAST_RX == select) { ret_val = this_mac->emac_base->RX_PTP_UNICAST; } else { ret_val = this_mac->emac_base->TX_PTP_UNICAST; } } else { if(MSS_MAC_TSU_UNICAST_RX == select) { ret_val = this_mac->mac_base->RX_PTP_UNICAST; } else { ret_val = this_mac->mac_base->TX_PTP_UNICAST; } } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_VLAN_only_mode(const mss_mac_instance_t *this_mac, bool enable) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { if(false == enable) { this_mac->emac_base->NETWORK_CONFIG &= ~GEM_DISCARD_NON_VLAN_FRAMES; } else { this_mac->emac_base->NETWORK_CONFIG |= GEM_DISCARD_NON_VLAN_FRAMES; } } else { if(false == enable) { this_mac->mac_base->NETWORK_CONFIG &= ~GEM_DISCARD_NON_VLAN_FRAMES; } else { this_mac->mac_base->NETWORK_CONFIG |= GEM_DISCARD_NON_VLAN_FRAMES; } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ bool MSS_MAC_get_VLAN_only_mode(const mss_mac_instance_t *this_mac) { bool ret_val = false; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { ret_val = 0U != (this_mac->emac_base->NETWORK_CONFIG & GEM_DISCARD_NON_VLAN_FRAMES); } else { ret_val = 0U != (this_mac->mac_base->NETWORK_CONFIG & GEM_DISCARD_NON_VLAN_FRAMES); } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_stacked_VLAN(const mss_mac_instance_t *this_mac, uint16_t tag) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { if(GEM_VLAN_ETHERTYPE_MIN > tag) { this_mac->emac_base->STACKED_VLAN = 0U; } else { this_mac->emac_base->STACKED_VLAN = (uint32_t)tag | GEM_ENABLE_PROCESSING; } } else { if(GEM_VLAN_ETHERTYPE_MIN > tag) { this_mac->mac_base->STACKED_VLAN = 0U; } else { this_mac->mac_base->STACKED_VLAN = (uint32_t)tag | GEM_ENABLE_PROCESSING; } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint16_t MSS_MAC_get_stacked_VLAN(const mss_mac_instance_t *this_mac) { uint16_t ret_val = GEM_VLAN_NO_STACK; /* Return 0 if stacked VLANs not enabled */ if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { if(0U != (this_mac->emac_base->STACKED_VLAN & GEM_ENABLE_PROCESSING)) { ret_val = (uint16_t)this_mac->emac_base->STACKED_VLAN; } } else { if(0U != (this_mac->mac_base->STACKED_VLAN & GEM_ENABLE_PROCESSING)) { ret_val = (uint16_t)this_mac->mac_base->STACKED_VLAN; } } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_hash(const mss_mac_instance_t *this_mac, uint64_t hash_in) { uint64_t hash = hash_in; /* Avoids warning about modifying fn parameter passed by value */ if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { if(0ULL == hash) /* Short cut for disabling */ { this_mac->emac_base->NETWORK_CONFIG &= ~(GEM_UNICAST_HASH_ENABLE | GEM_MULTICAST_HASH_ENABLE); } this_mac->emac_base->HASH_BOTTOM = (uint32_t)hash; hash >>= 32; this_mac->emac_base->HASH_TOP = (uint32_t)hash; } else { if(0ULL == hash) /* Short cut for disabling */ { this_mac->mac_base->NETWORK_CONFIG &= ~(GEM_UNICAST_HASH_ENABLE | GEM_MULTICAST_HASH_ENABLE); } this_mac->mac_base->HASH_BOTTOM = (uint32_t)hash; hash >>= 32; this_mac->mac_base->HASH_TOP = (uint32_t)hash; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint64_t MSS_MAC_get_hash(const mss_mac_instance_t *this_mac) { uint64_t ret_val = 0U; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { ret_val = (uint64_t)this_mac->emac_base->HASH_TOP; ret_val <<= 32; ret_val |= (uint64_t)this_mac->emac_base->HASH_BOTTOM; } else { ret_val = (uint64_t)this_mac->mac_base->HASH_TOP; ret_val <<= 32; ret_val |= (uint64_t)this_mac->mac_base->HASH_BOTTOM; } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_hash_mode(const mss_mac_instance_t *this_mac, mss_mac_hash_mode_t mode) { uint32_t temp; /* * Enum values are matched to register bits but just to be safe we mask them * to ensure only the two hash control bits are modified... */ if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { temp = this_mac->emac_base->NETWORK_CONFIG & ~(GEM_UNICAST_HASH_ENABLE | GEM_MULTICAST_HASH_ENABLE); temp |= (uint32_t)mode & (GEM_UNICAST_HASH_ENABLE | GEM_MULTICAST_HASH_ENABLE); this_mac->emac_base->NETWORK_CONFIG = temp; } else { temp = this_mac->mac_base->NETWORK_CONFIG & ~(GEM_UNICAST_HASH_ENABLE | GEM_MULTICAST_HASH_ENABLE); temp |= (uint32_t)mode & (GEM_UNICAST_HASH_ENABLE | GEM_MULTICAST_HASH_ENABLE); this_mac->mac_base->NETWORK_CONFIG = temp; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ mss_mac_hash_mode_t MSS_MAC_get_hash_mode(const mss_mac_instance_t *this_mac) { mss_mac_hash_mode_t ret_val = 0; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { ret_val = (mss_mac_hash_mode_t)(this_mac->emac_base->NETWORK_CONFIG & (GEM_UNICAST_HASH_ENABLE | GEM_MULTICAST_HASH_ENABLE)); } else { ret_val = (mss_mac_hash_mode_t)(this_mac->mac_base->NETWORK_CONFIG & (GEM_UNICAST_HASH_ENABLE | GEM_MULTICAST_HASH_ENABLE)); } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_type_filter(const mss_mac_instance_t *this_mac, uint32_t filter, uint16_t value) { volatile uint32_t *p_reg; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->SPEC_TYPE1; } else { p_reg = &this_mac->mac_base->SPEC_TYPE1; } if((filter <= 4U) && (filter > 0U)) /* Filter is in range 1 to 4 to match register naming */ { p_reg += filter - 1U; if(0U == value) /* Disable filter if match value is 0 as this should not be a valid type */ { *p_reg = 0U; } else { *p_reg = (uint32_t)(0x80000000UL | (uint32_t)value); } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint16_t MSS_MAC_get_type_filter(const mss_mac_instance_t *this_mac, uint32_t filter) { volatile uint32_t *p_reg; uint16_t ret_val = 0U; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->SPEC_TYPE1; } else { p_reg = &this_mac->mac_base->SPEC_TYPE1; } if((filter <= 4U) && (filter > 0U)) /* Filter is in range 1 to 4 to match register naming */ { p_reg += filter - 1U; if(0U != (*p_reg & 0x80000000UL)) /* Not disabled filter... */ { ret_val = (uint16_t)*p_reg; } } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_sa_filter(const mss_mac_instance_t *this_mac, uint32_t filter, uint16_t control, const uint8_t *mac_addr) { volatile uint32_t *p_reg; uint32_t address32_l; uint32_t address32_h; ASSERT(NULL_POINTER!= mac_addr); if((MSS_MAC_AVAILABLE == this_mac->mac_available) && (NULL_POINTER != mac_addr)) { if((filter >= 2U) && (filter <= 4U)) /* SA Filter 1 is for our address */ { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->SPEC_ADD2_BOTTOM; } else { p_reg = &this_mac->mac_base->SPEC_ADD2_BOTTOM; } p_reg = p_reg + ((filter - 2U) * 2U); if(MSS_MAC_SA_FILTER_DISABLE == control) { /* * Clear filter and disable - must be done in this order... * Writing to [0] disables and [1] enables So this sequence * clears the registers and disables the filter as opposed to * setting a destination address filter for 00:00:00:00:00:00. */ p_reg[1] = 0U; p_reg[0] = 0U; } else { /* Assemble correct register values */ address32_l = ((uint32_t)mac_addr[3]) << 24; address32_l |= ((uint32_t)mac_addr[2]) << 16; address32_l |= ((uint32_t)mac_addr[1]) << 8; address32_l |= ((uint32_t)mac_addr[0]); address32_h = ((uint32_t)mac_addr[5]) << 8; address32_h |= ((uint32_t)mac_addr[4]); address32_h |= (uint32_t)control << 16; /* Update hardware registers - must be done in this order... */ p_reg[0] = address32_l; p_reg[1] = address32_h; } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint16_t MSS_MAC_get_sa_filter(const mss_mac_instance_t *this_mac, uint32_t filter, uint8_t *mac_addr) { volatile uint32_t *p_reg; uint32_t temp_reg; uint16_t ret_val = 0U; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(NULL_POINTER != mac_addr) { (void)memset(mac_addr, 0, 6); /* Consistent result if bad parameters passed... */ } if((filter >= 2U) && (filter <= 4U)) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->SPEC_ADD2_BOTTOM; } else { p_reg = &this_mac->mac_base->SPEC_ADD2_BOTTOM; } p_reg = p_reg + ((filter - 2U) * 2U); if(NULL_POINTER != mac_addr) /* Want MAC address and filter control info? */ { temp_reg = p_reg[0]; mac_addr[0] = (uint8_t)temp_reg & BITS_08; temp_reg >>= 8; mac_addr[1] = (uint8_t)temp_reg & BITS_08; temp_reg >>= 8; mac_addr[2] = (uint8_t)temp_reg & BITS_08; temp_reg >>= 8; mac_addr[3] = (uint8_t)temp_reg & BITS_08; temp_reg = p_reg[1]; mac_addr[4] = (uint8_t)temp_reg & BITS_08; temp_reg >>= 8; mac_addr[5] = (uint8_t)temp_reg & BITS_08; } ret_val = (uint16_t)(p_reg[1] >> 16); } } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_type_1_filter(const mss_mac_instance_t *this_mac, uint32_t filter_no, const mss_mac_type_1_filter_t *filter) { if((MSS_MAC_AVAILABLE == this_mac->mac_available) && (filter_no < MSS_MAC_TYPE_1_SCREENERS)) { volatile uint32_t *p_reg; uint32_t temp_reg; if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->SCREENING_TYPE_1_REGISTER_0; } else { p_reg = &this_mac->mac_base->SCREENING_TYPE_1_REGISTER_0; } temp_reg = (uint32_t)filter->queue_no & GEM_QUEUE_NUMBER; temp_reg |= ((uint32_t)filter->dstc << GEM_DSTC_MATCH_SHIFT) & GEM_DSTC_MATCH; temp_reg |= ((uint32_t)filter->udp_port << GEM_UDP_PORT_MATCH_SHIFT) & GEM_UDP_PORT_MATCH; if(0U != filter->drop_on_match) { temp_reg |= GEM_DROP_ON_MATCH; } if(0U != filter->dstc_enable) { temp_reg |= GEM_DSTC_ENABLE; } if(0U != filter->udp_port_enable) { temp_reg |= GEM_UDP_PORT_MATCH_ENABLE; } p_reg[filter_no] = temp_reg; } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_get_type_1_filter(const mss_mac_instance_t *this_mac, uint32_t filter_no, mss_mac_type_1_filter_t *filter) { if((MSS_MAC_AVAILABLE == this_mac->mac_available) && (filter_no < MSS_MAC_TYPE_1_SCREENERS)) { volatile uint32_t *p_reg; uint32_t temp_reg; (void)memset(filter, 0, sizeof(mss_mac_type_1_filter_t)); /* Blank canvass to start */ if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->SCREENING_TYPE_1_REGISTER_0; } else { p_reg = &this_mac->mac_base->SCREENING_TYPE_1_REGISTER_0; } temp_reg = p_reg[filter_no]; filter->queue_no = (uint8_t)(temp_reg & GEM_QUEUE_NUMBER); temp_reg >>= 4; filter->dstc = (uint8_t)(temp_reg & GEM_DSTC_MATCH); temp_reg >>= GEM_DSTC_MATCH_SHIFT; filter->udp_port = (uint16_t)(temp_reg & GEM_UDP_PORT_MATCH); temp_reg >>= GEM_UDP_PORT_MATCH_SHIFT; filter->dstc_enable = (uint8_t)(temp_reg & 1U); temp_reg >>= 1; filter->udp_port_enable = (uint8_t)(temp_reg & 1U); temp_reg >>= 1; filter->drop_on_match = (uint8_t)(temp_reg & 1U); } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_type_2_ethertype(const mss_mac_instance_t *this_mac, uint32_t ethertype_no, uint16_t ethertype) { volatile uint32_t *p_reg; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U == this_mac->is_emac) /* Ethertype filter not supported on eMAC */ { p_reg = &this_mac->mac_base->SCREENING_TYPE_2_ETHERTYPE_REG_0; if(ethertype_no < MSS_MAC_TYPE_2_ETHERTYPES) { p_reg[ethertype_no] = (uint32_t)ethertype; } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint16_t MSS_MAC_get_type_2_ethertype(const mss_mac_instance_t *this_mac, uint32_t ethertype_no) { volatile uint32_t *p_reg; uint16_t temp_reg = 0U; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U == this_mac->is_emac) /* Ethertype filter not supported on eMAC */ { p_reg = &this_mac->mac_base->SCREENING_TYPE_2_ETHERTYPE_REG_0; if(ethertype_no < MSS_MAC_TYPE_2_ETHERTYPES) { temp_reg = (uint16_t)p_reg[ethertype_no]; } } } return(temp_reg); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_type_2_compare(const mss_mac_instance_t *this_mac, uint32_t comparer_no_in, const mss_mac_type_2_compare_t *comparer) { volatile uint32_t *p_reg; uint32_t limit; uint32_t temp_reg; uint32_t comparer_no = comparer_no_in; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) /* eMAC limits are different to pMAC ones */ { p_reg = &this_mac->emac_base->TYPE2_COMPARE_0_WORD_0; limit = MSS_MAC_EMAC_TYPE_2_COMPARERS; } else { p_reg = &this_mac->mac_base->TYPE2_COMPARE_0_WORD_0; limit = MSS_MAC_TYPE_2_COMPARERS; } if(comparer_no < limit) { comparer_no *= 2U; /* Working with consecutive pairs of registers for this one */ if(0U != comparer->disable_mask) { p_reg[comparer_no] = comparer->data; /* Mask disabled so just 4 byte compare value */ temp_reg = GEM_DISABLE_MASK; /* and no mask */ } else { temp_reg = comparer->data << 16; /* 16 bit compare value and 16 bit mask */ temp_reg |= (uint32_t)comparer->mask; p_reg[comparer_no] = temp_reg; temp_reg = 0U; } if(0U != comparer->compare_vlan_c_id) { temp_reg |= GEM_COMPARE_VLAN_ID; } else if(0U != comparer->compare_vlan_s_id) { temp_reg |= GEM_COMPARE_VLAN_ID | GEM_COMPARE_S_TAG; } else { temp_reg |= (uint32_t)comparer->offset_value & BITS_07; temp_reg |= ((uint32_t)comparer->compare_offset & BITS_02) << GEM_COMPARE_OFFSET_SHIFT; } p_reg[comparer_no + 1] = temp_reg; /* Set second word of comparer */ } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_get_type_2_compare(const mss_mac_instance_t *this_mac, uint32_t comparer_no_in, mss_mac_type_2_compare_t *comparer) { volatile uint32_t *p_reg; uint32_t limit; uint32_t comparer_no = comparer_no_in; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) /* eMAC limits are different to pMAC ones */ { p_reg = &this_mac->emac_base->TYPE2_COMPARE_0_WORD_0; limit = MSS_MAC_EMAC_TYPE_2_COMPARERS; } else { p_reg = &this_mac->mac_base->TYPE2_COMPARE_0_WORD_0; limit = MSS_MAC_TYPE_2_COMPARERS; } (void)memset(comparer, 0, sizeof(mss_mac_type_2_compare_t)); if(comparer_no < limit) { comparer_no *= 2U; /* Working with consecutive pairs of registers for this one */ if(0U != (p_reg[comparer_no + 1] & GEM_DISABLE_MASK)) { comparer->data = p_reg[comparer_no]; /* Mask disabled so just 4 byte compare value */ comparer->disable_mask = 1; /* and no mask */ } else { comparer->data = p_reg[comparer_no] >> 16; /* 16 bit compare value and 16 bit mask */ comparer->mask = (uint16_t)p_reg[comparer_no]; } if(0U != (p_reg[comparer_no + 1U] & GEM_COMPARE_VLAN_ID)) { if(0U != (p_reg[comparer_no + 1U] & GEM_COMPARE_S_TAG)) { comparer->compare_vlan_s_id = 1U; } else { comparer->compare_vlan_c_id = 1U; } } else { comparer->compare_offset = (uint8_t)((p_reg[comparer_no + 1U] >> GEM_COMPARE_OFFSET_SHIFT) & BITS_02); } comparer->offset_value = (uint8_t)(p_reg[comparer_no + 1U] & GEM_OFFSET_VALUE); } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_type_2_filter(const mss_mac_instance_t *this_mac, uint32_t filter_no, const mss_mac_type_2_filter_t *filter) { volatile uint32_t *p_reg; uint32_t limit; uint32_t temp_reg = 0U; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) /* eMAC limits are different to pMAC ones */ { p_reg = &this_mac->emac_base->SCREENING_TYPE_2_REGISTER_0; limit = MSS_MAC_EMAC_TYPE_2_SCREENERS; } else { p_reg = &this_mac->mac_base->SCREENING_TYPE_2_REGISTER_0; limit = MSS_MAC_TYPE_2_SCREENERS; } if(filter_no < limit) /* Lets go build a filter... */ { if(0u != (filter->drop_on_match)) { temp_reg = GEM_T2_DROP_ON_MATCH; } else { temp_reg = 0U; } if(0U != (filter->compare_a_enable)) { temp_reg |= GEM_COMPARE_A_ENABLE; temp_reg |= ((uint32_t)filter->compare_a_index & BITS_05) << GEM_COMPARE_A_SHIFT; } if(0U != (filter->compare_b_enable)) { temp_reg |= GEM_COMPARE_B_ENABLE; temp_reg |= ((uint32_t)filter->compare_b_index & BITS_05) << GEM_COMPARE_B_SHIFT; } if(0U != (filter->compare_c_enable)) { temp_reg |= GEM_COMPARE_C_ENABLE; temp_reg |= ((uint32_t)filter->compare_c_index & BITS_05) << GEM_COMPARE_C_SHIFT; } if(0U != (filter->ethertype_enable)) { temp_reg |= GEM_ETHERTYPE_ENABLE; temp_reg |= ((uint32_t)filter->ethertype_index & BITS_03) << GEM_ETHERTYPE_REG_INDEX_SHIFT; } if(0U != (filter->vlan_priority_enable)) { temp_reg |= GEM_VLAN_ENABLE; temp_reg |= ((uint32_t)filter->vlan_priority & BITS_03) << GEM_VLAN_PRIORITY_SHIFT; } temp_reg |= (uint32_t)filter->queue_no & GEM_QUEUE_NUMBER; p_reg[filter_no] = temp_reg; /* Set filter up at last */ } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_get_type_2_filter(const mss_mac_instance_t *this_mac, uint32_t filter_no, mss_mac_type_2_filter_t *filter) { volatile uint32_t *p_reg; uint32_t limit; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { (void)memset(filter, 0, sizeof(mss_mac_type_2_filter_t)); if(0U != this_mac->is_emac) /* eMAC limits are different to pMAC ones */ { p_reg = &this_mac->emac_base->SCREENING_TYPE_2_REGISTER_0; limit = MSS_MAC_EMAC_TYPE_2_SCREENERS; } else { p_reg = &this_mac->mac_base->SCREENING_TYPE_2_REGISTER_0; limit = MSS_MAC_TYPE_2_SCREENERS; } if(filter_no < limit) /* Lets go fetch a filter... */ { if(0U != (p_reg[filter_no] & GEM_T2_DROP_ON_MATCH)) { filter->drop_on_match = 1; } if(0U != (p_reg[filter_no] & GEM_COMPARE_A_ENABLE)) { filter->compare_a_enable = 1; } if(0U != (p_reg[filter_no] & GEM_COMPARE_B_ENABLE)) { filter->compare_b_enable = 1; } if(0U != (p_reg[filter_no] & GEM_COMPARE_C_ENABLE)) { filter->compare_c_enable = 1; } if(0U != (p_reg[filter_no] & GEM_ETHERTYPE_ENABLE)) { filter->ethertype_enable = 1; } if(0U != (p_reg[filter_no] & GEM_VLAN_ENABLE)) { filter->vlan_priority_enable = 1; } filter->compare_a_index = (uint8_t)((p_reg[filter_no] & GEM_COMPARE_A) >> GEM_COMPARE_A_SHIFT); filter->compare_b_index = (uint8_t)((p_reg[filter_no] & GEM_COMPARE_B) >> GEM_COMPARE_B_SHIFT); filter->compare_c_index = (uint8_t)((p_reg[filter_no] & GEM_COMPARE_C) >> GEM_COMPARE_C_SHIFT); filter->ethertype_index = (uint8_t)((p_reg[filter_no] & GEM_ETHERTYPE_REG_INDEX) >> GEM_ETHERTYPE_REG_INDEX_SHIFT); filter->vlan_priority = (uint8_t)((p_reg[filter_no] & GEM_VLAN_PRIORITY) >> GEM_VLAN_PRIORITY_SHIFT); filter->queue_no = (uint8_t)(p_reg[filter_no] & GEM_QUEUE_NUMBER); } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_mmsl_mode(const mss_mac_instance_t *this_mac, const mss_mac_mmsl_config_t *mmsl_cfg) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != mmsl_cfg->preemption) /* Preemption is enabled so deal with it */ { /* * Need to shut preemption down in case it is already enabled * otherwise the new settings may not be recognised properly. */ this_mac->mac_base->MMSL_CONTROL = 0U; if(0U != mmsl_cfg->verify_disable) { this_mac->mac_base->MMSL_CONTROL |= ((uint32_t)(mmsl_cfg->frag_size)) | GEM_VERIFY_DISABLE; this_mac->mac_base->MMSL_CONTROL |= GEM_PRE_ENABLE; } else { this_mac->mac_base->MMSL_CONTROL |= (uint32_t)(mmsl_cfg->frag_size); this_mac->mac_base->MMSL_CONTROL |= GEM_PRE_ENABLE; } } else /* Preemption is not enabled so see which MAC we want to use */ { if(0U != mmsl_cfg->use_pmac) { this_mac->mac_base->MMSL_CONTROL = GEM_ROUTE_RX_TO_PMAC; } else { this_mac->mac_base->MMSL_CONTROL = 0U; } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_get_mmsl_mode(const mss_mac_instance_t *this_mac, mss_mac_mmsl_config_t *mmsl_cfg) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { (void)memset(mmsl_cfg, 0, sizeof(mss_mac_mmsl_config_t)); if(0U != (this_mac->mac_base->MMSL_CONTROL & GEM_ROUTE_RX_TO_PMAC)) { mmsl_cfg->use_pmac = 1U; } if(0U != (this_mac->mac_base->MMSL_CONTROL & GEM_PRE_ENABLE)) { mmsl_cfg->preemption = 1U; } if(0U != (this_mac->mac_base->MMSL_CONTROL & GEM_VERIFY_DISABLE)) { mmsl_cfg->verify_disable = 1U; } mmsl_cfg->frag_size = (mss_mac_frag_size_t)(this_mac->mac_base->MMSL_CONTROL & BITS_02); } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_start_preemption_verify(const mss_mac_instance_t *this_mac) { if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != (this_mac->mac_base->MMSL_CONTROL & GEM_PRE_ENABLE)) /* Preemption is enabled */ { this_mac->mac_base->MMSL_CONTROL |= GEM_RESTART_VER; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint32_t MSS_MAC_get_mmsl_status(const mss_mac_instance_t *this_mac) { uint32_t ret_val = 0U; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { ret_val = this_mac->mac_base->MMSL_STATUS; /* Just return raw value, user can decode using defined bits */ } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_get_mmsl_stats(const mss_mac_instance_t *this_mac, mss_mac_mmsl_stats_t *stats) { /* * We return these differently to the general statistics as they are pMAC * specific and don't have a corresponding eMAC equivalent. */ if(MSS_MAC_AVAILABLE == this_mac->mac_available) { (void)memset(stats, 0, sizeof(mss_mac_mmsl_stats_t)); stats->smd_err_count = (this_mac->mac_base->MMSL_ERR_STATS & GEM_SMD_ERR_COUNT) >> GEM_SMD_ERR_COUNT_SHIFT; stats->ass_err_count = this_mac->mac_base->MMSL_ERR_STATS & GEM_ASS_ERR_COUNT; stats->ass_ok_count = this_mac->mac_base->MMSL_ASS_OK_COUNT; stats->frag_count_rx = this_mac->mac_base->MMSL_FRAG_COUNT_RX; stats->frag_count_tx = this_mac->mac_base->MMSL_FRAG_COUNT_TX; } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_tx_cutthru(const mss_mac_instance_t *this_mac, uint32_t level) { volatile uint32_t *p_reg; uint32_t mask; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { p_reg = (0U != this_mac->is_emac) ? &this_mac->emac_base->PBUF_TXCUTTHRU : &this_mac->mac_base->PBUF_TXCUTTHRU; mask = (0U != this_mac->is_emac) ? GEM_DMA_EMAC_CUTTHRU_THRESHOLD : GEM_DMA_TX_CUTTHRU_THRESHOLD; if(0U == level) /* Disabling cutthru? */ { *p_reg = 0U; } else { *p_reg = GEM_DMA_CUTTHRU | (level & mask); } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_rx_cutthru(const mss_mac_instance_t *this_mac, uint32_t level) { volatile uint32_t *p_reg; uint32_t mask; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { p_reg = (0U != this_mac->is_emac) ? &this_mac->emac_base->PBUF_RXCUTTHRU : &this_mac->mac_base->PBUF_RXCUTTHRU; mask = (0U != this_mac->is_emac) ? GEM_DMA_EMAC_CUTTHRU_THRESHOLD : GEM_DMA_RX_CUTTHRU_THRESHOLD; if(0U == level) /* Disabling cutthru? */ { *p_reg = 0U; } else { *p_reg = GEM_DMA_CUTTHRU | (level & mask); } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint32_t MSS_MAC_get_tx_cutthru(const mss_mac_instance_t *this_mac) { uint32_t temp_reg = 0U; volatile uint32_t *p_reg; uint32_t mask; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { p_reg = (0U != this_mac->is_emac) ? &this_mac->emac_base->PBUF_TXCUTTHRU : &this_mac->mac_base->PBUF_TXCUTTHRU; mask = (0U != this_mac->is_emac) ? GEM_DMA_EMAC_CUTTHRU_THRESHOLD : GEM_DMA_TX_CUTTHRU_THRESHOLD; temp_reg = *p_reg; if(0U == (temp_reg & GEM_DMA_CUTTHRU)) { temp_reg = 0U; } else { temp_reg &= mask; } } return(temp_reg); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint32_t MSS_MAC_get_rx_cutthru(const mss_mac_instance_t *this_mac) { uint32_t temp_reg = 0U; volatile uint32_t *p_reg; uint32_t mask; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { p_reg = (0U != this_mac->is_emac) ? &this_mac->emac_base->PBUF_RXCUTTHRU : &this_mac->mac_base->PBUF_RXCUTTHRU; mask = (0U != this_mac->is_emac) ? GEM_DMA_EMAC_CUTTHRU_THRESHOLD : GEM_DMA_RX_CUTTHRU_THRESHOLD; temp_reg = *p_reg; if(0U == (temp_reg & GEM_DMA_CUTTHRU)) { temp_reg = 0U; } else { temp_reg &= mask; } } return(temp_reg); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_tx_enable(const mss_mac_instance_t *this_mac) { /* Don't do this if already done in case it has side effects... */ if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { if(0U == (this_mac->emac_base->NETWORK_CONTROL & GEM_ENABLE_TRANSMIT)) { this_mac->emac_base->NETWORK_CONTROL |= GEM_ENABLE_TRANSMIT; } } else { if(0U == (this_mac->mac_base->NETWORK_CONTROL & GEM_ENABLE_TRANSMIT)) { this_mac->mac_base->NETWORK_CONTROL |= GEM_ENABLE_TRANSMIT; } } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_jumbo_frames_mode(const mss_mac_instance_t *this_mac, bool state) { volatile uint32_t *p_reg = &this_mac->mac_base->NETWORK_CONFIG; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->NETWORK_CONFIG; } if(0U != this_mac->jumbo_frame_enable) /* Only look at this if the feature is enabled in config */ { if(state) { *p_reg |= GEM_JUMBO_FRAMES; } else { *p_reg &= ~GEM_JUMBO_FRAMES; } } else /* Ensure it is disabled if not allowed... */ { *p_reg &= ~GEM_JUMBO_FRAMES; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ bool MSS_MAC_get_jumbo_frames_mode(const mss_mac_instance_t *this_mac) { bool ret_val = false; volatile uint32_t *p_reg = &this_mac->mac_base->NETWORK_CONFIG; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->NETWORK_CONFIG; } ret_val = ((*p_reg & GEM_JUMBO_FRAMES) != 0U); } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_jumbo_frame_length(const mss_mac_instance_t *this_mac, uint32_t length_in) { volatile uint32_t *p_reg = &this_mac->mac_base->JUMBO_MAX_LENGTH; uint32_t length = length_in; /* Avoids warning about modifying parameter passed by value */ if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->JUMBO_MAX_LENGTH; } /* Set up maximum jumbo frame size - but bounds check first */ if(length > MSS_MAC_JUMBO_MAX) { length = MSS_MAC_JUMBO_MAX; } if(0U != this_mac->jumbo_frame_enable) /* Only look at this if the feature is enabled in config */ { *p_reg = length; } else /* Ensure it is set to reset value if not allowed... */ { *p_reg = MSS_MAC_JUMBO_MAX; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ uint32_t MSS_MAC_get_jumbo_frame_length(const mss_mac_instance_t *this_mac) { uint32_t ret_val = 0; volatile uint32_t *p_reg = &this_mac->mac_base->JUMBO_MAX_LENGTH; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->JUMBO_MAX_LENGTH; } ret_val = *p_reg; } return(ret_val); } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ void MSS_MAC_set_pause_frame_copy_to_mem(const mss_mac_instance_t *this_mac, bool state) { volatile uint32_t *p_reg = &this_mac->mac_base->NETWORK_CONFIG; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->NETWORK_CONFIG; } /* * Logic is inverted as enabling copy to memory means disabling * GEM_DISABLE_COPY_OF_PAUSE_FRAMES */ if(state) { *p_reg &= ~GEM_DISABLE_COPY_OF_PAUSE_FRAMES; } else { *p_reg |= GEM_DISABLE_COPY_OF_PAUSE_FRAMES; } } } /****************************************************************************** * See mss_ethernet_mac.h for details of how to use this function. */ bool MSS_MAC_get_pause_frame_copy_to_mem(const mss_mac_instance_t *this_mac) { bool ret_val = false; volatile uint32_t *p_reg = &this_mac->mac_base->NETWORK_CONFIG; if(MSS_MAC_AVAILABLE == this_mac->mac_available) { if(0U != this_mac->is_emac) { p_reg = &this_mac->emac_base->NETWORK_CONFIG; } /* * Logic is inverted as enabling copy to memory means disabling * GEM_DISABLE_COPY_OF_PAUSE_FRAMES */ ret_val = 0U == (*p_reg & GEM_DISABLE_COPY_OF_PAUSE_FRAMES); } return(ret_val); } /**************************************************************************/ /* Private Function definitions */ /**************************************************************************/ /****************************************************************************** * This is default "Receive packet interrupt handler. This function finds the * descriptor that received the packet and caused the interrupt. * This informs the received packet size to the application and * relinquishes the packet buffer from the associated DMA descriptor. */ static void rxpkt_handler ( mss_mac_instance_t *this_mac, uint64_t queue_no ) { mss_mac_queue_t *this_queue = &this_mac->queue[queue_no]; mss_mac_rx_desc_t * cdesc = &this_queue->rx_desc_tab[this_queue->first_rx_desc_index]; uint64_t burst = MSS_MAC_RX_RING_SIZE; if(0U != (cdesc->addr_low & GEM_RX_DMA_USED)) /* Check in case we already got it... */ { /* Execution comes here because at-least one packet is received. */ do { uint8_t * p_rx_packet; uint32_t pckt_length; #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) uint64_t addr_temp; #else uint32_t addr_temp; #endif ++this_queue->nb_available_rx_desc; #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) addr_temp = (uint64_t)(cdesc->addr_low & ~(GEM_RX_DMA_WRAP | GEM_RX_DMA_USED | GEM_RX_DMA_TS_PRESENT)); addr_temp |= (uint64_t)cdesc->addr_high << 32; #else addr_temp = (cdesc->addr_low & ~(GEM_RX_DMA_WRAP | GEM_RX_DMA_USED | GEM_RX_DMA_TS_PRESENT)); #endif p_rx_packet = (uint8_t *)addr_temp; /* * Pass received packet up to application layer - if enabled... * * Note if rx_packet comes back as 0 we can't recover and will leave a * used packet stuck in the queue... */ #if !defined(MSS_MAC_UNH_TEST) if((NULL_POINTER != this_queue->pckt_rx_callback) && (NULL_POINTER != p_rx_packet) && (0U == this_mac->rx_discard)) #endif { pckt_length = cdesc->status & (GEM_RX_DMA_BUFF_LEN | GEM_RX_DMA_JUMBO_BIT_13); this_queue->ingress += pckt_length; this_queue->pckt_rx_callback(this_mac, queue_no, p_rx_packet, pckt_length, cdesc, this_queue->rx_caller_info[this_queue->first_rx_desc_index]); } #if !defined(MSS_MAC_UNH_TEST) if((NULL_POINTER != p_rx_packet) && (0U != this_mac->rx_discard)) { /* * Need to return receive packet buffer to the queue as rx handler * hasn't been called to do it for us... */ (void)MSS_MAC_receive_pkt(this_mac, queue_no, p_rx_packet, this_queue->rx_caller_info[this_queue->first_rx_desc_index], MSS_MAC_INT_ENABLE); } #endif /* Point the curr_rx_desc to next descriptor in the ring */ /* Wrap around in case next descriptor is pointing to last in the ring */ ++this_queue->first_rx_desc_index; this_queue->first_rx_desc_index %= MSS_MAC_RX_RING_SIZE; cdesc = &this_queue->rx_desc_tab[this_queue->first_rx_desc_index]; burst--; } while(0 != (cdesc->addr_low & GEM_RX_DMA_USED) && (0 != burst)); /* loop while there are packets available */ } if(0U == (this_mac->mac_base->NETWORK_CONTROL & GEM_ENABLE_RECEIVE)) { this_mac->mac_base->NETWORK_CONTROL |= GEM_ENABLE_RECEIVE; } } /****************************************************************************** * This is default "Transmit packet interrupt handler. This function finds the * descriptor that transmitted the packet and caused the interrupt. * This relinquishes the packet buffer from the associated DMA descriptor. */ static void txpkt_handler ( mss_mac_instance_t *this_mac, uint64_t queue_no ) { #if defined(MSS_MAC_SIMPLE_TX_QUEUE) mss_mac_queue_t *this_queue = &this_mac->queue[queue_no]; mss_mac_tx_desc_t *p_current_desc; uint32_t finished; /* * Simple multi packet TX queue where only the one packet buffer is used * per frame but we do have 1 extra descriptor to stop DMA engine running * over itself... */ p_current_desc = &this_queue->tx_desc_tab[this_queue->current_tx_desc]; finished = 0; while(!finished) { if(this_queue->nb_available_tx_desc == MSS_MAC_TX_RING_SIZE) { *this_queue->transmit_q_ptr = (uint32_t)((uint64_t)this_queue->tx_desc_tab) | 1UL; /* Disable the TX queue */ finished = 1; } else { if(p_current_desc->status & GEM_TX_DMA_USED) { if(NULL_POINTER != this_queue->pckt_tx_callback) { this_queue->pckt_tx_callback(this_mac, queue_no, p_current_desc, this_queue->tx_caller_info[this_queue->current_tx_desc]); } this_queue->nb_available_tx_desc++; p_current_desc++; this_queue->current_tx_desc++; } else { finished = 1; } } } #else #error "Multi packet TX not implemented!" #endif } /****************************************************************************** * */ static void tx_desc_ring_init(mss_mac_instance_t *this_mac) { int32_t inc; int32_t queue_no; for(queue_no = 0; queue_no < MSS_MAC_QUEUE_COUNT; queue_no++) { #if defined(MSS_MAC_USE_DDR) this_mac->queue[queue_no].tx_desc_tab = g_mss_mac_ddr_ptr; g_mss_mac_ddr_ptr += MSS_MAC_TX_RING_SIZE * sizeof(mss_mac_tx_desc_t); #endif for(inc = 0; inc < MSS_MAC_TX_RING_SIZE; ++inc) { this_mac->queue[queue_no].tx_desc_tab[inc].addr_low = 0U; #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) this_mac->queue[queue_no].tx_desc_tab[inc].addr_high = 0U; #endif this_mac->queue[queue_no].tx_desc_tab[inc].status = GEM_TX_DMA_USED; } inc--; /* Step back to last buffer descriptor and mark as end of list */ this_mac->queue[queue_no].tx_desc_tab[inc].status |= GEM_TX_DMA_WRAP; } } /****************************************************************************** * */ static void rx_desc_ring_init(mss_mac_instance_t *this_mac) { uint32_t inc; int32_t queue_no; for(queue_no = 0; queue_no < MSS_MAC_QUEUE_COUNT; queue_no++) { #if defined(MSS_MAC_USE_DDR) this_mac->queue[queue_no].rx_desc_tab = g_mss_mac_ddr_ptr; g_mss_mac_ddr_ptr += MSS_MAC_RX_RING_SIZE * sizeof(mss_mac_rx_desc_t); #endif for(inc = 0U; inc < MSS_MAC_RX_RING_SIZE; ++inc) { this_mac->queue[queue_no].rx_desc_tab[inc].addr_low = 0U; /* Mark buffers as used for now in case DMA gets enabled before we attach a buffer */ #if defined(MSS_MAC_64_BIT_ADDRESS_MODE) this_mac->queue[queue_no].rx_desc_tab[inc].addr_high = 0U; #endif this_mac->queue[queue_no].rx_desc_tab[inc].status = 0U; } inc--; /* Step back to last buffer descriptor and mark as end of list */ this_mac->queue[queue_no].rx_desc_tab[inc].addr_low |= GEM_RX_DMA_WRAP; } } /****************************************************************************** * */ static void assign_station_addr ( mss_mac_instance_t *this_mac, const uint8_t mac_addr[MSS_MAC_MAC_LEN] ) { uint32_t address32_l; uint32_t address32_h; ASSERT(NULL_POINTER != mac_addr); if((NULL_POINTER != mac_addr) && (NULL_POINTER != this_mac)) { /* Update current instance data */ (void)memcpy(this_mac->mac_addr, mac_addr, MSS_MAC_MAC_LEN); /* Assemble correct register values */ address32_l = ((uint32_t)mac_addr[3]) << 24; address32_l |= ((uint32_t)mac_addr[2]) << 16; address32_l |= ((uint32_t)mac_addr[1]) << 8; address32_l |= ((uint32_t)mac_addr[0]); address32_h = ((uint32_t)mac_addr[5]) << 8; address32_h |= ((uint32_t)mac_addr[4]); /* Update hardware registers */ if(0U != this_mac->is_emac) { this_mac->emac_base->SPEC_ADD1_BOTTOM = address32_l; this_mac->emac_base->SPEC_ADD1_TOP = address32_h; /* * If being done from init for first time we also make sure the * specific address filters are disabled... */ if(this_mac->mac_available != MSS_MAC_AVAILABLE) { this_mac->emac_base->SPEC_ADD2_TOP = 0U; this_mac->emac_base->SPEC_ADD2_BOTTOM = 0U; this_mac->emac_base->SPEC_ADD3_TOP = 0U; this_mac->emac_base->SPEC_ADD3_BOTTOM = 0U; this_mac->emac_base->SPEC_ADD4_TOP = 0U; this_mac->emac_base->SPEC_ADD4_BOTTOM = 0U; } } else { this_mac->mac_base->SPEC_ADD1_BOTTOM = address32_l; this_mac->mac_base->SPEC_ADD1_TOP = address32_h; /* * If being done from init for first time we also make sure the * specific address filters are disabled... */ if(this_mac->mac_available != MSS_MAC_AVAILABLE) { this_mac->mac_base->SPEC_ADD2_TOP = 0U; this_mac->mac_base->SPEC_ADD2_BOTTOM = 0U; this_mac->mac_base->SPEC_ADD3_TOP = 0U; this_mac->mac_base->SPEC_ADD3_BOTTOM = 0U; this_mac->mac_base->SPEC_ADD4_TOP = 0U; this_mac->mac_base->SPEC_ADD4_BOTTOM = 0U; } } } } /******************************************************************************* * Auto-detect the PHY's address by attempting to read the PHY identification * register containing the PHY manufacturer's identifier. * Attempting to read a PHY register using an incorrect PHY address will result * in a value with all bits set to one on the MDIO bus. Reading any other value * means that a PHY responded to the read request, therefore we have found the * PHY's address. * This function returns the detected PHY's address or 32 (PHY_ADDRESS_MAX + 1) * if no PHY is responding. */ static uint8_t probe_phy(const mss_mac_instance_t *this_mac) { uint8_t phy_address = PHY_ADDRESS_MIN; const uint16_t ALL_BITS_HIGH = 0xFFFFU; const uint8_t PHYREG_PHYID1R = 0x02U; /* PHY Identifier 1 register address. */ uint32_t found; do { uint16_t reg; reg = MSS_MAC_read_phy_reg(this_mac, phy_address, PHYREG_PHYID1R); if (reg != ALL_BITS_HIGH) { found = 1U; } else { found = 0U; ++phy_address; } } while ((phy_address <= PHY_ADDRESS_MAX) && (0U == found)); return phy_address; } /******************************************************************************* * MSS MAC TBI interface */ static void msgmii_init(const mss_mac_instance_t *this_mac) { if(GMII_SGMII == this_mac->interface_type) { if(0U == this_mac->is_emac) { this_mac->mac_base->PCS_CONTROL = 0x9000UL; /* Reset and enable autonegotiation */ } } if(TBI == this_mac->interface_type) { if(0U == this_mac->is_emac) { this_mac->mac_base->PCS_CONTROL = 0x9000UL; /* Reset and enable autonegotiation */ } } } /******************************************************************************* * */ void msgmii_autonegotiate(const mss_mac_instance_t *this_mac) { uint16_t phy_reg; uint16_t autoneg_complete; uint8_t link_fullduplex; mss_mac_speed_t link_speed; uint8_t copper_link_up; volatile uint32_t sgmii_aneg_timeout = 100000U; copper_link_up = this_mac->phy_get_link_status(this_mac, &link_speed, &link_fullduplex); if(MSS_MAC_LINK_UP == copper_link_up) { /* Initiate auto-negotiation on the TBI SGMII link. */ if(TBI == this_mac->interface_type) { phy_reg = (uint16_t)this_mac->mac_base->PCS_CONTROL; phy_reg |= 0x1000U; this_mac->mac_base->PCS_CONTROL = phy_reg; phy_reg |= 0x0200U; this_mac->mac_base->PCS_CONTROL = phy_reg; /* Wait for SGMII auto-negotiation to complete. */ do { phy_reg = (uint16_t)this_mac->mac_base->PCS_STATUS; autoneg_complete = phy_reg & BMSR_AUTO_NEGOTIATION_COMPLETE; --sgmii_aneg_timeout; } while(((0U == autoneg_complete) && (sgmii_aneg_timeout != 0U)) || (0xFFFFU == phy_reg)); } } } #if 0 /******************************************************************************* * SGMII or 1000BaseX interface with CoreSGMII */ #if (MSS_MAC_PHY_INTERFACE == SGMII) #define CORE_SGMII_PHY_ADDR MSS_MAC_INTERFACE_MDIO_ADDR /******************************************************************************* * */ static void coresgmii_init(void) { uint16_t phy_reg; /* Reset C-SGMII. */ MSS_MAC_write_phy_reg(CORE_SGMII_PHY_ADDR, 0x00U, 0x9000U); /* Register 0x04 of C-SGMII must be always be set to 0x0001. */ MSS_MAC_write_phy_reg(CORE_SGMII_PHY_ADDR, 0x04U, 0x0001U); /* Enable auto-negotiation inside CoreSGMII block. */ phy_reg = MSS_MAC_read_phy_reg(CORE_SGMII_PHY_ADDR, 0x00U); phy_reg |= 0x1000U; MSS_MAC_write_phy_reg(CORE_SGMII_PHY_ADDR, 0x00U, phy_reg); } /******************************************************************************* * */ static void coresgmii_autonegotiate(void) { uint16_t phy_reg; uint16_t autoneg_complete; volatile uint32_t sgmii_aneg_timeout = 1000000U; uint8_t link_fullduplex; mss_mac_speed_t link_speed; uint8_t copper_link_up; copper_link_up = MSS_MAC_phy_get_link_status(&link_speed, &link_fullduplex); if(MSS_MAC_LINK_UP == copper_link_up) { SYSREG->MAC_CR = (SYSREG->MAC_CR & ~MAC_CONFIG_SPEED_MASK) | link_speed; /* Configure duplex mode */ if(MSS_MAC_HALF_DUPLEX == link_fullduplex) { /* half duplex */ MAC->CFG2 &= ~CFG2_FDX_MASK; } else { /* full duplex */ MAC->CFG2 |= CFG2_FDX_MASK; } /* Initiate auto-negotiation on the SGMII link. */ phy_reg = MSS_MAC_read_phy_reg(CORE_SGMII_PHY_ADDR, 0x00U); phy_reg |= 0x1000U; MSS_MAC_write_phy_reg(CORE_SGMII_PHY_ADDR, 0x00U, phy_reg); phy_reg |= 0x0200U; MSS_MAC_write_phy_reg(CORE_SGMII_PHY_ADDR, 0x00U, phy_reg); /* Wait for SGMII auto-negotiation to complete. */ do { phy_reg = MSS_MAC_read_phy_reg(CORE_SGMII_PHY_ADDR, MII_BMSR); autoneg_complete = phy_reg & BMSR_AUTO_NEGOTIATION_COMPLETE; --sgmii_aneg_timeout; } while(((0U == autoneg_complete) && (sgmii_aneg_timeout != 0U)) || (0xFFFFU == phy_reg)); } } /******************************************************************************* * Generate clock 2.5/25/125MHz for 10/100/1000Mbps using Clock Condition Circuit(CCC) */ static void coresgmii_set_link_speed(uint32_t speed) { uint16_t phy_reg; phy_reg = MSS_MAC_read_phy_reg(CORE_SGMII_PHY_ADDR, 0x11U); phy_reg |= (speed << 2); MSS_MAC_write_phy_reg(CORE_SGMII_PHY_ADDR, 0x11U, phy_reg); } #endif /* #if (MSS_MAC_PHY_INTERFACE == SGMII) */ #endif /******************************************************************************* * Setup hardware addresses etc for instance structure(s). */ static void instances_init(mss_mac_instance_t *this_mac, mss_mac_cfg_t *cfg) { #if defined(TARGET_ALOE) /* These are unused for Aloe as there is only one pMAC and no alternate * address support required */ (void)this_mac; (void)cfg; (void)memset(&g_mac0, 0, sizeof(g_mac0)); g_mac0.use_hi_address = cfg->use_hi_address; /* Not really needed but to be consistent... */ g_mac0.is_emac = 0U; g_mac0.mac_base = (MAC_TypeDef *)MSS_MAC0_BASE; g_mac0.emac_base = (eMAC_TypeDef *)MSS_MAC0_BASE; g_mac0.mac_q_int[0] = ethernet_PLIC_53; g_mac0.queue[0].int_status = &g_mac0.mac_base->INT_STATUS; g_mac0.queue[0].int_mask = &g_mac0.mac_base->INT_MASK; g_mac0.queue[0].int_enable = &g_mac0.mac_base->INT_ENABLE; g_mac0.queue[0].int_disable = &g_mac0.mac_base->INT_DISABLE; g_mac0.queue[0].receive_q_ptr = &g_mac0.mac_base->RECEIVE_Q_PTR; g_mac0.queue[0].transmit_q_ptr = &g_mac0.mac_base->TRANSMIT_Q_PTR; g_mac0.queue[0].dma_rxbuf_size = &g_mac0.mac_base->DMA_RXBUF_SIZE_Q1; /* Not really true as this is done differently for queue 0 */ #endif #if defined(TARGET_G5_SOC) (void)memset(this_mac, 0, sizeof(mss_mac_instance_t)); /* Start with blank canvas */ this_mac->use_hi_address = cfg->use_hi_address; /* Need to remember this for sanity checking */ this_mac->use_local_ints = cfg->use_local_ints; /* Need to remember this for interrupt management */ if(&g_mac0 == this_mac) { if(MSS_MAC_DISABLE == cfg->use_hi_address) { this_mac->mac_base = (MAC_TypeDef *)MSS_MAC0_BASE; this_mac->emac_base = (eMAC_TypeDef *)MSS_EMAC0_BASE; SYSREG->APBBUS_CR &= ~MSS_MAC_GEM0_ABP_BIT; } else { this_mac->mac_base = (MAC_TypeDef *)MSS_MAC0_BASE_HI; this_mac->emac_base = (eMAC_TypeDef *)MSS_EMAC0_BASE_HI; SYSREG->APBBUS_CR |= MSS_MAC_GEM0_ABP_BIT; } if(0U != this_mac->use_local_ints) { PLIC_DisableIRQ(MAC0_INT_PLIC); PLIC_DisableIRQ(MAC0_QUEUE1_PLIC); PLIC_DisableIRQ(MAC0_QUEUE2_PLIC); PLIC_DisableIRQ(MAC0_QUEUE3_PLIC); PLIC_DisableIRQ(MAC0_MMSL_PLIC); this_mac->mac_q_int[0] = MAC0_INT_U54_INT; this_mac->mac_q_int[1] = MAC0_QUEUE1_U54_INT; this_mac->mac_q_int[2] = MAC0_QUEUE2_U54_INT; this_mac->mac_q_int[3] = MAC0_QUEUE3_U54_INT; this_mac->mmsl_int = MAC0_MMSL_U54_INT; } else { __disable_local_irq(MAC0_INT_U54_INT); __disable_local_irq(MAC0_QUEUE1_U54_INT); __disable_local_irq(MAC0_QUEUE2_U54_INT); __disable_local_irq(MAC0_QUEUE3_U54_INT); __disable_local_irq(MAC0_MMSL_U54_INT); this_mac->mac_q_int[0] = MAC0_INT_PLIC; this_mac->mac_q_int[1] = MAC0_QUEUE1_PLIC; this_mac->mac_q_int[2] = MAC0_QUEUE2_PLIC; this_mac->mac_q_int[3] = MAC0_QUEUE3_PLIC; this_mac->mmsl_int = MAC0_MMSL_PLIC; } } else if(&g_mac1 == this_mac) { if(MSS_MAC_DISABLE == cfg->use_hi_address) { this_mac->mac_base = (MAC_TypeDef *)MSS_MAC1_BASE; this_mac->emac_base = (eMAC_TypeDef *)MSS_EMAC1_BASE; SYSREG->APBBUS_CR &= ~MSS_MAC_GEM1_ABP_BIT; } else { this_mac->mac_base = (MAC_TypeDef *)MSS_MAC1_BASE_HI; this_mac->emac_base = (eMAC_TypeDef *)MSS_EMAC1_BASE_HI; SYSREG->APBBUS_CR |= MSS_MAC_GEM1_ABP_BIT; } if(0U != this_mac->use_local_ints) { PLIC_DisableIRQ(MAC1_INT_PLIC); PLIC_DisableIRQ(MAC1_QUEUE1_PLIC); PLIC_DisableIRQ(MAC1_QUEUE2_PLIC); PLIC_DisableIRQ(MAC1_QUEUE3_PLIC); PLIC_DisableIRQ(MAC1_MMSL_PLIC); this_mac->mac_q_int[0] = MAC1_INT_U54_INT; this_mac->mac_q_int[1] = MAC1_QUEUE1_U54_INT; this_mac->mac_q_int[2] = MAC1_QUEUE2_U54_INT; this_mac->mac_q_int[3] = MAC1_QUEUE3_U54_INT; this_mac->mmsl_int = MAC1_MMSL_U54_INT; } else { __disable_local_irq(MAC1_INT_U54_INT); __disable_local_irq(MAC1_QUEUE1_U54_INT); __disable_local_irq(MAC1_QUEUE2_U54_INT); __disable_local_irq(MAC1_QUEUE3_U54_INT); __disable_local_irq(MAC1_MMSL_U54_INT); this_mac->mac_q_int[0] = MAC1_INT_PLIC; this_mac->mac_q_int[1] = MAC1_QUEUE1_PLIC; this_mac->mac_q_int[2] = MAC1_QUEUE2_PLIC; this_mac->mac_q_int[3] = MAC1_QUEUE3_PLIC; this_mac->mmsl_int = MAC1_MMSL_PLIC; } } else if(&g_emac0 == this_mac) { this_mac->is_emac = 1; if(NULL_POINTER != g_mac0.mac_base) /* If pMAC already configured must use same */ { cfg->use_hi_address = g_mac0.use_hi_address; } if(MSS_MAC_DISABLE == cfg->use_hi_address) { this_mac->mac_base = (MAC_TypeDef *)MSS_MAC0_BASE; this_mac->emac_base = (eMAC_TypeDef *)MSS_EMAC0_BASE; SYSREG->APBBUS_CR &= ~MSS_MAC_GEM0_ABP_BIT; } else { this_mac->mac_base = (MAC_TypeDef *)MSS_MAC0_BASE_HI; this_mac->emac_base = (eMAC_TypeDef *)MSS_EMAC0_BASE_HI; } if(0U != this_mac->use_local_ints) { PLIC_DisableIRQ(MAC0_EMAC_PLIC); this_mac->mac_q_int[0] = MAC0_EMAC_U54_INT; this_mac->mac_q_int[1] = LOCAL_INT_UNUSED; this_mac->mac_q_int[2] = LOCAL_INT_UNUSED; this_mac->mac_q_int[3] = LOCAL_INT_UNUSED; this_mac->mmsl_int = LOCAL_INT_UNUSED; } else { __disable_local_irq(MAC0_EMAC_U54_INT); this_mac->mac_q_int[0] = MAC0_EMAC_PLIC; this_mac->mac_q_int[1] = INVALID_IRQn; this_mac->mac_q_int[2] = INVALID_IRQn; this_mac->mac_q_int[3] = INVALID_IRQn; this_mac->mmsl_int = INVALID_IRQn; } } else { if(&g_emac1 == this_mac) { this_mac->is_emac = 1U; if(MSS_MAC_DISABLE == cfg->use_hi_address) { this_mac->mac_base = (MAC_TypeDef *)MSS_MAC1_BASE; this_mac->emac_base = (eMAC_TypeDef *)MSS_EMAC1_BASE; SYSREG->APBBUS_CR &= ~MSS_MAC_GEM1_ABP_BIT; } else { this_mac->mac_base = (MAC_TypeDef *)MSS_MAC1_BASE_HI; this_mac->emac_base = (eMAC_TypeDef *)MSS_EMAC1_BASE_HI; SYSREG->APBBUS_CR |= MSS_MAC_GEM1_ABP_BIT; } if(0U != this_mac->use_local_ints) { PLIC_DisableIRQ(MAC1_EMAC_PLIC); this_mac->mac_q_int[0] = MAC1_EMAC_U54_INT; this_mac->mac_q_int[1] = LOCAL_INT_UNUSED; this_mac->mac_q_int[2] = LOCAL_INT_UNUSED; this_mac->mac_q_int[3] = LOCAL_INT_UNUSED; this_mac->mmsl_int = LOCAL_INT_UNUSED; } else { __disable_local_irq(MAC1_EMAC_U54_INT); this_mac->mac_q_int[0] = MAC1_EMAC_PLIC; this_mac->mac_q_int[1] = INVALID_IRQn; this_mac->mac_q_int[2] = INVALID_IRQn; this_mac->mac_q_int[3] = INVALID_IRQn; this_mac->mmsl_int = INVALID_IRQn; } } } if(0U == this_mac->is_emac) { this_mac->queue[0].int_status = &this_mac->mac_base->INT_STATUS; this_mac->queue[1].int_status = &this_mac->mac_base->INT_Q1_STATUS; this_mac->queue[2].int_status = &this_mac->mac_base->INT_Q2_STATUS; this_mac->queue[3].int_status = &this_mac->mac_base->INT_Q3_STATUS; this_mac->queue[0].int_mask = &this_mac->mac_base->INT_MASK; this_mac->queue[1].int_mask = &this_mac->mac_base->INT_Q1_MASK; this_mac->queue[2].int_mask = &this_mac->mac_base->INT_Q2_MASK; this_mac->queue[3].int_mask = &this_mac->mac_base->INT_Q3_MASK; this_mac->queue[0].int_enable = &this_mac->mac_base->INT_ENABLE; this_mac->queue[1].int_enable = &this_mac->mac_base->INT_Q1_ENABLE; this_mac->queue[2].int_enable = &this_mac->mac_base->INT_Q2_ENABLE; this_mac->queue[3].int_enable = &this_mac->mac_base->INT_Q3_ENABLE; this_mac->queue[0].int_disable = &this_mac->mac_base->INT_DISABLE; this_mac->queue[1].int_disable = &this_mac->mac_base->INT_Q1_DISABLE; this_mac->queue[2].int_disable = &this_mac->mac_base->INT_Q2_DISABLE; this_mac->queue[3].int_disable = &this_mac->mac_base->INT_Q3_DISABLE; this_mac->queue[0].receive_q_ptr = &this_mac->mac_base->RECEIVE_Q_PTR; this_mac->queue[1].receive_q_ptr = &this_mac->mac_base->RECEIVE_Q1_PTR; this_mac->queue[2].receive_q_ptr = &this_mac->mac_base->RECEIVE_Q2_PTR; this_mac->queue[3].receive_q_ptr = &this_mac->mac_base->RECEIVE_Q3_PTR; this_mac->queue[0].transmit_q_ptr = &this_mac->mac_base->TRANSMIT_Q_PTR; this_mac->queue[1].transmit_q_ptr = &this_mac->mac_base->TRANSMIT_Q1_PTR; this_mac->queue[2].transmit_q_ptr = &this_mac->mac_base->TRANSMIT_Q2_PTR; this_mac->queue[3].transmit_q_ptr = &this_mac->mac_base->TRANSMIT_Q3_PTR; this_mac->queue[0].dma_rxbuf_size = &this_mac->mac_base->DMA_RXBUF_SIZE_Q1; /* Not really true as this is done differently for queue 0 */ this_mac->queue[1].dma_rxbuf_size = &this_mac->mac_base->DMA_RXBUF_SIZE_Q1; this_mac->queue[2].dma_rxbuf_size = &this_mac->mac_base->DMA_RXBUF_SIZE_Q2; this_mac->queue[3].dma_rxbuf_size = &this_mac->mac_base->DMA_RXBUF_SIZE_Q3; } else { this_mac->queue[0].int_status = &this_mac->emac_base->INT_STATUS; this_mac->queue[1].int_status = &this_mac->emac_base->INT_STATUS; /* Not Really correct but don't want 0 here... */ this_mac->queue[2].int_status = &this_mac->emac_base->INT_STATUS; this_mac->queue[3].int_status = &this_mac->emac_base->INT_STATUS; this_mac->queue[0].int_mask = &this_mac->emac_base->INT_MASK; this_mac->queue[1].int_mask = &this_mac->emac_base->INT_MASK; this_mac->queue[2].int_mask = &this_mac->emac_base->INT_MASK; this_mac->queue[3].int_mask = &this_mac->emac_base->INT_MASK; this_mac->queue[0].int_enable = &this_mac->emac_base->INT_ENABLE; this_mac->queue[1].int_enable = &this_mac->emac_base->INT_ENABLE; this_mac->queue[2].int_enable = &this_mac->emac_base->INT_ENABLE; this_mac->queue[3].int_enable = &this_mac->emac_base->INT_ENABLE; this_mac->queue[0].int_disable = &this_mac->emac_base->INT_DISABLE; this_mac->queue[1].int_disable = &this_mac->emac_base->INT_DISABLE; this_mac->queue[2].int_disable = &this_mac->emac_base->INT_DISABLE; this_mac->queue[3].int_disable = &this_mac->emac_base->INT_DISABLE; this_mac->queue[0].receive_q_ptr = &this_mac->emac_base->RECEIVE_Q_PTR; this_mac->queue[1].receive_q_ptr = &this_mac->emac_base->RECEIVE_Q_PTR; this_mac->queue[2].receive_q_ptr = &this_mac->emac_base->RECEIVE_Q_PTR; this_mac->queue[3].receive_q_ptr = &this_mac->emac_base->RECEIVE_Q_PTR; this_mac->queue[0].transmit_q_ptr = &this_mac->emac_base->TRANSMIT_Q_PTR; this_mac->queue[1].transmit_q_ptr = &this_mac->emac_base->TRANSMIT_Q_PTR; this_mac->queue[2].transmit_q_ptr = &this_mac->emac_base->TRANSMIT_Q_PTR; this_mac->queue[3].transmit_q_ptr = &this_mac->emac_base->TRANSMIT_Q_PTR; this_mac->queue[0].dma_rxbuf_size = &this_mac->mac_base->DMA_RXBUF_SIZE_Q1; /* Not really true as this is done differently for queue 0 */ this_mac->queue[1].dma_rxbuf_size = &this_mac->mac_base->DMA_RXBUF_SIZE_Q1; /* Not Really correct but don't want 0 here... */ this_mac->queue[2].dma_rxbuf_size = &this_mac->mac_base->DMA_RXBUF_SIZE_Q2; this_mac->queue[3].dma_rxbuf_size = &this_mac->mac_base->DMA_RXBUF_SIZE_Q3; } #endif /* defined(TARGET_G5_SOC) */ } #ifdef __cplusplus } #endif /******************************** END OF FILE ******************************/