drm/amd/powerplay: support enabled ppfeatures retrieving and setting V3
User can use "ppfeatures" sysfs interface to retrieve and set enabled powerplay features. V2: expose this feature for Vega10 and later dGPUs V3: squash in removal of unused variable (Alex) Signed-off-by: Evan Quan <evan.quan@amd.com> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
@@ -364,6 +364,14 @@ enum amdgpu_pcie_gen {
|
|||||||
((adev)->powerplay.pp_funcs->enable_mgpu_fan_boost(\
|
((adev)->powerplay.pp_funcs->enable_mgpu_fan_boost(\
|
||||||
(adev)->powerplay.pp_handle))
|
(adev)->powerplay.pp_handle))
|
||||||
|
|
||||||
|
#define amdgpu_dpm_get_ppfeature_status(adev, buf) \
|
||||||
|
((adev)->powerplay.pp_funcs->get_ppfeature_status(\
|
||||||
|
(adev)->powerplay.pp_handle, (buf)))
|
||||||
|
|
||||||
|
#define amdgpu_dpm_set_ppfeature_status(adev, ppfeatures) \
|
||||||
|
((adev)->powerplay.pp_funcs->set_ppfeature_status(\
|
||||||
|
(adev)->powerplay.pp_handle, (ppfeatures)))
|
||||||
|
|
||||||
struct amdgpu_dpm {
|
struct amdgpu_dpm {
|
||||||
struct amdgpu_ps *ps;
|
struct amdgpu_ps *ps;
|
||||||
/* number of valid power states */
|
/* number of valid power states */
|
||||||
|
@@ -625,6 +625,60 @@ static ssize_t amdgpu_get_pp_od_clk_voltage(struct device *dev,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOC: ppfeatures
|
||||||
|
*
|
||||||
|
* The amdgpu driver provides a sysfs API for adjusting what powerplay
|
||||||
|
* features to be enabled. The file ppfeatures is used for this. And
|
||||||
|
* this is only available for Vega10 and later dGPUs.
|
||||||
|
*
|
||||||
|
* Reading back the file will show you the followings:
|
||||||
|
* - Current ppfeature masks
|
||||||
|
* - List of the all supported powerplay features with their naming,
|
||||||
|
* bitmasks and enablement status('Y'/'N' means "enabled"/"disabled").
|
||||||
|
*
|
||||||
|
* To manually enable or disable a specific feature, just set or clear
|
||||||
|
* the corresponding bit from original ppfeature masks and input the
|
||||||
|
* new ppfeature masks.
|
||||||
|
*/
|
||||||
|
static ssize_t amdgpu_set_ppfeature_status(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
struct drm_device *ddev = dev_get_drvdata(dev);
|
||||||
|
struct amdgpu_device *adev = ddev->dev_private;
|
||||||
|
uint64_t featuremask;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtou64(buf, 0, &featuremask);
|
||||||
|
if (ret)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pr_debug("featuremask = 0x%llx\n", featuremask);
|
||||||
|
|
||||||
|
if (adev->powerplay.pp_funcs->set_ppfeature_status) {
|
||||||
|
ret = amdgpu_dpm_set_ppfeature_status(adev, featuremask);
|
||||||
|
if (ret)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t amdgpu_get_ppfeature_status(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct drm_device *ddev = dev_get_drvdata(dev);
|
||||||
|
struct amdgpu_device *adev = ddev->dev_private;
|
||||||
|
|
||||||
|
if (adev->powerplay.pp_funcs->get_ppfeature_status)
|
||||||
|
return amdgpu_dpm_get_ppfeature_status(adev, buf);
|
||||||
|
|
||||||
|
return snprintf(buf, PAGE_SIZE, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DOC: pp_dpm_sclk pp_dpm_mclk pp_dpm_pcie
|
* DOC: pp_dpm_sclk pp_dpm_mclk pp_dpm_pcie
|
||||||
*
|
*
|
||||||
@@ -1051,6 +1105,9 @@ static DEVICE_ATTR(pp_od_clk_voltage, S_IRUGO | S_IWUSR,
|
|||||||
static DEVICE_ATTR(gpu_busy_percent, S_IRUGO,
|
static DEVICE_ATTR(gpu_busy_percent, S_IRUGO,
|
||||||
amdgpu_get_busy_percent, NULL);
|
amdgpu_get_busy_percent, NULL);
|
||||||
static DEVICE_ATTR(pcie_bw, S_IRUGO, amdgpu_get_pcie_bw, NULL);
|
static DEVICE_ATTR(pcie_bw, S_IRUGO, amdgpu_get_pcie_bw, NULL);
|
||||||
|
static DEVICE_ATTR(ppfeatures, S_IRUGO | S_IWUSR,
|
||||||
|
amdgpu_get_ppfeature_status,
|
||||||
|
amdgpu_set_ppfeature_status);
|
||||||
|
|
||||||
static ssize_t amdgpu_hwmon_show_temp(struct device *dev,
|
static ssize_t amdgpu_hwmon_show_temp(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
@@ -2241,6 +2298,17 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((adev->asic_type >= CHIP_VEGA10) &&
|
||||||
|
!(adev->flags & AMD_IS_APU)) {
|
||||||
|
ret = device_create_file(adev->dev,
|
||||||
|
&dev_attr_ppfeatures);
|
||||||
|
if (ret) {
|
||||||
|
DRM_ERROR("failed to create device file "
|
||||||
|
"ppfeatures\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
adev->pm.sysfs_initialized = true;
|
adev->pm.sysfs_initialized = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2276,6 +2344,9 @@ void amdgpu_pm_sysfs_fini(struct amdgpu_device *adev)
|
|||||||
device_remove_file(adev->dev, &dev_attr_gpu_busy_percent);
|
device_remove_file(adev->dev, &dev_attr_gpu_busy_percent);
|
||||||
if (adev->flags & !AMD_IS_APU)
|
if (adev->flags & !AMD_IS_APU)
|
||||||
device_remove_file(adev->dev, &dev_attr_pcie_bw);
|
device_remove_file(adev->dev, &dev_attr_pcie_bw);
|
||||||
|
if ((adev->asic_type >= CHIP_VEGA10) &&
|
||||||
|
!(adev->flags & AMD_IS_APU))
|
||||||
|
device_remove_file(adev->dev, &dev_attr_ppfeatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
void amdgpu_pm_compute_clocks(struct amdgpu_device *adev)
|
void amdgpu_pm_compute_clocks(struct amdgpu_device *adev)
|
||||||
|
@@ -284,6 +284,8 @@ struct amd_pm_funcs {
|
|||||||
int (*get_asic_baco_capability)(void *handle, bool *cap);
|
int (*get_asic_baco_capability)(void *handle, bool *cap);
|
||||||
int (*get_asic_baco_state)(void *handle, int *state);
|
int (*get_asic_baco_state)(void *handle, int *state);
|
||||||
int (*set_asic_baco_state)(void *handle, int state);
|
int (*set_asic_baco_state)(void *handle, int state);
|
||||||
|
int (*get_ppfeature_status)(void *handle, char *buf);
|
||||||
|
int (*set_ppfeature_status)(void *handle, uint64_t ppfeature_masks);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1455,6 +1455,46 @@ static int pp_set_asic_baco_state(void *handle, int state)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pp_get_ppfeature_status(void *handle, char *buf)
|
||||||
|
{
|
||||||
|
struct pp_hwmgr *hwmgr = handle;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!hwmgr || !hwmgr->pm_en || !buf)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (hwmgr->hwmgr_func->get_ppfeature_status == NULL) {
|
||||||
|
pr_info_ratelimited("%s was not implemented.\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&hwmgr->smu_lock);
|
||||||
|
ret = hwmgr->hwmgr_func->get_ppfeature_status(hwmgr, buf);
|
||||||
|
mutex_unlock(&hwmgr->smu_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pp_set_ppfeature_status(void *handle, uint64_t ppfeature_masks)
|
||||||
|
{
|
||||||
|
struct pp_hwmgr *hwmgr = handle;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!hwmgr || !hwmgr->pm_en)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (hwmgr->hwmgr_func->set_ppfeature_status == NULL) {
|
||||||
|
pr_info_ratelimited("%s was not implemented.\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&hwmgr->smu_lock);
|
||||||
|
ret = hwmgr->hwmgr_func->set_ppfeature_status(hwmgr, ppfeature_masks);
|
||||||
|
mutex_unlock(&hwmgr->smu_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct amd_pm_funcs pp_dpm_funcs = {
|
static const struct amd_pm_funcs pp_dpm_funcs = {
|
||||||
.load_firmware = pp_dpm_load_fw,
|
.load_firmware = pp_dpm_load_fw,
|
||||||
.wait_for_fw_loading_complete = pp_dpm_fw_loading_complete,
|
.wait_for_fw_loading_complete = pp_dpm_fw_loading_complete,
|
||||||
@@ -1508,4 +1548,6 @@ static const struct amd_pm_funcs pp_dpm_funcs = {
|
|||||||
.get_asic_baco_capability = pp_get_asic_baco_capability,
|
.get_asic_baco_capability = pp_get_asic_baco_capability,
|
||||||
.get_asic_baco_state = pp_get_asic_baco_state,
|
.get_asic_baco_state = pp_get_asic_baco_state,
|
||||||
.set_asic_baco_state = pp_set_asic_baco_state,
|
.set_asic_baco_state = pp_set_asic_baco_state,
|
||||||
|
.get_ppfeature_status = pp_get_ppfeature_status,
|
||||||
|
.set_ppfeature_status = pp_set_ppfeature_status,
|
||||||
};
|
};
|
||||||
|
@@ -2776,6 +2776,108 @@ static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vega20_get_ppfeature_status(struct pp_hwmgr *hwmgr, char *buf)
|
||||||
|
{
|
||||||
|
static const char *ppfeature_name[] = {
|
||||||
|
"DPM_PREFETCHER",
|
||||||
|
"GFXCLK_DPM",
|
||||||
|
"UCLK_DPM",
|
||||||
|
"SOCCLK_DPM",
|
||||||
|
"UVD_DPM",
|
||||||
|
"VCE_DPM",
|
||||||
|
"ULV",
|
||||||
|
"MP0CLK_DPM",
|
||||||
|
"LINK_DPM",
|
||||||
|
"DCEFCLK_DPM",
|
||||||
|
"GFXCLK_DS",
|
||||||
|
"SOCCLK_DS",
|
||||||
|
"LCLK_DS",
|
||||||
|
"PPT",
|
||||||
|
"TDC",
|
||||||
|
"THERMAL",
|
||||||
|
"GFX_PER_CU_CG",
|
||||||
|
"RM",
|
||||||
|
"DCEFCLK_DS",
|
||||||
|
"ACDC",
|
||||||
|
"VR0HOT",
|
||||||
|
"VR1HOT",
|
||||||
|
"FW_CTF",
|
||||||
|
"LED_DISPLAY",
|
||||||
|
"FAN_CONTROL",
|
||||||
|
"GFX_EDC",
|
||||||
|
"GFXOFF",
|
||||||
|
"CG",
|
||||||
|
"FCLK_DPM",
|
||||||
|
"FCLK_DS",
|
||||||
|
"MP1CLK_DS",
|
||||||
|
"MP0CLK_DS",
|
||||||
|
"XGMI"};
|
||||||
|
static const char *output_title[] = {
|
||||||
|
"FEATURES",
|
||||||
|
"BITMASK",
|
||||||
|
"ENABLEMENT"};
|
||||||
|
uint64_t features_enabled;
|
||||||
|
int i;
|
||||||
|
int ret = 0;
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
ret = vega20_get_enabled_smc_features(hwmgr, &features_enabled);
|
||||||
|
PP_ASSERT_WITH_CODE(!ret,
|
||||||
|
"[EnableAllSmuFeatures] Failed to get enabled smc features!",
|
||||||
|
return ret);
|
||||||
|
|
||||||
|
size += sprintf(buf + size, "Current ppfeatures: 0x%016llx\n", features_enabled);
|
||||||
|
size += sprintf(buf + size, "%-19s %-22s %s\n",
|
||||||
|
output_title[0],
|
||||||
|
output_title[1],
|
||||||
|
output_title[2]);
|
||||||
|
for (i = 0; i < GNLD_FEATURES_MAX; i++) {
|
||||||
|
size += sprintf(buf + size, "%-19s 0x%016llx %6s\n",
|
||||||
|
ppfeature_name[i],
|
||||||
|
1ULL << i,
|
||||||
|
(features_enabled & (1ULL << i)) ? "Y" : "N");
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vega20_set_ppfeature_status(struct pp_hwmgr *hwmgr, uint64_t new_ppfeature_masks)
|
||||||
|
{
|
||||||
|
uint64_t features_enabled;
|
||||||
|
uint64_t features_to_enable;
|
||||||
|
uint64_t features_to_disable;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (new_ppfeature_masks >= (1ULL << GNLD_FEATURES_MAX))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = vega20_get_enabled_smc_features(hwmgr, &features_enabled);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
features_to_disable =
|
||||||
|
(features_enabled ^ new_ppfeature_masks) & features_enabled;
|
||||||
|
features_to_enable =
|
||||||
|
(features_enabled ^ new_ppfeature_masks) ^ features_to_disable;
|
||||||
|
|
||||||
|
pr_debug("features_to_disable 0x%llx\n", features_to_disable);
|
||||||
|
pr_debug("features_to_enable 0x%llx\n", features_to_enable);
|
||||||
|
|
||||||
|
if (features_to_disable) {
|
||||||
|
ret = vega20_enable_smc_features(hwmgr, false, features_to_disable);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features_to_enable) {
|
||||||
|
ret = vega20_enable_smc_features(hwmgr, true, features_to_enable);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
|
static int vega20_print_clock_levels(struct pp_hwmgr *hwmgr,
|
||||||
enum pp_clock_type type, char *buf)
|
enum pp_clock_type type, char *buf)
|
||||||
{
|
{
|
||||||
@@ -3572,6 +3674,8 @@ static const struct pp_hwmgr_func vega20_hwmgr_funcs = {
|
|||||||
.force_clock_level = vega20_force_clock_level,
|
.force_clock_level = vega20_force_clock_level,
|
||||||
.print_clock_levels = vega20_print_clock_levels,
|
.print_clock_levels = vega20_print_clock_levels,
|
||||||
.read_sensor = vega20_read_sensor,
|
.read_sensor = vega20_read_sensor,
|
||||||
|
.get_ppfeature_status = vega20_get_ppfeature_status,
|
||||||
|
.set_ppfeature_status = vega20_set_ppfeature_status,
|
||||||
/* powergate related */
|
/* powergate related */
|
||||||
.powergate_uvd = vega20_power_gate_uvd,
|
.powergate_uvd = vega20_power_gate_uvd,
|
||||||
.powergate_vce = vega20_power_gate_vce,
|
.powergate_vce = vega20_power_gate_vce,
|
||||||
|
@@ -341,6 +341,8 @@ struct pp_hwmgr_func {
|
|||||||
int (*get_asic_baco_capability)(struct pp_hwmgr *hwmgr, bool *cap);
|
int (*get_asic_baco_capability)(struct pp_hwmgr *hwmgr, bool *cap);
|
||||||
int (*get_asic_baco_state)(struct pp_hwmgr *hwmgr, enum BACO_STATE *state);
|
int (*get_asic_baco_state)(struct pp_hwmgr *hwmgr, enum BACO_STATE *state);
|
||||||
int (*set_asic_baco_state)(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
|
int (*set_asic_baco_state)(struct pp_hwmgr *hwmgr, enum BACO_STATE state);
|
||||||
|
int (*get_ppfeature_status)(struct pp_hwmgr *hwmgr, char *buf);
|
||||||
|
int (*set_ppfeature_status)(struct pp_hwmgr *hwmgr, uint64_t ppfeature_masks);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pp_table_func {
|
struct pp_table_func {
|
||||||
|
Reference in New Issue
Block a user