* [PATCH v2 1/6] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers
2026-07-13 18:39 [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
@ 2026-07-13 18:39 ` Shyam Sundar S K
2026-07-13 18:39 ` [PATCH v2 2/6] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended " Shyam Sundar S K
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Shyam Sundar S K @ 2026-07-13 18:39 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, Patil.Reddy, mario.limonciello,
Shyam Sundar S K
Different AMD platforms use varying SMU register layouts for PMF-SMU
mailbox communication. The register offsets are currently hardcoded as
AMD_PMF_REGISTER_MESSAGE, AMD_PMF_REGISTER_RESPONSE and
AMD_PMF_REGISTER_ARGUMENT directly in amd_pmf_send_cmd() and
amd_pmf_dump_registers(), making it difficult to support platforms that
use a different mailbox register layout without scattering per-platform
conditionals across the send path.
Introduce struct amd_pmf_smu_regs to capture the SoC-specific SMU
mailbox register offsets (msg_reg, resp_reg, arg_reg) and add a
pointer to it in struct amd_pmf_dev. RMB, PS, 1AH_M20H and 1AH_M60H
all share the same legacy register layout and point to a single shared
amd_pmf_smu_regs_v1 instance, avoiding redundant struct definitions.
Convert the pmf_pci_ids[] table from PCI_DEVICE() to PCI_DEVICE_DATA(),
embedding the smu_regs pointer directly as driver_data. Introduce
amd_pmf_get_smu_mb_offset() which resolves the matching PCI entry via
pci_match_id() at probe time and assigns driver_data to dev->smu_regs.
Update all SMU register accesses in amd_pmf_send_cmd() and
amd_pmf_dump_registers() to go through dev->smu_regs. Remove the
hardcoded register offset references from the send path. New platform
support requires only a new smu_regs instance and a corresponding
PCI_DEVICE_DATA() entry.
No functional changes for existing platforms.
Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
drivers/platform/x86/amd/pmf/core.c | 51 +++++++++++++++++++++--------
drivers/platform/x86/amd/pmf/pmf.h | 12 +++++++
2 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 58d86b4c2828..3018c861776b 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -176,13 +176,13 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
{
u32 value;
- value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_RESPONSE);
+ value = amd_pmf_reg_read(dev, dev->smu_regs->resp_reg);
dev_dbg(dev->dev, "AMD_PMF_REGISTER_RESPONSE:%x\n", value);
- value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
+ value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg);
dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value);
- value = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_MESSAGE);
+ value = amd_pmf_reg_read(dev, dev->smu_regs->msg_reg);
dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
}
@@ -208,7 +208,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
guard(mutex)(&dev->lock);
/* Wait until we get a valid response */
- rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
+ rc = readx_poll_timeout(ioread32, dev->regbase + dev->smu_regs->resp_reg,
val, val != 0, PMF_MSG_DELAY_MIN_US,
PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
if (rc) {
@@ -217,16 +217,16 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
}
/* Write zero to response register */
- amd_pmf_reg_write(dev, AMD_PMF_REGISTER_RESPONSE, 0);
+ amd_pmf_reg_write(dev, dev->smu_regs->resp_reg, 0);
/* Write argument into argument register */
- amd_pmf_reg_write(dev, AMD_PMF_REGISTER_ARGUMENT, arg);
+ amd_pmf_reg_write(dev, dev->smu_regs->arg_reg, arg);
/* Write message ID to message ID register */
- amd_pmf_reg_write(dev, AMD_PMF_REGISTER_MESSAGE, message);
+ amd_pmf_reg_write(dev, dev->smu_regs->msg_reg, message);
/* Wait until we get a valid response */
- rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
+ rc = readx_poll_timeout(ioread32, dev->regbase + dev->smu_regs->resp_reg,
val, val != 0, PMF_MSG_DELAY_MIN_US,
PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
if (rc) {
@@ -239,7 +239,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
if (get) {
/* PMFW may take longer time to return back the data */
usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
- *data = amd_pmf_reg_read(dev, AMD_PMF_REGISTER_ARGUMENT);
+ *data = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg);
}
break;
case AMD_PMF_RESULT_CMD_REJECT_BUSY:
@@ -262,11 +262,18 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
return rc;
}
+/* RMB, PS, 1AH_M20H and 1AH_M60H share the same v1 SMU mailbox registers */
+static const struct amd_pmf_smu_regs amd_pmf_smu_regs_v1 = {
+ .msg_reg = AMD_PMF_REGISTER_MESSAGE,
+ .resp_reg = AMD_PMF_REGISTER_RESPONSE,
+ .arg_reg = AMD_PMF_REGISTER_ARGUMENT,
+};
+
static const struct pci_device_id pmf_pci_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RMB) },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M60H_ROOT) },
+ { PCI_DEVICE_DATA(AMD, CPU_ID_RMB, &amd_pmf_smu_regs_v1) },
+ { PCI_DEVICE_DATA(AMD, CPU_ID_PS, &amd_pmf_smu_regs_v1) },
+ { PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, &amd_pmf_smu_regs_v1) },
+ { PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, &amd_pmf_smu_regs_v1) },
{ }
};
@@ -536,6 +543,19 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
}
}
+static int amd_pmf_get_smu_mb_offset(struct amd_pmf_dev *pdev, struct pci_dev *rdev)
+{
+ const struct pci_device_id *id;
+
+ id = pci_match_id(pmf_pci_ids, rdev);
+ if (!id)
+ return -ENODEV;
+
+ pdev->smu_regs = (const struct amd_pmf_smu_regs *)id->driver_data;
+
+ return 0;
+}
+
static const struct acpi_device_id amd_pmf_acpi_ids[] = {
{"AMDI0100", 0x100},
{"AMDI0102", 0},
@@ -624,6 +644,11 @@ static int amd_pmf_probe(struct platform_device *pdev)
if (err)
return err;
+ /* Populate smu_regs with SoC-specific SMU mailbox register offsets */
+ err = amd_pmf_get_smu_mb_offset(dev, rdev);
+ if (err)
+ return err;
+
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
amd_pmf_dbgfs_register(dev);
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 752fa5dd2267..7a8fd9d399de 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -29,6 +29,10 @@
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122
+/* Aliases required by PCI_DEVICE_DATA() macro naming convention */
+#define PCI_DEVICE_ID_AMD_CPU_ID_RMB AMD_CPU_ID_RMB
+#define PCI_DEVICE_ID_AMD_CPU_ID_PS AMD_CPU_ID_PS
+
struct cookie_header {
u32 sign;
u32 length;
@@ -392,6 +396,13 @@ struct pmf_cbi_ring_buffer {
int tail;
};
+/* SoC-specific SMU mailbox register offsets */
+struct amd_pmf_smu_regs {
+ u32 msg_reg;
+ u32 resp_reg;
+ u32 arg_reg;
+};
+
struct amd_pmf_dev {
void __iomem *regbase;
void __iomem *smu_virt_addr;
@@ -444,6 +455,7 @@ struct amd_pmf_dev {
struct mutex cbi_mutex; /* Protects ring buffer access */
struct mutex metrics_mutex;
u32 bios_output[BIOS_OUTPUT_MAX];
+ const struct amd_pmf_smu_regs *smu_regs;
};
struct apmf_sps_prop_granular_v2 {
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 2/6] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended SMU mailbox registers
2026-07-13 18:39 [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
2026-07-13 18:39 ` [PATCH v2 1/6] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers Shyam Sundar S K
@ 2026-07-13 18:39 ` Shyam Sundar S K
2026-07-13 18:39 ` [PATCH v2 3/6] platform/x86/amd/pmf: Move metrics code to dedicated file Shyam Sundar S K
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Shyam Sundar S K @ 2026-07-13 18:39 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, Patil.Reddy, mario.limonciello,
Shyam Sundar S K
Add PCI device ID (0x115b) and ACPI ID (AMDI0109) to enable PMF driver
support for the AMD 1AH_M80H (Family 1AH Model 80H).
The 1AH_M80H platform introduces an extended SMU mailbox interface that
uses three argument registers instead of the single register used by
earlier platforms. Define five new register offsets for the 1AH_M80H
mailbox: message, response and three argument registers. The extended
argument registers are required because 1AH_M80H exposes metrics through
a firmware managed DRAM region. The GET_METRICS_TABLE_DRAM_ADDR command
returns a 64-bit physical address split across arg_reg[0] (low 32-bit)
and arg_reg[1] (high 32-bit), with the metrics table size in arg_reg[2].
Define amd_pmf_smu_regs_v2 to capture this extended register layout and
register it in pmf_pci_ids[] via PCI_DEVICE_DATA(), keeping the existing
amd_pmf_smu_regs_v1 shared instance for all prior platforms unchanged.
Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
drivers/platform/x86/amd/pmf/core.c | 28 ++++++++++++++++++++++++----
drivers/platform/x86/amd/pmf/pmf.h | 3 ++-
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 3018c861776b..0f85ac0ba803 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -26,6 +26,13 @@
#define AMD_PMF_REGISTER_RESPONSE 0xA78
#define AMD_PMF_REGISTER_ARGUMENT 0xA58
+/* PMF-SMU communication registers for 1AH_M80H */
+#define AMD_PMF_REGISTER_MESSAGE_V2 0xA04
+#define AMD_PMF_REGISTER_RESPONSE_V2 0xA08
+#define AMD_PMF_REGISTER_ARGUMENT0_V2 0xA0C
+#define AMD_PMF_REGISTER_ARGUMENT1_V2 0xAAC
+#define AMD_PMF_REGISTER_ARGUMENT2_V2 0xAB0
+
/* Base address of SMU for mapping physical address to virtual address */
#define AMD_PMF_MAPPING_SIZE 0x01000
#define AMD_PMF_BASE_ADDR_OFFSET 0x10000
@@ -179,7 +186,7 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
value = amd_pmf_reg_read(dev, dev->smu_regs->resp_reg);
dev_dbg(dev->dev, "AMD_PMF_REGISTER_RESPONSE:%x\n", value);
- value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg);
+ value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[0]);
dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value);
value = amd_pmf_reg_read(dev, dev->smu_regs->msg_reg);
@@ -220,7 +227,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
amd_pmf_reg_write(dev, dev->smu_regs->resp_reg, 0);
/* Write argument into argument register */
- amd_pmf_reg_write(dev, dev->smu_regs->arg_reg, arg);
+ amd_pmf_reg_write(dev, dev->smu_regs->arg_reg[0], arg);
/* Write message ID to message ID register */
amd_pmf_reg_write(dev, dev->smu_regs->msg_reg, message);
@@ -239,7 +246,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
if (get) {
/* PMFW may take longer time to return back the data */
usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
- *data = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg);
+ *data = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[0]);
}
break;
case AMD_PMF_RESULT_CMD_REJECT_BUSY:
@@ -266,7 +273,18 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
static const struct amd_pmf_smu_regs amd_pmf_smu_regs_v1 = {
.msg_reg = AMD_PMF_REGISTER_MESSAGE,
.resp_reg = AMD_PMF_REGISTER_RESPONSE,
- .arg_reg = AMD_PMF_REGISTER_ARGUMENT,
+ .arg_reg = { AMD_PMF_REGISTER_ARGUMENT, 0, 0 },
+};
+
+/* 1AH_M80H uses an extended mailbox with three argument registers */
+static const struct amd_pmf_smu_regs amd_pmf_smu_regs_v2 = {
+ .msg_reg = AMD_PMF_REGISTER_MESSAGE_V2,
+ .resp_reg = AMD_PMF_REGISTER_RESPONSE_V2,
+ .arg_reg = {
+ AMD_PMF_REGISTER_ARGUMENT0_V2,
+ AMD_PMF_REGISTER_ARGUMENT1_V2,
+ AMD_PMF_REGISTER_ARGUMENT2_V2,
+ },
};
static const struct pci_device_id pmf_pci_ids[] = {
@@ -274,6 +292,7 @@ static const struct pci_device_id pmf_pci_ids[] = {
{ PCI_DEVICE_DATA(AMD, CPU_ID_PS, &amd_pmf_smu_regs_v1) },
{ PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, &amd_pmf_smu_regs_v1) },
{ PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, &amd_pmf_smu_regs_v1) },
+ { PCI_DEVICE_DATA(AMD, 1AH_M80H_ROOT, &amd_pmf_smu_regs_v2) },
{ }
};
@@ -563,6 +582,7 @@ static const struct acpi_device_id amd_pmf_acpi_ids[] = {
{"AMDI0105", 0},
{"AMDI0107", 0},
{"AMDI0108", 0},
+ {"AMDI0109", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 7a8fd9d399de..088edfab08f0 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -28,6 +28,7 @@
#define AMD_CPU_ID_PS 0x14e8
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122
+#define PCI_DEVICE_ID_AMD_1AH_M80H_ROOT 0x115b
/* Aliases required by PCI_DEVICE_DATA() macro naming convention */
#define PCI_DEVICE_ID_AMD_CPU_ID_RMB AMD_CPU_ID_RMB
@@ -400,7 +401,7 @@ struct pmf_cbi_ring_buffer {
struct amd_pmf_smu_regs {
u32 msg_reg;
u32 resp_reg;
- u32 arg_reg;
+ u32 arg_reg[3];
};
struct amd_pmf_dev {
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 3/6] platform/x86/amd/pmf: Move metrics code to dedicated file
2026-07-13 18:39 [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
2026-07-13 18:39 ` [PATCH v2 1/6] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers Shyam Sundar S K
2026-07-13 18:39 ` [PATCH v2 2/6] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended " Shyam Sundar S K
@ 2026-07-13 18:39 ` Shyam Sundar S K
2026-07-21 16:09 ` Ilpo Järvinen
2026-07-13 18:39 ` [PATCH v2 4/6] platform/x86/amd/pmf: Use upper/lower_32_bits() in amd_pmf_set_dram_addr() Shyam Sundar S K
` (3 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Shyam Sundar S K @ 2026-07-13 18:39 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, Patil.Reddy, mario.limonciello,
Shyam Sundar S K, Mario Limonciello
Refactor metrics related code from core.c into a new metrics.c file
to improve code organization and maintainability. The metrics
functionality is evolving with new platform support, warranting a
separate file.
No functional changes.
Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
drivers/platform/x86/amd/pmf/Makefile | 2 +-
drivers/platform/x86/amd/pmf/core.c | 156 +--------------------
drivers/platform/x86/amd/pmf/metrics.c | 180 +++++++++++++++++++++++++
drivers/platform/x86/amd/pmf/pmf.h | 3 +
4 files changed, 186 insertions(+), 155 deletions(-)
create mode 100644 drivers/platform/x86/amd/pmf/metrics.c
diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
index bf7aad80b9e9..8cac51182433 100644
--- a/drivers/platform/x86/amd/pmf/Makefile
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -7,6 +7,6 @@
obj-$(CONFIG_AMD_PMF) += amd-pmf.o
amd-pmf-y := core.o acpi.o sps.o \
auto-mode.o cnqf.o \
- tee-if.o spc.o
+ tee-if.o spc.o metrics.o
# Build util.c only when AMD_PMF_UTIL_SUPPORT is enabled
amd-pmf-$(CONFIG_AMD_PMF_UTIL_SUPPORT) += util.o
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 0f85ac0ba803..600992f0e2c7 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -55,7 +55,7 @@
#define DELAY_MAX_US 3000
/* override Metrics Table sample size time (in ms) */
-static int metrics_table_loop_ms = 1000;
+int metrics_table_loop_ms = 1000;
module_param(metrics_table_loop_ms, int, 0644);
MODULE_PARM_DESC(metrics_table_loop_ms, "Metrics Table sample size time (default = 1000ms)");
@@ -68,8 +68,6 @@ static bool smart_pc_support = true;
module_param(smart_pc_support, bool, 0444);
MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
-static struct device *pmf_device;
-
static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)
{
struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);
@@ -138,37 +136,6 @@ int amd_pmf_get_power_source(void)
return POWER_SOURCE_DC;
}
-static void amd_pmf_get_metrics(struct work_struct *work)
-{
- struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
- ktime_t time_elapsed_ms;
- int socket_power;
-
- guard(mutex)(&dev->update_mutex);
-
- /* Transfer table contents */
- memset(dev->buf, 0, sizeof(dev->m_table));
- amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
- memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
-
- time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
- /* Calculate the avg SoC power consumption */
- socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
-
- if (dev->amt_enabled) {
- /* Apply the Auto Mode transition */
- amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
- }
-
- if (dev->cnqf_enabled) {
- /* Apply the CnQF transition */
- amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
- }
-
- dev->start_time = ktime_to_ms(ktime_get());
- schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
-}
-
static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
{
return ioread32(dev->regbase + reg_offset);
@@ -296,125 +263,6 @@ static const struct pci_device_id pmf_pci_ids[] = {
{ }
};
-int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
-{
- u64 phys_addr;
- u32 hi, low;
-
- /* Get Metrics Table Address */
- if (alloc_buffer) {
- switch (dev->cpu_id) {
- case AMD_CPU_ID_PS:
- case AMD_CPU_ID_RMB:
- dev->mtable_size = sizeof(dev->m_table);
- break;
- case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
- case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
- dev->mtable_size = sizeof(dev->m_table_v2);
- break;
- default:
- dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id);
- }
-
- dev->buf = devm_kzalloc(dev->dev, dev->mtable_size, GFP_KERNEL);
- if (!dev->buf)
- return -ENOMEM;
- }
-
- phys_addr = virt_to_phys(dev->buf);
- hi = phys_addr >> 32;
- low = phys_addr & GENMASK(31, 0);
-
- amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, SET_CMD, hi, NULL);
- amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, SET_CMD, low, NULL);
-
- return 0;
-}
-
-int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
-{
- int ret;
-
- INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
-
- ret = amd_pmf_set_dram_addr(dev, true);
- if (ret)
- return ret;
-
- /*
- * Start collecting the metrics data after a small delay
- * or else, we might end up getting stale values from PMFW.
- */
- schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms * 3));
-
- return 0;
-}
-
-static int is_npu_metrics_supported(struct amd_pmf_dev *pdev)
-{
- switch (pdev->cpu_id) {
- case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
- case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
- return 0;
- default:
- return -EOPNOTSUPP;
- }
-}
-
-static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_metrics *data)
-{
- int ret, i;
-
- guard(mutex)(&dev->metrics_mutex);
-
- ret = is_npu_metrics_supported(dev);
- if (ret)
- return ret;
-
- ret = amd_pmf_set_dram_addr(dev, true);
- if (ret)
- return ret;
-
- memset(dev->buf, 0, dev->mtable_size);
-
- /* Send SMU command to get NPU metrics */
- ret = amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
- if (ret) {
- dev_err(dev->dev, "SMU command failed to get NPU metrics: %d\n", ret);
- return ret;
- }
-
- memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
-
- data->npuclk_freq = dev->m_table_v2.npuclk_freq;
- for (i = 0; i < ARRAY_SIZE(data->npu_busy); i++)
- data->npu_busy[i] = dev->m_table_v2.npu_busy[i];
- data->npu_power = dev->m_table_v2.npu_power;
- data->mpnpuclk_freq = dev->m_table_v2.mpnpuclk_freq;
- data->npu_reads = dev->m_table_v2.npu_reads;
- data->npu_writes = dev->m_table_v2.npu_writes;
-
- return 0;
-}
-
-int amd_pmf_get_npu_data(struct amd_pmf_npu_metrics *info)
-{
- struct amd_pmf_dev *pdev;
-
- if (!info)
- return -EINVAL;
-
- if (!pmf_device)
- return -ENODEV;
-
- pdev = dev_get_drvdata(pmf_device);
- if (!pdev)
- return -ENODEV;
-
- return amd_pmf_get_smu_metrics(pdev, info);
-}
-EXPORT_SYMBOL_NS_GPL(amd_pmf_get_npu_data, "AMD_PMF");
-
static int amd_pmf_reinit_ta(struct amd_pmf_dev *pdev)
{
bool status;
@@ -677,7 +525,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD);
- pmf_device = dev->dev;
+ amd_pmf_set_device(dev->dev);
err = amd_pmf_cdev_register(dev);
if (err)
diff --git a/drivers/platform/x86/amd/pmf/metrics.c b/drivers/platform/x86/amd/pmf/metrics.c
new file mode 100644
index 000000000000..4fabe4ee4149
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/metrics.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Platform Management Framework Driver - Metrics Support
+ *
+ * Copyright (c) 2026, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ * Patil Rajesh Reddy <Patil.Reddy@amd.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/device/devres.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "pmf.h"
+
+static struct device *pmf_device;
+
+static void amd_pmf_get_metrics(struct work_struct *work)
+{
+ struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
+ ktime_t time_elapsed_ms;
+ int socket_power;
+
+ guard(mutex)(&dev->update_mutex);
+
+ /* Transfer table contents */
+ memset(dev->buf, 0, sizeof(dev->m_table));
+ amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
+ memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
+
+ time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
+ /* Calculate the avg SoC power consumption */
+ socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
+
+ if (dev->amt_enabled) {
+ /* Apply the Auto Mode transition */
+ amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
+ }
+
+ if (dev->cnqf_enabled) {
+ /* Apply the CnQF transition */
+ amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
+ }
+
+ dev->start_time = ktime_to_ms(ktime_get());
+ schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
+}
+
+int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
+{
+ u64 phys_addr;
+ u32 hi, low;
+
+ /* Get Metrics Table Address */
+ if (alloc_buffer) {
+ switch (dev->cpu_id) {
+ case AMD_CPU_ID_PS:
+ case AMD_CPU_ID_RMB:
+ dev->mtable_size = sizeof(dev->m_table);
+ break;
+ case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
+ case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
+ dev->mtable_size = sizeof(dev->m_table_v2);
+ break;
+ default:
+ dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id);
+ }
+
+ dev->buf = devm_kzalloc(dev->dev, dev->mtable_size, GFP_KERNEL);
+ if (!dev->buf)
+ return -ENOMEM;
+ }
+
+ phys_addr = virt_to_phys(dev->buf);
+ hi = phys_addr >> 32;
+ low = phys_addr & GENMASK(31, 0);
+
+ amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, SET_CMD, hi, NULL);
+ amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, SET_CMD, low, NULL);
+
+ return 0;
+}
+
+int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
+{
+ int ret;
+
+ INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
+
+ ret = amd_pmf_set_dram_addr(dev, true);
+ if (ret)
+ return ret;
+
+ /*
+ * Start collecting the metrics data after a small delay
+ * or else, we might end up getting stale values from PMFW.
+ */
+ schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms * 3));
+
+ return 0;
+}
+
+void amd_pmf_set_device(struct device *p_device)
+{
+ pmf_device = p_device;
+}
+
+static int is_npu_metrics_supported(struct amd_pmf_dev *pdev)
+{
+ switch (pdev->cpu_id) {
+ case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
+ case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_metrics *data)
+{
+ int ret, i;
+
+ guard(mutex)(&dev->metrics_mutex);
+
+ ret = is_npu_metrics_supported(dev);
+ if (ret)
+ return ret;
+
+ ret = amd_pmf_set_dram_addr(dev, true);
+ if (ret)
+ return ret;
+
+ memset(dev->buf, 0, dev->mtable_size);
+
+ /* Send SMU command to get NPU metrics */
+ ret = amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
+ if (ret) {
+ dev_err(dev->dev, "SMU command failed to get NPU metrics: %d\n", ret);
+ return ret;
+ }
+
+ memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
+
+ data->npuclk_freq = dev->m_table_v2.npuclk_freq;
+ for (i = 0; i < ARRAY_SIZE(data->npu_busy); i++)
+ data->npu_busy[i] = dev->m_table_v2.npu_busy[i];
+ data->npu_power = dev->m_table_v2.npu_power;
+ data->mpnpuclk_freq = dev->m_table_v2.mpnpuclk_freq;
+ data->npu_reads = dev->m_table_v2.npu_reads;
+ data->npu_writes = dev->m_table_v2.npu_writes;
+
+ return 0;
+}
+
+int amd_pmf_get_npu_data(struct amd_pmf_npu_metrics *info)
+{
+ struct amd_pmf_dev *pdev;
+
+ if (!info)
+ return -EINVAL;
+
+ if (!pmf_device)
+ return -ENODEV;
+
+ pdev = dev_get_drvdata(pmf_device);
+ if (!pdev)
+ return -ENODEV;
+
+ return amd_pmf_get_smu_metrics(pdev, info);
+}
+EXPORT_SYMBOL_NS_GPL(amd_pmf_get_npu_data, "AMD_PMF");
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 088edfab08f0..4873685c84ad 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -137,6 +137,8 @@ struct cookie_header {
#define METRICS_TABLE_ID 7
#define BIOS_OUTPUT_MAX 10
+extern int metrics_table_loop_ms;
+
typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data);
static const uuid_t amd_pmf_ta_uuid[] __used = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8,
@@ -864,6 +866,7 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag);
u32 fixp_q88_fromint(u32 val);
int is_apmf_bios_input_notifications_supported(struct amd_pmf_dev *pdev);
+void amd_pmf_set_device(struct device *p_device);
/* SPS Layer */
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v2 3/6] platform/x86/amd/pmf: Move metrics code to dedicated file
2026-07-13 18:39 ` [PATCH v2 3/6] platform/x86/amd/pmf: Move metrics code to dedicated file Shyam Sundar S K
@ 2026-07-21 16:09 ` Ilpo Järvinen
0 siblings, 0 replies; 10+ messages in thread
From: Ilpo Järvinen @ 2026-07-21 16:09 UTC (permalink / raw)
To: Shyam Sundar S K
Cc: Hans de Goede, platform-driver-x86, Patil.Reddy,
mario.limonciello, Mario Limonciello
On Tue, 14 Jul 2026, Shyam Sundar S K wrote:
> Refactor metrics related code from core.c into a new metrics.c file
> to improve code organization and maintainability. The metrics
> functionality is evolving with new platform support, warranting a
> separate file.
>
> No functional changes.
>
> Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
> Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Not a full review but noticed a few issues while scanning through it
quickly...
> ---
> drivers/platform/x86/amd/pmf/Makefile | 2 +-
> drivers/platform/x86/amd/pmf/core.c | 156 +--------------------
> drivers/platform/x86/amd/pmf/metrics.c | 180 +++++++++++++++++++++++++
> drivers/platform/x86/amd/pmf/pmf.h | 3 +
> 4 files changed, 186 insertions(+), 155 deletions(-)
> create mode 100644 drivers/platform/x86/amd/pmf/metrics.c
>
> diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
> index bf7aad80b9e9..8cac51182433 100644
> --- a/drivers/platform/x86/amd/pmf/Makefile
> +++ b/drivers/platform/x86/amd/pmf/Makefile
> @@ -7,6 +7,6 @@
> obj-$(CONFIG_AMD_PMF) += amd-pmf.o
> amd-pmf-y := core.o acpi.o sps.o \
> auto-mode.o cnqf.o \
> - tee-if.o spc.o
> + tee-if.o spc.o metrics.o
> # Build util.c only when AMD_PMF_UTIL_SUPPORT is enabled
> amd-pmf-$(CONFIG_AMD_PMF_UTIL_SUPPORT) += util.o
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 0f85ac0ba803..600992f0e2c7 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -55,7 +55,7 @@
> #define DELAY_MAX_US 3000
>
> /* override Metrics Table sample size time (in ms) */
> -static int metrics_table_loop_ms = 1000;
> +int metrics_table_loop_ms = 1000;
> module_param(metrics_table_loop_ms, int, 0644);
> MODULE_PARM_DESC(metrics_table_loop_ms, "Metrics Table sample size time (default = 1000ms)");
>
> @@ -68,8 +68,6 @@ static bool smart_pc_support = true;
> module_param(smart_pc_support, bool, 0444);
> MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
>
> -static struct device *pmf_device;
> -
> static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)
> {
> struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);
> @@ -138,37 +136,6 @@ int amd_pmf_get_power_source(void)
> return POWER_SOURCE_DC;
> }
>
> -static void amd_pmf_get_metrics(struct work_struct *work)
> -{
> - struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
> - ktime_t time_elapsed_ms;
> - int socket_power;
> -
> - guard(mutex)(&dev->update_mutex);
> -
> - /* Transfer table contents */
> - memset(dev->buf, 0, sizeof(dev->m_table));
> - amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
> - memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
> -
> - time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
> - /* Calculate the avg SoC power consumption */
> - socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
> -
> - if (dev->amt_enabled) {
> - /* Apply the Auto Mode transition */
> - amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> - }
> -
> - if (dev->cnqf_enabled) {
> - /* Apply the CnQF transition */
> - amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
> - }
> -
> - dev->start_time = ktime_to_ms(ktime_get());
> - schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
> -}
> -
> static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
> {
> return ioread32(dev->regbase + reg_offset);
> @@ -296,125 +263,6 @@ static const struct pci_device_id pmf_pci_ids[] = {
> { }
> };
>
> -int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
> -{
> - u64 phys_addr;
> - u32 hi, low;
> -
> - /* Get Metrics Table Address */
> - if (alloc_buffer) {
> - switch (dev->cpu_id) {
> - case AMD_CPU_ID_PS:
> - case AMD_CPU_ID_RMB:
> - dev->mtable_size = sizeof(dev->m_table);
> - break;
> - case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
> - case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
> - dev->mtable_size = sizeof(dev->m_table_v2);
> - break;
> - default:
> - dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id);
> - }
> -
> - dev->buf = devm_kzalloc(dev->dev, dev->mtable_size, GFP_KERNEL);
> - if (!dev->buf)
> - return -ENOMEM;
> - }
> -
> - phys_addr = virt_to_phys(dev->buf);
> - hi = phys_addr >> 32;
> - low = phys_addr & GENMASK(31, 0);
> -
> - amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, SET_CMD, hi, NULL);
> - amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, SET_CMD, low, NULL);
> -
> - return 0;
> -}
> -
> -int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
> -{
> - int ret;
> -
> - INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
> -
> - ret = amd_pmf_set_dram_addr(dev, true);
> - if (ret)
> - return ret;
> -
> - /*
> - * Start collecting the metrics data after a small delay
> - * or else, we might end up getting stale values from PMFW.
> - */
> - schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms * 3));
> -
> - return 0;
> -}
> -
> -static int is_npu_metrics_supported(struct amd_pmf_dev *pdev)
> -{
> - switch (pdev->cpu_id) {
> - case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
> - case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
> - return 0;
> - default:
> - return -EOPNOTSUPP;
> - }
> -}
> -
> -static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_metrics *data)
> -{
> - int ret, i;
> -
> - guard(mutex)(&dev->metrics_mutex);
> -
> - ret = is_npu_metrics_supported(dev);
> - if (ret)
> - return ret;
> -
> - ret = amd_pmf_set_dram_addr(dev, true);
> - if (ret)
> - return ret;
> -
> - memset(dev->buf, 0, dev->mtable_size);
> -
> - /* Send SMU command to get NPU metrics */
> - ret = amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
> - if (ret) {
> - dev_err(dev->dev, "SMU command failed to get NPU metrics: %d\n", ret);
> - return ret;
> - }
> -
> - memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
> -
> - data->npuclk_freq = dev->m_table_v2.npuclk_freq;
> - for (i = 0; i < ARRAY_SIZE(data->npu_busy); i++)
> - data->npu_busy[i] = dev->m_table_v2.npu_busy[i];
> - data->npu_power = dev->m_table_v2.npu_power;
> - data->mpnpuclk_freq = dev->m_table_v2.mpnpuclk_freq;
> - data->npu_reads = dev->m_table_v2.npu_reads;
> - data->npu_writes = dev->m_table_v2.npu_writes;
> -
> - return 0;
> -}
> -
> -int amd_pmf_get_npu_data(struct amd_pmf_npu_metrics *info)
> -{
> - struct amd_pmf_dev *pdev;
> -
> - if (!info)
> - return -EINVAL;
> -
> - if (!pmf_device)
> - return -ENODEV;
> -
> - pdev = dev_get_drvdata(pmf_device);
> - if (!pdev)
> - return -ENODEV;
> -
> - return amd_pmf_get_smu_metrics(pdev, info);
> -}
> -EXPORT_SYMBOL_NS_GPL(amd_pmf_get_npu_data, "AMD_PMF");
> -
You're moving quite a bit of code, so check if the includes are still
used in this file after the removal.
> static int amd_pmf_reinit_ta(struct amd_pmf_dev *pdev)
> {
> bool status;
> @@ -677,7 +525,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
> if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
> amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD);
>
> - pmf_device = dev->dev;
> + amd_pmf_set_device(dev->dev);
>
> err = amd_pmf_cdev_register(dev);
> if (err)
> diff --git a/drivers/platform/x86/amd/pmf/metrics.c b/drivers/platform/x86/amd/pmf/metrics.c
> new file mode 100644
> index 000000000000..4fabe4ee4149
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/metrics.c
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * AMD Platform Management Framework Driver - Metrics Support
> + *
> + * Copyright (c) 2026, Advanced Micro Devices, Inc.
> + * All Rights Reserved.
> + *
> + * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> + * Patil Rajesh Reddy <Patil.Reddy@amd.com>
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/cleanup.h>
> +#include <linux/container_of.h>
> +#include <linux/device.h>
> +#include <linux/device/devres.h>
> +#include <linux/io.h>
> +#include <linux/ktime.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/workqueue.h>
> +
> +#include "pmf.h"
> +
> +static struct device *pmf_device;
> +
> +static void amd_pmf_get_metrics(struct work_struct *work)
> +{
> + struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
> + ktime_t time_elapsed_ms;
> + int socket_power;
> +
> + guard(mutex)(&dev->update_mutex);
> +
> + /* Transfer table contents */
> + memset(dev->buf, 0, sizeof(dev->m_table));
> + amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
> + memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table));
> +
> + time_elapsed_ms = ktime_to_ms(ktime_get()) - dev->start_time;
> + /* Calculate the avg SoC power consumption */
> + socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power;
> +
> + if (dev->amt_enabled) {
> + /* Apply the Auto Mode transition */
> + amd_pmf_trans_automode(dev, socket_power, time_elapsed_ms);
> + }
> +
> + if (dev->cnqf_enabled) {
> + /* Apply the CnQF transition */
> + amd_pmf_trans_cnqf(dev, socket_power, time_elapsed_ms);
> + }
> +
> + dev->start_time = ktime_to_ms(ktime_get());
> + schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
> +}
> +
> +int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
> +{
> + u64 phys_addr;
> + u32 hi, low;
> +
> + /* Get Metrics Table Address */
> + if (alloc_buffer) {
> + switch (dev->cpu_id) {
> + case AMD_CPU_ID_PS:
> + case AMD_CPU_ID_RMB:
> + dev->mtable_size = sizeof(dev->m_table);
> + break;
> + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
> + case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
> + dev->mtable_size = sizeof(dev->m_table_v2);
> + break;
> + default:
> + dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id);
Missing \n (probably in the original as well, can be fixed in a separate
patch to keep this as a plain move patch which easy to automatically
validate using diff of diffs).
> + }
> +
> + dev->buf = devm_kzalloc(dev->dev, dev->mtable_size, GFP_KERNEL);
> + if (!dev->buf)
> + return -ENOMEM;
> + }
> +
> + phys_addr = virt_to_phys(dev->buf);
> + hi = phys_addr >> 32;
> + low = phys_addr & GENMASK(31, 0);
> +
> + amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, SET_CMD, hi, NULL);
> + amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, SET_CMD, low, NULL);
> +
> + return 0;
> +}
> +
> +int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev)
> +{
> + int ret;
> +
> + INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics);
> +
> + ret = amd_pmf_set_dram_addr(dev, true);
> + if (ret)
> + return ret;
> +
> + /*
> + * Start collecting the metrics data after a small delay
> + * or else, we might end up getting stale values from PMFW.
> + */
> + schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms * 3));
> +
> + return 0;
> +}
> +
> +void amd_pmf_set_device(struct device *p_device)
> +{
> + pmf_device = p_device;
> +}
> +
> +static int is_npu_metrics_supported(struct amd_pmf_dev *pdev)
> +{
> + switch (pdev->cpu_id) {
> + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
> + case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_metrics *data)
> +{
> + int ret, i;
> +
> + guard(mutex)(&dev->metrics_mutex);
> +
> + ret = is_npu_metrics_supported(dev);
> + if (ret)
> + return ret;
> +
> + ret = amd_pmf_set_dram_addr(dev, true);
> + if (ret)
> + return ret;
> +
> + memset(dev->buf, 0, dev->mtable_size);
> +
> + /* Send SMU command to get NPU metrics */
> + ret = amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
> + if (ret) {
> + dev_err(dev->dev, "SMU command failed to get NPU metrics: %d\n", ret);
> + return ret;
> + }
> +
> + memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
> +
> + data->npuclk_freq = dev->m_table_v2.npuclk_freq;
> + for (i = 0; i < ARRAY_SIZE(data->npu_busy); i++)
Add include.
> + data->npu_busy[i] = dev->m_table_v2.npu_busy[i];
> + data->npu_power = dev->m_table_v2.npu_power;
> + data->mpnpuclk_freq = dev->m_table_v2.mpnpuclk_freq;
> + data->npu_reads = dev->m_table_v2.npu_reads;
> + data->npu_writes = dev->m_table_v2.npu_writes;
> +
> + return 0;
> +}
> +
> +int amd_pmf_get_npu_data(struct amd_pmf_npu_metrics *info)
> +{
> + struct amd_pmf_dev *pdev;
> +
> + if (!info)
> + return -EINVAL;
> +
> + if (!pmf_device)
> + return -ENODEV;
> +
> + pdev = dev_get_drvdata(pmf_device);
> + if (!pdev)
> + return -ENODEV;
> +
> + return amd_pmf_get_smu_metrics(pdev, info);
> +}
> +EXPORT_SYMBOL_NS_GPL(amd_pmf_get_npu_data, "AMD_PMF");
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 088edfab08f0..4873685c84ad 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -137,6 +137,8 @@ struct cookie_header {
> #define METRICS_TABLE_ID 7
> #define BIOS_OUTPUT_MAX 10
>
> +extern int metrics_table_loop_ms;
> +
> typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data);
>
> static const uuid_t amd_pmf_ta_uuid[] __used = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8,
> @@ -864,6 +866,7 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
> int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag);
> u32 fixp_q88_fromint(u32 val);
> int is_apmf_bios_input_notifications_supported(struct amd_pmf_dev *pdev);
> +void amd_pmf_set_device(struct device *p_device);
>
> /* SPS Layer */
> int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
>
--
i.
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 4/6] platform/x86/amd/pmf: Use upper/lower_32_bits() in amd_pmf_set_dram_addr()
2026-07-13 18:39 [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
` (2 preceding siblings ...)
2026-07-13 18:39 ` [PATCH v2 3/6] platform/x86/amd/pmf: Move metrics code to dedicated file Shyam Sundar S K
@ 2026-07-13 18:39 ` Shyam Sundar S K
2026-07-13 18:39 ` [PATCH v2 5/6] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility Shyam Sundar S K
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Shyam Sundar S K @ 2026-07-13 18:39 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, Patil.Reddy, mario.limonciello,
Shyam Sundar S K
Replace the open coded manual bit shifting used to split a 64-bit
physical address into its high and low 32-bit halves with the standard
kernel helpers upper_32_bits() and lower_32_bits().
No functional changes.
Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
drivers/platform/x86/amd/pmf/metrics.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/platform/x86/amd/pmf/metrics.c b/drivers/platform/x86/amd/pmf/metrics.c
index 4fabe4ee4149..13dfc7fbd420 100644
--- a/drivers/platform/x86/amd/pmf/metrics.c
+++ b/drivers/platform/x86/amd/pmf/metrics.c
@@ -9,7 +9,6 @@
* Patil Rajesh Reddy <Patil.Reddy@amd.com>
*/
-#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/device.h>
@@ -18,6 +17,7 @@
#include <linux/ktime.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/wordpart.h>
#include <linux/workqueue.h>
#include "pmf.h"
@@ -81,8 +81,8 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
}
phys_addr = virt_to_phys(dev->buf);
- hi = phys_addr >> 32;
- low = phys_addr & GENMASK(31, 0);
+ hi = upper_32_bits(phys_addr);
+ low = lower_32_bits(phys_addr);
amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, SET_CMD, hi, NULL);
amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, SET_CMD, low, NULL);
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 5/6] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility
2026-07-13 18:39 [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
` (3 preceding siblings ...)
2026-07-13 18:39 ` [PATCH v2 4/6] platform/x86/amd/pmf: Use upper/lower_32_bits() in amd_pmf_set_dram_addr() Shyam Sundar S K
@ 2026-07-13 18:39 ` Shyam Sundar S K
2026-07-13 18:39 ` [PATCH v2 6/6] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support Shyam Sundar S K
2026-07-13 18:47 ` [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Mario Limonciello
6 siblings, 0 replies; 10+ messages in thread
From: Shyam Sundar S K @ 2026-07-13 18:39 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, Patil.Reddy, mario.limonciello,
Shyam Sundar S K, Mario Limonciello
Refactor the NPU metrics retrieval code to use a switch-case structure
based on CPU ID, preparing the driver for supporting additional
platforms with different metrics table formats.
This change restructures amd_pmf_get_smu_metrics() to handle
platform-specific metrics retrieval paths. The existing logic for
1AH_M20H and 1AH_M60H platforms is preserved within the switch-case
block.
No functional changes.
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
drivers/platform/x86/amd/pmf/metrics.c | 42 +++++++++++++++-----------
1 file changed, 24 insertions(+), 18 deletions(-)
diff --git a/drivers/platform/x86/amd/pmf/metrics.c b/drivers/platform/x86/amd/pmf/metrics.c
index 13dfc7fbd420..c4f5f4d36bd6 100644
--- a/drivers/platform/x86/amd/pmf/metrics.c
+++ b/drivers/platform/x86/amd/pmf/metrics.c
@@ -9,6 +9,7 @@
* Patil Rajesh Reddy <Patil.Reddy@amd.com>
*/
+#include <linux/array_size.h>
#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/device.h>
@@ -135,28 +136,33 @@ static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_m
if (ret)
return ret;
- ret = amd_pmf_set_dram_addr(dev, true);
- if (ret)
- return ret;
+ switch (dev->cpu_id) {
+ case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
+ case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
+ ret = amd_pmf_set_dram_addr(dev, true);
+ if (ret)
+ return ret;
- memset(dev->buf, 0, dev->mtable_size);
+ memset(dev->buf, 0, dev->mtable_size);
- /* Send SMU command to get NPU metrics */
- ret = amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
- if (ret) {
- dev_err(dev->dev, "SMU command failed to get NPU metrics: %d\n", ret);
- return ret;
- }
+ /* Send SMU command to get NPU metrics */
+ ret = amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL);
+ if (ret) {
+ dev_err(dev->dev, "SMU command failed to get NPU metrics: %d\n", ret);
+ return ret;
+ }
- memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
+ memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size);
- data->npuclk_freq = dev->m_table_v2.npuclk_freq;
- for (i = 0; i < ARRAY_SIZE(data->npu_busy); i++)
- data->npu_busy[i] = dev->m_table_v2.npu_busy[i];
- data->npu_power = dev->m_table_v2.npu_power;
- data->mpnpuclk_freq = dev->m_table_v2.mpnpuclk_freq;
- data->npu_reads = dev->m_table_v2.npu_reads;
- data->npu_writes = dev->m_table_v2.npu_writes;
+ data->npuclk_freq = dev->m_table_v2.npuclk_freq;
+ for (i = 0; i < ARRAY_SIZE(data->npu_busy); i++)
+ data->npu_busy[i] = dev->m_table_v2.npu_busy[i];
+ data->npu_power = dev->m_table_v2.npu_power;
+ data->mpnpuclk_freq = dev->m_table_v2.mpnpuclk_freq;
+ data->npu_reads = dev->m_table_v2.npu_reads;
+ data->npu_writes = dev->m_table_v2.npu_writes;
+ break;
+ }
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 6/6] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support
2026-07-13 18:39 [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
` (4 preceding siblings ...)
2026-07-13 18:39 ` [PATCH v2 5/6] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility Shyam Sundar S K
@ 2026-07-13 18:39 ` Shyam Sundar S K
2026-07-21 16:13 ` Ilpo Järvinen
2026-07-13 18:47 ` [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Mario Limonciello
6 siblings, 1 reply; 10+ messages in thread
From: Shyam Sundar S K @ 2026-07-13 18:39 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, Patil.Reddy, mario.limonciello,
Shyam Sundar S K
The 1AH_M80H platform introduces a new firmware managed DRAM based
metrics table (amd_pmf_metrics_v3) covering the full platform telemetry
including power, voltages, frequencies, throttlers and activity monitors.
As a first consumer of this table, add NPU metrics retrieval. Unlike
earlier platforms that use a transfer table command, 1AH_M80H metrics
are accumulator based and require delta calculation between consecutive
samples.
Extend amd_pmf_npu_metrics with npu_temp, populated from the
npu_temp_acc accumulator field available on 1AH_M80H.
Key changes include:
- Add DRAM based metrics table support for the 1AH_M80H platform
- Introduce amd_pmf_get_tbl_dram_addr() to obtain the DRAM address
- Add amd_pmf_get_metrics_table_log_sample() to trigger metrics updates
- Add struct amd_pmf_metrics_v3 for the 1AH_M80H metrics format
- Implement accumulator based delta calculation for metrics
- Introduce amd_pmf_calculate_acc_npu_metrics() to get NPU metrics
- Introduce amd_pmf_supports_accumulator_metrics() to check the
accumulator based metrics support.
Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
drivers/platform/x86/amd/pmf/core.c | 29 ++++
drivers/platform/x86/amd/pmf/metrics.c | 144 ++++++++++++++++
drivers/platform/x86/amd/pmf/pmf.h | 217 +++++++++++++++++++++++++
include/linux/amd-pmf-io.h | 2 +
4 files changed, 392 insertions(+)
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 600992f0e2c7..303cf3c45712 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -68,6 +68,16 @@ static bool smart_pc_support = true;
module_param(smart_pc_support, bool, 0444);
MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
+static bool amd_pmf_supports_accumulator_metrics(struct amd_pmf_dev *pdev)
+{
+ switch (pdev->cpu_id) {
+ case PCI_DEVICE_ID_AMD_1AH_M80H_ROOT:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)
{
struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);
@@ -155,6 +165,12 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[0]);
dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value);
+ if (amd_pmf_supports_accumulator_metrics(dev)) {
+ value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[1]);
+ dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT1:%d\n", value);
+ value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[2]);
+ dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT2:%d\n", value);
+ }
value = amd_pmf_reg_read(dev, dev->smu_regs->msg_reg);
dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
@@ -214,6 +230,13 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
/* PMFW may take longer time to return back the data */
usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
*data = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[0]);
+ if (amd_pmf_supports_accumulator_metrics(dev) &&
+ message == GET_1AH_M80H_METRICS_TABLE_DRAM_ADDR) {
+ dev->dram_addr.hi = amd_pmf_reg_read(dev,
+ dev->smu_regs->arg_reg[1]);
+ dev->dram_addr.size = amd_pmf_reg_read(dev,
+ dev->smu_regs->arg_reg[2]);
+ }
}
break;
case AMD_PMF_RESULT_CMD_REJECT_BUSY:
@@ -517,6 +540,12 @@ static int amd_pmf_probe(struct platform_device *pdev)
if (err)
return err;
+ if (amd_pmf_supports_accumulator_metrics(dev)) {
+ err = amd_pmf_get_tbl_dram_addr(dev);
+ if (err)
+ return err;
+ }
+
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
amd_pmf_dbgfs_register(dev);
diff --git a/drivers/platform/x86/amd/pmf/metrics.c b/drivers/platform/x86/amd/pmf/metrics.c
index c4f5f4d36bd6..ceff4f139043 100644
--- a/drivers/platform/x86/amd/pmf/metrics.c
+++ b/drivers/platform/x86/amd/pmf/metrics.c
@@ -16,8 +16,12 @@
#include <linux/device/devres.h>
#include <linux/io.h>
#include <linux/ktime.h>
+#include <linux/limits.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/units.h>
#include <linux/wordpart.h>
#include <linux/workqueue.h>
@@ -25,6 +29,89 @@
static struct device *pmf_device;
+static u16 amd_pmf_q10_acc_to_mW(u64 avg_acc)
+{
+ u64 val = DIV_U64_ROUND_CLOSEST(avg_acc, 1024) * MILLIWATT_PER_WATT;
+
+ return min_t(u16, val, U16_MAX);
+}
+
+static u16 amd_pmf_q10_acc_to_int(u64 avg_acc)
+{
+ u64 val = DIV_U64_ROUND_CLOSEST(avg_acc, 1024);
+
+ return min_t(u16, val, U16_MAX);
+}
+
+static u64 amd_pmf_avg_acc_metric(u32 curr_counter, u32 prev_counter,
+ u64 curr_value, u64 prev_value)
+{
+ u32 counter_diff;
+ u64 val_diff;
+
+ /* Check for counter reset */
+ if (curr_counter <= prev_counter)
+ return 0;
+
+ counter_diff = curr_counter - prev_counter;
+
+ if (curr_value < prev_value)
+ return 0;
+
+ val_diff = curr_value - prev_value;
+
+ return DIV_U64_ROUND_CLOSEST(val_diff, counter_diff);
+}
+
+int amd_pmf_get_tbl_dram_addr(struct amd_pmf_dev *dev)
+{
+ int ret;
+
+ ret = amd_pmf_send_cmd(dev, GET_1AH_M80H_METRICS_TABLE_DRAM_ADDR, GET_CMD,
+ ARG_NONE, &dev->dram_addr.lo);
+ if (ret) {
+ dev_err(dev->dev, "Failed to get DRAM address: %d\n", ret);
+ return ret;
+ }
+
+ dev->metrics_table_phys = ((u64)dev->dram_addr.hi << 32) | dev->dram_addr.lo;
+ dev->mtable_size = dev->dram_addr.size;
+
+ if (dev->mtable_size != sizeof(dev->mtable_v3)) {
+ dev_err(dev->dev, "Metrics table size mismatch: got %u, expected %zu\n",
+ dev->dram_addr.size, sizeof(dev->mtable_v3));
+ return -EINVAL;
+ }
+
+ dev->metrics_table_virt = devm_ioremap(dev->dev, dev->metrics_table_phys, dev->mtable_size);
+ if (!dev->metrics_table_virt) {
+ dev_err(dev->dev, "Failed to map DRAM address for PMF metrics table\n");
+ dev->metrics_table_phys = 0;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int amd_pmf_get_metrics_table_log_sample(struct amd_pmf_dev *dev)
+{
+ int ret;
+
+ if (!dev->metrics_table_virt) {
+ dev_err(dev->dev, "Metrics DRAM not mapped\n");
+ return -EINVAL;
+ }
+
+ /* Send command to PMFW to update metrics table log sample */
+ ret = amd_pmf_send_cmd(dev, GET_1AH_M80H_METRICS_TABLE_LOG_SAMPLE, SET_CMD, ARG_NONE, NULL);
+ if (ret) {
+ dev_err(dev->dev, "Failed to request metrics log sample: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static void amd_pmf_get_metrics(struct work_struct *work)
{
struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
@@ -120,12 +207,46 @@ static int is_npu_metrics_supported(struct amd_pmf_dev *pdev)
switch (pdev->cpu_id) {
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
+ case PCI_DEVICE_ID_AMD_1AH_M80H_ROOT:
return 0;
default:
return -EOPNOTSUPP;
}
}
+static void amd_pmf_calculate_acc_npu_metrics(struct amd_pmf_dev *dev,
+ struct amd_pmf_npu_metrics *data)
+{
+ struct amd_pmf_metrics_iod *curr, *prev;
+ u64 val;
+ int i;
+
+ curr = &dev->mtable_v3.iod;
+ prev = &dev->prev_metrics.iod;
+
+ val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
+ curr->npu_temp_acc, prev->npu_temp_acc);
+ data->npu_temp = amd_pmf_q10_acc_to_int(val);
+ val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
+ curr->npu_power_acc, prev->npu_power_acc);
+ data->npu_power = amd_pmf_q10_acc_to_mW(val);
+ val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
+ curr->npuhclk_freq_eff_acc,
+ prev->npuhclk_freq_eff_acc);
+ data->mpnpuclk_freq = amd_pmf_q10_acc_to_int(val);
+ val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
+ curr->aieclk_freq_eff_acc,
+ prev->aieclk_freq_eff_acc);
+ data->npuclk_freq = amd_pmf_q10_acc_to_int(val);
+
+ for (i = 0; i < ARRAY_SIZE(curr->npu_busy_acc); i++) {
+ val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
+ curr->npu_busy_acc[i],
+ prev->npu_busy_acc[i]);
+ data->npu_busy[i] = amd_pmf_q10_acc_to_int(val);
+ }
+}
+
static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_metrics *data)
{
int ret, i;
@@ -136,6 +257,8 @@ static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_m
if (ret)
return ret;
+ memset(data, 0, sizeof(*data));
+
switch (dev->cpu_id) {
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
@@ -162,6 +285,27 @@ static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_m
data->npu_reads = dev->m_table_v2.npu_reads;
data->npu_writes = dev->m_table_v2.npu_writes;
break;
+ case PCI_DEVICE_ID_AMD_1AH_M80H_ROOT:
+ ret = amd_pmf_get_metrics_table_log_sample(dev);
+ if (ret)
+ return ret;
+
+ memcpy_fromio(&dev->mtable_v3, dev->metrics_table_virt, sizeof(dev->mtable_v3));
+
+ /*Ignore the first sample, as previous metrics is uninitialized (zero)
+ * Metrics are calculated as:
+ * metrics = current_metrics - previous_metrics
+ * Skipping the initial sample ensures accurate delta calculations.
+ */
+ if (!dev->npu_metrics_have_prev) {
+ memcpy(&dev->prev_metrics, &dev->mtable_v3, sizeof(dev->mtable_v3));
+ dev->npu_metrics_have_prev = true;
+ return 0;
+ }
+
+ amd_pmf_calculate_acc_npu_metrics(dev, data);
+ memcpy(&dev->prev_metrics, &dev->mtable_v3, sizeof(dev->mtable_v3));
+ break;
}
return 0;
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 4873685c84ad..0f8e6cc9a453 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -75,6 +75,10 @@ struct cookie_header {
#define SET_PMF_PPT 0x25
#define SET_PMF_PPT_APU_ONLY 0x26
+/* Message IDs for 1AH_M80H platform */
+#define GET_1AH_M80H_METRICS_TABLE_LOG_SAMPLE 0x0E
+#define GET_1AH_M80H_METRICS_TABLE_DRAM_ADDR 0x0F
+
/* OS slider update notification */
#define DC_BEST_PERF 0
#define DC_BETTER_PERF 1
@@ -139,6 +143,11 @@ struct cookie_header {
extern int metrics_table_loop_ms;
+#define AMD_PMF_METRIC_CORE_TYPE_MAX 4
+#define AMD_PMF_METRIC_CCX_MAX 4
+#define AMD_PMF_NUM_CLK_DPM_LEVELS 8
+#define AMD_PMF_NUM_MAX_CORES 12
+
typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data);
static const uuid_t amd_pmf_ta_uuid[] __used = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8,
@@ -250,6 +259,199 @@ struct apmf_fan_idx {
u32 fan_ctl_idx;
} __packed;
+struct amd_pmf_metrics_iod {
+ u32 counter_acc;
+ /* Set Voltage */
+ u64 vddcr_set_voltage;
+ u64 vddcr_soc_set_voltage;
+ u64 vddcr_npu_set_voltage;
+ u64 vddcr_lp_set_voltage;
+ u64 vddcr_gfx_set_voltage;
+ u64 vdd_misc_set_voltage;
+ /* Telemetry Voltages */
+ u64 vddcr_telemetry_voltage;
+ u64 vddcr_soc_telemetry_voltage;
+ u64 vddcr_npu_telemetry_voltage;
+ u64 vddcr_lp_telemetry_voltage;
+ u64 vddcr_gfx_telemetry_voltage;
+ u64 vdd_misc_telemetry_voltage;
+ /* Telemetry Powers */
+ u64 vddcr_telemetry_power;
+ u64 vddcr_soc_telemetry_power;
+ u64 vddcr_npu_telemetry_power;
+ u64 vddcr_lp_telemetry_power;
+ u64 vddcr_gfx_telemetry_power;
+ u64 vdd_misc_telemetry_power;
+ /* Throttlers - Fast PPT */
+ u32 fppt_fused_limit;
+ u32 fppt_max_irm_limit;
+ u32 fppt_max_pbo_limit;
+ u32 fppt_limit;
+ u64 fppt_value_acc;
+ u32 fppt_residency_acc;
+ /* Throttlers - Slow PPT */
+ u32 sppt_fused_limit;
+ u32 sppt_max_irm_limit;
+ u32 sppt_max_pbo_limit;
+ u32 sppt_limit;
+ u64 sppt_value_acc;
+ u32 sppt_residency_acc;
+ /* Throttlers - STAPM */
+ u32 spl_fused_limit;
+ u32 spl_max_irm_limit;
+ u32 spl_max_pbo_limit;
+ u32 spl_limit;
+ u64 spl_value_acc;
+ u32 spl_residency_acc;
+ /* Throttlers - TDC VDDCR */
+ u32 tdc_vddcr_fused_limit;
+ u32 tdc_vddcr_max_irm_limit;
+ u32 tdc_vddcr_max_pbo_limit;
+ u32 tdc_vddcr_limit;
+ u64 tdc_vddcr_value_acc;
+ u32 tdc_vddcr_residency_acc;
+ /* Throttlers - TDC VDDCR_SOC */
+ u32 tdc_vddcr_soc_fused_limit;
+ u32 tdc_vddcr_soc_max_irm_limit;
+ u32 tdc_vddcr_soc_max_pbo_limit;
+ u32 tdc_vddcr_soc_limit;
+ u64 tdc_vddcr_soc_value_acc;
+ u32 tdc_vddcr_soc_residency_acc;
+ /* Throttlers - TDC VDDCR_NPU */
+ u32 tdc_vddcr_npu_fused_limit;
+ u32 tdc_vddcr_npu_max_irm_limit;
+ u32 tdc_vddcr_npu_max_pbo_limit;
+ u32 tdc_vddcr_npu_limit;
+ u64 tdc_vddcr_npu_value_acc;
+ u32 tdc_vddcr_npu_residency_acc;
+ /* Throttlers - TDC VDDCR_LP */
+ u32 tdc_vddcr_lp_fused_limit;
+ u32 tdc_vddcr_lp_max_irm_limit;
+ u32 tdc_vddcr_lp_max_pbo_limit;
+ u32 tdc_vddcr_lp_limit;
+ u64 tdc_vddcr_lp_value_acc;
+ u32 tdc_vddcr_lp_residency_acc;
+ /* Throttlers - TDC VDDCR_GFX */
+ u32 tdc_vddcr_gfx_fused_limit;
+ u32 tdc_vddcr_gfx_max_irm_limit;
+ u32 tdc_vddcr_gfx_max_pbo_limit;
+ u32 tdc_vddcr_gfx_limit;
+ u64 tdc_vddcr_gfx_value_acc;
+ u32 tdc_vddcr_gfx_residency_acc;
+ /* Throttlers - EDC VDDCR */
+ u32 edc_vddcr_fused_limit;
+ u32 edc_vddcr_max_irm_limit;
+ u32 edc_vddcr_max_pbo_limit;
+ u32 edc_vddcr_limit;
+ /* Throttlers - Thermal */
+ u32 thm_fused_limit;
+ u32 thm_limit;
+ u64 thm_value_acc;
+ u32 thm_residency_acc;
+ u32 prochot_residency_acc;
+ u64 gfx_temp_acc;
+ u64 soc_temp_acc;
+ u32 p3t_fused_limit;
+ u64 p3t_value_acc;
+ /* Power */
+ u64 system_power_acc;
+ u64 apu_power_acc;
+ u64 dgpu_power_acc;
+ u64 npu_power_acc;
+ /* Frequencies */
+ u64 fclk_freq_eff_acc;
+ u64 memclk_freq_eff_acc;
+ u64 lclk_freq_eff_acc;
+ u64 gfxclk_freq_eff_acc;
+ u64 socclk_freq_eff_acc;
+ u64 vclk_freq_eff_acc;
+ u64 vpeclk_freq_eff_acc;
+ u64 aieclk_freq_eff_acc;
+ u64 npuhclk_freq_eff_acc;
+ /* Bandwidth */
+ u64 dram_read_bandwidth;
+ u64 dram_write_bandwidth;
+ /* Activity Monitors */
+ u64 gfx_busy_acc;
+ u64 vcn_busy_acc;
+ u64 npu_busy_acc[3];
+ /* STT Limits */
+ u32 stt_min_limit;
+ u64 stt_apu_hotspot_temp_acc;
+ u64 stt_hs2_hotspot_temp_acc;
+ u32 stt_apu_temp_limit;
+ u64 stt_apu_skin_temp_acc;
+ /* Residencies */
+ u64 cpuoff_residency_ccx0;
+ u64 cpuoff_residency_ccx1;
+ u64 cpuoff_residency_ccx2;
+ u64 cpuoff_residency_ccx3;
+ /* DF-pstates */
+ u32 fclk_freq_table[AMD_PMF_NUM_CLK_DPM_LEVELS];
+ u32 uclk_freq_table[AMD_PMF_NUM_CLK_DPM_LEVELS];
+ u32 ddr_rate_table[AMD_PMF_NUM_CLK_DPM_LEVELS];
+ u8 dfpstate_source[AMD_PMF_NUM_CLK_DPM_LEVELS];
+ /* System */
+ u8 gfx_disabled;
+ u8 spare2[3];
+ u32 gfxclk_fmax;
+ u8 cclk_core_fuse_enable[AMD_PMF_METRIC_CORE_TYPE_MAX][AMD_PMF_NUM_MAX_CORES];
+ u8 cclk_core_enabled[AMD_PMF_METRIC_CORE_TYPE_MAX][AMD_PMF_NUM_MAX_CORES];
+ u32 cclk_fmax[AMD_PMF_METRIC_CORE_TYPE_MAX][AMD_PMF_NUM_MAX_CORES];
+ /* Overclock Capable */
+ u8 cpu_precise_and_direct_oc_capable;
+ u8 gfx_precise_and_direct_oc_capable;
+ u8 pbo_basic_oc_capable;
+ u8 pbo_advanced_oc_capable;
+ u8 pbo_nitro_oc_capable;
+ u8 memory_and_fabric_oc_capable;
+ u8 misc_oc_capable;
+ u8 extreme_cold_oc_capable;
+ u8 down_config_control_capable;
+ u8 spare0[3];
+ /* Overclock Status */
+ u32 fit_limit_scalar;
+ u8 ln2_enabled;
+ u8 cpu_precise_and_direct_oc_enabled;
+ u8 gfx_precise_and_direct_oc_enabled;
+ u8 spare1[2];
+ /* Voltage Guardband in PSM count */
+ s8 psm_guardband[5][5][3];
+ s32 core_power_limit_offset;
+ u32 max_freq_offset[5];
+ u64 npu_temp_acc;
+ u64 dfpstate_residency_acc[AMD_PMF_NUM_CLK_DPM_LEVELS];
+ u32 cclk_fboost;
+ /* PMF */
+ u32 pmf_fast_apu_ppt_limit;
+ u64 pmf_fast_apu_ppt_value_acc;
+ u32 pmf_fast_apu_ppt_residency_acc;
+ u32 pmf_slow_apu_ppt_limit;
+ u64 pmf_slow_apu_ppt_value_acc;
+ u32 pmf_slow_apu_ppt_residency_acc;
+ u32 pmf_fast_spm_limit;
+ u64 pmf_fast_spm_value_acc;
+ u32 pmf_fast_spm_residency_acc;
+ u32 pmf_slow_spm_limit;
+ u64 pmf_slow_spm_value_acc;
+ u32 pmf_slow_spm_residency_acc;
+ u32 spare3[5];
+} __packed __aligned(4);
+
+struct amd_pmf_metrics_ccx {
+ u64 core_c0[AMD_PMF_NUM_MAX_CORES];
+ u64 core_cc6[AMD_PMF_NUM_MAX_CORES];
+ u64 core_freq[AMD_PMF_NUM_MAX_CORES];
+ u64 core_freqeff[AMD_PMF_NUM_MAX_CORES];
+ u64 core_temp[AMD_PMF_NUM_MAX_CORES];
+ u64 core_power[AMD_PMF_NUM_MAX_CORES];
+} __packed __aligned(4);
+
+struct amd_pmf_metrics_v3 {
+ struct amd_pmf_metrics_iod iod;
+ struct amd_pmf_metrics_ccx ccx[AMD_PMF_METRIC_CCX_MAX];
+} __packed __aligned(4);
+
struct smu_pmf_metrics_v2 {
u16 core_frequency[16]; /* MHz */
u16 core_power[16]; /* mW */
@@ -406,6 +608,12 @@ struct amd_pmf_smu_regs {
u32 arg_reg[3];
};
+struct amd_pmf_arg_data {
+ u32 lo;
+ u32 hi;
+ u32 size;
+};
+
struct amd_pmf_dev {
void __iomem *regbase;
void __iomem *smu_virt_addr;
@@ -459,6 +667,12 @@ struct amd_pmf_dev {
struct mutex metrics_mutex;
u32 bios_output[BIOS_OUTPUT_MAX];
const struct amd_pmf_smu_regs *smu_regs;
+ void __iomem *metrics_table_virt; /* Mapped DRAM virtual address for metrics table */
+ phys_addr_t metrics_table_phys; /* DRAM physical address for metrics table */
+ struct amd_pmf_metrics_v3 mtable_v3; /* IOD and CCX */
+ struct amd_pmf_arg_data dram_addr;
+ struct amd_pmf_metrics_v3 prev_metrics; /* Previous metrics for delta calculation */
+ bool npu_metrics_have_prev;
};
struct apmf_sps_prop_granular_v2 {
@@ -868,6 +1082,9 @@ u32 fixp_q88_fromint(u32 val);
int is_apmf_bios_input_notifications_supported(struct amd_pmf_dev *pdev);
void amd_pmf_set_device(struct device *p_device);
+/* Metrics layer */
+int amd_pmf_get_tbl_dram_addr(struct amd_pmf_dev *dev);
+
/* SPS Layer */
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
diff --git a/include/linux/amd-pmf-io.h b/include/linux/amd-pmf-io.h
index e014d4ce5a20..b84c200b0f8e 100644
--- a/include/linux/amd-pmf-io.h
+++ b/include/linux/amd-pmf-io.h
@@ -60,6 +60,7 @@ struct amd_sfh_info {
* @mpnpuclk_freq: MPNPU [MHz]
* @npu_reads: NPU read bandwidth [MB/sec]
* @npu_writes: NPU write bandwidth [MB/sec]
+ * @npu_temp: NPU temperature [C]
*/
struct amd_pmf_npu_metrics {
u16 npuclk_freq;
@@ -68,6 +69,7 @@ struct amd_pmf_npu_metrics {
u16 mpnpuclk_freq;
u16 npu_reads;
u16 npu_writes;
+ u16 npu_temp;
};
int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op);
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v2 6/6] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support
2026-07-13 18:39 ` [PATCH v2 6/6] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support Shyam Sundar S K
@ 2026-07-21 16:13 ` Ilpo Järvinen
0 siblings, 0 replies; 10+ messages in thread
From: Ilpo Järvinen @ 2026-07-21 16:13 UTC (permalink / raw)
To: Shyam Sundar S K
Cc: Hans de Goede, platform-driver-x86, Patil.Reddy,
mario.limonciello
On Tue, 14 Jul 2026, Shyam Sundar S K wrote:
> The 1AH_M80H platform introduces a new firmware managed DRAM based
> metrics table (amd_pmf_metrics_v3) covering the full platform telemetry
> including power, voltages, frequencies, throttlers and activity monitors.
>
> As a first consumer of this table, add NPU metrics retrieval. Unlike
> earlier platforms that use a transfer table command, 1AH_M80H metrics
> are accumulator based and require delta calculation between consecutive
> samples.
>
> Extend amd_pmf_npu_metrics with npu_temp, populated from the
> npu_temp_acc accumulator field available on 1AH_M80H.
>
> Key changes include:
> - Add DRAM based metrics table support for the 1AH_M80H platform
> - Introduce amd_pmf_get_tbl_dram_addr() to obtain the DRAM address
> - Add amd_pmf_get_metrics_table_log_sample() to trigger metrics updates
> - Add struct amd_pmf_metrics_v3 for the 1AH_M80H metrics format
> - Implement accumulator based delta calculation for metrics
> - Introduce amd_pmf_calculate_acc_npu_metrics() to get NPU metrics
> - Introduce amd_pmf_supports_accumulator_metrics() to check the
> accumulator based metrics support.
>
> Co-developed-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
> Signed-off-by: Patil Rajesh Reddy <Patil.Reddy@amd.com>
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
> drivers/platform/x86/amd/pmf/core.c | 29 ++++
> drivers/platform/x86/amd/pmf/metrics.c | 144 ++++++++++++++++
> drivers/platform/x86/amd/pmf/pmf.h | 217 +++++++++++++++++++++++++
> include/linux/amd-pmf-io.h | 2 +
> 4 files changed, 392 insertions(+)
>
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 600992f0e2c7..303cf3c45712 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -68,6 +68,16 @@ static bool smart_pc_support = true;
> module_param(smart_pc_support, bool, 0444);
> MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
>
> +static bool amd_pmf_supports_accumulator_metrics(struct amd_pmf_dev *pdev)
> +{
> + switch (pdev->cpu_id) {
> + case PCI_DEVICE_ID_AMD_1AH_M80H_ROOT:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)
> {
> struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);
> @@ -155,6 +165,12 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
>
> value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[0]);
> dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT:%d\n", value);
> + if (amd_pmf_supports_accumulator_metrics(dev)) {
> + value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[1]);
> + dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT1:%d\n", value);
> + value = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[2]);
> + dev_dbg(dev->dev, "AMD_PMF_REGISTER_ARGUMENT2:%d\n", value);
> + }
>
> value = amd_pmf_reg_read(dev, dev->smu_regs->msg_reg);
> dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
> @@ -214,6 +230,13 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
> /* PMFW may take longer time to return back the data */
> usleep_range(DELAY_MIN_US, 10 * DELAY_MAX_US);
> *data = amd_pmf_reg_read(dev, dev->smu_regs->arg_reg[0]);
> + if (amd_pmf_supports_accumulator_metrics(dev) &&
> + message == GET_1AH_M80H_METRICS_TABLE_DRAM_ADDR) {
> + dev->dram_addr.hi = amd_pmf_reg_read(dev,
> + dev->smu_regs->arg_reg[1]);
> + dev->dram_addr.size = amd_pmf_reg_read(dev,
> + dev->smu_regs->arg_reg[2]);
> + }
> }
> break;
> case AMD_PMF_RESULT_CMD_REJECT_BUSY:
> @@ -517,6 +540,12 @@ static int amd_pmf_probe(struct platform_device *pdev)
> if (err)
> return err;
>
> + if (amd_pmf_supports_accumulator_metrics(dev)) {
> + err = amd_pmf_get_tbl_dram_addr(dev);
> + if (err)
> + return err;
> + }
> +
> apmf_acpi_init(dev);
> platform_set_drvdata(pdev, dev);
> amd_pmf_dbgfs_register(dev);
> diff --git a/drivers/platform/x86/amd/pmf/metrics.c b/drivers/platform/x86/amd/pmf/metrics.c
> index c4f5f4d36bd6..ceff4f139043 100644
> --- a/drivers/platform/x86/amd/pmf/metrics.c
> +++ b/drivers/platform/x86/amd/pmf/metrics.c
> @@ -16,8 +16,12 @@
> #include <linux/device/devres.h>
> #include <linux/io.h>
> #include <linux/ktime.h>
> +#include <linux/limits.h>
> +#include <linux/math64.h>
> +#include <linux/minmax.h>
> #include <linux/string.h>
> #include <linux/types.h>
> +#include <linux/units.h>
> #include <linux/wordpart.h>
> #include <linux/workqueue.h>
>
> @@ -25,6 +29,89 @@
>
> static struct device *pmf_device;
>
> +static u16 amd_pmf_q10_acc_to_mW(u64 avg_acc)
> +{
> + u64 val = DIV_U64_ROUND_CLOSEST(avg_acc, 1024) * MILLIWATT_PER_WATT;
> +
> + return min_t(u16, val, U16_MAX);
> +}
> +
> +static u16 amd_pmf_q10_acc_to_int(u64 avg_acc)
> +{
> + u64 val = DIV_U64_ROUND_CLOSEST(avg_acc, 1024);
> +
> + return min_t(u16, val, U16_MAX);
> +}
> +
> +static u64 amd_pmf_avg_acc_metric(u32 curr_counter, u32 prev_counter,
> + u64 curr_value, u64 prev_value)
> +{
> + u32 counter_diff;
> + u64 val_diff;
> +
> + /* Check for counter reset */
> + if (curr_counter <= prev_counter)
> + return 0;
> +
> + counter_diff = curr_counter - prev_counter;
> +
> + if (curr_value < prev_value)
> + return 0;
> +
> + val_diff = curr_value - prev_value;
> +
> + return DIV_U64_ROUND_CLOSEST(val_diff, counter_diff);
> +}
> +
> +int amd_pmf_get_tbl_dram_addr(struct amd_pmf_dev *dev)
> +{
> + int ret;
> +
> + ret = amd_pmf_send_cmd(dev, GET_1AH_M80H_METRICS_TABLE_DRAM_ADDR, GET_CMD,
> + ARG_NONE, &dev->dram_addr.lo);
> + if (ret) {
> + dev_err(dev->dev, "Failed to get DRAM address: %d\n", ret);
> + return ret;
> + }
> +
> + dev->metrics_table_phys = ((u64)dev->dram_addr.hi << 32) | dev->dram_addr.lo;
> + dev->mtable_size = dev->dram_addr.size;
> +
> + if (dev->mtable_size != sizeof(dev->mtable_v3)) {
> + dev_err(dev->dev, "Metrics table size mismatch: got %u, expected %zu\n",
> + dev->dram_addr.size, sizeof(dev->mtable_v3));
> + return -EINVAL;
> + }
> +
> + dev->metrics_table_virt = devm_ioremap(dev->dev, dev->metrics_table_phys, dev->mtable_size);
> + if (!dev->metrics_table_virt) {
> + dev_err(dev->dev, "Failed to map DRAM address for PMF metrics table\n");
> + dev->metrics_table_phys = 0;
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +static int amd_pmf_get_metrics_table_log_sample(struct amd_pmf_dev *dev)
> +{
> + int ret;
> +
> + if (!dev->metrics_table_virt) {
> + dev_err(dev->dev, "Metrics DRAM not mapped\n");
> + return -EINVAL;
> + }
> +
> + /* Send command to PMFW to update metrics table log sample */
> + ret = amd_pmf_send_cmd(dev, GET_1AH_M80H_METRICS_TABLE_LOG_SAMPLE, SET_CMD, ARG_NONE, NULL);
> + if (ret) {
> + dev_err(dev->dev, "Failed to request metrics log sample: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> static void amd_pmf_get_metrics(struct work_struct *work)
> {
> struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, work_buffer.work);
> @@ -120,12 +207,46 @@ static int is_npu_metrics_supported(struct amd_pmf_dev *pdev)
> switch (pdev->cpu_id) {
> case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
> case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
> + case PCI_DEVICE_ID_AMD_1AH_M80H_ROOT:
> return 0;
> default:
> return -EOPNOTSUPP;
> }
> }
>
> +static void amd_pmf_calculate_acc_npu_metrics(struct amd_pmf_dev *dev,
> + struct amd_pmf_npu_metrics *data)
> +{
> + struct amd_pmf_metrics_iod *curr, *prev;
> + u64 val;
> + int i;
> +
> + curr = &dev->mtable_v3.iod;
> + prev = &dev->prev_metrics.iod;
> +
> + val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
> + curr->npu_temp_acc, prev->npu_temp_acc);
> + data->npu_temp = amd_pmf_q10_acc_to_int(val);
> + val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
> + curr->npu_power_acc, prev->npu_power_acc);
> + data->npu_power = amd_pmf_q10_acc_to_mW(val);
> + val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
> + curr->npuhclk_freq_eff_acc,
> + prev->npuhclk_freq_eff_acc);
> + data->mpnpuclk_freq = amd_pmf_q10_acc_to_int(val);
> + val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
> + curr->aieclk_freq_eff_acc,
> + prev->aieclk_freq_eff_acc);
> + data->npuclk_freq = amd_pmf_q10_acc_to_int(val);
> +
> + for (i = 0; i < ARRAY_SIZE(curr->npu_busy_acc); i++) {
> + val = amd_pmf_avg_acc_metric(curr->counter_acc, prev->counter_acc,
> + curr->npu_busy_acc[i],
> + prev->npu_busy_acc[i]);
> + data->npu_busy[i] = amd_pmf_q10_acc_to_int(val);
> + }
> +}
> +
> static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_metrics *data)
> {
> int ret, i;
> @@ -136,6 +257,8 @@ static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_m
> if (ret)
> return ret;
>
> + memset(data, 0, sizeof(*data));
> +
> switch (dev->cpu_id) {
> case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
> case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
> @@ -162,6 +285,27 @@ static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_m
> data->npu_reads = dev->m_table_v2.npu_reads;
> data->npu_writes = dev->m_table_v2.npu_writes;
> break;
> + case PCI_DEVICE_ID_AMD_1AH_M80H_ROOT:
> + ret = amd_pmf_get_metrics_table_log_sample(dev);
> + if (ret)
> + return ret;
> +
> + memcpy_fromio(&dev->mtable_v3, dev->metrics_table_virt, sizeof(dev->mtable_v3));
> +
> + /*Ignore the first sample, as previous metrics is uninitialized (zero)
> + * Metrics are calculated as:
> + * metrics = current_metrics - previous_metrics
> + * Skipping the initial sample ensures accurate delta calculations.
> + */
> + if (!dev->npu_metrics_have_prev) {
> + memcpy(&dev->prev_metrics, &dev->mtable_v3, sizeof(dev->mtable_v3));
> + dev->npu_metrics_have_prev = true;
> + return 0;
> + }
> +
> + amd_pmf_calculate_acc_npu_metrics(dev, data);
> + memcpy(&dev->prev_metrics, &dev->mtable_v3, sizeof(dev->mtable_v3));
> + break;
> }
>
> return 0;
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index 4873685c84ad..0f8e6cc9a453 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -75,6 +75,10 @@ struct cookie_header {
> #define SET_PMF_PPT 0x25
> #define SET_PMF_PPT_APU_ONLY 0x26
>
> +/* Message IDs for 1AH_M80H platform */
> +#define GET_1AH_M80H_METRICS_TABLE_LOG_SAMPLE 0x0E
> +#define GET_1AH_M80H_METRICS_TABLE_DRAM_ADDR 0x0F
> +
> /* OS slider update notification */
> #define DC_BEST_PERF 0
> #define DC_BETTER_PERF 1
> @@ -139,6 +143,11 @@ struct cookie_header {
>
> extern int metrics_table_loop_ms;
>
> +#define AMD_PMF_METRIC_CORE_TYPE_MAX 4
> +#define AMD_PMF_METRIC_CCX_MAX 4
> +#define AMD_PMF_NUM_CLK_DPM_LEVELS 8
> +#define AMD_PMF_NUM_MAX_CORES 12
> +
> typedef void (*apmf_event_handler_t)(acpi_handle handle, u32 event, void *data);
>
> static const uuid_t amd_pmf_ta_uuid[] __used = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8,
> @@ -250,6 +259,199 @@ struct apmf_fan_idx {
> u32 fan_ctl_idx;
> } __packed;
>
> +struct amd_pmf_metrics_iod {
> + u32 counter_acc;
> + /* Set Voltage */
> + u64 vddcr_set_voltage;
> + u64 vddcr_soc_set_voltage;
> + u64 vddcr_npu_set_voltage;
> + u64 vddcr_lp_set_voltage;
> + u64 vddcr_gfx_set_voltage;
> + u64 vdd_misc_set_voltage;
> + /* Telemetry Voltages */
> + u64 vddcr_telemetry_voltage;
> + u64 vddcr_soc_telemetry_voltage;
> + u64 vddcr_npu_telemetry_voltage;
> + u64 vddcr_lp_telemetry_voltage;
> + u64 vddcr_gfx_telemetry_voltage;
> + u64 vdd_misc_telemetry_voltage;
> + /* Telemetry Powers */
> + u64 vddcr_telemetry_power;
> + u64 vddcr_soc_telemetry_power;
> + u64 vddcr_npu_telemetry_power;
> + u64 vddcr_lp_telemetry_power;
> + u64 vddcr_gfx_telemetry_power;
> + u64 vdd_misc_telemetry_power;
> + /* Throttlers - Fast PPT */
> + u32 fppt_fused_limit;
> + u32 fppt_max_irm_limit;
> + u32 fppt_max_pbo_limit;
> + u32 fppt_limit;
> + u64 fppt_value_acc;
> + u32 fppt_residency_acc;
> + /* Throttlers - Slow PPT */
> + u32 sppt_fused_limit;
> + u32 sppt_max_irm_limit;
> + u32 sppt_max_pbo_limit;
> + u32 sppt_limit;
> + u64 sppt_value_acc;
> + u32 sppt_residency_acc;
> + /* Throttlers - STAPM */
> + u32 spl_fused_limit;
> + u32 spl_max_irm_limit;
> + u32 spl_max_pbo_limit;
> + u32 spl_limit;
> + u64 spl_value_acc;
> + u32 spl_residency_acc;
> + /* Throttlers - TDC VDDCR */
> + u32 tdc_vddcr_fused_limit;
> + u32 tdc_vddcr_max_irm_limit;
> + u32 tdc_vddcr_max_pbo_limit;
> + u32 tdc_vddcr_limit;
> + u64 tdc_vddcr_value_acc;
> + u32 tdc_vddcr_residency_acc;
> + /* Throttlers - TDC VDDCR_SOC */
> + u32 tdc_vddcr_soc_fused_limit;
> + u32 tdc_vddcr_soc_max_irm_limit;
> + u32 tdc_vddcr_soc_max_pbo_limit;
> + u32 tdc_vddcr_soc_limit;
> + u64 tdc_vddcr_soc_value_acc;
> + u32 tdc_vddcr_soc_residency_acc;
> + /* Throttlers - TDC VDDCR_NPU */
> + u32 tdc_vddcr_npu_fused_limit;
> + u32 tdc_vddcr_npu_max_irm_limit;
> + u32 tdc_vddcr_npu_max_pbo_limit;
> + u32 tdc_vddcr_npu_limit;
> + u64 tdc_vddcr_npu_value_acc;
> + u32 tdc_vddcr_npu_residency_acc;
> + /* Throttlers - TDC VDDCR_LP */
> + u32 tdc_vddcr_lp_fused_limit;
> + u32 tdc_vddcr_lp_max_irm_limit;
> + u32 tdc_vddcr_lp_max_pbo_limit;
> + u32 tdc_vddcr_lp_limit;
> + u64 tdc_vddcr_lp_value_acc;
> + u32 tdc_vddcr_lp_residency_acc;
> + /* Throttlers - TDC VDDCR_GFX */
> + u32 tdc_vddcr_gfx_fused_limit;
> + u32 tdc_vddcr_gfx_max_irm_limit;
> + u32 tdc_vddcr_gfx_max_pbo_limit;
> + u32 tdc_vddcr_gfx_limit;
> + u64 tdc_vddcr_gfx_value_acc;
> + u32 tdc_vddcr_gfx_residency_acc;
> + /* Throttlers - EDC VDDCR */
> + u32 edc_vddcr_fused_limit;
> + u32 edc_vddcr_max_irm_limit;
> + u32 edc_vddcr_max_pbo_limit;
> + u32 edc_vddcr_limit;
> + /* Throttlers - Thermal */
> + u32 thm_fused_limit;
> + u32 thm_limit;
> + u64 thm_value_acc;
> + u32 thm_residency_acc;
> + u32 prochot_residency_acc;
> + u64 gfx_temp_acc;
> + u64 soc_temp_acc;
> + u32 p3t_fused_limit;
> + u64 p3t_value_acc;
> + /* Power */
> + u64 system_power_acc;
> + u64 apu_power_acc;
> + u64 dgpu_power_acc;
> + u64 npu_power_acc;
> + /* Frequencies */
> + u64 fclk_freq_eff_acc;
> + u64 memclk_freq_eff_acc;
> + u64 lclk_freq_eff_acc;
> + u64 gfxclk_freq_eff_acc;
> + u64 socclk_freq_eff_acc;
> + u64 vclk_freq_eff_acc;
> + u64 vpeclk_freq_eff_acc;
> + u64 aieclk_freq_eff_acc;
> + u64 npuhclk_freq_eff_acc;
> + /* Bandwidth */
> + u64 dram_read_bandwidth;
> + u64 dram_write_bandwidth;
> + /* Activity Monitors */
> + u64 gfx_busy_acc;
> + u64 vcn_busy_acc;
> + u64 npu_busy_acc[3];
> + /* STT Limits */
> + u32 stt_min_limit;
> + u64 stt_apu_hotspot_temp_acc;
> + u64 stt_hs2_hotspot_temp_acc;
> + u32 stt_apu_temp_limit;
> + u64 stt_apu_skin_temp_acc;
> + /* Residencies */
> + u64 cpuoff_residency_ccx0;
> + u64 cpuoff_residency_ccx1;
> + u64 cpuoff_residency_ccx2;
> + u64 cpuoff_residency_ccx3;
> + /* DF-pstates */
> + u32 fclk_freq_table[AMD_PMF_NUM_CLK_DPM_LEVELS];
> + u32 uclk_freq_table[AMD_PMF_NUM_CLK_DPM_LEVELS];
> + u32 ddr_rate_table[AMD_PMF_NUM_CLK_DPM_LEVELS];
> + u8 dfpstate_source[AMD_PMF_NUM_CLK_DPM_LEVELS];
> + /* System */
> + u8 gfx_disabled;
> + u8 spare2[3];
> + u32 gfxclk_fmax;
> + u8 cclk_core_fuse_enable[AMD_PMF_METRIC_CORE_TYPE_MAX][AMD_PMF_NUM_MAX_CORES];
> + u8 cclk_core_enabled[AMD_PMF_METRIC_CORE_TYPE_MAX][AMD_PMF_NUM_MAX_CORES];
> + u32 cclk_fmax[AMD_PMF_METRIC_CORE_TYPE_MAX][AMD_PMF_NUM_MAX_CORES];
> + /* Overclock Capable */
> + u8 cpu_precise_and_direct_oc_capable;
> + u8 gfx_precise_and_direct_oc_capable;
> + u8 pbo_basic_oc_capable;
> + u8 pbo_advanced_oc_capable;
> + u8 pbo_nitro_oc_capable;
> + u8 memory_and_fabric_oc_capable;
> + u8 misc_oc_capable;
> + u8 extreme_cold_oc_capable;
> + u8 down_config_control_capable;
> + u8 spare0[3];
> + /* Overclock Status */
> + u32 fit_limit_scalar;
> + u8 ln2_enabled;
> + u8 cpu_precise_and_direct_oc_enabled;
> + u8 gfx_precise_and_direct_oc_enabled;
> + u8 spare1[2];
> + /* Voltage Guardband in PSM count */
> + s8 psm_guardband[5][5][3];
> + s32 core_power_limit_offset;
> + u32 max_freq_offset[5];
> + u64 npu_temp_acc;
> + u64 dfpstate_residency_acc[AMD_PMF_NUM_CLK_DPM_LEVELS];
> + u32 cclk_fboost;
> + /* PMF */
> + u32 pmf_fast_apu_ppt_limit;
> + u64 pmf_fast_apu_ppt_value_acc;
> + u32 pmf_fast_apu_ppt_residency_acc;
> + u32 pmf_slow_apu_ppt_limit;
> + u64 pmf_slow_apu_ppt_value_acc;
> + u32 pmf_slow_apu_ppt_residency_acc;
> + u32 pmf_fast_spm_limit;
> + u64 pmf_fast_spm_value_acc;
> + u32 pmf_fast_spm_residency_acc;
> + u32 pmf_slow_spm_limit;
> + u64 pmf_slow_spm_value_acc;
> + u32 pmf_slow_spm_residency_acc;
> + u32 spare3[5];
> +} __packed __aligned(4);
Include is missing (it's a pre-existing problem).
> +
> +struct amd_pmf_metrics_ccx {
> + u64 core_c0[AMD_PMF_NUM_MAX_CORES];
> + u64 core_cc6[AMD_PMF_NUM_MAX_CORES];
> + u64 core_freq[AMD_PMF_NUM_MAX_CORES];
> + u64 core_freqeff[AMD_PMF_NUM_MAX_CORES];
> + u64 core_temp[AMD_PMF_NUM_MAX_CORES];
> + u64 core_power[AMD_PMF_NUM_MAX_CORES];
> +} __packed __aligned(4);
> +
> +struct amd_pmf_metrics_v3 {
> + struct amd_pmf_metrics_iod iod;
> + struct amd_pmf_metrics_ccx ccx[AMD_PMF_METRIC_CCX_MAX];
> +} __packed __aligned(4);
> +
> struct smu_pmf_metrics_v2 {
> u16 core_frequency[16]; /* MHz */
> u16 core_power[16]; /* mW */
> @@ -406,6 +608,12 @@ struct amd_pmf_smu_regs {
> u32 arg_reg[3];
> };
>
> +struct amd_pmf_arg_data {
> + u32 lo;
> + u32 hi;
> + u32 size;
> +};
> +
> struct amd_pmf_dev {
> void __iomem *regbase;
> void __iomem *smu_virt_addr;
> @@ -459,6 +667,12 @@ struct amd_pmf_dev {
> struct mutex metrics_mutex;
> u32 bios_output[BIOS_OUTPUT_MAX];
> const struct amd_pmf_smu_regs *smu_regs;
> + void __iomem *metrics_table_virt; /* Mapped DRAM virtual address for metrics table */
> + phys_addr_t metrics_table_phys; /* DRAM physical address for metrics table */
> + struct amd_pmf_metrics_v3 mtable_v3; /* IOD and CCX */
> + struct amd_pmf_arg_data dram_addr;
> + struct amd_pmf_metrics_v3 prev_metrics; /* Previous metrics for delta calculation */
> + bool npu_metrics_have_prev;
> };
>
> struct apmf_sps_prop_granular_v2 {
> @@ -868,6 +1082,9 @@ u32 fixp_q88_fromint(u32 val);
> int is_apmf_bios_input_notifications_supported(struct amd_pmf_dev *pdev);
> void amd_pmf_set_device(struct device *p_device);
>
> +/* Metrics layer */
> +int amd_pmf_get_tbl_dram_addr(struct amd_pmf_dev *dev);
> +
> /* SPS Layer */
> int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
> void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
> diff --git a/include/linux/amd-pmf-io.h b/include/linux/amd-pmf-io.h
> index e014d4ce5a20..b84c200b0f8e 100644
> --- a/include/linux/amd-pmf-io.h
> +++ b/include/linux/amd-pmf-io.h
> @@ -60,6 +60,7 @@ struct amd_sfh_info {
> * @mpnpuclk_freq: MPNPU [MHz]
> * @npu_reads: NPU read bandwidth [MB/sec]
> * @npu_writes: NPU write bandwidth [MB/sec]
> + * @npu_temp: NPU temperature [C]
> */
> struct amd_pmf_npu_metrics {
> u16 npuclk_freq;
> @@ -68,6 +69,7 @@ struct amd_pmf_npu_metrics {
> u16 mpnpuclk_freq;
> u16 npu_reads;
> u16 npu_writes;
> + u16 npu_temp;
> };
>
> int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op);
>
--
i.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics
2026-07-13 18:39 [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
` (5 preceding siblings ...)
2026-07-13 18:39 ` [PATCH v2 6/6] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support Shyam Sundar S K
@ 2026-07-13 18:47 ` Mario Limonciello
6 siblings, 0 replies; 10+ messages in thread
From: Mario Limonciello @ 2026-07-13 18:47 UTC (permalink / raw)
To: Shyam Sundar S K, hansg, ilpo.jarvinen; +Cc: platform-driver-x86, Patil.Reddy
On 7/13/26 13:39, Shyam Sundar S K wrote:
> This series cleans up the PMF-SMU mailbox handling, adds support for the
> AMD 1AH_M80H (Family 1AH Model 80H) platform, and introduces NPU metrics
> retrieval for it based on a firmware managed DRAM metrics table.
>
> Today the SMU mailbox register offsets are hardcoded in the send path,
> which does not scale to platforms that use a different mailbox layout.
> The first two patches address this: patch 1 moves the offsets into a
> per-SoC struct amd_pmf_smu_regs carried as PCI driver_data, so all
> existing platforms share a single legacy instance and new platforms only
> add a table entry. Patch 2 then adds the 1AH_M80H device/ACPI IDs and its
> extended mailbox, which uses three argument registers to return the
> 64-bit DRAM address and size of the metrics table.
>
> Patches 3 and 4 are pure refactors with no functional change: the metrics
> code moves out of core.c into a dedicated metrics.c, and the NPU metrics
> retrieval is restructured around a per-CPU-ID switch so additional table
> formats can be slotted in cleanly.
>
> Patch 5 builds on that to add 1AH_M80H NPU metrics. Unlike earlier
> platforms that copy a transfer table, 1AH_M80H exposes an accumulator
> based metrics table (amd_pmf_metrics_v3) in DRAM; NPU values are derived
> by delta calculation between consecutive samples, with the first sample
> discarded to seed the baseline. This also extends amd_pmf_npu_metrics
> with npu_temp, sourced from the new npu_temp_acc accumulator.
>
> The series applies cleanly and existing platforms see no functional
> change.
>
> This series is based on top of the review-ilpo-next branch, with commit
> f6ee11d77d15 as the head.
>
> v2:
> - Change the amd_pmf_legacy_smu_regs to amd_pmf_smu_regs_v1 and v2 for 1ah_m80h
> - Remove arg0, arg1, arg2 comments wriiten in the amd_pmf_smu_regs_v2 struct
> - Update git commit messages with amd_pmf_smu_regs_v1 and amd_pmf_smu_regs_v2 accordingly
> - Add includes and introduce separate patch for upper/lower_32_bits()
> - Use DIV_U64_ROUND_CLOSEST() instead of DIV_ROUND_CLOSEST_ULL()
> - Use macros present in units.h
> - Replace is_amd_pmf_1ah_m80h() with amd_pmf_supports_accumulator_metrics()
> - Add Reviewed-by tags
>
> Shyam Sundar S K (6):
> platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox
> registers
> platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended SMU mailbox
> registers
> platform/x86/amd/pmf: Move metrics code to dedicated file
> platform/x86/amd/pmf: Use upper/lower_32_bits() in
> amd_pmf_set_dram_addr()
> platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility
> platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics
> support
>
> drivers/platform/x86/amd/pmf/Makefile | 2 +-
> drivers/platform/x86/amd/pmf/core.c | 254 +++++++------------
> drivers/platform/x86/amd/pmf/metrics.c | 330 +++++++++++++++++++++++++
> drivers/platform/x86/amd/pmf/pmf.h | 233 +++++++++++++++++
> include/linux/amd-pmf-io.h | 2 +
> 5 files changed, 654 insertions(+), 167 deletions(-)
> create mode 100644 drivers/platform/x86/amd/pmf/metrics.c
>
For remaining patches I didn't previously review.
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
^ permalink raw reply [flat|nested] 10+ messages in thread