AMD General

Reviewed-by: Asad Kamal <[email protected]>

Thanks & Regards
Asad

-----Original Message-----
From: Lazar, Lijo <[email protected]>
Sent: Friday, September 18, 2026 9:28 AM
To: [email protected]
Cc: Zhang, Hawking <[email protected]>; Deucher, Alexander 
<[email protected]>; Kamal, Asad <[email protected]>
Subject: [PATCH] drm/amd/pm: Create NPM sysfs files from capability

Advertise each NPM board attribute from a per-field R/W bitmap instead of 
probing a single sensor. SMU 13.0.6/13.0.12 reports the cap only when NPM 
metrics and MaxNodePowerLimit are present.

Signed-off-by: Lijo Lazar <[email protected]>
Assisted-by: Cursor Grok 4.6
---
 .../gpu/drm/amd/include/kgd_pp_interface.h    | 16 +++++++
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c           | 23 +++++++++
 drivers/gpu/drm/amd/pm/amdgpu_pm.c            | 47 +++++++++++++------
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h       |  1 +
 drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c     | 11 +++++
 drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h |  7 +++  
.../drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c | 19 ++++++++  
.../drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c  |  1 +  
.../drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h  |  1 +
 9 files changed, 111 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h 
b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
index 606d48115301..5e8476c4b808 100644
--- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
@@ -174,6 +174,21 @@ enum amd_pp_sensors {
        AMDGPU_PP_SENSOR_UBB_POWER_LIMIT,
 };

