diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c')
| -rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 442 |
1 files changed, 433 insertions, 9 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index f0e4d020f4c7..665a2632379d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -52,6 +52,17 @@ static int psp_load_smu_fw(struct psp_context *psp); static int psp_rap_terminate(struct psp_context *psp); static int psp_securedisplay_terminate(struct psp_context *psp); +static const char * const amdgpu_ptl_fmt_str[] = { + [AMDGPU_PTL_FMT_I8] = "I8", + [AMDGPU_PTL_FMT_F16] = "F16", + [AMDGPU_PTL_FMT_BF16] = "BF16", + [AMDGPU_PTL_FMT_F32] = "F32", + [AMDGPU_PTL_FMT_F64] = "F64", + [AMDGPU_PTL_FMT_F8] = "F8", + [AMDGPU_PTL_FMT_VECTOR] = "VECTOR", + [AMDGPU_PTL_FMT_INVALID] = "INVALID", +}; + static int psp_ring_init(struct psp_context *psp, enum psp_ring_type ring_type) { @@ -518,7 +529,7 @@ static int psp_sw_init(struct amdgpu_ip_block *ip_block) } ret = amdgpu_bo_create_kernel(adev, PSP_1_MEG, PSP_1_MEG, - (amdgpu_sriov_vf(adev) || adev->debug_use_vram_fw_buf) ? + (amdgpu_sriov_vf(adev) || adev->debug_use_vram_fw_buf || adev->gmc.xgmi.connected_to_cpu) ? AMDGPU_GEM_DOMAIN_VRAM : AMDGPU_GEM_DOMAIN_GTT, &psp->fw_pri_bo, &psp->fw_pri_mc_addr, @@ -682,6 +693,8 @@ static const char *psp_gfx_cmd_name(enum psp_gfx_cmd_id cmd_id) return "SPATIAL_PARTITION"; case GFX_CMD_ID_FB_NPS_MODE: return "NPS_MODE_CHANGE"; + case GFX_CMD_ID_PERF_HW: + return "PERF MONITORING HW"; default: return "UNKNOWN CMD"; } @@ -844,7 +857,11 @@ static int psp_load_toc(struct psp_context *psp, struct psp_gfx_cmd_resp *cmd = acquire_psp_cmd_buf(psp); /* Copy toc to psp firmware private buffer */ - psp_copy_fw(psp, psp->toc.start_addr, psp->toc.size_bytes); + ret = psp_copy_fw(psp, psp->toc.start_addr, psp->toc.size_bytes); + if (ret) { + release_psp_cmd_buf(psp); + return ret; + } psp_prep_load_toc_cmd_buf(cmd, psp->fw_pri_mc_addr, psp->toc.size_bytes); @@ -1160,8 +1177,11 @@ static int psp_rl_load(struct amdgpu_device *adev) cmd = acquire_psp_cmd_buf(psp); - memset(psp->fw_pri_buf, 0, PSP_1_MEG); - memcpy(psp->fw_pri_buf, psp->rl.start_addr, psp->rl.size_bytes); + ret = psp_copy_fw(psp, psp->rl.start_addr, psp->rl.size_bytes); + if (ret) { + release_psp_cmd_buf(psp); + return ret; + } cmd->cmd_id = GFX_CMD_ID_LOAD_IP_FW; cmd->cmd.cmd_load_ip_fw.fw_phy_addr_lo = lower_32_bits(psp->fw_pri_mc_addr); @@ -1201,6 +1221,369 @@ int psp_memory_partition(struct psp_context *psp, int mode) return ret; } +static int psp_ptl_fmt_verify(struct psp_context *psp, enum amdgpu_ptl_fmt fmt, + uint32_t *ptl_fmt) +{ + struct amdgpu_device *adev = psp->adev; + + if (amdgpu_ip_version(adev, GC_HWIP, 0) != IP_VERSION(9, 4, 4)) + return -EINVAL; + + switch (fmt) { + case AMDGPU_PTL_FMT_I8: + *ptl_fmt = GFX_FTYPE_I8; + break; + case AMDGPU_PTL_FMT_F16: + *ptl_fmt = GFX_FTYPE_F16; + break; + case AMDGPU_PTL_FMT_BF16: + *ptl_fmt = GFX_FTYPE_BF16; + break; + case AMDGPU_PTL_FMT_F32: + *ptl_fmt = GFX_FTYPE_F32; + break; + case AMDGPU_PTL_FMT_F64: + *ptl_fmt = GFX_FTYPE_F64; + break; + case AMDGPU_PTL_FMT_F8: + *ptl_fmt = GFX_FTYPE_F8; + break; + case AMDGPU_PTL_FMT_VECTOR: + *ptl_fmt = GFX_FTYPE_VECTOR; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int psp_ptl_invoke(struct psp_context *psp, u32 req_code, + uint32_t *ptl_state, uint32_t *fmt1, uint32_t *fmt2) +{ + struct psp_gfx_cmd_resp *cmd; + struct amdgpu_ptl *ptl = &psp->ptl; + int ret; + + cmd = acquire_psp_cmd_buf(psp); + + cmd->cmd_id = GFX_CMD_ID_PERF_HW; + cmd->cmd.cmd_req_perf_hw.req = req_code; + cmd->cmd.cmd_req_perf_hw.ptl_state = *ptl_state; + cmd->cmd.cmd_req_perf_hw.pref_format1 = *fmt1; + cmd->cmd.cmd_req_perf_hw.pref_format2 = *fmt2; + + ret = psp_cmd_submit_buf(psp, NULL, cmd, psp->fence_buf_mc_addr); + if (ret) + goto out; + + /* + * Check response status explicitly to avoid + * updating cached PTL state with invalid data. + */ + if (cmd->resp.status) { + dev_err(psp->adev->dev, + "PTL command 0x%x failed, PSP response status: 0x%X fw resp=0x%X\n", + req_code, cmd->resp.status, + cmd->resp.uresp.perf_hw_info.resp); + ret = -EIO; + goto out; + } + + /* Parse response */ + switch (req_code) { + case PSP_PTL_PERF_MON_QUERY: + *ptl_state = cmd->resp.uresp.perf_hw_info.ptl_state; + *fmt1 = cmd->resp.uresp.perf_hw_info.pref_format1; + *fmt2 = cmd->resp.uresp.perf_hw_info.pref_format2; + dev_dbg(psp->adev->dev, "PTL query: state=%d, fmt1=%d, fmt2=%d\n", + *ptl_state, *fmt1, *fmt2); + break; + case PSP_PTL_PERF_MON_SET: + /* Update cached state only on success */ + ptl->enabled = *ptl_state; + ptl->fmt1 = *fmt1; + ptl->fmt2 = *fmt2; + dev_dbg(psp->adev->dev, "PTL set: state=%d, fmt1=%d, fmt2=%d\n", + *ptl_state, *fmt1, *fmt2); + break; + } + +out: + release_psp_cmd_buf(psp); + return ret; +} + +int amdgpu_ptl_perf_monitor_ctrl(struct amdgpu_device *adev, u32 req_code, + uint32_t *ptl_state, + enum amdgpu_ptl_fmt *fmt1, + enum amdgpu_ptl_fmt *fmt2) +{ + uint32_t ptl_fmt1, ptl_fmt2; + struct psp_context *psp; + struct amdgpu_ptl *ptl; + int ret; + + if (!adev || !ptl_state || !fmt1 || !fmt2) + return -EINVAL; + + if (amdgpu_sriov_vf(adev)) + return 0; + + psp = &adev->psp; + ptl = &psp->ptl; + + if (ptl->permanently_disabled && *ptl_state == 1) + return 0; + + if (amdgpu_ip_version(adev, GC_HWIP, 0) != IP_VERSION(9, 4, 4) || + psp->sos.fw_version < 0x0036081a) + return -EOPNOTSUPP; + + /* Verify formats */ + if (psp_ptl_fmt_verify(psp, *fmt1, &ptl_fmt1) || + psp_ptl_fmt_verify(psp, *fmt2, &ptl_fmt2)) + return -EINVAL; + + /* + * Add check to skip if state and formats are identical to current ones + */ + if (req_code == PSP_PTL_PERF_MON_SET && + ptl->enabled == *ptl_state && + ptl->fmt1 == ptl_fmt1 && + ptl->fmt2 == ptl_fmt2) + return 0; + + /* If enabling PTL, check disable bitmap */ + if (req_code == PSP_PTL_PERF_MON_SET && *ptl_state == 1) { + if (!bitmap_empty(ptl->disable_bitmap, + AMDGPU_PTL_DISABLE_MAX)) { + dev_dbg(adev->dev, + "PTL enable blocked: SYSFS=%d, PROFILER=%d (ref=%d)\n", + test_bit(AMDGPU_PTL_DISABLE_SYSFS, + ptl->disable_bitmap), + test_bit(AMDGPU_PTL_DISABLE_PROFILER, + ptl->disable_bitmap), + atomic_read(&ptl->disable_ref)); + return 0; + } + } + + if (req_code == PSP_PTL_PERF_MON_SET) { + amdgpu_amdkfd_stop_sched_all(adev); + /* Wait for GFX engine idle before PTL state transition */ + ret = amdgpu_device_ip_wait_for_idle(adev, + AMD_IP_BLOCK_TYPE_GFX); + if (ret) { + amdgpu_amdkfd_start_sched_all(adev); + dev_err(adev->dev, "GFX not idle before PTL operation (%d)\n", ret); + return ret; + } + ret = psp_ptl_invoke(psp, req_code, ptl_state, &ptl_fmt1, &ptl_fmt2); + amdgpu_amdkfd_start_sched_all(adev); + } else { + ret = psp_ptl_invoke(psp, req_code, ptl_state, &ptl_fmt1, &ptl_fmt2); + } + + return ret; +} + +static enum amdgpu_ptl_fmt str_to_ptl_fmt(const char *str) +{ + int i; + + for (i = 0; i < AMDGPU_PTL_FMT_INVALID; ++i) { + if (!strcmp(str, amdgpu_ptl_fmt_str[i])) + return (enum amdgpu_ptl_fmt)i; + } + + return AMDGPU_PTL_FMT_INVALID; +} + +static ssize_t ptl_supported_formats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + for (int i = 0; i < AMDGPU_PTL_FMT_INVALID; ++i) { + const char *fmt = amdgpu_ptl_fmt_str[i]; + + len += sysfs_emit_at(buf, len, "%s%s", + fmt ? fmt : "UNKNOWN", + (i < AMDGPU_PTL_FMT_INVALID - 1) ? "," : "\n"); + } + + return len; +} + +static ssize_t ptl_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + struct amdgpu_ptl *ptl = &adev->psp.ptl; + uint32_t ptl_state, fmt1, fmt2; + int ret; + bool enable; + bool bit_changed = false; + + mutex_lock(&ptl->mutex); + if (sysfs_streq(buf, "enabled") || sysfs_streq(buf, "1")) { + enable = true; + } else if (sysfs_streq(buf, "disabled") || sysfs_streq(buf, "0")) { + enable = false; + } else { + mutex_unlock(&ptl->mutex); + return -EINVAL; + } + + /* Block enable when permanently disabled */ + if (ptl->permanently_disabled) { + mutex_unlock(&ptl->mutex); + return -EPERM; + } + + fmt1 = ptl->fmt1; + fmt2 = ptl->fmt2; + ptl_state = enable ? 1 : 0; + + if (enable) + bit_changed = test_and_clear_bit(AMDGPU_PTL_DISABLE_SYSFS, + ptl->disable_bitmap); + + ret = amdgpu_ptl_perf_monitor_ctrl(adev, PSP_PTL_PERF_MON_SET, &ptl_state, &fmt1, &fmt2); + if (ret) { + dev_err(adev->dev, "Failed to set PTL err = %d\n", ret); + if (enable && bit_changed) + set_bit(AMDGPU_PTL_DISABLE_SYSFS, ptl->disable_bitmap); + mutex_unlock(&ptl->mutex); + return ret; + } + + if (!enable) + set_bit(AMDGPU_PTL_DISABLE_SYSFS, ptl->disable_bitmap); + + mutex_unlock(&ptl->mutex); + + return count; +} + +static ssize_t ptl_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + struct amdgpu_ptl *ptl = &adev->psp.ptl; + + if (ptl->permanently_disabled) + return sysfs_emit(buf, "permanently disabled\n"); + + return sysfs_emit(buf, "%s\n", ptl->enabled ? "enabled" : "disabled"); +} + +static ssize_t ptl_format_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + char fmt1_str[8], fmt2_str[8]; + enum amdgpu_ptl_fmt fmt1_enum, fmt2_enum; + struct amdgpu_ptl *ptl = &adev->psp.ptl; + uint32_t ptl_state, fmt1, fmt2; + int ret; + + /* Only allow format update when PTL is enabled */ + if (!ptl->enabled) + return -EPERM; + + mutex_lock(&ptl->mutex); + /* Parse input, expecting "FMT1,FMT2" */ + if (sscanf(buf, "%7[^,],%7s", fmt1_str, fmt2_str) != 2) { + mutex_unlock(&ptl->mutex); + return -EINVAL; + } + + fmt1_enum = str_to_ptl_fmt(fmt1_str); + fmt2_enum = str_to_ptl_fmt(fmt2_str); + + if (fmt1_enum >= AMDGPU_PTL_FMT_INVALID || + fmt2_enum >= AMDGPU_PTL_FMT_INVALID || + fmt1_enum == fmt2_enum) { + mutex_unlock(&ptl->mutex); + return -EINVAL; + } + + ptl_state = ptl->enabled; + fmt1 = fmt1_enum; + fmt2 = fmt2_enum; + ret = amdgpu_ptl_perf_monitor_ctrl(adev, PSP_PTL_PERF_MON_SET, &ptl_state, &fmt1, &fmt2); + if (ret) { + dev_err(adev->dev, "Failed to update PTL err = %d\n", ret); + mutex_unlock(&ptl->mutex); + return ret; + } + mutex_unlock(&ptl->mutex); + + return count; +} + +static ssize_t ptl_format_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + struct psp_context *psp = &adev->psp; + + return sysfs_emit(buf, "%s,%s\n", + amdgpu_ptl_fmt_str[psp->ptl.fmt1], + amdgpu_ptl_fmt_str[psp->ptl.fmt2]); +} + +static umode_t amdgpu_ptl_is_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct drm_device *ddev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(ddev); + + /* Only show PTL sysfs files if PTL hardware is supported */ + if (!adev->psp.ptl.hw_supported) + return 0; + + return attr->mode; +} + +int amdgpu_ptl_sysfs_init(struct amdgpu_device *adev) +{ + struct amdgpu_ptl *ptl = &adev->psp.ptl; + int ret; + + if (!ptl->hw_supported) + return 0; + + if (ptl->ptl_sysfs_created) + return 0; + + ret = sysfs_create_group(&adev->dev->kobj, &amdgpu_ptl_attr_group); + if (!ret) + ptl->ptl_sysfs_created = true; + + return ret; +} + +void amdgpu_ptl_sysfs_fini(struct amdgpu_device *adev) +{ + struct amdgpu_ptl *ptl = &adev->psp.ptl; + + if (!ptl->hw_supported) + return; + + if (!ptl->ptl_sysfs_created) + return; + + sysfs_remove_group(&adev->dev->kobj, &amdgpu_ptl_attr_group); + ptl->ptl_sysfs_created = false; +} + int psp_spatial_partition(struct psp_context *psp, int mode) { struct psp_gfx_cmd_resp *cmd; @@ -1384,8 +1767,12 @@ int psp_ta_load(struct psp_context *psp, struct ta_context *context) cmd = acquire_psp_cmd_buf(psp); - psp_copy_fw(psp, context->bin_desc.start_addr, - context->bin_desc.size_bytes); + ret = psp_copy_fw(psp, context->bin_desc.start_addr, + context->bin_desc.size_bytes); + if (ret) { + release_psp_cmd_buf(psp); + return ret; + } if (amdgpu_virt_xgmi_migrate_enabled(psp->adev) && context->mem_context.shared_bo) @@ -3527,7 +3914,12 @@ int psp_init_toc_microcode(struct psp_context *psp, const char *chip_name) const struct psp_firmware_header_v1_0 *toc_hdr; int err = 0; - err = amdgpu_ucode_request(adev, &adev->psp.toc_fw, AMDGPU_UCODE_REQUIRED, + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 8) && + adev->rev_id == 0) + err = amdgpu_ucode_request(adev, &adev->psp.toc_fw, AMDGPU_UCODE_REQUIRED, + "amdgpu/%s_toc_1.bin", chip_name); + else + err = amdgpu_ucode_request(adev, &adev->psp.toc_fw, AMDGPU_UCODE_REQUIRED, "amdgpu/%s_toc.bin", chip_name); if (err) goto out; @@ -4181,17 +4573,24 @@ fail: return count; } -void psp_copy_fw(struct psp_context *psp, uint8_t *start_addr, uint32_t bin_size) +int psp_copy_fw(struct psp_context *psp, uint8_t *start_addr, uint32_t bin_size) { int idx; if (!drm_dev_enter(adev_to_drm(psp->adev), &idx)) - return; + return -ENODEV; + + if (!bin_size || bin_size > PSP_1_MEG) { + dev_err(psp->adev->dev, "PSP firmware is invalid\n"); + drm_dev_exit(idx); + return -EINVAL; + } memset(psp->fw_pri_buf, 0, PSP_1_MEG); memcpy(psp->fw_pri_buf, start_addr, bin_size); drm_dev_exit(idx); + return 0; } /** @@ -4202,6 +4601,31 @@ void psp_copy_fw(struct psp_context *psp, uint8_t *start_addr, uint32_t bin_size static DEVICE_ATTR(usbc_pd_fw, 0644, psp_usbc_pd_fw_sysfs_read, psp_usbc_pd_fw_sysfs_write); +/** + * DOC: PTL sysfs attributes + * These sysfs files under /sys/class/drm/cardX/device/ptl allow users to enable or disable + * the Peak Tops Limiter (PTL), configure preferred PTL data formats, and query supported + * formats for each GPU. + */ +static DEVICE_ATTR(ptl_enable, 0644, + ptl_enable_show, ptl_enable_store); +static DEVICE_ATTR(ptl_format, 0644, + ptl_format_show, ptl_format_store); +static DEVICE_ATTR(ptl_supported_formats, 0444, + ptl_supported_formats_show, NULL); + +static struct attribute *ptl_attrs[] = { + &dev_attr_ptl_enable.attr, + &dev_attr_ptl_format.attr, + &dev_attr_ptl_supported_formats.attr, + NULL, +}; + +const struct attribute_group amdgpu_ptl_attr_group = { + .name = "ptl", + .attrs = ptl_attrs, + .is_visible = amdgpu_ptl_is_visible, +}; int is_psp_fw_valid(struct psp_bin_desc bin) { |
