All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics
@ 2026-07-10 17:42 Shyam Sundar S K
  2026-07-10 17:42 ` [PATCH 1/5] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers Shyam Sundar S K
                   ` (4 more replies)
  0 siblings, 5 replies; 17+ messages in thread
From: Shyam Sundar S K @ 2026-07-10 17:42 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.

Shyam Sundar S K (5):
  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: 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    | 251 +++++++------------
 drivers/platform/x86/amd/pmf/metrics.c | 327 +++++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/pmf.h     | 234 ++++++++++++++++++
 include/linux/amd-pmf-io.h             |   2 +
 5 files changed, 649 insertions(+), 167 deletions(-)
 create mode 100644 drivers/platform/x86/amd/pmf/metrics.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/5] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers
  2026-07-10 17:42 [PATCH 0/5] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
@ 2026-07-10 17:42 ` Shyam Sundar S K
  2026-07-10 19:07   ` Mario Limonciello
  2026-07-10 17:42 ` [PATCH 2/5] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended " Shyam Sundar S K
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 17+ messages in thread
From: Shyam Sundar S K @ 2026-07-10 17:42 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_legacy_smu_regs 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..1826fc64bf56 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 legacy SMU mailbox registers */
+static const struct amd_pmf_smu_regs amd_pmf_legacy_smu_regs = {
+	.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_legacy_smu_regs) },
+	{ PCI_DEVICE_DATA(AMD, CPU_ID_PS,     &amd_pmf_legacy_smu_regs) },
+	{ PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, &amd_pmf_legacy_smu_regs) },
+	{ PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, &amd_pmf_legacy_smu_regs) },
 	{ }
 };
 
@@ -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] 17+ messages in thread

* [PATCH 2/5] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended SMU mailbox registers
  2026-07-10 17:42 [PATCH 0/5] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
  2026-07-10 17:42 ` [PATCH 1/5] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers Shyam Sundar S K
@ 2026-07-10 17:42 ` Shyam Sundar S K
  2026-07-10 19:10   ` Mario Limonciello
  2026-07-10 17:42 ` [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file Shyam Sundar S K
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 17+ messages in thread
From: Shyam Sundar S K @ 2026-07-10 17:42 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_1ah_m80h_smu_regs to capture this extended register layout
and register it in pmf_pci_ids[] via PCI_DEVICE_DATA(), keeping the
existing amd_pmf_legacy_smu_regs 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 | 29 +++++++++++++++++++++++++----
 drivers/platform/x86/amd/pmf/pmf.h  |  3 ++-
 2 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 1826fc64bf56..7c5a1ec3d7fc 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_REG_MESSAGE_1AH_M80H	0xA04
+#define AMD_PMF_REG_RESPONSE_1AH_M80H	0xA08
+#define AMD_PMF_REG_ARGUMENT0_1AH_M80H	0xA0C
+#define AMD_PMF_REG_ARGUMENT1_1AH_M80H	0xAAC
+#define AMD_PMF_REG_ARGUMENT2_1AH_M80H	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,19 @@ 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_legacy_smu_regs = {
 	.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_1ah_m80h_smu_regs = {
+	.msg_reg	= AMD_PMF_REG_MESSAGE_1AH_M80H,
+	.resp_reg	= AMD_PMF_REG_RESPONSE_1AH_M80H,
+	/* arg_reg[0], arg_reg[1] and arg_reg[2] carry low, hi address and size for DRAM metrics */
+	.arg_reg	= {
+		AMD_PMF_REG_ARGUMENT0_1AH_M80H,
+		AMD_PMF_REG_ARGUMENT1_1AH_M80H,
+		AMD_PMF_REG_ARGUMENT2_1AH_M80H,
+	},
 };
 
 static const struct pci_device_id pmf_pci_ids[] = {
@@ -274,6 +293,7 @@ static const struct pci_device_id pmf_pci_ids[] = {
 	{ PCI_DEVICE_DATA(AMD, CPU_ID_PS,     &amd_pmf_legacy_smu_regs) },
 	{ PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, &amd_pmf_legacy_smu_regs) },
 	{ PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, &amd_pmf_legacy_smu_regs) },
+	{ PCI_DEVICE_DATA(AMD, 1AH_M80H_ROOT, &amd_pmf_1ah_m80h_smu_regs) },
 	{ }
 };
 
@@ -563,6 +583,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] 17+ messages in thread

* [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file
  2026-07-10 17:42 [PATCH 0/5] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
  2026-07-10 17:42 ` [PATCH 1/5] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers Shyam Sundar S K
  2026-07-10 17:42 ` [PATCH 2/5] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended " Shyam Sundar S K
@ 2026-07-10 17:42 ` Shyam Sundar S K
  2026-07-10 18:04   ` Ilpo Järvinen
  2026-07-10 19:10   ` Mario Limonciello
  2026-07-10 17:42 ` [PATCH 4/5] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility Shyam Sundar S K
  2026-07-10 17:42 ` [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support Shyam Sundar S K
  4 siblings, 2 replies; 17+ messages in thread
From: Shyam Sundar S K @ 2026-07-10 17:42 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, Patil.Reddy, mario.limonciello,
	Shyam Sundar S K

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>
---
 drivers/platform/x86/amd/pmf/Makefile  |   2 +-
 drivers/platform/x86/amd/pmf/core.c    | 156 +--------------------
 drivers/platform/x86/amd/pmf/metrics.c | 179 +++++++++++++++++++++++++
 drivers/platform/x86/amd/pmf/pmf.h     |   3 +
 4 files changed, 185 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 7c5a1ec3d7fc..c0a21808b867 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);
