int bdk_if_phy_marvell_setup(bdk_node_t node, int qlm, int mdio_bus, int phy_addr) { BDK_TRACE(PHY,"In %s\n",__FUNCTION__); /* Check if the PHY is marvell PHY we expect */ int phy_status = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID1); if (phy_status != 0x0141) return 0; /* Check that the GSER mode is SGMII */ /* Switch the marvell PHY to the correct mode */ bdk_qlm_modes_t qlm_mode = bdk_qlm_get_mode(node, qlm); BDK_TRACE(PHY,"%s: QLM:%d QLM_MODE:%d\n",__FUNCTION__, qlm, qlm_mode); if ((qlm_mode != BDK_QLM_MODE_SGMII_1X1) && (qlm_mode != BDK_QLM_MODE_SGMII_2X1)) return 0; BDK_TRACE(PHY,"%s: Detected Marvell Phy in SGMII mode\n", __FUNCTION__); for (int port = 0; port < 2; port++) { setup_marvell_phy(node, mdio_bus, phy_addr + port); } return 0; }
/** * Do all the DRAM Margin tests * * @param node Node to test * * @return Success or Fail */ void bdk_dram_margin(int node) { BDK_TRACE(DRAM, "N%d: Starting DRAM margining\n", node); libdram_margin(node); BDK_TRACE(DRAM, "N%d: Finished DRAM margining.\n", node); return; }
/** * Determine the DLM/QLM mode based on a QSFP/QSFP+ connected to * the port. This code is sloppy about finding the BGX PHY for * the DLM/QLM because not all lanes may be used. * * @param node Node to determine mode for * @param qlm DLM/QLM the SFP/SFP+ is connected to * * @return QLM mode or -1 on failure */ static int init_qsfp(int node, int qlm) { int mode = BDK_QLM_MODE_XLAUI_1X4; /* Default to XLAUI if detection fails */ int bgx = -1; int bgx_lane_mask = 0; find_bgx(node, qlm, &bgx, &bgx_lane_mask); if (bgx == -1) return mode; BDK_TRACE(INIT, "N%d.QLM%d: Checking for QSFP/QSFP+\n", node, qlm); int index = 0; /* Lookup the PHY address for this BGX port */ int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, node, bgx, index); /* QSFP/QSFP+ are connected with TWSI, so only check ports with PHYs connected with TWSI */ if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_TWSI) return mode; /* For TWSI: Bits[31:24]: Node ID, 0xff for device node Bits[23:16]: TWSI internal address width in bytes (0-2) Bits[15:12]: 2=TWSI Bits[11:8]: TWSI bus number Bits[7:0]: TWSI address */ int n = (phy_addr >> 24) & 0xff; int twsi_ia_width = (phy_addr >> 16) & 0xff; int twsi_bus = (phy_addr >> 8) & 0xf; int twsi_addr = 0x50; /* From SFP spec */ if (n == 0xff) n = node; /* Byte 0: Identifier, should be 0x03 for SFP/SFP+ 0x03 = SFP of SFP+ 0x0c = QSFP 0x0d = QSFP+ */ int64_t eeprom_00 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 0, 1, twsi_ia_width); switch (eeprom_00) { case 0x03: BDK_TRACE(INIT, "N%d.QLM%d: BGX%d QSFP/QSFP+ contains a SFP+\n", node, qlm, bgx); mode = init_sfp(node, qlm); break; case 0x0c: case 0x0d: BDK_TRACE(INIT, "N%d.QLM%d: BGX%d Found a QSFP/QSFP+, assuming 40G\n", node, qlm, bgx); mode = BDK_QLM_MODE_XLAUI_1X4; break; default: BDK_TRACE(INIT, "N%d.QLM%d: BGX%d QSFP/QSFP+ not detected\n", node, qlm, bgx); break; } return mode; }
static void boot_init_qlm_clk(void) { /* Setup reference clocks */ for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++) { if (!bdk_numa_exists(n)) continue; int num_qlms = bdk_qlm_get_num(n); BDK_TRACE(INIT, "Initializing QLM clocks on Node %d\n", n); for (int qlm = 0; qlm < num_qlms; qlm++) { bdk_qlm_clock_t clk = bdk_config_get_int(BDK_CONFIG_QLM_CLK, n, qlm); if (BDK_QLM_CLK_LAST == clk) /* no entry */ continue; if (clk > BDK_QLM_CLK_LAST) { bdk_warn("Invalid clock source %d for QLM%d on node %d. Not configuring.\n", clk, qlm, n); continue; } if (0 != bdk_qlm_set_clock(n, qlm, clk)) { bdk_error("Error setting clock source %d for QLM%d on node %d. Ignoring.\n", clk, qlm, n); } } } }
/** * Lookup a DRAM configuration by name and initialize DRAM using it * * @param node Node to configure * @param ddr_clock_override * If non zero, override the DRAM frequency specified * in the config with this value * * @return Amount of DRAM in MB, or negative on failure */ int bdk_dram_config(int node, int ddr_clock_override) { const dram_config_t *config = libdram_config_load(node); if (!config) { printf("N%d: No DRAM config specified, skipping DRAM init\n", node); return 0; } BDK_TRACE(DRAM, "N%d: Starting DRAM init (config=%p, ddr_clock_override=%d)\n", node, config, ddr_clock_override); int mbytes = libdram_config(node, config, ddr_clock_override); BDK_TRACE(DRAM, "N%d: DRAM init returned %d\n", node, mbytes); if (mbytes <= 0) { printf("ERROR: DDR initialization failed\n"); return -1; } return mbytes; }
/** * Setup marvell PHYs * This function sets up one port in a marvell 88E1512 in SGMII mode */ static void setup_marvell_phy(bdk_node_t node, int mdio_bus, int mdio_addr) { int phy_status = 0; BDK_TRACE(PHY, "%s In SGMII mode for Marvell PHY 88E1512\n", __FUNCTION__); /* Switch to Page 18 */ phy_status = bdk_mdio_write(node, mdio_bus, mdio_addr, 22, 18); if (phy_status < 0) return; phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 22); if (phy_status < 0) return; /* Change the Phy System mode from RGMII(default hw reset mode) to SGMII */ phy_status = bdk_mdio_write(node, mdio_bus, mdio_addr, 20, 1); if (phy_status < 0) return; /* Requires a Software reset */ phy_status = bdk_mdio_write(node, mdio_bus, mdio_addr, 20, 0x8001); if (phy_status < 0) return; phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 20); if (phy_status < 0) return; /* Change the Page back to 0 */ phy_status = bdk_mdio_write(node, mdio_bus, mdio_addr, 22, 0); if (phy_status < 0) return; phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 22); if (phy_status < 0) return; phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 17); if (phy_status < 0) return; }
/** * Given a node and DLM/QLM, return the possible BGX lanes connected to it. This * is needed to determine which PHY address to use for SFP/SFP+ detection. * * @param node Node the DLM/QLM is on * @param qlm DLM/QLM to find the BGX for * @param bgx Output: The BGX instance number, or -1 on failure * @param bgx_lane_mask * Output: Which BGX indexes may be connected to this port */ static void find_bgx(int node, int qlm, int *bgx, int *bgx_lane_mask) { *bgx = -1; *bgx_lane_mask = 0; if (CAVIUM_IS_MODEL(CAVIUM_CN88XX)) { switch (qlm) { case 0: /* BGX0 -> QLM0 */ case 1: /* BGX1 -> QLM1 */ *bgx = qlm; *bgx_lane_mask = 0xf; return; default: BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm); return; } } else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX)) { switch (qlm) { case 2: /* BGX0 -> QLM2 */ *bgx = 0; *bgx_lane_mask = 0xf; return; case 3: /* BGX1 -> QLM3 */ *bgx = 1; *bgx_lane_mask = 0xf; return; case 4: /* BGX3 -> DLM4 */ *bgx = 3; *bgx_lane_mask = 0x3; return; case 5: /* BGX2 -> DLM5 */ *bgx = 2; *bgx_lane_mask = 0x3; return; case 6: /* BGX2 -> DLM6 */ *bgx = 2; *bgx_lane_mask = 0xc; return; default: BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm); return; } } else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX)) { switch (qlm) { case 0: /* BGX0 -> DLM0 */ *bgx = 0; *bgx_lane_mask = 0x3; return; case 1: /* BGX0 -> DLM1 */ *bgx = 0; *bgx_lane_mask = 0xc; return; case 2: /* BGX1 -> DLM2 */ *bgx = 1; *bgx_lane_mask = 0x3; return; case 3: /* BGX1 -> DLM3 */ *bgx = 1; *bgx_lane_mask = 0xc; return; default: BDK_TRACE(INIT, "N%d.QLM%d: No BGX for this QLM, illegal config\n", node, qlm); return; } } else bdk_error("N%d.QLM%d: Unsupported chip, update %s()\n", node, qlm, __FUNCTION__); }
static void boot_init_qlm_mode(void) { /* Check if QLM autoconfig is requested */ int qlm_auto = bdk_config_get_int(BDK_CONFIG_QLM_AUTO_CONFIG); if (qlm_auto) { /* Auto configuration of QLMs */ for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++) { if (bdk_numa_exists(n)) { BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n); bdk_qlm_auto_config(n); } } } /* * Check if QLM autoconfig from DIP switch settings is requested */ else if (bdk_config_get_int(BDK_CONFIG_QLM_DIP_AUTO_CONFIG)) { BDK_TRACE(INIT, "Reading DIP Switch settings for QLM Auto configuration\n"); /* Auto configuration of QLMs */ for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++) { if (bdk_numa_exists(n)) { BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n); if (bdk_qlm_dip_auto_config(n)) bdk_error("QLM Auto configuration failed!\n"); } } } else { /* Initialize the QLMs based on configuration file settings */ boot_init_qlm_clk(); for (bdk_node_t n = BDK_NODE_0; n < BDK_NUMA_MAX_NODES; n++) { if (!bdk_numa_exists(n)) continue; int num_qlms = bdk_qlm_get_num(n); BDK_TRACE(INIT, "Initializing QLMs on Node %d\n", n); for (int qlm = 0; qlm < num_qlms; qlm++) { const char *cfg_val; cfg_val = bdk_config_get_str(BDK_CONFIG_QLM_MODE, n, qlm); if (!cfg_val) continue; int mode; int freq; /* Check for special token telling us to configure the QLM based on the SFP/SFP+/QSFP/QSFP+ plugged into the system. */ if ((strcmp(cfg_val, "SFP+") == 0) || (strcmp(cfg_val, "QSFP+") == 0)) { if (strcmp(cfg_val, "SFP+") == 0) mode = init_sfp(n, qlm); else mode = init_qsfp(n, qlm); if (mode == BDK_QLM_MODE_SGMII_4X1) freq = 1250; else freq = 10321; } else { mode = bdk_qlm_cfg_string_to_mode(cfg_val); freq = bdk_config_get_int(BDK_CONFIG_QLM_FREQ, n, qlm); } if (-1 == mode) { bdk_error("Invalid QLM mode string '%s' for QLM%d on node %d. " "Not configuring.\n", cfg_val, qlm, n); continue; } if (-1 == freq) { bdk_error("No frequency setting for QLM%d on node %d. " "Not configuring.\n", qlm, n); continue; } bdk_qlm_set_mode(n, qlm, mode, freq, 0); } } } }
/** * Determine the DLM/QLM mode based on a SFP/SFP+ connected to the port. Note that * the CN8XXX parts can't control mode per lane, so all SFP/SFP+ on a DLM/QLM must * be the same mode. This code is sloppy about finding the BGX PHY for the DLM/QLM * because not all lanes may be used. * * @param node Node to determine mode for * @param qlm DLM/QLM the SFP/SFP+ is connected to * * @return QLM mode or -1 on failure */ static int init_sfp(int node, int qlm) { int mode = BDK_QLM_MODE_XFI_4X1; /* Default to XFI if detection fails */ int bgx = -1; int bgx_lane_mask = 0; find_bgx(node, qlm, &bgx, &bgx_lane_mask); if (bgx == -1) return mode; BDK_TRACE(INIT, "N%d.QLM%d: Checking for SFP/SFP+\n", node, qlm); for (int index = 0; index < 4; index++) { /* Skip BGX indexes that aren't applicable */ if ((bgx_lane_mask & (1 << index)) == 0) continue; /* Lookup the PHY address for this BGX port */ int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, node, bgx, index); /* SFP/SFP+ are connected with TWSI, so only check ports with PHYs connected with TWSI */ if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_TWSI) continue; /* For TWSI: Bits[31:24]: Node ID, 0xff for device node Bits[23:16]: TWSI internal address width in bytes (0-2) Bits[15:12]: 2=TWSI Bits[11:8]: TWSI bus number Bits[7:0]: TWSI address */ int n = (phy_addr >> 24) & 0xff; int twsi_ia_width = (phy_addr >> 16) & 0xff; int twsi_bus = (phy_addr >> 8) & 0xf; int twsi_addr = 0x50; /* From SFP spec */ if (n == 0xff) n = node; /* Read bytes 0-3 from eeprom. Note read is big endian, so byte 0 is bits 31:24 in the result */ int64_t eeprom_00_03 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 0, 4, twsi_ia_width); if (eeprom_00_03 == -1) { BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index); continue; } int64_t eeprom_04_07 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 4, 4, twsi_ia_width); if (eeprom_04_07 == -1) { BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index); continue; } int64_t eeprom_08_11 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 8, 4, twsi_ia_width); if (eeprom_08_11 == -1) { BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index); continue; } int64_t eeprom_12 = bdk_twsix_read_ia(n, twsi_bus, twsi_addr, 12, 1, twsi_ia_width); if (eeprom_12 == -1) { BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ eeprom access failed\n", node, qlm, bgx, index); continue; } /* Byte 0: Identifier, should be 0x03 for SFP/SFP+ 0x03 = SFP of SFP+ 0x0c = QSFP 0x0d = QSFP+ */ if (bdk_extract(eeprom_00_03, 24, 8) != 0x03) { /* Byte 0 of eeprom should be 0x03 for SFP/SFP+ */ BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ not detected\n", node, qlm, bgx, index); continue; } /* Byte 1: Extended Identifier, should be 0x04 */ if (bdk_extract(eeprom_00_03, 16, 8) != 0x04) { BDK_TRACE(INIT, "N%d.QLM%d: BGX%d.%d SFP/SFP+ incorrect extended identifier\n", node, qlm, bgx, index); continue; } /* Byte 2: Connector Value Description of connector 00h Unknown or unspecified 01h SC 02h Fibre Channel Style 1 copper connector 03h Fibre Channel Style 2 copper connector 04h BNC/TNC 05h Fibre Channel coaxial headers 06h FiberJack 07h LC 08h MT-RJ 09h MU 0Ah SG 0Bh Optical pigtail 0Ch MPO Parallel Optic 0Dh-1Fh Reserved, Unallocated 20h HSSDC II 21h Copper Pigtail 22h RJ45 23h-7Fh Reserved, Unallocated 80-FFh Vendor specific */ bool isOptical = false; switch (bdk_extract(eeprom_00_03, 8, 8)) { case 0x01: /* SC - Short channel */ case 0x07: /* LC - Long channel */ case 0x0B: /* Optical pigtail */ isOptical = true; break; } BDK_TRACE(INIT, "N%d.QLM%d: SFP/SFP+ eeprom Bytes[0:3] 0x%0llx, Bytes[4:7] 0x%08llx, [8:11] 0x%08llx [12] 0x%02llx\n", node, qlm, eeprom_00_03, eeprom_04_07, eeprom_08_11, eeprom_12); /* Byte 3: Transceiver info first byte. See comments below */ /* Byte 3, bits 4-7 correspond to 10G Ethernet speeds */ /* 10G Ethernet Compliance Codes Byte 3[7] 10G BASE-ER (Fiber - Extended Reach) Byte 3[6] 10G BASE-LRM (Fiber - Long reach multi-mode) Byte 3[5] 10G BASE-LR (Fiber - Long reach) Byte 3[4] 10G BASE-SR (Fiber - Short reach) */ bool isXFI = bdk_extract(eeprom_00_03, 0, 8) != 0; /* Byte 6, bits 0-7 correspond to Gigabit Ethernet speeds */ /* Gigabit Ethernet Compliance Codes Byte 6[7] BASE-PX Byte 6[6] BASE-BX10 Byte 6[5] 100BASE-FX Byte 6[4] 100BASE-LX/LX10 (Fiber) Byte 6[3] 1000BASE-T (Twisted pair) Byte 6[2] 1000BASE-CX (Shielded balanced copper) Byte 6[1] 1000BASE-LX (Fiber) Byte 6[0] 1000BASE-SX (Fiber) */ bool isSGMII = bdk_extract(eeprom_04_07, 8, 8) != 0; /* Byte 12 is the nominal bit rate, units of 100 MBits/sec. */ int bit_rate = eeprom_12 * 100; if (bit_rate) { BDK_TRACE(INIT, "N%d.QLM%d: Nominal bit rate %d MBits/sec\n", node, qlm, bit_rate); isXFI = (bit_rate >= 10000); isSGMII = (bit_rate <= 2500); } if (isXFI) { mode = BDK_QLM_MODE_XFI_4X1; if (isOptical) BDK_TRACE(INIT, "N%d.QLM%d: SFP+ selecting XFI Optical\n", node, qlm); else BDK_TRACE(INIT, "N%d.QLM%d: SFP+ selecting XFI Copper\n", node, qlm); } else if (isSGMII) { mode = BDK_QLM_MODE_SGMII_4X1; if (isOptical) { /* This should be 1000BASE-X, gigabit over fiber */ BDK_TRACE(INIT, "N%d.QLM%d: SFP selecting SGMII Optical\n", node, qlm); } else /* This should be SGMII, gigabit over copper */ BDK_TRACE(INIT, "N%d.QLM%d: SFP selecting SGMII Copper\n", node, qlm); } } return mode; }