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

Huang Rui ray.huang at amd.com
Sun Feb 7 08:34:24 UTC 2021


On Fri, Feb 05, 2021 at 08:39:24PM +0800, Hou, Xiaomeng (Matthew) wrote:
> 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: Xiaomeng Hou <Xiaomeng.Hou at amd.com>

Series are Reviewed-by: Huang Rui <ray.huang at amd.com>

> ---
>  drivers/gpu/drm/amd/pm/amdgpu_pm.c            |  45 ++++++-
>  drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h       |  12 ++
>  drivers/gpu/drm/amd/pm/inc/smu_v11_0.h        |   9 ++
>  drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c     |  35 ++++--
>  .../gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c    |   8 +-
>  .../gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c  | 113 ++++++++++++++++++
>  6 files changed, 204 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
> index 39899e7989a2..5fa65f191a37 100644
> --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
> +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
> @@ -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;
>  }
>  
> diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
> index 82a5f4a4faf5..10b0624ade65 100644
> --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
> +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
> @@ -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.
>  	 */
> diff --git a/drivers/gpu/drm/amd/pm/inc/smu_v11_0.h b/drivers/gpu/drm/amd/pm/inc/smu_v11_0.h
> index e49c2d08a983..d4cddd2390a2 100644
> --- a/drivers/gpu/drm/amd/pm/inc/smu_v11_0.h
> +++ b/drivers/gpu/drm/amd/pm/inc/smu_v11_0.h
> @@ -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,
> diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
> index 9017024642bb..d143ef1b460b 100644
> --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
> +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
> @@ -2046,29 +2046,38 @@ 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);
>  
> -	switch (limit_level) {
> -	case SMU_PPT_LIMIT_CURRENT:
> -		*limit = smu->current_power_limit;
> -		break;
> -	case SMU_PPT_LIMIT_MAX:
> -		*limit = smu->max_power_limit;
> -		break;
> -	default:
> -		break;
> +	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;
> +			break;
> +		case SMU_PPT_LIMIT_MAX:
> +			*limit = smu->max_power_limit;
> +			break;
> +		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",
> 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 36d651342a76..90585461a56e 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
> @@ -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;
>  }
> 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 42271e80c0b4..3277014b5881 100644
> --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
> +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
> @@ -122,6 +122,10 @@ static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = {
>  	MSG_MAP(SetSoftMinCclk,                     PPSMC_MSG_SetSoftMinCclk,						0),
>  	MSG_MAP(SetSoftMaxCclk,                     PPSMC_MSG_SetSoftMaxCclk,						0),
>  	MSG_MAP(RequestActiveWgp,                   PPSMC_MSG_RequestActiveWgp,                     0),
> +	MSG_MAP(SetFastPPTLimit,                    PPSMC_MSG_SetFastPPTLimit,						0),
> +	MSG_MAP(SetSlowPPTLimit,                    PPSMC_MSG_SetSlowPPTLimit,						0),
> +	MSG_MAP(GetFastPPTLimit,                    PPSMC_MSG_GetFastPPTLimit,						0),
> +	MSG_MAP(GetSlowPPTLimit,                    PPSMC_MSG_GetSlowPPTLimit,						0),
>  };
>  
>  static struct cmn2asic_mapping vangogh_feature_mask_map[SMU_FEATURE_COUNT] = {
> @@ -1773,6 +1777,112 @@ static int vangogh_mode2_reset(struct smu_context *smu)
>  	return vangogh_mode_reset(smu, SMU_RESET_MODE_2);
>  }
>  
> +static int vangogh_get_power_limit(struct smu_context *smu)
> +{
> +	struct smu_11_5_power_context *power_context =
> +								smu->smu_power.power_context;
> +	uint32_t ppt_limit;
> +	int ret = 0;
> +
> +	if (smu->adev->pm.fw_version < 0x43f1e00)
> +		return ret;
> +
> +	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 */
> +	smu->current_power_limit = ppt_limit / 1000;
> +	smu->max_power_limit = 29;
> +
> +	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");
> +		return ret;
> +	}
> +	/* convert from milliwatt to watt */
> +	power_context->current_fast_ppt_limit = ppt_limit / 1000;
> +	power_context->max_fast_ppt_limit = 30;
> +
> +	return ret;
> +}
> +
> +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)
> +{
> +	struct smu_11_5_power_context *power_context =
> +							smu->smu_power.power_context;
> +
> +	if (!power_context)
> +		return -EOPNOTSUPP;
> +
> +	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;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int vangogh_set_power_limit(struct smu_context *smu, uint32_t ppt_limit)
> +{
> +	struct smu_11_5_power_context *power_context =
> +							smu->smu_power.power_context;
> +	uint32_t limit_type = ppt_limit >> 24;
> +	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");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	switch (limit_type) {
> +	case SMU_DEFAULT_PPT_LIMIT:
> +		ret = smu_cmn_send_smc_msg_with_param(smu,
> +				SMU_MSG_SetSlowPPTLimit,
> +				ppt_limit * 1000, /* convert from watt to milliwatt */
> +				NULL);
> +		if (ret)
> +			return ret;
> +
> +		smu->current_power_limit = ppt_limit;
> +		break;
> +	case SMU_FAST_PPT_LIMIT:
> +		ppt_limit &= ~(SMU_FAST_PPT_LIMIT << 24);
> +		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 */
> +				NULL);
> +		if (ret)
> +			return ret;
> +
> +		power_context->current_fast_ppt_limit = ppt_limit;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
>  static const struct pptable_funcs vangogh_ppt_funcs = {
>  
>  	.check_fw_status = smu_v11_0_check_fw_status,
> @@ -1809,6 +1919,9 @@ static const struct pptable_funcs vangogh_ppt_funcs = {
>  	.post_init = vangogh_post_smu_init,
>  	.mode2_reset = vangogh_mode2_reset,
>  	.gfx_off_control = smu_v11_0_gfx_off_control,
> +	.get_ppt_limit = vangogh_get_ppt_limit,
> +	.get_power_limit = vangogh_get_power_limit,
> +	.set_power_limit = vangogh_set_power_limit,
>  };
>  
>  void vangogh_set_ppt_funcs(struct smu_context *smu)
> -- 
> 2.17.1
> 


More information about the amd-gfx mailing list