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, each one only if the module
advertises it in the options of page 00h. A flag is shown as
"None" if no lane is flagged, or as a list per lane otherwise,
following the output of ethtool.

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                  | 35 ++++++++++++++++++++++++++
 lib/ethdev/sff_8636.h                  | 12 +++++++++
 lib/ethdev/sff_common.c                | 25 ++++++++++++++++++
 lib/ethdev/sff_common.h                |  2 ++
 5 files changed, 76 insertions(+)

diff --git a/doc/guides/rel_notes/release_26_11.rst 
b/doc/guides/rel_notes/release_26_11.rst
index d25dc72edf..08c554cedb 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -84,6 +84,8 @@ New Features
   It can decode data read with ``rte_eth_dev_get_module_eeprom()``
   or obtained from any other source with the same layout,
   such as the Linux ethtool ``ETHTOOL_GMODULEEEPROM`` ioctl.
+  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..8fc48e1798 100644
--- a/lib/ethdev/sff_8636.c
+++ b/lib/ethdev/sff_8636.c
@@ -452,6 +452,40 @@ 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 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];
+
+       /* There is no Rx LOS implemented bit, use the Tx one for both */
+       if (data[SFF_8636_OPTION_4_OFFSET] & SFF_8636_O4_TX_LOS) {
+               sff_show_lane_status("Rx loss of signal", SFF_MAX_CHANNEL_NUM,
+                                    SFF_8636_LANES_LOW(los), d);
+               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 +795,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