Re: [PATCH] drm/amd/pm: compute SMU v15.0.0 gpu_metrics from accumulator table

"Lazar, Lijo" <[email protected]> Mon, 10 Aug 2026 09:13:42 +0530
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>

On 07-Aug-26 5:56 PM, Shubhankar Milind Sardeshpande wrote:
> Rework smu_v15_0_0_get_gpu_metrics() to derive values from the raw
> accumulator double-buffer instead of the firmware-computed SmuMetrics_t
> snapshot:
> 
> - Add SMU_V15_IOD_AVG()/SMU_V15_CCX_AVG() helpers: wrapping_sub() delta
>    over the sampling window, scaled by the caller's unit factor, then
>    descaled from 1024x fixed-point with >>10 (scale before shift to keep
>    sub-unit precision); typeof() preserves each field's width.
> - Reuse the same macro in smu_v15_0_0_compute_all_metrics(), dropping
>    the open-coded div_u64(..., 1024) boilerplate.
> - Drop the unused smu_v15_0_0_get_gpu_metrics_table() helper and the
>    SMU_TABLE_SMU_METRICS allocation.
> 
> Signed-off-by: Shubhankar Milind Sardeshpande <[email protected]>
> Assisted-by: Claude:claude-opus-4.8

A couple of nits below -

Reviewed-by: Lijo Lazar <[email protected]>

