Thread (2 messages) 2 messages, 2 authors, 2026-02-19

Re: [PATCH net v2] net: atlantic: fix reading SFP module info on some AQC100 cards

From: Jakub Kicinski <kuba@kernel.org>
Date: 2026-02-19 01:30:01
Also in: lkml

On Mon, 16 Feb 2026 16:49:12 -0600 Tiernan Hubble wrote:
Commit 853a2944aaf3 ("net: atlantic: support reading SFP module info")
added support for reading SFP module info on AQC100-based cards. However,
it only supports reading directly from the controller's hardware
registers, and this does not seem to be supported on certain cards,
including my TRENDnet TEG-10GECSFP V3. "ethtool -m" times out when reading
certain registers, even when I increase the read poll timeout values.

The DPDK "atlantic" driver reads module info via firmware calls instead of
directly reading the hardware registers, provided that the NIC's firmware
version supports it.

This change adapts the DPDK firmware call code to the kernel driver. It
preserves the old hardware-based module read code as a fallback when the
firmware does not support it, to avoid breaking cards that are currently
working.

Tested on 2 different TRENDnet TEG-10GECSFP V3 cards, both with firmware
version 3.1.121 (current at the time of this patch). Both cards correctly
reported module info for a passive DAC cable and 2 different 10G optical
transceivers.

Fixes: 853a2944aaf3 ("net: atlantic: support reading SFP module info")
Signed-off-by: Tiernan Hubble <redacted>
Overall looks quite nice, but I don't think it qualifies as a fix.
Fix means something regressed from previous release, crashes, etc
For the adapters in question IIUC ethtool -m has never worked before.
Please drop the Fixes tag and repost next week for net-next.

Some drive by comments below..
quoted hunk ↗ jump to hunk
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
index a6e1826dd5d7..5be9e9ee7e9f 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
@@ -983,6 +983,36 @@ static int aq_ethtool_set_phy_tunable(struct net_device *ndev,
 	return err;
 }
 
+static bool aq_ethtool_can_read_module_eeprom(struct aq_nic_s *aq_nic)
+{
+	return aq_nic->aq_fw_ops->read_module_eeprom ||
+		aq_nic->aq_hw_ops->hw_read_module_eeprom;
+}
+
+static int aq_ethtool_read_module_eeprom(struct aq_nic_s *aq_nic, u8 dev_addr,
+					 u8 reg_start_addr, int len, u8 *data)
+{
+	int err = -EOPNOTSUPP;
maybe add a temp variable:

	const struct aq_fw_ops *ops = aq_nic->aq_fw_ops;
+	if (aq_nic->aq_fw_ops->read_module_eeprom) {
+		err = aq_nic->aq_fw_ops->read_module_eeprom(aq_nic->aq_hw,
to avoid the long lines and awkward wrapping ?
+			dev_addr, reg_start_addr, len, data);
+
+		/* If the only error is that the firmware version doesn't
+		 * support reading EEPROM, we can still attempt to read it
+		 * directly from the hardware if supported.
+		 */
+		if (err != -EOPNOTSUPP)
+			return err;
+	}
+
+	if (aq_nic->aq_hw_ops->hw_read_module_eeprom)
+		err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
and here
+			dev_addr, reg_start_addr, len, data);
+
+	return err;
+}
+static int aq_fw2x_read_module_eeprom(struct aq_hw_s *self, u8 dev_addr,
+				      u8 reg_start_addr, int len, u8 *data)
+{
+	u32 low_status, orig_low_status, low_req = 0;
+	u32 res_bytes_remain_cnt = len % sizeof(u32);
+	u32 res_dword_cnt = len / sizeof(u32);
+	struct smbus_request request = { 0 };
+	u32 req_dword_cnt;
+	u32 result = 0;
+	u32 caps_lo;
+	u32 offset;
+	int err;
+
+	caps_lo = aq_fw2x_get_link_capabilities(self);
+	if (!(caps_lo & BIT(CAPS_LO_SMBUS_READ)))
+		return -EOPNOTSUPP;
+
+	request.msg_id = 0;
+	request.device_id = dev_addr;
+	request.address = reg_start_addr;
+	request.length = len;
+
+	/* Write SMBUS request to cfg memory */
+	req_dword_cnt = (sizeof(request) + sizeof(u32) - 1) / sizeof(u32);
DIV_ROUND_UP()
+	err = hw_atl_write_fwcfg_dwords(self, (void *)&request, req_dword_cnt);
+	if (err < 0)
+		return err;
+
+	/* Toggle 0x368.CAPS_LO_SMBUS_READ bit */
+	low_req = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR);
+	orig_low_status = low_req & BIT(CAPS_LO_SMBUS_READ);
the mixing of BIT(CAPS_LO_SMBUS_READ)..
+	low_req ^= HW_ATL_FW2X_CAP_SMBUS_READ;
.. and HW_ATL_FW2X_CAP_SMBUS_READ which is defined to

+#define HW_ATL_FW2X_CAP_SMBUS_READ       BIT(CAPS_LO_SMBUS_READ)

seems unnecessary and confusing?
-- 
pw-bot: cr
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help