drm/amd/pm: use milliwatts for GPU power sensors

GPU average and input power backends report a mix of whole watts,
milliwatts, Q24.8 watts and decimal-packed fractions. Q24.8 is inherited
from the legacy PowerPlay sensor format. Milliwatts are a more natural unit
for the hwmon and pm_info consumers in amdgpu_pm.c. A common decoder cannot
distinguish these formats, and converting native milliwatts through Q24.8
also loses precision.

Use milliwatts as the internal unit across all PPT and PowerPlay backends.
Decode Q24.8 only at the legacy smu7 input boundary and encode it only for
the raw amdgpu_sensors debugfs interface. This gives hwmon, pm_info and the
sensor ioctl one unambiguous unit while preserving the format used by UMR.

Fixes: 5b79d0482f ("drm/amd/pp: Remove struct pp_gpu_power")
Fixes: 01992b121f ("drm/amd/pm: fix amdgpu_pm_info power display units")
Signed-off-by: Yang Wang <kevinyang.wang@amd.com>
Reviewed-by: Kenneth Feng <kenneth.feng@amd.com>
Reported-by: Lars Nieradzik <l.nieradzik@gmail.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 757ba0790b)
Cc: stable@vger.kernel.org
This commit is contained in:
Yang Wang 2026-07-27 12:23:17 +08:00 committed by Alex Deucher
parent f931c54b24
commit 1849a64165
27 changed files with 65 additions and 51 deletions

View File

@ -43,6 +43,11 @@
#if defined(CONFIG_DEBUG_FS)
/* Encode milliwatts in the raw Q24.8 sensor report format used by UMR. */
#define AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(power_mw) \
DIV_ROUND_CLOSEST_ULL((u64)(power_mw) * BIT(8), \
MILLIWATT_PER_WATT)
/**
* amdgpu_debugfs_process_reg_op - Handle MMIO register reads/writes
*
@ -1104,6 +1109,10 @@ static ssize_t amdgpu_debugfs_sensor_read(struct file *f, char __user *buf,
return r;
}
if (idx == AMDGPU_PP_SENSOR_GPU_AVG_POWER ||
idx == AMDGPU_PP_SENSOR_GPU_INPUT_POWER)
values[0] = AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(values[0]);
if (size > valuesize) {
amdgpu_virt_disable_access_debugfs(adev);
return -EINVAL;

View File

@ -1210,7 +1210,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
return -EINVAL;
}
}
ui32 >>= 8;
ui32 /= MILLIWATT_PER_WATT;
break;
case AMDGPU_INFO_SENSOR_GPU_INPUT_POWER:
/* get input GPU power */
@ -1219,7 +1219,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
(void *)&ui32, &ui32_size)) {
return -EINVAL;
}
ui32 >>= 8;
ui32 /= MILLIWATT_PER_WATT;
break;
case AMDGPU_INFO_SENSOR_VDDNB:
/* get VDDNB in millivolts */

View File

@ -24,6 +24,8 @@
#ifndef __KGD_PP_INTERFACE_H__
#define __KGD_PP_INTERFACE_H__
#include <linux/units.h>
extern const struct amdgpu_ip_block_version pp_smu_ip_block;
extern const struct amdgpu_ip_block_version smu_v11_0_ip_block;
extern const struct amdgpu_ip_block_version smu_v12_0_ip_block;
@ -150,8 +152,8 @@ enum amd_pp_sensors {
AMDGPU_PP_SENSOR_MEM_TEMP,
AMDGPU_PP_SENSOR_VCE_POWER,
AMDGPU_PP_SENSOR_UVD_POWER,
AMDGPU_PP_SENSOR_GPU_AVG_POWER,
AMDGPU_PP_SENSOR_GPU_INPUT_POWER,
AMDGPU_PP_SENSOR_GPU_AVG_POWER, /* milliwatts */
AMDGPU_PP_SENSOR_GPU_INPUT_POWER, /* milliwatts */
AMDGPU_PP_SENSOR_SS_APU_SHARE,
AMDGPU_PP_SENSOR_SS_DGPU_SHARE,
AMDGPU_PP_SENSOR_STABLE_PSTATE_SCLK,

View File

@ -41,8 +41,6 @@
#define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name)
#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff))
struct od_attribute {
struct kobj_attribute attribute;
struct list_head entry;
@ -3351,7 +3349,7 @@ static int amdgpu_hwmon_get_power(struct device *dev,
return r;
/* convert to microwatts */
return power_2_mwatt(query) * 1000;
return query * 1000;
}
static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev,
@ -4919,7 +4917,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a
seq_printf(m, "\t%u mV (VDDNB)\n", value);
size = sizeof(uint32_t);
if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) {
mwatt = power_2_mwatt(query);
mwatt = query;
centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
if (adev->flags & AMD_IS_APU)
seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
@ -4928,7 +4926,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a
}
size = sizeof(uint32_t);
if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) {
mwatt = power_2_mwatt(query);
mwatt = query;
centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
if (adev->flags & AMD_IS_APU)
seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100);