> ---
>   .../inc/pmfw_if/smu15_driver_if_v15_0_0.h     |  46 ---
>   .../drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c  | 282 +++++++++---------
>   2 files changed, 140 insertions(+), 188 deletions(-)
> 
> diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h
> index a5d3e8d88860..94932d8f3e61 100644
> --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h
> +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h
> @@ -182,52 +182,6 @@ typedef struct {
>   	uint32_t Spare9[8];
>   } DpmClocks_t_v15_0_5;
>   
> -typedef struct {
> -  uint16_t CoreFrequency[16];          //Target core frequency [MHz]
> -  uint16_t CorePower[16];              //CAC calculated core power [mW]
> -  uint16_t CoreTemperature[16];        //TSEN measured core temperature [centi-C]
> -  uint16_t GfxTemperature;             //TSEN measured GFX temperature [centi-C]
> -  uint16_t SocTemperature;             //TSEN measured SOC temperature [centi-C]
> -  uint16_t StapmOpnLimit;              //Maximum IRM defined STAPM power limit [mW]
> -  uint16_t StapmCurrentLimit;          //Time filtered STAPM power limit [mW]
> -  uint16_t InfrastructureCpuMaxFreq;   //CCLK frequency limit enforced on classic cores [MHz]
> -  uint16_t InfrastructureGfxMaxFreq;   //GFXCLK frequency limit enforced on GFX [MHz]
> -  uint16_t SkinTemp;                   //Maximum skin temperature reported by APU and HS2 chassis sensors [centi-C]
> -  uint16_t GfxclkFrequency;            //Time filtered target GFXCLK frequency [MHz]
> -  uint16_t FclkFrequency;              //Time filtered target FCLK frequency [MHz]
> -  uint16_t GfxActivity;                //Time filtered GFX busy % [0-100]
> -  uint16_t SocclkFrequency;            //Time filtered target SOCCLK frequency [MHz]
> -  uint16_t VclkFrequency;              //Time filtered target VCLK frequency [MHz]
> -  uint16_t VcnActivity;                //Time filtered VCN busy % [0-100]
> -  uint16_t VpeclkFrequency;            //Time filtered target VPECLK frequency [MHz]
> -  uint16_t NpuclkFrequency;            //Time filtered target NPUCLK frequency [MHz]
> -  uint16_t NpuBusy[8];                 //Time filtered NPU per-column busy % [0-100]
> -  uint16_t DRAMReads;                  //Time filtered DRAM read bandwidth [MB/sec]
> -  uint16_t DRAMWrites;                 //Time filtered DRAM write bandwidth [MB/sec]
> -  uint16_t CoreC0Residency[16];        //Time filtered per-core C0 residency % [0-100]
> -  uint16_t NpuPower;                   //Time filtered NPU power [mW]
> -  uint32_t ApuPower;                   //Time filtered APU power [mW]
> -  uint32_t GfxPower;                   //Time filtered GFX power [mW]
> -  uint32_t dGpuPower;                  //Time filtered dGPU power [mW]
> -  uint32_t SocketPower;                //Time filtered power used for PPT/STAPM [APU+dGPU] [mW]
> -  uint32_t AllCorePower;               //Time filtered sum of core power across all cores in the socket [mW]
> -  uint32_t FilterAlphaValue;           //Metrics table alpha filter time constant [us]
> -  uint32_t MetricsCounter;             //Counter that is incremented on every metrics table update [PM_TIMER cycles]
> -  uint16_t MemclkFrequency;            //Time filtered target MEMCLK frequency [MHz]
> -  uint16_t AieclkFrequency;            //Time filtered target AIECLK frequency [MHz]
> -  uint16_t NpuReads;                   //Time filtered NPU read bandwidth [MB/sec]
> -  uint16_t NpuWrites;                  //Time filtered NPU write bandwidth [MB/sec]
> -  uint32_t ThrottleResidency_PROCHOT;  //Counter that is incremented on every metrics table update when PROCHOT was engaged [PM_TIMER cycles]
> -  uint32_t ThrottleResidency_SPL;      //Counter that is incremented on every metrics table update when SPL was engaged [PM_TIMER cycles]
> -  uint32_t ThrottleResidency_FPPT;     //Counter that is incremented on every metrics table update when fast PPT was engaged [PM_TIMER cycles]
> -  uint32_t ThrottleResidency_SPPT;     //Counter that is incremented on every metrics table update when slow PPT was engaged [PM_TIMER cycles]
> -  uint32_t ThrottleResidency_THM_VDD;  //Counter that is incremented on every metrics table update when VDD thermal throttling was engaged [PM_TIMER cycles]
> -  uint32_t ThrottleResidency_THM_SOC;  //Counter that is incremented on every metrics table update when SOC thermal throttling was engaged [PM_TIMER cycles]
> -  uint16_t Psys;                       //Time filtered Psys power [mW]
> -  uint16_t spare1;
> -  uint32_t spare[6];
> -} SmuMetrics_t;
> -
>   //ISP tile definitions
>   typedef enum {
>     TILE_XTILE = 0,         //ONO0
> diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
> index 12fe2bc0488e..2bcd20072583 100644
> --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
> +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
> @@ -203,8 +203,6 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu)
>   		PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
>   	SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t_v15_0_5),
>   		PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
> -	SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
> -		PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
>   
>   	smu_table->metrics_table = kzalloc_obj(SMU_15_0_0_MetricsInfo_t);
>   	if (!smu_table->metrics_table)
> @@ -348,36 +346,6 @@ static int smu_v15_0_0_set_default_dpm_tables(struct smu_context *smu)
>   				    smu_table->clocks_table, false);
>   }
>   
> -static int smu_v15_0_0_get_gpu_metrics_table(struct smu_context *smu,
> -							void *metrics_table,
> -							bool bypass_cache)
> -{
> -	struct smu_table_context *smu_table = &smu->smu_table;
> -	uint32_t table_size =
> -			smu_table->tables[SMU_TABLE_SMU_METRICS].size;
> -	int ret;
> -
> -	if (bypass_cache ||
> -		!smu_table->metrics_time ||
> -		time_after(jiffies, smu_table->metrics_time + msecs_to_jiffies(1))) {
> -		ret = smu_v15_0_0_update_table(smu,
> -						SMU_TABLE_SMU_METRICS,
> -						0,
> -						smu_table->metrics_table,
> -						false);
> -		if (ret) {
> -			dev_info(smu->adev->dev, "Failed to export SMU15_0_0 metrics table!\n");
> -			return ret;
> -		}
> -		smu_table->metrics_time = jiffies;
> -	}
> -
> -	if (metrics_table)
> -		memcpy(metrics_table, smu_table->metrics_table, table_size);
> -
> -	return 0;
> -}
> -
>   /*
>    * Fetch a fresh metrics sample into the inactive buffer.
>    * Returns 0 if a new sample was copied, 1 if the cached sample is still
> @@ -424,85 +392,74 @@ static int smu_v15_0_0_get_metrics_table(struct smu_context *smu,
>   }
>   
>   /*
> - * Accumulators monotonically increase and roll over at their type width.
> - * Use the kernel wrapping_sub() API to compute the delta so the subtraction
> - * wraps modulo 2^n (correct across a single rollover) without tripping any
> - * wrap-around sanitizers.
> + * All accumulators below are 1024x fixed-point values in their native unit.
> + * The average over the sampling window is (curr - prev) / AccumulationCounter,
> + * still 1024x fixed-point. We multiply by @scale first (to preserve the

Better name as it as Q10 -> https://en.wikipedia.org/wiki/Q_(number_format)

> + * sub-unit fractional bits the scaled unit needs) and only then descale by
> + * 1024 with a >>10 shift. wrapping_sub() handles a single accumulator rollover
> + * without tripping wrap sanitizers; typeof() keeps the modular subtraction at
> + * the field's real width (the *_ResidencyAcc fields are u32, the rest u64).
> + *
> + * @scale folds in the unit conversion the consumer expects:
> + *   1    -> native unit  (MHz, %)
> + *   1000 -> W->mW, GHz->MHz, GB/s->MB/s, V->mV
> + *   100  -> C->centi-C
> + */
> +#define SMU_V15_IOD_AVG(field, scale)						\
> +	((uint32_t)((div64_u64(wrapping_sub(typeof(c->field),			\
> +					    c->field, p->field),		\
> +			       counter) * (scale)) >> 10))
> +#define SMU_V15_CCX_AVG(field, scale)	

