QSFP+ detection support in U-boot
authorFlorin Chiculita <florinlaurentiu.chiculita@nxp.com>
Tue, 12 Feb 2019 13:08:50 +0000 (15:08 +0200)
committerPriyanka Jain <priyanka.jain@nxp.com>
Tue, 20 Apr 2021 10:19:25 +0000 (15:49 +0530)
Add QSFP transceiver detection at boot time. Transceiver model,
serial number and part number are displayed if module is detected.
An environment variable used by the Cortina 40G PHY is set, forcing
the initialization based on cable type.

Signed-off-by: Florin Chiculita <florinlaurentiu.chiculita@nxp.com>
Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com> # rebased over v2021.04
board/freescale/common/Makefile
board/freescale/common/qsfp_eeprom.c [new file with mode: 0644]
board/freescale/common/qsfp_eeprom.h [new file with mode: 0644]
board/freescale/lx2160a/Kconfig
board/freescale/lx2160a/eth_lx2160ardb.c
board/freescale/lx2160a/lx2160a.c
include/configs/lx2160a_common.h

index 114b7ba..68dc314 100644 (file)
@@ -63,6 +63,7 @@ obj-$(CONFIG_POWER_MC34VR500) += mc34vr500.o
 obj-$(CONFIG_LS102XA_STREAM_ID)        += ls102xa_stream_id.o
 
 obj-$(CONFIG_EMC2305)              += emc2305.o
+obj-$(CONFIG_QSFP_EEPROM)      += qsfp_eeprom.o
 
 # deal with common files for P-series corenet based devices
 obj-$(CONFIG_TARGET_P2041RDB)  += p_corenet/
diff --git a/board/freescale/common/qsfp_eeprom.c b/board/freescale/common/qsfp_eeprom.c
new file mode 100644 (file)
index 0000000..4e0f52c
--- /dev/null
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019-2021 NXP
+ */
+#include <common.h>
+#include <command.h>
+#include <i2c.h>
+#include <linux/ctype.h>
+
+#define DEV_ID_QSFP            0x0c
+#define DEV_ID_QSFP_PLUS       0x0d
+
+static struct __attribute__ ((__packed__)) qsfp_eeprom_map {
+       struct __attribute__ ((__packed__)) qsfp_low_mem {
+       /*      field                   byte address */
+       u8      identifier;             /* 0 */
+       u16     status;                 /* 1-2 */
+       u8      reserved[124];          /* 3-126 */
+       u8      page_select;            /* 127 */
+       } base;
+
+       struct __attribute__ ((__packed__)) qsfp_page00_mem {
+       /*      field                   byte address */
+       u8      identifier;             /* 128 */
+       u8      ext_identifier;         /* 129 */
+       u8      connector;              /* 130 */
+       u8      compat[8];              /* 131-138 */
+       u8      reserved[3];            /* 139-142 */
+       u8      length_fiber[4];
+       u8      length_copper;          /* 146 */
+       u8      tech;                   /* 147 */
+       u8      vendor_name[16];        /* 148-163 */
+       u8      reserved2;              /* 164 */
+       u8      oui[3];                 /* 165-167 */
+       u8      pn[16];                 /* 168-183 */
+       u8      rev[2];                 /* 184-185 */
+       u8      reserved3[10];          /* 186-195 */
+       u8      serial[16];             /* 196-211 */
+       u8      date[8];                /* 212-219 */
+       u8      reserved4[35];          /* 220-255 */
+       } page0;
+} qsfp;
+
+unsigned char get_qsfp_compat0(void)
+{
+       int ret;
+       char vendor[20] = {0};
+       char serial[20] = {0};
+       char pname[20] = {0};
+       char mfgdt[20] = {0};
+#ifdef CONFIG_DM_I2C
+       struct udevice *dev;
+#endif
+
+       memset(&qsfp, 0, sizeof(qsfp));
+#ifndef CONFIG_DM_I2C
+       ret = i2c_read(I2C_SFP_EEPROM_ADDR,
+                      0,
+                      I2C_SFP_EEPROM_ADDR_LEN,
+                      (void *)&qsfp,
+                      sizeof(qsfp));
+#else
+       ret = i2c_get_chip_for_busnum(0, I2C_SFP_EEPROM_ADDR, 1, &dev);
+       if (!ret)
+               ret = dm_i2c_read(dev, 0, (void *)&qsfp, sizeof(qsfp));
+#endif
+
+       if (ret != 0) {
+               debug("\nQSFP: no module detected\n");
+               return 0;
+       }
+       /* check if QSFP type */
+       if (qsfp.base.identifier != DEV_ID_QSFP_PLUS) {
+               debug("\nQSFP: unrecognized module\n");
+               return 0;
+       }
+
+       /* copy fields and trim the whitespaces and dump on screen */
+       snprintf(vendor, sizeof(vendor), "%.16s", qsfp.page0.vendor_name);
+       snprintf(serial, sizeof(serial), "%.16s", qsfp.page0.serial);
+       snprintf(pname, sizeof(pname), "%.16s", qsfp.page0.pn);
+       snprintf(mfgdt, sizeof(mfgdt), "%.2s/%.2s/%.2s",
+                &qsfp.page0.date[0], &qsfp.page0.date[2], &qsfp.page0.date[4]);
+
+       printf("QSFP: detected %s %s s/n: %s mfgdt: %s\n",
+              strim(vendor), strim(pname), strim(serial), strim(mfgdt));
+
+       /* return ethernet compatibility code*/
+       return qsfp.page0.compat[0];
+}
diff --git a/board/freescale/common/qsfp_eeprom.h b/board/freescale/common/qsfp_eeprom.h
new file mode 100644 (file)
index 0000000..4cbbcb3
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2019-2021 NXP
+ */
+
+#ifndef __QSFP_EEPROM_H_
+#define __QSFP_EEPROM_H_
+/*
+ * QSFP eeprom reader external API interface.
+ */
+
+/* return the ethernet compatibility field 0 */
+unsigned char get_qsfp_compat0(void);
+#endif  /* __QSFP_EEPROM_H_ */
index 7556f7d..ce2ef5d 100644 (file)
@@ -12,6 +12,15 @@ config SYS_SOC
 config SYS_CONFIG_NAME
        default "lx2160ardb"
 