+/*
+ * NPM sysfs capability bitmap: two bits per field, R then W.
+ * 00 = unsupported, 01 = read-only, 1x = writable (or RW).
+ */
+enum amdgpu_npm_cap_field {
+       AMDGPU_NPM_CAP_CUR_NODE_POWER_LIMIT,
+       AMDGPU_NPM_CAP_NODE_POWER,
+       AMDGPU_NPM_CAP_GLOBAL_PPT_RESID,
+       AMDGPU_NPM_CAP_MAX_NODE_POWER_LIMIT,
+       AMDGPU_NPM_CAP_NPM_STATUS,
+};
+
+#define AMDGPU_NPM_CAP_R(field)        BIT_ULL((field) * 2)
+#define AMDGPU_NPM_CAP_W(field)        BIT_ULL((field) * 2 + 1)
+
 enum amd_pp_task {
        AMD_PP_TASK_DISPLAY_CONFIG_CHANGE,
        AMD_PP_TASK_ENABLE_USER_STATE,
@@ -513,6 +528,7 @@ struct amd_pm_funcs {
        ssize_t (*get_gpu_metrics)(void *handle, void **table);
        ssize_t (*get_temp_metrics)(void *handle, enum smu_temp_metric_type 
type, void *table);
        bool (*temp_metrics_is_supported)(void *handle, enum 
smu_temp_metric_type type);
+       u64 (*get_npm_cap)(void *handle);
        ssize_t (*get_xcp_metrics)(void *handle, int xcp_id, void *table);
        ssize_t (*get_pm_metrics)(void *handle, void *pmmetrics, size_t size);
        int (*set_watermarks_for_clock_ranges)(void *handle, diff --git 
a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index c70fd6691461..df5bbdab1ea3 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -2101,6 +2101,29 @@ bool amdgpu_dpm_is_temp_metrics_supported(struct 
amdgpu_device *adev,
        return support_temp_metrics;
 }

+/**
+ * amdgpu_dpm_get_npm_cap - Return NPM sysfs capability bitmap
+ * @adev: Pointer to the device.
+ *
+ * Two bits per field (R then W). Both 0 means the field is unsupported.
+ *
+ * Return: Capability bitmap, or 0 if NPM is not supported.
+ */
+u64 amdgpu_dpm_get_npm_cap(struct amdgpu_device *adev) {
+       const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+       u64 cap = 0;
+
+       if (!pp_funcs || !pp_funcs->get_npm_cap)
+               return 0;
+
+       mutex_lock(&adev->pm.mutex);
+       cap = pp_funcs->get_npm_cap(adev->powerplay.pp_handle);
+       mutex_unlock(&adev->pm.mutex);
+
+       return cap;
+}
+
 /**
  * amdgpu_dpm_get_xcp_metrics - Retrieve metrics for a specific compute
  * partition
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c 
b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index c0e677328af5..6f8252376127 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -4726,6 +4726,37 @@ static int amdgpu_od_set_init(struct amdgpu_device *adev)
        return ret;
 }

+static void amdgpu_pm_npm_sysfs_init(struct amdgpu_device *adev) {
+       static const struct {
+               struct device_attribute *attr;
+               unsigned int field;
+       } files[] = {
+               { &dev_attr_cur_node_power_limit, 
AMDGPU_NPM_CAP_CUR_NODE_POWER_LIMIT },
+               { &dev_attr_node_power, AMDGPU_NPM_CAP_NODE_POWER },
+               { &dev_attr_global_ppt_resid, AMDGPU_NPM_CAP_GLOBAL_PPT_RESID },
+               { &dev_attr_max_node_power_limit, 
AMDGPU_NPM_CAP_MAX_NODE_POWER_LIMIT },
+               { &dev_attr_npm_status, AMDGPU_NPM_CAP_NPM_STATUS },
+       };
+       u64 cap = amdgpu_dpm_get_npm_cap(adev);
+       int i;
+
+       if (!cap)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(files); i++) {
+               unsigned int bits = (cap >> (files[i].field * 2)) & 0x3;
+               struct device_attribute *attr = files[i].attr;
+
+               if (!bits)
+                       continue;
+
+               attr->attr.mode = ((bits & BIT(1)) && attr->store) ? 0644 : 
0444;
+               sysfs_add_file_to_group(&adev->dev->kobj, &attr->attr,
+                                       amdgpu_board_attr_group.name);
+       }
+}
+
 int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)  {
        enum amdgpu_sriov_vf_mode mode;
@@ -4797,21 +4828,7 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
                                            &amdgpu_board_attr_group);
                if (ret)
                        goto err_out1;
-               if (amdgpu_pm_get_sensor_generic(adev, 
AMDGPU_PP_SENSOR_MAXNODEPOWERLIMIT,
-                                                (void *)&tmp) != -EOPNOTSUPP) {
-                       sysfs_add_file_to_group(&adev->dev->kobj,
-                                               
&dev_attr_cur_node_power_limit.attr,
-                                               amdgpu_board_attr_group.name);
-                       sysfs_add_file_to_group(&adev->dev->kobj, 
&dev_attr_node_power.attr,
-                                               amdgpu_board_attr_group.name);
-                       sysfs_add_file_to_group(&adev->dev->kobj, 
&dev_attr_global_ppt_resid.attr,
-                                               amdgpu_board_attr_group.name);
-                       sysfs_add_file_to_group(&adev->dev->kobj,
-                                               
&dev_attr_max_node_power_limit.attr,
-                                               amdgpu_board_attr_group.name);
-                       sysfs_add_file_to_group(&adev->dev->kobj, 
&dev_attr_npm_status.attr,
-                                               amdgpu_board_attr_group.name);
-               }
+               amdgpu_pm_npm_sysfs_init(adev);
                if (amdgpu_pm_get_sensor_generic(adev, 
AMDGPU_PP_SENSOR_UBB_POWER_LIMIT,
                                                 (void *)&tmp) != -EOPNOTSUPP) {
                        sysfs_add_file_to_group(&adev->dev->kobj,
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h 
b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index 8e8a052e1d53..ed1e7385243d 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -614,5 +614,6 @@ int amdgpu_dpm_reset_vcn(struct amdgpu_device *adev, 
uint32_t inst_mask);  bool amdgpu_dpm_reset_vcn_is_supported(struct 
amdgpu_device *adev);  bool amdgpu_dpm_is_temp_metrics_supported(struct 
amdgpu_device *adev,
                                          enum smu_temp_metric_type type);
+u64 amdgpu_dpm_get_npm_cap(struct amdgpu_device *adev);

 #endif
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c 
b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index 4290a4872f3b..0a6daff8766f 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -4002,6 +4002,16 @@ static bool smu_temp_metrics_is_supported(void *handle, 
enum smu_temp_metric_typ
        return ret;
 }

+static u64 smu_get_npm_cap(void *handle) {
+       struct smu_context *smu = handle;
+
+       if (!smu->pm_enabled || !smu->ppt_funcs || !smu->ppt_funcs->get_npm_cap)
+               return 0;
+
+       return smu->ppt_funcs->get_npm_cap(smu); }
+
 static ssize_t smu_sys_get_xcp_metrics(void *handle, int xcp_id, void *table)  
{
        struct smu_context *smu = handle;
@@ -4076,6 +4086,7 @@ static const struct amd_pm_funcs swsmu_pm_funcs = {
        .get_xcp_metrics                  = smu_sys_get_xcp_metrics,
        .get_temp_metrics             = smu_sys_get_temp_metrics,
        .temp_metrics_is_supported      = smu_temp_metrics_is_supported,
+       .get_npm_cap                  = smu_get_npm_cap,
 };

 int smu_wait_for_event(struct smu_context *smu, enum smu_event_type event, 
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h 
b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index ab43a5cd0dcd..76625cac74cb 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -1620,6 +1620,13 @@ struct pptable_funcs {
         */
        ssize_t (*get_xcp_metrics)(struct smu_context *smu, int xcp_id,
                                   void *table);
+       /**
+        * @get_npm_cap: Return NPM sysfs capability bitmap.
+        *
+        * Two bits per field (R then W). Both 0 means the field is
+        * unsupported.
+        */
+       u64 (*get_npm_cap)(struct smu_context *smu);
        /**
         * @ras_send_msg: Send a message with a parameter from Ras
         * &msg: Type of message.
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c 
b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
index d8ddad531d26..0ca5973391e8 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
@@ -695,6 +695,25 @@ int smu_v13_0_12_get_npm_data(struct smu_context *smu,
        return ret;
 }

+u64 smu_v13_0_12_get_npm_cap(struct smu_context *smu) {
+       struct PPTable_t *pptable =
+               (struct PPTable_t *)smu->smu_table.driver_pptable;
+       u64 cap = 0;
+
+       if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(NPM_METRICS)) ||
+           !pptable->MaxNodePowerLimit)
+               return 0;
+
+       cap |= AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_CUR_NODE_POWER_LIMIT);
+       cap |= AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_NODE_POWER);
+       cap |= AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_GLOBAL_PPT_RESID);
+       cap |= AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_MAX_NODE_POWER_LIMIT);
+       cap |= AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_NPM_STATUS);
+
+       return cap;
+}
+
 static ssize_t smu_v13_0_12_get_temp_metrics(struct smu_context *smu,
                                             enum smu_temp_metric_type type, 
void *table)  { diff --git 
a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c 
b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
index ee380488d212..83a5a4614a4b 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
@@ -3312,6 +3312,7 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = 
{
        .get_gpu_metrics = smu_v13_0_6_get_gpu_metrics,
        .get_pm_metrics = smu_v13_0_6_get_pm_metrics,
        .get_xcp_metrics = smu_v13_0_6_get_xcp_metrics,
+       .get_npm_cap = smu_v13_0_12_get_npm_cap,
        .get_thermal_temperature_range = 
smu_v13_0_6_get_thermal_temperature_range,
        .mode1_reset_is_support = smu_v13_0_6_is_mode1_reset_supported,
        .mode1_reset = smu_v13_0_6_mode1_reset, diff --git 
a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h 
b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h
index 6554780afff7..1b6926625cc3 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h
@@ -111,6 +111,7 @@ void smu_v13_0_12_tables_fini(struct smu_context *smu);  
int smu_v13_0_12_get_npm_data(struct smu_context *smu,
                              enum amd_pp_sensors sensor,
                              uint32_t *value);
+u64 smu_v13_0_12_get_npm_cap(struct smu_context *smu);
 int smu_v13_0_12_get_system_power(struct smu_context *smu,
                                  enum amd_pp_sensors sensor,
                                  uint32_t *value);
--
2.49.0

Reply via email to