@@ -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)
@@ -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)