View File

@ -4062,7 +4062,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query)
(adev->asic_type != CHIP_FIJI) &&
(adev->asic_type != CHIP_TONGA)) {
smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0, &tmp);
*query = tmp;
*query = PP_PWR_Q24_8_TO_MW(tmp);
if (tmp != 0)
return 0;
@ -4081,7 +4081,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query)
if (tmp != 0)
break;
}
*query = tmp;
*query = PP_PWR_Q24_8_TO_MW(tmp);
return 0;
}

View File

@ -3934,8 +3934,8 @@ static int vega10_get_gpu_power(struct pp_hwmgr *hwmgr,
if (ret)
return ret;
/* SMC returning actual watts, keep consistent with legacy asics, low 8 bit as 8 fractional bits */
*query = value << 8;
/* SMC returns whole Watts, while power sensors use milliwatts. */
*query = value * MILLIWATT_PER_WATT;
return 0;
}

View File

@ -1419,7 +1419,7 @@ static int vega12_get_gpu_power(struct pp_hwmgr *hwmgr, uint32_t *query)
if (ret)
return ret;
*query = metrics_table.CurrSocketPower << 8;
*query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT;
return ret;
}

View File

@ -2154,12 +2154,13 @@ static int vega20_get_gpu_power(struct pp_hwmgr *hwmgr, int idx,
switch (idx) {
case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
if (hwmgr->smu_version == 0x282e00)
*query = metrics_table.AverageSocketPower << 8;
*query = metrics_table.AverageSocketPower *
MILLIWATT_PER_WATT;
else
ret = -EOPNOTSUPP;
break;
case AMDGPU_PP_SENSOR_GPU_INPUT_POWER:
*query = metrics_table.CurrSocketPower << 8;
*query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT;
break;
}

View File

@ -35,6 +35,11 @@ struct pp_hwmgr;
struct phm_fan_speed_info;
struct pp_atomctrl_voltage_table;
/* Decode legacy unsigned Q24.8 watts to internal milliwatts. */
#define PP_PWR_Q24_8_TO_MW(power) \
DIV_ROUND_CLOSEST_ULL((u64)(power) * MILLIWATT_PER_WATT, \
BIT(8))
#define VOLTAGE_SCALE 4
#define VOLTAGE_VID_OFFSET_SCALE1 625
#define VOLTAGE_VID_OFFSET_SCALE2 100

View File

@ -658,7 +658,7 @@ static int arcturus_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->VcnActivityPercentage;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *

View File

@ -158,12 +158,10 @@ cyan_skillfish_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->Current.MemclkFrequency;
break;
case METRICS_CURR_SOCKETPOWER:
*value = (metrics->Current.CurrentSocketPower << 8) /
1000;
*value = metrics->Current.CurrentSocketPower;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = (metrics->Average.CurrentSocketPower << 8) /
1000;
*value = metrics->Average.CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->Current.GfxTemperature / 100 *

View File

@ -602,7 +602,7 @@ static int navi10_get_legacy_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *
@ -691,7 +691,7 @@ static int navi10_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *
@ -777,7 +777,7 @@ static int navi12_get_legacy_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *
@ -866,7 +866,7 @@ static int navi12_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *

View File

@ -855,9 +855,10 @@ static int sienna_cichlid_get_smu_metrics_data(struct smu_context *smu,
metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = use_metrics_v3 ? metrics_v3->AverageSocketPower << 8 :
use_metrics_v2 ? metrics_v2->AverageSocketPower << 8 :
metrics->AverageSocketPower << 8;
*value = use_metrics_v3 ? metrics_v3->AverageSocketPower :
use_metrics_v2 ? metrics_v2->AverageSocketPower :
metrics->AverageSocketPower;
*value *= MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = (use_metrics_v3 ? metrics_v3->TemperatureEdge :

View File

@ -318,8 +318,7 @@ static int vangogh_get_legacy_smu_metrics_data(struct smu_context *smu,
*value = metrics->UvdActivity / 100;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = (metrics->CurrentSocketPower << 8) /
1000 ;
*value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *
@ -387,12 +386,10 @@ static int vangogh_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->Current.UvdActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = (metrics->Average.CurrentSocketPower << 8) /
1000;
*value = metrics->Average.CurrentSocketPower;
break;
case METRICS_CURR_SOCKETPOWER:
*value = (metrics->Current.CurrentSocketPower << 8) /
1000;
*value = metrics->Current.CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->Current.GfxTemperature / 100 *

View File

@ -1215,9 +1215,10 @@ static int renoir_get_smu_metrics_data(struct smu_context *smu,
((amdgpu_ip_version(adev, MP1_HWIP, 0) ==
IP_VERSION(12, 0, 0)) &&
(adev->pm.fw_version >= 0x373200)))
*value = metrics->CurrentSocketPower << 8;
*value = metrics->CurrentSocketPower *
MILLIWATT_PER_WATT;
else
*value = (metrics->CurrentSocketPower << 8) / 1000;
*value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = (metrics->GfxTemperature / 100) *

View File

@ -677,7 +677,8 @@ static int aldebaran_get_smu_metrics_data(struct smu_context *smu,
case METRICS_AVERAGE_SOCKETPOWER:
/* Valid power data is available only from primary die */
if (aldebaran_is_primary(smu))
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower *
MILLIWATT_PER_WATT;
else
ret = -EOPNOTSUPP;
break;

View File

@ -807,7 +807,7 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu,
metrics->Vcn1ActivityPercentage);
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->AvgTemperature[TEMP_EDGE] *

View File

@ -434,7 +434,8 @@ int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu,
*value = SMUQ10_ROUND(metrics->DramBandwidthUtilization);
break;
case METRICS_CURR_SOCKETPOWER:
*value = SMUQ10_ROUND(metrics->SocketPower) << 8;
*value = SMUQ10_ROUND(metrics->SocketPower) *
MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_HOTSPOT:
*value = SMUQ10_ROUND(metrics->MaxSocketTemperature) *

View File

@ -338,10 +338,10 @@ static int smu_v13_0_4_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->UvdActivity / 100;
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = (metrics->AverageSocketPower << 8) / 1000;
*value = metrics->AverageSocketPower;
break;
case METRICS_CURR_SOCKETPOWER:
*value = (metrics->CurrentSocketPower << 8) / 1000;
*value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *

View File

@ -295,7 +295,7 @@ static int smu_v13_0_5_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->UvdActivity / 100;
break;
case METRICS_CURR_SOCKETPOWER:
*value = (metrics->CurrentSocketPower << 8) / 1000;
*value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *

View File

@ -1321,7 +1321,8 @@ static int smu_v13_0_6_get_smu_metrics_data(struct smu_context *smu,
*value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, version));
break;
case METRICS_CURR_SOCKETPOWER:
*value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) << 8;
*value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) *
MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_HOTSPOT:
*value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, version)) *

