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 11/52] mips: octeon: Add cvmx-helper-board.c
Date: Thu,  7 Apr 2022 09:11:13 +0200	[thread overview]
Message-ID: <20220407071154.51997-12-sr@denx.de> (raw)
In-Reply-To: <20220407071154.51997-1-sr@denx.de>

From: Aaron Williams <awilliams@marvell.com>

Import cvmx-helper-board.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-board.c | 1824 +++++++++++++++++++++
 1 file changed, 1824 insertions(+)
 create mode 100644 arch/mips/mach-octeon/cvmx-helper-board.c

diff --git a/arch/mips/mach-octeon/cvmx-helper-board.c b/arch/mips/mach-octeon/cvmx-helper-board.c
new file mode 100644
index 000000000000..6dcc4e557e12
--- /dev/null
+++ b/arch/mips/mach-octeon/cvmx-helper-board.c
@@ -0,0 +1,1824 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2022 Marvell International Ltd.
+ *
+ * Helper functions to abstract board specific data about
+ * network ports from the rest of the cvmx-helper files.
+ */
+
+#include <i2c.h>
+#include <log.h>
+#include <malloc.h>
+#include <net.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/octeon_fdt.h>
+#include <mach/cvmx-helper.h>
+#include <mach/cvmx-helper-board.h>
+#include <mach/cvmx-helper-cfg.h>
+#include <mach/cvmx-helper-fdt.h>
+#include <mach/cvmx-helper-gpio.h>
+
+#include <mach/cvmx-smix-defs.h>
+#include <mach/cvmx-mdio.h>
+#include <mach/cvmx-qlm.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static bool sfp_parsed;
+
+static int __cvmx_helper_78xx_parse_phy(struct cvmx_phy_info *phy_info,
+					int ipd_port);
+static int __get_phy_info_from_dt(cvmx_phy_info_t *phy_info, int ipd_port);
+
+/**
+ * Writes to a Microsemi VSC7224 16-bit register
+ *
+ * @param[in]	i2c_bus	i2c bus data structure (must be enabled)
+ * @param	addr	Address of VSC7224 on the i2c bus
+ * @param	reg	8-bit register number to write to
+ * @param	val	16-bit value to write
+ *
+ * @return	0 for success
+ */
+static int cvmx_write_vsc7224_reg(const struct cvmx_fdt_i2c_bus_info *i2c_bus,
+				  u8 addr, u8 reg, u16 val)
+{
+	struct udevice *dev;
+	u8 buffer[2];
+	int ret;
+
+	ret = i2c_get_chip(i2c_bus->i2c_bus, addr, 1, &dev);
+	if (ret) {
+		debug("Cannot find I2C device: %d\n", ret);
+		return -1;
+	}
+
+	ret = dm_i2c_write(dev, reg, buffer, 2);
+	if (ret) {
+		debug("Cannot write I2C device: %d\n", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Writes to a Microsemi VSC7224 16-bit register
+ *
+ * @param[in]	i2c_bus	i2c bus data structure (must be enabled)
+ * @param	addr	Address of VSC7224 on the i2c bus
+ * @param	reg	8-bit register number to write to
+ *
+ * @return	16-bit value or error if < 0
+ */
+static int cvmx_read_vsc7224_reg(const struct cvmx_fdt_i2c_bus_info *i2c_bus,
+				 u8 addr, u8 reg)
+{
+	struct udevice *dev;
+	u8 buffer[2];
+	int ret;
+
+	ret = i2c_get_chip(i2c_bus->i2c_bus, addr, 1, &dev);
+	if (ret) {
+		debug("Cannot find I2C device: %d\n", ret);
+		return -1;
+	}
+
+	ret = dm_i2c_read(dev, reg, buffer, 2);
+	if (ret) {
+		debug("Cannot read I2C device: %d\n", ret);
+		return -1;
+	}
+
+	return (buffer[0] << 8) | buffer[1];
+}
+
+/**
+ * Function called whenever mod_abs/mod_prs has changed for Microsemi VSC7224
+ *
+ * @param	sfp	pointer to SFP data structure
+ * @param	val	1 if absent, 0 if present, otherwise not set
+ * @param	data	user-defined data
+ *
+ * @return	0 for success, -1 on error
+ */
+int cvmx_sfp_vsc7224_mod_abs_changed(struct cvmx_fdt_sfp_info *sfp, int val,
+				     void *data)
+{
+	int err;
+	struct cvmx_sfp_mod_info *mod_info;
+	int length;
+	struct cvmx_vsc7224 *vsc7224;
+	struct cvmx_vsc7224_chan *vsc7224_chan;
+	struct cvmx_vsc7224_tap *taps, *match = NULL;
+	int i;
+
+	debug("%s(%s, %d, %p): Module %s\n", __func__, sfp->name, val, data,
+	      val ? "absent" : "present");
+	if (val)
+		return 0;
+
+	/* We're here if we detect that the module is now present */
+	err = cvmx_sfp_read_i2c_eeprom(sfp);
+	if (err) {
+		debug("%s: Error reading the SFP module eeprom for %s\n",
+		      __func__, sfp->name);
+		return err;
+	}
+	mod_info = &sfp->sfp_info;
+
+	if (!mod_info->valid || !sfp->valid) {
+		debug("%s: Module data is invalid\n", __func__);
+		return -1;
+	}
+
+	vsc7224_chan = sfp->vsc7224_chan;
+	while (vsc7224_chan) {
+		/* We don't do any rx tuning */
+		if (!vsc7224_chan->is_tx) {
+			vsc7224_chan = vsc7224_chan->next;
+			continue;
+		}
+
+		/* Walk through all the channels */
+		taps = vsc7224_chan->taps;
+		if (mod_info->limiting)
+			length = 0;
+		else
+			length = mod_info->max_copper_cable_len;
+		debug("%s: limiting: %d, length: %d\n", __func__,
+		      mod_info->limiting, length);
+
+		/* Find a matching length in the taps table */
+		for (i = 0; i < vsc7224_chan->num_taps; i++) {
+			if (length >= taps->len)
+				match = taps;
+			taps++;
+		}
+		if (!match) {
+			debug("%s(%s, %d, %p): Error: no matching tap for length %d\n",
+			      __func__, sfp->name, val, data, length);
+			return -1;
+		}
+		debug("%s(%s): Applying %cx taps to vsc7224 %s:%d for cable length %d+\n",
+		      __func__, sfp->name, vsc7224_chan->is_tx ? 't' : 'r',
+		      vsc7224_chan->vsc7224->name, vsc7224_chan->lane,
+		      match->len);
+		/* Program the taps */
+		vsc7224 = vsc7224_chan->vsc7224;
+		cvmx_write_vsc7224_reg(vsc7224->i2c_bus, vsc7224->i2c_addr,
+				       0x7f, vsc7224_chan->lane);
+		if (!vsc7224_chan->maintap_disable)
+			cvmx_write_vsc7224_reg(vsc7224->i2c_bus,
+					       vsc7224->i2c_addr, 0x99,
+					       match->main_tap);
+		if (!vsc7224_chan->pretap_disable)
+			cvmx_write_vsc7224_reg(vsc7224->i2c_bus,
+					       vsc7224->i2c_addr, 0x9a,
+					       match->pre_tap);
+		if (!vsc7224_chan->posttap_disable)
+			cvmx_write_vsc7224_reg(vsc7224->i2c_bus,
+					       vsc7224->i2c_addr, 0x9b,
+					       match->post_tap);
+
+		/* Re-use val and disable taps if needed */
+		if (vsc7224_chan->maintap_disable ||
+		    vsc7224_chan->pretap_disable ||
+		    vsc7224_chan->posttap_disable) {
+			val = cvmx_read_vsc7224_reg(vsc7224->i2c_bus,
+						    vsc7224->i2c_addr, 0x97);
+			if (vsc7224_chan->maintap_disable)
+				val |= 0x800;
+			if (vsc7224_chan->pretap_disable)
+				val |= 0x1000;
+			if (vsc7224_chan->posttap_disable)
+				val |= 0x400;
+			cvmx_write_vsc7224_reg(vsc7224->i2c_bus,
+					       vsc7224->i2c_addr, 0x97, val);
+		}
+		vsc7224_chan = vsc7224_chan->next;
+	}
+
+	return err;
+}
+
+/**
+ * Update the mod_abs and error LED
+ *
+ * @param	ipd_port	ipd port number
+ * @param	link		link information
+ */
+static void __cvmx_helper_update_sfp(int ipd_port,
+				     struct cvmx_fdt_sfp_info *sfp_info,
+				     cvmx_helper_link_info_t link)
+{
+	debug("%s(%d): checking mod_abs\n", __func__, ipd_port);
+
+	cvmx_sfp_check_mod_abs(sfp_info, sfp_info->mod_abs_data);
+}
+
+static void cvmx_sfp_update_link(struct cvmx_fdt_sfp_info *sfp,
+				 cvmx_helper_link_info_t link)
+{
+	while (sfp) {
+		debug("%s(%s): checking mod_abs\n", __func__, sfp->name);
+		if (link.s.link_up && sfp->last_mod_abs)
+			cvmx_sfp_check_mod_abs(sfp, sfp->mod_abs_data);
+		sfp = sfp->next_iface_sfp;
+	}
+}
+
+/**
+ * @INTERNAL
+ * This function is used ethernet ports link speed. This functions uses the
+ * device tree information to determine the phy address and type of PHY.
+ * The only supproted PHYs are Marvell and Broadcom.
+ *
+ * @param ipd_port IPD input port associated with the port we want to get link
+ *                 status for.
+ *
+ * @return The ports link status. If the link isn't fully resolved, this must
+ *         return zero.
+ */
+cvmx_helper_link_info_t __cvmx_helper_board_link_get_from_dt(int ipd_port)
+{
+	cvmx_helper_link_info_t result;
+	cvmx_phy_info_t *phy_info = NULL;
+	cvmx_phy_info_t local_phy_info;
+	int xiface = 0, index = 0;
+	bool use_inband = false;
+	struct cvmx_fdt_sfp_info *sfp_info;
+	const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob);
+
+	result.u64 = 0;
+
+	if (ipd_port >= 0) {
+		int mode;
+
+		xiface = cvmx_helper_get_interface_num(ipd_port);
+		index = cvmx_helper_get_interface_index_num(ipd_port);
+		mode = cvmx_helper_interface_get_mode(xiface);
+		if (!cvmx_helper_get_port_autonegotiation(xiface, index)) {
+			result.s.link_up = 1;
+			result.s.full_duplex = 1;
+			switch (mode) {
+			case CVMX_HELPER_INTERFACE_MODE_RGMII:
+			case CVMX_HELPER_INTERFACE_MODE_GMII:
+			case CVMX_HELPER_INTERFACE_MODE_SGMII:
+			case CVMX_HELPER_INTERFACE_MODE_QSGMII:
+			case CVMX_HELPER_INTERFACE_MODE_AGL:
+			case CVMX_HELPER_INTERFACE_MODE_SPI:
+				if (OCTEON_IS_MODEL(OCTEON_CN70XX)) {
+					struct cvmx_xiface xi =
+						cvmx_helper_xiface_to_node_interface(
+							xiface);
+					u64 gbaud = cvmx_qlm_get_gbaud_mhz(0);
+
+					result.s.speed = gbaud * 8 / 10;
+					if (cvmx_qlm_get_dlm_mode(
+						    0, xi.interface) ==
+					    CVMX_QLM_MODE_SGMII)
+						result.s.speed >>= 1;
+					else
+						result.s.speed >>= 2;
+				} else {
+					result.s.speed = 1000;
+				}
+				break;
+			case CVMX_HELPER_INTERFACE_MODE_RXAUI:
+			case CVMX_HELPER_INTERFACE_MODE_XAUI:
+			case CVMX_HELPER_INTERFACE_MODE_10G_KR:
+			case CVMX_HELPER_INTERFACE_MODE_XFI:
+				result.s.speed = 10000;
+				break;
+			case CVMX_HELPER_INTERFACE_MODE_XLAUI:
+			case CVMX_HELPER_INTERFACE_MODE_40G_KR4:
+				result.s.speed = 40000;
+				break;
+			default:
+				break;
+			}
+
+			sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index);
+			/* Initialize the SFP info if it hasn't already been
+			 * done.
+			 */
+			if (!sfp_info && !sfp_parsed) {
+				cvmx_sfp_parse_device_tree(fdt_addr);
+				sfp_parsed = true;
+				cvmx_sfp_read_all_modules();
+				sfp_info = cvmx_helper_cfg_get_sfp_info(xiface,
+									index);
+			}
+			/* If the link is down or the link is up but we still
+			 * register the module as being absent, re-check
+			 * mod_abs.
+			 */
+			cvmx_sfp_update_link(sfp_info, result);
+
+			cvmx_helper_update_link_led(xiface, index, result);
+
+			return result;
+		}
+		phy_info = cvmx_helper_get_port_phy_info(xiface, index);
+		if (!phy_info) {
+			debug("%s: phy info not saved in config, allocating for 0x%x:%d\n",
+			      __func__, xiface, index);
+
+			phy_info = (cvmx_phy_info_t *)cvmx_bootmem_alloc(
+				sizeof(*phy_info), 0);
+			if (!phy_info) {
+				debug("%s: Out of memory\n", __func__);
+				return result;
+			}
+			memset(phy_info, 0, sizeof(*phy_info));
+			phy_info->phy_addr = -1;
+			debug("%s: Setting phy info for 0x%x:%d to %p\n",
+			      __func__, xiface, index, phy_info);
+			cvmx_helper_set_port_phy_info(xiface, index, phy_info);
+		}
+	} else {
+		/* For management ports we don't store the PHY information
+		 * so we use a local copy instead.
+		 */
+		phy_info = &local_phy_info;
+		memset(phy_info, 0, sizeof(*phy_info));
+		phy_info->phy_addr = -1;
+	}
+
+	if (phy_info->phy_addr == -1) {
+		if (octeon_has_feature(OCTEON_FEATURE_BGX)) {
+			if (__cvmx_helper_78xx_parse_phy(phy_info, ipd_port)) {
+				phy_info->phy_addr = -1;
+				use_inband = true;
+			}
+		} else if (__get_phy_info_from_dt(phy_info, ipd_port) < 0) {
+			phy_info->phy_addr = -1;
+			use_inband = true;
+		}
+	}
+
+	/* If we can't get the PHY info from the device tree then try
+	 * the inband state.
+	 */
+	if (use_inband) {
+		result.s.full_duplex = 1;
+		result.s.link_up = 1;
+		result.s.speed = 1000;
+		return result;
+	}
+
+	if (phy_info->phy_addr < 0)
+		return result;
+
+	if (phy_info->link_function)
+		result = phy_info->link_function(phy_info);
+	else
+		result = cvmx_helper_link_get(ipd_port);
+
+	sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index);
+	while (sfp_info) {
+		/* If the link is down or the link is up but we still register
+		 * the module as being absent, re-check mod_abs.
+		 */
+		if (!result.s.link_up ||
+		    (result.s.link_up && sfp_info->last_mod_abs))
+			__cvmx_helper_update_sfp(ipd_port, sfp_info, result);
+		sfp_info = sfp_info->next_iface_sfp;
+	}
+
+	return result;
+}
+
+cvmx_helper_link_info_t __cvmx_helper_board_link_get(int ipd_port)
+{
+	cvmx_helper_link_info_t result;
+
+	/* Unless we fix it later, all links are defaulted to down */
+	result.u64 = 0;
+
+	return __cvmx_helper_board_link_get_from_dt(ipd_port);
+}
+
+void cvmx_helper_update_link_led(int xiface, int index,
+				 cvmx_helper_link_info_t result)
+{
+}
+
+void cvmx_helper_leds_show_error(struct cvmx_phy_gpio_leds *leds, bool error)
+{
+}
+
+int __cvmx_helper_board_interface_probe(int interface, int supported_ports)
+{
+	return supported_ports;
+}
+
+/**
+ * Returns the Ethernet node offset in the device tree
+ *
+ * @param     fdt_addr - pointer to flat device tree in memory
+ * @param     aliases    - offset of alias in device tree
+ * @param     ipd_port - ipd port number to look up
+ *
+ * @returns   offset of Ethernet node if >= 0, error if -1
+ */
+int __pip_eth_node(const void *fdt_addr, int aliases, int ipd_port)
+{
+	char name_buffer[20];
+	const char *pip_path;
+	int pip, iface, eth;
+	int interface_num = cvmx_helper_get_interface_num(ipd_port);
+	int interface_index = cvmx_helper_get_interface_index_num(ipd_port);
+	cvmx_helper_interface_mode_t interface_mode =
+		cvmx_helper_interface_get_mode(interface_num);
+
+	/* The following are not found in the device tree */
+	switch (interface_mode) {
+	case CVMX_HELPER_INTERFACE_MODE_ILK:
+	case CVMX_HELPER_INTERFACE_MODE_LOOP:
+	case CVMX_HELPER_INTERFACE_MODE_SRIO:
+		debug("ERROR: No node expected for interface: %d, port: %d, mode: %s\n",
+		      interface_index, ipd_port,
+		      cvmx_helper_interface_mode_to_string(interface_mode));
+		return -1;
+	default:
+		break;
+	}
+	pip_path = (const char *)fdt_getprop(fdt_addr, aliases, "pip", NULL);
+	if (!pip_path) {
+		debug("ERROR: pip path not found in device tree\n");
+		return -1;
+	}
+	pip = fdt_path_offset(fdt_addr, pip_path);
+	debug("ipdd_port=%d pip_path=%s pip=%d ", ipd_port, pip_path, pip);
+	if (pip < 0) {
+		debug("ERROR: pip not found in device tree\n");
+		return -1;
+	}
+	snprintf(name_buffer, sizeof(name_buffer), "interface@%d",
+		 interface_num);
+	iface = fdt_subnode_offset(fdt_addr, pip, name_buffer);
+	debug("iface=%d ", iface);
+	if (iface < 0) {
+		debug("ERROR : pip intf %d not found in device tree\n",
+		      interface_num);
+		return -1;
+	}
+	snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x",
+		 interface_index);
+	eth = fdt_subnode_offset(fdt_addr, iface, name_buffer);
+	debug("eth=%d\n", eth);
+	if (eth < 0) {
+		debug("ERROR : pip interface@%d ethernet@%d not found in device tree\n",
+		      interface_num, interface_index);
+		return -1;
+	}
+	return eth;
+}
+
+int __mix_eth_node(const void *fdt_addr, int aliases, int interface_index)
+{
+	char name_buffer[20];
+	const char *mix_path;
+	int mix;
+
+	snprintf(name_buffer, sizeof(name_buffer), "mix%d", interface_index);
+	mix_path =
+		(const char *)fdt_getprop(fdt_addr, aliases, name_buffer, NULL);
+	if (!mix_path) {
+		debug("ERROR: mix%d path not found in device tree\n",
+		      interface_index);
+	}
+	mix = fdt_path_offset(fdt_addr, mix_path);
+	if (mix < 0) {
+		debug("ERROR: %s not found in device tree\n", mix_path);
+		return -1;
+	}
+	return mix;
+}
+
+static int __mdiobus_addr_to_unit(u32 addr)
+{
+	int unit = (addr >> 7) & 3;
+
+	if (!OCTEON_IS_MODEL(OCTEON_CN68XX) && !OCTEON_IS_MODEL(OCTEON_CN78XX))
+		unit >>= 1;
+	return unit;
+}
+
+/**
+ * Parse the muxed MDIO interface information from the device tree
+ *
+ * @param phy_info - pointer to phy info data structure to update
+ * @param mdio_offset - offset of MDIO bus
+ * @param mux_offset - offset of MUX, parent of mdio_offset
+ *
+ * @return 0 for success or -1
+ */
+static int __get_muxed_mdio_info_from_dt(cvmx_phy_info_t *phy_info,
+					 int mdio_offset, int mux_offset)
+{
+	const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob);
+	int phandle;
+	int smi_offset;
+	int gpio_offset;
+	u64 smi_addr = 0;
+	int len;
+	u32 *pgpio_handle;
+	int gpio_count = 0;
+	u32 *prop_val;
+	int offset;
+	const char *prop_name;
+
+	debug("%s(%p, 0x%x, 0x%x)\n", __func__, phy_info, mdio_offset,
+	      mux_offset);
+
+	/* Get register value to put onto the GPIO lines to select */
+	phy_info->gpio_value =
+		cvmx_fdt_get_int(fdt_addr, mdio_offset, "reg", -1);
+	if (phy_info->gpio_value < 0) {
+		debug("Could not get register value for muxed MDIO bus from DT\n");
+		return -1;
+	}
+
+	smi_offset = cvmx_fdt_lookup_phandle(fdt_addr, mux_offset,
+					     "mdio-parent-bus");
+	if (smi_offset < 0) {
+		debug("Invalid SMI offset for muxed MDIO interface in device tree\n");
+		return -1;
+	}
+	smi_addr = cvmx_fdt_get_uint64(fdt_addr, smi_offset, "reg", 0);
+
+	/* Convert SMI address to a MDIO interface */
+	switch (smi_addr) {
+	case 0x1180000001800:
+	case 0x1180000003800: /* 68XX address */
+		phy_info->mdio_unit = 0;
+		break;
+	case 0x1180000001900:
+	case 0x1180000003880:
+		phy_info->mdio_unit = 1;
+		break;
+	case 0x1180000003900:
+		phy_info->mdio_unit = 2;
+		break;
+	case 0x1180000003980:
+		phy_info->mdio_unit = 3;
+		break;
+	default:
+		phy_info->mdio_unit = 1;
+		break;
+	}
+	/* Find the GPIO MUX controller */
+	pgpio_handle =
+		(u32 *)fdt_getprop(fdt_addr, mux_offset, "gpios", &len);
+	if (!pgpio_handle || len < 12 || (len % 12) != 0 ||
+	    len > CVMX_PHY_MUX_MAX_GPIO * 12) {
+		debug("Invalid GPIO for muxed MDIO controller in DT\n");
+		return -1;
+	}
+
+	for (gpio_count = 0; gpio_count < len / 12; gpio_count++) {
+		phandle = fdt32_to_cpu(pgpio_handle[gpio_count * 3]);
+		phy_info->gpio[gpio_count] =
+			fdt32_to_cpu(pgpio_handle[gpio_count * 3 + 1]);
+		gpio_offset = fdt_node_offset_by_phandle(fdt_addr, phandle);
+		if (gpio_offset < 0) {
+			debug("Cannot access parent GPIO node in DT\n");
+			return -1;
+		}
+		if (!fdt_node_check_compatible(fdt_addr, gpio_offset,
+					       "cavium,octeon-3860-gpio")) {
+			phy_info->gpio_type[gpio_count] = GPIO_OCTEON;
+		} else if (!fdt_node_check_compatible(fdt_addr, gpio_offset,
+						      "nxp,pca8574")) {
+			/* GPIO is a TWSI GPIO unit which might sit behind
+			 * another mux.
+			 */
+			phy_info->gpio_type[gpio_count] = GPIO_PCA8574;
+			prop_val = (u32 *)fdt_getprop(
+				fdt_addr, gpio_offset, "reg", NULL);
+			if (!prop_val) {
+				debug("Could not find TWSI address of npx pca8574 GPIO from DT\n");
+				return -1;
+			}
+			/* Get the TWSI address of the GPIO unit */
+			phy_info->cvmx_gpio_twsi[gpio_count] =
+				fdt32_to_cpu(*prop_val);
+			/* Get the selector on the GPIO mux if present */
+			offset = fdt_parent_offset(fdt_addr, gpio_offset);
+			prop_val = (u32 *)fdt_getprop(fdt_addr, offset,
+							   "reg", NULL);
+			if (prop_val) {
+				phy_info->gpio_parent_mux_select =
+					fdt32_to_cpu(*prop_val);
+				/* Go up another level */
+				offset = fdt_parent_offset(fdt_addr, offset);
+				if (!fdt_node_check_compatible(fdt_addr, offset,
+							       "nxp,pca9548")) {
+					prop_val = (u32 *)fdt_getprop(
+						fdt_addr, offset, "reg", NULL);
+					if (!prop_val) {
+						debug("Could not read MUX TWSI address from DT\n");
+						return -1;
+					}
+					phy_info->gpio_parent_mux_twsi =
+						fdt32_to_cpu(*prop_val);
+				}
+			}
+		} else {
+			prop_name = (char *)fdt_getprop(fdt_addr, gpio_offset,
+							"compatible", NULL);
+			debug("Unknown GPIO type %s\n", prop_name);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/**
+ * @INTERNAL
+ * Converts a BGX address to the node, interface and port number
+ *
+ * @param bgx_addr	Address of CSR register
+ *
+ * @return node, interface and port number, will be -1 for invalid address.
+ */
+static struct cvmx_xiface __cvmx_bgx_reg_addr_to_xiface(u64 bgx_addr)
+{
+	struct cvmx_xiface xi = { -1, -1 };
+
+	xi.node = cvmx_csr_addr_to_node(bgx_addr);
+	bgx_addr = cvmx_csr_addr_strip_node(bgx_addr);
+	if ((bgx_addr & 0xFFFFFFFFF0000000) != 0x00011800E0000000) {
+		debug("%s: Invalid BGX address 0x%llx\n", __func__,
+		      (unsigned long long)bgx_addr);
+		xi.node = -1;
+		return xi;
+	}
+	xi.interface = (bgx_addr >> 24) & 0x0F;
+
+	return xi;
+}
+
+static cvmx_helper_link_info_t
+__get_marvell_phy_link_state(cvmx_phy_info_t *phy_info)
+{
+	cvmx_helper_link_info_t result;
+	int phy_status;
+	u32 phy_addr = phy_info->phy_addr;
+
+	result.u64 = 0;
+	/* Set to page 0 */
+	cvmx_mdio_write(phy_addr >> 8, phy_addr & 0xff, 22, 0);
+	/* All the speed information can be read from register 17 in one go. */
+	phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17);
+
+	/* If the resolve bit 11 isn't set, see if autoneg is turned off
+	 * (bit 12, reg 0). The resolve bit doesn't get set properly when
+	 * autoneg is off, so force it
+	 */
+	if ((phy_status & (1 << 11)) == 0) {
+		int auto_status =
+			cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0);
+		if ((auto_status & (1 << 12)) == 0)
+			phy_status |= 1 << 11;
+	}
+
+	/* Link is up = Speed/Duplex Resolved + RT-Link Up + G-Link Up. */
+	if ((phy_status & 0x0c08) == 0x0c08) {
+		result.s.link_up = 1;
+		result.s.full_duplex = ((phy_status >> 13) & 1);
+		switch ((phy_status >> 14) & 3) {
+		case 0: /* 10 Mbps */
+			result.s.speed = 10;
+			break;
+		case 1: /* 100 Mbps */
+			result.s.speed = 100;
+			break;
+		case 2: /* 1 Gbps */
+			result.s.speed = 1000;
+			break;
+		case 3: /* Illegal */
+			result.u64 = 0;
+			break;
+		}
+	}
+	return result;
+}
+
+/**
+ * @INTERNAL
+ * Get link state of broadcom PHY
+ *
+ * @param phy_info	PHY information
+ */
+static cvmx_helper_link_info_t
+__get_broadcom_phy_link_state(cvmx_phy_info_t *phy_info)
+{
+	cvmx_helper_link_info_t result;
+	u32 phy_addr = phy_info->phy_addr;
+	int phy_status;
+
+	result.u64 = 0;
+	/* Below we are going to read SMI/MDIO register 0x19 which works
+	 * on Broadcom parts
+	 */
+	phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x19);
+	switch ((phy_status >> 8) & 0x7) {
+	case 0:
+		result.u64 = 0;
+		break;
+	case 1:
+		result.s.link_up = 1;
+		result.s.full_duplex = 0;
+		result.s.speed = 10;
+		break;
+	case 2:
+		result.s.link_up = 1;
+		result.s.full_duplex = 1;
+		result.s.speed = 10;
+		break;
+	case 3:
+		result.s.link_up = 1;
+		result.s.full_duplex = 0;
+		result.s.speed = 100;
+		break;
+	case 4:
+		result.s.link_up = 1;
+		result.s.full_duplex = 1;
+		result.s.speed = 100;
+		break;
+	case 5:
+		result.s.link_up = 1;
+		result.s.full_duplex = 1;
+		result.s.speed = 100;
+		break;
+	case 6:
+		result.s.link_up = 1;
+		result.s.full_duplex = 0;
+		result.s.speed = 1000;
+		break;
+	case 7:
+		result.s.link_up = 1;
+		result.s.full_duplex = 1;
+		result.s.speed = 1000;
+		break;
+	}
+	return result;
+}
+
+/**
+ * @INTERNAL
+ * Get link state of generic gigabit PHY
+ *
+ * @param phy_info - information about the PHY
+ *
+ * @returns link status of the PHY
+ */
+static cvmx_helper_link_info_t
+__cvmx_get_generic_8023_c22_phy_link_state(cvmx_phy_info_t *phy_info)
+{
+	cvmx_helper_link_info_t result;
+	u32 phy_addr = phy_info->phy_addr;
+	int phy_basic_control;	 /* Register 0x0 */
+	int phy_basic_status;	 /* Register 0x1 */
+	int phy_anog_adv;	 /* Register 0x4 */
+	int phy_link_part_avail; /* Register 0x5 */
+	int phy_control;	 /* Register 0x9 */
+	int phy_status;		 /* Register 0xA */
+
+	result.u64 = 0;
+
+	phy_basic_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 1);
+	if (!(phy_basic_status & 0x4)) /* Check if link is up */
+		return result;	       /* Link is down, return link down */
+
+	result.s.link_up = 1;
+	phy_basic_control = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0);
+	/* Check if autonegotiation is enabled and completed */
+	if ((phy_basic_control & (1 << 12)) && (phy_basic_status & (1 << 5))) {
+		phy_status =
+			cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0xA);
+		phy_control =
+			cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x9);
+
+		phy_status &= phy_control << 2;
+		phy_link_part_avail =
+			cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x5);
+		phy_anog_adv =
+			cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0x4);
+		phy_link_part_avail &= phy_anog_adv;
+
+		if (phy_status & 0xC00) { /* Gigabit full or half */
+			result.s.speed = 1000;
+			result.s.full_duplex = !!(phy_status & 0x800);
+		} else if (phy_link_part_avail &
+			   0x0180) { /* 100 full or half */
+			result.s.speed = 100;
+			result.s.full_duplex = !!(phy_link_part_avail & 0x100);
+		} else if (phy_link_part_avail & 0x0060) {
+			result.s.speed = 10;
+			result.s.full_duplex = !!(phy_link_part_avail & 0x0040);
+		}
+	} else {
+		/* Not autonegotiated */
+		result.s.full_duplex = !!(phy_basic_control & (1 << 8));
+
+		if (phy_basic_control & (1 << 6))
+			result.s.speed = 1000;
+		else if (phy_basic_control & (1 << 13))
+			result.s.speed = 100;
+		else
+			result.s.speed = 10;
+	}
+	return result;
+}
+
+static cvmx_helper_link_info_t
+__cvmx_get_qualcomm_s17_phy_link_state(cvmx_phy_info_t *phy_info)
+{
+	cvmx_helper_link_info_t result;
+	u32 phy_addr = phy_info->phy_addr;
+	int phy_status;
+	int auto_status;
+
+	result.u64 = 0;
+
+	phy_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 17);
+
+	/* If bit 11 isn't set see if autonegotiation is turned off
+	 * (bit 12, reg 0).  The resolved bit doesn't get set properly when
+	 * autonegotiation is off, so force it.
+	 */
+	if ((phy_status & (1 << 11)) == 0) {
+		auto_status = cvmx_mdio_read(phy_addr >> 8, phy_addr & 0xff, 0);
+		if ((auto_status & (1 << 12)) == 0)
+			phy_status |= 1 << 11;
+	}
+	/* Only return a link if the PHY has finished autonegotiation and set
+	 * the resolved bit (bit 11).
+	 */
+	if (phy_status & (1 << 11)) {
+		result.s.link_up = 1;
+		result.s.full_duplex = !!(phy_status & (1 << 13));
+		switch ((phy_status >> 14) & 3) {
+		case 0: /* 10Mbps */
+			result.s.speed = 10;
+			break;
+		case 1: /* 100Mbps */
+			result.s.speed = 100;
+			break;
+		case 2: /* 1Gbps */
+			result.s.speed = 1000;
+			break;
+		default: /* Illegal */
+			result.u64 = 0;
+			break;
+		}
+	}
+	debug("   link: %s, duplex: %s, speed: %lu\n",
+	      result.s.link_up ? "up" : "down",
+	      result.s.full_duplex ? "full" : "half",
+	      (unsigned long)result.s.speed);
+	return result;
+}
+
+static cvmx_helper_link_info_t
+__get_generic_8023_c45_phy_link_state(cvmx_phy_info_t *phy_info)
+{
+	cvmx_helper_link_info_t result;
+	int phy_status;
+	int pma_ctrl1;
+	u32 phy_addr = phy_info->phy_addr;
+
+	result.u64 = 0;
+	pma_ctrl1 = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 1, 0);
+	if ((pma_ctrl1 & 0x207c) == 0x2040)
+		result.s.speed = 10000;
+	/* PMA Status 1 (1x0001) */
+	phy_status = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 1, 0xa);
+	if (phy_status < 0)
+		return result;
+
+	result.s.full_duplex = 1;
+	if ((phy_status & 1) == 0)
+		return result;
+	phy_status = cvmx_mdio_45_read(phy_addr >> 8, phy_addr & 0xff, 4, 0x18);
+	if (phy_status < 0)
+		return result;
+	result.s.link_up = (phy_status & 0x1000) ? 1 : 0;
+
+	return result;
+}
+
+static cvmx_helper_link_info_t
+__cvmx_get_cortina_phy_link_state(cvmx_phy_info_t *phy_info)
+{
+	cvmx_helper_link_info_t result;
+
+	result.s.link_up = 1;
+	result.s.full_duplex = 1;
+	result.s.speed = 1000;
+	return result;
+}
+
+static cvmx_helper_link_info_t
+__get_vitesse_vsc8490_phy_link_state(cvmx_phy_info_t *phy_info)
+{
+	cvmx_helper_link_info_t result;
+
+	result.s.link_up = 1;
+	result.s.full_duplex = 1;
+	result.s.speed = 1000;
+	return result;
+}
+
+static cvmx_helper_link_info_t
+__get_aquantia_phy_link_state(cvmx_phy_info_t *phy_info)
+{
+	cvmx_helper_link_info_t result;
+
+	result.s.link_up = 1;
+	result.s.full_duplex = 1;
+	result.s.speed = 1000;
+	return result;
+}
+
+static int __cvmx_helper_78xx_parse_phy(struct cvmx_phy_info *phy_info,
+					int ipd_port)
+{
+	const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob);
+	const char *compat;
+	int phy;
+	int parent;
+	u64 mdio_base;
+	int node, bus;
+	int phy_addr;
+	int index = cvmx_helper_get_interface_index_num(ipd_port);
+	int xiface = cvmx_helper_get_interface_num(ipd_port);
+	int compat_len = 0;
+
+	debug("%s(0x%p, %d) ENTER\n", __func__, phy_info, ipd_port);
+
+	phy = cvmx_helper_get_phy_fdt_node_offset(xiface, index);
+	debug("%s: xiface: 0x%x, index: %d, ipd_port: %d, phy fdt offset: %d\n",
+	      __func__, xiface, index, ipd_port, phy);
+	if (phy < 0) {
+		/* If this is the first time through we need to first parse the
+		 * device tree to get the node offsets.
+		 */
+		debug("No config present, calling __cvmx_helper_parse_bgx_dt\n");
+		if (__cvmx_helper_parse_bgx_dt(fdt_addr)) {
+			printf("Error: could not parse BGX device tree\n");
+			return -1;
+		}
+		if (__cvmx_fdt_parse_vsc7224(fdt_addr)) {
+			debug("Error: could not parse Microsemi VSC7224 in DT\n");
+			return -1;
+		}
+		if (octeon_has_feature(OCTEON_FEATURE_BGX_XCV) &&
+		    __cvmx_helper_parse_bgx_rgmii_dt(fdt_addr)) {
+			printf("Error: could not parse BGX XCV device tree\n");
+			return -1;
+		}
+		phy = cvmx_helper_get_phy_fdt_node_offset(xiface, index);
+		if (phy < 0) {
+			debug("%s: Could not get PHY node offset for IPD port 0x%x, xiface: 0x%x, index: %d\n",
+			      __func__, ipd_port, xiface, index);
+			return -1;
+		}
+		debug("%s: phy: %d (%s)\n", __func__, phy,
+		      fdt_get_name(fdt_addr, phy, NULL));
+	}
+
+	compat = (const char *)fdt_getprop(fdt_addr, phy, "compatible",
+					   &compat_len);
+	if (!compat) {
+		printf("ERROR: %d:%d:no compatible prop in phy\n", xiface,
+		       index);
+		return -1;
+	}
+
+	debug("  compatible: %s\n", compat);
+
+	phy_info->fdt_offset = phy;
+	phy_addr = cvmx_fdt_get_int(fdt_addr, phy, "reg", -1);
+	if (phy_addr == -1) {
+		printf("Error: %d:%d:could not get PHY address\n", xiface,
+		       index);
+		return -1;
+	}
+	debug("  PHY address: %d, compat: %s\n", phy_addr, compat);
+
+	if (!memcmp("marvell", compat, strlen("marvell"))) {
+		phy_info->phy_type = MARVELL_GENERIC_PHY;
+		phy_info->link_function = __get_marvell_phy_link_state;
+	} else if (!memcmp("broadcom", compat, strlen("broadcom"))) {
+		phy_info->phy_type = BROADCOM_GENERIC_PHY;
+		phy_info->link_function = __get_broadcom_phy_link_state;
+	} else if (!memcmp("cortina", compat, strlen("cortina"))) {
+		phy_info->phy_type = CORTINA_PHY;
+		phy_info->link_function = __cvmx_get_cortina_phy_link_state;
+	} else if (!strcmp("vitesse,vsc8490", compat)) {
+		phy_info->phy_type = VITESSE_VSC8490_PHY;
+		phy_info->link_function = __get_vitesse_vsc8490_phy_link_state;
+	} else if (fdt_stringlist_contains(compat, compat_len,
+					   "ethernet-phy-ieee802.3-c22")) {
+		phy_info->phy_type = GENERIC_8023_C22_PHY;
+		phy_info->link_function =
+			__cvmx_get_generic_8023_c22_phy_link_state;
+	} else if (fdt_stringlist_contains(compat, compat_len,
+					   "ethernet-phy-ieee802.3-c45")) {
+		phy_info->phy_type = GENERIC_8023_C22_PHY;
+		phy_info->link_function = __get_generic_8023_c45_phy_link_state;
+	}
+
+	phy_info->ipd_port = ipd_port;
+	phy_info->phy_sub_addr = 0;
+	phy_info->direct_connect = 1;
+
+	parent = fdt_parent_offset(fdt_addr, phy);
+	if (!fdt_node_check_compatible(fdt_addr, parent,
+				       "ethernet-phy-nexus")) {
+		debug("  nexus PHY found\n");
+		if (phy_info->phy_type == CORTINA_PHY) {
+			/* The Cortina CS422X uses the same PHY device for
+			 * multiple ports for XFI.  In this case we use a
+			 * nexus and each PHY address is the slice or
+			 * sub-address and the actual PHY address is the
+			 * nexus address.
+			 */
+			phy_info->phy_sub_addr = phy_addr;
+			phy_addr =
+				cvmx_fdt_get_int(fdt_addr, parent, "reg", -1);
+			debug("  Cortina PHY real address: 0x%x\n", phy_addr);
+		}
+		parent = fdt_parent_offset(fdt_addr, parent);
+	}
+
+	debug("  Parent: %s\n", fdt_get_name(fdt_addr, parent, NULL));
+	if (!fdt_node_check_compatible(fdt_addr, parent,
+				       "cavium,octeon-3860-mdio")) {
+		debug("  Found Octeon MDIO\n");
+		mdio_base = cvmx_fdt_get_uint64(fdt_addr, parent, "reg",
+						FDT_ADDR_T_NONE);
+		debug("  MDIO address: 0x%llx\n",
+		      (unsigned long long)mdio_base);
+
+		mdio_base = cvmx_fdt_translate_address(fdt_addr, parent,
+						       (u32 *)&mdio_base);
+		debug("  Translated: 0x%llx\n", (unsigned long long)mdio_base);
+		if (mdio_base == FDT_ADDR_T_NONE) {
+			printf("Could not get MDIO base address from reg field\n");
+			return -1;
+		}
+		__cvmx_mdio_addr_to_node_bus(mdio_base, &node, &bus);
+		if (bus < 0) {
+			printf("Invalid MDIO address 0x%llx, could not detect bus and node\n",
+			       (unsigned long long)mdio_base);
+			return -1;
+		}
+		debug("  MDIO node: %d, bus: %d\n", node, bus);
+		phy_info->mdio_unit = (node << 2) | (bus & 3);
+		phy_info->phy_addr = phy_addr | (phy_info->mdio_unit << 8);
+	} else {
+		printf("%s: Error: incompatible MDIO bus %s for IPD port %d\n",
+		       __func__,
+		       (const char *)fdt_get_name(fdt_addr, parent, NULL),
+		       ipd_port);
+		return -1;
+	}
+
+	debug("%s: EXIT 0\n", __func__);
+
+	return 0;
+}
+
+/**
+ * Return the MII PHY address associated with the given IPD
+ * port. The phy address is obtained from the device tree.
+ *
+ * @param[out] phy_info - PHY information data structure updated
+ * @param ipd_port Octeon IPD port to get the MII address for.
+ *
+ * @return MII PHY address and bus number, -1 on error, -2 if PHY info missing (OK).
+ */
+static int __get_phy_info_from_dt(cvmx_phy_info_t *phy_info, int ipd_port)
+{
+	const void *fdt_addr = CASTPTR(const void *, gd->fdt_blob);
+	int aliases, eth, phy, phy_parent, ret, i;
+	int mdio_parent;
+	const char *phy_compatible_str;
+	const char *host_mode_str = NULL;
+	int interface;
+	int phy_addr_offset = 0;
+
+	debug("%s(%p, %d)\n", __func__, phy_info, ipd_port);
+
+	if (octeon_has_feature(OCTEON_FEATURE_BGX))
+		return __cvmx_helper_78xx_parse_phy(phy_info, ipd_port);
+
+	phy_info->phy_addr = -1;
+	phy_info->phy_sub_addr = 0;
+	phy_info->ipd_port = ipd_port;
+	phy_info->direct_connect = -1;
+	phy_info->phy_type = (cvmx_phy_type_t)-1;
+	for (i = 0; i < CVMX_PHY_MUX_MAX_GPIO; i++)
+		phy_info->gpio[i] = -1;
+	phy_info->mdio_unit = -1;
+	phy_info->gpio_value = -1;
+	phy_info->gpio_parent_mux_twsi = -1;
+	phy_info->gpio_parent_mux_select = -1;
+	phy_info->link_function = NULL;
+	phy_info->fdt_offset = -1;
+	if (!fdt_addr) {
+		debug("No device tree found.\n");
+		return -1;
+	}
+
+	aliases = fdt_path_offset(fdt_addr, "/aliases");
+	if (aliases < 0) {
+		debug("Error: No /aliases node in device tree.\n");
+		return -1;
+	}
+	if (ipd_port < 0) {
+		int interface_index =
+			ipd_port - CVMX_HELPER_BOARD_MGMT_IPD_PORT;
+		eth = __mix_eth_node(fdt_addr, aliases, interface_index);
+	} else {
+		eth = __pip_eth_node(fdt_addr, aliases, ipd_port);
+	}
+	if (eth < 0) {
+		debug("ERROR : cannot find interface for ipd_port=%d\n",
+		      ipd_port);
+		return -1;
+	}
+
+	interface = cvmx_helper_get_interface_num(ipd_port);
+	/* Get handle to phy */
+	phy = cvmx_fdt_lookup_phandle(fdt_addr, eth, "phy-handle");
+	if (phy < 0) {
+		cvmx_helper_interface_mode_t if_mode;
+
+		/* Note that it's OK for RXAUI and ILK to not have a PHY
+		 * connected (i.e. EBB boards in loopback).
+		 */
+		debug("Cannot get phy-handle for ipd_port: %d\n", ipd_port);
+		if_mode = cvmx_helper_interface_get_mode(interface);
+		if (if_mode != CVMX_HELPER_INTERFACE_MODE_RXAUI &&
+		    if_mode != CVMX_HELPER_INTERFACE_MODE_ILK) {
+			debug("ERROR : phy handle not found in device tree ipd_port=%d\n",
+			      ipd_port);
+			return -1;
+		} else {
+			return -2;
+		}
+	}
+
+	phy_compatible_str =
+		(const char *)fdt_getprop(fdt_addr, phy, "compatible", NULL);
+	if (!phy_compatible_str) {
+		debug("ERROR: no compatible prop in phy\n");
+		return -1;
+	}
+	debug("Checking compatible string \"%s\" for ipd port %d\n",
+	      phy_compatible_str, ipd_port);
+	phy_info->fdt_offset = phy;
+	if (!memcmp("marvell", phy_compatible_str, strlen("marvell"))) {
+		debug("Marvell PHY detected for ipd_port %d\n", ipd_port);
+		phy_info->phy_type = MARVELL_GENERIC_PHY;
+		phy_info->link_function = __get_marvell_phy_link_state;
+	} else if (!memcmp("broadcom", phy_compatible_str,
+			   strlen("broadcom"))) {
+		phy_info->phy_type = BROADCOM_GENERIC_PHY;
+		phy_info->link_function = __get_broadcom_phy_link_state;
+		debug("Broadcom PHY detected for ipd_port %d\n", ipd_port);
+	} else if (!memcmp("vitesse", phy_compatible_str, strlen("vitesse"))) {
+		debug("Vitesse PHY detected for ipd_port %d\n", ipd_port);
+		if (!fdt_node_check_compatible(fdt_addr, phy,
+					       "vitesse,vsc8490")) {
+			phy_info->phy_type = VITESSE_VSC8490_PHY;
+			debug("Vitesse VSC8490 detected\n");
+			phy_info->link_function =
+				__get_vitesse_vsc8490_phy_link_state;
+		} else if (!fdt_node_check_compatible(
+				   fdt_addr, phy,
+				   "ethernet-phy-ieee802.3-c22")) {
+			phy_info->phy_type = GENERIC_8023_C22_PHY;
+			phy_info->link_function =
+				__cvmx_get_generic_8023_c22_phy_link_state;
+			debug("Vitesse 802.3 c22 detected\n");
+		} else {
+			phy_info->phy_type = GENERIC_8023_C45_PHY;
+			phy_info->link_function =
+				__get_generic_8023_c45_phy_link_state;
+			debug("Vitesse 802.3 c45 detected\n");
+		}
+	} else if (!memcmp("aquantia", phy_compatible_str,
+			   strlen("aquantia"))) {
+		phy_info->phy_type = AQUANTIA_PHY;
+		phy_info->link_function = __get_aquantia_phy_link_state;
+		debug("Aquantia c45 PHY detected\n");
+	} else if (!memcmp("cortina", phy_compatible_str, strlen("cortina"))) {
+		phy_info->phy_type = CORTINA_PHY;
+		phy_info->link_function = __cvmx_get_cortina_phy_link_state;
+		host_mode_str = (const char *)fdt_getprop(
+			fdt_addr, phy, "cortina,host-mode", NULL);
+		debug("Cortina PHY detected for ipd_port %d\n", ipd_port);
+	} else if (!memcmp("ti", phy_compatible_str, strlen("ti"))) {
+		phy_info->phy_type = GENERIC_8023_C45_PHY;
+		phy_info->link_function = __get_generic_8023_c45_phy_link_state;
+		debug("TI PHY detected for ipd_port %d\n", ipd_port);
+	} else if (!fdt_node_check_compatible(fdt_addr, phy,
+					      "atheros,ar8334") ||
+		   !fdt_node_check_compatible(fdt_addr, phy,
+					      "qualcomm,qca8334") ||
+		   !fdt_node_check_compatible(fdt_addr, phy,
+					      "atheros,ar8337") ||
+		   !fdt_node_check_compatible(fdt_addr, phy,
+					      "qualcomm,qca8337")) {
+		phy_info->phy_type = QUALCOMM_S17;
+		phy_info->link_function =
+			__cvmx_get_qualcomm_s17_phy_link_state;
+		debug("Qualcomm QCA833X switch detected\n");
+	} else if (!fdt_node_check_compatible(fdt_addr, phy,
+					      "ethernet-phy-ieee802.3-c22")) {
+		phy_info->phy_type = GENERIC_8023_C22_PHY;
+		phy_info->link_function =
+			__cvmx_get_generic_8023_c22_phy_link_state;
+		debug("Generic 802.3 c22 PHY detected\n");
+	} else if (!fdt_node_check_compatible(fdt_addr, phy,
+					      "ethernet-phy-ieee802.3-c45")) {
+		phy_info->phy_type = GENERIC_8023_C45_PHY;
+		phy_info->link_function = __get_generic_8023_c45_phy_link_state;
+		debug("Generic 802.3 c45 PHY detected\n");
+	} else {
+		debug("Unknown PHY compatibility\n");
+		phy_info->phy_type = (cvmx_phy_type_t)-1;
+		phy_info->link_function = NULL;
+	}
+
+	phy_info->host_mode = CVMX_PHY_HOST_MODE_UNKNOWN;
+	if (host_mode_str) {
+		if (strcmp(host_mode_str, "rxaui") == 0)
+			phy_info->host_mode = CVMX_PHY_HOST_MODE_RXAUI;
+		else if (strcmp(host_mode_str, "xaui") == 0)
+			phy_info->host_mode = CVMX_PHY_HOST_MODE_XAUI;
+		else if (strcmp(host_mode_str, "sgmii") == 0)
+			phy_info->host_mode = CVMX_PHY_HOST_MODE_SGMII;
+		else if (strcmp(host_mode_str, "qsgmii") == 0)
+			phy_info->host_mode = CVMX_PHY_HOST_MODE_QSGMII;
+		else
+			debug("Unknown PHY host mode\n");
+	}
+
+	/* Check if PHY parent is the octeon MDIO bus. Some boards are connected
+	 * though a MUX and for them direct_connect_to_phy will be 0
+	 */
+	phy_parent = fdt_parent_offset(fdt_addr, phy);
+	if (phy_parent < 0) {
+		debug("ERROR : cannot find phy parent for ipd_port=%d ret=%d\n",
+		      ipd_port, phy_parent);
+		return -1;
+	}
+	/* For multi-phy devices and devices on a MUX, go to the parent */
+	ret = fdt_node_check_compatible(fdt_addr, phy_parent,
+					"ethernet-phy-nexus");
+	if (ret == 0) {
+		/* It's a nexus so check the grandparent. */
+		phy_addr_offset =
+			cvmx_fdt_get_int(fdt_addr, phy_parent, "reg", 0);
+		phy_parent = fdt_parent_offset(fdt_addr, phy_parent);
+	}
+
+	/* Check for a muxed MDIO interface */
+	mdio_parent = fdt_parent_offset(fdt_addr, phy_parent);
+	ret = fdt_node_check_compatible(fdt_addr, mdio_parent,
+					"cavium,mdio-mux");
+	if (ret == 0) {
+		ret = __get_muxed_mdio_info_from_dt(phy_info, phy_parent,
+						    mdio_parent);
+		if (ret) {
+			printf("Error reading mdio mux information for ipd port %d\n",
+			       ipd_port);
+			return -1;
+		}
+	}
+	ret = fdt_node_check_compatible(fdt_addr, phy_parent,
+					"cavium,octeon-3860-mdio");
+	if (ret == 0) {
+		u32 *mdio_reg_base =
+			(u32 *)fdt_getprop(fdt_addr, phy_parent, "reg", 0);
+		phy_info->direct_connect = 1;
+		if (mdio_reg_base == 0) {
+			debug("ERROR : unable to get reg property in phy mdio\n");
+			return -1;
+		}
+		phy_info->mdio_unit =
+			__mdiobus_addr_to_unit(fdt32_to_cpu(mdio_reg_base[1]));
+		debug("phy parent=%s reg_base=%08x mdio_unit=%d\n",
+		      fdt_get_name(fdt_addr, phy_parent, NULL),
+		      (int)mdio_reg_base[1], phy_info->mdio_unit);
+	} else {
+		phy_info->direct_connect = 0;
+		/* The PHY is not directly connected to the Octeon MDIO bus.
+		 * SE doesn't  have abstractions for MDIO MUX or MDIO MUX
+		 * drivers and hence for the non direct cases code will be
+		 * needed which is board specific.
+		 * For now the MDIO Unit is defaulted to 1.
+		 */
+		debug("%s PHY at address: %d is not directly connected\n",
+		      __func__, phy_info->phy_addr);
+	}
+
+	phy_info->phy_addr = cvmx_fdt_get_int(fdt_addr, phy, "reg", -1);
+	if (phy_info->phy_addr < 0) {
+		debug("ERROR: Could not read phy address from reg in DT\n");
+		return -1;
+	}
+	phy_info->phy_addr += phy_addr_offset;
+	phy_info->phy_addr |= phy_info->mdio_unit << 8;
+	debug("%s(%p, %d) => 0x%x\n", __func__, phy_info, ipd_port,
+	      phy_info->phy_addr);
+	return phy_info->phy_addr;
+}
+
+/**
+ * @INTERNAL
+ * Parse the device tree and set whether a port is valid or not.
+ *
+ * @param fdt_addr	Pointer to device tree
+ *
+ * @return 0 for success, -1 on error.
+ */
+int __cvmx_helper_parse_bgx_dt(const void *fdt_addr)
+{
+	int port_index;
+	struct cvmx_xiface xi;
+	int fdt_port_node = -1;
+	int fdt_interface_node;
+	int fdt_phy_node;
+	u64 reg_addr;
+	int xiface;
+	struct cvmx_phy_info *phy_info;
+	static bool parsed;
+	int err;
+	int ipd_port;
+
+	if (parsed) {
+		debug("%s: Already parsed\n", __func__);
+		return 0;
+	}
+	while ((fdt_port_node = fdt_node_offset_by_compatible(
+			fdt_addr, fdt_port_node,
+			"cavium,octeon-7890-bgx-port")) >= 0) {
+		/* Get the port number */
+		port_index =
+			cvmx_fdt_get_int(fdt_addr, fdt_port_node, "reg", -1);
+		if (port_index < 0) {
+			debug("Error: missing reg field for bgx port in device tree\n");
+			return -1;
+		}
+		debug("%s: Parsing BGX port %d\n", __func__, port_index);
+		/* Get the interface number */
+		fdt_interface_node = fdt_parent_offset(fdt_addr, fdt_port_node);
+		if (fdt_interface_node < 0) {
+			debug("Error: device tree corrupt!\n");
+			return -1;
+		}
+		if (fdt_node_check_compatible(fdt_addr, fdt_interface_node,
+					      "cavium,octeon-7890-bgx")) {
+			debug("Error: incompatible Ethernet MAC Nexus in device tree!\n");
+			return -1;
+		}
+		reg_addr =
+			cvmx_fdt_get_addr(fdt_addr, fdt_interface_node, "reg");
+		debug("%s: BGX interface address: 0x%llx\n", __func__,
+		      (unsigned long long)reg_addr);
+		if (reg_addr == FDT_ADDR_T_NONE) {
+			debug("Device tree BGX node has invalid address 0x%llx\n",
+			      (unsigned long long)reg_addr);
+			return -1;
+		}
+		reg_addr = cvmx_fdt_translate_address(fdt_addr,
+						      fdt_interface_node,
+						      (u32 *)&reg_addr);
+		xi = __cvmx_bgx_reg_addr_to_xiface(reg_addr);
+		if (xi.node < 0) {
+			debug("Device tree BGX node has invalid address 0x%llx\n",
+			      (unsigned long long)reg_addr);
+			return -1;
+		}
+		debug("%s: Found BGX node %d, interface %d\n", __func__,
+		      xi.node, xi.interface);
+		xiface = cvmx_helper_node_interface_to_xiface(xi.node,
+							      xi.interface);
+		cvmx_helper_set_port_fdt_node_offset(xiface, port_index,
+						     fdt_port_node);
+		cvmx_helper_set_port_valid(xiface, port_index, true);
+
+		cvmx_helper_set_port_fdt_node_offset(xiface, port_index,
+						     fdt_port_node);
+		if (fdt_getprop(fdt_addr, fdt_port_node,
+				"cavium,sgmii-mac-phy-mode", NULL))
+			cvmx_helper_set_mac_phy_mode(xiface, port_index, true);
+		else
+			cvmx_helper_set_mac_phy_mode(xiface, port_index, false);
+
+		if (fdt_getprop(fdt_addr, fdt_port_node, "cavium,force-link-up",
+				NULL))
+			cvmx_helper_set_port_force_link_up(xiface, port_index,
+							   true);
+		else
+			cvmx_helper_set_port_force_link_up(xiface, port_index,
+							   false);
+
+		if (fdt_getprop(fdt_addr, fdt_port_node,
+				"cavium,sgmii-mac-1000x-mode", NULL))
+			cvmx_helper_set_1000x_mode(xiface, port_index, true);
+		else
+			cvmx_helper_set_1000x_mode(xiface, port_index, false);
+
+		if (fdt_getprop(fdt_addr, fdt_port_node,
+				"cavium,disable-autonegotiation", NULL))
+			cvmx_helper_set_port_autonegotiation(xiface, port_index,
+							     false);
+		else
+			cvmx_helper_set_port_autonegotiation(xiface, port_index,
+							     true);
+
+		fdt_phy_node = cvmx_fdt_lookup_phandle(fdt_addr, fdt_port_node,
+						       "phy-handle");
+		if (fdt_phy_node >= 0) {
+			cvmx_helper_set_phy_fdt_node_offset(xiface, port_index,
+							    fdt_phy_node);
+			debug("%s: Setting PHY fdt node offset for interface 0x%x, port %d to %d\n",
+			      __func__, xiface, port_index, fdt_phy_node);
+			debug("%s: PHY node name: %s\n", __func__,
+			      fdt_get_name(fdt_addr, fdt_phy_node, NULL));
+			cvmx_helper_set_port_phy_present(xiface, port_index,
+							 true);
+			ipd_port = cvmx_helper_get_ipd_port(xiface, port_index);
+			if (ipd_port >= 0) {
+				debug("%s: Allocating phy info for 0x%x:%d\n",
+				      __func__, xiface, port_index);
+				phy_info =
+					(cvmx_phy_info_t *)cvmx_bootmem_alloc(
+						sizeof(*phy_info), 0);
+				if (!phy_info) {
+					debug("%s: Out of memory\n", __func__);
+					return -1;
+				}
+				memset(phy_info, 0, sizeof(*phy_info));
+				phy_info->phy_addr = -1;
+				err = __get_phy_info_from_dt(phy_info,
+							     ipd_port);
+				if (err) {
+					debug("%s: Error parsing phy info for ipd port %d\n",
+					      __func__, ipd_port);
+					return -1;
+				}
+				cvmx_helper_set_port_phy_info(
+					xiface, port_index, phy_info);
+				debug("%s: Saved phy info\n", __func__);
+			}
+		} else {
+			cvmx_helper_set_phy_fdt_node_offset(xiface, port_index,
+							    -1);
+			debug("%s: No PHY fdt node offset for interface 0x%x, port %d to %d\n",
+			      __func__, xiface, port_index, fdt_phy_node);
+			cvmx_helper_set_port_phy_present(xiface, port_index,
+							 false);
+		}
+	}
+	if (!sfp_parsed)
+		if (cvmx_sfp_parse_device_tree(fdt_addr))
+			debug("%s: Error parsing SFP device tree\n", __func__);
+	parsed = true;
+	return 0;
+}
+
+int __cvmx_helper_parse_bgx_rgmii_dt(const void *fdt_addr)
+{
+	u64 reg_addr;
+	struct cvmx_xiface xi;
+	int fdt_port_node = -1;
+	int fdt_interface_node;
+	int fdt_phy_node;
+	int port_index;
+	int xiface;
+
+	/* There's only one xcv (RGMII) interface, so just search for the one
+	 * that's part of a BGX entry.
+	 */
+	while ((fdt_port_node = fdt_node_offset_by_compatible(
+			fdt_addr, fdt_port_node, "cavium,octeon-7360-xcv")) >=
+	       0) {
+		fdt_interface_node = fdt_parent_offset(fdt_addr, fdt_port_node);
+		if (fdt_interface_node < 0) {
+			printf("Error: device tree corrupt!\n");
+			return -1;
+		}
+		debug("%s: XCV parent node compatible: %s\n", __func__,
+		      (char *)fdt_getprop(fdt_addr, fdt_interface_node,
+					  "compatible", NULL));
+		if (!fdt_node_check_compatible(fdt_addr, fdt_interface_node,
+					       "cavium,octeon-7890-bgx"))
+			break;
+	}
+	if (fdt_port_node == -FDT_ERR_NOTFOUND) {
+		debug("No XCV/RGMII interface found in device tree\n");
+		return 0;
+	} else if (fdt_port_node < 0) {
+		debug("%s: Error %d parsing device tree\n", __func__,
+		      fdt_port_node);
+		return -1;
+	}
+	port_index = cvmx_fdt_get_int(fdt_addr, fdt_port_node, "reg", -1);
+	if (port_index != 0) {
+		printf("%s: Error: port index (reg) must be 0, not %d.\n",
+		       __func__, port_index);
+		return -1;
+	}
+	reg_addr = cvmx_fdt_get_addr(fdt_addr, fdt_interface_node, "reg");
+	if (reg_addr == FDT_ADDR_T_NONE) {
+		printf("%s: Error: could not get BGX interface address\n",
+		       __func__);
+		return -1;
+	}
+	/* We don't have to bother translating since only 78xx supports OCX and
+	 * doesn't support RGMII.
+	 */
+	xi = __cvmx_bgx_reg_addr_to_xiface(reg_addr);
+	debug("%s: xi.node: %d, xi.interface: 0x%x, addr: 0x%llx\n", __func__,
+	      xi.node, xi.interface, (unsigned long long)reg_addr);
+	if (xi.node < 0) {
+		printf("%s: Device tree BGX node has invalid address 0x%llx\n",
+		       __func__, (unsigned long long)reg_addr);
+		return -1;
+	}
+	debug("%s: Found XCV (RGMII) interface on interface %d\n", __func__,
+	      xi.interface);
+	debug("  phy handle: 0x%x\n",
+	      cvmx_fdt_get_int(fdt_addr, fdt_port_node, "phy-handle", -1));
+	fdt_phy_node =
+		cvmx_fdt_lookup_phandle(fdt_addr, fdt_port_node, "phy-handle");
+	debug("%s: phy-handle node: 0x%x\n", __func__, fdt_phy_node);
+	xiface = cvmx_helper_node_interface_to_xiface(xi.node, xi.interface);
+
+	cvmx_helper_set_port_fdt_node_offset(xiface, port_index, fdt_port_node);
+	if (fdt_phy_node >= 0) {
+		debug("%s: Setting PHY fdt node offset for interface 0x%x, port %d to %d\n",
+		      __func__, xiface, port_index, fdt_phy_node);
+		debug("%s: PHY node name: %s\n", __func__,
+		      fdt_get_name(fdt_addr, fdt_phy_node, NULL));
+		cvmx_helper_set_phy_fdt_node_offset(xiface, port_index,
+						    fdt_phy_node);
+		cvmx_helper_set_port_phy_present(xiface, port_index, true);
+	} else {
+		cvmx_helper_set_phy_fdt_node_offset(xiface, port_index, -1);
+		debug("%s: No PHY fdt node offset for interface 0x%x, port %d to %d\n",
+		      __func__, xiface, port_index, fdt_phy_node);
+		cvmx_helper_set_port_phy_present(xiface, port_index, false);
+	}
+
+	return 0;
+}
+
+/**
+ * Returns if a port is present on an interface
+ *
+ * @param fdt_addr - address fo flat device tree
+ * @param ipd_port - IPD port number
+ *
+ * @return 1 if port is present, 0 if not present, -1 if error
+ */
+int __cvmx_helper_board_get_port_from_dt(void *fdt_addr, int ipd_port)
+{
+	int port_index;
+	int aliases;
+	const char *pip_path;
+	char name_buffer[24];
+	int pip, iface, eth;
+	cvmx_helper_interface_mode_t mode;
+	int xiface = cvmx_helper_get_interface_num(ipd_port);
+	struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface);
+	u32 val;
+	int phy_node_offset;
+	int parse_bgx_dt_err;
+	int parse_vsc7224_err;
+
+	debug("%s(%p, %d)\n", __func__, fdt_addr, ipd_port);
+	if (octeon_has_feature(OCTEON_FEATURE_BGX)) {
+		static int fdt_ports_initialized;
+
+		port_index = cvmx_helper_get_interface_index_num(ipd_port);
+
+		if (!fdt_ports_initialized) {
+			if (octeon_has_feature(OCTEON_FEATURE_BGX_XCV)) {
+				if (!__cvmx_helper_parse_bgx_rgmii_dt(fdt_addr))
+					fdt_ports_initialized = 1;
+				parse_bgx_dt_err =
+					__cvmx_helper_parse_bgx_dt(fdt_addr);
+				parse_vsc7224_err =
+					__cvmx_fdt_parse_vsc7224(fdt_addr);
+				if (!parse_bgx_dt_err && !parse_vsc7224_err)
+					fdt_ports_initialized = 1;
+			} else {
+				debug("%s: Error parsing FDT\n", __func__);
+				return -1;
+			}
+		}
+
+		return cvmx_helper_is_port_valid(xiface, port_index);
+	}
+
+	mode = cvmx_helper_interface_get_mode(xiface);
+
+	switch (mode) {
+	/* Device tree has information about the following mode types. */
+	case CVMX_HELPER_INTERFACE_MODE_RGMII:
+	case CVMX_HELPER_INTERFACE_MODE_GMII:
+	case CVMX_HELPER_INTERFACE_MODE_SPI:
+	case CVMX_HELPER_INTERFACE_MODE_XAUI:
+	case CVMX_HELPER_INTERFACE_MODE_SGMII:
+	case CVMX_HELPER_INTERFACE_MODE_QSGMII:
+	case CVMX_HELPER_INTERFACE_MODE_RXAUI:
+	case CVMX_HELPER_INTERFACE_MODE_AGL:
+	case CVMX_HELPER_INTERFACE_MODE_XLAUI:
+	case CVMX_HELPER_INTERFACE_MODE_XFI:
+		aliases = 1;
+		break;
+	default:
+		aliases = 0;
+		break;
+	}
+
+	/* The device tree information is present on interfaces that have phy */
+	if (!aliases)
+		return 1;
+
+	port_index = cvmx_helper_get_interface_index_num(ipd_port);
+
+	aliases = fdt_path_offset(fdt_addr, "/aliases");
+	if (aliases < 0) {
+		debug("%s: ERROR: /aliases not found in device tree fdt_addr=%p\n",
+		      __func__, fdt_addr);
+		return -1;
+	}
+
+	pip_path = (const char *)fdt_getprop(fdt_addr, aliases, "pip", NULL);
+	if (!pip_path) {
+		debug("%s: ERROR: interface %x pip path not found in device tree\n",
+		      __func__, xiface);
+		return -1;
+	}
+	pip = fdt_path_offset(fdt_addr, pip_path);
+	if (pip < 0) {
+		debug("%s: ERROR: interface %x pip not found in device tree\n",
+		      __func__, xiface);
+		return -1;
+	}
+	snprintf(name_buffer, sizeof(name_buffer), "interface@%d",
+		 xi.interface);
+	iface = fdt_subnode_offset(fdt_addr, pip, name_buffer);
+	if (iface < 0)
+		return 0;
+	snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", port_index);
+	eth = fdt_subnode_offset(fdt_addr, iface, name_buffer);
+	debug("%s: eth subnode offset %d from %s\n", __func__, eth,
+	      name_buffer);
+
+	if (eth < 0)
+		return -1;
+
+	cvmx_helper_set_port_fdt_node_offset(xiface, port_index, eth);
+
+	phy_node_offset = cvmx_fdt_get_int(fdt_addr, eth, "phy", -1);
+	cvmx_helper_set_phy_fdt_node_offset(xiface, port_index,
+					    phy_node_offset);
+
+	if (fdt_getprop(fdt_addr, eth, "cavium,sgmii-mac-phy-mode", NULL))
+		cvmx_helper_set_mac_phy_mode(xiface, port_index, true);
+	else
+		cvmx_helper_set_mac_phy_mode(xiface, port_index, false);
+
+	if (fdt_getprop(fdt_addr, eth, "cavium,force-link-up", NULL))
+		cvmx_helper_set_port_force_link_up(xiface, port_index, true);
+	else
+		cvmx_helper_set_port_force_link_up(xiface, port_index, false);
+
+	if (fdt_getprop(fdt_addr, eth, "cavium,sgmii-mac-1000x-mode", NULL))
+		cvmx_helper_set_1000x_mode(xiface, port_index, true);
+	else
+		cvmx_helper_set_1000x_mode(xiface, port_index, false);
+
+	if (fdt_getprop(fdt_addr, eth, "cavium,disable-autonegotiation", NULL))
+		cvmx_helper_set_port_autonegotiation(xiface, port_index, false);
+	else
+		cvmx_helper_set_port_autonegotiation(xiface, port_index, true);
+
+	if (mode == CVMX_HELPER_INTERFACE_MODE_AGL) {
+		bool tx_bypass = false;
+
+		if (fdt_getprop(fdt_addr, eth, "cavium,rx-clk-delay-bypass",
+				NULL))
+			cvmx_helper_set_agl_rx_clock_delay_bypass(
+				xiface, port_index, true);
+		else
+			cvmx_helper_set_agl_rx_clock_delay_bypass(
+				xiface, port_index, false);
+
+		val = cvmx_fdt_get_int(fdt_addr, eth, "cavium,rx-clk-skew", 0);
+		cvmx_helper_set_agl_rx_clock_skew(xiface, port_index, val);
+
+		if (fdt_getprop(fdt_addr, eth, "cavium,tx-clk-delay-bypass",
+				NULL))
+			tx_bypass = true;
+
+		val = cvmx_fdt_get_int(fdt_addr, eth, "tx-clk-delay", 0);
+		cvmx_helper_cfg_set_rgmii_tx_clk_delay(xiface, port_index,
+						       tx_bypass, val);
+
+		val = cvmx_fdt_get_int(fdt_addr, eth, "cavium,refclk-sel", 0);
+		cvmx_helper_set_agl_refclk_sel(xiface, port_index, val);
+	}
+
+	return (eth >= 0);
+}
+
+/**
+ * Given the address of the MDIO registers, output the CPU node and MDIO bus
+ *
+ * @param	addr	64-bit address of MDIO registers (from device tree)
+ * @param[out]	node	CPU node number (78xx)
+ * @param[out]	bus	MDIO bus number
+ */
+void __cvmx_mdio_addr_to_node_bus(u64 addr, int *node, int *bus)
+{
+	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		if (node)
+			*node = cvmx_csr_addr_to_node(addr);
+		addr = cvmx_csr_addr_strip_node(addr);
+	} else {
+		if (node)
+			*node = 0;
+	}
+	if (OCTEON_IS_MODEL(OCTEON_CN68XX) || OCTEON_IS_MODEL(OCTEON_CN78XX)) {
+		switch (addr) {
+		case 0x0001180000003800:
+			*bus = 0;
+			break;
+		case 0x0001180000003880:
+			*bus = 1;
+			break;
+		case 0x0001180000003900:
+			*bus = 2;
+			break;
+		case 0x0001180000003980:
+			*bus = 3;
+			break;
+		default:
+			*bus = -1;
+			printf("%s: Invalid SMI bus address 0x%llx\n", __func__,
+			       (unsigned long long)addr);
+			break;
+		}
+	} else if (OCTEON_IS_MODEL(OCTEON_CN73XX) ||
+		   OCTEON_IS_MODEL(OCTEON_CNF75XX)) {
+		switch (addr) {
+		case 0x0001180000003800:
+			*bus = 0;
+			break;
+		case 0x0001180000003880:
+			*bus = 1;
+			break;
+		default:
+			*bus = -1;
+			printf("%s: Invalid SMI bus address 0x%llx\n", __func__,
+			       (unsigned long long)addr);
+			break;
+		}
+	} else {
+		switch (addr) {
+		case 0x0001180000001800:
+			*bus = 0;
+			break;
+		case 0x0001180000001900:
+			*bus = 1;
+			break;
+		default:
+			*bus = -1;
+			printf("%s: Invalid SMI bus address 0x%llx\n", __func__,
+			       (unsigned long long)addr);
+			break;
+		}
+	}
+}
-- 
2.35.1


  parent reply	other threads:[~2022-04-07  7:14 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 ` [PATCH v2 10/52] mips: octeon: Add cvmx-helper-bgx.c Stefan Roese
2022-04-07  7:11 ` Stefan Roese [this message]
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-12-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.