diff mbox series

[net-next,2/6] bnxt_en: add support for QSFP optional EEPROM data

Message ID 1664326724-1415-3-git-send-email-michael.chan@broadcom.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series bnxt_en: Driver updates | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 5 this patch: 5
netdev/cc_maintainers success CCed 5 of 5 maintainers
netdev/build_clang success Errors and warnings before: 2 this patch: 2
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 4 this patch: 4
netdev/checkpatch warning WARNING: line length of 82 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Michael Chan Sept. 28, 2022, 12:58 a.m. UTC
From: Edwin Peer <edwin.peer@broadcom.com>

SFF 8636 defines several optional pages. This patch adds support up to
and including page 3. The ethtool offset needs to be mapped onto the
appropriate device page and I2C address, which is handled differently
depending on module type. The necessary linear offset to raw page
mapping is performed based on a table that is configured according to
the module capabilities.

Signed-off-by: Edwin Peer <edwin.peer@broadcom.com>
Signed-off-by: Michael Chan <michael.chan@broadcom.com>
---
 drivers/net/ethernet/broadcom/bnxt/bnxt.h     |   5 +
 .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 120 +++++++++++++++---
 2 files changed, 108 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index b1b17f911300..c54f8c9ab3ad 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -2206,6 +2206,11 @@  struct bnxt {
 #define SFF_MODULE_ID_QSFP			0xc
 #define SFF_MODULE_ID_QSFP_PLUS			0xd
 #define SFF_MODULE_ID_QSFP28			0x11
+#define SFF8636_FLATMEM_OFFSET			0x2
+#define SFF8636_FLATMEM_MASK			0x4
+#define SFF8636_OPT_PAGES_OFFSET		0xc3
+#define SFF8636_PAGE1_MASK			0x40
+#define SFF8636_PAGE2_MASK			0x80
 #define BNXT_MAX_PHY_I2C_RESP_SIZE		64
 
 static inline u32 bnxt_tx_avail(struct bnxt *bp, struct bnxt_tx_ring_info *txr)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index f57e524c7e30..6596dca94c3d 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -3220,7 +3220,9 @@  static int bnxt_get_module_info(struct net_device *dev,
 			break;
 		case SFF_MODULE_ID_QSFP28:
 			modinfo->type = ETH_MODULE_SFF_8636;
-			modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
+			modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
+			if (data[SFF8636_FLATMEM_OFFSET] & SFF8636_FLATMEM_MASK)
+				modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
 			break;
 		default:
 			rc = -EOPNOTSUPP;
@@ -3234,32 +3236,116 @@  static int bnxt_get_module_eeprom(struct net_device *dev,
 				  struct ethtool_eeprom *eeprom,
 				  u8 *data)
 {
+	u8 pg_addr[5] = { I2C_DEV_ADDR_A0, I2C_DEV_ADDR_A0 };
+	u16 offset = eeprom->offset, length = eeprom->len;
+	u8 module_info[SFF_DIAG_SUPPORT_OFFSET + 1];
 	struct bnxt *bp = netdev_priv(dev);
-	u16  start = eeprom->offset, length = eeprom->len;
+	u8 page = offset >> 7;
+	u8 max_pages = 2;
 	int rc = 0;
 
-	memset(data, 0, eeprom->len);
+	rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, 0,
+					      SFF_DIAG_SUPPORT_OFFSET + 1,
+					      module_info);
+	if (rc)
+		return rc;
+
+	switch (module_info[0]) {
+	case SFF_MODULE_ID_SFP:
+		if (module_info[SFF_DIAG_SUPPORT_OFFSET]) {
+			pg_addr[2] = I2C_DEV_ADDR_A2;
+			pg_addr[3] = I2C_DEV_ADDR_A2;
+			max_pages = 4;
+		}
+		break;
+	case SFF_MODULE_ID_QSFP28: {
+		u8 opt_pages;
 
-	/* Read A0 portion of the EEPROM */
-	if (start < ETH_MODULE_SFF_8436_LEN) {
-		if (start + eeprom->len > ETH_MODULE_SFF_8436_LEN)
-			length = ETH_MODULE_SFF_8436_LEN - start;
 		rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0,
-						      start, length, data);
+						      SFF8636_OPT_PAGES_OFFSET,
+						      1, &opt_pages);
 		if (rc)
 			return rc;
-		start += length;
-		data += length;
-		length = eeprom->len - length;
+
+		if (opt_pages & SFF8636_PAGE1_MASK) {
+			pg_addr[2] = I2C_DEV_ADDR_A0;
+			max_pages = 3;
+		}
+		if (opt_pages & SFF8636_PAGE2_MASK) {
+			pg_addr[3] = I2C_DEV_ADDR_A0;
+			max_pages = 4;
+		}
+		if (~module_info[SFF8636_FLATMEM_OFFSET] & SFF8636_FLATMEM_MASK) {
+			pg_addr[4] = I2C_DEV_ADDR_A0;
+			max_pages = 5;
+		}
+		break;
 	}
+	default:
+		break;
+	}
+
+	memset(data, 0, eeprom->len);
 
-	/* Read A2 portion of the EEPROM */
-	if (length) {
-		start -= ETH_MODULE_SFF_8436_LEN;
-		rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A2, 0,
-						      start, length, data);
+	/* Read the two 128B base pages in a single pass, since they are
+	 * always supported and both sourced from I2C_DEV_ADDR_A0. Then,
+	 * read individual 128B or 256B chunks as appropriate, according
+	 * to the mappings defined in pg_addr[], which is setup based on
+	 * module capabilities.
+	 *
+	 * The first two pages are both numbered page zero, lower page 0
+	 * and upper page 0 respectively. Raw device pages are numbered
+	 * sequentially thereafter. For SFP modules, reads are always
+	 * from page zero. In this case, the A2 base address is used in
+	 * lieu of the page number to signal reading the upper 256B, with
+	 * offsets relative to the base of this larger I2C region.
+	 *
+	 * Note there may be gaps in the linear ethtool mapping that are
+	 * not backed by raw module pages. Reads to such pages should not
+	 * be attempted because the HWRM call would fail. The caller will
+	 * simply see the preinitialized zeroes in these holes.
+	 *
+	 * Also note, the implementation below depends on pages mapped as
+	 * I2C_DEV_ADDR_A2 in pg_addr[] appearing as 256B aligned pairs.
+	 * This constraint means that it doesn't matter whether the even
+	 * or odd page is used in determining the I2C base address of a
+	 * given region. This allows for larger chunk sizes to be read
+	 * for A2 pages and happens to correspond nicely with the memory
+	 * maps of all currently supported modules. The optional 128B
+	 * A0 pages need to be read relative to an offset of 128B, which
+	 * is where they appear in module memory maps, while the 256B A2
+	 * page pair regions are interpreted by firmware relative to
+	 * offset 0.
+	 */
+	offset &= 0xff;
+	while (length && page < max_pages) {
+		u8 raw_page = page ? page - 1 : 0;
+		u16 chunk;
+
+		if (pg_addr[page] == I2C_DEV_ADDR_A2)
+			raw_page = 0;
+		else if (page)
+			offset |= 0x80;
+		chunk = min_t(u16, length, 256 - offset);
+
+		if (pg_addr[page]) {
+			rc = bnxt_read_sfp_module_eeprom_info(bp, pg_addr[page],
+							      raw_page, offset,
+							      chunk, data);
+			if (rc)
+				return rc;
+		}
+
+		data += chunk;
+		length -= chunk;
+		offset = 0;
+		page += 1 + (chunk > 128);
 	}
-	return rc;
+
+	if (length)
+		return -EINVAL;
+
+	return 0;
 }
 
 static int bnxt_nway_reset(struct net_device *dev)