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 <[email protected]>
---
 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

Reply via email to