diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index d13b727dee42..51d21328c73d 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -596,6 +596,8 @@ void pdsc_fw_up(struct pdsc *pdsc) return; } + pdsc_fw_components_invalidate(pdsc); + err = pdsc_setup(pdsc, PDSC_SETUP_RECOVERY); if (err) goto err_out; diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index 3b763ee1715e..63fe45e91f71 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -93,14 +93,120 @@ int pdsc_dl_flash_update(struct devlink *dl, return pdsc_firmware_update(pdsc, params, extack); } +static int pdsc_dl_report_component(struct devlink_info_req *req, + struct pds_core_fw_component_info *info) +{ + enum devlink_info_version_type ver_type; + u16 flags = le16_to_cpu(info->flags); + char *ver = info->version; + const char *name; + char buf[32]; + + /* Main firmware is reported as generic "fw" */ + if (info->component_type == PDS_CORE_FW_TYPE_MAIN) { + if (info->slot_id == PDS_CORE_FW_SLOT_GOLD) + snprintf(buf, sizeof(buf), "fw.gold"); + else + snprintf(buf, sizeof(buf), "fw"); + } else { + name = pdsc_fw_type_to_name(info->component_type); + if (!name) + return 0; + + if (info->slot_id == PDS_CORE_FW_SLOT_GOLD) + snprintf(buf, sizeof(buf), "fw.%s.gold", name); + else + snprintf(buf, sizeof(buf), "fw.%s", name); + } + + ver_type = DEVLINK_INFO_VERSION_TYPE_NONE; + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME) + ver_type = DEVLINK_INFO_VERSION_TYPE_COMPONENT; + + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_FIXED) { + int err; + + err = devlink_info_version_fixed_put(req, buf, ver); + if (err) + return err; + } + + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_RUNNING) { + int err; + + err = devlink_info_version_running_put_ext(req, buf, + ver, ver_type); + if (err) + return err; + } + + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_STARTUP) { + int err; + + err = devlink_info_version_stored_put_ext(req, buf, + ver, ver_type); + if (err) + return err; + } + + return 0; +} + +static int pdsc_dl_report_fw_ver(struct devlink_info_req *req, char *fw_ver) +{ + return devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + fw_ver); +} + +static int pdsc_dl_component_info_get(struct devlink *dl, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct pdsc *pdsc = devlink_priv(dl); + u8 num_components; + int err; + int i; + + /* Pairs with WRITE_ONCE in pdsc_fw_components_invalidate(). + * Use READ_ONCE to get a consistent snapshot of num_components. + * pdsc_fw_components_invalidate() can zero it concurrently during + * firmware recovery; using the local copy avoids iterating zero + * times when we already decided the cache was valid. + */ + num_components = READ_ONCE(pdsc->fw_components.num_components); + if (!num_components) { + err = pdsc_get_component_info(pdsc); + if (err) + return pdsc_dl_report_fw_ver(req, + pdsc->dev_info.fw_version); + num_components = READ_ONCE(pdsc->fw_components.num_components); + if (!num_components) + return pdsc_dl_report_fw_ver(req, + pdsc->dev_info.fw_version); + } + + num_components = min_t(u16, num_components, + le16_to_cpu(pdsc->dev_ident.max_fw_slots)); + for (i = 0; i < num_components; i++) { + err = pdsc_dl_report_component(req, + &pdsc->fw_components.info[i]); + if (err) + return err; + } + + return 0; +} + static char *fw_slotnames[] = { "fw.goldfw", "fw.mainfwa", "fw.mainfwb", }; -int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, - struct netlink_ext_ack *extack) +static int pdsc_dl_fw_list_info_get(struct devlink *dl, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) { union pds_core_dev_cmd cmd = { .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL, @@ -134,12 +240,41 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, return err; } - err = devlink_info_version_running_put(req, - DEVLINK_INFO_VERSION_GENERIC_FW, - pdsc->dev_info.fw_version); + return 0; +} + +static int pdsc_dl_info_get_v1(struct devlink *dl, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct pdsc *pdsc = devlink_priv(dl); + int err; + + err = pdsc_dl_fw_list_info_get(dl, req, extack); if (err) return err; + /* Version 1: report fw from dev_info (running only) */ + return pdsc_dl_report_fw_ver(req, pdsc->dev_info.fw_version); +} + +int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct pdsc *pdsc = devlink_priv(dl); + char buf[32]; + int err; + + if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2) { + err = pdsc_dl_component_info_get(dl, req, extack); + if (err) + return err; + } else { + err = pdsc_dl_info_get_v1(dl, req, extack); + if (err) + return err; + } + snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_type); err = devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,