git: f9ce33b0d8ef - main - ixgbe: Add 10GBase-BX BiDi SFP+ module support

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Mon, 10 Aug 2026 16:55:51 UTC
The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=f9ce33b0d8ef233063bd6c27bdba2580f97d9094

commit f9ce33b0d8ef233063bd6c27bdba2580f97d9094
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-10 16:29:24 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-10 16:54:09 +0000

    ixgbe: Add 10GBase-BX BiDi SFP+ module support
    
    10G-BX optics use paired wavelengths to carry 10 Gb/s Ethernet over a
    single strand of single-mode fiber.  Their 10G compliance byte is
    empty, so identify them from the SFF-8472 nominal signaling rate and
    single-mode reach fields.
    
    When an EEPROM also advertises 1G BASE-BX10, give the complete 10G
    bitrate and reach signature precedence.  Otherwise retain FreeBSD's
    permissive 1G-BX identification rather than requiring a nominal
    1.3 GBd rate.
    
    MFC after:      2 weeks
    Relnotes:       yes
---
 sys/dev/ixgbe/if_ix.c       | 10 ++++++++
 sys/dev/ixgbe/ixgbe_82599.c |  7 ++++++
 sys/dev/ixgbe/ixgbe_phy.c   | 61 +++++++++++++++++++++++++++++++++++++++++----
 sys/dev/ixgbe/ixgbe_phy.h   |  3 +++
 sys/dev/ixgbe/ixgbe_type.h  |  3 +++
 sys/dev/ixgbe/ixgbe_x550.c  |  2 ++
 6 files changed, 81 insertions(+), 5 deletions(-)

diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c
index ba1f3121ae4f..9a59ce6b4fe8 100644
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -1755,6 +1755,10 @@ ixgbe_add_media_types(if_ctx_t ctx)
 			ifmedia_add(sc->media, IFM_ETHER | IFM_1000_LX, 0,
 			    NULL);
 	}
+	if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_BX) {
+		device_printf(dev, "Media supported: 10Gbase-BX\n");
+		ifmedia_add(sc->media, IFM_ETHER | IFM_10G_BX, 0, NULL);
+	}
 	if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_SR) {
 		ifmedia_add(sc->media, IFM_ETHER | IFM_10G_SR, 0, NULL);
 		if (hw->phy.multispeed_fiber)
@@ -2896,6 +2900,9 @@ ixgbe_if_media_status(if_ctx_t ctx, struct ifmediareq * ifmr)
 			ifmr->ifm_active |= IFM_1000_LX | IFM_FDX;
 			break;
 		}