CoreAvg may be a better name. For readability, pass ccx/core also as 
arguments.

					\
> +	((uint32_t)((div64_u64(wrapping_sub(typeof(curr->CCX[ccx].field[core]),	\
> +					    curr->CCX[ccx].field[core],		\
> +					    prev->CCX[ccx].field[core]),	\
> +			       counter) * (scale)) >> 10))
> +
> +/*
> + * Compute the windowed average of every sensor exposed through read_sensor and
> + * cache it in avg_metric[]. Units here follow the hwmon/sysfs sensor ABI
> + * (milli-C, mW, mV), which differs from the gpu_metrics scaling used in
> + * smu_v15_0_0_get_gpu_metrics(). SMU_V15_IOD_AVG() relies on the c, p and
> + * counter locals declared below.
>    */
>   static void smu_v15_0_0_compute_all_metrics(
>   		uint32_t *avg_metric,
>   		MetricsTable_t *prev,
>   		MetricsTable_t *curr)
>   {
> -	uint64_t counter, val;
> -	uint32_t mw;
>   	MetricsTable_IOD_t *p = &prev->IOD;
>   	MetricsTable_IOD_t *c = &curr->IOD;
> +	uint64_t counter;
>   
>   	counter = wrapping_sub(u32, c->AccumulationCounter, p->AccumulationCounter);
>   	if (!counter)
>   		return;
>   
> -	/* Accumulator-based clock frequencies (fixed-point /1024) */
> -	val = wrapping_sub(u64, c->GfxclkFreqEffAcc, p->GfxclkFreqEffAcc);
> -	avg_metric[METRICS_AVERAGE_GFXCLK] = div_u64(div64_u64(val, counter), 1024);
> -
> -	val = wrapping_sub(u64, c->SocclkFreqEffAcc, p->SocclkFreqEffAcc);
> -	avg_metric[METRICS_AVERAGE_SOCCLK] = div_u64(div64_u64(val, counter), 1024);
> +	/* Effective clocks: accumulator already in MHz. */
> +	avg_metric[METRICS_AVERAGE_GFXCLK] = SMU_V15_IOD_AVG(GfxclkFreqEffAcc, 1);
> +	avg_metric[METRICS_AVERAGE_SOCCLK] = SMU_V15_IOD_AVG(SocclkFreqEffAcc, 1);
> +	avg_metric[METRICS_AVERAGE_VCLK] = SMU_V15_IOD_AVG(VclkFreqEffAcc, 1);
> +	avg_metric[METRICS_AVERAGE_UCLK] = SMU_V15_IOD_AVG(MemclkFreqEffAcc, 1);
> +	avg_metric[METRICS_AVERAGE_FCLK] = SMU_V15_IOD_AVG(FclkFreqEffAcc, 1);
> +	avg_metric[METRICS_AVERAGE_NPUCLK] = SMU_V15_IOD_AVG(NpuhclkFreqEffAcc, 1);
>   
> -	val = wrapping_sub(u64, c->VclkFreqEffAcc, p->VclkFreqEffAcc);
> -	avg_metric[METRICS_AVERAGE_VCLK] = div_u64(div64_u64(val, counter), 1024);
> +	/* Activity: accumulator already in %. */
> +	avg_metric[METRICS_AVERAGE_GFXACTIVITY] = SMU_V15_IOD_AVG(GfxBusyAcc, 1);
> +	avg_metric[METRICS_AVERAGE_VCNACTIVITY] = SMU_V15_IOD_AVG(VcnBusyAcc, 1);
>   
> -	val = wrapping_sub(u64, c->MemclkFreqEffAcc, p->MemclkFreqEffAcc);
> -	avg_metric[METRICS_AVERAGE_UCLK] = div_u64(div64_u64(val, counter), 1024);
> +	/* Power: accumulator in W -> mW (hwmon/debugfs). */
> +	avg_metric[METRICS_AVERAGE_SOCKETPOWER] = SMU_V15_IOD_AVG(ApuPowerAcc, 1000);
> +	avg_metric[METRICS_CURR_SOCKETPOWER] = SMU_V15_IOD_AVG(SystemPowerAcc, 1000);
>   
> -	val = wrapping_sub(u64, c->FclkFreqEffAcc, p->FclkFreqEffAcc);
> -	avg_metric[METRICS_AVERAGE_FCLK] = div_u64(div64_u64(val, counter), 1024);
> -
> -	val = wrapping_sub(u64, c->NpuhclkFreqEffAcc, p->NpuhclkFreqEffAcc);
> -	avg_metric[METRICS_AVERAGE_NPUCLK] = div_u64(div64_u64(val, counter), 1024);
> -
> -	/* Activity (fixed-point /1024) */
> -	val = wrapping_sub(u64, c->GfxBusyAcc, p->GfxBusyAcc);
> -	avg_metric[METRICS_AVERAGE_GFXACTIVITY] = div_u64(div64_u64(val, counter), 1024);
> -
> -	val = wrapping_sub(u64, c->VcnBusyAcc, p->VcnBusyAcc);
> -	avg_metric[METRICS_AVERAGE_VCNACTIVITY] = div_u64(div64_u64(val, counter), 1024);
> -
> -	/*
> -	 * Power: accumulator holds a 1024x fixed-point value in Watts.
> -	 * Average it into milliwatts, which is the unit expected by
> -	 * power sensor consumers (hwmon/debugfs).
> -	 */
> -	val = wrapping_sub(u64, c->ApuPowerAcc, p->ApuPowerAcc);
> -	mw = div_u64(div64_u64(val, counter) * 1000, 1024);
> -	avg_metric[METRICS_AVERAGE_SOCKETPOWER] = mw;
> -
> -	val = wrapping_sub(u64, c->SystemPowerAcc, p->SystemPowerAcc);
> -	mw = div_u64(div64_u64(val, counter) * 1000, 1024);
> -	avg_metric[METRICS_CURR_SOCKETPOWER] = mw;
> -
> -	/*
> -	 * Temperature: accumulator holds a 1024x fixed-point value in
> -	 * Celsius. Descale by 1024 and convert to millidegrees C as the
> -	 * hwmon/sysfs consumers expect (temp*_input is in millidegrees).
> -	 */
> -	val = wrapping_sub(u64, c->GFX_TempAcc, p->GFX_TempAcc);
> +	/* Temperature: accumulator in Celsius -> millidegrees C (temp*_input). */
>   	avg_metric[METRICS_TEMPERATURE_VRGFX] =
> -		div_u64(div64_u64(val, counter) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES, 1024);
> -
> -	val = wrapping_sub(u64, c->STT_APU_HotSpotTempAcc, p->STT_APU_HotSpotTempAcc);
> +		SMU_V15_IOD_AVG(GFX_TempAcc, SMU_TEMPERATURE_UNITS_PER_CENTIGRADES);
>   	avg_metric[METRICS_TEMPERATURE_HOTSPOT] =
> -		div_u64(div64_u64(val, counter) * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES, 1024);
> -
> -	/* Voltage: accumulator holds a 1024x fixed-point value in Volts;
> -	 * convert to millivolts for the hwmon/sysfs consumers.
> -	 */
> -	val = wrapping_sub(u64, c->VDDCR_GFX_TelemetryVoltage, p->VDDCR_GFX_TelemetryVoltage);
> -	avg_metric[METRICS_VOLTAGE_VDDGFX] = div_u64(div64_u64(val, counter) * 1000, 1024);
> +		SMU_V15_IOD_AVG(STT_APU_HotSpotTempAcc, SMU_TEMPERATURE_UNITS_PER_CENTIGRADES);
>   
> -	val = wrapping_sub(u64, c->VDDCR_SOC_TelemetryVoltage, p->VDDCR_SOC_TelemetryVoltage);
> -	avg_metric[METRICS_VOLTAGE_VDDSOC] = div_u64(div64_u64(val, counter) * 1000, 1024);
> +	/* Voltage: accumulator in Volts -> millivolts. */
> +	avg_metric[METRICS_VOLTAGE_VDDGFX] = SMU_V15_IOD_AVG(VDDCR_GFX_TelemetryVoltage, 1000);
> +	avg_metric[METRICS_VOLTAGE_VDDSOC] = SMU_V15_IOD_AVG(VDDCR_SOC_TelemetryVoltage, 1000);
>   }
>   
>   static int smu_v15_0_0_get_smu_metrics_data(struct smu_context *smu,
> @@ -714,61 +671,99 @@ static ssize_t smu_v15_0_0_get_gpu_metrics(struct smu_context *smu,
>   	struct gpu_metrics_v3_0 *gpu_metrics =
>   		(struct gpu_metrics_v3_0 *)smu_driver_table_ptr(
>   			smu, SMU_DRIVER_TABLE_GPU_METRICS);
> -	SmuMetrics_t metrics;
> -	int ret = 0;
> +	struct smu_table_context *smu_table = &smu->smu_table;
> +	SMU_15_0_0_MetricsInfo_t *metrics_info =
> +		(SMU_15_0_0_MetricsInfo_t *)smu_table->metrics_table;
> +	uint32_t all_core_power = 0;
> +	MetricsTable_IOD_t *p, *c;
> +	MetricsTable_t *prev, *curr;
> +	size_t i, ccx, core;
> +	uint32_t coreclk;
> +	uint64_t counter;
> +	int ret;
>   
> -	ret = smu_v15_0_0_get_gpu_metrics_table(smu, &metrics, false);
> -	if (ret)
> +	/* Refresh the accumulator double-buffer (may reuse a cached sample). */
> +	ret = smu_v15_0_0_get_metrics_table(smu, metrics_info);
> +	if (ret < 0)
>   		return ret;
>   
> +	curr = &metrics_info->metrics[metrics_info->active_idx];
> +	prev = &metrics_info->metrics[!metrics_info->active_idx];
> +	c = &curr->IOD;
> +	p = &prev->IOD;
> +
>   	smu_cmn_init_soft_gpu_metrics(gpu_metrics, 3, 0);
>   
> -	gpu_metrics->temperature_gfx = metrics.GfxTemperature;
> -	gpu_metrics->temperature_soc = metrics.SocTemperature;
> -	memcpy(&gpu_metrics->temperature_core[0],
> -		&metrics.CoreTemperature[0],
> -		sizeof(uint16_t) * 16);
> -	gpu_metrics->temperature_skin = metrics.SkinTemp;
> -
> -	gpu_metrics->average_gfx_activity = metrics.GfxActivity;
> -	gpu_metrics->average_vcn_activity = metrics.VcnActivity;
> -
> -	memcpy(&gpu_metrics->average_core_c0_activity[0],
> -		&metrics.CoreC0Residency[0],
> -		sizeof(uint16_t) * 16);
> -	gpu_metrics->average_dram_reads = metrics.DRAMReads;
> -	gpu_metrics->average_dram_writes = metrics.DRAMWrites;
> -
> -	gpu_metrics->average_socket_power = metrics.SocketPower;
> -	gpu_metrics->average_apu_power = metrics.ApuPower;
> -	gpu_metrics->average_gfx_power = metrics.GfxPower;
> -	gpu_metrics->average_dgpu_power = metrics.dGpuPower;
> -	gpu_metrics->average_all_core_power = metrics.AllCorePower;
> -	gpu_metrics->average_sys_power = metrics.Psys;
> -	memcpy(&gpu_metrics->average_core_power[0],
> -		&metrics.CorePower[0],
> -		sizeof(uint16_t) * 16);
> -
> -	gpu_metrics->average_gfxclk_frequency = metrics.GfxclkFrequency;
> -	gpu_metrics->average_socclk_frequency = metrics.SocclkFrequency;
> -	gpu_metrics->average_vpeclk_frequency = metrics.VpeclkFrequency;
> -	gpu_metrics->average_fclk_frequency = metrics.FclkFrequency;
> -	gpu_metrics->average_vclk_frequency = metrics.VclkFrequency;
> -	gpu_metrics->average_uclk_frequency = metrics.MemclkFrequency;
> -
> -	memcpy(&gpu_metrics->current_coreclk[0],
> -		&metrics.CoreFrequency[0],
> -		sizeof(uint16_t) * 16);
> -	gpu_metrics->current_core_maxfreq = metrics.InfrastructureCpuMaxFreq;
> -	gpu_metrics->current_gfx_maxfreq = metrics.InfrastructureGfxMaxFreq;
> -
> -	gpu_metrics->throttle_residency_prochot = metrics.ThrottleResidency_PROCHOT;
> -	gpu_metrics->throttle_residency_spl = metrics.ThrottleResidency_SPL;
> -	gpu_metrics->throttle_residency_fppt = metrics.ThrottleResidency_FPPT;
> -	gpu_metrics->throttle_residency_sppt = metrics.ThrottleResidency_SPPT;
> -	gpu_metrics->throttle_residency_thm_soc = metrics.ThrottleResidency_THM_SOC;
> -
> -	gpu_metrics->time_filter_alphavalue = metrics.FilterAlphaValue;
> +	counter = wrapping_sub(u32, c->AccumulationCounter, p->AccumulationCounter);
> +	if (counter) {
> +		/* Temperatures: accumulator in Celsius -> centi-C. */
> +		gpu_metrics->temperature_gfx = SMU_V15_IOD_AVG(GFX_TempAcc, 100);
> +		gpu_metrics->temperature_soc = SMU_V15_IOD_AVG(SOC_TempAcc, 100);
> +		gpu_metrics->temperature_skin = SMU_V15_IOD_AVG(STT_APU_SkinTempAcc, 100);
> +
> +		/* Activity: accumulator already in %. */
> +		gpu_metrics->average_gfx_activity = SMU_V15_IOD_AVG(GfxBusyAcc, 1);
> +		gpu_metrics->average_vcn_activity = SMU_V15_IOD_AVG(VcnBusyAcc, 1);
> +
> +		/* Bandwidth: accumulator in GB/s -> MB/s. */
> +		gpu_metrics->average_dram_reads = SMU_V15_IOD_AVG(DramReadBandwidth, 1000);
> +		gpu_metrics->average_dram_writes = SMU_V15_IOD_AVG(DramWriteBandwidth, 1000);
> +
> +		/* Power: accumulator in W -> mW. */
> +		gpu_metrics->average_apu_power = SMU_V15_IOD_AVG(ApuPowerAcc, 1000);
> +		gpu_metrics->average_gfx_power = SMU_V15_IOD_AVG(VDDCR_GFX_TelemetryPower, 1000);
> +		gpu_metrics->average_dgpu_power = SMU_V15_IOD_AVG(dGpuPowerAcc, 1000);
> +		gpu_metrics->average_sys_power = SMU_V15_IOD_AVG(SystemPowerAcc, 1000);
> +		/* No dedicated socket accumulator: APU + dGPU, matching PPT/STAPM. */
> +		gpu_metrics->average_socket_power =
> +			gpu_metrics->average_apu_power + gpu_metrics->average_dgpu_power;
> +
> +		/* Effective clocks: accumulator already in MHz. */
> +		gpu_metrics->average_gfxclk_frequency = SMU_V15_IOD_AVG(GfxclkFreqEffAcc, 1);
> +		gpu_metrics->average_socclk_frequency = SMU_V15_IOD_AVG(SocclkFreqEffAcc, 1);
> +		gpu_metrics->average_vpeclk_frequency = SMU_V15_IOD_AVG(VpeclkFreqEffAcc, 1);
> +		gpu_metrics->average_fclk_frequency = SMU_V15_IOD_AVG(FclkFreqEffAcc, 1);
> +		gpu_metrics->average_vclk_frequency = SMU_V15_IOD_AVG(VclkFreqEffAcc, 1);
> +		gpu_metrics->average_uclk_frequency = SMU_V15_IOD_AVG(MemclkFreqEffAcc, 1);
> +
> +		/* Per-core metrics: report only active cores (non-zero effective
> +		 * clock), flattening CCX-major into the 16 gpu_metrics slots.
> +		 */
> +		i = 0;
> +		for (ccx = 0; ccx < METRIC_CCX_MAX &&
> +			      i < ARRAY_SIZE(gpu_metrics->current_coreclk); ccx++) {
> +			for (core = 0; core < NUM_MAX_CORES &&
> +				       i < ARRAY_SIZE(gpu_metrics->current_coreclk);
> +			     core++) {
> +				/* Effective clock gates whether the core is active. */
> +				coreclk = SMU_V15_CCX_AVG(Core_FREQEFF, 1000);
> +				if (!coreclk)
> +					continue;
> +
> +				gpu_metrics->current_coreclk[i] = coreclk;
> +				gpu_metrics->temperature_core[i] =
> +					SMU_V15_CCX_AVG(Core_TEMP, 100);
> +				gpu_metrics->average_core_c0_activity[i] =
> +					SMU_V15_CCX_AVG(Core_C0, 1);
> +				gpu_metrics->average_core_power[i] =
> +					SMU_V15_CCX_AVG(Core_POWER, 1000);
> +				all_core_power += gpu_metrics->average_core_power[i];
> +				i++;
> +			}
> +		}
> +		gpu_metrics->average_all_core_power = all_core_power;

The name gives the impression that it's average of all core powers 
(all_core_power / total_num_cores). Currently, it is the sum of of all 
core powers.

Thanks,
Lijo

> +
> +		/* Throttle residencies: accumulator in % (semantics differ from
> +		 * the PM_TIMER cycle counters exposed by the legacy metrics path).
> +		 */
> +		gpu_metrics->throttle_residency_prochot =
> +			SMU_V15_IOD_AVG(PROCHOT_ResidencyAcc, 1);
> +		gpu_metrics->throttle_residency_spl = SMU_V15_IOD_AVG(SPL_ResidencyAcc, 1);
> +		gpu_metrics->throttle_residency_fppt = SMU_V15_IOD_AVG(fPPT_ResidencyAcc, 1);
> +		gpu_metrics->throttle_residency_sppt = SMU_V15_IOD_AVG(sPPT_ResidencyAcc, 1);
> +		gpu_metrics->throttle_residency_thm_soc = SMU_V15_IOD_AVG(THM_ResidencyAcc, 1);
> +	}
> +
>   	gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
>   
>   	*table = (void *)gpu_metrics;
> @@ -778,6 +773,9 @@ static ssize_t smu_v15_0_0_get_gpu_metrics(struct smu_context *smu,
>   	return sizeof(struct gpu_metrics_v3_0);
>   }
>   
> +#undef SMU_V15_IOD_AVG
> +#undef SMU_V15_CCX_AVG
> +
>   static int smu_v15_0_0_mode2_reset(struct smu_context *smu)
>   {
>   	int ret;