summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c55
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_hw.h3
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h7
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c80
4 files changed, 135 insertions, 10 deletions
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
index a6e1826dd5d7..420af958d486 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
@@ -983,6 +983,38 @@ 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)
+{
+ const struct aq_fw_ops *fw_ops = aq_nic->aq_fw_ops;
+ const struct aq_hw_ops *hw_ops = aq_nic->aq_hw_ops;
+ int err = -EOPNOTSUPP;
+
+ if (fw_ops->read_module_eeprom) {
+ err = fw_ops->read_module_eeprom(aq_nic->aq_hw, 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 (hw_ops->hw_read_module_eeprom)
+ err = hw_ops->hw_read_module_eeprom(aq_nic->aq_hw, dev_addr,
+ reg_start_addr, len, data);
+
+ return err;
+}
+
static int aq_ethtool_get_module_info(struct net_device *ndev,
struct ethtool_modinfo *modinfo)
{
@@ -992,16 +1024,18 @@ static int aq_ethtool_get_module_info(struct net_device *ndev,
/* Module EEPROM is only supported for controllers with external PHY */
if (aq_nic->aq_nic_cfg.aq_hw_caps->media_type != AQ_HW_MEDIA_TYPE_FIBRE ||
- !aq_nic->aq_hw_ops->hw_read_module_eeprom)
+ !aq_ethtool_can_read_module_eeprom(aq_nic))
return -EOPNOTSUPP;
- err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
- SFF_8472_ID_ADDR, SFF_8472_COMP_ADDR, 1, &compliance_val);
+ err = aq_ethtool_read_module_eeprom(aq_nic, SFF_8472_ID_ADDR,
+ SFF_8472_COMP_ADDR, 1,
+ &compliance_val);
if (err)
return err;
- err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
- SFF_8472_ID_ADDR, SFF_8472_DOM_TYPE_ADDR, 1, &dom_type);
+ err = aq_ethtool_read_module_eeprom(aq_nic, SFF_8472_ID_ADDR,
+ SFF_8472_DOM_TYPE_ADDR, 1,
+ &dom_type);
if (err)
return err;
@@ -1022,7 +1056,7 @@ static int aq_ethtool_get_module_eeprom(struct net_device *ndev,
unsigned int first, last, len;
int err;
- if (!aq_nic->aq_hw_ops->hw_read_module_eeprom)
+ if (!aq_ethtool_can_read_module_eeprom(aq_nic))
return -EOPNOTSUPP;
first = ee->offset;
@@ -1032,8 +1066,8 @@ static int aq_ethtool_get_module_eeprom(struct net_device *ndev,
len = min(last, ETH_MODULE_SFF_8079_LEN);
len -= first;
- err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
- SFF_8472_ID_ADDR, first, len, data);
+ err = aq_ethtool_read_module_eeprom(aq_nic, SFF_8472_ID_ADDR,
+ first, len, data);
if (err)
return err;
@@ -1045,8 +1079,9 @@ static int aq_ethtool_get_module_eeprom(struct net_device *ndev,
len -= first;
first -= ETH_MODULE_SFF_8079_LEN;
- err = aq_nic->aq_hw_ops->hw_read_module_eeprom(aq_nic->aq_hw,
- SFF_8472_DIAGNOSTICS_ADDR, first, len, data);
+ err = aq_ethtool_read_module_eeprom(aq_nic,
+ SFF_8472_DIAGNOSTICS_ADDR,
+ first, len, data);
if (err)
return err;
}
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
index 4e66fd9b2ab1..57ea59026a2c 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
@@ -404,6 +404,9 @@ struct aq_fw_ops {
int (*send_macsec_req)(struct aq_hw_s *self,
struct macsec_msg_fw_request *msg,
struct macsec_msg_fw_response *resp);
+
+ int (*read_module_eeprom)(struct aq_hw_s *self, u8 dev_addr,
+ u8 reg_start_addr, int len, u8 *data);
};
#endif /* AQ_HW_H */
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
index f6b990b7f5b4..404c84adad4a 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
@@ -319,6 +319,13 @@ struct __packed hw_atl_utils_settings {
u32 media_detect;
};
+struct __packed smbus_request {
+ u32 msg_id;
+ u32 device_id;
+ u32 address;
+ u32 length;
+};
+
enum macsec_msg_type {
macsec_cfg_msg = 0,
macsec_add_rx_sc_msg,
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
index 4d4cfbc91e19..2cac0d9670bf 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
@@ -703,6 +703,85 @@ static int aq_fw2x_send_macsec_req(struct aq_hw_s *hw,
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 = DIV_ROUND_UP(sizeof(request), sizeof(u32));
+ 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);
+ low_req ^= BIT(CAPS_LO_SMBUS_READ);
+ aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR, low_req);
+
+ /* Wait FW to report back */
+ err = readx_poll_timeout_atomic(aq_fw2x_state_get, self, low_status,
+ orig_low_status != (low_status &
+ BIT(CAPS_LO_SMBUS_READ)),
+ 10U, 100000U);
+ if (err)
+ return err;
+
+ /* Read status of read operation */
+ offset = self->rpc_addr + sizeof(u32);
+ err = hw_atl_utils_fw_downld_dwords(self, offset, &result,
+ sizeof(result) / sizeof(u32));
+ if (err < 0)
+ return err;
+ if (result)
+ return -EIO;
+
+ /* Read response full DWORD data */
+ if (res_dword_cnt) {
+ offset = self->rpc_addr + sizeof(u32) * 2;
+ err = hw_atl_utils_fw_downld_dwords(self, offset, (u32 *)data,
+ res_dword_cnt);
+ if (err < 0)
+ return err;
+ }
+
+ /* Read response trailing bytes data */
+ if (res_bytes_remain_cnt) {
+ u32 bytes_remain_val = 0;
+
+ offset = self->rpc_addr +
+ (sizeof(u32) * 2) +
+ (res_dword_cnt * sizeof(u32));
+ err = hw_atl_utils_fw_downld_dwords(self, offset,
+ &bytes_remain_val, 1);
+ if (err < 0)
+ return err;
+
+ memcpy(data + len - res_bytes_remain_cnt,
+ &bytes_remain_val, res_bytes_remain_cnt);
+ }
+
+ return 0;
+}
+
const struct aq_fw_ops aq_fw_2x_ops = {
.init = aq_fw2x_init,
.deinit = aq_fw2x_deinit,
@@ -729,4 +808,5 @@ const struct aq_fw_ops aq_fw_2x_ops = {
.adjust_ptp = aq_fw3x_adjust_ptp,
.get_link_capabilities = aq_fw2x_get_link_capabilities,
.send_macsec_req = aq_fw2x_send_macsec_req,
+ .read_module_eeprom = aq_fw2x_read_module_eeprom,
};