/* * Copyright 2008-2012 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Freescale Semiconductor nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * * ALTERNATIVELY, this software may be distributed under the terms of the * GNU General Public License ("GPL") as published by the Free Software * Foundation, either version 2 of that License or (at your option) any * later version. * * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /****************************************************************************** @File memac.c @Description FM mEMAC driver *//***************************************************************************/ #include "std_ext.h" #include "string_ext.h" #include "error_ext.h" #include "xx_ext.h" #include "endian_ext.h" #include "debug_ext.h" #include "fm_common.h" #include "memac.h" /*****************************************************************************/ /* Internal routines */ /*****************************************************************************/ /* ......................................................................... */ static uint32_t GetMacAddrHashCode(uint64_t ethAddr) { uint64_t mask1, mask2; uint32_t xorVal = 0; uint8_t i, j; for (i=0; i<6; i++) { mask1 = ethAddr & (uint64_t)0x01; ethAddr >>= 1; for (j=0; j<7; j++) { mask2 = ethAddr & (uint64_t)0x01; mask1 ^= mask2; ethAddr >>= 1; } xorVal |= (mask1 << (5-i)); } return xorVal; } /* ......................................................................... */ static void SetupSgmiiInternalPhy(t_Memac *p_Memac, uint8_t phyAddr) { uint16_t tmpReg16; e_EnetMode enetMode; /* In case the higher MACs are used (i.e. the MACs that should support 10G), speed=10000 is provided for SGMII ports. Temporary modify enet mode to 1G one, so MII functions can work correctly. */ enetMode = p_Memac->enetMode; /* SGMII mode + AN enable */ tmpReg16 = PHY_SGMII_IF_MODE_AN | PHY_SGMII_IF_MODE_SGMII; if ((p_Memac->enetMode) == e_ENET_MODE_SGMII_2500) tmpReg16 = PHY_SGMII_CR_PHY_RESET | PHY_SGMII_IF_SPEED_GIGABIT | PHY_SGMII_IF_MODE_SGMII; p_Memac->enetMode = MAKE_ENET_MODE(ENET_INTERFACE_FROM_MODE(p_Memac->enetMode), e_ENET_SPEED_1000); MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x14, tmpReg16); /* Device ability according to SGMII specification */ tmpReg16 = PHY_SGMII_DEV_ABILITY_SGMII; MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x4, tmpReg16); /* Adjust link timer for SGMII - According to Cisco SGMII specification the timer should be 1.6 ms. The link_timer register is configured in units of the clock. - When running as 1G SGMII, Serdes clock is 125 MHz, so unit = 1 / (125*10^6 Hz) = 8 ns. 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2 * 10^5 = 0x30d40 - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so unit = 1 / (312.5*10^6 Hz) = 3.2 ns. 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5 * 10^5 = 0x7a120. Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, we always set up here a value of 2.5 SGMII. */ MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x13, 0x0007); MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x12, 0xa120); /* Restart AN */ tmpReg16 = PHY_SGMII_CR_DEF_VAL | PHY_SGMII_CR_RESET_AN; MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x0, tmpReg16); /* Restore original enet mode */ p_Memac->enetMode = enetMode; } /* ......................................................................... */ static void SetupSgmiiInternalPhyBaseX(t_Memac *p_Memac, uint8_t phyAddr) { uint16_t tmpReg16; e_EnetMode enetMode; /* In case the higher MACs are used (i.e. the MACs that should support 10G), speed=10000 is provided for SGMII ports. Temporary modify enet mode to 1G one, so MII functions can work correctly. */ enetMode = p_Memac->enetMode; p_Memac->enetMode = MAKE_ENET_MODE(ENET_INTERFACE_FROM_MODE(p_Memac->enetMode), e_ENET_SPEED_1000); /* 1000BaseX mode */ tmpReg16 = PHY_SGMII_IF_MODE_1000X; MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x14, tmpReg16); /* AN Device capability */ tmpReg16 = PHY_SGMII_DEV_ABILITY_1000X; MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x4, tmpReg16); /* Adjust link timer for SGMII - For Serdes 1000BaseX auto-negotiation the timer should be 10 ms. The link_timer register is configured in units of the clock. - When running as 1G SGMII, Serdes clock is 125 MHz, so unit = 1 / (125*10^6 Hz) = 8 ns. 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0 - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so unit = 1 / (312.5*10^6 Hz) = 3.2 ns. 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08. Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, we always set up here a value of 2.5 SGMII. */ MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x13, 0x002f); MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x12, 0xaf08); /* Restart AN */ tmpReg16 = PHY_SGMII_CR_DEF_VAL | PHY_SGMII_CR_RESET_AN; MEMAC_MII_WritePhyReg(p_Memac, phyAddr, 0x0, tmpReg16); /* Restore original enet mode */ p_Memac->enetMode = enetMode; } /* ......................................................................... */ static t_Error CheckInitParameters(t_Memac *p_Memac) { e_FmMacType portType; portType = ((ENET_SPEED_FROM_MODE(p_Memac->enetMode) < e_ENET_SPEED_10000) ? e_FM_MAC_1G : e_FM_MAC_10G); #if (FM_MAX_NUM_OF_10G_MACS > 0) if ((portType == e_FM_MAC_10G) && (p_Memac->macId >= FM_MAX_NUM_OF_10G_MACS)) RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("10G MAC ID must be less than %d", FM_MAX_NUM_OF_10G_MACS)); #endif /* (FM_MAX_NUM_OF_10G_MACS > 0) */ if ((portType == e_FM_MAC_1G) && (p_Memac->macId >= FM_MAX_NUM_OF_1G_MACS)) RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("1G MAC ID must be less than %d", FM_MAX_NUM_OF_1G_MACS)); if (p_Memac->addr == 0) RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Ethernet MAC must have a valid MAC address")); if (!p_Memac->f_Exception) RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Uninitialized f_Exception")); if (!p_Memac->f_Event) RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Uninitialized f_Event")); #ifdef FM_LEN_CHECK_ERRATA_FMAN_SW002 if (!p_Memac->p_MemacDriverParam->no_length_check_enable) RETURN_ERROR(MINOR, E_NOT_SUPPORTED, ("LengthCheck!")); #endif /* FM_LEN_CHECK_ERRATA_FMAN_SW002 */ return E_OK; } /* ........................................................................... */ static void MemacErrException(t_Handle h_Memac) { t_Memac *p_Memac = (t_Memac *)h_Memac; uint32_t event, imask; event = fman_memac_get_event(p_Memac->p_MemMap, 0xffffffff); imask = fman_memac_get_interrupt_mask(p_Memac->p_MemMap); /* Imask include both error and notification/event bits. Leaving only error bits enabled by imask. The imask error bits are shifted by 16 bits offset from their corresponding location in the ievent - hence the >> 16 */ event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); fman_memac_ack_event(p_Memac->p_MemMap, event); if (event & MEMAC_IEVNT_TS_ECC_ER) p_Memac->f_Exception(p_Memac->h_App, e_FM_MAC_EX_TS_FIFO_ECC_ERR); if (event & MEMAC_IEVNT_TX_ECC_ER) p_Memac->f_Exception(p_Memac->h_App, e_FM_MAC_EX_10G_1TX_ECC_ER); if (event & MEMAC_IEVNT_RX_ECC_ER) p_Memac->f_Exception(p_Memac->h_App, e_FM_MAC_EX_10G_RX_ECC_ER); } static void MemacException(t_Handle h_Memac) { t_Memac *p_Memac = (t_Memac *)h_Memac; uint32_t event, imask; event = fman_memac_get_event(p_Memac->p_MemMap, 0xffffffff); imask = fman_memac_get_interrupt_mask(p_Memac->p_MemMap); /* Imask include both error and notification/event bits. Leaving only error bits enabled by imask. The imask error bits are shifted by 16 bits offset from their corresponding location in the ievent - hence the >> 16 */ event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); fman_memac_ack_event(p_Memac->p_MemMap, event); if (event & MEMAC_IEVNT_MGI) p_Memac->f_Exception(p_Memac->h_App, e_FM_MAC_EX_MAGIC_PACKET_INDICATION); } /* ......................................................................... */ static void FreeInitResources(t_Memac *p_Memac) { e_FmMacType portType; portType = ((ENET_SPEED_FROM_MODE(p_Memac->enetMode) < e_ENET_SPEED_10000) ? e_FM_MAC_1G : e_FM_MAC_10G); if (portType == e_FM_MAC_10G) FmUnregisterIntr(p_Memac->fmMacControllerDriver.h_Fm, e_FM_MOD_10G_MAC, p_Memac->macId, e_FM_INTR_TYPE_ERR); else FmUnregisterIntr(p_Memac->fmMacControllerDriver.h_Fm, e_FM_MOD_1G_MAC, p_Memac->macId, e_FM_INTR_TYPE_ERR); /* release the driver's group hash table */ FreeHashTable(p_Memac->p_MulticastAddrHash); p_Memac->p_MulticastAddrHash = NULL; /* release the driver's individual hash table */ FreeHashTable(p_Memac->p_UnicastAddrHash); p_Memac->p_UnicastAddrHash = NULL; } /*****************************************************************************/ /* mEMAC API routines */ /*****************************************************************************/ /* ......................................................................... */ static t_Error MemacEnable(t_Handle h_Memac, e_CommMode mode) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); fman_memac_enable(p_Memac->p_MemMap, (mode & e_COMM_MODE_RX), (mode & e_COMM_MODE_TX)); return E_OK; } /* ......................................................................... */ static t_Error MemacDisable (t_Handle h_Memac, e_CommMode mode) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); fman_memac_disable(p_Memac->p_MemMap, (mode & e_COMM_MODE_RX), (mode & e_COMM_MODE_TX)); return E_OK; } /* ......................................................................... */ static t_Error MemacSetPromiscuous(t_Handle h_Memac, bool newVal) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); fman_memac_set_promiscuous(p_Memac->p_MemMap, newVal); return E_OK; } /* .............................................................................. */ static t_Error MemacAdjustLink(t_Handle h_Memac, e_EnetSpeed speed, bool fullDuplex) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); if ((speed >= e_ENET_SPEED_1000) && (!fullDuplex)) RETURN_ERROR(MAJOR, E_CONFLICT, ("Ethernet MAC 1G or 10G does not support half-duplex")); fman_memac_adjust_link(p_Memac->p_MemMap, (enum enet_interface)ENET_INTERFACE_FROM_MODE(p_Memac->enetMode), (enum enet_speed)speed, fullDuplex); return E_OK; } /*****************************************************************************/ /* Memac Configs modification functions */ /*****************************************************************************/ /* ......................................................................... */ static t_Error MemacConfigLoopback(t_Handle h_Memac, bool newVal) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(p_Memac->p_MemacDriverParam, E_INVALID_STATE); p_Memac->p_MemacDriverParam->loopback_enable = newVal; return E_OK; } /* ......................................................................... */ static t_Error MemacConfigWan(t_Handle h_Memac, bool newVal) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(p_Memac->p_MemacDriverParam, E_INVALID_STATE); p_Memac->p_MemacDriverParam->wan_mode_enable = newVal; return E_OK; } /* ......................................................................... */ static t_Error MemacConfigMaxFrameLength(t_Handle h_Memac, uint16_t newVal) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(p_Memac->p_MemacDriverParam, E_INVALID_STATE); p_Memac->p_MemacDriverParam->max_frame_length = newVal; return E_OK; } /* ......................................................................... */ static t_Error MemacConfigPad(t_Handle h_Memac, bool newVal) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(p_Memac->p_MemacDriverParam, E_INVALID_STATE); p_Memac->p_MemacDriverParam->pad_enable = newVal; return E_OK; } /* ......................................................................... */ static t_Error MemacConfigLengthCheck(t_Handle h_Memac, bool newVal) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(p_Memac->p_MemacDriverParam, E_INVALID_STATE); p_Memac->p_MemacDriverParam->no_length_check_enable = !newVal; return E_OK; } /* ......................................................................... */ static t_Error MemacConfigException(t_Handle h_Memac, e_FmMacExceptions exception, bool enable) { t_Memac *p_Memac = (t_Memac *)h_Memac; uint32_t bitMask = 0; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(p_Memac->p_MemacDriverParam, E_INVALID_STATE); GET_EXCEPTION_FLAG(bitMask, exception); if (bitMask) { if (enable) p_Memac->exceptions |= bitMask; else p_Memac->exceptions &= ~bitMask; } else RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Undefined exception")); return E_OK; } /* ......................................................................... */ static t_Error MemacConfigResetOnInit(t_Handle h_Memac, bool enable) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(p_Memac->p_MemacDriverParam, E_INVALID_STATE); p_Memac->p_MemacDriverParam->reset_on_init = enable; return E_OK; } /*****************************************************************************/ /* Memac Run Time API functions */ /*****************************************************************************/ /* ......................................................................... */ static t_Error MemacSetTxPauseFrames(t_Handle h_Memac, uint8_t priority, uint16_t pauseTime, uint16_t threshTime) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_STATE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); if (priority != 0xFF) { bool PortConfigured, PreFetchEnabled; if (FmGetTnumAgingPeriod(p_Memac->fmMacControllerDriver.h_Fm) == 0) RETURN_ERROR(MAJOR, E_CONFLICT, ("For PFC operation, TNUM aging must be enabled")); FmGetPortPreFetchConfiguration(p_Memac->fmMacControllerDriver.h_Fm, p_Memac->fmMacControllerDriver.macId, &PortConfigured, &PreFetchEnabled); if ((ENET_SPEED_FROM_MODE(p_Memac->fmMacControllerDriver.enetMode) == e_ENET_SPEED_1000) && !PortConfigured) DBG(INFO, ("For PFC correct operation, prefetch must be configured on the FM Tx PORT")); if ((ENET_SPEED_FROM_MODE(p_Memac->fmMacControllerDriver.enetMode) == e_ENET_SPEED_1000) && PortConfigured && !PreFetchEnabled) DBG(WARNING, ("For PFC correct operation, prefetch must be configured on the FM Tx PORT")); } fman_memac_set_tx_pause_frames(p_Memac->p_MemMap, priority, pauseTime, threshTime); return E_OK; } /* ......................................................................... */ static t_Error MemacSetTxAutoPauseFrames(t_Handle h_Memac, uint16_t pauseTime) { return MemacSetTxPauseFrames(h_Memac, FM_MAC_NO_PFC, pauseTime, 0); } /* ......................................................................... */ static t_Error MemacSetRxIgnorePauseFrames(t_Handle h_Memac, bool en) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_STATE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); fman_memac_set_rx_ignore_pause_frames(p_Memac->p_MemMap, en); return E_OK; } /* ......................................................................... */ static t_Error MemacSetWakeOnLan(t_Handle h_Memac, bool en) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_STATE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); fman_memac_set_wol(p_Memac->p_MemMap, en); return E_OK; } /* .............................................................................. */ static t_Error MemacEnable1588TimeStamp(t_Handle h_Memac) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); UNUSED(p_Memac); DBG(WARNING, ("mEMAC has 1588 always enabled!")); return E_OK; } /* Counters handling */ /* ......................................................................... */ static t_Error MemacGetStatistics(t_Handle h_Memac, t_FmMacStatistics *p_Statistics) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_NULL_POINTER); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); SANITY_CHECK_RETURN_ERROR(p_Statistics, E_NULL_POINTER); p_Statistics->eStatPkts64 = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_R64); p_Statistics->eStatPkts65to127 = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_R127); p_Statistics->eStatPkts128to255 = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_R255); p_Statistics->eStatPkts256to511 = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_R511); p_Statistics->eStatPkts512to1023 = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_R1023); p_Statistics->eStatPkts1024to1518 = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_R1518); p_Statistics->eStatPkts1519to1522 = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_R1519X); /* */ p_Statistics->eStatFragments = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RFRG); p_Statistics->eStatJabbers = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RJBR); p_Statistics->eStatsDropEvents = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RDRP); p_Statistics->eStatCRCAlignErrors = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RALN); p_Statistics->eStatUndersizePkts = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_TUND); p_Statistics->eStatOversizePkts = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_ROVR); /* Pause */ p_Statistics->reStatPause = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RXPF); p_Statistics->teStatPause = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_TXPF); /* MIB II */ p_Statistics->ifInOctets = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_ROCT); p_Statistics->ifInUcastPkts = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RUCA); p_Statistics->ifInMcastPkts = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RMCA); p_Statistics->ifInBcastPkts = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RBCA); p_Statistics->ifInPkts = p_Statistics->ifInUcastPkts + p_Statistics->ifInMcastPkts + p_Statistics->ifInBcastPkts; p_Statistics->ifInDiscards = 0; p_Statistics->ifInErrors = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_RERR); p_Statistics->ifOutOctets = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_TOCT); p_Statistics->ifOutUcastPkts = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_TUCA); p_Statistics->ifOutMcastPkts = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_TMCA); p_Statistics->ifOutBcastPkts = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_TBCA); p_Statistics->ifOutPkts = p_Statistics->ifOutUcastPkts + p_Statistics->ifOutMcastPkts + p_Statistics->ifOutBcastPkts; p_Statistics->ifOutDiscards = 0; p_Statistics->ifOutErrors = fman_memac_get_counter(p_Memac->p_MemMap, E_MEMAC_COUNTER_TERR); return E_OK; } /* ......................................................................... */ static t_Error MemacModifyMacAddress (t_Handle h_Memac, t_EnetAddr *p_EnetAddr) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_NULL_POINTER); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); fman_memac_add_addr_in_paddr(p_Memac->p_MemMap, (uint8_t *)(*p_EnetAddr), 0); return E_OK; } /* ......................................................................... */ static t_Error MemacResetCounters (t_Handle h_Memac) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); fman_memac_reset_stat(p_Memac->p_MemMap); return E_OK; } /* ......................................................................... */ static t_Error MemacAddExactMatchMacAddress(t_Handle h_Memac, t_EnetAddr *p_EthAddr) { t_Memac *p_Memac = (t_Memac *) h_Memac; uint64_t ethAddr; uint8_t paddrNum; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); ethAddr = ENET_ADDR_TO_UINT64(*p_EthAddr); if (ethAddr & GROUP_ADDRESS) /* Multicast address has no effect in PADDR */ RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Multicast address")); /* Make sure no PADDR contains this address */ for (paddrNum = 0; paddrNum < MEMAC_NUM_OF_PADDRS; paddrNum++) if (p_Memac->indAddrRegUsed[paddrNum]) if (p_Memac->paddr[paddrNum] == ethAddr) RETURN_ERROR(MAJOR, E_ALREADY_EXISTS, NO_MSG); /* Find first unused PADDR */ for (paddrNum = 0; paddrNum < MEMAC_NUM_OF_PADDRS; paddrNum++) if (!(p_Memac->indAddrRegUsed[paddrNum])) { /* mark this PADDR as used */ p_Memac->indAddrRegUsed[paddrNum] = TRUE; /* store address */ p_Memac->paddr[paddrNum] = ethAddr; /* put in hardware */ fman_memac_add_addr_in_paddr(p_Memac->p_MemMap, (uint8_t*)(*p_EthAddr), paddrNum); p_Memac->numOfIndAddrInRegs++; return E_OK; } /* No free PADDR */ RETURN_ERROR(MAJOR, E_FULL, NO_MSG); } /* ......................................................................... */ static t_Error MemacDelExactMatchMacAddress(t_Handle h_Memac, t_EnetAddr *p_EthAddr) { t_Memac *p_Memac = (t_Memac *) h_Memac; uint64_t ethAddr; uint8_t paddrNum; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); ethAddr = ENET_ADDR_TO_UINT64(*p_EthAddr); /* Find used PADDR containing this address */ for (paddrNum = 0; paddrNum < MEMAC_NUM_OF_PADDRS; paddrNum++) { if ((p_Memac->indAddrRegUsed[paddrNum]) && (p_Memac->paddr[paddrNum] == ethAddr)) { /* mark this PADDR as not used */ p_Memac->indAddrRegUsed[paddrNum] = FALSE; /* clear in hardware */ fman_memac_clear_addr_in_paddr(p_Memac->p_MemMap, paddrNum); p_Memac->numOfIndAddrInRegs--; return E_OK; } } RETURN_ERROR(MAJOR, E_NOT_FOUND, NO_MSG); } /* ......................................................................... */ static t_Error MemacGetId(t_Handle h_Memac, uint32_t *macId) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); *macId = p_Memac->macId; return E_OK; } /* ......................................................................... */ static t_Error MemacAddHashMacAddress(t_Handle h_Memac, t_EnetAddr *p_EthAddr) { t_Memac *p_Memac = (t_Memac *)h_Memac; t_EthHashEntry *p_HashEntry; uint32_t hash; uint64_t ethAddr; SANITY_CHECK_RETURN_ERROR(p_Memac, E_NULL_POINTER); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); ethAddr = ENET_ADDR_TO_UINT64(*p_EthAddr); if (!(ethAddr & GROUP_ADDRESS)) /* Unicast addresses not supported in hash */ RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("Unicast Address")); hash = GetMacAddrHashCode(ethAddr) & HASH_CTRL_ADDR_MASK; /* Create element to be added to the driver hash table */ p_HashEntry = (t_EthHashEntry *)XX_Malloc(sizeof(t_EthHashEntry)); p_HashEntry->addr = ethAddr; INIT_LIST(&p_HashEntry->node); LIST_AddToTail(&(p_HashEntry->node), &(p_Memac->p_MulticastAddrHash->p_Lsts[hash])); fman_memac_set_hash_table(p_Memac->p_MemMap, (hash | HASH_CTRL_MCAST_EN)); return E_OK; } /* ......................................................................... */ static t_Error MemacDelHashMacAddress(t_Handle h_Memac, t_EnetAddr *p_EthAddr) { t_Memac *p_Memac = (t_Memac *)h_Memac; t_EthHashEntry *p_HashEntry = NULL; t_List *p_Pos; uint32_t hash; uint64_t ethAddr; SANITY_CHECK_RETURN_ERROR(p_Memac, E_NULL_POINTER); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); ethAddr = ENET_ADDR_TO_UINT64(*p_EthAddr); hash = GetMacAddrHashCode(ethAddr) & HASH_CTRL_ADDR_MASK; LIST_FOR_EACH(p_Pos, &(p_Memac->p_MulticastAddrHash->p_Lsts[hash])) { p_HashEntry = ETH_HASH_ENTRY_OBJ(p_Pos); if (p_HashEntry->addr == ethAddr) { LIST_DelAndInit(&p_HashEntry->node); XX_Free(p_HashEntry); break; } } if (LIST_IsEmpty(&p_Memac->p_MulticastAddrHash->p_Lsts[hash])) fman_memac_set_hash_table(p_Memac->p_MemMap, (hash & ~HASH_CTRL_MCAST_EN)); return E_OK; } /* ......................................................................... */ static t_Error MemacSetException(t_Handle h_Memac, e_FmMacExceptions exception, bool enable) { t_Memac *p_Memac = (t_Memac *)h_Memac; uint32_t bitMask = 0; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(!p_Memac->p_MemacDriverParam, E_INVALID_STATE); GET_EXCEPTION_FLAG(bitMask, exception); if (bitMask) { if (enable) p_Memac->exceptions |= bitMask; else p_Memac->exceptions &= ~bitMask; } else RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Undefined exception")); fman_memac_set_exception(p_Memac->p_MemMap, bitMask, enable); return E_OK; } /* ......................................................................... */ static uint16_t MemacGetMaxFrameLength(t_Handle h_Memac) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_VALUE(p_Memac, E_INVALID_HANDLE, 0); SANITY_CHECK_RETURN_VALUE(!p_Memac->p_MemacDriverParam, E_INVALID_STATE, 0); return fman_memac_get_max_frame_len(p_Memac->p_MemMap); } /*****************************************************************************/ /* mEMAC Init & Free API */ /*****************************************************************************/ /* ......................................................................... */ void *g_MemacRegs; static t_Error MemacInit(t_Handle h_Memac) { t_Memac *p_Memac = (t_Memac *)h_Memac; struct memac_cfg *p_MemacDriverParam; enum enet_interface enet_interface; enum enet_speed enet_speed; uint8_t i, phyAddr; t_EnetAddr ethAddr; e_FmMacType portType; t_Error err; bool slow_10g_if = FALSE; if (p_Memac->macId == 3) /* This is a quick WA */ g_MemacRegs = p_Memac->p_MemMap; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); SANITY_CHECK_RETURN_ERROR(p_Memac->p_MemacDriverParam, E_INVALID_STATE); SANITY_CHECK_RETURN_ERROR(p_Memac->fmMacControllerDriver.h_Fm, E_INVALID_HANDLE); FM_GetRevision(p_Memac->fmMacControllerDriver.h_Fm, &p_Memac->fmMacControllerDriver.fmRevInfo); if (p_Memac->fmMacControllerDriver.fmRevInfo.majorRev == 6 && p_Memac->fmMacControllerDriver.fmRevInfo.minorRev == 4) slow_10g_if = TRUE; CHECK_INIT_PARAMETERS(p_Memac, CheckInitParameters); p_MemacDriverParam = p_Memac->p_MemacDriverParam; portType = ((ENET_SPEED_FROM_MODE(p_Memac->enetMode) < e_ENET_SPEED_10000) ? e_FM_MAC_1G : e_FM_MAC_10G); /* First, reset the MAC if desired. */ if (p_MemacDriverParam->reset_on_init) fman_memac_reset(p_Memac->p_MemMap); /* MAC Address */ MAKE_ENET_ADDR_FROM_UINT64(p_Memac->addr, ethAddr); fman_memac_add_addr_in_paddr(p_Memac->p_MemMap, (uint8_t*)ethAddr, 0); enet_interface = (enum enet_interface) ENET_INTERFACE_FROM_MODE(p_Memac->enetMode); enet_speed = (enum enet_speed) ENET_SPEED_FROM_MODE(p_Memac->enetMode); fman_memac_init(p_Memac->p_MemMap, p_Memac->p_MemacDriverParam, enet_interface, enet_speed, slow_10g_if, p_Memac->exceptions); #ifdef FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 { uint32_t tmpReg = 0; FM_GetRevision(p_Memac->fmMacControllerDriver.h_Fm, &p_Memac->fmMacControllerDriver.fmRevInfo); /* check the FMAN version - the bug exists only in rev1 */ if ((p_Memac->fmMacControllerDriver.fmRevInfo.majorRev == 6) && (p_Memac->fmMacControllerDriver.fmRevInfo.minorRev == 0)) { /* MAC strips CRC from received frames - this workaround should decrease the likelihood of bug appearance */ tmpReg = GET_UINT32(p_Memac->p_MemMap->command_config); tmpReg &= ~CMD_CFG_CRC_FWD; WRITE_UINT32(p_Memac->p_MemMap->command_config, tmpReg); /* DBG(WARNING, ("mEMAC strips CRC from received frames as part of A006320 errata workaround"));*/ } } #endif /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 */ if (ENET_INTERFACE_FROM_MODE(p_Memac->enetMode) == e_ENET_IF_SGMII) { /* Configure internal SGMII PHY */ if (p_Memac->enetMode & ENET_IF_SGMII_BASEX) SetupSgmiiInternalPhyBaseX(p_Memac, PHY_MDIO_ADDR); else SetupSgmiiInternalPhy(p_Memac, PHY_MDIO_ADDR); } else if (ENET_INTERFACE_FROM_MODE(p_Memac->enetMode) == e_ENET_IF_QSGMII) { /* Configure 4 internal SGMII PHYs */ for (i = 0; i < 4; i++) { /* QSGMII PHY address occupies 3 upper bits of 5-bit phyAddress; the lower 2 bits are used to extend register address space and access each one of 4 ports inside QSGMII. */ phyAddr = (uint8_t)((PHY_MDIO_ADDR << 2) | i); if (p_Memac->enetMode & ENET_IF_SGMII_BASEX) SetupSgmiiInternalPhyBaseX(p_Memac, phyAddr); else SetupSgmiiInternalPhy(p_Memac, phyAddr); } } /* Max Frame Length */ err = FmSetMacMaxFrame(p_Memac->fmMacControllerDriver.h_Fm, portType, p_Memac->fmMacControllerDriver.macId, p_MemacDriverParam->max_frame_length); if (err) RETURN_ERROR(MAJOR, err, ("settings Mac max frame length is FAILED")); p_Memac->p_MulticastAddrHash = AllocHashTable(HASH_TABLE_SIZE); if (!p_Memac->p_MulticastAddrHash) { FreeInitResources(p_Memac); RETURN_ERROR(MAJOR, E_NO_MEMORY, ("allocation hash table is FAILED")); } p_Memac->p_UnicastAddrHash = AllocHashTable(HASH_TABLE_SIZE); if (!p_Memac->p_UnicastAddrHash) { FreeInitResources(p_Memac); RETURN_ERROR(MAJOR, E_NO_MEMORY, ("allocation hash table is FAILED")); } FmRegisterIntr(p_Memac->fmMacControllerDriver.h_Fm, (portType == e_FM_MAC_10G) ? e_FM_MOD_10G_MAC : e_FM_MOD_1G_MAC, p_Memac->macId, e_FM_INTR_TYPE_ERR, MemacErrException, p_Memac); FmRegisterIntr(p_Memac->fmMacControllerDriver.h_Fm, (portType == e_FM_MAC_10G) ? e_FM_MOD_10G_MAC : e_FM_MOD_1G_MAC, p_Memac->macId, e_FM_INTR_TYPE_NORMAL, MemacException, p_Memac); XX_Free(p_MemacDriverParam); p_Memac->p_MemacDriverParam = NULL; return E_OK; } /* ......................................................................... */ static t_Error MemacFree(t_Handle h_Memac) { t_Memac *p_Memac = (t_Memac *)h_Memac; SANITY_CHECK_RETURN_ERROR(p_Memac, E_INVALID_HANDLE); if (p_Memac->p_MemacDriverParam) { /* Called after config */ XX_Free(p_Memac->p_MemacDriverParam); p_Memac->p_MemacDriverParam = NULL; } else /* Called after init */ FreeInitResources(p_Memac); XX_Free(p_Memac); return E_OK; } /* ......................................................................... */ static void InitFmMacControllerDriver(t_FmMacControllerDriver *p_FmMacControllerDriver) { p_FmMacControllerDriver->f_FM_MAC_Init = MemacInit; p_FmMacControllerDriver->f_FM_MAC_Free = MemacFree; p_FmMacControllerDriver->f_FM_MAC_SetStatistics = NULL; p_FmMacControllerDriver->f_FM_MAC_ConfigLoopback = MemacConfigLoopback; p_FmMacControllerDriver->f_FM_MAC_ConfigMaxFrameLength = MemacConfigMaxFrameLength; p_FmMacControllerDriver->f_FM_MAC_ConfigWan = MemacConfigWan; p_FmMacControllerDriver->f_FM_MAC_ConfigPadAndCrc = MemacConfigPad; p_FmMacControllerDriver->f_FM_MAC_ConfigHalfDuplex = NULL; /* half-duplex is detected automatically */ p_FmMacControllerDriver->f_FM_MAC_ConfigLengthCheck = MemacConfigLengthCheck; p_FmMacControllerDriver->f_FM_MAC_ConfigException = MemacConfigException; p_FmMacControllerDriver->f_FM_MAC_ConfigResetOnInit = MemacConfigResetOnInit; p_FmMacControllerDriver->f_FM_MAC_SetException = MemacSetException; p_FmMacControllerDriver->f_FM_MAC_Enable1588TimeStamp = MemacEnable1588TimeStamp; /* always enabled */ p_FmMacControllerDriver->f_FM_MAC_Disable1588TimeStamp = NULL; p_FmMacControllerDriver->f_FM_MAC_SetPromiscuous = MemacSetPromiscuous; p_FmMacControllerDriver->f_FM_MAC_AdjustLink = MemacAdjustLink; p_FmMacControllerDriver->f_FM_MAC_RestartAutoneg = NULL; p_FmMacControllerDriver->f_FM_MAC_Enable = MemacEnable; p_FmMacControllerDriver->f_FM_MAC_Disable = MemacDisable; p_FmMacControllerDriver->f_FM_MAC_SetTxAutoPauseFrames = MemacSetTxAutoPauseFrames; p_FmMacControllerDriver->f_FM_MAC_SetTxPauseFrames = MemacSetTxPauseFrames; p_FmMacControllerDriver->f_FM_MAC_SetRxIgnorePauseFrames = MemacSetRxIgnorePauseFrames; p_FmMacControllerDriver->f_FM_MAC_SetWakeOnLan = MemacSetWakeOnLan; p_FmMacControllerDriver->f_FM_MAC_ResetCounters = MemacResetCounters; p_FmMacControllerDriver->f_FM_MAC_GetStatistics = MemacGetStatistics; p_FmMacControllerDriver->f_FM_MAC_ModifyMacAddr = MemacModifyMacAddress; p_FmMacControllerDriver->f_FM_MAC_AddHashMacAddr = MemacAddHashMacAddress; p_FmMacControllerDriver->f_FM_MAC_RemoveHashMacAddr = MemacDelHashMacAddress; p_FmMacControllerDriver->f_FM_MAC_AddExactMatchMacAddr = MemacAddExactMatchMacAddress; p_FmMacControllerDriver->f_FM_MAC_RemovelExactMatchMacAddr = MemacDelExactMatchMacAddress; p_FmMacControllerDriver->f_FM_MAC_GetId = MemacGetId; p_FmMacControllerDriver->f_FM_MAC_GetVersion = NULL; p_FmMacControllerDriver->f_FM_MAC_GetMaxFrameLength = MemacGetMaxFrameLength; p_FmMacControllerDriver->f_FM_MAC_MII_WritePhyReg = MEMAC_MII_WritePhyReg; p_FmMacControllerDriver->f_FM_MAC_MII_ReadPhyReg = MEMAC_MII_ReadPhyReg; } /*****************************************************************************/ /* mEMAC Config Main Entry */ /*****************************************************************************/ /* ......................................................................... */ t_Handle MEMAC_Config(t_FmMacParams *p_FmMacParam) { t_Memac *p_Memac; struct memac_cfg *p_MemacDriverParam; uintptr_t baseAddr; SANITY_CHECK_RETURN_VALUE(p_FmMacParam, E_NULL_POINTER, NULL); baseAddr = p_FmMacParam->baseAddr; /* Allocate memory for the mEMAC data structure */ p_Memac = (t_Memac *)XX_Malloc(sizeof(t_Memac)); if (!p_Memac) { REPORT_ERROR(MAJOR, E_NO_MEMORY, ("mEMAC driver structure")); return NULL; } memset(p_Memac, 0, sizeof(t_Memac)); InitFmMacControllerDriver(&p_Memac->fmMacControllerDriver); /* Allocate memory for the mEMAC driver parameters data structure */ p_MemacDriverParam = (struct memac_cfg *)XX_Malloc(sizeof(struct memac_cfg)); if (!p_MemacDriverParam) { REPORT_ERROR(MAJOR, E_NO_MEMORY, ("mEMAC driver parameters")); XX_Free(p_Memac); return NULL; } memset(p_MemacDriverParam, 0, sizeof(struct memac_cfg)); /* Plant parameter structure pointer */ p_Memac->p_MemacDriverParam = p_MemacDriverParam; fman_memac_defconfig(p_MemacDriverParam); p_Memac->addr = ENET_ADDR_TO_UINT64(p_FmMacParam->addr); p_Memac->p_MemMap = (struct memac_regs *)UINT_TO_PTR(baseAddr); p_Memac->p_MiiMemMap = (struct memac_mii_access_mem_map*)UINT_TO_PTR(baseAddr + MEMAC_TO_MII_OFFSET); p_Memac->enetMode = p_FmMacParam->enetMode; p_Memac->macId = p_FmMacParam->macId; p_Memac->exceptions = MEMAC_default_exceptions; p_Memac->f_Exception = p_FmMacParam->f_Exception; p_Memac->f_Event = p_FmMacParam->f_Event; p_Memac->h_App = p_FmMacParam->h_App; return p_Memac; }