Skip to content

Commit 853a294

Browse files
lorenzlunn
authored andcommitted
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 <[email protected]> Reviewed-by: Simon Horman <[email protected]> Message-ID: <[email protected]> Signed-off-by: Andrew Lunn <[email protected]>
1 parent 69297b0 commit 853a294

File tree

7 files changed

+312
-0
lines changed

7 files changed

+312
-0
lines changed

drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "aq_macsec.h"
1616
#include "aq_main.h"
1717

18+
#include <linux/ethtool.h>
1819
#include <linux/linkmode.h>
1920
#include <linux/ptp_clock_kernel.h>
2021

@@ -977,6 +978,76 @@ static int aq_ethtool_set_phy_tunable(struct net_device *ndev,
977978
return err;
978979
}
979980

981+
static int aq_ethtool_get_module_info(struct net_device *ndev,
982+
struct ethtool_modinfo *modinfo)
983+
{
984+
struct aq_nic_s *aq_nic = netdev_priv(ndev);
985+
u8 compliance_val, dom_type;
986+
int err;
987+
988+
/* Module EEPROM is only supported for controllers with external PHY */
989+
if (aq_nic->aq_nic_cfg.aq_hw_caps->media_type != AQ_HW_MEDIA_TYPE_FIBRE ||
990+
!aq_nic->aq_hw_ops->hw_read_module_eeprom)
991+
return -EOPNOTSUPP;
992+
993+
err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
994+
SFF_8472_ID_ADDR, SFF_8472_COMP_ADDR, 1, &compliance_val);
995+
if (err)
996+
return err;
997+
998+
err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
999+
SFF_8472_ID_ADDR, SFF_8472_DOM_TYPE_ADDR, 1, &dom_type);
1000+
if (err)
1001+
return err;
1002+
1003+
if (dom_type & SFF_8472_ADDRESS_CHANGE_REQ_MASK || compliance_val == 0x00) {
1004+
modinfo->type = ETH_MODULE_SFF_8079;
1005+
modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
1006+
} else {
1007+
modinfo->type = ETH_MODULE_SFF_8472;
1008+
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
1009+
}
1010+
return 0;
1011+
}
1012+
1013+
static int aq_ethtool_get_module_eeprom(struct net_device *ndev,
1014+
struct ethtool_eeprom *ee, unsigned char *data)
1015+
{
1016+
struct aq_nic_s *aq_nic = netdev_priv(ndev);
1017+
unsigned int first, last, len;
1018+
int err;
1019+
1020+
if (!aq_nic->aq_hw_ops->hw_read_module_eeprom)
1021+
return -EOPNOTSUPP;
1022+
1023+
first = ee->offset;
1024+
last = ee->offset + ee->len;
1025+
1026+
if (first < ETH_MODULE_SFF_8079_LEN) {
1027+
len = min(last, ETH_MODULE_SFF_8079_LEN);
1028+
len -= first;
1029+
1030+
err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
1031+
SFF_8472_ID_ADDR, first, len, data);
1032+
if (err)
1033+
return err;
1034+
1035+
first += len;
1036+
data += len;
1037+
}
1038+
if (first < ETH_MODULE_SFF_8472_LEN && last > ETH_MODULE_SFF_8079_LEN) {
1039+
len = min(last, ETH_MODULE_SFF_8472_LEN);
1040+
len -= first;
1041+
first -= ETH_MODULE_SFF_8079_LEN;
1042+
1043+
err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
1044+
SFF_8472_DIAGNOSTICS_ADDR, first, len, data);
1045+
if (err)
1046+
return err;
1047+
}
1048+
return 0;
1049+
}
1050+
9801051
const struct ethtool_ops aq_ethtool_ops = {
9811052
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
9821053
ETHTOOL_COALESCE_MAX_FRAMES,
@@ -1014,4 +1085,6 @@ const struct ethtool_ops aq_ethtool_ops = {
10141085
.get_ts_info = aq_ethtool_get_ts_info,
10151086
.get_phy_tunable = aq_ethtool_get_phy_tunable,
10161087
.set_phy_tunable = aq_ethtool_set_phy_tunable,
1088+
.get_module_info = aq_ethtool_get_module_info,
1089+
.get_module_eeprom = aq_ethtool_get_module_eeprom,
10171090
};

drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,12 @@
1414
extern const struct ethtool_ops aq_ethtool_ops;
1515
#define AQ_PRIV_FLAGS_MASK (AQ_HW_LOOPBACK_MASK)
1616

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+
1725
#endif /* AQ_ETHTOOL_H */

drivers/net/ethernet/aquantia/atlantic/aq_hw.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ struct aq_hw_ops {
340340
int (*hw_set_loopback)(struct aq_hw_s *self, u32 mode, bool enable);
341341

342342
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);
343346
};
344347

345348
struct aq_fw_ops {

drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,137 @@ static int hw_atl_b0_get_mac_temp(struct aq_hw_s *self, u32 *temp)
16541654
return 0;
16551655
}
16561656

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+
16571788
const struct aq_hw_ops hw_atl_ops_b0 = {
16581789
.hw_soft_reset = hw_atl_utils_soft_reset,
16591790
.hw_prepare = hw_atl_utils_initfw,
@@ -1712,4 +1843,5 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
17121843
.hw_set_fc = hw_atl_b0_set_fc,
17131844

17141845
.hw_get_mac_temp = hw_atl_b0_get_mac_temp,
1846+
.hw_read_module_eeprom = hw_atl_b0_read_module_eeprom,
17151847
};

drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,49 @@ u32 hw_atl_ts_data_get(struct aq_hw_s *aq_hw)
5757
HW_ATL_TS_DATA_OUT_SHIFT);
5858
}
5959

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+
60103
/* global */
61104
void hw_atl_reg_glb_cpu_sem_set(struct aq_hw_s *aq_hw, u32 glb_cpu_sem,
62105
u32 semaphore)

drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@ u32 hw_atl_ts_ready_latch_high_get(struct aq_hw_s *aq_hw);
3434
/* get temperature sense data */
3535
u32 hw_atl_ts_data_get(struct aq_hw_s *aq_hw);
3636

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+
3758
/* global */
3859

3960
/* set global microprocessor semaphore */

drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,38 @@
4242
#define HW_ATL_TS_DATA_OUT_SHIFT 0
4343
#define HW_ATL_TS_DATA_OUT_WIDTH 12
4444

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+
4577
/* global microprocessor semaphore definitions
4678
* base address: 0x000003a0
4779
* parameter: semaphore {s} | stride size 0x4 | range [0, 15]

0 commit comments

Comments
 (0)