From: Aaron Williams <awilli...@marvell.com>

Import cvmx-helper-sfp.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 <awilli...@marvell.com>
Signed-off-by: Stefan Roese <s...@denx.de>
---
 arch/mips/mach-octeon/cvmx-helper-sfp.c | 1877 +++++++++++++++++++++++
 1 file changed, 1877 insertions(+)
 create mode 100644 arch/mips/mach-octeon/cvmx-helper-sfp.c

diff --git a/arch/mips/mach-octeon/cvmx-helper-sfp.c 
b/arch/mips/mach-octeon/cvmx-helper-sfp.c
new file mode 100644
index 000000000000..a17ac542fcf8
--- /dev/null
+++ b/arch/mips/mach-octeon/cvmx-helper-sfp.c
@@ -0,0 +1,1877 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2022 Marvell International Ltd.
+ */
+
+#include <errno.h>
+#include <i2c.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/delay.h>
+#include <display_options.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-helper.h>
+#include <mach/cvmx-helper-board.h>
+#include <mach/cvmx-helper-fdt.h>
+#include <mach/cvmx-helper-cfg.h>
+#include <mach/cvmx-helper-gpio.h>
+#include <mach/cvmx-helper-util.h>
+
+extern void octeon_i2c_unblock(int bus);
+
+static struct cvmx_fdt_sfp_info *sfp_list;
+
+/**
+ * Local allocator to handle both SE and U-Boot that also zeroes out memory
+ *
+ * @param      size    number of bytes to allocate
+ *
+ * @return     pointer to allocated memory or NULL if out of memory.
+ *             Alignment is set to 8-bytes.
+ */
+static void *cvm_sfp_alloc(size_t size)
+{
+       return calloc(size, 1);
+}
+
+/**
+ * Free allocated memory.
+ *
+ * @param      ptr     pointer to memory to free
+ *
+ * NOTE: This only works in U-Boot since SE does not really have a freeing
+ *      mechanism.  In SE the memory is zeroed out and not freed so this
+ *      is a memory leak if errors occur.
+ */
+static inline void cvm_sfp_free(void *ptr, size_t size)
+{
+       free(ptr);
+}
+
+/**
+ * Select a QSFP device before accessing the EEPROM
+ *
+ * @param      sfp     handle for sfp/qsfp connector
+ * @param      enable  Set true to select, false to deselect
+ *
+ * @return     0 on success or if SFP or no select GPIO, -1 on GPIO error
+ */
+static int cvmx_qsfp_select(const struct cvmx_fdt_sfp_info *sfp, bool enable)
+{
+       /* Select is only needed for QSFP modules */
+       if (!sfp->is_qsfp) {
+               debug("%s(%s, %d): not QSFP\n", __func__, sfp->name, enable);
+               return 0;
+       }
+
+       if (dm_gpio_is_valid(&sfp->select)) {
+               /* Note that select is active low */
+               return dm_gpio_set_value(&sfp->select, !enable);
+       }
+
+       debug("%s: select GPIO unknown\n", __func__);
+       return 0;
+}
+
+static int cvmx_sfp_parse_sfp_buffer(struct cvmx_sfp_mod_info *sfp_info,
+                                    const uint8_t *buffer)
+{
+       u8 csum = 0;
+       bool csum_good = false;
+       int i;
+
+       /* Validate the checksum */
+       for (i = 0; i < 0x3f; i++)
+               csum += buffer[i];
+       csum_good = csum == buffer[0x3f];
+       debug("%s: Lower checksum: 0x%02x, expected: 0x%02x\n", __func__, csum,
+             buffer[0x3f]);
+       csum = 0;
+       for (i = 0x40; i < 0x5f; i++)
+               csum += buffer[i];
+       debug("%s: Upper checksum: 0x%02x, expected: 0x%02x\n", __func__, csum,
+             buffer[0x5f]);
+       if (csum != buffer[0x5f] || !csum_good) {
+               debug("Error: SFP EEPROM checksum information is incorrect\n");
+               return -1;
+       }
+
+       sfp_info->conn_type = buffer[0];
+       if (buffer[1] < 1 || buffer[1] > 7) { /* Extended ID */
+               debug("Error: Unknown SFP extended identifier 0x%x\n",
+                     buffer[1]);
+               return -1;
+       }
+       if (buffer[1] != 4) {
+               debug("Module is not SFP/SFP+/SFP28/QSFP+\n");
+               return -1;
+       }
+       sfp_info->mod_type = buffer[2];
+       sfp_info->eth_comp = buffer[3] & 0xf0;
+       sfp_info->cable_comp = buffer[0x24];
+
+       /* There are several ways a cable can be marked as active or
+        * passive.  8.[2-3] specify the SFP+ cable technology.  Some
+        * modules also use 3.[0-1] for Infiniband, though it's
+        * redundant.
+        */
+       if ((buffer[8] & 0x0C) == 0x08) {
+               sfp_info->limiting = true;
+               sfp_info->active_cable = true;
+       } else if ((buffer[8] & 0xC) == 0x4) {
+               sfp_info->limiting = false;
+               sfp_info->active_cable = false;
+       }
+       if ((buffer[3] & 3) == 2) {
+               sfp_info->active_cable = true;
+               sfp_info->limiting = true;
+       }
+
+       switch (sfp_info->mod_type) {
+       case CVMX_SFP_MOD_OPTICAL_LC:
+       case CVMX_SFP_MOD_OPTICAL_PIGTAIL:
+               sfp_info->copper_cable = false;
+               break;
+       case CVMX_SFP_MOD_COPPER_PIGTAIL:
+               sfp_info->copper_cable = true;
+               break;
+       case CVMX_SFP_MOD_NO_SEP_CONN:
+               switch (sfp_info->cable_comp) {
+               case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_HIGH_BER:
+               case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_LOW_BER:
+               case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_LOW_BER:
+                       sfp_info->copper_cable = false;
+                       sfp_info->limiting = true;
+                       sfp_info->active_cable = true;
+                       break;
+
+               case CVMX_SFP_CABLE_100G_SR4_25G_SR:
+               case CVMX_SFP_CABLE_100G_LR4_25G_LR:
+               case CVMX_SFP_CABLE_100G_ER4_25G_ER:
+               case CVMX_SFP_CABLE_100G_SR10:
+               case CVMX_SFP_CABLE_100G_CWDM4_MSA:
+               case CVMX_SFP_CABLE_100G_PSM4:
+               case CVMX_SFP_CABLE_100G_CWDM4:
+               case CVMX_SFP_CABLE_40G_ER4:
+               case CVMX_SFP_CABLE_4X10G_SR:
+               case CVMX_SFP_CABLE_G959_1_P1I1_2D1:
+               case CVMX_SFP_CABLE_G959_1_P1S1_2D2:
+               case CVMX_SFP_CABLE_G959_1_P1L1_2D2:
+               case CVMX_SFP_CABLE_100G_CLR4:
+               case CVMX_SFP_CABLE_100G_2_LAMBDA_DWDM:
+               case CVMX_SFP_CABLE_40G_SWDM4:
+               case CVMX_SFP_CABLE_100G_SWDM4:
+               case CVMX_SFP_CABLE_100G_PAM4_BIDI:
+                       sfp_info->copper_cable = false;
+                       break;
+
+               case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_HIGH_BER:
+               case CVMX_SFP_CABLE_10GBASE_T:
+               case CVMX_SFP_CABLE_10GBASE_T_SR:
+               case CVMX_SFP_CABLE_5GBASE_T:
+               case CVMX_SFP_CABLE_2_5GBASE_T:
+                       sfp_info->copper_cable = true;
+                       sfp_info->limiting = true;
+                       sfp_info->active_cable = true;
+                       break;
+
+               case CVMX_SFP_CABLE_100G_CR4_25G_CR_CA_L:
+               case CVMX_SFP_CABLE_25G_CR_CA_S:
+               case CVMX_SFP_CABLE_25G_CR_CA_N:
+               case CVMX_SFP_CABLE_40G_PSM4:
+                       sfp_info->copper_cable = true;
+                       break;
+
+               default:
+                       switch (sfp_info->eth_comp) {
+                       case CVMX_SFP_CABLE_10GBASE_ER:
+                       case CVMX_SFP_CABLE_10GBASE_LRM:
+                       case CVMX_SFP_CABLE_10GBASE_LR:
+                       case CVMX_SFP_CABLE_10GBASE_SR:
+                               sfp_info->copper_cable = false;
+                               break;
+                       }
+                       break;
+               }
+               break;
+
+       case CVMX_SFP_MOD_RJ45:
+               debug("%s: RJ45 adapter\n", __func__);
+               sfp_info->copper_cable = true;
+               sfp_info->active_cable = true;
+               sfp_info->limiting = true;
+               break;
+       case CVMX_SFP_MOD_UNKNOWN:
+               /* The Avago 1000Base-X to 1000Base-T module reports that it
+                * is an unknown module type but the Ethernet compliance code
+                * says it is 1000Base-T.  We'll change the reporting to RJ45.
+                */
+               if (buffer[6] & 8) {
+                       debug("RJ45 gigabit module detected\n");
+                       sfp_info->mod_type = CVMX_SFP_MOD_RJ45;
+                       sfp_info->copper_cable = false;
+                       sfp_info->limiting = true;
+                       sfp_info->active_cable = true;
+                       sfp_info->max_copper_cable_len = buffer[0x12];
+                       sfp_info->rate = CVMX_SFP_RATE_1G;
+               } else {
+                       debug("Unknown module type 0x%x\n", sfp_info->mod_type);
+               }
+               sfp_info->limiting = true;
+               break;
+       case CVMX_SFP_MOD_MXC_2X16:
+               debug("%s: MXC 2X16\n", __func__);
+               break;
+       default:
+               sfp_info->limiting = true;
+               break;
+       }
+
+       if (sfp_info->copper_cable)
+               sfp_info->max_copper_cable_len = buffer[0x12];
+       else
+               sfp_info->max_50um_om4_cable_length = buffer[0x12] * 10;
+
+       if (buffer[0xe])
+               sfp_info->max_single_mode_cable_length = buffer[0xe] * 1000;
+       else
+               sfp_info->max_single_mode_cable_length = buffer[0xf] * 100000;
+
+       sfp_info->max_50um_om2_cable_length = buffer[0x10] * 10;
+       sfp_info->max_62_5um_om1_cable_length = buffer[0x11] * 10;
+       sfp_info->max_50um_om3_cable_length = buffer[0x13] * 10;
+
+       if (buffer[0xc] == 0xff) {
+               if (buffer[0x42] >= 255)
+                       sfp_info->rate = CVMX_SFP_RATE_100G;
+               else if (buffer[0x42] >= 160)
+                       sfp_info->rate = CVMX_SFP_RATE_40G;
+               else if (buffer[0x42] >= 100)
+                       sfp_info->rate = CVMX_SFP_RATE_25G;
+               else
+                       sfp_info->rate = CVMX_SFP_RATE_UNKNOWN;
+       } else if (buffer[0xc] >= 100) {
+               sfp_info->rate = CVMX_SFP_RATE_10G;
+       } else if (buffer[0xc] >= 10) {
+               sfp_info->rate = CVMX_SFP_RATE_1G;
+       } else {
+               sfp_info->rate = CVMX_SFP_RATE_UNKNOWN;
+       }
+
+       if (sfp_info->rate == CVMX_SFP_RATE_UNKNOWN) {
+               switch (sfp_info->cable_comp) {
+               case CVMX_SFP_CABLE_100G_SR10:
+               case CVMX_SFP_CABLE_100G_CWDM4_MSA:
+               case CVMX_SFP_CABLE_100G_PSM4:
+               case CVMX_SFP_CABLE_100G_CWDM4:
+               case CVMX_SFP_CABLE_100G_CLR4:
+               case CVMX_SFP_CABLE_100G_2_LAMBDA_DWDM:
+               case CVMX_SFP_CABLE_100G_SWDM4:
+               case CVMX_SFP_CABLE_100G_PAM4_BIDI:
+                       sfp_info->rate = CVMX_SFP_RATE_100G;
+                       break;
+               case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_HIGH_BER:
+               case CVMX_SFP_CABLE_100G_SR4_25G_SR:
+               case CVMX_SFP_CABLE_100G_LR4_25G_LR:
+               case CVMX_SFP_CABLE_100G_ER4_25G_ER:
+               case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_HIGH_BER:
+               case CVMX_SFP_CABLE_100G_CR4_25G_CR_CA_L:
+               case CVMX_SFP_CABLE_25G_CR_CA_S:
+               case CVMX_SFP_CABLE_25G_CR_CA_N:
+               case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_LOW_BER:
+               case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_LOW_BER:
+                       sfp_info->rate = CVMX_SFP_RATE_25G;
+                       break;
+               case CVMX_SFP_CABLE_40G_ER4:
+               case CVMX_SFP_CABLE_4X10G_SR:
+               case CVMX_SFP_CABLE_40G_PSM4:
+               case CVMX_SFP_CABLE_40G_SWDM4:
+                       sfp_info->rate = CVMX_SFP_RATE_40G;
+                       break;
+               case CVMX_SFP_CABLE_G959_1_P1I1_2D1:
+               case CVMX_SFP_CABLE_G959_1_P1S1_2D2:
+               case CVMX_SFP_CABLE_G959_1_P1L1_2D2:
+               case CVMX_SFP_CABLE_10GBASE_T:
+               case CVMX_SFP_CABLE_10GBASE_T_SR:
+               case CVMX_SFP_CABLE_5GBASE_T:
+               case CVMX_SFP_CABLE_2_5GBASE_T:
+                       sfp_info->rate = CVMX_SFP_RATE_10G;
+                       break;
+               default:
+                       switch (sfp_info->eth_comp) {
+                       case CVMX_SFP_CABLE_10GBASE_ER:
+                       case CVMX_SFP_CABLE_10GBASE_LRM:
+                       case CVMX_SFP_CABLE_10GBASE_LR:
+                       case CVMX_SFP_CABLE_10GBASE_SR:
+                               sfp_info->rate = CVMX_SFP_RATE_10G;
+                               break;
+                       default:
+                               sfp_info->rate = CVMX_SFP_RATE_UNKNOWN;
+                               break;
+                       }
+                       break;
+               }
+       }
+
+       if (buffer[0xc] < 0xff)
+               sfp_info->bitrate_max = buffer[0xc] * 100;
+       else
+               sfp_info->bitrate_max = buffer[0x42] * 250;
+
+       if ((buffer[8] & 0xc) == 8) {
+               if (buffer[0x3c] & 0x4)
+                       sfp_info->limiting = true;
+       }
+
+       /* Currently we only set this for 25G.  FEC is required for CA-S cables
+        * and for cable lengths >= 5M as of this writing.
+        */
+       if ((sfp_info->rate == CVMX_SFP_RATE_25G &&
+            sfp_info->copper_cable) &&
+           (sfp_info->cable_comp == CVMX_SFP_CABLE_25G_CR_CA_S ||
+            sfp_info->max_copper_cable_len >= 5))
+               sfp_info->fec_required = true;
+
+       /* copy strings and vendor info, strings will be automatically NUL
+        * terminated.
+        */
+       memcpy(sfp_info->vendor_name, &buffer[0x14], 16);
+       memcpy(sfp_info->vendor_oui, &buffer[0x25], 3);
+       memcpy(sfp_info->vendor_pn, &buffer[0x28], 16);
+       memcpy(sfp_info->vendor_rev, &buffer[0x38], 4);
+       memcpy(sfp_info->vendor_sn, &buffer[0x44], 16);
+       memcpy(sfp_info->date_code, &buffer[0x54], 8);
+
+       sfp_info->cooled_laser = !!(buffer[0x40] & 4);
+       sfp_info->internal_cdr = !!(buffer[0x40] & 8);
+
+       if (buffer[0x40] & 0x20)
+               sfp_info->power_level = 3;
+       else
+               sfp_info->power_level = (buffer[0x40] & 2) ? 2 : 1;
+
+       sfp_info->diag_paging = !!(buffer[0x40] & 0x10);
+       sfp_info->linear_rx_output = !(buffer[0x40] & 1);
+       sfp_info->los_implemented = !!(buffer[0x41] & 2);
+       sfp_info->los_inverted = !!(buffer[0x41] & 4);
+       sfp_info->tx_fault_implemented = !!(buffer[0x41] & 8);
+       sfp_info->tx_disable_implemented = !!(buffer[0x41] & 0x10);
+       sfp_info->rate_select_implemented = !!(buffer[0x41] & 0x20);
+       sfp_info->tuneable_transmitter = !!(buffer[0x41] & 0x40);
+       sfp_info->rx_decision_threshold_implemented = !!(buffer[0x41] & 0x80);
+
+       sfp_info->diag_monitoring = !!(buffer[0x5c] & 0x40);
+       sfp_info->diag_rx_power_averaged = !!(buffer[0x5c] & 0x8);
+       sfp_info->diag_externally_calibrated = !!(buffer[0x5c] & 0x10);
+       sfp_info->diag_internally_calibrated = !!(buffer[0x5c] & 0x20);
+       sfp_info->diag_addr_change_required = !!(buffer[0x5c] & 0x4);
+       sfp_info->diag_soft_rate_select_control = !!(buffer[0x5d] & 2);
+       sfp_info->diag_app_select_control = !!(buffer[0x5d] & 4);
+       sfp_info->diag_soft_rate_select_control = !!(buffer[0x5d] & 8);
+       sfp_info->diag_soft_rx_los_implemented = !!(buffer[0x5d] & 0x10);
+       sfp_info->diag_soft_tx_fault_implemented = !!(buffer[0x5d] & 0x20);
+       sfp_info->diag_soft_tx_disable_implemented = !!(buffer[0x5d] & 0x40);
+       sfp_info->diag_alarm_warning_flags_implemented =
+               !!(buffer[0x5d] & 0x80);
+       sfp_info->diag_rev = buffer[0x5e];
+
+       return 0;
+}
+
+static int cvmx_sfp_parse_qsfp_buffer(struct cvmx_sfp_mod_info *sfp_info,
+                                     const uint8_t *buffer)
+{
+       u8 csum = 0;
+       bool csum_good = false;
+       int i;
+
+       /* Validate the checksum */
+       for (i = 0x80; i < 0xbf; i++)
+               csum += buffer[i];
+       csum_good = csum == buffer[0xbf];
+       debug("%s: Lower checksum: 0x%02x, expected: 0x%02x\n", __func__, csum,
+             buffer[0xbf]);
+       csum = 0;
+       for (i = 0xc0; i < 0xdf; i++)
+               csum += buffer[i];
+       debug("%s: Upper checksum: 0x%02x, expected: 0x%02x\n", __func__, csum,
+             buffer[0xdf]);
+       if (csum != buffer[0xdf] || !csum_good) {
+               debug("Error: SFP EEPROM checksum information is incorrect\n");
+               return -1;
+       }
+
+       sfp_info->conn_type = buffer[0x80];
+       sfp_info->mod_type = buffer[0x82];
+       sfp_info->eth_comp = buffer[0x83] & 0xf0;
+       sfp_info->cable_comp = buffer[0xa4];
+
+       switch (sfp_info->mod_type) {
+       case CVMX_SFP_MOD_COPPER_PIGTAIL:
+       case CVMX_SFP_MOD_NO_SEP_CONN:
+               debug("%s: copper pigtail or no separable cable\n", __func__);
+               /* There are several ways a cable can be marked as active or
+                * passive.  8.[2-3] specify the SFP+ cable technology.  Some
+                * modules also use 3.[0-1] for Infiniband, though it's
+                * redundant.
+                */
+               sfp_info->copper_cable = true;
+               if ((buffer[0x88] & 0x0C) == 0x08) {
+                       sfp_info->limiting = true;
+                       sfp_info->active_cable = true;
+               } else if ((buffer[0x88] & 0xC) == 0x4) {
+                       sfp_info->limiting = false;
+                       sfp_info->active_cable = false;
+               }
+               if ((buffer[0x83] & 3) == 2) {
+                       sfp_info->active_cable = true;
+                       sfp_info->limiting = true;
+               }
+               break;
+       case CVMX_SFP_MOD_RJ45:
+               debug("%s: RJ45 adapter\n", __func__);
+               sfp_info->copper_cable = true;
+               sfp_info->active_cable = true;
+               sfp_info->limiting = true;
+               break;
+       case CVMX_SFP_MOD_UNKNOWN:
+               debug("Unknown module type\n");
+               /* The Avago 1000Base-X to 1000Base-T module reports that it
+                * is an unknown module type but the Ethernet compliance code
+                * says it is 1000Base-T.  We'll change the reporting to RJ45.
+                */
+               if (buffer[0x86] & 8) {
+                       sfp_info->mod_type = CVMX_SFP_MOD_RJ45;
+                       sfp_info->copper_cable = false;
+                       sfp_info->limiting = true;
+                       sfp_info->active_cable = true;
+                       sfp_info->max_copper_cable_len = buffer[0x92];
+                       sfp_info->rate = CVMX_SFP_RATE_1G;
+               }
+               fallthrough;
+       default:
+               sfp_info->limiting = true;
+               break;
+       }
+
+       if (sfp_info->copper_cable)
+               sfp_info->max_copper_cable_len = buffer[0x92];
+       else
+               sfp_info->max_50um_om4_cable_length = buffer[0x92] * 10;
+
+       debug("%s: copper cable: %d, max copper cable len: %d\n", __func__,
+             sfp_info->copper_cable, sfp_info->max_copper_cable_len);
+       if (buffer[0xe])
+               sfp_info->max_single_mode_cable_length = buffer[0x8e] * 1000;
+       else
+               sfp_info->max_single_mode_cable_length = buffer[0x8f] * 100000;
+
+       sfp_info->max_50um_om2_cable_length = buffer[0x90] * 10;
+       sfp_info->max_62_5um_om1_cable_length = buffer[0x91] * 10;
+       sfp_info->max_50um_om3_cable_length = buffer[0x93] * 10;
+
+       if (buffer[0x8c] == 12) {
+               sfp_info->rate = CVMX_SFP_RATE_1G;
+       } else if (buffer[0x8c] == 103) {
+               sfp_info->rate = CVMX_SFP_RATE_10G;
+       } else if (buffer[0x8c] == 0xff) {
+               if (buffer[0xc2] == 103)
+                       sfp_info->rate = CVMX_SFP_RATE_100G;
+       }
+
+       if (buffer[0x8c] < 0xff)
+               sfp_info->bitrate_max = buffer[0x8c] * 100;
+       else
+               sfp_info->bitrate_max = buffer[0xc2] * 250;
+
+       if ((buffer[0x88] & 0xc) == 8) {
+               if (buffer[0xbc] & 0x4)
+                       sfp_info->limiting = true;
+       }
+
+       /* Currently we only set this for 25G.  FEC is required for CA-S cables
+        * and for cable lengths >= 5M as of this writing.
+        */
+       /* copy strings and vendor info, strings will be automatically NUL
+        * terminated.
+        */
+       memcpy(sfp_info->vendor_name, &buffer[0x94], 16);
+       memcpy(sfp_info->vendor_oui, &buffer[0xa5], 3);
+       memcpy(sfp_info->vendor_pn, &buffer[0xa8], 16);
+       memcpy(sfp_info->vendor_rev, &buffer[0xb8], 4);
+       memcpy(sfp_info->vendor_sn, &buffer[0xc4], 16);
+       memcpy(sfp_info->date_code, &buffer[0xd4], 8);
+
+       sfp_info->linear_rx_output = !!(buffer[0xc0] & 1);
+       sfp_info->cooled_laser = !!(buffer[0xc0] & 4);
+       sfp_info->internal_cdr = !!(buffer[0xc0] & 8);
+
+       if (buffer[0xc0] & 0x20)
+               sfp_info->power_level = 3;
+       else
+               sfp_info->power_level = (buffer[0xc0] & 2) ? 2 : 1;
+
+       sfp_info->diag_paging = !!(buffer[0xc0] & 0x10);
+       sfp_info->los_implemented = !!(buffer[0xc1] & 2);
+       sfp_info->los_inverted = !!(buffer[0xc1] & 4);
+       sfp_info->tx_fault_implemented = !!(buffer[0xc1] & 8);
+       sfp_info->tx_disable_implemented = !!(buffer[0xc1] & 0x10);
+       sfp_info->rate_select_implemented = !!(buffer[0xc1] & 0x20);
+       sfp_info->tuneable_transmitter = !!(buffer[0xc1] & 0x40);
+       sfp_info->rx_decision_threshold_implemented = !!(buffer[0xc1] & 0x80);
+
+       sfp_info->diag_monitoring = !!(buffer[0xdc] & 0x40);
+       sfp_info->diag_rx_power_averaged = !!(buffer[0xdc] & 0x8);
+       sfp_info->diag_externally_calibrated = !!(buffer[0xdc] & 0x10);
+       sfp_info->diag_internally_calibrated = !!(buffer[0xdc] & 0x20);
+       sfp_info->diag_addr_change_required = !!(buffer[0xdc] & 0x4);
+       sfp_info->diag_soft_rate_select_control = !!(buffer[0xdd] & 2);
+       sfp_info->diag_app_select_control = !!(buffer[0xdd] & 4);
+       sfp_info->diag_soft_rate_select_control = !!(buffer[0xdd] & 8);
+       sfp_info->diag_soft_rx_los_implemented = !!(buffer[0xdd] & 0x10);
+       sfp_info->diag_soft_tx_fault_implemented = !!(buffer[0xdd] & 0x20);
+       sfp_info->diag_soft_tx_disable_implemented = !!(buffer[0xdd] & 0x40);
+       sfp_info->diag_alarm_warning_flags_implemented =
+               !!(buffer[0xdd] & 0x80);
+       sfp_info->diag_rev = buffer[0xde];
+
+       return 0;
+}
+
+static bool sfp_verify_checksum(const uint8_t *buffer)
+{
+       u8 csum = 0;
+       u8 offset;
+       bool csum_good = false;
+       int i;
+
+       switch (buffer[0]) {
+       case CVMX_SFP_CONN_QSFP:
+       case CVMX_SFP_CONN_QSFPP:
+       case CVMX_SFP_CONN_QSFP28:
+       case CVMX_SFP_CONN_MICRO_QSFP:
+       case CVMX_SFP_CONN_QSFP_DD:
+               offset = 0x80;
+               break;
+       default:
+               offset = 0;
+               break;
+       }
+       for (i = offset; i < offset + 0x3f; i++)
+               csum += buffer[i];
+       csum_good = csum == buffer[offset + 0x3f];
+       if (!csum_good) {
+               debug("%s: Lower checksum bad, got 0x%x, expected 0x%x\n",
+                     __func__, csum, buffer[offset + 0x3f]);
+               return false;
+       }
+       csum = 0;
+       for (i = offset + 0x40; i < offset + 0x5f; i++)
+               csum += buffer[i];
+       if (csum != buffer[offset + 0x5f]) {
+               debug("%s: Upper checksum bad, got 0x%x, expected 0x%x\n",
+                     __func__, csum, buffer[offset + 0x5f]);
+               return false;
+       }
+       return true;
+}
+
+/**
+ * Reads and parses SFP/QSFP EEPROM
+ *
+ * @param      sfp     sfp handle to read
+ *
+ * @return     0 for success, -1 on error.
+ */
+int cvmx_sfp_read_i2c_eeprom(struct cvmx_fdt_sfp_info *sfp)
+{
+       const struct cvmx_fdt_i2c_bus_info *bus = sfp->i2c_bus;
+       int oct_bus = cvmx_fdt_i2c_get_root_bus(bus);
+       struct udevice *dev;
+       u8 buffer[256];
+       bool is_qsfp;
+       int retry;
+       int err;
+
+       if (!bus) {
+               debug("%s(%s): Error: i2c bus undefined for eeprom\n", __func__,
+                     sfp->name);
+               return -1;
+       }
+
+       is_qsfp = (sfp->sfp_info.conn_type == CVMX_SFP_CONN_QSFP ||
+                  sfp->sfp_info.conn_type == CVMX_SFP_CONN_QSFPP ||
+                  sfp->sfp_info.conn_type == CVMX_SFP_CONN_QSFP28 ||
+                  sfp->sfp_info.conn_type == CVMX_SFP_CONN_MICRO_QSFP) ||
+                 sfp->is_qsfp;
+
+       err = cvmx_qsfp_select(sfp, true);
+       if (err) {
+               debug("%s: Error selecting SFP/QSFP slot\n", __func__);
+               return err;
+       }
+
+       debug("%s: Reading eeprom from i2c address %d:0x%x\n", __func__,
+             oct_bus, sfp->i2c_eeprom_addr);
+       for (retry = 0; retry < 3; retry++) {
+               err = i2c_get_chip(bus->i2c_bus, sfp->i2c_eeprom_addr, 1, &dev);
+               if (err) {
+                       debug("Cannot find I2C device: %d\n", err);
+                       goto error;
+               }
+
+               err = dm_i2c_read(dev, 0, buffer, 256);
+               if (err || !sfp_verify_checksum(buffer)) {
+                       debug("%s: Error %d reading eeprom at 0x%x, bus %d\n",
+                             __func__, err, sfp->i2c_eeprom_addr, oct_bus);
+                       debug("%s: Retry %d\n", __func__, retry + 1);
+                       mdelay(1000);
+               } else {
+                       break;
+               }
+       }
+       if (err) {
+               debug("%s: Error reading eeprom from SFP %s\n", __func__,
+                     sfp->name);
+               return -1;
+       }
+#ifdef DEBUG
+       print_buffer(0, buffer, 1, 256, 0);
+#endif
+       memset(&sfp->sfp_info, 0, sizeof(struct cvmx_sfp_mod_info));
+
+       switch (buffer[0]) {
+       case CVMX_SFP_CONN_SFP:
+               err = cvmx_sfp_parse_sfp_buffer(&sfp->sfp_info, buffer);
+               break;
+       case CVMX_SFP_CONN_QSFP:
+       case CVMX_SFP_CONN_QSFPP:
+       case CVMX_SFP_CONN_QSFP28:
+       case CVMX_SFP_CONN_MICRO_QSFP:
+               err = cvmx_sfp_parse_qsfp_buffer(&sfp->sfp_info, buffer);
+               break;
+       default:
+               debug("%s: Unknown SFP transceiver type 0x%x\n", __func__,
+                     buffer[0]);
+               err = -1;
+               break;
+       }
+
+error:
+       if (is_qsfp)
+               err |= cvmx_qsfp_select(sfp, false);
+
+       if (!err) {
+               sfp->valid = true;
+               sfp->sfp_info.valid = true;
+       } else {
+               sfp->valid = false;
+               sfp->sfp_info.valid = false;
+       }
+
+       return err;
+}
+
+/**
+ * Returns the information about a SFP/QSFP device
+ *
+ * @param       sfp             sfp handle
+ *
+ * @return      sfp_info        Pointer sfp mod info data structure
+ */
+const struct cvmx_sfp_mod_info *
+cvmx_phy_get_sfp_mod_info(const struct cvmx_fdt_sfp_info *sfp)
+{
+       return (sfp) ? &sfp->sfp_info : NULL;
+}
+
+/**
+ * Function called to check and return the status of the mod_abs pin or
+ * mod_pres pin for QSFPs.
+ *
+ * @param      sfp     Handle to SFP information.
+ * @param      data    User-defined data passed to the function
+ *
+ * @return     0 if absent, 1 if present, -1 on error
+ */
+int cvmx_sfp_check_mod_abs(struct cvmx_fdt_sfp_info *sfp, void *data)
+{
+       int val;
+       int err = 0;
+       int mode;
+
+       if (!dm_gpio_is_valid(&sfp->mod_abs)) {
+               debug("%s: Error: mod_abs not set for %s\n", __func__,
+                     sfp->name);
+               return -1;
+       }
+       val = dm_gpio_get_value(&sfp->mod_abs);
+       debug("%s(%s, %p) mod_abs: %d\n", __func__, sfp->name, data, val);
+       if (val >= 0 && val != sfp->last_mod_abs && sfp->mod_abs_changed) {
+               err = 0;
+               if (!val) {
+                       err = cvmx_sfp_read_i2c_eeprom(sfp);
+                       if (err)
+                               debug("%s: Error reading SFP %s EEPROM\n",
+                                     __func__, sfp->name);
+               }
+               err = sfp->mod_abs_changed(sfp, val, sfp->mod_abs_changed_data);
+       }
+       debug("%s(%s (%p)): Last mod_abs: %d, current: %d, changed: %p, rc: %d, 
next: %p, caller: %p\n",
+             __func__, sfp->name, sfp, sfp->last_mod_abs, val,
+             sfp->mod_abs_changed, err, sfp->next_iface_sfp,
+             __builtin_return_address(0));
+
+       if (err >= 0) {
+               sfp->last_mod_abs = val;
+               mode = cvmx_helper_interface_get_mode(sfp->xiface);
+               cvmx_sfp_validate_module(sfp, mode);
+       } else {
+               debug("%s: mod_abs_changed for %s returned error\n", __func__,
+                     sfp->name);
+       }
+
+       return err < 0 ? err : val;
+}
+
+/**
+ * Reads the EEPROMs of all SFP modules.
+ *
+ * @return 0 for success
+ */
+int cvmx_sfp_read_all_modules(void)
+{
+       struct cvmx_fdt_sfp_info *sfp;
+       int val;
+       bool error = false;
+       int rc;
+
+       for (sfp = sfp_list; sfp; sfp = sfp->next) {
+               if (dm_gpio_is_valid(&sfp->mod_abs)) {
+                       /* Check if module absent */
+                       val = dm_gpio_get_value(&sfp->mod_abs);
+                       sfp->last_mod_abs = val;
+                       if (val)
+                               continue;
+               }
+               rc = cvmx_sfp_read_i2c_eeprom(sfp);
+               if (rc) {
+                       debug("%s: Error reading eeprom from SFP %s\n",
+                             __func__, sfp->name);
+                       error = true;
+               }
+       }
+
+       return error ? -1 : 0;
+}
+
+/**
+ * Registers a function to be called to check mod_abs/mod_pres for a SFP/QSFP
+ * slot.
+ *
+ * @param      sfp             Handle to SFP data structure
+ * @param      check_mod_abs   Function to be called or NULL to remove
+ * @param      mod_abs_data    User-defined data to be passed to check_mod_abs
+ *
+ * @return     0 for success
+ */
+int cvmx_sfp_register_check_mod_abs(struct cvmx_fdt_sfp_info *sfp,
+                                   int (*check_mod_abs)(struct 
cvmx_fdt_sfp_info *sfp,
+                                                        void *data),
+                                   void *mod_abs_data)
+{
+       struct cvmx_fdt_sfp_info *nsfp = sfp; /** For walking list */
+
+       do {
+               if (nsfp->xiface == sfp->xiface && nsfp->index == sfp->index) {
+                       nsfp->check_mod_abs = check_mod_abs;
+                       nsfp->mod_abs_data = mod_abs_data;
+               }
+               nsfp = nsfp->next;
+       } while (nsfp);
+
+       return 0;
+}
+
+/**
+ * Registers a function to be called whenever the mod_abs/mod_pres signal
+ * changes.
+ *
+ * @param      sfp             Handle to SFP data structure
+ * @param      mod_abs_changed Function called whenever mod_abs is changed
+ *                             or NULL to remove.
+ * @param      mod_abs_changed_data    User-defined data passed to
+ *                                     mod_abs_changed
+ *
+ * @return     0 for success
+ *
+ * @NOTE: If multiple SFP slots are linked together, all subsequent slots
+ *       will also be registered for the same handler.
+ */
+int cvmx_sfp_register_mod_abs_changed(struct cvmx_fdt_sfp_info *sfp,
+                                     int (*mod_abs_changed)(struct 
cvmx_fdt_sfp_info *sfp,
+                                                            int val, void 
*data),
+                                     void *mod_abs_changed_data)
+{
+       sfp->mod_abs_changed = mod_abs_changed;
+       sfp->mod_abs_changed_data = mod_abs_changed_data;
+
+       sfp->last_mod_abs = -2; /* undefined */
+
+       return 0;
+}
+
+/**
+ * Function called to check and return the status of the tx_fault pin
+ *
+ * @param      sfp     Handle to SFP information.
+ * @param      data    User-defined data passed to the function
+ *
+ * @return     0 if signal present, 1 if signal absent, -1 on error
+ */
+int cvmx_sfp_check_tx_fault(struct cvmx_fdt_sfp_info *sfp, void *data)
+{
+       int val;
+
+       debug("%s(%s, %p)\n", __func__, sfp->name, data);
+       if (!dm_gpio_is_valid(&sfp->tx_error)) {
+               printf("%s: Error: tx_error not set for %s\n", __func__,
+                      sfp->name);
+               return -1;
+       }
+       val = dm_gpio_get_value(&sfp->tx_error);
+       debug("%s: tx_fault: %d\n", __func__, val);
+
+       return val;
+}
+
+/**
+ * Function called to check and return the status of the rx_los pin
+ *
+ * @param      sfp     Handle to SFP information.
+ * @param      data    User-defined data passed to the function
+ *
+ * @return     0 if signal present, 1 if signal absent, -1 on error
+ */
+int cvmx_sfp_check_rx_los(struct cvmx_fdt_sfp_info *sfp, void *data)
+{
+       int val;
+       int err;
+
+       debug("%s(%s, %p)\n", __func__, sfp->name, data);
+       if (!dm_gpio_is_valid(&sfp->rx_los)) {
+               printf("%s: Error: rx_los not set for %s\n", __func__,
+                      sfp->name);
+               return -1;
+       }
+       val = dm_gpio_get_value(&sfp->rx_los);
+       if (val >= 0 && val != sfp->last_rx_los && sfp->rx_los_changed)
+               err = sfp->rx_los_changed(sfp, val, sfp->rx_los_changed_data);
+       debug("%s: Last rx_los: %d, current: %d, changed: %p, rc: %d\n",
+             __func__, sfp->last_rx_los, val, sfp->rx_los_changed, err);
+       sfp->last_rx_los = val;
+
+       return val;
+}
+
+/**
+ * Registers a function to be called whenever rx_los changes
+ *
+ * @param      sfp             Handle to SFP data structure
+ * @param      rx_los_changed  Function to be called when rx_los changes
+ *                             or NULL to remove the function
+ * @param      rx_los_changed_data     User-defined data passed to
+ *                                     rx_los_changed
+ *
+ * @return     0 for success
+ */
+int cvmx_sfp_register_rx_los_changed(struct cvmx_fdt_sfp_info *sfp,
+                                    int (*rx_los_changed)(struct 
cvmx_fdt_sfp_info *sfp,
+                                                          int val, void *data),
+                                    void *rx_los_changed_data)
+{
+       sfp->rx_los_changed = rx_los_changed;
+       sfp->rx_los_changed_data = rx_los_changed_data;
+       sfp->last_rx_los = -2;
+
+       return 0;
+}
+
+/**
+ * Parses a SFP slot from the device tree
+ *
+ * @param      sfp             SFP handle to store data in
+ * @param      fdt_addr        Address of flat device tree
+ * @param      of_offset       Node in device tree for SFP slot
+ *
+ * @return     0 on success, -1 on error
+ */
+static int cvmx_sfp_parse_sfp(struct cvmx_fdt_sfp_info *sfp, ofnode node)
+{
+       struct ofnode_phandle_args phandle;
+       int err;
+
+       sfp->name = ofnode_get_name(node);
+       sfp->of_offset = ofnode_to_offset(node);
+
+       err = gpio_request_by_name_nodev(node, "tx_disable", 0,
+                                        &sfp->tx_disable, GPIOD_IS_OUT);
+       if (err) {
+               printf("%s: tx_disable not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+       dm_gpio_set_value(&sfp->tx_disable, 0);
+
+       err = gpio_request_by_name_nodev(node, "mod_abs", 0,
+                                        &sfp->mod_abs, GPIOD_IS_IN);
+       if (err) {
+               printf("%s: mod_abs not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+
+       err = gpio_request_by_name_nodev(node, "tx_error", 0,
+                                        &sfp->tx_error, GPIOD_IS_IN);
+       if (err) {
+               printf("%s: tx_error not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+
+       err = gpio_request_by_name_nodev(node, "rx_los", 0,
+                                        &sfp->rx_los, GPIOD_IS_IN);
+       if (err) {
+               printf("%s: rx_los not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+
+       err = ofnode_parse_phandle_with_args(node, "eeprom", NULL, 0, 0,
+                                            &phandle);
+       if (!err) {
+               sfp->i2c_eeprom_addr = ofnode_get_addr(phandle.node);
+               debug("%s: eeprom address: 0x%x\n", __func__,
+                     sfp->i2c_eeprom_addr);
+
+               debug("%s: Getting eeprom i2c bus for %s\n", __func__,
+                     sfp->name);
+               sfp->i2c_bus = 
cvmx_ofnode_get_i2c_bus(ofnode_get_parent(phandle.node));
+       }
+
+       err = ofnode_parse_phandle_with_args(node, "diag", NULL, 0, 0,
+                                            &phandle);
+       if (!err) {
+               sfp->i2c_diag_addr = ofnode_get_addr(phandle.node);
+               if (!sfp->i2c_bus)
+                       sfp->i2c_bus = 
cvmx_ofnode_get_i2c_bus(ofnode_get_parent(phandle.node));
+       }
+
+       sfp->last_mod_abs = -2;
+       sfp->last_rx_los = -2;
+
+       if (!sfp->i2c_bus) {
+               debug("%s(%s): Error: could not get i2c bus from device tree\n",
+                     __func__, sfp->name);
+               err = -1;
+       }
+
+       if (err) {
+               dm_gpio_free(sfp->tx_disable.dev, &sfp->tx_disable);
+               dm_gpio_free(sfp->mod_abs.dev, &sfp->mod_abs);
+               dm_gpio_free(sfp->tx_error.dev, &sfp->tx_error);
+               dm_gpio_free(sfp->rx_los.dev, &sfp->rx_los);
+       } else {
+               sfp->valid = true;
+       }
+
+       return err;
+}
+
+/**
+ * Parses a QSFP slot from the device tree
+ *
+ * @param      sfp             SFP handle to store data in
+ * @param      fdt_addr        Address of flat device tree
+ * @param      of_offset       Node in device tree for SFP slot
+ *
+ * @return     0 on success, -1 on error
+ */
+static int cvmx_sfp_parse_qsfp(struct cvmx_fdt_sfp_info *sfp, ofnode node)
+{
+       struct ofnode_phandle_args phandle;
+       int err;
+
+       sfp->is_qsfp = true;
+       sfp->name = ofnode_get_name(node);
+       sfp->of_offset = ofnode_to_offset(node);
+
+       err = gpio_request_by_name_nodev(node, "lp_mode", 0,
+                                        &sfp->lp_mode, GPIOD_IS_OUT);
+       if (err) {
+               printf("%s: lp_mode not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+
+       err = gpio_request_by_name_nodev(node, "mod_prs", 0,
+                                        &sfp->mod_abs, GPIOD_IS_IN);
+       if (err) {
+               printf("%s: mod_prs not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+
+       err = gpio_request_by_name_nodev(node, "select", 0,
+                                        &sfp->select, GPIOD_IS_IN);
+       if (err) {
+               printf("%s: select not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+
+       err = gpio_request_by_name_nodev(node, "reset", 0,
+                                        &sfp->reset, GPIOD_IS_OUT);
+       if (err) {
+               printf("%s: reset not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+
+       err = gpio_request_by_name_nodev(node, "interrupt", 0,
+                                        &sfp->interrupt, GPIOD_IS_IN);
+       if (err) {
+               printf("%s: interrupt not found in DT!\n", __func__);
+               return -ENODEV;
+       }
+
+       err = ofnode_parse_phandle_with_args(node, "eeprom", NULL, 0, 0,
+                                            &phandle);
+       if (!err) {
+               sfp->i2c_eeprom_addr = ofnode_get_addr(phandle.node);
+               sfp->i2c_bus = 
cvmx_ofnode_get_i2c_bus(ofnode_get_parent(phandle.node));
+       }
+
+       err = ofnode_parse_phandle_with_args(node, "diag", NULL, 0, 0,
+                                            &phandle);
+       if (!err) {
+               sfp->i2c_diag_addr = ofnode_get_addr(phandle.node);
+               if (!sfp->i2c_bus)
+                       sfp->i2c_bus = 
cvmx_ofnode_get_i2c_bus(ofnode_get_parent(phandle.node));
+       }
+
+       sfp->last_mod_abs = -2;
+       sfp->last_rx_los = -2;
+
+       if (!sfp->i2c_bus) {
+               cvmx_printf("%s(%s): Error: could not get i2c bus from device 
tree\n",
+                           __func__, sfp->name);
+               err = -1;
+       }
+
+       if (err) {
+               dm_gpio_free(sfp->lp_mode.dev, &sfp->lp_mode);
+               dm_gpio_free(sfp->mod_abs.dev, &sfp->mod_abs);
+               dm_gpio_free(sfp->select.dev, &sfp->select);
+               dm_gpio_free(sfp->reset.dev, &sfp->reset);
+               dm_gpio_free(sfp->interrupt.dev, &sfp->interrupt);
+       } else {
+               sfp->valid = true;
+       }
+
+       return err;
+}
+
+/**
+ * Parses the device tree for SFP and QSFP slots
+ *
+ * @param      fdt_addr        Address of flat device-tree
+ *
+ * @return     0 for success, -1 on error
+ */
+int cvmx_sfp_parse_device_tree(const void *fdt_addr)
+{
+       struct cvmx_fdt_sfp_info *sfp, *first_sfp = NULL, *last_sfp = NULL;
+       ofnode node;
+       int err = 0;
+       int reg;
+       static bool parsed;
+
+       debug("%s(%p): Parsing...\n", __func__, fdt_addr);
+       if (parsed) {
+               debug("%s(%p): Already parsed\n", __func__, fdt_addr);
+               return 0;
+       }
+
+       ofnode_for_each_compatible_node(node, "ethernet,sfp-slot") {
+               if (!ofnode_valid(node))
+                       continue;
+
+               sfp = cvm_sfp_alloc(sizeof(*sfp));
+               if (!sfp)
+                       return -1;
+
+               err = cvmx_sfp_parse_sfp(sfp, node);
+               if (!err) {
+                       if (!sfp_list)
+                               sfp_list = sfp;
+                       if (last_sfp)
+                               last_sfp->next = sfp;
+                       sfp->prev = last_sfp;
+                       last_sfp = sfp;
+                       debug("%s: parsed %s\n", __func__, sfp->name);
+               } else {
+                       debug("%s: Error parsing SFP at node %s\n",
+                             __func__, ofnode_get_name(node));
+                       return err;
+               }
+       }
+
+       ofnode_for_each_compatible_node(node, "ethernet,qsfp-slot") {
+               if (!ofnode_valid(node))
+                       continue;
+
+               sfp = cvm_sfp_alloc(sizeof(*sfp));
+               if (!sfp)
+                       return -1;
+
+               err = cvmx_sfp_parse_qsfp(sfp, node);
+               if (!err) {
+                       if (!sfp_list)
+                               sfp_list = sfp;
+                       if (last_sfp)
+                               last_sfp->next = sfp;
+                       sfp->prev = last_sfp;
+                       last_sfp = sfp;
+                       debug("%s: parsed %s\n", __func__, sfp->name);
+               } else {
+                       debug("%s: Error parsing QSFP at node %s\n",
+                             __func__, ofnode_get_name(node));
+                       return err;
+               }
+       }
+
+       if (!octeon_has_feature(OCTEON_FEATURE_BGX))
+               return 0;
+
+       err = 0;
+       ofnode_for_each_compatible_node(node, "cavium,octeon-7890-bgx-port") {
+               int sfp_nodes[4];
+               ofnode sfp_ofnodes[4];
+               int num_sfp_nodes;
+               u64 reg_addr;
+               struct cvmx_xiface xi;
+               int xiface, index;
+               cvmx_helper_interface_mode_t mode;
+               int i;
+               int rc;
+
+               if (!ofnode_valid(node))
+                       break;
+
+               num_sfp_nodes = ARRAY_SIZE(sfp_nodes);
+               rc = cvmx_ofnode_lookup_phandles(node, "sfp-slot",
+                                                &num_sfp_nodes, sfp_ofnodes);
+               if (rc != 0 || num_sfp_nodes < 1)
+                       rc = cvmx_ofnode_lookup_phandles(node, "qsfp-slot",
+                                                        &num_sfp_nodes,
+                                                        sfp_ofnodes);
+               /* If no SFP or QSFP slot found, go to next port */
+               if (rc < 0)
+                       continue;
+
+               last_sfp = NULL;
+               for (i = 0; i < num_sfp_nodes; i++) {
+                       sfp = 
cvmx_sfp_find_slot_by_fdt_node(ofnode_to_offset(sfp_ofnodes[i]));
+                       debug("%s: Adding sfp %s (%p) to BGX port\n",
+                             __func__, sfp->name, sfp);
+                       if (last_sfp)
+                               last_sfp->next_iface_sfp = sfp;
+                       else
+                               first_sfp = sfp;
+                       last_sfp = sfp;
+               }
+               if (!first_sfp) {
+                       debug("%s: Error: could not find SFP slot for BGX port 
%s\n",
+                             __func__,
+                             fdt_get_name(fdt_addr, sfp_nodes[0],
+                                          NULL));
+                       err = -1;
+                       break;
+               }
+
+               /* Get the port index */
+               reg = ofnode_get_addr(node);
+               if (reg < 0) {
+                       debug("%s: Error: could not get BGX port reg value\n",
+                             __func__);
+                       err = -1;
+                       break;
+               }
+               index = reg;
+
+               /* Get BGX node and address */
+               reg_addr = ofnode_get_addr(ofnode_get_parent(node));
+               /* Extrace node */
+               xi.node = cvmx_csr_addr_to_node(reg_addr);
+               /* Extract reg address */
+               reg_addr = cvmx_csr_addr_strip_node(reg_addr);
+               if ((reg_addr & 0xFFFFFFFFF0000000) !=
+                   0x00011800E0000000) {
+                       debug("%s: Invalid BGX address 0x%llx\n",
+                             __func__, (unsigned long long)reg_addr);
+                       xi.node = -1;
+                       err = -1;
+                       break;
+               }
+
+               /* Extract interface from address */
+               xi.interface = (reg_addr >> 24) & 0x0F;
+               /* Convert to xiface */
+               xiface = cvmx_helper_node_interface_to_xiface(xi.node,
+                                                             xi.interface);
+               debug("%s: Parsed %d SFP slots for interface 0x%x, index %d\n",
+                     __func__, num_sfp_nodes, xiface, index);
+
+               mode = cvmx_helper_interface_get_mode(xiface);
+               for (sfp = first_sfp; sfp; sfp = sfp->next_iface_sfp) {
+                       sfp->xiface = xiface;
+                       sfp->index = index;
+                       /* Convert to IPD port */
+                       sfp->ipd_port[0] =
+                               cvmx_helper_get_ipd_port(xiface, index);
+                       debug("%s: sfp %s (%p) xi: 0x%x, index: 0x%x, node: %d, 
mode: 0x%x, next: %p\n",
+                             __func__, sfp->name, sfp, sfp->xiface,
+                             sfp->index, xi.node, mode,
+                             sfp->next_iface_sfp);
+                       if (mode == CVMX_HELPER_INTERFACE_MODE_XLAUI ||
+                           mode == CVMX_HELPER_INTERFACE_MODE_40G_KR4)
+                               for (i = 1; i < 4; i++)
+                                       sfp->ipd_port[i] = -1;
+                       else
+                               for (i = 1; i < 4; i++)
+                                       sfp->ipd_port[i] =
+                                               cvmx_helper_get_ipd_port(
+                                                       xiface, i);
+               }
+               cvmx_helper_cfg_set_sfp_info(xiface, index, first_sfp);
+       }
+
+       if (!err) {
+               parsed = true;
+               cvmx_sfp_read_all_modules();
+       }
+
+       return err;
+}
+
+/**
+ * Given an IPD port number find the corresponding SFP or QSFP slot
+ *
+ * @param      ipd_port        IPD port number to search for
+ *
+ * @return     pointer to SFP data structure or NULL if not found
+ */
+struct cvmx_fdt_sfp_info *cvmx_sfp_find_slot_by_port(int ipd_port)
+{
+       struct cvmx_fdt_sfp_info *sfp = sfp_list;
+       int i;
+
+       while (sfp) {
+               for (i = 0; i < 4; i++)
+                       if (sfp->ipd_port[i] == ipd_port)
+                               return sfp;
+               sfp = sfp->next;
+       }
+       return NULL;
+}
+
+/**
+ * Given a fdt node offset find the corresponding SFP or QSFP slot
+ *
+ * @param      of_offset       flat device tree node offset
+ *
+ * @return     pointer to SFP data structure or NULL if not found
+ */
+struct cvmx_fdt_sfp_info *cvmx_sfp_find_slot_by_fdt_node(int of_offset)
+{
+       struct cvmx_fdt_sfp_info *sfp = sfp_list;
+
+       while (sfp) {
+               if (sfp->of_offset == of_offset)
+                       return sfp;
+               sfp = sfp->next;
+       }
+       return NULL;
+}
+
+static bool cvmx_sfp_validate_quad(struct cvmx_fdt_sfp_info *sfp,
+                                  struct cvmx_phy_gpio_leds *leds)
+{
+       bool multi_led = leds && (leds->next);
+       bool error = false;
+       int mod_abs;
+
+       do {
+               /* Skip missing modules */
+               if (dm_gpio_is_valid(&sfp->mod_abs))
+                       mod_abs = dm_gpio_get_value(&sfp->mod_abs);
+               else
+                       mod_abs = 0;
+               if (!mod_abs) {
+                       if (cvmx_sfp_read_i2c_eeprom(sfp)) {
+                               debug("%s: Error reading eeprom for %s\n",
+                                     __func__, sfp->name);
+                       }
+                       if (sfp->sfp_info.rate < CVMX_SFP_RATE_10G) {
+                               cvmx_helper_leds_show_error(leds, true);
+                               error = true;
+                       } else if (sfp->sfp_info.rate >= CVMX_SFP_RATE_10G) {
+                               /* We don't support 10GBase-T modules in
+                                * this mode.
+                                */
+                               switch (sfp->sfp_info.cable_comp) {
+                               case CVMX_SFP_CABLE_10GBASE_T:
+                               case CVMX_SFP_CABLE_10GBASE_T_SR:
+                               case CVMX_SFP_CABLE_5GBASE_T:
+                               case CVMX_SFP_CABLE_2_5GBASE_T:
+                                       cvmx_helper_leds_show_error(leds, true);
+                                       error = true;
+                                       break;
+                               default:
+                                       break;
+                               }
+                       }
+               } else if (multi_led) {
+                       cvmx_helper_leds_show_error(leds, false);
+               }
+
+               if (multi_led && leds->next)
+                       leds = leds->next;
+               sfp = sfp->next_iface_sfp;
+       } while (sfp);
+
+       if (!multi_led)
+               cvmx_helper_leds_show_error(leds, error);
+
+       return error;
+}
+
+/**
+ * Validates if the module is correct for the specified port
+ *
+ * @param[in]  sfp     SFP port to check
+ * @param      xiface  interface
+ * @param      index   port index
+ * @param      speed   link speed, -1 if unknown
+ * @param      mode    interface mode
+ *
+ * @return     true if module is valid, false if invalid
+ * NOTE: This will also toggle the error LED, if present
+ */
+bool cvmx_sfp_validate_module(struct cvmx_fdt_sfp_info *sfp, int mode)
+{
+       const struct cvmx_sfp_mod_info *mod_info = &sfp->sfp_info;
+       int xiface = sfp->xiface;
+       int index = sfp->index;
+       struct cvmx_phy_gpio_leds *leds;
+       bool error = false;
+       bool quad_mode = false;
+
+       debug("%s(%s, 0x%x, 0x%x, 0x%x)\n", __func__, sfp->name, xiface, index,
+             mode);
+       if (!sfp) {
+               debug("%s: Error: sfp is NULL\n", __func__);
+               return false;
+       }
+       /* No module is valid */
+       leds = cvmx_helper_get_port_phy_leds(xiface, index);
+       if (!leds)
+               debug("%s: No leds for 0x%x:0x%x\n", __func__, xiface, index);
+
+       if (mode != CVMX_HELPER_INTERFACE_MODE_XLAUI &&
+           mode != CVMX_HELPER_INTERFACE_MODE_40G_KR4 && !sfp->is_qsfp &&
+           sfp->last_mod_abs && leds) {
+               cvmx_helper_leds_show_error(leds, false);
+               debug("%s: %s: last_mod_abs: %d, no error\n", __func__,
+                     sfp->name, sfp->last_mod_abs);
+               return true;
+       }
+
+       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 ((mod_info->active_cable &&
+                    mod_info->rate != CVMX_SFP_RATE_1G) ||
+                   mod_info->rate < CVMX_SFP_RATE_1G)
+                       error = true;
+               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:
+               if ((mod_info->active_cable &&
+                    mod_info->rate != CVMX_SFP_RATE_10G) ||
+                   mod_info->rate < CVMX_SFP_RATE_10G)
+                       error = true;
+               break;
+       case CVMX_HELPER_INTERFACE_MODE_XLAUI:
+       case CVMX_HELPER_INTERFACE_MODE_40G_KR4:
+               if (!sfp->is_qsfp) {
+                       quad_mode = true;
+                       error = cvmx_sfp_validate_quad(sfp, leds);
+               } else {
+                       if ((mod_info->active_cable &&
+                            mod_info->rate != CVMX_SFP_RATE_40G) ||
+                           mod_info->rate < CVMX_SFP_RATE_25G)
+                               error = true;
+               }
+               break;
+       default:
+               debug("%s: Unsupported interface mode %d on xiface 0x%x\n",
+                     __func__, mode, xiface);
+               return false;
+       }
+       debug("%s: %s: error: %d\n", __func__, sfp->name, error);
+       if (leds && !quad_mode)
+               cvmx_helper_leds_show_error(leds, error);
+
+       return !error;
+}
+
+/**
+ * Prints information about the SFP module
+ *
+ * @param[in]  sfp     sfp data structure
+ */
+void cvmx_sfp_print_info(const struct cvmx_fdt_sfp_info *sfp)
+{
+       const struct cvmx_sfp_mod_info *mi = &sfp->sfp_info;
+       const char *conn_type;
+       const char *mod_type;
+       const char *rate_str;
+       const char *cable_comp;
+
+       if (!sfp) {
+               printf("Invalid SFP (NULL)\n");
+               return;
+       }
+
+       /* Please refer to the SFF-8024 and SFF-8472 documents */
+       switch (mi->conn_type) {
+       case CVMX_SFP_CONN_GBIC:
+               conn_type = "GBIC";
+               break;
+       case CVMX_SFP_CONN_SFP:
+               conn_type = "SFP/SFP+/SFP28";
+               break;
+       case CVMX_SFP_CONN_QSFP:
+               conn_type = "QSFP";
+               break;
+       case CVMX_SFP_CONN_QSFPP:
+               conn_type = "QSFP+";
+               break;
+       case CVMX_SFP_CONN_QSFP28:
+               conn_type = "QSFP28";
+               break;
+       case CVMX_SFP_CONN_MICRO_QSFP:
+               conn_type = "Micro QSFP";
+               break;
+       case CVMX_SFP_CONN_QSFP_DD:
+               conn_type = "QSFP-DD";
+               break;
+       case CVMX_SFP_CONN_SFP_DD:
+               conn_type = "SFP-DD";
+               break;
+       default:
+               conn_type = "Unknown";
+               break;
+       }
+
+       switch (mi->mod_type) {
+       case CVMX_SFP_MOD_UNKNOWN:
+               mod_type = "Unknown";
+               break;
+       case CVMX_SFP_MOD_OPTICAL_LC:
+               mod_type = "Optical LC";
+               break;
+       case CVMX_SFP_MOD_MULTIPLE_OPTICAL:
+               mod_type = "Multiple Optical";
+               break;
+       case CVMX_SFP_MOD_OPTICAL_PIGTAIL:
+               mod_type = "Optical Pigtail";
+               break;
+       case CVMX_SFP_MOD_COPPER_PIGTAIL:
+               mod_type = "Copper Pigtail";
+               break;
+       case CVMX_SFP_MOD_RJ45:
+               mod_type = "Copper RJ45";
+               break;
+       case CVMX_SFP_MOD_NO_SEP_CONN:
+               mod_type = "No Separable Connector";
+               break;
+       case CVMX_SFP_MOD_MXC_2X16:
+               mod_type = "MXC 2X16";
+               break;
+       case CVMX_SFP_MOD_CS_OPTICAL:
+               mod_type = "CS Optical";
+               break;
+       case CVMX_SFP_MOD_MINI_CS_OPTICAL:
+               mod_type = "Mini CS Optical";
+               break;
+       case CVMX_SFP_MOD_OTHER:
+               mod_type = "Unknown/Other";
+               break;
+       default:
+               mod_type = "Undefined";
+               break;
+       }
+
+       switch (mi->cable_comp) {
+       case CVMX_SFP_CABLE_UNSPEC:
+               cable_comp = "";
+               break;
+       case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_HIGH_BER:
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       cable_comp = " 25GAUI-C2M AOC HIGH BER";
+               else
+                       cable_comp = " 100G AOC HIGH BER";
+               break;
+       case CVMX_SFP_CABLE_100G_SR4_25G_SR:
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       cable_comp = " 25GBASE-SR";
+               else
+                       cable_comp = " 100GBASE-SR4";
+               break;
+       case CVMX_SFP_CABLE_100G_LR4_25G_LR:
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       cable_comp = " 25GBASE-LR";
+               else
+                       cable_comp = " 100GBASE-LR4";
+               break;
+       case CVMX_SFP_CABLE_100G_ER4_25G_ER:
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       cable_comp = " 25GBASE-ER";
+               else
+                       cable_comp = " 100GBASE-ER4";
+               break;
+       case CVMX_SFP_CABLE_100G_SR10:
+               cable_comp = " 100GBASE-SR10";
+               break;
+       case CVMX_SFP_CABLE_100G_CWDM4_MSA:
+               cable_comp = " 100G CWDM4";
+               break;
+       case CVMX_SFP_CABLE_100G_PSM4:
+               cable_comp = " 100G PSM4 Parallel SMF";
+               break;
+       case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_HIGH_BER:
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       cable_comp = " 25GAUI C2M ACC";
+               else
+                       cable_comp = " 100G ACC";
+               break;
+       case CVMX_SFP_CABLE_100G_CWDM4:
+               cable_comp = " 100G CWDM4 MSA";
+               break;
+       case CVMX_SFP_CABLE_100G_CR4_25G_CR_CA_L:
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       cable_comp = " 25GBASE-CR CA-L";
+               else
+                       cable_comp = " 100GBASE-CR4";
+               break;
+       case CVMX_SFP_CABLE_25G_CR_CA_S:
+               cable_comp = " 25GBase-CR CA-S";
+               break;
+       case CVMX_SFP_CABLE_25G_CR_CA_N:
+               cable_comp = " 25GBase-CR CA-N";
+               break;
+       case CVMX_SFP_CABLE_40G_ER4:
+               cable_comp = " 40GBASE-ER4";
+               break;
+       case CVMX_SFP_CABLE_4X10G_SR:
+               cable_comp = " 4 X 10GBASE-T SR";
+               break;
+       case CVMX_SFP_CABLE_40G_PSM4:
+               cable_comp = " 40G PSM4 Parallel SMF";
+               break;
+       case CVMX_SFP_CABLE_G959_1_P1I1_2D1:
+               cable_comp = " G959.1 profile P1I1-2D1";
+               break;
+       case CVMX_SFP_CABLE_G959_1_P1S1_2D2:
+               cable_comp = " G959.1 profile P1S1-2D2";
+               break;
+       case CVMX_SFP_CABLE_G959_1_P1L1_2D2:
+               cable_comp = " G959.1 profile P1L1-2D2";
+               break;
+       case CVMX_SFP_CABLE_10GBASE_T:
+               cable_comp = " 10GBase-T";
+               break;
+       case CVMX_SFP_CABLE_100G_CLR4:
+               cable_comp = " 10G CLR4";
+               break;
+       case CVMX_SFP_CABLE_100G_25GAUI_C2M_AOC_LOW_BER:
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       cable_comp = " 25GAUI C2M AOC LOW BER";
+               else
+                       cable_comp = " 100G AOC LOW BER";
+               break;
+       case CVMX_SFP_CABLE_100G_25GAUI_C2M_ACC_LOW_BER:
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       cable_comp = " 25GAUI C2M ACC LOW BER";
+               else
+                       cable_comp = " 100G ACC LOW BER";
+               break;
+       case CVMX_SFP_CABLE_100G_2_LAMBDA_DWDM:
+               cable_comp = " 100GE-DWDM2";
+               break;
+       case CVMX_SFP_CABLE_100G_1550NM_WDM:
+               cable_comp = " 100GE 1550nm WDM";
+               break;
+       case CVMX_SFP_CABLE_10GBASE_T_SR:
+               cable_comp = " 10GBase-T short reach (30 meters)";
+               break;
+       case CVMX_SFP_CABLE_5GBASE_T:
+               cable_comp = " 5GBase-T";
+               break;
+       case CVMX_SFP_CABLE_2_5GBASE_T:
+               cable_comp = " 2.5GBase-T";
+               break;
+       case CVMX_SFP_CABLE_40G_SWDM4:
+               cable_comp = " 40G SWDM4";
+               break;
+       case CVMX_SFP_CABLE_100G_SWDM4:
+               cable_comp = " 100G SWDM4";
+               break;
+       case CVMX_SFP_CABLE_100G_PAM4_BIDI:
+               cable_comp = " 100G SWDM4";
+               break;
+       case CVMX_SFP_CABLE_100G_4WDM_10_FEC_HOST:
+               cable_comp = " 4WDM-10 MSA (10KM CWDM4) FEC in host";
+               break;
+       case CVMX_SFP_CABLE_100G_4WDM_20_FEC_HOST:
+               cable_comp = " 4WDM-20 MSA (20KM 100GBASE-LR4) FEC in host";
+               break;
+       case CVMX_SFP_CABLE_100G_4WDM_40_FEC_HOST:
+               cable_comp = " 4WDM-40 MSA (40KM APD receiver) FEC in host";
+               break;
+       case CVMX_SFP_CABLE_100GBASE_DR_CAUI4_NO_FEC:
+               cable_comp = " 100GBASE-DR with CAUI-4, no FEC";
+               break;
+       case CVMX_SFP_CABLE_100G_FR_CAUI4_NO_FEC:
+               cable_comp = " 100G-FR with CAUI-4, no FEC";
+               break;
+       case CVMX_SFP_CABLE_100G_LR_CAUI4_NO_FEC:
+               cable_comp = " 100G-LR with CAUI-4, no FEC";
+               break;
+       case CVMX_SFP_CABLE_ACTIVE_COPPER_50_100_200GAUI_LOW_BER:
+               cable_comp = " Active Copper Cable with 50GAUI, 100GAUI-2 or 
200GAUI-4 C2M, BER 10^(-6) or lower";
+               break;
+       case CVMX_SFP_CABLE_ACTIVE_OPTICAL_50_100_200GAUI_LOW_BER:
+               cable_comp = " Active Optical Cable with 50GAUI, 100GAUI-2 or 
200GAUI-4 C2M, BER 10^(-6) or lower";
+               break;
+       case CVMX_SFP_CABLE_ACTIVE_COPPER_50_100_200GAUI_HI_BER:
+               cable_comp = " Active Copper Cable with 50GAUI, 100GAUI-2 or 
200GAUI-4 AUI, BER 2.6 * 10^(-4) or lower";
+               break;
+       case CVMX_SFP_CABLE_ACTIVE_OPTICAL_50_100_200GAUI_HI_BER:
+               cable_comp = " Active Optical Cable with 50GAUI, 100GAUI-2 or 
200GAUI-4 AUI, BER 2.6 10^(-4) or lower";
+               break;
+       case CVMX_SFP_CABLE_50_100_200G_CR:
+               cable_comp = " 50GBASE-CR, 100G-CR2 or 200G-CR4";
+               break;
+       case CVMX_SFP_CABLE_50_100_200G_SR:
+               cable_comp = " 50GBASE-SR, 100G-SR2 or 200G-SR4";
+               break;
+       case CVMX_SFP_CABLE_50GBASE_FR_200GBASE_DR4:
+               cable_comp = " 50GBASE-FR or 200GBASE-DR4";
+               break;
+       case CVMX_SFP_CABLE_200GBASE_FR4:
+               cable_comp = " 200GBASE-FR4";
+               break;
+       case CVMX_SFP_CABLE_200G_1550NM_PSM4:
+               cable_comp = " 200G 1550nm PSM4";
+               break;
+       case CVMX_SFP_CABLE_50GBASE_LR:
+               cable_comp = "50GBASE-LR";
+               break;
+       case CVMX_SFP_CABLE_200GBASE_LR4:
+               cable_comp = " 200GBASE-LR4";
+               break;
+       case CVMX_SFP_CABLE_64GFC_EA:
+               cable_comp = " 64GFC EA";
+               break;
+       case CVMX_SFP_CABLE_64GFC_SW:
+               cable_comp = " 64GFC SW";
+               break;
+       case CVMX_SFP_CABLE_64GFC_LW:
+               cable_comp = " 64GFC LW";
+               break;
+       case CVMX_SFP_CABLE_128GFC_EA:
+               cable_comp = " 64GFC EA";
+               break;
+       case CVMX_SFP_CABLE_128GFC_SW:
+               cable_comp = " 64GFC SW";
+               break;
+       case CVMX_SFP_CABLE_128GFC_LW:
+               cable_comp = " 64GFC LW";
+               break;
+       default:
+               cable_comp = " Unknown or Unsupported";
+               break;
+       }
+
+       switch (mi->rate) {
+       case CVMX_SFP_RATE_UNKNOWN:
+       default:
+               rate_str = "Unknown";
+               break;
+       case CVMX_SFP_RATE_1G:
+               rate_str = "1G";
+               break;
+       case CVMX_SFP_RATE_10G:
+               switch (mi->cable_comp) {
+               case CVMX_SFP_CABLE_5GBASE_T:
+                       rate_str = "5G";
+                       break;
+               case CVMX_SFP_CABLE_2_5GBASE_T:
+                       rate_str = "2.5G";
+                       break;
+               default:
+                       rate_str = "10G";
+                       break;
+               }
+               break;
+       case CVMX_SFP_RATE_25G:
+               rate_str = "25G";
+               break;
+       case CVMX_SFP_RATE_40G:
+               rate_str = "40G";
+               break;
+       case CVMX_SFP_RATE_100G:
+               rate_str = "100G";
+               break;
+       }
+
+       cvmx_printf("%s %s%s module detected\n", mod_type, conn_type,
+                   cable_comp);
+       debug("Vendor: %s\n", mi->vendor_name);
+       debug("Vendor OUI: %02X:%02X:%02X\n", mi->vendor_oui[0],
+             mi->vendor_oui[1], mi->vendor_oui[2]);
+       debug("Vendor part number: %s Revision: %s\n", mi->vendor_pn,
+             mi->vendor_rev);
+       debug("Manufacturing date code: %s\n", mi->date_code);
+       debug("Rate: %s\n", rate_str);
+       if (mi->copper_cable) {
+               debug("Copper cable type: %s\n",
+                     mi->active_cable ? "Active" : "Passive");
+               debug("Cable length: %u meters\n", mi->max_copper_cable_len);
+               if (mi->rate == CVMX_SFP_RATE_25G)
+                       debug("Forward error correction is%s %s\n",
+                             mi->fec_required ? "" : " not",
+                             (mi->max_copper_cable_len >= 5 ||
+                              !mi->fec_required) ?
+                                     "required" :
+                                           "suggested");
+       } else {
+               bool more = false;
+
+               debug("Supported optical types: ");
+               if (mi->eth_comp & CVMX_SFP_CABLE_10GBASE_ER) {
+                       more = !!(mi->eth_comp & ~CVMX_SFP_CABLE_10GBASE_ER);
+                       debug("10GBase-ER%s", more ? ", " : "");
+               }
+               if (mi->eth_comp & CVMX_SFP_CABLE_10GBASE_LRM) {
+                       more = !!(mi->eth_comp & ~(CVMX_SFP_CABLE_10GBASE_ER |
+                                                  CVMX_SFP_CABLE_10GBASE_LRM));
+                       debug("10GBase-LRM%s", more ? ", " : "");
+               }
+               if (mi->eth_comp & CVMX_SFP_CABLE_10GBASE_LR) {
+                       more = !!(mi->eth_comp & ~(CVMX_SFP_CABLE_10GBASE_ER |
+                                                  CVMX_SFP_CABLE_10GBASE_LRM |
+                                                  CVMX_SFP_CABLE_10GBASE_LR));
+                       debug("10GBase-LR%s", more ? ", " : "");
+               }
+               if (mi->eth_comp & CVMX_SFP_CABLE_10GBASE_SR)
+                       debug("10GBase-SR");
+               debug("\nMaximum single mode cable length: %d meters\n",
+                     mi->max_single_mode_cable_length);
+               debug("Maximum 62.5um OM1 cable length:  %d meters\n",
+                     mi->max_62_5um_om1_cable_length);
+               debug("Maximum 50um OM2 cable length:    %d meters\n",
+                     mi->max_50um_om2_cable_length);
+               debug("Maximum 50um OM3 cable length:    %d meters\n",
+                     mi->max_50um_om3_cable_length);
+               debug("Maximum 50um OM4 cable length:    %d meters\n",
+                     mi->max_50um_om4_cable_length);
+               debug("Laser is%s cooled\n", mi->cooled_laser ? "" : " not");
+       }
+       debug("Limiting is %sabled\n", mi->limiting ? "en" : "dis");
+       debug("Power level: %d\n", mi->power_level);
+       debug("RX LoS is%s implemented and is%s inverted\n",
+             mi->los_implemented ? "" : " not",
+             mi->los_inverted ? "" : " not");
+       debug("TX fault is%s implemented\n",
+             mi->tx_fault_implemented ? "" : " not");
+       debug("TX disable is%s implemented\n",
+             mi->tx_disable_implemented ? "" : " not");
+       debug("Rate select is%s implemented\n",
+             mi->rate_select_implemented ? "" : " not");
+       debug("RX output is %s\n",
+             mi->linear_rx_output ? "linear" : "limiting");
+       debug("Diagnostic monitoring is%s implemented\n",
+             mi->diag_monitoring ? "" : " not");
+       if (mi->diag_monitoring) {
+               const char *diag_rev;
+
+               switch (mi->diag_rev) {
+               case CVMX_SFP_SFF_8472_NO_DIAG:
+                       diag_rev = "none";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_9_3:
+                       diag_rev = "9.3";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_9_5:
+                       diag_rev = "9.5";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_10_2:
+                       diag_rev = "10.2";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_10_4:
+                       diag_rev = "10.4";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_11_0:
+                       diag_rev = "11.0";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_11_3:
+                       diag_rev = "11.3";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_11_4:
+                       diag_rev = "11.4";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_12_0:
+                       diag_rev = "12.0";
+                       break;
+               case CVMX_SFP_SFF_8472_REV_UNALLOCATED:
+                       diag_rev = "Unallocated";
+                       break;
+               default:
+                       diag_rev = "Unknown";
+                       break;
+               }
+               debug("Diagnostics revision: %s\n", diag_rev);
+               debug("Diagnostic address change is%s required\n",
+                     mi->diag_addr_change_required ? "" : " not");
+               debug("Diagnostics are%s internally calibrated\n",
+                     mi->diag_internally_calibrated ? "" : " not");
+               debug("Diagnostics are%s externally calibrated\n",
+                     mi->diag_externally_calibrated ? "" : " not");
+               debug("Receive power measurement type: %s\n",
+                     mi->diag_rx_power_averaged ? "Averaged" : "OMA");
+       }
+}
-- 
2.35.1

Reply via email to