/* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This file contain content copied from Synopsis driver, * provided under the license below */ /* ========================================================================= * The Synopsys DWC ETHER QOS Software Driver and documentation (hereinafter * "Software") is an unsupported proprietary work of Synopsys, Inc. unless * otherwise expressly agreed to in writing between Synopsys and you. * * The Software IS NOT an item of Licensed Software or Licensed Product under * any End User Software License Agreement or Agreement for Licensed Product * with Synopsys or any supplement thereto. Permission is hereby granted, * free of charge, to any person obtaining a copy of this software annotated * with this license and 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. * * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * ========================================================================= */ /*!@file: DWC_ETH_QOS_dev.c * @brief: Driver functions. */ #include "DWC_ETH_QOS_yheader.h" #include "DWC_ETH_QOS_yapphdr.h" extern ULONG dwc_eth_qos_base_addr; #include "DWC_ETH_QOS_yregacc.h" #ifdef DWC_ETH_QOS_CONFIG_PGTEST static INT prepare_dev_pktgen(struct DWC_ETH_QOS_prv_data *pdata) { UINT QINX = 0; /* set MAC loop back mode */ MAC_MCR_LM_UDFWR(0x1); /* Do not strip received VLAN tag */ MAC_VLANTR_EVLS_UDFWR(0x0); /* set promiscuous mode */ MAC_MPFR_PR_UDFWR(0x1); /* disable autopad or CRC stripping */ MAC_MCR_ACS_UDFWR(0); /* enable drop tx status */ MTL_OMR_DTXSTS_UDFWR(0x1); for (QINX = 0; QINX < DWC_ETH_QOS_TX_QUEUE_CNT; QINX++) { /* enable avg bits per slot interrupt */ MTL_QECR_ABPSSIE_UDFWR(QINX, 0x1); /* enable OSF mode */ DMA_TCR_OSP_UDFWR(QINX, 0x1); /* disable slot checks */ DMA_SFCSR_RGWR(QINX, 0); } return Y_SUCCESS; } /*! * \brief This sequence is used to configure slot count The software * can program the number of slots(of duration 125us) over which the * average transmitted bits per slot need to be computed for * channel 1 to 7 when CBS alogorithm is enabled. * \param[in] QINX * \param[in] slot_count * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_slot_count(UINT QINX, UCHAR slot_count) { if (slot_count == 1) MTL_QECR_SLC_UDFWR(QINX, 0); else if (slot_count == 2) MTL_QECR_SLC_UDFWR(QINX, 0x1); else if (slot_count == 4) MTL_QECR_SLC_UDFWR(QINX, 0x3); else if (slot_count == 8) MTL_QECR_SLC_UDFWR(QINX, 0x4); else if (slot_count == 16) MTL_QECR_SLC_UDFWR(QINX, 0x5); return Y_SUCCESS; } /*! * \brief This sequence is used to enable/disable slot interrupt: * When this bit is set,the MAC asserts an interrupt when the average * bits per slot status is updated for channel 1 to 7. * \param[in] QINX * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_slot_interrupt(UINT QINX, UCHAR slot_int) { MTL_QECR_ABPSSIE_UDFWR(QINX, slot_int); return Y_SUCCESS; } /*! * \brief This sequence is used to configure DMA Tx:Rx/Rx:Tx * Priority Ratio These bits control the priority ratio in WRR * arbitration between the TX and RX DAM. * \param[in] prio_ratio * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_tx_rx_prio_ratio(UCHAR prio_ratio) { DMA_BMR_PR_UDFWR(prio_ratio); return Y_SUCCESS; } /*! * \brief This sequence is used to configure DMA Transmit Arbitration algorithm * \param[in] arb_algo * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_dma_tx_arb_algorithm(UCHAR arb_algo) { DMA_BMR_TAA_UDFWR(arb_algo); return Y_SUCCESS; } /*! * \brief This sequence is used to configure DMA Tx Priority When this * bit is set, it indicates that the TX DMA has higher priority than * the RX DMA during arbitration for the system-side bus. * \param[in] prio * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_tx_rx_prio(UCHAR prio) { DMA_BMR_TXPR_UDFWR(prio); return Y_SUCCESS; } /*! * \brief This sequence is used to configure DMA Tx/Rx Arbitration Scheme * This bit specifies the arbitration scheme between the Tx and Rx paths * of all channels. * \param[in] prio_policy * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_tx_rx_prio_policy(UCHAR prio_policy) { DMA_BMR_DA_UDFWR(prio_policy); return Y_SUCCESS; } /*! * \brief This sequence is used to configure TX Channel Weight * \param[in] QINX * \param[in] weight * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_ch_arb_weights(UINT QINX, UCHAR weight) { if (weight == 1) DMA_TCR_TCW_UDFWR(QINX, 0); else if (weight == 2) DMA_TCR_TCW_UDFWR(QINX, 0x1); else if (weight == 3) DMA_TCR_TCW_UDFWR(QINX, 0x2); else if (weight == 4) DMA_TCR_TCW_UDFWR(QINX, 0x3); else if (weight == 5) DMA_TCR_TCW_UDFWR(QINX, 0x4); else if (weight == 6) DMA_TCR_TCW_UDFWR(QINX, 0x5); else if (weight == 7) DMA_TCR_TCW_UDFWR(QINX, 0x6); else if (weight == 8) DMA_TCR_TCW_UDFWR(QINX, 0x7); return Y_SUCCESS; } #endif /* end of DWC_ETH_QOS_CONFIG_PGTEST */ /*! * \brief This sequence is used to enable/disable MAC loopback mode * \param[in] enb_dis * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_mac_loopback_mode(UINT enb_dis) { MAC_MCR_LM_UDFWR(enb_dis); return Y_SUCCESS; } /* enable/disable PFC(Priority Based Flow Control) */ static void config_pfc(int enb_dis) { MAC_RFCR_PFCE_UDFWR(enb_dis); } /*! * \brief This sequence is used to configure mac double vlan processing feature. * \param[in] enb_dis * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_tx_outer_vlan(UINT op_type, UINT outer_vlt) { EMACDBG("--> config_tx_outer_vlan()\n"); MAC_VLANTIRR_VLTI_UDFWR(0x0); MAC_VLANTIRR_VLT_UDFWR(outer_vlt); MAC_VLANTIRR_VLP_UDFWR(0x1); MAC_VLANTIRR_VLC_UDFWR(op_type); if (op_type == DWC_ETH_QOS_DVLAN_NONE) { MAC_VLANTIRR_VLP_UDFWR(0x0); MAC_VLANTIRR_VLT_UDFWR(0x0); } EMACDBG("<-- config_tx_outer_vlan()\n"); return Y_SUCCESS; } static INT config_tx_inner_vlan(UINT op_type, UINT inner_vlt) { EMACDBG("--> config_tx_inner_vlan()\n"); MAC_IVLANTIRR_VLTI_UDFWR(0x0); MAC_IVLANTIRR_VLT_UDFWR(inner_vlt); MAC_IVLANTIRR_VLP_UDFWR(0x1); MAC_IVLANTIRR_VLC_UDFWR(op_type); if (op_type == DWC_ETH_QOS_DVLAN_NONE) { MAC_IVLANTIRR_VLP_UDFWR(0x0); MAC_IVLANTIRR_VLT_UDFWR(0x0); } EMACDBG("<-- config_tx_inner_vlan()\n"); return Y_SUCCESS; } static INT config_svlan(UINT flags) { INT ret = Y_SUCCESS; EMACDBG("--> config_svlan()\n"); MAC_VLANTR_ESVL_UDFWR(1); if (flags == DWC_ETH_QOS_DVLAN_NONE) { MAC_VLANTR_ESVL_UDFWR(0); MAC_IVLANTIRR_CSVL_UDFWR(0); MAC_VLANTIRR_CSVL_UDFWR(0); } else if (flags == DWC_ETH_QOS_DVLAN_INNER) { MAC_IVLANTIRR_CSVL_UDFWR(1); } else if (flags == DWC_ETH_QOS_DVLAN_OUTER) { MAC_VLANTIRR_CSVL_UDFWR(1); } else if (flags == DWC_ETH_QOS_DVLAN_BOTH) { MAC_IVLANTIRR_CSVL_UDFWR(1); MAC_VLANTIRR_CSVL_UDFWR(1); } else { EMACERR("ERROR : double VLAN enable SVLAN configuration - Invalid argument"); ret = Y_FAILURE; } EMACDBG("<-- config_svlan()\n"); return ret; } static VOID config_dvlan(bool enb_dis) { MAC_VLANTR_EDVLP_UDFWR(enb_dis); } /*! * \brief This sequence is used to enable/disable ARP offload * \param[in] enb_dis * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_arp_offload(int enb_dis) { MAC_MCR_ARPEN_UDFWR(enb_dis); return Y_SUCCESS; } /*! * \brief This sequence is used to update the IP addr into MAC ARP Add reg, * which is used by MAC for replying to ARP packets * \param[in] addr * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int update_arp_offload_ip_addr(UCHAR addr[]) { MAC_ARPA_RGWR( (addr[3] | (addr[2] << 8) | (addr[1] << 16) | addr[0] << 24)); return Y_SUCCESS; } /*! * \brief This sequence is used to get the status of LPI/EEE mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static u32 get_lpi_status(void) { u32 varmac_lps; MAC_LPS_RGRD(varmac_lps); return varmac_lps; } /*! * \brief This sequence is used to enable EEE mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int set_eee_mode(void) { MAC_LPS_LPIEN_UDFWR(0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to disable EEE mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int reset_eee_mode(void) { MAC_LPS_LPITXA_UDFWR(0); MAC_LPS_LPIEN_UDFWR(0); return Y_SUCCESS; } /*! * \brief This sequence is used to set PLS bit * \param[in] phy_link * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int set_eee_pls(int phy_link) { if (phy_link == 1) MAC_LPS_PLS_UDFWR(0x1); else MAC_LPS_PLS_UDFWR(0); return Y_SUCCESS; } /*! * \brief This sequence is used to set EEE timer values * \param[in] lpi_lst * \param[in] lpi_twt * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int set_eee_timer(int lpi_lst, int lpi_twt) { /* mim time(us) for which the MAC waits after it stops * transmitting * the LPI pattern to the PHY and before it resumes the * normal transmission. */ MAC_LPC_TWT_UDFWR(lpi_twt); /* mim time(ms) for which the link status from the PHY * should be Up before * the LPI pattern can be transmitted to the PHY. */ MAC_LPC_TLPIEX_UDFWR(lpi_lst); return Y_SUCCESS; } static int set_lpi_tx_automate(void) { MAC_LPS_LPITXA_UDFWR(0x1); return Y_SUCCESS; } static int set_lpi_tx_auto_entry_timer_en(void) { MAC_LPS_LPIATE_UDFWR(0x1); return Y_SUCCESS; } static int set_lpi_tx_auto_entry_timer(u32 data) { MAC_LPET_LPIET_UDFWR(data); return Y_SUCCESS; } static int set_lpi_us_tic_counter(u32 data) { MAC_1USTICK_CNTR_UDFWR(data); return Y_SUCCESS; } /*! * \brief This sequence is used to enable/disable Auto-Negotiation * and restart the autonegotiation * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int control_an(bool enable, bool restart) { MAC_ANC_ANE_UDFWR(enable); MAC_ANC_RAN_UDFWR(restart); return Y_SUCCESS; } /*! * \brief This sequence is used to get Auto-Negotiation advertisment * pause parameter * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int get_an_adv_pause_param(void) { unsigned long varmac_aad; MAC_AAD_RGRD(varmac_aad); return GET_VALUE(varmac_aad, MAC_AAD_PSE_LPOS, MAC_AAD_PSE_HPOS); } /*! * \brief This sequence is used to get Auto-Negotiation advertisment * duplex parameter. Returns one if Full duplex mode is selected * else returns zero * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int get_an_adv_duplex_param(void) { unsigned long varmac_aad; MAC_AAD_RGRD(varmac_aad); if (GET_VALUE(varmac_aad, MAC_AAD_FD_LPOS, MAC_AAD_FD_HPOS) == 1) return 1; else return 0; } /*! * \brief This sequence is used to get Link partner Auto-Negotiation * advertisment pause parameter * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int get_lp_an_adv_pause_param(void) { unsigned long varmac_alpa; MAC_ALPA_RGRD(varmac_alpa); return GET_VALUE(varmac_alpa, MAC_ALPA_PSE_LPOS, MAC_ALPA_PSE_HPOS); } /*! * \brief This sequence is used to get Link partner Auto-Negotiation * advertisment duplex parameter. Returns one if Full duplex mode * is selected else returns zero * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int get_lp_an_adv_duplex_param(void) { unsigned long varmac_alpa; MAC_ALPA_RGRD(varmac_alpa); if (GET_VALUE(varmac_alpa, MAC_ALPA_FD_LPOS, MAC_ALPA_FD_HPOS) == 1) return 1; else return 0; } static UINT get_vlan_tag_comparison(void) { UINT etv; MAC_VLANTR_ETV_UDFRD(etv); return etv; } /*! * \brief This sequence is used to update the * EMAC_MAC_VLAN_TAG_DATA VLAN register with * VLAN tag, offset and 12-bit comparison enable/disable info * \param[in] VLAN tag * \param[in] offset value * \param[in] 12-bit comparison enable/disable * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_vlan_tag_data(UINT vlan_tag, INT vlan_reg_offset, bool enable_12_bit_vlan_tag_comparison) { ULONG RETRYCOUNT = 1000; ULONG current_cnt = 0; volatile int op_busy; int reg_val = 0; EMACDBG("Enter\n"); while (1) { if (current_cnt > RETRYCOUNT) return -Y_FAILURE; MAC_VLANTR_OB_UDFRD(op_busy); if (op_busy == 0) break; current_cnt++; mdelay(1); } EMACDBG("Write to MAC_VLAN_TAG_DATA reg as OB bit = %d\n", op_busy); /* Write the VLAN tag data */ MAC_VLAN_TAG_DATA_15_0_UDFWR(vlan_tag); MAC_VLAN_TAG_DATA_16_UDFWR(0x1); MAC_VLAN_TAG_DATA_17_UDFWR(enable_12_bit_vlan_tag_comparison); MAC_VLAN_TAG_DATA_18_UDFWR(0x1); MAC_VLANTR_OFS_UDFWR(vlan_reg_offset); MAC_VLANTR_CT_UDFWR(0x0); MAC_VLANTR_OB_UDFWR(0x1); while (1) { if (current_cnt > RETRYCOUNT) return -Y_FAILURE; MAC_VLANTR_OB_UDFRD(op_busy); if (op_busy == 0) break; current_cnt++; mdelay(1); } MAC_VLANTR_OFS_UDFWR(vlan_reg_offset); MAC_VLANTR_CT_UDFWR(0x1); MAC_VLANTR_OB_UDFWR(0x1); while (1) { if (current_cnt > RETRYCOUNT) return -Y_FAILURE; MAC_VLANTR_OB_UDFRD(op_busy); if (op_busy == 0) break; current_cnt++; mdelay(1); } MAC_VLAN_TAG_DATA_RGRD(reg_val); EMACDBG("Read from MAC_VLAN_TAG_DATA reg (0x%p) for offset %d = %#x\n", MAC_VLAN_TAG_DATA_RGOFFADDR, vlan_reg_offset, reg_val); EMACDBG("Exit\n"); return Y_SUCCESS; } /*! * \brief This sequence is used to enable/disable VLAN filtering and * also selects VLAN filtering mode- perfect/hash * \param[in] filter_enb_dis * \param[in] perfect_hash * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_vlan_filtering(INT filter_enb_dis, INT perfect_hash_filtering, INT perfect_inverse_match) { MAC_MPFR_VTFE_UDFWR(filter_enb_dis); MAC_VLANTR_VTIM_UDFWR(perfect_inverse_match); MAC_VLANTR_VTHM_UDFWR(perfect_hash_filtering); /* To enable only HASH filtering then VL/VID * should be > zero. Hence we are writing 1 into VL. * It also means that MAC will always receive VLAN pkt with * VID = 1 if inverse march is not set. */ if (perfect_hash_filtering) MAC_VLANTR_VL_UDFWR(0x1); /* By default enable MAC to calculate vlan hash on * only 12-bits of received VLAN tag (ie only on * VLAN id and ignore priority and other fields) */ if (perfect_hash_filtering) MAC_VLANTR_ETV_UDFWR(0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to update the VLAN ID for perfect filtering * \param[in] vid * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_vlan_id(USHORT vid) { MAC_VLANTR_VL_UDFWR(vid); return Y_SUCCESS; } /*! * \brief This sequence is used to update the VLAN Hash Table reg * with new VLAN ID * \param[in] data * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_vlan_hash_table_reg(USHORT data) { MAC_VLANHTR_VLHT_UDFWR(data); return Y_SUCCESS; } /*! * \brief This sequence is used to get the content of VLAN Hash Table reg * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT get_vlan_hash_table_reg(void) { ULONG VARMAC_VLANHTR; MAC_VLANHTR_RGRD(VARMAC_VLANHTR); return GET_VALUE( VARMAC_VLANHTR, MAC_VLANHTR_VLHT_LPOS, MAC_VLANHTR_VLHT_HPOS); } /*! * \brief This sequence is used to update Destination Port Number for * L4(TCP/UDP) layer filtering * \param[in] filter_no * \param[in] port_no * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_l4_da_port_no(INT filter_no, USHORT port_no) { MAC_L4AR_L4DP0_UDFWR(filter_no, port_no); return Y_SUCCESS; } /*! * \brief This sequence is used to update Source Port Number for * L4(TCP/UDP) layer filtering * \param[in] filter_no * \param[in] port_no * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_l4_sa_port_no(INT filter_no, USHORT port_no) { MAC_L4AR_L4SP0_UDFWR(filter_no, port_no); return Y_SUCCESS; } /*! * \brief This sequence is used to configure L4(TCP/UDP) filters for * SA and DA Port Number matching * \param[in] filter_no * \param[in] tcp_udp_match * \param[in] src_dst_port_match * \param[in] perfect_inverse_match * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_l4_filters(INT filter_no, INT enb_dis, INT tcp_udp_match, INT src_dst_port_match, INT perfect_inverse_match) { MAC_L3L4CR_L4PEN0_UDFWR(filter_no, tcp_udp_match); if (src_dst_port_match == 0) { if (enb_dis == 1) { /* Enable L4 filters for SOURCE Port No matching */ MAC_L3L4CR_L4SPM0_UDFWR(filter_no, 0x1); MAC_L3L4CR_L4SPIM0_UDFWR( filter_no, perfect_inverse_match); } else { /* Disable L4 filters for SOURCE Port No matching */ MAC_L3L4CR_L4SPM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L4SPIM0_UDFWR(filter_no, 0x0); } } else { if (enb_dis == 1) { /* Enable L4 filters for DESTINATION port No matching */ MAC_L3L4CR_L4DPM0_UDFWR(filter_no, 0x1); MAC_L3L4CR_L4DPIM0_UDFWR( filter_no, perfect_inverse_match); } else { /* Disable L4 filters for DESTINATION port * No matching */ MAC_L3L4CR_L4DPM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L4DPIM0_UDFWR(filter_no, 0x0); } } return Y_SUCCESS; } /*! * \brief This sequence is used to update IPv6 source/destination * Address for L3 layer filtering * \param[in] filter_no * \param[in] addr * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_ip6_addr(INT filter_no, USHORT addr[]) { /* update Bits[31:0] of 128-bit IP addr */ MAC_L3A0R_RGWR(filter_no, (addr[7] | (addr[6] << 16))); /* update Bits[63:32] of 128-bit IP addr */ MAC_L3A1R_RGWR(filter_no, (addr[5] | (addr[4] << 16))); /* update Bits[95:64] of 128-bit IP addr */ MAC_L3A2R_RGWR(filter_no, (addr[3] | (addr[2] << 16))); /* update Bits[127:96] of 128-bit IP addr */ MAC_L3A3R_RGWR(filter_no, (addr[1] | (addr[0] << 16))); return Y_SUCCESS; } /*! * \brief This sequence is used to update IPv4 destination * Address for L3 layer filtering * \param[in] filter_no * \param[in] addr * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_ip4_addr1(INT filter_no, UCHAR addr[]) { MAC_L3A1R_RGWR( filter_no, (addr[3] | (addr[2] << 8) | (addr[1] << 16) | (addr[0] << 24))); return Y_SUCCESS; } /*! * \brief This sequence is used to update IPv4 source * Address for L3 layer filtering * \param[in] filter_no * \param[in] addr * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_ip4_addr0(INT filter_no, UCHAR addr[]) { MAC_L3A0R_RGWR( filter_no, (addr[3] | (addr[2] << 8) | (addr[1] << 16) | (addr[0] << 24))); return Y_SUCCESS; } /*! * \brief This sequence is used to configure L3(IPv4/IPv6) filters * for SA/DA Address matching * \param[in] filter_no * \param[in] ipv4_ipv6_match * \param[in] src_dst_addr_match * \param[in] perfect_inverse_match * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_l3_filters(INT filter_no, INT enb_dis, INT ipv4_ipv6_match, INT src_dst_addr_match, INT perfect_inverse_match) { MAC_L3L4CR_L3PEN0_UDFWR(filter_no, ipv4_ipv6_match); /* For IPv6 either SA/DA can be checked, not both */ if (ipv4_ipv6_match == 1) { if (enb_dis == 1) { if (src_dst_addr_match == 0) { /* Enable L3 filters for IPv6 SOURCE addr * matching */ MAC_L3L4CR_L3SAM0_UDFWR(filter_no, 0x1); MAC_L3L4CR_L3SAIM0_UDFWR( filter_no, perfect_inverse_match); MAC_L3L4CR_L3DAM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3DAIM0_UDFWR(filter_no, 0x0); } else { /* Enable L3 filters for IPv6 DESTINATION addr * matching */ MAC_L3L4CR_L3SAM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3SAIM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3DAM0_UDFWR(filter_no, 0x1); MAC_L3L4CR_L3DAIM0_UDFWR( filter_no, perfect_inverse_match); } } else { /* Disable L3 filters for IPv6 SOURCE/DESTINATION addr * matching */ MAC_L3L4CR_L3PEN0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3SAM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3SAIM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3DAM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3DAIM0_UDFWR(filter_no, 0x0); } } else { if (src_dst_addr_match == 0) { if (enb_dis == 1) { /* Enable L3 filters for IPv4 SOURCE addr * matching */ MAC_L3L4CR_L3SAM0_UDFWR(filter_no, 0x1); MAC_L3L4CR_L3SAIM0_UDFWR( filter_no, perfect_inverse_match); } else { /* Disable L3 filters for IPv4 SOURCE addr * matching */ MAC_L3L4CR_L3SAM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3SAIM0_UDFWR(filter_no, 0x0); } } else { if (enb_dis == 1) { /* Enable L3 filters for IPv4 DESTINATION addr * matching */ MAC_L3L4CR_L3DAM0_UDFWR(filter_no, 0x1); MAC_L3L4CR_L3DAIM0_UDFWR( filter_no, perfect_inverse_match); } else { /* Disable L3 filters for IPv4 DESTINATION addr * matching */ MAC_L3L4CR_L3DAM0_UDFWR(filter_no, 0x0); MAC_L3L4CR_L3DAIM0_UDFWR(filter_no, 0x0); } } } return Y_SUCCESS; } /*! * \brief This sequence is used to configure MAC in differnet pkt processing * modes like promiscuous, multicast, unicast, hash unicast/multicast. * \param[in] pr_mode * \param[in] huc_mode * \param[in] hmc_mode * \param[in] pm_mode * \param[in] hpf_mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_mac_pkt_filter_reg(UCHAR pr_mode, UCHAR huc_mode, UCHAR hmc_mode, UCHAR pm_mode, UCHAR hpf_mode) { ULONG VARMAC_MPFR; /* configure device in differnet modes */ /* promiscuous, hash unicast, hash multicast, */ /* all multicast and perfect/hash filtering mode. */ MAC_MPFR_RGRD(VARMAC_MPFR); VARMAC_MPFR = VARMAC_MPFR & (ULONG)(0x803103e8); VARMAC_MPFR = VARMAC_MPFR | ((pr_mode) << 0) | ((huc_mode) << 1) | ((hmc_mode) << 2) | ((pm_mode) << 4) | ((hpf_mode) << 10); MAC_MPFR_RGWR(VARMAC_MPFR); return Y_SUCCESS; } /*! * \brief This sequence is used to enable/disable L3 and L4 filtering * \param[in] filter_enb_dis * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_l3_l4_filter_enable(INT filter_enb_dis) { MAC_MPFR_IPFE_UDFWR(filter_enb_dis); return Y_SUCCESS; } /*! * \brief This sequence is used to select perfect/inverse matching for L2 DA * \param[in] perfect_inverse_match * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_l2_da_perfect_inverse_match(INT perfect_inverse_match) { MAC_MPFR_DAIF_UDFWR(perfect_inverse_match); return Y_SUCCESS; } /*! * \brief This sequence is used to update the MAC address in last 96 MAC * address Low and High register(32-127) for L2 layer filtering * \param[in] idx * \param[in] addr * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_mac_addr32_127_low_high_reg(INT idx, UCHAR addr[]) { MAC_MA32_127LR_RGWR( idx, (addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24))); MAC_MA32_127HR_ADDRHI_UDFWR(idx, (addr[4] | (addr[5] << 8))); MAC_MA32_127HR_AE_UDFWR(idx, 0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to update the MAC address in first 31 MAC * address Low and High register(1-31) for L2 layer filtering * \param[in] idx * \param[in] addr * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_mac_addr1_31_low_high_reg(INT idx, UCHAR addr[]) { MAC_MA1_31LR_RGWR( idx, (addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24))); MAC_MA1_31HR_ADDRHI_UDFWR(idx, (addr[4] | (addr[5] << 8))); MAC_MA1_31HR_AE_UDFWR(idx, 0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to configure hash table register for * hash address filtering * \param[in] idx * \param[in] data * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT update_hash_table_reg(INT idx, UINT data) { MAC_HTR_RGWR(idx, data); return Y_SUCCESS; } /*! * \brief This sequence is used check whether Tx drop status in the * MTL is enabled or not, returns 1 if it is enabled and 0 if * it is disabled. * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT drop_tx_status_enabled(void) { ULONG VARMTL_OMR; MTL_OMR_RGRD(VARMTL_OMR); return GET_VALUE(VARMTL_OMR, MTL_OMR_DTXSTS_LPOS, MTL_OMR_DTXSTS_HPOS); } /*! * \brief This sequence is used configure MAC SSIR * \param[in] ptp_clock * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_sub_second_increment(ULONG ptp_clock) { ULONG VARMAC_TCR; double ss_inc = 0; double sns_inc = 0; MAC_TCR_RGRD(VARMAC_TCR); /* convert the PTP_CLOCK to nano second */ /* formula is : ((1/ptp_clock) * 1000000000) */ /* where, ptp_clock = 50MHz if FINE correction */ /* and ptp_clock = DWC_ETH_QOS_SYSCLOCK if COARSE correction */ if (GET_VALUE(VARMAC_TCR, MAC_TCR_TSCFUPDT_LPOS, MAC_TCR_TSCFUPDT_HPOS) == 1) { EMACDBG("Using PTP clock %ld MHz\n", ptp_clock); ss_inc = (double)1000000000.0 / (double)ptp_clock; } else { EMACDBG("Using SYSCLOCK for coarse correction\n"); ss_inc = (double)1000000000.0 / (double)DWC_ETH_QOS_SYSCLOCK; } /* 0.465ns accuracy */ if (GET_VALUE( VARMAC_TCR, MAC_TCR_TSCTRLSSR_LPOS, MAC_TCR_TSCTRLSSR_HPOS) == 0) { EMACDBG("using 0.465 ns accuracy"); ss_inc /= 0.465; } sns_inc = ss_inc - (int)ss_inc; // take remainder sns_inc *= 256.0; // sns_inc needs to be multiplied by 2^8, per spec. sns_inc += 0.5; // round to nearest int value. MAC_SSIR_SSINC_UDFWR((int)ss_inc); MAC_SSIR_SNSINC_UDFWR((int)sns_inc); EMACDBG("ss_inc = %d, sns_inc = %d\n", (int)ss_inc, (int)sns_inc); return Y_SUCCESS; } /*! * \brief * \param[in] * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_default_addend(struct DWC_ETH_QOS_prv_data *pdata, ULONG ptp_clock) { struct hw_if_struct *hw_if = &pdata->hw_if; u64 temp; /* formula is : * addend = 2^32/freq_div_ratio; * * where, freq_div_ratio = DWC_ETH_QOS_SYSCLOCK/50MHz * * hence, addend = ((2^32) * 50MHz)/DWC_ETH_QOS_SYSCLOCK; * * NOTE: DWC_ETH_QOS_SYSCLOCK should be >= 50MHz to * achive 20ns accuracy. * * 2^x * y == (y << x), hence * 2^32 * 50000000 ==> (50000000 << 32) */ if (ptp_clock == DWC_ETH_QOS_SYSCLOCK) { // If PTP_CLOCK == SYS_CLOCK, best we can do is 2^32 - 1 pdata->default_addend = 0xFFFFFFFF; } else { temp = (u64)((u64)ptp_clock << 32); pdata->default_addend = div_u64(temp, DWC_ETH_QOS_SYSCLOCK); } hw_if->config_addend(pdata->default_addend); EMACDBG("PPS: PTPCLK_Config: freq=%dHz, addend_reg=0x%x\n", ptp_clock, (unsigned int)pdata->default_addend); return Y_SUCCESS; } /*! * \brief This sequence is used get 64-bit system time in nano sec * \return (unsigned long long) on success * \retval ns */ static ULONG_LONG get_systime(void) { ULONG_LONG ns; ULONG varmac_stnsr; ULONG varmac_stsr; MAC_STNSR_RGRD(varmac_stnsr); ns = GET_VALUE(varmac_stnsr, MAC_STNSR_TSSS_LPOS, MAC_STNSR_TSSS_HPOS); /* convert sec/high time value to nanosecond */ MAC_STSR_RGRD(varmac_stsr); ns = ns + (varmac_stsr * 1000000000ull); return ns; } /*! * \brief This sequence is used to adjust/update the system time * \param[in] sec * \param[in] nsec * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT adjust_systime(UINT sec, UINT nsec, INT add_sub, bool one_nsec_accuracy) { ULONG RETRYCOUNT = 100000; ULONG vy_count; volatile ULONG VARMAC_TCR; /* wait for previous(if any) time adjust/update to complete. */ /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; MAC_TCR_RGRD(VARMAC_TCR); if (GET_VALUE( VARMAC_TCR, MAC_TCR_TSUPDT_LPOS, MAC_TCR_TSUPDT_HPOS) == 0) break; vy_count++; mdelay(1); } if (add_sub) { /* If the new sec value needs to be subtracted with * the system time, then MAC_STSUR reg should be * programmed with (2^32 – ) */ sec = (0x100000000ull - sec); /* 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^31 - if MAC_TCR.TSCTRLSSR is reset) */ if (one_nsec_accuracy) nsec = (0x3B9ACA00 - nsec); else nsec = (0x80000000 - nsec); } MAC_STSUR_RGWR(sec); MAC_STNSUR_TSSS_UDFWR(nsec); MAC_STNSUR_ADDSUB_UDFWR(add_sub); /* issue command to initialize system time with the value */ /* specified in MAC_STSUR and MAC_STNSUR. */ MAC_TCR_TSUPDT_UDFWR(0x1); /* wait for present time initialize to complete. */ /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; MAC_TCR_RGRD(VARMAC_TCR); if (GET_VALUE( VARMAC_TCR, MAC_TCR_TSUPDT_LPOS, MAC_TCR_TSUPDT_HPOS) == 0) break; vy_count++; mdelay(1); } return Y_SUCCESS; } /*! * \brief This sequence is used to adjust the ptp operating frequency. * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_addend(UINT data) { ULONG RETRYCOUNT = 100000; ULONG vy_count; volatile ULONG VARMAC_TCR; /* wait for previous(if any) added update to complete. */ /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; MAC_TCR_RGRD(VARMAC_TCR); if (GET_VALUE( VARMAC_TCR, MAC_TCR_TSADDREG_LPOS, MAC_TCR_TSADDREG_HPOS) == 0) break; vy_count++; mdelay(1); } MAC_TAR_RGWR(data); /* issue command to update the added value */ MAC_TCR_TSADDREG_UDFWR(0x1); /* wait for present added update to complete. */ /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; MAC_TCR_RGRD(VARMAC_TCR); if (GET_VALUE( VARMAC_TCR, MAC_TCR_TSADDREG_LPOS, MAC_TCR_TSADDREG_HPOS) == 0) break; vy_count++; mdelay(1); } return Y_SUCCESS; } /*! * \brief This sequence is used to initialize the system time * \param[in] sec * \param[in] nsec * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT init_systime(UINT sec, UINT nsec) { ULONG RETRYCOUNT = 100000; ULONG vy_count; volatile ULONG VARMAC_TCR; /* wait for previous(if any) time initialize to complete. */ /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; MAC_TCR_RGRD(VARMAC_TCR); if (GET_VALUE( VARMAC_TCR, MAC_TCR_TSINIT_LPOS, MAC_TCR_TSINIT_HPOS) == 0) break; vy_count++; mdelay(1); } MAC_STSUR_RGWR(sec); MAC_STNSUR_RGWR(nsec); /* issue command to initialize system time with the value */ /* specified in MAC_STSUR and MAC_STNSUR. */ MAC_TCR_TSINIT_UDFWR(0x1); /* wait for present time initialize to complete. */ /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; MAC_TCR_RGRD(VARMAC_TCR); if (GET_VALUE( VARMAC_TCR, MAC_TCR_TSINIT_LPOS, MAC_TCR_TSINIT_HPOS) == 0) break; vy_count++; mdelay(1); } return Y_SUCCESS; } /*! * \brief This sequence is used to enable HW time stamping * and receive frames * \param[in] count * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_hw_time_stamping(UINT config_val) { MAC_TCR_RGWR(config_val); return Y_SUCCESS; } /*! * \brief This sequence is used get the 64-bit of the timestamp * captured by the device for the corresponding received packet * in nanosecond. * \param[in] rxdesc * \return (unsigned long long) on success * \retval ns */ static ULONG_LONG get_rx_tstamp(t_RX_CONTEXT_DESC *rxdesc) { ULONG_LONG ns; ULONG varrdes1; RX_CONTEXT_DESC_RDES0_ML_RD(rxdesc->RDES0, ns); RX_CONTEXT_DESC_RDES1_ML_RD(rxdesc->RDES1, varrdes1); ns = ns + (varrdes1 * 1000000000ull); return ns; } /*! * \brief This sequence is used to check whether the captured timestamp * for the corresponding received packet is valid or not. * Returns 0 if no context descriptor * Returns 1 if timestamp is valid * Returns 2 if time stamp is corrupted * \param[in] rxdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static UINT get_rx_tstamp_status(t_RX_CONTEXT_DESC *rxdesc) { UINT VAROWN; UINT VARCTXT; UINT VARRDES0; UINT VARRDES1; /* check for own bit and CTXT bit */ RX_CONTEXT_DESC_RDES3_OWN_MLF_RD(rxdesc->RDES3, VAROWN); RX_CONTEXT_DESC_RDES3_CTXT_MLF_RD(rxdesc->RDES3, VARCTXT); if ((VAROWN == 0) && (VARCTXT == 0x1)) { RX_CONTEXT_DESC_RDES0_ML_RD(rxdesc->RDES0, VARRDES0); RX_CONTEXT_DESC_RDES1_ML_RD(rxdesc->RDES1, VARRDES1); if ((VARRDES0 == 0xffffffff) && (VARRDES1 == 0xffffffff)) /* time stamp is corrupted */ return 2; /* time stamp is valid */ return 1; } /* no CONTEX desc to hold time stamp value */ return 0; } /*! * \brief This sequence is used to check whether the timestamp value * is available in a context descriptor or not. Returns 1 if timestamp * is available else returns 0 * \param[in] rxdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static UINT rx_tstamp_available(t_RX_NORMAL_DESC *rxdesc) { UINT VARRS1V; UINT VARTSA; RX_NORMAL_DESC_RDES3_RS1V_MLF_RD(rxdesc->RDES3, VARRS1V); if (VARRS1V == 1) { RX_NORMAL_DESC_RDES1_TSA_MLF_RD(rxdesc->RDES1, VARTSA); return VARTSA; } else { return 0; } } /*! * \brief This sequence is used get the least 64-bit of the timestamp * captured by the device for the corresponding transmit packet in nanosecond * \return (unsigned long long) on success * \retval ns */ static ULONG_LONG get_tx_tstamp_via_reg(void) { ULONG_LONG ns; ULONG varmac_ttn; MAC_TTSN_TXTSSTSLO_UDFRD(ns); MAC_TTN_TXTSSTSHI_UDFRD(varmac_ttn); ns = ns + (varmac_ttn * 1000000000ull); return ns; } /*! * \brief This sequence is used to check whether a timestamp has been * captured for the corresponding transmit packet. Returns 1 if * timestamp is taken else returns 0 * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static UINT get_tx_tstamp_status_via_reg(void) { ULONG VARMAC_TCR; ULONG VARMAC_TTSN; /* device is configured to overwrite the timesatmp of * earlier packet if driver has not yet read it. */ MAC_TCR_RGRD(VARMAC_TCR); if (GET_VALUE( VARMAC_TCR, MAC_TCR_TXTSSTSM_LPOS, MAC_TCR_TXTSSTSM_HPOS) == 1) /* nothing to do */ return 0; /* timestamp of the current pkt is ignored or not captured */ MAC_TTSN_RGRD(VARMAC_TTSN); VARMAC_TTSN = GET_VALUE( VARMAC_TTSN, MAC_TTSN_TXTSSTSMIS_LPOS, MAC_TTSN_TXTSSTSMIS_HPOS); if (VARMAC_TTSN == 1) return 0; else return 1; } /*! * \brief This sequence is used get the 64-bit of the timestamp captured * by the device for the corresponding transmit packet in nanosecond. * \param[in] txdesc * \return (unsigned long long) on success * \retval ns */ static ULONG_LONG get_tx_tstamp(t_TX_NORMAL_DESC *txdesc) { ULONG_LONG ns; ULONG vartdes1; TX_NORMAL_DESC_TDES0_ML_RD(txdesc->TDES0, ns); TX_NORMAL_DESC_TDES1_ML_RD(txdesc->TDES1, vartdes1); ns = ns + (vartdes1 * 1000000000ull); return ns; } /*! * \brief This sequence is used to check whether a timestamp has been * captured for the corresponding transmit packet. Returns 1 if * timestamp is taken else returns 0 * \param[in] txdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static UINT get_tx_tstamp_status(t_TX_NORMAL_DESC *txdesc) { UINT VARTDES3; TX_NORMAL_DESC_TDES3_ML_RD(txdesc->TDES3, VARTDES3); return (VARTDES3 & 0x20000); } /*! * \brief This sequence is used to enable/disable split header feature * \param[in] QINX * \param[in] sph_en * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_split_header_mode(UINT QINX, USHORT sph_en) { DMA_CR_SPH_UDFWR(QINX, sph_en); return Y_SUCCESS; } /*! * \brief This sequence is used to configure header size in case * of split header feature * \param[in] header_size * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_header_size(USHORT header_size) { if (header_size == 64) MAC_MECR_HDSMS_UDFWR(0); else if (header_size == 128) MAC_MECR_HDSMS_UDFWR(0x1); else if (header_size == 256) MAC_MECR_HDSMS_UDFWR(0x2); else if (header_size == 512) MAC_MECR_HDSMS_UDFWR(0x3); else MAC_MECR_HDSMS_UDFWR(0x4); return Y_SUCCESS; } /*! * \brief This sequence is used to set tx queue operating mode for Queue[0 - 7] * \param[in] QINX * \param[in] q_mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_tx_queue_operating_mode(UINT QINX, UINT q_mode) { MTL_QTOMR_TXQEN_UDFWR(QINX, q_mode); return Y_SUCCESS; } /*! * \brief This sequence is used to select Tx Scheduling * Algorithm for AVB feature for Queue[1 - 7] * \param[in] avb_algo * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_avb_algorithm(UINT QINX, UCHAR avb_algo) { MTL_QECR_AVALG_UDFWR(QINX, avb_algo); return Y_SUCCESS; } /*! * \brief This sequence is used to configure credit-control for Queue[1 - 7] * \param[in] QINX * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_credit_control(UINT QINX, UINT cc) { MTL_QECR_CC_UDFWR(QINX, cc); return Y_SUCCESS; } /*! * \brief This sequence is used to configure send slope credit value * required for the credit-based shaper alogorithm for Queue[1 - 7] * \param[in] QINX * \param[in] sendSlope * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_send_slope(UINT QINX, UINT SENDSLOPE) { MTL_QSSCR_SSC_UDFWR(QINX, SENDSLOPE); return Y_SUCCESS; } /*! * \brief This sequence is used to configure idle slope credit value * required for the credit-based shaper alogorithm for Queue[1 - 7] * \param[in] QINX * \param[in] idleSlope * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_idle_slope(UINT QINX, UINT IDLESLOPE) { MTL_QW_ISCQW_UDFWR(QINX, IDLESLOPE); return Y_SUCCESS; } /*! * \brief This sequence is used to configure low credit value * required for the credit-based shaper alogorithm for Queue[1 - 7] * \param[in] QINX * \param[in] lowCredit * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_low_credit(UINT QINX, UINT LOWCREDIT) { INT LOWCREDIT_NEG = LOWCREDIT; pr_crit( "lowCreidt = %08x lowCredit_neg:%08x\n", LOWCREDIT, LOWCREDIT_NEG); MTL_QLCR_LC_UDFWR(QINX, LOWCREDIT_NEG); return Y_SUCCESS; } /*! * \brief This sequence is used to enable/disable slot number check When set, * this bit enables the checking of the slot number programmed in the TX * descriptor with the current reference given in the RSN field. The DMA fetches * the data from the corresponding buffer only when the slot number is: equal to * the reference slot number or ahead of the reference slot number by one. * \param[in] QINX * \param[in] slot_check * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_slot_num_check(UINT QINX, UCHAR slot_check) { DMA_SFCSR_ESC_UDFWR(QINX, slot_check); return Y_SUCCESS; } /*! * \brief This sequence is used to enable/disable advance slot check When set, * this bit enables the DAM to fetch the data from the buffer when the slot * number programmed in TX descriptor is equal to the reference slot number * given in RSN field or ahead of the reference number by upto two slots * \param[in] QINX * \param[in] adv_slot_check * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_advance_slot_num_check(UINT QINX, UCHAR adv_slot_check) { DMA_SFCSR_ASC_UDFWR(QINX, adv_slot_check); return Y_SUCCESS; } /*! * \brief This sequence is used to configure high credit value required * for the credit-based shaper alogorithm for Queue[1 - 7] * \param[in] QINX * \param[in] hiCredit * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_high_credit(UINT QINX, UINT HICREDIT) { MTL_QHCR_HC_UDFWR(QINX, HICREDIT); return Y_SUCCESS; } /*! * \brief This sequence is used to set weights for DCB feature for Queue[0 - 7] * \param[in] QINX * \param[in] q_weight * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_dcb_queue_weight(UINT QINX, UINT q_weight) { MTL_QW_ISCQW_UDFWR(QINX, q_weight); return Y_SUCCESS; } /*! * \brief This sequence is used to select Tx Scheduling * Algorithm for DCB feature * \param[in] dcb_algo * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_dcb_algorithm(UCHAR dcb_algo) { MTL_OMR_SCHALG_UDFWR(dcb_algo); return Y_SUCCESS; } /*! * \brief This sequence is used to get Tx queue count * \param[in] count * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ UCHAR get_tx_queue_count(void) { UCHAR count; ULONG VARMAC_HFR2; MAC_HFR2_RGRD(VARMAC_HFR2); count = GET_VALUE( VARMAC_HFR2, MAC_HFR2_TXQCNT_LPOS, MAC_HFR2_TXQCNT_HPOS); return (count + 1); } /*! * \brief This sequence is used to get Rx queue count * \param[in] count * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ UCHAR get_rx_queue_count(void) { UCHAR count; ULONG VARMAC_HFR2; MAC_HFR2_RGRD(VARMAC_HFR2); count = GET_VALUE( VARMAC_HFR2, MAC_HFR2_RXQCNT_LPOS, MAC_HFR2_RXQCNT_HPOS); return (count + 1); } /*! * \brief This sequence is used to disables all Tx/Rx MMC interrupts * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT disable_mmc_interrupts(void) { /* disable all TX interrupts */ MMC_INTR_MASK_TX_RGWR(0xffffffff); /* disable all RX interrupts */ MMC_INTR_MASK_RX_RGWR(0xffffffff); /* Disable MMC Rx Interrupts for IPC */ MMC_IPC_INTR_MASK_RX_RGWR(0xffffffff); return Y_SUCCESS; } /*! * \brief This sequence is used to configure MMC counters * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_mmc_counters(void) { ULONG VARMMC_CNTRL; /* set COUNTER RESET */ /* set RESET ON READ */ /* set COUNTER PRESET */ /* set FULL_HALF PRESET */ MMC_CNTRL_RGRD(VARMMC_CNTRL); VARMMC_CNTRL = VARMMC_CNTRL & (ULONG)(0x10a); VARMMC_CNTRL = VARMMC_CNTRL | ((0x1) << 0) | ((0x1) << 2) | ((0x1) << 4) | ((0x1) << 5); MMC_CNTRL_RGWR(VARMMC_CNTRL); return Y_SUCCESS; } /*! * \brief This sequence is used to disable given DMA channel rx interrupts * \param[in] QINX * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT disable_rx_interrupt(UINT QINX) { DMA_IER_RBUE_UDFWR(QINX, 0); DMA_IER_RIE_UDFWR(QINX, 0); return Y_SUCCESS; } /*! * \brief This sequence is used to enable given DMA channel rx interrupts * \param[in] QINX * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT enable_rx_interrupt(UINT QINX) { DMA_IER_RBUE_UDFWR(QINX, 0x1); DMA_IER_RIE_UDFWR(QINX, 0x1); return Y_SUCCESS; } static VOID configure_sa_via_reg(u32 cmd) { MAC_MCR_SARC_UDFWR(cmd); } static VOID configure_mac_addr1_reg(UCHAR *mac_addr) { MAC_MA1HR_RGWR(((mac_addr[5] << 8) | (mac_addr[4]))); MAC_MA1LR_RGWR(((mac_addr[3] << 24) | (mac_addr[2] << 16) | (mac_addr[1] << 8) | (mac_addr[0]))); } static VOID configure_mac_addr0_reg(UCHAR *mac_addr) { MAC_MA0HR_RGWR(((mac_addr[5] << 8) | (mac_addr[4]))); MAC_MA0LR_RGWR(((mac_addr[3] << 24) | (mac_addr[2] << 16) | (mac_addr[1] << 8) | (mac_addr[0]))); } static VOID config_rx_outer_vlan_stripping(u32 cmd) { MAC_VLANTR_EVLS_UDFWR(cmd); } static VOID config_rx_inner_vlan_stripping(u32 cmd) { MAC_VLANTR_EIVLS_UDFWR(cmd); } static VOID config_ptpoffload_engine(UINT pto_cr, UINT mc_uc) { MAC_PTO_CR_RGWR(pto_cr); MAC_TCR_TSENMACADDR_UDFWR(mc_uc); } static VOID configure_reg_vlan_control( struct DWC_ETH_QOS_tx_wrapper_descriptor *desc_data) { USHORT vlan_id = desc_data->vlan_tag_id; UINT vlan_control = desc_data->tx_vlan_tag_ctrl; MAC_VLANTIRR_RGWR(((1 << 18) | (vlan_control << 16) | (vlan_id << 0))); } static VOID configure_desc_vlan_control(struct DWC_ETH_QOS_prv_data *pdata) { MAC_VLANTIRR_RGWR((1 << 20)); } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT configure_mac_for_vlan_pkt(void) { /* Do not strip VLAN Tag*/ MAC_VLANTR_EVLS_UDFWR(0x0); /* Enable operation on the outer VLAN Tag, if present */ MAC_VLANTR_ERIVLT_UDFWR(0); /* Disable double VLAN Tag processing on TX and RX */ MAC_VLANTR_EDVLP_UDFWR(0); /* Enable VLAN Tag in RX Status. */ MAC_VLANTR_EVLRXS_UDFWR(0x1); /* Disable VLAN Type Check */ MAC_VLANTR_DOVLTC_UDFWR(0x1); /* configure MAC to get VLAN Tag to be inserted/replaced from */ /* TX descriptor(context desc) */ MAC_VLANTIRR_VLTI_UDFWR(0x1); /* insert/replace C_VLAN in 13th ans 14th bytes of transmitted frames */ MAC_VLANTIRR_CSVL_UDFWR(0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_pblx8(UINT QINX, UINT val) { DMA_CR_PBLX8_UDFWR(QINX, val); return Y_SUCCESS; } /*! * \return INT * \retval programmed Tx PBL value */ static INT get_tx_pbl_val(UINT QINX) { UINT tx_pbl; DMA_TCR_PBL_UDFRD(QINX, tx_pbl); return tx_pbl; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_tx_pbl_val(UINT QINX, UINT tx_pbl) { DMA_TCR_PBL_UDFWR(QINX, tx_pbl); return Y_SUCCESS; } /*! * \return INT * \retval programmed Rx PBL value */ static INT get_rx_pbl_val(UINT QINX) { UINT rx_pbl; DMA_RCR_PBL_UDFRD(QINX, rx_pbl); return rx_pbl; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_rx_pbl_val(UINT QINX, UINT rx_pbl) { DMA_RCR_PBL_UDFWR(QINX, rx_pbl); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_axi_rorl_val(UINT axi_rorl) { DMA_SBUS_RD_OSR_LMT_UDFWR(axi_rorl); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_axi_worl_val(UINT axi_worl) { DMA_SBUS_WR_OSR_LMT_UDFWR(axi_worl); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_axi_pbl_val(UINT axi_pbl) { UINT VARDMA_SBUS; DMA_SBUS_RGRD(VARDMA_SBUS); VARDMA_SBUS &= ~DMA_SBUS_AXI_PBL_MASK; VARDMA_SBUS |= axi_pbl; DMA_SBUS_RGWR(VARDMA_SBUS); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_incr_incrx_mode(UINT val) { DMA_SBUS_UNDEF_FB_UDFWR(val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_osf_mode(UINT QINX, UINT val) { DMA_TCR_OSP_UDFWR(QINX, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_rsf_mode(UINT QINX, UINT val) { /* if (QINX == 0) { */ /* MTL_Q0ROMR_RSF_UdfWr(val); */ /* } */ /* else { */ MTL_QROMR_RSF_UDFWR(QINX, val); /* } */ return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_tsf_mode(UINT QINX, UINT val) { MTL_QTOMR_TSF_UDFWR(QINX, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_rx_threshold(UINT QINX, UINT val) { MTL_QROMR_RTC_UDFWR(QINX, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_tx_threshold(UINT QINX, UINT val) { MTL_QTOMR_TTC_UDFWR(QINX, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT config_rx_watchdog_timer(UINT QINX, u32 riwt) { DMA_RIWTR_RWT_UDFWR(QINX, riwt); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT enable_magic_pmt_operation(void) { MAC_PMTCSR_MGKPKTEN_UDFWR(0x1); MAC_PMTCSR_PWRDWN_UDFWR(0x1); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT disable_magic_pmt_operation(void) { UINT VARPMTCSR_PWRDWN; MAC_PMTCSR_MGKPKTEN_UDFWR(0x0); MAC_PMTCSR_PWRDWN_UDFRD(VARPMTCSR_PWRDWN); if (VARPMTCSR_PWRDWN == 0x1) MAC_PMTCSR_PWRDWN_UDFWR(0x0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT enable_remote_pmt_operation(void) { MAC_PMTCSR_RWKPKTEN_UDFWR(0x1); MAC_PMTCSR_PWRDWN_UDFWR(0x1); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT disable_remote_pmt_operation(void) { UINT VARPMTCSR_PWRDWN; MAC_PMTCSR_RWKPKTEN_UDFWR(0x0); MAC_PMTCSR_PWRDWN_UDFRD(VARPMTCSR_PWRDWN); if (VARPMTCSR_PWRDWN == 0x1) MAC_PMTCSR_PWRDWN_UDFWR(0x0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT configure_rwk_filter_registers(UINT *value, UINT count) { UINT i; MAC_PMTCSR_RWKFILTRST_UDFWR(1); for (i = 0; i < count; i++) MAC_RWPFFR_RGWR(value[i]); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT disable_tx_flow_ctrl(UINT QINX) { MAC_QTFCR_TFE_UDFWR(QINX, 0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT enable_tx_flow_ctrl(UINT QINX) { MAC_QTFCR_TFE_UDFWR(QINX, 1); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT disable_rx_flow_ctrl(void) { MAC_RFCR_RFE_UDFWR(0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT enable_rx_flow_ctrl(void) { MAC_RFCR_RFE_UDFWR(0x1); return Y_SUCCESS; } /*! * \param[in] QINX * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT stop_dma_rx(UINT QINX) { ULONG RETRYCOUNT = 10; ULONG vy_count; volatile ULONG VARDMA_DSR0; volatile ULONG VARDMA_DSR1; volatile ULONG VARDMA_DSR2; /* issue Rx dma stop command */ DMA_RCR_ST_UDFWR(QINX, 0); /* wait for Rx DMA to stop, ie wait till Rx DMA * goes in either Running or Suspend state. */ if (QINX == 0) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Rx Channel 0 stop failed, DSR0 = %#lx\n", VARDMA_DSR0); return -Y_FAILURE; } DMA_DSR0_RGRD(VARDMA_DSR0); if ((GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS0_LPOS, DMA_DSR0_RPS0_HPOS) == 0x3) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS0_LPOS, DMA_DSR0_RPS0_HPOS) == 0x4) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS0_LPOS, DMA_DSR0_RPS0_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 1) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Rx Channel 1 stop failed, DSR0 = %#lx\n", VARDMA_DSR0); return -Y_FAILURE; } DMA_DSR0_RGRD(VARDMA_DSR0); if ((GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS1_LPOS, DMA_DSR0_RPS1_HPOS) == 0x3) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS1_LPOS, DMA_DSR0_RPS1_HPOS) == 0x4) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS1_LPOS, DMA_DSR0_RPS1_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 2) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Rx Channel 2 stop failed, DSR0 = %#lx\n", VARDMA_DSR0); return -Y_FAILURE; } DMA_DSR0_RGRD(VARDMA_DSR0); if ((GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS2_LPOS, DMA_DSR0_RPS2_HPOS) == 0x3) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS2_LPOS, DMA_DSR0_RPS2_HPOS) == 0x4) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_RPS2_LPOS, DMA_DSR0_RPS2_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 3) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Rx Channel 3 stop failed, DSR0 = %#lx\n", VARDMA_DSR1); return -Y_FAILURE; } DMA_DSR1_RGRD(VARDMA_DSR1); if ((GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS3_LPOS, DMA_DSR1_RPS3_HPOS) == 0x3) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS3_LPOS, DMA_DSR1_RPS3_HPOS) == 0x4) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS3_LPOS, DMA_DSR1_RPS3_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 4) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Rx Channel 4 stop failed, DSR0 = %#lx\n", VARDMA_DSR1); return -Y_FAILURE; } DMA_DSR1_RGRD(VARDMA_DSR1); if ((GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS4_LPOS, DMA_DSR1_RPS4_HPOS) == 0x3) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS4_LPOS, DMA_DSR1_RPS4_HPOS) == 0x4) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS4_LPOS, DMA_DSR1_RPS4_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 5) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Rx Channel 5 stop failed, DSR0 = %#lx\n", VARDMA_DSR1); return -Y_FAILURE; } DMA_DSR1_RGRD(VARDMA_DSR1); if ((GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS5_LPOS, DMA_DSR1_RPS5_HPOS) == 0x3) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS5_LPOS, DMA_DSR1_RPS5_HPOS) == 0x4) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS5_LPOS, DMA_DSR1_RPS5_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 6) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Rx Channel 6 stop failed, DSR0 = %#lx\n", VARDMA_DSR1); return -Y_FAILURE; } DMA_DSR1_RGRD(VARDMA_DSR1); if ((GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS6_LPOS, DMA_DSR1_RPS6_HPOS) == 0x3) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS6_LPOS, DMA_DSR1_RPS6_HPOS) == 0x4) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_RPS6_LPOS, DMA_DSR1_RPS6_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 7) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Rx Channel 7 stop failed, DSR0 = %#lx\n", VARDMA_DSR2); return -Y_FAILURE; } DMA_DSR2_RGRD(VARDMA_DSR2); if ((GET_VALUE( VARDMA_DSR2, DMA_DSR2_RPS7_LPOS, DMA_DSR2_RPS7_HPOS) == 0x3) || (GET_VALUE( VARDMA_DSR2, DMA_DSR2_RPS7_LPOS, DMA_DSR2_RPS7_HPOS) == 0x4) || (GET_VALUE( VARDMA_DSR2, DMA_DSR2_RPS7_LPOS, DMA_DSR2_RPS7_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } return Y_SUCCESS; } /*! * \param[in] QINX * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT start_dma_rx(UINT QINX) { DMA_RCR_ST_UDFWR(QINX, 0x1); return Y_SUCCESS; } /*! * \param[in] QINX * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT stop_dma_tx(UINT QINX) { ULONG RETRYCOUNT = 10; ULONG vy_count; volatile ULONG VARDMA_DSR0; volatile ULONG VARDMA_DSR1; volatile ULONG VARDMA_DSR2; /* issue Tx dma stop command */ DMA_TCR_ST_UDFWR(QINX, 0); /* wait for Tx DMA to stop, ie wait till Tx DMA * goes in Suspend state or stopped state. */ if (QINX == 0) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Tx Channel 0 stop failed, DSR0 = %lx\n", VARDMA_DSR0); return -Y_FAILURE; } DMA_DSR0_RGRD(VARDMA_DSR0); if ((GET_VALUE( VARDMA_DSR0, DMA_DSR0_TPS0_LPOS, DMA_DSR0_TPS0_HPOS) == 0x6) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_TPS0_LPOS, DMA_DSR0_TPS0_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 1) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Channel 1 stop failed, DSR0 = %lx\n", VARDMA_DSR0); return -Y_FAILURE; } DMA_DSR0_RGRD(VARDMA_DSR0); if ((GET_VALUE( VARDMA_DSR0, DMA_DSR0_TPS1_LPOS, DMA_DSR0_TPS1_HPOS) == 0x6) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_TPS1_LPOS, DMA_DSR0_TPS1_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 2) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Channel 2 stop failed, DSR0 = %lx\n", VARDMA_DSR0); return -Y_FAILURE; } DMA_DSR0_RGRD(VARDMA_DSR0); if ((GET_VALUE( VARDMA_DSR0, DMA_DSR0_TPS2_LPOS, DMA_DSR0_TPS2_HPOS) == 0x6) || (GET_VALUE( VARDMA_DSR0, DMA_DSR0_TPS2_LPOS, DMA_DSR0_TPS2_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 3) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Channel 3 stop failed, DSR0 = %lx\n", VARDMA_DSR1); return -Y_FAILURE; } DMA_DSR1_RGRD(VARDMA_DSR1); if ((GET_VALUE( VARDMA_DSR1, DMA_DSR1_TPS3_LPOS, DMA_DSR1_TPS3_HPOS) == 0x6) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_TPS3_LPOS, DMA_DSR1_TPS3_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 4) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Channel 4 stop failed, DSR0 = %lx\n", VARDMA_DSR1); return -Y_FAILURE; } DMA_DSR1_RGRD(VARDMA_DSR1); if ((GET_VALUE( VARDMA_DSR1, DMA_DSR1_TPS4_LPOS, DMA_DSR1_TPS4_HPOS) == 0x6) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_TPS4_LPOS, DMA_DSR1_TPS4_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 5) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Channel 5 stop failed, DSR0 = %lx\n", VARDMA_DSR1); return -Y_FAILURE; } DMA_DSR1_RGRD(VARDMA_DSR1); if ((GET_VALUE( VARDMA_DSR1, DMA_DSR1_TPS5_LPOS, DMA_DSR1_TPS5_HPOS) == 0x6) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_TPS5_LPOS, DMA_DSR1_TPS5_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 6) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Channel 6 stop failed, DSR0 = %lx\n", VARDMA_DSR1); return -Y_FAILURE; } DMA_DSR1_RGRD(VARDMA_DSR1); if ((GET_VALUE( VARDMA_DSR1, DMA_DSR1_TPS6_LPOS, DMA_DSR1_TPS6_HPOS) == 0x6) || (GET_VALUE( VARDMA_DSR1, DMA_DSR1_TPS6_LPOS, DMA_DSR1_TPS6_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } else if (QINX == 7) { /*Poll*/ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR( "ERROR: Channel 7 stop failed, DSR0 = %lx\n", VARDMA_DSR2); return -Y_FAILURE; } DMA_DSR2_RGRD(VARDMA_DSR2); if ((GET_VALUE( VARDMA_DSR2, DMA_DSR2_TPS7_LPOS, DMA_DSR2_TPS7_HPOS) == 0x6) || (GET_VALUE( VARDMA_DSR2, DMA_DSR2_TPS7_LPOS, DMA_DSR2_TPS7_HPOS) == 0x0)) break; vy_count++; mdelay(1); } } return Y_SUCCESS; } /*! * \param[in] QINX * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT start_dma_tx(UINT QINX) { DMA_TCR_ST_UDFWR(QINX, 0x1); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT stop_mac_tx_rx(void) { ULONG VARMAC_MCR; MAC_MCR_RGRD(VARMAC_MCR); VARMAC_MCR = VARMAC_MCR & (ULONG)(0xffffff7c); VARMAC_MCR = VARMAC_MCR | ((0) << 1) | ((0) << 0); MAC_MCR_RGWR(VARMAC_MCR); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT start_mac_tx_rx(void) { ULONG VARMAC_MCR; MAC_MCR_RGRD(VARMAC_MCR); VARMAC_MCR = VARMAC_MCR & (ULONG)(0xffffff7c); VARMAC_MCR = VARMAC_MCR | ((0x1) << 1) | ((0x1) << 0); MAC_MCR_RGWR(VARMAC_MCR); return Y_SUCCESS; } /*! * \brief This sequence is used to enable DMA interrupts * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT enable_tx_dma_interrupts(UINT QINX, struct DWC_ETH_QOS_prv_data *pdata) { UINT tmp; ULONG VARDMA_SR; ULONG VARDMA_IER; ULONG DMA_TX_INT_MASK = 0xFC07; ULONG DMA_TX_INT_RESET_MASK = 0xFBC0; /* clear all the interrupts which are set */ DMA_SR_RGRD(QINX, VARDMA_SR); tmp = VARDMA_SR & DMA_TX_INT_MASK; DMA_SR_RGWR(QINX, tmp); /* Enable following interrupts for Queue 0 */ /* NIE - Normal Interrupt Summary Enable */ /* AIE - Abnormal Interrupt Summary Enable */ /* FBE - Fatal Bus Error Enable */ /* TXSE - Transmit Stopped Enable */ DMA_IER_RGRD(QINX, VARDMA_IER); /* Reset all Tx interrupt bits */ VARDMA_IER = VARDMA_IER & DMA_TX_INT_RESET_MASK; VARDMA_IER = VARDMA_IER | ((0x1) << 1) | ((0x1) << 12) | ((0x1) << 14) | ((0x1) << 15); #ifndef DWC_ETH_QOS_TXPOLLING_MODE_ENABLE /* TIE - Transmit Interrupt Enable */ /* TBUE - Transmit Buffer Unavailable Enable */ if (!(pdata->ipa_enabled && QINX == IPA_DMA_TX_CH)) VARDMA_IER |= ((0x1) << 0); #endif DMA_IER_RGWR(QINX, VARDMA_IER); return Y_SUCCESS; } static INT enable_rx_dma_interrupts(UINT QINX, struct DWC_ETH_QOS_prv_data *pdata) { UINT tmp; ULONG VARDMA_SR; ULONG VARDMA_IER; ULONG DMA_RX_INT_MASK = 0xFBC0; ULONG DMA_RX_INT_RESET_MASK = 0xF407; /* clear all the interrupts which are set */ DMA_SR_RGRD(QINX, VARDMA_SR); tmp = VARDMA_SR & DMA_RX_INT_MASK; DMA_SR_RGWR(QINX, tmp); /* Enable following interrupts for Queue 0 */ /* NIE - Normal Interrupt Summary Enable */ /* AIE - Abnormal Interrupt Summary Enable */ /* FBE - Fatal Bus Error Enable */ /* RIE - Receive Interrupt Enable */ /* RBUE - Receive Buffer Unavailable Enable */ /* RSE - Receive Stopped Enable */ DMA_IER_RGRD(QINX, VARDMA_IER); /* Reset all Rx interrupt bits */ VARDMA_IER = VARDMA_IER & (ULONG)(DMA_RX_INT_RESET_MASK); VARDMA_IER = VARDMA_IER | ((0x1) << 7) | ((0x1) << 8) | ((0x1) << 14) | ((0x1) << 12) | ((0x1) << 15); /* RIE - Receive Interrupt Enable */ if (!(pdata->ipa_enabled && QINX == IPA_DMA_RX_CH)) VARDMA_IER |= ((0x1) << 6); DMA_IER_RGWR(QINX, VARDMA_IER); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the MAC registers for * GMII-1000Mbps speed * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_gmii_speed(void) { MAC_MCR_PS_UDFWR(0); MAC_MCR_FES_UDFWR(0); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the MAC registers for * MII-10Mpbs speed * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_mii_speed_10(void) { MAC_MCR_PS_UDFWR(0x1); MAC_MCR_FES_UDFWR(0); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the MAC registers for * MII-100Mpbs speed * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_mii_speed_100(void) { MAC_MCR_PS_UDFWR(0x1); MAC_MCR_FES_UDFWR(0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the MAC registers for * half duplex mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_half_duplex(void) { MAC_MCR_DM_UDFWR(0); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the MAC registers for * full duplex mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_full_duplex(void) { MAC_MCR_DM_UDFWR(0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the device in list of * multicast mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_multicast_list_mode(void) { MAC_MPFR_HMC_UDFWR(0); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the device in unicast mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_unicast_mode(void) { MAC_MPFR_HUC_UDFWR(0); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the device in all multicast mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_all_multicast_mode(void) { MAC_MPFR_PM_UDFWR(0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to configure the device in promiscuous mode * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT set_promiscuous_mode(void) { MAC_MPFR_PR_UDFWR(0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to write into phy registers * \param[in] phy_id * \param[in] phy_reg * \param[in] phy_reg_data * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT write_phy_regs(INT phy_id, INT phy_reg, INT phy_reg_data) { ULONG RETRYCOUNT = 5000; ULONG vy_count; volatile ULONG VARMAC_GMIIAR; /* wait for any previous MII read/write operation to complete */ /*Poll Until Poll Condition */ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; vy_count++; udelay(200); MAC_GMIIAR_RGRD(VARMAC_GMIIAR); if (GET_VALUE( VARMAC_GMIIAR, MAC_GMIIAR_GB_LPOS, MAC_GMIIAR_GB_HPOS) == 0) break; } /* write the data */ MAC_GMIIDR_GD_UDFWR(phy_reg_data); /* initiate the MII write operation by updating desired */ /* phy address/id (0 - 31) */ /* phy register offset */ /* CSR Clock Range (20 - 35MHz) */ /* Select write operation */ /* set busy bit */ MAC_GMIIAR_RGRD(VARMAC_GMIIAR); VARMAC_GMIIAR = VARMAC_GMIIAR & (ULONG)(0x12); VARMAC_GMIIAR = VARMAC_GMIIAR | ((phy_id) << 21) | ((phy_reg) << 16) | ((0x1) << 8) | ((0x1) << 2) | ((0x1) << 0); MAC_GMIIAR_RGWR(VARMAC_GMIIAR); /*DELAY IMPLEMENTATION USING udelay() */ udelay(10); /* wait for MII write operation to complete */ /*Poll Until Poll Condition */ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; vy_count++; udelay(200); MAC_GMIIAR_RGRD(VARMAC_GMIIAR); if (GET_VALUE( VARMAC_GMIIAR, MAC_GMIIAR_GB_LPOS, MAC_GMIIAR_GB_HPOS) == 0) break; } return Y_SUCCESS; } /*! * \brief This sequence is used to read the phy registers * \param[in] phy_id * \param[in] phy_reg * \param[out] phy_reg_data * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT read_phy_regs(INT phy_id, INT phy_reg, INT *phy_reg_data) { ULONG RETRYCOUNT = 5000; ULONG vy_count; volatile ULONG VARMAC_GMIIAR; ULONG VARMAC_GMIIDR; /* wait for any previous MII read/write operation to complete */ /*Poll Until Poll Condition */ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; vy_count++; udelay(200); MAC_GMIIAR_RGRD(VARMAC_GMIIAR); if (GET_VALUE( VARMAC_GMIIAR, MAC_GMIIAR_GB_LPOS, MAC_GMIIAR_GB_HPOS) == 0) break; } /* initiate the MII read operation by updating desired */ /* phy address/id (0 - 31) */ /* phy register offset */ /* CSR Clock Range (20 - 35MHz) */ /* Select read operation */ /* set busy bit */ MAC_GMIIAR_RGRD(VARMAC_GMIIAR); VARMAC_GMIIAR = VARMAC_GMIIAR & (ULONG)(0x12); VARMAC_GMIIAR = VARMAC_GMIIAR | ((phy_id) << 21) | ((phy_reg) << 16) | ((0x1) << 8) | ((0x3) << 2) | ((0x1) << 0); MAC_GMIIAR_RGWR(VARMAC_GMIIAR); /*DELAY IMPLEMENTATION USING udelay() */ udelay(10); /* wait for MII write operation to complete */ /*Poll Until Poll Condition */ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) return -Y_FAILURE; vy_count++; udelay(200); MAC_GMIIAR_RGRD(VARMAC_GMIIAR); if (GET_VALUE( VARMAC_GMIIAR, MAC_GMIIAR_GB_LPOS, MAC_GMIIAR_GB_HPOS) == 0) break; } /* read the data */ MAC_GMIIDR_RGRD(VARMAC_GMIIDR); *phy_reg_data = GET_VALUE( VARMAC_GMIIDR, MAC_GMIIDR_GD_LPOS, MAC_GMIIDR_GD_HPOS); return Y_SUCCESS; } /*! * \brief This sequence is used to check whether transmitted pkts have * fifo under run loss error or not, returns 1 if fifo under run error * else returns 0 * \param[in] txdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT tx_fifo_underrun(t_TX_NORMAL_DESC *txdesc) { UINT VARTDES3; /* check TDES3.UF bit */ TX_NORMAL_DESC_TDES3_ML_RD(txdesc->TDES3, VARTDES3); if ((VARTDES3 & 0x4) == 0x4) return 1; else return 0; } /*! * \brief This sequence is used to check whether transmitted pkts have * carrier loss error or not, returns 1 if carrier loss error else returns 0 * \param[in] txdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT tx_carrier_lost_error(t_TX_NORMAL_DESC *txdesc) { UINT VARTDES3; /* check TDES3.LoC and TDES3.NC bits */ TX_NORMAL_DESC_TDES3_ML_RD(txdesc->TDES3, VARTDES3); if (((VARTDES3 & 0x800) == 0x800) || ((VARTDES3 & 0x400) == 0x400)) return 1; else return 0; } /*! * \brief This sequence is used to check whether transmission is aborted * or not returns 1 if transmission is aborted else returns 0 * \param[in] txdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT tx_aborted_error(t_TX_NORMAL_DESC *txdesc) { UINT VARTDES3; /* check for TDES3.LC and TDES3.EC */ TX_NORMAL_DESC_TDES3_ML_RD(txdesc->TDES3, VARTDES3); if (((VARTDES3 & 0x200) == 0x200) || ((VARTDES3 & 0x100) == 0x100)) return 1; else return 0; } /*! * \brief This sequence is used to check whether the pkt transmitted is * successful or not, returns 1 if transmission is success else returns 0 * \param[in] txdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT tx_complete(t_TX_NORMAL_DESC *txdesc) { UINT VAROWN; TX_NORMAL_DESC_TDES3_OWN_MLF_RD(txdesc->TDES3, VAROWN); if (VAROWN == 0) return 1; else return 0; } /*! * \brief This sequence is used to check whethet rx csum is enabled/disabled * returns 1 if rx csum is enabled else returns 0 * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT get_rx_csum_status(void) { ULONG VARMAC_MCR; MAC_MCR_RGRD(VARMAC_MCR); if (GET_VALUE(VARMAC_MCR, MAC_MCR_IPC_LPOS, MAC_MCR_IPC_HPOS) == 0x1) { return 1; } else { return 0; } } /*! * \brief This sequence is used to disable the rx csum * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT disable_rx_csum(void) { /* enable rx checksum */ MAC_MCR_IPC_UDFWR(0); return Y_SUCCESS; } /*! * \brief This sequence is used to enable the rx csum * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT enable_rx_csum(void) { /* enable rx checksum */ MAC_MCR_IPC_UDFWR(0x1); return Y_SUCCESS; } /*! * \brief This sequence is used to reinitialize the TX descriptor fields, * so that device can reuse the descriptors * \param[in] idx * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT tx_descriptor_reset(UINT idx, struct DWC_ETH_QOS_prv_data *pdata, UINT QINX) { struct s_TX_NORMAL_DESC *TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, idx); DBGPR("-->tx_descriptor_reset\n"); /* update buffer 1 address pointer to zero */ TX_NORMAL_DESC_TDES0_ML_WR(TX_NORMAL_DESC->TDES0, 0); /* update buffer 2 address pointer to zero */ TX_NORMAL_DESC_TDES1_ML_WR(TX_NORMAL_DESC->TDES1, 0); /* set all other control bits (IC, TTSE, B2L & B1L) to zero */ TX_NORMAL_DESC_TDES2_ML_WR(TX_NORMAL_DESC->TDES2, 0); /* set all other control bits (OWN, CTXT, FD, LD, CPC, * CIC etc) to zero */ TX_NORMAL_DESC_TDES3_ML_WR(TX_NORMAL_DESC->TDES3, 0); DBGPR("<--tx_descriptor_reset\n"); return Y_SUCCESS; } /*! * \brief This sequence is used to reinitialize the RX descriptor fields, * so that device can reuse the descriptors * \param[in] idx * \param[in] pdata */ static void rx_descriptor_reset(UINT idx, struct DWC_ETH_QOS_prv_data *pdata, unsigned int inte, UINT QINX) { struct DWC_ETH_QOS_rx_buffer *buffer = GET_RX_BUF_PTR(QINX, idx); struct s_RX_NORMAL_DESC *RX_NORMAL_DESC = GET_RX_DESC_PTR(QINX, idx); DBGPR("-->rx_descriptor_reset\n"); memset(RX_NORMAL_DESC, 0, sizeof(struct s_RX_NORMAL_DESC)); /* update buffer 1 address pointer */ RX_NORMAL_DESC_RDES0_ML_WR(RX_NORMAL_DESC->RDES0, buffer->dma); /* set to zero */ RX_NORMAL_DESC_RDES1_ML_WR(RX_NORMAL_DESC->RDES1, 0); if ((pdata->dev->mtu > DWC_ETH_QOS_ETH_FRAME_LEN) || (pdata->rx_split_hdr == 1)) { /* update buffer 2 address pointer */ RX_NORMAL_DESC_RDES2_ML_WR(RX_NORMAL_DESC->RDES2, buffer->dma2); /* set control bits - OWN, INTE, BUF1V and BUF2V */ RX_NORMAL_DESC_RDES3_ML_WR(RX_NORMAL_DESC->RDES3, (0x83000000 | inte)); } else { /* set buffer 2 address pointer to zero */ RX_NORMAL_DESC_RDES2_ML_WR(RX_NORMAL_DESC->RDES2, 0); /* set control bits - OWN, INTE and BUF1V */ RX_NORMAL_DESC_RDES3_ML_WR(RX_NORMAL_DESC->RDES3, (0x81000000 | inte)); } DBGPR("<--rx_descriptor_reset\n"); } /*! * \brief This sequence is used to initialize the rx descriptors. * \param[in] pdata */ static void rx_descriptor_init(struct DWC_ETH_QOS_prv_data *pdata, UINT QINX) { struct DWC_ETH_QOS_rx_wrapper_descriptor *rx_desc_data = GET_RX_WRAPPER_DESC(QINX); struct DWC_ETH_QOS_rx_buffer *buffer = GET_RX_BUF_PTR(QINX, rx_desc_data->cur_rx); struct s_RX_NORMAL_DESC *RX_NORMAL_DESC = GET_RX_DESC_PTR(QINX, rx_desc_data->cur_rx); INT i; INT start_index = rx_desc_data->cur_rx; INT last_index; DBGPR("-->rx_descriptor_init\n"); /* initialize all desc */ for (i = 0; i < pdata->rx_queue[QINX].desc_cnt; i++) { memset(RX_NORMAL_DESC, 0, sizeof(struct s_RX_NORMAL_DESC)); /* update buffer 1 address pointer */ RX_NORMAL_DESC_RDES0_ML_WR(RX_NORMAL_DESC->RDES0, buffer->dma); /* set to zero */ RX_NORMAL_DESC_RDES1_ML_WR(RX_NORMAL_DESC->RDES1, 0); if ((pdata->dev->mtu > DWC_ETH_QOS_ETH_FRAME_LEN) || (pdata->rx_split_hdr == 1)) { /* update buffer 2 address pointer */ RX_NORMAL_DESC_RDES2_ML_WR(RX_NORMAL_DESC->RDES2, buffer->dma2); /* set control bits - OWN, INTE, BUF1V and BUF2V */ RX_NORMAL_DESC_RDES3_ML_WR( RX_NORMAL_DESC->RDES3, (0xc3000000)); } else { /* set buffer 2 address pointer to zero */ RX_NORMAL_DESC_RDES2_ML_WR(RX_NORMAL_DESC->RDES2, 0); /* set control bits - OWN, INTE and BUF1V */ RX_NORMAL_DESC_RDES3_ML_WR( RX_NORMAL_DESC->RDES3, (0xc1000000)); } /* Don't Set the IOC bit for IPA controlled Desc */ if (pdata->ipa_enabled && QINX == IPA_DMA_RX_CH) { UINT varRDES3 = 0; RX_NORMAL_DESC_RDES3_ML_RD(RX_NORMAL_DESC->RDES3, varRDES3); /* reset IOC for all buffers */ RX_NORMAL_DESC_RDES3_ML_WR(RX_NORMAL_DESC->RDES3, (varRDES3 & ~(1 << 30))); } else { buffer->inte = (1 << 30); /* reconfigure INTE bit if RX watchdog timer is enabled */ if (rx_desc_data->use_riwt) { if ((i % rx_desc_data->rx_coal_frames) != 0) { UINT VARRDES3 = 0; RX_NORMAL_DESC_RDES3_ML_RD( RX_NORMAL_DESC->RDES3, VARRDES3); /* reset INTE */ RX_NORMAL_DESC_RDES3_ML_WR( RX_NORMAL_DESC->RDES3, (VARRDES3 & ~(1 << 30))); buffer->inte = 0; } } } INCR_RX_DESC_INDEX(rx_desc_data->cur_rx, 1, pdata->rx_queue[QINX].desc_cnt); RX_NORMAL_DESC = GET_RX_DESC_PTR(QINX, rx_desc_data->cur_rx); buffer = GET_RX_BUF_PTR(QINX, rx_desc_data->cur_rx); } /* Reset the OWN bit of last descriptor */ if (pdata->ipa_enabled && QINX == IPA_DMA_RX_CH) { RX_NORMAL_DESC = GET_RX_DESC_PTR(QINX, pdata->rx_queue[QINX].desc_cnt - 1); RX_CONTEXT_DESC_RDES3_OWN_MLF_WR(RX_NORMAL_DESC->RDES3, 0); } /* update the total no of Rx descriptors count */ DMA_RDRLR_RGWR(QINX, (pdata->rx_queue[QINX].desc_cnt - 1)); /* update the Rx Descriptor Tail Pointer */ last_index = GET_RX_CURRENT_RCVD_LAST_DESC_INDEX(start_index, 0, pdata->rx_queue[QINX].desc_cnt); DMA_RDTP_RPDR_RGWR(QINX, GET_RX_DESC_DMA_ADDR(QINX, last_index)); /* update the starting address of desc chain/ring */ DMA_RDLAR_RGWR(QINX, GET_RX_DESC_DMA_ADDR(QINX, start_index)); DBGPR("<--rx_descriptor_init\n"); } /*! * \brief This sequence is used to initialize the tx descriptors. * \param[in] pdata */ static void tx_descriptor_init(struct DWC_ETH_QOS_prv_data *pdata, UINT QINX) { struct DWC_ETH_QOS_tx_wrapper_descriptor *tx_desc_data = GET_TX_WRAPPER_DESC(QINX); struct s_TX_NORMAL_DESC *TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); INT i; INT start_index = tx_desc_data->cur_tx; DBGPR("-->tx_descriptor_init\n"); /* initialze all descriptors. */ for (i = 0; i < pdata->tx_queue[QINX].desc_cnt; i++) { /* update buffer 1 address pointer to zero */ TX_NORMAL_DESC_TDES0_ML_WR(TX_NORMAL_DESC->TDES0, 0); /* update buffer 2 address pointer to zero */ TX_NORMAL_DESC_TDES1_ML_WR(TX_NORMAL_DESC->TDES1, 0); /* set all other control bits (IC, TTSE, B2L & B1L) to zero */ TX_NORMAL_DESC_TDES2_ML_WR(TX_NORMAL_DESC->TDES2, 0); /* set all other control bits (OWN, CTXT, FD, LD, CPC, CIC etc) * to zero */ TX_NORMAL_DESC_TDES3_ML_WR(TX_NORMAL_DESC->TDES3, 0); INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1, pdata->tx_queue[QINX].desc_cnt); TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); } /* update the total no of Tx descriptors count */ DMA_TDRLR_RGWR(QINX, (pdata->tx_queue[QINX].desc_cnt - 1)); /* update the starting address of desc chain/ring */ DMA_TDLAR_RGWR(QINX, GET_TX_DESC_DMA_ADDR(QINX, start_index)); DBGPR("<--tx_descriptor_init\n"); } /*! * \brief This sequence is used to prepare tx descriptor for * packet transmission and issue the poll demand command to TxDMA * * \param[in] pdata */ static void pre_transmit(struct DWC_ETH_QOS_prv_data *pdata, UINT QINX, UINT int_mod) { struct DWC_ETH_QOS_tx_wrapper_descriptor *tx_desc_data = GET_TX_WRAPPER_DESC(QINX); struct DWC_ETH_QOS_tx_buffer *buffer = GET_TX_BUF_PTR(QINX, tx_desc_data->cur_tx); struct s_TX_NORMAL_DESC *TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); struct s_TX_CONTEXT_DESC *TX_CONTEXT_DESC = (struct s_TX_CONTEXT_DESC *)GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); UINT varcsum_enable; #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG UINT varvlan_pkt; UINT varvt; #endif INT i; INT start_index = tx_desc_data->cur_tx; INT last_index, original_start_index = tx_desc_data->cur_tx; struct s_tx_pkt_features *tx_pkt_features = GET_TX_PKT_FEATURES_PTR; #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT INT update_tail = 0; UINT VARQTDR; #endif UINT vartso_enable = 0; UINT varmss = 0; UINT varpay_len = 0; UINT vartcp_udp_hdr_len = 0; UINT varptp_enable = 0; INT total_len = 0; DBGPR("-->pre_transmit: QINX = %u\n", QINX); #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT if (QINX == 0) MTL_Q0TDR_TXQSTS_UDFRD(VARQTDR); else MTL_QTDR_TXQSTS_UDFRD(QINX, VARQTDR); /* No activity on MAC Tx-Fifo and fifo is empty */ if (VARQTDR == 0) { /* disable MAC Transmit */ MAC_MCR_TE_UDFWR(0); update_tail = 1; } #endif #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG TX_PKT_FEATURES_PKT_ATTRIBUTES_VLAN_PKT_MLF_RD( tx_pkt_features->pkt_attributes, varvlan_pkt); if (varvlan_pkt == 0x1) { /* put vlan tag in contex descriptor and set other control * bits accordingly */ TX_PKT_FEATURES_VLAN_TAG_VT_MLF_RD( tx_pkt_features->vlan_tag, varvt); TX_CONTEXT_DESC_TDES3_VT_MLF_WR(TX_CONTEXT_DESC->TDES3, varvt); TX_CONTEXT_DESC_TDES3_VLTV_MLF_WR(TX_CONTEXT_DESC->TDES3, 0x1); TX_CONTEXT_DESC_TDES3_CTXT_MLF_WR(TX_CONTEXT_DESC->TDES3, 0x1); TX_CONTEXT_DESC_TDES3_OWN_MLF_WR(TX_CONTEXT_DESC->TDES3, 0x1); original_start_index = tx_desc_data->cur_tx; INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1, pdata->tx_queue[QINX].desc_cnt); start_index = tx_desc_data->cur_tx; TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); buffer = GET_TX_BUF_PTR(QINX, tx_desc_data->cur_tx); } #endif /* DWC_ETH_QOS_ENABLE_VLAN_TAG */ #ifdef DWC_ETH_QOS_ENABLE_DVLAN if (pdata->via_reg_or_desc == DWC_ETH_QOS_VIA_DESC) { /* put vlan tag in contex descriptor and set other control * bits accordingly */ if (pdata->in_out & DWC_ETH_QOS_DVLAN_OUTER) { TX_CONTEXT_DESC_TDES3_VT_MLF_WR(TX_CONTEXT_DESC->TDES3, pdata->outer_vlan_tag); TX_CONTEXT_DESC_TDES3_VLTV_MLF_WR( TX_CONTEXT_DESC->TDES3, 0x1); /* operation (insertion/replacement/deletion/none) * will be specified in normal descriptor TDES2 */ } if (pdata->in_out & DWC_ETH_QOS_DVLAN_INNER) { TX_CONTEXT_DESC_TDES2_IVT_MLF_WR( TX_CONTEXT_DESC->TDES2, pdata->inner_vlan_tag); TX_CONTEXT_DESC_TDES3_IVLTV_MLF_WR( TX_CONTEXT_DESC->TDES3, 0x1); TX_CONTEXT_DESC_TDES3_IVTIR_MLF_WR( TX_CONTEXT_DESC->TDES3, pdata->op_type); } TX_CONTEXT_DESC_TDES3_CTXT_MLF_WR(TX_CONTEXT_DESC->TDES3, 0x1); TX_CONTEXT_DESC_TDES3_OWN_MLF_WR(TX_CONTEXT_DESC->TDES3, 0x1); original_start_index = tx_desc_data->cur_tx; INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1, pdata->tx_queue[QINX].desc_cnt); start_index = tx_desc_data->cur_tx; TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); buffer = GET_TX_BUF_PTR(QINX, tx_desc_data->cur_tx); } #endif /* End of DWC_ETH_QOS_ENABLE_DVLAN */ /* prepare CONTEXT descriptor for TSO */ TX_PKT_FEATURES_PKT_ATTRIBUTES_TSO_ENABLE_MLF_RD( tx_pkt_features->pkt_attributes, vartso_enable); if (vartso_enable && ( tx_pkt_features->mss != tx_desc_data->default_mss)) { /* get MSS and update */ TX_PKT_FEATURES_MSS_MSS_MLF_RD(tx_pkt_features->mss, varmss); TX_CONTEXT_DESC_TDES2_MSS_MLF_WR( TX_CONTEXT_DESC->TDES2, varmss); /* set MSS valid, CTXT and OWN bits */ TX_CONTEXT_DESC_TDES3_TCMSSV_MLF_WR( TX_CONTEXT_DESC->TDES3, 0x1); TX_CONTEXT_DESC_TDES3_CTXT_MLF_WR( TX_CONTEXT_DESC->TDES3, 0x1); TX_CONTEXT_DESC_TDES3_OWN_MLF_WR( TX_CONTEXT_DESC->TDES3, 0x1); /* DMA uses the MSS value programed in DMA_CR if driver * doesn't provided the CONTEXT descriptor */ DMA_CR_MSS_UDFWR(QINX, tx_pkt_features->mss); tx_desc_data->default_mss = tx_pkt_features->mss; original_start_index = tx_desc_data->cur_tx; INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1, pdata->tx_queue[QINX].desc_cnt); start_index = tx_desc_data->cur_tx; TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); buffer = GET_TX_BUF_PTR(QINX, tx_desc_data->cur_tx); } /* update the first buffer pointer and length */ TX_NORMAL_DESC_TDES0_ML_WR(TX_NORMAL_DESC->TDES0, buffer->dma); TX_NORMAL_DESC_TDES2_HL_B1L_MLF_WR(TX_NORMAL_DESC->TDES2, buffer->len); if (buffer->dma2 != 0) { /* update the second buffer pointer and length */ TX_NORMAL_DESC_TDES1_ML_WR(TX_NORMAL_DESC->TDES1, buffer->dma2); TX_NORMAL_DESC_TDES2_B2L_MLF_WR( TX_NORMAL_DESC->TDES2, buffer->len2); } if (vartso_enable) { /* update TCP payload length (only for the * descriptor with FD set) */ TX_PKT_FEATURES_PAY_LEN_ML_RD( tx_pkt_features->pay_len, varpay_len); /* TDES3[17:0] will be TCP payload length */ TX_NORMAL_DESC->TDES3 |= varpay_len; } else { /* update total length of packet */ GET_TX_TOT_LEN( GET_TX_BUF_PTR(QINX, 0), tx_desc_data->cur_tx, GET_CURRENT_XFER_DESC_CNT(QINX), total_len, pdata->tx_queue[QINX].desc_cnt); TX_NORMAL_DESC_TDES3_FL_MLF_WR( TX_NORMAL_DESC->TDES3, total_len); } #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG /* Insert a VLAN tag with a tag value programmed in MAC Reg 24 or * CONTEXT descriptor */ if ( tx_desc_data->vlan_tag_present && tx_desc_data->tx_vlan_tag_via_reg == Y_FALSE) { /* pr_alert("VLAN control info update via descriptor\n\n"); */ TX_NORMAL_DESC_TDES2_VTIR_MLF_WR( TX_NORMAL_DESC->TDES2, tx_desc_data->tx_vlan_tag_ctrl); } #endif /* DWC_ETH_QOS_ENABLE_VLAN_TAG */ #ifdef DWC_ETH_QOS_ENABLE_DVLAN if (pdata->via_reg_or_desc == DWC_ETH_QOS_VIA_DESC) { if (pdata->in_out & DWC_ETH_QOS_DVLAN_OUTER) { TX_NORMAL_DESC_TDES2_VTIR_MLF_WR( TX_NORMAL_DESC->TDES2, pdata->op_type); } } #endif /* End of DWC_ETH_QOS_ENABLE_DVLAN */ /* Mark it as First Descriptor */ TX_NORMAL_DESC_TDES3_FD_MLF_WR(TX_NORMAL_DESC->TDES3, 0x1); /* Enable CRC and Pad Insertion (NOTE: set this only * for FIRST descriptor) */ TX_NORMAL_DESC_TDES3_CPC_MLF_WR(TX_NORMAL_DESC->TDES3, 0); /* Mark it as NORMAL descriptor */ TX_NORMAL_DESC_TDES3_CTXT_MLF_WR(TX_NORMAL_DESC->TDES3, 0); /* Enable HW CSUM */ TX_PKT_FEATURES_PKT_ATTRIBUTES_CSUM_ENABLE_MLF_RD( tx_pkt_features->pkt_attributes, varcsum_enable); if (varcsum_enable == 0x1) TX_NORMAL_DESC_TDES3_CIC_MLF_WR(TX_NORMAL_DESC->TDES3, 0x3); /* configure SA Insertion Control */ TX_NORMAL_DESC_TDES3_SAIC_MLF_WR( TX_NORMAL_DESC->TDES3, pdata->tx_sa_ctrl_via_desc); if (vartso_enable) { /* set TSE bit */ TX_NORMAL_DESC_TDES3_TSE_MLF_WR(TX_NORMAL_DESC->TDES3, 0x1); /* update tcp data offset or tcp hdr len */ TX_PKT_FEATURES_TCP_HDR_LEN_ML_RD( tx_pkt_features->tcp_udp_hdr_len, vartcp_udp_hdr_len); /* convert to bit value */ vartcp_udp_hdr_len = vartcp_udp_hdr_len / 4; TX_NORMAL_DESC_TDES3_SLOTNUM_TCPHDRLEN_MLF_WR( TX_NORMAL_DESC->TDES3, vartcp_udp_hdr_len); } /* enable timestamping */ TX_PKT_FEATURES_PKT_ATTRIBUTES_PTP_ENABLE_MLF_RD( tx_pkt_features->pkt_attributes, varptp_enable); if (varptp_enable) TX_NORMAL_DESC_TDES2_TTSE_MLF_WR(TX_NORMAL_DESC->TDES2, 0x1); INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1, pdata->tx_queue[QINX].desc_cnt); TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); buffer = GET_TX_BUF_PTR(QINX, tx_desc_data->cur_tx); for (i = 1; i < GET_CURRENT_XFER_DESC_CNT(QINX); i++) { /* update the first buffer pointer and length */ TX_NORMAL_DESC_TDES0_ML_WR(TX_NORMAL_DESC->TDES0, buffer->dma); TX_NORMAL_DESC_TDES2_HL_B1L_MLF_WR( TX_NORMAL_DESC->TDES2, buffer->len); if (buffer->dma2 != 0) { /* update the second buffer pointer and length */ TX_NORMAL_DESC_TDES1_ML_WR( TX_NORMAL_DESC->TDES1, buffer->dma2); TX_NORMAL_DESC_TDES2_B2L_MLF_WR( TX_NORMAL_DESC->TDES2, buffer->len2); } /* set own bit */ TX_NORMAL_DESC_TDES3_OWN_MLF_WR(TX_NORMAL_DESC->TDES3, 0x1); /* Mark it as NORMAL descriptor */ TX_NORMAL_DESC_TDES3_CTXT_MLF_WR(TX_NORMAL_DESC->TDES3, 0); INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1, pdata->tx_queue[QINX].desc_cnt); TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, tx_desc_data->cur_tx); buffer = GET_TX_BUF_PTR(QINX, tx_desc_data->cur_tx); } /* Mark it as LAST descriptor */ last_index = GET_TX_CURRENT_XFER_LAST_DESC_INDEX(QINX, start_index, 0, pdata->tx_queue[QINX].desc_cnt); TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, last_index); TX_NORMAL_DESC_TDES3_LD_MLF_WR(TX_NORMAL_DESC->TDES3, 0x1); /* set Interrupt on Completion for last descriptor */ #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT pdata->mac_enable_count += 1; if ((pdata->mac_enable_count % pdata->drop_tx_pktburstcnt) == 0) TX_NORMAL_DESC_TDES2_IC_MLF_WR(TX_NORMAL_DESC->TDES2, 0x1); #else TX_NORMAL_DESC_TDES2_IC_MLF_WR(TX_NORMAL_DESC->TDES2, (!(tx_desc_data->cur_tx % int_mod))); #endif /* set OWN bit of FIRST descriptor at end to avoid race condition */ TX_NORMAL_DESC = GET_TX_DESC_PTR(QINX, start_index); TX_NORMAL_DESC_TDES3_OWN_MLF_WR(TX_NORMAL_DESC->TDES3, 0x1); #ifdef DWC_ETH_QOS_ENABLE_TX_DESC_DUMP dump_tx_desc( pdata, original_start_index, (tx_desc_data->cur_tx - 1), 1, QINX); #endif #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT /* updating descriptor tail pointer for DMA Transmit * under two conditions, * 1. if burst number of packets are present in descriptor list * 2. MAC has no activity on Tx fifo */ if ( (pdata->mac_enable_count >= pdata->drop_tx_pktburstcnt) && (update_tail == 1)) { pdata->mac_enable_count -= pdata->drop_tx_pktburstcnt; /* issue a poll command to Tx DMA by writing address * of next immediate free descriptor */ last_index = GET_TX_CURRENT_XFER_LAST_DESC_INDEX( QINX, start_index, 1, pdata->tx_queue[QINX].desc_cnt); DMA_TDTP_TPDR_RGWR(QINX, GET_TX_DESC_DMA_ADDR(QINX, last_index)); } #else /* issue a poll command to Tx DMA by writing address * of next immediate free descriptor */ last_index = GET_TX_CURRENT_XFER_LAST_DESC_INDEX(QINX, start_index, 1, pdata->tx_queue[QINX].desc_cnt); DMA_TDTP_TPDR_RGWR(QINX, GET_TX_DESC_DMA_ADDR(QINX, last_index)); #endif if (pdata->eee_enabled && (!pdata->use_lpi_auto_entry_timer)) { /* restart EEE timer */ mod_timer(&pdata->eee_ctrl_timer, DWC_ETH_QOS_LPI_TIMER(DWC_ETH_QOS_DEFAULT_LPI_TIMER)); } DBGPR("<--pre_transmit\n"); } /*! * \brief This sequence is used to read data from device, * it checks whether data is good or bad and updates the errors appropriately * \param[in] pdata */ static void device_read(struct DWC_ETH_QOS_prv_data *pdata, UINT QINX) { struct DWC_ETH_QOS_rx_wrapper_descriptor *rx_desc_data = GET_RX_WRAPPER_DESC(QINX); struct s_RX_NORMAL_DESC *RX_NORMAL_DESC = GET_RX_DESC_PTR(QINX, rx_desc_data->cur_rx); UINT VAROWN; UINT VARES; struct DWC_ETH_QOS_rx_buffer *buffer = GET_RX_BUF_PTR(QINX, rx_desc_data->cur_rx); UINT VARRS1V; UINT VARIPPE; UINT VARIPCB; UINT VARIPHE; struct s_rx_pkt_features *rx_pkt_features = GET_RX_PKT_FEATURES_PTR; #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG UINT VARRS0V; UINT VARLT; UINT VARRDES0; #endif UINT VAROE; struct s_rx_error_counters *rx_error_counters = GET_RX_ERROR_COUNTERS_PTR; UINT VARCE; UINT VARRE; UINT VARLD; DBGPR("-->device_read: cur_rx = %d\n", rx_desc_data->cur_rx); /* check for data availability */ RX_NORMAL_DESC_RDES3_OWN_MLF_RD(RX_NORMAL_DESC->RDES3, VAROWN); if (VAROWN == 0) { /* check whether it is good packet or bad packet */ RX_NORMAL_DESC_RDES3_ES_MLF_RD(RX_NORMAL_DESC->RDES3, VARES); RX_NORMAL_DESC_RDES3_LD_MLF_RD(RX_NORMAL_DESC->RDES3, VARLD); #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT_HALFDUPLEX /* Synopsys testing and debugging purposes only */ if (VARES == 1 && VARLD == 1) { VARES = 0; DBGPR("Forwarding error pkts as good pkts to stack\n"); } #endif if ((VARES == 0) && (VARLD == 1)) { /* get the packet length */ RX_NORMAL_DESC_RDES3_FL_MLF_RD( RX_NORMAL_DESC->RDES3, buffer->len); RX_NORMAL_DESC_RDES3_RS1V_MLF_RD( RX_NORMAL_DESC->RDES3, VARRS1V); if (VARRS1V == 0x1) { /* check whether device has done csum * correctly or not */ RX_NORMAL_DESC_RDES1_IPPE_MLF_RD( RX_NORMAL_DESC->RDES1, VARIPPE); RX_NORMAL_DESC_RDES1_IPCB_MLF_RD( RX_NORMAL_DESC->RDES1, VARIPCB); RX_NORMAL_DESC_RDES1_IPHE_MLF_RD( RX_NORMAL_DESC->RDES1, VARIPHE); if ( (VARIPPE == 0) && (VARIPCB == 0) && (VARIPHE == 0)) { /* IPC Checksum done */ RX_PKT_ATTR_CSUM_DONE_MLF_WR( rx_pkt_features->pkt_attributes, 0x1); } } #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG RX_NORMAL_DESC_RDES3_RS0V_MLF_RD(RX_NORMAL_DESC->RDES3, VARRS0V); if (VARRS0V == 0x1) { /* device received frame with VLAN Tag or * double VLAN Tag ? */ RX_NORMAL_DESC_RDES3_LT_MLF_RD( RX_NORMAL_DESC->RDES3, VARLT); if ((VARLT == 0x4) || (VARLT == 0x5)) { RX_PKT_ATTR_VLAN_PKT_MLF_WR( rx_pkt_features->pkt_attributes, 0x1); /* get the VLAN Tag */ RX_NORMAL_DESC_RDES0_ML_RD( RX_NORMAL_DESC->RDES0, VARRDES0); RX_PKT_FEATURES_VLAN_TAG_VT_MLF_WR( rx_pkt_features->vlan_tag, (VARRDES0 & 0xffff)); } } #endif } else { #ifdef DWC_ETH_QOS_ENABLE_RX_DESC_DUMP dump_rx_desc( QINX, RX_NORMAL_DESC, rx_desc_data->cur_rx); #endif /* not a good packet, hence check for * appropriate errors. */ RX_NORMAL_DESC_RDES3_OE_MLF_RD( RX_NORMAL_DESC->RDES3, VAROE); if (VAROE == 1) RX_ERR_COUNTERS_RX_ERR_OVERRUN_ERR_MLF_WR( rx_error_counters->rx_errors, 1); RX_NORMAL_DESC_RDES3_CE_MLF_RD( RX_NORMAL_DESC->RDES3, VARCE); if (VARCE == 1) { RX_ERROR_COUNTERS_RX_ERRORS_CRC_ERROR_MLF_WR( rx_error_counters->rx_errors, 1); } RX_NORMAL_DESC_RDES3_RE_MLF_RD( RX_NORMAL_DESC->RDES3, VARRE); if (VARRE == 1) { RX_ERROR_COUNTERS_RX_ERRORS_FRAME_ERROR_MLF_WR( rx_error_counters->rx_errors, 1); } RX_NORMAL_DESC_RDES3_LD_MLF_RD( RX_NORMAL_DESC->RDES3, VARLD); if (VARRE == 0) { RX_ERR_COUNTERS_RX_ERR_OVERRUN_ERR_MLF_WR( rx_error_counters->rx_errors, 1); } } } DBGPR("<--device_read: cur_rx = %d\n", rx_desc_data->cur_rx); } static void update_rx_tail_ptr(unsigned int QINX, unsigned int dma_addr) { DMA_RDTP_RPDR_RGWR(QINX, dma_addr); } /*! * \brief This sequence is used to check whether CTXT bit is * set or not returns 1 if CTXT is set else returns zero * \param[in] txdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT get_tx_descriptor_ctxt(t_TX_NORMAL_DESC *txdesc) { ULONG VARCTXT; /* check TDES3.CTXT bit */ TX_NORMAL_DESC_TDES3_CTXT_MLF_RD(txdesc->TDES3, VARCTXT); if (VARCTXT == 1) return 1; else return 0; } /*! * \brief This sequence is used to check whether LD bit is set or not * returns 1 if LD is set else returns zero * \param[in] txdesc * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static INT get_tx_descriptor_last(t_TX_NORMAL_DESC *txdesc) { ULONG VARLD; /* check TDES3.LD bit */ TX_NORMAL_DESC_TDES3_LD_MLF_RD(txdesc->TDES3, VARLD); if (VARLD == 1) return 1; else return 0; } /*! * \brief Exit routine * \details Exit function that unregisters the device, deallocates buffers, * unbinds the driver from controlling the device etc. * * \return Returns successful execution of the routine * \retval Y_SUCCESS Function executed successfully */ static INT DWC_ETH_QOS_yexit(void) { ULONG RETRYCOUNT = 1000; ULONG vy_count; volatile ULONG VARDMA_BMR; DBGPR("-->DWC_ETH_QOS_yexit\n"); /*issue a software reset */ DMA_BMR_SWR_UDFWR(0x1); /*DELAY IMPLEMENTATION USING udelay() */ udelay(10); /*Poll Until Poll Condition */ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT) { EMACERR("Unable to reset MAC 0x%x\n", VARDMA_BMR); return -Y_FAILURE; } vy_count++; mdelay(1); DMA_BMR_RGRD(VARDMA_BMR); if (GET_VALUE( VARDMA_BMR, DMA_BMR_SWR_LPOS, DMA_BMR_SWR_HPOS) == 0) { break; } } DBGPR("<--DWC_ETH_QOS_yexit\n"); return Y_SUCCESS; } /*! * \details This API will calculate per queue FIFO size. * * \param[in] fifo_size - total fifo size in h/w register * \param[in] queue_count - total queue count * * \return returns integer * \retval - fifo size per queue. */ static UINT calculate_per_queue_fifo(ULONG fifo_size, UCHAR queue_count) { ULONG q_fifo_size = 0; /* calculated fifo size per queue */ /* per queue fifo sizeprogrammable value */ ULONG p_fifo = EDWC_ETH_QOS_256; /* calculate Tx/Rx fifo share per queue */ switch (fifo_size) { case 0: q_fifo_size = FIFO_SIZE_B(128); break; case 1: q_fifo_size = FIFO_SIZE_B(256); break; case 2: q_fifo_size = FIFO_SIZE_B(512); break; case 3: q_fifo_size = FIFO_SIZE_KB(1); break; case 4: q_fifo_size = FIFO_SIZE_KB(2); break; case 5: q_fifo_size = FIFO_SIZE_KB(4); break; case 6: q_fifo_size = FIFO_SIZE_KB(8); break; case 7: q_fifo_size = FIFO_SIZE_KB(16); break; case 8: q_fifo_size = FIFO_SIZE_KB(32); break; case 9: q_fifo_size = FIFO_SIZE_KB(64); break; case 10: q_fifo_size = FIFO_SIZE_KB(128); break; case 11: q_fifo_size = FIFO_SIZE_KB(256); break; } q_fifo_size = q_fifo_size / queue_count; if (q_fifo_size >= FIFO_SIZE_KB(32)) p_fifo = EDWC_ETH_QOS_32K; else if (q_fifo_size >= FIFO_SIZE_KB(16)) p_fifo = EDWC_ETH_QOS_16K; else if (q_fifo_size >= FIFO_SIZE_KB(8)) p_fifo = EDWC_ETH_QOS_8K; else if (q_fifo_size >= FIFO_SIZE_KB(4)) p_fifo = EDWC_ETH_QOS_4K; else if (q_fifo_size >= FIFO_SIZE_KB(2)) p_fifo = EDWC_ETH_QOS_2K; else if (q_fifo_size >= FIFO_SIZE_KB(1)) p_fifo = EDWC_ETH_QOS_1K; else if (q_fifo_size >= FIFO_SIZE_B(512)) p_fifo = EDWC_ETH_QOS_512; else if (q_fifo_size >= FIFO_SIZE_B(256)) p_fifo = EDWC_ETH_QOS_256; return p_fifo; } static INT configure_tx_queue(UINT queue_index) { UINT scheduling_algo = EDWC_ETH_QOS_DCB_SP; UINT tsf_config = 0x1; UINT vy_count = 0; ULONG RETRYCOUNT = 1000; UINT op_mode = DWC_ETH_QOS_Q_GENERIC; UINT desc_posted_write = 0x1; volatile ULONG VARMTL_QTOMR; EMACDBG("Enter\n"); /*Flush Tx Queue */ MTL_QTOMR_FTQ_UDFWR(queue_index, 0x1); /*Poll Until Poll Condition */ while (1) { if (vy_count > RETRYCOUNT) { EMACERR("unable to flush tx queue %d", queue_index); return -Y_FAILURE; } vy_count++; usleep_range(1000, 1500); MTL_QTOMR_RGRD(queue_index, VARMTL_QTOMR); if (GET_VALUE( VARMTL_QTOMR, MTL_QTOMR_FTQ_LPOS, MTL_QTOMR_FTQ_HPOS) == 0) break; } switch (queue_index) { case 0: op_mode = DWC_ETH_QOS_Q_GENERIC; break; case 1: op_mode = DWC_ETH_QOS_Q_GENERIC; MTL_QTOMR_TXQEN_UDFWR(queue_index, op_mode); break; case 2: op_mode = DWC_ETH_QOS_Q_AVB; MTL_QTOMR_TXQEN_UDFWR(queue_index, op_mode); break; case 3: op_mode = DWC_ETH_QOS_Q_AVB; MTL_QTOMR_TXQEN_UDFWR(queue_index, op_mode); break; case 4: op_mode = DWC_ETH_QOS_Q_AVB; MTL_QTOMR_TXQEN_UDFWR(queue_index, op_mode); break; } /* Transmit Store and Forward enabled for all queues */ config_tsf_mode(queue_index, tsf_config); /* Select Tx Scheduling Algorithm */ MTL_OMR_SCHALG_UDFWR(scheduling_algo); DMA_BMR_DSPW_UDFWR(desc_posted_write); return Y_SUCCESS; EMACDBG("Exit\n"); } static void configure_avb_ip_rx_filtering(void) { UINT untagged_avb_ctrl_queue = 0x2, tagged_avb_ctrl_queue_en = 0x1; UINT untagged_pkt_queue = 0x0; UINT multicast_queue = 0x1; UINT multi_braodcast_routing_en = 0x1; /* Assign queue for untagged AVB control packets*/ MAC_RQC1R_AVUCPQ_UDFWR(untagged_avb_ctrl_queue); /* Assign queue for untagged packets */ MAC_RQC1R_UPQ_UDFWR(untagged_pkt_queue); /* Assign queue for multicast and broadcast packets */ MAC_RQC1R_MCBCQ_UDFWR(multicast_queue); /* Enable multicast and broadcast routing to RX queue */ MAC_RQC1R_MCBCQEN_UDFWR(multi_braodcast_routing_en); /* Assign queue for tagged AVB control packets*/ MAC_RQC1R_TACPQE_UDFWR(tagged_avb_ctrl_queue_en); /* Set priorities 2 and 3 for PSRQ3 in order to route all tagged * AVB data packets to queue 3 */ MAC_RQC2R_PSRQ3_UDFWR(0x0C); /* Set prority zero to route all vlan tagged * BE/IP traffic to queue 0 */ MAC_RQC2R_PSRQ0_UDFWR(0x1); /* Strict priority arbitration algorithm set on Rx side */ MTL_OMR_RAA_UDFWR(0x0); /* Mapping MTL Rx queue and DMA Rx channel. */ MTL_RQDCM0R_RGWR(0x3020100); } static void configure_ptp_rx_filtering(void) { UINT ptp_pkt_queue = 0x1, tagged_ptp_queue = 0x1; /* Assign queue for untagged PTP packets */ MAC_RQC1R_AVPTPQ_UDFWR(ptp_pkt_queue); /* Assign queue for tagged PTP packets */ MAC_RQC1R_TPQC_UDFWR(tagged_ptp_queue); /* Set TSIPENA bit to 1 to route tagged and untagged PTP pkts * to RX queue as per TPQC and PTPQ settings */ MAC_TCR_TSIPENA_UDFWR(0x1); /* Set TSENMACADDR to do PTP packet filtering that * sent over Ethernet packet */ MAC_TCR_TSENMACADDR_UDFWR(0x1); } static INT configure_rx_queue(UINT queue_index) { int op_mode = DWC_ETH_QOS_Q_GENERIC; UINT rsf_config = 0x1; UINT fup_config = 0x1; UINT fep_config = 0x1; UINT disable_csum_err_pkt_drop = 0x1; EMACDBG("Enter\n"); switch (queue_index) { case 0: op_mode = DWC_ETH_QOS_Q_GENERIC; /* Enable Rx queue for generic traffic */ MAC_RQC0R_RXQEN_UDFWR(queue_index, 0x2); break; case 1: op_mode = DWC_ETH_QOS_Q_GENERIC; /* Enable Rx queue for AVB traffic */ MAC_RQC0R_RXQEN_UDFWR(queue_index, op_mode); break; case 2: op_mode = DWC_ETH_QOS_Q_AVB; /* Enable Rx queue for AVB traffic */ MAC_RQC0R_RXQEN_UDFWR(queue_index, op_mode); break; case 3: op_mode = DWC_ETH_QOS_Q_AVB; /* Enable Rx queue for AVB traffic */ MAC_RQC0R_RXQEN_UDFWR(queue_index, op_mode); break; } /* Receive Store and Forward enabled for all RX queues */ config_rsf_mode(queue_index, rsf_config); MTL_QROMR_FUP_UDFWR(queue_index, fup_config); MTL_QROMR_FEP_UDFWR(queue_index, fep_config); /* Disable Dropping of TCP/IP Checksum Error Packets */ MTL_QROMR_DIS_TCP_EF_UDFWR(queue_index, disable_csum_err_pkt_drop); /* Receive Queue Packet Arbitration reset for all RX queues */ MTL_QRCR_RXQ_PKT_ARBIT_UDFWR(queue_index, 0x0); return Y_SUCCESS; EMACDBG("Exit\n"); } static INT configure_mtl_queue(UINT QINX, struct DWC_ETH_QOS_prv_data *pdata) { struct DWC_ETH_QOS_tx_queue *queue_data = GET_TX_QUEUE_PTR(QINX); ULONG RETRYCOUNT = 1000; ULONG vy_count; volatile ULONG VARMTL_QTOMR; UINT p_rx_fifo = EDWC_ETH_QOS_256, p_tx_fifo = EDWC_ETH_QOS_256; DBGPR("-->configure_mtl_queue\n"); /*Flush Tx Queue */ MTL_QTOMR_FTQ_UDFWR(QINX, 0x1); /*Poll Until Poll Condition */ vy_count = 0; while (1) { if (vy_count > RETRYCOUNT){ EMACERR("unable to flush tx queue %d", QINX); return -Y_FAILURE; } vy_count++; mdelay(1); MTL_QTOMR_RGRD(QINX, VARMTL_QTOMR); if (GET_VALUE( VARMTL_QTOMR, MTL_QTOMR_FTQ_LPOS, MTL_QTOMR_FTQ_HPOS) == 0) break; } /*Enable Store and Forward mode for TX */ MTL_QTOMR_TSF_UDFWR(QINX, 0x1); /* Program Tx operating mode */ MTL_QTOMR_TXQEN_UDFWR(QINX, queue_data->q_op_mode); /* Transmit Queue weight */ MTL_QW_ISCQW_UDFWR(QINX, (0x10 + QINX)); MTL_QROMR_FEP_UDFWR(QINX, 0x1); /* Configure for Jumbo frame in MTL */ if (pdata->dev->mtu > DWC_ETH_QOS_ETH_FRAME_LEN) { /* Disable RX Store and Forward mode */ MTL_QROMR_RSF_UDFWR(QINX, 0x0); pr_alert("RX is configured in threshold mode and threshold = 64Byte\n"); } p_rx_fifo = calculate_per_queue_fifo( pdata->hw_feat.rx_fifo_size, DWC_ETH_QOS_RX_QUEUE_CNT); p_tx_fifo = calculate_per_queue_fifo( pdata->hw_feat.tx_fifo_size, DWC_ETH_QOS_TX_QUEUE_CNT); /* Transmit/Receive queue fifo size programmed */ MTL_QROMR_RQS_UDFWR(QINX, p_rx_fifo); MTL_QTOMR_TQS_UDFWR(QINX, p_tx_fifo); EMACDBG( "Queue%d Tx fifo size %d, Rx fifo size %d\n", QINX, ((p_tx_fifo + 1) * 256), ((p_rx_fifo + 1) * 256)); /* flow control will be used only if * each channel gets 8KB or more fifo */ if (p_rx_fifo >= EDWC_ETH_QOS_4K) { /* Enable Rx FLOW CTRL in MTL and MAC * Programming is valid only if Rx fifo size is greater than * or equal to 8k */ if ((pdata->flow_ctrl & DWC_ETH_QOS_FLOW_CTRL_TX) == DWC_ETH_QOS_FLOW_CTRL_TX) { MTL_QROMR_EHFC_UDFWR(QINX, 0x1); #ifdef DWC_ETH_QOS_VER_4_0 if (p_rx_fifo == EDWC_ETH_QOS_4K) { /* This violates the above formula because of * FIFO size limit therefore overflow may occur * inspite of this */ MTL_QROMR_RFD_UDFWR(QINX, 0x2); MTL_QROMR_RFA_UDFWR(QINX, 0x1); } else if (p_rx_fifo == EDWC_ETH_QOS_8K) { MTL_QROMR_RFD_UDFWR(QINX, 0x4); MTL_QROMR_RFA_UDFWR(QINX, 0x2); } else if (p_rx_fifo == EDWC_ETH_QOS_16K) { MTL_QROMR_RFD_UDFWR(QINX, 0x5); MTL_QROMR_RFA_UDFWR(QINX, 0x2); } else if (p_rx_fifo == EDWC_ETH_QOS_32K) { MTL_QROMR_RFD_UDFWR(QINX, 0x7); MTL_QROMR_RFA_UDFWR(QINX, 0x2); } #else /* Set Threshold for Activating Flow Contol space * for min 2 frames ie, (1500 * 1) = 1500 bytes * Set Threshold for Deactivating Flow Contol for * space of min 1 frame (frame size 1500bytes) * in receive fifo */ if (p_rx_fifo == EDWC_ETH_QOS_4K) { /* This violates the above formula because of * FIFO size limit therefore overflow may occur * inspite of this */ /* Full - 3K */ MTL_QROMR_RFD_UDFWR(QINX, 0x3); /* Full - 1.5K */ MTL_QROMR_RFA_UDFWR(QINX, 0x1); } else if (p_rx_fifo == EDWC_ETH_QOS_8K) { MTL_QROMR_RFD_UDFWR(QINX, 0x6); /* Full - 4K */ MTL_QROMR_RFA_UDFWR(QINX, 0xA); /* Full - 6K */ } else if (p_rx_fifo == EDWC_ETH_QOS_16K) { MTL_QROMR_RFD_UDFWR(QINX, 0x6); /* Full - 4K */ /* Full - 10K */ MTL_QROMR_RFA_UDFWR(QINX, 0x12); } else if (p_rx_fifo == EDWC_ETH_QOS_32K) { MTL_QROMR_RFD_UDFWR(QINX, 0x6); /* Full - 4K */ /* Full - 16K */ MTL_QROMR_RFA_UDFWR(QINX, 0x1E); } #endif } } DBGPR("<--configure_mtl_queue\n"); return Y_SUCCESS; } static void configure_dma_sys_bus(struct DWC_ETH_QOS_prv_data *pdata) { /* Setting INCRx */ DMA_SBUS_RGWR(0x0); /* To get Best Performance */ DMA_SBUS_BLEN16_UDFWR(1); DMA_SBUS_BLEN8_UDFWR(1); DMA_SBUS_BLEN4_UDFWR(1); DMA_SBUS_RD_OSR_LMT_UDFWR(2); } static void configure_tx_dma_channel(UINT QINX, struct DWC_ETH_QOS_prv_data *pdata) { DBGPR("-->configure_tx_dma_channel\n"); /*Enable OSF mode */ DMA_TCR_OSP_UDFWR(QINX, 0x1); /* set PBLx8 */ DMA_CR_PBLX8_UDFWR(QINX, 0x1); /* set TX PBL = 16 */ DMA_TCR_PBL_UDFWR(QINX, 32); enable_tx_dma_interrupts(QINX, pdata); /* enable TSO if HW supports */ if (pdata->hw_feat.tso_en) DMA_TCR_TSE_UDFWR(QINX, 0x1); EMACDBG( "%s TSO\n", (pdata->hw_feat.tso_en ? "Enabled" : "Disabled")); /* For PG don't start TX DMA now.*/ #ifndef DWC_ETH_QOS_CONFIG_PGTEST /* start TX DMA */ DMA_TCR_ST_UDFWR(QINX, 0x1); #endif DBGPR("<--configure_tx_dma_channel\n"); } static void configure_rx_dma_channel(UINT QINX, struct DWC_ETH_QOS_prv_data *pdata) { struct DWC_ETH_QOS_rx_wrapper_descriptor *rx_desc_data = GET_RX_WRAPPER_DESC(QINX); DBGPR("-->configure_rx_dma_channel\n"); /*Select Rx Buffer size = 2048bytes */ switch (pdata->rx_buffer_len) { case 16384: DMA_RCR_RBSZ_UDFWR(QINX, 16384); break; case 8192: DMA_RCR_RBSZ_UDFWR(QINX, 8192); break; case 4096: DMA_RCR_RBSZ_UDFWR(QINX, 4096); break; default: /* default is 2K */ DMA_RCR_RBSZ_UDFWR(QINX, 2048); break; } /* program RX watchdog timer */ if (rx_desc_data->use_riwt) DMA_RIWTR_RWT_UDFWR(QINX, rx_desc_data->rx_riwt); else DMA_RIWTR_RWT_UDFWR(QINX, 0); EMACDBG( "%s Rx watchdog timer\n", (rx_desc_data->use_riwt ? "Enabled" : "Disabled")); /* set PBLx8 */ DMA_CR_PBLX8_UDFWR(QINX, 0x1); /* set RX PBL = 16 */ DMA_RCR_PBL_UDFWR(QINX, 32); enable_rx_dma_interrupts(QINX, pdata); /* program split header mode */ DMA_CR_SPH_UDFWR(QINX, pdata->rx_split_hdr); EMACDBG( "%s Rx Split header mode\n", (pdata->rx_split_hdr ? "Enabled" : "Disabled")); /* start RX DMA */ DMA_RCR_ST_UDFWR(QINX, 0x1); DBGPR("<--configure_rx_dma_channel\n"); } /*! * \brief This sequence is used to enable MAC interrupts * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int enable_mac_interrupts(void) { unsigned long varmac_imr; /* Enable following interrupts */ MAC_IMR_RGRD(varmac_imr); varmac_imr = varmac_imr & (unsigned long)(0x1000); MAC_IMR_RGWR(varmac_imr); return Y_SUCCESS; } static INT configure_mac(struct DWC_ETH_QOS_prv_data *pdata) { ULONG VARMAC_MCR; UINT QINX; DBGPR("-->configure_mac\n"); /* Set Tx flow control parameters */ for (QINX = 0; QINX < DWC_ETH_QOS_TX_QUEUE_CNT; QINX++) { /* set Pause Time */ MAC_QTFCR_PT_UDFWR(QINX, 0xffff); #ifndef DWC_ETH_QOS_QUEUE_SELECT_ALGO /* Assign priority for RX flow control */ /* Assign priority for TX flow control */ switch (QINX) { case 0: MAC_TQPM0R_PSTQ0_UDFWR(0); MAC_RQC2R_PSRQ0_UDFWR(0x1 << QINX); break; case 1: MAC_TQPM0R_PSTQ1_UDFWR(1); MAC_RQC2R_PSRQ1_UDFWR(0x1 << QINX); break; case 2: MAC_TQPM0R_PSTQ2_UDFWR(2); MAC_RQC2R_PSRQ2_UDFWR(0x1 << QINX); break; case 3: MAC_TQPM0R_PSTQ3_UDFWR(3); MAC_RQC2R_PSRQ3_UDFWR(0x1 << QINX); break; case 4: MAC_TQPM1R_PSTQ4_UDFWR(4); MAC_RQC3R_PSRQ4_UDFWR(0x1 << QINX); break; case 5: MAC_TQPM1R_PSTQ5_UDFWR(5); MAC_RQC3R_PSRQ5_UDFWR(0x1 << QINX); break; case 6: MAC_TQPM1R_PSTQ6_UDFWR(6); MAC_RQC3R_PSRQ6_UDFWR(0x1 << QINX); break; case 7: MAC_TQPM1R_PSTQ7_UDFWR(7); MAC_RQC3R_PSRQ7_UDFWR(0x1 << QINX); break; } #endif #ifdef DWC_ETH_QOS_QUEUE_SELECT_ALGO configure_tx_queue(QINX); #endif if ((pdata->flow_ctrl & DWC_ETH_QOS_FLOW_CTRL_TX) == DWC_ETH_QOS_FLOW_CTRL_TX) enable_tx_flow_ctrl(QINX); else disable_tx_flow_ctrl(QINX); } /* Set Rx flow control parameters */ if ((pdata->flow_ctrl & DWC_ETH_QOS_FLOW_CTRL_RX) == DWC_ETH_QOS_FLOW_CTRL_RX) enable_rx_flow_ctrl(); else disable_rx_flow_ctrl(); /* Configure for Jumbo frame in MAC */ if (pdata->dev->mtu > DWC_ETH_QOS_ETH_FRAME_LEN) { if (pdata->dev->mtu < DWC_ETH_QOS_MAX_GPSL) { MAC_MCR_JE_UDFWR(0x1); MAC_MCR_WD_UDFWR(0x0); MAC_MCR_GPSLCE_UDFWR(0x0); MAC_MCR_JD_UDFWR(0x0); } else { MAC_MCR_JE_UDFWR(0x0); MAC_MCR_WD_UDFWR(0x1); MAC_MCR_GPSLCE_UDFWR(0x1); MAC_MECR_GPSL_UDFWR(DWC_ETH_QOS_MAX_SUPPORTED_MTU); MAC_MCR_JD_UDFWR(0x1); EMACDBG( "Configured Gaint Packet Size Limit to %d\n", DWC_ETH_QOS_MAX_SUPPORTED_MTU); } EMACDBG("Enabled JUMBO pkt\n"); } else { MAC_MCR_JE_UDFWR(0x0); MAC_MCR_WD_UDFWR(0x0); MAC_MCR_GPSLCE_UDFWR(0x0); MAC_MCR_JD_UDFWR(0x0); EMACDBG("Disabled JUMBO pkt\n"); } /* update the MAC address */ MAC_MA0HR_RGWR(((pdata->dev->dev_addr[5] << 8) | (pdata->dev->dev_addr[4]))); MAC_MA0LR_RGWR(((pdata->dev->dev_addr[3] << 24) | (pdata->dev->dev_addr[2] << 16) | (pdata->dev->dev_addr[1] << 8) | (pdata->dev->dev_addr[0]))); /*Enable MAC Transmit process */ /*Enable MAC Receive process */ /*Enable padding - disabled */ /*Enable CRC stripping - disabled */ MAC_MCR_RGRD(VARMAC_MCR); VARMAC_MCR = VARMAC_MCR & (ULONG)(0xffcfff7c); VARMAC_MCR = VARMAC_MCR | ((0x1) << 0) | ((0x1) << 20) | ((0x1) << 21); #ifndef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT VARMAC_MCR |= ((0x1) << 1); #endif MAC_MCR_RGWR(VARMAC_MCR); switch (pdata->speed) { case SPEED_1000: set_gmii_speed(); break; case SPEED_100: set_mii_speed_100(); break; case SPEED_10: set_mii_speed_10(); break; } if (pdata->hw_feat.rx_coe_sel && ((pdata->dev_state & NETIF_F_RXCSUM) == NETIF_F_RXCSUM)) MAC_MCR_IPC_UDFWR(0x1); #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG configure_mac_for_vlan_pkt(); if (pdata->hw_feat.vlan_hash_en) { /* Configure vlan tag based filtering options*/ config_vlan_filtering( DWC_ETH_QOS_VLAN_FILTERING_EN_DIS, DWC_ETH_QOS_VLAN_HASH_FILTERING, DWC_ETH_QOS_VLAN_INVERSE_MATCHING); } #endif if (pdata->hw_feat.mmc_sel) { /* disable all MMC intterrupt as MMC are managed in SW and * registers are cleared on each READ eventually */ disable_mmc_interrupts(); config_mmc_counters(); } for (QINX = 0; QINX < DWC_ETH_QOS_RX_QUEUE_CNT; QINX++) { #ifdef DWC_ETH_QOS_QUEUE_SELECT_ALGO configure_rx_queue(QINX); #else MAC_RQC0R_RXQEN_UDFWR(QINX, 0x2); #endif /* Fix for unintended stopping of Rx DMA channels when any one Rx DMA channel is stopped */ DMA_RCR_RPF_UDFWR(QINX, 0x1); } configure_avb_ip_rx_filtering(); configure_ptp_rx_filtering(); enable_mac_interrupts(); DBGPR("<--configure_mac\n"); return Y_SUCCESS; } static int DWC_ETH_QOS_yinit_offload(struct DWC_ETH_QOS_prv_data *pdata) { DBGPR("-->DWC_ETH_QOS_yinit_offload\n"); configure_tx_dma_channel(IPA_DMA_TX_CH, pdata); configure_rx_dma_channel(IPA_DMA_RX_CH, pdata); DBGPR("-->DWC_ETH_QOS_yinit_offload\n"); return Y_SUCCESS; } static int DWC_ETH_QOS_yexit_offload(void) { DBGPR("-->DWC_ETH_QOS_yexit_offload\n"); DBGPR("-->DWC_ETH_QOS_yexit_offload\n"); return Y_SUCCESS; } /*! * \brief Initialises device registers. * \details This function initialises device registers. * * \return none */ static INT DWC_ETH_QOS_yinit(struct DWC_ETH_QOS_prv_data *pdata) { UINT QINX; DBGPR("-->DWC_ETH_QOS_yinit\n"); /* reset mmc counters */ MMC_CNTRL_RGWR(0x1); for (QINX = 0; QINX < DWC_ETH_QOS_TX_QUEUE_CNT; QINX++) configure_mtl_queue(QINX, pdata); #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT /* enable tx drop status */ MTL_OMR_DTXSTS_UDFWR(0x1); #endif configure_mac(pdata); configure_dma_sys_bus(pdata); /* Mapping MTL Rx queue and DMA Rx channel. */ if (pdata->res_data->early_eth_en) MTL_RQDCM0R_RGWR(0x3020101); else /* Mapped RX queue 0 to DMA channel 1 */ MTL_RQDCM0R_RGWR(0x3020100); for (QINX = 0; QINX < DWC_ETH_QOS_TX_QUEUE_CNT; QINX++) { if (pdata->ipa_enabled && QINX == IPA_DMA_TX_CH) continue; configure_tx_dma_channel(QINX, pdata); } for (QINX = 0; QINX < DWC_ETH_QOS_RX_QUEUE_CNT; QINX++) { if (pdata->ipa_enabled && QINX == IPA_DMA_RX_CH) continue; configure_rx_dma_channel(QINX, pdata); } #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT_HALFDUPLEX MTL_Q0ROMR_FEP_UDFWR(0x1); MAC_MPFR_RA_UDFWR(0x1); MAC_MCR_BE_UDFWR(0x1); #endif DBGPR("<--DWC_ETH_QOS_yinit\n"); return Y_SUCCESS; } #ifdef DWC_ETH_QOS_CONFIG_PGTEST /*! * \brief This sequence is used to initialize the tx descriptors. * \param[in] pdata */ static void tx_descriptor_init_pg(struct DWC_ETH_QOS_prv_data *pdata, UINT qinx) { struct DWC_ETH_QOS_tx_wrapper_descriptor *tx_desc_data = GET_TX_WRAPPER_DESC(qinx); struct s_TX_NORMAL_DESC *TX_NORMAL_DESC = GET_TX_DESC_PTR(qinx, tx_desc_data->cur_tx); struct DWC_ETH_QOS_tx_buffer *buffer = GET_TX_BUF_PTR(qinx, tx_desc_data->cur_tx); INT i; INT start_index = tx_desc_data->cur_tx; DBGPR("-->tx_descriptor_init_pg\n"); /* initialze all descriptors. */ for (i = 0; i < pdata->tx_queue[qinx].desc_cnt; i++) { /* update buffer 1 address pointer to zero */ TX_NORMAL_DESC_TDES0_ML_WR(TX_NORMAL_DESC->TDES0, 0); /* update buffer 2 address pointer to zero */ TX_NORMAL_DESC_TDES1_ML_WR(TX_NORMAL_DESC->TDES1, 0); /* set all other control bits (IC, TTSE, B2L & B1L) to zero */ TX_NORMAL_DESC_TDES2_ML_WR(TX_NORMAL_DESC->TDES2, 0); /* set all other control bits (OWN, CTXT, FD, LD, CPC, CIC etc) * to zero */ TX_NORMAL_DESC_TDES3_ML_WR(TX_NORMAL_DESC->TDES3, 0); INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1, pdata->tx_queue[qinx].desc_cnt); TX_NORMAL_DESC = GET_TX_DESC_PTR(qinx, tx_desc_data->cur_tx); buffer = GET_TX_BUF_PTR(qinx, tx_desc_data->cur_tx); } /* update the total no of Tx descriptors count */ DMA_TDRLR_RGWR(qinx, (pdata->tx_queue[qinx].desc_cnt - 1)); /* update the starting address of desc chain/ring */ DMA_TDLAR_RGWR(qinx, GET_TX_DESC_DMA_ADDR(qinx, start_index)); DBGPR("<--tx_descriptor_init_pg\n"); } /*! * \brief This sequence is used to initialize the rx descriptors. * \param[in] pdata */ static void rx_descriptor_init_pg(struct DWC_ETH_QOS_prv_data *pdata, UINT QINX) { struct DWC_ETH_QOS_rx_wrapper_descriptor *rx_desc_data = GET_RX_WRAPPER_DESC(QINX); struct DWC_ETH_QOS_rx_buffer *buffer = GET_RX_BUF_PTR(QINX, rx_desc_data->cur_rx); struct s_RX_NORMAL_DESC *RX_NORMAL_DESC = GET_RX_DESC_PTR(QINX, rx_desc_data->cur_rx); INT i; INT start_index = rx_desc_data->cur_rx; INT last_index; DBGPR("-->rx_descriptor_init_pg\n"); /* initialize all desc */ for (i = 0; i < pdata->rx_queue[QINX].desc_cnt; i++) { memset(RX_NORMAL_DESC, 0, sizeof(struct s_RX_NORMAL_DESC)); /* update buffer 1 address pointer */ RX_NORMAL_DESC_RDES0_ML_WR(RX_NORMAL_DESC->RDES0, buffer->dma); /* set to zero */ RX_NORMAL_DESC_RDES1_ML_WR(RX_NORMAL_DESC->RDES1, 0); /* set buffer 2 address pointer to zero */ RX_NORMAL_DESC_RDES2_ML_WR(RX_NORMAL_DESC->RDES2, 0); /* set control bits - OWN, INTE and BUF1V */ RX_NORMAL_DESC_RDES3_ML_WR(RX_NORMAL_DESC->RDES3, (0xc1000000)); INCR_RX_DESC_INDEX(rx_desc_data->cur_rx, 1, pdata->rx_queue[QINX].desc_cnt); RX_NORMAL_DESC = GET_RX_DESC_PTR(QINX, rx_desc_data->cur_rx); buffer = GET_RX_BUF_PTR(QINX, rx_desc_data->cur_rx); } /* update the total no of Rx descriptors count */ DMA_RDRLR_RGWR(QINX, (pdata->rx_queue[QINX].desc_cnt - 1)); /* update the Rx Descriptor Tail Pointer */ last_index = GET_RX_CURRENT_RCVD_LAST_DESC_INDEX(start_index, 0, pdata->rx_queue[QINX].desc_cnt); DMA_RDTP_RPDR_RGWR(QINX, GET_RX_DESC_DMA_ADDR(QINX, last_index)); /* update the starting address of desc chain/ring */ DMA_RDLAR_RGWR(QINX, GET_RX_DESC_DMA_ADDR(QINX, start_index)); DBGPR("<--rx_descriptor_init_pg\n"); } #endif /* end of DWC_ETH_QOS_CONFIG_PGTEST */ /*! * \brief This sequence is used to enable PHY interrupt to EMAC * \retval 0 Success */ static int enable_mac_phy_interrupt(void) { unsigned long varmac_imr = 0; /* PHYIE - PHY Interrupt Enable */ MAC_IMR_RGRD(varmac_imr); varmac_imr = varmac_imr & (unsigned long)(0x1008); varmac_imr = varmac_imr | ((0x1) << 3); MAC_IMR_RGWR(varmac_imr); EMACDBG("Enabled MAC-PHY interrupt\n"); return Y_SUCCESS; } /*! * \brief API to initialize the function pointers. * * \details This function is called in probe to initialize all the * function pointers which are used in other functions to capture * the different device features. * * \param[in] hw_if - pointer to hw_if_struct structure. * * \return void. */ void DWC_ETH_QOS_init_function_ptrs_dev(struct hw_if_struct *hw_if) { DBGPR("-->DWC_ETH_QOS_init_function_ptrs_dev\n"); hw_if->tx_complete = tx_complete; hw_if->tx_window_error = NULL; hw_if->tx_aborted_error = tx_aborted_error; hw_if->tx_carrier_lost_error = tx_carrier_lost_error; hw_if->tx_fifo_underrun = tx_fifo_underrun; hw_if->tx_get_collision_count = NULL; hw_if->tx_handle_aborted_error = NULL; hw_if->tx_update_fifo_threshold = NULL; hw_if->tx_config_threshold = NULL; hw_if->set_promiscuous_mode = set_promiscuous_mode; hw_if->set_all_multicast_mode = set_all_multicast_mode; hw_if->set_multicast_list_mode = set_multicast_list_mode; hw_if->set_unicast_mode = set_unicast_mode; hw_if->enable_rx_csum = enable_rx_csum; hw_if->disable_rx_csum = disable_rx_csum; hw_if->get_rx_csum_status = get_rx_csum_status; hw_if->write_phy_regs = write_phy_regs; hw_if->read_phy_regs = read_phy_regs; hw_if->set_full_duplex = set_full_duplex; hw_if->set_half_duplex = set_half_duplex; hw_if->set_mii_speed_100 = set_mii_speed_100; hw_if->set_mii_speed_10 = set_mii_speed_10; hw_if->set_gmii_speed = set_gmii_speed; /* for PMT */ hw_if->start_dma_rx = start_dma_rx; hw_if->stop_dma_rx = stop_dma_rx; hw_if->start_dma_tx = start_dma_tx; hw_if->stop_dma_tx = stop_dma_tx; hw_if->start_mac_tx_rx = start_mac_tx_rx; hw_if->stop_mac_tx_rx = stop_mac_tx_rx; hw_if->pre_xmit = pre_transmit; hw_if->dev_read = device_read; hw_if->init = DWC_ETH_QOS_yinit; hw_if->exit = DWC_ETH_QOS_yexit; hw_if->init_offload = DWC_ETH_QOS_yinit_offload; hw_if->exit_offload = DWC_ETH_QOS_yexit_offload; /* Descriptor related Sequences have to be initialized here */ hw_if->tx_desc_init = tx_descriptor_init; hw_if->rx_desc_init = rx_descriptor_init; hw_if->rx_desc_reset = rx_descriptor_reset; hw_if->tx_desc_reset = tx_descriptor_reset; hw_if->get_tx_desc_ls = get_tx_descriptor_last; hw_if->get_tx_desc_ctxt = get_tx_descriptor_ctxt; hw_if->update_rx_tail_ptr = update_rx_tail_ptr; /* for FLOW ctrl */ hw_if->enable_rx_flow_ctrl = enable_rx_flow_ctrl; hw_if->disable_rx_flow_ctrl = disable_rx_flow_ctrl; hw_if->enable_tx_flow_ctrl = enable_tx_flow_ctrl; hw_if->disable_tx_flow_ctrl = disable_tx_flow_ctrl; /* for PMT operation */ hw_if->enable_magic_pmt = enable_magic_pmt_operation; hw_if->disable_magic_pmt = disable_magic_pmt_operation; hw_if->enable_remote_pmt = enable_remote_pmt_operation; hw_if->disable_remote_pmt = disable_remote_pmt_operation; hw_if->configure_rwk_filter = configure_rwk_filter_registers; /* for TX vlan control */ hw_if->enable_vlan_reg_control = configure_reg_vlan_control; hw_if->enable_vlan_desc_control = configure_desc_vlan_control; /* for rx vlan stripping */ hw_if->config_rx_outer_vlan_stripping = config_rx_outer_vlan_stripping; hw_if->config_rx_inner_vlan_stripping = config_rx_inner_vlan_stripping; /* for sa(source address) insert/replace */ hw_if->configure_mac_addr0_reg = configure_mac_addr0_reg; hw_if->configure_mac_addr1_reg = configure_mac_addr1_reg; hw_if->configure_sa_via_reg = configure_sa_via_reg; /* for RX watchdog timer */ hw_if->config_rx_watchdog = config_rx_watchdog_timer; /* for RX and TX threshold config */ hw_if->config_rx_threshold = config_rx_threshold; hw_if->config_tx_threshold = config_tx_threshold; /* for RX and TX Store and Forward Mode config */ hw_if->config_rsf_mode = config_rsf_mode; hw_if->config_tsf_mode = config_tsf_mode; /* for TX DMA Operating on Second Frame config */ hw_if->config_osf_mode = config_osf_mode; /* for INCR/INCRX config */ hw_if->config_incr_incrx_mode = config_incr_incrx_mode; /* for AXI PBL config */ hw_if->config_axi_pbl_val = config_axi_pbl_val; /* for AXI WORL config */ hw_if->config_axi_worl_val = config_axi_worl_val; /* for AXI RORL config */ hw_if->config_axi_rorl_val = config_axi_rorl_val; /* for RX and TX PBL config */ hw_if->config_rx_pbl_val = config_rx_pbl_val; hw_if->get_rx_pbl_val = get_rx_pbl_val; hw_if->config_tx_pbl_val = config_tx_pbl_val; hw_if->get_tx_pbl_val = get_tx_pbl_val; hw_if->config_pblx8 = config_pblx8; hw_if->disable_rx_interrupt = disable_rx_interrupt; hw_if->enable_rx_interrupt = enable_rx_interrupt; /* for handling MMC */ hw_if->disable_mmc_interrupts = disable_mmc_interrupts; hw_if->config_mmc_counters = config_mmc_counters; /* for handling split header */ hw_if->config_split_header_mode = config_split_header_mode; hw_if->config_header_size = config_header_size; hw_if->set_dcb_algorithm = set_dcb_algorithm; hw_if->set_dcb_queue_weight = set_dcb_queue_weight; hw_if->set_tx_queue_operating_mode = set_tx_queue_operating_mode; hw_if->set_avb_algorithm = set_avb_algorithm; hw_if->config_credit_control = config_credit_control; hw_if->config_send_slope = config_send_slope; hw_if->config_idle_slope = config_idle_slope; hw_if->config_high_credit = config_high_credit; hw_if->config_low_credit = config_low_credit; hw_if->config_slot_num_check = config_slot_num_check; hw_if->config_advance_slot_num_check = config_advance_slot_num_check; #ifdef DWC_ETH_QOS_CONFIG_PGTEST hw_if->tx_desc_init_pg = tx_descriptor_init_pg; hw_if->rx_desc_init_pg = rx_descriptor_init_pg; hw_if->set_ch_arb_weights = set_ch_arb_weights; hw_if->config_slot_interrupt = config_slot_interrupt; hw_if->set_slot_count = set_slot_count; hw_if->set_tx_rx_prio_policy = set_tx_rx_prio_policy; hw_if->set_tx_rx_prio = set_tx_rx_prio; hw_if->set_tx_rx_prio_ratio = set_tx_rx_prio_ratio; hw_if->set_dma_tx_arb_algorithm = set_dma_tx_arb_algorithm; hw_if->prepare_dev_pktgen = prepare_dev_pktgen; #endif /* end of DWC_ETH_QOS_CONFIG_PGTEST */ /* for hw time stamping */ hw_if->config_hw_time_stamping = config_hw_time_stamping; hw_if->config_sub_second_increment = config_sub_second_increment; hw_if->config_default_addend = config_default_addend; hw_if->init_systime = init_systime; hw_if->config_addend = config_addend; hw_if->adjust_systime = adjust_systime; hw_if->get_systime = get_systime; hw_if->get_tx_tstamp_status = get_tx_tstamp_status; hw_if->get_tx_tstamp = get_tx_tstamp; hw_if->get_tx_tstamp_status_via_reg = get_tx_tstamp_status_via_reg; hw_if->get_tx_tstamp_via_reg = get_tx_tstamp_via_reg; hw_if->rx_tstamp_available = rx_tstamp_available; hw_if->get_rx_tstamp_status = get_rx_tstamp_status; hw_if->get_rx_tstamp = get_rx_tstamp; hw_if->drop_tx_status_enabled = drop_tx_status_enabled; /* for l3 and l4 layer filtering */ hw_if->config_l2_da_perfect_inverse_match = config_l2_da_perfect_inverse_match; hw_if->update_mac_addr32_127_low_high_reg = update_mac_addr32_127_low_high_reg; hw_if->update_mac_addr1_31_low_high_reg = update_mac_addr1_31_low_high_reg; hw_if->update_hash_table_reg = update_hash_table_reg; hw_if->config_mac_pkt_filter_reg = config_mac_pkt_filter_reg; hw_if->config_l3_l4_filter_enable = config_l3_l4_filter_enable; hw_if->config_l3_filters = config_l3_filters; hw_if->update_ip4_addr0 = update_ip4_addr0; hw_if->update_ip4_addr1 = update_ip4_addr1; hw_if->update_ip6_addr = update_ip6_addr; hw_if->config_l4_filters = config_l4_filters; hw_if->update_l4_sa_port_no = update_l4_sa_port_no; hw_if->update_l4_da_port_no = update_l4_da_port_no; /* for VLAN filtering */ hw_if->get_vlan_hash_table_reg = get_vlan_hash_table_reg; hw_if->update_vlan_hash_table_reg = update_vlan_hash_table_reg; hw_if->update_vlan_id = update_vlan_id; hw_if->config_vlan_filtering = config_vlan_filtering; hw_if->config_mac_for_vlan_pkt = configure_mac_for_vlan_pkt; hw_if->get_vlan_tag_comparison = get_vlan_tag_comparison; hw_if->config_vlan_tag_data = config_vlan_tag_data; /* for differnet PHY interconnect */ hw_if->control_an = control_an; hw_if->get_an_adv_pause_param = get_an_adv_pause_param; hw_if->get_an_adv_duplex_param = get_an_adv_duplex_param; hw_if->get_lp_an_adv_pause_param = get_lp_an_adv_pause_param; hw_if->get_lp_an_adv_duplex_param = get_lp_an_adv_duplex_param; /* for EEE */ hw_if->set_eee_mode = set_eee_mode; hw_if->reset_eee_mode = reset_eee_mode; hw_if->set_eee_pls = set_eee_pls; hw_if->set_eee_timer = set_eee_timer; hw_if->get_lpi_status = get_lpi_status; hw_if->set_lpi_tx_automate = set_lpi_tx_automate; hw_if->set_lpi_tx_auto_entry_timer_en = set_lpi_tx_auto_entry_timer_en; hw_if->set_lpi_tx_auto_entry_timer = set_lpi_tx_auto_entry_timer; hw_if->set_lpi_us_tic_counter = set_lpi_us_tic_counter; /* for ARP */ hw_if->config_arp_offload = config_arp_offload; hw_if->update_arp_offload_ip_addr = update_arp_offload_ip_addr; /* for MAC loopback */ hw_if->config_mac_loopback_mode = config_mac_loopback_mode; /* for PFC */ hw_if->config_pfc = config_pfc; /* for MAC Double VLAN Processing config */ hw_if->config_tx_outer_vlan = config_tx_outer_vlan; hw_if->config_tx_inner_vlan = config_tx_inner_vlan; hw_if->config_svlan = config_svlan; hw_if->config_dvlan = config_dvlan; hw_if->config_rx_outer_vlan_stripping = config_rx_outer_vlan_stripping; hw_if->config_rx_inner_vlan_stripping = config_rx_inner_vlan_stripping; /* for PTP Offloading */ hw_if->config_ptpoffload_engine = config_ptpoffload_engine; /* For enabling PHY interrupt to EMAC */ hw_if->enable_mac_phy_interrupt = enable_mac_phy_interrupt; DBGPR("<--DWC_ETH_QOS_init_function_ptrs_dev\n"); }