Commit ae07970a authored by Xiaomeng Hou's avatar Xiaomeng Hou Committed by Alex Deucher
Browse files

drm/amd/pm: add support for hwmon control of slow and fast PPT limit on vangogh



Implement hwmon API for reading/setting slow and fast PPT limit.

APU power is managed to system-level requirements through the PPT
(package power tracking) feature. PPT is intended to limit power to the
requirements of the power source and could be dynamically updated to
maximize APU performance within the system power budget.

Here FAST_PPT_LIMIT manages the ~10 ms moving average of APU power,
while SLOW_PPT_LIMIT manages the configurable, thermally significant
moving average of APU power (default ~5000 ms).

User could read slow/fast ppt limit using command "cat power*_cap" or
"sensors" in the hwmon device directory. User could adjust values of
slow/fast ppt limit as needed depending on workloads through command
"echo ## > power*_cap".

Example:
$ echo 15000000 > power1_cap
$ echo 18000000 > power2_cap
$ sensors
amdgpu-pci-0300
Adapter: PCI adapter
slowPPT:     9.04W (cap = 15.00 W)
fastPPT:     9.04W (cap = 18.00 W)

v2: align with existing interfaces for the getting/setting of PPT
    limits. Encode the upper 8 bits of limit value to distinguish
    slow and fast power limit type.

