drm/amd/powerplay: support enabled ppfeatures retrieving and setting V3
authorEvan Quan <evan.quan@amd.com>
Mon, 14 Jan 2019 06:06:54 +0000 (14:06 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Fri, 25 Jan 2019 21:15:34 +0000 (16:15 -0500)
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>
drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.h
drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
drivers/gpu/drm/amd/include/kgd_pp_interface.h
drivers/gpu/drm/amd/powerplay/amd_powerplay.c
drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c
drivers/gpu/drm/amd/powerplay/inc/hwmgr.h

index f972cd156795ab60ed786ee198b8d0048b90fc95..2f61e9edb1c1a10f69322807bb3f75d76a5ae97b 100644 (file)
@@ -364,6 +364,14 @@ enum amdgpu_pcie_gen {
                ((adev)->powerplay.pp_funcs->enable_mgpu_fan_boost(\
                        (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_ps        *ps;
        /* number of valid power states */
index 51eb2cf42b810a430382098ee7b539f17e63090b..f21f294e6735898e9da36b0f158d206fd2fc27c5 100644 (file)
@@ -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
  *
@@ -1051,6 +1105,9 @@ static DEVICE_ATTR(pp_od_clk_voltage, S_IRUGO | S_IWUSR,
 static DEVICE_ATTR(gpu_busy_percent, S_IRUGO,
                amdgpu_get_busy_percent, 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,
                                      struct device_attribute *attr,
@@ -2241,6 +2298,17 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
                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;
 
        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);
        if (adev->flags & !AMD_IS_APU)
                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)
index a2ea4c93336078349cd7dbb0d117d2645527f2a4..1130f293c4ee4d1b87de898c07a9a9e270623be7 100644 (file)
@@ -284,6 +284,8 @@ struct amd_pm_funcs {
        int (*get_asic_baco_capability)(void *handle, bool *cap);
        int (*get_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
index 5d8b5d3c24535e9abeb318c947aee869ccd30e2d..3f73f7cd18b97673e62bcfa0105e325f4f7ef4e6 100644 (file)
@@ -1455,6 +1455,46 @@ static int pp_set_asic_baco_state(void *handle, int state)
        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 = {
        .load_firmware = pp_dpm_load_fw,
        .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_state = pp_get_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,
 };
index 2ba387b0f27c6be9585ce12d51dbad9a71f00a81..3e97b9d6f450e4cf32955a7c54c165018f8146b8 100644 (file)
@@ -2776,6 +2776,108 @@ static int vega20_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
        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,
                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,
        .print_clock_levels = vega20_print_clock_levels,
        .read_sensor = vega20_read_sensor,
+       .get_ppfeature_status = vega20_get_ppfeature_status,
+       .set_ppfeature_status = vega20_set_ppfeature_status,
        /* powergate related */
        .powergate_uvd = vega20_power_gate_uvd,
        .powergate_vce = vega20_power_gate_vce,
index 577cec90aef1c783d6c68a0c36f7fe69e4a44b3a..b1cd70dcd6e7020dcd51d86b057cec27f1ebdb2f 100644 (file)
@@ -341,6 +341,8 @@ struct pp_hwmgr_func {
        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 (*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 {