diff options
Diffstat (limited to 'drivers/gpu/drm/amd/pm')
58 files changed, 3909 insertions, 2823 deletions
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index f76ba6753551..ce526db4d24a 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -508,7 +508,7 @@ void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev) amdgpu_dpm_notify_ac_dc(adev); if (is_support_sw_smu(adev)) - smu_set_ac_dc(adev->powerplay.pp_handle); + smu_set_ac_dc(adev->powerplay.pp_handle, true); mutex_unlock(&adev->pm.mutex); } @@ -1183,20 +1183,36 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table) +static bool amdgpu_dpm_is_pp_table_allowed(struct amdgpu_device *adev) +{ + return !amdgpu_sriov_vf(adev) && + !(adev->flags & AMD_IS_APU) && + !adev->scpm_enabled; +} + +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + char *pptable = NULL; int ret = 0; - if (!table) + if ((!table && size) || (table && !size)) return -EINVAL; - if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) + if (!amdgpu_dpm_is_pp_table_allowed(adev) || + !pp_funcs->get_pp_table) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle, - table); + &pptable); + if (ret > 0 && !pptable) { + ret = -EINVAL; + } else if (ret > 0 && table) { + ret = min_t(size_t, ret, size); + memcpy(table, pptable, ret); + } mutex_unlock(&adev->pm.mutex); return ret; @@ -1426,17 +1442,23 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table) +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - int ret = 0; + void *table; + ssize_t ret; if (!pp_funcs->get_gpu_metrics) return 0; mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle, - table); + &table); + if (ret > 0) { + ret = min_t(ssize_t, ret, size); + memcpy(buf, table, ret); + } mutex_unlock(&adev->pm.mutex); return ret; @@ -1703,7 +1725,8 @@ int amdgpu_dpm_set_pp_table(struct amdgpu_device *adev, if (!buf || !size) return -EINVAL; - if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled) + if (!amdgpu_dpm_is_pp_table_allowed(adev) || + !pp_funcs->set_pp_table) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); @@ -2110,10 +2133,3 @@ ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id, return ret; } - -const struct ras_smu_drv *amdgpu_dpm_get_ras_smu_driver(struct amdgpu_device *adev) -{ - void *pp_handle = adev->powerplay.pp_handle; - - return smu_get_ras_smu_driver(pp_handle); -} diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 97da01aff76c..c0e677328af5 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -34,6 +34,7 @@ #include <linux/nospec.h> #include <linux/pm_runtime.h> #include <linux/string_choices.h> +#include <linux/units.h> #include <asm/processor.h> #define MAX_NUM_OF_FEATURES_PER_SUBSET 8 @@ -41,8 +42,6 @@ #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) -#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff)) - struct od_attribute { struct kobj_attribute attribute; struct list_head entry; @@ -100,6 +99,37 @@ const char * const amdgpu_pp_profile_name[] = { "UNCAPPED", }; +static int amdgpu_pm_parse_long_params(char *str, long *params, + uint32_t max_params, + uint32_t *num_params) +{ + const char delimiter[] = { ' ', '\n', '\0' }; + uint32_t count = 0; + char *sub_str; + int ret; + + if (!params || !num_params) + return -EINVAL; + + while ((sub_str = strsep(&str, delimiter)) != NULL) { + if (strlen(sub_str) == 0) + continue; + if (count >= max_params) + return -EINVAL; + ret = kstrtol(sub_str, 0, ¶ms[count]); + if (ret) + return -EINVAL; + count++; + if (!str) + break; + while (isspace(*str)) + str++; + } + *num_params = count; + + return 0; +} + /** * amdgpu_pm_dev_state_check - Check if device can be accessed. * @adev: Target device. @@ -564,25 +594,19 @@ static ssize_t amdgpu_get_pp_table(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - char *table = NULL; int size, ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; - size = amdgpu_dpm_get_pp_table(adev, &table); + size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1); amdgpu_pm_put_access(adev); if (size <= 0) return size; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, table, size); - return size; } @@ -769,8 +793,6 @@ static ssize_t amdgpu_set_pp_od_clk_voltage(struct device *dev, long parameter[64]; char buf_cpy[128]; char *tmp_str; - char *sub_str; - const char delimiter[3] = {' ', '\n', '\0'}; uint32_t type; if (count > 127 || count == 0) @@ -805,22 +827,10 @@ static ssize_t amdgpu_set_pp_od_clk_voltage(struct device *dev, tmp_str++; while (isspace(*++tmp_str)); - while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) { - if (strlen(sub_str) == 0) - continue; - if (parameter_size >= ARRAY_SIZE(parameter)) - return -EINVAL; - ret = kstrtol(sub_str, 0, ¶meter[parameter_size]); - if (ret) - return -EINVAL; - parameter_size++; - - if (!tmp_str) - break; - - while (isspace(*tmp_str)) - tmp_str++; - } + ret = amdgpu_pm_parse_long_params( + tmp_str, parameter, ARRAY_SIZE(parameter), ¶meter_size); + if (ret) + return ret; ret = amdgpu_pm_get_access(adev); if (ret < 0) @@ -1393,11 +1403,9 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev, struct amdgpu_device *adev = drm_to_adev(ddev); uint32_t parameter_size = 0; long parameter[64]; - char *sub_str, buf_cpy[128]; - char *tmp_str; + char buf_cpy[128]; char tmp[2]; long int profile_mode = 0; - const char delimiter[3] = {' ', '\n', '\0'}; /* Reject empty/whitespace strings - fuzzing found this is not validated */ if (count == 0 || sysfs_streq(buf, "")) @@ -1415,19 +1423,11 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev, while (isspace(*buf)) buf++; strscpy(buf_cpy, buf, sizeof(buf_cpy)); - tmp_str = buf_cpy; - while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) { - if (strlen(sub_str) == 0) - continue; - ret = kstrtol(sub_str, 0, ¶meter[parameter_size]); - if (ret) - return -EINVAL; - parameter_size++; - if (!tmp_str) - break; - while (isspace(*tmp_str)) - tmp_str++; - } + ret = amdgpu_pm_parse_long_params(buf_cpy, parameter, + ARRAY_SIZE(parameter) - 1, + ¶meter_size); + if (ret) + return ret; } parameter[parameter_size] = profile_mode; @@ -1780,7 +1780,6 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - void *gpu_metrics; ssize_t size = 0; int ret; @@ -1788,15 +1787,10 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, if (ret) return ret; - size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics); + size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1); if (size <= 0) goto out; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, gpu_metrics, size); - out: amdgpu_pm_put_access(adev); @@ -2029,6 +2023,7 @@ static int pp_dpm_clk_default_attr_update(struct amdgpu_device *adev, struct amd gc_ver == IP_VERSION(11, 0, 1) || gc_ver == IP_VERSION(11, 0, 4) || gc_ver == IP_VERSION(11, 5, 0) || + gc_ver == IP_VERSION(11, 5, 1) || gc_ver == IP_VERSION(11, 0, 2) || gc_ver == IP_VERSION(11, 0, 3) || amdgpu_is_multi_aid(adev))) @@ -2037,7 +2032,8 @@ static int pp_dpm_clk_default_attr_update(struct amdgpu_device *adev, struct amd if (!((gc_ver == IP_VERSION(10, 3, 1) || gc_ver == IP_VERSION(10, 3, 0) || gc_ver == IP_VERSION(11, 0, 2) || - gc_ver == IP_VERSION(11, 0, 3)) && adev->vcn.num_vcn_inst >= 2)) + gc_ver == IP_VERSION(11, 0, 3) || + gc_ver == IP_VERSION(11, 5, 1)) && adev->vcn.num_vcn_inst >= 2)) *states = ATTR_STATE_UNSUPPORTED; } else if (DEVICE_ATTR_IS(pp_dpm_dclk)) { if (!(gc_ver == IP_VERSION(10, 3, 1) || @@ -2050,6 +2046,7 @@ static int pp_dpm_clk_default_attr_update(struct amdgpu_device *adev, struct amd gc_ver == IP_VERSION(11, 0, 1) || gc_ver == IP_VERSION(11, 0, 4) || gc_ver == IP_VERSION(11, 5, 0) || + gc_ver == IP_VERSION(11, 5, 1) || gc_ver == IP_VERSION(11, 0, 2) || gc_ver == IP_VERSION(11, 0, 3) || amdgpu_is_multi_aid(adev))) @@ -2058,7 +2055,8 @@ static int pp_dpm_clk_default_attr_update(struct amdgpu_device *adev, struct amd if (!((gc_ver == IP_VERSION(10, 3, 1) || gc_ver == IP_VERSION(10, 3, 0) || gc_ver == IP_VERSION(11, 0, 2) || - gc_ver == IP_VERSION(11, 0, 3)) && adev->vcn.num_vcn_inst >= 2)) + gc_ver == IP_VERSION(11, 0, 3) || + gc_ver == IP_VERSION(11, 5, 1)) && adev->vcn.num_vcn_inst >= 2)) *states = ATTR_STATE_UNSUPPORTED; } else if (DEVICE_ATTR_IS(pp_dpm_pcie)) { if (amdgpu_is_multi_aid(adev)) @@ -2726,10 +2724,9 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ *states = ATTR_STATE_UNSUPPORTED; } else if (DEVICE_ATTR_IS(pp_table)) { int ret; - char *tmp = NULL; - ret = amdgpu_dpm_get_pp_table(adev, &tmp); - if (ret == -EOPNOTSUPP || !tmp) + ret = amdgpu_dpm_get_pp_table(adev, NULL, 0); + if (ret <= 0) *states = ATTR_STATE_UNSUPPORTED; else *states = ATTR_STATE_SUPPORTED; @@ -3364,7 +3361,7 @@ static int amdgpu_hwmon_get_power(struct device *dev, return r; /* convert to microwatts */ - return power_2_mwatt(query) * 1000; + return query * 1000; } static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev, @@ -3958,18 +3955,14 @@ out_pm_put: return size; } -static int parse_input_od_command_lines(const char *buf, - size_t count, - u32 *type, - long *params, - size_t params_max, +static int parse_input_od_command_lines(const char *buf, size_t count, + u32 *type, long *params, + uint32_t max_params, uint32_t *num_of_params) { - const char delimiter[3] = {' ', '\n', '\0'}; uint32_t parameter_size = 0; char buf_cpy[128] = {0}; - char *tmp_str, *sub_str; - int ret; + char *tmp_str; if (count > sizeof(buf_cpy) - 1) return -EINVAL; @@ -3994,28 +3987,8 @@ static int parse_input_od_command_lines(const char *buf, break; } - while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) { - if (strlen(sub_str) == 0) - continue; - - if (parameter_size >= params_max) - return -EINVAL; - - ret = kstrtol(sub_str, 0, ¶ms[parameter_size]); - if (ret) - return -EINVAL; - parameter_size++; - - if (!tmp_str) - break; - - while (isspace(*tmp_str)) - tmp_str++; - } - - *num_of_params = parameter_size; - - return 0; + return amdgpu_pm_parse_long_params(tmp_str, params, max_params, + num_of_params); } static int @@ -4028,10 +4001,7 @@ amdgpu_distribute_custom_od_settings(struct amdgpu_device *adev, long parameter[64]; int ret; - ret = parse_input_od_command_lines(in_buf, - count, - &cmd_type, - parameter, + ret = parse_input_od_command_lines(in_buf, count, &cmd_type, parameter, ARRAY_SIZE(parameter), ¶meter_size); if (ret) @@ -4907,9 +4877,8 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a { uint32_t mp1_ver = amdgpu_ip_version(adev, MP1_HWIP, 0); uint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0); - uint32_t value, mwatt, centiwatt; uint64_t value64 = 0; - uint32_t query = 0; + uint32_t value; int size; /* GPU Clocks */ @@ -4930,25 +4899,22 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a seq_printf(m, "\t%u mV (VDDGFX)\n", value); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VDDNB, (void *)&value, &size)) seq_printf(m, "\t%u mV (VDDNB)\n", value); - size = sizeof(uint32_t); - if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) { - mwatt = power_2_mwatt(query); - centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); + if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&value, &size)) { if (adev->flags & AMD_IS_APU) - seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100); + seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", + (u32)(value / MILLIWATT_PER_WATT), (u32)(value % MILLIWATT_PER_WATT) / 10); else - seq_printf(m, "\t%u.%02u W (average SoC)\n", centiwatt / 100, centiwatt % 100); + seq_printf(m, "\t%u.%02u W (average SoC)\n", + (u32)(value / MILLIWATT_PER_WATT), (u32)(value % MILLIWATT_PER_WATT) / 10); } - size = sizeof(uint32_t); - if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) { - mwatt = power_2_mwatt(query); - centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); + if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&value, &size)) { if (adev->flags & AMD_IS_APU) - seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100); + seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", + (u32)(value / MILLIWATT_PER_WATT), (u32)(value % MILLIWATT_PER_WATT) / 10); else - seq_printf(m, "\t%u.%02u W (current SoC)\n", centiwatt / 100, centiwatt % 100); + seq_printf(m, "\t%u.%02u W (current SoC)\n", + (u32)(value / MILLIWATT_PER_WATT), (u32)(value % MILLIWATT_PER_WATT) / 10); } - size = sizeof(value); seq_printf(m, "\n"); /* GPU Temp */ diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index aa3f427819a0..8e8a052e1d53 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -487,7 +487,8 @@ int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev, int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, enum amd_pp_task task_id, enum amd_pm_state_type *user_state); -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table); +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size); int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev, uint32_t type, long *input, @@ -517,7 +518,8 @@ int amdgpu_dpm_get_power_profile_mode(struct amdgpu_device *adev, char *buf); int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, long *input, uint32_t size); -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table); +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size); ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id, void *table); ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev, @@ -612,6 +614,5 @@ int amdgpu_dpm_reset_vcn(struct amdgpu_device *adev, uint32_t inst_mask); bool amdgpu_dpm_reset_vcn_is_supported(struct amdgpu_device *adev); bool amdgpu_dpm_is_temp_metrics_supported(struct amdgpu_device *adev, enum smu_temp_metric_type type); -const struct ras_smu_drv *amdgpu_dpm_get_ras_smu_driver(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index 8079da7c5335..012227c70600 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -3892,13 +3892,16 @@ static void si_notify_hw_of_powersource(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - /* Check if the platform already manages the AC/DC switch via dedicated GPIO. */ - if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) - return; - - /* The SMU automatically notices DC, but needs to be notified when switching to AC. */ - if (adev->pm.ac_power) + /* + * Check if the platform already manages the AC/DC switch via dedicated GPIO. + * Otherwise SMU automatically notices DC, but needs to be notified of AC. + */ + if (adev->pm.ac_power && + (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)) amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC); + + /* Recompute clocks with updated max_limits. */ + amdgpu_legacy_dpm_compute_clocks(adev); } static PPSMC_Result si_send_msg_to_smc_with_parameter(struct amdgpu_device *adev, @@ -7689,7 +7692,7 @@ static int si_dpm_process_interrupt(struct amdgpu_device *adev, break; } - if (queue_thermal) + if (queue_thermal && amdgpu_dpm) schedule_work(&adev->pm.dpm.thermal.work); return 0; diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c index 6f5c27bdc1e9..7c70e228a5ba 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c +++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c @@ -660,25 +660,20 @@ static int amd_powerplay_reset(void *handle) static int pp_dpm_set_pp_table(void *handle, const char *buf, size_t size) { struct pp_hwmgr *hwmgr = handle; + void *hardcode_pp_table; int ret = -ENOMEM; - if (!hwmgr || !hwmgr->pm_en) - return -EINVAL; - - if (size > hwmgr->soft_pp_table_size) + if (!hwmgr || !hwmgr->pm_en || !buf || !size || size > U32_MAX) return -EINVAL; - if (!hwmgr->hardcode_pp_table) { - hwmgr->hardcode_pp_table = kmemdup(hwmgr->soft_pp_table, - hwmgr->soft_pp_table_size, - GFP_KERNEL); - if (!hwmgr->hardcode_pp_table) - return ret; - } - - memcpy(hwmgr->hardcode_pp_table, buf, size); + hardcode_pp_table = kmemdup(buf, size, GFP_KERNEL); + if (!hardcode_pp_table) + return ret; + kfree(hwmgr->hardcode_pp_table); + hwmgr->hardcode_pp_table = hardcode_pp_table; hwmgr->soft_pp_table = hwmgr->hardcode_pp_table; + hwmgr->soft_pp_table_size = size; ret = amd_powerplay_reset(handle); if (ret) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c index 1d6e30269d56..4d553be56396 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c @@ -106,11 +106,8 @@ int hwmgr_early_init(struct pp_hwmgr *hwmgr) hwmgr->od_enabled = false; switch (hwmgr->chip_id) { case CHIP_BONAIRE: - /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM - * R7 260X cards with old MC ucode: MCLK DPM is unstable - */ - if (adev->pdev->subsystem_vendor == 0x106B || - adev->pdev->device == 0x6658) { + /* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM */ + if (adev->pdev->subsystem_vendor == 0x106B) { dev_info(adev->dev, "disabling MCLK DPM on quirky ASIC"); adev->pm.pp_feature &= ~PP_MCLK_DPM_MASK; hwmgr->feature_mask &= ~PP_MCLK_DPM_MASK; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c index ce166a7f8e42..4b796d60b03d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c @@ -46,16 +46,22 @@ union voltage_object_info { static int atomctrl_retrieve_ac_timing( uint8_t index, ATOM_INIT_REG_BLOCK *reg_block, + u8 *table_end, pp_atomctrl_mc_reg_table *table) { uint32_t i, j; + u16 stride = le16_to_cpu(reg_block->usRegDataBlkSize); uint8_t tmem_id; ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) ((uint8_t *)reg_block + (2 * sizeof(uint16_t)) + le16_to_cpu(reg_block->usRegIndexTblSize)); uint8_t num_ranges = 0; - while (*(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK && + if (stride < sizeof(uint32_t)) + return -EINVAL; + + while ((uint8_t *)reg_data + sizeof(uint32_t) <= table_end && + *(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK && num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES) { tmem_id = (uint8_t)((*(uint32_t *)reg_data & MEM_ID_MASK) >> MEM_ID_SHIFT); @@ -67,6 +73,10 @@ static int atomctrl_retrieve_ac_timing( for (i = 0, j = 1; i < table->last; i++) { if ((table->mc_reg_address[i].uc_pre_reg_data & LOW_NIBBLE_MASK) == DATA_FROM_TABLE) { + if ((uint8_t *)reg_data + + (j + 1) * sizeof(uint32_t) > table_end) + return -EINVAL; + table->mc_reg_table_entry[num_ranges].mc_data[i] = (uint32_t)*((uint32_t *)reg_data + j); j++; @@ -81,11 +91,13 @@ static int atomctrl_retrieve_ac_timing( } reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) - ((uint8_t *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)) ; + ((uint8_t *)reg_data + stride); } - PP_ASSERT_WITH_CODE((*(uint32_t *)reg_data == END_OF_REG_DATA_BLOCK), - "Invalid VramInfo table.", return -1); + if ((uint8_t *)reg_data + sizeof(uint32_t) > table_end || + *(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK) + return -EINVAL; + table->num_entries = num_ranges; return 0; @@ -136,6 +148,7 @@ int atomctrl_initialize_mc_reg_table( { ATOM_VRAM_INFO_HEADER_V2_1 *vram_info; ATOM_INIT_REG_BLOCK *reg_block; + u8 *table_end; int result = 0; u8 frev, crev; u16 size; @@ -157,6 +170,7 @@ int atomctrl_initialize_mc_reg_table( } if (0 == result) { + table_end = (uint8_t *)vram_info + size; reg_block = (ATOM_INIT_REG_BLOCK *) ((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset)); result = atomctrl_set_mc_reg_address_table(reg_block, table); @@ -164,7 +178,7 @@ int atomctrl_initialize_mc_reg_table( if (0 == result) { result = atomctrl_retrieve_ac_timing(module_index, - reg_block, table); + reg_block, table_end, table); } return result; @@ -177,6 +191,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( { ATOM_VRAM_INFO_HEADER_V2_2 *vram_info; ATOM_INIT_REG_BLOCK *reg_block; + u8 *table_end; int result = 0; u8 frev, crev; u16 size; @@ -198,6 +213,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( } if (0 == result) { + table_end = (uint8_t *)vram_info + size; reg_block = (ATOM_INIT_REG_BLOCK *) ((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset)); result = atomctrl_set_mc_reg_address_table(reg_block, table); @@ -205,7 +221,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( if (0 == result) { result = atomctrl_retrieve_ac_timing(module_index, - reg_block, table); + reg_block, table_end, table); } return result; @@ -268,15 +284,21 @@ static const ATOM_VOLTAGE_OBJECT_V3 *atomctrl_lookup_voltage_type_v3( unsigned int offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]); uint8_t *start = (uint8_t *)voltage_object_info_table; - while (offset < size) { + while (offset + sizeof(ATOM_VOLTAGE_OBJECT_HEADER_V3) <= size) { const ATOM_VOLTAGE_OBJECT_V3 *voltage_object = (const ATOM_VOLTAGE_OBJECT_V3 *)(start + offset); + u16 obj_size; + + obj_size = le16_to_cpu(voltage_object->asGpioVoltageObj.sHeader.usSize); + if (obj_size < sizeof(voltage_object->asGpioVoltageObj.sHeader) || + offset + obj_size > size) + break; if (voltage_type == voltage_object->asGpioVoltageObj.sHeader.ucVoltageType && voltage_mode == voltage_object->asGpioVoltageObj.sHeader.ucVoltageMode) return voltage_object; - offset += le16_to_cpu(voltage_object->asGpioVoltageObj.sHeader.usSize); + offset += obj_size; } return NULL; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c index 6120f14caab0..69aee8661d1e 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c @@ -36,16 +36,21 @@ static const union atom_voltage_object_v4 *pp_atomfwctrl_lookup_voltage_type_v4( offsetof(struct atom_voltage_objects_info_v4_1, voltage_object[0]); unsigned long start = (unsigned long)voltage_object_info_table; - while (offset < size) { + while (offset + sizeof(struct atom_voltage_object_header_v4) <= size) { const union atom_voltage_object_v4 *voltage_object = (const union atom_voltage_object_v4 *)(start + offset); + u16 obj_size; + + obj_size = le16_to_cpu(voltage_object->gpio_voltage_obj.header.object_size); + if (obj_size < sizeof(voltage_object->gpio_voltage_obj.header) || + offset + obj_size > size) + break; if (voltage_type == voltage_object->gpio_voltage_obj.header.voltage_type && voltage_mode == voltage_object->gpio_voltage_obj.header.voltage_mode) return voltage_object; - offset += le16_to_cpu(voltage_object->gpio_voltage_obj.header.object_size); - + offset += obj_size; } return NULL; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c index 6fcca65bd7d4..c5673077c895 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c @@ -150,6 +150,368 @@ static const void *get_powerplay_table(struct pp_hwmgr *hwmgr) return table_address; } +static bool tonga_pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset, + size_t size) +{ + size_t table_size = hwmgr->soft_pp_table_size; + + return offset <= table_size && size <= table_size - offset; +} + +static int get_tonga_subtable(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + u16 table_offset, size_t table_size, const void **table) +{ + PP_ASSERT_WITH_CODE((table_offset != 0), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *table = (const void *)(((unsigned long)powerplay_table) + table_offset); + + return 0; +} + +static int validate_tonga_table_entries(struct pp_hwmgr *hwmgr, + u16 table_offset, size_t entries_offset, + u8 num_entries, size_t entry_size) +{ + size_t table_size; + + PP_ASSERT_WITH_CODE((num_entries != 0), + "Invalid PowerPlay Table!", return -1); + + table_size = entries_offset + num_entries * entry_size; + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + return 0; +} + +static int get_tonga_voltage_lookup_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + u16 table_offset, uint32_t max_levels, + const ATOM_Tonga_Voltage_Lookup_Table **lookup_table) +{ + const ATOM_Tonga_Voltage_Lookup_Table *table; + size_t table_size; + int ret; + + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + PP_ASSERT_WITH_CODE((table->ucNumEntries != 0 && + table->ucNumEntries <= max_levels), + "Invalid PowerPlay Table!", return -1); + + table_size = offsetof(ATOM_Tonga_Voltage_Lookup_Table, entries) + + table->ucNumEntries * sizeof(ATOM_Tonga_Voltage_Lookup_Record); + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *lookup_table = table; + + return 0; +} + +static int get_tonga_mclk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_MCLK_Dependency_Table **mclk_dep_table) +{ + const ATOM_Tonga_MCLK_Dependency_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usMclkDependencyTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_tonga_table_entries(hwmgr, table_offset, + offsetof(ATOM_Tonga_MCLK_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Tonga_MCLK_Dependency_Record)); + if (ret) + return ret; + + *mclk_dep_table = table; + + return 0; +} + +static int get_tonga_mm_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_MM_Dependency_Table **mm_dep_table) +{ + const ATOM_Tonga_MM_Dependency_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usMMDependencyTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_tonga_table_entries(hwmgr, table_offset, + offsetof(ATOM_Tonga_MM_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Tonga_MM_Dependency_Record)); + if (ret) + return ret; + + *mm_dep_table = table; + + return 0; +} + +static int get_tonga_sclk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const PPTable_Generic_SubTable_Header **sclk_dep_table) +{ + const PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t entries_offset; + size_t entry_size; + u8 num_entries; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usSclkDependencyTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId < 1) { + const ATOM_Tonga_SCLK_Dependency_Table *table = + (const ATOM_Tonga_SCLK_Dependency_Table *)header; + + entries_offset = offsetof(ATOM_Tonga_SCLK_Dependency_Table, entries); + entry_size = sizeof(ATOM_Tonga_SCLK_Dependency_Record); + num_entries = table->ucNumEntries; + } else { + const ATOM_Polaris_SCLK_Dependency_Table *table = + (const ATOM_Polaris_SCLK_Dependency_Table *)header; + + entries_offset = offsetof(ATOM_Polaris_SCLK_Dependency_Table, entries); + entry_size = sizeof(ATOM_Polaris_SCLK_Dependency_Record); + num_entries = table->ucNumEntries; + } + + ret = validate_tonga_table_entries(hwmgr, table_offset, entries_offset, + num_entries, entry_size); + if (ret) + return ret; + + *sclk_dep_table = header; + + return 0; +} + +static int get_tonga_pcie_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const PPTable_Generic_SubTable_Header **pcie_table) +{ + const PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t entries_offset; + size_t entry_size; + u8 num_entries; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usPCIETableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId < 1) { + const ATOM_Tonga_PCIE_Table *table = + (const ATOM_Tonga_PCIE_Table *)header; + + entries_offset = offsetof(ATOM_Tonga_PCIE_Table, entries); + entry_size = sizeof(ATOM_Tonga_PCIE_Record); + num_entries = table->ucNumEntries; + } else { + const ATOM_Polaris10_PCIE_Table *table = + (const ATOM_Polaris10_PCIE_Table *)header; + + entries_offset = offsetof(ATOM_Polaris10_PCIE_Table, entries); + entry_size = sizeof(ATOM_Polaris10_PCIE_Record); + num_entries = table->ucNumEntries; + } + + ret = validate_tonga_table_entries(hwmgr, table_offset, entries_offset, + num_entries, entry_size); + if (ret) + return ret; + + *pcie_table = header; + + return 0; +} + +static int get_tonga_hard_limit_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_Hard_Limit_Table **hard_limit_table) +{ + const ATOM_Tonga_Hard_Limit_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usHardLimitTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_tonga_table_entries(hwmgr, table_offset, + offsetof(ATOM_Tonga_Hard_Limit_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Tonga_Hard_Limit_Record)); + if (ret) + return ret; + + *hard_limit_table = table; + + return 0; +} + +static int get_tonga_thermal_controller_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_Thermal_Controller **thermal_controller) +{ + u16 table_offset; + + table_offset = le16_to_cpu(powerplay_table->usThermalControllerOffset); + + return get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(**thermal_controller), + (const void **)thermal_controller); +} + +static int get_tonga_fan_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const PPTable_Generic_SubTable_Header **fan_table) +{ + const PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t table_size; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usFanTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId < 8) + table_size = sizeof(ATOM_Tonga_Fan_Table); + else if (header->ucRevId == 8) + table_size = sizeof(ATOM_Fiji_Fan_Table); + else + table_size = sizeof(ATOM_Polaris_Fan_Table); + + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *fan_table = header; + + return 0; +} + +static int get_tonga_power_tune_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const PPTable_Generic_SubTable_Header **power_tune_table) +{ + const PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t table_size; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usPowerTuneTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId < 3) + table_size = sizeof(ATOM_Tonga_PowerTune_Table); + else if (header->ucRevId < 4) + table_size = sizeof(ATOM_Fiji_PowerTune_Table); + else + table_size = sizeof(ATOM_Polaris_PowerTune_Table); + + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *power_tune_table = header; + + return 0; +} + +static int get_tonga_ppm_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_PPM_Table **ppm_table) +{ + u16 table_offset; + + table_offset = le16_to_cpu(powerplay_table->usPPMTableOffset); + + return get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(**ppm_table), (const void **)ppm_table); +} + +static int get_tonga_gpio_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_GPIO_Table **gpio_table) +{ + u16 table_offset; + + table_offset = le16_to_cpu(powerplay_table->usGPIOTableOffset); + + return get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(**gpio_table), (const void **)gpio_table); +} + +static int get_tonga_vce_state_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_VCE_State_Table **vce_state_table) +{ + const ATOM_Tonga_VCE_State_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usVCEStateTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_tonga_table_entries(hwmgr, table_offset, + offsetof(ATOM_Tonga_VCE_State_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Tonga_VCE_State_Record)); + if (ret) + return ret; + + *vce_state_table = table; + + return 0; +} + static int get_vddc_lookup_table( struct pp_hwmgr *hwmgr, phm_ppt_v1_voltage_lookup_table **lookup_table, @@ -158,6 +520,7 @@ static int get_vddc_lookup_table( ) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_voltage_lookup_table *table; phm_ppt_v1_voltage_lookup_record *record; ATOM_Tonga_Voltage_Lookup_Record *atom_record; @@ -165,13 +528,22 @@ static int get_vddc_lookup_table( PP_ASSERT_WITH_CODE((0 != vddc_lookup_pp_tables->ucNumEntries), "Invalid CAC Leakage PowerPlay Table!", return 1); - table = kzalloc_flex(*table, entries, max_levels); + num_entries = min_t(uint32_t, vddc_lookup_pp_tables->ucNumEntries, + min_t(uint32_t, max_levels, + pp_entries_max(hwmgr, vddc_lookup_pp_tables, + sizeof(*vddc_lookup_pp_tables), + sizeof(ATOM_Tonga_Voltage_Lookup_Record)))); + if (num_entries < vddc_lookup_pp_tables->ucNumEntries) + pr_warn("amdgpu: VddcLookup table: clamping ucNumEntries %u -> %u\n", + vddc_lookup_pp_tables->ucNumEntries, num_entries); + + table = kzalloc_flex(*table, entries, num_entries); if (!table) return -ENOMEM; - table->count = vddc_lookup_pp_tables->ucNumEntries; + table->count = num_entries; - for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( phm_ppt_v1_voltage_lookup_record, entries, table, i); @@ -198,7 +570,7 @@ static int get_vddc_lookup_table( */ static int get_platform_power_management_table( struct pp_hwmgr *hwmgr, - ATOM_Tonga_PPM_Table *atom_ppm_table) + const ATOM_Tonga_PPM_Table *atom_ppm_table) { struct phm_ppm_table *ptr = kzalloc_obj(*ptr); struct phm_ppt_v1_information *pp_table_information = @@ -246,7 +618,7 @@ static int init_dpm_2_parameters( { int result = 0; struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable); - ATOM_Tonga_PPM_Table *atom_ppm_table; + const ATOM_Tonga_PPM_Table *atom_ppm_table; uint32_t disable_ppm = 0; uint32_t disable_power_control = 0; @@ -275,30 +647,39 @@ static int init_dpm_2_parameters( } if (0 != powerplay_table->usVddcLookupTableOffset) { - const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable = - (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddcLookupTableOffset)); - - result = get_vddc_lookup_table(hwmgr, - &pp_table_information->vddc_lookup_table, pVddcCACTable, 16); + const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable; + + result = get_tonga_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddcLookupTableOffset), + 16, &pVddcCACTable); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_information->vddc_lookup_table, + pVddcCACTable, 16); } - if (0 != powerplay_table->usVddgfxLookupTableOffset) { - const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable = - (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset)); + if (!result && 0 != powerplay_table->usVddgfxLookupTableOffset) { + const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable; - result = get_vddc_lookup_table(hwmgr, - &pp_table_information->vddgfx_lookup_table, pVddgfxCACTable, 16); + result = get_tonga_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset), + 16, &pVddgfxCACTable); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_information->vddgfx_lookup_table, + pVddgfxCACTable, 16); } disable_ppm = 0; if (0 == disable_ppm) { - atom_ppm_table = (ATOM_Tonga_PPM_Table *) - (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset)); - if (0 != powerplay_table->usPPMTableOffset) { - if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) { + int ret; + + ret = get_tonga_ppm_table(hwmgr, powerplay_table, + &atom_ppm_table); + if (!ret && + get_platform_power_management_table(hwmgr, + atom_ppm_table) == 0) { phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_EnablePlatformPowerManagement); } @@ -363,6 +744,7 @@ static int get_mclk_voltage_dependency_table( ) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_clock_voltage_dependency_table *mclk_table; phm_ppt_v1_clock_voltage_dependency_record *mclk_table_record; ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record; @@ -370,14 +752,21 @@ static int get_mclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((0 != mclk_dep_table->ucNumEntries), "Invalid PowerPlay Table!", return -1); - mclk_table = kzalloc_flex(*mclk_table, entries, - mclk_dep_table->ucNumEntries); + num_entries = min_t(uint32_t, mclk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, mclk_dep_table, + sizeof(*mclk_dep_table), + sizeof(ATOM_Tonga_MCLK_Dependency_Record))); + if (num_entries < mclk_dep_table->ucNumEntries) + pr_warn("amdgpu: MCLK dependency table: clamping ucNumEntries %u -> %u\n", + mclk_dep_table->ucNumEntries, num_entries); + + mclk_table = kzalloc_flex(*mclk_table, entries, num_entries); if (!mclk_table) return -ENOMEM; - mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries; + mclk_table->count = num_entries; - for (i = 0; i < mclk_dep_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { mclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( phm_ppt_v1_clock_voltage_dependency_record, entries, mclk_table, i); @@ -403,6 +792,7 @@ static int get_sclk_voltage_dependency_table( ) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_clock_voltage_dependency_table *sclk_table; phm_ppt_v1_clock_voltage_dependency_record *sclk_table_record; @@ -414,14 +804,21 @@ static int get_sclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((0 != tonga_table->ucNumEntries), "Invalid PowerPlay Table!", return -1); - sclk_table = kzalloc_flex(*sclk_table, entries, - tonga_table->ucNumEntries); + num_entries = min_t(uint32_t, tonga_table->ucNumEntries, + pp_entries_max(hwmgr, tonga_table, + sizeof(*tonga_table), + sizeof(ATOM_Tonga_SCLK_Dependency_Record))); + if (num_entries < tonga_table->ucNumEntries) + pr_warn("amdgpu: Tonga SCLK dependency table: clamping ucNumEntries %u -> %u\n", + tonga_table->ucNumEntries, num_entries); + + sclk_table = kzalloc_flex(*sclk_table, entries, num_entries); if (!sclk_table) return -ENOMEM; - sclk_table->count = (uint32_t)tonga_table->ucNumEntries; + sclk_table->count = num_entries; - for (i = 0; i < tonga_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_SCLK_Dependency_Record, entries, tonga_table, i); @@ -443,14 +840,21 @@ static int get_sclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((0 != polaris_table->ucNumEntries), "Invalid PowerPlay Table!", return -1); - sclk_table = kzalloc_flex(*sclk_table, entries, - polaris_table->ucNumEntries); + num_entries = min_t(uint32_t, polaris_table->ucNumEntries, + pp_entries_max(hwmgr, polaris_table, + sizeof(*polaris_table), + sizeof(ATOM_Polaris_SCLK_Dependency_Record))); + if (num_entries < polaris_table->ucNumEntries) + pr_warn("amdgpu: Polaris SCLK dependency table: clamping ucNumEntries %u -> %u\n", + polaris_table->ucNumEntries, num_entries); + + sclk_table = kzalloc_flex(*sclk_table, entries, num_entries); if (!sclk_table) return -ENOMEM; - sclk_table->count = (uint32_t)polaris_table->ucNumEntries; + sclk_table->count = num_entries; - for (i = 0; i < polaris_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Polaris_SCLK_Dependency_Record, entries, polaris_table, i); @@ -715,20 +1119,29 @@ static int get_mm_clock_voltage_table( ) { uint32_t i; + uint32_t num_entries; const ATOM_Tonga_MM_Dependency_Record *mm_dependency_record; phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table; phm_ppt_v1_mm_clock_voltage_dependency_record *mm_table_record; PP_ASSERT_WITH_CODE((0 != mm_dependency_table->ucNumEntries), "Invalid PowerPlay Table!", return -1); - mm_table = kzalloc_flex(*mm_table, entries, - mm_dependency_table->ucNumEntries); + + num_entries = min_t(uint32_t, mm_dependency_table->ucNumEntries, + pp_entries_max(hwmgr, mm_dependency_table, + sizeof(*mm_dependency_table), + sizeof(ATOM_Tonga_MM_Dependency_Record))); + if (num_entries < mm_dependency_table->ucNumEntries) + pr_warn("amdgpu: MM dependency table: clamping ucNumEntries %u -> %u\n", + mm_dependency_table->ucNumEntries, num_entries); + + mm_table = kzalloc_flex(*mm_table, entries, num_entries); if (!mm_table) return -ENOMEM; - mm_table->count = mm_dependency_table->ucNumEntries; + mm_table->count = num_entries; - for (i = 0; i < mm_dependency_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { mm_dependency_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_MM_Dependency_Record, entries, mm_dependency_table, i); @@ -789,28 +1202,13 @@ static int init_clock_voltage_dependency( int result = 0; struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable); - - const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table = - (const ATOM_Tonga_MM_Dependency_Table *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usMMDependencyTableOffset)); - const PPTable_Generic_SubTable_Header *pPowerTuneTable = - (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPowerTuneTableOffset)); - const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table = - (const ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usMclkDependencyTableOffset)); - const PPTable_Generic_SubTable_Header *sclk_dep_table = - (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usSclkDependencyTableOffset)); - const ATOM_Tonga_Hard_Limit_Table *pHardLimits = - (const ATOM_Tonga_Hard_Limit_Table *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usHardLimitTableOffset)); - const PPTable_Generic_SubTable_Header *pcie_table = - (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPCIETableOffset)); - const ATOM_Tonga_GPIO_Table *gpio_table = - (const ATOM_Tonga_GPIO_Table *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usGPIOTableOffset)); + const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table; + const PPTable_Generic_SubTable_Header *pPowerTuneTable; + const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table; + const PPTable_Generic_SubTable_Header *sclk_dep_table; + const ATOM_Tonga_Hard_Limit_Table *pHardLimits; + const PPTable_Generic_SubTable_Header *pcie_table; + const ATOM_Tonga_GPIO_Table *gpio_table; pp_table_information->vdd_dep_on_sclk = NULL; pp_table_information->vdd_dep_on_mclk = NULL; @@ -818,29 +1216,58 @@ static int init_clock_voltage_dependency( pp_table_information->pcie_table = NULL; pp_table_information->gpio_table = NULL; - if (powerplay_table->usMMDependencyTableOffset != 0) - result = get_mm_clock_voltage_table(hwmgr, - &pp_table_information->mm_dep_table, mm_dependency_table); + if (powerplay_table->usMMDependencyTableOffset != 0) { + result = get_tonga_mm_dependency_table(hwmgr, powerplay_table, + &mm_dependency_table); + if (!result) + result = get_mm_clock_voltage_table(hwmgr, + &pp_table_information->mm_dep_table, + mm_dependency_table); + } - if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0) - result = get_cac_tdp_table(hwmgr, - &pp_table_information->cac_dtp_table, pPowerTuneTable); + if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0) { + result = get_tonga_power_tune_table(hwmgr, powerplay_table, + &pPowerTuneTable); + if (!result) + result = get_cac_tdp_table(hwmgr, + &pp_table_information->cac_dtp_table, + pPowerTuneTable); + } - if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0) - result = get_sclk_voltage_dependency_table(hwmgr, - &pp_table_information->vdd_dep_on_sclk, sclk_dep_table); + if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0) { + result = get_tonga_sclk_dependency_table(hwmgr, powerplay_table, + &sclk_dep_table); + if (!result) + result = get_sclk_voltage_dependency_table(hwmgr, + &pp_table_information->vdd_dep_on_sclk, + sclk_dep_table); + } - if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0) - result = get_mclk_voltage_dependency_table(hwmgr, - &pp_table_information->vdd_dep_on_mclk, mclk_dep_table); + if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0) { + result = get_tonga_mclk_dependency_table(hwmgr, powerplay_table, + &mclk_dep_table); + if (!result) + result = get_mclk_voltage_dependency_table(hwmgr, + &pp_table_information->vdd_dep_on_mclk, + mclk_dep_table); + } - if (result == 0 && powerplay_table->usPCIETableOffset != 0) - result = get_pcie_table(hwmgr, - &pp_table_information->pcie_table, pcie_table); + if (result == 0 && powerplay_table->usPCIETableOffset != 0) { + result = get_tonga_pcie_table(hwmgr, powerplay_table, + &pcie_table); + if (!result) + result = get_pcie_table(hwmgr, + &pp_table_information->pcie_table, pcie_table); + } - if (result == 0 && powerplay_table->usHardLimitTableOffset != 0) - result = get_hard_limits(hwmgr, - &pp_table_information->max_clock_voltage_on_dc, pHardLimits); + if (result == 0 && powerplay_table->usHardLimitTableOffset != 0) { + result = get_tonga_hard_limit_table(hwmgr, powerplay_table, + &pHardLimits); + if (!result) + result = get_hard_limits(hwmgr, + &pp_table_information->max_clock_voltage_on_dc, + pHardLimits); + } hwmgr->dyn_state.max_clock_voltage_on_dc.sclk = pp_table_information->max_clock_voltage_on_dc.sclk; @@ -861,9 +1288,13 @@ static int init_clock_voltage_dependency( result = get_valid_clk(hwmgr, &pp_table_information->valid_sclk_values, pp_table_information->vdd_dep_on_sclk); - if (!result && gpio_table) - result = get_gpio_table(hwmgr, &pp_table_information->gpio_table, - gpio_table); + if (!result && powerplay_table->usGPIOTableOffset) { + result = get_tonga_gpio_table(hwmgr, powerplay_table, + &gpio_table); + if (!result) + result = get_gpio_table(hwmgr, + &pp_table_information->gpio_table, gpio_table); + } return result; } @@ -908,14 +1339,17 @@ static int init_thermal_controller( ) { const PPTable_Generic_SubTable_Header *fan_table; - ATOM_Tonga_Thermal_Controller *thermal_controller; + const ATOM_Tonga_Thermal_Controller *thermal_controller; + int ret; - thermal_controller = (ATOM_Tonga_Thermal_Controller *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usThermalControllerOffset)); PP_ASSERT_WITH_CODE((0 != powerplay_table->usThermalControllerOffset), "Thermal controller table not set!", return -1); + ret = get_tonga_thermal_controller_table(hwmgr, powerplay_table, + &thermal_controller); + if (ret) + return ret; + hwmgr->thermal_controller.ucType = thermal_controller->ucType; hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine; hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress; @@ -943,12 +1377,13 @@ static int init_thermal_controller( return 0; } - fan_table = (const PPTable_Generic_SubTable_Header *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usFanTableOffset)); - PP_ASSERT_WITH_CODE((0 != powerplay_table->usFanTableOffset), "Fan table not set!", return -1); + + ret = get_tonga_fan_table(hwmgr, powerplay_table, &fan_table); + if (ret) + return ret; + PP_ASSERT_WITH_CODE((0 < fan_table->ucRevId), "Unsupported fan table format!", return -1); @@ -1104,21 +1539,24 @@ static int init_thermal_controller( } /** - * check_powerplay_tables - Private Function used during initialization. - * Inspect the PowerPlay table for obvious signs of corruption. + * get_tonga_state_array - Get the Tonga state array from the PowerPlay table. * @hwmgr: Pointer to the hardware manager. * @powerplay_table: Pointer to the PowerPlay Table. - * Exception: 2 if the powerplay table is incorrect. + * @state_array: Pointer to the returned Tonga state array. + * + * Return: 0 on success, negative error code on failure. */ -static int check_powerplay_tables( - struct pp_hwmgr *hwmgr, - const ATOM_Tonga_POWERPLAYTABLE *powerplay_table - ) +static int get_tonga_state_array(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_State_Array **state_array) { const ATOM_Tonga_State_Array *state_arrays; + u16 state_array_offset; + size_t state_array_size; + size_t table_size = hwmgr->soft_pp_table_size; - state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + PP_ASSERT_WITH_CODE((table_size >= sizeof(*powerplay_table)), + "Invalid PowerPlay Table!", return -1); PP_ASSERT_WITH_CODE((ATOM_Tonga_TABLE_REVISION_TONGA <= powerplay_table->sHeader.ucTableFormatRevision), @@ -1127,12 +1565,34 @@ static int check_powerplay_tables( "State table is not set!", return -1); PP_ASSERT_WITH_CODE((0 < powerplay_table->sHeader.usStructureSize), "Invalid PowerPlay Table!", return -1); + + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + PP_ASSERT_WITH_CODE((state_array_offset <= + table_size - sizeof(*state_arrays)), + "Invalid PowerPlay Table!", return -1); + + state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) + + state_array_offset); PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries), "Invalid PowerPlay Table!", return -1); + state_array_size = struct_size(state_arrays, entries, state_arrays->ucNumEntries); + PP_ASSERT_WITH_CODE((state_array_size <= table_size - state_array_offset), + "Invalid PowerPlay Table!", return -1); + + *state_array = state_arrays; + return 0; } +static int check_powerplay_tables(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table) +{ + const ATOM_Tonga_State_Array *state_arrays; + + return get_tonga_state_array(hwmgr, powerplay_table, &state_arrays); +} + static int pp_tables_v1_0_initialize(struct pp_hwmgr *hwmgr) { int result = 0; @@ -1236,17 +1696,16 @@ const struct pp_table_func pptable_v1_0_funcs = { int get_number_of_powerplay_table_entries_v1_0(struct pp_hwmgr *hwmgr) { - ATOM_Tonga_State_Array const *state_arrays; + const ATOM_Tonga_State_Array *state_arrays; const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); + int result; PP_ASSERT_WITH_CODE((NULL != pp_table), "Missing PowerPlay Table!", return -1); - PP_ASSERT_WITH_CODE((pp_table->sHeader.ucTableFormatRevision >= - ATOM_Tonga_TABLE_REVISION_TONGA), - "Incorrect PowerPlay table revision!", return -1); - state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); + result = get_tonga_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), + "Invalid PowerPlay Table State Array.", return result); return (uint32_t)(state_arrays->ucNumEntries); } @@ -1287,13 +1746,15 @@ static int ppt_get_num_of_vce_state_table_entries_v1_0(struct pp_hwmgr *hwmgr) { const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); const ATOM_Tonga_VCE_State_Table *vce_state_table; + int ret; if (pp_table == NULL) return 0; - vce_state_table = (void *)pp_table + - le16_to_cpu(pp_table->usVCEStateTableOffset); + ret = get_tonga_vce_state_table(hwmgr, pp_table, &vce_state_table); + if (ret) + return 0; return vce_state_table->ucNumEntries; } @@ -1302,18 +1763,39 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i struct amd_vce_state *vce_state, void **clock_info, uint32_t *flag) { const ATOM_Tonga_VCE_State_Record *vce_state_record; - ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record; + ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record = NULL; + ATOM_Polaris_SCLK_Dependency_Record *polaris_sclk_dep_record = NULL; ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record; ATOM_Tonga_MM_Dependency_Record *mm_dep_record; const ATOM_Tonga_POWERPLAYTABLE *pptable = get_powerplay_table(hwmgr); - const ATOM_Tonga_VCE_State_Table *vce_state_table = (ATOM_Tonga_VCE_State_Table *)(((unsigned long)pptable) - + le16_to_cpu(pptable->usVCEStateTableOffset)); - const ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table = (ATOM_Tonga_SCLK_Dependency_Table *)(((unsigned long)pptable) - + le16_to_cpu(pptable->usSclkDependencyTableOffset)); - const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table = (ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long)pptable) - + le16_to_cpu(pptable->usMclkDependencyTableOffset)); - const ATOM_Tonga_MM_Dependency_Table *mm_dep_table = (ATOM_Tonga_MM_Dependency_Table *)(((unsigned long)pptable) - + le16_to_cpu(pptable->usMMDependencyTableOffset)); + const ATOM_Tonga_VCE_State_Table *vce_state_table; + const PPTable_Generic_SubTable_Header *sclk_dep_table_header; + const ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table; + const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table; + const ATOM_Tonga_MM_Dependency_Table *mm_dep_table; + int ret; + + if (!pptable) + return -EINVAL; + + ret = get_tonga_vce_state_table(hwmgr, pptable, &vce_state_table); + if (ret) + return ret; + + ret = get_tonga_sclk_dependency_table(hwmgr, pptable, + &sclk_dep_table_header); + if (ret) + return ret; + sclk_dep_table = (const ATOM_Tonga_SCLK_Dependency_Table *) + sclk_dep_table_header; + + ret = get_tonga_mclk_dependency_table(hwmgr, pptable, &mclk_dep_table); + if (ret) + return ret; + + ret = get_tonga_mm_dependency_table(hwmgr, pptable, &mm_dep_table); + if (ret) + return ret; PP_ASSERT_WITH_CODE((i < vce_state_table->ucNumEntries), "Requested state entry ID is out of range!", @@ -1322,10 +1804,27 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i vce_state_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_VCE_State_Record, entries, vce_state_table, i); - sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( - ATOM_Tonga_SCLK_Dependency_Record, - entries, sclk_dep_table, - vce_state_record->ucSCLKIndex); + PP_ASSERT_WITH_CODE((vce_state_record->ucSCLKIndex < + sclk_dep_table->ucNumEntries), + "Invalid PowerPlay Table!", return -EINVAL); + PP_ASSERT_WITH_CODE((vce_state_record->ucVCEClockIndex < + mm_dep_table->ucNumEntries), + "Invalid PowerPlay Table!", return -EINVAL); + PP_ASSERT_WITH_CODE((mclk_dep_table->ucNumEntries != 0), + "Invalid PowerPlay Table!", return -EINVAL); + + if (sclk_dep_table_header->ucRevId < 1) + sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( + ATOM_Tonga_SCLK_Dependency_Record, + entries, sclk_dep_table, + vce_state_record->ucSCLKIndex); + else + polaris_sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( + ATOM_Polaris_SCLK_Dependency_Record, + entries, + (ATOM_Polaris_SCLK_Dependency_Table *) + sclk_dep_table_header, + vce_state_record->ucSCLKIndex); mm_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_MM_Dependency_Record, entries, mm_dep_table, @@ -1334,7 +1833,10 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i vce_state->evclk = le32_to_cpu(mm_dep_record->ulEClk); vce_state->ecclk = le32_to_cpu(mm_dep_record->ulEClk); - vce_state->sclk = le32_to_cpu(sclk_dep_record->ulSclk); + if (sclk_dep_record) + vce_state->sclk = le32_to_cpu(sclk_dep_record->ulSclk); + else + vce_state->sclk = le32_to_cpu(polaris_sclk_dep_record->ulSclk); if (vce_state_record->ucMCLKIndex >= mclk_dep_table->ucNumEntries) mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( @@ -1377,15 +1879,11 @@ int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr, if (pp_table->sHeader.ucTableFormatRevision >= ATOM_Tonga_TABLE_REVISION_TONGA) { - state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); - - PP_ASSERT_WITH_CODE((0 < pp_table->usStateArrayOffset), - "Invalid PowerPlay Table State Array Offset.", return -1); - PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries), - "Invalid PowerPlay Table State Array.", return -1); - PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries), - "Invalid PowerPlay Table State Array Entry.", return -1); + result = get_tonga_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), + "Invalid PowerPlay Table State Array.", return result); + PP_ASSERT_WITH_CODE((entry_index < state_arrays->ucNumEntries), + "Invalid PowerPlay Table State Array Entry.", return -1); state_entry = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_State, entries, @@ -1411,4 +1909,3 @@ int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr, return result; } - diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c index bfd8fbb0b49d..56926eec6820 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c @@ -47,26 +47,53 @@ #define NUM_BITS_CLOCK_INFO_ARRAY_INDEX 6 +static bool pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset, + size_t size) +{ + size_t table_size = hwmgr->soft_pp_table_size; + + return offset <= table_size && size <= table_size - offset; +} + +static const ATOM_PPLIB_EXTENDEDHEADER * +get_extended_header(struct pp_hwmgr *hwmgr, + const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table, + size_t min_size) +{ + const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; + u16 offset; + + if (le16_to_cpu(powerplay_table->usTableSize) < + sizeof(ATOM_PPLIB_POWERPLAYTABLE3) || + !pp_table_has_space(hwmgr, 0, sizeof(ATOM_PPLIB_POWERPLAYTABLE3))) + return NULL; + + powerplay_table3 = (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; + offset = le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset); + if (!offset || !pp_table_has_space(hwmgr, offset, + sizeof(extended_header->usSize))) + return NULL; + + extended_header = (const ATOM_PPLIB_EXTENDEDHEADER *) + (((unsigned long)powerplay_table) + offset); + if (le16_to_cpu(extended_header->usSize) < min_size || + !pp_table_has_space(hwmgr, offset, min_size)) + return NULL; + + return extended_header; +} + static uint16_t get_vce_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t vce_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2) - vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2); + if (extended_header) + vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset); return vce_table_offset; } @@ -93,7 +120,14 @@ static uint16_t get_vce_clock_info_array_size(struct pp_hwmgr *hwmgr, if (table_offset > 0) { const VCEClockInfoArray *p = (const VCEClockInfoArray *) (((unsigned long) powerplay_table) + table_offset); - table_size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo); + size_t size; + + if (!pp_table_has_space(hwmgr, table_offset, sizeof(p->ucNumEntries))) + return 0; + + size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; @@ -104,10 +138,13 @@ static uint16_t get_vce_clock_voltage_limit_table_offset(struct pp_hwmgr *hwmgr, { uint16_t table_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + get_vce_clock_info_array_size(hwmgr, - powerplay_table); + if (table_offset > 0) { + table_size = get_vce_clock_info_array_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -121,8 +158,15 @@ static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr, if (table_offset > 0) { const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *ptable = (const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *)(((unsigned long) powerplay_table) + table_offset); + size_t size; - table_size = sizeof(uint8_t) + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record); + if (!pp_table_has_space(hwmgr, table_offset, sizeof(ptable->numEntries))) + return 0; + + size = sizeof(uint8_t) + + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; } @@ -130,9 +174,13 @@ static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr, static uint16_t get_vce_state_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t table_offset = get_vce_clock_voltage_limit_table_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table); + if (table_offset > 0) { + table_size = get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -143,8 +191,12 @@ static const ATOM_PPLIB_VCE_State_Table *get_vce_state_table( { uint16_t table_offset = get_vce_state_table_offset(hwmgr, powerplay_table); - if (table_offset > 0) - return (const ATOM_PPLIB_VCE_State_Table *)(((unsigned long) powerplay_table) + table_offset); + if (table_offset > 0) { + if (pp_table_has_space(hwmgr, table_offset, + sizeof(((ATOM_PPLIB_VCE_State_Table *)0)->numEntries))) + return (const ATOM_PPLIB_VCE_State_Table *) + (((unsigned long)powerplay_table) + table_offset); + } return NULL; } @@ -153,21 +205,13 @@ static uint16_t get_uvd_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t uvd_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; + + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3); + if (extended_header) + uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset); - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3) - uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset); - } - } return uvd_table_offset; } @@ -193,8 +237,14 @@ static uint16_t get_uvd_clock_info_array_size(struct pp_hwmgr *hwmgr, const UVDClockInfoArray *p = (const UVDClockInfoArray *) (((unsigned long) powerplay_table) + table_offset); - table_size = sizeof(UCHAR) + - p->ucNumEntries * sizeof(UVDClockInfo); + size_t size; + + if (!pp_table_has_space(hwmgr, table_offset, sizeof(p->ucNumEntries))) + return 0; + + size = sizeof(UCHAR) + p->ucNumEntries * sizeof(UVDClockInfo); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; @@ -206,10 +256,13 @@ static uint16_t get_uvd_clock_voltage_limit_table_offset( { uint16_t table_offset = get_uvd_clock_info_array_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + - get_uvd_clock_info_array_size(hwmgr, powerplay_table); + if (table_offset > 0) { + table_size = get_uvd_clock_info_array_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -218,21 +271,12 @@ static uint16_t get_samu_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t samu_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4) - samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4); + if (extended_header) + samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset); return samu_table_offset; } @@ -254,21 +298,12 @@ static uint16_t get_acp_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t acp_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6) - acp_table_offset = le16_to_cpu(pExtendedHeader->usACPTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6); + if (extended_header) + acp_table_offset = le16_to_cpu(extended_header->usACPTableOffset); return acp_table_offset; } @@ -290,21 +325,12 @@ static uint16_t get_cacp_tdp_table_offset( const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t cacTdpTableOffset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7) - cacTdpTableOffset = le16_to_cpu(pExtendedHeader->usPowerTuneTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7); + if (extended_header) + cacTdpTableOffset = le16_to_cpu(extended_header->usPowerTuneTableOffset); return cacTdpTableOffset; } @@ -341,22 +367,13 @@ static uint16_t get_sclk_vdd_gfx_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t sclk_vdd_gfx_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8) - sclk_vdd_gfx_table_offset = - le16_to_cpu(pExtendedHeader->usSclkVddgfxTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8); + if (extended_header) + sclk_vdd_gfx_table_offset = + le16_to_cpu(extended_header->usSclkVddgfxTableOffset); return sclk_vdd_gfx_table_offset; } @@ -769,20 +786,37 @@ static ULONG size_of_entry_v2(ULONG num_dpm_levels) } static const ATOM_PPLIB_STATE_V2 *get_state_entry_v2( + struct pp_hwmgr *hwmgr, const StateArray * pstate_arrays, + u16 state_array_offset, ULONG entry_index) { ULONG i; const ATOM_PPLIB_STATE_V2 *pstate; + size_t entry_offset; + size_t entry_size; + + if (entry_index >= pstate_arrays->ucNumEntries) + return NULL; + entry_offset = state_array_offset + sizeof(pstate_arrays->ucNumEntries); pstate = pstate_arrays->states; - if (entry_index <= pstate_arrays->ucNumEntries) { - for (i = 0; i < entry_index; i++) - pstate = (ATOM_PPLIB_STATE_V2 *)( - (unsigned long)pstate + - size_of_entry_v2(pstate->ucNumDPMLevels)); + for (i = 0; i <= entry_index; i++) { + if (!pp_table_has_space(hwmgr, entry_offset, sizeof(*pstate))) + return NULL; + + entry_size = size_of_entry_v2(pstate->ucNumDPMLevels); + if (!pp_table_has_space(hwmgr, entry_offset, entry_size)) + return NULL; + + if (i == entry_index) + return pstate; + + entry_offset += entry_size; + pstate = (ATOM_PPLIB_STATE_V2 *)((unsigned long)pstate + entry_size); } - return pstate; + + return NULL; } static const unsigned char soft_dummy_pp_table[] = { @@ -850,6 +884,8 @@ int pp_tables_get_response_times(struct pp_hwmgr *hwmgr, PP_ASSERT_WITH_CODE(NULL != powerplay_tab, "Missing PowerPlay Table!", return -EINVAL); + PP_ASSERT_WITH_CODE(pp_table_has_space(hwmgr, 0, sizeof(*powerplay_tab)), + "Invalid PowerPlay Table!", return -EINVAL); *vol_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usVoltageTime); *bb_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usBackbiasTime); @@ -862,13 +898,21 @@ int pp_tables_get_num_of_entries(struct pp_hwmgr *hwmgr, { const StateArray *pstate_arrays; const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr); + u16 state_array_offset; if (powerplay_table == NULL) return -1; + if (!pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; if (powerplay_table->sHeader.ucTableFormatRevision >= 6) { + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + if (!pp_table_has_space(hwmgr, state_array_offset, + sizeof(pstate_arrays->ucNumEntries))) + return -1; + pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + state_array_offset); *num_of_entries = (unsigned long)(pstate_arrays->ucNumEntries); } else @@ -895,49 +939,128 @@ int pp_tables_get_entry(struct pp_hwmgr *hwmgr, const NonClockInfoArray *pnon_clock_arrays; const ATOM_PPLIB_STATE *pstate_entry; + u16 state_array_offset; + u16 clock_info_array_offset; + u16 non_clock_info_array_offset; + size_t clock_info_offset; + size_t non_clock_info_offset; + size_t state_entry_offset; if (powerplay_table == NULL) return -1; + if (!pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; ps->classification.bios_index = entry_index; if (powerplay_table->sHeader.ucTableFormatRevision >= 6) { + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + if (!pp_table_has_space(hwmgr, state_array_offset, + sizeof(pstate_arrays->ucNumEntries))) + return -1; + pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + state_array_offset); + + if (entry_index >= pstate_arrays->ucNumEntries) + return -1; - if (entry_index > pstate_arrays->ucNumEntries) + pstate_entry_v2 = get_state_entry_v2(hwmgr, pstate_arrays, + state_array_offset, + entry_index); + if (!pstate_entry_v2) + return -1; + + clock_info_array_offset = + le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, clock_info_array_offset, + sizeof(*pclock_arrays))) return -1; - pstate_entry_v2 = get_state_entry_v2(pstate_arrays, entry_index); pclock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset)); + clock_info_array_offset); + if (!pclock_arrays->ucEntrySize) + return -1; + + non_clock_info_array_offset = + le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, non_clock_info_array_offset, + sizeof(*pnon_clock_arrays))) + return -1; pnon_clock_arrays = (NonClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset)); + non_clock_info_array_offset); + if (!pnon_clock_arrays->ucEntrySize || + pnon_clock_arrays->ucEntrySize < ATOM_PPLIB_NONCLOCKINFO_VER1 || + (pnon_clock_arrays->ucEntrySize > ATOM_PPLIB_NONCLOCKINFO_VER1 && + pnon_clock_arrays->ucEntrySize < ATOM_PPLIB_NONCLOCKINFO_VER2) || + pstate_entry_v2->nonClockInfoIndex >= pnon_clock_arrays->ucNumEntries) + return -1; + non_clock_info_offset = non_clock_info_array_offset + + offsetof(NonClockInfoArray, nonClockInfo) + + pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, non_clock_info_offset, + pnon_clock_arrays->ucEntrySize)) + return -1; pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)(pnon_clock_arrays->nonClockInfo) + (pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize)); result = init_non_clock_fields(hwmgr, ps, pnon_clock_arrays->ucEntrySize, pnon_clock_info); for (i = 0; i < pstate_entry_v2->ucNumDPMLevels; i++) { - const void *pclock_info = (const void *)( - (unsigned long)(pclock_arrays->clockInfo) + - (pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize)); + const void *pclock_info; + + if (pstate_entry_v2->clockInfoIndex[i] >= + pclock_arrays->ucNumEntries) + return -1; + + clock_info_offset = clock_info_array_offset + + offsetof(ClockInfoArray, clockInfo) + + pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, clock_info_offset, + pclock_arrays->ucEntrySize)) + return -1; + + pclock_info = (const void *) + ((unsigned long)(pclock_arrays->clockInfo) + + (pstate_entry_v2->clockInfoIndex[i] * + pclock_arrays->ucEntrySize)); res = func(hwmgr, &ps->hardware, i, pclock_info); if ((0 == result) && (0 != res)) result = res; } } else { - if (entry_index > powerplay_table->ucNumStates) + if (entry_index >= powerplay_table->ucNumStates || + !powerplay_table->ucStateEntrySize || + !powerplay_table->ucNonClockSize || + powerplay_table->ucNonClockSize < ATOM_PPLIB_NONCLOCKINFO_VER1 || + (powerplay_table->ucNonClockSize > ATOM_PPLIB_NONCLOCKINFO_VER1 && + powerplay_table->ucNonClockSize < ATOM_PPLIB_NONCLOCKINFO_VER2) || + !powerplay_table->ucClockInfoSize) + return -1; + + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + state_entry_offset = state_array_offset + + entry_index * powerplay_table->ucStateEntrySize; + if (!pp_table_has_space(hwmgr, state_entry_offset, + powerplay_table->ucStateEntrySize)) return -1; pstate_entry = (ATOM_PPLIB_STATE *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usStateArrayOffset) + + state_array_offset + entry_index * powerplay_table->ucStateEntrySize); + non_clock_info_array_offset = + le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset); + non_clock_info_offset = non_clock_info_array_offset + + pstate_entry->ucNonClockStateIndex * powerplay_table->ucNonClockSize; + if (!pp_table_has_space(hwmgr, non_clock_info_offset, + powerplay_table->ucNonClockSize)) + return -1; + pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset) + + non_clock_info_array_offset + pstate_entry->ucNonClockStateIndex * powerplay_table->ucNonClockSize); @@ -946,12 +1069,23 @@ int pp_tables_get_entry(struct pp_hwmgr *hwmgr, pnon_clock_info); for (i = 0; i < powerplay_table->ucStateEntrySize-1; i++) { - const void *pclock_info = (const void *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset) + + const void *pclock_info; + + clock_info_array_offset = + le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + clock_info_offset = clock_info_array_offset + + pstate_entry->ucClockStateIndices[i] * + powerplay_table->ucClockInfoSize; + if (!pp_table_has_space(hwmgr, clock_info_offset, + powerplay_table->ucClockInfoSize)) + return -1; + + pclock_info = (const void *)((unsigned long)powerplay_table + + clock_info_array_offset + pstate_entry->ucClockStateIndices[i] * powerplay_table->ucClockInfoSize); - int res = func(hwmgr, &ps->hardware, i, pclock_info); + res = func(hwmgr, &ps->hardware, i, pclock_info); if ((0 == result) && (0 != res)) result = res; @@ -1661,21 +1795,70 @@ static int get_vce_state_table_entry(struct pp_hwmgr *hwmgr, unsigned long *flag) { const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr); + const ATOM_PPLIB_VCE_State_Table *vce_state_table; + const ATOM_PPLIB_VCE_State_Record *record; + const VCEClockInfoArray *vce_clock_info_array; + const VCEClockInfo *vce_clock_info; + const ClockInfoArray *clock_arrays; + u16 vce_state_table_offset; + u16 vce_clock_info_array_offset; + u16 clock_info_array_offset; + unsigned long clockInfoIndex; + size_t record_offset; + size_t vce_clock_info_offset; + size_t clock_info_offset; + + if (!powerplay_table || !pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; - const ATOM_PPLIB_VCE_State_Table *vce_state_table = get_vce_state_table(hwmgr, powerplay_table); + vce_state_table_offset = get_vce_state_table_offset(hwmgr, powerplay_table); + vce_state_table = get_vce_state_table(hwmgr, powerplay_table); + if (!vce_state_table || i >= vce_state_table->numEntries) + return -1; + + record_offset = vce_state_table_offset + + offsetof(ATOM_PPLIB_VCE_State_Table, entries) + + i * sizeof(*record); + if (!pp_table_has_space(hwmgr, record_offset, sizeof(*record))) + return -1; + + record = &vce_state_table->entries[i]; + + vce_clock_info_array_offset = + get_vce_clock_info_array_offset(hwmgr, powerplay_table); + if (!pp_table_has_space(hwmgr, vce_clock_info_array_offset, + sizeof(vce_clock_info_array->ucNumEntries))) + return -1; - unsigned short vce_clock_info_array_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table); + vce_clock_info_array = (const VCEClockInfoArray *) + (((unsigned long)powerplay_table) + vce_clock_info_array_offset); + if (record->ucVCEClockInfoIndex >= vce_clock_info_array->ucNumEntries) + return -1; - const VCEClockInfoArray *vce_clock_info_array = (const VCEClockInfoArray *)(((unsigned long) powerplay_table) + vce_clock_info_array_offset); + vce_clock_info_offset = vce_clock_info_array_offset + + offsetof(VCEClockInfoArray, entries) + + record->ucVCEClockInfoIndex * sizeof(*vce_clock_info); + if (!pp_table_has_space(hwmgr, vce_clock_info_offset, sizeof(*vce_clock_info))) + return -1; - const ClockInfoArray *clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset)); + vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex]; - const ATOM_PPLIB_VCE_State_Record *record = &vce_state_table->entries[i]; + clock_info_array_offset = le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, clock_info_array_offset, + sizeof(*clock_arrays))) + return -1; - const VCEClockInfo *vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex]; + clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + + clock_info_array_offset); + clockInfoIndex = record->ucClockInfoIndex & 0x3F; + if (!clock_arrays->ucEntrySize || clockInfoIndex >= clock_arrays->ucNumEntries) + return -1; - unsigned long clockInfoIndex = record->ucClockInfoIndex & 0x3F; + clock_info_offset = clock_info_array_offset + + offsetof(ClockInfoArray, clockInfo) + + clockInfoIndex * clock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, clock_info_offset, clock_arrays->ucEntrySize)) + return -1; *flag = (record->ucClockInfoIndex >> NUM_BITS_CLOCK_INFO_ARRAY_INDEX); @@ -1699,6 +1882,10 @@ static int pp_tables_initialize(struct pp_hwmgr *hwmgr) hwmgr->need_pp_table_upload = true; powerplay_table = get_powerplay_table(hwmgr); + PP_ASSERT_WITH_CODE((powerplay_table), + "Missing PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE(pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table)), + "Invalid PowerPlay Table!", return -1); result = init_powerplay_tables(hwmgr, powerplay_table); @@ -1801,4 +1988,3 @@ const struct pp_table_func pptable_funcs = { .pptable_get_vce_state_table_entry = get_vce_state_table_entry, }; - diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index 95bf187f02a5..bdf1e489369e 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -2216,12 +2216,24 @@ static int smu7_patch_voltage_dependency_tables_with_lookup_table( if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) { for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) { voltage_id = sclk_table->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddgfx_lookup_table->count) { + pr_err("amdgpu: sclk[%u] vddgfx index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddgfx_lookup_table->count); + return -EINVAL; + } sclk_table->entries[entry_id].vddgfx = table_info->vddgfx_lookup_table->entries[voltage_id].us_vdd; } } else { for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) { voltage_id = sclk_table->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: sclk[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } sclk_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } @@ -2229,12 +2241,24 @@ static int smu7_patch_voltage_dependency_tables_with_lookup_table( for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) { voltage_id = mclk_table->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: mclk[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } mclk_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } for (entry_id = 0; entry_id < mm_table->count; ++entry_id) { voltage_id = mm_table->entries[entry_id].vddcInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: mm[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } mm_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } @@ -2964,7 +2988,7 @@ static int smu7_init_voltage_dependency_on_display_clock_table(struct pp_hwmgr * if (!amdgpu_device_ip_get_ip_block(hwmgr->adev, AMD_IP_BLOCK_TYPE_DCE)) return 0; - table = kzalloc(struct_size(table, entries, 4), GFP_KERNEL); + table = kzalloc_flex(*table, entries, 4); if (!table) return -ENOMEM; @@ -4062,7 +4086,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) (adev->asic_type != CHIP_FIJI) && (adev->asic_type != CHIP_TONGA)) { smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0, &tmp); - *query = tmp; + *query = PP_PWR_Q24_8_TO_MW(tmp); if (tmp != 0) return 0; @@ -4081,7 +4105,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) if (tmp != 0) break; } - *query = tmp; + *query = PP_PWR_Q24_8_TO_MW(tmp); return 0; } @@ -5648,23 +5672,29 @@ static int smu7_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, } for (i = 0; i < size; i += 3) { - if (i + 3 > size || input[i] >= podn_dpm_table_in_backend->num_of_pl) { - pr_info("invalid clock voltage input \n"); - return 0; + if (i + 3 > size) { + pr_info("truncated clock/voltage input\n"); + return -EINVAL; } - input_level = input[i]; - input_clk = input[i+1] * 100; - input_vol = input[i+2]; - - if (smu7_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) { - podn_dpm_table_in_backend->entries[input_level].clock = input_clk; - podn_vdd_dep_in_backend->entries[input_level].clk = input_clk; - podn_dpm_table_in_backend->entries[input_level].vddc = input_vol; - podn_vdd_dep_in_backend->entries[input_level].vddc = input_vol; - podn_vdd_dep_in_backend->entries[input_level].vddgfx = input_vol; - } else { + if (input[i] < 0 || input[i] >= podn_dpm_table_in_backend->num_of_pl) { + pr_info("invalid clock/voltage level\n"); return -EINVAL; } + input_clk = input[i + 1] * 100; + input_vol = input[i + 2]; + if (!smu7_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) + return -EINVAL; + } + + for (i = 0; i < size; i += 3) { + input_level = input[i]; + input_clk = input[i + 1] * 100; + input_vol = input[i + 2]; + podn_dpm_table_in_backend->entries[input_level].clock = input_clk; + podn_vdd_dep_in_backend->entries[input_level].clk = input_clk; + podn_dpm_table_in_backend->entries[input_level].vddc = input_vol; + podn_vdd_dep_in_backend->entries[input_level].vddc = input_vol; + podn_vdd_dep_in_backend->entries[input_level].vddgfx = input_vol; } return 0; @@ -5857,15 +5887,19 @@ static int smu7_power_off_asic(struct pp_hwmgr *hwmgr) static void smu7_notify_ac_dc(struct pp_hwmgr *hwmgr) { struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev); + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - /* Check if the platform already manages the AC/DC switch via dedicated GPIO. */ - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, + /* + * Check if the platform already manages the AC/DC switch via dedicated GPIO. + * Otherwise SMU automatically notices DC, but needs to be notified of AC. + */ + if (adev->pm.ac_power && + phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_AutomaticDCTransition)) - return; - - /* The SMU automatically notices DC, but needs to be notified when switching to AC. */ - if (adev->pm.ac_power) smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunningOnAC, NULL); + + /* Recompute clocks with updated max_limits. */ + pp_funcs->pm_compute_clocks(adev->powerplay.pp_handle); } static const struct pp_hwmgr_func smu7_hwmgr_funcs = { diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c index 4b92b52aba2b..05b1e69116fd 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c @@ -685,10 +685,18 @@ static int vega10_patch_voltage_dependency_tables_with_lookup_table( case 3: vdt = table_info->vdd_dep_on_pixclk; break; case 4: vdt = table_info->vdd_dep_on_dispclk; break; case 5: vdt = table_info->vdd_dep_on_phyclk; break; + default: + continue; } for (entry_id = 0; entry_id < vdt->count; entry_id++) { voltage_id = vdt->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: clk_dep[%u][%u] vddc index %u out of bounds (%u)\n", + i, entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } vdt->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } @@ -696,23 +704,48 @@ static int vega10_patch_voltage_dependency_tables_with_lookup_table( for (entry_id = 0; entry_id < mm_table->count; ++entry_id) { voltage_id = mm_table->entries[entry_id].vddcInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: mm[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } mm_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) { voltage_id = mclk_table->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: mclk[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } mclk_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; + voltage_id = mclk_table->entries[entry_id].vddciInd; + if (voltage_id >= table_info->vddci_lookup_table->count) { + pr_err("amdgpu: mclk[%u] vddci index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddci_lookup_table->count); + return -EINVAL; + } mclk_table->entries[entry_id].vddci = table_info->vddci_lookup_table->entries[voltage_id].us_vdd; + voltage_id = mclk_table->entries[entry_id].mvddInd; + if (voltage_id >= table_info->vddmem_lookup_table->count) { + pr_err("amdgpu: mclk[%u] vddmem index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddmem_lookup_table->count); + return -EINVAL; + } mclk_table->entries[entry_id].mvdd = table_info->vddmem_lookup_table->entries[voltage_id].us_vdd; } - return 0; } @@ -3934,8 +3967,8 @@ static int vega10_get_gpu_power(struct pp_hwmgr *hwmgr, if (ret) return ret; - /* SMC returning actual watts, keep consistent with legacy asics, low 8 bit as 8 fractional bits */ - *query = value << 8; + /* SMC returns whole Watts, while power sensors use milliwatts. */ + *query = value * MILLIWATT_PER_WATT; return 0; } @@ -5215,6 +5248,11 @@ static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui uint8_t min_active_level; uint32_t power_profile_mode = input[size]; + if (power_profile_mode > PP_SMC_POWER_PROFILE_CUSTOM) { + pr_err("Invalid power profile mode %u\n", power_profile_mode); + return -EINVAL; + } + if (power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) { if (size != 0 && size != 4) return -EINVAL; @@ -5230,6 +5268,10 @@ static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui return -EINVAL; } + if ((input[0] & ~0xFF) || (input[1] & ~0xFF) || + (input[2] & ~0xFF) || (input[3] & ~0xFF)) + return -EINVAL; + data->custom_profile_mode[0] = busy_set_point = input[0]; data->custom_profile_mode[1] = FPS = input[1]; data->custom_profile_mode[2] = use_rlc_busy = input[2]; @@ -5454,11 +5496,9 @@ static int vega10_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, if (PP_OD_EDIT_SCLK_VDDC_TABLE == type) { dpm_table = &data->dpm_table.gfx_table; podn_vdd_dep_table = &data->odn_dpm_table.vdd_dep_on_sclk; - data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK; } else if (PP_OD_EDIT_MCLK_VDDC_TABLE == type) { dpm_table = &data->dpm_table.mem_table; podn_vdd_dep_table = &data->odn_dpm_table.vdd_dep_on_mclk; - data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK; } else if (PP_OD_RESTORE_DEFAULT_TABLE == type) { memcpy(&(data->dpm_table), &(data->golden_dpm_table), sizeof(struct vega10_dpm_table)); vega10_odn_initial_default_setting(hwmgr); @@ -5476,21 +5516,32 @@ static int vega10_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, } for (i = 0; i < size; i += 3) { - if (i + 3 > size || input[i] >= podn_vdd_dep_table->count) { - pr_info("invalid clock voltage input\n"); - return 0; + if (i + 3 > size) { + pr_info("truncated clock/voltage input\n"); + return -EINVAL; } - input_level = input[i]; - input_clk = input[i+1] * 100; - input_vol = input[i+2]; - - if (vega10_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) { - dpm_table->dpm_levels[input_level].value = input_clk; - podn_vdd_dep_table->entries[input_level].clk = input_clk; - podn_vdd_dep_table->entries[input_level].vddc = input_vol; - } else { + if (input[i] < 0 || input[i] >= podn_vdd_dep_table->count) { + pr_info("invalid clock/voltage level\n"); return -EINVAL; } + input_clk = input[i + 1] * 100; + input_vol = input[i + 2]; + if (!vega10_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) + return -EINVAL; + } + + if (type == PP_OD_EDIT_SCLK_VDDC_TABLE) + data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK; + else + data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK; + + for (i = 0; i < size; i += 3) { + input_level = input[i]; + input_clk = input[i + 1] * 100; + input_vol = input[i + 2]; + dpm_table->dpm_levels[input_level].value = input_clk; + podn_vdd_dep_table->entries[input_level].clk = input_clk; + podn_vdd_dep_table->entries[input_level].vddc = input_vol; } vega10_odn_update_soc_table(hwmgr, type); return 0; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c index 052d139584fd..62b1e068f90d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c @@ -63,14 +63,57 @@ static const void *get_powerplay_table(struct pp_hwmgr *hwmgr) return table_address; } -static int check_powerplay_tables( - struct pp_hwmgr *hwmgr, - const ATOM_Vega10_POWERPLAYTABLE *powerplay_table) +static bool vega10_pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset, + size_t size) +{ + size_t table_size = hwmgr->soft_pp_table_size; + + return offset <= table_size && size <= table_size - offset; +} + +static int get_vega10_subtable(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + u16 table_offset, size_t table_size, const void **table) +{ + PP_ASSERT_WITH_CODE((table_offset != 0), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *table = (const void *)(((unsigned long)powerplay_table) + table_offset); + + return 0; +} + +static int validate_vega10_table_entries(struct pp_hwmgr *hwmgr, + u16 table_offset, size_t entries_offset, + u8 num_entries, size_t entry_size) +{ + size_t table_size; + + PP_ASSERT_WITH_CODE((num_entries != 0), + "Invalid PowerPlay Table!", return -1); + + table_size = entries_offset + num_entries * entry_size; + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + return 0; +} + +static int get_vega10_state_array(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_State_Array **state_array) { const ATOM_Vega10_State_Array *state_arrays; + u16 state_array_offset; + size_t state_array_size; + size_t table_size = hwmgr->soft_pp_table_size; - state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + PP_ASSERT_WITH_CODE((table_size >= sizeof(*powerplay_table)), + "Invalid PowerPlay Table!", return -1); PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_Vega10_TABLE_REVISION_VEGA10), @@ -79,12 +122,321 @@ static int check_powerplay_tables( "State table is not set!", return -1); PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0, "Invalid PowerPlay Table!", return -1); + + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + PP_ASSERT_WITH_CODE((state_array_offset <= + table_size - sizeof(*state_arrays)), + "Invalid PowerPlay Table!", return -1); + + state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) + + state_array_offset); PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0, "Invalid PowerPlay Table!", return -1); + state_array_size = struct_size(state_arrays, states, state_arrays->ucNumEntries); + PP_ASSERT_WITH_CODE((state_array_size <= table_size - state_array_offset), + "Invalid PowerPlay Table!", return -1); + + *state_array = state_arrays; + return 0; } +static int get_vega10_gfxclk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_GFXCLK_Dependency_Table **gfxclk_dep_table) +{ + const ATOM_Vega10_GFXCLK_Dependency_Table *table; + u16 table_offset; + size_t table_size; + size_t entry_size; + + table_offset = le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset); + if (!table_offset) + return -EINVAL; + + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + sizeof(*table))), + "Invalid PowerPlay Table!", return -1); + + table = (const ATOM_Vega10_GFXCLK_Dependency_Table *) + (((unsigned long)powerplay_table) + table_offset); + PP_ASSERT_WITH_CODE((table->ucNumEntries != 0), + "Invalid PowerPlay Table!", return -1); + + if (table->ucRevId == 0) + entry_size = sizeof(ATOM_Vega10_GFXCLK_Dependency_Record); + else if (table->ucRevId == 1) + entry_size = sizeof(ATOM_Vega10_GFXCLK_Dependency_Record_V2); + else + PP_ASSERT_WITH_CODE(false, + "Unsupported GFXClockDependencyTable Revision!", + return -EINVAL); + + table_size = offsetof(ATOM_Vega10_GFXCLK_Dependency_Table, entries) + + table->ucNumEntries * entry_size; + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *gfxclk_dep_table = table; + + return 0; +} + +static int get_vega10_clk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + u16 table_offset, + const ATOM_Vega10_SOCCLK_Dependency_Table **clk_dep_table) +{ + const ATOM_Vega10_SOCCLK_Dependency_Table *table; + int ret; + + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_SOCCLK_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_CLK_Dependency_Record)); + if (ret) + return ret; + + *clk_dep_table = table; + + return 0; +} + +static int get_vega10_mclk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_MCLK_Dependency_Table **mclk_dep_table) +{ + const ATOM_Vega10_MCLK_Dependency_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usMclkDependencyTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_MCLK_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_MCLK_Dependency_Record)); + if (ret) + return ret; + + *mclk_dep_table = table; + + return 0; +} + +static int get_vega10_mm_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_MM_Dependency_Table **mm_dep_table) +{ + const ATOM_Vega10_MM_Dependency_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usMMDependencyTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_MM_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_MM_Dependency_Record)); + if (ret) + return ret; + + *mm_dep_table = table; + + return 0; +} + +static int get_vega10_pcie_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const Vega10_PPTable_Generic_SubTable_Header **pcie_table) +{ + const ATOM_Vega10_PCIE_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usPCIETableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + if (!table->ucNumEntries) { + *pcie_table = (const Vega10_PPTable_Generic_SubTable_Header *)table; + return 0; + } + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_PCIE_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_PCIE_Record)); + if (ret) + return ret; + + *pcie_table = (const Vega10_PPTable_Generic_SubTable_Header *)table; + + return 0; +} + +static int get_vega10_hard_limit_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_Hard_Limit_Table **hard_limit_table) +{ + const ATOM_Vega10_Hard_Limit_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usHardLimitTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_Hard_Limit_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_Hard_Limit_Record)); + if (ret) + return ret; + + *hard_limit_table = table; + + return 0; +} + +static int get_vega10_thermal_controller_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_Thermal_Controller **thermal_controller) +{ + u16 table_offset; + + table_offset = le16_to_cpu(powerplay_table->usThermalControllerOffset); + + return get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(**thermal_controller), + (const void **)thermal_controller); +} + +static int get_vega10_fan_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const Vega10_PPTable_Generic_SubTable_Header **fan_table) +{ + const Vega10_PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t table_size; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usFanTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId == 10) + table_size = sizeof(ATOM_Vega10_Fan_Table); + else if (header->ucRevId == 0xb) + table_size = sizeof(ATOM_Vega10_Fan_Table_V2); + else if (header->ucRevId > 0xb) + table_size = sizeof(ATOM_Vega10_Fan_Table_V3); + else + table_size = sizeof(*header); + + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *fan_table = header; + + return 0; +} + +static int get_vega10_power_tune_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const Vega10_PPTable_Generic_SubTable_Header **power_tune_table) +{ + const Vega10_PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t table_size; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usPowerTuneTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId == 5) + table_size = sizeof(ATOM_Vega10_PowerTune_Table); + else if (header->ucRevId == 6) + table_size = sizeof(ATOM_Vega10_PowerTune_Table_V2); + else + table_size = sizeof(ATOM_Vega10_PowerTune_Table_V3); + + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *power_tune_table = header; + + return 0; +} + +static int get_vega10_voltage_lookup_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + u16 table_offset, uint32_t max_levels, + const ATOM_Vega10_Voltage_Lookup_Table **lookup_table) +{ + const ATOM_Vega10_Voltage_Lookup_Table *table; + size_t table_size; + int ret; + + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + PP_ASSERT_WITH_CODE((table->ucNumEntries != 0 && + table->ucNumEntries <= max_levels), + "Invalid PowerPlay Table!", return -1); + + table_size = offsetof(ATOM_Vega10_Voltage_Lookup_Table, entries) + + table->ucNumEntries * sizeof(ATOM_Vega10_Voltage_Lookup_Record); + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *lookup_table = table; + + return 0; +} + +static int check_powerplay_tables(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table) +{ + const ATOM_Vega10_State_Array *state_arrays; + + return get_vega10_state_array(hwmgr, powerplay_table, &state_arrays); +} + static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps) { set_hw_cap( @@ -124,14 +476,16 @@ static int init_thermal_controller( const ATOM_Vega10_Fan_Table *fan_table_v1; const ATOM_Vega10_Fan_Table_V2 *fan_table_v2; const ATOM_Vega10_Fan_Table_V3 *fan_table_v3; - - thermal_controller = (ATOM_Vega10_Thermal_Controller *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usThermalControllerOffset)); + int ret; PP_ASSERT_WITH_CODE((powerplay_table->usThermalControllerOffset != 0), "Thermal controller table not set!", return -EINVAL); + ret = get_vega10_thermal_controller_table(hwmgr, powerplay_table, + &thermal_controller); + if (ret) + return ret; + hwmgr->thermal_controller.ucType = thermal_controller->ucType; hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine; hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress; @@ -160,9 +514,9 @@ static int init_thermal_controller( if (!powerplay_table->usFanTableOffset) return 0; - header = (const Vega10_PPTable_Generic_SubTable_Header *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usFanTableOffset)); + ret = get_vega10_fan_table(hwmgr, powerplay_table, &header); + if (ret) + return ret; if (header->ucRevId == 10) { fan_table_v1 = (ATOM_Vega10_Fan_Table *)header; @@ -307,12 +661,15 @@ static int init_over_drive_limits( struct pp_hwmgr *hwmgr, const ATOM_Vega10_POWERPLAYTABLE *powerplay_table) { - const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table = - (const ATOM_Vega10_GFXCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset)); + const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table; bool is_acg_enabled = false; ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_v2; + int ret; + + ret = get_vega10_gfxclk_dependency_table(hwmgr, powerplay_table, + &gfxclk_dep_table); + if (ret) + return ret; if (gfxclk_dep_table->ucRevId == 1) { patom_record_v2 = @@ -344,20 +701,28 @@ static int get_mm_clock_voltage_table( const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table) { uint32_t i; + uint32_t num_entries; const ATOM_Vega10_MM_Dependency_Record *mm_dependency_record; phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table; PP_ASSERT_WITH_CODE((mm_dependency_table->ucNumEntries != 0), "Invalid PowerPlay Table!", return -1); - mm_table = kzalloc_flex(*mm_table, entries, - mm_dependency_table->ucNumEntries); + num_entries = min_t(uint32_t, mm_dependency_table->ucNumEntries, + pp_entries_max(hwmgr, mm_dependency_table, + sizeof(*mm_dependency_table), + sizeof(ATOM_Vega10_MM_Dependency_Record))); + if (num_entries < mm_dependency_table->ucNumEntries) + pr_warn("amdgpu: Vega10 MM dependency table: clamping ucNumEntries %u -> %u\n", + mm_dependency_table->ucNumEntries, num_entries); + + mm_table = kzalloc_flex(*mm_table, entries, num_entries); if (!mm_table) return -ENOMEM; - mm_table->count = mm_dependency_table->ucNumEntries; + mm_table->count = num_entries; - for (i = 0; i < mm_dependency_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { mm_dependency_record = &mm_dependency_table->entries[i]; mm_table->entries[i].vddcInd = mm_dependency_record->ucVddcInd; mm_table->entries[i].samclock = @@ -568,19 +933,27 @@ static int get_socclk_voltage_dependency_table( const ATOM_Vega10_SOCCLK_Dependency_Table *clk_dep_table) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_clock_voltage_dependency_table *clk_table; PP_ASSERT_WITH_CODE(clk_dep_table->ucNumEntries, "Invalid PowerPlay Table!", return -1); - clk_table = kzalloc_flex(*clk_table, entries, - clk_dep_table->ucNumEntries); + num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_CLK_Dependency_Record))); + if (num_entries < clk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 SOCCLK dependency table: clamping ucNumEntries %u -> %u\n", + clk_dep_table->ucNumEntries, num_entries); + + clk_table = kzalloc_flex(*clk_table, entries, num_entries); if (!clk_table) return -ENOMEM; - clk_table->count = (uint32_t)clk_dep_table->ucNumEntries; + clk_table->count = num_entries; - for (i = 0; i < clk_dep_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { clk_table->entries[i].vddInd = clk_dep_table->entries[i].ucVddInd; clk_table->entries[i].clk = @@ -598,19 +971,27 @@ static int get_mclk_voltage_dependency_table( const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_clock_voltage_dependency_table *mclk_table; PP_ASSERT_WITH_CODE(mclk_dep_table->ucNumEntries, "Invalid PowerPlay Table!", return -1); - mclk_table = kzalloc_flex(*mclk_table, entries, - mclk_dep_table->ucNumEntries); + num_entries = min_t(uint32_t, mclk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, mclk_dep_table, + sizeof(*mclk_dep_table), + sizeof(ATOM_Vega10_MCLK_Dependency_Record))); + if (num_entries < mclk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 MCLK dependency table: clamping ucNumEntries %u -> %u\n", + mclk_dep_table->ucNumEntries, num_entries); + + mclk_table = kzalloc_flex(*mclk_table, entries, num_entries); if (!mclk_table) return -ENOMEM; - mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries; + mclk_table->count = num_entries; - for (i = 0; i < mclk_dep_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { mclk_table->entries[i].vddInd = mclk_dep_table->entries[i].ucVddInd; mclk_table->entries[i].vddciInd = @@ -633,6 +1014,7 @@ static int get_gfxclk_voltage_dependency_table( const ATOM_Vega10_GFXCLK_Dependency_Table *clk_dep_table) { uint32_t i; + uint32_t num_entries; struct phm_ppt_v1_clock_voltage_dependency_table *clk_table; ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_v2; @@ -640,15 +1022,34 @@ static int get_gfxclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0), "Invalid PowerPlay Table!", return -1); - clk_table = kzalloc_flex(*clk_table, entries, - clk_dep_table->ucNumEntries); + if (clk_dep_table->ucRevId == 0) { + num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_GFXCLK_Dependency_Record))); + } else if (clk_dep_table->ucRevId == 1) { + num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_GFXCLK_Dependency_Record_V2))); + } else { + PP_ASSERT_WITH_CODE(false, + "Unsupported GFXClockDependencyTable Revision!", + return -EINVAL); + } + + if (num_entries < clk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 GFXCLK dependency table: clamping ucNumEntries %u -> %u\n", + clk_dep_table->ucNumEntries, num_entries); + + clk_table = kzalloc_flex(*clk_table, entries, num_entries); if (!clk_table) return -ENOMEM; - clk_table->count = clk_dep_table->ucNumEntries; + clk_table->count = num_entries; if (clk_dep_table->ucRevId == 0) { - for (i = 0; i < clk_table->count; i++) { + for (i = 0; i < num_entries; i++) { clk_table->entries[i].vddInd = clk_dep_table->entries[i].ucVddInd; clk_table->entries[i].clk = @@ -661,9 +1062,9 @@ static int get_gfxclk_voltage_dependency_table( clk_table->entries[i].sclk_offset = le16_to_cpu(clk_dep_table->entries[i].usAVFSOffset); } - } else if (clk_dep_table->ucRevId == 1) { + } else { patom_record_v2 = (ATOM_Vega10_GFXCLK_Dependency_Record_V2 *)clk_dep_table->entries; - for (i = 0; i < clk_table->count; i++) { + for (i = 0; i < num_entries; i++) { clk_table->entries[i].vddInd = patom_record_v2->ucVddInd; clk_table->entries[i].clk = @@ -677,11 +1078,6 @@ static int get_gfxclk_voltage_dependency_table( le16_to_cpu(patom_record_v2->usAVFSOffset); patom_record_v2++; } - } else { - kfree(clk_table); - PP_ASSERT_WITH_CODE(false, - "Unsupported GFXClockDependencyTable Revision!", - return -EINVAL); } *pp_vega10_clk_dep_table = clk_table; @@ -696,20 +1092,28 @@ static int get_pix_clk_voltage_dependency_table( const ATOM_Vega10_PIXCLK_Dependency_Table *clk_dep_table) { uint32_t i; + uint32_t num_entries; struct phm_ppt_v1_clock_voltage_dependency_table *clk_table; PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0), "Invalid PowerPlay Table!", return -1); - clk_table = kzalloc_flex(*clk_table, entries, - clk_dep_table->ucNumEntries); + num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_CLK_Dependency_Record))); + if (num_entries < clk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 PIXCLK dependency table: clamping ucNumEntries %u -> %u\n", + clk_dep_table->ucNumEntries, num_entries); + + clk_table = kzalloc_flex(*clk_table, entries, num_entries); if (!clk_table) return -ENOMEM; - clk_table->count = clk_dep_table->ucNumEntries; + clk_table->count = num_entries; - for (i = 0; i < clk_table->count; i++) { + for (i = 0; i < num_entries; i++) { clk_table->entries[i].vddInd = clk_dep_table->entries[i].ucVddInd; clk_table->entries[i].clk = @@ -728,6 +1132,7 @@ static int get_dcefclk_voltage_dependency_table( const ATOM_Vega10_DCEFCLK_Dependency_Table *clk_dep_table) { uint32_t i; + uint32_t safe_entries; uint8_t num_entries; struct phm_ppt_v1_clock_voltage_dependency_table *clk_table; @@ -738,6 +1143,14 @@ static int get_dcefclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0), "Invalid PowerPlay Table!", return -1); + safe_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_CLK_Dependency_Record))); + if (safe_entries < clk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 DCEFCLK dependency table: clamping ucNumEntries %u -> %u\n", + clk_dep_table->ucNumEntries, safe_entries); + /* * workaround needed to add another DPM level for pioneer cards * as VBIOS is locked down. @@ -747,12 +1160,12 @@ static int get_dcefclk_voltage_dependency_table( dev_id = adev->pdev->device; rev_id = adev->pdev->revision; - if (dev_id == 0x6863 && rev_id == 0 && - clk_dep_table->entries[clk_dep_table->ucNumEntries - 1].ulClk < 90000) - num_entries = clk_dep_table->ucNumEntries + 1 > NUM_DSPCLK_LEVELS ? - NUM_DSPCLK_LEVELS : clk_dep_table->ucNumEntries + 1; + if (dev_id == 0x6863 && rev_id == 0 && safe_entries > 0 && + clk_dep_table->entries[safe_entries - 1].ulClk < 90000) + num_entries = safe_entries + 1 > NUM_DSPCLK_LEVELS ? + NUM_DSPCLK_LEVELS : safe_entries + 1; else - num_entries = clk_dep_table->ucNumEntries; + num_entries = safe_entries; clk_table = kzalloc_flex(*clk_table, entries, num_entries); @@ -761,7 +1174,7 @@ static int get_dcefclk_voltage_dependency_table( clk_table->count = (uint32_t)num_entries; - for (i = 0; i < clk_dep_table->ucNumEntries; i++) { + for (i = 0; i < safe_entries; i++) { clk_table->entries[i].vddInd = clk_dep_table->entries[i].ucVddInd; clk_table->entries[i].clk = @@ -873,51 +1286,13 @@ static int init_powerplay_extended_tables( int result = 0; struct phm_ppt_v2_information *pp_table_info = (struct phm_ppt_v2_information *)(hwmgr->pptable); - - const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table = - (const ATOM_Vega10_MM_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usMMDependencyTableOffset)); - const Vega10_PPTable_Generic_SubTable_Header *power_tune_table = - (const Vega10_PPTable_Generic_SubTable_Header *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPowerTuneTableOffset)); - const ATOM_Vega10_SOCCLK_Dependency_Table *socclk_dep_table = - (const ATOM_Vega10_SOCCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usSocclkDependencyTableOffset)); - const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table = - (const ATOM_Vega10_GFXCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset)); - const ATOM_Vega10_DCEFCLK_Dependency_Table *dcefclk_dep_table = - (const ATOM_Vega10_DCEFCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usDcefclkDependencyTableOffset)); - const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table = - (const ATOM_Vega10_MCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usMclkDependencyTableOffset)); - const ATOM_Vega10_Hard_Limit_Table *hard_limits = - (const ATOM_Vega10_Hard_Limit_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usHardLimitTableOffset)); - const Vega10_PPTable_Generic_SubTable_Header *pcie_table = - (const Vega10_PPTable_Generic_SubTable_Header *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPCIETableOffset)); - const ATOM_Vega10_PIXCLK_Dependency_Table *pixclk_dep_table = - (const ATOM_Vega10_PIXCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPixclkDependencyTableOffset)); - const ATOM_Vega10_PHYCLK_Dependency_Table *phyclk_dep_table = - (const ATOM_Vega10_PHYCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPhyClkDependencyTableOffset)); - const ATOM_Vega10_DISPCLK_Dependency_Table *dispclk_dep_table = - (const ATOM_Vega10_DISPCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usDispClkDependencyTableOffset)); + const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table; + const Vega10_PPTable_Generic_SubTable_Header *power_tune_table; + const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table; + const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table; + const ATOM_Vega10_Hard_Limit_Table *hard_limits; + const Vega10_PPTable_Generic_SubTable_Header *pcie_table; + const ATOM_Vega10_SOCCLK_Dependency_Table *clk_dep_table; pp_table_info->vdd_dep_on_socclk = NULL; pp_table_info->vdd_dep_on_sclk = NULL; @@ -929,63 +1304,114 @@ static int init_powerplay_extended_tables( pp_table_info->vdd_dep_on_phyclk = NULL; pp_table_info->vdd_dep_on_dispclk = NULL; - if (powerplay_table->usMMDependencyTableOffset) - result = get_mm_clock_voltage_table(hwmgr, + if (powerplay_table->usMMDependencyTableOffset) { + result = get_vega10_mm_dependency_table(hwmgr, powerplay_table, + &mm_dependency_table); + if (!result) + result = get_mm_clock_voltage_table(hwmgr, &pp_table_info->mm_dep_table, mm_dependency_table); + } - if (!result && powerplay_table->usPowerTuneTableOffset) - result = get_tdp_table(hwmgr, + if (!result && powerplay_table->usPowerTuneTableOffset) { + result = get_vega10_power_tune_table(hwmgr, powerplay_table, + &power_tune_table); + if (!result) + result = get_tdp_table(hwmgr, &pp_table_info->tdp_table, power_tune_table); + } - if (!result && powerplay_table->usSocclkDependencyTableOffset) - result = get_socclk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usSocclkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usSocclkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_socclk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_socclk, - socclk_dep_table); + (const ATOM_Vega10_SOCCLK_Dependency_Table *) + clk_dep_table); + } - if (!result && powerplay_table->usGfxclkDependencyTableOffset) - result = get_gfxclk_voltage_dependency_table(hwmgr, - &pp_table_info->vdd_dep_on_sclk, - gfxclk_dep_table); + if (!result && powerplay_table->usGfxclkDependencyTableOffset) { + result = get_vega10_gfxclk_dependency_table(hwmgr, + powerplay_table, &gfxclk_dep_table); + if (!result) + result = get_gfxclk_voltage_dependency_table(hwmgr, + &pp_table_info->vdd_dep_on_sclk, + gfxclk_dep_table); + } - if (!result && powerplay_table->usPixclkDependencyTableOffset) - result = get_pix_clk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usPixclkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usPixclkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_pix_clk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_pixclk, (const ATOM_Vega10_PIXCLK_Dependency_Table *) - pixclk_dep_table); + clk_dep_table); + } - if (!result && powerplay_table->usPhyClkDependencyTableOffset) - result = get_pix_clk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usPhyClkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usPhyClkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_pix_clk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_phyclk, (const ATOM_Vega10_PIXCLK_Dependency_Table *) - phyclk_dep_table); + clk_dep_table); + } - if (!result && powerplay_table->usDispClkDependencyTableOffset) - result = get_pix_clk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usDispClkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usDispClkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_pix_clk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_dispclk, (const ATOM_Vega10_PIXCLK_Dependency_Table *) - dispclk_dep_table); + clk_dep_table); + } - if (!result && powerplay_table->usDcefclkDependencyTableOffset) - result = get_dcefclk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usDcefclkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usDcefclkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_dcefclk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_dcefclk, - dcefclk_dep_table); + (const ATOM_Vega10_DCEFCLK_Dependency_Table *) + clk_dep_table); + } - if (!result && powerplay_table->usMclkDependencyTableOffset) - result = get_mclk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usMclkDependencyTableOffset) { + result = get_vega10_mclk_dependency_table(hwmgr, powerplay_table, + &mclk_dep_table); + if (!result) + result = get_mclk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_mclk, mclk_dep_table); + } - if (!result && powerplay_table->usPCIETableOffset) - result = get_pcie_table(hwmgr, + if (!result && powerplay_table->usPCIETableOffset) { + result = get_vega10_pcie_table(hwmgr, powerplay_table, + &pcie_table); + if (!result) + result = get_pcie_table(hwmgr, &pp_table_info->pcie_table, pcie_table); + } - if (!result && powerplay_table->usHardLimitTableOffset) - result = get_hard_limits(hwmgr, + if (!result && powerplay_table->usHardLimitTableOffset) { + result = get_vega10_hard_limit_table(hwmgr, powerplay_table, + &hard_limits); + if (!result) + result = get_hard_limits(hwmgr, &pp_table_info->max_clock_voltage_on_dc, hard_limits); + } hwmgr->dyn_state.max_clock_voltage_on_dc.sclk = pp_table_info->max_clock_voltage_on_dc.sclk; @@ -1034,18 +1460,28 @@ static int get_vddc_lookup_table( uint32_t max_levels) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_voltage_lookup_table *table; PP_ASSERT_WITH_CODE((vddc_lookup_pp_tables->ucNumEntries != 0), "Invalid SOC_VDDD Lookup Table!", return 1); - table = kzalloc_flex(*table, entries, max_levels); + num_entries = min_t(uint32_t, vddc_lookup_pp_tables->ucNumEntries, + min_t(uint32_t, max_levels, + pp_entries_max(hwmgr, vddc_lookup_pp_tables, + sizeof(*vddc_lookup_pp_tables), + sizeof(ATOM_Vega10_Voltage_Lookup_Record)))); + if (num_entries < vddc_lookup_pp_tables->ucNumEntries) + pr_warn("amdgpu: Vega10 VddcLookup table: clamping ucNumEntries %u -> %u\n", + vddc_lookup_pp_tables->ucNumEntries, num_entries); + + table = kzalloc_flex(*table, entries, num_entries); if (!table) return -ENOMEM; - table->count = vddc_lookup_pp_tables->ucNumEntries; + table->count = num_entries; - for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) + for (i = 0; i < num_entries; i++) table->entries[i].us_vdd = le16_to_cpu(vddc_lookup_pp_tables->entries[i].usVdd); @@ -1113,30 +1549,39 @@ static int init_dpm_2_parameters( } if (powerplay_table->usVddcLookupTableOffset) { - const ATOM_Vega10_Voltage_Lookup_Table *vddc_table = - (ATOM_Vega10_Voltage_Lookup_Table *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddcLookupTableOffset)); - result = get_vddc_lookup_table(hwmgr, - &pp_table_info->vddc_lookup_table, vddc_table, 8); + const ATOM_Vega10_Voltage_Lookup_Table *vddc_table; + + result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddcLookupTableOffset), + 8, &vddc_table); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_info->vddc_lookup_table, + vddc_table, 8); } - if (powerplay_table->usVddmemLookupTableOffset) { - const ATOM_Vega10_Voltage_Lookup_Table *vdd_mem_table = - (ATOM_Vega10_Voltage_Lookup_Table *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddmemLookupTableOffset)); - result = get_vddc_lookup_table(hwmgr, - &pp_table_info->vddmem_lookup_table, vdd_mem_table, 4); + if (!result && powerplay_table->usVddmemLookupTableOffset) { + const ATOM_Vega10_Voltage_Lookup_Table *vdd_mem_table; + + result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddmemLookupTableOffset), + 4, &vdd_mem_table); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_info->vddmem_lookup_table, + vdd_mem_table, 4); } - if (powerplay_table->usVddciLookupTableOffset) { - const ATOM_Vega10_Voltage_Lookup_Table *vddci_table = - (ATOM_Vega10_Voltage_Lookup_Table *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddciLookupTableOffset)); - result = get_vddc_lookup_table(hwmgr, - &pp_table_info->vddci_lookup_table, vddci_table, 4); + if (!result && powerplay_table->usVddciLookupTableOffset) { + const ATOM_Vega10_Voltage_Lookup_Table *vddci_table; + + result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddciLookupTableOffset), + 4, &vddci_table); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_info->vddci_lookup_table, + vddci_table, 4); } return result; @@ -1247,15 +1692,14 @@ int vega10_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr) { const ATOM_Vega10_State_Array *state_arrays; const ATOM_Vega10_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); + int result; PP_ASSERT_WITH_CODE((pp_table != NULL), "Missing PowerPlay Table!", return -1); - PP_ASSERT_WITH_CODE((pp_table->sHeader.format_revision >= - ATOM_Vega10_TABLE_REVISION_VEGA10), - "Incorrect PowerPlay table revision!", return -1); - state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); + result = get_vega10_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), + "Invalid PowerPlay Table State Array.", return result); return (uint32_t)(state_arrays->ucNumEntries); } @@ -1306,17 +1750,11 @@ int vega10_get_powerplay_table_entry(struct pp_hwmgr *hwmgr, if (pp_table->sHeader.format_revision >= ATOM_Vega10_TABLE_REVISION_VEGA10) { - state_arrays = (ATOM_Vega10_State_Array *) - (((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); - - PP_ASSERT_WITH_CODE(pp_table->usStateArrayOffset > 0, - "Invalid PowerPlay Table State Array Offset.", - return -1); - PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0, + result = get_vega10_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), "Invalid PowerPlay Table State Array.", - return -1); - PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries), + return result); + PP_ASSERT_WITH_CODE((entry_index < state_arrays->ucNumEntries), "Invalid PowerPlay Table State Array Entry.", return -1); @@ -1358,4 +1796,3 @@ int vega10_baco_set_cap(struct pp_hwmgr *hwmgr) PHM_PlatformCaps_BACO); return result; } - diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c index 69a9074058ce..b466b08e4a67 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c @@ -1419,7 +1419,7 @@ static int vega12_get_gpu_power(struct pp_hwmgr *hwmgr, uint32_t *query) if (ret) return ret; - *query = metrics_table.CurrSocketPower << 8; + *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT; return ret; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c index 55e13f376039..dcb9c749eba3 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c @@ -64,6 +64,13 @@ static int check_powerplay_tables( struct pp_hwmgr *hwmgr, const ATOM_Vega12_POWERPLAYTABLE *powerplay_table) { + size_t smc_pptable_size = + offsetofend(ATOM_Vega12_POWERPLAYTABLE, smcPPTable); + size_t table_size = hwmgr->soft_pp_table_size; + + PP_ASSERT_WITH_CODE((table_size >= smc_pptable_size), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_VEGA12_TABLE_REVISION_VEGA12), "Unsupported PPTable format!", return -1); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c index 7b8f4c1b80eb..e9c194056e32 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c @@ -2154,12 +2154,13 @@ static int vega20_get_gpu_power(struct pp_hwmgr *hwmgr, int idx, switch (idx) { case AMDGPU_PP_SENSOR_GPU_AVG_POWER: if (hwmgr->smu_version == 0x282e00) - *query = metrics_table.AverageSocketPower << 8; + *query = metrics_table.AverageSocketPower * + MILLIWATT_PER_WATT; else ret = -EOPNOTSUPP; break; case AMDGPU_PP_SENSOR_GPU_INPUT_POWER: - *query = metrics_table.CurrSocketPower << 8; + *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT; break; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c index 36cb7aa80d07..a0c884c2341d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c @@ -66,6 +66,13 @@ static int check_powerplay_tables( struct pp_hwmgr *hwmgr, const ATOM_Vega20_POWERPLAYTABLE *powerplay_table) { + size_t smc_pptable_size = + offsetofend(ATOM_Vega20_POWERPLAYTABLE, smcPPTable); + size_t table_size = hwmgr->soft_pp_table_size; + + PP_ASSERT_WITH_CODE((table_size >= smc_pptable_size), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_VEGA20_TABLE_REVISION_VEGA20), "Unsupported PPTable format!", return -1); diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index ca71efaa1656..448246cc52cc 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -35,6 +35,11 @@ struct pp_hwmgr; struct phm_fan_speed_info; struct pp_atomctrl_voltage_table; +/* Decode legacy unsigned Q24.8 watts to internal milliwatts. */ +#define PP_PWR_Q24_8_TO_MW(power) \ + DIV_ROUND_CLOSEST_ULL((u64)(power) * MILLIWATT_PER_WATT, \ + BIT(8)) + #define VOLTAGE_SCALE 4 #define VOLTAGE_VID_OFFSET_SCALE1 625 #define VOLTAGE_VID_OFFSET_SCALE2 100 @@ -829,4 +834,24 @@ int smu8_init_function_pointers(struct pp_hwmgr *hwmgr); int vega12_hwmgr_init(struct pp_hwmgr *hwmgr); int vega20_hwmgr_init(struct pp_hwmgr *hwmgr); +static inline uint32_t pp_entries_max(const struct pp_hwmgr *hwmgr, + const void *sub_table, + size_t hdr_size, size_t rec_size) +{ + const char *pp_start = hwmgr->soft_pp_table; + const char *pp_end = pp_start + hwmgr->soft_pp_table_size; + const char *entries = (const char *)sub_table + hdr_size; + + if (!hwmgr->hardcode_pp_table) { + struct amdgpu_device *adev = hwmgr->adev; + const char *bios_end = (const char *)adev->bios + adev->bios_size; + + if (pp_end > bios_end) + return 0; + } + if (!rec_size || entries >= pp_end) + return 0; + return (uint32_t)((pp_end - entries) / rec_size); +} + #endif /* _HWMGR_H_ */ diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c index 3e0068a6aeb2..45504ac3a31d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu7_smumgr.c @@ -134,14 +134,6 @@ int smu7_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg) PHM_WAIT_FIELD_UNEQUAL(hwmgr, SMC_RESP_0, SMC_RESP, 0); - ret = PHM_READ_FIELD(hwmgr->device, SMC_RESP_0, SMC_RESP); - - if (ret == 0xFE) - dev_dbg(adev->dev, "last message was not supported\n"); - else if (ret != 1) - dev_info(adev->dev, - "\nlast message was failed ret is %d\n", ret); - cgs_write_register(hwmgr->device, mmSMC_RESP_0, 0); cgs_write_register(hwmgr->device, mmSMC_MESSAGE_0, msg); @@ -149,13 +141,16 @@ int smu7_send_msg_to_smc(struct pp_hwmgr *hwmgr, uint16_t msg) ret = PHM_READ_FIELD(hwmgr->device, SMC_RESP_0, SMC_RESP); - if (ret == 0xFE) - dev_dbg(adev->dev, "message %x was not supported\n", msg); - else if (ret != 1) - dev_dbg(adev->dev, - "failed to send message %x ret is %d \n", msg, ret); - - return 0; + switch (ret) { + case 1: + return 0; + case 0xFE: + dev_dbg(adev->dev, "SMU message %#x was not supported\n", msg); + return -EOPNOTSUPP; + default: + dev_info(adev->dev, "SMU message %#x failed: response is %d\n", msg, ret); + return ret != 0xFFFF ? -EIO : -ENXIO; + } } int smu7_send_msg_to_smc_with_parameter(struct pp_hwmgr *hwmgr, uint16_t msg, uint32_t parameter) diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 208a2fba6d40..cca32f74abf0 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -28,6 +28,7 @@ #include <linux/reboot.h> #include "amdgpu.h" +#include "amdgpu_reset.h" #include "amdgpu_smu.h" #include "smu_internal.h" #include "atom.h" @@ -70,7 +71,7 @@ static int smu_handle_task(struct smu_context *smu, static int smu_reset(struct smu_context *smu); static int smu_set_fan_speed_pwm(void *handle, u32 speed); static int smu_set_fan_control_mode(void *handle, u32 value); -static int smu_set_power_limit(void *handle, uint32_t limit_type, uint32_t limit); +static int smu_set_ppt_limit(void *handle, uint32_t limit_type, uint32_t limit); static int smu_set_fan_speed_rpm(void *handle, uint32_t speed); static int smu_set_gfx_cgpg(struct smu_context *smu, bool enabled); static int smu_set_mp1_state(void *handle, enum pp_mp1_state mp1_state); @@ -488,13 +489,52 @@ static void smu_set_user_clk_dependencies(struct smu_context *smu, enum smu_clk_ return; } +static void smu_restore_ppt_limits(struct smu_context *smu, + bool restore_defaults) +{ + enum smu_power_src_type power_source; + struct smu_ppt_limit_range *range; + uint32_t restore_mask; + uint32_t limit; + int i, ret; + + power_source = smu->adev->pm.ac_power ? + SMU_POWER_SOURCE_AC : SMU_POWER_SOURCE_DC; + restore_mask = smu->user_dpm_profile.ppt_limit_user_mask[power_source] & + smu->ppt_limits.supported_mask; + if (!restore_mask && !restore_defaults) + return; + + smu->user_dpm_profile.flags |= SMU_DPM_USER_PROFILE_RESTORE; + + for (i = SMU_PPT_LIMIT_PPT0; i < SMU_LIMIT_TYPE_COUNT; i++) { + if (!(smu->ppt_limits.supported_mask & BIT(i))) + continue; + + if (restore_mask & BIT(i)) { + limit = smu->user_dpm_profile.ppt_limits[power_source][i]; + } else if (restore_defaults) { + range = &smu->ppt_limits.range[power_source][i]; + limit = range->default_value; + } else { + continue; + } + + ret = smu_set_ppt_limit(smu, i, limit); + if (ret) + dev_err(smu->adev->dev, + "Failed to restore PPT%d limit: %d\n", i, ret); + } + + smu->user_dpm_profile.flags &= ~SMU_DPM_USER_PROFILE_RESTORE; +} + /** * smu_restore_dpm_user_profile - reinstate user dpm profile * * @smu: smu_context pointer * - * Restore the saved user power configurations include power limit, - * clock frequencies, fan control mode and fan speed. + * Restore saved user clock frequencies and fan settings. */ static void smu_restore_dpm_user_profile(struct smu_context *smu) { @@ -510,16 +550,6 @@ static void smu_restore_dpm_user_profile(struct smu_context *smu) /* Enable restore flag */ smu->user_dpm_profile.flags |= SMU_DPM_USER_PROFILE_RESTORE; - /* set the user dpm power limits */ - for (int i = SMU_DEFAULT_PPT_LIMIT; i < SMU_LIMIT_TYPE_COUNT; i++) { - if (!smu->user_dpm_profile.power_limits[i]) - continue; - ret = smu_set_power_limit(smu, i, - smu->user_dpm_profile.power_limits[i]); - if (ret) - dev_err(smu->adev->dev, "Failed to set %d power limit value\n", i); - } - /* set the user dpm clock configurations */ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_MANUAL) { enum smu_clk_type clk_type; @@ -591,17 +621,13 @@ static int smu_get_power_num_states(void *handle, return 0; } -bool is_support_sw_smu(struct amdgpu_device *adev) +void amdgpu_smu_early_init(struct amdgpu_device *adev) { /* vega20 is 11.0.2, but it's supported via the powerplay code */ - if (adev->asic_type == CHIP_VEGA20) - return false; - - if ((amdgpu_ip_version(adev, MP1_HWIP, 0) >= IP_VERSION(11, 0, 0)) && - amdgpu_device_ip_is_valid(adev, AMD_IP_BLOCK_TYPE_SMC)) - return true; - - return false; + adev->is_sw_smu = adev->asic_type != CHIP_VEGA20 && + (amdgpu_ip_version(adev, MP1_HWIP, 0) >= + IP_VERSION(11, 0, 0) && + amdgpu_device_ip_is_valid(adev, AMD_IP_BLOCK_TYPE_SMC)); } bool is_support_cclk_dpm(struct amdgpu_device *adev) @@ -667,27 +693,31 @@ static int smu_sys_set_pp_table(void *handle, { struct smu_context *smu = handle; struct smu_table_context *smu_table = &smu->smu_table; - ATOM_COMMON_TABLE_HEADER *header = (ATOM_COMMON_TABLE_HEADER *)buf; + ATOM_COMMON_TABLE_HEADER *header; + void *hardcode_pptable; int ret = 0; if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled) return -EOPNOTSUPP; + if (!buf || size < sizeof(*header)) + return -EINVAL; + + header = (ATOM_COMMON_TABLE_HEADER *)buf; if (header->usStructureSize != size) { dev_err(smu->adev->dev, "pp table size not matched !\n"); return -EIO; } - if (!smu_table->hardcode_pptable || smu_table->power_play_table_size < size) { - kfree(smu_table->hardcode_pptable); - smu_table->hardcode_pptable = kzalloc(size, GFP_KERNEL); - if (!smu_table->hardcode_pptable) - return -ENOMEM; - } + hardcode_pptable = kmemdup(buf, size, GFP_KERNEL); + if (!hardcode_pptable) + return -ENOMEM; - memcpy(smu_table->hardcode_pptable, buf, size); + kfree(smu_table->hardcode_pptable); + smu_table->hardcode_pptable = hardcode_pptable; smu_table->power_play_table = smu_table->hardcode_pptable; smu_table->power_play_table_size = size; + memset(&smu->ppt_limits, 0, sizeof(smu->ppt_limits)); /* * Special hw_fini action(for Navi1x, the DPMs disablement will be @@ -802,6 +832,7 @@ static int smu_set_funcs(struct amdgpu_device *adev) break; case IP_VERSION(15, 0, 0): case IP_VERSION(15, 0, 5): + case IP_VERSION(15, 0, 9): smu_v15_0_0_set_ppt_funcs(smu); break; case IP_VERSION(15, 0, 8): @@ -929,7 +960,7 @@ static int smu_late_init(struct amdgpu_ip_block *ip_block) * is unnecessary. */ adev->pm.ac_power = power_supply_is_system_supplied() > 0; - smu_set_ac_dc(smu); + smu_set_ac_dc(smu, false); if ((amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 1)) || (amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 3))) @@ -949,16 +980,6 @@ static int smu_late_init(struct amdgpu_ip_block *ip_block) return ret; } - ret = smu_get_asic_power_limits(smu, - &smu->current_power_limit, - &smu->default_power_limit, - &smu->max_power_limit, - &smu->min_power_limit); - if (ret) { - dev_err(adev->dev, "Failed to get asic power limits!\n"); - return ret; - } - if (!amdgpu_sriov_vf(adev)) smu_get_unique_id(smu); @@ -974,6 +995,8 @@ static int smu_late_init(struct amdgpu_ip_block *ip_block) return ret; } + if (adev->in_suspend || amdgpu_reset_in_recovery(adev)) + smu_restore_ppt_limits(smu, false); smu_restore_dpm_user_profile(smu); return 0; @@ -1366,6 +1389,14 @@ static void smu_feature_cap_init(struct smu_context *smu) bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT); } +static int smu_set_power_dep(struct smu_context *smu, bool enable) +{ + if (!smu->ppt_funcs->set_power_dep) + return 0; + + return smu->ppt_funcs->set_power_dep(smu, enable); +} + static int smu_sw_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -1427,6 +1458,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block) if (!smu->ppt_funcs->get_fan_control_mode) smu->adev->pm.no_fan = true; + smu_set_power_dep(smu, true); + return 0; } @@ -1449,6 +1482,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block) smu_fini_microcode(smu); + smu_set_power_dep(smu, false); + return 0; } @@ -2739,7 +2774,7 @@ static int smu_set_watermarks_for_clock_ranges(void *handle, return smu_set_watermarks_table(smu, clock_ranges); } -int smu_set_ac_dc(struct smu_context *smu) +int smu_set_ac_dc(struct smu_context *smu, bool restore_ppt_policy) { int ret = 0; @@ -2747,17 +2782,22 @@ int smu_set_ac_dc(struct smu_context *smu) return -EOPNOTSUPP; /* controlled by firmware */ - if (smu->dc_controlled_by_gpio) - return 0; + if (!smu->dc_controlled_by_gpio) { + ret = smu_set_power_source(smu, + smu->adev->pm.ac_power ? + SMU_POWER_SOURCE_AC : + SMU_POWER_SOURCE_DC); + if (ret) { + dev_err(smu->adev->dev, "Failed to switch to %s mode!\n", + smu->adev->pm.ac_power ? "AC" : "DC"); + return ret; + } + } - ret = smu_set_power_source(smu, - smu->adev->pm.ac_power ? SMU_POWER_SOURCE_AC : - SMU_POWER_SOURCE_DC); - if (ret) - dev_err(smu->adev->dev, "Failed to switch to %s mode!\n", - smu->adev->pm.ac_power ? "AC" : "DC"); + if (restore_ppt_policy) + smu_restore_ppt_limits(smu, true); - return ret; + return 0; } const struct amd_ip_funcs smu_ip_funcs = { @@ -2772,7 +2812,6 @@ const struct amd_ip_funcs smu_ip_funcs = { .suspend = smu_suspend, .resume = smu_resume, .is_idle = NULL, - .check_soft_reset = NULL, .wait_for_idle = NULL, .soft_reset = NULL, .set_clockgating_state = smu_set_clockgating_state, @@ -2819,17 +2858,6 @@ const struct amdgpu_ip_block_version smu_v15_0_ip_block = { .funcs = &smu_ip_funcs, }; -const struct ras_smu_drv *smu_get_ras_smu_driver(void *handle) -{ - struct smu_context *smu = (struct smu_context *)handle; - const struct ras_smu_drv *tmp = NULL; - int ret; - - ret = smu_get_ras_smu_drv(smu, &tmp); - - return ret ? NULL : tmp; -} - static int smu_load_microcode(void *handle) { struct smu_context *smu = handle; @@ -2900,7 +2928,7 @@ static int smu_set_fan_speed_rpm(void *handle, uint32_t speed) } /** - * smu_get_power_limit - Request one of the SMU Power Limits + * smu_get_ppt_limit - Request one of the SMU PPT limits * * @handle: pointer to smu context * @limit: requested limit is written back to this variable @@ -2909,13 +2937,13 @@ static int smu_set_fan_speed_rpm(void *handle, uint32_t speed) * Return: 0 on success, <0 on error * */ -int smu_get_power_limit(void *handle, +int smu_get_ppt_limit(void *handle, uint32_t *limit, enum pp_power_limit_level pp_limit_level, enum pp_power_type pp_power_type) { struct smu_context *smu = handle; - struct amdgpu_device *adev = smu->adev; + enum smu_power_src_type power_source; enum smu_ppt_limit_level limit_level; uint32_t limit_type; int ret = 0; @@ -2926,9 +2954,12 @@ int smu_get_power_limit(void *handle, if (!limit) return -EINVAL; + power_source = smu->adev->pm.ac_power ? + SMU_POWER_SOURCE_AC : SMU_POWER_SOURCE_DC; + switch (pp_power_type) { case PP_PWR_TYPE_SUSTAINED: - limit_type = SMU_DEFAULT_PPT_LIMIT; + limit_type = SMU_SLOW_PPT_LIMIT; break; case PP_PWR_TYPE_FAST: limit_type = SMU_FAST_PPT_LIMIT; @@ -2954,75 +2985,78 @@ int smu_get_power_limit(void *handle, return -EOPNOTSUPP; } - if (limit_type != SMU_DEFAULT_PPT_LIMIT) { - if (smu->ppt_funcs->get_ppt_limit) - ret = smu->ppt_funcs->get_ppt_limit(smu, limit, limit_type, limit_level); - else - return -EOPNOTSUPP; - } else { - switch (limit_level) { - case SMU_PPT_LIMIT_CURRENT: - switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { - case IP_VERSION(13, 0, 2): - case IP_VERSION(13, 0, 6): - case IP_VERSION(13, 0, 12): - case IP_VERSION(13, 0, 14): - case IP_VERSION(11, 0, 7): - case IP_VERSION(11, 0, 11): - case IP_VERSION(11, 0, 12): - case IP_VERSION(11, 0, 13): - case IP_VERSION(15, 0, 8): - ret = smu_get_asic_power_limits(smu, - &smu->current_power_limit, - NULL, NULL, NULL); - break; - default: - break; - } - *limit = smu->current_power_limit; - break; - case SMU_PPT_LIMIT_DEFAULT: - *limit = smu->default_power_limit; - break; - case SMU_PPT_LIMIT_MAX: - *limit = smu->max_power_limit; - break; - case SMU_PPT_LIMIT_MIN: - *limit = smu->min_power_limit; - break; - default: - return -EINVAL; - } + if (!(smu->ppt_limits.supported_mask & BIT(limit_type))) + return -EOPNOTSUPP; + + switch (limit_level) { + case SMU_PPT_LIMIT_CURRENT: + ret = smu_get_asic_ppt_limit(smu, limit_type, limit); + break; + case SMU_PPT_LIMIT_DEFAULT: + *limit = smu->ppt_limits.range[power_source][limit_type].default_value; + break; + case SMU_PPT_LIMIT_MAX: + *limit = smu->od_enabled ? + smu->ppt_limits.range[power_source][limit_type].od_max : + smu->ppt_limits.range[power_source][limit_type].max; + break; + case SMU_PPT_LIMIT_MIN: + *limit = smu->od_enabled ? + smu->ppt_limits.range[power_source][limit_type].od_min : + smu->ppt_limits.range[power_source][limit_type].min; + break; + default: + return -EINVAL; } return ret; } -static int smu_set_power_limit(void *handle, uint32_t limit_type, uint32_t limit) +static int smu_set_ppt_limit(void *handle, uint32_t limit_type, uint32_t limit) { struct smu_context *smu = handle; + enum smu_power_src_type power_source; + struct smu_ppt_limit_range *range; + uint32_t min_limit, max_limit; int ret = 0; if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled) return -EOPNOTSUPP; + if (limit_type >= SMU_LIMIT_TYPE_COUNT) + return -EINVAL; - if (limit_type == SMU_DEFAULT_PPT_LIMIT) { - if (!limit) - limit = smu->current_power_limit; - if ((limit > smu->max_power_limit) || (limit < smu->min_power_limit)) { - dev_err(smu->adev->dev, - "New power limit (%d) is out of range [%d,%d]\n", - limit, smu->min_power_limit, smu->max_power_limit); - return -EINVAL; - } - } + power_source = smu->adev->pm.ac_power ? + SMU_POWER_SOURCE_AC : SMU_POWER_SOURCE_DC; + range = &smu->ppt_limits.range[power_source][limit_type]; + min_limit = smu->od_enabled ? range->od_min : range->min; + max_limit = smu->od_enabled ? range->od_max : range->max; + + if (!(smu->ppt_limits.supported_mask & BIT(limit_type))) + return -EOPNOTSUPP; - if (smu->ppt_funcs->set_power_limit) { - ret = smu->ppt_funcs->set_power_limit(smu, limit_type, limit); + if (limit_type == SMU_DEFAULT_PPT_LIMIT && !limit) { + ret = smu_get_asic_ppt_limit(smu, limit_type, &limit); if (ret) return ret; - if (!(smu->user_dpm_profile.flags & SMU_DPM_USER_PROFILE_RESTORE)) - smu->user_dpm_profile.power_limits[limit_type] = limit; + } + + if (limit > max_limit || limit < min_limit) { + dev_err(smu->adev->dev, + "New PPT limit (%d) is out of range [%d,%d]\n", + limit, min_limit, max_limit); + return -EINVAL; + } + + if (!smu->ppt_funcs->set_ppt_limit) + return -EOPNOTSUPP; + + ret = smu->ppt_funcs->set_ppt_limit(smu, limit_type, limit); + if (ret) + return ret; + if (!(smu->user_dpm_profile.flags & SMU_DPM_USER_PROFILE_RESTORE)) { + smu->user_dpm_profile.ppt_limits[power_source][limit_type] = limit; + smu->user_dpm_profile.ppt_limit_user_mask[power_source] |= + BIT(limit_type); } return 0; @@ -3970,8 +4004,8 @@ static const struct amd_pm_funcs swsmu_pm_funcs = { .dispatch_tasks = smu_handle_dpm_task, .load_firmware = smu_load_microcode, .set_powergating_by_smu = smu_dpm_set_power_gate, - .set_power_limit = smu_set_power_limit, - .get_power_limit = smu_get_power_limit, + .set_power_limit = smu_set_ppt_limit, + .get_power_limit = smu_get_ppt_limit, .get_power_profile_mode = smu_get_power_profile_mode, .set_power_profile_mode = smu_set_power_profile_mode, .odn_edit_dpm_table = smu_od_edit_dpm_table, diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index d76e0b005308..3ec65630ab99 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -34,7 +34,7 @@ #define SMU_THERMAL_MINIMUM_ALERT_TEMP 0 #define SMU_THERMAL_MAXIMUM_ALERT_TEMP 255 -#define SMU_TEMPERATURE_UNITS_PER_CENTIGRADES 1000 +#define SMU_TEMPERATURE_UNITS_PER_CENTIGRADES MILLIDEGREE_PER_DEGREE #define SMU_FW_NAME_LEN 0x24 #define SMU_DPM_USER_PROFILE_RESTORE (1 << 0) @@ -212,9 +212,12 @@ enum smu_power_src_type { }; enum smu_ppt_limit_type { - SMU_DEFAULT_PPT_LIMIT = 0, - SMU_FAST_PPT_LIMIT, + SMU_PPT_LIMIT_PPT0 = 0, + SMU_PPT_LIMIT_PPT1, SMU_LIMIT_TYPE_COUNT, + SMU_DEFAULT_PPT_LIMIT = SMU_PPT_LIMIT_PPT0, + SMU_SLOW_PPT_LIMIT = SMU_PPT_LIMIT_PPT0, + SMU_FAST_PPT_LIMIT = SMU_PPT_LIMIT_PPT1, }; enum smu_ppt_limit_level { @@ -224,6 +227,20 @@ enum smu_ppt_limit_level { SMU_PPT_LIMIT_MAX, }; +struct smu_ppt_limit_range { + uint32_t default_value; + uint32_t min; + uint32_t max; + uint32_t od_min; + uint32_t od_max; +}; + +struct smu_ppt_limit_context { + struct smu_ppt_limit_range + range[SMU_POWER_SOURCE_COUNT][SMU_LIMIT_TYPE_COUNT]; + uint32_t supported_mask; +}; + enum smu_memory_pool_size { SMU_MEMORY_POOL_SIZE_ZERO = 0, SMU_MEMORY_POOL_SIZE_256_MB = 0x10000000, @@ -234,7 +251,8 @@ enum smu_memory_pool_size { struct smu_user_dpm_profile { uint32_t fan_mode; - uint32_t power_limits[SMU_LIMIT_TYPE_COUNT]; + uint32_t ppt_limits[SMU_POWER_SOURCE_COUNT][SMU_LIMIT_TYPE_COUNT]; + uint32_t ppt_limit_user_mask[SMU_POWER_SOURCE_COUNT]; uint32_t fan_speed_pwm; uint32_t fan_speed_rpm; uint32_t flags; @@ -719,10 +737,7 @@ struct smu_context { uint32_t pstate_mclk; bool od_enabled; - uint32_t current_power_limit; - uint32_t default_power_limit; - uint32_t max_power_limit; - uint32_t min_power_limit; + struct smu_ppt_limit_context ppt_limits; /* soft pptable */ uint32_t ppt_offset_bytes; @@ -749,6 +764,9 @@ struct smu_context { bool pm_enabled; bool is_apu; + /* Power dependency link from an integrated xHCI controller to the GPU */ + struct device_link *usb_power_link; + uint32_t smc_driver_if_version; uint32_t smc_fw_if_version; uint32_t smc_fw_version; @@ -849,8 +867,6 @@ struct pptable_funcs { */ int (*set_default_dpm_table)(struct smu_context *smu); - int (*set_power_state)(struct smu_context *smu); - /** * @populate_umd_state_clk: Populate the UMD power state table with * defaults. @@ -904,16 +920,6 @@ struct pptable_funcs { pp_clock_levels_with_latency *clocks); /** - * @get_clock_by_type_with_voltage: Get the speed and voltage of a clock - * domain. - */ - int (*get_clock_by_type_with_voltage)(struct smu_context *smu, - enum amd_pp_clock_type type, - struct - pp_clock_levels_with_voltage - *clocks); - - /** * @get_power_profile_mode: Print all power profile modes to * buffer. Star current mode. */ @@ -1054,19 +1060,11 @@ struct pptable_funcs { int (*display_disable_memory_clock_switch)(struct smu_context *smu, bool disable_memory_clock_switch); /** - * @get_power_limit: Get the device's power limits. - */ - int (*get_power_limit)(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit); - - /** - * @get_ppt_limit: Get the device's ppt limits. + * @get_ppt_limit: Get the effective runtime PPT0 or PPT1 limit. */ - int (*get_ppt_limit)(struct smu_context *smu, uint32_t *ppt_limit, - enum smu_ppt_limit_type limit_type, enum smu_ppt_limit_level limit_level); + int (*get_ppt_limit)(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *current_ppt_limit); /** * @set_df_cstate: Set data fabric cstate. @@ -1254,11 +1252,11 @@ struct pptable_funcs { int (*notify_display_change)(struct smu_context *smu); /** - * @set_power_limit: Set power limit in watts. + * @set_ppt_limit: Set the PPT0 or PPT1 limit in watts. */ - int (*set_power_limit)(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit); + int (*set_ppt_limit)(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit); /** * @init_max_sustainable_clocks: Populate max sustainable clock speed @@ -1345,7 +1343,7 @@ struct pptable_funcs { u32 (*set_gfx_off_residency)(struct smu_context *smu, bool start); /** - * @get_gfx_off_residency: Average GFXOFF residency % during the logging interval + * @get_gfx_off_residency: Live GFXOFF residency percentage */ u32 (*get_gfx_off_residency)(struct smu_context *smu, uint32_t *residency); @@ -1355,11 +1353,6 @@ struct pptable_funcs { int (*register_irq_handler)(struct smu_context *smu); /** - * @set_azalia_d3_pme: Wake the audio decode engine from d3 sleep. - */ - int (*set_azalia_d3_pme)(struct smu_context *smu); - - /** * @get_max_sustainable_clocks_by_dc: Get a copy of the max sustainable * clock speeds table. * @@ -1376,18 +1369,6 @@ struct pptable_funcs { int (*get_bamaco_support)(struct smu_context *smu); /** - * @baco_get_state: Get the current BACO state. - * - * Return: Current BACO state. - */ - enum smu_baco_state (*baco_get_state)(struct smu_context *smu); - - /** - * @baco_set_state: Enter/exit BACO. - */ - int (*baco_set_state)(struct smu_context *smu, enum smu_baco_state state); - - /** * @baco_enter: Enter BACO. */ int (*baco_enter)(struct smu_context *smu); @@ -1648,12 +1629,13 @@ struct pptable_funcs { int (*ras_send_msg)(struct smu_context *smu, enum smu_message_type msg, uint32_t param, uint32_t *read_arg); - /** - * @get_ras_smu_drv: Get RAS smu driver interface - * Return: ras_smu_drv * + * @set_power_dep: Create or destroy a power dependency link + * from an integrated xHCI controller to the GPU so that the GPU is + * resumed before the USB controller during PM resume. @enable is true + * to create the link and false to tear it down. */ - int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv); + int (*set_power_dep)(struct smu_context *smu, bool enable); }; typedef enum { @@ -1940,10 +1922,10 @@ smu_driver_table_update_cache_time(struct smu_context *smu, } #if !defined(SWSMU_CODE_LAYER_L2) && !defined(SWSMU_CODE_LAYER_L3) && !defined(SWSMU_CODE_LAYER_L4) -int smu_get_power_limit(void *handle, - uint32_t *limit, - enum pp_power_limit_level pp_limit_level, - enum pp_power_type pp_power_type); +int smu_get_ppt_limit(void *handle, + uint32_t *limit, + enum pp_power_limit_level pp_limit_level, + enum pp_power_type pp_power_type); bool smu_mode1_reset_is_support(struct smu_context *smu); bool smu_link_reset_is_support(struct smu_context *smu); @@ -1952,7 +1934,13 @@ int smu_link_reset(struct smu_context *smu); extern const struct amd_ip_funcs smu_ip_funcs; -bool is_support_sw_smu(struct amdgpu_device *adev); +void amdgpu_smu_early_init(struct amdgpu_device *adev); + +static inline bool is_support_sw_smu(struct amdgpu_device *adev) +{ + return adev->is_sw_smu; +} + bool is_support_cclk_dpm(struct amdgpu_device *adev); int smu_write_watermarks_table(struct smu_context *smu); @@ -1964,7 +1952,7 @@ int smu_set_soft_freq_range(struct smu_context *smu, enum pp_clock_type clk_type int smu_set_gfx_power_up_by_imu(struct smu_context *smu); -int smu_set_ac_dc(struct smu_context *smu); +int smu_set_ac_dc(struct smu_context *smu, bool restore_ppt_policy); int smu_set_xgmi_plpd_mode(struct smu_context *smu, enum pp_xgmi_plpd_mode mode); @@ -1995,7 +1983,6 @@ int smu_set_pm_policy(struct smu_context *smu, enum pp_pm_policy p_type, int level); ssize_t smu_get_pm_policy_info(struct smu_context *smu, enum pp_pm_policy p_type, char *sysbuf); -const struct ras_smu_drv *smu_get_ras_smu_driver(void *handle); int amdgpu_smu_ras_send_msg(struct amdgpu_device *adev, enum smu_message_type msg, uint32_t param, uint32_t *readarg); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h index 47ddf533b08f..a5d3e8d88860 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h @@ -144,6 +144,44 @@ typedef struct { uint32_t MaxGfxClk; } DpmClocks_t; +//Freq in MHz +//Voltage in milli volts with 2 fractional bits +typedef struct { + uint32_t DcfClocks[NUM_DCFCLK_DPM_LEVELS]; + uint32_t DispClocks[NUM_DISPCLK_DPM_LEVELS]; + uint32_t DppClocks[NUM_DPPCLK_DPM_LEVELS]; + uint32_t SocClocks[NUM_SOCCLK_DPM_LEVELS]; + + uint32_t VPEClocks[NUM_VPE_DPM_LEVELS]; + uint32_t FclkClocks_Freq[NUM_FCLK_DPM_LEVELS]; + uint32_t FclkClocks_Voltage[NUM_FCLK_DPM_LEVELS]; + uint32_t SocVoltage[NUM_SOC_VOLTAGE_LEVELS]; + MemPstateTable_t MemPstateTable[NUM_MEM_PSTATE_LEVELS]; + + uint8_t NumDcfClkLevelsEnabled; + uint8_t NumDispClkLevelsEnabled; //Applies to both Dispclk and Dppclk + uint8_t NumSocClkLevelsEnabled; + + uint8_t VpeClkLevelsEnabled; + + uint8_t NumMemPstatesEnabled; + uint8_t NumFclkLevelsEnabled; + uint8_t Spare1; + uint8_t Spare2; + + uint8_t Spare3; + uint8_t Spare4; + uint8_t Spare5[2]; + + uint32_t MinGfxClk; + uint32_t MaxGfxClk; + + uint32_t Spare6[8]; + uint32_t Spare7[8]; + uint32_t Spare8[8]; + uint32_t Spare9[8]; +} DpmClocks_t_v15_0_5; + typedef struct { uint16_t CoreFrequency[16]; //Target core frequency [MHz] uint16_t CorePower[16]; //CAC calculated core power [mW] diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h index 7471e2df2828..4206514765cd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h @@ -110,8 +110,10 @@ #define PPSMC_MSG_GetSlowPPTLimit 0x4C #define PPSMC_MSG_GetGfxOffStatus 0x50 #define PPSMC_MSG_GetGfxOffEntryCount 0x51 -#define PPSMC_MSG_LogGfxOffResidency 0x52 -#define PPSMC_Message_Count 0x53 +#define PPSMC_MSG_GfxOffResidencyLogReadSample 0x52 +#define PPSMC_MSG_StopGfxOffResidencyLogging 0x53 +#define PPSMC_MSG_StartGfxOffResidencyLogging 0x56 +#define PPSMC_Message_Count 0x57 //Argument for PPSMC_MSG_GfxDeviceDriverReset enum { diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_0_metrics.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_0_metrics.h new file mode 100644 index 000000000000..698c08875760 --- /dev/null +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_0_metrics.h @@ -0,0 +1,230 @@ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __SMU_V15_0_0_METRICS_H__ +#define __SMU_V15_0_0_METRICS_H__ + +#define METRICS_TABLE_VERSION 0x04 +#define NUM_MAX_CORES 12 +#define NUM_CLK_DPM_LEVELS 8 +#define METRIC_CORE_TYPE_MAX 4 +#define METRIC_CCX_MAX 4 + +/* Variable Type | Description */ +#pragma pack(push, 4) +typedef struct { +uint32_t AccumulationCounter; + +/*SET_VOLTAGES */ +uint64_t VDDCR_SetVoltage; /* Acc Float (V) */ +uint64_t VDDCR_SOC_SetVoltage; /* Acc Float (V) */ +uint64_t VDDCR_NPU_SetVoltage; /* Acc Float (V) */ +uint64_t VDDCR_LP_SetVoltage; /* Acc Float (V) */ +uint64_t VDDCR_GFX_SetVoltage; /* Acc Float (V) */ +uint64_t VDD_MISC_SetVoltage; /* Acc Float (V) */ + +/*TELEMETRY_VOLTAGES */ +uint64_t VDDCR_TelemetryVoltage; /* Acc Float (V) */ +uint64_t VDDCR_SOC_TelemetryVoltage; /* Acc Float (V) */ +uint64_t VDDCR_NPU_TelemetryVoltage; /* Acc Float (V) */ +uint64_t VDDCR_LP_TelemetryVoltage; /* Acc Float (V) */ +uint64_t VDDCR_GFX_TelemetryVoltage; /* Acc Float (V) */ +uint64_t VDD_MISC_TelemetryVoltage; /* Acc Float (V) */ + +/*TELEMETRY_POWERS */ +uint64_t VDDCR_TelemetryPower; /* Acc Float (W) */ +uint64_t VDDCR_SOC_TelemetryPower; /* Acc Float (W) */ +uint64_t VDDCR_NPU_TelemetryPower; /* Acc Float (W) */ +uint64_t VDDCR_LP_TelemetryPower; /* Acc Float (W) */ +uint64_t VDDCR_GFX_TelemetryPower; /* Acc Float (W) */ +uint64_t VDD_MISC_TelemetryPower; /* Acc Float (W) */ + +/*THROTTLERS */ +uint32_t fPPT_FusedLimit; /* Inst Float (W) */ +uint32_t fPPT_MaxIrmLimit; /* Inst Float (W) */ +uint32_t fPPT_MaxPboLimit; /* Inst Float (W) */ +uint32_t fPPT_Limit; /* Inst Float (W) */ +uint64_t fPPT_ValueAcc; /* Acc Int (W) */ +uint32_t fPPT_ResidencyAcc; /* Acc Float */ + +uint32_t sPPT_FusedLimit; /* Inst Float (W) */ +uint32_t sPPT_MaxIrmLimit; /* Inst Float (W) */ +uint32_t sPPT_MaxPboLimit; /* Inst Float (W) */ +uint32_t sPPT_Limit; /* Inst Float (W) */ +uint64_t sPPT_ValueAcc; /* Acc Int (W) */ +uint32_t sPPT_ResidencyAcc; /* Acc Float */ + +uint32_t SPL_FusedLimit; /* Inst Float (W) */ +uint32_t SPL_MaxIrmLimit; /* Inst Float (W) */ +uint32_t SPL_MaxPboLimit; /* Inst Float (W) */ +uint32_t SPL_Limit; /* Inst Float (W) */ +uint64_t SPL_ValueAcc; /* Acc Int (W) */ +uint32_t SPL_ResidencyAcc; /* Acc Float */ + +uint32_t TDC_VDDCR_FusedLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_MaxIrmLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_MaxPboLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_Limit; /* Inst Float (A) */ +uint64_t TDC_VDDCR_ValueAcc; /* Acc Int (A) */ +uint32_t TDC_VDDCR_ResidencyAcc; /* Acc Float */ + +uint32_t TDC_VDDCR_SOC_FusedLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_SOC_MaxIrmLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_SOC_MaxPboLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_SOC_Limit; /* Inst Float (A) */ +uint64_t TDC_VDDCR_SOC_ValueAcc; /* Acc Int (A) */ +uint32_t TDC_VDDCR_SOC_ResidencyAcc; /* Acc Float */ + +uint32_t TDC_VDDCR_NPU_FusedLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_NPU_MaxIrmLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_NPU_MaxPboLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_NPU_Limit; /* Inst Float (A) */ +uint64_t TDC_VDDCR_NPU_ValueAcc; /* Acc Int (A) */ +uint32_t TDC_VDDCR_NPU_ResidencyAcc; /* Acc Float */ + +uint32_t TDC_VDDCR_LP_FusedLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_LP_MaxIrmLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_LP_MaxPboLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_LP_Limit; /* Inst Float (A) */ +uint64_t TDC_VDDCR_LP_ValueAcc; /* Acc Int (A) */ +uint32_t TDC_VDDCR_LP_ResidencyAcc; /* Acc Float */ + +uint32_t TDC_VDDCR_GFX_FusedLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_GFX_MaxIrmLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_GFX_MaxPboLimit; /* Inst Float (A) */ +uint32_t TDC_VDDCR_GFX_Limit; /* Inst Float (A) */ +uint64_t TDC_VDDCR_GFX_ValueAcc; /* Acc Int (A) */ +uint32_t TDC_VDDCR_GFX_ResidencyAcc; /* Acc Float */ + +uint32_t EDC_VDDCR_FusedLimit; /* Inst Float (A) */ +uint32_t EDC_VDDCR_MaxIrmLimit; /* Inst Float (A) */ +uint32_t EDC_VDDCR_MaxPboLimit; /* Inst Float (A) */ +uint32_t EDC_VDDCR_Limit; /* Inst Float (A) */ + +uint32_t THM_FusedLimit; /* Inst Float (C) */ +uint32_t THM_Limit; /* Inst Float (C) */ +uint64_t THM_ValueAcc; /* Acc Float (C) */ +uint32_t THM_ResidencyAcc; /* Acc Float */ +uint32_t PROCHOT_ResidencyAcc; /* Acc Float */ +uint64_t GFX_TempAcc; /* Acc Float (C) */ +uint64_t SOC_TempAcc; /* Acc Float (C) */ +uint32_t P3T_FusedLimit; /* Inst Float (W) */ +uint64_t P3T_ValueAcc; /* Acc Float (W) */ + +/*POWER */ +uint64_t SystemPowerAcc; /* Acc Float (W) */ +uint64_t ApuPowerAcc; /* Acc Float (W) */ +uint64_t dGpuPowerAcc; /* Acc Float (W) */ +uint64_t NpuPowerAcc; /* Acc Float (W) */ + +/*FREQUENCIES */ +uint64_t FclkFreqEffAcc; /* Acc Float (MHz) */ +uint64_t MemclkFreqEffAcc; /* Acc Float (MHz) */ +uint64_t LclkFreqEffAcc; /* Acc Float (MHz) */ +uint64_t GfxclkFreqEffAcc; /* Acc Float (MHz) */ +uint64_t SocclkFreqEffAcc; /* Acc Float (MHz) */ +uint64_t VclkFreqEffAcc; /* Acc Float (MHz) */ +uint64_t VpeclkFreqEffAcc; /* Acc Float (MHz) */ +uint64_t AieclkFreqEffAcc; /* Acc Float (MHz) */ +uint64_t NpuhclkFreqEffAcc; /* Acc Float (MHz) */ +/*BANDWIDTH */ +uint64_t DramReadBandwidth; /* Acc Float (GB/sec) */ +uint64_t DramWriteBandwidth; /* Acc Float (GB/sec) */ + +/*ACTIVITY MONITORS */ +uint64_t GfxBusyAcc; /* Acc Float (%) */ +uint64_t VcnBusyAcc; /* Acc Float (%) */ +uint64_t NpuBusyAcc[3]; /* Acc Float (%) */ + +/*STT */ +uint32_t STT_MinLimit; /* Inst Float (W) */ +uint64_t STT_APU_HotSpotTempAcc; /* Acc Float (C) */ +uint64_t STT_HS2_HotSpotTempAcc; /* Acc Float (C) */ +uint32_t STT_APU_Temp_Limit; /* Inst Float (C) */ +uint64_t STT_APU_SkinTempAcc; /* Acc Float (C) */ + +/*RESIDENCIES */ +uint64_t CpuOffResidency_CCX0; /* Acc Float (%) */ +uint64_t CpuOffResidency_CCX1; /* Acc Float (%) */ +uint64_t CpuOffResidency_CCX2; /* Acc Float (%) */ +uint64_t CpuOffResidency_CCX3; /* Acc Float (%) */ + +/*DFPSTATES */ +uint32_t FclkFreqTable[NUM_CLK_DPM_LEVELS]; /* Inst Int (MHz) */ +uint32_t UclkFreqTable[NUM_CLK_DPM_LEVELS]; /* Inst Int (MHz) */ +uint32_t DdrRateTable[NUM_CLK_DPM_LEVELS]; /* Inst Int (MT/s) */ +uint8_t DfPstate_Source[NUM_CLK_DPM_LEVELS]; /* Inst Int */ + +/*SYSTEM */ +uint8_t GfxDisabled; /* Inst Int */ +uint8_t spare2[3]; +uint32_t GfxClk_Fmax; /* Inst Float (GHz) */ +uint8_t CClk_CoreFuseEnable[METRIC_CORE_TYPE_MAX][NUM_MAX_CORES]; /* Inst Int */ +uint8_t CClk_CoreEnabled[METRIC_CORE_TYPE_MAX][NUM_MAX_CORES]; /* Inst Int */ +uint32_t CClk_Fmax[METRIC_CORE_TYPE_MAX][NUM_MAX_CORES]; /* Inst Float (GHz) */ + +/*OVERCLOCK CAPABLE */ +uint8_t CpuPreciseAndDirectOverClockingCapable; /* Inst Int */ +uint8_t GfxPreciseAndDirectOverClockingCapable; /* Inst Int */ +uint8_t PboBasicOverClockingCapable; /* Inst Int */ +uint8_t PboAdvancedOverClockingCapable; /* Inst Int */ +uint8_t PboNitroOverClockingCapable; /* Inst Int */ +uint8_t MemoryAndFabricOverClockingCapable; /* Inst Int */ +uint8_t MiscOverClockingCapable; /* Inst Int */ +uint8_t ExtremeColdOverclockingCapable; /* Inst Int */ +uint8_t DownConfigControlCapable; /* Inst Int */ +uint8_t spare0[3]; + +/*OVERCLOCK STATUS */ +uint32_t FIT_LimitScalar; /* Inst Float */ +uint8_t LN2Enabled; /* Inst Int */ +uint8_t CpuPreciseAndDirectOcEnabled; /* Inst Int */ +uint8_t GfxPreciseAndDirectOcEnabled; /* Inst Int */ +uint8_t spare1[2]; +int8_t PsmGuardband[5][5][3]; /* Inst Int */ +int32_t CorePowerLimitOffset; /* Inst Float (W) */ +uint32_t MaxFreqOffset[5]; /* Inst Float (GHz) */ + +uint64_t NpuTempAcc; /* Acc Float (C) */ +uint64_t DfPStateResidencyAcc[NUM_CLK_DPM_LEVELS]; /* Acc Int (%) */ +uint32_t CClk_Fboost; /* Inst Float (GHz) */ +uint32_t spare3[5]; +} MetricsTable_IOD_t; + +typedef struct { +uint64_t Core_C0[NUM_MAX_CORES]; /* Acc Float (%) */ +uint64_t Core_CC6[NUM_MAX_CORES]; /* Acc Float (%) */ +uint64_t Core_FREQ[NUM_MAX_CORES]; /* Acc Float (GHz) */ +uint64_t Core_FREQEFF[NUM_MAX_CORES]; /* Acc Float (GHz) */ +uint64_t Core_TEMP[NUM_MAX_CORES]; /* Acc Float (C) */ +uint64_t Core_POWER[NUM_MAX_CORES]; /* Acc Float (W) */ +} MetricsTable_CCX_t; + +typedef struct { +MetricsTable_IOD_t IOD; +MetricsTable_CCX_t CCX[METRIC_CCX_MAX]; +} MetricsTable_t; + +#pragma pack(pop) + +#endif diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_0_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_0_ppsmc.h index bf216f83e689..9f627b5fef5b 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_0_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_0_ppsmc.h @@ -100,7 +100,10 @@ #define PPSMC_MSG_DisableLSdma 0x1F ///< Disable LSDMA #define PPSMC_MSG_SetSoftMaxVpe 0x20 ///< #define PPSMC_MSG_SetSoftMinVpe 0x21 ///< -#define PPSMC_Message_Count 0x22 ///< Total number of PPSMC messages +#define PPSMC_MSG_GetMetricsTableVersion 0x22 +#define PPSMC_MSG_GetMetricsTableLogSample 0x23 +#define PPSMC_MSG_GetMetricsTableLogDramAddr 0x24 +#define PPSMC_Message_Count 0x25 ///< Total number of PPSMC messages /** @}*/ /** diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h index 636ff90923d9..d6675b4ee9be 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h @@ -252,7 +252,9 @@ __SMU_DUMMY_MAP(DriverMode2Reset), \ __SMU_DUMMY_MAP(GetGfxOffStatus), \ __SMU_DUMMY_MAP(GetGfxOffEntryCount), \ - __SMU_DUMMY_MAP(LogGfxOffResidency), \ + __SMU_DUMMY_MAP(StartGfxOffResidencyLogging), \ + __SMU_DUMMY_MAP(GfxOffResidencyLogReadSample), \ + __SMU_DUMMY_MAP(StopGfxOffResidencyLogging), \ __SMU_DUMMY_MAP(SetNumBadMemoryPagesRetired), \ __SMU_DUMMY_MAP(SetBadMemoryPagesRetiredFlagsPerChannel), \ __SMU_DUMMY_MAP(AllowGpo), \ @@ -295,6 +297,9 @@ __SMU_DUMMY_MAP(GetSmartShiftStatus), \ __SMU_DUMMY_MAP(EnableLSdma), \ __SMU_DUMMY_MAP(DisableLSdma), \ + __SMU_DUMMY_MAP(GetMetricsTableVersion), \ + __SMU_DUMMY_MAP(GetMetricsTableLogSample), \ + __SMU_DUMMY_MAP(GetMetricsTableLogDramAddr), \ __SMU_DUMMY_MAP(InitializeGfx), \ __SMU_DUMMY_MAP(SetSoftMaxFclk), \ __SMU_DUMMY_MAP(SetSoftMaxGl2clk), \ diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h index dd94e8a9e218..9f20c631d2b7 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h @@ -108,10 +108,6 @@ struct smu_11_5_power_context { uint32_t power_source; uint8_t in_power_limit_boost_mode; enum smu_11_0_power_state power_state; - - uint32_t current_fast_ppt_limit; - uint32_t default_fast_ppt_limit; - uint32_t max_fast_ppt_limit; }; #if defined(SWSMU_CODE_LAYER_L2) || defined(SWSMU_CODE_LAYER_L3) @@ -151,12 +147,13 @@ int smu_v11_0_set_allowed_mask(struct smu_context *smu); int smu_v11_0_notify_display_change(struct smu_context *smu); -int smu_v11_0_get_current_power_limit(struct smu_context *smu, - uint32_t *power_limit); +int smu_v11_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit); -int smu_v11_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit); +int smu_v11_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit); int smu_v11_0_init_max_sustainable_clocks(struct smu_context *smu); @@ -199,8 +196,6 @@ int smu_v11_0_gfx_off_control(struct smu_context *smu, bool enable); int smu_v11_0_register_irq_handler(struct smu_context *smu); -int smu_v11_0_set_azalia_d3_pme(struct smu_context *smu); - int smu_v11_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu, struct pp_smu_nv_clock_table *max_clocks); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h index 89bbda0670ef..ffff02489c3e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h @@ -55,6 +55,13 @@ #define SMUQ10_TO_UINT(x) ((x) >> 10) #define SMUQ10_FRAC(x) ((x) & 0x3ff) #define SMUQ10_ROUND(x) ((SMUQ10_TO_UINT(x)) + ((SMUQ10_FRAC(x)) >= 0x200)) +/* Convert Q10 watts to milliwatts, preserving the fractional part */ +#define SMUQ10_TO_MILLIWATT(x) (SMUQ10_TO_UINT(x) * MILLIWATT_PER_WATT + \ + ((SMUQ10_FRAC(x) * MILLIWATT_PER_WATT) >> 10)) +/* Convert Q10 degrees Celsius to millidegrees, preserving the fractional part */ +#define SMUQ10_TO_MILLICELSIUS(x) \ + (SMUQ10_TO_UINT(x) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES + \ + ((SMUQ10_FRAC(x) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES) >> 10)) #define SMU_V13_SOFT_FREQ_ROUND(x) ((x) + 1) extern const int pmfw_decoded_link_speed[5]; @@ -145,12 +152,13 @@ int smu_v13_0_set_allowed_mask(struct smu_context *smu); int smu_v13_0_notify_display_change(struct smu_context *smu); -int smu_v13_0_get_current_power_limit(struct smu_context *smu, - uint32_t *power_limit); +int smu_v13_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit); -int smu_v13_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit); +int smu_v13_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit); int smu_v13_0_init_max_sustainable_clocks(struct smu_context *smu); @@ -180,8 +188,6 @@ int smu_v13_0_gfx_off_control(struct smu_context *smu, bool enable); int smu_v13_0_register_irq_handler(struct smu_context *smu); -int smu_v13_0_set_azalia_d3_pme(struct smu_context *smu); - int smu_v13_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu, struct pp_smu_nv_clock_table *max_clocks); @@ -255,10 +261,6 @@ void smu_v13_0_init_msg_ctl(struct smu_context *smu, int smu_v13_0_mode1_reset(struct smu_context *smu); -int smu_v13_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id); int smu_v13_0_update_pcie_parameters(struct smu_context *smu, uint8_t pcie_gen_cap, diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h index 4eb40ff8aff2..dc222f3b3655 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h @@ -132,12 +132,13 @@ int smu_v14_0_set_allowed_mask(struct smu_context *smu); int smu_v14_0_notify_display_change(struct smu_context *smu); -int smu_v14_0_get_current_power_limit(struct smu_context *smu, - uint32_t *power_limit); +int smu_v14_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit); -int smu_v14_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit); +int smu_v14_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit); int smu_v14_0_gfx_off_control(struct smu_context *smu, bool enable); @@ -203,10 +204,6 @@ int smu_v14_0_set_gfx_power_up_by_imu(struct smu_context *smu); int smu_v14_0_set_default_dpm_tables(struct smu_context *smu); -int smu_v14_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id); int smu_v14_0_od_edit_dpm_table(struct smu_context *smu, enum PP_OD_DPM_TABLE_COMMAND type, diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h index e6fd8be2cc4a..ec96dc775485 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h @@ -26,7 +26,7 @@ #include "amdgpu_smu.h" #define SMU15_DRIVER_IF_VERSION_INV 0xFFFFFFFF -#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x7 +#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x9 #define FEATURE_MASK(feature) (1ULL << feature) @@ -142,12 +142,13 @@ int smu_v15_0_set_allowed_mask(struct smu_context *smu); int smu_v15_0_notify_display_change(struct smu_context *smu); -int smu_v15_0_get_current_power_limit(struct smu_context *smu, - uint32_t *power_limit); +int smu_v15_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit); -int smu_v15_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit); +int smu_v15_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit); int smu_v15_0_gfx_off_control(struct smu_context *smu, bool enable); @@ -211,10 +212,6 @@ int smu_v15_0_deep_sleep_control(struct smu_context *smu, int smu_v15_0_set_gfx_power_up_by_imu(struct smu_context *smu); -int smu_v15_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id); int smu_v15_0_od_edit_dpm_table(struct smu_context *smu, enum PP_OD_DPM_TABLE_COMMAND type, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c index 54d3dba7d354..edb0393505ba 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c @@ -67,6 +67,8 @@ #define SMU11_DRIVER_IF_VERSION_ARCT 0x17 +static int arcturus_init_ppt_limits(struct smu_context *smu); + static const struct smu_feature_bits arcturus_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_DPM_PREFETCHER_BIT), SMU_FEATURE_BIT_INIT(FEATURE_DPM_GFXCLK_BIT), @@ -544,7 +546,7 @@ static int arcturus_setup_pptable(struct smu_context *smu) if (ret) return ret; - return ret; + return arcturus_init_ppt_limits(smu); } static int arcturus_run_btc(struct smu_context *smu) @@ -658,7 +660,7 @@ static int arcturus_get_smu_metrics_data(struct smu_context *smu, *value = metrics->VcnActivityPercentage; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -1259,37 +1261,42 @@ static int arcturus_get_fan_parameters(struct smu_context *smu) return 0; } -static int arcturus_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int arcturus_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) +{ + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; + + return smu_v11_0_get_ppt_limit(smu, limit_type, ppt_limit); +} + +static int arcturus_init_ppt_limits(struct smu_context *smu) { PPTable_t *pptable = smu->smu_table.driver_pptable; - uint32_t power_limit; + uint32_t default_limit; + int i; - if (smu_v11_0_get_current_power_limit(smu, &power_limit)) { - /* the last hope to figure out the ppt limit */ - if (!pptable) { - dev_err(smu->adev->dev, "Cannot get PPT limit due to pptable missing!"); - return -EINVAL; - } - power_limit = - pptable->SocketPowerLimitAc[PPT_THROTTLER_PPT0]; + if (!pptable) { + dev_err(smu->adev->dev, + "Cannot get PPT limit due to pptable missing!"); + return -EINVAL; } - if (current_power_limit) - *current_power_limit = power_limit; - if (default_power_limit) - *default_power_limit = power_limit; - if (max_power_limit) - *max_power_limit = power_limit; - /* - * No lower bound is imposed on the limit. Any unreasonable limit set - * will result in frequent throttling. - */ - if (min_power_limit) - *min_power_limit = 0; + default_limit = pptable->SocketPowerLimitAc[PPT_THROTTLER_PPT0]; + + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + default_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = + default_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = 0; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + default_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = 0; + } + + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); return 0; } @@ -1466,9 +1473,10 @@ static int arcturus_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != ARCTURUS_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= ARCTURUS_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + ARCTURUS_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + ARCTURUS_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * ARCTURUS_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -1890,7 +1898,7 @@ static const struct pptable_funcs arcturus_ppt_funcs = { .get_power_profile_mode = arcturus_get_power_profile_mode, .set_power_profile_mode = arcturus_set_power_profile_mode, .set_performance_level = arcturus_set_performance_level, - .get_power_limit = arcturus_get_power_limit, + .get_ppt_limit = arcturus_get_ppt_limit, .is_dpm_running = arcturus_is_dpm_running, .dpm_set_vcn_enable = arcturus_dpm_set_vcn_enable, .i2c_init = arcturus_i2c_control_init, @@ -1919,7 +1927,7 @@ static const struct pptable_funcs arcturus_ppt_funcs = { .feature_is_enabled = smu_cmn_feature_is_enabled, .disable_all_features_with_exception = smu_cmn_disable_all_features_with_exception, .notify_display_change = NULL, - .set_power_limit = smu_v11_0_set_power_limit, + .set_ppt_limit = smu_v11_0_set_ppt_limit, .init_max_sustainable_clocks = smu_v11_0_init_max_sustainable_clocks, .enable_thermal_alert = smu_v11_0_enable_thermal_alert, .disable_thermal_alert = smu_v11_0_disable_thermal_alert, @@ -1932,7 +1940,6 @@ static const struct pptable_funcs arcturus_ppt_funcs = { .set_xgmi_pstate = smu_v11_0_set_xgmi_pstate, .gfx_off_control = smu_v11_0_gfx_off_control, .register_irq_handler = smu_v11_0_register_irq_handler, - .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme, .get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc, .get_bamaco_support = smu_v11_0_get_bamaco_support, .baco_enter = smu_v11_0_baco_enter, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c index e6e009df9840..bb9620666dae 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c @@ -158,12 +158,10 @@ cyan_skillfish_get_smu_metrics_data(struct smu_context *smu, *value = metrics->Current.MemclkFrequency; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->Current.CurrentSocketPower << 8) / - 1000; + *value = metrics->Current.CurrentSocketPower; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->Average.CurrentSocketPower << 8) / - 1000; + *value = metrics->Average.CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->Current.GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index cd0457e13f54..f8ca5eb9adc6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -58,6 +58,8 @@ #undef pr_info #undef pr_debug +static int navi10_init_ppt_limits(struct smu_context *smu); + static const struct smu_feature_bits navi10_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_DPM_PREFETCHER_BIT), @@ -488,7 +490,7 @@ static int navi10_setup_pptable(struct smu_context *smu) if (ret) return ret; - return ret; + return navi10_init_ppt_limits(smu); } static int navi10_tables_init(struct smu_context *smu) @@ -602,7 +604,7 @@ static int navi10_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -691,7 +693,7 @@ static int navi10_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -777,7 +779,7 @@ static int navi12_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -866,7 +868,7 @@ static int navi12_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -1843,9 +1845,10 @@ static int navi10_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != NAVI10_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= NAVI10_CUSTOM_PARAMS_CLOCKS_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + NAVI10_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + NAVI10_CUSTOM_PARAMS_CLOCKS_COUNT)) return -EINVAL; idx = custom_params[0] * NAVI10_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -2133,57 +2136,61 @@ static int navi10_display_disable_memory_clock_switch(struct smu_context *smu, return ret; } -static int navi10_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int navi10_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) +{ + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; + + return smu_v11_0_get_ppt_limit(smu, limit_type, ppt_limit); +} + +static int navi10_init_ppt_limits(struct smu_context *smu) { struct smu_11_0_powerplay_table *powerplay_table = (struct smu_11_0_powerplay_table *)smu->smu_table.power_play_table; struct smu_11_0_overdrive_table *od_settings = smu->od_settings; PPTable_t *pptable = smu->smu_table.driver_pptable; - uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; + uint32_t default_limit[SMU_POWER_SOURCE_COUNT]; + uint32_t od_percent_upper = 0, od_percent_lower = 0; + int i; - if (smu_v11_0_get_current_power_limit(smu, &power_limit)) { - /* the last hope to figure out the ppt limit */ - if (!pptable) { - dev_err(smu->adev->dev, "Cannot get PPT limit due to pptable missing!"); - return -EINVAL; - } - power_limit = - pptable->SocketPowerLimitAc[PPT_THROTTLER_PPT0]; - } - - if (current_power_limit) - *current_power_limit = power_limit; - if (default_power_limit) - *default_power_limit = power_limit; - - if (powerplay_table) { - if (smu->od_enabled && - navi10_od_feature_is_supported(od_settings, SMU_11_0_ODCAP_POWER_LIMIT)) { - od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_11_0_ODSETTING_POWERPERCENTAGE]); - od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_ODSETTING_POWERPERCENTAGE]); - } else if (navi10_od_feature_is_supported(od_settings, SMU_11_0_ODCAP_POWER_LIMIT)) { - od_percent_upper = 0; - od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_ODSETTING_POWERPERCENTAGE]); - } + if (!pptable) { + dev_err(smu->adev->dev, + "Cannot get PPT limit due to pptable missing!"); + return -EINVAL; } - dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); + default_limit[SMU_POWER_SOURCE_AC] = + pptable->SocketPowerLimitAc[PPT_THROTTLER_PPT0]; + default_limit[SMU_POWER_SOURCE_DC] = + pptable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; - if (max_power_limit) { - *max_power_limit = power_limit * (100 + od_percent_upper); - *max_power_limit /= 100; + if (powerplay_table && + navi10_od_feature_is_supported(od_settings, + SMU_11_0_ODCAP_POWER_LIMIT)) { + od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[ + SMU_11_0_ODSETTING_POWERPERCENTAGE]); + od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[ + SMU_11_0_ODSETTING_POWERPERCENTAGE]); } - if (min_power_limit) { - *min_power_limit = power_limit * (100 - od_percent_lower); - *min_power_limit /= 100; + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + default_limit[i]; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = + default_limit[i]; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = + default_limit[i] * (100 - od_percent_lower) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + default_limit[i] * (100 + od_percent_upper) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min; } + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); + return 0; } @@ -3300,7 +3307,7 @@ static const struct pptable_funcs navi10_ppt_funcs = { .set_performance_level = smu_v11_0_set_performance_level, .get_thermal_temperature_range = navi10_get_thermal_temperature_range, .display_disable_memory_clock_switch = navi10_display_disable_memory_clock_switch, - .get_power_limit = navi10_get_power_limit, + .get_ppt_limit = navi10_get_ppt_limit, .update_pcie_parameters = navi10_update_pcie_parameters, .init_microcode = smu_v11_0_init_microcode, .load_microcode = smu_v11_0_load_microcode, @@ -3324,7 +3331,7 @@ static const struct pptable_funcs navi10_ppt_funcs = { .feature_is_enabled = smu_cmn_feature_is_enabled, .disable_all_features_with_exception = smu_cmn_disable_all_features_with_exception, .notify_display_change = smu_v11_0_notify_display_change, - .set_power_limit = smu_v11_0_set_power_limit, + .set_ppt_limit = smu_v11_0_set_ppt_limit, .init_max_sustainable_clocks = smu_v11_0_init_max_sustainable_clocks, .enable_thermal_alert = smu_v11_0_enable_thermal_alert, .disable_thermal_alert = smu_v11_0_disable_thermal_alert, @@ -3337,7 +3344,6 @@ static const struct pptable_funcs navi10_ppt_funcs = { .set_xgmi_pstate = smu_v11_0_set_xgmi_pstate, .gfx_off_control = smu_v11_0_gfx_off_control, .register_irq_handler = smu_v11_0_register_irq_handler, - .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme, .get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc, .get_bamaco_support = smu_v11_0_get_bamaco_support, .baco_enter = navi10_baco_enter, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index f799e489b481..b42897e7c066 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -60,6 +60,8 @@ #undef pr_info #undef pr_debug +static int sienna_cichlid_init_ppt_limits(struct smu_context *smu); + static const struct smu_feature_bits sienna_cichlid_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_DPM_PREFETCHER_BIT), @@ -524,7 +526,11 @@ static int sienna_cichlid_setup_pptable(struct smu_context *smu) if (ret) return ret; - return sienna_cichlid_patch_pptable_quirk(smu); + ret = sienna_cichlid_patch_pptable_quirk(smu); + if (ret) + return ret; + + return sienna_cichlid_init_ppt_limits(smu); } static int sienna_cichlid_tables_init(struct smu_context *smu) @@ -624,53 +630,58 @@ static bool sienna_cichlid_is_od_feature_supported(struct smu_11_0_7_overdrive_t return od_table->cap[cap]; } -static int sienna_cichlid_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int sienna_cichlid_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) +{ + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; + + return smu_v11_0_get_ppt_limit(smu, limit_type, ppt_limit); +} + +static int sienna_cichlid_init_ppt_limits(struct smu_context *smu) { struct smu_11_0_7_powerplay_table *powerplay_table = (struct smu_11_0_7_powerplay_table *)smu->smu_table.power_play_table; struct smu_11_0_7_overdrive_table *od_settings = smu->od_settings; - uint32_t power_limit, od_percent_upper = 0, od_percent_lower = 0; - uint16_t *table_member; - - GET_PPTABLE_MEMBER(SocketPowerLimitAc, &table_member); + uint32_t default_limit[SMU_POWER_SOURCE_COUNT]; + uint32_t od_percent_upper = 0, od_percent_lower = 0; + uint16_t *ppt_limit_ac, *ppt_limit_dc; + int i; - if (smu_v11_0_get_current_power_limit(smu, &power_limit)) { - power_limit = - table_member[PPT_THROTTLER_PPT0]; - } + GET_PPTABLE_MEMBER(SocketPowerLimitAc, &ppt_limit_ac); + GET_PPTABLE_MEMBER(SocketPowerLimitDc, &ppt_limit_dc); - if (current_power_limit) - *current_power_limit = power_limit; - if (default_power_limit) - *default_power_limit = power_limit; + default_limit[SMU_POWER_SOURCE_AC] = + ppt_limit_ac[PPT_THROTTLER_PPT0]; + default_limit[SMU_POWER_SOURCE_DC] = + ppt_limit_dc[PPT_THROTTLER_PPT0]; - if (powerplay_table) { - if (smu->od_enabled && - sienna_cichlid_is_od_feature_supported(od_settings, SMU_11_0_7_ODCAP_POWER_LIMIT)) { - od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_11_0_7_ODSETTING_POWERPERCENTAGE]); - od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_7_ODSETTING_POWERPERCENTAGE]); - } else if ((sienna_cichlid_is_od_feature_supported(od_settings, SMU_11_0_7_ODCAP_POWER_LIMIT))) { - od_percent_upper = 0; - od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_11_0_7_ODSETTING_POWERPERCENTAGE]); - } + if (powerplay_table && + sienna_cichlid_is_od_feature_supported(od_settings, + SMU_11_0_7_ODCAP_POWER_LIMIT)) { + od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[ + SMU_11_0_7_ODSETTING_POWERPERCENTAGE]); + od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[ + SMU_11_0_7_ODSETTING_POWERPERCENTAGE]); } - dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); - - if (max_power_limit) { - *max_power_limit = power_limit * (100 + od_percent_upper); - *max_power_limit /= 100; + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + default_limit[i]; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = + default_limit[i]; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = + default_limit[i] * (100 - od_percent_lower) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + default_limit[i] * (100 + od_percent_upper) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min; } - if (min_power_limit) { - *min_power_limit = power_limit * (100 - od_percent_lower); - *min_power_limit /= 100; - } + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); + return 0; } @@ -683,22 +694,29 @@ static void sienna_cichlid_get_smartshift_power_percentage(struct smu_context *s &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics_V4); uint16_t powerRatio = 0; uint16_t apu_power_limit = 0; - uint16_t dgpu_power_limit = 0; + uint16_t dgpu_ppt_limit = 0; uint32_t apu_boost = 0; uint32_t dgpu_boost = 0; - uint32_t cur_power_limit; + uint32_t cur_ppt_limit; + enum smu_power_src_type power_source; if (metrics_v4->ApuSTAPMSmartShiftLimit != 0) { - sienna_cichlid_get_power_limit(smu, &cur_power_limit, NULL, NULL, NULL); + if (sienna_cichlid_get_ppt_limit(smu, SMU_PPT_LIMIT_PPT0, + &cur_ppt_limit)) { + power_source = smu->adev->pm.ac_power ? + SMU_POWER_SOURCE_AC : SMU_POWER_SOURCE_DC; + cur_ppt_limit = smu->ppt_limits.range[power_source] + [SMU_PPT_LIMIT_PPT0].default_value; + } apu_power_limit = metrics_v4->ApuSTAPMLimit; - dgpu_power_limit = cur_power_limit; + dgpu_ppt_limit = cur_ppt_limit; powerRatio = (((apu_power_limit + - dgpu_power_limit) * 100) / + dgpu_ppt_limit) * 100) / metrics_v4->ApuSTAPMSmartShiftLimit); if (powerRatio > 100) { apu_power_limit = (apu_power_limit * 100) / powerRatio; - dgpu_power_limit = (dgpu_power_limit * 100) / + dgpu_ppt_limit = (dgpu_ppt_limit * 100) / powerRatio; } if (metrics_v4->AverageApuSocketPower > apu_power_limit && @@ -710,11 +728,11 @@ static void sienna_cichlid_get_smartshift_power_percentage(struct smu_context *s apu_boost = 100; } - if (metrics_v4->AverageSocketPower > dgpu_power_limit && - dgpu_power_limit != 0) { + if (metrics_v4->AverageSocketPower > dgpu_ppt_limit && + dgpu_ppt_limit != 0) { dgpu_boost = ((metrics_v4->AverageSocketPower - - dgpu_power_limit) * 100) / - dgpu_power_limit; + dgpu_ppt_limit) * 100) / + dgpu_ppt_limit; if (dgpu_boost > 100) dgpu_boost = 100; } @@ -855,9 +873,10 @@ static int sienna_cichlid_get_smu_metrics_data(struct smu_context *smu, metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = use_metrics_v3 ? metrics_v3->AverageSocketPower << 8 : - use_metrics_v2 ? metrics_v2->AverageSocketPower << 8 : - metrics->AverageSocketPower << 8; + *value = use_metrics_v3 ? metrics_v3->AverageSocketPower : + use_metrics_v2 ? metrics_v2->AverageSocketPower : + metrics->AverageSocketPower; + *value *= MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = (use_metrics_v3 ? metrics_v3->TemperatureEdge : @@ -1755,9 +1774,10 @@ static int sienna_cichlid_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != SIENNA_CICHLID_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + SIENNA_CICHLID_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * SIENNA_CICHLID_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -3107,7 +3127,7 @@ static const struct pptable_funcs sienna_cichlid_ppt_funcs = { .set_performance_level = smu_v11_0_set_performance_level, .get_thermal_temperature_range = sienna_cichlid_get_thermal_temperature_range, .display_disable_memory_clock_switch = sienna_cichlid_display_disable_memory_clock_switch, - .get_power_limit = sienna_cichlid_get_power_limit, + .get_ppt_limit = sienna_cichlid_get_ppt_limit, .update_pcie_parameters = sienna_cichlid_update_pcie_parameters, .init_microcode = smu_v11_0_init_microcode, .load_microcode = smu_v11_0_load_microcode, @@ -3131,7 +3151,7 @@ static const struct pptable_funcs sienna_cichlid_ppt_funcs = { .feature_is_enabled = smu_cmn_feature_is_enabled, .disable_all_features_with_exception = smu_cmn_disable_all_features_with_exception, .notify_display_change = NULL, - .set_power_limit = smu_v11_0_set_power_limit, + .set_ppt_limit = smu_v11_0_set_ppt_limit, .init_max_sustainable_clocks = smu_v11_0_init_max_sustainable_clocks, .enable_thermal_alert = smu_v11_0_enable_thermal_alert, .disable_thermal_alert = smu_v11_0_disable_thermal_alert, @@ -3144,7 +3164,6 @@ static const struct pptable_funcs sienna_cichlid_ppt_funcs = { .set_xgmi_pstate = smu_v11_0_set_xgmi_pstate, .gfx_off_control = smu_v11_0_gfx_off_control, .register_irq_handler = smu_v11_0_register_irq_handler, - .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme, .get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc, .get_bamaco_support = smu_v11_0_get_bamaco_support, .baco_enter = sienna_cichlid_baco_enter, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c index d68ceee16d8f..f0b2e7da67b1 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c @@ -192,81 +192,22 @@ int smu_v11_0_check_fw_status(struct smu_context *smu) return -EIO; } -static int smu_v11_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t ppt_offset_bytes; - const struct smc_firmware_header_v2_0 *v2; - - v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; - - ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); - *size = le32_to_cpu(v2->ppt_size_bytes); - *table = (uint8_t *)v2 + ppt_offset_bytes; - - return 0; -} - -static int smu_v11_0_set_pptable_v2_1(struct smu_context *smu, void **table, - uint32_t *size, uint32_t pptable_id) -{ - struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v2_1 *v2_1; - struct smc_soft_pptable_entry *entries; - uint32_t pptable_count = 0; - int i = 0; - - v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; - entries = (struct smc_soft_pptable_entry *) - ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); - pptable_count = le32_to_cpu(v2_1->pptable_count); - for (i = 0; i < pptable_count; i++) { - if (le32_to_cpu(entries[i].id) == pptable_id) { - *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); - *size = le32_to_cpu(entries[i].ppt_size_bytes); - break; - } - } - - if (i == pptable_count) - return -EINVAL; - - return 0; -} - int smu_v11_0_setup_pptable(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v1_0 *hdr; - int ret, index; - uint32_t size = 0; uint16_t atom_table_size; uint8_t frev, crev; + uint32_t size = 0; + int ret, index; void *table; - uint16_t version_major, version_minor; - - if (!amdgpu_sriov_vf(adev)) { - hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; - version_major = le16_to_cpu(hdr->header.header_version_major); - version_minor = le16_to_cpu(hdr->header.header_version_minor); - if (version_major == 2 && smu->smu_table.boot_values.pp_table_id > 0) { - dev_info(adev->dev, "use driver provided pptable %d\n", smu->smu_table.boot_values.pp_table_id); - switch (version_minor) { - case 0: - ret = smu_v11_0_set_pptable_v2_0(smu, &table, &size); - break; - case 1: - ret = smu_v11_0_set_pptable_v2_1(smu, &table, &size, - smu->smu_table.boot_values.pp_table_id); - break; - default: - ret = -EINVAL; - break; - } - if (ret) - return ret; - goto out; - } + + if (!amdgpu_sriov_vf(adev) && + smu->smu_table.boot_values.pp_table_id > 0) { + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, + smu->smu_table.boot_values.pp_table_id); + if (ret) + return ret; + goto out; } dev_info(adev->dev, "use vbios provided pptable\n"); @@ -829,14 +770,17 @@ int smu_v11_0_init_max_sustainable_clocks(struct smu_context *smu) return 0; } -int smu_v11_0_get_current_power_limit(struct smu_context *smu, - uint32_t *power_limit) +int smu_v11_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) { int power_src; int ret = 0; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) return -EINVAL; + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; power_src = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_PWR, @@ -853,26 +797,26 @@ int smu_v11_0_get_current_power_limit(struct smu_context *smu, ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetPptLimit, (0 << 24) | (power_src << 16), - power_limit); + ppt_limit); if (ret) dev_err(smu->adev->dev, "[%s] get PPT limit failed!", __func__); return ret; } -int smu_v11_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +int smu_v11_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { int power_src; int ret = 0; uint32_t limit_param; - if (limit_type != SMU_DEFAULT_PPT_LIMIT) + if (limit_type != SMU_PPT_LIMIT_PPT0) return -EINVAL; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) { - dev_err(smu->adev->dev, "Setting new power limit is not supported!\n"); + dev_err(smu->adev->dev, "Setting new PPT limit is not supported!\n"); return -EOPNOTSUPP; } @@ -894,12 +838,10 @@ int smu_v11_0_set_power_limit(struct smu_context *smu, limit_param |= (power_src) << 16; ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetPptLimit, limit_param, NULL); if (ret) { - dev_err(smu->adev->dev, "[%s] Set power limit Failed!\n", __func__); + dev_err(smu->adev->dev, "[%s] Set PPT limit failed!\n", __func__); return ret; } - smu->current_power_limit = limit; - return 0; } @@ -1476,11 +1418,6 @@ int smu_v11_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu, return 0; } -int smu_v11_0_set_azalia_d3_pme(struct smu_context *smu) -{ - return smu_cmn_send_smc_msg(smu, SMU_MSG_BacoAudioD3PME, NULL); -} - int smu_v11_0_baco_set_armd3_sequence(struct smu_context *smu, enum smu_baco_seq baco_seq) { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index 016a5c893fee..b38bc49c43dd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -58,6 +58,8 @@ #define SMUIO_GFX_MISC_CNTL__SMU_GFX_cold_vs_gfxoff_MASK 0x00000001L #define SMUIO_GFX_MISC_CNTL__PWR_GFXOFF_STATUS_MASK 0x00000006L +static int vangogh_init_ppt_limits(struct smu_context *smu); + static const struct smu_feature_bits vangogh_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_CCLK_DPM_BIT), @@ -72,6 +74,12 @@ static const struct smu_feature_bits vangogh_dpm_features = { } }; +/* + * SMU support new GFXOFF residency log interface since version 4.63.62.00, + * use this to get live readings of GFXOFF residency + */ +#define SUPPORT_LIVE_RESIDENCY_MSG_VERSION 0x043f3e00 + static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 0), MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 0), @@ -142,7 +150,9 @@ static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(GetSlowPPTLimit, PPSMC_MSG_GetSlowPPTLimit, 0), MSG_MAP(GetGfxOffStatus, PPSMC_MSG_GetGfxOffStatus, 0), MSG_MAP(GetGfxOffEntryCount, PPSMC_MSG_GetGfxOffEntryCount, 0), - MSG_MAP(LogGfxOffResidency, PPSMC_MSG_LogGfxOffResidency, 0), + MSG_MAP(StartGfxOffResidencyLogging, PPSMC_MSG_StartGfxOffResidencyLogging, 0), + MSG_MAP(GfxOffResidencyLogReadSample, PPSMC_MSG_GfxOffResidencyLogReadSample, 0), + MSG_MAP(StopGfxOffResidencyLogging, PPSMC_MSG_StopGfxOffResidencyLogging, 0), }; static struct cmn2asic_mapping vangogh_feature_mask_map[SMU_FEATURE_COUNT] = { @@ -310,8 +320,7 @@ static int vangogh_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / - 1000 ; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * @@ -379,12 +388,10 @@ static int vangogh_get_smu_metrics_data(struct smu_context *smu, *value = metrics->Current.UvdActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->Average.CurrentSocketPower << 8) / - 1000; + *value = metrics->Average.CurrentSocketPower; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->Current.CurrentSocketPower << 8) / - 1000; + *value = metrics->Current.CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->Current.GfxTemperature / 100 * @@ -2238,6 +2245,10 @@ static int vangogh_post_smu_init(struct smu_context *smu) if (adev->in_s0ix) return 0; + ret = vangogh_init_ppt_limits(smu); + if (ret) + return ret; + /* allow message will be sent after enable message on Vangogh*/ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT) && (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG)) { @@ -2317,91 +2328,75 @@ static u32 vangogh_get_gfxoff_status(struct smu_context *smu) return gfxoff_status; } -static int vangogh_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int vangogh_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) { - struct smu_11_5_power_context *power_context = smu->smu_power.power_context; - uint32_t ppt_limit; - int ret = 0; + enum smu_message_type msg; + uint32_t raw_limit; + int ret; if (smu->adev->pm.fw_version < 0x43f1e00) - return ret; + return -EOPNOTSUPP; - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetSlowPPTLimit, &ppt_limit); - if (ret) { - dev_err(smu->adev->dev, "Get slow PPT limit failed!\n"); - return ret; - } - /* convert from milliwatt to watt */ - if (current_power_limit) - *current_power_limit = ppt_limit / 1000; - if (default_power_limit) - *default_power_limit = ppt_limit / 1000; - if (max_power_limit) - *max_power_limit = 29; - if (min_power_limit) - *min_power_limit = 0; - - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetFastPPTLimit, &ppt_limit); + msg = limit_type == SMU_PPT_LIMIT_PPT1 ? + SMU_MSG_GetFastPPTLimit : SMU_MSG_GetSlowPPTLimit; + ret = smu_cmn_send_smc_msg(smu, msg, &raw_limit); if (ret) { - dev_err(smu->adev->dev, "Get fast PPT limit failed!\n"); + dev_err(smu->adev->dev, "Get PPT%d limit failed!\n", + limit_type); return ret; } - /* convert from milliwatt to watt */ - power_context->current_fast_ppt_limit = - power_context->default_fast_ppt_limit = ppt_limit / 1000; - power_context->max_fast_ppt_limit = 30; - return ret; + *ppt_limit = raw_limit / 1000; + + return 0; } -static int vangogh_get_ppt_limit(struct smu_context *smu, - uint32_t *ppt_limit, - enum smu_ppt_limit_type type, - enum smu_ppt_limit_level level) +static int vangogh_init_ppt_limits(struct smu_context *smu) { - struct smu_11_5_power_context *power_context = smu->smu_power.power_context; + struct smu_ppt_limit_range *range; + uint32_t default_limit; + int i, j, ret; - if (!power_context) - return -EOPNOTSUPP; + if (smu->adev->pm.fw_version < 0x43f1e00) + return 0; - if (type == SMU_FAST_PPT_LIMIT) { - switch (level) { - case SMU_PPT_LIMIT_MAX: - *ppt_limit = power_context->max_fast_ppt_limit; - break; - case SMU_PPT_LIMIT_CURRENT: - *ppt_limit = power_context->current_fast_ppt_limit; - break; - case SMU_PPT_LIMIT_DEFAULT: - *ppt_limit = power_context->default_fast_ppt_limit; - break; - default: - break; + for (i = SMU_PPT_LIMIT_PPT0; i < SMU_LIMIT_TYPE_COUNT; i++) { + if (smu->ppt_limits.supported_mask & BIT(i)) + continue; + + ret = vangogh_get_ppt_limit(smu, i, &default_limit); + if (ret) + return ret; + + for (j = SMU_POWER_SOURCE_AC; j < SMU_POWER_SOURCE_COUNT; j++) { + range = &smu->ppt_limits.range[j][i]; + range->default_value = default_limit; + range->min = 0; + range->max = i == SMU_PPT_LIMIT_PPT1 ? 30 : 29; + range->od_min = range->min; + range->od_max = range->max; } + smu->ppt_limits.supported_mask |= BIT(i); } return 0; } -static int vangogh_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t ppt_limit) +static int vangogh_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t ppt_limit) { - struct smu_11_5_power_context *power_context = - smu->smu_power.power_context; int ret = 0; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) { - dev_err(smu->adev->dev, "Setting new power limit is not supported!\n"); + dev_err(smu->adev->dev, "Setting new PPT limit is not supported!\n"); return -EOPNOTSUPP; } switch (limit_type) { - case SMU_DEFAULT_PPT_LIMIT: + case SMU_SLOW_PPT_LIMIT: ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetSlowPPTLimit, ppt_limit * 1000, /* convert from watt to milliwatt */ @@ -2409,16 +2404,8 @@ static int vangogh_set_power_limit(struct smu_context *smu, if (ret) return ret; - smu->current_power_limit = ppt_limit; break; case SMU_FAST_PPT_LIMIT: - if (ppt_limit > power_context->max_fast_ppt_limit) { - dev_err(smu->adev->dev, - "New power limit (%d) is over the max allowed %d\n", - ppt_limit, power_context->max_fast_ppt_limit); - return ret; - } - ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetFastPPTLimit, ppt_limit * 1000, /* convert from watt to milliwatt */ @@ -2426,7 +2413,6 @@ static int vangogh_set_power_limit(struct smu_context *smu, if (ret) return ret; - power_context->current_fast_ppt_limit = ppt_limit; break; default: return -EINVAL; @@ -2449,19 +2435,32 @@ static int vangogh_set_power_limit(struct smu_context *smu, static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start) { int ret = 0; - u32 residency; struct amdgpu_device *adev = smu->adev; if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) return 0; - ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_LogGfxOffResidency, - start, &residency); - if (ret) - return ret; + if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) { + u32 residency; - if (!start) - adev->gfx.gfx_off_residency = residency; + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxOffResidencyLogReadSample, + start, &residency); + if (ret) + return ret; + + if (!start) + adev->gfx.gfx_off_residency = residency; + } else { + if (start) { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StartGfxOffResidencyLogging, NULL); + if (ret) + return ret; + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StopGfxOffResidencyLogging, NULL); + if (ret) + return ret; + } + } return ret; } @@ -2478,11 +2477,20 @@ static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start) */ static u32 vangogh_get_gfxoff_residency(struct smu_context *smu, uint32_t *residency) { + int ret = 0; struct amdgpu_device *adev = smu->adev; - *residency = adev->gfx.gfx_off_residency; + if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) + return 0; - return 0; + if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) { + *residency = adev->gfx.gfx_off_residency; + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GfxOffResidencyLogReadSample, + residency); + } + + return ret; } /** @@ -2550,8 +2558,7 @@ static const struct pptable_funcs vangogh_ppt_funcs = { .get_gfx_off_residency = vangogh_get_gfxoff_residency, .set_gfx_off_residency = vangogh_set_gfxoff_residency, .get_ppt_limit = vangogh_get_ppt_limit, - .get_power_limit = vangogh_get_power_limit, - .set_power_limit = vangogh_set_power_limit, + .set_ppt_limit = vangogh_set_ppt_limit, .get_vbios_bootup_values = smu_v11_0_get_vbios_bootup_values, }; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c index 75335da224c7..0ec17c1054c0 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c @@ -1215,9 +1215,10 @@ static int renoir_get_smu_metrics_data(struct smu_context *smu, ((amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(12, 0, 0)) && (adev->pm.fw_version >= 0x373200))) - *value = metrics->CurrentSocketPower << 8; + *value = metrics->CurrentSocketPower * + MILLIWATT_PER_WATT; else - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = (metrics->GfxTemperature / 100) * @@ -1444,7 +1445,6 @@ static int renoir_get_enabled_mask(struct smu_context *smu, } static const struct pptable_funcs renoir_ppt_funcs = { - .set_power_state = NULL, .emit_clk_levels = renoir_emit_clk_levels, .get_current_power_state = renoir_get_current_power_state, .dpm_set_vcn_enable = renoir_dpm_set_vcn_enable, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c index 9d8b1227388f..1b1ffe19d002 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c @@ -61,6 +61,9 @@ [smu_feature] = {1, (aldebaran_feature)} #define FEATURE_MASK(feature) (1ULL << feature) + +static int aldebaran_init_ppt_limits(struct smu_context *smu); + static const struct smu_feature_bits aldebaran_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_DATA_CALCULATIONS), @@ -537,7 +540,7 @@ static int aldebaran_setup_pptable(struct smu_context *smu) if (ret) return ret; - return ret; + return aldebaran_init_ppt_limits(smu); } static bool aldebaran_is_primary(struct smu_context *smu) @@ -677,7 +680,8 @@ static int aldebaran_get_smu_metrics_data(struct smu_context *smu, case METRICS_AVERAGE_SOCKETPOWER: /* Valid power data is available only from primary die */ if (aldebaran_is_primary(smu)) - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * + MILLIWATT_PER_WATT; else ret = -EOPNOTSUPP; break; @@ -1110,28 +1114,17 @@ static int aldebaran_read_sensor(struct smu_context *smu, return ret; } -static int aldebaran_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int aldebaran_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) { - PPTable_t *pptable = smu->smu_table.driver_pptable; - uint32_t power_limit = 0; int ret; - if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) { - if (current_power_limit) - *current_power_limit = 0; - if (default_power_limit) - *default_power_limit = 0; - if (max_power_limit) - *max_power_limit = 0; - if (min_power_limit) - *min_power_limit = 0; - dev_warn(smu->adev->dev, - "PPT feature is not enabled, power values can't be fetched."); + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; + if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) { + *ppt_limit = 0; return 0; } @@ -1140,42 +1133,47 @@ static int aldebaran_get_power_limit(struct smu_context *smu, */ if (aldebaran_is_primary(smu)) { ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetPptLimit, - &power_limit); - - if (ret) { - /* the last hope to figure out the ppt limit */ - if (!pptable) { - dev_err(smu->adev->dev, - "Cannot get PPT limit due to pptable missing!"); - return -EINVAL; - } - power_limit = pptable->PptLimit; - } + ppt_limit); + if (ret) + return ret; + } else { + *ppt_limit = 0; } - if (current_power_limit) - *current_power_limit = power_limit; - if (default_power_limit) - *default_power_limit = power_limit; + return 0; +} + +static int aldebaran_init_ppt_limits(struct smu_context *smu) +{ + PPTable_t *pptable = smu->smu_table.driver_pptable; + int i; + + if (!pptable) + return -EINVAL; - if (max_power_limit) { - if (pptable) - *max_power_limit = pptable->PptLimit; + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + pptable->PptLimit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = 0; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = + pptable->PptLimit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = 0; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + pptable->PptLimit; } - if (min_power_limit) - *min_power_limit = 0; + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); return 0; } -static int aldebaran_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +static int aldebaran_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { - /* Power limit can be set only through primary die */ + /* PPT limit can be set only through primary die */ if (aldebaran_is_primary(smu)) - return smu_v13_0_set_power_limit(smu, limit_type, limit); + return smu_v13_0_set_ppt_limit(smu, limit_type, limit); return -EINVAL; } @@ -1975,7 +1973,7 @@ static const struct pptable_funcs aldebaran_ppt_funcs = { .force_clk_levels = aldebaran_force_clk_levels, .read_sensor = aldebaran_read_sensor, .set_performance_level = aldebaran_set_performance_level, - .get_power_limit = aldebaran_get_power_limit, + .get_ppt_limit = aldebaran_get_ppt_limit, .is_dpm_running = aldebaran_is_dpm_running, .get_unique_id = aldebaran_get_unique_id, .init_microcode = smu_v13_0_init_microcode, @@ -1998,13 +1996,12 @@ static const struct pptable_funcs aldebaran_ppt_funcs = { .get_enabled_mask = smu_cmn_get_enabled_mask, .feature_is_enabled = smu_cmn_feature_is_enabled, .disable_all_features_with_exception = smu_cmn_disable_all_features_with_exception, - .set_power_limit = aldebaran_set_power_limit, + .set_ppt_limit = aldebaran_set_ppt_limit, .init_max_sustainable_clocks = smu_v13_0_init_max_sustainable_clocks, .enable_thermal_alert = smu_v13_0_enable_thermal_alert, .disable_thermal_alert = smu_v13_0_disable_thermal_alert, .set_xgmi_pstate = smu_v13_0_set_xgmi_pstate, .register_irq_handler = smu_v13_0_register_irq_handler, - .set_azalia_d3_pme = smu_v13_0_set_azalia_d3_pme, .get_max_sustainable_clocks_by_dc = smu_v13_0_get_max_sustainable_clocks_by_dc, .get_bamaco_support = aldebaran_get_bamaco_support, .get_dpm_ultimate_freq = aldebaran_get_dpm_ultimate_freq, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c index be9a7a32de99..c4b2fe60da62 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c @@ -218,7 +218,7 @@ int smu_v13_0_init_pptable_microcode(struct smu_context *smu) if (!pptable_id) return 0; - ret = smu_v13_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -258,48 +258,6 @@ int smu_v13_0_check_fw_status(struct smu_context *smu) return -EIO; } -static int smu_v13_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t ppt_offset_bytes; - const struct smc_firmware_header_v2_0 *v2; - - v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; - - ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); - *size = le32_to_cpu(v2->ppt_size_bytes); - *table = (uint8_t *)v2 + ppt_offset_bytes; - - return 0; -} - -static int smu_v13_0_set_pptable_v2_1(struct smu_context *smu, void **table, - uint32_t *size, uint32_t pptable_id) -{ - struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v2_1 *v2_1; - struct smc_soft_pptable_entry *entries; - uint32_t pptable_count = 0; - int i = 0; - - v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; - entries = (struct smc_soft_pptable_entry *) - ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); - pptable_count = le32_to_cpu(v2_1->pptable_count); - for (i = 0; i < pptable_count; i++) { - if (le32_to_cpu(entries[i].id) == pptable_id) { - *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); - *size = le32_to_cpu(entries[i].ppt_size_bytes); - break; - } - } - - if (i == pptable_count) - return -EINVAL; - - return 0; -} - static int smu_v13_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size) { struct amdgpu_device *adev = smu->adev; @@ -322,45 +280,6 @@ static int smu_v13_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl return 0; } -int smu_v13_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id) -{ - const struct smc_firmware_header_v1_0 *hdr; - struct amdgpu_device *adev = smu->adev; - uint16_t version_major, version_minor; - int ret; - - hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; - if (!hdr) - return -EINVAL; - - dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); - - version_major = le16_to_cpu(hdr->header.header_version_major); - version_minor = le16_to_cpu(hdr->header.header_version_minor); - if (version_major != 2) { - dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", - version_major, version_minor); - return -EINVAL; - } - - switch (version_minor) { - case 0: - ret = smu_v13_0_set_pptable_v2_0(smu, table, size); - break; - case 1: - ret = smu_v13_0_set_pptable_v2_1(smu, table, size, pptable_id); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - int smu_v13_0_setup_pptable(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; @@ -380,7 +299,7 @@ int smu_v13_0_setup_pptable(struct smu_context *smu) if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1)) ret = smu_v13_0_get_pptable_from_vbios(smu, &table, &size); else - ret = smu_v13_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -896,14 +815,17 @@ int smu_v13_0_init_max_sustainable_clocks(struct smu_context *smu) return 0; } -int smu_v13_0_get_current_power_limit(struct smu_context *smu, - uint32_t *power_limit) +int smu_v13_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) { int power_src; int ret = 0; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) return -EINVAL; + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; power_src = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_PWR, @@ -916,35 +838,33 @@ int smu_v13_0_get_current_power_limit(struct smu_context *smu, ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetPptLimit, power_src << 16, - power_limit); + ppt_limit); if (ret) dev_err(smu->adev->dev, "[%s] get PPT limit failed!", __func__); return ret; } -int smu_v13_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +int smu_v13_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { int ret = 0; - if (limit_type != SMU_DEFAULT_PPT_LIMIT) + if (limit_type != SMU_PPT_LIMIT_PPT0) return -EINVAL; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) { - dev_err(smu->adev->dev, "Setting new power limit is not supported!\n"); + dev_err(smu->adev->dev, "Setting new PPT limit is not supported!\n"); return -EOPNOTSUPP; } ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetPptLimit, limit, NULL); if (ret) { - dev_err(smu->adev->dev, "[%s] Set power limit Failed!\n", __func__); + dev_err(smu->adev->dev, "[%s] Set PPT limit failed!\n", __func__); return ret; } - smu->current_power_limit = limit; - return 0; } @@ -1401,15 +1321,6 @@ int smu_v13_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu, return 0; } -int smu_v13_0_set_azalia_d3_pme(struct smu_context *smu) -{ - int ret = 0; - - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_BacoAudioD3PME, NULL); - - return ret; -} - static int smu_v13_0_wait_for_reset_complete(struct smu_context *smu, uint64_t event_arg) { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index acbd7046d8a5..6e741ec4a71e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -62,6 +62,7 @@ static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu, int od_feature_bit, int32_t *min, int32_t *max); +static int smu_v13_0_0_init_ppt_limits(struct smu_context *smu); static const struct smu_feature_bits smu_v13_0_0_dpm_features = { .bits = { @@ -467,7 +468,7 @@ static int smu_v13_0_0_setup_pptable(struct smu_context *smu) if (ret) return ret; - return ret; + return smu_v13_0_0_init_ppt_limits(smu); } static int smu_v13_0_0_tables_init(struct smu_context *smu) @@ -807,7 +808,7 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu, metrics->Vcn1ActivityPercentage); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * @@ -2389,58 +2390,71 @@ static int smu_v13_0_0_enable_mgpu_fan_boost(struct smu_context *smu) NULL); } -static int smu_v13_0_0_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int smu_v13_0_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) +{ + OverDriveTableExternal_t od_table; + int ret; + + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; + + ret = smu_v13_0_get_ppt_limit(smu, limit_type, ppt_limit); + if (ret) + return ret; + + ret = smu_v13_0_0_get_overdrive_table(smu, &od_table); + if (ret) + return ret; + + if (od_table.OverDriveTable.Ppt > 0) + *ppt_limit = *ppt_limit * + (100 + od_table.OverDriveTable.Ppt) / 100; + + return 0; +} + +static int smu_v13_0_0_init_ppt_limits(struct smu_context *smu) { struct smu_table_context *table_context = &smu->smu_table; struct smu_13_0_0_powerplay_table *powerplay_table = (struct smu_13_0_0_powerplay_table *)table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t pp_limit = smu->adev->pm.ac_power ? - skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : - skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); - uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); uint32_t od_percent_upper = 0, od_percent_lower = 0; - int ret; - - if (current_power_limit) { - ret = smu_v13_0_get_current_power_limit(smu, current_power_limit); - if (ret) - *current_power_limit = pp_limit; - } + uint32_t pp_limit, msg_limit, min_limit, max_limit; + int i; - if (default_power_limit) - *default_power_limit = pp_limit; - - if (powerplay_table) { - if (smu->od_enabled && - smu_v13_0_0_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { - od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_13_0_0_ODSETTING_POWERPERCENTAGE]); - od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_13_0_0_ODSETTING_POWERPERCENTAGE]); - } else if (smu_v13_0_0_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { - od_percent_upper = 0; - od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_13_0_0_ODSETTING_POWERPERCENTAGE]); - } + if (powerplay_table && + smu_v13_0_0_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { + od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[ + SMU_13_0_0_ODSETTING_POWERPERCENTAGE]); + od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[ + SMU_13_0_0_ODSETTING_POWERPERCENTAGE]); } - dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, pp_limit); - - if (max_power_limit) { - *max_power_limit = max_limit * (100 + od_percent_upper); - *max_power_limit /= 100; + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + pp_limit = i == SMU_POWER_SOURCE_AC ? + skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : + skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][i]; + min_limit = min(pp_limit, msg_limit); + max_limit = max(pp_limit, msg_limit); + + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + pp_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = + max_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = + min_limit * (100 - od_percent_lower) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + max_limit * (100 + od_percent_upper) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min; } - if (min_power_limit) { - *min_power_limit = min_limit * (100 - od_percent_lower); - *min_power_limit /= 100; - } + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); return 0; } @@ -2619,9 +2633,10 @@ static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != SMU_13_0_0_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= SMU_13_0_0_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + SMU_13_0_0_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + SMU_13_0_0_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * SMU_13_0_0_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -3030,23 +3045,29 @@ static bool smu_v13_0_0_wbrf_support_check(struct smu_context *smu) } } -static int smu_v13_0_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +static int smu_v13_0_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { PPTable_t *pptable = smu->smu_table.driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t msg_limit = skutable->MsgLimits.Power + [PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; struct smu_table_context *table_context = &smu->smu_table; OverDriveTableExternal_t *od_table = (OverDriveTableExternal_t *)table_context->overdrive_table; + OverDriveTableExternal_t current_od_table; int ret = 0; - if (limit_type != SMU_DEFAULT_PPT_LIMIT) + if (limit_type != SMU_PPT_LIMIT_PPT0) return -EINVAL; if (limit <= msg_limit) { - if (smu->current_power_limit > msg_limit) { + ret = smu_v13_0_0_get_overdrive_table(smu, ¤t_od_table); + if (ret) + return ret; + + if (current_od_table.OverDriveTable.Ppt) { od_table->OverDriveTable.Ppt = 0; od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT; @@ -3056,9 +3077,9 @@ static int smu_v13_0_0_set_power_limit(struct smu_context *smu, return ret; } } - return smu_v13_0_set_power_limit(smu, limit_type, limit); + return smu_v13_0_set_ppt_limit(smu, limit_type, limit); } else if (smu->od_enabled) { - ret = smu_v13_0_set_power_limit(smu, limit_type, msg_limit); + ret = smu_v13_0_set_ppt_limit(smu, limit_type, msg_limit); if (ret) return ret; @@ -3071,7 +3092,6 @@ static int smu_v13_0_0_set_power_limit(struct smu_context *smu, return ret; } - smu->current_power_limit = limit; } else { return -EINVAL; } @@ -3214,8 +3234,8 @@ static const struct pptable_funcs smu_v13_0_0_ppt_funcs = { .get_fan_control_mode = smu_v13_0_get_fan_control_mode, .set_fan_control_mode = smu_v13_0_set_fan_control_mode, .enable_mgpu_fan_boost = smu_v13_0_0_enable_mgpu_fan_boost, - .get_power_limit = smu_v13_0_0_get_power_limit, - .set_power_limit = smu_v13_0_0_set_power_limit, + .get_ppt_limit = smu_v13_0_0_get_ppt_limit, + .set_ppt_limit = smu_v13_0_0_set_ppt_limit, .set_power_source = smu_v13_0_set_power_source, .get_power_profile_mode = smu_v13_0_0_get_power_profile_mode, .set_power_profile_mode = smu_v13_0_0_set_power_profile_mode, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c index fe929bd89058..0033453f27e5 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c @@ -434,22 +434,19 @@ int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(metrics->DramBandwidthUtilization); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(metrics->SocketPower) << 8; + *value = SMUQ10_TO_MILLIWATT(metrics->SocketPower); break; case METRICS_TEMPERATURE_HOTSPOT: - *value = SMUQ10_ROUND(metrics->MaxSocketTemperature) * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(metrics->MaxSocketTemperature); break; case METRICS_TEMPERATURE_MEM: - *value = SMUQ10_ROUND(metrics->MaxHbmTemperature) * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(metrics->MaxHbmTemperature); break; /* This is the max of all VRs and not just SOC VR. * No need to define another data type for the same. */ case METRICS_TEMPERATURE_VRSOC: - *value = SMUQ10_ROUND(metrics->MaxVrTemperature) * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(metrics->MaxVrTemperature); break; default: *value = UINT_MAX; @@ -1017,139 +1014,3 @@ const struct smu_temp_funcs smu_v13_0_12_temp_funcs = { .temp_metrics_is_supported = smu_v13_0_12_is_temp_metrics_supported, .get_temp_metrics = smu_v13_0_12_get_temp_metrics, }; - -static int smu_v13_0_12_get_ras_table_version(struct amdgpu_device *adev, - uint32_t *table_version) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - - return smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_GetRASTableVersion, 0, table_version); -} - -static int smu_v13_0_12_get_badpage_count(struct amdgpu_device *adev, uint32_t *count, - uint32_t timeout) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - uint64_t end, now; - int ret = 0; - - now = (uint64_t)ktime_to_ms(ktime_get()); - end = now + timeout; - do { - ret = smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_GetBadPageCount, 0, count); - /* eeprom is not ready */ - if (ret != -EBUSY) - return ret; - mdelay(10); - now = (uint64_t)ktime_to_ms(ktime_get()); - } while (now < end); - - dev_err(adev->dev, - "smu get bad page count timeout!\n"); - return ret; -} - -static int smu_v13_0_12_set_timestamp(struct amdgpu_device *adev, uint64_t timestamp) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - - return smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_SetTimestamp, (uint32_t)timestamp, 0); -} - -static int smu_v13_0_12_get_timestamp(struct amdgpu_device *adev, - uint16_t index, uint64_t *timestamp) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - uint32_t temp; - int ret; - - ret = smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_GetTimestamp, index, &temp); - if (!ret) - *timestamp = temp; - - return ret; -} - -static int smu_v13_0_12_get_badpage_ipid(struct amdgpu_device *adev, - uint16_t index, uint64_t *ipid) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - uint32_t temp_arg, temp_ipid_lo, temp_ipid_high; - int ret; - - temp_arg = index | (1 << 16); - ret = smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_GetBadPageIpid, temp_arg, &temp_ipid_lo); - if (ret) - return ret; - - temp_arg = index | (2 << 16); - ret = smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_GetBadPageIpid, temp_arg, &temp_ipid_high); - if (!ret) - *ipid = (uint64_t)temp_ipid_high << 32 | temp_ipid_lo; - return ret; -} - -static int smu_v13_0_12_erase_ras_table(struct amdgpu_device *adev, - uint32_t *result) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - - return smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_EraseRasTable, 0, result); -} - -static int smu_v13_0_12_get_badpage_mca_addr(struct amdgpu_device *adev, - uint16_t index, uint64_t *mca_addr) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - uint32_t temp_arg, temp_addr_lo, temp_addr_high; - int ret; - - temp_arg = index | (1 << 16); - ret = smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_GetBadPageMcaAddr, temp_arg, &temp_addr_lo); - if (ret) - return ret; - - temp_arg = index | (2 << 16); - ret = smu_cmn_send_smc_msg_with_param(smu, - SMU_MSG_GetBadPageMcaAddr, temp_arg, &temp_addr_high); - if (!ret) - *mca_addr = (uint64_t)temp_addr_high << 32 | temp_addr_lo; - return ret; -} - -static const struct ras_eeprom_smu_funcs smu_v13_0_12_eeprom_smu_funcs = { - .get_ras_table_version = smu_v13_0_12_get_ras_table_version, - .get_badpage_count = smu_v13_0_12_get_badpage_count, - .get_badpage_mca_addr = smu_v13_0_12_get_badpage_mca_addr, - .set_timestamp = smu_v13_0_12_set_timestamp, - .get_timestamp = smu_v13_0_12_get_timestamp, - .get_badpage_ipid = smu_v13_0_12_get_badpage_ipid, - .erase_ras_table = smu_v13_0_12_erase_ras_table, -}; - -static void smu_v13_0_12_ras_smu_feature_flags(struct amdgpu_device *adev, uint64_t *flags) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - - if (!flags) - return; - - *flags = 0ULL; - - if (smu_v13_0_6_cap_supported(smu, SMU_CAP(RAS_EEPROM))) - *flags |= RAS_SMU_FEATURE_BIT__RAS_EEPROM; - -} - -const struct ras_smu_drv smu_v13_0_12_ras_smu_drv = { - .smu_eeprom_funcs = &smu_v13_0_12_eeprom_smu_funcs, - .ras_smu_feature_flags = smu_v13_0_12_ras_smu_feature_flags, -}; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c index ba91bf590eed..a9d558f5b739 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c @@ -338,10 +338,10 @@ static int smu_v13_0_4_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->AverageSocketPower << 8) / 1000; + *value = metrics->AverageSocketPower; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c index 27372a60e83d..f95b15af436d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c @@ -295,7 +295,7 @@ static int smu_v13_0_5_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index b12388134489..84c02e6d9673 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -44,8 +44,6 @@ #include "amdgpu_xgmi.h" #include <linux/pci.h> #include "amdgpu_ras.h" -#include "amdgpu_mca.h" -#include "amdgpu_aca.h" #include "smu_cmn.h" #include "mp/mp_13_0_6_offset.h" #include "mp/mp_13_0_6_sh_mask.h" @@ -76,6 +74,9 @@ MODULE_FIRMWARE("amdgpu/smu_13_0_14.bin"); [smu_feature] = { 1, (smu_13_0_6_feature) } #define FEATURE_MASK(feature) (1ULL << feature) + +static int smu_v13_0_6_init_ppt_limits(struct smu_context *smu); + static const struct smu_feature_bits smu_v13_0_6_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_DATA_CALCULATION), @@ -99,25 +100,6 @@ static const struct smu_feature_bits smu_v13_0_6_dpm_features = { #define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK 0xE0 #define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT 0x5 #define LINK_SPEED_MAX 4 -#define MCA_BANK_IPID(_ip, _hwid, _type) \ - [AMDGPU_MCA_IP_##_ip] = { .hwid = _hwid, .mcatype = _type, } - -struct mca_bank_ipid { - enum amdgpu_mca_ip ip; - uint16_t hwid; - uint16_t mcatype; -}; - -struct mca_ras_info { - enum amdgpu_ras_block blkid; - enum amdgpu_mca_ip ip; - int *err_code_array; - int err_code_count; - int (*get_err_count)(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count); - bool (*bank_is_valid)(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry); -}; #define P2S_TABLE_ID_A 0x50325341 #define P2S_TABLE_ID_X 0x50325358 @@ -1171,7 +1153,7 @@ static int smu_v13_0_6_set_default_dpm_table(struct smu_context *smu) } } - return 0; + return smu_v13_0_6_init_ppt_limits(smu); } static int smu_v13_0_6_setup_pptable(struct smu_context *smu) @@ -1321,22 +1303,19 @@ static int smu_v13_0_6_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, version)); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) << 8; + *value = SMUQ10_TO_MILLIWATT(GET_METRIC_FIELD(SocketPower, version)); break; case METRICS_TEMPERATURE_HOTSPOT: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, version)) * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(GET_METRIC_FIELD(MaxSocketTemperature, version)); break; case METRICS_TEMPERATURE_MEM: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxHbmTemperature, version)) * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(GET_METRIC_FIELD(MaxHbmTemperature, version)); break; /* This is the max of all VRs and not just SOC VR. * No need to define another data type for the same. */ case METRICS_TEMPERATURE_VRSOC: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxVrTemperature, version)) * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(GET_METRIC_FIELD(MaxVrTemperature, version)); break; default: *value = UINT_MAX; @@ -1719,54 +1698,82 @@ static int smu_v13_0_6_read_sensor(struct smu_context *smu, return ret; } -static int smu_v13_0_6_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int smu_v13_0_6_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) { - struct smu_table_context *smu_table = &smu->smu_table; - struct PPTable_t *pptable = - (struct PPTable_t *)smu_table->driver_pptable; - uint32_t power_limit = 0; int ret; - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetPptLimit, &power_limit); - + if (limit_type == SMU_PPT_LIMIT_PPT1) { + if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(FAST_PPT))) + return -EOPNOTSUPP; + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetFastPptLimit, + ppt_limit); + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetPptLimit, + ppt_limit); + } if (ret) { dev_err(smu->adev->dev, "Couldn't get PPT limit"); return -EINVAL; } - if (current_power_limit) - *current_power_limit = power_limit; - if (default_power_limit) - *default_power_limit = pptable->MaxSocketPowerLimit; + return 0; +} + +static int smu_v13_0_6_init_ppt_limits(struct smu_context *smu) +{ + struct smu_table_context *smu_table = &smu->smu_table; + struct PPTable_t *pptable = + (struct PPTable_t *)smu_table->driver_pptable; + int i; - if (max_power_limit) { - *max_power_limit = pptable->MaxSocketPowerLimit; + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + pptable->MaxSocketPowerLimit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = + pptable->MaxSocketPowerLimit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = 0; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + pptable->MaxSocketPowerLimit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = 0; + } + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); + + if (smu_v13_0_6_cap_supported(smu, SMU_CAP(FAST_PPT))) { + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].default_value = + pptable->PPT1Default; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].max = + pptable->PPT1Max; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].min = + pptable->PPT1Min; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].od_max = + pptable->PPT1Max; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].od_min = + pptable->PPT1Min; + } + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT1); } - if (min_power_limit) - *min_power_limit = 0; return 0; } -static int smu_v13_0_6_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +static int smu_v13_0_6_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { struct smu_table_context *smu_table = &smu->smu_table; struct PPTable_t *pptable = (struct PPTable_t *)smu_table->driver_pptable; int ret; - if (limit_type == SMU_FAST_PPT_LIMIT) { + if (limit_type == SMU_PPT_LIMIT_PPT1) { if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(FAST_PPT))) return -EOPNOTSUPP; if (limit > pptable->PPT1Max || limit < pptable->PPT1Min) { dev_err(smu->adev->dev, - "New power limit (%d) should be between min %d max %d\n", + "New PPT limit (%d) should be between min %d max %d\n", limit, pptable->PPT1Min, pptable->PPT1Max); return -EINVAL; } @@ -1777,43 +1784,7 @@ static int smu_v13_0_6_set_power_limit(struct smu_context *smu, return ret; } - return smu_v13_0_set_power_limit(smu, limit_type, limit); -} - -static int smu_v13_0_6_get_ppt_limit(struct smu_context *smu, - uint32_t *ppt_limit, - enum smu_ppt_limit_type type, - enum smu_ppt_limit_level level) -{ - struct smu_table_context *smu_table = &smu->smu_table; - struct PPTable_t *pptable = - (struct PPTable_t *)smu_table->driver_pptable; - int ret = 0; - - if (type == SMU_FAST_PPT_LIMIT) { - if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(FAST_PPT))) - return -EOPNOTSUPP; - switch (level) { - case SMU_PPT_LIMIT_MAX: - *ppt_limit = pptable->PPT1Max; - break; - case SMU_PPT_LIMIT_CURRENT: - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetFastPptLimit, ppt_limit); - if (ret) - dev_err(smu->adev->dev, "Get fast PPT limit failed!\n"); - break; - case SMU_PPT_LIMIT_DEFAULT: - *ppt_limit = pptable->PPT1Default; - break; - case SMU_PPT_LIMIT_MIN: - *ppt_limit = pptable->PPT1Min; - break; - default: - return -EOPNOTSUPP; - } - return ret; - } - return -EOPNOTSUPP; + return smu_v13_0_set_ppt_limit(smu, limit_type, limit); } static int smu_v13_0_6_irq_process(struct amdgpu_device *adev, @@ -1943,17 +1914,6 @@ static int smu_v13_0_6_notify_unload(struct smu_context *smu) return 0; } -static int smu_v13_0_6_mca_set_debug_mode(struct smu_context *smu, bool enable) -{ - /* NOTE: this ClearMcaOnRead message is only supported for smu version 85.72.0 or higher */ - if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(MCA_DEBUG_MODE))) - return 0; - - return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_ClearMcaOnRead, - enable ? 0 : ClearMcaOnRead_UE_FLAG_MASK | ClearMcaOnRead_CE_POLL_MASK, - NULL); -} - static int smu_v13_0_6_system_features_control(struct smu_context *smu, bool enable) { @@ -3299,651 +3259,12 @@ static int smu_v13_0_6_post_init(struct smu_context *smu) return 0; } -static int mca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - - return smu_v13_0_6_mca_set_debug_mode(smu, enable); -} - -static int smu_v13_0_6_get_valid_mca_count(struct smu_context *smu, enum amdgpu_mca_error_type type, uint32_t *count) -{ - uint32_t msg; - int ret; - - if (!count) - return -EINVAL; - - switch (type) { - case AMDGPU_MCA_ERROR_TYPE_UE: - msg = SMU_MSG_QueryValidMcaCount; - break; - case AMDGPU_MCA_ERROR_TYPE_CE: - msg = SMU_MSG_QueryValidMcaCeCount; - break; - default: - return -EINVAL; - } - - ret = smu_cmn_send_smc_msg(smu, msg, count); - if (ret) { - *count = 0; - return ret; - } - - return 0; -} - -static int __smu_v13_0_6_mca_dump_bank(struct smu_context *smu, enum amdgpu_mca_error_type type, - int idx, int offset, uint32_t *val) -{ - uint32_t msg, param; - - switch (type) { - case AMDGPU_MCA_ERROR_TYPE_UE: - msg = SMU_MSG_McaBankDumpDW; - break; - case AMDGPU_MCA_ERROR_TYPE_CE: - msg = SMU_MSG_McaBankCeDumpDW; - break; - default: - return -EINVAL; - } - - param = ((idx & 0xffff) << 16) | (offset & 0xfffc); - - return smu_cmn_send_smc_msg_with_param(smu, msg, param, val); -} - -static int smu_v13_0_6_mca_dump_bank(struct smu_context *smu, enum amdgpu_mca_error_type type, - int idx, int offset, uint32_t *val, int count) -{ - int ret, i; - - if (!val) - return -EINVAL; - - for (i = 0; i < count; i++) { - ret = __smu_v13_0_6_mca_dump_bank(smu, type, idx, offset + (i << 2), &val[i]); - if (ret) - return ret; - } - - return 0; -} - -static const struct mca_bank_ipid smu_v13_0_6_mca_ipid_table[AMDGPU_MCA_IP_COUNT] = { - MCA_BANK_IPID(UMC, 0x96, 0x0), - MCA_BANK_IPID(SMU, 0x01, 0x1), - MCA_BANK_IPID(MP5, 0x01, 0x2), - MCA_BANK_IPID(PCS_XGMI, 0x50, 0x0), -}; - -static void mca_bank_entry_info_decode(struct mca_bank_entry *entry, struct mca_bank_info *info) -{ - u64 ipid = entry->regs[MCA_REG_IDX_IPID]; - u32 instidhi, instid; - - /* NOTE: All MCA IPID register share the same format, - * so the driver can share the MCMP1 register header file. - * */ - - info->hwid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, HardwareID); - info->mcatype = REG_GET_FIELD(ipid, MCMP1_IPIDT0, McaType); - - /* - * Unfied DieID Format: SAASS. A:AID, S:Socket. - * Unfied DieID[4] = InstanceId[0] - * Unfied DieID[0:3] = InstanceIdHi[0:3] - */ - instidhi = REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdHi); - instid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdLo); - info->aid = ((instidhi >> 2) & 0x03); - info->socket_id = ((instid & 0x1) << 2) | (instidhi & 0x03); -} - -static int mca_bank_read_reg(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, - int idx, int reg_idx, uint64_t *val) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - uint32_t data[2] = {0, 0}; - int ret; - - if (!val || reg_idx >= MCA_REG_IDX_COUNT) - return -EINVAL; - - ret = smu_v13_0_6_mca_dump_bank(smu, type, idx, reg_idx * 8, data, ARRAY_SIZE(data)); - if (ret) - return ret; - - *val = (uint64_t)data[1] << 32 | data[0]; - - dev_dbg(adev->dev, "mca read bank reg: type:%s, index: %d, reg_idx: %d, val: 0x%016llx\n", - type == AMDGPU_MCA_ERROR_TYPE_UE ? "UE" : "CE", idx, reg_idx, *val); - - return 0; -} - -static int mca_get_mca_entry(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, - int idx, struct mca_bank_entry *entry) -{ - int i, ret; - - /* NOTE: populated all mca register by default */ - for (i = 0; i < ARRAY_SIZE(entry->regs); i++) { - ret = mca_bank_read_reg(adev, type, idx, i, &entry->regs[i]); - if (ret) - return ret; - } - - entry->idx = idx; - entry->type = type; - - mca_bank_entry_info_decode(entry, &entry->info); - - return 0; -} - -static int mca_decode_ipid_to_hwip(uint64_t val) -{ - const struct mca_bank_ipid *ipid; - uint16_t hwid, mcatype; - int i; - - hwid = REG_GET_FIELD(val, MCMP1_IPIDT0, HardwareID); - mcatype = REG_GET_FIELD(val, MCMP1_IPIDT0, McaType); - - for (i = 0; i < ARRAY_SIZE(smu_v13_0_6_mca_ipid_table); i++) { - ipid = &smu_v13_0_6_mca_ipid_table[i]; - - if (!ipid->hwid) - continue; - - if (ipid->hwid == hwid && ipid->mcatype == mcatype) - return i; - } - - return AMDGPU_MCA_IP_UNKNOW; -} - -static int mca_umc_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count) -{ - uint64_t status0; - uint32_t ext_error_code; - uint32_t odecc_err_cnt; - - status0 = entry->regs[MCA_REG_IDX_STATUS]; - ext_error_code = MCA_REG__STATUS__ERRORCODEEXT(status0); - odecc_err_cnt = MCA_REG__MISC0__ERRCNT(entry->regs[MCA_REG_IDX_MISC0]); - - if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) { - *count = 0; - return 0; - } - - if (umc_v12_0_is_deferred_error(adev, status0) || - umc_v12_0_is_uncorrectable_error(adev, status0) || - umc_v12_0_is_correctable_error(adev, status0)) - *count = (ext_error_code == 0) ? odecc_err_cnt : 1; - - amdgpu_umc_update_ecc_status(adev, - entry->regs[MCA_REG_IDX_STATUS], - entry->regs[MCA_REG_IDX_IPID], - entry->regs[MCA_REG_IDX_ADDR]); - - return 0; -} - -static int mca_pcs_xgmi_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, - uint32_t *count) -{ - u32 ext_error_code; - u32 err_cnt; - - ext_error_code = MCA_REG__STATUS__ERRORCODEEXT(entry->regs[MCA_REG_IDX_STATUS]); - err_cnt = MCA_REG__MISC0__ERRCNT(entry->regs[MCA_REG_IDX_MISC0]); - - if (type == AMDGPU_MCA_ERROR_TYPE_UE && - (ext_error_code == 0 || ext_error_code == 9)) - *count = err_cnt; - else if (type == AMDGPU_MCA_ERROR_TYPE_CE && ext_error_code == 6) - *count = err_cnt; - - return 0; -} - -static bool mca_smu_check_error_code(struct amdgpu_device *adev, const struct mca_ras_info *mca_ras, - uint32_t errcode) -{ - int i; - - if (!mca_ras->err_code_count || !mca_ras->err_code_array) - return true; - - for (i = 0; i < mca_ras->err_code_count; i++) { - if (errcode == mca_ras->err_code_array[i]) - return true; - } - - return false; -} - -static int mca_gfx_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count) -{ - uint64_t status0, misc0; - - status0 = entry->regs[MCA_REG_IDX_STATUS]; - if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) { - *count = 0; - return 0; - } - - if (type == AMDGPU_MCA_ERROR_TYPE_UE && - REG_GET_FIELD(status0, MCMP1_STATUST0, UC) == 1 && - REG_GET_FIELD(status0, MCMP1_STATUST0, PCC) == 1) { - *count = 1; - return 0; - } else { - misc0 = entry->regs[MCA_REG_IDX_MISC0]; - *count = REG_GET_FIELD(misc0, MCMP1_MISC0T0, ErrCnt); - } - - return 0; -} - -static int mca_smu_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count) -{ - uint64_t status0, misc0; - - status0 = entry->regs[MCA_REG_IDX_STATUS]; - if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) { - *count = 0; - return 0; - } - - if (type == AMDGPU_MCA_ERROR_TYPE_UE && - REG_GET_FIELD(status0, MCMP1_STATUST0, UC) == 1 && - REG_GET_FIELD(status0, MCMP1_STATUST0, PCC) == 1) { - if (count) - *count = 1; - return 0; - } - - misc0 = entry->regs[MCA_REG_IDX_MISC0]; - *count = REG_GET_FIELD(misc0, MCMP1_MISC0T0, ErrCnt); - - return 0; -} - -static bool mca_gfx_smu_bank_is_valid(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry) -{ - uint32_t instlo; - - instlo = REG_GET_FIELD(entry->regs[MCA_REG_IDX_IPID], MCMP1_IPIDT0, InstanceIdLo); - instlo &= GENMASK(31, 1); - switch (instlo) { - case 0x36430400: /* SMNAID XCD 0 */ - case 0x38430400: /* SMNAID XCD 1 */ - case 0x40430400: /* SMNXCD XCD 0, NOTE: FIXME: fix this error later */ - return true; - default: - return false; - } - - return false; -}; - -static bool mca_smu_bank_is_valid(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - uint32_t errcode, instlo; - - instlo = REG_GET_FIELD(entry->regs[MCA_REG_IDX_IPID], MCMP1_IPIDT0, InstanceIdLo); - instlo &= GENMASK(31, 1); - if (instlo != 0x03b30400) - return false; - - if (smu_v13_0_6_cap_supported(smu, SMU_CAP(ACA_SYND))) { - errcode = MCA_REG__SYND__ERRORINFORMATION(entry->regs[MCA_REG_IDX_SYND]); - errcode &= 0xff; - } else { - errcode = REG_GET_FIELD(entry->regs[MCA_REG_IDX_STATUS], MCMP1_STATUST0, ErrorCode); - } - - return mca_smu_check_error_code(adev, mca_ras, errcode); -} - -static int sdma_err_codes[] = { CODE_SDMA0, CODE_SDMA1, CODE_SDMA2, CODE_SDMA3 }; -static int mmhub_err_codes[] = { - CODE_DAGB0, CODE_DAGB0 + 1, CODE_DAGB0 + 2, CODE_DAGB0 + 3, CODE_DAGB0 + 4, /* DAGB0-4 */ - CODE_EA0, CODE_EA0 + 1, CODE_EA0 + 2, CODE_EA0 + 3, CODE_EA0 + 4, /* MMEA0-4*/ - CODE_VML2, CODE_VML2_WALKER, CODE_MMCANE, -}; - -static int vcn_err_codes[] = { - CODE_VIDD, CODE_VIDV, -}; -static int jpeg_err_codes[] = { - CODE_JPEG0S, CODE_JPEG0D, CODE_JPEG1S, CODE_JPEG1D, - CODE_JPEG2S, CODE_JPEG2D, CODE_JPEG3S, CODE_JPEG3D, - CODE_JPEG4S, CODE_JPEG4D, CODE_JPEG5S, CODE_JPEG5D, - CODE_JPEG6S, CODE_JPEG6D, CODE_JPEG7S, CODE_JPEG7D, -}; - -static const struct mca_ras_info mca_ras_table[] = { - { - .blkid = AMDGPU_RAS_BLOCK__UMC, - .ip = AMDGPU_MCA_IP_UMC, - .get_err_count = mca_umc_mca_get_err_count, - }, { - .blkid = AMDGPU_RAS_BLOCK__GFX, - .ip = AMDGPU_MCA_IP_SMU, - .get_err_count = mca_gfx_mca_get_err_count, - .bank_is_valid = mca_gfx_smu_bank_is_valid, - }, { - .blkid = AMDGPU_RAS_BLOCK__SDMA, - .ip = AMDGPU_MCA_IP_SMU, - .err_code_array = sdma_err_codes, - .err_code_count = ARRAY_SIZE(sdma_err_codes), - .get_err_count = mca_smu_mca_get_err_count, - .bank_is_valid = mca_smu_bank_is_valid, - }, { - .blkid = AMDGPU_RAS_BLOCK__MMHUB, - .ip = AMDGPU_MCA_IP_SMU, - .err_code_array = mmhub_err_codes, - .err_code_count = ARRAY_SIZE(mmhub_err_codes), - .get_err_count = mca_smu_mca_get_err_count, - .bank_is_valid = mca_smu_bank_is_valid, - }, { - .blkid = AMDGPU_RAS_BLOCK__XGMI_WAFL, - .ip = AMDGPU_MCA_IP_PCS_XGMI, - .get_err_count = mca_pcs_xgmi_mca_get_err_count, - }, { - .blkid = AMDGPU_RAS_BLOCK__VCN, - .ip = AMDGPU_MCA_IP_SMU, - .err_code_array = vcn_err_codes, - .err_code_count = ARRAY_SIZE(vcn_err_codes), - .get_err_count = mca_smu_mca_get_err_count, - .bank_is_valid = mca_smu_bank_is_valid, - }, { - .blkid = AMDGPU_RAS_BLOCK__JPEG, - .ip = AMDGPU_MCA_IP_SMU, - .err_code_array = jpeg_err_codes, - .err_code_count = ARRAY_SIZE(jpeg_err_codes), - .get_err_count = mca_smu_mca_get_err_count, - .bank_is_valid = mca_smu_bank_is_valid, - }, -}; - -static const struct mca_ras_info *mca_get_mca_ras_info(struct amdgpu_device *adev, enum amdgpu_ras_block blkid) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mca_ras_table); i++) { - if (mca_ras_table[i].blkid == blkid) - return &mca_ras_table[i]; - } - - return NULL; -} - -static int mca_get_valid_mca_count(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, uint32_t *count) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - int ret; - - switch (type) { - case AMDGPU_MCA_ERROR_TYPE_UE: - case AMDGPU_MCA_ERROR_TYPE_CE: - ret = smu_v13_0_6_get_valid_mca_count(smu, type, count); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static bool mca_bank_is_valid(struct amdgpu_device *adev, const struct mca_ras_info *mca_ras, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry) -{ - if (mca_decode_ipid_to_hwip(entry->regs[MCA_REG_IDX_IPID]) != mca_ras->ip) - return false; - - if (mca_ras->bank_is_valid) - return mca_ras->bank_is_valid(mca_ras, adev, type, entry); - - return true; -} - -static int mca_smu_parse_mca_error_count(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type, - struct mca_bank_entry *entry, uint32_t *count) -{ - const struct mca_ras_info *mca_ras; - - if (!entry || !count) - return -EINVAL; - - mca_ras = mca_get_mca_ras_info(adev, blk); - if (!mca_ras) - return -EOPNOTSUPP; - - if (!mca_bank_is_valid(adev, mca_ras, type, entry)) { - *count = 0; - return 0; - } - - return mca_ras->get_err_count(mca_ras, adev, type, entry, count); -} - -static int mca_smu_get_mca_entry(struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, int idx, struct mca_bank_entry *entry) -{ - return mca_get_mca_entry(adev, type, idx, entry); -} - -static int mca_smu_get_valid_mca_count(struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, uint32_t *count) -{ - return mca_get_valid_mca_count(adev, type, count); -} - -static const struct amdgpu_mca_smu_funcs smu_v13_0_6_mca_smu_funcs = { - .max_ue_count = 12, - .max_ce_count = 12, - .mca_set_debug_mode = mca_smu_set_debug_mode, - .mca_parse_mca_error_count = mca_smu_parse_mca_error_count, - .mca_get_mca_entry = mca_smu_get_mca_entry, - .mca_get_valid_mca_count = mca_smu_get_valid_mca_count, -}; - -static int aca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - - return smu_v13_0_6_mca_set_debug_mode(smu, enable); -} - -static int smu_v13_0_6_get_valid_aca_count(struct smu_context *smu, enum aca_smu_type type, u32 *count) -{ - uint32_t msg; - int ret; - - if (!count) - return -EINVAL; - - switch (type) { - case ACA_SMU_TYPE_UE: - msg = SMU_MSG_QueryValidMcaCount; - break; - case ACA_SMU_TYPE_CE: - msg = SMU_MSG_QueryValidMcaCeCount; - break; - default: - return -EINVAL; - } - - ret = smu_cmn_send_smc_msg(smu, msg, count); - if (ret) { - *count = 0; - return ret; - } - - return 0; -} - -static int aca_smu_get_valid_aca_count(struct amdgpu_device *adev, - enum aca_smu_type type, u32 *count) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - int ret; - - switch (type) { - case ACA_SMU_TYPE_UE: - case ACA_SMU_TYPE_CE: - ret = smu_v13_0_6_get_valid_aca_count(smu, type, count); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int __smu_v13_0_6_aca_bank_dump(struct smu_context *smu, enum aca_smu_type type, - int idx, int offset, u32 *val) -{ - uint32_t msg, param; - - switch (type) { - case ACA_SMU_TYPE_UE: - msg = SMU_MSG_McaBankDumpDW; - break; - case ACA_SMU_TYPE_CE: - msg = SMU_MSG_McaBankCeDumpDW; - break; - default: - return -EINVAL; - } - - param = ((idx & 0xffff) << 16) | (offset & 0xfffc); - - return smu_cmn_send_smc_msg_with_param(smu, msg, param, (uint32_t *)val); -} - -static int smu_v13_0_6_aca_bank_dump(struct smu_context *smu, enum aca_smu_type type, - int idx, int offset, u32 *val, int count) -{ - int ret, i; - - if (!val) - return -EINVAL; - - for (i = 0; i < count; i++) { - ret = __smu_v13_0_6_aca_bank_dump(smu, type, idx, offset + (i << 2), &val[i]); - if (ret) - return ret; - } - - return 0; -} - -static int aca_bank_read_reg(struct amdgpu_device *adev, enum aca_smu_type type, - int idx, int reg_idx, u64 *val) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - u32 data[2] = {0, 0}; - int ret; - - if (!val || reg_idx >= ACA_REG_IDX_COUNT) - return -EINVAL; - - ret = smu_v13_0_6_aca_bank_dump(smu, type, idx, reg_idx * 8, data, ARRAY_SIZE(data)); - if (ret) - return ret; - - *val = (u64)data[1] << 32 | data[0]; - - dev_dbg(adev->dev, "mca read bank reg: type:%s, index: %d, reg_idx: %d, val: 0x%016llx\n", - type == ACA_SMU_TYPE_UE ? "UE" : "CE", idx, reg_idx, *val); - - return 0; -} - -static int aca_smu_get_valid_aca_bank(struct amdgpu_device *adev, - enum aca_smu_type type, int idx, struct aca_bank *bank) -{ - int i, ret, count; - - count = min_t(int, 16, ARRAY_SIZE(bank->regs)); - for (i = 0; i < count; i++) { - ret = aca_bank_read_reg(adev, type, idx, i, &bank->regs[i]); - if (ret) - return ret; - } - - return 0; -} - -static int aca_smu_parse_error_code(struct amdgpu_device *adev, struct aca_bank *bank) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - int error_code; - - if (smu_v13_0_6_cap_supported(smu, SMU_CAP(ACA_SYND))) - error_code = ACA_REG__SYND__ERRORINFORMATION(bank->regs[ACA_REG_IDX_SYND]); - else - error_code = ACA_REG__STATUS__ERRORCODE(bank->regs[ACA_REG_IDX_STATUS]); - - return error_code & 0xff; -} - -static const struct aca_smu_funcs smu_v13_0_6_aca_smu_funcs = { - .max_ue_bank_count = 12, - .max_ce_bank_count = 12, - .set_debug_mode = aca_smu_set_debug_mode, - .get_valid_aca_count = aca_smu_get_valid_aca_count, - .get_valid_aca_bank = aca_smu_get_valid_aca_bank, - .parse_error_code = aca_smu_parse_error_code, -}; - static void smu_v13_0_6_set_temp_funcs(struct smu_context *smu) { smu->smu_temp.temp_funcs = (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 12)) ? &smu_v13_0_12_temp_funcs : NULL; } -static int smu_v13_0_6_get_ras_smu_drv(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv) -{ - if (!ras_smu_drv) - return -EINVAL; - - if (amdgpu_sriov_vf(smu->adev)) - return -EOPNOTSUPP; - - if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_HROM_EN_BIT)) - smu_v13_0_6_cap_set(smu, SMU_CAP(RAS_EEPROM)); - - switch (amdgpu_ip_version(smu->adev, MP1_HWIP, 0)) { - case IP_VERSION(13, 0, 12): - *ras_smu_drv = &smu_v13_0_12_ras_smu_drv; - break; - default: - *ras_smu_drv = NULL; - break; - } - - return 0; -} - static const struct pptable_funcs smu_v13_0_6_ppt_funcs = { /* init dpm */ .init_allowed_features = smu_v13_0_6_init_allowed_features, @@ -3954,7 +3275,7 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = { .force_clk_levels = smu_v13_0_6_force_clk_levels, .read_sensor = smu_v13_0_6_read_sensor, .set_performance_level = smu_v13_0_6_set_performance_level, - .get_power_limit = smu_v13_0_6_get_power_limit, + .get_ppt_limit = smu_v13_0_6_get_ppt_limit, .is_dpm_running = smu_v13_0_6_is_dpm_running, .get_unique_id = smu_v13_0_6_get_unique_id, .init_microcode = smu_v13_0_6_init_microcode, @@ -3972,8 +3293,7 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = { .system_features_control = smu_v13_0_6_system_features_control, .get_enabled_mask = smu_v13_0_6_get_enabled_mask, .feature_is_enabled = smu_cmn_feature_is_enabled, - .set_power_limit = smu_v13_0_6_set_power_limit, - .get_ppt_limit = smu_v13_0_6_get_ppt_limit, + .set_ppt_limit = smu_v13_0_6_set_ppt_limit, .set_xgmi_pstate = smu_v13_0_set_xgmi_pstate, .register_irq_handler = smu_v13_0_6_register_irq_handler, .enable_thermal_alert = smu_v13_0_enable_thermal_alert, @@ -4002,7 +3322,6 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = { .dpm_reset_vcn = smu_v13_0_6_reset_vcn, .post_init = smu_v13_0_6_post_init, .ras_send_msg = smu_v13_0_6_ras_send_msg, - .get_ras_smu_drv = smu_v13_0_6_get_ras_smu_drv, }; void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu) @@ -4020,7 +3339,5 @@ void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu) smu->smc_fw_caps |= SMU_FW_CAP_RAS_PRI; smu_v13_0_init_msg_ctl(smu, message_map); smu_v13_0_6_set_temp_funcs(smu); - amdgpu_mca_smu_init_funcs(smu->adev, &smu_v13_0_6_mca_smu_funcs); - amdgpu_aca_set_smu_funcs(smu->adev, &smu_v13_0_6_aca_smu_funcs); } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h index a150fc88902c..6554780afff7 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h @@ -117,7 +117,6 @@ int smu_v13_0_12_get_system_power(struct smu_context *smu, extern const struct cmn2asic_mapping smu_v13_0_12_feature_mask_map[]; extern const struct cmn2asic_msg_mapping smu_v13_0_12_message_map[]; extern const struct smu_temp_funcs smu_v13_0_12_temp_funcs; -extern const struct ras_smu_drv smu_v13_0_12_ras_smu_drv; #if defined(SWSMU_CODE_LAYER_L2) #include "smu_cmn.h" @@ -145,15 +144,15 @@ extern const struct ras_smu_drv smu_v13_0_12_ras_smu_drv; SMU_SCALAR(SMU_MATTR(ACCUMULATION_COUNTER), SMU_MUNIT(NONE), \ SMU_MTYPE(U64), accumulation_counter); \ SMU_SCALAR(SMU_MATTR(PROCHOT_RESIDENCY_ACC), SMU_MUNIT(NONE), \ - SMU_MTYPE(U32), prochot_residency_acc); \ + SMU_MTYPE(U64), prochot_residency_acc); \ SMU_SCALAR(SMU_MATTR(PPT_RESIDENCY_ACC), SMU_MUNIT(NONE), \ - SMU_MTYPE(U32), ppt_residency_acc); \ + SMU_MTYPE(U64), ppt_residency_acc); \ SMU_SCALAR(SMU_MATTR(SOCKET_THM_RESIDENCY_ACC), SMU_MUNIT(NONE), \ - SMU_MTYPE(U32), socket_thm_residency_acc); \ + SMU_MTYPE(U64), socket_thm_residency_acc); \ SMU_SCALAR(SMU_MATTR(VR_THM_RESIDENCY_ACC), SMU_MUNIT(NONE), \ - SMU_MTYPE(U32), vr_thm_residency_acc); \ + SMU_MTYPE(U64), vr_thm_residency_acc); \ SMU_SCALAR(SMU_MATTR(HBM_THM_RESIDENCY_ACC), SMU_MUNIT(NONE), \ - SMU_MTYPE(U32), hbm_thm_residency_acc); \ + SMU_MTYPE(U64), hbm_thm_residency_acc); \ SMU_SCALAR(SMU_MATTR(GFXCLK_LOCK_STATUS), SMU_MUNIT(NONE), \ SMU_MTYPE(U32), gfxclk_lock_status); \ SMU_SCALAR(SMU_MATTR(PCIE_LINK_WIDTH), SMU_MUNIT(NONE), \ @@ -164,11 +163,11 @@ extern const struct ras_smu_drv smu_v13_0_12_ras_smu_drv; SMU_MTYPE(U16), xgmi_link_width); \ SMU_SCALAR(SMU_MATTR(XGMI_LINK_SPEED), SMU_MUNIT(SPEED_1), \ SMU_MTYPE(U16), xgmi_link_speed); \ - SMU_SCALAR(SMU_MATTR(GFX_ACTIVITY_ACC), SMU_MUNIT(PERCENT), \ - SMU_MTYPE(U32), gfx_activity_acc); \ - SMU_SCALAR(SMU_MATTR(MEM_ACTIVITY_ACC), SMU_MUNIT(PERCENT), \ - SMU_MTYPE(U32), mem_activity_acc); \ - SMU_SCALAR(SMU_MATTR(PCIE_BANDWIDTH_ACC), SMU_MUNIT(PERCENT), \ + SMU_SCALAR(SMU_MATTR(GFX_ACTIVITY_ACC), SMU_MUNIT(NONE), \ + SMU_MTYPE(U64), gfx_activity_acc); \ + SMU_SCALAR(SMU_MATTR(MEM_ACTIVITY_ACC), SMU_MUNIT(NONE), \ + SMU_MTYPE(U64), mem_activity_acc); \ + SMU_SCALAR(SMU_MATTR(PCIE_BANDWIDTH_ACC), SMU_MUNIT(NONE), \ SMU_MTYPE(U64), pcie_bandwidth_acc); \ SMU_SCALAR(SMU_MATTR(PCIE_BANDWIDTH_INST), SMU_MUNIT(BW_1), \ SMU_MTYPE(U64), pcie_bandwidth_inst); \ @@ -179,9 +178,9 @@ extern const struct ras_smu_drv smu_v13_0_12_ras_smu_drv; SMU_SCALAR(SMU_MATTR(PCIE_REPLAY_ROVER_COUNT_ACC), SMU_MUNIT(NONE), \ SMU_MTYPE(U64), pcie_replay_rover_count_acc); \ SMU_SCALAR(SMU_MATTR(PCIE_NAK_SENT_COUNT_ACC), SMU_MUNIT(NONE), \ - SMU_MTYPE(U32), pcie_nak_sent_count_acc); \ + SMU_MTYPE(U64), pcie_nak_sent_count_acc); \ SMU_SCALAR(SMU_MATTR(PCIE_NAK_RCVD_COUNT_ACC), SMU_MUNIT(NONE), \ - SMU_MTYPE(U32), pcie_nak_rcvd_count_acc); \ + SMU_MTYPE(U64), pcie_nak_rcvd_count_acc); \ SMU_ARRAY(SMU_MATTR(XGMI_READ_DATA_ACC), SMU_MUNIT(DATA_1), \ SMU_MTYPE(U64), xgmi_read_data_acc, \ SMU_13_0_6_NUM_XGMI_LINKS); \ @@ -204,7 +203,7 @@ extern const struct ras_smu_drv smu_v13_0_12_ras_smu_drv; SMU_SCALAR(SMU_MATTR(CURRENT_UCLK), SMU_MUNIT(CLOCK_1), \ SMU_MTYPE(U16), current_uclk); \ SMU_SCALAR(SMU_MATTR(PCIE_LC_PERF_OTHER_END_RECOVERY), \ - SMU_MUNIT(NONE), SMU_MTYPE(U32), \ + SMU_MUNIT(NONE), SMU_MTYPE(U64), \ pcie_lc_perf_other_end_recovery); \ SMU_ARRAY(SMU_MATTR(GFX_BUSY_INST), SMU_MUNIT(PERCENT), \ SMU_MTYPE(U32), gfx_busy_inst, SMU_13_0_6_MAX_XCC); \ @@ -212,7 +211,7 @@ extern const struct ras_smu_drv smu_v13_0_12_ras_smu_drv; jpeg_busy, SMU_13_0_6_MAX_JPEG); \ SMU_ARRAY(SMU_MATTR(VCN_BUSY), SMU_MUNIT(PERCENT), SMU_MTYPE(U16), \ vcn_busy, SMU_13_0_6_MAX_VCN); \ - SMU_ARRAY(SMU_MATTR(GFX_BUSY_ACC), SMU_MUNIT(PERCENT), SMU_MTYPE(U64), \ + SMU_ARRAY(SMU_MATTR(GFX_BUSY_ACC), SMU_MUNIT(NONE), SMU_MTYPE(U64), \ gfx_busy_acc, SMU_13_0_6_MAX_XCC); \ SMU_ARRAY(SMU_MATTR(GFX_BELOW_HOST_LIMIT_PPT_ACC), SMU_MUNIT(NONE), \ SMU_MTYPE(U64), gfx_below_host_limit_ppt_acc, \ @@ -257,7 +256,7 @@ void smu_v13_0_12_get_gpu_metrics(struct smu_context *smu, void **table, jpeg_busy, SMU_13_0_6_MAX_JPEG); \ SMU_ARRAY(SMU_MATTR(VCN_BUSY), SMU_MUNIT(PERCENT), SMU_MTYPE(U16), \ vcn_busy, SMU_13_0_6_MAX_VCN); \ - SMU_ARRAY(SMU_MATTR(GFX_BUSY_ACC), SMU_MUNIT(PERCENT), SMU_MTYPE(U64), \ + SMU_ARRAY(SMU_MATTR(GFX_BUSY_ACC), SMU_MUNIT(NONE), SMU_MTYPE(U64), \ gfx_busy_acc, SMU_13_0_6_MAX_XCC); \ SMU_ARRAY(SMU_MATTR(GFX_BELOW_HOST_LIMIT_PPT_ACC), SMU_MUNIT(NONE), \ SMU_MTYPE(U64), gfx_below_host_limit_ppt_acc, \ diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 42c9ceeb4f7d..b94ae43586df 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -62,6 +62,7 @@ static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu, int od_feature_bit, int32_t *min, int32_t *max); +static int smu_v13_0_7_init_ppt_limits(struct smu_context *smu); static const struct smu_feature_bits smu_v13_0_7_dpm_features = { .bits = { @@ -503,7 +504,7 @@ static int smu_v13_0_7_setup_pptable(struct smu_context *smu) if (ret) return ret; - return ret; + return smu_v13_0_7_init_ppt_limits(smu); } static int smu_v13_0_7_tables_init(struct smu_context *smu) @@ -817,7 +818,7 @@ static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu, *value = smu_safe_u16_nn(metrics->AverageUclkActivity); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * @@ -2371,58 +2372,71 @@ static int smu_v13_0_7_enable_mgpu_fan_boost(struct smu_context *smu) NULL); } -static int smu_v13_0_7_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int smu_v13_0_7_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) +{ + OverDriveTableExternal_t od_table; + int ret; + + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; + + ret = smu_v13_0_get_ppt_limit(smu, limit_type, ppt_limit); + if (ret) + return ret; + + ret = smu_v13_0_7_get_overdrive_table(smu, &od_table); + if (ret) + return ret; + + if (od_table.OverDriveTable.Ppt > 0) + *ppt_limit = *ppt_limit * + (100 + od_table.OverDriveTable.Ppt) / 100; + + return 0; +} + +static int smu_v13_0_7_init_ppt_limits(struct smu_context *smu) { struct smu_table_context *table_context = &smu->smu_table; struct smu_13_0_7_powerplay_table *powerplay_table = (struct smu_13_0_7_powerplay_table *)table_context->power_play_table; PPTable_t *pptable = table_context->driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t pp_limit = smu->adev->pm.ac_power ? - skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : - skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); - uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); uint32_t od_percent_upper = 0, od_percent_lower = 0; - int ret; + uint32_t pp_limit, msg_limit, min_limit, max_limit; + int i; - if (current_power_limit) { - ret = smu_v13_0_get_current_power_limit(smu, current_power_limit); - if (ret) - *current_power_limit = pp_limit; + if (powerplay_table && + smu_v13_0_7_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { + od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[ + SMU_13_0_7_ODSETTING_POWERPERCENTAGE]); + od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[ + SMU_13_0_7_ODSETTING_POWERPERCENTAGE]); } - if (default_power_limit) - *default_power_limit = pp_limit; - - if (powerplay_table) { - if (smu->od_enabled && - (smu_v13_0_7_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT))) { - od_percent_upper = le32_to_cpu(powerplay_table->overdrive_table.max[SMU_13_0_7_ODSETTING_POWERPERCENTAGE]); - od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_13_0_7_ODSETTING_POWERPERCENTAGE]); - } else if (smu_v13_0_7_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { - od_percent_upper = 0; - od_percent_lower = le32_to_cpu(powerplay_table->overdrive_table.min[SMU_13_0_7_ODSETTING_POWERPERCENTAGE]); - } + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + pp_limit = i == SMU_POWER_SOURCE_AC ? + skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : + skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][i]; + min_limit = min(pp_limit, msg_limit); + max_limit = max(pp_limit, msg_limit); + + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + pp_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = + max_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = + min_limit * (100 - od_percent_lower) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + max_limit * (100 + od_percent_upper) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min; } - dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, pp_limit); - - if (max_power_limit) { - *max_power_limit = max_limit * (100 + od_percent_upper); - *max_power_limit /= 100; - } - - if (min_power_limit) { - *min_power_limit = min_limit * (100 - od_percent_lower); - *min_power_limit /= 100; - } + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); return 0; } @@ -2574,9 +2588,10 @@ static int smu_v13_0_7_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != SMU_13_0_7_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= SMU_13_0_7_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + SMU_13_0_7_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + SMU_13_0_7_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * SMU_13_0_7_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -2650,23 +2665,29 @@ static bool smu_v13_0_7_wbrf_support_check(struct smu_context *smu) return smu->smc_fw_version > 0x00524600; } -static int smu_v13_0_7_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +static int smu_v13_0_7_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { PPTable_t *pptable = smu->smu_table.driver_pptable; SkuTable_t *skutable = &pptable->SkuTable; - uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t msg_limit = skutable->MsgLimits.Power + [PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; struct smu_table_context *table_context = &smu->smu_table; OverDriveTableExternal_t *od_table = (OverDriveTableExternal_t *)table_context->overdrive_table; + OverDriveTableExternal_t current_od_table; int ret = 0; - if (limit_type != SMU_DEFAULT_PPT_LIMIT) + if (limit_type != SMU_PPT_LIMIT_PPT0) return -EINVAL; if (limit <= msg_limit) { - if (smu->current_power_limit > msg_limit) { + ret = smu_v13_0_7_get_overdrive_table(smu, ¤t_od_table); + if (ret) + return ret; + + if (current_od_table.OverDriveTable.Ppt) { od_table->OverDriveTable.Ppt = 0; od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT; @@ -2676,9 +2697,9 @@ static int smu_v13_0_7_set_power_limit(struct smu_context *smu, return ret; } } - return smu_v13_0_set_power_limit(smu, limit_type, limit); + return smu_v13_0_set_ppt_limit(smu, limit_type, limit); } else if (smu->od_enabled) { - ret = smu_v13_0_set_power_limit(smu, limit_type, msg_limit); + ret = smu_v13_0_set_ppt_limit(smu, limit_type, msg_limit); if (ret) return ret; @@ -2691,7 +2712,6 @@ static int smu_v13_0_7_set_power_limit(struct smu_context *smu, return ret; } - smu->current_power_limit = limit; } else { return -EINVAL; } @@ -2860,8 +2880,8 @@ static const struct pptable_funcs smu_v13_0_7_ppt_funcs = { .get_fan_control_mode = smu_v13_0_get_fan_control_mode, .set_fan_control_mode = smu_v13_0_set_fan_control_mode, .enable_mgpu_fan_boost = smu_v13_0_7_enable_mgpu_fan_boost, - .get_power_limit = smu_v13_0_7_get_power_limit, - .set_power_limit = smu_v13_0_7_set_power_limit, + .get_ppt_limit = smu_v13_0_7_get_ppt_limit, + .set_ppt_limit = smu_v13_0_7_set_ppt_limit, .set_power_source = smu_v13_0_set_power_source, .get_power_profile_mode = smu_v13_0_7_get_power_profile_mode, .set_power_profile_mode = smu_v13_0_7_set_power_profile_mode, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c index 7bf88ffd311b..edc3fedca9f8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c @@ -372,7 +372,7 @@ static int yellow_carp_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c index d0a8df1aa6b6..80dac277d1ae 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c @@ -194,7 +194,7 @@ int smu_v14_0_init_pptable_microcode(struct smu_context *smu) if (!pptable_id) return 0; - ret = smu_v14_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -229,48 +229,6 @@ int smu_v14_0_check_fw_status(struct smu_context *smu) return -EIO; } -static int smu_v14_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t ppt_offset_bytes; - const struct smc_firmware_header_v2_0 *v2; - - v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; - - ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); - *size = le32_to_cpu(v2->ppt_size_bytes); - *table = (uint8_t *)v2 + ppt_offset_bytes; - - return 0; -} - -static int smu_v14_0_set_pptable_v2_1(struct smu_context *smu, void **table, - uint32_t *size, uint32_t pptable_id) -{ - struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v2_1 *v2_1; - struct smc_soft_pptable_entry *entries; - uint32_t pptable_count = 0; - int i = 0; - - v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; - entries = (struct smc_soft_pptable_entry *) - ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); - pptable_count = le32_to_cpu(v2_1->pptable_count); - for (i = 0; i < pptable_count; i++) { - if (le32_to_cpu(entries[i].id) == pptable_id) { - *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); - *size = le32_to_cpu(entries[i].ppt_size_bytes); - break; - } - } - - if (i == pptable_count) - return -EINVAL; - - return 0; -} - static int smu_v14_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size) { struct amdgpu_device *adev = smu->adev; @@ -293,45 +251,6 @@ static int smu_v14_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl return 0; } -int smu_v14_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id) -{ - const struct smc_firmware_header_v1_0 *hdr; - struct amdgpu_device *adev = smu->adev; - uint16_t version_major, version_minor; - int ret; - - hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; - if (!hdr) - return -EINVAL; - - dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); - - version_major = le16_to_cpu(hdr->header.header_version_major); - version_minor = le16_to_cpu(hdr->header.header_version_minor); - if (version_major != 2) { - dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", - version_major, version_minor); - return -EINVAL; - } - - switch (version_minor) { - case 0: - ret = smu_v14_0_set_pptable_v2_0(smu, table, size); - break; - case 1: - ret = smu_v14_0_set_pptable_v2_1(smu, table, size, pptable_id); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - int smu_v14_0_setup_pptable(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; @@ -351,7 +270,7 @@ int smu_v14_0_setup_pptable(struct smu_context *smu) if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1)) ret = smu_v14_0_get_pptable_from_vbios(smu, &table, &size); else - ret = smu_v14_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -748,14 +667,17 @@ int smu_v14_0_notify_display_change(struct smu_context *smu) return ret; } -int smu_v14_0_get_current_power_limit(struct smu_context *smu, - uint32_t *power_limit) +int smu_v14_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) { int power_src; int ret = 0; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) return -EINVAL; + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; power_src = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_PWR, @@ -768,35 +690,33 @@ int smu_v14_0_get_current_power_limit(struct smu_context *smu, ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetPptLimit, power_src << 16, - power_limit); + ppt_limit); if (ret) dev_err(smu->adev->dev, "[%s] get PPT limit failed!", __func__); return ret; } -int smu_v14_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +int smu_v14_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { int ret = 0; - if (limit_type != SMU_DEFAULT_PPT_LIMIT) + if (limit_type != SMU_PPT_LIMIT_PPT0) return -EINVAL; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) { - dev_err(smu->adev->dev, "Setting new power limit is not supported!\n"); + dev_err(smu->adev->dev, "Setting new PPT limit is not supported!\n"); return -EOPNOTSUPP; } ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetPptLimit, limit, NULL); if (ret) { - dev_err(smu->adev->dev, "[%s] Set power limit Failed!\n", __func__); + dev_err(smu->adev->dev, "[%s] Set PPT limit failed!\n", __func__); return ret; } - smu->current_power_limit = limit; - return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index 75719c47a41e..c83c58d5a2e2 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -260,6 +260,35 @@ static int smu_v14_0_0_system_features_control(struct smu_context *smu, bool en) return ret; } +/** + * smu_v14_0_0_find_clk_level - find the closest DPM level for a frequency + * @freqs: array of frequencies (one per DPM level) + * @count: number of valid entries in @freqs + * @target: the frequency to match + * + * Returns the index of the DPM level whose frequency is closest to @target. + * If an exact match exists it is preferred; otherwise the nearest level by + * absolute difference is returned. + */ +static uint8_t smu_v14_0_0_find_clk_level(const uint32_t *freqs, uint8_t count, + uint32_t target) +{ + uint8_t i, closest = 0; + uint32_t best_diff = U32_MAX; + + for (i = 0; i < count; i++) { + uint32_t diff = abs((int)target - (int)freqs[i]); + + if (diff < best_diff) { + best_diff = diff; + closest = i; + } + if (freqs[i] == target) + return i; + } + return closest; +} + static int smu_v14_0_0_get_smu_metrics_data(struct smu_context *smu, MetricsMember_t member, uint32_t *value) @@ -284,7 +313,30 @@ static int smu_v14_0_0_get_smu_metrics_data(struct smu_context *smu, *value = metrics->VclkFrequency; break; case METRICS_AVERAGE_DCLK: + /* + * SmuMetrics_t has no DclkFrequency field. DCLK and VCLK + * share the same DPM level count, so find the DPM level + * whose VCLK matches the reported VclkFrequency and return + * the DCLK frequency at that level. + */ *value = 0; + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(14, 0, 1)) { + DpmClocks_t_v14_0_1 *clk_table = smu->smu_table.clocks_table; + uint8_t lvl = smu_v14_0_0_find_clk_level( + clk_table->VClocks0, + clk_table->Vcn0ClkLevelsEnabled, + metrics->VclkFrequency); + + *value = clk_table->DClocks0[lvl]; + } else { + DpmClocks_t *clk_table = smu->smu_table.clocks_table; + uint8_t lvl = smu_v14_0_0_find_clk_level( + clk_table->VClocks, + clk_table->VcnClkLevelsEnabled, + metrics->VclkFrequency); + + *value = clk_table->DClocks[lvl]; + } break; case METRICS_AVERAGE_UCLK: *value = metrics->MemclkFrequency; @@ -312,8 +364,7 @@ static int smu_v14_0_0_get_smu_metrics_data(struct smu_context *smu, break; case METRICS_AVERAGE_SOCKETPOWER: case METRICS_CURR_SOCKETPOWER: - *value = (metrics->SocketPower / 1000 << 8) + - (metrics->SocketPower % 1000 / 10); + *value = metrics->SocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * @@ -683,6 +734,11 @@ static int smu_v14_0_1_get_dpm_freq_by_index(struct smu_context *smu, return -EINVAL; *freq = clk_table->FclkClocks_Freq[dpm_level]; break; + case SMU_DCEFCLK: + if (dpm_level >= clk_table->NumDcfClkLevelsEnabled) + return -EINVAL; + *freq = clk_table->DcfClocks[dpm_level]; + break; default: return -EINVAL; } @@ -727,6 +783,11 @@ static int smu_v14_0_0_get_dpm_freq_by_index(struct smu_context *smu, return -EINVAL; *freq = clk_table->FclkClocks_Freq[dpm_level]; break; + case SMU_DCEFCLK: + if (dpm_level >= clk_table->NumDcfClkLevelsEnabled) + return -EINVAL; + *freq = clk_table->DcfClocks[dpm_level]; + break; default: return -EINVAL; } @@ -1090,6 +1151,9 @@ static int smu_v14_0_1_get_dpm_level_count(struct smu_context *smu, case SMU_FCLK: *count = clk_table->NumFclkLevelsEnabled; break; + case SMU_DCEFCLK: + *count = clk_table->NumDcfClkLevelsEnabled; + break; default: break; } @@ -1119,6 +1183,9 @@ static int smu_v14_0_0_get_dpm_level_count(struct smu_context *smu, case SMU_FCLK: *count = clk_table->NumFclkLevelsEnabled; break; + case SMU_DCEFCLK: + *count = clk_table->NumDcfClkLevelsEnabled; + break; default: break; } @@ -1175,14 +1242,41 @@ static int smu_v14_0_0_emit_clk_levels(struct smu_context *smu, if (ret) return ret; + /* + * Build a frequency table and use find_clk_level() to + * locate the closest DPM level. The SMU often reports + * time-averaged frequencies that do not match any DPM + * entry exactly. + */ + { + uint32_t freqs[NUM_SOCCLK_DPM_LEVELS]; + int active; + + for (i = 0; i < count; i++) { + idx = (clk_type == SMU_MCLK) ? (count - i - 1) : i; + ret = smu_v14_0_common_get_dpm_freq_by_index(smu, clk_type, idx, &freqs[i]); + if (ret) + return ret; + } + + active = smu_v14_0_0_find_clk_level(freqs, count, cur_value); + + for (i = 0; i < count; i++) + size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", + i, freqs[i], + i == active ? "*" : ""); + } + break; + case SMU_DCEFCLK: + ret = smu_v14_0_common_get_dpm_level_count(smu, clk_type, &count); + if (ret) + return ret; + for (i = 0; i < count; i++) { - idx = (clk_type == SMU_MCLK) ? (count - i - 1) : i; - ret = smu_v14_0_common_get_dpm_freq_by_index(smu, clk_type, idx, &value); + ret = smu_v14_0_common_get_dpm_freq_by_index(smu, clk_type, i, &value); if (ret) return ret; - - size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i, value, - cur_value == value ? "*" : ""); + size += sysfs_emit_at(buf, size, "%d: %uMhz\n", i, value); } break; case SMU_GFXCLK: @@ -1701,6 +1795,50 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu) return 0; } +/* + * Link any xHCI controller sharing the GPU's PCIe root port as a consumer + * of the GPU so the GPU resumes first, avoiding an xHCI resume race. + */ +static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool enable) +{ + struct amdgpu_device *adev = smu->adev; + struct pci_dev *gpu_pdev = adev->pdev; + struct pci_dev *root_port, *usb_pdev = NULL; + struct device_link *link; + + if (!enable) { + if (smu->usb_power_link) { + device_link_del(smu->usb_power_link); + smu->usb_power_link = NULL; + } + return 0; + } + + root_port = pcie_find_root_port(gpu_pdev); + while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, usb_pdev))) { + struct pci_dev *usb_root; + + usb_root = pcie_find_root_port(usb_pdev); + if (usb_root != root_port) + continue; + + /* Create device link: USB (consumer) depends on GPU (supplier) */ + link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME); + if (link) { + smu->usb_power_link = link; + drm_info(adev_to_drm(adev), "USB controller %s D0 power state depends on %s\n", + pci_name(usb_pdev), pci_name(gpu_pdev)); + /* Only create one link for the first USB controller found */ + break; + } + } + + pci_dev_put(usb_pdev); + + return 0; +} + static const struct pptable_funcs smu_v14_0_0_ppt_funcs = { .check_fw_status = smu_v14_0_check_fw_status, .check_fw_version = smu_cmn_check_fw_version, @@ -1734,6 +1872,7 @@ static const struct pptable_funcs smu_v14_0_0_ppt_funcs = { .dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable, .get_dpm_clock_table = smu_v14_0_common_get_dpm_table, .set_mall_enable = smu_v14_0_common_set_mall_enable, + .set_power_dep = smu_v14_0_0_set_power_dep, }; static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index fdc1456b885c..56a5c11bc196 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -59,6 +59,9 @@ static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu, int od_feature_bit, int32_t *min, int32_t *max); +static int smu_v14_0_2_init_ppt_limits(struct smu_context *smu); +static int smu_v14_0_2_get_overdrive_table(struct smu_context *smu, + OverDriveTableExternal_t *od_table); static const struct smu_feature_bits smu_v14_0_2_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_DPM_GFXCLK_BIT), @@ -370,7 +373,7 @@ static int smu_v14_0_2_setup_pptable(struct smu_context *smu) if (ret) return ret; - return ret; + return smu_v14_0_2_init_ppt_limits(smu); } static int smu_v14_0_2_tables_init(struct smu_context *smu) @@ -695,7 +698,7 @@ static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu, metrics->Vcn1ActivityPercentage); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * @@ -1610,11 +1613,32 @@ static int smu_v14_0_2_get_fan_speed_rpm(struct smu_context *smu, speed); } -static int smu_v14_0_2_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int smu_v14_0_2_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) +{ + OverDriveTableExternal_t od_table; + int ret; + + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; + + ret = smu_v14_0_get_ppt_limit(smu, limit_type, ppt_limit); + if (ret) + return ret; + + ret = smu_v14_0_2_get_overdrive_table(smu, &od_table); + if (ret) + return ret; + + if (od_table.OverDriveTable.Ppt > 0) + *ppt_limit = *ppt_limit * + (100 + od_table.OverDriveTable.Ppt) / 100; + + return 0; +} + +static int smu_v14_0_2_init_ppt_limits(struct smu_context *smu) { struct smu_table_context *table_context = &smu->smu_table; struct smu_14_0_2_powerplay_table *powerplay_table = @@ -1622,42 +1646,36 @@ static int smu_v14_0_2_get_power_limit(struct smu_context *smu, PPTable_t *pptable = table_context->driver_pptable; CustomSkuTable_t *skutable = &pptable->CustomSkuTable; int16_t od_percent_upper = 0, od_percent_lower = 0; - uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; - uint32_t power_limit; - - if (smu_v14_0_get_current_power_limit(smu, &power_limit)) - power_limit = smu->adev->pm.ac_power ? - skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : - skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; - - if (current_power_limit) - *current_power_limit = power_limit; - if (default_power_limit) - *default_power_limit = power_limit; - - if (powerplay_table) { - if (smu->od_enabled && - smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { - od_percent_upper = pptable->SkuTable.OverDriveLimitsBasicMax.Ppt; - od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt; - } else if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { - od_percent_upper = 0; - od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt; - } - } - - dev_dbg(smu->adev->dev, "od percent upper:%d, od percent lower:%d (default power: %d)\n", - od_percent_upper, od_percent_lower, power_limit); - - if (max_power_limit) { - *max_power_limit = msg_limit * (100 + od_percent_upper); - *max_power_limit /= 100; - } - - if (min_power_limit) { - *min_power_limit = power_limit * (100 + od_percent_lower); - *min_power_limit /= 100; - } + uint32_t pp_limit, msg_limit, min_limit, max_limit; + int i; + + if (powerplay_table && + smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_PPT_BIT)) { + od_percent_upper = pptable->SkuTable.OverDriveLimitsBasicMax.Ppt; + od_percent_lower = pptable->SkuTable.OverDriveLimitsBasicMin.Ppt; + } + + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + pp_limit = i == SMU_POWER_SOURCE_AC ? + skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : + skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; + msg_limit = pptable->SkuTable.MsgLimits.Power + [PPT_THROTTLER_PPT0][i]; + min_limit = min(pp_limit, msg_limit); + max_limit = max(pp_limit, msg_limit); + + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + pp_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = max_limit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = + min_limit * (100 + od_percent_lower) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + max_limit * (100 + od_percent_upper) / 100; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min; + } + + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); return 0; } @@ -1828,9 +1846,10 @@ static int smu_v14_0_2_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != SMU_14_0_2_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + SMU_14_0_2_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * SMU_14_0_2_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -2784,22 +2803,28 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu, return ret; } -static int smu_v14_0_2_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +static int smu_v14_0_2_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { PPTable_t *pptable = smu->smu_table.driver_pptable; - uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power + [PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; struct smu_table_context *table_context = &smu->smu_table; OverDriveTableExternal_t *od_table = (OverDriveTableExternal_t *)table_context->overdrive_table; + OverDriveTableExternal_t current_od_table; int ret = 0; - if (limit_type != SMU_DEFAULT_PPT_LIMIT) + if (limit_type != SMU_PPT_LIMIT_PPT0) return -EINVAL; if (limit <= msg_limit) { - if (smu->current_power_limit > msg_limit) { + ret = smu_v14_0_2_get_overdrive_table(smu, ¤t_od_table); + if (ret) + return ret; + + if (current_od_table.OverDriveTable.Ppt) { od_table->OverDriveTable.Ppt = 0; od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT; @@ -2809,9 +2834,9 @@ static int smu_v14_0_2_set_power_limit(struct smu_context *smu, return ret; } } - return smu_v14_0_set_power_limit(smu, limit_type, limit); + return smu_v14_0_set_ppt_limit(smu, limit_type, limit); } else if (smu->od_enabled) { - ret = smu_v14_0_set_power_limit(smu, limit_type, msg_limit); + ret = smu_v14_0_set_ppt_limit(smu, limit_type, msg_limit); if (ret) return ret; @@ -2824,7 +2849,6 @@ static int smu_v14_0_2_set_power_limit(struct smu_context *smu, return ret; } - smu->current_power_limit = limit; } else { return -EINVAL; } @@ -2878,8 +2902,8 @@ static const struct pptable_funcs smu_v14_0_2_ppt_funcs = { .get_unique_id = smu_v14_0_2_get_unique_id, .get_fan_speed_pwm = smu_v14_0_2_get_fan_speed_pwm, .get_fan_speed_rpm = smu_v14_0_2_get_fan_speed_rpm, - .get_power_limit = smu_v14_0_2_get_power_limit, - .set_power_limit = smu_v14_0_2_set_power_limit, + .get_ppt_limit = smu_v14_0_2_get_ppt_limit, + .set_ppt_limit = smu_v14_0_2_set_ppt_limit, .get_power_profile_mode = smu_v14_0_2_get_power_profile_mode, .set_power_profile_mode = smu_v14_0_2_set_power_profile_mode, .run_btc = smu_v14_0_run_btc, @@ -2889,8 +2913,6 @@ static const struct pptable_funcs smu_v14_0_2_ppt_funcs = { .deep_sleep_control = smu_v14_0_deep_sleep_control, .gfx_ulv_control = smu_v14_0_gfx_ulv_control, .get_bamaco_support = smu_v14_0_get_bamaco_support, - .baco_get_state = smu_v14_0_baco_get_state, - .baco_set_state = smu_v14_0_baco_set_state, .baco_enter = smu_v14_0_2_baco_enter, .baco_exit = smu_v14_0_2_baco_exit, .mode1_reset_is_support = smu_v14_0_2_is_mode1_reset_supported, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c index a1318409e4b5..4c0236196de4 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c @@ -174,7 +174,7 @@ int smu_v15_0_init_pptable_microcode(struct smu_context *smu) if (!pptable_id) return 0; - ret = smu_v15_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -207,48 +207,6 @@ int smu_v15_0_check_fw_status(struct smu_context *smu) return -EIO; } -static int smu_v15_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t ppt_offset_bytes; - const struct smc_firmware_header_v2_0 *v2; - - v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; - - ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); - *size = le32_to_cpu(v2->ppt_size_bytes); - *table = (uint8_t *)v2 + ppt_offset_bytes; - - return 0; -} - -static int smu_v15_0_set_pptable_v2_1(struct smu_context *smu, void **table, - uint32_t *size, uint32_t pptable_id) -{ - struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v2_1 *v2_1; - struct smc_soft_pptable_entry *entries; - uint32_t pptable_count = 0; - int i = 0; - - v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; - entries = (struct smc_soft_pptable_entry *) - ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); - pptable_count = le32_to_cpu(v2_1->pptable_count); - for (i = 0; i < pptable_count; i++) { - if (le32_to_cpu(entries[i].id) == pptable_id) { - *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); - *size = le32_to_cpu(entries[i].ppt_size_bytes); - break; - } - } - - if (i == pptable_count) - return -EINVAL; - - return 0; -} - static int smu_v15_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size) { struct amdgpu_device *adev = smu->adev; @@ -271,45 +229,6 @@ static int smu_v15_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl return 0; } -int smu_v15_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id) -{ - const struct smc_firmware_header_v1_0 *hdr; - struct amdgpu_device *adev = smu->adev; - uint16_t version_major, version_minor; - int ret; - - hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; - if (!hdr) - return -EINVAL; - - dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); - - version_major = le16_to_cpu(hdr->header.header_version_major); - version_minor = le16_to_cpu(hdr->header.header_version_minor); - if (version_major != 2) { - dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", - version_major, version_minor); - return -EINVAL; - } - - switch (version_minor) { - case 0: - ret = smu_v15_0_set_pptable_v2_0(smu, table, size); - break; - case 1: - ret = smu_v15_0_set_pptable_v2_1(smu, table, size, pptable_id); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - int smu_v15_0_setup_pptable(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; @@ -329,7 +248,7 @@ int smu_v15_0_setup_pptable(struct smu_context *smu) if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1)) ret = smu_v15_0_get_pptable_from_vbios(smu, &table, &size); else - ret = smu_v15_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -664,6 +583,8 @@ int smu_v15_0_gfx_off_control(struct smu_context *smu, bool enable) switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 5): + case IP_VERSION(15, 0, 9): if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) return 0; if (enable) @@ -698,14 +619,17 @@ int smu_v15_0_notify_display_change(struct smu_context *smu) return ret; } -int smu_v15_0_get_current_power_limit(struct smu_context *smu, - uint32_t *power_limit) +int smu_v15_0_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) { int power_src; int ret = 0; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) return -EINVAL; + if (limit_type != SMU_PPT_LIMIT_PPT0) + return -EOPNOTSUPP; power_src = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_PWR, @@ -718,35 +642,33 @@ int smu_v15_0_get_current_power_limit(struct smu_context *smu, ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetPptLimit, power_src << 16, - power_limit); + ppt_limit); if (ret) dev_err(smu->adev->dev, "[%s] get PPT limit failed!", __func__); return ret; } -int smu_v15_0_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +int smu_v15_0_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { int ret = 0; - if (limit_type != SMU_DEFAULT_PPT_LIMIT) + if (limit_type != SMU_PPT_LIMIT_PPT0) return -EINVAL; if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_PPT_BIT)) { - dev_err(smu->adev->dev, "Setting new power limit is not supported!\n"); + dev_err(smu->adev->dev, "Setting new PPT limit is not supported!\n"); return -EOPNOTSUPP; } ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetPptLimit, limit, NULL); if (ret) { - dev_err(smu->adev->dev, "[%s] Set power limit Failed!\n", __func__); + dev_err(smu->adev->dev, "[%s] Set PPT limit failed!\n", __func__); return ret; } - smu->current_power_limit = limit; - return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c index a214ddbd4c86..cbfeb9736e6c 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c @@ -33,6 +33,9 @@ #include "smu_v15_0_0_pmfw.h" #include "smu_cmn.h" +#include <linux/overflow.h> +#include <linux/math64.h> + /* * DO NOT use these for err/warn/info/debug messages. * Use dev_err, dev_warn, dev_info and dev_dbg instead. @@ -119,6 +122,9 @@ static struct cmn2asic_msg_mapping smu_v15_0_0_message_map[SMU_MSG_MAX_COUNT] = MSG_MAP(DisableLSdma, PPSMC_MSG_DisableLSdma, 1), MSG_MAP(SetSoftMaxVpe, PPSMC_MSG_SetSoftMaxVpe, 1), MSG_MAP(SetSoftMinVpe, PPSMC_MSG_SetSoftMinVpe, 1), + MSG_MAP(GetMetricsTableVersion, PPSMC_MSG_GetMetricsTableVersion, 1), + MSG_MAP(GetMetricsTableLogSample, PPSMC_MSG_GetMetricsTableLogSample, 1), + MSG_MAP(GetMetricsTableLogDramAddr, PPSMC_MSG_GetMetricsTableLogDramAddr, 1), }; static struct cmn2asic_mapping smu_v15_0_0_feature_mask_map[SMU_FEATURE_COUNT] = { @@ -160,25 +166,52 @@ static struct cmn2asic_mapping smu_v15_0_0_table_map[SMU_TABLE_COUNT] = { TAB_MAP_VALID(DPMCLOCKS), }; +static int smu_v15_0_0_get_metrics_table_dram_addr(struct smu_context *smu, + SMU_15_0_0_MetricsInfo_t *metrics_info) +{ + struct smu_msg_ctl *ctl = &smu->msg_ctl; + struct smu_msg_args args = { + .msg = SMU_MSG_GetMetricsTableLogDramAddr, + .num_args = 0, + .num_out_args = 3, + }; + int ret; + + ret = ctl->ops->send_msg(ctl, &args); + if (ret) + return ret; + + metrics_info->addr = ((uint64_t)args.out_args[1] << 32) | args.out_args[0]; + metrics_info->table_size = args.out_args[2]; + + metrics_info->cpu_addr = ioremap_wc(metrics_info->addr, + metrics_info->table_size); + if (!metrics_info->cpu_addr) + return -ENOMEM; + + return 0; +} + static int smu_v15_0_0_init_smc_tables(struct smu_context *smu) { struct smu_table_context *smu_table = &smu->smu_table; struct smu_table *tables = smu_table->tables; + SMU_15_0_0_MetricsInfo_t *metrics_info; int ret; SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); - SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t), + SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t_v15_0_5), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); - smu_table->metrics_table = kzalloc_obj(SmuMetrics_t); + smu_table->metrics_table = kzalloc_obj(SMU_15_0_0_MetricsInfo_t); if (!smu_table->metrics_table) goto err0_out; smu_table->metrics_time = 0; - smu_table->clocks_table = kzalloc_obj(DpmClocks_t); + smu_table->clocks_table = kzalloc_obj(DpmClocks_t_v15_0_5); if (!smu_table->clocks_table) goto err1_out; @@ -192,8 +225,16 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu) if (ret) goto err3_out; + metrics_info = (SMU_15_0_0_MetricsInfo_t *)smu_table->metrics_table; + + ret = smu_v15_0_0_get_metrics_table_dram_addr(smu, metrics_info); + if (ret) + goto err4_out; + return 0; +err4_out: + smu_driver_table_fini(smu, SMU_DRIVER_TABLE_GPU_METRICS); err3_out: kfree(smu_table->watermarks_table); err2_out: @@ -207,6 +248,14 @@ err0_out: static int smu_v15_0_0_fini_smc_tables(struct smu_context *smu) { struct smu_table_context *smu_table = &smu->smu_table; + SMU_15_0_0_MetricsInfo_t *metrics_info = smu_table->metrics_table; + + if (metrics_info) { + if (metrics_info->cpu_addr) { + iounmap(metrics_info->cpu_addr); + metrics_info->cpu_addr = NULL; + } + } kfree(smu_table->clocks_table); smu_table->clocks_table = NULL; @@ -299,7 +348,7 @@ static int smu_v15_0_0_set_default_dpm_tables(struct smu_context *smu) smu_table->clocks_table, false); } -static int smu_v15_0_0_get_metrics_table(struct smu_context *smu, +static int smu_v15_0_0_get_gpu_metrics_table(struct smu_context *smu, void *metrics_table, bool bypass_cache) { @@ -329,115 +378,161 @@ static int smu_v15_0_0_get_metrics_table(struct smu_context *smu, return 0; } +/* + * Fetch a fresh metrics sample into the inactive buffer. + * Returns 0 if a new sample was copied, 1 if the cached sample is still + * fresh (nothing copied), or a negative errno on failure. + */ +static int smu_v15_0_0_get_metrics_table(struct smu_context *smu, + SMU_15_0_0_MetricsInfo_t *metrics_info) +{ + struct smu_table_context *smu_table = &smu->smu_table; + void __iomem *cpu_addr = metrics_info->cpu_addr; + struct smu_msg_ctl *ctl = &smu->msg_ctl; + struct smu_msg_args args = {0}; + size_t table_size = metrics_info->table_size; + int ret; + + if (smu_table->metrics_time && + !time_after(jiffies, smu_table->metrics_time + msecs_to_jiffies(1))) + return 1; + + if (!cpu_addr) + return -ENOMEM; + + args.msg = SMU_MSG_GetMetricsTableLogSample; + args.num_args = 0; + args.num_out_args = 0; + + ret = ctl->ops->send_msg(ctl, &args); + if (ret) + return ret; + + /* best to flush before copy */ + amdgpu_hdp_invalidate(smu->adev, NULL); + if (table_size <= sizeof(MetricsTable_t)) + memcpy_fromio(&metrics_info->metrics[!metrics_info->active_idx], + cpu_addr, table_size); + else + memcpy_fromio(&metrics_info->metrics[!metrics_info->active_idx], + cpu_addr, sizeof(MetricsTable_t)); + + metrics_info->active_idx = !metrics_info->active_idx; + smu_table->metrics_time = jiffies; + + return 0; +} + +/* + * Accumulators monotonically increase and roll over at their type width. + * Use the kernel wrapping_sub() API to compute the delta so the subtraction + * wraps modulo 2^n (correct across a single rollover) without tripping any + * wrap-around sanitizers. + */ +static void smu_v15_0_0_compute_all_metrics( + uint32_t *avg_metric, + MetricsTable_t *prev, + MetricsTable_t *curr) +{ + uint64_t counter, val; + uint32_t mw; + MetricsTable_IOD_t *p = &prev->IOD; + MetricsTable_IOD_t *c = &curr->IOD; + + counter = wrapping_sub(u32, c->AccumulationCounter, p->AccumulationCounter); + if (!counter) + return; + + /* Accumulator-based clock frequencies (fixed-point /1024) */ + val = wrapping_sub(u64, c->GfxclkFreqEffAcc, p->GfxclkFreqEffAcc); + avg_metric[METRICS_AVERAGE_GFXCLK] = div_u64(div64_u64(val, counter), 1024); + + val = wrapping_sub(u64, c->SocclkFreqEffAcc, p->SocclkFreqEffAcc); + avg_metric[METRICS_AVERAGE_SOCCLK] = div_u64(div64_u64(val, counter), 1024); + + val = wrapping_sub(u64, c->VclkFreqEffAcc, p->VclkFreqEffAcc); + avg_metric[METRICS_AVERAGE_VCLK] = div_u64(div64_u64(val, counter), 1024); + + val = wrapping_sub(u64, c->MemclkFreqEffAcc, p->MemclkFreqEffAcc); + avg_metric[METRICS_AVERAGE_UCLK] = div_u64(div64_u64(val, counter), 1024); + + val = wrapping_sub(u64, c->FclkFreqEffAcc, p->FclkFreqEffAcc); + avg_metric[METRICS_AVERAGE_FCLK] = div_u64(div64_u64(val, counter), 1024); + + val = wrapping_sub(u64, c->NpuhclkFreqEffAcc, p->NpuhclkFreqEffAcc); + avg_metric[METRICS_AVERAGE_NPUCLK] = div_u64(div64_u64(val, counter), 1024); + + /* Activity (fixed-point /1024) */ + val = wrapping_sub(u64, c->GfxBusyAcc, p->GfxBusyAcc); + avg_metric[METRICS_AVERAGE_GFXACTIVITY] = div_u64(div64_u64(val, counter), 1024); + + val = wrapping_sub(u64, c->VcnBusyAcc, p->VcnBusyAcc); + avg_metric[METRICS_AVERAGE_VCNACTIVITY] = div_u64(div64_u64(val, counter), 1024); + + /* + * Power: accumulator holds a 1024x fixed-point value in Watts. + * Average it into milliwatts, which is the unit expected by + * power sensor consumers (hwmon/debugfs). + */ + val = wrapping_sub(u64, c->ApuPowerAcc, p->ApuPowerAcc); + mw = div_u64(div64_u64(val, counter) * 1000, 1024); + avg_metric[METRICS_AVERAGE_SOCKETPOWER] = mw; + + val = wrapping_sub(u64, c->SystemPowerAcc, p->SystemPowerAcc); + mw = div_u64(div64_u64(val, counter) * 1000, 1024); + avg_metric[METRICS_CURR_SOCKETPOWER] = mw; + + /* + * Temperature: accumulator holds a 1024x fixed-point value in + * Celsius. Descale by 1024 and convert to millidegrees C as the + * hwmon/sysfs consumers expect (temp*_input is in millidegrees). + */ + val = wrapping_sub(u64, c->GFX_TempAcc, p->GFX_TempAcc); + avg_metric[METRICS_TEMPERATURE_VRGFX] = + div_u64(div64_u64(val, counter) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES, 1024); + + val = wrapping_sub(u64, c->STT_APU_HotSpotTempAcc, p->STT_APU_HotSpotTempAcc); + avg_metric[METRICS_TEMPERATURE_HOTSPOT] = + div_u64(div64_u64(val, counter) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES, 1024); + + /* Voltage: accumulator holds a 1024x fixed-point value in Volts; + * convert to millivolts for the hwmon/sysfs consumers. + */ + val = wrapping_sub(u64, c->VDDCR_GFX_TelemetryVoltage, p->VDDCR_GFX_TelemetryVoltage); + avg_metric[METRICS_VOLTAGE_VDDGFX] = div_u64(div64_u64(val, counter) * 1000, 1024); + + val = wrapping_sub(u64, c->VDDCR_SOC_TelemetryVoltage, p->VDDCR_SOC_TelemetryVoltage); + avg_metric[METRICS_VOLTAGE_VDDSOC] = div_u64(div64_u64(val, counter) * 1000, 1024); +} + static int smu_v15_0_0_get_smu_metrics_data(struct smu_context *smu, MetricsMember_t member, uint32_t *value) { struct smu_table_context *smu_table = &smu->smu_table; + SMU_15_0_0_MetricsInfo_t *metrics_info = + (SMU_15_0_0_MetricsInfo_t *)smu_table->metrics_table; + int ret; - SmuMetrics_t *metrics = (SmuMetrics_t *)smu_table->metrics_table; - int ret = 0; + if (member >= ARRAY_SIZE(metrics_info->avg_metric)) + return -EINVAL; - ret = smu_v15_0_0_get_metrics_table(smu, NULL, false); - if (ret) + ret = smu_v15_0_0_get_metrics_table(smu, metrics_info); + if (ret < 0) return ret; - switch (member) { - case METRICS_AVERAGE_GFXCLK: - *value = metrics->GfxclkFrequency; - break; - case METRICS_AVERAGE_SOCCLK: - *value = metrics->SocclkFrequency; - break; - case METRICS_AVERAGE_VCLK: - *value = metrics->VclkFrequency; - break; - case METRICS_AVERAGE_DCLK: - *value = 0; - break; - case METRICS_AVERAGE_UCLK: - *value = metrics->MemclkFrequency; - break; - case METRICS_AVERAGE_FCLK: - *value = metrics->FclkFrequency; - break; - case METRICS_AVERAGE_VPECLK: - *value = metrics->VpeclkFrequency; - break; - case METRICS_AVERAGE_NPUCLK: - *value = metrics->NpuclkFrequency; - break; - case METRICS_AVERAGE_GFXACTIVITY: - if ((smu->smc_fw_version > 0x5d4600)) - *value = metrics->GfxActivity; - else - *value = metrics->GfxActivity / 100; - break; - case METRICS_AVERAGE_VCNACTIVITY: - *value = metrics->VcnActivity / 100; - break; - case METRICS_AVERAGE_SOCKETPOWER: - case METRICS_CURR_SOCKETPOWER: - *value = (metrics->SocketPower / 1000 << 8) + - (metrics->SocketPower % 1000 / 10); - break; - case METRICS_TEMPERATURE_EDGE: - *value = metrics->GfxTemperature / 100 * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; - break; - case METRICS_TEMPERATURE_HOTSPOT: - *value = metrics->SocTemperature / 100 * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; - break; - case METRICS_THROTTLER_RESIDENCY_PROCHOT: - *value = metrics->ThrottleResidency_PROCHOT; - break; - case METRICS_THROTTLER_RESIDENCY_SPL: - *value = metrics->ThrottleResidency_SPL; - break; - case METRICS_THROTTLER_RESIDENCY_FPPT: - *value = metrics->ThrottleResidency_FPPT; - break; - case METRICS_THROTTLER_RESIDENCY_SPPT: - *value = metrics->ThrottleResidency_SPPT; - break; - case METRICS_THROTTLER_RESIDENCY_THM_SOC: - *value = metrics->ThrottleResidency_THM_SOC; - break; - case METRICS_VOLTAGE_VDDGFX: - *value = 0; - break; - case METRICS_VOLTAGE_VDDSOC: - *value = 0; - break; - case METRICS_SS_APU_SHARE: - /* return the percentage of APU power with respect to APU's power limit. - * percentage is reported, this isn't boost value. Smartshift power - * boost/shift is only when the percentage is more than 100. - */ - if (metrics->StapmOpnLimit > 0) - *value = (metrics->ApuPower * 100) / metrics->StapmOpnLimit; - else - *value = 0; - break; - case METRICS_SS_DGPU_SHARE: - /* return the percentage of dGPU power with respect to dGPU's power limit. - * percentage is reported, this isn't boost value. Smartshift power - * boost/shift is only when the percentage is more than 100. - */ - if ((metrics->dGpuPower > 0) && - (metrics->StapmCurrentLimit > metrics->StapmOpnLimit)) - *value = (metrics->dGpuPower * 100) / - (metrics->StapmCurrentLimit - metrics->StapmOpnLimit); - else - *value = 0; - break; - default: - *value = UINT_MAX; - break; - } + if (ret == 0 && + metrics_info->metrics[metrics_info->active_idx].IOD.AccumulationCounter != + metrics_info->metrics[!metrics_info->active_idx].IOD.AccumulationCounter) { + /* New sample: active_idx already points to the latest sample. */ + smu_v15_0_0_compute_all_metrics(metrics_info->avg_metric, + &metrics_info->metrics[!metrics_info->active_idx], + &metrics_info->metrics[metrics_info->active_idx]); + } - return ret; + *value = metrics_info->avg_metric[member]; + + return 0; } static int smu_v15_0_0_read_sensor(struct smu_context *smu, @@ -474,9 +569,9 @@ static int smu_v15_0_0_read_sensor(struct smu_context *smu, (uint32_t *)data); *size = 4; break; - case AMDGPU_PP_SENSOR_EDGE_TEMP: + case AMDGPU_PP_SENSOR_GPU_TEMP: ret = smu_v15_0_0_get_smu_metrics_data(smu, - METRICS_TEMPERATURE_EDGE, + METRICS_TEMPERATURE_VRGFX, (uint32_t *)data); *size = 4; break; @@ -512,18 +607,6 @@ static int smu_v15_0_0_read_sensor(struct smu_context *smu, (uint32_t *)data); *size = 4; break; - case AMDGPU_PP_SENSOR_SS_APU_SHARE: - ret = smu_v15_0_0_get_smu_metrics_data(smu, - METRICS_SS_APU_SHARE, - (uint32_t *)data); - *size = 4; - break; - case AMDGPU_PP_SENSOR_SS_DGPU_SHARE: - ret = smu_v15_0_0_get_smu_metrics_data(smu, - METRICS_SS_DGPU_SHARE, - (uint32_t *)data); - *size = 4; - break; default: ret = -EOPNOTSUPP; break; @@ -634,7 +717,7 @@ static ssize_t smu_v15_0_0_get_gpu_metrics(struct smu_context *smu, SmuMetrics_t metrics; int ret = 0; - ret = smu_v15_0_0_get_metrics_table(smu, &metrics, false); + ret = smu_v15_0_0_get_gpu_metrics_table(smu, &metrics, false); if (ret) return ret; @@ -752,11 +835,48 @@ static int smu_v15_0_0_get_dpm_freq_by_index(struct smu_context *smu, return 0; } +static int smu_v15_0_5_get_dpm_freq_by_index(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t dpm_level, + uint32_t *freq) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + if (!clk_table || clk_type >= SMU_CLK_COUNT) + return -EINVAL; + + switch (clk_type) { + case SMU_SOCCLK: + if (dpm_level >= clk_table->NumSocClkLevelsEnabled) + return -EINVAL; + *freq = clk_table->SocClocks[dpm_level]; + break; + case SMU_UCLK: + case SMU_MCLK: + if (dpm_level >= clk_table->NumMemPstatesEnabled) + return -EINVAL; + *freq = clk_table->MemPstateTable[dpm_level].MemClk; + break; + case SMU_FCLK: + if (dpm_level >= clk_table->NumFclkLevelsEnabled) + return -EINVAL; + *freq = clk_table->FclkClocks_Freq[dpm_level]; + break; + default: + return -EINVAL; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_freq_by_index(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t dpm_level, uint32_t *freq) { + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq); + else smu_v15_0_0_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq); return 0; @@ -907,12 +1027,116 @@ failed: return ret; } +static int smu_v15_0_5_get_dpm_ultimate_freq(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *min, + uint32_t *max) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + uint32_t clock_limit; + uint32_t max_dpm_level, min_dpm_level; + int ret = 0; + + if (!smu_v15_0_0_clk_dpm_is_enabled(smu, clk_type)) { + switch (clk_type) { + case SMU_MCLK: + case SMU_UCLK: + clock_limit = smu->smu_table.boot_values.uclk; + break; + case SMU_FCLK: + clock_limit = smu->smu_table.boot_values.fclk; + break; + case SMU_GFXCLK: + case SMU_SCLK: + clock_limit = smu->smu_table.boot_values.gfxclk; + break; + case SMU_SOCCLK: + clock_limit = smu->smu_table.boot_values.socclk; + break; + default: + clock_limit = 0; + break; + } + + /* clock in Mhz unit */ + if (min) + *min = clock_limit / 100; + if (max) + *max = clock_limit / 100; + + return 0; + } + + if (max) { + switch (clk_type) { + case SMU_GFXCLK: + case SMU_SCLK: + *max = clk_table->MaxGfxClk; + break; + case SMU_MCLK: + case SMU_UCLK: + max_dpm_level = 0; + break; + case SMU_FCLK: + max_dpm_level = clk_table->NumFclkLevelsEnabled - 1; + break; + case SMU_SOCCLK: + max_dpm_level = clk_table->NumSocClkLevelsEnabled - 1; + break; + default: + ret = -EINVAL; + goto failed; + } + + if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) { + ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type, + max_dpm_level, max); + if (ret) + goto failed; + } + } + + if (min) { + switch (clk_type) { + case SMU_GFXCLK: + case SMU_SCLK: + *min = clk_table->MinGfxClk; + break; + case SMU_MCLK: + case SMU_UCLK: + min_dpm_level = clk_table->NumMemPstatesEnabled - 1; + break; + case SMU_FCLK: + min_dpm_level = 0; + break; + case SMU_SOCCLK: + min_dpm_level = 0; + break; + default: + ret = -EINVAL; + goto failed; + } + + if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) { + ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type, + min_dpm_level, min); + if (ret) + goto failed; + } + } + +failed: + return ret; +} + static int smu_v15_0_common_get_dpm_ultimate_freq(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *min, uint32_t *max) { - if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_ultimate_freq(smu, clk_type, min, max); + else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) smu_v15_0_0_get_dpm_ultimate_freq(smu, clk_type, min, max); return 0; @@ -986,11 +1210,36 @@ static int smu_v15_0_0_get_dpm_level_count(struct smu_context *smu, return 0; } +static int smu_v15_0_5_get_dpm_level_count(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *count) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + switch (clk_type) { + case SMU_SOCCLK: + *count = clk_table->NumSocClkLevelsEnabled; + break; + case SMU_MCLK: + *count = clk_table->NumMemPstatesEnabled; + break; + case SMU_FCLK: + *count = clk_table->NumFclkLevelsEnabled; + break; + default: + break; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_level_count(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *count) { - if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_level_count(smu, clk_type, count); + else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) smu_v15_0_0_get_dpm_level_count(smu, clk_type, count); return 0; @@ -1177,7 +1426,8 @@ static int smu_v15_0_common_get_dpm_profile_freq(struct smu_context *smu, smu_v15_0_common_get_dpm_ultimate_freq(smu, SMU_SOCCLK, NULL, &clk_limit); break; case SMU_FCLK: - if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 0)) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 0) || + amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 9)) smu_v15_0_common_get_dpm_ultimate_freq(smu, SMU_FCLK, NULL, &clk_limit); else clk_limit = SMU_15_0_UMD_PSTATE_FCLK; @@ -1362,9 +1612,24 @@ static int smu_v15_0_0_set_fine_grain_gfx_freq_parameters(struct smu_context *sm return 0; } +static int smu_v15_0_5_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + smu->gfx_default_hard_min_freq = clk_table->MinGfxClk; + smu->gfx_default_soft_max_freq = clk_table->MaxGfxClk; + smu->gfx_actual_hard_min_freq = 0; + smu->gfx_actual_soft_max_freq = 0; + + return 0; +} + static int smu_v15_0_common_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) { - smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu); + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_set_fine_grain_gfx_freq_parameters(smu); + else + smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu); return 0; } @@ -1404,9 +1669,36 @@ static int smu_v15_0_0_get_dpm_table(struct smu_context *smu, struct dpm_clocks return 0; } +static int smu_v15_0_5_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + uint8_t idx; + + /* + * Only the Clock information of SOC and + * VPE is copied to provide VPE DPM settings for use. + */ + for (idx = 0; idx < NUM_SOCCLK_DPM_LEVELS; idx++) { + clock_table->SocClocks[idx].Freq = + (idx < clk_table->NumSocClkLevelsEnabled) ? clk_table->SocClocks[idx]:0; + clock_table->SocClocks[idx].Vol = 0; + } + + for (idx = 0; idx < NUM_VPE_DPM_LEVELS; idx++) { + clock_table->VPEClocks[idx].Freq = + (idx < clk_table->VpeClkLevelsEnabled) ? clk_table->VPEClocks[idx]:0; + clock_table->VPEClocks[idx].Vol = 0; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table) { - smu_v15_0_0_get_dpm_table(smu, clock_table); + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_table(smu, clock_table); + else + smu_v15_0_0_get_dpm_table(smu, clock_table); return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.h index 8365b9fea77e..8471ba1a353e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.h @@ -23,6 +23,19 @@ #ifndef __SMU_V15_0_0_PPT_H__ #define __SMU_V15_0_0_PPT_H__ +#include "amdgpu_smu.h" + +#include "pmfw_if/smu_v15_0_0_metrics.h" + +typedef struct { + MetricsTable_t metrics[2]; + bool active_idx; + uint32_t table_size; + uint64_t addr; + void __iomem *cpu_addr; + uint32_t avg_metric[METRICS_AVERAGE_NPUCLK+1]; +} SMU_15_0_0_MetricsInfo_t; + extern void smu_v15_0_0_set_ppt_funcs(struct smu_context *smu); #endif
\ No newline at end of file diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c index 2105a1d7bb34..4f820db5f8dd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c @@ -55,6 +55,13 @@ #define SMUQ10_TO_UINT(x) ((x) >> 10) #define SMUQ10_FRAC(x) ((x) & 0x3ff) #define SMUQ10_ROUND(x) ((SMUQ10_TO_UINT(x)) + ((SMUQ10_FRAC(x)) >= 0x200)) +/* Convert Q10 watts to milliwatts, preserving the fractional part */ +#define SMUQ10_TO_MILLIWATT(x) (SMUQ10_TO_UINT(x) * MILLIWATT_PER_WATT + \ + ((SMUQ10_FRAC(x) * MILLIWATT_PER_WATT) >> 10)) +/* Convert Q10 degrees Celsius to millidegrees, preserving the fractional part */ +#define SMUQ10_TO_MILLICELSIUS(x) \ + (SMUQ10_TO_UINT(x) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES + \ + ((SMUQ10_FRAC(x) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES) >> 10)) #define hbm_stack_mask_valid(umc_mask) \ (((umc_mask) & 0xF) == 0xF) @@ -72,6 +79,8 @@ #define FEATURE_MASK(feature) (1ULL << feature) +static int smu_v15_0_8_init_ppt_limits(struct smu_context *smu); + static const struct smu_feature_bits smu_v15_0_8_dpm_features = { .bits = { SMU_FEATURE_BIT_INIT(FEATURE_ID_DATA_CALCULATION), SMU_FEATURE_BIT_INIT(FEATURE_ID_DPM_GFXCLK), @@ -213,7 +222,7 @@ static int smu_v15_0_8_tables_init(struct smu_context *smu) smu_table->metrics_time = 0; - driver_pptable = kzalloc(sizeof(PPTable_t), GFP_KERNEL); + driver_pptable = kzalloc_obj(PPTable_t); if (!driver_pptable) return -ENOMEM; @@ -411,11 +420,10 @@ static int smu_v15_0_8_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(metrics->DramBandwidthUtilization); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(metrics->SocketPower) << 8; + *value = SMUQ10_TO_MILLIWATT(metrics->SocketPower); break; case METRICS_TEMPERATURE_HOTSPOT: - *value = SMUQ10_ROUND(metrics->MaxSocketTemperature) * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(metrics->MaxSocketTemperature); break; case METRICS_TEMPERATURE_MEM: { @@ -433,19 +441,18 @@ static int smu_v15_0_8_get_smu_metrics_data(struct smu_context *smu, if (!hbm_stack_mask_valid(mask)) continue; - temp = SMUQ10_ROUND(metrics->HbmTemperature[stack_idx]); + temp = metrics->HbmTemperature[stack_idx]; if (temp > max_hbm_temp) max_hbm_temp = temp; } } - *value = max_hbm_temp * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(max_hbm_temp); break; } /* This is the max of all VRs and not just SOC VR. */ case METRICS_TEMPERATURE_VRSOC: - *value = SMUQ10_ROUND(metrics->MaxVrTemperature) * - SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + *value = SMUQ10_TO_MILLICELSIUS(metrics->MaxVrTemperature); break; default: *value = UINT_MAX; @@ -1019,7 +1026,7 @@ static int smu_v15_0_8_fru_get_product_info(struct smu_context *smu, struct amdgpu_device *adev = smu->adev; if (!adev->fru_info) { - adev->fru_info = kzalloc(sizeof(*adev->fru_info), GFP_KERNEL); + adev->fru_info = kzalloc_obj(*adev->fru_info); if (!adev->fru_info) return -ENOMEM; } @@ -1152,7 +1159,7 @@ static int smu_v15_0_8_set_default_dpm_table(struct smu_context *smu) if (ret) return ret; - return 0; + return smu_v15_0_8_init_ppt_limits(smu); } static int smu_v15_0_8_irq_process(struct amdgpu_device *adev, @@ -1766,12 +1773,17 @@ static ssize_t smu_v15_0_8_get_gpu_metrics(struct smu_context *smu, void **table idx++; } - /* Per-VCN clocks */ + /* Per-VCN clocks and busy */ for (i = 0; i < adev->vcn.num_vcn_inst; ++i) { inst = GET_INST(VCN, i); if (inst >= 0) { gpu_metrics->current_vclk0[i] = SMUQ10_ROUND(metrics->VclkFrequency[inst]); gpu_metrics->current_dclk0[i] = SMUQ10_ROUND(metrics->DclkFrequency[inst]); + gpu_metrics->vcn_busy[i] = SMUQ10_ROUND(metrics->VcnBusy[inst]); + for (j = 0; j < NUM_JPEG_RINGS_FW; ++j) + gpu_metrics->jpeg_busy[(i * NUM_JPEG_RINGS_FW) + j] = + SMUQ10_ROUND(metrics->JpegBusy[(inst * NUM_JPEG_RINGS_FW) + + j]); } } @@ -1842,34 +1854,59 @@ static void smu_v15_0_8_get_unique_id(struct smu_context *smu) adev->unique_id = pptable->PublicSerialNumberMID; } -static int smu_v15_0_8_get_power_limit(struct smu_context *smu, - uint32_t *current_power_limit, - uint32_t *default_power_limit, - uint32_t *max_power_limit, - uint32_t *min_power_limit) +static int smu_v15_0_8_get_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t *ppt_limit) { - struct smu_table_context *smu_table = &smu->smu_table; - PPTable_t *pptable = (PPTable_t *)smu_table->driver_pptable; - uint32_t power_limit = 0; int ret; - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetPptLimit, &power_limit); + if (limit_type == SMU_PPT_LIMIT_PPT1) + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetFastPptLimit, + ppt_limit); + else + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetPptLimit, + ppt_limit); if (ret) { dev_err(smu->adev->dev, "Couldn't get PPT limit"); return -EINVAL; } - if (current_power_limit) - *current_power_limit = power_limit; - - if (default_power_limit) - *default_power_limit = pptable->MaxSocketPowerLimit; - - if (max_power_limit) - *max_power_limit = pptable->MaxSocketPowerLimit; + return 0; +} - if (min_power_limit) - *min_power_limit = 0; +static int smu_v15_0_8_init_ppt_limits(struct smu_context *smu) +{ + struct smu_table_context *smu_table = &smu->smu_table; + PPTable_t *pptable = (PPTable_t *)smu_table->driver_pptable; + int i; + + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].default_value = + pptable->MaxSocketPowerLimit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].max = + pptable->MaxSocketPowerLimit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].min = 0; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_max = + pptable->MaxSocketPowerLimit; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT0].od_min = 0; + } + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT0); + + if (pptable->PPT1Max) { + for (i = SMU_POWER_SOURCE_AC; i < SMU_POWER_SOURCE_COUNT; i++) { + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].default_value = + pptable->PPT1Default; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].max = + pptable->PPT1Max; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].min = + pptable->PPT1Min; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].od_max = + pptable->PPT1Max; + smu->ppt_limits.range[i][SMU_PPT_LIMIT_PPT1].od_min = + pptable->PPT1Min; + } + smu->ppt_limits.supported_mask |= BIT(SMU_PPT_LIMIT_PPT1); + } return 0; } @@ -1914,7 +1951,7 @@ static int smu_v15_0_8_set_performance_level(struct smu_context *smu, struct smu_dpm_table *gfx_table = &dpm_context->dpm_tables.gfx_table; struct smu_dpm_table *uclk_table = &dpm_context->dpm_tables.uclk_table; struct smu_umd_pstate_table *pstate_table = &smu->pstate_table; - int ret; + int ret = 0; switch (level) { case AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM: @@ -1955,9 +1992,6 @@ static int smu_v15_0_8_set_performance_level(struct smu_context *smu, SMU_DPM_TABLE_MAX(uclk_table); } - if (ret) - goto out; - smu_cmn_reset_custom_level(smu); break; @@ -2207,15 +2241,15 @@ static int smu_v15_0_8_get_thermal_temperature_range(struct smu_context *smu, return 0; } -static int smu_v15_0_8_set_power_limit(struct smu_context *smu, - enum smu_ppt_limit_type limit_type, - uint32_t limit) +static int smu_v15_0_8_set_ppt_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit) { struct smu_table_context *smu_table = &smu->smu_table; PPTable_t *pptable = (PPTable_t *)smu_table->driver_pptable; int ret; - if (limit_type == SMU_FAST_PPT_LIMIT) { + if (limit_type == SMU_PPT_LIMIT_PPT1) { if (!pptable->PPT1Max) return -EOPNOTSUPP; @@ -2234,49 +2268,7 @@ static int smu_v15_0_8_set_power_limit(struct smu_context *smu, return ret; } - return smu_v15_0_set_power_limit(smu, limit_type, limit); -} - -static int smu_v15_0_8_get_ppt_limit(struct smu_context *smu, - uint32_t *ppt_limit, - enum smu_ppt_limit_type type, - enum smu_ppt_limit_level level) -{ - struct smu_table_context *smu_table = &smu->smu_table; - PPTable_t *pptable = (PPTable_t *)smu_table->driver_pptable; - int ret = 0; - - if (!ppt_limit) - return -EINVAL; - - if (type == SMU_FAST_PPT_LIMIT) { - if (!pptable->PPT1Max) - return -EOPNOTSUPP; - - switch (level) { - case SMU_PPT_LIMIT_MAX: - *ppt_limit = pptable->PPT1Max; - break; - case SMU_PPT_LIMIT_CURRENT: - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetFastPptLimit, - ppt_limit); - if (ret) - dev_err(smu->adev->dev, - "Get fast PPT limit failed!\n"); - break; - case SMU_PPT_LIMIT_DEFAULT: - *ppt_limit = pptable->PPT1Default; - break; - case SMU_PPT_LIMIT_MIN: - *ppt_limit = pptable->PPT1Min; - break; - default: - return -EOPNOTSUPP; - } - return ret; - } - - return -EOPNOTSUPP; + return smu_v15_0_set_ppt_limit(smu, limit_type, limit); } static uint32_t smu_v15_0_8_get_throttler_status(struct smu_context *smu) @@ -2373,9 +2365,8 @@ static const struct pptable_funcs smu_v15_0_8_ppt_funcs = { .get_dpm_ultimate_freq = smu_v15_0_8_get_dpm_ultimate_freq, .get_gpu_metrics = smu_v15_0_8_get_gpu_metrics, .get_unique_id = smu_v15_0_8_get_unique_id, - .get_power_limit = smu_v15_0_8_get_power_limit, - .set_power_limit = smu_v15_0_8_set_power_limit, .get_ppt_limit = smu_v15_0_8_get_ppt_limit, + .set_ppt_limit = smu_v15_0_8_set_ppt_limit, .emit_clk_levels = smu_v15_0_8_emit_clk_levels, .read_sensor = smu_v15_0_8_read_sensor, .populate_umd_state_clk = smu_v15_0_8_populate_umd_state_clk, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h index 4dfc40aaffcb..46fce0a79c21 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h @@ -125,7 +125,7 @@ typedef struct { SMU_ARRAY(SMU_MATTR(PCIE_BANDWIDTH_ACC), SMU_MUNIT(NONE), \ SMU_MTYPE(U64), pcie_bandwidth_acc, SMU_15_0_8_MAX_MID); \ SMU_ARRAY(SMU_MATTR(PCIE_BANDWIDTH_INST), SMU_MUNIT(BW_1), \ - SMU_MTYPE(U32), pcie_bandwidth_inst, SMU_15_0_8_MAX_MID); \ + SMU_MTYPE(U64), pcie_bandwidth_inst, SMU_15_0_8_MAX_MID); \ SMU_SCALAR(SMU_MATTR(PCIE_L0_TO_RECOV_COUNT_ACC), SMU_MUNIT(NONE), \ SMU_MTYPE(U64), pcie_l0_to_recov_count_acc); \ SMU_SCALAR(SMU_MATTR(PCIE_REPLAY_COUNT_ACC), SMU_MUNIT(NONE), \ diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index d365f06ac1ac..2bd3ea17e789 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -1544,3 +1544,143 @@ int smu_cmn_dpm_pcie_width_idx(int width) return ret; } + +static int smu_cmn_get_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) +{ + const struct smc_firmware_header_v2_0 *v2; + struct amdgpu_device *adev = smu->adev; + size_t fw_size = adev->pm.fw->size; + uint32_t ppt_offset_bytes; + uint32_t ppt_size_bytes; + + if (fw_size < sizeof(*v2)) { + dev_err(adev->dev, + "SMC firmware too small for v2.0 header: %zu < %zu\n", + fw_size, sizeof(*v2)); + return -EINVAL; + } + + v2 = (const struct smc_firmware_header_v2_0 *)adev->pm.fw->data; + + ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); + ppt_size_bytes = le32_to_cpu(v2->ppt_size_bytes); + + if (ppt_offset_bytes > fw_size || + ppt_size_bytes > fw_size - ppt_offset_bytes) { + dev_err(adev->dev, + "pptable v2.0 exceeds firmware binary: offset %u + size %u > %zu\n", + ppt_offset_bytes, ppt_size_bytes, fw_size); + return -EINVAL; + } + + *size = ppt_size_bytes; + *table = (uint8_t *)v2 + ppt_offset_bytes; + + return 0; +} + +static int smu_cmn_get_pptable_v2_1(struct smu_context *smu, void **table, + uint32_t *size, uint32_t pptable_id) +{ + const struct smc_firmware_header_v2_1 *v2_1; + struct amdgpu_device *adev = smu->adev; + struct smc_soft_pptable_entry *entries; + size_t fw_size = adev->pm.fw->size; + uint32_t pptable_entry_offset; + uint32_t ppt_offset_bytes; + uint32_t ppt_size_bytes; + uint32_t pptable_count; + int i; + + if (fw_size < sizeof(*v2_1)) { + dev_err(adev->dev, + "SMC firmware too small for v2.1 header: %zu < %zu\n", + fw_size, sizeof(*v2_1)); + return -EINVAL; + } + + v2_1 = (const struct smc_firmware_header_v2_1 *)adev->pm.fw->data; + + pptable_entry_offset = le32_to_cpu(v2_1->pptable_entry_offset); + pptable_count = le32_to_cpu(v2_1->pptable_count); + + if (pptable_entry_offset > fw_size || + pptable_count > (fw_size - pptable_entry_offset) / sizeof(*entries)) { + dev_err(adev->dev, + "pptable v2.1 entry array exceeds firmware binary: offset %u, count %u\n", + pptable_entry_offset, pptable_count); + return -EINVAL; + } + + entries = (struct smc_soft_pptable_entry *) + ((uint8_t *)v2_1 + pptable_entry_offset); + + for (i = 0; i < pptable_count; i++) { + if (le32_to_cpu(entries[i].id) != pptable_id) + continue; + + ppt_offset_bytes = le32_to_cpu(entries[i].ppt_offset_bytes); + ppt_size_bytes = le32_to_cpu(entries[i].ppt_size_bytes); + + if (ppt_offset_bytes > fw_size || + ppt_size_bytes > fw_size - ppt_offset_bytes) { + dev_err(adev->dev, + "pptable entry %d exceeds firmware binary: offset %u + size %u > %zu\n", + i, ppt_offset_bytes, ppt_size_bytes, fw_size); + return -EINVAL; + } + + *table = (uint8_t *)v2_1 + ppt_offset_bytes; + *size = ppt_size_bytes; + return 0; + } + + return -EINVAL; +} + +/** + * smu_cmn_get_pptable_from_firmware - locate the soft pptable embedded in the + * SMC firmware binary. + * @smu: SMU context + * @table: on success, set to the start of the pptable within the firmware + * blob + * @size: on success, set to the pptable size in bytes + * @pptable_id: the entry ID to search for (used only for v2.1 binaries) + * + * Reads the firmware header version and dispatches to the appropriate v2.x + * parser. Only major version 2 is supported; minor version selects between + * the single-entry (v2.0) and multi-entry directory (v2.1) layouts. + * + * Return: 0 on success, -EINVAL for an unsupported version or if the + * requested pptable cannot be found or exceeds the binary bounds. + */ +int smu_cmn_get_pptable_from_firmware(struct smu_context *smu, void **table, + uint32_t *size, uint32_t pptable_id) +{ + const struct smc_firmware_header_v1_0 *hdr; + struct amdgpu_device *adev = smu->adev; + uint16_t version_major, version_minor; + + hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data; + if (!hdr) + return -EINVAL; + + dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); + + version_major = le16_to_cpu(hdr->header.header_version_major); + version_minor = le16_to_cpu(hdr->header.header_version_minor); + if (version_major != 2) { + dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", + version_major, version_minor); + return -EINVAL; + } + + switch (version_minor) { + case 0: + return smu_cmn_get_pptable_v2_0(smu, table, size); + case 1: + return smu_cmn_get_pptable_v2_1(smu, table, size, pptable_id); + default: + return -EINVAL; + } +} diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h index 0e119965ce13..ae6742f5298f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h @@ -113,6 +113,16 @@ static inline int pcie_gen_to_speed(uint32_t gen) return ((gen == 0) ? link_speed[0] : link_speed[gen - 1]); } +static inline bool smu_cmn_custom_params_count_valid(u32 max_idx, u32 params_count) +{ + return max_idx == params_count; +} + +static inline bool smu_cmn_custom_params_clock_valid(long clock_idx, long clock_count) +{ + return clock_idx >= 0 && clock_idx < clock_count; +} + int smu_cmn_send_smc_msg_with_param(struct smu_context *smu, enum smu_message_type msg, uint32_t param, @@ -239,6 +249,9 @@ int smu_cmn_dpm_pcie_gen_idx(int gen); int smu_cmn_dpm_pcie_width_idx(int width); int smu_cmn_check_fw_version(struct smu_context *smu); +int smu_cmn_get_pptable_from_firmware(struct smu_context *smu, void **table, + uint32_t *size, uint32_t pptable_id); + /*SMU gpu metrics */ /* Attribute ID mapping */ diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h b/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h index 24848da90234..6244f8895668 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h @@ -82,7 +82,8 @@ #define smu_i2c_fini(smu) smu_ppt_funcs(i2c_fini, 0, smu) #define smu_get_unique_id(smu) smu_ppt_funcs(get_unique_id, 0, smu) #define smu_log_thermal_throttling(smu) smu_ppt_funcs(log_thermal_throttling_event, 0, smu) -#define smu_get_asic_power_limits(smu, current, default, max, min) smu_ppt_funcs(get_power_limit, 0, smu, current, default, max, min) +#define smu_get_asic_ppt_limit(smu, type, current) \ + smu_ppt_funcs(get_ppt_limit, -EOPNOTSUPP, smu, type, current) #define smu_get_pp_feature_mask(smu, buf) smu_ppt_funcs(get_pp_feature_mask, 0, smu, buf) #define smu_set_pp_feature_mask(smu, new_mask) smu_ppt_funcs(set_pp_feature_mask, 0, smu, new_mask) #define smu_gfx_ulv_control(smu, enablement) smu_ppt_funcs(gfx_ulv_control, 0, smu, enablement) @@ -98,7 +99,6 @@ #define smu_is_asic_wbrf_supported(smu) smu_ppt_funcs(is_asic_wbrf_supported, false, smu) #define smu_enable_uclk_shadow(smu, enable) smu_ppt_funcs(enable_uclk_shadow, 0, smu, enable) #define smu_set_wbrf_exclusion_ranges(smu, freq_band_range) smu_ppt_funcs(set_wbrf_exclusion_ranges, -EOPNOTSUPP, smu, freq_band_range) -#define smu_get_ras_smu_drv(smu, ras_smu_drv) smu_ppt_funcs(get_ras_smu_drv, -EOPNOTSUPP, smu, ras_smu_drv) #endif #endif |