+	if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_BX &&
+	    sc->link_speed == IXGBE_LINK_SPEED_10GB_FULL)
+		ifmr->ifm_active |= IFM_10G_BX | IFM_FDX;
 	if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_LRM)
 		switch (sc->link_speed) {
 		case IXGBE_LINK_SPEED_10GB_FULL:
@@ -3028,6 +3035,9 @@ ixgbe_if_media_change(if_ctx_t ctx)
 		speed |= IXGBE_LINK_SPEED_1GB_FULL;
 		speed |= IXGBE_LINK_SPEED_10GB_FULL;
 		break;
+	case IFM_10G_BX:
+		speed |= IXGBE_LINK_SPEED_10GB_FULL;
+		break;
 	case IFM_10G_LRM:
 	case IFM_10G_LR:
 #ifndef IFM_ETH_XTYPE
diff --git a/sys/dev/ixgbe/ixgbe_82599.c b/sys/dev/ixgbe/ixgbe_82599.c
index d23c84743b33..c8f1fbb9983e 100644
--- a/sys/dev/ixgbe/ixgbe_82599.c
+++ b/sys/dev/ixgbe/ixgbe_82599.c
@@ -454,6 +454,13 @@ s32 ixgbe_get_link_capabilities_82599(struct ixgbe_hw *hw,
 		goto out;
 	}
 
+	if (hw->phy.sfp_type == ixgbe_sfp_type_10g_bx_core0 ||
+	    hw->phy.sfp_type == ixgbe_sfp_type_10g_bx_core1) {
+		*speed = IXGBE_LINK_SPEED_10GB_FULL;
+		*autoneg = false;
+		goto out;
+	}
+
 	/*
 	 * Determine link capabilities based on the stored value of AUTOC,
 	 * which represents EEPROM defaults.  If AUTOC value has not
diff --git a/sys/dev/ixgbe/ixgbe_phy.c b/sys/dev/ixgbe/ixgbe_phy.c
index a614ffe50c0d..99bfebd3acf1 100644
--- a/sys/dev/ixgbe/ixgbe_phy.c
+++ b/sys/dev/ixgbe/ixgbe_phy.c
@@ -1299,8 +1299,12 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
 	u8 oui_bytes[3] = {0, 0, 0};
 	u8 cable_tech = 0;
 	u8 cable_spec = 0;
+	u8 bitrate_nominal = 0;
+	u8 sm_length_100m = 0;
+	u8 sm_length_km = 0;
 	u8 retries;
 	u16 enforce_sfp = 0;
+	bool is_10g_bx = false;
 	static bool warned_once = false;
 
 	DEBUGFUNC("ixgbe_identify_sfp_module_generic");
@@ -1358,6 +1362,37 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
 		if (status != IXGBE_SUCCESS)
 			goto err_read_i2c_eeprom;
 
+		/*
+		 * SFF-8472 identifies 10G-BX by an empty 10G compliance
+		 * byte, a nominal 10.3 GBd rate, and at least 1 km of
+		 * single-mode fiber reach.  Resolve that strong signature
+		 * before falling back to the permissive 1G-BX check below.
+		 */
+		if (hw->mac.type != ixgbe_mac_82598EB &&
+		    comp_codes_10g == 0 &&
+		    (comp_codes_1g & (IXGBE_SFF_1GBASET_CAPABLE |
+		    IXGBE_SFF_1GBASESX_CAPABLE |
+		    IXGBE_SFF_1GBASELX_CAPABLE)) == 0 &&
+		    (cable_tech & (IXGBE_SFF_DA_PASSIVE_CABLE |
+		    IXGBE_SFF_DA_ACTIVE_CABLE)) == 0) {
+			status = hw->phy.ops.read_i2c_eeprom(hw,
+			    IXGBE_SFF_BITRATE_NOMINAL, &bitrate_nominal);
+			if (status != IXGBE_SUCCESS)
+				goto err_read_i2c_eeprom;
+			if (bitrate_nominal == IXGBE_SFF_10G_BX_BITRATE) {
+				status = hw->phy.ops.read_i2c_eeprom(hw,
+				    IXGBE_SFF_SM_LENGTH_KM, &sm_length_km);
+				if (status != IXGBE_SUCCESS)
+					goto err_read_i2c_eeprom;
+				status = hw->phy.ops.read_i2c_eeprom(hw,
+				    IXGBE_SFF_SM_LENGTH_100M, &sm_length_100m);
+				if (status != IXGBE_SUCCESS)
+					goto err_read_i2c_eeprom;
+			}
+			is_10g_bx = bitrate_nominal == IXGBE_SFF_10G_BX_BITRATE &&
+			    (sm_length_km > 0 || sm_length_100m >= 10);
+		}
+
 		 /* ID Module
 		  * =========
 		  * 0   SFP_DA_CU
@@ -1437,6 +1472,13 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
 				else
 					hw->phy.sfp_type =
 						ixgbe_sfp_type_1g_lx_core1;
+			} else if (is_10g_bx) {
+				if (hw->bus.lan_id == 0)
+					hw->phy.sfp_type =
+					    ixgbe_sfp_type_10g_bx_core0;
+				else
+					hw->phy.sfp_type =
+					    ixgbe_sfp_type_10g_bx_core1;
 			} else if (comp_codes_1g & IXGBE_SFF_BASEBX10_CAPABLE) {
 				if (hw->bus.lan_id == 0)
 					hw->phy.sfp_type =
@@ -1536,7 +1578,9 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
 		      hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
 		      hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1 ||
 		      hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core0 ||
-		      hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core1)) {
+		      hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core1 ||
+		      hw->phy.sfp_type == ixgbe_sfp_type_10g_bx_core0 ||
+		      hw->phy.sfp_type == ixgbe_sfp_type_10g_bx_core1)) {
 			hw->phy.type = ixgbe_phy_sfp_unsupported;
 			status = IXGBE_ERR_SFP_NOT_SUPPORTED;
 			goto out;
@@ -1557,7 +1601,9 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
 		      hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
 		      hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1 ||
 		      hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core0 ||
-		      hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core1)) {
+		      hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core1 ||
+		      hw->phy.sfp_type == ixgbe_sfp_type_10g_bx_core0 ||
+		      hw->phy.sfp_type == ixgbe_sfp_type_10g_bx_core1)) {
 			/* Make sure we're a supported PHY type */
 			if (hw->phy.type == ixgbe_phy_sfp_intel) {
 				status = IXGBE_SUCCESS;
@@ -1612,6 +1658,9 @@ u64 ixgbe_get_supported_phy_sfp_layer_generic(struct ixgbe_hw *hw)
 	hw->phy.ops.identify_sfp(hw);
 	if (hw->phy.sfp_type == ixgbe_sfp_type_not_present)
 		return physical_layer;
+	if (hw->phy.sfp_type == ixgbe_sfp_type_10g_bx_core0 ||
+	    hw->phy.sfp_type == ixgbe_sfp_type_10g_bx_core1)
+		return IXGBE_PHYSICAL_LAYER_10GBASE_BX;
 
 	switch (hw->phy.type) {
 	case ixgbe_phy_sfp_passive_tyco:
@@ -1884,20 +1933,22 @@ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
 		return IXGBE_ERR_SFP_NOT_SUPPORTED;
 
 	/*
-	 * Limiting active cables and 1G Phys must be initialized as
+	 * Limiting active cables, 10G-BX, and 1G PHYs must be initialized as
 	 * SR modules
 	 */
 	if (sfp_type == ixgbe_sfp_type_da_act_lmt_core0 ||
 	    sfp_type == ixgbe_sfp_type_1g_lx_core0 ||
 	    sfp_type == ixgbe_sfp_type_1g_cu_core0 ||
 	    sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
-	    sfp_type == ixgbe_sfp_type_1g_bx_core0)
+	    sfp_type == ixgbe_sfp_type_1g_bx_core0 ||
+	    sfp_type == ixgbe_sfp_type_10g_bx_core0)
 		sfp_type = ixgbe_sfp_type_srlr_core0;
 	else if (sfp_type == ixgbe_sfp_type_da_act_lmt_core1 ||
 		 sfp_type == ixgbe_sfp_type_1g_lx_core1 ||
 		 sfp_type == ixgbe_sfp_type_1g_cu_core1 ||
 		 sfp_type == ixgbe_sfp_type_1g_sx_core1 ||
-		 sfp_type == ixgbe_sfp_type_1g_bx_core1)
+		 sfp_type == ixgbe_sfp_type_1g_bx_core1 ||
+		 sfp_type == ixgbe_sfp_type_10g_bx_core1)
 		sfp_type = ixgbe_sfp_type_srlr_core1;
 
 	/* Read offset to PHY init contents */
diff --git a/sys/dev/ixgbe/ixgbe_phy.h b/sys/dev/ixgbe/ixgbe_phy.h
index c1ba73851397..8768267a383a 100644
--- a/sys/dev/ixgbe/ixgbe_phy.h
+++ b/sys/dev/ixgbe/ixgbe_phy.h
@@ -50,6 +50,8 @@
 #define IXGBE_SFF_10GBE_COMP_CODES	0x3
 #define IXGBE_SFF_CABLE_TECHNOLOGY	0x8
 #define IXGBE_SFF_BITRATE_NOMINAL	0xC
+#define IXGBE_SFF_SM_LENGTH_KM		0xE
+#define IXGBE_SFF_SM_LENGTH_100M		0xF
 #define IXGBE_SFF_CABLE_SPEC_COMP	0x3C
 #define IXGBE_SFF_SFF_8472_SWAP		0x5C
 #define IXGBE_SFF_SFF_8472_COMP		0x5E
@@ -75,6 +77,7 @@
 #define IXGBE_SFF_10GBASESR_CAPABLE	0x10
 #define IXGBE_SFF_10GBASELR_CAPABLE	0x20
 #define IXGBE_SFF_BASEBX10_CAPABLE	0x40
+#define IXGBE_SFF_10G_BX_BITRATE	0x67
 #define IXGBE_SFF_SOFT_RS_SELECT_MASK	0x8
 #define IXGBE_SFF_SOFT_RS_SELECT_10G	0x8
 #define IXGBE_SFF_SOFT_RS_SELECT_1G	0x0
diff --git a/sys/dev/ixgbe/ixgbe_type.h b/sys/dev/ixgbe/ixgbe_type.h
index 648f790194db..4986673b65c4 100644
--- a/sys/dev/ixgbe/ixgbe_type.h
+++ b/sys/dev/ixgbe/ixgbe_type.h
@@ -3551,6 +3551,7 @@ typedef u64 ixgbe_physical_layer;
 #define IXGBE_PHYSICAL_LAYER_2500BASE_KX	0x10000
 #define IXGBE_PHYSICAL_LAYER_2500BASE_T		0x20000
 #define IXGBE_PHYSICAL_LAYER_5000BASE_T		0x40000
+#define IXGBE_PHYSICAL_LAYER_10GBASE_BX		0x80000
 
 /* Flow Control Data Sheet defined values
  * Calculation and defines taken from 802.1bb Annex O
@@ -3827,6 +3828,8 @@ enum ixgbe_sfp_type {
 	ixgbe_sfp_type_1g_lx_core1 = 14,
 	ixgbe_sfp_type_1g_bx_core0 = 15,
 	ixgbe_sfp_type_1g_bx_core1 = 16,
+	ixgbe_sfp_type_10g_bx_core0 = 17,
+	ixgbe_sfp_type_10g_bx_core1 = 18,
 	ixgbe_sfp_type_not_present = 0xFFFE,
 	ixgbe_sfp_type_unknown = 0xFFFF
 };
diff --git a/sys/dev/ixgbe/ixgbe_x550.c b/sys/dev/ixgbe/ixgbe_x550.c
index 2891bc258db8..4f708e03b00e 100644
--- a/sys/dev/ixgbe/ixgbe_x550.c
+++ b/sys/dev/ixgbe/ixgbe_x550.c
@@ -1508,6 +1508,8 @@ static s32 ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear)
 	case ixgbe_sfp_type_1g_lx_core1:
 	case ixgbe_sfp_type_1g_bx_core0:
 	case ixgbe_sfp_type_1g_bx_core1:
+	case ixgbe_sfp_type_10g_bx_core0:
+	case ixgbe_sfp_type_10g_bx_core1:
 		*linear = false;
 		break;
 	case ixgbe_sfp_type_unknown: