git: 3320f0f69f06 - main - if_bnxt: Pluggable Module Display Support

From: Sumit Saxena <ssaxena_at_FreeBSD.org>
Date: Thu, 07 Mar 2024 19:27:16 UTC
The branch main has been updated by ssaxena:

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

commit 3320f0f69f06df5c36daa8ff71e0c07908ff0f1a
Author:     Sumit Saxena <ssaxena@FreeBSD.org>
AuthorDate: 2024-03-06 13:21:21 +0000
Commit:     Sumit Saxena <ssaxena@FreeBSD.org>
CommitDate: 2024-03-07 19:16:39 +0000

    if_bnxt: Pluggable Module Display Support
    
    This update enables the display of pluggable module information
    to users via the ifconfig utility.
    
    Reviewed by:            imp
    Approved by:            imp
    Differential revision:  https://reviews.freebsd.org/D42958
---
 sys/dev/bnxt/bnxt.h      |  8 +++++++
 sys/dev/bnxt/bnxt_hwrm.c | 40 ++++++++++++++++++++++++++++++++
 sys/dev/bnxt/if_bnxt.c   | 59 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+)

diff --git a/sys/dev/bnxt/bnxt.h b/sys/dev/bnxt/bnxt.h
index a5c607dbe247..4397e0478a0c 100644
--- a/sys/dev/bnxt/bnxt.h
+++ b/sys/dev/bnxt/bnxt.h
@@ -319,6 +319,8 @@ struct bnxt_link_info {
 #define BNXT_AUTONEG_FLOW_CTRL	2
 	uint8_t		req_duplex;
 	uint16_t	req_link_speed;
+	uint8_t		module_status;
+	struct hwrm_port_phy_qcfg_output    phy_qcfg_resp;
 };
 
 enum bnxt_cp_type {
@@ -802,9 +804,15 @@ struct bnxt_filter_info {
 	uint64_t	l2_filter_id_hint;
 };
 
+#define I2C_DEV_ADDR_A0                 0xa0
+#define BNXT_MAX_PHY_I2C_RESP_SIZE      64
+
 /* Function declarations */
 void bnxt_report_link(struct bnxt_softc *softc);
 bool bnxt_check_hwrm_version(struct bnxt_softc *softc);
 struct bnxt_softc *bnxt_find_dev(uint32_t domain, uint32_t bus, uint32_t dev_fn, char *name);
+int bnxt_read_sfp_module_eeprom_info(struct bnxt_softc *bp, uint16_t i2c_addr,
+    uint16_t page_number, uint8_t bank, bool bank_sel_en, uint16_t start_addr,
+    uint16_t data_length, uint8_t *buf);
 
 #endif /* _BNXT_H */
diff --git a/sys/dev/bnxt/bnxt_hwrm.c b/sys/dev/bnxt/bnxt_hwrm.c
index 38e6c0db670f..97574c768235 100644
--- a/sys/dev/bnxt/bnxt_hwrm.c
+++ b/sys/dev/bnxt/bnxt_hwrm.c
@@ -2184,6 +2184,44 @@ bnxt_hwrm_fw_set_time(struct bnxt_softc *softc, uint16_t year, uint8_t month,
 	return hwrm_send_message(softc, &req, sizeof(req));
 }
 
+int bnxt_read_sfp_module_eeprom_info(struct bnxt_softc *softc, uint16_t i2c_addr,
+    uint16_t page_number, uint8_t bank,bool bank_sel_en, uint16_t start_addr,
+    uint16_t data_length, uint8_t *buf)
+{
+	struct hwrm_port_phy_i2c_read_output *output =
+			(void *)softc->hwrm_cmd_resp.idi_vaddr;
+	struct hwrm_port_phy_i2c_read_input req = {0};
+	int rc = 0, byte_offset = 0;
+
+	BNXT_HWRM_LOCK(softc);
+	bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_PORT_PHY_I2C_READ);
+
+	req.i2c_slave_addr = i2c_addr;
+	req.page_number = htole16(page_number);
+	req.port_id = htole16(softc->pf.port_id);
+	do {
+		uint16_t xfer_size;
+
+		xfer_size = min_t(uint16_t, data_length, BNXT_MAX_PHY_I2C_RESP_SIZE);
+		data_length -= xfer_size;
+		req.page_offset = htole16(start_addr + byte_offset);
+		req.data_length = xfer_size;
+		req.bank_number = bank;
+		req.enables = htole32((start_addr + byte_offset ?
+				HWRM_PORT_PHY_I2C_READ_INPUT_ENABLES_PAGE_OFFSET : 0) |
+				(bank_sel_en ?
+				HWRM_PORT_PHY_I2C_READ_INPUT_ENABLES_BANK_NUMBER : 0));
+		rc = hwrm_send_message(softc, &req, sizeof(req));
+		if (!rc)
+			memcpy(buf + byte_offset, output->data, xfer_size);
+		byte_offset += xfer_size;
+	} while (!rc && data_length > 0);
+
+	BNXT_HWRM_UNLOCK(softc);
+
+	return rc;
+}
+
 int
 bnxt_hwrm_port_phy_qcfg(struct bnxt_softc *softc)
 {
@@ -2200,6 +2238,7 @@ bnxt_hwrm_port_phy_qcfg(struct bnxt_softc *softc)
 	if (rc)
 		goto exit;
 
+	memcpy(&link_info->phy_qcfg_resp, resp, sizeof(*resp));
 	link_info->phy_link_status = resp->link;
 	link_info->duplex =  resp->duplex_cfg;
 	link_info->auto_mode = resp->auto_mode;
@@ -2264,6 +2303,7 @@ bnxt_hwrm_port_phy_qcfg(struct bnxt_softc *softc)
 	link_info->transceiver = resp->xcvr_pkg_type;
 	link_info->phy_addr = resp->eee_config_phy_addr &
 	    HWRM_PORT_PHY_QCFG_OUTPUT_PHY_ADDR_MASK;
+	link_info->module_status = resp->module_status;
 
 exit:
 	BNXT_HWRM_UNLOCK(softc);
diff --git a/sys/dev/bnxt/if_bnxt.c b/sys/dev/bnxt/if_bnxt.c
index e4a14aa954f5..f82d32c3ff5b 100644
--- a/sys/dev/bnxt/if_bnxt.c
+++ b/sys/dev/bnxt/if_bnxt.c
@@ -224,6 +224,8 @@ static uint64_t bnxt_get_baudrate(struct bnxt_link_info *link);
 static void bnxt_get_wol_settings(struct bnxt_softc *softc);
 static int bnxt_wol_config(if_ctx_t ctx);
 static bool bnxt_if_needs_restart(if_ctx_t, enum iflib_restart_event);
+static int bnxt_i2c_req(if_ctx_t ctx, struct ifi2creq *i2c);
+static void bnxt_get_port_module_status(struct bnxt_softc *softc);
 
 /*
  * Device Interface Declaration
@@ -287,6 +289,7 @@ static device_method_t bnxt_iflib_methods[] = {
 	DEVMETHOD(ifdi_suspend, bnxt_suspend),
 	DEVMETHOD(ifdi_shutdown, bnxt_shutdown),
 	DEVMETHOD(ifdi_resume, bnxt_resume),
+	DEVMETHOD(ifdi_i2c_req, bnxt_i2c_req),
 
 	DEVMETHOD(ifdi_needs_restart, bnxt_if_needs_restart),
 
@@ -1807,6 +1810,33 @@ bnxt_rss_grp_tbl_init(struct bnxt_softc *softc)
 	}
 }
 
+static void bnxt_get_port_module_status(struct bnxt_softc *softc)
+{
+	struct bnxt_link_info *link_info = &softc->link_info;
+	struct hwrm_port_phy_qcfg_output *resp = &link_info->phy_qcfg_resp;
+	uint8_t module_status;
+
+	if (bnxt_update_link(softc, false))
+		return;
+
+	module_status = link_info->module_status;
+	switch (module_status) {
+	case HWRM_PORT_PHY_QCFG_OUTPUT_MODULE_STATUS_DISABLETX:
+	case HWRM_PORT_PHY_QCFG_OUTPUT_MODULE_STATUS_PWRDOWN:
+	case HWRM_PORT_PHY_QCFG_OUTPUT_MODULE_STATUS_WARNINGMSG:
+		device_printf(softc->dev, "Unqualified SFP+ module detected on port %d\n",
+			    softc->pf.port_id);
+		if (softc->hwrm_spec_code >= 0x10201) {
+			device_printf(softc->dev, "Module part number %s\n",
+				    resp->phy_vendor_partnumber);
+		}
+		if (module_status == HWRM_PORT_PHY_QCFG_OUTPUT_MODULE_STATUS_DISABLETX)
+			device_printf(softc->dev, "TX is disabled\n");
+		if (module_status == HWRM_PORT_PHY_QCFG_OUTPUT_MODULE_STATUS_PWRDOWN)
+			device_printf(softc->dev, "SFP+ module is shutdown\n");
+	}
+}
+
 /* Device configuration */
 static void
 bnxt_init(if_ctx_t ctx)
@@ -1969,6 +1999,7 @@ skip_def_cp_ring:
 	}
 
 	bnxt_do_enable_intr(&softc->def_cp_ring);
