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)
{
/* 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;
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;
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;
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;
}
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,
.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,
};