@@ -297,125 +264,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;
@@ -678,7 +526,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..bb4f9127c655
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/metrics.c
@@ -0,0 +1,179 @@
+// 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/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] 17+ messages in thread

* [PATCH 4/5] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility
  2026-07-10 17:42 [PATCH 0/5] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
                   ` (2 preceding siblings ...)
  2026-07-10 17:42 ` [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file Shyam Sundar S K
@ 2026-07-10 17:42 ` Shyam Sundar S K
  2026-07-10 19:11   ` Mario Limonciello
  2026-07-10 17:42 ` [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support Shyam Sundar S K
  4 siblings, 1 reply; 17+ messages in thread
From: Shyam Sundar S K @ 2026-07-10 17:42 UTC (permalink / raw)
  To: hansg, ilpo.jarvinen
  Cc: platform-driver-x86, Patil.Reddy, mario.limonciello,
	Shyam Sundar S K

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.

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 bb4f9127c655..5a8ebacbc3b3 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/bits.h>
 #include <linux/cleanup.h>
 #include <linux/container_of.h>
@@ -134,28 +135,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] 17+ messages in thread

* [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support
  2026-07-10 17:42 [PATCH 0/5] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
                   ` (3 preceding siblings ...)
  2026-07-10 17:42 ` [PATCH 4/5] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility Shyam Sundar S K
@ 2026-07-10 17:42 ` Shyam Sundar S K
  2026-07-10 18:16   ` Ilpo Järvinen
  2026-07-10 18:21   ` Mario Limonciello
  4 siblings, 2 replies; 17+ messages in thread
From: Shyam Sundar S K @ 2026-07-10 17:42 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

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    |  25 +++
 drivers/platform/x86/amd/pmf/metrics.c | 142 ++++++++++++++++
 drivers/platform/x86/amd/pmf/pmf.h     | 218 +++++++++++++++++++++++++
 include/linux/amd-pmf-io.h             |   2 +
 4 files changed, 387 insertions(+)

diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index c0a21808b867..5d744eb71418 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -68,6 +68,12 @@ static bool smart_pc_support = true;
 module_param(smart_pc_support, bool, 0444);
 MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
 
+/* Helper to check if platform is 1AH_M80H */
+bool is_amd_pmf_1ah_m80h(struct amd_pmf_dev *pdev)
+{
+	return pdev->cpu_id == PCI_DEVICE_ID_AMD_1AH_M80H_ROOT;
+}
+
 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 +161,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 (is_amd_pmf_1ah_m80h(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 +226,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 (is_amd_pmf_1ah_m80h(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:
@@ -518,6 +537,12 @@ static int amd_pmf_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	if (is_amd_pmf_1ah_m80h(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 5a8ebacbc3b3..4cb45a8bbdb4 100644
--- a/drivers/platform/x86/amd/pmf/metrics.c
+++ b/drivers/platform/x86/amd/pmf/metrics.c
@@ -17,6 +17,8 @@
 #include <linux/device/devres.h>
 #include <linux/io.h>
 #include <linux/ktime.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
 #include <linux/string.h>
 #include <linux/workqueue.h>
 
@@ -24,6 +26,89 @@
 
 static struct device *pmf_device;
 
+static u16 amd_pmf_q10_acc_to_mW(u64 avg_acc)
+{
+	u64 val = DIV_ROUND_CLOSEST_ULL(avg_acc, 1024) * 1000;
+
+	return min_t(u16, val, U16_MAX);
+}
+
+static u16 amd_pmf_q10_acc_to_int(u64 avg_acc)
+{
+	u64 val = DIV_ROUND_CLOSEST_ULL(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_ROUND_CLOSEST_ULL(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);
@@ -119,12 +204,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;
@@ -135,6 +254,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:
@@ -161,6 +282,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..76cfeb2b963c 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_1ah_m80h_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_1ah_m80h_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 {
@@ -867,6 +1081,10 @@ 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);
+bool is_amd_pmf_1ah_m80h(struct amd_pmf_dev *pdev);
+
+/* 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);
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] 17+ messages in thread

* Re: [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file
  2026-07-10 17:42 ` [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file Shyam Sundar S K
@ 2026-07-10 18:04   ` Ilpo Järvinen
  2026-07-10 18:07     ` Ilpo Järvinen
  2026-07-10 19:10   ` Mario Limonciello
  1 sibling, 1 reply; 17+ messages in thread
From: Ilpo Järvinen @ 2026-07-10 18:04 UTC (permalink / raw)
  To: Shyam Sundar S K
  Cc: Hans de Goede, platform-driver-x86, Patil.Reddy,
	mario.limonciello

On Fri, 10 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>
> ---
>  drivers/platform/x86/amd/pmf/Makefile  |   2 +-
>  drivers/platform/x86/amd/pmf/core.c    | 156 +--------------------
>  drivers/platform/x86/amd/pmf/metrics.c | 179 +++++++++++++++++++++++++
>  drivers/platform/x86/amd/pmf/pmf.h     |   3 +
>  4 files changed, 185 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 7c5a1ec3d7fc..c0a21808b867 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);
> @@ -297,125 +264,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;
> @@ -678,7 +526,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..bb4f9127c655
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/metrics.c
> @@ -0,0 +1,179 @@
> +// 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/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);

upper/lower_32_bits(). Don't forget to add the header.

> +
> +	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++)

Typically developers always forget these, ARRAY_SIZE() and dev_*() 
printing need headers.

Check also types.h. I had 4th one in my mind but seem to have forgotten it 
while typing...

> +		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] 17+ messages in thread

* Re: [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file
  2026-07-10 18:04   ` Ilpo Järvinen
@ 2026-07-10 18:07     ` Ilpo Järvinen
  0 siblings, 0 replies; 17+ messages in thread
From: Ilpo Järvinen @ 2026-07-10 18:07 UTC (permalink / raw)
  To: Shyam Sundar S K
  Cc: Hans de Goede, platform-driver-x86, Patil.Reddy,
	mario.limonciello

[-- Attachment #1: Type: text/plain, Size: 15324 bytes --]

On Fri, 10 Jul 2026, Ilpo Järvinen wrote:

> On Fri, 10 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>
> > ---
> >  drivers/platform/x86/amd/pmf/Makefile  |   2 +-
> >  drivers/platform/x86/amd/pmf/core.c    | 156 +--------------------
> >  drivers/platform/x86/amd/pmf/metrics.c | 179 +++++++++++++++++++++++++
> >  drivers/platform/x86/amd/pmf/pmf.h     |   3 +
> >  4 files changed, 185 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 7c5a1ec3d7fc..c0a21808b867 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);
> > @@ -297,125 +264,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;
> > @@ -678,7 +526,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..bb4f9127c655
> > --- /dev/null
> > +++ b/drivers/platform/x86/amd/pmf/metrics.c
> > @@ -0,0 +1,179 @@
> > +// 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/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);
> 
> upper/lower_32_bits(). Don't forget to add the header.

Ah, this was a move patch so you may want to do that conversion in own 
patch (it will be easier for me to review moves using diff trickery if 
there is as little other changes as possible).

--
 i.

> > +
> > +	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++)
> 
> Typically developers always forget these, ARRAY_SIZE() and dev_*() 
> printing need headers.
> 
> Check also types.h. I had 4th one in my mind but seem to have forgotten it 
> while typing...
> 
> > +		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);
> > 
> 
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support
  2026-07-10 17:42 ` [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support Shyam Sundar S K
@ 2026-07-10 18:16   ` Ilpo Järvinen
  2026-07-13 18:39     ` Shyam Sundar S K
  2026-07-10 18:21   ` Mario Limonciello
  1 sibling, 1 reply; 17+ messages in thread
From: Ilpo Järvinen @ 2026-07-10 18:16 UTC (permalink / raw)
  To: Shyam Sundar S K
  Cc: Hans de Goede, platform-driver-x86, Patil.Reddy,
	mario.limonciello

On Fri, 10 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
> 
> 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    |  25 +++
>  drivers/platform/x86/amd/pmf/metrics.c | 142 ++++++++++++++++
>  drivers/platform/x86/amd/pmf/pmf.h     | 218 +++++++++++++++++++++++++
>  include/linux/amd-pmf-io.h             |   2 +
>  4 files changed, 387 insertions(+)
> 
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index c0a21808b867..5d744eb71418 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -68,6 +68,12 @@ static bool smart_pc_support = true;
>  module_param(smart_pc_support, bool, 0444);
>  MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
>  
> +/* Helper to check if platform is 1AH_M80H */
> +bool is_amd_pmf_1ah_m80h(struct amd_pmf_dev *pdev)
> +{
> +	return pdev->cpu_id == PCI_DEVICE_ID_AMD_1AH_M80H_ROOT;
> +}
> +
>  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 +161,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 (is_amd_pmf_1ah_m80h(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 +226,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 (is_amd_pmf_1ah_m80h(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:
> @@ -518,6 +537,12 @@ static int amd_pmf_probe(struct platform_device *pdev)
>  	if (err)
>  		return err;
>  
> +	if (is_amd_pmf_1ah_m80h(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 5a8ebacbc3b3..4cb45a8bbdb4 100644
> --- a/drivers/platform/x86/amd/pmf/metrics.c
> +++ b/drivers/platform/x86/amd/pmf/metrics.c
> @@ -17,6 +17,8 @@
>  #include <linux/device/devres.h>
>  #include <linux/io.h>
>  #include <linux/ktime.h>
> +#include <linux/math.h>
> +#include <linux/minmax.h>

This was the fourth header I was missing earlier from the almost always 
forgotten list. :-)

>  #include <linux/string.h>
>  #include <linux/workqueue.h>
>  
> @@ -24,6 +26,89 @@
>  
>  static struct device *pmf_device;
>  
> +static u16 amd_pmf_q10_acc_to_mW(u64 avg_acc)
> +{
> +	u64 val = DIV_ROUND_CLOSEST_ULL(avg_acc, 1024) * 1000;

Please use one of the 64-bit divide helpers whenever any of the values is 
64-bit.

Is that 1000 something from units.h?

> +
> +	return min_t(u16, val, U16_MAX);
> +}
> +
> +static u16 amd_pmf_q10_acc_to_int(u64 avg_acc)
> +{
> +	u64 val = DIV_ROUND_CLOSEST_ULL(avg_acc, 1024);

Ditto.

> +
> +	return min_t(u16, val, U16_MAX);

+ limits.h

> +}
> +
> +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_ROUND_CLOSEST_ULL(val_diff, counter_diff);

Use 64-bit helpers.

> +}
> +
> +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);
> @@ -119,12 +204,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;
> @@ -135,6 +254,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:
> @@ -161,6 +282,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..76cfeb2b963c 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,


-- 
 i.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support
  2026-07-10 17:42 ` [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support Shyam Sundar S K
  2026-07-10 18:16   ` Ilpo Järvinen
@ 2026-07-10 18:21   ` Mario Limonciello
  1 sibling, 0 replies; 17+ messages in thread
From: Mario Limonciello @ 2026-07-10 18:21 UTC (permalink / raw)
  To: Shyam Sundar S K, hansg, ilpo.jarvinen; +Cc: platform-driver-x86, Patil.Reddy



On 7/10/26 12:42, 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
> 
> 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    |  25 +++
>   drivers/platform/x86/amd/pmf/metrics.c | 142 ++++++++++++++++
>   drivers/platform/x86/amd/pmf/pmf.h     | 218 +++++++++++++++++++++++++
>   include/linux/amd-pmf-io.h             |   2 +
>   4 files changed, 387 insertions(+)
> 
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index c0a21808b867..5d744eb71418 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -68,6 +68,12 @@ static bool smart_pc_support = true;
>   module_param(smart_pc_support, bool, 0444);
>   MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
>   
> +/* Helper to check if platform is 1AH_M80H */
> +bool is_amd_pmf_1ah_m80h(struct amd_pmf_dev *pdev)
> +{
> +	return pdev->cpu_id == PCI_DEVICE_ID_AMD_1AH_M80H_ROOT;
> +}
> +

The main difference is that metrics come from an accumulator format.
Althought 1ah M80h works this way, it's not intended to be a straggler. 
I'd think this helper would be better structured like this:

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 +161,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 (is_amd_pmf_1ah_m80h(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 +226,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 (is_amd_pmf_1ah_m80h(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:
> @@ -518,6 +537,12 @@ static int amd_pmf_probe(struct platform_device *pdev)
>   	if (err)
>   		return err;
>   
> +	if (is_amd_pmf_1ah_m80h(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 5a8ebacbc3b3..4cb45a8bbdb4 100644
> --- a/drivers/platform/x86/amd/pmf/metrics.c
> +++ b/drivers/platform/x86/amd/pmf/metrics.c
> @@ -17,6 +17,8 @@
>   #include <linux/device/devres.h>
>   #include <linux/io.h>
>   #include <linux/ktime.h>
> +#include <linux/math.h>
> +#include <linux/minmax.h>
>   #include <linux/string.h>
>   #include <linux/workqueue.h>
>   
> @@ -24,6 +26,89 @@
>   
>   static struct device *pmf_device;
>   
> +static u16 amd_pmf_q10_acc_to_mW(u64 avg_acc)
> +{
> +	u64 val = DIV_ROUND_CLOSEST_ULL(avg_acc, 1024) * 1000;
> +
> +	return min_t(u16, val, U16_MAX);
> +}
> +
> +static u16 amd_pmf_q10_acc_to_int(u64 avg_acc)
> +{
> +	u64 val = DIV_ROUND_CLOSEST_ULL(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_ROUND_CLOSEST_ULL(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);
> @@ -119,12 +204,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;
> @@ -135,6 +254,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:
> @@ -161,6 +282,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..76cfeb2b963c 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_1ah_m80h_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_1ah_m80h_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 {
> @@ -867,6 +1081,10 @@ 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);
> +bool is_amd_pmf_1ah_m80h(struct amd_pmf_dev *pdev);
> +
> +/* 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);
> 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);


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/5] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers
  2026-07-10 17:42 ` [PATCH 1/5] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers Shyam Sundar S K
@ 2026-07-10 19:07   ` Mario Limonciello
  0 siblings, 0 replies; 17+ messages in thread
From: Mario Limonciello @ 2026-07-10 19:07 UTC (permalink / raw)
  To: Shyam Sundar S K, hansg, ilpo.jarvinen; +Cc: platform-driver-x86, Patil.Reddy



On 7/10/26 12:42, Shyam Sundar S K wrote:
> 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_legacy_smu_regs 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..1826fc64bf56 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 legacy SMU mailbox registers */
> +static const struct amd_pmf_smu_regs amd_pmf_legacy_smu_regs = {
> +	.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_legacy_smu_regs) },
> +	{ PCI_DEVICE_DATA(AMD, CPU_ID_PS,     &amd_pmf_legacy_smu_regs) },
> +	{ PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, &amd_pmf_legacy_smu_regs) },
> +	{ PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, &amd_pmf_legacy_smu_regs) },

I don't feel super strongly about this, but I generally feel that the 
word 'legacy' ages poorly.  How about a 'v1', 'v2', etc layout?

>   	{ }
>   };
>   
> @@ -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 {


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 2/5] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended SMU mailbox registers
  2026-07-10 17:42 ` [PATCH 2/5] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended " Shyam Sundar S K
@ 2026-07-10 19:10   ` Mario Limonciello
  2026-07-13 18:37     ` Shyam Sundar S K
  0 siblings, 1 reply; 17+ messages in thread
From: Mario Limonciello @ 2026-07-10 19:10 UTC (permalink / raw)
  To: Shyam Sundar S K, hansg, ilpo.jarvinen; +Cc: platform-driver-x86, Patil.Reddy



On 7/10/26 12:42, Shyam Sundar S K wrote:
> 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_1ah_m80h_smu_regs to capture this extended register layout
> and register it in pmf_pci_ids[] via PCI_DEVICE_DATA(), keeping the
> existing amd_pmf_legacy_smu_regs 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 | 29 +++++++++++++++++++++++++----
>   drivers/platform/x86/amd/pmf/pmf.h  |  3 ++-
>   2 files changed, 27 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 1826fc64bf56..7c5a1ec3d7fc 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_REG_MESSAGE_1AH_M80H	0xA04
> +#define AMD_PMF_REG_RESPONSE_1AH_M80H	0xA08
> +#define AMD_PMF_REG_ARGUMENT0_1AH_M80H	0xA0C
> +#define AMD_PMF_REG_ARGUMENT1_1AH_M80H	0xAAC
> +#define AMD_PMF_REG_ARGUMENT2_1AH_M80H	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,19 @@ 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_legacy_smu_regs = {
>   	.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_1ah_m80h_smu_regs = {
> +	.msg_reg	= AMD_PMF_REG_MESSAGE_1AH_M80H,
> +	.resp_reg	= AMD_PMF_REG_RESPONSE_1AH_M80H,
> +	/* arg_reg[0], arg_reg[1] and arg_reg[2] carry low, hi address and size for DRAM metrics */
> +	.arg_reg	= {

Rather than overloading arg_reg how about if you just have a define in 
"struct amd_pmf_smu_regs" for low, hi, size?

So f1ahm80h wouldn't have arg_reg defined, it would have low_reg, 
high_reg, size_reg.

> +		AMD_PMF_REG_ARGUMENT0_1AH_M80H,
> +		AMD_PMF_REG_ARGUMENT1_1AH_M80H,
> +		AMD_PMF_REG_ARGUMENT2_1AH_M80H,
> +	},
>   };
>   
>   static const struct pci_device_id pmf_pci_ids[] = {
> @@ -274,6 +293,7 @@ static const struct pci_device_id pmf_pci_ids[] = {
>   	{ PCI_DEVICE_DATA(AMD, CPU_ID_PS,     &amd_pmf_legacy_smu_regs) },
>   	{ PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, &amd_pmf_legacy_smu_regs) },
>   	{ PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, &amd_pmf_legacy_smu_regs) },
> +	{ PCI_DEVICE_DATA(AMD, 1AH_M80H_ROOT, &amd_pmf_1ah_m80h_smu_regs) },
>   	{ }
>   };
>   
> @@ -563,6 +583,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];

See above comment.

>   };
>   
>   struct amd_pmf_dev {


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file
  2026-07-10 17:42 ` [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file Shyam Sundar S K
  2026-07-10 18:04   ` Ilpo Järvinen
@ 2026-07-10 19:10   ` Mario Limonciello
  1 sibling, 0 replies; 17+ messages in thread
From: Mario Limonciello @ 2026-07-10 19:10 UTC (permalink / raw)
  To: Shyam Sundar S K, hansg, ilpo.jarvinen; +Cc: platform-driver-x86, Patil.Reddy



On 7/10/26 12:42, 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>
> ---
>   drivers/platform/x86/amd/pmf/Makefile  |   2 +-
>   drivers/platform/x86/amd/pmf/core.c    | 156 +--------------------
>   drivers/platform/x86/amd/pmf/metrics.c | 179 +++++++++++++++++++++++++
>   drivers/platform/x86/amd/pmf/pmf.h     |   3 +
>   4 files changed, 185 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 7c5a1ec3d7fc..c0a21808b867 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);
> @@ -297,125 +264,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;
> @@ -678,7 +526,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..bb4f9127c655
> --- /dev/null
> +++ b/drivers/platform/x86/amd/pmf/metrics.c
> @@ -0,0 +1,179 @@
> +// 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/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);


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/5] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility
  2026-07-10 17:42 ` [PATCH 4/5] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility Shyam Sundar S K
@ 2026-07-10 19:11   ` Mario Limonciello
  0 siblings, 0 replies; 17+ messages in thread
From: Mario Limonciello @ 2026-07-10 19:11 UTC (permalink / raw)
  To: Shyam Sundar S K, hansg, ilpo.jarvinen; +Cc: platform-driver-x86, Patil.Reddy



On 7/10/26 12:42, Shyam Sundar S K wrote:
> 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.
> 
> 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/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 bb4f9127c655..5a8ebacbc3b3 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/bits.h>
>   #include <linux/cleanup.h>
>   #include <linux/container_of.h>
> @@ -134,28 +135,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;
>   }


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 2/5] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended SMU mailbox registers
  2026-07-10 19:10   ` Mario Limonciello
@ 2026-07-13 18:37     ` Shyam Sundar S K
  2026-07-13 18:40       ` Mario Limonciello
  0 siblings, 1 reply; 17+ messages in thread
From: Shyam Sundar S K @ 2026-07-13 18:37 UTC (permalink / raw)
  To: Mario Limonciello, hansg, ilpo.jarvinen; +Cc: platform-driver-x86, Patil.Reddy



On 7/11/2026 00:40, Mario Limonciello wrote:
> 
> 
> On 7/10/26 12:42, Shyam Sundar S K wrote:
>> 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_1ah_m80h_smu_regs to capture this extended register
>> layout
>> and register it in pmf_pci_ids[] via PCI_DEVICE_DATA(), keeping the
>> existing amd_pmf_legacy_smu_regs 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 | 29 ++++++++++++++++++++++++
>> +----
>>   drivers/platform/x86/amd/pmf/pmf.h  |  3 ++-
>>   2 files changed, 27 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/
>> x86/amd/pmf/core.c
>> index 1826fc64bf56..7c5a1ec3d7fc 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_REG_MESSAGE_1AH_M80H    0xA04
>> +#define AMD_PMF_REG_RESPONSE_1AH_M80H    0xA08
>> +#define AMD_PMF_REG_ARGUMENT0_1AH_M80H    0xA0C
>> +#define AMD_PMF_REG_ARGUMENT1_1AH_M80H    0xAAC
>> +#define AMD_PMF_REG_ARGUMENT2_1AH_M80H    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,19 @@ 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_legacy_smu_regs = {
>>       .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_1ah_m80h_smu_regs = {
>> +    .msg_reg    = AMD_PMF_REG_MESSAGE_1AH_M80H,
>> +    .resp_reg    = AMD_PMF_REG_RESPONSE_1AH_M80H,
>> +    /* arg_reg[0], arg_reg[1] and arg_reg[2] carry low, hi address
>> and size for DRAM metrics */
>> +    .arg_reg    = {
> 
> Rather than overloading arg_reg how about if you just have a define in
> "struct amd_pmf_smu_regs" for low, hi, size?
> 
> So f1ahm80h wouldn't have arg_reg defined, it would have low_reg,
> high_reg, size_reg.

Ack to other comments and I have pulled the tags.

Specific to this comment, I spoke to PMFW folks and understand that
they would have the ARG0/ARG1/ARG2 combinations going forward and it
would not be mapped to low/high/size (as happened in this
family/model). So, to make the code generic, I have retained the
variable names to reflect the PMFW behavior.

I have adapted to some of your comments, that gets mostly reflected in
6/6.

Thanks,
Shyam

> 
>> +        AMD_PMF_REG_ARGUMENT0_1AH_M80H,
>> +        AMD_PMF_REG_ARGUMENT1_1AH_M80H,
>> +        AMD_PMF_REG_ARGUMENT2_1AH_M80H,
>> +    },
>>   };
>>     static const struct pci_device_id pmf_pci_ids[] = {
>> @@ -274,6 +293,7 @@ static const struct pci_device_id pmf_pci_ids[] = {
>>       { PCI_DEVICE_DATA(AMD, CPU_ID_PS,    
>> &amd_pmf_legacy_smu_regs) },
>>       { PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT,
>> &amd_pmf_legacy_smu_regs) },
>>       { PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT,
>> &amd_pmf_legacy_smu_regs) },
>> +    { PCI_DEVICE_DATA(AMD, 1AH_M80H_ROOT,
>> &amd_pmf_1ah_m80h_smu_regs) },
>>       { }
>>   };
>>   @@ -563,6 +583,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];
> 
> See above comment.
> 
>>   };
>>     struct amd_pmf_dev {
> 


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support
  2026-07-10 18:16   ` Ilpo Järvinen
@ 2026-07-13 18:39     ` Shyam Sundar S K
  0 siblings, 0 replies; 17+ messages in thread
From: Shyam Sundar S K @ 2026-07-13 18:39 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: Hans de Goede, platform-driver-x86, Patil.Reddy,
	mario.limonciello



On 7/10/2026 23:46, Ilpo Järvinen wrote:
> On Fri, 10 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
>>
>> 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    |  25 +++
>>  drivers/platform/x86/amd/pmf/metrics.c | 142 ++++++++++++++++
>>  drivers/platform/x86/amd/pmf/pmf.h     | 218 +++++++++++++++++++++++++
>>  include/linux/amd-pmf-io.h             |   2 +
>>  4 files changed, 387 insertions(+)
>>
>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>> index c0a21808b867..5d744eb71418 100644
>> --- a/drivers/platform/x86/amd/pmf/core.c
>> +++ b/drivers/platform/x86/amd/pmf/core.c
>> @@ -68,6 +68,12 @@ static bool smart_pc_support = true;
>>  module_param(smart_pc_support, bool, 0444);
>>  MODULE_PARM_DESC(smart_pc_support, "Smart PC Support (default = true)");
>>  
>> +/* Helper to check if platform is 1AH_M80H */
>> +bool is_amd_pmf_1ah_m80h(struct amd_pmf_dev *pdev)
>> +{
>> +	return pdev->cpu_id == PCI_DEVICE_ID_AMD_1AH_M80H_ROOT;
>> +}
>> +
>>  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 +161,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 (is_amd_pmf_1ah_m80h(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 +226,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 (is_amd_pmf_1ah_m80h(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:
>> @@ -518,6 +537,12 @@ static int amd_pmf_probe(struct platform_device *pdev)
>>  	if (err)
>>  		return err;
>>  
>> +	if (is_amd_pmf_1ah_m80h(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 5a8ebacbc3b3..4cb45a8bbdb4 100644
>> --- a/drivers/platform/x86/amd/pmf/metrics.c
>> +++ b/drivers/platform/x86/amd/pmf/metrics.c
>> @@ -17,6 +17,8 @@
>>  #include <linux/device/devres.h>
>>  #include <linux/io.h>
>>  #include <linux/ktime.h>
>> +#include <linux/math.h>
>> +#include <linux/minmax.h>
> 
> This was the fourth header I was missing earlier from the almost always 
> forgotten list. :-)
> 
>>  #include <linux/string.h>
>>  #include <linux/workqueue.h>
>>  
>> @@ -24,6 +26,89 @@
>>  
>>  static struct device *pmf_device;
>>  
>> +static u16 amd_pmf_q10_acc_to_mW(u64 avg_acc)
>> +{
>> +	u64 val = DIV_ROUND_CLOSEST_ULL(avg_acc, 1024) * 1000;
> 
> Please use one of the 64-bit divide helpers whenever any of the values is 
> 64-bit.
> 
> Is that 1000 something from units.h?
> 
>> +
>> +	return min_t(u16, val, U16_MAX);
>> +}
>> +
>> +static u16 amd_pmf_q10_acc_to_int(u64 avg_acc)
>> +{
>> +	u64 val = DIV_ROUND_CLOSEST_ULL(avg_acc, 1024);
> 
> Ditto.
> 
>> +
>> +	return min_t(u16, val, U16_MAX);
> 
> + limits.h
> 
>> +}
>> +
>> +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_ROUND_CLOSEST_ULL(val_diff, counter_diff);
> 
> Use 64-bit helpers.
> 

OK. Ack to all comments in this revision.

Thanks,
Shyam



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 2/5] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended SMU mailbox registers
  2026-07-13 18:37     ` Shyam Sundar S K
@ 2026-07-13 18:40       ` Mario Limonciello
  0 siblings, 0 replies; 17+ messages in thread
From: Mario Limonciello @ 2026-07-13 18:40 UTC (permalink / raw)
  To: Shyam Sundar S K, hansg, ilpo.jarvinen; +Cc: platform-driver-x86, Patil.Reddy



On 7/13/26 13:37, Shyam Sundar S K wrote:
> 
> 
> On 7/11/2026 00:40, Mario Limonciello wrote:
>>
>>
>> On 7/10/26 12:42, Shyam Sundar S K wrote:
>>> 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_1ah_m80h_smu_regs to capture this extended register
>>> layout
>>> and register it in pmf_pci_ids[] via PCI_DEVICE_DATA(), keeping the
>>> existing amd_pmf_legacy_smu_regs 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 | 29 ++++++++++++++++++++++++
>>> +----
>>>    drivers/platform/x86/amd/pmf/pmf.h  |  3 ++-
>>>    2 files changed, 27 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/
>>> x86/amd/pmf/core.c
>>> index 1826fc64bf56..7c5a1ec3d7fc 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_REG_MESSAGE_1AH_M80H    0xA04
>>> +#define AMD_PMF_REG_RESPONSE_1AH_M80H    0xA08
>>> +#define AMD_PMF_REG_ARGUMENT0_1AH_M80H    0xA0C
>>> +#define AMD_PMF_REG_ARGUMENT1_1AH_M80H    0xAAC
>>> +#define AMD_PMF_REG_ARGUMENT2_1AH_M80H    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,19 @@ 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_legacy_smu_regs = {
>>>        .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_1ah_m80h_smu_regs = {
>>> +    .msg_reg    = AMD_PMF_REG_MESSAGE_1AH_M80H,
>>> +    .resp_reg    = AMD_PMF_REG_RESPONSE_1AH_M80H,
>>> +    /* arg_reg[0], arg_reg[1] and arg_reg[2] carry low, hi address
>>> and size for DRAM metrics */
>>> +    .arg_reg    = {
>>
>> Rather than overloading arg_reg how about if you just have a define in
>> "struct amd_pmf_smu_regs" for low, hi, size?
>>
>> So f1ahm80h wouldn't have arg_reg defined, it would have low_reg,
>> high_reg, size_reg.
> 
> Ack to other comments and I have pulled the tags.
> 
> Specific to this comment, I spoke to PMFW folks and understand that
> they would have the ARG0/ARG1/ARG2 combinations going forward and it
> would not be mapped to low/high/size (as happened in this
> family/model). So, to make the code generic, I have retained the
> variable names to reflect the PMFW behavior.
> 
> I have adapted to some of your comments, that gets mostly reflected in
> 6/6.

Got it, thanks for confirming.

> 
> Thanks,
> Shyam
> 
>>
>>> +        AMD_PMF_REG_ARGUMENT0_1AH_M80H,
>>> +        AMD_PMF_REG_ARGUMENT1_1AH_M80H,
>>> +        AMD_PMF_REG_ARGUMENT2_1AH_M80H,
>>> +    },
>>>    };
>>>      static const struct pci_device_id pmf_pci_ids[] = {
>>> @@ -274,6 +293,7 @@ static const struct pci_device_id pmf_pci_ids[] = {
>>>        { PCI_DEVICE_DATA(AMD, CPU_ID_PS,
>>> &amd_pmf_legacy_smu_regs) },
>>>        { PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT,
>>> &amd_pmf_legacy_smu_regs) },
>>>        { PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT,
>>> &amd_pmf_legacy_smu_regs) },
>>> +    { PCI_DEVICE_DATA(AMD, 1AH_M80H_ROOT,
>>> &amd_pmf_1ah_m80h_smu_regs) },
>>>        { }
>>>    };
>>>    @@ -563,6 +583,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];
>>
>> See above comment.
>>
>>>    };
>>>      struct amd_pmf_dev {
>>
> 


^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-07-13 18:40 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10 17:42 [PATCH 0/5] platform/x86/amd/pmf: Add 1AH_M80H support and accumulator based NPU metrics Shyam Sundar S K
2026-07-10 17:42 ` [PATCH 1/5] platform/x86/amd/pmf: Use per-SoC smu_regs struct for SMU mailbox registers Shyam Sundar S K
2026-07-10 19:07   ` Mario Limonciello
2026-07-10 17:42 ` [PATCH 2/5] platform/x86/amd/pmf: Add 1AH_M80H device IDs and extended " Shyam Sundar S K
2026-07-10 19:10   ` Mario Limonciello
2026-07-13 18:37     ` Shyam Sundar S K
2026-07-13 18:40       ` Mario Limonciello
2026-07-10 17:42 ` [PATCH 3/5] platform/x86/amd/pmf: Move metrics code to dedicated file Shyam Sundar S K
2026-07-10 18:04   ` Ilpo Järvinen
2026-07-10 18:07     ` Ilpo Järvinen
2026-07-10 19:10   ` Mario Limonciello
2026-07-10 17:42 ` [PATCH 4/5] platform/x86/amd/pmf: Refactor NPU metrics for platform extensibility Shyam Sundar S K
2026-07-10 19:11   ` Mario Limonciello
2026-07-10 17:42 ` [PATCH 5/5] platform/x86/amd/pmf: Add 1AH_M80H metrics table and NPU metrics support Shyam Sundar S K
2026-07-10 18:16   ` Ilpo Järvinen
2026-07-13 18:39     ` Shyam Sundar S K
2026-07-10 18:21   ` 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.