View File

@ -817,7 +817,7 @@ static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu,
*value = smu_safe_u16_nn(metrics->AverageUclkActivity);
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->AvgTemperature[TEMP_EDGE] *

View File

@ -372,7 +372,7 @@ static int yellow_carp_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->UvdActivity / 100;
break;
case METRICS_CURR_SOCKETPOWER:
*value = (metrics->CurrentSocketPower << 8) / 1000;
*value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *

View File

@ -312,8 +312,7 @@ static int smu_v14_0_0_get_smu_metrics_data(struct smu_context *smu,
break;
case METRICS_AVERAGE_SOCKETPOWER:
case METRICS_CURR_SOCKETPOWER:
*value = (metrics->SocketPower / 1000 << 8) +
(metrics->SocketPower % 1000 / 10);
*value = metrics->SocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *

View File

@ -695,7 +695,7 @@ static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu,
metrics->Vcn1ActivityPercentage);
break;
case METRICS_AVERAGE_SOCKETPOWER:
*value = metrics->AverageSocketPower << 8;
*value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->AvgTemperature[TEMP_EDGE] *

View File

@ -378,8 +378,7 @@ static int smu_v15_0_0_get_smu_metrics_data(struct smu_context *smu,
break;
case METRICS_AVERAGE_SOCKETPOWER:
case METRICS_CURR_SOCKETPOWER:
*value = (metrics->SocketPower / 1000 << 8) +
(metrics->SocketPower % 1000 / 10);
*value = metrics->SocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *

View File

@ -411,7 +411,8 @@ static int smu_v15_0_8_get_smu_metrics_data(struct smu_context *smu,
*value = SMUQ10_ROUND(metrics->DramBandwidthUtilization);
break;
case METRICS_CURR_SOCKETPOWER:
*value = SMUQ10_ROUND(metrics->SocketPower) << 8;
*value = SMUQ10_ROUND(metrics->SocketPower) *
MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_HOTSPOT:
*value = SMUQ10_ROUND(metrics->MaxSocketTemperature) *