/* ========================================================================= * 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. * ========================================================================= */ /* * Includes Intel Corporation's changes dated: 2020. * Changed portions - Copyright 2020, Intel Corporation. */ /*!@file: DWC_ETH_QOS_dev.c * @brief: Driver functions. */ #include "DWC_ETH_QOS_yheader.h" #include "DWC_ETH_QOS_yapphdr.h" #include "DWC_ETH_QOS_yregacc.h" /*! * \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(uint32_t enb_dis) { DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_LM, enb_dis); return Y_SUCCESS; } /* enable/disable PFC(Priority Based Flow Control) */ static void config_pfc(int enb_dis) { DWC_REG_WR_BIT(MAC_RQFCR, MAC_RQFCR_PFCE, enb_dis); } /*! * \brief This sequence is used to configure mac vlan processing feature. * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_tx_vlan(uint32_t op_type, uint32_t vlan_tag, bool inner_tag) { uint8_t idx = (inner_tag)? MAC_VLAN_IN_TAG_IDX : MAC_VLAN_OUT_TAG_IDX; DBGPR("--> config_tx_vlan()\n"); DWC_REG_WR_BIT(MAC_VLANTIR(idx), MAC_VLANTIR_VLTI, 0x0); DWC_REG_WR_FIELD(MAC_VLANTIR(idx), MAC_VLANTIR_VLT, vlan_tag); DWC_REG_WR_BIT(MAC_VLANTIR(idx), MAC_VLANTIR_VLP, 0x1); DWC_REG_WR_FIELD(MAC_VLANTIR(idx), MAC_VLANTIR_VLC, op_type); if (op_type == DWC_ETH_QOS_DVLAN_NONE) { DWC_REG_WR_BIT(MAC_VLANTIR(idx), MAC_VLANTIR_VLP, 0x0); DWC_REG_WR_FIELD(MAC_VLANTIR(idx), MAC_VLANTIR_VLT, 0x0); } DBGPR("<-- config_tx_vlan()\n"); return Y_SUCCESS; } static int config_svlan(uint32_t flags) { int ret = Y_SUCCESS; printk(KERN_ALERT "--> config_svlan()\n"); DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_ESVL, 0x1); if (flags == DWC_ETH_QOS_DVLAN_NONE) { DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_ESVL, 0x0); DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_IN_TAG_IDX), MAC_VLANTIR_CSVL, 0x0); DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_OUT_TAG_IDX), MAC_VLANTIR_CSVL, 0x0); } else if (flags == DWC_ETH_QOS_DVLAN_INNER) { DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_IN_TAG_IDX), MAC_VLANTIR_CSVL, 0x1); } else if (flags == DWC_ETH_QOS_DVLAN_OUTER) { DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_OUT_TAG_IDX), MAC_VLANTIR_CSVL, 0x1); } else if (flags == DWC_ETH_QOS_DVLAN_BOTH) { DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_IN_TAG_IDX), MAC_VLANTIR_CSVL, 0x1); DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_OUT_TAG_IDX), MAC_VLANTIR_CSVL, 0x1); } else { printk(KERN_ALERT "ERROR : double VLAN enable SVLAN configuration - Invalid argument"); ret = Y_FAILURE; } printk(KERN_ALERT "<-- config_svlan()\n"); return ret; } static void config_dvlan(bool enb_dis) { DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_EDVLP, 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) { DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_ARPEN, 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(uint8_t addr[], uint32_t version) { uint32_t mac_arpa = (version == MAC_VER_4_00)? MAC_ARPA_4_00 : MAC_ARPA_4_10; DWC_REG_WR(mac_arpa, 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 uint32_t get_lpi_status(void) { return DWC_REG_RD(MAC_LPI_CSR); } /*! * \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) { DWC_REG_WR_BIT(MAC_LPI_CSR, MAC_LPI_CSR_LPIEN, 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) { DWC_REG_WR_BIT(MAC_LPI_CSR, MAC_LPI_CSR_LPITXA, 0x0); DWC_REG_WR_BIT(MAC_LPI_CSR, MAC_LPI_CSR_LPIEN, 0x0); 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) { DWC_REG_WR_BIT(MAC_LPI_CSR, MAC_LPI_CSR_PLS, 0x1); } else { DWC_REG_WR_BIT(MAC_LPI_CSR, MAC_LPI_CSR_PLS, 0x0); } 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. */ DWC_REG_WR_FIELD(MAC_LPI_TCR, MAC_LPI_TCR_TWT, 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. */ DWC_REG_WR_FIELD(MAC_LPI_TCR, MAC_LPI_TCR_LST, lpi_lst); return Y_SUCCESS; } static int set_lpi_tx_automate(void) { DWC_REG_WR_BIT(MAC_LPI_CSR, MAC_LPI_CSR_LPITXA, 0x1); 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) { DWC_REG_WR_BIT(MAC_ANCR, MAC_ANCR_ANE, enable); DWC_REG_WR_BIT(MAC_ANCR, MAC_ANCR_RAN, 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) { return DWC_REG_RD_FIELD(MAC_AADR, MAC_AADR_PSE); } /*! * \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) { return DWC_REG_RD_BIT(MAC_AADR, MAC_AADR_FD); } /*! * \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) { return DWC_REG_RD_FIELD(MAC_ALPAR, MAC_ALPAR_PSE); } /*! * \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) { return DWC_REG_RD_BIT(MAC_ALPAR, MAC_ALPAR_FD); } static uint32_t get_vlan_tag_comparison(void) { return DWC_REG_RD_BIT(MAC_VLANTR, MAC_VLANTR_ETV); } /*! * \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) { DWC_REG_WR_BIT(MAC_PFR, MAC_PFR_VTFE, filter_enb_dis); DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_VTIM, perfect_inverse_match); DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_VTHM, perfect_hash_filtering); /* To enable only HASH filtering then VL/VID * should be > zero. Hence we are writting 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) DWC_REG_WR_FIELD(MAC_VLANTR, MAC_VLANTR_VL, 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(uint16_t vid) { DWC_REG_WR_FIELD(MAC_VLANTR, MAC_VLANTR_VL, 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(uint16_t data) { DWC_REG_WR_FIELD(MAC_VLANHTR, MAC_VLANHTR_VLHT, 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) { return DWC_REG_RD_FIELD(MAC_VLANHTR, MAC_VLANHTR_VLHT); } /*! * \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, uint16_t port_no) { DWC_REG_WR_FIELD(MAC_L4A(filter_no), MAC_L4A_L4DP, 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, uint16_t port_no) { DWC_REG_WR_FIELD(MAC_L4A(filter_no), MAC_L4A_L4SP, 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) { DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4PEN, tcp_udp_match); if (src_dst_port_match == 0) { if (enb_dis == 1) { /* Enable L4 filters for SOURCE Port No matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4SPM, 0x1); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4SPIM, perfect_inverse_match); } else { /* Disable L4 filters for SOURCE Port No matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4SPM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4SPIM, 0x0); } } else { if (enb_dis == 1) { /* Enable L4 filters for DESTINATION port No matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4DPM, 0x1); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4DPIM, perfect_inverse_match); } else { /* Disable L4 filters for DESTINATION port No matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4DPM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L4DPIM, 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, uint16_t addr[]) { /* update Bits[31:0] of 128-bit IP addr */ DWC_REG_WR(MAC_L3AxRy(0, filter_no), (addr[7] | (addr[6] << 16))); /* update Bits[63:32] of 128-bit IP addr */ DWC_REG_WR(MAC_L3AxRy(1, filter_no), (addr[5] | (addr[4] << 16))); /* update Bits[95:64] of 128-bit IP addr */ DWC_REG_WR(MAC_L3AxRy(2, filter_no), (addr[3] | (addr[2] << 16))); /* update Bits[127:96] of 128-bit IP addr */ DWC_REG_WR(MAC_L3AxRy(3, 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, uint8_t addr[]) { DWC_REG_WR(MAC_L3AxRy(1, 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, uint8_t addr[]) { DWC_REG_WR(MAC_L3AxRy(0, 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) { DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3PEN, 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 */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAM, 0x1); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAIM, perfect_inverse_match); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAIM, 0x0); } else { /* Enable L3 filters for IPv6 DESTINATION addr matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAIM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAM, 0x1); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAIM, perfect_inverse_match); } } else { /* Disable L3 filters for IPv6 SOURCE/DESTINATION addr matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3PEN, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAIM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAIM, 0x0); } } else { if (src_dst_addr_match == 0) { if (enb_dis == 1) { /* Enable L3 filters for IPv4 SOURCE addr matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAM, 0x1); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAIM, perfect_inverse_match); } else { /* Disable L3 filters for IPv4 SOURCE addr matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3SAIM, 0x0); } } else { if (enb_dis == 1) { /* Enable L3 filters for IPv4 DESTINATION addr matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAM, 0x1); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAIM, perfect_inverse_match); } else { /* Disable L3 filters for IPv4 DESTINATION addr matching */ DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAM, 0x0); DWC_REG_WR_BIT(MAC_L3L4CR(filter_no), MAC_L3L4CR_L3DAIM, 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(uint8_t pr_mode, uint8_t huc_mode, uint8_t hmc_mode, uint8_t pm_mode, uint8_t hpf_mode) { uint32_t varMAC_MPFR = DWC_REG_RD(MAC_PFR); /* configure device in different modes */ /* promiscuous, hash unicast, hash multicast, */ /* all multicast and perfect/hash filtering mode. */ varMAC_MPFR &= 0x803103e8; VAR32_SET_BIT(varMAC_MPFR, MAC_PFR_PR, pr_mode); VAR32_SET_BIT(varMAC_MPFR, MAC_PFR_HUC, huc_mode); VAR32_SET_BIT(varMAC_MPFR, MAC_PFR_HMC, hmc_mode); VAR32_SET_BIT(varMAC_MPFR, MAC_PFR_PM, pm_mode); VAR32_SET_BIT(varMAC_MPFR, MAC_PFR_HPF, hpf_mode); DWC_REG_WR(MAC_PFR, varMAC_MPFR); CFG_PRINT("[%s] MAC_MPFR = 0x%08x\n", __FUNCTION__, 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) { DWC_REG_WR_BIT(MAC_PFR, MAC_PFR_IPFE, 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) { DWC_REG_WR_BIT(MAC_PFR, MAC_PFR_DAIF, 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 for L2 layer filtering * \param[in] idx * \param[in] addr * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int update_mac_addr(int idx, uint8_t addr[]) { DWC_REG_WR_BIT(MAC_MAHR(idx), MAC_MAHR_AE, 0x1); DWC_REG_WR_FIELD(MAC_MAHR(idx), MAC_MAHR_ADDRHI, (addr[4] | (addr[5] << 8))); DWC_REG_WR(MAC_MALR(idx), (addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24))); 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, uint32_t data) { DWC_REG_WR(MAC_HTR(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) { return DWC_REG_RD_BIT(MTL_OMR, MTL_OMR_DTXSTS); } /*! * \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(unsigned long ptp_clock) { uint32_t val; uint32_t varMAC_TCR; varMAC_TCR = DWC_REG_RD(MAC_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 (VAR32_GET_BIT(varMAC_TCR, MAC_TCR_TSCFUPDT)) { val = ((1 * ONE_SEC_TO_NS) / 50000000); } else { val = ((1 * ONE_SEC_TO_NS) / ptp_clock); } /* 0.465ns accuracy */ if (VAR32_GET_BIT(varMAC_TCR, MAC_TCR_TSCTRLSSR)) { val = (val * 1000) / 465; } DWC_REG_WR_FIELD(MAC_SSIR, MAC_SSIR_SSINC, val); 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 uint64_t get_systime(void) { uint64_t ns = DWC_REG_RD_FIELD(MAC_STNR, MAC_STNR_TSSS); /* Convert sec/high time value to nanosecond */ ns += DWC_REG_RD(MAC_STSR) * (uint64_t)ONE_SEC_TO_NS; 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(uint32_t sec, uint32_t nsec, int add_sub, bool one_nsec_accuracy) { uint32_t retryCount = 100000; /* Wait for previous(if any) time adjust/update to complete. */ while (DWC_REG_RD_BIT(MAC_TCR, MAC_TCR_TSUPDT) && --retryCount) mdelay(1); if (!retryCount) { return -Y_FAILURE; } 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); } DWC_REG_WR(MAC_STSUR, sec); DWC_REG_WR_FIELD(MAC_STNUR, MAC_STNUR_TSSS, nsec); DWC_REG_WR_BIT(MAC_STNUR, MAC_STNUR_ADDSUB, add_sub); /* issue command to initialize system time with the value */ /* specified in MAC_STSUR and MAC_STNSUR. */ DWC_REG_WR_BIT(MAC_TCR, MAC_TCR_TSUPDT, 0x1); /* Wait for present time initialize to complete. */ retryCount = 100000; while (DWC_REG_RD_BIT(MAC_TCR, MAC_TCR_TSUPDT) && --retryCount) mdelay(1); if (!retryCount) { return -Y_FAILURE; } 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(uint32_t data) { uint32_t retryCount = 100000; /* Wait for previous(if any) added update to complete. */ while (DWC_REG_RD_BIT(MAC_TCR, MAC_TCR_TSADDREG) && --retryCount) mdelay(1); if (!retryCount) { return -Y_FAILURE; } DWC_REG_WR(MAC_TAR, data); /* Issue command to update the added value */ DWC_REG_WR_BIT(MAC_TCR, MAC_TCR_TSADDREG, 0x1); /* Wait for present added update to complete. */ retryCount = 100000; while (DWC_REG_RD_BIT(MAC_TCR, MAC_TCR_TSADDREG) && --retryCount) mdelay(1); if (!retryCount) { return -Y_FAILURE; } 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(uint32_t sec, uint32_t nsec) { uint32_t retryCount = 100000; /* Wait for previous(if any) time initialize to complete. */ while (DWC_REG_RD_BIT(MAC_TCR, MAC_TCR_TSINIT) && --retryCount) mdelay(1); if (!retryCount) { return -Y_FAILURE; } DWC_REG_WR(MAC_STSUR, sec); DWC_REG_WR(MAC_STNUR, nsec); /* issue command to initialize system time with the value */ /* specified in MAC_STSUR and MAC_STNSUR. */ DWC_REG_WR_BIT(MAC_TCR, MAC_TCR_TSINIT, 0x1); /* Wait for present time initialize to complete. */ retryCount = 100000; while (DWC_REG_RD_BIT(MAC_TCR, MAC_TCR_TSINIT) && --retryCount) mdelay(1); if (!retryCount) { return -Y_FAILURE; } 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(uint32_t config_val) { DWC_REG_WR(MAC_TCR, 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 uint64_t get_rx_tstamp(rx_descriptor_t *rxdesc) { return (rxdesc->RDES0 + (rxdesc->RDES1 * (uint64_t)ONE_SEC_TO_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 uint32_t get_rx_tstamp_status(rx_descriptor_t *rxdesc) { uint32_t varOWN; uint32_t varCTXT; /* check for own bit and CTXT bit */ varOWN = VAR32_GET_BIT(rxdesc->RDES3, NORMAL_WB_RDES3_OWN); varCTXT = VAR32_GET_BIT(rxdesc->RDES3, NORMAL_WB_RDES3_TYPE); if ((varOWN == 0) && (varCTXT == 0x1)) { if ((rxdesc->RDES0 == 0xffffffff) && (rxdesc->RDES1 == 0xffffffff)) { /* time stamp is corrupted */ return 2; } else { /* time stamp is valid */ return 1; } } else { /* 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 uint32_t rx_tstamp_available(rx_descriptor_t *rxdesc) { uint32_t varRS1V; uint32_t varTSA = 0; varRS1V = VAR32_GET_BIT(rxdesc->RDES3, NORMAL_WB_RDES3_RS1V); if (varRS1V == 1) { varTSA = VAR32_GET_BIT(rxdesc->RDES1, NORMAL_WB_RDES1_TSA); } return varTSA; } /*! * \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 uint64_t get_tx_tstamp_via_reg(void) { uint64_t ns = DWC_REG_RD_FIELD(MAC_TxTSNR, MAC_TxTSNR_TXTSSTSLO); ns += (DWC_REG_RD(MAC_TxTSSR) * (uint64_t)ONE_SEC_TO_NS); 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 uint32_t get_tx_tstamp_status_via_reg(void) { uint32_t ret = 0; /* Device is configured to overwrite the timestamp of */ /* earlier packet if driver has not yet read it. */ if (!DWC_REG_RD_BIT(MAC_TCR, MAC_TCR_TXTSSTSM)) { /* Timestamp of the current pkt is ignored or not captured */ ret = (DWC_REG_RD_BIT(MAC_TxTSNR, MAC_TxTSNR_TXTSSTSMIS))? 0 : 1; } return ret; } /*! * \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 uint64_t get_tx_tstamp(tx_descriptor_t *txdesc) { return (txdesc->TDES0 + (txdesc->TDES1 * (uint64_t)ONE_SEC_TO_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 uint32_t get_tx_tstamp_status(tx_descriptor_t *txdesc) { return VAR32_GET_BIT(txdesc->TDES3, NORMAL_WB_TDES3_TTSS); } /*! * \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(uint32_t qInx, uint16_t sph_en) { DWC_REG_WR_BIT(DMA_CR(qInx), DMA_CR_SPH, 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(uint16_t header_size) { if (header_size == 64) { DWC_REG_WR_FIELD(MAC_MECR, MAC_MECR_HDSMS, 0); } else if (header_size == 128) { DWC_REG_WR_FIELD(MAC_MECR, MAC_MECR_HDSMS, 0x1); } else if (header_size == 256) { DWC_REG_WR_FIELD(MAC_MECR, MAC_MECR_HDSMS, 0x2); } else if (header_size == 512) { DWC_REG_WR_FIELD(MAC_MECR, MAC_MECR_HDSMS, 0x3); } else { DWC_REG_WR_FIELD(MAC_MECR, MAC_MECR_HDSMS, 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(uint32_t qInx, uint32_t q_mode) { DWC_REG_WR_FIELD(MTL_TXQ_OMR(qInx), MTL_TXQ_OMR_TXQEN, 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(uint32_t qInx, uint8_t avb_algo) { DWC_REG_WR_BIT(MTL_TXQ_ECR(qInx), MTL_TXQ_ECR_AVALG, 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(uint32_t qInx, uint32_t cc) { DWC_REG_WR_BIT(MTL_TXQ_ECR(qInx), MTL_TXQ_ECR_CC, 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(uint32_t qInx, uint32_t sendSlope) { DWC_REG_WR_FIELD(MTL_TXQ_SSCR(qInx), MTL_TXQ_SSCR_SSC, 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(uint32_t qInx, uint32_t idleSlope) { DWC_REG_WR_FIELD(MTL_TXQ_QW(qInx), MTL_TXQ_QW_ISCQW, 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(uint32_t qInx, uint32_t lowCredit) { int lowCredit_neg = lowCredit; printk(KERN_CRIT "lowCredit = %08x lowCredit_neg:%08x\n", lowCredit, lowCredit_neg); DWC_REG_WR_FIELD(MTL_TXQ_LCR(qInx), MTL_TXQ_LCR_LC, lowCredit_neg); DWC_REG_WR_FIELD(MTL_TXQ_LCR(qInx), MTL_TXQ_LCR_LC, lowCredit); 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(uint32_t qInx, uint8_t slot_check) { DWC_REG_WR_BIT(DMA_SFCSR(qInx), DMA_SFCSR_ESC, 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(uint32_t qInx, uint8_t adv_slot_check) { DWC_REG_WR_BIT(DMA_SFCSR(qInx), DMA_SFCSR_ASC, 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(uint32_t qInx, uint32_t hiCredit) { DWC_REG_WR_FIELD(MTL_TXQ_HCR(qInx), MTL_TXQ_HCR_HC, 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(uint32_t qInx, uint32_t q_weight) { DWC_REG_WR_FIELD(MTL_TXQ_QW(qInx), MTL_TXQ_QW_ISCQW, 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(uint8_t dcb_algo) { DWC_REG_WR_FIELD(MTL_OMR, MTL_OMR_SCHALG, 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 */ uint8_t get_tx_queue_count(void) { return (DWC_REG_RD_FIELD(MAC_HF2R, MAC_HF2R_TXQCNT) + 1); } /*! * \brief This sequence is used to get Rx queue count * \param[in] count * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ uint8_t get_rx_queue_count(void) { return (DWC_REG_RD_FIELD(MAC_HF2R, MAC_HF2R_RXQCNT) + 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 */ DWC_REG_WR(MMC_TX_IMR, 0xffffffff); /* disable all RX interrupts */ DWC_REG_WR(MMC_RX_IMR, 0xffffffff); /* disable all IPC RX interrupts */ DWC_REG_WR(MMC_IPC_RX_IMR, 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) { uint32_t varMMC_CNTRL; varMMC_CNTRL = DWC_REG_RD(MMC_CR); varMMC_CNTRL &= 0x10a; /* set COUNTER RESET */ VAR32_SET_BIT(varMMC_CNTRL, MMC_CR_CNTRST, 0x1); /* set RESET ON READ */ VAR32_SET_BIT(varMMC_CNTRL, MMC_CR_RSTONRD, 0x1); /* set COUNTER PRESET */ VAR32_SET_BIT(varMMC_CNTRL, MMC_CR_CNTPRST, 0x1); /* set FULL_HALF PRESET */ VAR32_SET_BIT(varMMC_CNTRL, MMC_CR_CNTPRSTLVL, 0x1); DWC_REG_WR(MMC_CR, varMMC_CNTRL); return Y_SUCCESS; } /* Disable given DMA channel rx interrupts */ static void disable_rx_interrupt(uint32_t qInx, hw_config_t *hw_cfg) { /* Disable Rx interrupts */ VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_RBUE, 0x0); VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_RIE, 0x0); DWC_REG_WR(DMA_IER(qInx), hw_cfg->dma_ier); /* Clear any Rx pending interrupt */ DWC_REG_WR_BIT(DMA_SR(qInx), DMA_SR_RBU, 0x1); DWC_REG_WR_BIT(DMA_SR(qInx), DMA_SR_RI, 0x1); } /* Enable given DMA channel rx interrupts */ static void enable_rx_interrupt(uint32_t qInx, hw_config_t *hw_cfg) { VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_RBUE, 0x1); VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_RIE, 0x1); DWC_REG_WR(DMA_IER(qInx), hw_cfg->dma_ier); } /*Disable given DMA channel tx interrupts */ static void disable_tx_interrupt(uint32_t qInx, hw_config_t *hw_cfg) { /* Disable and clear Tx interrupts */ #ifdef DWC_ETH_QOS_TXPOLLING_MODE_ENABLE VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_TIE, 0x0); #else VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_TBUE, 0x0) #endif DWC_REG_WR(DMA_IER(qInx), hw_cfg->dma_ier); DWC_REG_WR_BIT(DMA_SR(qInx), DMA_SR_TI, 0x1); DWC_REG_WR_BIT(DMA_SR(qInx), DMA_SR_TBU, 0x1); } /* Enable given DMA channel tx interrupts */ static void enable_tx_interrupt(uint32_t qInx, hw_config_t *hw_cfg) { #ifdef DWC_ETH_QOS_TXPOLLING_MODE_ENABLE VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_TIE, 0x1); #else VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_TBUE, 0x1) #endif DWC_REG_WR(DMA_IER(qInx), hw_cfg->dma_ier); } static void configure_sa_via_reg(uint32_t cmd) { DWC_REG_WR_FIELD(MAC_MCR, MAC_MCR_SARC, cmd); } static void config_rx_outer_vlan_stripping(uint32_t cmd) { DWC_REG_WR_FIELD(MAC_VLANTR, MAC_VLANTR_EVLS, cmd); } static void config_rx_inner_vlan_stripping(uint32_t cmd) { DWC_REG_WR_FIELD(MAC_VLANTR, MAC_VLANTR_EIVLS, cmd); } static void config_ptpoffload_engine(uint32_t pto_cr, uint32_t mc_uc) { DWC_REG_WR(MAC_PTO_CR, pto_cr); DWC_REG_WR_BIT(MAC_TCR, MAC_TCR_TSENMACADDR, mc_uc); } static void configure_reg_vlan_control(struct DWC_ETH_QOS_tx_wrapper_descriptor *desc_data) { uint32_t vlan_tir = 0; VAR32_SET_BIT(vlan_tir, MAC_VLANTIR_VLP, 0x1); VAR32_SET_FIELD(vlan_tir, MAC_VLANTIR_VLC, desc_data->tx_vlan_tag_ctrl); VAR32_SET_FIELD(vlan_tir, MAC_VLANTIR_VLT, desc_data->vlan_tag_id); DWC_REG_WR(MAC_VLANTIR(MAC_VLAN_OUT_TAG_IDX), vlan_tir); } static void configure_desc_vlan_control(struct DWC_ETH_QOS_prv_data *pdata) { DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_OUT_TAG_IDX), MAC_VLANTIR_VLTI, 0x1); } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int configure_mac_for_vlan_pkt(void) { /* Enable VLAN Tag stripping always */ DWC_REG_WR_FIELD(MAC_VLANTR, MAC_VLANTR_EVLS, 0x3); /* Enable operation on the outer VLAN Tag, if present */ DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_ERIVLT, 0x0); /* Disable double VLAN Tag processing on TX and RX */ DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_EDVLP, 0x0); /* Enable VLAN Tag in RX Status. */ DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_EVLRXS, 0x1); /* Disable VLAN Type Check */ DWC_REG_WR_BIT(MAC_VLANTR, MAC_VLANTR_DOVLTC, 0x1); /* configure MAC to get VLAN Tag to be inserted/replaced from */ /* TX descriptor(context desc) */ DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_OUT_TAG_IDX), MAC_VLANTIR_VLTI, 0x1); /* insert/replace C_VLAN in 13th ans 14th bytes of transmitted frames */ DWC_REG_WR_BIT(MAC_VLANTIR(MAC_VLAN_OUT_TAG_IDX), MAC_VLANTIR_CSVL, 0x0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_pblx8(uint32_t qInx, uint32_t val) { DWC_REG_WR_BIT(DMA_CR(qInx), DMA_CR_PBLx8, val); return Y_SUCCESS; } /*! * \return int * \retval programmed Tx PBL value */ static int get_tx_pbl_val(uint32_t qInx) { return DWC_REG_RD_FIELD(DMA_TCR(qInx), DMA_TCR_PBL); } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_tx_pbl_val(uint32_t qInx, uint32_t tx_pbl) { DWC_REG_WR_FIELD(DMA_TCR(qInx), DMA_TCR_PBL, tx_pbl); return Y_SUCCESS; } /*! * \return int * \retval programmed Rx PBL value */ static int get_rx_pbl_val(uint32_t qInx) { return DWC_REG_RD_FIELD(DMA_RCR(qInx), DMA_RCR_PBL); } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_rx_pbl_val(uint32_t qInx, uint32_t rx_pbl) { DWC_REG_WR_FIELD(DMA_RCR(qInx), DMA_RCR_PBL, rx_pbl); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_axi_rorl_val(uint32_t axi_rorl) { DWC_REG_WR_FIELD(DMA_SBMR, DMA_SBMR_RD_OSR_LMT, axi_rorl); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_axi_worl_val(uint32_t axi_worl) { DWC_REG_WR_FIELD(DMA_SBMR, DMA_SBMR_WR_OSR_LMT, axi_worl); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_axi_pbl_val(uint32_t axi_pbl) { uint32_t varDMA_SBUS; varDMA_SBUS = DWC_REG_RD(DMA_SBMR); varDMA_SBUS &= ~DMA_SBUS_AXI_PBL_MASK; varDMA_SBUS |= axi_pbl; DWC_REG_WR(DMA_SBMR, varDMA_SBUS); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_incr_incrx_mode(uint32_t val) { DWC_REG_WR_BIT(DMA_SBMR, DMA_SBMR_FB, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_osf_mode(uint32_t qInx, uint32_t val) { DWC_REG_WR_BIT(DMA_TCR(qInx), DMA_TCR_OSP, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_rsf_mode(uint32_t qInx, uint32_t val) { DWC_REG_WR_BIT(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RSF, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_tsf_mode(uint32_t qInx, uint32_t val) { DWC_REG_WR_BIT(MTL_TXQ_OMR(qInx), MTL_TXQ_OMR_TSF, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_rx_threshold(uint32_t qInx, uint32_t val) { DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RTC, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_tx_threshold(uint32_t qInx, uint32_t val) { DWC_REG_WR_FIELD(MTL_TXQ_OMR(qInx), MTL_TXQ_OMR_TTC, val); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int config_rx_watchdog_timer(uint32_t qInx, uint32_t riwt) { DWC_REG_WR_FIELD(DMA_RIWTR(qInx), DMA_RIWTR_RWT, riwt); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int enable_magic_pmt_operation(void) { DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_MGKPKTEN, 0x1); DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_PWRDWN, 0x1); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int disable_magic_pmt_operation(void) { DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_MGKPKTEN, 0x0); if (DWC_REG_RD_BIT(MAC_PMT_CSR, MAC_PMT_CSR_PWRDWN)) DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_PWRDWN, 0x0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int enable_remote_pmt_operation(void) { DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_RWKPKTEN, 0x1); DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_PWRDWN, 0x1); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int disable_remote_pmt_operation(void) { DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_RWKPKTEN, 0x1); if (DWC_REG_RD_BIT(MAC_PMT_CSR, MAC_PMT_CSR_PWRDWN)) DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_PWRDWN, 0x0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int configure_rwk_filter_registers(uint32_t *value, uint32_t count) { uint32_t i; DWC_REG_WR_BIT(MAC_PMT_CSR, MAC_PMT_CSR_RWKFILTRST, 0x1); for (i = 0; i < count; i++) DWC_REG_WR(MAC_RWK_PFR, value[i]); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int disable_tx_flow_ctrl(uint32_t qInx) { DWC_REG_WR_BIT(MAC_TQFCR(qInx), MAC_TQFCR_TFE, 0x0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int enable_tx_flow_ctrl(uint32_t qInx) { DWC_REG_WR_BIT(MAC_TQFCR(qInx), MAC_TQFCR_TFE, 0x1); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int disable_rx_flow_ctrl(void) { DWC_REG_WR_BIT(MAC_RQFCR, MAC_RQFCR_RFE, 0x0); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int enable_rx_flow_ctrl(void) { DWC_REG_WR_BIT(MAC_RQFCR, MAC_RQFCR_RFE, 0x1); return Y_SUCCESS; } /*! * \param[in] qInx * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int stop_dma_rx(uint32_t qInx) { int retryCount = 10; uint32_t ch_state = 0; int ret = Y_SUCCESS; /* issue Rx dma stop command */ DWC_REG_WR_BIT(DMA_RCR(qInx), DMA_RCR_SR, 0x0); /* Wait for Rx DMA to stop, ie wait till Rx DMA * goes in either Running or Suspend state. * */ do { switch (qInx) { case 0: ch_state = DWC_REG_RD_FIELD(DMA_DSR0, DMA_DSR0_RPS0); break; case 1: ch_state = DWC_REG_RD_FIELD(DMA_DSR0, DMA_DSR0_RPS1); break; case 2: ch_state = DWC_REG_RD_FIELD(DMA_DSR0, DMA_DSR0_RPS2); break; case 3: ch_state = DWC_REG_RD_FIELD(DMA_DSR1, DMA_DSR1_RPS3); break; case 4: ch_state = DWC_REG_RD_FIELD(DMA_DSR1, DMA_DSR1_RPS4); break; case 5: ch_state = DWC_REG_RD_FIELD(DMA_DSR1, DMA_DSR1_RPS5); break; case 6: ch_state = DWC_REG_RD_FIELD(DMA_DSR1, DMA_DSR1_RPS6); break; case 7: ch_state = DWC_REG_RD_FIELD(DMA_DSR2, DMA_DSR2_RPS7); break; } ch_state &= 0x7; //MSB is not used if (ch_state == 0x0 || ch_state == 0x4 || ch_state == 0x3) { retryCount = -1; } else if(--retryCount) { mdelay(1); } else { printk(KERN_ALERT "ERROR: Rx Channel %d stop failed!\n", qInx); ret = -Y_FAILURE; } } while (retryCount > 0); return ret; } /*! * \param[in] qInx * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int start_dma_rx(uint32_t qInx) { DWC_REG_WR_BIT(DMA_RCR(qInx), DMA_RCR_SR, 0x1); return Y_SUCCESS; } /*! * \param[in] qInx * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int stop_dma_tx(uint32_t qInx) { int retryCount = 10; uint32_t ch_state = 0; int ret = Y_SUCCESS; /* issue Tx dma stop command */ DWC_REG_WR_BIT(DMA_TCR(qInx), DMA_TCR_ST, 0x0); /* Wait for Rx DMA to stop, ie wait till Rx DMA * goes in either Running or Suspend state. * */ do { switch (qInx) { case 0: ch_state = DWC_REG_RD_FIELD(DMA_DSR0, DMA_DSR0_TPS0); break; case 1: ch_state = DWC_REG_RD_FIELD(DMA_DSR0, DMA_DSR0_TPS1); break; case 2: ch_state = DWC_REG_RD_FIELD(DMA_DSR0, DMA_DSR0_TPS2); break; case 3: ch_state = DWC_REG_RD_FIELD(DMA_DSR1, DMA_DSR1_TPS3); break; case 4: ch_state = DWC_REG_RD_FIELD(DMA_DSR1, DMA_DSR1_TPS4); break; case 5: ch_state = DWC_REG_RD_FIELD(DMA_DSR1, DMA_DSR1_TPS5); break; case 6: ch_state = DWC_REG_RD_FIELD(DMA_DSR1, DMA_DSR1_TPS6); break; case 7: ch_state = DWC_REG_RD_FIELD(DMA_DSR2, DMA_DSR2_TPS7); break; } ch_state &= 0x7; //MSB is not used if (ch_state == 0x0 || ch_state == 0x6) { retryCount = -1; } else if(--retryCount) { mdelay(1); } else { printk(KERN_ALERT "ERROR: Tx Channel %d stop failed!\n", qInx); ret = -Y_FAILURE; } } while (retryCount > 0); return ret; } /*! * \param[in] qInx * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int start_dma_tx(uint32_t qInx) { DWC_REG_WR_BIT(DMA_TCR(qInx), DMA_TCR_ST, 0x1); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int stop_mac_tx_rx(void) { uint32_t varMAC_MCR; varMAC_MCR = DWC_REG_RD(MAC_MCR); varMAC_MCR &= ~(0x3); DWC_REG_WR(MAC_MCR, varMAC_MCR); return Y_SUCCESS; } /*! * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int start_mac_tx_rx(void) { uint32_t varMAC_MCR; varMAC_MCR = DWC_REG_RD(MAC_MCR); varMAC_MCR |= (0x3); DWC_REG_WR(MAC_MCR, 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_dma_interrupts(uint32_t qInx, uint32_t version, hw_config_t *hw_cfg) { uint32_t varDMA_SR; /* Clear any current set interrupt */ varDMA_SR = DWC_REG_RD(DMA_SR(qInx)); DWC_REG_WR(DMA_SR(qInx), varDMA_SR); /* Enable required DMA interrupts */ VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_RIE, 0x1); /* Receive Interrupt */ VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_RBUE, 0x1); /* Receive Buffer Unavailable */ VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_FBEE, 0x1); /* Fatal Bus Error */ #if 0 // THESE ARE NOT NEEDED VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_TSE, 0x1); /* Transmit Stoppped */ VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_RSE, 0x1); /* Receive Stoppped */ #endif if (version == MAC_VER_4_00) { VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_AIE_4_00, 0x1); /* Abnormal Interrupt Summary */ VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_NIE_4_00, 0x1); /* Normal Interrupt Summary */ } else { VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_AIE_4_10, 0x1); /* Abnormal Interrupt Summary */ VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_NIE_4_10, 0x1); /* Normal Interrupt Summary */ } #ifdef DWC_ETH_QOS_TXPOLLING_MODE_ENABLE VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_TIE, 0x1); /* Transmit Interrupt */ #else VAR32_SET_BIT(hw_cfg->dma_ier, DMA_IER_TBUE, 0x1); /* Transmit Buffer Unavailable */ #endif DWC_REG_WR(DMA_IER(qInx), hw_cfg->dma_ier); return Y_SUCCESS; } static int set_speed(struct DWC_ETH_QOS_prv_data *pdata, unsigned int speed) { uint32_t mac_ps = 0, mac_fes = 0; void __iomem *reg_base = pdata->gbe_base; switch(speed) { case 10: pdata->pcs_speed = SPEED_10; pdata->rate = GBE_GCR5_PHY_SPEED_10M; mac_ps = 1; break; case 100: pdata->pcs_speed = SPEED_100; pdata->rate = GBE_GCR5_PHY_SPEED_100M; mac_ps = 1; mac_fes = 1; break; case 1000: pdata->pcs_speed = SPEED_1000; pdata->rate = GBE_GCR5_PHY_SPEED_1G; break; case 2500: pdata->pcs_speed = SPEED_1000; pdata->rate = GBE_GCR5_PHY_SPEED_2_5G; break; case 5000: pdata->pcs_speed = SPEED_1000; pdata->rate = GBE_GCR5_PHY_SPEED_5G; break; default: printk(KERN_ALERT "Invalid speed value (%u)!\n", speed); return -Y_FAILURE; } // Configure MAC register DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_PS, mac_ps); DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_FES, mac_fes); // Configure GBE top register GBE_REG_WR_FIELD(GBE_GCR5, GBE_GCR5_PHY_SPEED, pdata->rate); 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) { DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_DM, 0x0); 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) { DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_DM, 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) { DWC_REG_WR_BIT(MAC_PFR, MAC_PFR_HMC, 0x0); 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) { DWC_REG_WR_BIT(MAC_PFR, MAC_PFR_HUC, 0x0); 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) { DWC_REG_WR_BIT(MAC_PFR, MAC_PFR_PM, 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) { DWC_REG_WR_BIT(MAC_PFR, MAC_PFR_PR, 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) { uint32_t retryCount = 1000, varMAC_MDIOAR; int ret = Y_SUCCESS; /* Wait for any previous MII read/write operation to complete */ while (DWC_REG_RD_BIT(MAC_MDIO_AR, MAC_MDIO_AR_GB) && --retryCount) mdelay(1); if (!retryCount) { ret = -Y_FAILURE; } else { /* Write the data */ DWC_REG_WR_FIELD(MAC_MDIO_DR, MAC_MDIO_DR_GD, 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 */ varMAC_MDIOAR = DWC_REG_RD(MAC_MDIO_AR); varMAC_MDIOAR &= 0x12; VAR32_SET_BIT(varMAC_MDIOAR, MAC_MDIO_AR_GB, 0x1); VAR32_SET_FIELD(varMAC_MDIOAR, MAC_MDIO_AR_GOC, 0x1); VAR32_SET_FIELD(varMAC_MDIOAR, MAC_MDIO_AR_CR, 0x2); VAR32_SET_FIELD(varMAC_MDIOAR, MAC_MDIO_AR_GMIIR, phy_reg); VAR32_SET_FIELD(varMAC_MDIOAR, MAC_MDIO_AR_PA, phy_id); DWC_REG_WR(MAC_MDIO_AR, varMAC_MDIOAR); /* Delay operation 10uSec */ udelay(10); /* Wait for MII write operation to complete */ retryCount = 1000; while (DWC_REG_RD_BIT(MAC_MDIO_AR, MAC_MDIO_AR_GB) && --retryCount) mdelay(1); if (!retryCount) ret = -Y_FAILURE; } return ret; } /*! * \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) { uint32_t retryCount = 1000, varMAC_MDIOAR; int ret = Y_SUCCESS; /* Wait for any previous MII read/write operation to complete */ while (DWC_REG_RD_BIT(MAC_MDIO_AR, MAC_MDIO_AR_GB) && --retryCount) mdelay(1); if (!retryCount) { ret = -Y_FAILURE; } else { /* 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 */ varMAC_MDIOAR = DWC_REG_RD(MAC_MDIO_AR); varMAC_MDIOAR &= 0x12; VAR32_SET_BIT(varMAC_MDIOAR, MAC_MDIO_AR_GB, 0x1); VAR32_SET_FIELD(varMAC_MDIOAR, MAC_MDIO_AR_GOC, 0x3); VAR32_SET_FIELD(varMAC_MDIOAR, MAC_MDIO_AR_CR, 0x2); VAR32_SET_FIELD(varMAC_MDIOAR, MAC_MDIO_AR_GMIIR, phy_reg); VAR32_SET_FIELD(varMAC_MDIOAR, MAC_MDIO_AR_PA, phy_id); DWC_REG_WR(MAC_MDIO_AR, varMAC_MDIOAR); /* Delay operation 10uSec */ udelay(10); /* Wait for MII write operation to complete */ retryCount = 1000; while (DWC_REG_RD_BIT(MAC_MDIO_AR, MAC_MDIO_AR_GB) && --retryCount) mdelay(1); if (!retryCount) { ret = -Y_FAILURE; } else { *phy_reg_data = DWC_REG_RD_FIELD(MAC_MDIO_DR, MAC_MDIO_DR_GD); } } return ret; } /*! * \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(tx_descriptor_t *txdesc) { return VAR32_GET_BIT(txdesc->TDES3, NORMAL_WB_TDES3_UF);; } /*! * \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(tx_descriptor_t *txdesc) { uint32_t loc, nc; /* check TDES3.LoC and TDES3.NC bits */ loc = VAR32_GET_BIT(txdesc->TDES3, NORMAL_WB_TDES3_LOC); nc = VAR32_GET_BIT(txdesc->TDES3, NORMAL_WB_TDES3_NC); return (loc | nc); } /*! * \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(tx_descriptor_t *txdesc) { uint32_t lc, ec; /* check for TDES3.LC and TDES3.EC */ lc = VAR32_GET_BIT(txdesc->TDES3, NORMAL_WB_TDES3_LC); ec = VAR32_GET_BIT(txdesc->TDES3, NORMAL_WB_TDES3_EC); return (lc | ec); } /*! * \brief This sequence is used to check whether the pkt transmitted is * successfull 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(tx_descriptor_t *txdesc) { return ((~VAR32_GET_BIT(txdesc->TDES3, NORMAL_WB_TDES3_OWN)) & ONE_BIT_MASK); } /*! * \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) { return DWC_REG_RD_BIT(MAC_MCR, MAC_MCR_IPC); } /*! * \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) { DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_IPC, 0x0); 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) { DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_IPC, 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(uint32_t idx, struct DWC_ETH_QOS_prv_data *pdata, uint32_t qInx) { tx_descriptor_t *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 = 0; /* update buffer 2 address pointer to zero */ TX_NORMAL_DESC->TDES1 = 0; /* set all other control bits (IC, TTSE, B2L & B1L) to zero */ TX_NORMAL_DESC->TDES2 = 0; /* set all other control bits (OWN, CTXT, FD, LD, CPC, CIC etc) to zero */ 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(uint32_t idx, struct DWC_ETH_QOS_prv_data *pdata, unsigned int inte, uint32_t qInx) { struct DWC_ETH_QOS_rx_buffer *buffer = GET_RX_BUF_PTR(qInx, idx); rx_descriptor_t *RX_NORMAL_DESC = GET_RX_DESC_PTR(qInx, idx); DBGPR("-->rx_descriptor_reset\n"); memset(RX_NORMAL_DESC, 0, sizeof(rx_descriptor_t)); /* update buffer 1 address pointer */ RX_NORMAL_DESC->RDES0 = buffer->dma; /* set to zero */ 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 = buffer->dma2; /* set control bits - OWN, INTE, BUF1V and BUF2V */ RX_NORMAL_DESC->RDES3 = (0x83000000 | inte); } else { /* set buffer 2 address pointer to zero */ RX_NORMAL_DESC->RDES2 = 0; /* set control bits - OWN, INTE and BUF1V */ 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, uint32_t 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); rx_descriptor_t *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 < RX_DESC_CNT; i++) { memset(RX_NORMAL_DESC, 0, sizeof(rx_descriptor_t)); /* update buffer 1 address pointer */ RX_NORMAL_DESC->RDES0 = buffer->dma; /* set to zero */ 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 = buffer->dma2; /* set control bits - OWN, INTE, BUF1V and BUF2V */ RX_NORMAL_DESC->RDES3 = 0xc3000000; } else { /* set buffer 2 address pointer to zero */ RX_NORMAL_DESC->RDES2 = 0; /* set control bits - OWN, INTE and BUF1V */ RX_NORMAL_DESC->RDES3 = 0xc1000000; } 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) { /* reset INTE */ VAR32_SET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_RF_RDES3_INTE, 0); buffer->inte = 0; } } INCR_RX_DESC_INDEX(rx_desc_data->cur_rx, 1); 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 */ DWC_REG_WR_FIELD(DMA_RDRLR(qInx), DMA_RDRLR_RDRL, (RX_DESC_CNT - 1)); /* update the Rx Descriptor Tail Pointer */ last_index = GET_CURRENT_RCVD_LAST_DESC_INDEX(start_index, 0); DWC_REG_WR(DMA_RDTPR(qInx), GET_RX_DESC_DMA_ADDR(qInx, last_index)); /* update the starting address of desc chain/ring */ DWC_REG_WR(DMA_RDLAR(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, uint32_t qInx) { struct DWC_ETH_QOS_tx_wrapper_descriptor *tx_desc_data = GET_TX_WRAPPER_DESC(qInx); tx_descriptor_t *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 < TX_DESC_CNT; i++) { /* update buffer 1 address pointer to zero */ TX_NORMAL_DESC->TDES0 = 0; /* update buffer 2 address pointer to zero */ TX_NORMAL_DESC->TDES1 = 0; /* set all other control bits (IC, TTSE, B2L & B1L) to zero */ TX_NORMAL_DESC->TDES2 = 0; /* set all other control bits (OWN, CTXT, FD, LD, CPC, CIC etc) to zero */ TX_NORMAL_DESC->TDES3 = 0; INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1); TX_NORMAL_DESC = GET_TX_DESC_PTR(qInx, tx_desc_data->cur_tx); } /* update the total no of Tx descriptors count */ DWC_REG_WR(DMA_TDRLR(qInx), (TX_DESC_CNT - 1)); /* update the starting address of desc chain/ring */ DWC_REG_WR(DMA_TDLAR(qInx), GET_TX_DESC_DMA_ADDR(qInx, start_index)); DBGPR("<--tx_descriptor_init\n"); } /*! * \brief This sequence is used to instruct the MAC to perform one of the * the following based on the value of tx_vlan_tag_ctrl * ======================================== * | tx_vlan_tag_ctrl | Action | * =======================================| * | 00 | Do Not add tags | * | 01 | Remove the tags | * | 10 | Insert the tags | * | 11 | Replace the tags| * ======================================== * \param[in] pdata * \param[in] qInx */ static void process_vlan_tags(struct DWC_ETH_QOS_prv_data *pdata, uint32_t qInx) { struct DWC_ETH_QOS_tx_wrapper_descriptor *tx_desc_data = GET_TX_WRAPPER_DESC(qInx); tx_descriptor_t *tx_desc = GET_TX_DESC_PTR(qInx, tx_desc_data->cur_tx); #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 && Y_FALSE == tx_desc_data->tx_vlan_tag_via_reg) { //printk(KERN_ALERT "VLAN control info update via descriptor\n\n"); VAR32_SET_FIELD(tx_desc->TDES2, NORMAL_RF_TDES2_VTIR, 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) { VAR32_SET_FIELD(tx_desc->TDES2, NORMAL_RF_TDES2_VTIR, pdata->op_type); } } #endif /* End of DWC_ETH_QOS_ENABLE_DVLAN */ } /*! * \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, uint32_t qInx) { 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); tx_descriptor_t *tx_desc = GET_TX_DESC_PTR(qInx, tx_desc_data->cur_tx); uint32_t varcsum_enable, varvlan_pkt; int i, start_index = tx_desc_data->cur_tx, last_index; tx_pkt_features_t *tx_pkt_features = &pdata->tx_pkt_features; #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT int update_tail = 0; uint32_t varQTDR; #endif uint32_t vartso_enable = 0; uint32_t vartcp_hdr_len = 0; uint32_t 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 (0 == varQTDR) { /* disable MAC Transmit */ DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_TE, 0x0); update_tail = 1; } #endif #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG varvlan_pkt = VAR32_GET_BIT(tx_pkt_features->pkt_attributes, TX_PKT_FEATURES_ATTR_VLAN_PKT); if (varvlan_pkt == 0x1) { /* put vlan tag in contex descriptor and set other control * bits accordingly */ VAR32_SET_FIELD(tx_desc->TDES3, CONTEXT_TDES3_VT, tx_pkt_features->vlan_tag); VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_VLTV, 1); VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_TYPE, 1); VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_OWN, 1); #ifdef GBE_DEBUG /* Print VLAN context descriptor */ PRINT_TX_DESC(tx_desc, VLAN); #endif INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1); start_index = tx_desc_data->cur_tx; tx_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) { VAR32_SET_FIELD(tx_desc->TDES3, CONTEXT_TDES3_VT, pdata->outer_vlan_tag); VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_VLTV, 1); /* operation (insertion/replacement/deletion/none) will be * specified in normal descriptor TDES2 * */ } if (pdata->in_out & DWC_ETH_QOS_DVLAN_INNER) { VAR32_SET_FIELD(tx_desc->TDES2, CONTEXT_TDES2_IVT, pdata->inner_vlan_tag); VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_IVLTV, 1); VAR32_SET_FIELD(tx_desc->TDES3, CONTEXT_TDES3_IVTIR, pdata->op_type); } VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_TYPE, 1); VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_OWN, 1); #ifdef GBE_DEBUG /* Print DVLAN context descriptor */ PRINT_TX_DESC(tx_desc, VLAN); #endif INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1); start_index = tx_desc_data->cur_tx; tx_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 */ vartso_enable = VAR32_GET_BIT(tx_pkt_features->pkt_attributes, TX_PKT_FEATURES_ATTR_TSO_ENABLE); if (vartso_enable && (tx_pkt_features->mss != tx_desc_data->default_mss)) { /* Update MSS */ VAR32_SET_FIELD(tx_desc->TDES2, CONTEXT_TDES2_MSS, tx_pkt_features->mss); /* set MSS valid, CTXT and OWN bits */ VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_TCMSSV, 1); VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_TYPE, 1); VAR32_SET_BIT(tx_desc->TDES3, CONTEXT_TDES3_OWN, 1); /* Deleting the writing of MSS into DMA_CR as the * spec says it should be written into either the * CONTEXT descriptor or DMA_CR register. The * context descriptor is updated with MSS in the * above code. Refer to the * DWC_ether_qos_databook_4.00a.pdf section 11.12 */ tx_desc_data->default_mss = tx_pkt_features->mss; #ifdef GBE_DEBUG /* Print TSO context descriptor */ PRINT_TX_DESC(tx_desc, TSO); #endif INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1); start_index = tx_desc_data->cur_tx; tx_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_desc->TDES0 = buffer->dma; VAR32_SET_FIELD(tx_desc->TDES2, NORMAL_RF_TDES2_HL_B1L, buffer->len); if (buffer->dma2 != 0) { /* update the second buffer pointer and length */ tx_desc->TDES1 = buffer->dma2; VAR32_SET_FIELD(tx_desc->TDES2, NORMAL_RF_TDES2_B2L, buffer->len2); } if (vartso_enable) { /* update TCP payload length (only for the descriptor with FD set) */ VAR32_SET_FIELD(tx_desc->TDES3, NORMAL_RF_TDES3_TPL, tx_pkt_features->pay_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); VAR32_SET_FIELD(tx_desc->TDES3, NORMAL_RF_TDES3_PL, total_len); } process_vlan_tags(pdata, qInx); /* Mark it as First Descriptor */ VAR32_SET_BIT(tx_desc->TDES3, NORMAL_RF_TDES3_FD, 1); /* Enable CRC and Pad Insertion in FIRST descriptor only */ VAR32_SET_FIELD(tx_desc->TDES3, NORMAL_RF_TDES3_CPC, 0); #ifdef GBE_DEBUG if (metadata_on_crc) /* Disable CRC and Pad Insertion to test CRC on metadata */ VAR32_SET_FIELD(tx_desc->TDES3, NORMAL_RF_TDES3_CPC, 0x2); #endif /* Mark it as NORMAL descriptor */ VAR32_SET_BIT(tx_desc->TDES3, NORMAL_RF_TDES3_TYPE, 0); /* Enable HW CSUM */ varcsum_enable = VAR32_GET_BIT(tx_pkt_features->pkt_attributes, TX_PKT_FEATURES_ATTR_CSUM_ENABLE); if (varcsum_enable) { VAR32_SET_FIELD(tx_desc->TDES3, NORMAL_RF_TDES3_CIC, 0x3); } /* configure SA Insertion Control */ VAR32_SET_FIELD(tx_desc->TDES3, NORMAL_RF_TDES3_SAIC, pdata->tx_sa_ctrl_via_desc); if (vartso_enable) { /* set TSE bit */ VAR32_SET_BIT(tx_desc->TDES3, NORMAL_RF_TDES3_TSE, 1); /* update tcp data offset or tcp hdr len */ /* convert to bit value */ vartcp_hdr_len = tx_pkt_features->tcp_hdr_len/4; VAR32_SET_FIELD(tx_desc->TDES3, NORMAL_RF_TDES3_THL, vartcp_hdr_len); } /* enable timestamping */ varptp_enable = VAR32_GET_BIT(tx_pkt_features->pkt_attributes, TX_PKT_FEATURES_ATTR_PTP_ENABLE); if (varptp_enable) { VAR32_SET_BIT(tx_desc->TDES2, NORMAL_RF_TDES2_TTSE, 1); } #ifdef GBE_DEBUG /* Print normal read-format first descriptor */ PRINT_TX_DESC(tx_desc, NORMAL_RF_FD); #endif INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1); tx_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_desc->TDES0 = buffer->dma; VAR32_SET_FIELD(tx_desc->TDES2, NORMAL_RF_TDES2_HL_B1L, buffer->len); if (buffer->dma2 != 0) { /* update the second buffer pointer and length */ tx_desc->TDES1 = buffer->dma2; VAR32_SET_FIELD(tx_desc->TDES2, NORMAL_RF_TDES2_B2L, buffer->len2); } process_vlan_tags(pdata, qInx); /* set own bit */ VAR32_SET_BIT(tx_desc->TDES3, NORMAL_RF_TDES3_OWN, 1); /* Mark it as NORMAL descriptor */ VAR32_SET_BIT(tx_desc->TDES3, NORMAL_RF_TDES3_TYPE, 0); #ifdef GBE_DEBUG /* Print normal read-format descriptor */ PRINT_TX_DESC(tx_desc, NORMAL_RF); #endif INCR_TX_DESC_INDEX(tx_desc_data->cur_tx, 1); tx_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_CURRENT_XFER_LAST_DESC_INDEX(qInx, start_index, 0); tx_desc = GET_TX_DESC_PTR(qInx, last_index); VAR32_SET_BIT(tx_desc->TDES3, NORMAL_RF_TDES3_LD, 1); /* 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) VAR32_SET_BIT(tx_desc->TDES2, NORMAL_RF_TDES2_IOC, 1); #else VAR32_SET_BIT(tx_desc->TDES2, NORMAL_RF_TDES2_IOC, 1); #endif /* set OWN bit of FIRST descriptor at end to avoid race condition */ tx_desc = GET_TX_DESC_PTR(qInx, start_index); VAR32_SET_BIT(tx_desc->TDES3, NORMAL_RF_TDES3_OWN, 1); #ifdef GBE_DEBUG /* Print normal read-format last descriptor */ PRINT_TX_DESC(tx_desc, NORMAL_RF_LD); #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) && (1 == update_tail)) { 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_CURRENT_XFER_LAST_DESC_INDEX(qInx, start_index, 1); DWC_REG_WR(DMA_TDTPR(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_CURRENT_XFER_LAST_DESC_INDEX(qInx, start_index, 1); DWC_REG_WR(DMA_TDTPR(qInx), GET_TX_DESC_DMA_ADDR(qInx, last_index)); #endif if (pdata->eee_enabled) { /* 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, uint32_t qInx) { struct DWC_ETH_QOS_rx_wrapper_descriptor *rx_desc_data = GET_RX_WRAPPER_DESC(qInx); rx_descriptor_t *RX_NORMAL_DESC = GET_RX_DESC_PTR(qInx, rx_desc_data->cur_rx); uint32_t varOWN; uint32_t varES; struct DWC_ETH_QOS_rx_buffer *buffer = GET_RX_BUF_PTR(qInx, rx_desc_data->cur_rx); uint32_t varRS1V; uint32_t varIPCE; uint32_t varIPCB; uint32_t varIPHE; rx_pkt_features_t *rx_pkt_features = &pdata->rx_pkt_features; uint32_t varRS0V; uint32_t varLT; uint32_t varOE; uint32_t varCE; uint32_t varRE; uint32_t varDE; uint32_t varLD; DBGPR("-->device_read: cur_rx = %d\n", rx_desc_data->cur_rx); /* check for data availability */ varOWN = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_OWN); if (varOWN == 0) { /* check whether it is good packet or bad packet */ varES = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_ES); varLD = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_LD); #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT_HALFDUPLEX /* Synopsys testing and debugging purposes only */ if (varES == 1 && varLD == 1) { varES = 0; DBGPR("Forwarding error packets as good packets to stack\n"); } #endif if ((varES == 0) && (varLD == 1)) { /* get the packet length */ buffer->len = VAR32_GET_FIELD(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_PL); varRS1V = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_RS1V); if (varRS1V == 0x1) { /* check whether device has done csum correctly or not */ varIPCE = VAR32_GET_BIT(RX_NORMAL_DESC->RDES1, NORMAL_WB_RDES1_IPCE); varIPCB = VAR32_GET_BIT(RX_NORMAL_DESC->RDES1, NORMAL_WB_RDES1_IPCB); varIPHE = VAR32_GET_BIT(RX_NORMAL_DESC->RDES1, NORMAL_WB_RDES1_IPHE); if ((varIPCE == 0) && (varIPCB == 0) && (varIPHE == 0)) { /* IPC Checksum done */ VAR32_SET_BIT(rx_pkt_features->pkt_attributes, RX_PKT_FEATURES_ATTR_CSUM_DONE, 1); } } #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG varRS0V = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_RS0V); if (varRS0V == 0x1) { /* device received frame with VLAN Tag or double VLAN Tag ? */ varLT = VAR32_GET_FIELD(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_LT); if ((varLT == 0x4) || (varLT == 0x5)) { VAR32_SET_BIT(rx_pkt_features->pkt_attributes, RX_PKT_FEATURES_ATTR_VLAN_PKT, 1); /* get the VLAN Tag */ rx_pkt_features->vlan_tag = VAR32_GET_FIELD( RX_NORMAL_DESC->RDES0, NORMAL_WB_RDES0_VT); } } #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. */ varOE = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_OE); VAR32_SET_BIT(pdata->rx_error_counters, RX_ERROR_OVERRUN, varOE); varCE = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_CE); VAR32_SET_BIT(pdata->rx_error_counters, RX_ERROR_CRC, varCE); varRE = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_RE); VAR32_SET_BIT(pdata->rx_error_counters, RX_ERROR_FRAME, varRE); varDE = VAR32_GET_BIT(RX_NORMAL_DESC->RDES3, NORMAL_WB_RDES3_DE); VAR32_SET_BIT(pdata->rx_error_counters, RX_ERROR_MISSED, varDE); } } 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) { DWC_REG_WR(DMA_RDTPR(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(tx_descriptor_t *txdesc) { return VAR32_GET_BIT(txdesc->TDES3, CONTEXT_TDES3_TYPE); } /*! * \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(tx_descriptor_t *txdesc) { return VAR32_GET_BIT(txdesc->TDES3, NORMAL_WB_TDES3_LD); } /* Sotware reset */ static int DWC_ETH_QOS_sw_reset(void) { uint32_t retryCount = 1000; DBGPR("-->DWC_ETH_QOS_sw_reset\n"); /* Issue a software reset */ DWC_REG_WR_BIT(DMA_BMR, DMA_BMR_SWR, 0x1); udelay(10); /* Wait for software reset */ while(DWC_REG_RD_BIT(DMA_BMR, DMA_BMR_SWR) && --retryCount) mdelay(1); DBGPR("<--DWC_ETH_QOS_sw_reset\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 uint32_t calculate_per_queue_fifo(uint32_t fifo_size, uint8_t queue_count) { uint32_t q_fifo_size = 0; /* calculated fifo size per queue */ uint32_t p_fifo = eDWC_ETH_QOS_256; /* per queue fifo size programmable value */ /* 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_mtl_queue(uint32_t qInx, struct DWC_ETH_QOS_prv_data *pdata) { struct DWC_ETH_QOS_tx_queue *queue_data = GET_TX_QUEUE_PTR(qInx); uint32_t retryCount = 1000; uint32_t tx_queue_size, rx_queue_size; uint32_t p_rx_fifo = eDWC_ETH_QOS_256, p_tx_fifo = eDWC_ETH_QOS_256; DBGPR("-->configure_mtl_queue\n"); /*Flush Tx Queue */ DWC_REG_WR_BIT(MTL_TXQ_OMR(qInx), MTL_TXQ_OMR_FTQ, 0x1); /*Poll Until Poll Condition */ while (DWC_REG_RD_BIT(MTL_TXQ_OMR(qInx), MTL_TXQ_OMR_FTQ) && --retryCount) mdelay(1); if (!retryCount) return -Y_FAILURE; /*Enable Store and Forward mode for TX */ DWC_REG_WR_BIT(MTL_TXQ_OMR(qInx), MTL_TXQ_OMR_TSF, 0x1); /* Program Tx operating mode */ DWC_REG_WR_FIELD(MTL_TXQ_OMR(qInx), MTL_TXQ_OMR_TXQEN, queue_data->q_op_mode); /* Transmit Queue weight */ DWC_REG_WR_FIELD(MTL_TXQ_QW(qInx), MTL_TXQ_QW_ISCQW, (0x10 + qInx)); DWC_REG_WR_BIT(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_FEP, 0x1); /* Configure for Jumbo frame in MTL */ if (pdata->dev->mtu > DWC_ETH_QOS_ETH_FRAME_LEN) { /* Disable RX Store and Forward mode */ DWC_REG_WR_BIT(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RSF, 0x0); printk(KERN_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 */ DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RQS, p_rx_fifo); DWC_REG_WR_FIELD(MTL_TXQ_OMR(qInx), MTL_TXQ_OMR_TQS, p_tx_fifo); tx_queue_size = (p_tx_fifo + 1) * 256; rx_queue_size = (p_rx_fifo + 1) * 256; CFG_PRINT("Queue[%d] Tx fifo size %d, Rx fifo size %d\n", qInx, tx_queue_size, rx_queue_size); /* 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) { DWC_REG_WR_BIT(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_EHFC, 0x1); if (pdata->version == MAC_VER_4_00) { /* Set Threshold for Activating Flow Contol space for min 2 frames * ie, (1500 * 2) + (64 * 2) = 3128 bytes, rounding off to 4k * * 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 * */ DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFD_4_00, 0x2); DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFA_4_00, 0x1); } else if (p_rx_fifo == eDWC_ETH_QOS_8k) { DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFD_4_00, 0x4); DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFA_4_00, 0x2); } else if (p_rx_fifo == eDWC_ETH_QOS_16k) { DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFD_4_00, 0x5); DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFA_4_00, 0x2); } else if (p_rx_fifo == eDWC_ETH_QOS_32k) { DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFD_4_00, 0x7); DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFA_4_00, 0x2); } } else { // (pdata->version == MAC_VER_4_10) /* 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 * */ DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFD_4_10, 0x3); //Full - 3K DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFA_4_10, 0x1); //Full - 1.5K } else if (p_rx_fifo == eDWC_ETH_QOS_8k) { DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFD_4_10, 0x6); //Full - 4K DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFA_4_10, 0xA); //Full - 6K } else if (p_rx_fifo == eDWC_ETH_QOS_16k) { DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFD_4_10, 0x6); //Full - 4K DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFA_4_10, 0x12); //Full - 10K } else if (p_rx_fifo == eDWC_ETH_QOS_32k) { DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFD_4_10, 0x6); //Full - 4K DWC_REG_WR_FIELD(MTL_RXQ_OMR(qInx), MTL_RXQ_OMR_RFA_4_10, 0x1E); //Full - 16K } } } } DBGPR("<--configure_mtl_queue\n"); return Y_SUCCESS; } static int configure_dma_channel(uint32_t 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_dma_channel\n"); /*Enable OSF mode */ DWC_REG_WR_BIT(DMA_TCR(qInx), DMA_TCR_OSP, 0x1); /*Select Rx Buffer size = 2048bytes */ switch (pdata->rx_buffer_len) { case 16384: case 8192: case 4096: DWC_REG_WR_FIELD(DMA_RCR(qInx), DMA_RCR_RBSZ, pdata->rx_buffer_len); break; default: /* default is 2K */ DWC_REG_WR_FIELD(DMA_RCR(qInx), DMA_RCR_RBSZ, 2048); break; } /* program RX watchdog timer */ if (rx_desc_data->use_riwt) { DWC_REG_WR_FIELD(DMA_RIWTR(qInx), DMA_RIWTR_RWT, rx_desc_data->rx_riwt); } else { DWC_REG_WR_FIELD(DMA_RIWTR(qInx), DMA_RIWTR_RWT, 0); } CFG_PRINT("%s Rx watchdog timer\n", (rx_desc_data->use_riwt ? "Enabled" : "Disabled")); enable_dma_interrupts(qInx, pdata->version, &pdata->hw_cfg); /* set PBLx8 */ DWC_REG_WR_BIT(DMA_CR(qInx), DMA_CR_PBLx8, 0x1); /* set TX PBL = 256 */ DWC_REG_WR_FIELD(DMA_TCR(qInx), DMA_TCR_PBL, 32); /* set RX PBL = 256 */ DWC_REG_WR_FIELD(DMA_RCR(qInx), DMA_RCR_PBL, 32); /* To get Best Performance */ DWC_REG_WR_BIT(DMA_SBMR, DMA_SBMR_BLEN16, 0x1); DWC_REG_WR_BIT(DMA_SBMR, DMA_SBMR_BLEN8, 0x1); DWC_REG_WR_BIT(DMA_SBMR, DMA_SBMR_BLEN4, 0x1); DWC_REG_WR_FIELD(DMA_SBMR, DMA_SBMR_RD_OSR_LMT, 0x2); /* enable TSO if HW supports */ if (pdata->hw_feat.tso_en) DWC_REG_WR_BIT(DMA_TCR(qInx), DMA_TCR_TSE, 0x1); CFG_PRINT("%s TSO\n", (pdata->hw_feat.tso_en ? "Enabled" : "Disabled")); /* program split header mode */ DWC_REG_WR_BIT(DMA_CR(qInx), DMA_CR_SPH, pdata->rx_split_hdr); CFG_PRINT("%s Rx Split header mode\n", (pdata->rx_split_hdr ? "Enabled" : "Disabled")); /* start TX DMA */ DWC_REG_WR_BIT(DMA_TCR(qInx), DMA_TCR_ST, 0x1); /* start RX DMA */ DWC_REG_WR_BIT(DMA_RCR(qInx), DMA_RCR_SR, 0x1); DBGPR("<--configure_dma_channel\n"); return Y_SUCCESS; } /*! * \brief This sequence is used to enable MAC interrupts * \return Success or Failure * \retval 0 Success * \retval -1 Failure */ static int enable_mac_interrupts(hw_config_t *hw_cfg) { /* RGMII/SMII interrupt */ VAR32_SET_BIT(hw_cfg->mac_ier, MAC_IER_RGMIIIE, 0x1); /* PCS Link Status Interrupt */ VAR32_SET_BIT(hw_cfg->mac_ier, MAC_IER_PCSLCHGIE, 0x1); /* PCS AN Completion Interrupt */ VAR32_SET_BIT(hw_cfg->mac_ier, MAC_IER_PCSANCIE, 0x1); /* LPIIM - LPI Interrupt Enable - THIS IS ONLY REQUIRED FOR EEE */ VAR32_SET_BIT(hw_cfg->mac_ier, MAC_IER_LPIIE, 0x1); DWC_REG_WR(MAC_IER, hw_cfg->mac_ier); CFG_PRINT("[%s] MAC_IER = 0x%08x\n", __FUNCTION__, hw_cfg->mac_ier); return Y_SUCCESS; } static int configure_mac(struct DWC_ETH_QOS_prv_data *pdata) { uint32_t varMAC_MCR; uint32_t qInx; DBGPR("-->configure_mac\n"); for (qInx = 0; qInx < DWC_ETH_QOS_RX_QUEUE_CNT; qInx++) { DWC_REG_WR_IDX_FIELD(MAC_RQCR0, MAC_RQCR0_QEN, qInx, 0x2); } /* Set Tx flow control parameters */ for (qInx = 0; qInx < DWC_ETH_QOS_TX_QUEUE_CNT; qInx++) { /* set Pause Time */ DWC_REG_WR_FIELD(MAC_TQFCR(qInx), MAC_TQFCR_PT, 0xffff); /* Assign priority for RX flow control */ /* Assign priority for TX flow control */ if (qInx >= 0 && qInx <= 3) { DWC_REG_WR_IDX_FIELD(MAC_TQPMR0, MAC_TQPMR0_PSTQ, qInx, qInx); DWC_REG_WR_IDX_FIELD(MAC_RQCR2, MAC_RQCR2_PSRQ, qInx, 0x1 << qInx); } else if (qInx >= 4 && qInx <= 7) { DWC_REG_WR_IDX_FIELD(MAC_TQPMR1, MAC_TQPMR1_PSTQ, qInx, qInx); DWC_REG_WR_IDX_FIELD(MAC_RQCR3, MAC_RQCR3_PSRQ, qInx, 0x1 << qInx); } 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(); /* Read current MAC configuration */ varMAC_MCR = DWC_REG_RD(MAC_MCR); /* Configure MAC for Jumbo frame */ if (pdata->dev->mtu > DWC_ETH_QOS_ETH_FRAME_LEN) { if (pdata->dev->mtu < DWC_ETH_QOS_MAX_GPSL) { VAR32_SET_BIT(varMAC_MCR, MAC_MCR_JE, 0x1); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_WD, 0x0); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_GPSLCE, 0x0); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_JD, 0x0); } else { DWC_REG_WR_FIELD(MAC_MECR, MAC_MECR_GPSL, DWC_ETH_QOS_MAX_SUPPORTED_MTU); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_JE, 0x0); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_WD, 0x1); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_GPSLCE, 0x1); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_JD, 0x1); printk(KERN_ALERT "Configured Gaint Packet Size Limit to %d\n", DWC_ETH_QOS_MAX_SUPPORTED_MTU); } CFG_PRINT("Enabled JUMBO pkt\n"); } else { VAR32_SET_BIT(varMAC_MCR, MAC_MCR_JE, 0x0); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_WD, 0x0); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_GPSLCE, 0x0); VAR32_SET_BIT(varMAC_MCR, MAC_MCR_JD, 0x0); CFG_PRINT("Disabled JUMBO pkt\n"); } /* Enable CRC stripping for Type packets */ VAR32_SET_BIT(varMAC_MCR, MAC_MCR_CST, 0x1); /* Enable Automatic Pad or CRC Stripping */ VAR32_SET_BIT(varMAC_MCR, MAC_MCR_ACS, 0x1); /* Enable MAC Transmit process */ VAR32_SET_BIT(varMAC_MCR, MAC_MCR_TE, 0x1); /* Enable MAC Receive process */ VAR32_SET_BIT(varMAC_MCR, MAC_MCR_RE, 0x1); /* Configure MAC IP Checksum Offload */ if (pdata->hw_feat.rx_coe_sel && ((pdata->dev_state & NETIF_F_RXCSUM) == NETIF_F_RXCSUM)) VAR32_SET_BIT(varMAC_MCR, MAC_MCR_IPC, 0x1); /* Write MAC configuration */ DWC_REG_WR(MAC_MCR, varMAC_MCR); /* Update the MAC address */ update_mac_addr(0, pdata->dev->dev_addr); #ifdef DWC_ETH_QOS_ENABLE_VLAN_TAG configure_mac_for_vlan_pkt(); if (pdata->hw_feat.vlan_hash_en) config_vlan_filtering(1, 1, 0); #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(); } enable_mac_interrupts(&pdata->hw_cfg); DBGPR("<--configure_mac\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) { uint32_t qInx; DBGPR("-->DWC_ETH_QOS_yinit\n"); /* reset mmc counters */ DWC_REG_WR(MMC_CR, 0x1); for (qInx = 0; qInx < DWC_ETH_QOS_TX_QUEUE_CNT; qInx++) { configure_mtl_queue(qInx, pdata); } //Mapping MTL Rx queue and DMA Rx channel. DWC_REG_WR(MTL_RQDCM0, 0x03020100); DWC_REG_WR(MTL_RQDCM1, 0x07060504); #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT /* enable tx drop status */ DWC_REG_WR_BIT(MTL_OMR, MTL_OMR_DTXSTS, 0x1); #endif configure_mac(pdata); /* Setting INCRx */ DWC_REG_WR(DMA_SBMR, 0x0); for (qInx = 0; qInx < DWC_ETH_QOS_TX_QUEUE_CNT; qInx++) { configure_dma_channel(qInx, pdata); } #ifdef DWC_ETH_QOS_CERTIFICATION_PKTBURSTCNT_HALFDUPLEX MTL_Q0ROMR_FEP_UdfWr(0x1); DWC_REG_WR_BIT(MAC_PFR, MAC_PFR_RA, 0x1); DWC_REG_WR_BIT(MAC_MCR, MAC_MCR_BE, 0x1); #endif DBGPR("<--DWC_ETH_QOS_yinit\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(hw_interface_t *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_speed = set_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->sw_reset = DWC_ETH_QOS_sw_reset; /* 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_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; hw_if->disable_tx_interrupt = disable_tx_interrupt; hw_if->enable_tx_interrupt = enable_tx_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; /* 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->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_addr = update_mac_addr; 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; /* 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; /* 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_vlan = config_tx_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; DBGPR("<--DWC_ETH_QOS_init_function_ptrs_dev\n"); }