| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright(c) 2023 Advanced Micro Devices, Inc */ |
| |
| #include "core.h" |
| #include <linux/pds/pds_auxbus.h> |
| |
| static struct |
| pdsc_viftype *pdsc_dl_find_viftype_by_id(struct pdsc *pdsc, |
| enum devlink_param_type dl_id) |
| { |
| int vt; |
| |
| if (!pdsc->viftype_status) |
| return NULL; |
| |
| for (vt = 0; vt < PDS_DEV_TYPE_MAX; vt++) { |
| if (pdsc->viftype_status[vt].dl_id == dl_id) |
| return &pdsc->viftype_status[vt]; |
| } |
| |
| return NULL; |
| } |
| |
| int pdsc_dl_enable_get(struct devlink *dl, u32 id, |
| struct devlink_param_gset_ctx *ctx) |
| { |
| struct pdsc *pdsc = devlink_priv(dl); |
| struct pdsc_viftype *vt_entry; |
| |
| vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id); |
| if (!vt_entry) |
| return -ENOENT; |
| |
| ctx->val.vbool = vt_entry->enabled; |
| |
| return 0; |
| } |
| |
| int pdsc_dl_enable_set(struct devlink *dl, u32 id, |
| struct devlink_param_gset_ctx *ctx) |
| { |
| struct pdsc *pdsc = devlink_priv(dl); |
| struct pdsc_viftype *vt_entry; |
| int err = 0; |
| int vf_id; |
| |
| vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id); |
| if (!vt_entry || !vt_entry->supported) |
| return -EOPNOTSUPP; |
| |
| if (vt_entry->enabled == ctx->val.vbool) |
| return 0; |
| |
| vt_entry->enabled = ctx->val.vbool; |
| for (vf_id = 0; vf_id < pdsc->num_vfs; vf_id++) { |
| struct pdsc *vf = pdsc->vfs[vf_id].vf; |
| |
| err = ctx->val.vbool ? pdsc_auxbus_dev_add(vf, pdsc) : |
| pdsc_auxbus_dev_del(vf, pdsc); |
| } |
| |
| return err; |
| } |
| |
| int pdsc_dl_enable_validate(struct devlink *dl, u32 id, |
| union devlink_param_value val, |
| struct netlink_ext_ack *extack) |
| { |
| struct pdsc *pdsc = devlink_priv(dl); |
| struct pdsc_viftype *vt_entry; |
| |
| vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id); |
| if (!vt_entry || !vt_entry->supported) |
| return -EOPNOTSUPP; |
| |
| if (!pdsc->viftype_status[vt_entry->vif_id].supported) |
| return -ENODEV; |
| |
| return 0; |
| } |
| |
| int pdsc_dl_flash_update(struct devlink *dl, |
| struct devlink_flash_update_params *params, |
| struct netlink_ext_ack *extack) |
| { |
| struct pdsc *pdsc = devlink_priv(dl); |
| |
| return pdsc_firmware_update(pdsc, params->fw, extack); |
| } |
| |
| 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) |
| { |
| union pds_core_dev_cmd cmd = { |
| .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL, |
| .fw_control.oper = PDS_CORE_FW_GET_LIST, |
| }; |
| struct pds_core_fw_list_info fw_list; |
| struct pdsc *pdsc = devlink_priv(dl); |
| union pds_core_dev_comp comp; |
| char buf[32]; |
| int listlen; |
| int err; |
| int i; |
| |
| mutex_lock(&pdsc->devcmd_lock); |
| err = pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2); |
| if (!err) |
| memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list)); |
| mutex_unlock(&pdsc->devcmd_lock); |
| if (err && err != -EIO) |
| return err; |
| |
| listlen = fw_list.num_fw_slots; |
| for (i = 0; i < listlen; i++) { |
| if (i < ARRAY_SIZE(fw_slotnames)) |
| strscpy(buf, fw_slotnames[i], sizeof(buf)); |
| else |
| snprintf(buf, sizeof(buf), "fw.slot_%d", i); |
| err = devlink_info_version_stored_put(req, buf, |
| fw_list.fw_names[i].fw_version); |
| if (err) |
| return err; |
| } |
| |
| err = devlink_info_version_running_put(req, |
| DEVLINK_INFO_VERSION_GENERIC_FW, |
| pdsc->dev_info.fw_version); |
| 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, |
| buf); |
| if (err) |
| return err; |
| |
| snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_rev); |
| err = devlink_info_version_fixed_put(req, |
| DEVLINK_INFO_VERSION_GENERIC_ASIC_REV, |
| buf); |
| if (err) |
| return err; |
| |
| return devlink_info_serial_number_put(req, pdsc->dev_info.serial_num); |
| } |
| |
| int pdsc_fw_reporter_diagnose(struct devlink_health_reporter *reporter, |
| struct devlink_fmsg *fmsg, |
| struct netlink_ext_ack *extack) |
| { |
| struct pdsc *pdsc = devlink_health_reporter_priv(reporter); |
| |
| mutex_lock(&pdsc->config_lock); |
| if (test_bit(PDSC_S_FW_DEAD, &pdsc->state)) |
| devlink_fmsg_string_pair_put(fmsg, "Status", "dead"); |
| else if (!pdsc_is_fw_good(pdsc)) |
| devlink_fmsg_string_pair_put(fmsg, "Status", "unhealthy"); |
| else |
| devlink_fmsg_string_pair_put(fmsg, "Status", "healthy"); |
| mutex_unlock(&pdsc->config_lock); |
| |
| devlink_fmsg_u32_pair_put(fmsg, "State", |
| pdsc->fw_status & ~PDS_CORE_FW_STS_F_GENERATION); |
| devlink_fmsg_u32_pair_put(fmsg, "Generation", pdsc->fw_generation >> 4); |
| devlink_fmsg_u32_pair_put(fmsg, "Recoveries", pdsc->fw_recoveries); |
| |
| return 0; |
| } |