| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2018-2022 Marvell International Ltd. |
| * |
| * Functions to configure the BGX MAC. |
| */ |
| |
| #include <log.h> |
| #include <linux/delay.h> |
| |
| #include <mach/cvmx-regs.h> |
| #include <mach/cvmx-csr.h> |
| #include <mach/cvmx-bootmem.h> |
| #include <mach/octeon-model.h> |
| #include <mach/cvmx-fuse.h> |
| #include <mach/octeon-feature.h> |
| #include <mach/cvmx-qlm.h> |
| #include <mach/octeon_qlm.h> |
| #include <mach/cvmx-pcie.h> |
| #include <mach/cvmx-coremask.h> |
| |
| #include <mach/cvmx-agl-defs.h> |
| #include <mach/cvmx-bgxx-defs.h> |
| #include <mach/cvmx-gmxx-defs.h> |
| #include <mach/cvmx-ipd-defs.h> |
| #include <mach/cvmx-pki-defs.h> |
| #include <mach/cvmx-xcv-defs.h> |
| |
| #include <mach/cvmx-helper.h> |
| #include <mach/cvmx-helper-board.h> |
| #include <mach/cvmx-helper-fdt.h> |
| #include <mach/cvmx-helper-bgx.h> |
| #include <mach/cvmx-helper-cfg.h> |
| #include <mach/cvmx-helper-util.h> |
| #include <mach/cvmx-helper-pki.h> |
| |
| #include <mach/cvmx-global-resources.h> |
| #include <mach/cvmx-pko-internal-ports-range.h> |
| #include <mach/cvmx-ilk.h> |
| #include <mach/cvmx-pip.h> |
| |
| /* Enable this define to see BGX error messages */ |
| /*#define DEBUG_BGX */ |
| |
| /* Enable this variable to trace functions called for initializing BGX */ |
| static const int debug; |
| |
| /** |
| * cvmx_helper_bgx_override_autoneg(int xiface, int index) is a function pointer |
| * to override enabling/disabling of autonegotiation for SGMII, 10G-KR or 40G-KR4 |
| * interfaces. This function is called when interface is initialized. |
| */ |
| int (*cvmx_helper_bgx_override_autoneg)(int xiface, int index) = NULL; |
| |
| /* |
| * cvmx_helper_bgx_override_fec(int xiface) is a function pointer |
| * to override enabling/disabling of FEC for 10G interfaces. This function |
| * is called when interface is initialized. |
| */ |
| int (*cvmx_helper_bgx_override_fec)(int xiface, int index) = NULL; |
| |
| /** |
| * Delay after enabling an interface based on the mode. Different modes take |
| * different amounts of time. |
| */ |
| static void |
| __cvmx_helper_bgx_interface_enable_delay(cvmx_helper_interface_mode_t mode) |
| { |
| switch (mode) { |
| case CVMX_HELPER_INTERFACE_MODE_10G_KR: |
| case CVMX_HELPER_INTERFACE_MODE_40G_KR4: |
| case CVMX_HELPER_INTERFACE_MODE_XLAUI: |
| case CVMX_HELPER_INTERFACE_MODE_XFI: |
| mdelay(250); |
| break; |
| case CVMX_HELPER_INTERFACE_MODE_RXAUI: |
| case CVMX_HELPER_INTERFACE_MODE_XAUI: |
| mdelay(100); |
| break; |
| case CVMX_HELPER_INTERFACE_MODE_SGMII: |
| mdelay(50); |
| break; |
| default: |
| mdelay(50); |
| break; |
| } |
| } |
| |
| /** |
| * @INTERNAL |
| * |
| * Returns number of ports based on interface |
| * @param xiface Which xiface |
| * @return Number of ports based on xiface |
| */ |
| int __cvmx_helper_bgx_enumerate(int xiface) |
| { |
| cvmx_bgxx_cmr_tx_lmacs_t lmacs; |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| |
| lmacs.u64 = csr_rd_node(xi.node, CVMX_BGXX_CMR_TX_LMACS(xi.interface)); |
| return lmacs.s.lmacs; |
| } |
| |
| /** |
| * @INTERNAL |
| * |
| * Returns mode of each BGX LMAC (port). |
| * This is different than 'cvmx_helper_interface_get_mode()' which |
| * provides mode of an entire interface, but when BGX is in "mixed" |
| * mode this function should be called instead to get the protocol |
| * for each port (BGX LMAC) individually. |
| * Both function return the same enumerated mode. |
| * |
| * @param xiface is the global interface identifier |
| * @param index is the interface port index |
| * @returns mode of the individual port |
| */ |
| cvmx_helper_interface_mode_t cvmx_helper_bgx_get_mode(int xiface, int index) |
| { |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_bgxx_spux_br_pmd_control_t pmd_control; |
| |
| cmr_config.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| |
| switch (cmr_config.s.lmac_type) { |
| case 0: |
| return CVMX_HELPER_INTERFACE_MODE_SGMII; |
| case 1: |
| return CVMX_HELPER_INTERFACE_MODE_XAUI; |
| case 2: |
| return CVMX_HELPER_INTERFACE_MODE_RXAUI; |
| case 3: |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) |
| return cvmx_helper_interface_get_mode(xiface); |
| pmd_control.u64 = csr_rd_node( |
| xi.node, |
| CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, xi.interface)); |
| if (pmd_control.s.train_en) |
| return CVMX_HELPER_INTERFACE_MODE_10G_KR; |
| else |
| return CVMX_HELPER_INTERFACE_MODE_XFI; |
| break; |
| case 4: |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) |
| return cvmx_helper_interface_get_mode(xiface); |
| pmd_control.u64 = csr_rd_node( |
| xi.node, |
| CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, xi.interface)); |
| if (pmd_control.s.train_en) |
| return CVMX_HELPER_INTERFACE_MODE_40G_KR4; |
| else |
| return CVMX_HELPER_INTERFACE_MODE_XLAUI; |
| break; |
| case 5: |
| return CVMX_HELPER_INTERFACE_MODE_RGMII; |
| default: |
| return CVMX_HELPER_INTERFACE_MODE_DISABLED; |
| } |
| } |
| |
| static int __cvmx_helper_bgx_rgmii_speed(cvmx_helper_link_info_t link_info) |
| { |
| cvmx_xcv_reset_t xcv_reset; |
| cvmx_xcv_ctl_t xcv_ctl; |
| cvmx_xcv_batch_crd_ret_t crd_ret; |
| cvmx_xcv_dll_ctl_t dll_ctl; |
| cvmx_xcv_comp_ctl_t comp_ctl; |
| int speed; |
| int up = link_info.s.link_up; |
| int do_credits; |
| |
| if (link_info.s.speed == 100) |
| speed = 1; |
| else if (link_info.s.speed == 10) |
| speed = 0; |
| else |
| speed = 2; |
| |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_ctl.u64 = csr_rd(CVMX_XCV_CTL); |
| do_credits = up && !xcv_reset.s.enable; |
| |
| if (xcv_ctl.s.lpbk_int) { |
| xcv_reset.s.clkrst = 0; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| } |
| |
| if (up && (!xcv_reset.s.enable || xcv_ctl.s.speed != speed)) { |
| if (debug) |
| debug("%s: *** Enabling XCV block\n", __func__); |
| /* Enable the XCV block */ |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.enable = 1; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| |
| /* Set operating mode */ |
| xcv_ctl.u64 = csr_rd(CVMX_XCV_CTL); |
| xcv_ctl.s.speed = speed; |
| csr_wr(CVMX_XCV_CTL, xcv_ctl.u64); |
| |
| /* Configure DLL - enable or bypass */ |
| /* TX no bypass, RX bypass */ |
| dll_ctl.u64 = csr_rd(CVMX_XCV_DLL_CTL); |
| dll_ctl.s.clkrx_set = 0; |
| dll_ctl.s.clkrx_byp = 1; |
| dll_ctl.s.clktx_byp = 0; |
| csr_wr(CVMX_XCV_DLL_CTL, dll_ctl.u64); |
| |
| /* Enable */ |
| dll_ctl.u64 = csr_rd(CVMX_XCV_DLL_CTL); |
| dll_ctl.s.refclk_sel = 0; |
| csr_wr(CVMX_XCV_DLL_CTL, dll_ctl.u64); |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.dllrst = 0; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| |
| /* Delay deems to be need so XCV_DLL_CTL[CLK_SET] works */ |
| udelay(10); |
| |
| comp_ctl.u64 = csr_rd(CVMX_XCV_COMP_CTL); |
| //comp_ctl.s.drv_pctl = 0; |
| //comp_ctl.s.drv_nctl = 0; |
| comp_ctl.s.drv_byp = 0; |
| csr_wr(CVMX_XCV_COMP_CTL, comp_ctl.u64); |
| |
| /* enable */ |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.comp = 1; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| |
| /* setup the RXC */ |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.clkrst = !xcv_ctl.s.lpbk_int; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| |
| /* datapaths come out of the reset |
| * - the datapath resets will disengage BGX from the RGMII |
| * interface |
| * - XCV will continue to return TX credits for each tick that |
| * is sent on the TX data path |
| */ |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.tx_dat_rst_n = 1; |
| xcv_reset.s.rx_dat_rst_n = 1; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| } else if (debug) { |
| debug("%s: *** Not enabling XCV\n", __func__); |
| debug(" up: %s, xcv_reset.s.enable: %d, xcv_ctl.s.speed: %d, speed: %d\n", |
| up ? "true" : "false", (unsigned int)xcv_reset.s.enable, |
| (unsigned int)xcv_ctl.s.speed, speed); |
| } |
| |
| /* enable the packet flow |
| * - The packet resets will be only disengage on packet boundaries |
| * - XCV will continue to return TX credits for each tick that is |
| * sent on the TX datapath |
| */ |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.tx_pkt_rst_n = up; |
| xcv_reset.s.rx_pkt_rst_n = up; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| |
| /* Full reset when link is down */ |
| if (!up) { |
| if (debug) |
| debug("%s: *** Disabling XCV reset\n", __func__); |
| /* wait 2*MTU in time */ |
| mdelay(10); |
| /* reset the world */ |
| csr_wr(CVMX_XCV_RESET, 0); |
| } |
| |
| /* grant PKO TX credits */ |
| if (do_credits) { |
| crd_ret.u64 = csr_rd(CVMX_XCV_BATCH_CRD_RET); |
| crd_ret.s.crd_ret = 1; |
| csr_wr(CVMX_XCV_BATCH_CRD_RET, crd_ret.u64); |
| } |
| |
| return 0; |
| } |
| |
| static void __cvmx_bgx_common_init_pknd(int xiface, int index) |
| { |
| int num_ports; |
| int num_chl = 16; |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| int node = xi.node; |
| int pknd; |
| cvmx_bgxx_cmrx_rx_bp_on_t bgx_rx_bp_on; |
| cvmx_bgxx_cmrx_rx_id_map_t cmr_rx_id_map; |
| cvmx_bgxx_cmr_chan_msk_and_t chan_msk_and; |
| cvmx_bgxx_cmr_chan_msk_or_t chan_msk_or; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| num_ports = cvmx_helper_ports_on_interface(xiface); |
| /* Modify bp_on mark, depending on number of LMACS on that interface |
| * and write it for every port |
| */ |
| bgx_rx_bp_on.u64 = 0; |
| bgx_rx_bp_on.s.mark = (CVMX_BGX_RX_FIFO_SIZE / (num_ports * 4 * 16)); |
| |
| /* Setup pkind */ |
| pknd = cvmx_helper_get_pknd(xiface, index); |
| cmr_rx_id_map.u64 = csr_rd_node( |
| node, CVMX_BGXX_CMRX_RX_ID_MAP(index, xi.interface)); |
| cmr_rx_id_map.s.pknd = pknd; |
| /* Change the default reassembly id (RID), as max 14 RIDs allowed */ |
| if (OCTEON_IS_MODEL(OCTEON_CN73XX)) |
| cmr_rx_id_map.s.rid = ((4 * xi.interface) + 2 + index); |
| csr_wr_node(node, CVMX_BGXX_CMRX_RX_ID_MAP(index, xi.interface), |
| cmr_rx_id_map.u64); |
| /* Set backpressure channel mask AND/OR registers */ |
| chan_msk_and.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMR_CHAN_MSK_AND(xi.interface)); |
| chan_msk_or.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMR_CHAN_MSK_OR(xi.interface)); |
| chan_msk_and.s.msk_and |= ((1 << num_chl) - 1) << (16 * index); |
| chan_msk_or.s.msk_or |= ((1 << num_chl) - 1) << (16 * index); |
| csr_wr_node(node, CVMX_BGXX_CMR_CHAN_MSK_AND(xi.interface), |
| chan_msk_and.u64); |
| csr_wr_node(node, CVMX_BGXX_CMR_CHAN_MSK_OR(xi.interface), |
| chan_msk_or.u64); |
| /* set rx back pressure (bp_on) on value */ |
| csr_wr_node(node, CVMX_BGXX_CMRX_RX_BP_ON(index, xi.interface), |
| bgx_rx_bp_on.u64); |
| } |
| |
| /** |
| * @INTERNAL |
| * Probe a SGMII interface and determine the number of ports |
| * connected to it. The SGMII interface should still be down after |
| * this call. This is used by interfaces using the bgx mac. |
| * |
| * @param xiface Interface to probe |
| * |
| * @return Number of ports on the interface. Zero to disable. |
| */ |
| int __cvmx_helper_bgx_probe(int xiface) |
| { |
| return __cvmx_helper_bgx_enumerate(xiface); |
| } |
| |
| /** |
| * @INTERNAL |
| * Return the size of the BGX TX_FIFO for a given LMAC, |
| * or 0 if the requested LMAC is inactive. |
| * |
| * TBD: Need also to add a "__cvmx_helper_bgx_speed()" function to |
| * return the speed of each LMAC. |
| */ |
| int __cvmx_helper_bgx_fifo_size(int xiface, unsigned int lmac) |
| { |
| cvmx_bgxx_cmr_tx_lmacs_t lmacs; |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| unsigned int tx_fifo_size = CVMX_BGX_TX_FIFO_SIZE; |
| |
| /* FIXME: Add validation for interface# < BGX_count */ |
| lmacs.u64 = csr_rd_node(xi.node, CVMX_BGXX_CMR_TX_LMACS(xi.interface)); |
| |
| switch (lmacs.s.lmacs) { |
| case 1: |
| if (lmac > 0) |
| return 0; |
| else |
| return tx_fifo_size; |
| case 2: |
| if (lmac > 1) |
| return 0; |
| else |
| return tx_fifo_size >> 1; |
| case 3: |
| if (lmac > 2) |
| return 0; |
| else |
| return tx_fifo_size >> 2; |
| case 4: |
| if (lmac > 3) |
| return 0; |
| else |
| return tx_fifo_size >> 2; |
| default: |
| return 0; |
| } |
| } |
| |
| /** |
| * @INTERNAL |
| * Perform initialization required only once for an SGMII port. |
| * |
| * @param xiface Interface to init |
| * @param index Index of prot on the interface |
| * |
| * @return Zero on success, negative on failure |
| */ |
| static int __cvmx_helper_bgx_sgmii_hardware_init_one_time(int xiface, int index) |
| { |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| int node = xi.node; |
| const u64 clock_mhz = 1200; /* todo: fixme */ |
| cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_misc_ctl; |
| cvmx_bgxx_gmp_pcs_linkx_timer_t gmp_timer; |
| |
| if (!cvmx_helper_is_port_valid(xi.interface, index)) |
| return 0; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| /* |
| * Write PCS*_LINK*_TIMER_COUNT_REG[COUNT] with the |
| * appropriate value. 1000BASE-X specifies a 10ms |
| * interval. SGMII specifies a 1.6ms interval. |
| */ |
| gmp_misc_ctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); |
| /* Adjust the MAC mode if requested by device tree */ |
| gmp_misc_ctl.s.mac_phy = cvmx_helper_get_mac_phy_mode(xiface, index); |
| gmp_misc_ctl.s.mode = cvmx_helper_get_1000x_mode(xiface, index); |
| csr_wr_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface), |
| gmp_misc_ctl.u64); |
| |
| gmp_timer.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_LINKX_TIMER(index, xi.interface)); |
| if (gmp_misc_ctl.s.mode) |
| /* 1000BASE-X */ |
| gmp_timer.s.count = (10000ull * clock_mhz) >> 10; |
| else |
| /* SGMII */ |
| gmp_timer.s.count = (1600ull * clock_mhz) >> 10; |
| |
| csr_wr_node(node, CVMX_BGXX_GMP_PCS_LINKX_TIMER(index, xi.interface), |
| gmp_timer.u64); |
| |
| /* |
| * Write the advertisement register to be used as the |
| * tx_Config_Reg<D15:D0> of the autonegotiation. In |
| * 1000BASE-X mode, tx_Config_Reg<D15:D0> is PCS*_AN*_ADV_REG. |
| * In SGMII PHY mode, tx_Config_Reg<D15:D0> is |
| * PCS*_SGM*_AN_ADV_REG. In SGMII MAC mode, |
| * tx_Config_Reg<D15:D0> is the fixed value 0x4001, so this |
| * step can be skipped. |
| */ |
| if (gmp_misc_ctl.s.mode) { |
| /* 1000BASE-X */ |
| cvmx_bgxx_gmp_pcs_anx_adv_t gmp_an_adv; |
| |
| gmp_an_adv.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_ANX_ADV(index, xi.interface)); |
| gmp_an_adv.s.rem_flt = 0; |
| gmp_an_adv.s.pause = 3; |
| gmp_an_adv.s.hfd = 1; |
| gmp_an_adv.s.fd = 1; |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_PCS_ANX_ADV(index, xi.interface), |
| gmp_an_adv.u64); |
| } else { |
| if (gmp_misc_ctl.s.mac_phy) { |
| /* PHY Mode */ |
| cvmx_bgxx_gmp_pcs_sgmx_an_adv_t gmp_sgmx_an_adv; |
| |
| gmp_sgmx_an_adv.u64 = |
| csr_rd_node(node, CVMX_BGXX_GMP_PCS_SGMX_AN_ADV( |
| index, xi.interface)); |
| gmp_sgmx_an_adv.s.dup = 1; |
| gmp_sgmx_an_adv.s.speed = 2; |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_PCS_SGMX_AN_ADV(index, |
| xi.interface), |
| gmp_sgmx_an_adv.u64); |
| } else { |
| /* MAC Mode - Nothing to do */ |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * @INTERNAL |
| * Bring up the SGMII interface to be ready for packet I/O but |
| * leave I/O disabled using the GMX override. This function |
| * follows the bringup documented in 10.6.3 of the manual. |
| * |
| * @param xiface Interface to bringup |
| * @param num_ports Number of ports on the interface |
| * |
| * @return Zero on success, negative on failure |
| */ |
| static int __cvmx_helper_bgx_sgmii_hardware_init(int xiface, int num_ports) |
| { |
| int index; |
| int do_link_set = 1; |
| |
| for (index = 0; index < num_ports; index++) { |
| int xipd_port = cvmx_helper_get_ipd_port(xiface, index); |
| cvmx_helper_interface_mode_t mode; |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| continue; |
| |
| __cvmx_helper_bgx_port_init(xipd_port, 0); |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) |
| continue; |
| |
| if (do_link_set) |
| __cvmx_helper_bgx_sgmii_link_set( |
| xipd_port, |
| __cvmx_helper_bgx_sgmii_link_get(xipd_port)); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @INTERNAL |
| * Bringup and enable a SGMII interface. After this call packet |
| * I/O should be fully functional. This is called with IPD |
| * enabled but PKO disabled. This is used by interfaces using |
| * the bgx mac. |
| * |
| * @param xiface Interface to bring up |
| * |
| * @return Zero on success, negative on failure |
| */ |
| int __cvmx_helper_bgx_sgmii_enable(int xiface) |
| { |
| int num_ports; |
| |
| num_ports = cvmx_helper_ports_on_interface(xiface); |
| __cvmx_helper_bgx_sgmii_hardware_init(xiface, num_ports); |
| |
| return 0; |
| } |
| |
| /** |
| * @INTERNAL |
| * Initialize the SERDES link for the first time or after a loss |
| * of link. |
| * |
| * @param xiface Interface to init |
| * @param index Index of prot on the interface |
| * |
| * @return Zero on success, negative on failure |
| */ |
| static int __cvmx_helper_bgx_sgmii_hardware_init_link(int xiface, int index) |
| { |
| cvmx_bgxx_gmp_pcs_mrx_control_t gmp_control; |
| cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_misc_ctl; |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| int phy_mode, mode_1000x; |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| int interface = xi.interface; |
| int node = xi.node; |
| int autoneg = 0; |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| return 0; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| gmp_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface)); |
| /* Take PCS through a reset sequence */ |
| gmp_control.s.reset = 1; |
| csr_wr_node(node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface), |
| gmp_control.u64); |
| |
| /* Wait until GMP_PCS_MRX_CONTROL[reset] comes out of reset */ |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface), |
| cvmx_bgxx_gmp_pcs_mrx_control_t, reset, ==, 0, 10000)) { |
| debug("SGMII%d: Timeout waiting for port %d to finish reset\n", |
| interface, index); |
| return -1; |
| } |
| |
| cmr_config.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| |
| gmp_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface)); |
| if (cvmx_helper_get_port_phy_present(xiface, index)) { |
| gmp_control.s.pwr_dn = 0; |
| } else { |
| gmp_control.s.spdmsb = 1; |
| gmp_control.s.spdlsb = 0; |
| gmp_control.s.pwr_dn = 0; |
| } |
| /* Write GMP_PCS_MR*_CONTROL[RST_AN]=1 to ensure a fresh SGMII |
| * negotiation starts. |
| */ |
| autoneg = cvmx_helper_get_port_autonegotiation(xiface, index); |
| gmp_control.s.rst_an = 1; |
| gmp_control.s.an_en = (cmr_config.s.lmac_type != 5) && autoneg; |
| csr_wr_node(node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface), |
| gmp_control.u64); |
| |
| phy_mode = cvmx_helper_get_mac_phy_mode(xiface, index); |
| mode_1000x = cvmx_helper_get_1000x_mode(xiface, index); |
| |
| gmp_misc_ctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); |
| gmp_misc_ctl.s.mac_phy = phy_mode; |
| gmp_misc_ctl.s.mode = mode_1000x; |
| csr_wr_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface), |
| gmp_misc_ctl.u64); |
| |
| if (phy_mode || !autoneg) |
| /* In PHY mode we can't query the link status so we just |
| * assume that the link is up |
| */ |
| return 0; |
| |
| /* Wait for GMP_PCS_MRX_CONTROL[an_cpt] to be set, indicating that |
| * SGMII autonegotiation is complete. In MAC mode this isn't an |
| * ethernet link, but a link between OCTEON and PHY. |
| */ |
| if (cmr_config.s.lmac_type != 5 && |
| CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_GMP_PCS_MRX_STATUS(index, xi.interface), |
| cvmx_bgxx_gmp_pcs_mrx_status_t, an_cpt, ==, 1, 10000)) { |
| debug("SGMII%d: Port %d link timeout\n", interface, index); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @INTERNAL |
| * Configure an SGMII link to the specified speed after the SERDES |
| * link is up. |
| * |
| * @param xiface Interface to init |
| * @param index Index of prot on the interface |
| * @param link_info Link state to configure |
| * |
| * @return Zero on success, negative on failure |
| */ |
| static int __cvmx_helper_bgx_sgmii_hardware_init_link_speed( |
| int xiface, int index, cvmx_helper_link_info_t link_info) |
| { |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_miscx_ctl; |
| cvmx_bgxx_gmp_gmi_prtx_cfg_t gmp_prtx_cfg; |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| int node = xi.node; |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| return 0; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| /* Disable GMX before we make any changes. */ |
| cmr_config.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| cmr_config.s.data_pkt_tx_en = 0; |
| cmr_config.s.data_pkt_rx_en = 0; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), |
| cmr_config.u64); |
| |
| /* Wait for GMX to be idle */ |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface), |
| cvmx_bgxx_gmp_gmi_prtx_cfg_t, rx_idle, ==, 1, 10000) || |
| CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface), |
| cvmx_bgxx_gmp_gmi_prtx_cfg_t, tx_idle, ==, 1, 10000)) { |
| debug("SGMII%d:%d: Timeout waiting for port %d to be idle\n", |
| node, xi.interface, index); |
| return -1; |
| } |
| |
| /* Read GMX CFG again to make sure the disable completed */ |
| gmp_prtx_cfg.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface)); |
| |
| /* |
| * Get the misc control for PCS. We will need to set the |
| * duplication amount. |
| */ |
| gmp_miscx_ctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); |
| |
| /* |
| * Use GMXENO to force the link down if the status we get says |
| * it should be down. |
| */ |
| gmp_miscx_ctl.s.gmxeno = !link_info.s.link_up; |
| |
| /* Only change the duplex setting if the link is up */ |
| if (link_info.s.link_up) |
| gmp_prtx_cfg.s.duplex = link_info.s.full_duplex; |
| |
| /* Do speed based setting for GMX */ |
| switch (link_info.s.speed) { |
| case 10: |
| gmp_prtx_cfg.s.speed = 0; |
| gmp_prtx_cfg.s.speed_msb = 1; |
| gmp_prtx_cfg.s.slottime = 0; |
| /* Setting from GMX-603 */ |
| gmp_miscx_ctl.s.samp_pt = 25; |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_GMI_TXX_SLOT(index, xi.interface), |
| 64); |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_GMI_TXX_BURST(index, xi.interface), |
| 0); |
| break; |
| case 100: |
| gmp_prtx_cfg.s.speed = 0; |
| gmp_prtx_cfg.s.speed_msb = 0; |
| gmp_prtx_cfg.s.slottime = 0; |
| gmp_miscx_ctl.s.samp_pt = 0x5; |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_GMI_TXX_SLOT(index, xi.interface), |
| 64); |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_GMI_TXX_BURST(index, xi.interface), |
| 0); |
| break; |
| case 1000: |
| gmp_prtx_cfg.s.speed = 1; |
| gmp_prtx_cfg.s.speed_msb = 0; |
| gmp_prtx_cfg.s.slottime = 1; |
| gmp_miscx_ctl.s.samp_pt = 1; |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_GMI_TXX_SLOT(index, xi.interface), |
| 512); |
| if (gmp_prtx_cfg.s.duplex) |
| /* full duplex */ |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_GMI_TXX_BURST(index, |
| xi.interface), |
| 0); |
| else |
| /* half duplex */ |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_GMI_TXX_BURST(index, |
| xi.interface), |
| 8192); |
| break; |
| default: |
| break; |
| } |
| |
| /* Write the new misc control for PCS */ |
| csr_wr_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface), |
| gmp_miscx_ctl.u64); |
| |
| /* Write the new GMX settings with the port still disabled */ |
| csr_wr_node(node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface), |
| gmp_prtx_cfg.u64); |
| |
| /* Read GMX CFG again to make sure the config completed */ |
| csr_rd_node(node, CVMX_BGXX_GMP_GMI_PRTX_CFG(index, xi.interface)); |
| |
| /* Enable back BGX. */ |
| cmr_config.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| if (debug) |
| debug("%s: Enabling tx and rx packets on %d:%d\n", __func__, |
| xi.interface, index); |
| cmr_config.s.data_pkt_tx_en = 1; |
| cmr_config.s.data_pkt_rx_en = 1; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), |
| cmr_config.u64); |
| |
| return 0; |
| } |
| |
| /** |
| * @INTERNAL |
| * Return the link state of an IPD/PKO port as returned by |
| * auto negotiation. The result of this function may not match |
| * Octeon's link config if auto negotiation has changed since |
| * the last call to cvmx_helper_link_set(). This is used by |
| * interfaces using the bgx mac. |
| * |
| * @param xipd_port IPD/PKO port to query |
| * |
| * @return Link state |
| */ |
| cvmx_helper_link_info_t __cvmx_helper_bgx_sgmii_link_get(int xipd_port) |
| { |
| cvmx_helper_link_info_t result; |
| cvmx_bgxx_gmp_pcs_mrx_control_t gmp_control; |
| cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_misc_ctl; |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); |
| int node = xi.node; |
| int index = cvmx_helper_get_interface_index_num(xp.port); |
| |
| result.u64 = 0; |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| return result; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| gmp_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface)); |
| if (gmp_control.s.loopbck1) { |
| int qlm = cvmx_qlm_lmac(xiface, index); |
| int speed; |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX)) |
| speed = cvmx_qlm_get_gbaud_mhz_node(node, qlm); |
| else |
| speed = cvmx_qlm_get_gbaud_mhz(qlm); |
| /* Force 1Gbps full duplex link for internal loopback */ |
| result.s.link_up = 1; |
| result.s.full_duplex = 1; |
| result.s.speed = speed * 8 / 10; |
| return result; |
| } |
| |
| gmp_misc_ctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); |
| if (gmp_misc_ctl.s.mac_phy || |
| cvmx_helper_get_port_force_link_up(xiface, index)) { |
| int qlm = cvmx_qlm_lmac(xiface, index); |
| int speed; |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX)) |
| speed = cvmx_qlm_get_gbaud_mhz_node(node, qlm); |
| else |
| speed = cvmx_qlm_get_gbaud_mhz(qlm); |
| /* PHY Mode */ |
| /* Note that this also works for 1000base-X mode */ |
| |
| result.s.speed = speed * 8 / 10; |
| result.s.full_duplex = 1; |
| result.s.link_up = 1; |
| return result; |
| } |
| |
| /* MAC Mode */ |
| return __cvmx_helper_board_link_get(xipd_port); |
| } |
| |
| /** |
| * This sequence brings down the link for the XCV RGMII interface |
| * |
| * @param interface Interface (BGX) number. Port index is always 0 |
| */ |
| static void __cvmx_helper_bgx_rgmii_link_set_down(int interface) |
| { |
| union cvmx_xcv_reset xcv_reset; |
| union cvmx_bgxx_cmrx_config cmr_config; |
| union cvmx_bgxx_gmp_pcs_mrx_control mr_control; |
| union cvmx_bgxx_cmrx_rx_fifo_len rx_fifo_len; |
| union cvmx_bgxx_cmrx_tx_fifo_len tx_fifo_len; |
| |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.rx_pkt_rst_n = 0; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| csr_rd(CVMX_XCV_RESET); |
| mdelay(10); /* Wait for 1 MTU */ |
| |
| cmr_config.u64 = csr_rd(CVMX_BGXX_CMRX_CONFIG(0, interface)); |
| cmr_config.s.data_pkt_rx_en = 0; |
| csr_wr(CVMX_BGXX_CMRX_CONFIG(0, interface), cmr_config.u64); |
| |
| /* Wait for RX and TX to be idle */ |
| do { |
| rx_fifo_len.u64 = |
| csr_rd(CVMX_BGXX_CMRX_RX_FIFO_LEN(0, interface)); |
| tx_fifo_len.u64 = |
| csr_rd(CVMX_BGXX_CMRX_TX_FIFO_LEN(0, interface)); |
| } while (rx_fifo_len.s.fifo_len > 0 && tx_fifo_len.s.lmac_idle != 1); |
| |
| cmr_config.u64 = csr_rd(CVMX_BGXX_CMRX_CONFIG(0, interface)); |
| cmr_config.s.data_pkt_tx_en = 0; |
| csr_wr(CVMX_BGXX_CMRX_CONFIG(0, interface), cmr_config.u64); |
| |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.tx_pkt_rst_n = 0; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| mr_control.u64 = csr_rd(CVMX_BGXX_GMP_PCS_MRX_CONTROL(0, interface)); |
| mr_control.s.pwr_dn = 1; |
| csr_wr(CVMX_BGXX_GMP_PCS_MRX_CONTROL(0, interface), mr_control.u64); |
| } |
| |
| /** |
| * Sets a BGS SGMII link down. |
| * |
| * @param node Octeon node number |
| * @param iface BGX interface number |
| * @param index BGX port index |
| */ |
| static void __cvmx_helper_bgx_sgmii_link_set_down(int node, int iface, |
| int index) |
| { |
| union cvmx_bgxx_gmp_pcs_miscx_ctl gmp_misc_ctl; |
| union cvmx_bgxx_gmp_pcs_mrx_control gmp_control; |
| union cvmx_bgxx_cmrx_config cmr_config; |
| |
| cmr_config.u64 = csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, iface)); |
| cmr_config.s.data_pkt_tx_en = 0; |
| cmr_config.s.data_pkt_rx_en = 0; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, iface), cmr_config.u64); |
| |
| gmp_misc_ctl.u64 = |
| csr_rd_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, iface)); |
| |
| /* Disable autonegotiation only when in MAC mode. */ |
| if (gmp_misc_ctl.s.mac_phy == 0) { |
| gmp_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, iface)); |
| gmp_control.s.an_en = 0; |
| csr_wr_node(node, CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, iface), |
| gmp_control.u64); |
| } |
| |
| /* Use GMXENO to force the link down. It will get reenabled later... */ |
| gmp_misc_ctl.s.gmxeno = 1; |
| csr_wr_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, iface), |
| gmp_misc_ctl.u64); |
| csr_rd_node(node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, iface)); |
| } |
| |
| /** |
| * @INTERNAL |
| * Configure an IPD/PKO port for the specified link state. This |
| * function does not influence auto negotiation at the PHY level. |
| * The passed link state must always match the link state returned |
| * by cvmx_helper_link_get(). It is normally best to use |
| * cvmx_helper_link_autoconf() instead. This is used by interfaces |
| * using the bgx mac. |
| * |
| * @param xipd_port IPD/PKO port to configure |
| * @param link_info The new link state |
| * |
| * @return Zero on success, negative on failure |
| */ |
| int __cvmx_helper_bgx_sgmii_link_set(int xipd_port, |
| cvmx_helper_link_info_t link_info) |
| { |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); |
| int node = xi.node; |
| int index = cvmx_helper_get_interface_index_num(xp.port); |
| const int iface = xi.interface; |
| int rc = 0; |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| return 0; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| cmr_config.u64 = csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, iface)); |
| if (link_info.s.link_up) { |
| cmr_config.s.enable = 1; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, iface), |
| cmr_config.u64); |
| /* Apply workaround for errata BGX-22429 */ |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && index) { |
| cvmx_bgxx_cmrx_config_t cmr0; |
| |
| cmr0.u64 = csr_rd_node(node, |
| CVMX_BGXX_CMRX_CONFIG(0, iface)); |
| cmr0.s.enable = 1; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(0, iface), |
| cmr0.u64); |
| } |
| __cvmx_helper_bgx_sgmii_hardware_init_link(xiface, index); |
| } else if (cvmx_helper_bgx_is_rgmii(xi.interface, index)) { |
| if (debug) |
| debug("%s: Bringing down XCV RGMII interface %d\n", |
| __func__, xi.interface); |
| __cvmx_helper_bgx_rgmii_link_set_down(xi.interface); |
| } else { /* Link is down, not RGMII */ |
| __cvmx_helper_bgx_sgmii_link_set_down(node, iface, index); |
| return 0; |
| } |
| rc = __cvmx_helper_bgx_sgmii_hardware_init_link_speed(xiface, index, |
| link_info); |
| if (cvmx_helper_bgx_is_rgmii(xiface, index)) |
| rc = __cvmx_helper_bgx_rgmii_speed(link_info); |
| |
| return rc; |
| } |
| |
| /** |
| * @INTERNAL |
| * Bringup XAUI interface. After this call packet I/O should be |
| * fully functional. |
| * |
| * @param index port on interface to bring up |
| * @param xiface Interface to bring up |
| * |
| * @return Zero on success, negative on failure |
| */ |
| static int __cvmx_helper_bgx_xaui_init(int index, int xiface) |
| { |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_bgxx_spux_br_pmd_control_t pmd_control; |
| cvmx_bgxx_spux_misc_control_t spu_misc_control; |
| cvmx_bgxx_spux_control1_t spu_control1; |
| cvmx_bgxx_spux_an_control_t spu_an_control; |
| cvmx_bgxx_spux_an_adv_t spu_an_adv; |
| cvmx_bgxx_spux_fec_control_t spu_fec_control; |
| cvmx_bgxx_spu_dbg_control_t spu_dbg_control; |
| cvmx_bgxx_smux_tx_append_t smu_tx_append; |
| cvmx_bgxx_smux_tx_ctl_t smu_tx_ctl; |
| cvmx_helper_interface_mode_t mode; |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| int interface = xi.interface; |
| int node = xi.node; |
| int use_auto_neg = 0; |
| int kr_mode = 0; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| |
| if (mode == CVMX_HELPER_INTERFACE_MODE_10G_KR || |
| mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4) { |
| kr_mode = 1; |
| if (cvmx_helper_bgx_override_autoneg) |
| use_auto_neg = |
| cvmx_helper_bgx_override_autoneg(xiface, index); |
| else |
| use_auto_neg = cvmx_helper_get_port_autonegotiation( |
| xiface, index); |
| } |
| |
| /* NOTE: This code was moved first, out of order compared to the HRM |
| * because the RESET causes all SPU registers to loose their value |
| */ |
| /* 4. Next, bring up the SMU/SPU and the BGX reconciliation layer |
| * logic: |
| */ |
| /* 4a. Take SMU/SPU through a reset sequence. Write |
| * BGX(0..5)_SPU(0..3)_CONTROL1[RESET] = 1. Read |
| * BGX(0..5)_SPU(0..3)_CONTROL1[RESET] until it changes value to 0. Keep |
| * BGX(0..5)_SPU(0..3)_MISC_CONTROL[RX_PACKET_DIS] = 1 to disable |
| * reception. |
| */ |
| spu_control1.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); |
| spu_control1.s.reset = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), |
| spu_control1.u64); |
| |
| /* 1. Wait for PCS to come out of reset */ |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), |
| cvmx_bgxx_spux_control1_t, reset, ==, 0, 10000)) { |
| debug("BGX%d:%d: SPU stuck in reset\n", node, interface); |
| return -1; |
| } |
| |
| /* 2. Write BGX(0..5)_CMR(0..3)_CONFIG[ENABLE] to 0, |
| * BGX(0..5)_SPU(0..3)_CONTROL1[LO_PWR] = 1 and |
| * BGX(0..5)_SPU(0..3)_MISC_CONTROL[RX_PACKET_DIS] = 1. |
| */ |
| spu_control1.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); |
| spu_control1.s.lo_pwr = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), |
| spu_control1.u64); |
| |
| spu_misc_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); |
| spu_misc_control.s.rx_packet_dis = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface), |
| spu_misc_control.u64); |
| |
| /* 3. At this point, it may be appropriate to disable all BGX and |
| * SMU/SPU interrupts, as a number of them will occur during bring-up |
| * of the Link. |
| * - zero BGX(0..5)_SMU(0..3)_RX_INT |
| * - zero BGX(0..5)_SMU(0..3)_TX_INT |
| * - zero BGX(0..5)_SPU(0..3)_INT |
| */ |
| csr_wr_node(node, CVMX_BGXX_SMUX_RX_INT(index, xi.interface), |
| csr_rd_node(node, |
| CVMX_BGXX_SMUX_RX_INT(index, xi.interface))); |
| csr_wr_node(node, CVMX_BGXX_SMUX_TX_INT(index, xi.interface), |
| csr_rd_node(node, |
| CVMX_BGXX_SMUX_TX_INT(index, xi.interface))); |
| csr_wr_node(node, CVMX_BGXX_SPUX_INT(index, xi.interface), |
| csr_rd_node(node, CVMX_BGXX_SPUX_INT(index, xi.interface))); |
| |
| /* 4. Configure the BGX LMAC. */ |
| /* 4a. Configure the LMAC type (40GBASE-R/10GBASE-R/RXAUI/XAUI) and |
| * SerDes selection in the BGX(0..5)_CMR(0..3)_CONFIG register, but keep |
| * the ENABLE, DATA_PKT_TX_EN and DATA_PKT_RX_EN bits clear. |
| */ |
| /* Already done in bgx_setup_one_time */ |
| |
| /* 4b. Write BGX(0..5)_SPU(0..3)_CONTROL1[LO_PWR] = 1 and |
| * BGX(0..5)_SPU(0..3)_MISC_CONTROL[RX_PACKET_DIS] = 1. |
| */ |
| /* 4b. Initialize the selected SerDes lane(s) in the QLM. See Section |
| * 28.1.2.2 in the GSER chapter. |
| */ |
| /* Already done in QLM setup */ |
| |
| /* 4c. For 10GBASE-KR or 40GBASE-KR, enable link training by writing |
| * BGX(0..5)_SPU(0..3)_BR_PMD_CONTROL[TRAIN_EN] = 1. |
| */ |
| |
| if (kr_mode && !OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_BR_PMD_LP_CUP(index, interface), 0); |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_BR_PMD_LD_CUP(index, interface), 0); |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_BR_PMD_LD_REP(index, interface), 0); |
| pmd_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, interface)); |
| pmd_control.s.train_en = 1; |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, interface), |
| pmd_control.u64); |
| } |
| |
| /* 4d. Program all other relevant BGX configuration while |
| * BGX(0..5)_CMR(0..3)_CONFIG[ENABLE] = 0. This includes all things |
| * described in this chapter. |
| */ |
| /* Always add FCS to PAUSE frames */ |
| smu_tx_append.u64 = csr_rd_node( |
| node, CVMX_BGXX_SMUX_TX_APPEND(index, xi.interface)); |
| smu_tx_append.s.fcs_c = 1; |
| csr_wr_node(node, CVMX_BGXX_SMUX_TX_APPEND(index, xi.interface), |
| smu_tx_append.u64); |
| |
| /* 4e. If Forward Error Correction is desired for 10GBASE-R or |
| * 40GBASE-R, enable it by writing |
| * BGX(0..5)_SPU(0..3)_FEC_CONTROL[FEC_EN] = 1. |
| */ |
| /* FEC is optional for 10GBASE-KR, 40GBASE-KR4, and XLAUI. We're going |
| * to disable it by default |
| */ |
| spu_fec_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_FEC_CONTROL(index, xi.interface)); |
| if (cvmx_helper_bgx_override_fec) |
| spu_fec_control.s.fec_en = |
| cvmx_helper_bgx_override_fec(xiface, index); |
| else |
| spu_fec_control.s.fec_en = |
| cvmx_helper_get_port_fec(xiface, index); |
| csr_wr_node(node, CVMX_BGXX_SPUX_FEC_CONTROL(index, xi.interface), |
| spu_fec_control.u64); |
| |
| /* 4f. If Auto-Negotiation is desired, configure and enable |
| * Auto-Negotiation as described in Section 33.6.2. |
| */ |
| spu_an_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_AN_CONTROL(index, xi.interface)); |
| /* Disable extended next pages */ |
| spu_an_control.s.xnp_en = 0; |
| spu_an_control.s.an_en = use_auto_neg; |
| csr_wr_node(node, CVMX_BGXX_SPUX_AN_CONTROL(index, xi.interface), |
| spu_an_control.u64); |
| |
| spu_fec_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_FEC_CONTROL(index, xi.interface)); |
| spu_an_adv.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_AN_ADV(index, xi.interface)); |
| spu_an_adv.s.fec_req = spu_fec_control.s.fec_en; |
| spu_an_adv.s.fec_able = 1; |
| spu_an_adv.s.a100g_cr10 = 0; |
| spu_an_adv.s.a40g_cr4 = 0; |
| spu_an_adv.s.a40g_kr4 = (mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4); |
| spu_an_adv.s.a10g_kr = (mode == CVMX_HELPER_INTERFACE_MODE_10G_KR); |
| spu_an_adv.s.a10g_kx4 = 0; |
| spu_an_adv.s.a1g_kx = 0; |
| spu_an_adv.s.xnp_able = 0; |
| spu_an_adv.s.rf = 0; |
| csr_wr_node(node, CVMX_BGXX_SPUX_AN_ADV(index, xi.interface), |
| spu_an_adv.u64); |
| |
| /* 3. Set BGX(0..5)_SPU_DBG_CONTROL[AN_ARB_LINK_CHK_EN] = 1. */ |
| spu_dbg_control.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPU_DBG_CONTROL(xi.interface)); |
| spu_dbg_control.s.an_nonce_match_dis = 1; /* Needed for loopback */ |
| spu_dbg_control.s.an_arb_link_chk_en |= kr_mode; |
| csr_wr_node(node, CVMX_BGXX_SPU_DBG_CONTROL(xi.interface), |
| spu_dbg_control.u64); |
| |
| /* 4. Execute the link bring-up sequence in Section 33.6.3. */ |
| |
| /* 5. If the auto-negotiation protocol is successful, |
| * BGX(0..5)_SPU(0..3)_AN_ADV[AN_COMPLETE] is set along with |
| * BGX(0..5)_SPU(0..3)_INT[AN_COMPLETE] when the link is up. |
| */ |
| |
| /* 3h. Set BGX(0..5)_CMR(0..3)_CONFIG[ENABLE] = 1 and |
| * BGX(0..5)_SPU(0..3)_CONTROL1[LO_PWR] = 0 to enable the LMAC. |
| */ |
| cmr_config.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| cmr_config.s.enable = 1; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), |
| cmr_config.u64); |
| /* Apply workaround for errata BGX-22429 */ |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && index) { |
| cvmx_bgxx_cmrx_config_t cmr0; |
| |
| cmr0.u64 = csr_rd_node(node, |
| CVMX_BGXX_CMRX_CONFIG(0, xi.interface)); |
| cmr0.s.enable = 1; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(0, xi.interface), |
| cmr0.u64); |
| } |
| |
| spu_control1.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); |
| spu_control1.s.lo_pwr = 0; |
| csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), |
| spu_control1.u64); |
| |
| /* 4g. Set the polarity and lane swapping of the QLM SerDes. Refer to |
| * Section 33.4.1, BGX(0..5)_SPU(0..3)_MISC_CONTROL[XOR_TXPLRT,XOR_RXPLRT] |
| * and BGX(0..5)_SPU(0..3)_MISC_CONTROL[TXPLRT,RXPLRT]. |
| */ |
| |
| /* 4c. Write BGX(0..5)_SPU(0..3)_CONTROL1[LO_PWR] = 0. */ |
| spu_control1.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); |
| spu_control1.s.lo_pwr = 0; |
| csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), |
| spu_control1.u64); |
| |
| /* 4d. Select Deficit Idle Count mode and unidirectional enable/disable |
| * via BGX(0..5)_SMU(0..3)_TX_CTL[DIC_EN,UNI_EN]. |
| */ |
| smu_tx_ctl.u64 = |
| csr_rd_node(node, CVMX_BGXX_SMUX_TX_CTL(index, xi.interface)); |
| smu_tx_ctl.s.dic_en = 1; |
| smu_tx_ctl.s.uni_en = 0; |
| csr_wr_node(node, CVMX_BGXX_SMUX_TX_CTL(index, xi.interface), |
| smu_tx_ctl.u64); |
| |
| { |
| /* Calculate the number of s-clk cycles per usec. */ |
| const u64 clock_mhz = 1200; /* todo: fixme */ |
| cvmx_bgxx_spu_dbg_control_t dbg_control; |
| |
| dbg_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPU_DBG_CONTROL(xi.interface)); |
| dbg_control.s.us_clk_period = clock_mhz - 1; |
| csr_wr_node(node, CVMX_BGXX_SPU_DBG_CONTROL(xi.interface), |
| dbg_control.u64); |
| } |
| /* The PHY often takes at least 100ms to stabilize */ |
| __cvmx_helper_bgx_interface_enable_delay(mode); |
| return 0; |
| } |
| |
| static void __cvmx_bgx_start_training(int node, int unit, int index) |
| { |
| cvmx_bgxx_spux_int_t spu_int; |
| cvmx_bgxx_spux_br_pmd_control_t pmd_control; |
| cvmx_bgxx_spux_an_control_t an_control; |
| |
| /* Clear the training interrupts (W1C) */ |
| spu_int.u64 = 0; |
| spu_int.s.training_failure = 1; |
| spu_int.s.training_done = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_INT(index, unit), spu_int.u64); |
| |
| /* These registers aren't cleared when training is restarted. Manually |
| * clear them as per Errata BGX-20968. |
| */ |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LP_CUP(index, unit), 0); |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LD_CUP(index, unit), 0); |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LD_REP(index, unit), 0); |
| |
| /* Disable autonegotiation */ |
| an_control.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_AN_CONTROL(index, unit)); |
| an_control.s.an_en = 0; |
| csr_wr_node(node, CVMX_BGXX_SPUX_AN_CONTROL(index, unit), |
| an_control.u64); |
| udelay(1); |
| |
| /* Restart training */ |
| pmd_control.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit)); |
| pmd_control.s.train_en = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit), |
| pmd_control.u64); |
| |
| udelay(1); |
| pmd_control.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit)); |
| pmd_control.s.train_restart = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit), |
| pmd_control.u64); |
| } |
| |
| static void __cvmx_bgx_restart_training(int node, int unit, int index) |
| { |
| cvmx_bgxx_spux_int_t spu_int; |
| cvmx_bgxx_spux_br_pmd_control_t pmd_control; |
| |
| /* Clear the training interrupts (W1C) */ |
| spu_int.u64 = 0; |
| spu_int.s.training_failure = 1; |
| spu_int.s.training_done = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_INT(index, unit), spu_int.u64); |
| |
| udelay(1700); /* Wait 1.7 msec */ |
| |
| /* These registers aren't cleared when training is restarted. Manually |
| * clear them as per Errata BGX-20968. |
| */ |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LP_CUP(index, unit), 0); |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LD_CUP(index, unit), 0); |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_LD_REP(index, unit), 0); |
| |
| /* Restart training */ |
| pmd_control.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit)); |
| pmd_control.s.train_restart = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, unit), |
| pmd_control.u64); |
| } |
| |
| /* |
| * @INTERNAL |
| * Wrapper function to configure the BGX, does not enable. |
| * |
| * @param xipd_port IPD/PKO port to configure. |
| * @param phy_pres If set, enable disparity, only applies to RXAUI interface |
| * |
| * @return Zero on success, negative on failure. |
| */ |
| int __cvmx_helper_bgx_port_init(int xipd_port, int phy_pres) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); |
| int index = cvmx_helper_get_interface_index_num(xp.port); |
| cvmx_helper_interface_mode_t mode; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| |
| __cvmx_bgx_common_init_pknd(xiface, index); |
| |
| if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII || |
| mode == CVMX_HELPER_INTERFACE_MODE_RGMII) { |
| cvmx_bgxx_gmp_gmi_txx_thresh_t gmi_tx_thresh; |
| cvmx_bgxx_gmp_gmi_txx_append_t gmp_txx_append; |
| cvmx_bgxx_gmp_gmi_txx_sgmii_ctl_t gmp_sgmii_ctl; |
| |
| /* Set TX Threshold */ |
| gmi_tx_thresh.u64 = 0; |
| gmi_tx_thresh.s.cnt = 0x20; |
| csr_wr_node(xi.node, |
| CVMX_BGXX_GMP_GMI_TXX_THRESH(index, xi.interface), |
| gmi_tx_thresh.u64); |
| __cvmx_helper_bgx_sgmii_hardware_init_one_time(xiface, index); |
| gmp_txx_append.u64 = csr_rd_node( |
| xi.node, |
| CVMX_BGXX_GMP_GMI_TXX_APPEND(index, xi.interface)); |
| gmp_sgmii_ctl.u64 = csr_rd_node( |
| xi.node, |
| CVMX_BGXX_GMP_GMI_TXX_SGMII_CTL(index, xi.interface)); |
| gmp_sgmii_ctl.s.align = gmp_txx_append.s.preamble ? 0 : 1; |
| csr_wr_node(xi.node, |
| CVMX_BGXX_GMP_GMI_TXX_SGMII_CTL(index, |
| xi.interface), |
| gmp_sgmii_ctl.u64); |
| if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) { |
| /* Disable XCV interface when initialized */ |
| union cvmx_xcv_reset xcv_reset; |
| |
| if (debug) |
| debug("%s: Disabling RGMII XCV interface\n", |
| __func__); |
| xcv_reset.u64 = csr_rd(CVMX_XCV_RESET); |
| xcv_reset.s.enable = 0; |
| xcv_reset.s.tx_pkt_rst_n = 0; |
| xcv_reset.s.rx_pkt_rst_n = 0; |
| csr_wr(CVMX_XCV_RESET, xcv_reset.u64); |
| } |
| } else { |
| int res, cred; |
| cvmx_bgxx_smux_tx_thresh_t smu_tx_thresh; |
| |
| res = __cvmx_helper_bgx_xaui_init(index, xiface); |
| if (res == -1) { |
| #ifdef DEBUG_BGX |
| debug("Failed to enable XAUI for %d:BGX(%d,%d)\n", |
| xi.node, xi.interface, index); |
| #endif |
| return res; |
| } |
| /* See BVX_SMU_TX_THRESH register descriptin */ |
| cred = __cvmx_helper_bgx_fifo_size(xiface, index) >> 4; |
| smu_tx_thresh.u64 = 0; |
| smu_tx_thresh.s.cnt = cred - 10; |
| csr_wr_node(xi.node, |
| CVMX_BGXX_SMUX_TX_THRESH(index, xi.interface), |
| smu_tx_thresh.u64); |
| if (debug) |
| debug("%s: BGX%d:%d TX-thresh=%d\n", __func__, |
| xi.interface, index, |
| (unsigned int)smu_tx_thresh.s.cnt); |
| |
| /* Set disparity for RXAUI interface as described in the |
| * Marvell RXAUI Interface specification. |
| */ |
| if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI && phy_pres) { |
| cvmx_bgxx_spux_misc_control_t misc_control; |
| |
| misc_control.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_SPUX_MISC_CONTROL( |
| index, xi.interface)); |
| misc_control.s.intlv_rdisp = 1; |
| csr_wr_node(xi.node, |
| CVMX_BGXX_SPUX_MISC_CONTROL(index, |
| xi.interface), |
| misc_control.u64); |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * @INTERNAL |
| * Configure a port for internal and/or external loopback. Internal loopback |
| * causes packets sent by the port to be received by Octeon. External loopback |
| * causes packets received from the wire to sent out again. This is used by |
| * interfaces using the bgx mac. |
| * |
| * @param xipd_port IPD/PKO port to loopback. |
| * @param enable_internal |
| * Non zero if you want internal loopback |
| * @param enable_external |
| * Non zero if you want external loopback |
| * |
| * @return Zero on success, negative on failure. |
| */ |
| int __cvmx_helper_bgx_sgmii_configure_loopback(int xipd_port, |
| int enable_internal, |
| int enable_external) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); |
| int node = xi.node; |
| int index = cvmx_helper_get_interface_index_num(xp.port); |
| cvmx_bgxx_gmp_pcs_mrx_control_t gmp_mrx_control; |
| cvmx_bgxx_gmp_pcs_miscx_ctl_t gmp_misc_ctl; |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| return 0; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| if (cvmx_helper_bgx_is_rgmii(xi.interface, index)) { |
| cvmx_xcv_ctl_t xcv_ctl; |
| cvmx_helper_link_info_t link_info; |
| |
| xcv_ctl.u64 = csr_rd(CVMX_XCV_CTL); |
| xcv_ctl.s.lpbk_int = enable_internal; |
| xcv_ctl.s.lpbk_ext = enable_external; |
| csr_wr(CVMX_XCV_CTL, xcv_ctl.u64); |
| |
| /* Initialize link and speed */ |
| __cvmx_helper_bgx_sgmii_hardware_init_link(xiface, index); |
| link_info = __cvmx_helper_bgx_sgmii_link_get(xipd_port); |
| __cvmx_helper_bgx_sgmii_hardware_init_link_speed(xiface, index, |
| link_info); |
| __cvmx_helper_bgx_rgmii_speed(link_info); |
| } else { |
| gmp_mrx_control.u64 = csr_rd_node( |
| node, |
| CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface)); |
| gmp_mrx_control.s.loopbck1 = enable_internal; |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_PCS_MRX_CONTROL(index, xi.interface), |
| gmp_mrx_control.u64); |
| |
| gmp_misc_ctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface)); |
| gmp_misc_ctl.s.loopbck2 = enable_external; |
| csr_wr_node(node, |
| CVMX_BGXX_GMP_PCS_MISCX_CTL(index, xi.interface), |
| gmp_misc_ctl.u64); |
| __cvmx_helper_bgx_sgmii_hardware_init_link(xiface, index); |
| } |
| |
| return 0; |
| } |
| |
| static int __cvmx_helper_bgx_xaui_link_init(int index, int xiface) |
| { |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| int node = xi.node; |
| cvmx_bgxx_spux_status1_t spu_status1; |
| cvmx_bgxx_spux_status2_t spu_status2; |
| cvmx_bgxx_spux_br_status2_t br_status2; |
| cvmx_bgxx_spux_int_t spu_int; |
| cvmx_bgxx_spux_misc_control_t spu_misc_control; |
| cvmx_bgxx_spux_an_control_t spu_an_control; |
| cvmx_bgxx_spux_an_status_t spu_an_status; |
| cvmx_bgxx_spux_br_pmd_control_t pmd_control; |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_helper_interface_mode_t mode; |
| int use_training = 0; |
| int rgmii_first = 0; |
| int qlm = cvmx_qlm_lmac(xiface, index); |
| int use_ber = 0; |
| u64 err_blks; |
| u64 ber_cnt; |
| u64 error_debounce; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| rgmii_first = cvmx_helper_bgx_is_rgmii(xi.interface, index); |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| if (mode == CVMX_HELPER_INTERFACE_MODE_10G_KR || |
| mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4) |
| use_training = 1; |
| |
| if ((mode == CVMX_HELPER_INTERFACE_MODE_XFI || |
| mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || |
| mode == CVMX_HELPER_INTERFACE_MODE_10G_KR || |
| mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4)) |
| use_ber = 1; |
| |
| /* Disable packet reception, CMR as well as SPU block */ |
| cmr_config.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| cmr_config.s.data_pkt_tx_en = 0; |
| cmr_config.s.data_pkt_rx_en = 0; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), |
| cmr_config.u64); |
| spu_misc_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); |
| spu_misc_control.s.rx_packet_dis = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface), |
| spu_misc_control.u64); |
| |
| spu_an_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_AN_CONTROL(index, xi.interface)); |
| if (spu_an_control.s.an_en) { |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { |
| cvmx_bgxx_spux_int_t spu_int; |
| |
| spu_int.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_INT(index, xi.interface)); |
| if (!spu_int.s.an_link_good) { |
| static u64 restart_auto_neg[2][6][4] = { |
| [0 ... 1][0 ... 5] = { [0 ... 3] = 0 } |
| }; |
| u64 now = get_timer(0); |
| u64 next_restart = |
| restart_auto_neg[node][xi.interface] |
| [index] + |
| 2000; |
| |
| if (now >= next_restart) |
| return -1; |
| |
| restart_auto_neg[node][xi.interface][index] = |
| now; |
| |
| /* Clear the auto negotiation (W1C) */ |
| spu_int.u64 = 0; |
| spu_int.s.an_complete = 1; |
| spu_int.s.an_link_good = 1; |
| spu_int.s.an_page_rx = 1; |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_INT(index, |
| xi.interface), |
| spu_int.u64); |
| /* Restart auto negotiation */ |
| spu_an_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_AN_CONTROL( |
| index, xi.interface)); |
| spu_an_control.s.an_restart = 1; |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_AN_CONTROL( |
| index, xi.interface), |
| spu_an_control.u64); |
| return -1; |
| } |
| } else { |
| spu_an_status.u64 = csr_rd_node( |
| node, |
| CVMX_BGXX_SPUX_AN_STATUS(index, xi.interface)); |
| if (!spu_an_status.s.an_complete) { |
| static u64 restart_auto_neg[2][6][4] = { |
| [0 ... 1][0 ... 5] = { [0 ... 3] = 0 } |
| }; |
| u64 now = get_timer(0); |
| u64 next_restart = |
| restart_auto_neg[node][xi.interface] |
| [index] + |
| 2000; |
| if (now >= next_restart) { |
| #ifdef DEBUG_BGX |
| debug("WARNING: BGX%d:%d: Waiting for autoneg to complete\n", |
| xi.interface, index); |
| #endif |
| return -1; |
| } |
| |
| restart_auto_neg[node][xi.interface][index] = |
| now; |
| /* Restart auto negotiation */ |
| spu_an_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_AN_CONTROL( |
| index, xi.interface)); |
| spu_an_control.s.an_restart = 1; |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_AN_CONTROL( |
| index, xi.interface), |
| spu_an_control.u64); |
| return -1; |
| } |
| } |
| } |
| |
| if (use_training) { |
| spu_int.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_INT(index, xi.interface)); |
| pmd_control.u64 = csr_rd_node( |
| node, |
| CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, xi.interface)); |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && |
| pmd_control.s.train_en == 0) { |
| __cvmx_bgx_start_training(node, xi.interface, index); |
| return -1; |
| } |
| cvmx_qlm_gser_errata_27882(node, qlm, index); |
| spu_int.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_INT(index, xi.interface)); |
| |
| if (spu_int.s.training_failure && |
| !OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X)) { |
| __cvmx_bgx_restart_training(node, xi.interface, index); |
| return -1; |
| } |
| if (!spu_int.s.training_done) { |
| debug("Waiting for link training\n"); |
| return -1; |
| } |
| } |
| |
| /* (GSER-21957) GSER RX Equalization may make >= 5gbaud non-KR |
| * channel with DXAUI, RXAUI, XFI and XLAUI, we need to perform |
| * RX equalization when the link is receiving data the first time |
| */ |
| if (use_training == 0) { |
| int lane = index; |
| cvmx_bgxx_spux_control1_t control1; |
| |
| cmr_config.u64 = csr_rd_node( |
| node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| control1.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); |
| if (control1.s.loopbck) { |
| /* Skip RX equalization when in loopback */ |
| } else if (mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || |
| mode == CVMX_HELPER_INTERFACE_MODE_XAUI) { |
| lane = -1; |
| if (__cvmx_qlm_rx_equalization(node, qlm, lane)) { |
| #ifdef DEBUG_BGX |
| debug("%d:%d:%d: Waiting for RX Equalization on QLM%d\n", |
| node, xi.interface, index, qlm); |
| #endif |
| return -1; |
| } |
| /* If BGX2 uses both dlms, then configure other dlm also. */ |
| if (OCTEON_IS_MODEL(OCTEON_CN73XX) && |
| xi.interface == 2) { |
| if (__cvmx_qlm_rx_equalization(node, 6, lane)) { |
| #ifdef DEBUG_BGX |
| debug("%d:%d:%d: Waiting for RX Equalization on QLM%d\n", |
| node, xi.interface, index, qlm); |
| #endif |
| return -1; |
| } |
| } |
| /* RXAUI */ |
| } else if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI) { |
| lane = index * 2; |
| if (OCTEON_IS_MODEL(OCTEON_CN73XX) && index >= 2 && |
| xi.interface == 2) { |
| lane = 0; |
| } |
| if (rgmii_first) |
| lane--; |
| if (__cvmx_qlm_rx_equalization(node, qlm, lane) || |
| __cvmx_qlm_rx_equalization(node, qlm, lane + 1)) { |
| #ifdef DEBUG_BGX |
| debug("%d:%d:%d: Waiting for RX Equalization on QLM%d\n", |
| node, xi.interface, index, qlm); |
| #endif |
| return -1; |
| } |
| /* XFI */ |
| } else if (cmr_config.s.lmac_type != 5) { |
| if (rgmii_first) |
| lane--; |
| if (OCTEON_IS_MODEL(OCTEON_CN73XX) && index >= 2 && |
| xi.interface == 2) { |
| lane = index - 2; |
| } else if (OCTEON_IS_MODEL(OCTEON_CNF75XX) && |
| index >= 2) { |
| lane = index - 2; |
| } |
| if (__cvmx_qlm_rx_equalization(node, qlm, lane)) { |
| #ifdef DEBUG_BGX |
| debug("%d:%d:%d: Waiting for RX Equalization on QLM%d\n", |
| node, xi.interface, index, qlm); |
| #endif |
| return -1; |
| } |
| } |
| } |
| |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), |
| cvmx_bgxx_spux_control1_t, reset, ==, 0, 10000)) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: PCS in reset", node, xi.interface, |
| index); |
| #endif |
| return -1; |
| } |
| |
| if (use_ber) { |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, |
| CVMX_BGXX_SPUX_BR_STATUS1(index, xi.interface), |
| cvmx_bgxx_spux_br_status1_t, blk_lock, ==, 1, |
| 10000)) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: BASE-R PCS block not locked\n", |
| node, xi.interface, index); |
| |
| if (mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || |
| mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4) { |
| cvmx_bgxx_spux_br_algn_status_t bstatus; |
| |
| bstatus.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_BR_ALGN_STATUS( |
| index, xi.interface)); |
| debug("ERROR: %d:BGX%d:%d: LANE BLOCK_LOCK:%x LANE MARKER_LOCK:%x\n", |
| node, xi.interface, index, |
| bstatus.s.block_lock, |
| bstatus.s.marker_lock); |
| } |
| #endif |
| return -1; |
| } |
| } else { |
| /* (5) Check to make sure that the link appears up and stable. |
| */ |
| /* Wait for PCS to be aligned */ |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_SPUX_BX_STATUS(index, xi.interface), |
| cvmx_bgxx_spux_bx_status_t, alignd, ==, 1, 10000)) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: PCS not aligned\n", node, |
| xi.interface, index); |
| #endif |
| return -1; |
| } |
| } |
| |
| if (use_ber) { |
| /* Set the BGXX_SPUX_BR_STATUS2.latched_lock bit (latching low). |
| * This will be checked prior to enabling packet tx and rx, |
| * ensuring block lock is sustained throughout the BGX link-up |
| * procedure |
| */ |
| br_status2.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); |
| br_status2.s.latched_lock = 1; |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface), |
| br_status2.u64); |
| } |
| |
| /* Clear rcvflt bit (latching high) and read it back */ |
| spu_status2.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_STATUS2(index, xi.interface)); |
| spu_status2.s.rcvflt = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_STATUS2(index, xi.interface), |
| spu_status2.u64); |
| |
| spu_status2.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_STATUS2(index, xi.interface)); |
| if (spu_status2.s.rcvflt) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: Receive fault, need to retry\n", |
| node, xi.interface, index); |
| #endif |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && use_training) |
| __cvmx_bgx_restart_training(node, xi.interface, index); |
| /* debug("training restarting\n"); */ |
| return -1; |
| } |
| |
| /* Wait for MAC RX to be ready */ |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_SMUX_RX_CTL(index, xi.interface), |
| cvmx_bgxx_smux_rx_ctl_t, status, ==, 0, 10000)) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: RX not ready\n", node, xi.interface, |
| index); |
| #endif |
| return -1; |
| } |
| |
| /* Wait for BGX RX to be idle */ |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_SMUX_CTRL(index, xi.interface), |
| cvmx_bgxx_smux_ctrl_t, rx_idle, ==, 1, 10000)) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: RX not idle\n", node, xi.interface, |
| index); |
| #endif |
| return -1; |
| } |
| |
| /* Wait for GMX TX to be idle */ |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_SMUX_CTRL(index, xi.interface), |
| cvmx_bgxx_smux_ctrl_t, tx_idle, ==, 1, 10000)) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: TX not idle\n", node, xi.interface, |
| index); |
| #endif |
| return -1; |
| } |
| |
| /* rcvflt should still be 0 */ |
| spu_status2.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_STATUS2(index, xi.interface)); |
| if (spu_status2.s.rcvflt) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: Receive fault, need to retry\n", |
| node, xi.interface, index); |
| #endif |
| return -1; |
| } |
| |
| /* Receive link is latching low. Force it high and verify it */ |
| spu_status1.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface)); |
| spu_status1.s.rcv_lnk = 1; |
| csr_wr_node(node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface), |
| spu_status1.u64); |
| |
| if (CVMX_WAIT_FOR_FIELD64_NODE( |
| node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface), |
| cvmx_bgxx_spux_status1_t, rcv_lnk, ==, 1, 10000)) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: Receive link down\n", node, |
| xi.interface, index); |
| #endif |
| return -1; |
| } |
| |
| if (use_ber) { |
| /* Clearing BER_CNT and ERR_BLKs */ |
| br_status2.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); |
| |
| /* If set, clear the LATCHED_BER by writing it to a one. */ |
| if (br_status2.s.latched_ber) |
| csr_wr_node(node, |
| CVMX_BGXX_SPUX_BR_STATUS2(index, |
| xi.interface), |
| br_status2.u64); |
| |
| error_debounce = get_timer(0); |
| |
| /* Clear error counts */ |
| err_blks = 0; |
| ber_cnt = 0; |
| |
| /* Verify that the link is up and error free for 100ms */ |
| while (get_timer(error_debounce) < 100) { |
| spu_status1.u64 = csr_rd_node( |
| node, |
| CVMX_BGXX_SPUX_STATUS1(index, xi.interface)); |
| /* Checking that Receive link is still up (rcv_lnk = 1 (up)) */ |
| if (!spu_status1.s.rcv_lnk) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: Receive link down\n", |
| node, xi.interface, index); |
| #endif |
| return -1; |
| } |
| |
| /* Checking if latched_ber = 1 (BER >= 10e^4) */ |
| br_status2.u64 = csr_rd_node( |
| node, |
| CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); |
| err_blks += br_status2.s.err_blks; |
| ber_cnt += br_status2.s.ber_cnt; |
| |
| if (br_status2.s.latched_ber) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: BER test failed, BER >= 10e^4, need to retry\n", |
| node, xi.interface, index); |
| #endif |
| return -1; |
| } |
| /* Checking that latched BLOCK_LOCK is still set (Block Lock never lost) */ |
| if (!br_status2.s.latched_lock) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: BASE-R PCS block lock lost, need to retry\n", |
| node, xi.interface, index); |
| #endif |
| return -1; |
| } |
| |
| /* Check error counters. Must be 0 (this error rate# |
| * is much higher than 1E-12) |
| */ |
| if (err_blks > 0) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: BASE-R errored-blocks (%llu) detected, need to retry\n", |
| node, xi.interface, index, |
| (unsigned long long)err_blks); |
| #endif |
| return -1; |
| } |
| |
| if (ber_cnt > 0) { |
| #ifdef DEBUG_BGX |
| debug("ERROR: %d:BGX%d:%d: BASE-R bit-errors (%llu) detected, need to retry\n", |
| node, xi.interface, index, |
| (unsigned long long)ber_cnt); |
| #endif |
| return -1; |
| } |
| |
| udelay(1000); |
| } |
| |
| /* Clear out the BGX error counters/bits. These errors are |
| * expected as part of the BGX link up procedure |
| */ |
| /* BIP_ERR counters clear as part of this read */ |
| csr_rd_node(node, |
| CVMX_BGXX_SPUX_BR_BIP_ERR_CNT(index, xi.interface)); |
| /* BER_CNT and ERR_BLKs clear as part of this read */ |
| br_status2.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); |
| } |
| |
| /* (7) Enable packet transmit and receive */ |
| spu_misc_control.u64 = csr_rd_node( |
| node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); |
| spu_misc_control.s.rx_packet_dis = 0; |
| csr_wr_node(node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface), |
| spu_misc_control.u64); |
| |
| if (debug) |
| debug("%s: Enabling tx and rx data packets\n", __func__); |
| cmr_config.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| cmr_config.s.data_pkt_tx_en = 1; |
| cmr_config.s.data_pkt_rx_en = 1; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), |
| cmr_config.u64); |
| return 0; |
| } |
| |
| int __cvmx_helper_bgx_xaui_enable(int xiface) |
| { |
| int index; |
| cvmx_helper_interface_mode_t mode; |
| int num_ports = cvmx_helper_ports_on_interface(xiface); |
| |
| for (index = 0; index < num_ports; index++) { |
| int res; |
| int xipd_port = cvmx_helper_get_ipd_port(xiface, index); |
| int phy_pres; |
| struct cvmx_xiface xi = |
| cvmx_helper_xiface_to_node_interface(xiface); |
| static int count |
| [CVMX_MAX_NODES][CVMX_HELPER_MAX_IFACE] |
| [CVMX_HELPER_CFG_MAX_PORT_PER_IFACE] = { |
| [0 ... CVMX_MAX_NODES - |
| 1][0 ... CVMX_HELPER_MAX_IFACE - |
| 1] = { [0 ... CVMX_HELPER_CFG_MAX_PORT_PER_IFACE - |
| 1] = 0 } |
| }; |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| |
| /* Set disparity for RXAUI interface as described in the |
| * Marvell RXAUI Interface specification. |
| */ |
| if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI && |
| (cvmx_helper_get_port_phy_present(xiface, index))) |
| phy_pres = 1; |
| else |
| phy_pres = 0; |
| __cvmx_helper_bgx_port_init(xipd_port, phy_pres); |
| |
| retry_link: |
| res = __cvmx_helper_bgx_xaui_link_init(index, xiface); |
| /* RX Equalization or autonegotiation can take little longer |
| * retry the link maybe 5 times for now |
| */ |
| if (res == -1 && count[xi.node][xi.interface][index] < 5) { |
| count[xi.node][xi.interface][index]++; |
| #ifdef DEBUG_BGX |
| debug("%d:BGX(%d,%d): Failed to get link, retrying\n", |
| xi.node, xi.interface, index); |
| #endif |
| goto retry_link; |
| } |
| |
| if (res == -1) { |
| #ifdef DEBUG_BGX |
| debug("%d:BGX(%d,%d): Failed to get link\n", xi.node, |
| xi.interface, index); |
| #endif |
| continue; |
| } |
| } |
| return 0; |
| } |
| |
| cvmx_helper_link_info_t __cvmx_helper_bgx_xaui_link_get(int xipd_port) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); |
| int index = cvmx_helper_get_interface_index_num(xp.port); |
| cvmx_bgxx_spux_status1_t spu_status1; |
| cvmx_bgxx_smux_tx_ctl_t smu_tx_ctl; |
| cvmx_bgxx_smux_rx_ctl_t smu_rx_ctl; |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_helper_link_info_t result; |
| cvmx_helper_interface_mode_t mode; |
| cvmx_bgxx_spux_misc_control_t spu_misc_control; |
| cvmx_bgxx_spux_br_status2_t br_status2; |
| |
| result.u64 = 0; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) |
| return __cvmx_helper_bgx_sgmii_link_get(xipd_port); |
| |
| /* Reading current rx/tx link status */ |
| spu_status1.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface)); |
| smu_tx_ctl.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_SMUX_TX_CTL(index, xi.interface)); |
| smu_rx_ctl.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_SMUX_RX_CTL(index, xi.interface)); |
| /* Reading tx/rx packet enables */ |
| cmr_config.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| spu_misc_control.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); |
| |
| if (smu_tx_ctl.s.ls == 0 && smu_rx_ctl.s.status == 0 && |
| cmr_config.s.data_pkt_tx_en == 1 && |
| cmr_config.s.data_pkt_rx_en == 1 && |
| spu_misc_control.s.rx_packet_dis == 0 && |
| spu_status1.s.rcv_lnk) { |
| int lanes; |
| int qlm = cvmx_qlm_lmac(xiface, index); |
| u64 speed; |
| |
| result.s.link_up = 1; |
| result.s.full_duplex = 1; |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX)) |
| speed = cvmx_qlm_get_gbaud_mhz_node(xi.node, qlm); |
| else |
| speed = cvmx_qlm_get_gbaud_mhz(qlm); |
| |
| cmr_config.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| switch (cmr_config.s.lmac_type) { |
| default: |
| case 1: // XAUI |
| speed = (speed * 8 + 5) / 10; |
| lanes = 4; |
| break; |
| case 2: // RXAUI |
| speed = (speed * 8 + 5) / 10; |
| lanes = 2; |
| break; |
| case 3: // XFI |
| speed = (speed * 64 + 33) / 66; |
| lanes = 1; |
| break; |
| case 4: // XLAUI |
| /* Adjust the speed when XLAUI is configured at 6.250Gbps */ |
| if (speed == 6250) |
| speed = 6445; |
| speed = (speed * 64 + 33) / 66; |
| lanes = 4; |
| break; |
| } |
| |
| if (debug) |
| debug("%s: baud: %llu, lanes: %d\n", __func__, |
| (unsigned long long)speed, lanes); |
| speed *= lanes; |
| result.s.speed = speed; |
| } else { |
| int res; |
| u64 err_blks = 0; |
| u64 ber_cnt = 0; |
| |
| /* Check for err_blk and ber errors if 10G or 40G */ |
| if ((mode == CVMX_HELPER_INTERFACE_MODE_XFI || |
| mode == CVMX_HELPER_INTERFACE_MODE_XLAUI || |
| mode == CVMX_HELPER_INTERFACE_MODE_10G_KR || |
| mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4)) { |
| br_status2.u64 = csr_rd_node( |
| xi.node, |
| CVMX_BGXX_SPUX_BR_STATUS2(index, xi.interface)); |
| err_blks = br_status2.s.err_blks; |
| ber_cnt = br_status2.s.ber_cnt; |
| } |
| |
| /* Checking if the link is up and error-free but we are receiving remote-faults */ |
| if (smu_tx_ctl.s.ls != 1 && smu_rx_ctl.s.status != 1 && |
| cmr_config.s.data_pkt_tx_en == 1 && |
| cmr_config.s.data_pkt_rx_en == 1 && |
| spu_misc_control.s.rx_packet_dis == 0 && |
| err_blks == 0 && ber_cnt == 0 && |
| spu_status1.s.rcv_lnk) { |
| result.s.init_success = 1; |
| #ifdef DEBUG_BGX |
| debug("Receiving remote-fault ordered sets %d:BGX(%d,%d)\n", |
| xi.node, xi.interface, index); |
| #endif |
| |
| } else { |
| res = __cvmx_helper_bgx_xaui_link_init(index, xiface); |
| if (res == -1) { |
| #ifdef DEBUG_BGX |
| debug("Failed to get %d:BGX(%d,%d) link\n", |
| xi.node, xi.interface, index); |
| #endif |
| } else { |
| #ifdef DEBUG_BGX |
| debug("Link initialization successful %d:BGX(%d,%d)\n", |
| xi.node, xi.interface, index); |
| #endif |
| result.s.init_success = 1; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| int __cvmx_helper_bgx_xaui_link_set(int xipd_port, |
| cvmx_helper_link_info_t link_info) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); |
| int node = xi.node; |
| int index = cvmx_helper_get_interface_index_num(xp.port); |
| cvmx_bgxx_smux_tx_ctl_t smu_tx_ctl; |
| cvmx_bgxx_smux_rx_ctl_t smu_rx_ctl; |
| cvmx_bgxx_spux_status1_t spu_status1; |
| cvmx_helper_interface_mode_t mode; |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_bgxx_spux_misc_control_t spu_misc_control; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) |
| return __cvmx_helper_bgx_sgmii_link_set(xipd_port, link_info); |
| |
| /* Reading current rx/tx link status */ |
| smu_tx_ctl.u64 = |
| csr_rd_node(node, CVMX_BGXX_SMUX_TX_CTL(index, xi.interface)); |
| smu_rx_ctl.u64 = |
| csr_rd_node(node, CVMX_BGXX_SMUX_RX_CTL(index, xi.interface)); |
| spu_status1.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_STATUS1(index, xi.interface)); |
| /* Reading tx/rx packet enables */ |
| cmr_config.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| spu_misc_control.u64 = csr_rd_node( |
| xi.node, CVMX_BGXX_SPUX_MISC_CONTROL(index, xi.interface)); |
| |
| /* If the link shouldn't be up, then just return */ |
| if (!link_info.s.link_up) |
| return 0; |
| |
| /* Do nothing if both RX and TX are happy and packet |
| * transmission/reception is enabled |
| */ |
| if (smu_tx_ctl.s.ls == 0 && smu_rx_ctl.s.status == 0 && |
| cmr_config.s.data_pkt_tx_en == 1 && |
| cmr_config.s.data_pkt_rx_en == 1 && |
| spu_misc_control.s.rx_packet_dis == 0 && spu_status1.s.rcv_lnk) |
| return 0; |
| |
| /* Bring the link up */ |
| return __cvmx_helper_bgx_xaui_link_init(index, xiface); |
| } |
| |
| int __cvmx_helper_bgx_xaui_configure_loopback(int xipd_port, |
| int enable_internal, |
| int enable_external) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| struct cvmx_xport xp = cvmx_helper_ipd_port_to_xport(xipd_port); |
| int node = xi.node; |
| int index = cvmx_helper_get_interface_index_num(xp.port); |
| cvmx_bgxx_spux_control1_t spu_control1; |
| cvmx_bgxx_smux_ext_loopback_t smu_ext_loopback; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| /* INT_BEAT_GEN must be set for loopback if the QLMs are not clocked. |
| * Set it whenever we use internal loopback |
| */ |
| if (enable_internal) { |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| |
| cmr_config.u64 = csr_rd_node( |
| node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| cmr_config.s.int_beat_gen = 1; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), |
| cmr_config.u64); |
| } |
| /* Set the internal loop */ |
| spu_control1.u64 = |
| csr_rd_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface)); |
| spu_control1.s.loopbck = enable_internal; |
| csr_wr_node(node, CVMX_BGXX_SPUX_CONTROL1(index, xi.interface), |
| spu_control1.u64); |
| /* Set the external loop */ |
| smu_ext_loopback.u64 = csr_rd_node( |
| node, CVMX_BGXX_SMUX_EXT_LOOPBACK(index, xi.interface)); |
| smu_ext_loopback.s.en = enable_external; |
| csr_wr_node(node, CVMX_BGXX_SMUX_EXT_LOOPBACK(index, xi.interface), |
| smu_ext_loopback.u64); |
| |
| return __cvmx_helper_bgx_xaui_link_init(index, xiface); |
| } |
| |
| int __cvmx_helper_bgx_mixed_enable(int xiface) |
| { |
| int index; |
| int num_ports = cvmx_helper_ports_on_interface(xiface); |
| cvmx_helper_interface_mode_t mode; |
| |
| for (index = 0; index < num_ports; index++) { |
| int xipd_port, phy_pres = 0; |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| continue; |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| |
| xipd_port = cvmx_helper_get_ipd_port(xiface, index); |
| |
| if (mode == CVMX_HELPER_INTERFACE_MODE_RXAUI && |
| (cvmx_helper_get_port_phy_present(xiface, index))) |
| phy_pres = 1; |
| |
| if (__cvmx_helper_bgx_port_init(xipd_port, phy_pres)) |
| continue; |
| |
| /* For RGMII interface, initialize the link after PKO is setup */ |
| if (mode == CVMX_HELPER_INTERFACE_MODE_RGMII) |
| continue; |
| /* Call SGMII init code for lmac_type = 0|5 */ |
| else if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII) { |
| int do_link_set = 1; |
| |
| if (do_link_set) |
| __cvmx_helper_bgx_sgmii_link_set( |
| xipd_port, |
| __cvmx_helper_bgx_sgmii_link_get( |
| xipd_port)); |
| /* All other lmac type call XAUI init code */ |
| } else { |
| int res; |
| struct cvmx_xiface xi = |
| cvmx_helper_xiface_to_node_interface(xiface); |
| static int count |
| [CVMX_MAX_NODES][CVMX_HELPER_MAX_IFACE] |
| [CVMX_HELPER_CFG_MAX_PORT_PER_IFACE] = { |
| [0 ... CVMX_MAX_NODES - |
| 1][0 ... CVMX_HELPER_MAX_IFACE - |
| 1] = { [0 ... CVMX_HELPER_CFG_MAX_PORT_PER_IFACE - |
| 1] = 0 } |
| }; |
| |
| retry_link: |
| res = __cvmx_helper_bgx_xaui_link_init(index, xiface); |
| /* RX Equalization or autonegotiation can take little |
| * longer retry the link maybe 5 times for now |
| */ |
| if (res == -1 && |
| count[xi.node][xi.interface][index] < 5) { |
| count[xi.node][xi.interface][index]++; |
| goto retry_link; |
| } |
| |
| if (res == -1) { |
| #ifdef DEBUG_BGX |
| debug("Failed to get %d:BGX(%d,%d) link\n", |
| xi.node, xi.interface, index); |
| #endif |
| continue; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| cvmx_helper_link_info_t __cvmx_helper_bgx_mixed_link_get(int xipd_port) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| int index = cvmx_helper_get_interface_index_num(xipd_port); |
| cvmx_helper_interface_mode_t mode; |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII || |
| mode == CVMX_HELPER_INTERFACE_MODE_RGMII) |
| return __cvmx_helper_bgx_sgmii_link_get(xipd_port); |
| else |
| return __cvmx_helper_bgx_xaui_link_get(xipd_port); |
| } |
| |
| int __cvmx_helper_bgx_mixed_link_set(int xipd_port, |
| cvmx_helper_link_info_t link_info) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| int index = cvmx_helper_get_interface_index_num(xipd_port); |
| cvmx_helper_interface_mode_t mode; |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII || |
| mode == CVMX_HELPER_INTERFACE_MODE_RGMII) |
| return __cvmx_helper_bgx_sgmii_link_set(xipd_port, link_info); |
| else |
| return __cvmx_helper_bgx_xaui_link_set(xipd_port, link_info); |
| } |
| |
| int __cvmx_helper_bgx_mixed_configure_loopback(int xipd_port, |
| int enable_internal, |
| int enable_external) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| int index = cvmx_helper_get_interface_index_num(xipd_port); |
| cvmx_helper_interface_mode_t mode; |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| if (mode == CVMX_HELPER_INTERFACE_MODE_SGMII || |
| mode == CVMX_HELPER_INTERFACE_MODE_RGMII) |
| return __cvmx_helper_bgx_sgmii_configure_loopback( |
| xipd_port, enable_internal, enable_external); |
| else |
| return __cvmx_helper_bgx_xaui_configure_loopback( |
| xipd_port, enable_internal, enable_external); |
| } |
| |
| /** |
| * @INTERNAL |
| * Configure Priority-Based Flow Control (a.k.a. PFC/CBFC) |
| * on a specific BGX interface/port. |
| */ |
| void __cvmx_helper_bgx_xaui_config_pfc(unsigned int node, |
| unsigned int interface, |
| unsigned int index, bool pfc_enable) |
| { |
| int xiface = cvmx_helper_node_interface_to_xiface(node, interface); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| cvmx_bgxx_smux_cbfc_ctl_t cbfc_ctl; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| cbfc_ctl.u64 = |
| csr_rd_node(node, CVMX_BGXX_SMUX_CBFC_CTL(index, xi.interface)); |
| |
| /* Enable all PFC controls if requiested */ |
| cbfc_ctl.s.rx_en = pfc_enable; |
| cbfc_ctl.s.tx_en = pfc_enable; |
| if (debug) |
| debug("%s: CVMX_BGXX_SMUX_CBFC_CTL(%d,%d)=%#llx\n", __func__, |
| index, xi.interface, (unsigned long long)cbfc_ctl.u64); |
| csr_wr_node(node, CVMX_BGXX_SMUX_CBFC_CTL(index, xi.interface), |
| cbfc_ctl.u64); |
| } |
| |
| /** |
| * Function to control the generation of FCS, padding by the BGX |
| * |
| */ |
| void cvmx_helper_bgx_tx_options(unsigned int node, unsigned int interface, |
| unsigned int index, bool fcs_enable, |
| bool pad_enable) |
| { |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_bgxx_gmp_gmi_txx_append_t gmp_txx_append; |
| cvmx_bgxx_gmp_gmi_txx_min_pkt_t gmp_min_pkt; |
| cvmx_bgxx_smux_tx_min_pkt_t smu_min_pkt; |
| cvmx_bgxx_smux_tx_append_t smu_tx_append; |
| int xiface = cvmx_helper_node_interface_to_xiface(node, interface); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| return; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d, fcs: %s, pad: %s\n", __func__, |
| xi.node, xi.interface, index, |
| fcs_enable ? "true" : "false", |
| pad_enable ? "true" : "false"); |
| |
| cmr_config.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| |
| (void)cmr_config; /* In case we need LMAC_TYPE later */ |
| |
| /* Setting options for both BGX subsystems, regardless of LMAC type */ |
| |
| /* Set GMP (SGMII) Tx options */ |
| gmp_min_pkt.u64 = 0; |
| /* per HRM Sec 34.3.4.4 */ |
| gmp_min_pkt.s.min_size = 59; |
| csr_wr_node(node, CVMX_BGXX_GMP_GMI_TXX_MIN_PKT(index, xi.interface), |
| gmp_min_pkt.u64); |
| gmp_txx_append.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_GMI_TXX_APPEND(index, xi.interface)); |
| gmp_txx_append.s.fcs = fcs_enable; |
| gmp_txx_append.s.pad = pad_enable; |
| csr_wr_node(node, CVMX_BGXX_GMP_GMI_TXX_APPEND(index, xi.interface), |
| gmp_txx_append.u64); |
| |
| /* Set SMUX (XAUI/XFI) Tx options */ |
| /* HRM Sec 33.3.4.3 should read 64 */ |
| smu_min_pkt.u64 = 0; |
| smu_min_pkt.s.min_size = 0x40; |
| csr_wr_node(node, CVMX_BGXX_SMUX_TX_MIN_PKT(index, xi.interface), |
| smu_min_pkt.u64); |
| smu_tx_append.u64 = csr_rd_node( |
| node, CVMX_BGXX_SMUX_TX_APPEND(index, xi.interface)); |
| smu_tx_append.s.fcs_d = fcs_enable; /* Set data-packet FCS */ |
| smu_tx_append.s.pad = pad_enable; |
| csr_wr_node(node, CVMX_BGXX_SMUX_TX_APPEND(index, xi.interface), |
| smu_tx_append.u64); |
| } |
| |
| /** |
| * Set mac for the ipd_port |
| * |
| * @param xipd_port ipd_port to set the mac |
| * @param bcst If set, accept all broadcast packets |
| * @param mcst Multicast mode |
| * 0 = Force reject all multicast packets |
| * 1 = Force accept all multicast packets |
| * 2 = use the address filter CAM. |
| * @param mac mac address for the ipd_port, or 0 to disable MAC filtering |
| */ |
| void cvmx_helper_bgx_set_mac(int xipd_port, int bcst, int mcst, u64 mac) |
| { |
| int xiface = cvmx_helper_get_interface_num(xipd_port); |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| int node = xi.node; |
| int index; |
| cvmx_bgxx_cmr_rx_adrx_cam_t adr_cam; |
| cvmx_bgxx_cmrx_rx_adr_ctl_t adr_ctl; |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| int saved_state_tx, saved_state_rx; |
| |
| index = cvmx_helper_get_interface_index_num(xipd_port); |
| |
| if (!cvmx_helper_is_port_valid(xiface, index)) |
| return; |
| |
| if (debug) |
| debug("%s: interface %u:%d/%d\n", __func__, xi.node, |
| xi.interface, index); |
| |
| cmr_config.u64 = |
| csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface)); |
| saved_state_tx = cmr_config.s.data_pkt_tx_en; |
| saved_state_rx = cmr_config.s.data_pkt_rx_en; |
| cmr_config.s.data_pkt_tx_en = 0; |
| cmr_config.s.data_pkt_rx_en = 0; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), |
| cmr_config.u64); |
| |
| /* Set the mac */ |
| adr_cam.u64 = 0; |
| adr_cam.s.id = index; |
| |
| if (mac != 0ull) |
| adr_cam.s.en = 1; |
| adr_cam.s.adr = mac; |
| |
| csr_wr_node(node, CVMX_BGXX_CMR_RX_ADRX_CAM(index * 8, xi.interface), |
| adr_cam.u64); |
| |
| adr_ctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_CMRX_RX_ADR_CTL(index, xi.interface)); |
| if (mac != 0ull) |
| adr_ctl.s.cam_accept = |
| 1; /* Accept the packet on DMAC CAM address */ |
| else |
| adr_ctl.s.cam_accept = 0; /* No filtering, promiscuous */ |
| |
| adr_ctl.s.mcst_mode = mcst; /* Use the address filter CAM */ |
| adr_ctl.s.bcst_accept = bcst; /* Accept all broadcast packets */ |
| csr_wr_node(node, CVMX_BGXX_CMRX_RX_ADR_CTL(index, xi.interface), |
| adr_ctl.u64); |
| /* Set SMAC for PAUSE frames */ |
| csr_wr_node(node, CVMX_BGXX_GMP_GMI_SMACX(index, xi.interface), mac); |
| |
| /* Restore back the interface state */ |
| cmr_config.s.data_pkt_tx_en = saved_state_tx; |
| cmr_config.s.data_pkt_rx_en = saved_state_rx; |
| csr_wr_node(node, CVMX_BGXX_CMRX_CONFIG(index, xi.interface), |
| cmr_config.u64); |
| |
| /* Wait 100ms after bringing up the link to give the PHY some time */ |
| if (cmr_config.s.enable) { |
| cvmx_helper_interface_mode_t mode; |
| |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| __cvmx_helper_bgx_interface_enable_delay(mode); |
| } |
| } |
| |
| /** |
| * Disables the sending of flow control (pause) frames on the specified |
| * BGX port(s). |
| * |
| * @param xiface Which xiface |
| * @param port_mask Mask (4bits) of which ports on the interface to disable |
| * backpressure on. |
| * 1 => disable backpressure |
| * 0 => enable backpressure |
| * |
| * @return 0 on success |
| * -1 on error |
| * |
| * FIXME: Should change the API to handle a single port in every |
| * invocation, for consistency with other API calls. |
| */ |
| int cvmx_bgx_set_backpressure_override(int xiface, unsigned int port_mask) |
| { |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| cvmx_bgxx_cmr_rx_ovr_bp_t rx_ovr_bp; |
| int node = xi.node; |
| |
| if (xi.interface >= CVMX_HELPER_MAX_GMX) |
| return 0; |
| |
| if (debug) |
| debug("%s: interface %u:%d port_mask=%#x\n", __func__, xi.node, |
| xi.interface, port_mask); |
| |
| /* Check for valid arguments */ |
| rx_ovr_bp.u64 = 0; |
| rx_ovr_bp.s.en = port_mask; /* Per port Enable back pressure override */ |
| rx_ovr_bp.s.ign_fifo_bp = |
| port_mask; /* Ignore the RX FIFO full when computing BP */ |
| |
| csr_wr_node(node, CVMX_BGXX_CMR_RX_OVR_BP(xi.interface), rx_ovr_bp.u64); |
| return 0; |
| } |
| |
| int cvmx_bgx_set_flowctl_mode(int xipd_port, cvmx_qos_proto_t qos, |
| cvmx_qos_pkt_mode_t fc_mode) |
| { |
| int node, xiface, iface, index, mode; |
| struct cvmx_xiface xi; |
| const struct { |
| int bck; |
| int drp; |
| } fcmode[4] = { [CVMX_QOS_PKT_MODE_HWONLY] = { 1, 1 }, |
| [CVMX_QOS_PKT_MODE_SWONLY] = { 0, 0 }, |
| [CVMX_QOS_PKT_MODE_HWSW] = { 1, 0 }, |
| [CVMX_QOS_PKT_MODE_DROP] = { 0, 1 } }; |
| |
| xiface = cvmx_helper_get_interface_num(xipd_port); |
| xi = cvmx_helper_xiface_to_node_interface(xiface); |
| node = xi.node; |
| iface = xi.interface; |
| |
| if (xi.interface >= CVMX_HELPER_MAX_GMX) |
| return 0; |
| |
| index = cvmx_helper_get_interface_index_num(xipd_port); |
| mode = cvmx_helper_bgx_get_mode(xiface, index); |
| switch (mode) { |
| case CVMX_HELPER_INTERFACE_MODE_10G_KR: |
| case CVMX_HELPER_INTERFACE_MODE_40G_KR4: |
| case CVMX_HELPER_INTERFACE_MODE_XLAUI: |
| case CVMX_HELPER_INTERFACE_MODE_XFI: |
| case CVMX_HELPER_INTERFACE_MODE_RXAUI: |
| case CVMX_HELPER_INTERFACE_MODE_XAUI: { |
| cvmx_bgxx_smux_tx_ctl_t txctl; |
| cvmx_bgxx_smux_cbfc_ctl_t cbfc; |
| cvmx_bgxx_smux_rx_frm_ctl_t frmctl; |
| cvmx_bgxx_smux_hg2_control_t hg2ctl; |
| |
| txctl.u64 = |
| csr_rd_node(node, CVMX_BGXX_SMUX_TX_CTL(index, iface)); |
| cbfc.u64 = csr_rd_node(node, |
| CVMX_BGXX_SMUX_CBFC_CTL(index, iface)); |
| frmctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_SMUX_RX_FRM_CTL(index, iface)); |
| hg2ctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_SMUX_HG2_CONTROL(index, iface)); |
| switch (qos) { |
| case CVMX_QOS_PROTO_PAUSE: |
| cbfc.u64 = 0; |
| hg2ctl.u64 = 0; |
| frmctl.s.ctl_bck = fcmode[fc_mode].bck; |
| frmctl.s.ctl_drp = fcmode[fc_mode].drp; |
| frmctl.s.ctl_mcst = 1; |
| txctl.s.l2p_bp_conv = 1; |
| break; |
| case CVMX_QOS_PROTO_PFC: |
| hg2ctl.u64 = 0; |
| hg2ctl.s.logl_en = 0xff; |
| frmctl.s.ctl_bck = fcmode[fc_mode].bck; |
| frmctl.s.ctl_drp = fcmode[fc_mode].drp; |
| frmctl.s.ctl_mcst = 1; |
| cbfc.s.bck_en = fcmode[fc_mode].bck; |
| cbfc.s.drp_en = fcmode[fc_mode].drp; |
| cbfc.s.phys_en = 0; |
| cbfc.s.logl_en = 0xff; |
| cbfc.s.tx_en = 1; |
| cbfc.s.rx_en = 1; |
| break; |
| case CVMX_QOS_PROTO_NONE: |
| cbfc.u64 = 0; |
| hg2ctl.u64 = 0; |
| frmctl.s.ctl_bck = fcmode[CVMX_QOS_PKT_MODE_DROP].bck; |
| frmctl.s.ctl_drp = fcmode[CVMX_QOS_PKT_MODE_DROP].drp; |
| txctl.s.l2p_bp_conv = 0; |
| break; |
| default: |
| break; |
| } |
| csr_wr_node(node, CVMX_BGXX_SMUX_CBFC_CTL(index, iface), |
| cbfc.u64); |
| csr_wr_node(node, CVMX_BGXX_SMUX_RX_FRM_CTL(index, iface), |
| frmctl.u64); |
| csr_wr_node(node, CVMX_BGXX_SMUX_HG2_CONTROL(index, iface), |
| hg2ctl.u64); |
| csr_wr_node(node, CVMX_BGXX_SMUX_TX_CTL(index, iface), |
| txctl.u64); |
| break; |
| } |
| case CVMX_HELPER_INTERFACE_MODE_SGMII: |
| case CVMX_HELPER_INTERFACE_MODE_RGMII: { |
| cvmx_bgxx_gmp_gmi_rxx_frm_ctl_t gmi_frmctl; |
| |
| gmi_frmctl.u64 = csr_rd_node( |
| node, CVMX_BGXX_GMP_GMI_RXX_FRM_CTL(index, iface)); |
| switch (qos) { |
| case CVMX_QOS_PROTO_PAUSE: |
| gmi_frmctl.s.ctl_bck = fcmode[fc_mode].bck; |
| gmi_frmctl.s.ctl_drp = fcmode[fc_mode].drp; |
| gmi_frmctl.s.ctl_mcst = 1; |
| break; |
| case CVMX_QOS_PROTO_NONE: |
| gmi_frmctl.s.ctl_bck = |
| fcmode[CVMX_QOS_PKT_MODE_DROP].bck; |
| gmi_frmctl.s.ctl_drp = |
| fcmode[CVMX_QOS_PKT_MODE_DROP].drp; |
| break; |
| default: |
| break; |
| } |
| csr_wr_node(node, CVMX_BGXX_GMP_GMI_RXX_FRM_CTL(index, iface), |
| gmi_frmctl.u64); |
| } |
| } /*switch*/ |
| |
| return 0; |
| } |