+	bnxt_get_port_module_status(softc);
 	bnxt_media_status(softc->ctx, &ifmr);
 	bnxt_hwrm_cfa_l2_set_rx_mask(softc, &softc->vnic_info);
 	return;
@@ -2874,6 +2905,33 @@ exit:
 	return rc;
 }
 
+static int
+bnxt_i2c_req(if_ctx_t ctx, struct ifi2creq *i2c)
+{
+	struct bnxt_softc *softc = iflib_get_softc(ctx);
+	uint8_t *data = i2c->data;
+	int rc;
+
+	/* No point in going further if phy status indicates
+	 * module is not inserted or if it is powered down or
+	 * if it is of type 10GBase-T
+	 */
+	if (softc->link_info.module_status >
+		HWRM_PORT_PHY_QCFG_OUTPUT_MODULE_STATUS_WARNINGMSG)
+		return -EOPNOTSUPP;
+
+	/* This feature is not supported in older firmware versions */
+	if (!BNXT_CHIP_P5(softc) ||
+	    (softc->hwrm_spec_code < 0x10202))
+		return -EOPNOTSUPP;
+
+
+	rc = bnxt_read_sfp_module_eeprom_info(softc, I2C_DEV_ADDR_A0, 0, 0, 0,
+		i2c->offset, i2c->len, data);
+
+	return rc;
+}
+
 /*
  * Support functions
  */
@@ -2890,6 +2948,7 @@ bnxt_probe_phy(struct bnxt_softc *softc)
 		return (rc);
 	}
 
+	bnxt_get_port_module_status(softc);
 	/*initialize the ethool setting copy with NVM settings */
 	if (link_info->auto_mode != HWRM_PORT_PHY_QCFG_OUTPUT_AUTO_MODE_NONE)
 		link_info->autoneg |= BNXT_AUTONEG_SPEED;