All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Roese <sr@denx.de>
To: u-boot@lists.denx.de
Cc: daniel.schwierzeck@gmail.com, awilliams@marvell.com, cchavva@marvell.com
Subject: [PATCH v2 10/52] mips: octeon: Add cvmx-helper-bgx.c
Date: Thu,  7 Apr 2022 09:11:12 +0200	[thread overview]
Message-ID: <20220407071154.51997-11-sr@denx.de> (raw)
In-Reply-To: <20220407071154.51997-1-sr@denx.de>

From: Aaron Williams <awilliams@marvell.com>

Import cvmx-helper-bgx.c from 2013 U-Boot. It will be used by the later
added drivers to support networking on the MIPS Octeon II / III
platforms.

Signed-off-by: Aaron Williams <awilliams@marvell.com>
Signed-off-by: Stefan Roese <sr@denx.de>
---
 arch/mips/mach-octeon/cvmx-helper-bgx.c | 2737 +++++++++++++++++++++++
 1 file changed, 2737 insertions(+)
 create mode 100644 arch/mips/mach-octeon/cvmx-helper-bgx.c

diff --git a/arch/mips/mach-octeon/cvmx-helper-bgx.c b/arch/mips/mach-octeon/cvmx-helper-bgx.c
new file mode 100644
index 000000000000..7d6e178a447a
--- /dev/null
+++ b/arch/mips/mach-octeon/cvmx-helper-bgx.c
@@ -0,0 +1,2737 @@
+// 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;
+}
-- 
2.35.1


  parent reply	other threads:[~2022-04-07  7:15 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-07  7:11 [PATCH v2 00/52] mips: octeon: Add ethernet support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 01/52] mips: octeon: Add misc cvmx-* header files Stefan Roese
2022-04-07  7:11 ` [PATCH v2 02/52] mips: octeon: Add cvmx-ilk-defs.h header file Stefan Roese
2022-04-07  7:11 ` [PATCH v2 03/52] mips: octeon: Add cvmx-iob-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 04/52] mips: octeon: Add cvmx-lbk-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 05/52] mips: octeon: Add cvmx-npei-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 06/52] mips: octeon: Add cvmx-pcsxx-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 07/52] mips: octeon: Add cvmx-xcv-defs.h " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 08/52] mips: octeon: Misc changes to existing headers for upcoming eth support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 09/52] mips: octeon: Add cvmx-helper-agl.c Stefan Roese
2022-04-07  7:11 ` Stefan Roese [this message]
2022-04-07  7:11 ` [PATCH v2 11/52] mips: octeon: Add cvmx-helper-board.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 12/52] mips: octeon: Add cvmx-helper-fpa.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 13/52] mips: octeon: Add cvmx-helper-ilk.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 14/52] mips: octeon: Add cvmx-helper-ipd.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 15/52] mips: octeon: Add cvmx-helper-loop.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 16/52] mips: octeon: Add cvmx-helper-npi.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 17/52] mips: octeon: Add cvmx-helper-pki.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 18/52] mips: octeon: Add cvmx-helper-pko.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 19/52] mips: octeon: Add cvmx-helper-pko3.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 20/52] mips: octeon: Add cvmx-helper-rgmii.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 21/52] mips: octeon: Add cvmx-helper-sgmii.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 23/52] mips: octeon: Add cvmx-helper-xaui.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 24/52] mips: octeon: Add cvmx-agl.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 26/52] mips: octeon: Add cvmx-fau-compat.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 27/52] mips: octeon: Add cvmx-fpa.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 30/52] mips: octeon: Add cvmx-ilk.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 33/52] mips: octeon: Add cvmx-pki-resources.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 34/52] mips: octeon: Add cvmx-pko.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 35/52] mips: octeon: Add cvmx-pko3.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 37/52] mips: octeon: Add cvmx-pko3-compat.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 38/52] mips: octeon: Add cvmx-pko3-resources.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 39/52] mips: octeon: Add cvmx-pko-internal-ports-range.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 40/52] mips: octeon: Add cvmx-qlm-tables.c Stefan Roese
2022-04-07  7:11 ` [PATCH v2 42/52] mips: octeon: Misc changes to existing C files for upcoming eth support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 43/52] mips: octeon: Makefile: Enable building of the newly added C files Stefan Roese
2022-04-07  7:11 ` [PATCH v2 44/52] mips: octeon: cpu.c: Move bootmem init to arch_early_init_r() Stefan Roese
2022-04-07  7:11 ` [PATCH v2 45/52] mips: octeon: cpu.c: Implement configure_lmtdma_window() Stefan Roese
2022-04-07  7:11 ` [PATCH v2 46/52] mips: octeon: octeon_common.h: Move init SP because of increased image size Stefan Roese
2022-04-07  7:11 ` [PATCH v2 47/52] mips: octeon: mrvl, cn73xx.dtsi: Add ethernet (BGX) and SMI DT nodes Stefan Roese
2022-04-07  7:11 ` [PATCH v2 48/52] mips: octeon: mrvl, octeon-ebb7304.dts: Add ethernet DT support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 49/52] mips: octeon: mrvl, octeon-nic23.dts: " Stefan Roese
2022-04-07  7:11 ` [PATCH v2 50/52] net: Add ethernet support for MIPS Octeon Stefan Roese
2022-04-07  7:11 ` [PATCH v2 51/52] mips: octeon: ebb7304: Enable ethernet support Stefan Roese
2022-04-07  7:11 ` [PATCH v2 52/52] mips: octeon: nic23: " Stefan Roese
2022-05-02 16:00 ` [PATCH v2 00/52] mips: octeon: Add " Stefan Roese
2022-05-02 18:54   ` Daniel Schwierzeck
2022-05-04  9:25 ` Stefan Roese

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220407071154.51997-11-sr@denx.de \
    --to=sr@denx.de \
    --cc=awilliams@marvell.com \
    --cc=cchavva@marvell.com \
    --cc=daniel.schwierzeck@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.