[PATCH v2 1/4] ethdev: report SFF-8636 lane status flags
Roman Khromenok
roma55592 at yandex.ru
Thu Oct 8 20:02:35 CEST 2026
The SFF-8636 decoder shows the module identity and the digital
diagnostics, but not the channel status flags, which are usually
the first thing to check when a QSFP link does not come up.
Report the per-lane Rx and Tx loss of signal, Rx and Tx CDR loss
of lock, Tx fault and Tx adaptive equalization fault flags from
bytes 3 to 5 of the lower page. A flag is shown as "None" if no
lane is flagged, or as a list per lane otherwise, following the
output of ethtool.
Each flag is reported only if the module advertises it in the
options of page 00h, except the Rx loss of signal: it is optional
but has no implemented bit, so it is always reported, and a module
without it reads as not flagged. Gating it on the Tx loss of signal
bit, as ethtool does, would hide it on modules supporting Rx only.
These flags are latched and cleared on read, so they report the
events since the previous read of the module.
Signed-off-by: Roman Khromenok <roma55592 at yandex.ru>
---
doc/guides/rel_notes/release_26_11.rst | 2 ++
lib/ethdev/sff_8636.c | 34 ++++++++++++++++++++++++++
lib/ethdev/sff_8636.h | 12 +++++++++
lib/ethdev/sff_common.c | 25 +++++++++++++++++++
lib/ethdev/sff_common.h | 2 ++
5 files changed, 75 insertions(+)
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index d0c896634b..c3f8ccdccf 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -86,6 +86,8 @@ New Features
such as the Linux ethtool ``ETHTOOL_GMODULEEEPROM`` ioctl.
The testpmd command ``show port (port_id) module_eeprom decode``
uses it to display the decoded module EEPROM.
+ The SFF-8636 decoder also reports the per-lane loss of signal,
+ loss of lock and Tx fault flags.
* **Updated AF_XDP driver.**
diff --git a/lib/ethdev/sff_8636.c b/lib/ethdev/sff_8636.c
index 40e5404caf..ad36927540 100644
--- a/lib/ethdev/sff_8636.c
+++ b/lib/ethdev/sff_8636.c
@@ -452,6 +452,39 @@ static void sff_8636_show_rate_identifier(const uint8_t *data, struct sff_output
sff_output_field(d, "Rate identifier", val_string);
}
+/*
+ * Channel status flags are latched and cleared when read by the host,
+ * so they report the events since the previous read of the module.
+ * Each flag except Rx LOS is reported only if the module advertises it.
+ */
+static void sff_8636_show_signals(const uint8_t *data, struct sff_output *d)
+{
+ uint8_t los = data[SFF_8636_LOS_AW_OFFSET];
+ uint8_t fault = data[SFF_8636_FAULT_AW_OFFSET];
+ uint8_t lol = data[SFF_8636_LOL_AW_OFFSET];
+
+ /* Rx LOS has no implemented bit, it reads as not flagged if absent */
+ sff_show_lane_status("Rx loss of signal", SFF_MAX_CHANNEL_NUM,
+ SFF_8636_LANES_LOW(los), d);
+ if (data[SFF_8636_OPTION_4_OFFSET] & SFF_8636_O4_TX_LOS)
+ sff_show_lane_status("Tx loss of signal", SFF_MAX_CHANNEL_NUM,
+ SFF_8636_LANES_HIGH(los), d);
+
+ if (data[SFF_8636_OPTION_3_OFFSET] & SFF_8636_O3_RX_LOL)
+ sff_show_lane_status("Rx loss of lock", SFF_MAX_CHANNEL_NUM,
+ SFF_8636_LANES_LOW(lol), d);
+ if (data[SFF_8636_OPTION_3_OFFSET] & SFF_8636_O3_TX_LOL)
+ sff_show_lane_status("Tx loss of lock", SFF_MAX_CHANNEL_NUM,
+ SFF_8636_LANES_HIGH(lol), d);
+
+ if (data[SFF_8636_OPTION_4_OFFSET] & SFF_8636_O4_TX_FAULT)
+ sff_show_lane_status("Tx fault", SFF_MAX_CHANNEL_NUM,
+ SFF_8636_LANES_LOW(fault), d);
+ if (data[SFF_8636_OPTION_2_OFFSET] & SFF_8636_O2_TX_EQ_AUTO)
+ sff_show_lane_status("Tx adaptive eq fault", SFF_MAX_CHANNEL_NUM,
+ SFF_8636_LANES_HIGH(fault), d);
+}
+
static void sff_8636_show_oui(const uint8_t *data, struct sff_output *d)
{
sff_8024_show_oui(data, SFF_8636_VENDOR_OUI_OFFSET, d);
@@ -761,6 +794,7 @@ void sff_8636_show_all(const uint8_t *data, uint32_t eeprom_len, struct sff_outp
sff_show_ascii(data, SFF_8636_DATE_YEAR_OFFSET,
SFF_8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code", d);
sff_8636_show_revision_compliance(data, d);
+ sff_8636_show_signals(data, d);
sff_8636_show_dom(data, eeprom_len, d);
}
}
diff --git a/lib/ethdev/sff_8636.h b/lib/ethdev/sff_8636.h
index cf11a2c247..d2a923a7c0 100644
--- a/lib/ethdev/sff_8636.h
+++ b/lib/ethdev/sff_8636.h
@@ -55,6 +55,12 @@
#define SFF_8636_TX2_FAULT_AW RTE_BIT32(1)
#define SFF_8636_TX1_FAULT_AW RTE_BIT32(0)
+#define SFF_8636_LOL_AW_OFFSET 0x05
+
+/* Lanes 1-4 of the channel status flags are in bits 0-3 or 4-7 */
+#define SFF_8636_LANES_LOW(val) ((val) & 0xf)
+#define SFF_8636_LANES_HIGH(val) (((val) >> 4) & 0xf)
+
/* Module Monitor Interrupt Flags - 6-8 */
#define SFF_8636_TEMP_AW_OFFSET 0x06
#define SFF_8636_TEMP_HALARM_STATUS RTE_BIT32(7)
@@ -488,9 +494,15 @@
#define SFF_8636_ETHERNET_100G_ACC2 0x19
#define SFF_8636_OPTION_2_OFFSET 0xC1
+/* Tx input equalizers auto-adaptive */
+#define SFF_8636_O2_TX_EQ_AUTO RTE_BIT32(3)
/* Rx output amplitude */
#define SFF_8636_O2_RX_OUTPUT_AMP RTE_BIT32(0)
#define SFF_8636_OPTION_3_OFFSET 0xC2
+/* Tx CDR Loss of Lock flag implemented */
+#define SFF_8636_O3_TX_LOL RTE_BIT32(5)
+/* Rx CDR Loss of Lock flag implemented */
+#define SFF_8636_O3_RX_LOL RTE_BIT32(4)
/* Rx Squelch Disable */
#define SFF_8636_O3_RX_SQL_DSBL RTE_BIT32(3)
/* Rx Output Disable capable */
diff --git a/lib/ethdev/sff_common.c b/lib/ethdev/sff_common.c
index a36bf83026..0fc498edb0 100644
--- a/lib/ethdev/sff_common.c
+++ b/lib/ethdev/sff_common.c
@@ -84,6 +84,31 @@ void sff_show_ascii(const uint8_t *data, unsigned int first_reg,
sff_output_field(d, name, val_string);
}
+/*
+ * Report a per-lane status bitmap, lane 1 in bit 0:
+ * "None" if no lane is flagged, otherwise "[ Yes, No, ... ]".
+ */
+void sff_show_lane_status(const char *name, unsigned int lane_cnt,
+ unsigned int value, struct sff_output *d)
+{
+ char val_string[SFF_ITEM_VAL_COMPOSE_SIZE];
+ unsigned int lane;
+
+ if (value == 0) {
+ sff_output_field(d, name, "None");
+ return;
+ }
+
+ strlcpy(val_string, "[", sizeof(val_string));
+ for (lane = 0; lane < lane_cnt; lane++) {
+ strlcat(val_string, (value & RTE_BIT32(lane)) ? " Yes" : " No",
+ sizeof(val_string));
+ strlcat(val_string, lane + 1 < lane_cnt ? "," : " ]",
+ sizeof(val_string));
+ }
+ sff_output_field(d, name, val_string);
+}
+
void sff_8024_show_oui(const uint8_t *data, int id_offset, struct sff_output *d)
{
char val_string[SFF_ITEM_VAL_COMPOSE_SIZE];
diff --git a/lib/ethdev/sff_common.h b/lib/ethdev/sff_common.h
index d346e1b378..9041f8ed17 100644
--- a/lib/ethdev/sff_common.h
+++ b/lib/ethdev/sff_common.h
@@ -191,6 +191,8 @@ void sff_show_value_with_unit(const uint8_t *data, unsigned int reg,
void sff_show_ascii(const uint8_t *data, unsigned int first_reg,
unsigned int last_reg, const char *name, struct sff_output *d);
void sff_show_thresholds(struct sff_diags sd, struct sff_output *d);
+void sff_show_lane_status(const char *name, unsigned int lane_cnt,
+ unsigned int value, struct sff_output *d);
void sff_8024_show_oui(const uint8_t *data, int id_offset, struct sff_output *d);
void sff_8024_show_identifier(const uint8_t *data, int id_offset, struct sff_output *d);
--
2.47.3
More information about the dev
mailing list