| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* Copyright(c) 2023 Advanced Micro Devices, Inc */ |
| 3 | |
| 4 | #include "core.h" |
| 5 | #include <linux/pds/pds_auxbus.h> |
| 6 | |
| 7 | static struct |
| 8 | pdsc_viftype *pdsc_dl_find_viftype_by_id(struct pdsc *pdsc, |
| 9 | enum devlink_param_type dl_id) |
| 10 | { |
| 11 | int vt; |
| 12 | |
| 13 | if (!pdsc->viftype_status) |
| 14 | return NULL; |
| 15 | |
| 16 | for (vt = 0; vt < PDS_DEV_TYPE_MAX; vt++) { |
| 17 | if (pdsc->viftype_status[vt].dl_id == dl_id) |
| 18 | return &pdsc->viftype_status[vt]; |
| 19 | } |
| 20 | |
| 21 | return NULL; |
| 22 | } |
| 23 | |
| 24 | int pdsc_dl_enable_get(struct devlink *dl, u32 id, |
| 25 | struct devlink_param_gset_ctx *ctx, |
| 26 | struct netlink_ext_ack *extack) |
| 27 | { |
| 28 | struct pdsc *pdsc = devlink_priv(devlink: dl); |
| 29 | struct pdsc_viftype *vt_entry; |
| 30 | |
| 31 | vt_entry = pdsc_dl_find_viftype_by_id(pdsc, dl_id: id); |
| 32 | if (!vt_entry) |
| 33 | return -ENOENT; |
| 34 | |
| 35 | ctx->val.vbool = vt_entry->enabled; |
| 36 | |
| 37 | return 0; |
| 38 | } |
| 39 | |
| 40 | int pdsc_dl_enable_set(struct devlink *dl, u32 id, |
| 41 | struct devlink_param_gset_ctx *ctx, |
| 42 | struct netlink_ext_ack *extack) |
| 43 | { |
| 44 | struct pdsc *pdsc = devlink_priv(devlink: dl); |
| 45 | struct pdsc_viftype *vt_entry; |
| 46 | int err = 0; |
| 47 | int vf_id; |
| 48 | |
| 49 | vt_entry = pdsc_dl_find_viftype_by_id(pdsc, dl_id: id); |
| 50 | if (!vt_entry || !vt_entry->supported) |
| 51 | return -EOPNOTSUPP; |
| 52 | |
| 53 | if (vt_entry->enabled == ctx->val.vbool) |
| 54 | return 0; |
| 55 | |
| 56 | vt_entry->enabled = ctx->val.vbool; |
| 57 | for (vf_id = 0; vf_id < pdsc->num_vfs; vf_id++) { |
| 58 | struct pdsc *vf = pdsc->vfs[vf_id].vf; |
| 59 | |
| 60 | if (ctx->val.vbool) |
| 61 | err = pdsc_auxbus_dev_add(cf: vf, pf: pdsc, vt: vt_entry->vif_id, |
| 62 | pd_ptr: &pdsc->vfs[vf_id].padev); |
| 63 | else |
| 64 | pdsc_auxbus_dev_del(cf: vf, pf: pdsc, pd_ptr: &pdsc->vfs[vf_id].padev); |
| 65 | } |
| 66 | |
| 67 | return err; |
| 68 | } |
| 69 | |
| 70 | int pdsc_dl_enable_validate(struct devlink *dl, u32 id, |
| 71 | union devlink_param_value val, |
| 72 | struct netlink_ext_ack *extack) |
| 73 | { |
| 74 | struct pdsc *pdsc = devlink_priv(devlink: dl); |
| 75 | struct pdsc_viftype *vt_entry; |
| 76 | |
| 77 | vt_entry = pdsc_dl_find_viftype_by_id(pdsc, dl_id: id); |
| 78 | if (!vt_entry || !vt_entry->supported) |
| 79 | return -EOPNOTSUPP; |
| 80 | |
| 81 | if (!pdsc->viftype_status[vt_entry->vif_id].supported) |
| 82 | return -ENODEV; |
| 83 | |
| 84 | return 0; |
| 85 | } |
| 86 | |
| 87 | int pdsc_dl_flash_update(struct devlink *dl, |
| 88 | struct devlink_flash_update_params *params, |
| 89 | struct netlink_ext_ack *extack) |
| 90 | { |
| 91 | struct pdsc *pdsc = devlink_priv(devlink: dl); |
| 92 | |
| 93 | return pdsc_firmware_update(pdsc, fw: params->fw, extack); |
| 94 | } |
| 95 | |
| 96 | static char *fw_slotnames[] = { |
| 97 | "fw.goldfw" , |
| 98 | "fw.mainfwa" , |
| 99 | "fw.mainfwb" , |
| 100 | }; |
| 101 | |
| 102 | int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, |
| 103 | struct netlink_ext_ack *extack) |
| 104 | { |
| 105 | union pds_core_dev_cmd cmd = { |
| 106 | .fw_control.opcode = PDS_CORE_CMD_FW_CONTROL, |
| 107 | .fw_control.oper = PDS_CORE_FW_GET_LIST, |
| 108 | }; |
| 109 | struct pds_core_fw_list_info fw_list = {}; |
| 110 | struct pdsc *pdsc = devlink_priv(devlink: dl); |
| 111 | union pds_core_dev_comp comp; |
| 112 | char buf[32]; |
| 113 | int listlen; |
| 114 | int err; |
| 115 | int i; |
| 116 | |
| 117 | mutex_lock(&pdsc->devcmd_lock); |
| 118 | err = pdsc_devcmd_locked(pdsc, cmd: &cmd, comp: &comp, max_seconds: pdsc->devcmd_timeout * 2); |
| 119 | if (!err) |
| 120 | memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list)); |
| 121 | mutex_unlock(lock: &pdsc->devcmd_lock); |
| 122 | |
| 123 | listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names)); |
| 124 | for (i = 0; i < listlen; i++) { |
| 125 | if (i < ARRAY_SIZE(fw_slotnames)) |
| 126 | strscpy(buf, fw_slotnames[i], sizeof(buf)); |
| 127 | else |
| 128 | snprintf(buf, size: sizeof(buf), fmt: "fw.slot_%d" , i); |
| 129 | err = devlink_info_version_stored_put(req, version_name: buf, |
| 130 | version_value: fw_list.fw_names[i].fw_version); |
| 131 | if (err) |
| 132 | return err; |
| 133 | } |
| 134 | |
| 135 | err = devlink_info_version_running_put(req, |
| 136 | DEVLINK_INFO_VERSION_GENERIC_FW, |
| 137 | version_value: pdsc->dev_info.fw_version); |
| 138 | if (err) |
| 139 | return err; |
| 140 | |
| 141 | snprintf(buf, size: sizeof(buf), fmt: "0x%x" , pdsc->dev_info.asic_type); |
| 142 | err = devlink_info_version_fixed_put(req, |
| 143 | DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, |
| 144 | version_value: buf); |
| 145 | if (err) |
| 146 | return err; |
| 147 | |
| 148 | snprintf(buf, size: sizeof(buf), fmt: "0x%x" , pdsc->dev_info.asic_rev); |
| 149 | err = devlink_info_version_fixed_put(req, |
| 150 | DEVLINK_INFO_VERSION_GENERIC_ASIC_REV, |
| 151 | version_value: buf); |
| 152 | if (err) |
| 153 | return err; |
| 154 | |
| 155 | return devlink_info_serial_number_put(req, sn: pdsc->dev_info.serial_num); |
| 156 | } |
| 157 | |
| 158 | int pdsc_fw_reporter_diagnose(struct devlink_health_reporter *reporter, |
| 159 | struct devlink_fmsg *fmsg, |
| 160 | struct netlink_ext_ack *extack) |
| 161 | { |
| 162 | struct pdsc *pdsc = devlink_health_reporter_priv(reporter); |
| 163 | |
| 164 | mutex_lock(&pdsc->config_lock); |
| 165 | if (test_bit(PDSC_S_FW_DEAD, &pdsc->state)) |
| 166 | devlink_fmsg_string_pair_put(fmsg, name: "Status" , value: "dead" ); |
| 167 | else if (!pdsc_is_fw_good(pdsc)) |
| 168 | devlink_fmsg_string_pair_put(fmsg, name: "Status" , value: "unhealthy" ); |
| 169 | else |
| 170 | devlink_fmsg_string_pair_put(fmsg, name: "Status" , value: "healthy" ); |
| 171 | mutex_unlock(lock: &pdsc->config_lock); |
| 172 | |
| 173 | devlink_fmsg_u32_pair_put(fmsg, name: "State" , |
| 174 | value: pdsc->fw_status & ~PDS_CORE_FW_STS_F_GENERATION); |
| 175 | devlink_fmsg_u32_pair_put(fmsg, name: "Generation" , value: pdsc->fw_generation >> 4); |
| 176 | devlink_fmsg_u32_pair_put(fmsg, name: "Recoveries" , value: pdsc->fw_recoveries); |
| 177 | |
| 178 | return 0; |
| 179 | } |
| 180 | |