Signed-off-by: default avatarXiaomeng Hou <Xiaomeng.Hou@amd.com>
Reviewed-by: default avatarLijo Lazar <lijo.lazar@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 52d720b1
Loading
Loading
Loading
Loading
+40 −5
Original line number Diff line number Diff line
@@ -3059,7 +3059,8 @@ static ssize_t amdgpu_hwmon_show_power_cap_max(struct device *dev,
					 char *buf)
{
	struct amdgpu_device *adev = dev_get_drvdata(dev);
	uint32_t limit = 0;
	int limit_type = to_sensor_dev_attr(attr)->index;
	uint32_t limit = limit_type << 24;
	ssize_t size;
	int r;

@@ -3093,7 +3094,8 @@ static ssize_t amdgpu_hwmon_show_power_cap(struct device *dev,
					 char *buf)
{
	struct amdgpu_device *adev = dev_get_drvdata(dev);
	uint32_t limit = 0;
	int limit_type = to_sensor_dev_attr(attr)->index;
	uint32_t limit = limit_type << 24;
	ssize_t size;
	int r;

@@ -3122,6 +3124,15 @@ static ssize_t amdgpu_hwmon_show_power_cap(struct device *dev,
	return size;
}

static ssize_t amdgpu_hwmon_show_power_label(struct device *dev,
					 struct device_attribute *attr,
					 char *buf)
{
	int limit_type = to_sensor_dev_attr(attr)->index;

	return snprintf(buf, PAGE_SIZE, "%s\n",
		limit_type == SMU_FAST_PPT_LIMIT ? "fastPPT" : "slowPPT");
}

static ssize_t amdgpu_hwmon_set_power_cap(struct device *dev,
		struct device_attribute *attr,
@@ -3129,6 +3140,7 @@ static ssize_t amdgpu_hwmon_set_power_cap(struct device *dev,
		size_t count)
{
	struct amdgpu_device *adev = dev_get_drvdata(dev);
	int limit_type = to_sensor_dev_attr(attr)->index;
	int err;
	u32 value;

@@ -3143,7 +3155,7 @@ static ssize_t amdgpu_hwmon_set_power_cap(struct device *dev,
		return err;

	value = value / 1000000; /* convert to Watt */

	value |= limit_type << 24;

	err = pm_runtime_get_sync(adev_to_drm(adev)->dev);
	if (err < 0) {
@@ -3355,6 +3367,12 @@ static SENSOR_DEVICE_ATTR(power1_average, S_IRUGO, amdgpu_hwmon_show_power_avg,
static SENSOR_DEVICE_ATTR(power1_cap_max, S_IRUGO, amdgpu_hwmon_show_power_cap_max, NULL, 0);
static SENSOR_DEVICE_ATTR(power1_cap_min, S_IRUGO, amdgpu_hwmon_show_power_cap_min, NULL, 0);
static SENSOR_DEVICE_ATTR(power1_cap, S_IRUGO | S_IWUSR, amdgpu_hwmon_show_power_cap, amdgpu_hwmon_set_power_cap, 0);
static SENSOR_DEVICE_ATTR(power1_label, S_IRUGO, amdgpu_hwmon_show_power_label, NULL, 0);
static SENSOR_DEVICE_ATTR(power2_average, S_IRUGO, amdgpu_hwmon_show_power_avg, NULL, 1);
static SENSOR_DEVICE_ATTR(power2_cap_max, S_IRUGO, amdgpu_hwmon_show_power_cap_max, NULL, 1);
static SENSOR_DEVICE_ATTR(power2_cap_min, S_IRUGO, amdgpu_hwmon_show_power_cap_min, NULL, 1);
static SENSOR_DEVICE_ATTR(power2_cap, S_IRUGO | S_IWUSR, amdgpu_hwmon_show_power_cap, amdgpu_hwmon_set_power_cap, 1);
static SENSOR_DEVICE_ATTR(power2_label, S_IRUGO, amdgpu_hwmon_show_power_label, NULL, 1);
static SENSOR_DEVICE_ATTR(freq1_input, S_IRUGO, amdgpu_hwmon_show_sclk, NULL, 0);
static SENSOR_DEVICE_ATTR(freq1_label, S_IRUGO, amdgpu_hwmon_show_sclk_label, NULL, 0);
static SENSOR_DEVICE_ATTR(freq2_input, S_IRUGO, amdgpu_hwmon_show_mclk, NULL, 0);
@@ -3393,6 +3411,12 @@ static struct attribute *hwmon_attributes[] = {
	&sensor_dev_attr_power1_cap_max.dev_attr.attr,
	&sensor_dev_attr_power1_cap_min.dev_attr.attr,
	&sensor_dev_attr_power1_cap.dev_attr.attr,
	&sensor_dev_attr_power1_label.dev_attr.attr,
	&sensor_dev_attr_power2_average.dev_attr.attr,
	&sensor_dev_attr_power2_cap_max.dev_attr.attr,
	&sensor_dev_attr_power2_cap_min.dev_attr.attr,
	&sensor_dev_attr_power2_cap.dev_attr.attr,
	&sensor_dev_attr_power2_label.dev_attr.attr,
	&sensor_dev_attr_freq1_input.dev_attr.attr,
	&sensor_dev_attr_freq1_label.dev_attr.attr,
	&sensor_dev_attr_freq2_input.dev_attr.attr,
@@ -3485,8 +3509,9 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj,
			effective_mode &= ~S_IWUSR;
	}

	if (((adev->flags & AMD_IS_APU) ||
	     adev->family == AMDGPU_FAMILY_SI) &&	/* not implemented yet */
	if (((adev->family == AMDGPU_FAMILY_SI) ||
		 ((adev->flags & AMD_IS_APU) &&
	      (adev->asic_type != CHIP_VANGOGH))) &&	/* not implemented yet */
	    (attr == &sensor_dev_attr_power1_cap_max.dev_attr.attr ||
	     attr == &sensor_dev_attr_power1_cap_min.dev_attr.attr||
	     attr == &sensor_dev_attr_power1_cap.dev_attr.attr))
@@ -3549,6 +3574,16 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj,
	     attr == &sensor_dev_attr_temp3_label.dev_attr.attr))
		return 0;

	/* only Vangogh has fast PPT limit and power labels */
	if (!(adev->asic_type == CHIP_VANGOGH) &&
	    (attr == &sensor_dev_attr_power2_average.dev_attr.attr ||
		 attr == &sensor_dev_attr_power2_cap_max.dev_attr.attr ||
	     attr == &sensor_dev_attr_power2_cap_min.dev_attr.attr ||
		 attr == &sensor_dev_attr_power2_cap.dev_attr.attr ||
		 attr == &sensor_dev_attr_power2_label.dev_attr.attr ||
		 attr == &sensor_dev_attr_power1_label.dev_attr.attr))
		return 0;

	return effective_mode;
}

+12 −0
Original line number Diff line number Diff line
@@ -161,6 +161,12 @@ enum smu_power_src_type
	SMU_POWER_SOURCE_COUNT,
};

enum smu_ppt_limit_type
{
	SMU_DEFAULT_PPT_LIMIT = 0,
	SMU_FAST_PPT_LIMIT,
};

enum smu_ppt_limit_level
{
	SMU_PPT_LIMIT_MIN = -1,
@@ -708,6 +714,12 @@ struct pptable_funcs {
	 */
	int (*get_power_limit)(struct smu_context *smu);

	/**
	 * @get_ppt_limit: Get the device's ppt limits.
	 */
	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);

	/**
	 * @set_df_cstate: Set data fabric cstate.
	 */
+9 −0
Original line number Diff line number Diff line
@@ -129,6 +129,15 @@ struct smu_11_0_power_context {
	enum smu_11_0_power_state power_state;
};

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	max_fast_ppt_limit;
};

enum smu_v11_0_baco_seq {
	BACO_SEQ_BACO = 0,
	BACO_SEQ_MSR,
+25 −10
Original line number Diff line number Diff line
@@ -2046,11 +2046,18 @@ int smu_get_power_limit(struct smu_context *smu,
			uint32_t *limit,
			enum smu_ppt_limit_level limit_level)
{
	uint32_t limit_type = *limit >> 24;
	int ret = 0;

	if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
		return -EOPNOTSUPP;

	mutex_lock(&smu->mutex);

	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 {
		switch (limit_level) {
		case SMU_PPT_LIMIT_CURRENT:
			*limit = smu->current_power_limit;
@@ -2061,14 +2068,16 @@ int smu_get_power_limit(struct smu_context *smu,
		default:
			break;
		}
	}

	mutex_unlock(&smu->mutex);

	return 0;
	return ret;
}

int smu_set_power_limit(struct smu_context *smu, uint32_t limit)
{
	uint32_t limit_type = limit >> 24;
	int ret = 0;

	if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
@@ -2076,6 +2085,12 @@ int smu_set_power_limit(struct smu_context *smu, uint32_t limit)

	mutex_lock(&smu->mutex);

	if (limit_type != SMU_DEFAULT_PPT_LIMIT)
		if (smu->ppt_funcs->set_power_limit) {
			ret = smu->ppt_funcs->set_power_limit(smu, limit);
			goto out;
		}

	if (limit > smu->max_power_limit) {
		dev_err(smu->adev->dev,
			"New power limit (%d) is over the max allowed %d\n",
+5 −3
Original line number Diff line number Diff line
@@ -474,12 +474,14 @@ int smu_v11_0_fini_smc_tables(struct smu_context *smu)
int smu_v11_0_init_power(struct smu_context *smu)
{
	struct smu_power_context *smu_power = &smu->smu_power;
	size_t size = smu->adev->asic_type == CHIP_VANGOGH ?
			sizeof(struct smu_11_5_power_context) :
			sizeof(struct smu_11_0_power_context);

	smu_power->power_context = kzalloc(sizeof(struct smu_11_0_power_context),
					   GFP_KERNEL);
	smu_power->power_context = kzalloc(size, GFP_KERNEL);
	if (!smu_power->power_context)
		return -ENOMEM;
	smu_power->power_context_size = sizeof(struct smu_11_0_power_context);
	smu_power->power_context_size = size;

	return 0;
}
Loading