diff options
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, }; |
