* [PATCH v2 0/6] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics
@ 2026-07-13 18:39 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
` (6 more replies)
0 siblings, 7 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
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
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [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
* [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 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
* 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
* 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
end of thread, other threads:[~2026-07-21 16:13 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [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
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 ` [PATCH v2 5/6] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility 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-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
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.