+config QSFP_EEPROM
+        bool "Support for reading QSFP+ transceiver eeprom"
+        default y if PHY_CORTINA
+        help
+         This option enables the functionality for reading
+         QSFP+ cable eeprom. It can be used when PHYs are
+         requiring different initialization based on cable
+         type.
+
 source "board/freescale/common/Kconfig"
 endif
 
index 15cbc58..6e7b7c4 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright 2018, 2020 NXP
+ * Copyright 2018-2021 NXP
  *
  */
 
 #include <fsl-mc/fsl_mc.h>
 #include <fsl-mc/ldpaa_wriop.h>
 #include "lx2160a.h"
+#include "../common/qsfp_eeprom.h"
 
 DECLARE_GLOBAL_DATA_PTR;
 
+#if defined(CONFIG_QSFP_EEPROM) && defined(CONFIG_PHY_CORTINA)
+#define CS4223_CONFIG_ENV      "cs4223_autoconfig"
+#define CS4223_CONFIG_CR4      "copper"
+#define CS4223_CONFIG_SR4      "optical"
+
+enum qsfp_compat_codes {
+       QSFP_COMPAT_XLPPI = 0x01,
+       QSFP_COMPAT_LR4 = 0x02,
+       QSFP_COMPAT_SR4 = 0x04,
+       QSFP_COMPAT_CR4 = 0x08,
+};
+#endif /* CONFIG_QSFP_EEPROM && CONFIG_PHY_CORTINA */
+
 static bool get_inphi_phy_id(struct mii_dev *bus, int addr, int devad)
 {
        int phy_reg;
@@ -50,6 +64,9 @@ int board_eth_init(struct bd_info *bis)
        struct mii_dev *dev;
        struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
        u32 srds_s1;
+#if defined(CONFIG_QSFP_EEPROM) && defined(CONFIG_PHY_CORTINA)
+       u8 qsfp_compat_code;
+#endif
 
        srds_s1 = in_le32(&gur->rcwsr[28]) &
                                FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_MASK;
@@ -154,6 +171,26 @@ int board_eth_init(struct bd_info *bis)
        }
 
 next:
+#if defined(CONFIG_QSFP_EEPROM) && defined(CONFIG_PHY_CORTINA)
+       /* read qsfp+ eeprom & update environment for cs4223 init */
+       select_i2c_ch_pca9547(I2C_MUX_CH_SEC);
+       select_i2c_ch_pca9547_sec(I2C_MUX_CH_QSFP);
+       qsfp_compat_code = get_qsfp_compat0();
+       switch (qsfp_compat_code) {
+       case QSFP_COMPAT_CR4:
+               env_set(CS4223_CONFIG_ENV, CS4223_CONFIG_CR4);
+               break;
+       case QSFP_COMPAT_XLPPI:
+       case QSFP_COMPAT_SR4:
+               env_set(CS4223_CONFIG_ENV, CS4223_CONFIG_SR4);
+               break;
+       default:
+               /* do nothing if detection fails or not supported*/
+               break;
+       }
+       select_i2c_ch_pca9547(I2C_MUX_CH_DEFAULT);
+#endif /* CONFIG_QSFP_EEPROM & CONFIG_PHY_CORTINA */
+
        cpu_eth_init(bis);
 #endif /* CONFIG_FSL_MC_ENET */
 
index 8f75b48..4cddfcc 100644 (file)
@@ -100,6 +100,27 @@ int select_i2c_ch_pca9547(u8 ch)
        return 0;
 }
 
+int select_i2c_ch_pca9547_sec(u8 ch)
+{
+       int ret;
+
+#ifndef CONFIG_DM_I2C
+       ret = i2c_write(I2C_MUX_PCA_ADDR_SEC, 0, 1, &ch, 1);
+#else
+       struct udevice *dev;
+
+       ret = i2c_get_chip_for_busnum(0, I2C_MUX_PCA_ADDR_SEC, 1, &dev);
+       if (!ret)
+               ret = dm_i2c_write(dev, 0, &ch, 1);
+#endif
+       if (ret) {
+               puts("PCA: failed to select proper channel\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static void uart_get_clock(void)
 {
        serial0.clock = get_serial_clock();
index 9f2b899..959adfc 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * Copyright 2018-2020 NXP
+ * Copyright 2018-2021 NXP
  */
 
 #ifndef __LX2_COMMON_H
 
 /* I2C bus multiplexer */
 #define I2C_MUX_PCA_ADDR_PRI           0x77 /* Primary Mux*/
+#define I2C_MUX_PCA_ADDR_SEC           0x75 /* Secondary Mux*/
 #define I2C_MUX_CH_DEFAULT             0x8
+#define I2C_MUX_CH_SEC                 0xF
+
+/* QSFP+/SFP+ I2C MUX related */
+#define I2C_MUX_CH_QSFP                        0x8
+#define I2C_MUX_CH_SFP1                        0xC
+#define I2C_MUX_CH_SFP2                        0xD
 
 /* RTC */
 #define RTC
 #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS      3
 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS  5
 
+/* QSFP/SFP module EEPROMs */
+#define I2C_SFP_EEPROM_ADDR    0x50
+#define I2C_SFP_EEPROM_ADDR_LEN        1
+
 /* Qixis */
 #define CONFIG_FSL_QIXIS
 #define CONFIG_QIXIS_I2C_ACCESS
 #ifndef __ASSEMBLY__
 unsigned long get_board_sys_clk(void);
 unsigned long get_board_ddr_clk(void);
+int select_i2c_ch_pca9547(unsigned char ch);
+int select_i2c_ch_pca9547_sec(unsigned char ch);
 #endif
 
 #define CONFIG_SYS_CLK_FREQ            get_board_sys_clk()