/* * Copyright 2017 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. * * Authors: Rafał Miłecki <zajec5@gmail.com> * Alex Deucher <alexdeucher@gmail.com> */ #include "amdgpu.h"#include "amdgpu_drv.h"#include "amdgpu_pm.h"#include "amdgpu_dpm.h"#include "atom.h"#include <linux/pci.h>#include <linux/hwmon.h>#include <linux/hwmon-sysfs.h>#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#define MAX_NUM_OF_SUBSETS 8 #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) struct od_attribute { struct kobj_attribute attribute; struct list_head entry;}; struct od_kobj { struct kobject kobj; struct list_head entry; struct list_head attribute; void *priv;}; struct od_feature_ops { umode_t (*is_visible)(struct amdgpu_device *adev); ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf); ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count);}; struct od_feature_item { const char *name; struct od_feature_ops ops;}; struct od_feature_container { char *name; struct od_feature_ops ops; struct od_feature_item sub_feature[MAX_NUM_OF_FEATURES_PER_SUBSET];}; struct od_feature_set { struct od_feature_container containers[MAX_NUM_OF_SUBSETS];}; static const struct hwmon_temp_label { enum PP_HWMON_TEMP channel; const char *label;} temp_label[] = { {PP_TEMP_EDGE, "edge"}, {PP_TEMP_JUNCTION, "junction"}, {PP_TEMP_MEM, "mem"},}; const char * const amdgpu_pp_profile_name[] = { "BOOTUP_DEFAULT", "3D_FULL_SCREEN", "POWER_SAVING", "VIDEO", "VR", "COMPUTE", "CUSTOM", "WINDOW_3D", "CAPPED", "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. * @runpm: Check runpm status for suspend state checks. * * Checks the state of the @adev for access. Return 0 if the device is * accessible or a negative error code otherwise. */static int amdgpu_pm_dev_state_check(struct amdgpu_device *adev, bool runpm){ bool runpm_check = runpm ? adev->in_runpm : false; bool full_init = (adev->init_lvl->level == AMDGPU_INIT_LEVEL_DEFAULT); if (amdgpu_in_reset(adev) || !full_init) return -EBUSY; if (adev->in_suspend && !runpm_check) return -EBUSY; return 0;} /** * amdgpu_pm_get_access - Check if device can be accessed, resume if needed. * @adev: Target device. * * Checks the state of the @adev for access. Use runtime pm API to resume if * needed. Return 0 if the device is accessible or a negative error code * otherwise. */static int amdgpu_pm_get_access(struct amdgpu_device *adev){ int ret; ret = amdgpu_pm_dev_state_check(adev, true); if (ret) return ret; return pm_runtime_resume_and_get(adev->dev);} /** * amdgpu_pm_get_access_if_active - Check if device is active for access. * @adev: Target device. * * Checks the state of the @adev for access. Use runtime pm API to determine * if device is active. Allow access only if device is active.Return 0 if the * device is accessible or a negative error code otherwise. */static int amdgpu_pm_get_access_if_active(struct amdgpu_device *adev){ int ret; /* Ignore runpm status. If device is in suspended state, deny access */ ret = amdgpu_pm_dev_state_check(adev, false); if (ret) return ret; /* * Allow only if device is active. If runpm is disabled also, as in * kernels without CONFIG_PM, allow access. */ ret = pm_runtime_get_if_active(adev->dev); if (!ret) return -EPERM; return 0;} /** * amdgpu_pm_put_access - Put to auto suspend mode after a device access. * @adev: Target device. * * Should be paired with amdgpu_pm_get_access* calls */static inline void amdgpu_pm_put_access(struct amdgpu_device *adev){ pm_runtime_put_autosuspend(adev->dev);} /** * DOC: power_dpm_state * * The power_dpm_state file is a legacy interface and is only provided for * backwards compatibility. The amdgpu driver provides a sysfs API for adjusting * certain power related parameters. The file power_dpm_state is used for this. * It accepts the following arguments: * * - battery * * - balanced * * - performance * * battery * * On older GPUs, the vbios provided a special power state for battery * operation. Selecting battery switched to this state. This is no * longer provided on newer GPUs so the option does nothing in that case. * * balanced * * On older GPUs, the vbios provided a special power state for balanced * operation. Selecting balanced switched to this state. This is no * longer provided on newer GPUs so the option does nothing in that case. * * performance * * On older GPUs, the vbios provided a special power state for performance * operation. Selecting performance switched to this state. This is no * longer provided on newer GPUs so the option does nothing in that case. * */ static ssize_t amdgpu_get_power_dpm_state(struct device *dev, struct device_attribute *attr, char *buf){ struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); enum amd_pm_state_type pm; int ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; amdgpu_dpm_get_current_power_state(adev, &pm); amdgpu_pm_put_access(adev); return sysfs_emit(buf, "%s\n", (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");} static ssize_t amdgpu_set_power_dpm_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); enum amd_pm_state_type state; int ret; /* Reject empty/whitespace strings - fuzzing found this is not validated */ if (count == 0 || sysfs_streq(buf, "")) return -EINVAL; if (sysfs_streq(buf, "battery")) state = POWER_STATE_TYPE_BATTERY; else if (sysfs_streq(buf, "balanced")) state = POWER_STATE_TYPE_BALANCED; else if (sysfs_streq(buf, "performance")) state = POWER_STATE_TYPE_PERFORMANCE; else return -EINVAL; ret = amdgpu_pm_get_access(adev); if (ret < 0) return ret; amdgpu_dpm_set_power_state(adev, state); amdgpu_pm_put_access(adev); return count;} /** * DOC: power_dpm_force_performance_level * * The amdgpu driver provides a sysfs API for adjusting certain power * related parameters. The file power_dpm_force_performance_level is * used for this. It accepts the following arguments: * * - auto * * - low * * - high * * - manual * * - profile_standard * * - profile_min_sclk * * - profile_min_mclk * * - profile_peak * * auto * * When auto is selected, the driver will attempt to dynamically select * the optimal power profile for current conditions in the driver. * * low * * When low is selected, the clocks are forced to the lowest power state. * * high * * When high is selected, the clocks are forced to the highest power state. * * manual * * When manual is selected, the user can manually adjust which power states * are enabled for each clock domain via the sysfs pp_dpm_mclk, pp_dpm_sclk, * and pp_dpm_pcie files and adjust the power state transition heuristics * via the pp_power_profile_mode sysfs file. * * profile_standard * profile_min_sclk * profile_min_mclk * profile_peak * * When the profiling modes are selected, clock and power gating are * disabled and the clocks are set for different profiling cases. This * mode is recommended for profiling specific work loads where you do * not want clock or power gating for clock fluctuation to interfere * with your results. profile_standard sets the clocks to a fixed clock * level which varies from asic to asic. profile_min_sclk forces the sclk * to the lowest level. profile_min_mclk forces the mclk to the lowest level. * profile_peak sets all clocks (mclk, sclk, pcie) to the highest levels. * */ static ssize_t amdgpu_get_power_dpm_force_performance_level(struct device *dev, struct device_attribute *attr, char *buf){ struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); enum amd_dpm_forced_level level = 0xff; int ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; level = amdgpu_dpm_get_performance_level(adev); amdgpu_pm_put_access(adev); return sysfs_emit(buf, "%s\n", (level == AMD_DPM_FORCED_LEVEL_AUTO) ? "auto" : (level == AMD_DPM_FORCED_LEVEL_LOW) ? "low" : (level == AMD_DPM_FORCED_LEVEL_HIGH) ? "high" : (level == AMD_DPM_FORCED_LEVEL_MANUAL) ? "manual" : (level == AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD) ? "profile_standard" : (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) ? "profile_min_sclk" : (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) ? "profile_min_mclk" : (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) ? "profile_peak" : (level == AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM) ? "perf_determinism" : "unknown");} static ssize_t amdgpu_set_power_dpm_force_performance_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); enum amd_dpm_forced_level level; int ret = 0; /* Reject empty/whitespace strings - fuzzing found this is not validated */ if (count == 0 || sysfs_streq(buf, "")) return -EINVAL; if (sysfs_streq(buf, "low")) level = AMD_DPM_FORCED_LEVEL_LOW; else if (sysfs_streq(buf, "high")) level = AMD_DPM_FORCED_LEVEL_HIGH; else if (sysfs_streq(buf, "auto")) level = AMD_DPM_FORCED_LEVEL_AUTO; else if (sysfs_streq(buf, "manual")) level = AMD_DPM_FORCED_LEVEL_MANUAL; else if (sysfs_streq(buf, "profile_exit")) level = AMD_DPM_FORCED_LEVEL_PROFILE_EXIT; else if (sysfs_streq(buf, "profile_standard")) level = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD; else if (sysfs_streq(buf, "profile_min_sclk")) level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK; else if (sysfs_streq(buf, "profile_min_mclk")) level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK; else if (sysfs_streq(buf, "profile_peak")) level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; else if (sysfs_streq(buf, "perf_determinism")) level = AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM; else return -EINVAL; ret = amdgpu_pm_get_access(adev); if (ret < 0) return ret; mutex_lock(&adev->pm.stable_pstate_ctx_lock); if (amdgpu_dpm_force_performance_level(adev, level)) { amdgpu_pm_put_access(adev); mutex_unlock(&adev->pm.stable_pstate_ctx_lock); return -EINVAL; } /* override whatever a user ctx may have set */ adev->pm.stable_pstate_ctx = NULL; mutex_unlock(&adev->pm.stable_pstate_ctx_lock); amdgpu_pm_put_access(adev); return count;} static ssize_t amdgpu_get_pp_num_states(struct device *dev, struct device_attribute *attr, char *buf){ struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); struct pp_states_info data; uint32_t i; int buf_len, ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; if (amdgpu_dpm_get_pp_num_states(adev, &data)) memset(&data, 0, sizeof(data)); amdgpu_pm_put_access(adev); buf_len = sysfs_emit(buf, "states: %d\n", data.nums); for (i = 0; i < data.nums; i++) buf_len += sysfs_emit_at(buf, buf_len, "%d %s\n", i, (data.states[i] == POWER_STATE_TYPE_INTERNAL_BOOT) ? "boot" : (data.states[i] == POWER_STATE_TYPE_BATTERY) ? "battery" : (data.states[i] == POWER_STATE_TYPE_BALANCED) ? "balanced" : (data.states[i] == POWER_STATE_TYPE_PERFORMANCE) ? "performance" : "default"); return buf_len;} static ssize_t amdgpu_get_pp_cur_state(struct device *dev, struct device_attribute *attr, char *buf){ struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); struct pp_states_info data = {0}; enum amd_pm_state_type pm = 0; int i = 0, ret = 0; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; amdgpu_dpm_get_current_power_state(adev, &pm); ret = amdgpu_dpm_get_pp_num_states(adev, &data); amdgpu_pm_put_access(adev); if (ret) return ret;Lines 1–500 of 5119. Load more
/* * Copyright 2017 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. * * Authors: Rafał Miłecki <zajec5@gmail.com> * Alex Deucher <alexdeucher@gmail.com> */ #include "amdgpu.h" #include "amdgpu_drv.h" #include "amdgpu_pm.h" #include "amdgpu_dpm.h" #include "atom.h" #include <linux/pci.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #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 #define MAX_NUM_OF_SUBSETS 8 #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) struct od_attribute { struct kobj_attribute attribute; struct list_head entry; }; struct od_kobj { struct kobject kobj; struct list_head entry; struct list_head attribute; void *priv; }; struct od_feature_ops { umode_t (*is_visible)(struct amdgpu_device *adev); ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf); ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); }; struct od_feature_item { const char *name; struct od_feature_ops ops; }; struct od_feature_container { char *name; struct od_feature_ops ops; struct od_feature_item sub_feature[MAX_NUM_OF_FEATURES_PER_SUBSET]; }; struct od_feature_set { struct od_feature_container containers[MAX_NUM_OF_SUBSETS]; }; static const struct hwmon_temp_label { enum PP_HWMON_TEMP channel; const char *label; } temp_label[] = { {PP_TEMP_EDGE, "edge"}, {PP_TEMP_JUNCTION, "junction"}, {PP_TEMP_MEM, "mem"}, }; const char * const amdgpu_pp_profile_name[] = { "BOOTUP_DEFAULT", "3D_FULL_SCREEN", "POWER_SAVING", "VIDEO", "VR", "COMPUTE", "CUSTOM", "WINDOW_3D", "CAPPED", "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. * @runpm: Check runpm status for suspend state checks. * * Checks the state of the @adev for access. Return 0 if the device is * accessible or a negative error code otherwise. */ static int amdgpu_pm_dev_state_check(struct amdgpu_device *adev, bool runpm) { bool runpm_check = runpm ? adev->in_runpm : false; bool full_init = (adev->init_lvl->level == AMDGPU_INIT_LEVEL_DEFAULT); if (amdgpu_in_reset(adev) || !full_init) return -EBUSY; if (adev->in_suspend && !runpm_check) return -EBUSY; return 0; } /** * amdgpu_pm_get_access - Check if device can be accessed, resume if needed. * @adev: Target device. * * Checks the state of the @adev for access. Use runtime pm API to resume if * needed. Return 0 if the device is accessible or a negative error code * otherwise. */ static int amdgpu_pm_get_access(struct amdgpu_device *adev) { int ret; ret = amdgpu_pm_dev_state_check(adev, true); if (ret) return ret; return pm_runtime_resume_and_get(adev->dev); } /** * amdgpu_pm_get_access_if_active - Check if device is active for access. * @adev: Target device. * * Checks the state of the @adev for access. Use runtime pm API to determine * if device is active. Allow access only if device is active.Return 0 if the * device is accessible or a negative error code otherwise. */ static int amdgpu_pm_get_access_if_active(struct amdgpu_device *adev) { int ret; /* Ignore runpm status. If device is in suspended state, deny access */ ret = amdgpu_pm_dev_state_check(adev, false); if (ret) return ret; /* * Allow only if device is active. If runpm is disabled also, as in * kernels without CONFIG_PM, allow access. */ ret = pm_runtime_get_if_active(adev->dev); if (!ret) return -EPERM; return 0; } /** * amdgpu_pm_put_access - Put to auto suspend mode after a device access. * @adev: Target device. * * Should be paired with amdgpu_pm_get_access* calls */ static inline void amdgpu_pm_put_access(struct amdgpu_device *adev) { pm_runtime_put_autosuspend(adev->dev); } /** * DOC: power_dpm_state * * The power_dpm_state file is a legacy interface and is only provided for * backwards compatibility. The amdgpu driver provides a sysfs API for adjusting * certain power related parameters. The file power_dpm_state is used for this. * It accepts the following arguments: * * - battery * * - balanced * * - performance * * battery * * On older GPUs, the vbios provided a special power state for battery * operation. Selecting battery switched to this state. This is no * longer provided on newer GPUs so the option does nothing in that case. * * balanced * * On older GPUs, the vbios provided a special power state for balanced * operation. Selecting balanced switched to this state. This is no * longer provided on newer GPUs so the option does nothing in that case. * * performance * * On older GPUs, the vbios provided a special power state for performance * operation. Selecting performance switched to this state. This is no * longer provided on newer GPUs so the option does nothing in that case. * */ static ssize_t amdgpu_get_power_dpm_state(struct device *dev, struct device_attribute *attr, char *buf) { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); enum amd_pm_state_type pm; int ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; amdgpu_dpm_get_current_power_state(adev, &pm); amdgpu_pm_put_access(adev); return sysfs_emit(buf, "%s\n", (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); } static ssize_t amdgpu_set_power_dpm_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); enum amd_pm_state_type state; int ret; /* Reject empty/whitespace strings - fuzzing found this is not validated */ if (count == 0 || sysfs_streq(buf, "")) return -EINVAL; if (sysfs_streq(buf, "battery")) state = POWER_STATE_TYPE_BATTERY; else if (sysfs_streq(buf, "balanced")) state = POWER_STATE_TYPE_BALANCED; else if (sysfs_streq(buf, "performance")) state = POWER_STATE_TYPE_PERFORMANCE; else return -EINVAL; ret = amdgpu_pm_get_access(adev); if (ret < 0) return ret; amdgpu_dpm_set_power_state(adev, state); amdgpu_pm_put_access(adev); return count; } /** * DOC: power_dpm_force_performance_level * * The amdgpu driver provides a sysfs API for adjusting certain power * related parameters. The file power_dpm_force_performance_level is * used for this. It accepts the following arguments: * * - auto * * - low * * - high * * - manual * * - profile_standard * * - profile_min_sclk * * - profile_min_mclk * * - profile_peak * * auto * * When auto is selected, the driver will attempt to dynamically select * the optimal power profile for current conditions in the driver. * * low * * When low is selected, the clocks are forced to the lowest power state. * * high * * When high is selected, the clocks are forced to the highest power state. * * manual * * When manual is selected, the user can manually adjust which power states * are enabled for each clock domain via the sysfs pp_dpm_mclk, pp_dpm_sclk, * and pp_dpm_pcie files and adjust the power state transition heuristics * via the pp_power_profile_mode sysfs file. * * profile_standard * profile_min_sclk * profile_min_mclk * profile_peak * * When the profiling modes are selected, clock and power gating are * disabled and the clocks are set for different profiling cases. This * mode is recommended for profiling specific work loads where you do * not want clock or power gating for clock fluctuation to interfere * with your results. profile_standard sets the clocks to a fixed clock * level which varies from asic to asic. profile_min_sclk forces the sclk * to the lowest level. profile_min_mclk forces the mclk to the lowest level. * profile_peak sets all clocks (mclk, sclk, pcie) to the highest levels. * */ static ssize_t amdgpu_get_power_dpm_force_performance_level(struct device *dev, struct device_attribute *attr, char *buf) { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); enum amd_dpm_forced_level level = 0xff; int ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; level = amdgpu_dpm_get_performance_level(adev); amdgpu_pm_put_access(adev); return sysfs_emit(buf, "%s\n", (level == AMD_DPM_FORCED_LEVEL_AUTO) ? "auto" : (level == AMD_DPM_FORCED_LEVEL_LOW) ? "low" : (level == AMD_DPM_FORCED_LEVEL_HIGH) ? "high" : (level == AMD_DPM_FORCED_LEVEL_MANUAL) ? "manual" : (level == AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD) ? "profile_standard" : (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK) ? "profile_min_sclk" : (level == AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK) ? "profile_min_mclk" : (level == AMD_DPM_FORCED_LEVEL_PROFILE_PEAK) ? "profile_peak" : (level == AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM) ? "perf_determinism" : "unknown"); } static ssize_t amdgpu_set_power_dpm_force_performance_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); enum amd_dpm_forced_level level; int ret = 0; /* Reject empty/whitespace strings - fuzzing found this is not validated */ if (count == 0 || sysfs_streq(buf, "")) return -EINVAL; if (sysfs_streq(buf, "low")) level = AMD_DPM_FORCED_LEVEL_LOW; else if (sysfs_streq(buf, "high")) level = AMD_DPM_FORCED_LEVEL_HIGH; else if (sysfs_streq(buf, "auto")) level = AMD_DPM_FORCED_LEVEL_AUTO; else if (sysfs_streq(buf, "manual")) level = AMD_DPM_FORCED_LEVEL_MANUAL; else if (sysfs_streq(buf, "profile_exit")) level = AMD_DPM_FORCED_LEVEL_PROFILE_EXIT; else if (sysfs_streq(buf, "profile_standard")) level = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD; else if (sysfs_streq(buf, "profile_min_sclk")) level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK; else if (sysfs_streq(buf, "profile_min_mclk")) level = AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK; else if (sysfs_streq(buf, "profile_peak")) level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; else if (sysfs_streq(buf, "perf_determinism")) level = AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM; else return -EINVAL; ret = amdgpu_pm_get_access(adev); if (ret < 0) return ret; mutex_lock(&adev->pm.stable_pstate_ctx_lock); if (amdgpu_dpm_force_performance_level(adev, level)) { amdgpu_pm_put_access(adev); mutex_unlock(&adev->pm.stable_pstate_ctx_lock); return -EINVAL; } /* override whatever a user ctx may have set */ adev->pm.stable_pstate_ctx = NULL; mutex_unlock(&adev->pm.stable_pstate_ctx_lock); amdgpu_pm_put_access(adev); return count; } static ssize_t amdgpu_get_pp_num_states(struct device *dev, struct device_attribute *attr, char *buf) { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); struct pp_states_info data; uint32_t i; int buf_len, ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; if (amdgpu_dpm_get_pp_num_states(adev, &data)) memset(&data, 0, sizeof(data)); amdgpu_pm_put_access(adev); buf_len = sysfs_emit(buf, "states: %d\n", data.nums); for (i = 0; i < data.nums; i++) buf_len += sysfs_emit_at(buf, buf_len, "%d %s\n", i, (data.states[i] == POWER_STATE_TYPE_INTERNAL_BOOT) ? "boot" : (data.states[i] == POWER_STATE_TYPE_BATTERY) ? "battery" : (data.states[i] == POWER_STATE_TYPE_BALANCED) ? "balanced" : (data.states[i] == POWER_STATE_TYPE_PERFORMANCE) ? "performance" : "default"); return buf_len; } static ssize_t amdgpu_get_pp_cur_state(struct device *dev, struct device_attribute *attr, char *buf) { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); struct pp_states_info data = {0}; enum amd_pm_state_type pm = 0; int i = 0, ret = 0; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; amdgpu_dpm_get_current_power_state(adev, &pm); ret = amdgpu_dpm_get_pp_num_states(adev, &data); amdgpu_pm_put_access(adev); if (ret) return ret;