Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: atlantic: fix reading SFP module info on some AQC100 cards

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.

Signed-off-by: Tiernan Hubble <thubble@thubble.ca>
Link: https://patch.msgid.link/20260225002026.1754045-1-thubble@thubble.ca
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Tiernan Hubble and committed by
Jakub Kicinski
b70190d7 ed02c6b8

+135 -10
+45 -10
drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
··· 983 983 return err; 984 984 } 985 985 986 + static bool aq_ethtool_can_read_module_eeprom(struct aq_nic_s *aq_nic) 987 + { 988 + return aq_nic->aq_fw_ops->read_module_eeprom || 989 + aq_nic->aq_hw_ops->hw_read_module_eeprom; 990 + } 991 + 992 + static int aq_ethtool_read_module_eeprom(struct aq_nic_s *aq_nic, u8 dev_addr, 993 + u8 reg_start_addr, int len, u8 *data) 994 + { 995 + const struct aq_fw_ops *fw_ops = aq_nic->aq_fw_ops; 996 + const struct aq_hw_ops *hw_ops = aq_nic->aq_hw_ops; 997 + int err = -EOPNOTSUPP; 998 + 999 + if (fw_ops->read_module_eeprom) { 1000 + err = fw_ops->read_module_eeprom(aq_nic->aq_hw, dev_addr, 1001 + reg_start_addr, len, data); 1002 + 1003 + /* If the only error is that the firmware version doesn't 1004 + * support reading EEPROM, we can still attempt to read it 1005 + * directly from the hardware if supported. 1006 + */ 1007 + if (err != -EOPNOTSUPP) 1008 + return err; 1009 + } 1010 + 1011 + if (hw_ops->hw_read_module_eeprom) 1012 + err = hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, dev_addr, 1013 + reg_start_addr, len, data); 1014 + 1015 + return err; 1016 + } 1017 + 986 1018 static int aq_ethtool_get_module_info(struct net_device *ndev, 987 1019 struct ethtool_modinfo *modinfo) 988 1020 { ··· 1024 992 1025 993 /* Module EEPROM is only supported for controllers with external PHY */ 1026 994 if (aq_nic->aq_nic_cfg.aq_hw_caps->media_type != AQ_HW_MEDIA_TYPE_FIBRE || 1027 - !aq_nic->aq_hw_ops->hw_read_module_eeprom) 995 + !aq_ethtool_can_read_module_eeprom(aq_nic)) 1028 996 return -EOPNOTSUPP; 1029 997 1030 - err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, 1031 - SFF_8472_ID_ADDR, SFF_8472_COMP_ADDR, 1, &compliance_val); 998 + err = aq_ethtool_read_module_eeprom(aq_nic, SFF_8472_ID_ADDR, 999 + SFF_8472_COMP_ADDR, 1, 1000 + &compliance_val); 1032 1001 if (err) 1033 1002 return err; 1034 1003 1035 - err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, 1036 - SFF_8472_ID_ADDR, SFF_8472_DOM_TYPE_ADDR, 1, &dom_type); 1004 + err = aq_ethtool_read_module_eeprom(aq_nic, SFF_8472_ID_ADDR, 1005 + SFF_8472_DOM_TYPE_ADDR, 1, 1006 + &dom_type); 1037 1007 if (err) 1038 1008 return err; 1039 1009 ··· 1056 1022 unsigned int first, last, len; 1057 1023 int err; 1058 1024 1059 - if (!aq_nic->aq_hw_ops->hw_read_module_eeprom) 1025 + if (!aq_ethtool_can_read_module_eeprom(aq_nic)) 1060 1026 return -EOPNOTSUPP; 1061 1027 1062 1028 first = ee->offset; ··· 1066 1032 len = min(last, ETH_MODULE_SFF_8079_LEN); 1067 1033 len -= first; 1068 1034 1069 - err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, 1070 - SFF_8472_ID_ADDR, first, len, data); 1035 + err = aq_ethtool_read_module_eeprom(aq_nic, SFF_8472_ID_ADDR, 1036 + first, len, data); 1071 1037 if (err) 1072 1038 return err; 1073 1039 ··· 1079 1045 len -= first; 1080 1046 first -= ETH_MODULE_SFF_8079_LEN; 1081 1047 1082 - err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, 1083 - SFF_8472_DIAGNOSTICS_ADDR, first, len, data); 1048 + err = aq_ethtool_read_module_eeprom(aq_nic, 1049 + SFF_8472_DIAGNOSTICS_ADDR, 1050 + first, len, data); 1084 1051 if (err) 1085 1052 return err; 1086 1053 }
+3
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
··· 404 404 int (*send_macsec_req)(struct aq_hw_s *self, 405 405 struct macsec_msg_fw_request *msg, 406 406 struct macsec_msg_fw_response *resp); 407 + 408 + int (*read_module_eeprom)(struct aq_hw_s *self, u8 dev_addr, 409 + u8 reg_start_addr, int len, u8 *data); 407 410 }; 408 411 409 412 #endif /* AQ_HW_H */
+7
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
··· 319 319 u32 media_detect; 320 320 }; 321 321 322 + struct __packed smbus_request { 323 + u32 msg_id; 324 + u32 device_id; 325 + u32 address; 326 + u32 length; 327 + }; 328 + 322 329 enum macsec_msg_type { 323 330 macsec_cfg_msg = 0, 324 331 macsec_add_rx_sc_msg,
+80
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
··· 703 703 return err; 704 704 } 705 705 706 + static int aq_fw2x_read_module_eeprom(struct aq_hw_s *self, u8 dev_addr, 707 + u8 reg_start_addr, int len, u8 *data) 708 + { 709 + u32 low_status, orig_low_status, low_req = 0; 710 + u32 res_bytes_remain_cnt = len % sizeof(u32); 711 + u32 res_dword_cnt = len / sizeof(u32); 712 + struct smbus_request request = { 0 }; 713 + u32 req_dword_cnt; 714 + u32 result = 0; 715 + u32 caps_lo; 716 + u32 offset; 717 + int err; 718 + 719 + caps_lo = aq_fw2x_get_link_capabilities(self); 720 + if (!(caps_lo & BIT(CAPS_LO_SMBUS_READ))) 721 + return -EOPNOTSUPP; 722 + 723 + request.msg_id = 0; 724 + request.device_id = dev_addr; 725 + request.address = reg_start_addr; 726 + request.length = len; 727 + 728 + /* Write SMBUS request to cfg memory */ 729 + req_dword_cnt = DIV_ROUND_UP(sizeof(request), sizeof(u32)); 730 + err = hw_atl_write_fwcfg_dwords(self, (void *)&request, req_dword_cnt); 731 + if (err < 0) 732 + return err; 733 + 734 + /* Toggle 0x368.CAPS_LO_SMBUS_READ bit */ 735 + low_req = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR); 736 + orig_low_status = low_req & BIT(CAPS_LO_SMBUS_READ); 737 + low_req ^= BIT(CAPS_LO_SMBUS_READ); 738 + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR, low_req); 739 + 740 + /* Wait FW to report back */ 741 + err = readx_poll_timeout_atomic(aq_fw2x_state_get, self, low_status, 742 + orig_low_status != (low_status & 743 + BIT(CAPS_LO_SMBUS_READ)), 744 + 10U, 100000U); 745 + if (err) 746 + return err; 747 + 748 + /* Read status of read operation */ 749 + offset = self->rpc_addr + sizeof(u32); 750 + err = hw_atl_utils_fw_downld_dwords(self, offset, &result, 751 + sizeof(result) / sizeof(u32)); 752 + if (err < 0) 753 + return err; 754 + if (result) 755 + return -EIO; 756 + 757 + /* Read response full DWORD data */ 758 + if (res_dword_cnt) { 759 + offset = self->rpc_addr + sizeof(u32) * 2; 760 + err = hw_atl_utils_fw_downld_dwords(self, offset, (u32 *)data, 761 + res_dword_cnt); 762 + if (err < 0) 763 + return err; 764 + } 765 + 766 + /* Read response trailing bytes data */ 767 + if (res_bytes_remain_cnt) { 768 + u32 bytes_remain_val = 0; 769 + 770 + offset = self->rpc_addr + 771 + (sizeof(u32) * 2) + 772 + (res_dword_cnt * sizeof(u32)); 773 + err = hw_atl_utils_fw_downld_dwords(self, offset, 774 + &bytes_remain_val, 1); 775 + if (err < 0) 776 + return err; 777 + 778 + memcpy(data + len - res_bytes_remain_cnt, 779 + &bytes_remain_val, res_bytes_remain_cnt); 780 + } 781 + 782 + return 0; 783 + } 784 + 706 785 const struct aq_fw_ops aq_fw_2x_ops = { 707 786 .init = aq_fw2x_init, 708 787 .deinit = aq_fw2x_deinit, ··· 808 729 .adjust_ptp = aq_fw3x_adjust_ptp, 809 730 .get_link_capabilities = aq_fw2x_get_link_capabilities, 810 731 .send_macsec_req = aq_fw2x_send_macsec_req, 732 + .read_module_eeprom = aq_fw2x_read_module_eeprom, 811 733 };