/* * Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "../osi/common/common.h" #include #include #include #include "mgbe_core.h" #include "core_local.h" #include "xpcs.h" #include "mgbe_mmc.h" #include "core_common.h" #include "macsec.h" /** * @brief mgbe_poll_for_mac_accrtl - Poll for Indirect Access control and status * register operations complete. * * Algorithm: Waits for waits for transfer busy bit to be cleared in * MAC Indirect address control register to complete operations. * * @param[in] osi_core: osi core priv data structure * * @note MAC needs to be out of reset and proper clock configured. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_poll_for_mac_acrtl(struct osi_core_priv_data *osi_core) { nveu32_t count = 0U; nveu32_t mac_indir_addr_ctrl = 0U; nve32_t ret = -1; /* Poll Until MAC_Indir_Access_Ctrl OB is clear */ while (count < MGBE_MAC_INDIR_AC_OB_RETRY) { mac_indir_addr_ctrl = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_INDIR_AC); if ((mac_indir_addr_ctrl & MGBE_MAC_INDIR_AC_OB) == OSI_NONE) { /* OB is clear exit the loop */ ret = 0; break; } /* wait for 10 usec for OB clear and retry */ osi_core->osd_ops.udelay(MGBE_MAC_INDIR_AC_OB_WAIT); count++; } return ret; } /** * @brief mgbe_mac_indir_addr_write - MAC Indirect AC register write. * * Algorithm: writes MAC Indirect AC register * * @param[in] osi_core: osi core priv data structure * @param[in] mc_no: MAC AC Mode Select number * @param[in] addr_offset: MAC AC Address Offset. * @param[in] value: MAC AC register value * * @note MAC needs to be out of reset and proper clock configured. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_mac_indir_addr_write(struct osi_core_priv_data *osi_core, nveu32_t mc_no, nveu32_t addr_offset, nveu32_t value) { void *base = osi_core->base; nveu32_t addr = 0; nve32_t ret = 0; /* Write MAC_Indir_Access_Data register value */ osi_writela(osi_core, value, (nveu8_t *)base + MGBE_MAC_INDIR_DATA); /* Program MAC_Indir_Access_Ctrl */ addr = osi_readla(osi_core, (nveu8_t *)base + MGBE_MAC_INDIR_AC); /* update Mode Select */ addr &= ~(MGBE_MAC_INDIR_AC_MSEL); addr |= ((mc_no << MGBE_MAC_INDIR_AC_MSEL_SHIFT) & MGBE_MAC_INDIR_AC_MSEL); /* update Address Offset */ addr &= ~(MGBE_MAC_INDIR_AC_AOFF); addr |= ((addr_offset << MGBE_MAC_INDIR_AC_AOFF_SHIFT) & MGBE_MAC_INDIR_AC_AOFF); /* Set CMD filed bit 0 for write */ addr &= ~(MGBE_MAC_INDIR_AC_CMD); /* Set OB bit to initiate write */ addr |= MGBE_MAC_INDIR_AC_OB; /* Write MGBE_MAC_L3L4_ADDR_CTR */ osi_writela(osi_core, addr, (nveu8_t *)base + MGBE_MAC_INDIR_AC); /* Wait until OB bit reset */ if (mgbe_poll_for_mac_acrtl(osi_core) < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Fail to write MAC_Indir_Access_Ctrl\n", mc_no); ret = -1; } return ret; } /** * @brief mgbe_mac_indir_addr_read - MAC Indirect AC register read. * * Algorithm: Reads MAC Indirect AC register * * @param[in] osi_core: osi core priv data structure * @param[in] mc_no: MAC AC Mode Select number * @param[in] addr_offset: MAC AC Address Offset. * @param[in] value: Pointer MAC AC register value * * @note MAC needs to be out of reset and proper clock configured. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_mac_indir_addr_read(struct osi_core_priv_data *osi_core, nveu32_t mc_no, nveu32_t addr_offset, nveu32_t *value) { void *base = osi_core->base; nveu32_t addr = 0; nve32_t ret = 0; /* Program MAC_Indir_Access_Ctrl */ addr = osi_readla(osi_core, (nveu8_t *)base + MGBE_MAC_INDIR_AC); /* update Mode Select */ addr &= ~(MGBE_MAC_INDIR_AC_MSEL); addr |= ((mc_no << MGBE_MAC_INDIR_AC_MSEL_SHIFT) & MGBE_MAC_INDIR_AC_MSEL); /* update Address Offset */ addr &= ~(MGBE_MAC_INDIR_AC_AOFF); addr |= ((addr_offset << MGBE_MAC_INDIR_AC_AOFF_SHIFT) & MGBE_MAC_INDIR_AC_AOFF); /* Set CMD filed bit to 1 for read */ addr |= MGBE_MAC_INDIR_AC_CMD; /* Set OB bit to initiate write */ addr |= MGBE_MAC_INDIR_AC_OB; /* Write MGBE_MAC_L3L4_ADDR_CTR */ osi_writela(osi_core, addr, (nveu8_t *)base + MGBE_MAC_INDIR_AC); /* Wait until OB bit reset */ if (mgbe_poll_for_mac_acrtl(osi_core) < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Fail to write MAC_Indir_Access_Ctrl\n", mc_no); ret = -1; goto fail; } /* Read MAC_Indir_Access_Data register value */ *value = osi_readla(osi_core, (nveu8_t *)base + MGBE_MAC_INDIR_DATA); fail: return ret; } /** * @brief mgbe_filter_args_validate - Validates the filter arguments * * Algorithm: This function just validates all arguments provided by * the osi_filter structure variable. * * @param[in] osi_core: OSI core private data structure. * @param[in] filter: OSI filter structure. * * @note 1) MAC should be initialized and stated. see osi_start_mac() * 2) osi_core->osd should be populated. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_filter_args_validate(struct osi_core_priv_data *const osi_core, const struct osi_filter *filter) { nveu32_t idx = filter->index; nveu32_t dma_routing_enable = filter->dma_routing; nveu32_t dma_chan = filter->dma_chan; nveu32_t addr_mask = filter->addr_mask; nveu32_t src_dest = filter->src_dest; nveu32_t dma_chansel = filter->dma_chansel; nve32_t ret = 0; (void) osi_core; /* check for valid index (0 to 31) */ if (idx >= OSI_MGBE_MAX_MAC_ADDRESS_FILTER) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "invalid MAC filter index\n", idx); ret = -1; goto fail; } /* check for DMA channel index (0 to 9) */ if ((dma_chan > (OSI_MGBE_MAX_NUM_CHANS - 0x1U)) && (dma_chan != OSI_CHAN_ANY)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_OUTOFBOUND, "invalid dma channel\n", (nveul64_t)dma_chan); ret = -1; goto fail; } /* validate dma_chansel argument */ if (dma_chansel > MGBE_MAC_XDCS_DMA_MAX) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_OUTOFBOUND, "invalid dma_chansel value\n", dma_chansel); ret = -1; goto fail; } /* validate addr_mask argument */ if (addr_mask > MGBE_MAB_ADDRH_MBC_MAX_MASK) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid addr_mask value\n", addr_mask); ret = -1; goto fail; } /* validate src_dest argument */ if ((src_dest != OSI_SA_MATCH) && (src_dest != OSI_DA_MATCH)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid src_dest value\n", src_dest); ret = -1; goto fail; } /* validate dma_routing_enable argument */ if ((dma_routing_enable != OSI_ENABLE) && (dma_routing_enable != OSI_DISABLE)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid dma_routing value\n", dma_routing_enable); ret = -1; } fail: return ret; } /** * @brief mgbe_update_mac_addr_low_high_reg- Update L2 address in filter * register * * Algorithm: This routine update MAC address to register for filtering * based on dma_routing_enable, addr_mask and src_dest. Validation of * dma_chan as well as DCS bit enabled in RXQ to DMA mapping register * performed before updating DCS bits. * * @param[in] osi_core: OSI core private data structure. * @param[in] filter: OSI filter structure. * * @note 1) MAC should be initialized and stated. see osi_start_mac() * 2) osi_core->osd should be populated. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_update_mac_addr_low_high_reg( struct osi_core_priv_data *const osi_core, const struct osi_filter *filter) { nveu32_t idx = filter->index; nveu32_t dma_chan = filter->dma_chan; nveu32_t addr_mask = filter->addr_mask; nveu32_t src_dest = filter->src_dest; const nveu8_t *addr = filter->mac_address; nveu32_t dma_chansel = filter->dma_chansel; nveu32_t xdcs_check; nveu32_t value = 0x0U; nve32_t ret = 0; /* Validate filter values */ if (mgbe_filter_args_validate(osi_core, filter) < 0) { /* Filter argments validation got failed */ ret = -1; goto fail; } value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_ADDRH((idx))); /* read current value at index preserve XDCS current value */ ret = mgbe_mac_indir_addr_read(osi_core, MGBE_MAC_DCHSEL, idx, &xdcs_check); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "indirect register read failed\n", 0ULL); goto fail; } /* preserve last XDCS bits */ xdcs_check &= MGBE_MAC_XDCS_DMA_MAX; /* High address reset DCS and AE bits and XDCS in MAC_DChSel_IndReg */ if ((filter->oper_mode & OSI_OPER_ADDR_DEL) != OSI_NONE) { xdcs_check &= ~OSI_BIT(dma_chan); ret = mgbe_mac_indir_addr_write(osi_core, MGBE_MAC_DCHSEL, idx, xdcs_check); value &= ~(MGBE_MAC_ADDRH_DCS); /* XDCS values is always maintained */ if (xdcs_check == OSI_DISABLE) { value &= ~(MGBE_MAC_ADDRH_AE); } value |= OSI_MASK_16BITS; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_ADDRH((idx))); osi_writela(osi_core, OSI_MAX_32BITS, (nveu8_t *)osi_core->base + MGBE_MAC_ADDRL((idx))); } else { /* Add DMA channel to value in binary */ value = OSI_NONE; value |= ((dma_chan << MGBE_MAC_ADDRH_DCS_SHIFT) & MGBE_MAC_ADDRH_DCS); if (idx != 0U) { /* Add Address mask */ value |= ((addr_mask << MGBE_MAC_ADDRH_MBC_SHIFT) & MGBE_MAC_ADDRH_MBC); /* Setting Source/Destination Address match valid */ value |= ((src_dest << MGBE_MAC_ADDRH_SA_SHIFT) & MGBE_MAC_ADDRH_SA); } osi_writela(osi_core, ((nveu32_t)addr[4] | ((nveu32_t)addr[5] << 8) | MGBE_MAC_ADDRH_AE | value), (nveu8_t *)osi_core->base + MGBE_MAC_ADDRH((idx))); osi_writela(osi_core, ((nveu32_t)addr[0] | ((nveu32_t)addr[1] << 8) | ((nveu32_t)addr[2] << 16) | ((nveu32_t)addr[3] << 24)), (nveu8_t *)osi_core->base + MGBE_MAC_ADDRL((idx))); /* Write XDCS configuration into MAC_DChSel_IndReg(x) */ /* Append DCS DMA channel to XDCS hot bit selection */ xdcs_check |= (OSI_BIT(dma_chan) | dma_chansel); ret = mgbe_mac_indir_addr_write(osi_core, MGBE_MAC_DCHSEL, idx, xdcs_check); } fail: return ret; } /** * @brief mgbe_poll_for_l3l4crtl - Poll for L3_L4 filter register operations. * * Algorithm: Waits for waits for transfer busy bit to be cleared in * L3_L4 address control register to complete filter register operations. * * @param[in] osi_core: osi core priv data structure * * @note MAC needs to be out of reset and proper clock configured. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_poll_for_l3l4crtl(struct osi_core_priv_data *osi_core) { nveu32_t retry = 10; nveu32_t count; nveu32_t l3l4_addr_ctrl = 0; nve32_t cond = 1; nve32_t ret = 0; /* Poll Until L3_L4_Address_Control XB is clear */ count = 0; while (cond == 1) { if (count > retry) { /* Return error after max retries */ ret = -1; goto fail; } count++; l3l4_addr_ctrl = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_L3L4_ADDR_CTR); if ((l3l4_addr_ctrl & MGBE_MAC_L3L4_ADDR_CTR_XB) == OSI_NONE) { /* Set cond to 0 to exit loop */ cond = 0; } else { /* wait for 10 usec for XB clear */ osi_core->osd_ops.udelay(MGBE_MAC_XB_WAIT); } } fail: return ret; } /** * @brief mgbe_l3l4_filter_write - L3_L4 filter register write. * * Algorithm: writes L3_L4 filter register * * @param[in] osi_core: osi core priv data structure * @param[in] filter_no: MGBE L3_L4 filter number * @param[in] filter_type: MGBE L3_L4 filter register type. * @param[in] value: MGBE L3_L4 filter register value * * @note MAC needs to be out of reset and proper clock configured. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_l3l4_filter_write(struct osi_core_priv_data *osi_core, nveu32_t filter_no, nveu32_t filter_type, nveu32_t value) { void *base = osi_core->base; nveu32_t addr = 0; nve32_t ret = 0; /* Write MAC_L3_L4_Data register value */ osi_writela(osi_core, value, (nveu8_t *)base + MGBE_MAC_L3L4_DATA); /* Program MAC_L3_L4_Address_Control */ addr = osi_readla(osi_core, (nveu8_t *)base + MGBE_MAC_L3L4_ADDR_CTR); /* update filter number */ addr &= ~(MGBE_MAC_L3L4_ADDR_CTR_IDDR_FNUM); addr |= ((filter_no << MGBE_MAC_L3L4_ADDR_CTR_IDDR_FNUM_SHIFT) & MGBE_MAC_L3L4_ADDR_CTR_IDDR_FNUM); /* update filter type */ addr &= ~(MGBE_MAC_L3L4_ADDR_CTR_IDDR_FTYPE); addr |= ((filter_type << MGBE_MAC_L3L4_ADDR_CTR_IDDR_FTYPE_SHIFT) & MGBE_MAC_L3L4_ADDR_CTR_IDDR_FTYPE); /* Set TT filed 0 for write */ addr &= ~(MGBE_MAC_L3L4_ADDR_CTR_TT); /* Set XB bit to initiate write */ addr |= MGBE_MAC_L3L4_ADDR_CTR_XB; /* Write MGBE_MAC_L3L4_ADDR_CTR */ osi_writela(osi_core, addr, (nveu8_t *)base + MGBE_MAC_L3L4_ADDR_CTR); /* Wait untile XB bit reset */ if (mgbe_poll_for_l3l4crtl(osi_core) < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Fail to write L3_L4_Address_Control\n", filter_type); ret = -1; } return ret; } /** * @brief mgbe_config_l3l4_filters - Config L3L4 filters. * * @note * Algorithm: * - This sequence is used to configure L3L4 filters for SA and DA Port Number matching. * - Prepare register data using prepare_l3l4_registers(). * - Write l3l4 reigsters using mgbe_l3l4_filter_write(). * - Return 0 on success. * - Return -1 on any register failure. * * @param[in] osi_core: OSI core private data structure. * @param[in] filter_no_r: filter index * @param[in] l3_l4: Pointer to l3 l4 filter structure (#osi_l3_l4_filter) * * @note 1) MAC should be init and started. see osi_start_mac() * 2) osi_core->osd should be populated * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_l3l4_filters(struct osi_core_priv_data *const osi_core, nveu32_t filter_no_r, const struct osi_l3_l4_filter *const l3_l4) { #ifndef OSI_STRIPPED_LIB nveu32_t l3_addr0_reg = 0; nveu32_t l3_addr2_reg = 0; nveu32_t l3_addr3_reg = 0; nveu32_t l4_addr_reg = 0; #endif /* !OSI_STRIPPED_LIB */ nveu32_t l3_addr1_reg = 0; nveu32_t ctr_reg = 0; nveu32_t filter_no = filter_no_r & (OSI_MGBE_MAX_L3_L4_FILTER - 1U); nve32_t err; nve32_t ret = -1; prepare_l3l4_registers(osi_core, l3_l4, #ifndef OSI_STRIPPED_LIB &l3_addr0_reg, &l3_addr2_reg, &l3_addr3_reg, &l4_addr_reg, #endif /* !OSI_STRIPPED_LIB */ &l3_addr1_reg, &ctr_reg); #ifndef OSI_STRIPPED_LIB /* Update l3 ip addr MGBE_MAC_L3_AD0R register */ err = mgbe_l3l4_filter_write(osi_core, filter_no, MGBE_MAC_L3_AD0R, l3_addr0_reg); if (err < 0) { /* Write MGBE_MAC_L3_AD0R fail return error */ goto exit_func; } /* Update l3 ip addr MGBE_MAC_L3_AD2R register */ err = mgbe_l3l4_filter_write(osi_core, filter_no, MGBE_MAC_L3_AD2R, l3_addr2_reg); if (err < 0) { /* Write MGBE_MAC_L3_AD2R fail return error */ goto exit_func; } /* Update l3 ip addr MGBE_MAC_L3_AD3R register */ err = mgbe_l3l4_filter_write(osi_core, filter_no, MGBE_MAC_L3_AD3R, l3_addr3_reg); if (err < 0) { /* Write MGBE_MAC_L3_AD3R fail return error */ goto exit_func; } /* Update l4 port register MGBE_MAC_L4_ADDR register */ err = mgbe_l3l4_filter_write(osi_core, filter_no, MGBE_MAC_L4_ADDR, l4_addr_reg); if (err < 0) { /* Write MGBE_MAC_L4_ADDR fail return error */ goto exit_func; } #endif /* !OSI_STRIPPED_LIB */ /* Update l3 ip addr MGBE_MAC_L3_AD1R register */ err = mgbe_l3l4_filter_write(osi_core, filter_no, MGBE_MAC_L3_AD1R, l3_addr1_reg); if (err < 0) { /* Write MGBE_MAC_L3_AD1R fail return error */ goto exit_func; } /* Write CTR register */ err = mgbe_l3l4_filter_write(osi_core, filter_no, MGBE_MAC_L3L4_CTR, ctr_reg); if (err < 0) { /* Write MGBE_MAC_L3L4_CTR fail return error */ goto exit_func; } /* success */ ret = 0; exit_func: return ret; } #ifndef OSI_STRIPPED_LIB /** * @brief mgbe_config_vlan_filter_reg - config vlan filter register * * Algorithm: This sequence is used to enable/disable VLAN filtering and * also selects VLAN filtering mode- perfect/hash * * @param[in] osi_core: Base address from OSI core private data structure. * @param[in] filter_enb_dis: vlan filter enable/disable * @param[in] perfect_hash_filtering: perfect or hash filter * @param[in] perfect_inverse_match: normal or inverse filter * * @note 1) MAC should be init and started. see osi_start_mac() * 2) osi_core->osd should be populated * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_vlan_filtering(struct osi_core_priv_data *osi_core, const nveu32_t filter_enb_dis, const nveu32_t perfect_hash_filtering, const nveu32_t perfect_inverse_match) { nveu32_t value; nveu8_t *base = osi_core->base; /* validate perfect_inverse_match argument */ if (perfect_hash_filtering == OSI_HASH_FILTER_MODE) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_OPNOTSUPP, "VLAN hash filter is not supported, VTHM not updated\n", 0ULL); return -1; } if (perfect_hash_filtering != OSI_PERFECT_FILTER_MODE) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid perfect_hash_filtering value\n", perfect_hash_filtering); return -1; } /* validate filter_enb_dis argument */ if ((filter_enb_dis != OSI_ENABLE) && (filter_enb_dis != OSI_DISABLE)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid filter_enb_dis value\n", filter_enb_dis); return -1; } /* validate perfect_inverse_match argument */ if ((perfect_inverse_match != OSI_ENABLE) && (perfect_inverse_match != OSI_DISABLE)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid perfect_inverse_match value\n", perfect_inverse_match); return -1; } /* Read MAC PFR value set VTFE bit */ value = osi_readla(osi_core, base + MGBE_MAC_PFR); value &= ~(MGBE_MAC_PFR_VTFE); value |= ((filter_enb_dis << MGBE_MAC_PFR_VTFE_SHIFT) & MGBE_MAC_PFR_VTFE); osi_writela(osi_core, value, base + MGBE_MAC_PFR); /* Read MAC VLAN TR register value set VTIM bit */ value = osi_readla(osi_core, base + MGBE_MAC_VLAN_TR); value &= ~(MGBE_MAC_VLAN_TR_VTIM | MGBE_MAC_VLAN_TR_VTHM); value |= ((perfect_inverse_match << MGBE_MAC_VLAN_TR_VTIM_SHIFT) & MGBE_MAC_VLAN_TR_VTIM); osi_writela(osi_core, value, base + MGBE_MAC_VLAN_TR); return 0; } /** * @brief mgbe_config_ptp_rxq - Config PTP RX packets queue route * * Algorithm: This function is used to program the PTP RX packets queue. * * @param[in] osi_core: OSI core private data. * @param[in] rxq_idx: PTP RXQ index. * @param[in] enable: PTP RXQ route enable(1) or disable(0). * * @note 1) MAC should be init and started. see osi_start_mac() * 2) osi_core->osd should be populated * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_ptp_rxq(struct osi_core_priv_data *const osi_core, const nveu32_t rxq_idx, const nveu32_t enable) { nveu8_t *base = osi_core->base; nveu32_t value = 0U; nveu32_t i = 0U; /* Validate the RX queue index argument */ if (rxq_idx >= OSI_MGBE_MAX_NUM_QUEUES) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid PTP RX queue index\n", rxq_idx); return -1; } /* Validate enable argument */ if ((enable != OSI_ENABLE) && (enable != OSI_DISABLE)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid enable input\n", enable); return -1; } /* Validate PTP RX queue enable */ for (i = 0; i < osi_core->num_mtl_queues; i++) { if (osi_core->mtl_queues[i] == rxq_idx) { /* Given PTP RX queue is enabled */ break; } } if (i == osi_core->num_mtl_queues) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "PTP RX queue not enabled\n", rxq_idx); return -1; } /* Read MAC_RxQ_Ctrl1 */ value = osi_readla(osi_core, base + MGBE_MAC_RQC1R); /* Check for enable or disable */ if (enable == OSI_DISABLE) { /** Reset OMCBCQ bit to disable over-riding the MCBC Queue * priority for the PTP RX queue. **/ value &= ~MGBE_MAC_RQC1R_OMCBCQ; } else { /* Store PTP RX queue into OSI private data */ osi_core->ptp_config.ptp_rx_queue = rxq_idx; /* Program PTPQ with ptp_rxq */ value &= ~MGBE_MAC_RQC1R_PTPQ; value |= (rxq_idx << MGBE_MAC_RQC1R_PTPQ_SHIFT); /** Set TPQC to 0x1 for VLAN Tagged PTP over * ethernet packets are routed to Rx Queue specified * by PTPQ field **/ value |= MGBE_MAC_RQC1R_TPQC0; /** Set OMCBCQ bit to enable over-riding the MCBC Queue * priority for the PTP RX queue. **/ value |= MGBE_MAC_RQC1R_OMCBCQ; } /* Write MAC_RxQ_Ctrl1 */ osi_writela(osi_core, value, base + MGBE_MAC_RQC1R); return 0; } /** * @brief mgbe_config_mac_loopback - Configure MAC to support loopback * * @param[in] addr: Base address indicating the start of * memory mapped IO region of the MAC. * @param[in] lb_mode: Enable or Disable MAC loopback mode * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_mac_loopback(struct osi_core_priv_data *const osi_core, nveu32_t lb_mode) { nveu32_t value; void *addr = osi_core->base; /* don't allow only if loopback mode is other than 0 or 1 */ if ((lb_mode != OSI_ENABLE) && (lb_mode != OSI_DISABLE)) { return -1; } /* Read MAC Configuration Register */ value = osi_readla(osi_core, (nveu8_t *)addr + MGBE_MAC_RMCR); if (lb_mode == OSI_ENABLE) { /* Enable Loopback Mode */ value |= MGBE_MAC_RMCR_LM; } else { value &= ~MGBE_MAC_RMCR_LM; } osi_writela(osi_core, value, (nveu8_t *)addr + MGBE_MAC_RMCR); return 0; } /** * @brief mgbe_config_arp_offload - Enable/Disable ARP offload * * Algorithm: * 1) Read the MAC configuration register * 2) If ARP offload is to be enabled, program the IP address in * ARPPA register * 3) Enable/disable the ARPEN bit in MCR and write back to the MCR. * * @param[in] addr: MGBE virtual base address. * @param[in] enable: Flag variable to enable/disable ARP offload * @param[in] ip_addr: IP address of device to be programmed in HW. * HW will use this IP address to respond to ARP requests. * * @note 1) MAC should be init and started. see osi_start_mac() * 2) Valid 4 byte IP address as argument ip_addr * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_arp_offload(struct osi_core_priv_data *const osi_core, const nveu32_t enable, const nveu8_t *ip_addr) { nveu32_t mac_rmcr; nveu32_t val; void *addr = osi_core->base; if ((enable != OSI_ENABLE) && (enable != OSI_DISABLE)) { return -1; } mac_rmcr = osi_readla(osi_core, (nveu8_t *)addr + MGBE_MAC_RMCR); if (enable == OSI_ENABLE) { val = (((nveu32_t)ip_addr[0]) << 24) | (((nveu32_t)ip_addr[1]) << 16) | (((nveu32_t)ip_addr[2]) << 8) | (((nveu32_t)ip_addr[3])); osi_writela(osi_core, val, (nveu8_t *)addr + MGBE_MAC_ARPPA); mac_rmcr |= MGBE_MAC_RMCR_ARPEN; } else { mac_rmcr &= ~MGBE_MAC_RMCR_ARPEN; } osi_writela(osi_core, mac_rmcr, (nveu8_t *)addr + MGBE_MAC_RMCR); return 0; } #endif /* !OSI_STRIPPED_LIB */ /** * @brief mgbe_config_frp - Enable/Disale RX Flexible Receive Parser in HW * * Algorithm: * 1) Read the MTL OP Mode configuration register. * 2) Enable/Disable FRPE bit based on the input. * 3) Write the MTL OP Mode configuration register. * * @param[in] osi_core: OSI core private data structure. * @param[in] enabled: Flag to indicate feature is to be enabled/disabled. * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_frp(struct osi_core_priv_data *const osi_core, const nveu32_t enabled) { nveu8_t *base = osi_core->base; nveu32_t op_mode = 0U, val = 0U; nve32_t ret = 0; if ((enabled != OSI_ENABLE) && (enabled != OSI_DISABLE)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid enable input\n", enabled); ret = -1; goto done; } op_mode = osi_readla(osi_core, base + MGBE_MTL_OP_MODE); if (enabled == OSI_ENABLE) { /* Set FRPE bit of MTL_Operation_Mode register */ op_mode |= MGBE_MTL_OP_MODE_FRPE; osi_writela(osi_core, op_mode, base + MGBE_MTL_OP_MODE); /* Verify RXPI bit set in MTL_RXP_Control_Status */ ret = osi_readl_poll_timeout((base + MGBE_MTL_RXP_CS), (osi_core->osd_ops.udelay), (val), ((val & MGBE_MTL_RXP_CS_RXPI) == MGBE_MTL_RXP_CS_RXPI), (MGBE_MTL_FRP_READ_UDELAY), (MGBE_MTL_FRP_READ_RETRY)); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Fail to enable FRP\n", val); ret = -1; goto done; } /* Enable FRP Interrupts in MTL_RXP_Interrupt_Control_Status */ val = osi_readla(osi_core, base + MGBE_MTL_RXP_INTR_CS); val |= (MGBE_MTL_RXP_INTR_CS_NVEOVIE | MGBE_MTL_RXP_INTR_CS_NPEOVIE | MGBE_MTL_RXP_INTR_CS_FOOVIE | MGBE_MTL_RXP_INTR_CS_PDRFIE); osi_writela(osi_core, val, base + MGBE_MTL_RXP_INTR_CS); } else { /* Reset FRPE bit of MTL_Operation_Mode register */ op_mode &= ~MGBE_MTL_OP_MODE_FRPE; osi_writela(osi_core, op_mode, base + MGBE_MTL_OP_MODE); /* Verify RXPI bit reset in MTL_RXP_Control_Status */ ret = osi_readl_poll_timeout((base + MGBE_MTL_RXP_CS), (osi_core->osd_ops.udelay), (val), ((val & MGBE_MTL_RXP_CS_RXPI) == OSI_NONE), (MGBE_MTL_FRP_READ_UDELAY), (MGBE_MTL_FRP_READ_RETRY)); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Fail to disable FRP\n", val); ret = -1; goto done; } /* Disable FRP Interrupts in MTL_RXP_Interrupt_Control_Status */ val = osi_readla(osi_core, base + MGBE_MTL_RXP_INTR_CS); val &= ~(MGBE_MTL_RXP_INTR_CS_NVEOVIE | MGBE_MTL_RXP_INTR_CS_NPEOVIE | MGBE_MTL_RXP_INTR_CS_FOOVIE | MGBE_MTL_RXP_INTR_CS_PDRFIE); osi_writela(osi_core, val, base + MGBE_MTL_RXP_INTR_CS); } done: return ret; } /** * @brief mgbe_frp_write - Write FRP entry into HW * * Algorithm: This function will write FRP entry registers into HW. * * @param[in] osi_core: OSI core private data structure. * @param[in] acc_sel: FRP Indirect Access Selection. * 0x0 : Access FRP Instruction Table. * 0x1 : Access Indirect FRP Register block. * @param[in] addr: FRP register address. * @param[in] data: FRP register data. * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_frp_write(struct osi_core_priv_data *osi_core, nveu32_t acc_sel, nveu32_t addr, nveu32_t data) { nve32_t ret = 0; nveu8_t *base = osi_core->base; nveu32_t val = 0U; if ((acc_sel != OSI_ENABLE) && (acc_sel != OSI_DISABLE)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid acc_sel argment\n", acc_sel); ret = -1; goto done; } /* Wait for ready */ ret = osi_readl_poll_timeout((base + MGBE_MTL_RXP_IND_CS), (osi_core->osd_ops.udelay), (val), ((val & MGBE_MTL_RXP_IND_CS_BUSY) == OSI_NONE), (MGBE_MTL_FRP_READ_UDELAY), (MGBE_MTL_FRP_READ_RETRY)); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Fail to write\n", val); ret = -1; goto done; } /* Write data into MTL_RXP_Indirect_Acc_Data */ osi_writela(osi_core, data, base + MGBE_MTL_RXP_IND_DATA); /* Program MTL_RXP_Indirect_Acc_Control_Status */ val = osi_readla(osi_core, base + MGBE_MTL_RXP_IND_CS); /* Set/Reset ACCSEL for FRP Register block/Instruction Table */ if (acc_sel == OSI_ENABLE) { /* Set ACCSEL bit */ val |= MGBE_MTL_RXP_IND_CS_ACCSEL; } else { /* Reset ACCSEL bit */ val &= ~MGBE_MTL_RXP_IND_CS_ACCSEL; } /* Set WRRDN for write */ val |= MGBE_MTL_RXP_IND_CS_WRRDN; /* Clear and add ADDR */ val &= ~MGBE_MTL_RXP_IND_CS_ADDR; val |= (addr & MGBE_MTL_RXP_IND_CS_ADDR); /* Start write */ val |= MGBE_MTL_RXP_IND_CS_BUSY; osi_writela(osi_core, val, base + MGBE_MTL_RXP_IND_CS); /* Wait for complete */ ret = osi_readl_poll_timeout((base + MGBE_MTL_RXP_IND_CS), (osi_core->osd_ops.udelay), (val), ((val & MGBE_MTL_RXP_IND_CS_BUSY) == OSI_NONE), (MGBE_MTL_FRP_READ_UDELAY), (MGBE_MTL_FRP_READ_RETRY)); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Fail to write\n", val); ret = -1; } done: return ret; } /** * @brief mgbe_update_frp_entry - Update FRP Instruction Table entry in HW * * Algorithm: * * @param[in] osi_core: OSI core private data structure. * @param[in] pos: FRP Instruction Table entry location. * @param[in] data: FRP entry data structure. * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_update_frp_entry(struct osi_core_priv_data *const osi_core, const nveu32_t pos, struct osi_core_frp_data *const data) { nveu32_t val = 0U, tmp = 0U; nve32_t ret = -1; /* Validate pos value */ if (pos >= OSI_FRP_MAX_ENTRY) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid FRP table entry\n", pos); ret = -1; goto done; } /** Write Match Data into IE0 **/ val = data->match_data; ret = mgbe_frp_write(osi_core, OSI_DISABLE, MGBE_MTL_FRP_IE0(pos), val); if (ret < 0) { /* Match Data Write fail */ ret = -1; goto done; } /** Write Match Enable into IE1 **/ val = data->match_en; ret = mgbe_frp_write(osi_core, OSI_DISABLE, MGBE_MTL_FRP_IE1(pos), val); if (ret < 0) { /* Match Enable Write fail */ ret = -1; goto done; } /** Write AF, RF, IM, NIC, FO and OKI into IE2 **/ val = 0; if (data->accept_frame == OSI_ENABLE) { /* Set AF Bit */ val |= MGBE_MTL_FRP_IE2_AF; } if (data->reject_frame == OSI_ENABLE) { /* Set RF Bit */ val |= MGBE_MTL_FRP_IE2_RF; } if (data->inverse_match == OSI_ENABLE) { /* Set IM Bit */ val |= MGBE_MTL_FRP_IE2_IM; } if (data->next_ins_ctrl == OSI_ENABLE) { /* Set NIC Bit */ val |= MGBE_MTL_FRP_IE2_NC; } tmp = data->frame_offset; val |= ((tmp << MGBE_MTL_FRP_IE2_FO_SHIFT) & MGBE_MTL_FRP_IE2_FO); tmp = data->ok_index; val |= ((tmp << MGBE_MTL_FRP_IE2_OKI_SHIFT) & MGBE_MTL_FRP_IE2_OKI); tmp = data->dma_chsel; val |= ((tmp << MGBE_MTL_FRP_IE2_DCH_SHIFT) & MGBE_MTL_FRP_IE2_DCH); ret = mgbe_frp_write(osi_core, OSI_DISABLE, MGBE_MTL_FRP_IE2(pos), val); if (ret < 0) { /* FRP IE2 Write fail */ ret = -1; goto done; } /** Write DCH into IE3 **/ val = (data->dma_chsel & MGBE_MTL_FRP_IE3_DCH_MASK); ret = mgbe_frp_write(osi_core, OSI_DISABLE, MGBE_MTL_FRP_IE3(pos), val); if (ret < 0) { /* DCH Write fail */ ret = -1; } done: return ret; } /** * @brief mgbe_update_frp_nve - Update FRP NVE into HW * * Algorithm: * * @param[in] osi_core: osi core priv data structure * @param[in] nve: Number of Valid Entries. * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_update_frp_nve(struct osi_core_priv_data *const osi_core, const nveu32_t nve) { nveu32_t val; nveu8_t *base = osi_core->base; nve32_t ret; /* Validate the NVE value */ if (nve >= OSI_FRP_MAX_ENTRY) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid NVE value\n", nve); ret = -1; goto done; } /* Update NVE and NPE in MTL_RXP_Control_Status register */ val = osi_readla(osi_core, base + MGBE_MTL_RXP_CS); /* Clear old NVE and NPE */ val &= ~(MGBE_MTL_RXP_CS_NVE | MGBE_MTL_RXP_CS_NPE); /* Add new NVE and NPE */ val |= (nve & MGBE_MTL_RXP_CS_NVE); val |= ((nve << MGBE_MTL_RXP_CS_NPE_SHIFT) & MGBE_MTL_RXP_CS_NPE); osi_writela(osi_core, val, base + MGBE_MTL_RXP_CS); ret = 0; done: return ret; } /** * @brief mgbe_configure_mtl_queue - Configure MTL Queue * * Algorithm: This takes care of configuring the below * parameters for the MTL Queue * 1) Mapping MTL Rx queue and DMA Rx channel * 2) Flush TxQ * 3) Enable Store and Forward mode for Tx, Rx * 4) Configure Tx and Rx MTL Queue sizes * 5) Configure TxQ weight * 6) Enable Rx Queues * 7) Enable TX Underflow Interrupt for MTL Q * * @param[in] osi_core: OSI core private data structure. * @param[in] hw_qinx: Queue number that need to be configured. * * @note MAC has to be out of reset. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_configure_mtl_queue(struct osi_core_priv_data *osi_core, nveu32_t hw_qinx) { nveu32_t qinx = hw_qinx & 0xFU; /* * Total available Rx queue size is 192KB. * Below is the destribution among the Rx queueu - * Q0 - 160KB * Q1 to Q8 - 2KB each = 8 * 2KB = 16KB * Q9 - 16KB (MVBCQ) * * Formula to calculate the value to be programmed in HW * * vale= (size in KB / 256) - 1U */ const nveu32_t rx_fifo_sz[OSI_MGBE_MAX_NUM_QUEUES] = { FIFO_SZ(160U), FIFO_SZ(2U), FIFO_SZ(2U), FIFO_SZ(2U), FIFO_SZ(2U), FIFO_SZ(2U), FIFO_SZ(2U), FIFO_SZ(2U), FIFO_SZ(2U), FIFO_SZ(16U), }; const nveu32_t tx_fifo_sz[OSI_MGBE_MAX_NUM_QUEUES] = { TX_FIFO_SZ, TX_FIFO_SZ, TX_FIFO_SZ, TX_FIFO_SZ, TX_FIFO_SZ, TX_FIFO_SZ, TX_FIFO_SZ, TX_FIFO_SZ, TX_FIFO_SZ, TX_FIFO_SZ, }; const nveu32_t rfd_rfa[OSI_MGBE_MAX_NUM_QUEUES] = { FULL_MINUS_32_K, FULL_MINUS_1_5K, FULL_MINUS_1_5K, FULL_MINUS_1_5K, FULL_MINUS_1_5K, FULL_MINUS_1_5K, FULL_MINUS_1_5K, FULL_MINUS_1_5K, FULL_MINUS_1_5K, FULL_MINUS_1_5K, }; nveu32_t value = 0; nve32_t ret = 0; /* Program ETSALG (802.1Qaz) and RAA in MTL_Operation_Mode * register to initialize the MTL operation in case * of multiple Tx and Rx queues default : ETSALG WRR RAA SP */ /* Program the priorities mapped to the Selected Traffic * Classes in MTL_TC_Prty_Map0-3 registers. This register is * to tell traffic class x should be blocked from transmitting * for the specified pause time when a PFC packet is received * with priorities matching the priorities programmed in this field * default: 0x0 */ /* Program the Transmit Selection Algorithm (TSA) in * MTL_TC[n]_ETS_Control register for all the Selected * Traffic Classes (n=0, 1, …, Max selected number of TCs - 1) * Setting related to CBS will come here for TC. * default: 0x0 SP */ ret = hw_flush_mtl_tx_queue(osi_core, qinx); if (ret < 0) { goto fail; } if (osi_unlikely((qinx >= OSI_MGBE_MAX_NUM_QUEUES) || (osi_core->tc[qinx] >= OSI_MAX_TC_NUM))) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Incorrect queues/TC number\n", 0ULL); ret = -1; goto fail; } value = (tx_fifo_sz[qinx] << MGBE_MTL_TXQ_SIZE_SHIFT); /* Enable Store and Forward mode */ value |= MGBE_MTL_TSF; /*TTC not applicable for TX*/ /* Enable TxQ */ value |= MGBE_MTL_TXQEN; value |= (osi_core->tc[qinx] << MGBE_MTL_CHX_TX_OP_MODE_Q2TC_SH); osi_writela(osi_core, value, (nveu8_t *) osi_core->base + MGBE_MTL_CHX_TX_OP_MODE(qinx)); /* read RX Q0 Operating Mode Register */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_CHX_RX_OP_MODE(qinx)); value |= (rx_fifo_sz[qinx] << MGBE_MTL_RXQ_SIZE_SHIFT); /* Enable Store and Forward mode */ value |= MGBE_MTL_RSF; /* Enable HW flow control */ value |= MGBE_MTL_RXQ_OP_MODE_EHFC; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_CHX_RX_OP_MODE(qinx)); /* Update RFA and RFD * RFA: Threshold for Activating Flow Control * RFD: Threshold for Deactivating Flow Control */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_RXQ_FLOW_CTRL(qinx)); value &= ~MGBE_MTL_RXQ_OP_MODE_RFD_MASK; value &= ~MGBE_MTL_RXQ_OP_MODE_RFA_MASK; value |= (rfd_rfa[qinx] << MGBE_MTL_RXQ_OP_MODE_RFD_SHIFT) & MGBE_MTL_RXQ_OP_MODE_RFD_MASK; value |= (rfd_rfa[qinx] << MGBE_MTL_RXQ_OP_MODE_RFA_SHIFT) & MGBE_MTL_RXQ_OP_MODE_RFA_MASK; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_RXQ_FLOW_CTRL(qinx)); /* Transmit Queue weight, all TX weights are equal */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_QW(qinx)); value |= MGBE_MTL_TCQ_QW_ISCQW; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_QW(qinx)); /* Default ETS tx selection algo */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_CR(osi_core->tc[qinx])); value &= ~MGBE_MTL_TCQ_ETS_CR_AVALG; value |= OSI_MGBE_TXQ_AVALG_ETS; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_CR(osi_core->tc[qinx])); /* Enable Rx Queue Control */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_RQC0R); value |= ((osi_core->rxq_ctrl[qinx] & MGBE_MAC_RXQC0_RXQEN_MASK) << (MGBE_MAC_RXQC0_RXQEN_SHIFT(qinx))); osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_RQC0R); fail: return ret; } #ifndef OSI_STRIPPED_LIB /** * @brief mgbe_rss_write_reg - Write into RSS registers * * Algorithm: Programes RSS hash table or RSS hash key. * * @param[in] addr: MAC base address * @param[in] idx: Hash table or key index * @param[in] value: Value to be programmed in RSS data register. * @param[in] is_key: To represent passed value key or table data. * * @note MAC has to be out of reset. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_rss_write_reg(struct osi_core_priv_data *osi_core, nveu32_t idx, nveu32_t value, nveu32_t is_key) { nveu8_t *addr = (nveu8_t *)osi_core->base; nveu32_t retry = 100; nveu32_t ctrl = 0; nveu32_t count = 0; nve32_t cond = 1; /* data into RSS Lookup Table or RSS Hash Key */ osi_writela(osi_core, value, addr + MGBE_MAC_RSS_DATA); if (is_key == OSI_ENABLE) { ctrl |= MGBE_MAC_RSS_ADDR_ADDRT; } ctrl |= idx << MGBE_MAC_RSS_ADDR_RSSIA_SHIFT; ctrl |= MGBE_MAC_RSS_ADDR_OB; ctrl &= ~MGBE_MAC_RSS_ADDR_CT; osi_writela(osi_core, ctrl, addr + MGBE_MAC_RSS_ADDR); /* poll for write operation to complete */ while (cond == 1) { if (count > retry) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Failed to update RSS Hash key or table\n", 0ULL); return -1; } count++; value = osi_readla(osi_core, addr + MGBE_MAC_RSS_ADDR); if ((value & MGBE_MAC_RSS_ADDR_OB) == OSI_NONE) { cond = 0; } else { osi_core->osd_ops.udelay(100); } } return 0; } /** * @brief mgbe_config_rss - Configure RSS * * Algorithm: Programes RSS hash table or RSS hash key. * * @param[in] osi_core: OSI core private data. * * @note MAC has to be out of reset. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_rss(struct osi_core_priv_data *osi_core) { nveu8_t *addr = (nveu8_t *)osi_core->base; nveu32_t value = 0; nveu32_t i = 0, j = 0; nve32_t ret = 0; if (osi_core->rss.enable == OSI_DISABLE) { /* RSS not supported */ return 0; } /* No need to enable RSS for single Queue */ if (osi_core->num_mtl_queues == 1U) { return 0; } /* Program the hash key */ for (i = 0; i < OSI_RSS_HASH_KEY_SIZE; i += 4U) { value = ((nveu32_t)osi_core->rss.key[i] | ((nveu32_t)osi_core->rss.key[i + 1U] << 8U) | ((nveu32_t)osi_core->rss.key[i + 2U] << 16U) | ((nveu32_t)osi_core->rss.key[i + 3U] << 24U)); ret = mgbe_rss_write_reg(osi_core, j, value, OSI_ENABLE); if (ret < 0) { return ret; } j++; } /* Program Hash table */ for (i = 0; i < OSI_RSS_MAX_TABLE_SIZE; i++) { ret = mgbe_rss_write_reg(osi_core, i, osi_core->rss.table[i], OSI_NONE); if (ret < 0) { return ret; } } /* Enable RSS */ value = osi_readla(osi_core, addr + MGBE_MAC_RSS_CTRL); value |= MGBE_MAC_RSS_CTRL_UDP4TE | MGBE_MAC_RSS_CTRL_TCP4TE | MGBE_MAC_RSS_CTRL_IP2TE | MGBE_MAC_RSS_CTRL_RSSE; osi_writela(osi_core, value, addr + MGBE_MAC_RSS_CTRL); return 0; } /** * @brief mgbe_config_flow_control - Configure MAC flow control settings * * @param[in] osi_core: OSI core private data structure. * @param[in] flw_ctrl: flw_ctrl settings * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_flow_control(struct osi_core_priv_data *const osi_core, const nveu32_t flw_ctrl) { nveu32_t val; void *addr = osi_core->base; /* return on invalid argument */ if (flw_ctrl > (OSI_FLOW_CTRL_RX | OSI_FLOW_CTRL_TX)) { return -1; } /* Configure MAC Tx Flow control */ /* Read MAC Tx Flow control Register of Q0 */ val = osi_readla(osi_core, (nveu8_t *)addr + MGBE_MAC_QX_TX_FLW_CTRL(0U)); /* flw_ctrl BIT0: 1 is for tx flow ctrl enable * flw_ctrl BIT0: 0 is for tx flow ctrl disable */ if ((flw_ctrl & OSI_FLOW_CTRL_TX) == OSI_FLOW_CTRL_TX) { /* Enable Tx Flow Control */ val |= MGBE_MAC_QX_TX_FLW_CTRL_TFE; /* Mask and set Pause Time */ val &= ~MGBE_MAC_PAUSE_TIME_MASK; val |= MGBE_MAC_PAUSE_TIME & MGBE_MAC_PAUSE_TIME_MASK; } else { /* Disable Tx Flow Control */ val &= ~MGBE_MAC_QX_TX_FLW_CTRL_TFE; } /* Write to MAC Tx Flow control Register of Q0 */ osi_writela(osi_core, val, (nveu8_t *)addr + MGBE_MAC_QX_TX_FLW_CTRL(0U)); /* Configure MAC Rx Flow control*/ /* Read MAC Rx Flow control Register */ val = osi_readla(osi_core, (nveu8_t *)addr + MGBE_MAC_RX_FLW_CTRL); /* flw_ctrl BIT1: 1 is for rx flow ctrl enable * flw_ctrl BIT1: 0 is for rx flow ctrl disable */ if ((flw_ctrl & OSI_FLOW_CTRL_RX) == OSI_FLOW_CTRL_RX) { /* Enable Rx Flow Control */ val |= MGBE_MAC_RX_FLW_CTRL_RFE; } else { /* Disable Rx Flow Control */ val &= ~MGBE_MAC_RX_FLW_CTRL_RFE; } /* Write to MAC Rx Flow control Register */ osi_writela(osi_core, val, (nveu8_t *)addr + MGBE_MAC_RX_FLW_CTRL); return 0; } #endif /* !OSI_STRIPPED_LIB */ #ifdef HSI_SUPPORT /** * @brief mgbe_hsi_configure - Configure HSI * * Algorithm: enable LIC interrupt and HSI features * * @param[in, out] osi_core: OSI core private data structure. * @param[in] enable: OSI_ENABLE for Enabling HSI feature, else disable * * @retval 0 on success * @retval -1 on failure */ static nve32_t mgbe_hsi_configure(struct osi_core_priv_data *const osi_core, const nveu32_t enable) { nveu32_t value = 0U; nve32_t ret = 0; const nveu16_t osi_hsi_reporter_id[] = { OSI_HSI_MGBE0_REPORTER_ID, OSI_HSI_MGBE1_REPORTER_ID, OSI_HSI_MGBE2_REPORTER_ID, OSI_HSI_MGBE3_REPORTER_ID, }; if (enable == OSI_ENABLE) { osi_core->hsi.enabled = OSI_ENABLE; osi_core->hsi.reporter_id = osi_hsi_reporter_id[osi_core->instance_id]; /* T23X-MGBE_HSIv2-12:Initialization of Transaction Timeout in PCS */ /* T23X-MGBE_HSIv2-11:Initialization of Watchdog Timer */ value = (0xCCU << XPCS_SFTY_1US_MULT_SHIFT) & XPCS_SFTY_1US_MULT_MASK; ret = xpcs_write_safety(osi_core, XPCS_VR_XS_PCS_SFTY_TMR_CTRL, value); if (ret != 0) { goto fail; } /* T23X-MGBE_HSIv2-1 Configure ECC */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_CONTROL); value &= ~MGBE_MTL_ECC_MTXED; value &= ~MGBE_MTL_ECC_MRXED; value &= ~MGBE_MTL_ECC_MGCLED; value &= ~MGBE_MTL_ECC_MRXPED; value &= ~MGBE_MTL_ECC_TSOED; value &= ~MGBE_MTL_ECC_DESCED; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_CONTROL); /* T23X-MGBE_HSIv2-5: Enabling and Initialization of Transaction Timeout */ value = (0x198U << MGBE_TMR_SHIFT) & MGBE_TMR_MASK; value |= ((nveu32_t)0x0U << MGBE_CTMR_SHIFT) & MGBE_CTMR_MASK; value |= ((nveu32_t)0x2U << MGBE_LTMRMD_SHIFT) & MGBE_LTMRMD_MASK; value |= ((nveu32_t)0x2U << MGBE_NTMRMD_SHIFT) & MGBE_NTMRMD_MASK; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_DWCXG_CORE_MAC_FSM_ACT_TIMER); /* T23X-MGBE_HSIv2-3: Enabling and Initialization of Watchdog Timer */ /* T23X-MGBE_HSIv2-4: Enabling of Consistency Monitor for XGMAC FSM State */ /* TODO enable MGBE_TMOUTEN. Bug 3584387 */ value = MGBE_PRTYEN; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_FSM_CONTROL); /* T23X-MGBE_HSIv2-2: Enabling of Bus Parity */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_DPP_CONTROL); value &= ~MGBE_DDPP; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_DPP_CONTROL); /* T23X-MGBE_HSIv2-38: Initialization of Register Parity for control registers */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_SCSR_CONTROL); value |= MGBE_CPEN; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_SCSR_CONTROL); /* Enable Interrupt */ /* T23X-MGBE_HSIv2-1: Enabling of Memory ECC */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_INTERRUPT_ENABLE); value |= MGBE_MTL_TXCEIE; value |= MGBE_MTL_RXCEIE; value |= MGBE_MTL_GCEIE; value |= MGBE_MTL_RPCEIE; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_INTERRUPT_ENABLE); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_DMA_ECC_INTERRUPT_ENABLE); value |= MGBE_DMA_TCEIE; value |= MGBE_DMA_DCEIE; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_DMA_ECC_INTERRUPT_ENABLE); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); value |= MGBE_REGISTER_PARITY_ERR; value |= MGBE_CORE_CORRECTABLE_ERR; value |= MGBE_CORE_UNCORRECTABLE_ERR; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); value = osi_readla(osi_core, (nveu8_t *)osi_core->xpcs_base + XPCS_WRAP_INTERRUPT_CONTROL); value |= XPCS_CORE_CORRECTABLE_ERR; value |= XPCS_CORE_UNCORRECTABLE_ERR; value |= XPCS_REGISTER_PARITY_ERR; osi_writela(osi_core, value, (nveu8_t *)osi_core->xpcs_base + XPCS_WRAP_INTERRUPT_CONTROL); } else { osi_core->hsi.enabled = OSI_DISABLE; /* T23X-MGBE_HSIv2-11:Deinitialization of Watchdog Timer */ ret = xpcs_write_safety(osi_core, XPCS_VR_XS_PCS_SFTY_TMR_CTRL, 0); if (ret != 0) { goto fail; } /* T23X-MGBE_HSIv2-1 Disable ECC */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_CONTROL); value |= MGBE_MTL_ECC_MTXED; value |= MGBE_MTL_ECC_MRXED; value |= MGBE_MTL_ECC_MGCLED; value |= MGBE_MTL_ECC_MRXPED; value |= MGBE_MTL_ECC_TSOED; value |= MGBE_MTL_ECC_DESCED; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_CONTROL); /* T23X-MGBE_HSIv2-5: Enabling and Initialization of Transaction Timeout */ osi_writela(osi_core, 0, (nveu8_t *)osi_core->base + MGBE_DWCXG_CORE_MAC_FSM_ACT_TIMER); /* T23X-MGBE_HSIv2-4: Enabling of Consistency Monitor for XGMAC FSM State */ osi_writela(osi_core, 0, (nveu8_t *)osi_core->base + MGBE_MAC_FSM_CONTROL); /* T23X-MGBE_HSIv2-2: Disable of Bus Parity */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_DPP_CONTROL); value |= MGBE_DDPP; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_DPP_CONTROL); /* T23X-MGBE_HSIv2-38: Disable Register Parity for control registers */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_SCSR_CONTROL); value &= ~MGBE_CPEN; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_SCSR_CONTROL); /* Disable Interrupts */ osi_writela(osi_core, 0, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_INTERRUPT_ENABLE); osi_writela(osi_core, 0, (nveu8_t *)osi_core->base + MGBE_DMA_ECC_INTERRUPT_ENABLE); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); value &= ~MGBE_REGISTER_PARITY_ERR; value &= ~MGBE_CORE_CORRECTABLE_ERR; value &= ~MGBE_CORE_UNCORRECTABLE_ERR; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); value = osi_readla(osi_core, (nveu8_t *)osi_core->xpcs_base + XPCS_WRAP_INTERRUPT_CONTROL); value &= ~XPCS_CORE_CORRECTABLE_ERR; value &= ~XPCS_CORE_UNCORRECTABLE_ERR; value &= ~XPCS_REGISTER_PARITY_ERR; osi_writela(osi_core, value, (nveu8_t *)osi_core->xpcs_base + XPCS_WRAP_INTERRUPT_CONTROL); } fail: return ret; } /** * @brief mgbe_hsi_inject_err - Inject error * * Algorithm: Use error injection method to induce error * * @param[in] osi_core: OSI core private data structure. * @param[in] error_code: HSI Error code * * @retval 0 on success * @retval -1 on failure */ static nve32_t mgbe_hsi_inject_err(struct osi_core_priv_data *const osi_core, const nveu32_t error_code) { const nveu32_t val_ce = (MGBE_MTL_DEBUG_CONTROL_FDBGEN | MGBE_MTL_DEBUG_CONTROL_DBGMOD | MGBE_MTL_DEBUG_CONTROL_FIFORDEN | MGBE_MTL_DEBUG_CONTROL_EIEE | MGBE_MTL_DEBUG_CONTROL_EIEC); const nveu32_t val_ue = (MGBE_MTL_DEBUG_CONTROL_FDBGEN | MGBE_MTL_DEBUG_CONTROL_DBGMOD | MGBE_MTL_DEBUG_CONTROL_FIFORDEN | MGBE_MTL_DEBUG_CONTROL_EIEE); nve32_t ret = 0; switch (error_code) { case OSI_HSI_MGBE0_CE_CODE: case OSI_HSI_MGBE1_CE_CODE: case OSI_HSI_MGBE2_CE_CODE: case OSI_HSI_MGBE3_CE_CODE: osi_writela(osi_core, val_ce, (nveu8_t *)osi_core->base + MGBE_MTL_DEBUG_CONTROL); break; case OSI_HSI_MGBE0_UE_CODE: case OSI_HSI_MGBE1_UE_CODE: case OSI_HSI_MGBE2_UE_CODE: case OSI_HSI_MGBE3_UE_CODE: osi_writela(osi_core, val_ue, (nveu8_t *)osi_core->base + MGBE_MTL_DEBUG_CONTROL); break; default: ret = hsi_common_error_inject(osi_core, error_code); break; } return ret; } #endif /** * @brief mgbe_configure_mac - Configure MAC * * Algorithm: This takes care of configuring the below * parameters for the MAC * 1) Programming the MAC address * 2) Enable required MAC control fields in MCR * 3) Enable Multicast and Broadcast Queue * 4) Disable MMC nve32_terrupts and Configure the MMC counters * 5) Enable required MAC nve32_terrupts * * @param[in] osi_core: OSI core private data structure. * * @note MAC has to be out of reset. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_configure_mac(struct osi_core_priv_data *osi_core) { nveu32_t value = 0U, max_queue = 0U, i = 0U; /* TODO: Need to check if we need to enable anything in Tx configuration * value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_TMCR); */ /* Read MAC Rx Configuration Register */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_RMCR); /* Enable Automatic Pad or CRC Stripping */ /* Enable CRC stripping for Type packets */ /* Enable Rx checksum offload engine by default */ value |= MGBE_MAC_RMCR_ACS | MGBE_MAC_RMCR_CST | MGBE_MAC_RMCR_IPC; /* Jumbo Packet Enable */ if ((osi_core->mtu > OSI_DFLT_MTU_SIZE) && (osi_core->mtu <= OSI_MTU_SIZE_9000)) { value |= MGBE_MAC_RMCR_JE; } else if (osi_core->mtu > OSI_MTU_SIZE_9000){ /* if MTU greater 9K use GPSLCE */ value |= MGBE_MAC_RMCR_GPSLCE | MGBE_MAC_RMCR_WD; value &= ~MGBE_MAC_RMCR_GPSL_MSK; value |= ((((nveu32_t)OSI_MAX_MTU_SIZE) << 16U) & MGBE_MAC_RMCR_GPSL_MSK); } else { value &= ~MGBE_MAC_RMCR_JE; value &= ~MGBE_MAC_RMCR_GPSLCE; value &= ~MGBE_MAC_RMCR_WD; } osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_RMCR); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_TMCR); /* DDIC bit set is needed to improve MACSEC Tput */ value |= MGBE_MAC_TMCR_DDIC; /* Jabber Disable */ if (osi_core->mtu > OSI_DFLT_MTU_SIZE) { value |= MGBE_MAC_TMCR_JD; } osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_TMCR); /* Enable Multicast and Broadcast Queue */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_RQC1R); value |= MGBE_MAC_RQC1R_MCBCQEN; /* Set MCBCQ to highest enabled RX queue index */ for (i = 0; i < osi_core->num_mtl_queues; i++) { if ((max_queue < osi_core->mtl_queues[i]) && (osi_core->mtl_queues[i] < OSI_MGBE_MAX_NUM_QUEUES)) { /* Update max queue number */ max_queue = osi_core->mtl_queues[i]; } } value &= ~(MGBE_MAC_RQC1R_MCBCQ); value |= (max_queue << MGBE_MAC_RQC1R_MCBCQ_SHIFT); osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_RQC1R); /* Disable all MMC nve32_terrupts */ /* Disable all MMC Tx nve32_terrupts */ osi_writela(osi_core, OSI_NONE, (nveu8_t *)osi_core->base + MGBE_MMC_TX_INTR_EN); /* Disable all MMC RX nve32_terrupts */ osi_writela(osi_core, OSI_NONE, (nveu8_t *)osi_core->base + MGBE_MMC_RX_INTR_EN); /* Configure MMC counters */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MMC_CNTRL); value |= MGBE_MMC_CNTRL_CNTRST | MGBE_MMC_CNTRL_RSTONRD | MGBE_MMC_CNTRL_CNTMCT | MGBE_MMC_CNTRL_CNTPRST; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MMC_CNTRL); /* Enable MAC nve32_terrupts */ /* Read MAC IMR Register */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_IER); /* RGSMIIIM - RGMII/SMII interrupt and TSIE Enable */ /* TXESIE - Transmit Error Status Interrupt Enable */ /* TODO: LPI need to be enabled during EEE implementation */ #ifndef OSI_STRIPPED_LIB value |= (MGBE_IMR_TXESIE); #endif value |= (MGBE_IMR_RGSMIIIE | MGBE_IMR_TSIE); osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_IER); /* Enable common interrupt at wrapper level */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); value |= MGBE_MAC_SBD_INTR; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); /* Enable VLAN configuration */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_VLAN_TR); /* Enable VLAN Tag in RX Status * Disable double VLAN Tag processing on TX and RX */ if (osi_core->strip_vlan_tag == OSI_ENABLE) { /* Enable VLAN Tag stripping always */ value |= MGBE_MAC_VLANTR_EVLS_ALWAYS_STRIP; } value |= MGBE_MAC_VLANTR_EVLRXS | MGBE_MAC_VLANTR_DOVLTC; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_VLAN_TR); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_VLANTIR); /* Enable VLAN tagging through context descriptor */ value |= MGBE_MAC_VLANTIR_VLTI; /* insert/replace C_VLAN in 13th & 14th bytes of transmitted frames */ value &= ~MGBE_MAC_VLANTIRR_CSVL; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_VLANTIR); #ifndef OSI_STRIPPED_LIB /* Configure default flow control settings */ if (osi_core->pause_frames == OSI_PAUSE_FRAMES_ENABLE) { osi_core->flow_ctrl = (OSI_FLOW_CTRL_TX | OSI_FLOW_CTRL_RX); if (mgbe_config_flow_control(osi_core, osi_core->flow_ctrl) != 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Failed to set flow control configuration\n", 0ULL); } } /* TODO: USP (user Priority) to RxQ Mapping */ /* RSS cofiguration */ mgbe_config_rss(osi_core); #endif /* !OSI_STRIPPED_LIB */ return 0; } /** * @brief mgbe_configure_dma - Configure DMA * * Algorithm: This takes care of configuring the below * parameters for the DMA * 1) Programming different burst length for the DMA * 2) Enable enhanced Address mode * 3) Programming max read outstanding request limit * * @param[in] osi_core: OSI core private data structure. * * @note MAC has to be out of reset. */ static void mgbe_configure_dma(struct osi_core_priv_data *osi_core) { nveu32_t value = 0; /* Set AXI Undefined Burst Length */ value |= MGBE_DMA_SBUS_UNDEF; /* AXI Burst Length 256*/ value |= MGBE_DMA_SBUS_BLEN256; /* Enhanced Address Mode Enable */ value |= MGBE_DMA_SBUS_EAME; /* AXI Maximum Read Outstanding Request Limit = 63 */ value |= MGBE_DMA_SBUS_RD_OSR_LMT; /* AXI Maximum Write Outstanding Request Limit = 63 */ value |= MGBE_DMA_SBUS_WR_OSR_LMT; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_DMA_SBUS); /* Configure TDPS to 5 */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_DMA_TX_EDMA_CTRL); value |= MGBE_DMA_TX_EDMA_CTRL_TDPS; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_DMA_TX_EDMA_CTRL); /* Configure RDPS to 5 */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_DMA_RX_EDMA_CTRL); value |= MGBE_DMA_RX_EDMA_CTRL_RDPS; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_DMA_RX_EDMA_CTRL); } /** * @brief Map DMA channels to a specific VM IRQ. * * @param[in] osi_core: OSI core private data structure. * * @note OSD layer needs to update number of VM channels and * DMA channel list in osi_vm_irq_data. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_dma_chan_to_vmirq_map(struct osi_core_priv_data *osi_core) { #ifndef OSI_STRIPPED_LIB nveu32_t sid[4] = { MGBE0_SID, MGBE1_SID, MGBE2_SID, MGBE3_SID }; #endif struct osi_vm_irq_data *irq_data; nveu32_t i, j; nveu32_t chan; for (i = 0; i < osi_core->num_vm_irqs; i++) { irq_data = &osi_core->irq_data[i]; for (j = 0; j < irq_data->num_vm_chans; j++) { chan = irq_data->vm_chans[j]; if (chan >= OSI_MGBE_MAX_NUM_CHANS) { continue; } osi_writel(OSI_BIT(irq_data->vm_num), (nveu8_t *)osi_core->base + MGBE_VIRT_INTR_APB_CHX_CNTRL(chan)); } osi_writel(OSI_BIT(irq_data->vm_num), (nveu8_t *)osi_core->base + MGBE_VIRTUAL_APB_ERR_CTRL); } #ifndef OSI_STRIPPED_LIB if ((osi_core->use_virtualization == OSI_DISABLE) && (osi_core->hv_base != OSI_NULL)) { if (osi_core->instance_id > 3U) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Wrong MAC instance-ID\n", osi_core->instance_id); return -1; } osi_writela(osi_core, MGBE_SID_VAL1(sid[osi_core->instance_id]), (nveu8_t *)osi_core->hv_base + MGBE_WRAP_AXI_ASID0_CTRL); osi_writela(osi_core, MGBE_SID_VAL1(sid[osi_core->instance_id]), (nveu8_t *)osi_core->hv_base + MGBE_WRAP_AXI_ASID1_CTRL); osi_writela(osi_core, MGBE_SID_VAL2(sid[osi_core->instance_id]), (nveu8_t *)osi_core->hv_base + MGBE_WRAP_AXI_ASID2_CTRL); } #endif return 0; } /** * @brief mgbe_core_init - MGBE MAC, MTL and common DMA Initialization * * Algorithm: This function will take care of initializing MAC, MTL and * common DMA registers. * * @param[in] osi_core: OSI core private data structure. * * @note 1) MAC should be out of reset. See osi_poll_for_swr() for details. * 2) osi_core->base needs to be filled based on ioremap. * 3) osi_core->num_mtl_queues needs to be filled. * 4) osi_core->mtl_queues[qinx] need to be filled. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_core_init(struct osi_core_priv_data *const osi_core) { nve32_t ret = 0; nveu32_t qinx = 0; nveu32_t value = 0; /* reset mmc counters */ osi_writela(osi_core, MGBE_MMC_CNTRL_CNTRST, (nveu8_t *)osi_core->base + MGBE_MMC_CNTRL); /* Mapping MTL Rx queue and DMA Rx channel */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_RXQ_DMA_MAP0); value |= MGBE_RXQ_TO_DMA_CHAN_MAP0; value |= MGBE_RXQ_TO_DMA_MAP_DDMACH; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_RXQ_DMA_MAP0); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_RXQ_DMA_MAP1); value |= MGBE_RXQ_TO_DMA_CHAN_MAP1; value |= MGBE_RXQ_TO_DMA_MAP_DDMACH; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_RXQ_DMA_MAP1); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_RXQ_DMA_MAP2); value |= MGBE_RXQ_TO_DMA_CHAN_MAP2; value |= MGBE_RXQ_TO_DMA_MAP_DDMACH; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_RXQ_DMA_MAP2); /* Enable XDCS in MAC_Extended_Configuration */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_EXT_CNF); value |= MGBE_MAC_EXT_CNF_DDS; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_EXT_CNF); /* Configure MTL Queues */ /* TODO: Iterate over Number MTL queues need to be removed */ for (qinx = 0; qinx < osi_core->num_mtl_queues; qinx++) { ret = mgbe_configure_mtl_queue(osi_core, osi_core->mtl_queues[qinx]); if (ret < 0) { goto fail; } /* Enable by default to configure forward error packets. * Since this is a local function this will always return sucess, * so no need to check for return value */ ret = hw_config_fw_err_pkts(osi_core, osi_core->mtl_queues[qinx], OSI_ENABLE); if (ret < 0) { goto fail; } } /* configure MGBE MAC HW */ ret = mgbe_configure_mac(osi_core); if (ret < 0) { goto fail; } /* configure MGBE DMA */ mgbe_configure_dma(osi_core); /* tsn initialization */ if (osi_core->hw_feature != OSI_NULL) { hw_tsn_init(osi_core, osi_core->hw_feature->est_sel, osi_core->hw_feature->fpe_sel); } ret = mgbe_dma_chan_to_vmirq_map(osi_core); fail: return ret; } /** * @brief mgbe_handle_mac_fpe_intrs * * Algorithm: This function takes care of handling the * MAC FPE interrupts. * * @param[in] osi_core: OSI core private data structure. * * @note MAC interrupts need to be enabled * */ static void mgbe_handle_mac_fpe_intrs(struct osi_core_priv_data *osi_core) { nveu32_t val = 0; /* interrupt bit clear on read as CSR_SW is reset */ val = osi_readla(osi_core, (nveu8_t *) osi_core->base + MGBE_MAC_FPE_CTS); if ((val & MGBE_MAC_FPE_CTS_RVER) == MGBE_MAC_FPE_CTS_RVER) { val &= ~MGBE_MAC_FPE_CTS_RVER; val |= MGBE_MAC_FPE_CTS_SRSP; } if ((val & MGBE_MAC_FPE_CTS_RRSP) == MGBE_MAC_FPE_CTS_RRSP) { /* received respose packet Nothing to be done, it means other * IP also support FPE */ val &= ~MGBE_MAC_FPE_CTS_RRSP; val &= ~MGBE_MAC_FPE_CTS_TVER; osi_core->fpe_ready = OSI_ENABLE; val |= MGBE_MAC_FPE_CTS_EFPE; } if ((val & MGBE_MAC_FPE_CTS_TRSP) == MGBE_MAC_FPE_CTS_TRSP) { /* TX response packet sucessful */ osi_core->fpe_ready = OSI_ENABLE; /* Enable frame preemption */ val &= ~MGBE_MAC_FPE_CTS_TRSP; val &= ~MGBE_MAC_FPE_CTS_TVER; val |= MGBE_MAC_FPE_CTS_EFPE; } if ((val & MGBE_MAC_FPE_CTS_TVER) == MGBE_MAC_FPE_CTS_TVER) { /*Transmit verif packet sucessful*/ osi_core->fpe_ready = OSI_DISABLE; val &= ~MGBE_MAC_FPE_CTS_TVER; val &= ~MGBE_MAC_FPE_CTS_EFPE; } osi_writela(osi_core, val, (nveu8_t *) osi_core->base + MGBE_MAC_FPE_CTS); } /** * @brief Get free timestamp index from TS array by validating in_use param * * @param[in] l_core: Core local private data structure. * * @retval Free timestamp index. * * @post If timestamp index is MAX_TX_TS_CNT, then its no free count index * is available. */ static inline nveu32_t get_free_ts_idx(struct core_local *l_core) { nveu32_t i; for (i = 0; i < MAX_TX_TS_CNT; i++) { if (l_core->ts[i].in_use == OSI_NONE) { break; } } return i; } /** * @brief mgbe_handle_mac_intrs - Handle MAC interrupts * * Algorithm: This function takes care of handling the * MAC nve32_terrupts which includes speed, mode detection. * * @param[in] osi_core: OSI core private data structure. * * @note MAC nve32_terrupts need to be enabled */ static void mgbe_handle_mac_intrs(struct osi_core_priv_data *osi_core) { struct core_local *l_core = (struct core_local *)(void *)osi_core; nveu32_t mac_isr = 0; nveu32_t mac_ier = 0; nveu32_t tx_errors = 0; nveu8_t *base = (nveu8_t *)osi_core->base; #ifdef HSI_SUPPORT nveu64_t tx_frame_err = 0; #endif mac_isr = osi_readla(osi_core, base + MGBE_MAC_ISR); /* Check for Link status change interrupt */ if ((mac_isr & MGBE_MAC_ISR_LSI) == OSI_ENABLE) { /* For Local fault need to stop network data and restart the LANE bringup */ if ((mac_isr & MGBE_MAC_ISR_LS_MASK) == MGBE_MAC_ISR_LS_LOCAL_FAULT) { osi_core->osd_ops.restart_lane_bringup(osi_core->osd, OSI_DISABLE); } else if ((mac_isr & MGBE_MAC_ISR_LS_MASK) == MGBE_MAC_ISR_LS_LINK_OK) { osi_core->osd_ops.restart_lane_bringup(osi_core->osd, OSI_ENABLE); } else { /* Do Nothing */ } } mac_ier = osi_readla(osi_core, base + MGBE_MAC_IER); if (((mac_isr & MGBE_MAC_IMR_FPEIS) == MGBE_MAC_IMR_FPEIS) && ((mac_ier & MGBE_IMR_FPEIE) == MGBE_IMR_FPEIE)) { mgbe_handle_mac_fpe_intrs(osi_core); } /* Check for any MAC Transmit Error Status Interrupt */ if ((mac_isr & MGBE_IMR_TXESIE) == MGBE_IMR_TXESIE) { /* Check for the type of Tx error by reading MAC_Rx_Tx_Status * register */ tx_errors = osi_readl(base + MGBE_MAC_RX_TX_STS); #ifndef OSI_STRIPPED_LIB if ((tx_errors & MGBE_MAC_TX_TJT) == MGBE_MAC_TX_TJT) { /* increment Tx Jabber timeout stats */ osi_core->stats.mgbe_jabber_timeout_err = osi_update_stats_counter( osi_core->stats.mgbe_jabber_timeout_err, 1UL); } if ((tx_errors & MGBE_MAC_TX_IHE) == MGBE_MAC_TX_IHE) { /* IP Header Error */ osi_core->stats.mgbe_ip_header_err = osi_update_stats_counter( osi_core->stats.mgbe_ip_header_err, 1UL); } if ((tx_errors & MGBE_MAC_TX_PCE) == MGBE_MAC_TX_PCE) { /* Payload Checksum error */ osi_core->stats.mgbe_payload_cs_err = osi_update_stats_counter( osi_core->stats.mgbe_payload_cs_err, 1UL); } #endif /* !OSI_STRIPPED_LIB */ #ifdef HSI_SUPPORT tx_errors &= (MGBE_MAC_TX_TJT | MGBE_MAC_TX_IHE | MGBE_MAC_TX_PCE); if (tx_errors != OSI_NONE) { osi_core->hsi.tx_frame_err_count = osi_update_stats_counter( osi_core->hsi.tx_frame_err_count, 1UL); tx_frame_err = osi_core->hsi.tx_frame_err_count / osi_core->hsi.err_count_threshold; if (osi_core->hsi.tx_frame_err_threshold < tx_frame_err) { osi_core->hsi.tx_frame_err_threshold = tx_frame_err; osi_core->hsi.report_count_err[TX_FRAME_ERR_IDX] = OSI_ENABLE; } osi_core->hsi.err_code[TX_FRAME_ERR_IDX] = OSI_TX_FRAME_ERR; osi_core->hsi.report_err = OSI_ENABLE; } #endif } if ((mac_isr & MGBE_ISR_TSIS) == MGBE_ISR_TSIS) { struct osi_core_tx_ts *head = &l_core->tx_ts_head; if (__sync_fetch_and_add(&l_core->ts_lock, 1) == 1U) { /* mask return as initial value is returned always */ (void)__sync_fetch_and_sub(&l_core->ts_lock, 1); #ifndef OSI_STRIPPED_LIB osi_core->stats.ts_lock_add_fail = osi_update_stats_counter(osi_core->stats.ts_lock_add_fail, 1U); #endif /* !OSI_STRIPPED_LIB */ goto done; } /* TXTSC bit should get reset when all timestamp read */ while (((osi_readla(osi_core, base + MGBE_MAC_TSS) & MGBE_MAC_TSS_TXTSC) == MGBE_MAC_TSS_TXTSC)) { nveu32_t i = get_free_ts_idx(l_core); if (i == MAX_TX_TS_CNT) { struct osi_core_tx_ts *temp = l_core->tx_ts_head.next; /* Remove oldest stale TS from list to make * space for new TS */ OSI_CORE_INFO(osi_core->osd, OSI_LOG_ARG_INVALID, "Removing TS from queue pkt_id\n", temp->pkt_id); temp->in_use = OSI_DISABLE; /* remove temp node from the link */ temp->next->prev = temp->prev; temp->prev->next = temp->next; i = get_free_ts_idx(l_core); if (i == MAX_TX_TS_CNT) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "TS queue is full\n", i); break; } } l_core->ts[i].nsec = osi_readla(osi_core, base + MGBE_MAC_TSNSSEC); l_core->ts[i].in_use = OSI_ENABLE; l_core->ts[i].pkt_id = osi_readla(osi_core, base + MGBE_MAC_TSPKID); l_core->ts[i].sec = osi_readla(osi_core, base + MGBE_MAC_TSSEC); /* Add time stamp to end of list */ l_core->ts[i].next = head->prev->next; head->prev->next = &l_core->ts[i]; l_core->ts[i].prev = head->prev; head->prev = &l_core->ts[i]; } /* mask return as initial value is returned always */ (void)__sync_fetch_and_sub(&l_core->ts_lock, 1); } done: return; } #ifndef OSI_STRIPPED_LIB /** * @brief mgbe_update_dma_sr_stats - stats for dma_status error * * Algorithm: increament error stats based on corresponding bit filed. * * @param[in] osi_core: OSI core private data structure. * @param[in] dma_sr: Dma status register read value * @param[in] qinx: Queue index */ static inline void mgbe_update_dma_sr_stats(struct osi_core_priv_data *osi_core, nveu32_t dma_sr, nveu32_t qinx) { nveu64_t val; if ((dma_sr & MGBE_DMA_CHX_STATUS_RBU) == MGBE_DMA_CHX_STATUS_RBU) { val = osi_core->stats.rx_buf_unavail_irq_n[qinx]; osi_core->stats.rx_buf_unavail_irq_n[qinx] = osi_update_stats_counter(val, 1U); } if ((dma_sr & MGBE_DMA_CHX_STATUS_TPS) == MGBE_DMA_CHX_STATUS_TPS) { val = osi_core->stats.tx_proc_stopped_irq_n[qinx]; osi_core->stats.tx_proc_stopped_irq_n[qinx] = osi_update_stats_counter(val, 1U); } if ((dma_sr & MGBE_DMA_CHX_STATUS_TBU) == MGBE_DMA_CHX_STATUS_TBU) { val = osi_core->stats.tx_buf_unavail_irq_n[qinx]; osi_core->stats.tx_buf_unavail_irq_n[qinx] = osi_update_stats_counter(val, 1U); } if ((dma_sr & MGBE_DMA_CHX_STATUS_RPS) == MGBE_DMA_CHX_STATUS_RPS) { val = osi_core->stats.rx_proc_stopped_irq_n[qinx]; osi_core->stats.rx_proc_stopped_irq_n[qinx] = osi_update_stats_counter(val, 1U); } if ((dma_sr & MGBE_DMA_CHX_STATUS_FBE) == MGBE_DMA_CHX_STATUS_FBE) { val = osi_core->stats.fatal_bus_error_irq_n; osi_core->stats.fatal_bus_error_irq_n = osi_update_stats_counter(val, 1U); } } #endif /* !OSI_STRIPPED_LIB */ /** * @brief mgbe_set_avb_algorithm - Set TxQ/TC avb config * * Algorithm: * 1) Check if queue index is valid * 2) Update operation mode of TxQ/TC * 2a) Set TxQ operation mode * 2b) Set Algo and Credit contro * 2c) Set Send slope credit * 2d) Set Idle slope credit * 2e) Set Hi credit * 2f) Set low credit * 3) Update register values * * @param[in] osi_core: osi core priv data structure * @param[in] avb: structure having configuration for avb algorithm * * @note 1) MAC should be init and started. see osi_start_mac() * 2) osi_core->osd should be populated. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_set_avb_algorithm( struct osi_core_priv_data *const osi_core, const struct osi_core_avb_algorithm *const avb) { nveu32_t value; nve32_t ret = -1; nveu32_t qinx = 0U; nveu32_t tcinx = 0U; if (avb == OSI_NULL) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "avb structure is NULL\n", 0ULL); goto done; } /* queue index in range */ if (avb->qindex >= OSI_MGBE_MAX_NUM_QUEUES) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid Queue index\n", (nveul64_t)avb->qindex); goto done; } /* queue oper_mode in range check*/ if (avb->oper_mode >= OSI_MTL_QUEUE_MODEMAX) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid Queue mode\n", (nveul64_t)avb->qindex); goto done; } /* Validate algo is valid */ if (avb->algo > OSI_MTL_TXQ_AVALG_CBS) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid Algo input\n", (nveul64_t)avb->algo); goto done; } /* can't set AVB mode for queue 0 */ if ((avb->qindex == 0U) && (avb->oper_mode == OSI_MTL_QUEUE_AVB)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_OPNOTSUPP, "Not allowed to set AVB for Q0\n", (nveul64_t)avb->qindex); goto done; } /* TC index range check */ if ((avb->tcindex == 0U) || (avb->tcindex >= OSI_MAX_TC_NUM)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid Queue TC mapping\n", (nveul64_t)avb->tcindex); goto done; } qinx = avb->qindex; tcinx = avb->tcindex; value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_CHX_TX_OP_MODE(qinx)); value &= ~MGBE_MTL_TX_OP_MODE_TXQEN; /* Set TXQEN mode as per input struct after masking 3 bit */ value |= ((avb->oper_mode << MGBE_MTL_TX_OP_MODE_TXQEN_SHIFT) & MGBE_MTL_TX_OP_MODE_TXQEN); /* Set TC mapping */ value &= ~MGBE_MTL_TX_OP_MODE_Q2TCMAP; value |= ((tcinx << MGBE_MTL_TX_OP_MODE_Q2TCMAP_SHIFT) & MGBE_MTL_TX_OP_MODE_Q2TCMAP); osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_CHX_TX_OP_MODE(qinx)); /* Set Algo and Credit control */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_CR(tcinx)); value &= ~MGBE_MTL_TCQ_ETS_CR_AVALG; value &= ~MGBE_MTL_TCQ_ETS_CR_CC; if (avb->algo == OSI_MTL_TXQ_AVALG_CBS) { value |= (avb->credit_control << MGBE_MTL_TCQ_ETS_CR_CC_SHIFT) & MGBE_MTL_TCQ_ETS_CR_CC; value |= (OSI_MTL_TXQ_AVALG_CBS << MGBE_MTL_TCQ_ETS_CR_AVALG_SHIFT) & MGBE_MTL_TCQ_ETS_CR_AVALG; } else { value |= (OSI_MGBE_TXQ_AVALG_ETS << MGBE_MTL_TCQ_ETS_CR_AVALG_SHIFT) & MGBE_MTL_TCQ_ETS_CR_AVALG; } osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_CR(tcinx)); if (avb->algo == OSI_MTL_TXQ_AVALG_CBS) { /* Set Idle slope credit*/ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_QW(tcinx)); value &= ~MGBE_MTL_TCQ_ETS_QW_ISCQW_MASK; value |= avb->idle_slope & MGBE_MTL_TCQ_ETS_QW_ISCQW_MASK; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_QW(tcinx)); /* Set Send slope credit */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_SSCR(tcinx)); value &= ~MGBE_MTL_TCQ_ETS_SSCR_SSC_MASK; value |= avb->send_slope & MGBE_MTL_TCQ_ETS_SSCR_SSC_MASK; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_SSCR(tcinx)); /* Set Hi credit */ value = avb->hi_credit & MGBE_MTL_TCQ_ETS_HCR_HC_MASK; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_HCR(tcinx)); /* low credit is -ve number, osi_write need a nveu32_t * take only 28:0 bits from avb->low_credit */ value = avb->low_credit & MGBE_MTL_TCQ_ETS_LCR_LC_MASK; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_LCR(tcinx)); } else { /* Reset register values to POR/initialized values */ osi_writela(osi_core, MGBE_MTL_TCQ_QW_ISCQW, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_QW(tcinx)); osi_writela(osi_core, OSI_DISABLE, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_SSCR(tcinx)); osi_writela(osi_core, OSI_DISABLE, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_HCR(tcinx)); osi_writela(osi_core, OSI_DISABLE, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_LCR(tcinx)); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_CHX_TX_OP_MODE(qinx)); value &= ~MGBE_MTL_TX_OP_MODE_Q2TCMAP; value |= (osi_core->tc[qinx] << MGBE_MTL_CHX_TX_OP_MODE_Q2TC_SH); osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_CHX_TX_OP_MODE(qinx)); } ret = 0; done: return ret; } /** * @brief mgbe_get_avb_algorithm - Get TxQ/TC avb config * * Algorithm: * 1) Check if queue index is valid * 2) read operation mode of TxQ/TC * 2a) read TxQ operation mode * 2b) read Algo and Credit contro * 2c) read Send slope credit * 2d) read Idle slope credit * 2e) read Hi credit * 2f) read low credit * 3) updated pointer * * @param[in] osi_core: osi core priv data structure * @param[out] avb: structure pointer having configuration for avb algorithm * * @note 1) MAC should be init and started. see osi_start_mac() * 2) osi_core->osd should be populated. * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_get_avb_algorithm(struct osi_core_priv_data *const osi_core, struct osi_core_avb_algorithm *const avb) { nveu32_t value; nve32_t ret = 0; nveu32_t qinx = 0U; nveu32_t tcinx = 0U; if (avb == OSI_NULL) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "avb structure is NULL\n", 0ULL); ret = -1; goto fail; } if (avb->qindex >= OSI_MGBE_MAX_NUM_QUEUES) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Invalid Queue index\n", (nveul64_t)avb->qindex); ret = -1; goto fail; } qinx = avb->qindex; value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_CHX_TX_OP_MODE(qinx)); /* Get TxQ/TC mode as per input struct after masking 3:2 bit */ avb->oper_mode = (value & MGBE_MTL_TX_OP_MODE_TXQEN) >> MGBE_MTL_TX_OP_MODE_TXQEN_SHIFT; /* Get Queue Traffic Class Mapping */ avb->tcindex = ((value & MGBE_MTL_TX_OP_MODE_Q2TCMAP) >> MGBE_MTL_TX_OP_MODE_Q2TCMAP_SHIFT); tcinx = avb->tcindex; /* Get Algo and Credit control */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_CR(tcinx)); avb->credit_control = (value & MGBE_MTL_TCQ_ETS_CR_CC) >> MGBE_MTL_TCQ_ETS_CR_CC_SHIFT; avb->algo = (value & MGBE_MTL_TCQ_ETS_CR_AVALG) >> MGBE_MTL_TCQ_ETS_CR_AVALG_SHIFT; if (avb->algo == OSI_MTL_TXQ_AVALG_CBS) { /* Get Idle slope credit*/ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_QW(tcinx)); avb->idle_slope = value & MGBE_MTL_TCQ_ETS_QW_ISCQW_MASK; /* Get Send slope credit */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_SSCR(tcinx)); avb->send_slope = value & MGBE_MTL_TCQ_ETS_SSCR_SSC_MASK; /* Get Hi credit */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_HCR(tcinx)); avb->hi_credit = value & MGBE_MTL_TCQ_ETS_HCR_HC_MASK; /* Get Low credit for which bit 31:29 are unknown * return 28:0 valid bits to application */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_TCQ_ETS_LCR(tcinx)); avb->low_credit = value & MGBE_MTL_TCQ_ETS_LCR_LC_MASK; } fail: return ret; } /** * @brief mgbe_handle_mtl_intrs - Handle MTL interrupts * * Algorithm: Code to handle interrupt for MTL EST error and status. * There are possible 4 errors which can be part of common interrupt in case of * MTL_EST_SCH_ERR (sheduling error)- HLBS * MTL_EST_FRMS_ERR (Frame size error) - HLBF * MTL_EST_FRMC_ERR (frame check error) - HLBF * Constant Gate Control Error - when time interval in less * than or equal to cycle time, llr = 1 * There is one status interrupt which says swich to SWOL complete. * * @param[in] osi_core: osi core priv data structure * @param[in] mtl_isr: MTL interrupt status value * * @note MAC should be init and started. see osi_start_mac() */ static void mgbe_handle_mtl_intrs(struct osi_core_priv_data *osi_core, nveu32_t mtl_isr) { nveu32_t val = 0U; nveu32_t sch_err = 0U; nveu32_t frm_err = 0U; nveu32_t temp = 0U; nveu32_t i = 0; nveul64_t stat_val = 0U; nveu32_t value = 0U; nveu32_t qstatus = 0U; nveu32_t qinx = 0U; /* Check for all MTL queues */ for (i = 0; i < osi_core->num_mtl_queues; i++) { qinx = osi_core->mtl_queues[i]; if ((mtl_isr & OSI_BIT(qinx)) == OSI_BIT(qinx)) { /* check if Q has underflow error */ qstatus = osi_readl((nveu8_t *)osi_core->base + MGBE_MTL_QINT_STATUS(qinx)); /* Transmit Queue Underflow Interrupt Status */ if ((qstatus & MGBE_MTL_QINT_TXUNIFS) == MGBE_MTL_QINT_TXUNIFS) { #ifndef OSI_STRIPPED_LIB osi_core->stats.mgbe_tx_underflow_err = osi_update_stats_counter( osi_core->stats.mgbe_tx_underflow_err, 1UL); #endif /* !OSI_STRIPPED_LIB */ } /* Clear interrupt status by writing back with 1 */ osi_writel(1U, (nveu8_t *)osi_core->base + MGBE_MTL_QINT_STATUS(qinx)); } } if ((mtl_isr & MGBE_MTL_IS_ESTIS) != MGBE_MTL_IS_ESTIS) { goto done; } val = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_EST_STATUS); val &= (MGBE_MTL_EST_STATUS_CGCE | MGBE_MTL_EST_STATUS_HLBS | MGBE_MTL_EST_STATUS_HLBF | MGBE_MTL_EST_STATUS_BTRE | MGBE_MTL_EST_STATUS_SWLC); /* return if interrupt is not related to EST */ if (val == OSI_DISABLE) { goto done; } /* increase counter write 1 back will clear */ if ((val & MGBE_MTL_EST_STATUS_CGCE) == MGBE_MTL_EST_STATUS_CGCE) { osi_core->est_ready = OSI_DISABLE; stat_val = osi_core->stats.const_gate_ctr_err; osi_core->stats.const_gate_ctr_err = osi_update_stats_counter(stat_val, 1U); } if ((val & MGBE_MTL_EST_STATUS_HLBS) == MGBE_MTL_EST_STATUS_HLBS) { osi_core->est_ready = OSI_DISABLE; stat_val = osi_core->stats.head_of_line_blk_sch; osi_core->stats.head_of_line_blk_sch = osi_update_stats_counter(stat_val, 1U); /* Need to read MTL_EST_Sch_Error register and cleared */ sch_err = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_EST_SCH_ERR); for (i = 0U; i < OSI_MAX_TC_NUM; i++) { temp = OSI_ENABLE; temp = temp << i; if ((sch_err & temp) == temp) { stat_val = osi_core->stats.hlbs_q[i]; osi_core->stats.hlbs_q[i] = osi_update_stats_counter(stat_val, 1U); } } sch_err &= 0xFFU; //only 8 TC allowed so clearing all osi_writela(osi_core, sch_err, (nveu8_t *)osi_core->base + MGBE_MTL_EST_SCH_ERR); /* Reset EST with prnve32_t to configure it properly */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_EST_CONTROL); value &= ~MGBE_MTL_EST_EEST; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_EST_CONTROL); OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Disabling EST due to HLBS, correct GCL\n", OSI_NONE); } if ((val & MGBE_MTL_EST_STATUS_HLBF) == MGBE_MTL_EST_STATUS_HLBF) { osi_core->est_ready = OSI_DISABLE; stat_val = osi_core->stats.head_of_line_blk_frm; osi_core->stats.head_of_line_blk_frm = osi_update_stats_counter(stat_val, 1U); /* Need to read MTL_EST_Frm_Size_Error register and cleared */ frm_err = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_EST_FRMS_ERR); for (i = 0U; i < OSI_MAX_TC_NUM; i++) { temp = OSI_ENABLE; temp = temp << i; if ((frm_err & temp) == temp) { stat_val = osi_core->stats.hlbf_q[i]; osi_core->stats.hlbf_q[i] = osi_update_stats_counter(stat_val, 1U); } } frm_err &= 0xFFU; //only 8 TC allowed so clearing all osi_writela(osi_core, frm_err, (nveu8_t *)osi_core->base + MGBE_MTL_EST_FRMS_ERR); /* Reset EST with prnve32_t to configure it properly */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_EST_CONTROL); /* DDBF 1 means don't drop packets */ if ((value & MGBE_MTL_EST_CONTROL_DDBF) == MGBE_MTL_EST_CONTROL_DDBF) { value &= ~MGBE_MTL_EST_EEST; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_EST_CONTROL); OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Disabling EST due to HLBF, correct GCL\n", OSI_NONE); } } if ((val & MGBE_MTL_EST_STATUS_SWLC) == MGBE_MTL_EST_STATUS_SWLC) { if ((val & MGBE_MTL_EST_STATUS_BTRE) != MGBE_MTL_EST_STATUS_BTRE) { osi_core->est_ready = OSI_ENABLE; } stat_val = osi_core->stats.sw_own_list_complete; osi_core->stats.sw_own_list_complete = osi_update_stats_counter(stat_val, 1U); } if ((val & MGBE_MTL_EST_STATUS_BTRE) == MGBE_MTL_EST_STATUS_BTRE) { osi_core->est_ready = OSI_DISABLE; stat_val = osi_core->stats.base_time_reg_err; osi_core->stats.base_time_reg_err = osi_update_stats_counter(stat_val, 1U); osi_core->est_ready = OSI_DISABLE; } /* clear EST status register as interrupt is handled */ osi_writela(osi_core, val, (nveu8_t *)osi_core->base + MGBE_MTL_EST_STATUS); done: return; } #ifndef OSI_STRIPPED_LIB /** * @brief mgbe_config_ptp_offload - Enable/Disable PTP offload * * Algorithm: Based on input argument, update PTO and TSCR registers. * Update ptp_filter for TSCR register. * * @param[in] osi_core: OSI core private data structure. * * @note 1) MAC should be init and started. see osi_start_mac() * 2) configure_ptp() should be called after this API * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_config_ptp_offload(struct osi_core_priv_data *const osi_core, struct osi_pto_config *const pto_config) { nveu8_t *addr = (nveu8_t *)osi_core->base; nve32_t ret = 0; nveu32_t value = 0x0U; nveu32_t ptc_value = 0x0U; nveu32_t port_id = 0x0U; /* Read MAC TCR */ value = osi_readla(osi_core, (nveu8_t *)addr + MGBE_MAC_TCR); /* clear old configuration */ value &= ~(MGBE_MAC_TCR_TSENMACADDR | OSI_MAC_TCR_SNAPTYPSEL_3 | OSI_MAC_TCR_TSMASTERENA | OSI_MAC_TCR_TSEVENTENA | OSI_MAC_TCR_TSENA | OSI_MAC_TCR_TSCFUPDT | OSI_MAC_TCR_TSCTRLSSR | OSI_MAC_TCR_TSVER2ENA | OSI_MAC_TCR_TSIPENA); /** Handle PTO disable */ if (pto_config->en_dis == OSI_DISABLE) { /* update global setting in ptp_filter */ osi_core->ptp_config.ptp_filter = value; osi_writela(osi_core, ptc_value, addr + MGBE_MAC_PTO_CR); osi_writela(osi_core, value, addr + MGBE_MAC_TCR); /* Setting PORT ID as 0 */ osi_writela(osi_core, OSI_NONE, addr + MGBE_MAC_PIDR0); osi_writela(osi_core, OSI_NONE, addr + MGBE_MAC_PIDR1); osi_writela(osi_core, OSI_NONE, addr + MGBE_MAC_PIDR2); return 0; } /** Handle PTO enable */ /* Set PTOEN bit */ ptc_value |= MGBE_MAC_PTO_CR_PTOEN; ptc_value |= ((pto_config->domain_num << MGBE_MAC_PTO_CR_DN_SHIFT) & MGBE_MAC_PTO_CR_DN); /* Set TSCR register flag */ value |= (OSI_MAC_TCR_TSENA | OSI_MAC_TCR_TSCFUPDT | OSI_MAC_TCR_TSCTRLSSR | OSI_MAC_TCR_TSVER2ENA | OSI_MAC_TCR_TSIPENA); if (pto_config->snap_type > 0U) { /* Set APDREQEN bit if snap_type > 0 */ ptc_value |= MGBE_MAC_PTO_CR_APDREQEN; } /* Set SNAPTYPSEL for Taking Snapshots mode */ value |= ((pto_config->snap_type << MGBE_MAC_TCR_SNAPTYPSEL_SHIFT) & OSI_MAC_TCR_SNAPTYPSEL_3); /* Set/Reset TSMSTRENA bit for Master/Slave */ if (pto_config->master == OSI_ENABLE) { /* Set TSMSTRENA bit for master */ value |= OSI_MAC_TCR_TSMASTERENA; if (pto_config->snap_type != OSI_PTP_SNAP_P2P) { /* Set ASYNCEN bit on PTO Control Register */ ptc_value |= MGBE_MAC_PTO_CR_ASYNCEN; } } else { /* Reset TSMSTRENA bit for slave */ value &= ~OSI_MAC_TCR_TSMASTERENA; } /* Set/Reset TSENMACADDR bit for UC/MC MAC */ if (pto_config->mc_uc == OSI_ENABLE) { /* Set TSENMACADDR bit for MC/UC MAC PTP filter */ value |= MGBE_MAC_TCR_TSENMACADDR; } else { /* Reset TSENMACADDR bit */ value &= ~MGBE_MAC_TCR_TSENMACADDR; } /* Set TSEVNTENA bit for PTP events */ value |= OSI_MAC_TCR_TSEVENTENA; /* update global setting in ptp_filter */ osi_core->ptp_config.ptp_filter = value; /** Write PTO_CR and TCR registers */ osi_writela(osi_core, ptc_value, addr + MGBE_MAC_PTO_CR); osi_writela(osi_core, value, addr + MGBE_MAC_TCR); /* Port ID for PTP offload packet created */ port_id = pto_config->portid & MGBE_MAC_PIDR_PID_MASK; osi_writela(osi_core, port_id, addr + MGBE_MAC_PIDR0); osi_writela(osi_core, OSI_NONE, addr + MGBE_MAC_PIDR1); osi_writela(osi_core, OSI_NONE, addr + MGBE_MAC_PIDR2); return ret; } #endif /* !OSI_STRIPPED_LIB */ #ifdef HSI_SUPPORT /** * @brief mgbe_handle_hsi_intr - Handles hsi interrupt. * * Algorithm: * - Read safety interrupt status register and clear it. * - Update error code in osi_hsi_data structure * * @param[in] osi_core: OSI core private data structure. * * @note MAC should be init and started. see osi_start_mac() */ static void mgbe_handle_hsi_intr(struct osi_core_priv_data *osi_core) { nveu32_t val = 0; nveu32_t val2 = 0; void *xpcs_base = osi_core->xpcs_base; nveu64_t ce_count_threshold; const nveu32_t osi_hsi_err_code[][2] = { {OSI_HSI_MGBE0_UE_CODE, OSI_HSI_MGBE0_CE_CODE}, {OSI_HSI_MGBE1_UE_CODE, OSI_HSI_MGBE1_CE_CODE}, {OSI_HSI_MGBE2_UE_CODE, OSI_HSI_MGBE2_CE_CODE}, {OSI_HSI_MGBE3_UE_CODE, OSI_HSI_MGBE3_CE_CODE}, }; val = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_STATUS); if (((val & MGBE_REGISTER_PARITY_ERR) == MGBE_REGISTER_PARITY_ERR) || ((val & MGBE_CORE_UNCORRECTABLE_ERR) == MGBE_CORE_UNCORRECTABLE_ERR)) { osi_core->hsi.err_code[UE_IDX] = osi_hsi_err_code[osi_core->instance_id][UE_IDX]; osi_core->hsi.report_err = OSI_ENABLE; osi_core->hsi.report_count_err[UE_IDX] = OSI_ENABLE; /* Disable the interrupt */ val2 = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); val2 &= ~MGBE_REGISTER_PARITY_ERR; val2 &= ~MGBE_CORE_UNCORRECTABLE_ERR; osi_writela(osi_core, val2, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); } if ((val & MGBE_CORE_CORRECTABLE_ERR) == MGBE_CORE_CORRECTABLE_ERR) { osi_core->hsi.err_code[CE_IDX] = osi_hsi_err_code[osi_core->instance_id][CE_IDX]; osi_core->hsi.report_err = OSI_ENABLE; osi_core->hsi.ce_count = osi_update_stats_counter(osi_core->hsi.ce_count, 1UL); ce_count_threshold = osi_core->hsi.ce_count / osi_core->hsi.err_count_threshold; if (osi_core->hsi.ce_count_threshold < ce_count_threshold) { osi_core->hsi.ce_count_threshold = ce_count_threshold; osi_core->hsi.report_count_err[CE_IDX] = OSI_ENABLE; } } val &= ~MGBE_MAC_SBD_INTR; osi_writela(osi_core, val, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_STATUS); if (((val & MGBE_CORE_CORRECTABLE_ERR) == MGBE_CORE_CORRECTABLE_ERR) || ((val & MGBE_CORE_UNCORRECTABLE_ERR) == MGBE_CORE_UNCORRECTABLE_ERR)) { /* Clear status register for FSM errors. Clear on read*/ (void)osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_DPP_FSM_INTERRUPT_STATUS); /* Clear status register for ECC error */ val = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_INTERRUPT_STATUS); if (val != 0U) { osi_writela(osi_core, val, (nveu8_t *)osi_core->base + MGBE_MTL_ECC_INTERRUPT_STATUS); } val = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_DMA_ECC_INTERRUPT_STATUS); if (val != 0U) { osi_writela(osi_core, val, (nveu8_t *)osi_core->base + MGBE_DMA_ECC_INTERRUPT_STATUS); } } val = osi_readla(osi_core, (nveu8_t *)osi_core->xpcs_base + XPCS_WRAP_INTERRUPT_STATUS); if (((val & XPCS_CORE_UNCORRECTABLE_ERR) == XPCS_CORE_UNCORRECTABLE_ERR) || ((val & XPCS_REGISTER_PARITY_ERR) == XPCS_REGISTER_PARITY_ERR)) { osi_core->hsi.err_code[UE_IDX] = osi_hsi_err_code[osi_core->instance_id][UE_IDX]; osi_core->hsi.report_err = OSI_ENABLE; osi_core->hsi.report_count_err[UE_IDX] = OSI_ENABLE; /* Disable uncorrectable interrupts */ val2 = osi_readla(osi_core, (nveu8_t *)osi_core->xpcs_base + XPCS_WRAP_INTERRUPT_CONTROL); val2 &= ~XPCS_CORE_UNCORRECTABLE_ERR; val2 &= ~XPCS_REGISTER_PARITY_ERR; osi_writela(osi_core, val2, (nveu8_t *)osi_core->xpcs_base + XPCS_WRAP_INTERRUPT_CONTROL); } if ((val & XPCS_CORE_CORRECTABLE_ERR) == XPCS_CORE_CORRECTABLE_ERR) { osi_core->hsi.err_code[CE_IDX] = osi_hsi_err_code[osi_core->instance_id][CE_IDX]; osi_core->hsi.report_err = OSI_ENABLE; osi_core->hsi.ce_count = osi_update_stats_counter(osi_core->hsi.ce_count, 1UL); ce_count_threshold = osi_core->hsi.ce_count / osi_core->hsi.err_count_threshold; if (osi_core->hsi.ce_count_threshold < ce_count_threshold) { osi_core->hsi.ce_count_threshold = ce_count_threshold; osi_core->hsi.report_count_err[CE_IDX] = OSI_ENABLE; } } osi_writela(osi_core, val, (nveu8_t *)osi_core->xpcs_base + XPCS_WRAP_INTERRUPT_STATUS); if (((val & XPCS_CORE_CORRECTABLE_ERR) == XPCS_CORE_CORRECTABLE_ERR) || ((val & XPCS_CORE_UNCORRECTABLE_ERR) == XPCS_CORE_UNCORRECTABLE_ERR)) { /* Clear status register for PCS error */ val = xpcs_read(xpcs_base, XPCS_VR_XS_PCS_SFTY_UE_INTR0); if (val != 0U) { (void)xpcs_write_safety(osi_core, XPCS_VR_XS_PCS_SFTY_UE_INTR0, 0); } val = xpcs_read(xpcs_base, XPCS_VR_XS_PCS_SFTY_CE_INTR); if (val != 0U) { (void)xpcs_write_safety(osi_core, XPCS_VR_XS_PCS_SFTY_CE_INTR, 0); } } } #endif /** * @brief mgbe_handle_common_intr - Handles common interrupt. * * Algorithm: Clear common nve32_terrupt source. * * @param[in] osi_core: OSI core private data structure. * * @note MAC should be init and started. see osi_start_mac() */ static void mgbe_handle_common_intr(struct osi_core_priv_data *const osi_core) { void *base = osi_core->base; nveu32_t dma_isr = 0; nveu32_t qinx = 0; nveu32_t i = 0; nveu32_t dma_sr = 0; nveu32_t dma_ier = 0; nveu32_t mtl_isr = 0; nveu32_t val = 0; #ifdef HSI_SUPPORT if (osi_core->hsi.enabled == OSI_ENABLE) { mgbe_handle_hsi_intr(osi_core); } #endif dma_isr = osi_readla(osi_core, (nveu8_t *)base + MGBE_DMA_ISR); if (dma_isr == OSI_NONE) { goto done; } //FIXME Need to check how we can get the DMA channel here instead of //MTL Queues if ((dma_isr & MGBE_DMA_ISR_DCH0_DCH15_MASK) != OSI_NONE) { /* Handle Non-TI/RI nve32_terrupts */ for (i = 0; i < osi_core->num_mtl_queues; i++) { qinx = osi_core->mtl_queues[i]; if (qinx >= OSI_MGBE_MAX_NUM_CHANS) { continue; } /* read dma channel status register */ dma_sr = osi_readla(osi_core, (nveu8_t *)base + MGBE_DMA_CHX_STATUS(qinx)); /* read dma channel nve32_terrupt enable register */ dma_ier = osi_readla(osi_core, (nveu8_t *)base + MGBE_DMA_CHX_IER(qinx)); /* process only those nve32_terrupts which we * have enabled. */ dma_sr = (dma_sr & dma_ier); /* mask off RI and TI */ dma_sr &= ~(MGBE_DMA_CHX_STATUS_TI | MGBE_DMA_CHX_STATUS_RI); if (dma_sr == OSI_NONE) { continue; } /* ack non ti/ri nve32_ts */ osi_writela(osi_core, dma_sr, (nveu8_t *)base + MGBE_DMA_CHX_STATUS(qinx)); #ifndef OSI_STRIPPED_LIB mgbe_update_dma_sr_stats(osi_core, dma_sr, qinx); #endif /* !OSI_STRIPPED_LIB */ } } /* Handle MAC interrupts */ if ((dma_isr & MGBE_DMA_ISR_MACIS) == MGBE_DMA_ISR_MACIS) { mgbe_handle_mac_intrs(osi_core); } /* Handle MTL inerrupts */ mtl_isr = osi_readla(osi_core, (nveu8_t *)base + MGBE_MTL_INTR_STATUS); if ((dma_isr & MGBE_DMA_ISR_MTLIS) == MGBE_DMA_ISR_MTLIS) { mgbe_handle_mtl_intrs(osi_core, mtl_isr); } /* Clear common interrupt status in wrapper register */ osi_writela(osi_core, MGBE_MAC_SBD_INTR, (nveu8_t *)base + MGBE_WRAP_COMMON_INTR_STATUS); val = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); val |= MGBE_MAC_SBD_INTR; osi_writela(osi_core, val, (nveu8_t *)osi_core->base + MGBE_WRAP_COMMON_INTR_ENABLE); /* Clear FRP Interrupts in MTL_RXP_Interrupt_Control_Status */ val = osi_readla(osi_core, (nveu8_t *)base + MGBE_MTL_RXP_INTR_CS); val |= (MGBE_MTL_RXP_INTR_CS_NVEOVIS | MGBE_MTL_RXP_INTR_CS_NPEOVIS | MGBE_MTL_RXP_INTR_CS_FOOVIS | MGBE_MTL_RXP_INTR_CS_PDRFIS); osi_writela(osi_core, val, (nveu8_t *)base + MGBE_MTL_RXP_INTR_CS); done: return; } /** * @brief mgbe_pad_calibrate - PAD calibration * * Algorithm: Since PAD calibration not applicable for MGBE * it returns zero. * * @param[in] osi_core: OSI core private data structure. * * @retval zero always */ static nve32_t mgbe_pad_calibrate(OSI_UNUSED struct osi_core_priv_data *const osi_core) { return 0; } #if defined(MACSEC_SUPPORT) && !defined(OSI_STRIPPED_LIB) /** * @brief mgbe_config_mac_tx - Enable/Disable MAC Tx * * Algorithm: Enable/Disable MAC Transmitter engine * * @param[in] osi_core: OSI core private data structure. * @param[in] enable: Enable or Disable.MAC Tx * * @note 1) MAC init should be complete. See osi_hw_core_init() */ static void mgbe_config_mac_tx(struct osi_core_priv_data *const osi_core, const nveu32_t enable) { nveu32_t value; void *addr = osi_core->base; if (enable == OSI_ENABLE) { value = osi_readla(osi_core, (nveu8_t *)addr + MGBE_MAC_TMCR); /* Enable MAC Transmit */ value |= MGBE_MAC_TMCR_TE; osi_writela(osi_core, value, (nveu8_t *)addr + MGBE_MAC_TMCR); } else { value = osi_readla(osi_core, (nveu8_t *)addr + MGBE_MAC_TMCR); /* Disable MAC Transmit */ value &= ~MGBE_MAC_TMCR_TE; osi_writela(osi_core, value, (nveu8_t *)addr + MGBE_MAC_TMCR); } } #endif /* MACSEC_SUPPORT */ /** * @brief mgbe_mdio_busy_wait - MDIO busy wait loop * * Algorithm: Wait for any previous MII read/write operation to complete * * @param[in] osi_core: OSI core data struture. */ static nve32_t mgbe_mdio_busy_wait(struct osi_core_priv_data *const osi_core) { /* half second timeout */ nveu32_t retry = 50000; nveu32_t mac_gmiiar; nveu32_t count; nve32_t cond = 1; nve32_t ret = 0; count = 0; while (cond == 1) { if (count > retry) { ret = -1; goto fail; } count++; mac_gmiiar = osi_readla(osi_core, (nveu8_t *) osi_core->base + MGBE_MDIO_SCCD); if ((mac_gmiiar & MGBE_MDIO_SCCD_SBUSY) == 0U) { cond = 0; } else { osi_core->osd_ops.udelay(10U); } } fail: return ret; } /** * @brief mgbe_write_phy_reg - Write to a PHY register over MDIO bus. * * Algorithm: Write into a PHY register through MGBE MDIO bus. * * @param[in] osi_core: OSI core private data structure. * @param[in] phyaddr: PHY address (PHY ID) associated with PHY * @param[in] phyreg: Register which needs to be write to PHY. * @param[in] phydata: Data to write to a PHY register. * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_write_phy_reg(struct osi_core_priv_data *const osi_core, const nveu32_t phyaddr, const nveu32_t phyreg, const nveu16_t phydata) { nve32_t ret = 0; nveu32_t reg; /* Wait for any previous MII read/write operation to complete */ ret = mgbe_mdio_busy_wait(osi_core); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "MII operation timed out\n", 0ULL); goto fail; } /* set MDIO address register */ /* set device address */ reg = ((phyreg >> MGBE_MDIO_C45_DA_SHIFT) & MGBE_MDIO_SCCA_DA_MASK) << MGBE_MDIO_SCCA_DA_SHIFT; /* set port address and register address */ reg |= (phyaddr << MGBE_MDIO_SCCA_PA_SHIFT) | (phyreg & MGBE_MDIO_SCCA_RA_MASK); osi_writela(osi_core, reg, (nveu8_t *) osi_core->base + MGBE_MDIO_SCCA); /* Program Data register */ reg = phydata | (((nveu32_t)MGBE_MDIO_SCCD_CMD_WR) << MGBE_MDIO_SCCD_CMD_SHIFT) | MGBE_MDIO_SCCD_SBUSY; /** * On FPGA AXI/APB clock is 13MHz. To achive maximum MDC clock * of 2.5MHz need to enable CRS and CR to be set to 1. * On Silicon AXI/APB clock is 408MHz. To achive maximum MDC clock * of 2.5MHz only CR need to be set to 5. */ reg &= ~MGBE_MDIO_SCCD_CRS; reg |= ((((nveu32_t)0x5U) & MGBE_MDIO_SCCD_CR_MASK) << MGBE_MDIO_SCCD_CR_SHIFT); osi_writela(osi_core, reg, (nveu8_t *) osi_core->base + MGBE_MDIO_SCCD); /* wait for MII write operation to complete */ ret = mgbe_mdio_busy_wait(osi_core); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "MII operation timed out\n", 0ULL); } fail: return ret; } /** * @brief mgbe_read_phy_reg - Read from a PHY register over MDIO bus. * * Algorithm: Write into a PHY register through MGBE MDIO bus. * * @param[in] osi_core: OSI core private data structure. * @param[in] phyaddr: PHY address (PHY ID) associated with PHY * @param[in] phyreg: Register which needs to be read from PHY. * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_read_phy_reg(struct osi_core_priv_data *const osi_core, const nveu32_t phyaddr, const nveu32_t phyreg) { nveu32_t reg; nveu32_t data; nve32_t ret = 0; ret = mgbe_mdio_busy_wait(osi_core); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "MII operation timed out\n", 0ULL); goto fail; } /* set MDIO address register */ /* set device address */ reg = ((phyreg >> MGBE_MDIO_C45_DA_SHIFT) & MGBE_MDIO_SCCA_DA_MASK) << MGBE_MDIO_SCCA_DA_SHIFT; /* set port address and register address */ reg |= (phyaddr << MGBE_MDIO_SCCA_PA_SHIFT) | (phyreg & MGBE_MDIO_SCCA_RA_MASK); osi_writela(osi_core, reg, (nveu8_t *) osi_core->base + MGBE_MDIO_SCCA); /* Program Data register */ reg = (((nveu32_t)MGBE_MDIO_SCCD_CMD_RD) << MGBE_MDIO_SCCD_CMD_SHIFT) | MGBE_MDIO_SCCD_SBUSY; /** * On FPGA AXI/APB clock is 13MHz. To achive maximum MDC clock * of 2.5MHz need to enable CRS and CR to be set to 1. * On Silicon AXI/APB clock is 408MHz. To achive maximum MDC clock * of 2.5MHz only CR need to be set to 5. */ reg &= ~MGBE_MDIO_SCCD_CRS; reg |= ((((nveu32_t)0x5U) & MGBE_MDIO_SCCD_CR_MASK) << MGBE_MDIO_SCCD_CR_SHIFT); osi_writela(osi_core, reg, (nveu8_t *) osi_core->base + MGBE_MDIO_SCCD); ret = mgbe_mdio_busy_wait(osi_core); if (ret < 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "MII operation timed out\n", 0ULL); goto fail; } reg = osi_readla(osi_core, (nveu8_t *) osi_core->base + MGBE_MDIO_SCCD); data = (reg & MGBE_MDIO_SCCD_SDATA_MASK); ret = (nve32_t)data; fail: return ret; } #ifndef OSI_STRIPPED_LIB /** * @brief mgbe_disable_tx_lpi - Helper function to disable Tx LPI. * * Algorithm: * Clear the bits to enable Tx LPI, Tx LPI automate, LPI Tx Timer and * PHY Link status in the LPI control/status register * * @param[in] osi_core: OSI core private data structure. * * @note MAC has to be out of reset, and clocks supplied. */ static inline void mgbe_disable_tx_lpi(struct osi_core_priv_data *osi_core) { nveu32_t lpi_csr = 0; /* Disable LPI control bits */ lpi_csr = osi_readla(osi_core, (nveu8_t *) osi_core->base + MGBE_MAC_LPI_CSR); lpi_csr &= ~(MGBE_MAC_LPI_CSR_LPITE | MGBE_MAC_LPI_CSR_LPITXA | MGBE_MAC_LPI_CSR_PLS | MGBE_MAC_LPI_CSR_LPIEN); osi_writela(osi_core, lpi_csr, (nveu8_t *) osi_core->base + MGBE_MAC_LPI_CSR); } /** * @brief mgbe_configure_eee - Configure the EEE LPI mode * * Algorithm: This routine configures EEE LPI mode in the MAC. * 1) The time (in microsecond) to wait before resuming transmission after * exiting from LPI * 2) The time (in millisecond) to wait before LPI pattern can be transmitted * after PHY link is up) are not configurable. Default values are used in this * routine. * * @param[in] osi_core: OSI core private data structure. * @param[in] tx_lpi_enable: enable (1)/disable (0) flag for Tx lpi * @param[in] tx_lpi_timer: Tx LPI entry timer in usec. Valid upto * OSI_MAX_TX_LPI_TIMER in steps of 8usec. * * @note Required clks and resets has to be enabled. * MAC/PHY should be initialized * */ static void mgbe_configure_eee(struct osi_core_priv_data *const osi_core, const nveu32_t tx_lpi_enabled, const nveu32_t tx_lpi_timer) { nveu32_t lpi_csr = 0; nveu32_t lpi_timer_ctrl = 0; nveu32_t lpi_entry_timer = 0; nveu32_t tic_counter = 0; void *addr = osi_core->base; if (xpcs_eee(osi_core, tx_lpi_enabled) != 0) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "xpcs_eee call failed\n", 0ULL); return; } if (tx_lpi_enabled != OSI_DISABLE) { /** 3. Program LST (bits[25:16]) and TWT (bits[15:0]) in * MAC_LPI_Timers_Control Register * Configure the following timers. * a. LPI LS timer - minimum time (in milliseconds) for * which the link status from PHY should be up before * the LPI pattern can be transmitted to the PHY. Default * 1sec * b. LPI TW timer - minimum time (in microseconds) for which * MAC waits after it stops transmitting LPI pattern before * resuming normal tx. Default 21us */ lpi_timer_ctrl |= ((MGBE_DEFAULT_LPI_LS_TIMER << MGBE_LPI_LS_TIMER_SHIFT) & MGBE_LPI_LS_TIMER_MASK); lpi_timer_ctrl |= (MGBE_DEFAULT_LPI_TW_TIMER & MGBE_LPI_TW_TIMER_MASK); osi_writela(osi_core, lpi_timer_ctrl, (nveu8_t *)addr + MGBE_MAC_LPI_TIMER_CTRL); /* 4. For GMII, read the link status of the PHY chip by * using the MDIO interface and update Bit 17 of * MAC_LPI_Control_Status register accordingly. This update * should be done whenever the link status in the PHY chip * changes. For XGMII, the update is automatic unless * PLSDIS bit is set." (skip) */ /* 5. Program the MAC_1US_Tic_Counter as per the frequency * of the clock used for accessing the CSR slave port. */ /* Should be same as (ABP clock freq - 1) = 12 = 0xC, currently * from define but we should get it from pdata->clock TODO */ tic_counter = MGBE_1US_TIC_COUNTER; osi_writela(osi_core, tic_counter, (nveu8_t *)addr + MGBE_MAC_1US_TIC_COUNT); /* 6. Program the MAC_LPI_Auto_Entry_Timer register (LPIET) * with the IDLE time for which the MAC should wait * before entering the LPI state on its own. * LPI entry timer - Time in microseconds that MAC will wait * to enter LPI mode after all tx is complete. Default 1sec */ lpi_entry_timer |= (tx_lpi_timer & MGBE_LPI_ENTRY_TIMER_MASK); osi_writela(osi_core, lpi_entry_timer, (nveu8_t *)addr + MGBE_MAC_LPI_EN_TIMER); /* 7. Set LPIATE and LPITXA (bit[20:19]) of * MAC_LPI_Control_Status register to enable the auto-entry * into LPI and auto-exit of MAC from LPI state. * 8. Set LPITXEN (bit[16]) of MAC_LPI_Control_Status register * to make the MAC Transmitter enter the LPI state. The MAC * enters the LPI mode after completing all scheduled * packets and remain IDLE for the time indicated by LPIET. */ lpi_csr = osi_readla(osi_core, (nveu8_t *) addr + MGBE_MAC_LPI_CSR); lpi_csr |= (MGBE_MAC_LPI_CSR_LPITE | MGBE_MAC_LPI_CSR_LPITXA | MGBE_MAC_LPI_CSR_PLS | MGBE_MAC_LPI_CSR_LPIEN); osi_writela(osi_core, lpi_csr, (nveu8_t *) addr + MGBE_MAC_LPI_CSR); } else { /* Disable LPI control bits */ mgbe_disable_tx_lpi(osi_core); } } #endif /* !OSI_STRIPPED_LIB */ static nve32_t mgbe_get_hw_features(struct osi_core_priv_data *const osi_core, struct osi_hw_features *hw_feat) { nveu8_t *base = (nveu8_t *)osi_core->base; nveu32_t mac_hfr0 = 0; nveu32_t mac_hfr1 = 0; nveu32_t mac_hfr2 = 0; nveu32_t mac_hfr3 = 0; nveu32_t val = 0; mac_hfr0 = osi_readla(osi_core, base + MGBE_MAC_HFR0); mac_hfr1 = osi_readla(osi_core, base + MGBE_MAC_HFR1); mac_hfr2 = osi_readla(osi_core, base + MGBE_MAC_HFR2); mac_hfr3 = osi_readla(osi_core, base + MGBE_MAC_HFR3); hw_feat->rgmii_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_RGMIISEL_SHIFT) & MGBE_MAC_HFR0_RGMIISEL_MASK); hw_feat->gmii_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_GMIISEL_SHIFT) & MGBE_MAC_HFR0_GMIISEL_MASK); hw_feat->rmii_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_RMIISEL_SHIFT) & MGBE_MAC_HFR0_RMIISEL_MASK); hw_feat->hd_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_HDSEL_SHIFT) & MGBE_MAC_HFR0_HDSEL_MASK); hw_feat->vlan_hash_en = ((mac_hfr0 >> MGBE_MAC_HFR0_VLHASH_SHIFT) & MGBE_MAC_HFR0_VLHASH_MASK); hw_feat->sma_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_SMASEL_SHIFT) & MGBE_MAC_HFR0_SMASEL_MASK); hw_feat->rwk_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_RWKSEL_SHIFT) & MGBE_MAC_HFR0_RWKSEL_MASK); hw_feat->mgk_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_MGKSEL_SHIFT) & MGBE_MAC_HFR0_MGKSEL_MASK); hw_feat->mmc_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_MMCSEL_SHIFT) & MGBE_MAC_HFR0_MMCSEL_MASK); hw_feat->arp_offld_en = ((mac_hfr0 >> MGBE_MAC_HFR0_ARPOFFLDEN_SHIFT) & MGBE_MAC_HFR0_ARPOFFLDEN_MASK); hw_feat->rav_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_RAVSEL_SHIFT) & MGBE_MAC_HFR0_RAVSEL_MASK); hw_feat->av_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_AVSEL_SHIFT) & MGBE_MAC_HFR0_AVSEL_MASK); hw_feat->ts_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_TSSSEL_SHIFT) & MGBE_MAC_HFR0_TSSSEL_MASK); hw_feat->eee_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_EEESEL_SHIFT) & MGBE_MAC_HFR0_EEESEL_MASK); hw_feat->tx_coe_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_TXCOESEL_SHIFT) & MGBE_MAC_HFR0_TXCOESEL_MASK); hw_feat->rx_coe_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_RXCOESEL_SHIFT) & MGBE_MAC_HFR0_RXCOESEL_MASK); hw_feat->mac_addr_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_ADDMACADRSEL_SHIFT) & MGBE_MAC_HFR0_ADDMACADRSEL_MASK); hw_feat->act_phy_sel = ((mac_hfr0 >> MGBE_MAC_HFR0_PHYSEL_SHIFT) & MGBE_MAC_HFR0_PHYSEL_MASK); hw_feat->tsstssel = ((mac_hfr0 >> MGBE_MAC_HFR0_TSSTSSEL_SHIFT) & MGBE_MAC_HFR0_TSSTSSEL_MASK); hw_feat->sa_vlan_ins = ((mac_hfr0 >> MGBE_MAC_HFR0_SAVLANINS_SHIFT) & MGBE_MAC_HFR0_SAVLANINS_SHIFT); hw_feat->vxn = ((mac_hfr0 >> MGBE_MAC_HFR0_VXN_SHIFT) & MGBE_MAC_HFR0_VXN_MASK); hw_feat->ediffc = ((mac_hfr0 >> MGBE_MAC_HFR0_EDIFFC_SHIFT) & MGBE_MAC_HFR0_EDIFFC_MASK); hw_feat->edma = ((mac_hfr0 >> MGBE_MAC_HFR0_EDMA_SHIFT) & MGBE_MAC_HFR0_EDMA_MASK); hw_feat->rx_fifo_size = ((mac_hfr1 >> MGBE_MAC_HFR1_RXFIFOSIZE_SHIFT) & MGBE_MAC_HFR1_RXFIFOSIZE_MASK); hw_feat->pfc_en = ((mac_hfr1 >> MGBE_MAC_HFR1_PFCEN_SHIFT) & MGBE_MAC_HFR1_PFCEN_MASK); hw_feat->tx_fifo_size = ((mac_hfr1 >> MGBE_MAC_HFR1_TXFIFOSIZE_SHIFT) & MGBE_MAC_HFR1_TXFIFOSIZE_MASK); hw_feat->ost_en = ((mac_hfr1 >> MGBE_MAC_HFR1_OSTEN_SHIFT) & MGBE_MAC_HFR1_OSTEN_MASK); hw_feat->pto_en = ((mac_hfr1 >> MGBE_MAC_HFR1_PTOEN_SHIFT) & MGBE_MAC_HFR1_PTOEN_MASK); hw_feat->adv_ts_hword = ((mac_hfr1 >> MGBE_MAC_HFR1_ADVTHWORD_SHIFT) & MGBE_MAC_HFR1_ADVTHWORD_MASK); hw_feat->addr_64 = ((mac_hfr1 >> MGBE_MAC_HFR1_ADDR64_SHIFT) & MGBE_MAC_HFR1_ADDR64_MASK); hw_feat->dcb_en = ((mac_hfr1 >> MGBE_MAC_HFR1_DCBEN_SHIFT) & MGBE_MAC_HFR1_DCBEN_MASK); hw_feat->sph_en = ((mac_hfr1 >> MGBE_MAC_HFR1_SPHEN_SHIFT) & MGBE_MAC_HFR1_SPHEN_MASK); hw_feat->tso_en = ((mac_hfr1 >> MGBE_MAC_HFR1_TSOEN_SHIFT) & MGBE_MAC_HFR1_TSOEN_MASK); hw_feat->dma_debug_gen = ((mac_hfr1 >> MGBE_MAC_HFR1_DBGMEMA_SHIFT) & MGBE_MAC_HFR1_DBGMEMA_MASK); hw_feat->rss_en = ((mac_hfr1 >> MGBE_MAC_HFR1_RSSEN_SHIFT) & MGBE_MAC_HFR1_RSSEN_MASK); hw_feat->num_tc = ((mac_hfr1 >> MGBE_MAC_HFR1_NUMTC_SHIFT) & MGBE_MAC_HFR1_NUMTC_MASK); hw_feat->hash_tbl_sz = ((mac_hfr1 >> MGBE_MAC_HFR1_HASHTBLSZ_SHIFT) & MGBE_MAC_HFR1_HASHTBLSZ_MASK); hw_feat->l3l4_filter_num = ((mac_hfr1 >> MGBE_MAC_HFR1_L3L4FNUM_SHIFT) & MGBE_MAC_HFR1_L3L4FNUM_MASK); hw_feat->rx_q_cnt = ((mac_hfr2 >> MGBE_MAC_HFR2_RXQCNT_SHIFT) & MGBE_MAC_HFR2_RXQCNT_MASK); hw_feat->tx_q_cnt = ((mac_hfr2 >> MGBE_MAC_HFR2_TXQCNT_SHIFT) & MGBE_MAC_HFR2_TXQCNT_MASK); hw_feat->rx_ch_cnt = ((mac_hfr2 >> MGBE_MAC_HFR2_RXCHCNT_SHIFT) & MGBE_MAC_HFR2_RXCHCNT_MASK); hw_feat->tx_ch_cnt = ((mac_hfr2 >> MGBE_MAC_HFR2_TXCHCNT_SHIFT) & MGBE_MAC_HFR2_TXCHCNT_MASK); hw_feat->pps_out_num = ((mac_hfr2 >> MGBE_MAC_HFR2_PPSOUTNUM_SHIFT) & MGBE_MAC_HFR2_PPSOUTNUM_MASK); hw_feat->aux_snap_num = ((mac_hfr2 >> MGBE_MAC_HFR2_AUXSNAPNUM_SHIFT) & MGBE_MAC_HFR2_AUXSNAPNUM_MASK); hw_feat->num_vlan_filters = ((mac_hfr3 >> MGBE_MAC_HFR3_NRVF_SHIFT) & MGBE_MAC_HFR3_NRVF_MASK); hw_feat->frp_sel = ((mac_hfr3 >> MGBE_MAC_HFR3_FRPSEL_SHIFT) & MGBE_MAC_HFR3_FRPSEL_MASK); hw_feat->cbti_sel = ((mac_hfr3 >> MGBE_MAC_HFR3_CBTISEL_SHIFT) & MGBE_MAC_HFR3_CBTISEL_MASK); hw_feat->num_frp_pipes = ((mac_hfr3 >> MGBE_MAC_HFR3_FRPPIPE_SHIFT) & MGBE_MAC_HFR3_FRPPIPE_MASK); hw_feat->ost_over_udp = ((mac_hfr3 >> MGBE_MAC_HFR3_POUOST_SHIFT) & MGBE_MAC_HFR3_POUOST_MASK); val = ((mac_hfr3 >> MGBE_MAC_HFR3_FRPPB_SHIFT) & MGBE_MAC_HFR3_FRPPB_MASK); switch (val) { case MGBE_MAC_FRPPB_64: hw_feat->max_frp_bytes = MGBE_MAC_FRP_BYTES64; break; case MGBE_MAC_FRPPB_128: hw_feat->max_frp_bytes = MGBE_MAC_FRP_BYTES128; break; case MGBE_MAC_FRPPB_256: default: hw_feat->max_frp_bytes = MGBE_MAC_FRP_BYTES256; break; } val = ((mac_hfr3 >> MGBE_MAC_HFR3_FRPES_SHIFT) & MGBE_MAC_HFR3_FRPES_MASK); switch (val) { case MGBE_MAC_FRPES_64: hw_feat->max_frp_entries = MGBE_MAC_FRP_BYTES64; break; case MGBE_MAC_FRPES_128: hw_feat->max_frp_entries = MGBE_MAC_FRP_BYTES128; break; case MGBE_MAC_FRPES_256: default: hw_feat->max_frp_entries = MGBE_MAC_FRP_BYTES256; break; } hw_feat->double_vlan_en = ((mac_hfr3 >> MGBE_MAC_HFR3_DVLAN_SHIFT) & MGBE_MAC_HFR3_DVLAN_MASK); hw_feat->auto_safety_pkg = ((mac_hfr3 >> MGBE_MAC_HFR3_ASP_SHIFT) & MGBE_MAC_HFR3_ASP_MASK); hw_feat->tts_fifo_depth = ((mac_hfr3 >> MGBE_MAC_HFR3_TTSFD_SHIFT) & MGBE_MAC_HFR3_TTSFD_MASK); hw_feat->est_sel = ((mac_hfr3 >> MGBE_MAC_HFR3_ESTSEL_SHIFT) & MGBE_MAC_HFR3_ESTSEL_MASK); hw_feat->gcl_depth = ((mac_hfr3 >> MGBE_MAC_HFR3_GCLDEP_SHIFT) & MGBE_MAC_HFR3_GCLDEP_MASK); hw_feat->gcl_width = ((mac_hfr3 >> MGBE_MAC_HFR3_GCLWID_SHIFT) & MGBE_MAC_HFR3_GCLWID_MASK); hw_feat->fpe_sel = ((mac_hfr3 >> MGBE_MAC_HFR3_FPESEL_SHIFT) & MGBE_MAC_HFR3_FPESEL_MASK); hw_feat->tbs_sel = ((mac_hfr3 >> MGBE_MAC_HFR3_TBSSEL_SHIFT) & MGBE_MAC_HFR3_TBSSEL_MASK); hw_feat->num_tbs_ch = ((mac_hfr3 >> MGBE_MAC_HFR3_TBS_CH_SHIFT) & MGBE_MAC_HFR3_TBS_CH_MASK); return 0; } /** * @brief mgbe_poll_for_update_ts_complete - Poll for update time stamp * * Algorithm: Read time stamp update value from TCR register until it is * equal to zero. * * @param[in] osi_core: OSI core private data structure. * @param[in] mac_tcr: Address to store time stamp control register read value * * @note MAC should be init and started. see osi_start_mac() * * @retval 0 on success * @retval -1 on failure. */ static inline nve32_t mgbe_poll_for_update_ts_complete( struct osi_core_priv_data *osi_core, nveu32_t *mac_tcr) { nveu32_t retry = 0U; nve32_t ret = -1; while (retry < OSI_POLL_COUNT) { /* Read and Check TSUPDT in MAC_Timestamp_Control register */ *mac_tcr = osi_readla(osi_core, (nveu8_t *) osi_core->base + MGBE_MAC_TCR); if ((*mac_tcr & MGBE_MAC_TCR_TSUPDT) == 0U) { ret = 0; break; } retry++; osi_core->osd_ops.udelay(OSI_DELAY_1000US); } return ret; } /** * @brief mgbe_adjust_mactime - Adjust MAC time with system time * * Algorithm: Update MAC time with system time * * @param[in] osi_core: OSI core private data structure. * @param[in] sec: Seconds to be configured * @param[in] nsec: Nano seconds to be configured * @param[in] add_sub: To decide on add/sub with system time * @param[in] one_nsec_accuracy: One nano second accuracy * * @note 1) MAC should be init and started. see osi_start_mac() * 2) osi_core->ptp_config.one_nsec_accuracy need to be set to 1 * * @retval 0 on success * @retval -1 on failure. */ static nve32_t mgbe_adjust_mactime(struct osi_core_priv_data *const osi_core, const nveu32_t sec, const nveu32_t nsec, const nveu32_t add_sub, const nveu32_t one_nsec_accuracy) { void *addr = osi_core->base; nveu32_t mac_tcr; nveu32_t value = 0; nveul64_t temp = 0; nveu32_t temp_sec; nveu32_t temp_nsec; nve32_t ret = 0; temp_sec = sec; temp_nsec = nsec; /* To be sure previous write was flushed (if Any) */ ret = mgbe_poll_for_update_ts_complete(osi_core, &mac_tcr); if (ret == -1) { goto fail; } if (add_sub != 0U) { /* If the new sec value needs to be subtracted with * the system time, then MAC_STSUR reg should be * programmed with (2^32 – ) */ temp = (TWO_POWER_32 - temp_sec); if (temp < UINT_MAX) { temp_sec = (nveu32_t)temp; } else { /* do nothing here */ } /* If the new nsec value need to be subtracted with * the system time, then MAC_STNSUR.TSSS field should be * programmed with, (10^9 - ) if * MAC_TCR.TSCTRLSSR is set or * (2^32 - if MAC_TCR.TSCTRLSSR is reset) */ if (one_nsec_accuracy == OSI_ENABLE) { if (temp_nsec < UINT_MAX) { temp_nsec = (TEN_POWER_9 - temp_nsec); } } else { if (temp_nsec < UINT_MAX) { temp_nsec = (TWO_POWER_31 - temp_nsec); } } } /* write seconds value to MAC_System_Time_Seconds_Update register */ osi_writela(osi_core, temp_sec, (nveu8_t *)addr + MGBE_MAC_STSUR); /* write nano seconds value and add_sub to * MAC_System_Time_Nanoseconds_Update register */ value |= temp_nsec; value |= (add_sub << MGBE_MAC_STNSUR_ADDSUB_SHIFT); osi_writela(osi_core, value, (nveu8_t *)addr + MGBE_MAC_STNSUR); /* issue command to initialize system time with the value * specified in MAC_STSUR and MAC_STNSUR */ mac_tcr |= MGBE_MAC_TCR_TSUPDT; osi_writela(osi_core, mac_tcr, (nveu8_t *)addr + MGBE_MAC_TCR); ret = mgbe_poll_for_update_ts_complete(osi_core, &mac_tcr); fail: return ret; } /** * @brief mgbe_read_reg - Read a register * * @param[in] osi_core: OSI core private data structure. * @param[in] reg: Register address. * * @note * API Group: * - Initialization: Yes * - Run time: Yes * - De-initialization: Yes * @retval 0 */ static nveu32_t mgbe_read_reg(struct osi_core_priv_data *const osi_core, const nve32_t reg) { return osi_readla(osi_core, (nveu8_t *)osi_core->base + reg); } /** * @brief mgbe_write_reg - Write a reg * * @param[in] osi_core: OSI core private data structure. * @param[in] val: Value to be written. * @param[in] reg: Register address. * * @note * API Group: * - Initialization: Yes * - Run time: Yes * - De-initialization: Yes * @retval 0 */ static nveu32_t mgbe_write_reg(struct osi_core_priv_data *const osi_core, const nveu32_t val, const nve32_t reg) { osi_writela(osi_core, val, (nveu8_t *)osi_core->base + reg); return 0; } #ifdef MACSEC_SUPPORT /** * @brief mgbe_read_macsec_reg - Read a MACSEC register * * @param[in] osi_core: OSI core private data structure. * @param[in] reg: Register address. * * @note * API Group: * - Initialization: Yes * - Run time: Yes * - De-initialization: Yes * @retval 0 */ static nveu32_t mgbe_read_macsec_reg(struct osi_core_priv_data *const osi_core, const nve32_t reg) { return osi_readla(osi_core, (nveu8_t *)osi_core->macsec_base + reg); } /** * @brief mgbe_write_macsec_reg - Write to a MACSEC reg * * @param[in] osi_core: OSI core private data structure. * @param[in] val: Value to be written. * @param[in] reg: Register address. * * @note * API Group: * - Initialization: Yes * - Run time: Yes * - De-initialization: Yes * @retval 0 */ static nveu32_t mgbe_write_macsec_reg(struct osi_core_priv_data *const osi_core, const nveu32_t val, const nve32_t reg) { osi_writela(osi_core, val, (nveu8_t *)osi_core->macsec_base + reg); return 0; } #endif /* MACSEC_SUPPORT */ #ifndef OSI_STRIPPED_LIB /** * @brief eqos_write_reg - Write a reg * * @param[in] osi_core: OSI core private data structure. * @param[in] val: Value to be written. * @param[in] reg: Register address. * * @note * API Group: * - Initialization: Yes * - Run time: Yes * - De-initialization: Yes * @retval 0 */ static nve32_t mgbe_config_tx_status(OSI_UNUSED struct osi_core_priv_data *const osi_core, OSI_UNUSED const nveu32_t tx_status) { return 0; } /** * @brief eqos_write_reg - Write a reg * * @param[in] osi_core: OSI core private data structure. * @param[in] val: Value to be written. * @param[in] reg: Register address. * * @note * API Group: * - Initialization: Yes * - Run time: Yes * - De-initialization: Yes * @retval 0 */ static nve32_t mgbe_config_rx_crc_check(OSI_UNUSED struct osi_core_priv_data *const osi_core, OSI_UNUSED const nveu32_t crc_chk) { return 0; } /** * @brief eqos_write_reg - Write a reg * * @param[in] osi_core: OSI core private data structure. * @param[in] val: Value to be written. * @param[in] reg: Register address. * * @note * API Group: * - Initialization: Yes * - Run time: Yes * - De-initialization: Yes * @retval 0 */ static void mgbe_set_mdc_clk_rate(OSI_UNUSED struct osi_core_priv_data *const osi_core, OSI_UNUSED const nveu64_t csr_clk_rate) { } #endif /* !OSI_STRIPPED_LIB */ #if defined(MACSEC_SUPPORT) && !defined(OSI_STRIPPED_LIB) /** * @brief mgbe_config_for_macsec - Configure MAC according to macsec IAS * * @note * Algorithm: * - Stop MAC Tx * - Update MAC IPG value to accommodate macsec 32 byte SECTAG. * - Start MAC Tx * - Update MTL_EST value as MACSEC is enabled/disabled * * @param[in] osi_core: OSI core private data. * @param[in] enable: Enable or Disable MAC Tx engine * * @pre * 1) MAC has to be out of reset. * 2) Shall not use this ipg value in half duplex mode * * @note * API Group: * - Initialization: No * - Run time: Yes * - De-initialization: No */ static void mgbe_config_for_macsec(struct osi_core_priv_data *const osi_core, const nveu32_t enable) { nveu32_t value = 0U, temp = 0U; if ((enable != OSI_ENABLE) && (enable != OSI_DISABLE)) { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_INVALID, "Failed to config MGBE per MACSEC\n", 0ULL); goto done; } /* stop MAC Tx */ mgbe_config_mac_tx(osi_core, OSI_DISABLE); if (enable == OSI_ENABLE) { /* Configure IPG {EIPG,IPG} value according to macsec IAS in * MAC_Tx_Configuration and MAC_Extended_Configuration * IPG (12 B[default] + 32 B[sectag]) = 352 bits */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_TMCR); value &= ~MGBE_MAC_TMCR_IPG_MASK; value |= MGBE_MAC_TMCR_IFP; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_TMCR); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_EXT_CNF); value |= MGBE_MAC_EXT_CNF_EIPG; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_EXT_CNF); } else { /* Update MAC IPG to default value 12B */ value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_TMCR); value &= ~MGBE_MAC_TMCR_IPG_MASK; value &= ~MGBE_MAC_TMCR_IFP; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_TMCR); value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MAC_EXT_CNF); value &= ~MGBE_MAC_EXT_CNF_EIPG_MASK; osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MAC_EXT_CNF); } /* start MAC Tx */ mgbe_config_mac_tx(osi_core, OSI_ENABLE); if (osi_core->hw_feature != OSI_NULL) { /* Program MTL_EST depending on MACSEC enable/disable */ if (osi_core->hw_feature->est_sel == OSI_ENABLE) { value = osi_readla(osi_core, (nveu8_t *)osi_core->base + MGBE_MTL_EST_CONTROL); value &= ~MGBE_MTL_EST_CONTROL_CTOV; if (enable == OSI_ENABLE) { temp = MGBE_MTL_EST_CTOV_MACSEC_RECOMMEND; temp = temp << MGBE_MTL_EST_CONTROL_CTOV_SHIFT; value |= temp & MGBE_MTL_EST_CONTROL_CTOV; } else { temp = MGBE_MTL_EST_CTOV_RECOMMEND; temp = temp << MGBE_MTL_EST_CONTROL_CTOV_SHIFT; value |= temp & MGBE_MTL_EST_CONTROL_CTOV; } osi_writela(osi_core, value, (nveu8_t *)osi_core->base + MGBE_MTL_EST_CONTROL); } else { OSI_CORE_ERR(osi_core->osd, OSI_LOG_ARG_HW_FAIL, "Error: osi_core->hw_feature is NULL\n", 0ULL); } } done: return; } #endif /* MACSEC_SUPPORT */ /** * @brief mgbe_init_core_ops - Initialize MGBE MAC core operations */ void mgbe_init_core_ops(struct core_ops *ops) { ops->core_init = mgbe_core_init; ops->handle_common_intr = mgbe_handle_common_intr; ops->pad_calibrate = mgbe_pad_calibrate; ops->update_mac_addr_low_high_reg = mgbe_update_mac_addr_low_high_reg; ops->adjust_mactime = mgbe_adjust_mactime; ops->read_mmc = mgbe_read_mmc; ops->write_phy_reg = mgbe_write_phy_reg; ops->read_phy_reg = mgbe_read_phy_reg; ops->get_hw_features = mgbe_get_hw_features; ops->read_reg = mgbe_read_reg; ops->write_reg = mgbe_write_reg; ops->set_avb_algorithm = mgbe_set_avb_algorithm; ops->get_avb_algorithm = mgbe_get_avb_algorithm; ops->config_frp = mgbe_config_frp; ops->update_frp_entry = mgbe_update_frp_entry; ops->update_frp_nve = mgbe_update_frp_nve; #ifdef MACSEC_SUPPORT ops->read_macsec_reg = mgbe_read_macsec_reg; ops->write_macsec_reg = mgbe_write_macsec_reg; #ifndef OSI_STRIPPED_LIB ops->macsec_config_mac = mgbe_config_for_macsec; #endif /* !OSI_STRIPPED_LIB */ #endif /* MACSEC_SUPPORT */ ops->config_l3l4_filters = mgbe_config_l3l4_filters; #ifndef OSI_STRIPPED_LIB ops->config_tx_status = mgbe_config_tx_status; ops->config_rx_crc_check = mgbe_config_rx_crc_check; ops->config_flow_control = mgbe_config_flow_control; ops->config_arp_offload = mgbe_config_arp_offload; ops->config_ptp_offload = mgbe_config_ptp_offload; ops->config_vlan_filtering = mgbe_config_vlan_filtering; ops->reset_mmc = mgbe_reset_mmc; ops->configure_eee = mgbe_configure_eee; ops->set_mdc_clk_rate = mgbe_set_mdc_clk_rate; ops->config_mac_loopback = mgbe_config_mac_loopback; ops->config_rss = mgbe_config_rss; ops->config_ptp_rxq = mgbe_config_ptp_rxq; #endif /* !OSI_STRIPPED_LIB */ #ifdef HSI_SUPPORT ops->core_hsi_configure = mgbe_hsi_configure; ops->core_hsi_inject_err = mgbe_hsi_inject_err; #endif };