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

net: atlantic: support reading SFP module info

Add support for reading SFP module info and digital diagnostic
monitoring data if supported by the module. The only Aquantia
controller without an integrated PHY is the AQC100 which belongs to
the B0 revision, that's why it's only implemented there.

The register information was extracted from a diagnostic tool made
publicly available by Dell, but all code was written from scratch by me.

This has been tested to work with a variety of both optical and direct
attach modules I had lying around and seems to work fine with all of
them, including the diagnostics if supported by an optical module.
All tests have been done with an AQC100 on an TL-NT521F card on firmware
version 3.1.121 (current at the time of this patch).

Signed-off-by: Lorenz Brun <lorenz@brun.one>
Reviewed-by: Simon Horman <horms@kernel.org>
Message-ID: <20241018171721.2577386-1-lorenz@brun.one>
Signed-off-by: Andrew Lunn <andrew@lunn.ch>

authored by

Lorenz Brun and committed by
Andrew Lunn
853a2944 69297b0d

+312
+73
drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
··· 15 15 #include "aq_macsec.h" 16 16 #include "aq_main.h" 17 17 18 + #include <linux/ethtool.h> 18 19 #include <linux/linkmode.h> 19 20 #include <linux/ptp_clock_kernel.h> 20 21 ··· 978 977 return err; 979 978 } 980 979 980 + static int aq_ethtool_get_module_info(struct net_device *ndev, 981 + struct ethtool_modinfo *modinfo) 982 + { 983 + struct aq_nic_s *aq_nic = netdev_priv(ndev); 984 + u8 compliance_val, dom_type; 985 + int err; 986 + 987 + /* Module EEPROM is only supported for controllers with external PHY */ 988 + if (aq_nic->aq_nic_cfg.aq_hw_caps->media_type != AQ_HW_MEDIA_TYPE_FIBRE || 989 + !aq_nic->aq_hw_ops->hw_read_module_eeprom) 990 + return -EOPNOTSUPP; 991 + 992 + err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, 993 + SFF_8472_ID_ADDR, SFF_8472_COMP_ADDR, 1, &compliance_val); 994 + if (err) 995 + return err; 996 + 997 + err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, 998 + SFF_8472_ID_ADDR, SFF_8472_DOM_TYPE_ADDR, 1, &dom_type); 999 + if (err) 1000 + return err; 1001 + 1002 + if (dom_type & SFF_8472_ADDRESS_CHANGE_REQ_MASK || compliance_val == 0x00) { 1003 + modinfo->type = ETH_MODULE_SFF_8079; 1004 + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; 1005 + } else { 1006 + modinfo->type = ETH_MODULE_SFF_8472; 1007 + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; 1008 + } 1009 + return 0; 1010 + } 1011 + 1012 + static int aq_ethtool_get_module_eeprom(struct net_device *ndev, 1013 + struct ethtool_eeprom *ee, unsigned char *data) 1014 + { 1015 + struct aq_nic_s *aq_nic = netdev_priv(ndev); 1016 + unsigned int first, last, len; 1017 + int err; 1018 + 1019 + if (!aq_nic->aq_hw_ops->hw_read_module_eeprom) 1020 + return -EOPNOTSUPP; 1021 + 1022 + first = ee->offset; 1023 + last = ee->offset + ee->len; 1024 + 1025 + if (first < ETH_MODULE_SFF_8079_LEN) { 1026 + len = min(last, ETH_MODULE_SFF_8079_LEN); 1027 + len -= first; 1028 + 1029 + err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, 1030 + SFF_8472_ID_ADDR, first, len, data); 1031 + if (err) 1032 + return err; 1033 + 1034 + first += len; 1035 + data += len; 1036 + } 1037 + if (first < ETH_MODULE_SFF_8472_LEN && last > ETH_MODULE_SFF_8079_LEN) { 1038 + len = min(last, ETH_MODULE_SFF_8472_LEN); 1039 + len -= first; 1040 + first -= ETH_MODULE_SFF_8079_LEN; 1041 + 1042 + err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, 1043 + SFF_8472_DIAGNOSTICS_ADDR, first, len, data); 1044 + if (err) 1045 + return err; 1046 + } 1047 + return 0; 1048 + } 1049 + 981 1050 const struct ethtool_ops aq_ethtool_ops = { 982 1051 .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 983 1052 ETHTOOL_COALESCE_MAX_FRAMES, ··· 1085 1014 .get_ts_info = aq_ethtool_get_ts_info, 1086 1015 .get_phy_tunable = aq_ethtool_get_phy_tunable, 1087 1016 .set_phy_tunable = aq_ethtool_set_phy_tunable, 1017 + .get_module_info = aq_ethtool_get_module_info, 1018 + .get_module_eeprom = aq_ethtool_get_module_eeprom, 1088 1019 };
+8
drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h
··· 14 14 extern const struct ethtool_ops aq_ethtool_ops; 15 15 #define AQ_PRIV_FLAGS_MASK (AQ_HW_LOOPBACK_MASK) 16 16 17 + #define SFF_8472_ID_ADDR 0x50 18 + #define SFF_8472_DIAGNOSTICS_ADDR 0x51 19 + 20 + #define SFF_8472_COMP_ADDR 0x5e 21 + #define SFF_8472_DOM_TYPE_ADDR 0x5c 22 + 23 + #define SFF_8472_ADDRESS_CHANGE_REQ_MASK 0x4 24 + 17 25 #endif /* AQ_ETHTOOL_H */
+3
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
··· 340 340 int (*hw_set_loopback)(struct aq_hw_s *self, u32 mode, bool enable); 341 341 342 342 int (*hw_get_mac_temp)(struct aq_hw_s *self, u32 *temp); 343 + 344 + int (*hw_read_module_eeprom)(struct aq_hw_s *self, u8 dev_addr, 345 + u8 reg_start_addr, int len, u8 *data); 343 346 }; 344 347 345 348 struct aq_fw_ops {
+132
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
··· 1654 1654 return 0; 1655 1655 } 1656 1656 1657 + #define START_TRANSMIT 0x5001 1658 + #define START_READ_TRANSMIT 0x5101 1659 + #define STOP_TRANSMIT 0x3001 1660 + #define REPEAT_TRANSMIT 0x1001 1661 + #define REPEAT_NACK_TRANSMIT 0x1011 1662 + 1663 + static int hw_atl_b0_smb0_wait_result(struct aq_hw_s *self, bool expect_ack) 1664 + { 1665 + int err; 1666 + u32 val; 1667 + 1668 + err = readx_poll_timeout(hw_atl_smb0_byte_transfer_complete_get, 1669 + self, val, val == 1, 100U, 10000U); 1670 + if (err) 1671 + return err; 1672 + if (hw_atl_smb0_receive_acknowledged_get(self) != expect_ack) 1673 + return -EIO; 1674 + return 0; 1675 + } 1676 + 1677 + /* Starts an I2C/SMBUS write to a given address. addr is in 7-bit format, 1678 + * the read/write bit is not part of it. 1679 + */ 1680 + static int hw_atl_b0_smb0_start_write(struct aq_hw_s *self, u32 addr) 1681 + { 1682 + hw_atl_smb0_tx_data_set(self, (addr << 1) | 0); 1683 + hw_atl_smb0_provisioning2_set(self, START_TRANSMIT); 1684 + return hw_atl_b0_smb0_wait_result(self, 0); 1685 + } 1686 + 1687 + /* Writes a single byte as part of an ongoing write started by start_write. */ 1688 + static int hw_atl_b0_smb0_write_byte(struct aq_hw_s *self, u32 data) 1689 + { 1690 + hw_atl_smb0_tx_data_set(self, data); 1691 + hw_atl_smb0_provisioning2_set(self, REPEAT_TRANSMIT); 1692 + return hw_atl_b0_smb0_wait_result(self, 0); 1693 + } 1694 + 1695 + /* Starts an I2C/SMBUS read to a given address. addr is in 7-bit format, 1696 + * the read/write bit is not part of it. 1697 + */ 1698 + static int hw_atl_b0_smb0_start_read(struct aq_hw_s *self, u32 addr) 1699 + { 1700 + int err; 1701 + 1702 + hw_atl_smb0_tx_data_set(self, (addr << 1) | 1); 1703 + hw_atl_smb0_provisioning2_set(self, START_READ_TRANSMIT); 1704 + err = hw_atl_b0_smb0_wait_result(self, 0); 1705 + if (err) 1706 + return err; 1707 + if (hw_atl_smb0_repeated_start_detect_get(self) == 0) 1708 + return -EIO; 1709 + return 0; 1710 + } 1711 + 1712 + /* Reads a single byte as part of an ongoing read started by start_read. */ 1713 + static int hw_atl_b0_smb0_read_byte(struct aq_hw_s *self) 1714 + { 1715 + int err; 1716 + 1717 + hw_atl_smb0_provisioning2_set(self, REPEAT_TRANSMIT); 1718 + err = hw_atl_b0_smb0_wait_result(self, 0); 1719 + if (err) 1720 + return err; 1721 + return hw_atl_smb0_rx_data_get(self); 1722 + } 1723 + 1724 + /* Reads the last byte of an ongoing read. */ 1725 + static int hw_atl_b0_smb0_read_byte_nack(struct aq_hw_s *self) 1726 + { 1727 + int err; 1728 + 1729 + hw_atl_smb0_provisioning2_set(self, REPEAT_NACK_TRANSMIT); 1730 + err = hw_atl_b0_smb0_wait_result(self, 1); 1731 + if (err) 1732 + return err; 1733 + return hw_atl_smb0_rx_data_get(self); 1734 + } 1735 + 1736 + /* Sends a stop condition and ends a transfer. */ 1737 + static void hw_atl_b0_smb0_stop(struct aq_hw_s *self) 1738 + { 1739 + hw_atl_smb0_provisioning2_set(self, STOP_TRANSMIT); 1740 + } 1741 + 1742 + static int hw_atl_b0_read_module_eeprom(struct aq_hw_s *self, u8 dev_addr, 1743 + u8 reg_start_addr, int len, u8 *data) 1744 + { 1745 + int i, b; 1746 + int err; 1747 + u32 val; 1748 + 1749 + /* Wait for SMBUS0 to be idle */ 1750 + err = readx_poll_timeout(hw_atl_smb0_bus_busy_get, self, 1751 + val, val == 0, 100U, 10000U); 1752 + if (err) 1753 + return err; 1754 + 1755 + err = hw_atl_b0_smb0_start_write(self, dev_addr); 1756 + if (err) 1757 + goto out; 1758 + 1759 + err = hw_atl_b0_smb0_write_byte(self, reg_start_addr); 1760 + if (err) 1761 + goto out; 1762 + 1763 + err = hw_atl_b0_smb0_start_read(self, dev_addr); 1764 + if (err) 1765 + goto out; 1766 + 1767 + for (i = 0; i < len - 1; i++) { 1768 + b = hw_atl_b0_smb0_read_byte(self); 1769 + if (b < 0) { 1770 + err = b; 1771 + goto out; 1772 + } 1773 + data[i] = (u8)b; 1774 + } 1775 + 1776 + b = hw_atl_b0_smb0_read_byte_nack(self); 1777 + if (b < 0) { 1778 + err = b; 1779 + goto out; 1780 + } 1781 + data[i] = (u8)b; 1782 + 1783 + out: 1784 + hw_atl_b0_smb0_stop(self); 1785 + return err; 1786 + } 1787 + 1657 1788 const struct aq_hw_ops hw_atl_ops_b0 = { 1658 1789 .hw_soft_reset = hw_atl_utils_soft_reset, 1659 1790 .hw_prepare = hw_atl_utils_initfw, ··· 1843 1712 .hw_set_fc = hw_atl_b0_set_fc, 1844 1713 1845 1714 .hw_get_mac_temp = hw_atl_b0_get_mac_temp, 1715 + .hw_read_module_eeprom = hw_atl_b0_read_module_eeprom, 1846 1716 };
+43
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
··· 57 57 HW_ATL_TS_DATA_OUT_SHIFT); 58 58 } 59 59 60 + u32 hw_atl_smb0_bus_busy_get(struct aq_hw_s *aq_hw) 61 + { 62 + return aq_hw_read_reg_bit(aq_hw, HW_ATL_SMB0_BUS_BUSY_ADR, 63 + HW_ATL_SMB0_BUS_BUSY_MSK, 64 + HW_ATL_SMB0_BUS_BUSY_SHIFT); 65 + } 66 + 67 + u32 hw_atl_smb0_byte_transfer_complete_get(struct aq_hw_s *aq_hw) 68 + { 69 + return aq_hw_read_reg_bit(aq_hw, HW_ATL_SMB0_BYTE_TRANSFER_COMPLETE_ADR, 70 + HW_ATL_SMB0_BYTE_TRANSFER_COMPLETE_MSK, 71 + HW_ATL_SMB0_BYTE_TRANSFER_COMPLETE_SHIFT); 72 + } 73 + 74 + u32 hw_atl_smb0_receive_acknowledged_get(struct aq_hw_s *aq_hw) 75 + { 76 + return aq_hw_read_reg_bit(aq_hw, HW_ATL_SMB0_RX_ACKNOWLEDGED_ADR, 77 + HW_ATL_SMB0_RX_ACKNOWLEDGED_MSK, 78 + HW_ATL_SMB0_RX_ACKNOWLEDGED_SHIFT); 79 + } 80 + 81 + u32 hw_atl_smb0_repeated_start_detect_get(struct aq_hw_s *aq_hw) 82 + { 83 + return aq_hw_read_reg_bit(aq_hw, HW_ATL_SMB0_REPEATED_START_DETECT_ADR, 84 + HW_ATL_SMB0_REPEATED_START_DETECT_MSK, 85 + HW_ATL_SMB0_REPEATED_START_DETECT_SHIFT); 86 + } 87 + 88 + u32 hw_atl_smb0_rx_data_get(struct aq_hw_s *aq_hw) 89 + { 90 + return aq_hw_read_reg(aq_hw, HW_ATL_SMB0_RECEIVED_DATA_ADR); 91 + } 92 + 93 + void hw_atl_smb0_tx_data_set(struct aq_hw_s *aq_hw, u32 data) 94 + { 95 + return aq_hw_write_reg(aq_hw, HW_ATL_SMB0_TRANSMITTED_DATA_ADR, data); 96 + } 97 + 98 + void hw_atl_smb0_provisioning2_set(struct aq_hw_s *aq_hw, u32 data) 99 + { 100 + return aq_hw_write_reg(aq_hw, HW_ATL_SMB0_PROVISIONING2_ADR, data); 101 + } 102 + 60 103 /* global */ 61 104 void hw_atl_reg_glb_cpu_sem_set(struct aq_hw_s *aq_hw, u32 glb_cpu_sem, 62 105 u32 semaphore)
+21
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
··· 34 34 /* get temperature sense data */ 35 35 u32 hw_atl_ts_data_get(struct aq_hw_s *aq_hw); 36 36 37 + /* SMBUS0 bus busy */ 38 + u32 hw_atl_smb0_bus_busy_get(struct aq_hw_s *aq_hw); 39 + 40 + /* SMBUS0 byte transfer complete */ 41 + u32 hw_atl_smb0_byte_transfer_complete_get(struct aq_hw_s *aq_hw); 42 + 43 + /* SMBUS0 receive acknowledged */ 44 + u32 hw_atl_smb0_receive_acknowledged_get(struct aq_hw_s *aq_hw); 45 + 46 + /* SMBUS0 set transmitted data (only leftmost byte of data valid) */ 47 + void hw_atl_smb0_tx_data_set(struct aq_hw_s *aq_hw, u32 data); 48 + 49 + /* SMBUS0 provisioning2 command register */ 50 + void hw_atl_smb0_provisioning2_set(struct aq_hw_s *aq_hw, u32 data); 51 + 52 + /* SMBUS0 repeated start detect */ 53 + u32 hw_atl_smb0_repeated_start_detect_get(struct aq_hw_s *aq_hw); 54 + 55 + /* SMBUS0 received data register */ 56 + u32 hw_atl_smb0_rx_data_get(struct aq_hw_s *aq_hw); 57 + 37 58 /* global */ 38 59 39 60 /* set global microprocessor semaphore */
+32
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h
··· 42 42 #define HW_ATL_TS_DATA_OUT_SHIFT 0 43 43 #define HW_ATL_TS_DATA_OUT_WIDTH 12 44 44 45 + /* SMBUS0 Received Data register */ 46 + #define HW_ATL_SMB0_RECEIVED_DATA_ADR 0x00000748 47 + /* SMBUS0 Transmitted Data register */ 48 + #define HW_ATL_SMB0_TRANSMITTED_DATA_ADR 0x00000608 49 + 50 + /* SMBUS0 Global Provisioning 2 register */ 51 + #define HW_ATL_SMB0_PROVISIONING2_ADR 0x00000604 52 + 53 + /* SMBUS0 Bus Busy Bitfield Definitions */ 54 + #define HW_ATL_SMB0_BUS_BUSY_ADR 0x00000744 55 + #define HW_ATL_SMB0_BUS_BUSY_MSK 0x00000080 56 + #define HW_ATL_SMB0_BUS_BUSY_SHIFT 7 57 + #define HW_ATL_SMB0_BUS_BUSY_WIDTH 1 58 + 59 + /* SMBUS0 Byte Transfer Complete Bitfield Definitions */ 60 + #define HW_ATL_SMB0_BYTE_TRANSFER_COMPLETE_ADR 0x00000744 61 + #define HW_ATL_SMB0_BYTE_TRANSFER_COMPLETE_MSK 0x00000002 62 + #define HW_ATL_SMB0_BYTE_TRANSFER_COMPLETE_SHIFT 1 63 + #define HW_ATL_SMB0_BYTE_TRANSFER_COMPLETE_WIDTH 1 64 + 65 + /* SMBUS0 Receive Acknowledge Bitfield Definitions */ 66 + #define HW_ATL_SMB0_RX_ACKNOWLEDGED_ADR 0x00000744 67 + #define HW_ATL_SMB0_RX_ACKNOWLEDGED_MSK 0x00000100 68 + #define HW_ATL_SMB0_RX_ACKNOWLEDGED_SHIFT 8 69 + #define HW_ATL_SMB0_RX_ACKNOWLEDGED_WIDTH 1 70 + 71 + /* SMBUS0 Repeated Start Detect Bitfield Definitions */ 72 + #define HW_ATL_SMB0_REPEATED_START_DETECT_ADR 0x00000744 73 + #define HW_ATL_SMB0_REPEATED_START_DETECT_MSK 0x00000004 74 + #define HW_ATL_SMB0_REPEATED_START_DETECT_SHIFT 2 75 + #define HW_ATL_SMB0_REPEATED_START_DETECT_WIDTH 1 76 + 45 77 /* global microprocessor semaphore definitions 46 78 * base address: 0x000003a0 47 79 * parameter: semaphore {s} | stride size 0x4 | range [0, 15]