mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/4] platform/x86/amd/hsmp: Unify per-socket sysfs metrics
@ 2026-10-07 12:59 Erwan Velu
  2026-10-07 12:59 ` [PATCH 1/4] platform/x86/amd/hsmp: Move per-socket sysfs attributes to common code Erwan Velu
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Erwan Velu @ 2026-10-07 12:59 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: Naveen Krishna Chatradhi, Carlos Bilbao, Hans de Goede,
	Ilpo Järvinen, linux-kernel

From: Erwan Velu <e.velu@criteo.com>

The goal of this series is to ease server monitoring, by exposing the
HSMP metrics the same way whatever the driver, in a format that is
straightforward to integrate in metrics exporters.

The per-socket HSMP telemetry attributes (c0_residency_input,
ddr_max_bw, pwr_current_active_freq_limit, ...) are only implemented by
the ACPI driver, although they are plain HSMP messages the platform
driver can send as well. Systems probed by the platform driver therefore
only get metrics_bin.

Their location also depends on the driver: AMDI0097:0X with the ACPI
driver, amd_hsmp/socketN with the platform one.

This series moves the attributes to common code and exposes them from
both drivers under a single /sys/devices/amd_hsmp/socketN location. The
existing ACPI and platform sysfs paths are kept for compatibility.

Patch 1 is a pure code move, patch 2 adds the socket devices and wires
them in the ACPI driver, patch 3 does the same for the platform driver.
Patch 4 exposes the pwr_current_active_freq_limit_source bitmask as a
single hexadecimal value, whose interpretation can be centralized in the
monitoring stack (e.g. Grafana value mappings).

Tested on Zen3 and Zen4 (Family 19h) servers with the platform driver,
and on Zen5 (Family 1Ah) servers with the ACPI driver, in both single
and dual socket configurations.

Erwan Velu (4):
  platform/x86/amd/hsmp: Move per-socket sysfs attributes to common code
  platform/x86/amd/hsmp: Add /sys/devices/amd_hsmp socket devices
  platform/x86/amd/hsmp: Expose socket attributes from the platform
    driver
  platform/x86/amd/hsmp: Add pwr_current_active_freq_limit_source_raw

 Documentation/arch/x86/amd_hsmp.rst    |  20 +-
 drivers/platform/x86/amd/hsmp/Makefile |   2 +-
 drivers/platform/x86/amd/hsmp/acpi.c   | 327 +----------------
 drivers/platform/x86/amd/hsmp/hsmp.h   |   6 +
 drivers/platform/x86/amd/hsmp/plat.c   |  20 ++
 drivers/platform/x86/amd/hsmp/sysfs.c  | 466 +++++++++++++++++++++++++
 6 files changed, 517 insertions(+), 324 deletions(-)
 create mode 100644 drivers/platform/x86/amd/hsmp/sysfs.c


base-commit: 22430ae5d90ab288b0ee2ad99ae941f4a666b694
-- 
2.53.0


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

* [PATCH 1/4] platform/x86/amd/hsmp: Move per-socket sysfs attributes to common code
  2026-10-07 12:59 [PATCH 0/4] platform/x86/amd/hsmp: Unify per-socket sysfs metrics Erwan Velu
@ 2026-10-07 12:59 ` Erwan Velu
  2026-10-07 12:59 ` [PATCH 2/4] platform/x86/amd/hsmp: Add /sys/devices/amd_hsmp socket devices Erwan Velu
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Erwan Velu @ 2026-10-07 12:59 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: Naveen Krishna Chatradhi, Carlos Bilbao, Hans de Goede,
	Ilpo Järvinen, linux-kernel

From: Erwan Velu <e.velu@criteo.com>

The per-socket telemetry attributes (c0_residency_input, ddr_max_bw,
pwr_current_active_freq_limit, ...) are implemented in acpi.c, so only
the ACPI driver can expose them, although they are plain HSMP messages
the platform driver can send as well.

Move them to a new sysfs.c in hsmp_common and export the attribute
groups as hsmp_sock_groups, so they can be shared by both drivers. The
ACPI driver keeps exposing them on its socket devices via dev_groups.

No functional change.

Assisted-by: claude-opus-5-5
Signed-off-by: Erwan Velu <e.velu@criteo.com>
---
 drivers/platform/x86/amd/hsmp/Makefile |   2 +-
 drivers/platform/x86/amd/hsmp/acpi.c   | 321 +----------------------
 drivers/platform/x86/amd/hsmp/hsmp.h   |   1 +
 drivers/platform/x86/amd/hsmp/sysfs.c  | 338 +++++++++++++++++++++++++
 4 files changed, 341 insertions(+), 321 deletions(-)
 create mode 100644 drivers/platform/x86/amd/hsmp/sysfs.c

diff --git a/drivers/platform/x86/amd/hsmp/Makefile b/drivers/platform/x86/amd/hsmp/Makefile
index ce8342e71f50..21fc1d185550 100644
--- a/drivers/platform/x86/amd/hsmp/Makefile
+++ b/drivers/platform/x86/amd/hsmp/Makefile
@@ -5,7 +5,7 @@
 #
 
 obj-$(CONFIG_AMD_HSMP)			+= hsmp_common.o
-hsmp_common-y				:= hsmp.o
+hsmp_common-y				:= hsmp.o sysfs.o
 hsmp_common-$(CONFIG_HWMON)		+= hwmon.o
 obj-$(CONFIG_AMD_HSMP_PLAT)		+= amd_hsmp.o
 amd_hsmp-y				:= plat.o
diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c
index ddd7a04ee753..d76979baffa8 100644
--- a/drivers/platform/x86/amd/hsmp/acpi.c
+++ b/drivers/platform/x86/amd/hsmp/acpi.c
@@ -12,9 +12,6 @@
 #include <asm/amd/hsmp.h>
 
 #include <linux/acpi.h>
-#include <linux/array_size.h>
-#include <linux/bits.h>
-#include <linux/bitfield.h>
 #include <linux/cleanup.h>
 #include <linux/device.h>
 #include <linux/dev_printk.h>
@@ -27,7 +24,6 @@
 #include <linux/rwsem.h>
 #include <linux/slab.h>
 #include <linux/string.h>
-#include <linux/sysfs.h>
 #include <linux/topology.h>
 #include <linux/uuid.h>
 
@@ -55,11 +51,6 @@ static struct hsmp_plat_device *hsmp_pdev;
  */
 static struct kref hsmp_acpi_sock_kref;
 
-struct hsmp_sys_attr {
-	struct device_attribute dattr;
-	u32 msg_id;
-};
-
 static int amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset,
 			      u32 *value, bool write)
 {
@@ -270,252 +261,6 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
 	return 0;
 }
 
-static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj,
-					 const struct bin_attribute *bin_attr, char *buf,
-					 loff_t off, size_t count)
-{
-	struct device *dev = container_of(kobj, struct device, kobj);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-
-	/*
-	 * metrics_bin is a sysfs binary attribute and is capped at PAGE_SIZE.
-	 * It can therefore only carry the protocol version 6 metric table
-	 * (struct hsmp_metric_table).  The larger tables defined from protocol
-	 * version 7 onwards do not fit; userspace on those systems must read
-	 * the snapshot through HSMP_IOCTL_GET_TELEMETRY_DATA on /dev/hsmp.
-	 * Surface the unsupported case here as -EOPNOTSUPP rather than
-	 * silently truncating the snapshot.
-	 */
-	if (hsmp_pdev->proto_ver != HSMP_PROTO_VER6)
-		return -EOPNOTSUPP;
-
-	return hsmp_metric_tbl_read(sock, buf, count);
-}
-
-static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
-					 const struct bin_attribute *battr, int id)
-{
-	/*
-	 * Keep metrics_bin visible on protocol version 7 and later as well,
-	 * so that userspace which expects the file to exist gets a clear
-	 * -EOPNOTSUPP from the read handler instead of -ENOENT, and is
-	 * pointed at HSMP_IOCTL_GET_TELEMETRY_DATA as the supported path.
-	 */
-	if (hsmp_pdev->proto_ver >= HSMP_PROTO_VER6)
-		return battr->attr.mode;
-
-	return 0;
-}
-
-static umode_t hsmp_is_sock_dev_attr_visible(struct kobject *kobj,
-					     struct attribute *attr, int id)
-{
-	return attr->mode;
-}
-
-#define to_hsmp_sys_attr(_attr) container_of(_attr, struct hsmp_sys_attr, dattr)
-
-static ssize_t hsmp_msg_resp32_show(struct device *dev, struct device_attribute *attr,
-				    char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%u\n", data);
-}
-
-#define DDR_MAX_BW_MASK		GENMASK(31, 20)
-#define DDR_UTIL_BW_MASK	GENMASK(19, 8)
-#define DDR_UTIL_BW_PERC_MASK	GENMASK(7, 0)
-#define FW_VER_MAJOR_MASK	GENMASK(23, 16)
-#define FW_VER_MINOR_MASK	GENMASK(15, 8)
-#define FW_VER_DEBUG_MASK	GENMASK(7, 0)
-#define FMAX_MASK		GENMASK(31, 16)
-#define FMIN_MASK		GENMASK(15, 0)
-#define FREQ_LIMIT_MASK		GENMASK(31, 16)
-#define FREQ_SRC_IND_MASK	GENMASK(15, 0)
-
-static ssize_t hsmp_ddr_max_bw_show(struct device *dev, struct device_attribute *attr,
-				    char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%lu\n", FIELD_GET(DDR_MAX_BW_MASK, data));
-}
-
-static ssize_t hsmp_ddr_util_bw_show(struct device *dev, struct device_attribute *attr,
-				     char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%lu\n", FIELD_GET(DDR_UTIL_BW_MASK, data));
-}
-
-static ssize_t hsmp_ddr_util_bw_perc_show(struct device *dev, struct device_attribute *attr,
-					  char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%lu\n", FIELD_GET(DDR_UTIL_BW_PERC_MASK, data));
-}
-
-static ssize_t hsmp_msg_fw_ver_show(struct device *dev, struct device_attribute *attr,
-				    char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%lu.%lu.%lu\n",
-			  FIELD_GET(FW_VER_MAJOR_MASK, data),
-			  FIELD_GET(FW_VER_MINOR_MASK, data),
-			  FIELD_GET(FW_VER_DEBUG_MASK, data));
-}
-
-static ssize_t hsmp_fclk_show(struct device *dev, struct device_attribute *attr,
-			      char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data[2];
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, data, 2);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%u\n", data[0]);
-}
-
-static ssize_t hsmp_mclk_show(struct device *dev, struct device_attribute *attr,
-			      char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data[2];
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, data, 2);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%u\n", data[1]);
-}
-
-static ssize_t hsmp_clk_fmax_show(struct device *dev, struct device_attribute *attr,
-				  char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%lu\n", FIELD_GET(FMAX_MASK, data));
-}
-
-static ssize_t hsmp_clk_fmin_show(struct device *dev, struct device_attribute *attr,
-				  char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%lu\n", FIELD_GET(FMIN_MASK, data));
-}
-
-static ssize_t hsmp_freq_limit_show(struct device *dev, struct device_attribute *attr,
-				    char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	return sysfs_emit(buf, "%lu\n", FIELD_GET(FREQ_LIMIT_MASK, data));
-}
-
-static const char * const freqlimit_srcnames[] = {
-	"cHTC-Active",
-	"PROCHOT",
-	"TDC limit",
-	"PPT Limit",
-	"OPN Max",
-	"Reliability Limit",
-	"APML Agent",
-	"HSMP Agent",
-};
-
-static ssize_t hsmp_freq_limit_source_show(struct device *dev, struct device_attribute *attr,
-					   char *buf)
-{
-	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
-	struct hsmp_socket *sock = dev_get_drvdata(dev);
-	unsigned int index;
-	int len = 0;
-	u16 src_ind;
-	u32 data;
-	int ret;
-
-	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
-	if (ret)
-		return ret;
-
-	src_ind = FIELD_GET(FREQ_SRC_IND_MASK, data);
-	for (index = 0; index < ARRAY_SIZE(freqlimit_srcnames); index++) {
-		if (!src_ind)
-			break;
-		if (src_ind & 1)
-			len += sysfs_emit_at(buf, len, "%s\n", freqlimit_srcnames[index]);
-		src_ind >>= 1;
-	}
-	return len;
-}
-
 /*
  * Bring up one ACPI HSMP socket: parse its ACPI table, run the mailbox
  * handshake and register its sysfs/hwmon interfaces.
@@ -572,70 +317,6 @@ static int init_acpi(struct device *dev)
 	return 0;
 }
 
-static const struct bin_attribute  hsmp_metric_tbl_attr = {
-	.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444},
-	.read = hsmp_metric_tbl_acpi_read,
-	.size = sizeof(struct hsmp_metric_table),
-};
-
-static const struct bin_attribute *hsmp_attr_list[] = {
-	&hsmp_metric_tbl_attr,
-	NULL
-};
-
-#define HSMP_DEV_ATTR(_name, _msg_id, _show, _mode)	\
-static struct hsmp_sys_attr hattr_##_name = {		\
-	.dattr = __ATTR(_name, _mode, _show, NULL),	\
-	.msg_id = _msg_id,				\
-}
-
-HSMP_DEV_ATTR(c0_residency_input, HSMP_GET_C0_PERCENT, hsmp_msg_resp32_show, 0444);
-HSMP_DEV_ATTR(prochot_status, HSMP_GET_PROC_HOT, hsmp_msg_resp32_show, 0444);
-HSMP_DEV_ATTR(smu_fw_version, HSMP_GET_SMU_VER, hsmp_msg_fw_ver_show, 0444);
-HSMP_DEV_ATTR(protocol_version, HSMP_GET_PROTO_VER, hsmp_msg_resp32_show, 0444);
-HSMP_DEV_ATTR(cclk_freq_limit_input, HSMP_GET_CCLK_THROTTLE_LIMIT, hsmp_msg_resp32_show, 0444);
-HSMP_DEV_ATTR(ddr_max_bw, HSMP_GET_DDR_BANDWIDTH, hsmp_ddr_max_bw_show, 0444);
-HSMP_DEV_ATTR(ddr_utilised_bw_input, HSMP_GET_DDR_BANDWIDTH, hsmp_ddr_util_bw_show, 0444);
-HSMP_DEV_ATTR(ddr_utilised_bw_perc_input, HSMP_GET_DDR_BANDWIDTH, hsmp_ddr_util_bw_perc_show, 0444);
-HSMP_DEV_ATTR(fclk_input, HSMP_GET_FCLK_MCLK, hsmp_fclk_show, 0444);
-HSMP_DEV_ATTR(mclk_input, HSMP_GET_FCLK_MCLK, hsmp_mclk_show, 0444);
-HSMP_DEV_ATTR(clk_fmax, HSMP_GET_SOCKET_FMAX_FMIN, hsmp_clk_fmax_show, 0444);
-HSMP_DEV_ATTR(clk_fmin, HSMP_GET_SOCKET_FMAX_FMIN, hsmp_clk_fmin_show, 0444);
-HSMP_DEV_ATTR(pwr_current_active_freq_limit, HSMP_GET_SOCKET_FREQ_LIMIT,
-	      hsmp_freq_limit_show, 0444);
-HSMP_DEV_ATTR(pwr_current_active_freq_limit_source, HSMP_GET_SOCKET_FREQ_LIMIT,
-	      hsmp_freq_limit_source_show, 0444);
-
-static struct attribute *hsmp_dev_attr_list[] = {
-	&hattr_c0_residency_input.dattr.attr,
-	&hattr_prochot_status.dattr.attr,
-	&hattr_smu_fw_version.dattr.attr,
-	&hattr_protocol_version.dattr.attr,
-	&hattr_cclk_freq_limit_input.dattr.attr,
-	&hattr_ddr_max_bw.dattr.attr,
-	&hattr_ddr_utilised_bw_input.dattr.attr,
-	&hattr_ddr_utilised_bw_perc_input.dattr.attr,
-	&hattr_fclk_input.dattr.attr,
-	&hattr_mclk_input.dattr.attr,
-	&hattr_clk_fmax.dattr.attr,
-	&hattr_clk_fmin.dattr.attr,
-	&hattr_pwr_current_active_freq_limit.dattr.attr,
-	&hattr_pwr_current_active_freq_limit_source.dattr.attr,
-	NULL
-};
-
-static const struct attribute_group hsmp_attr_grp = {
-	.bin_attrs = hsmp_attr_list,
-	.attrs = hsmp_dev_attr_list,
-	.is_bin_visible = hsmp_is_sock_attr_visible,
-	.is_visible = hsmp_is_sock_dev_attr_visible,
-};
-
-static const struct attribute_group *hsmp_groups[] = {
-	&hsmp_attr_grp,
-	NULL
-};
-
 static const struct acpi_device_id amd_hsmp_acpi_ids[] = {
 	{ACPI_HSMP_DEVICE_HID, 0},
 	{}
@@ -793,7 +474,7 @@ static struct platform_driver amd_hsmp_driver = {
 	.driver		= {
 		.name	= DRIVER_NAME,
 		.acpi_match_table = amd_hsmp_acpi_ids,
-		.dev_groups = hsmp_groups,
+		.dev_groups = hsmp_sock_groups,
 	},
 };
 
diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h
index 8dbff16a87b1..692721e2e6d3 100644
--- a/drivers/platform/x86/amd/hsmp/hsmp.h
+++ b/drivers/platform/x86/amd/hsmp/hsmp.h
@@ -80,6 +80,7 @@ int hsmp_create_sensor(struct device *dev, u16 sock_ind);
 static inline int hsmp_create_sensor(struct device *dev, u16 sock_ind) { return 0; }
 #endif
 int hsmp_msg_get_nargs(u16 sock_ind, u32 msg_id, u32 *data, u8 num_args);
+extern const struct attribute_group *hsmp_sock_groups[];
 
 /*
  * Gates the HSMP data plane: hsmp_send_message() takes it for read; probe and
diff --git a/drivers/platform/x86/amd/hsmp/sysfs.c b/drivers/platform/x86/amd/hsmp/sysfs.c
new file mode 100644
index 000000000000..b1db3b4d77b9
--- /dev/null
+++ b/drivers/platform/x86/amd/hsmp/sysfs.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD HSMP sysfs support
+ * Copyright (c) 2024, AMD.
+ * All Rights Reserved.
+ *
+ * This file provides the per-socket sysfs attributes of the HSMP interface.
+ */
+
+#include <asm/amd/hsmp.h>
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+
+#include "hsmp.h"
+
+struct hsmp_sys_attr {
+	struct device_attribute dattr;
+	u32 msg_id;
+};
+
+static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj,
+					 const struct bin_attribute *bin_attr, char *buf,
+					 loff_t off, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+
+	/*
+	 * metrics_bin is a sysfs binary attribute and is capped at PAGE_SIZE.
+	 * It can therefore only carry the protocol version 6 metric table
+	 * (struct hsmp_metric_table).  The larger tables defined from protocol
+	 * version 7 onwards do not fit; userspace on those systems must read
+	 * the snapshot through HSMP_IOCTL_GET_TELEMETRY_DATA on /dev/hsmp.
+	 * Surface the unsupported case here as -EOPNOTSUPP rather than
+	 * silently truncating the snapshot.
+	 */
+	if (get_hsmp_pdev()->proto_ver != HSMP_PROTO_VER6)
+		return -EOPNOTSUPP;
+
+	return hsmp_metric_tbl_read(sock, buf, count);
+}
+
+static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
+					 const struct bin_attribute *battr, int id)
+{
+	/*
+	 * Keep metrics_bin visible on protocol version 7 and later as well,
+	 * so that userspace which expects the file to exist gets a clear
+	 * -EOPNOTSUPP from the read handler instead of -ENOENT, and is
+	 * pointed at HSMP_IOCTL_GET_TELEMETRY_DATA as the supported path.
+	 */
+	if (get_hsmp_pdev()->proto_ver >= HSMP_PROTO_VER6)
+		return battr->attr.mode;
+
+	return 0;
+}
+
+static umode_t hsmp_is_sock_dev_attr_visible(struct kobject *kobj,
+					     struct attribute *attr, int id)
+{
+	return attr->mode;
+}
+
+#define to_hsmp_sys_attr(_attr) container_of(_attr, struct hsmp_sys_attr, dattr)
+
+static ssize_t hsmp_msg_resp32_show(struct device *dev, struct device_attribute *attr,
+				    char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", data);
+}
+
+#define DDR_MAX_BW_MASK		GENMASK(31, 20)
+#define DDR_UTIL_BW_MASK	GENMASK(19, 8)
+#define DDR_UTIL_BW_PERC_MASK	GENMASK(7, 0)
+#define FW_VER_MAJOR_MASK	GENMASK(23, 16)
+#define FW_VER_MINOR_MASK	GENMASK(15, 8)
+#define FW_VER_DEBUG_MASK	GENMASK(7, 0)
+#define FMAX_MASK		GENMASK(31, 16)
+#define FMIN_MASK		GENMASK(15, 0)
+#define FREQ_LIMIT_MASK		GENMASK(31, 16)
+#define FREQ_SRC_IND_MASK	GENMASK(15, 0)
+
+static ssize_t hsmp_ddr_max_bw_show(struct device *dev, struct device_attribute *attr,
+				    char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%lu\n", FIELD_GET(DDR_MAX_BW_MASK, data));
+}
+
+static ssize_t hsmp_ddr_util_bw_show(struct device *dev, struct device_attribute *attr,
+				     char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%lu\n", FIELD_GET(DDR_UTIL_BW_MASK, data));
+}
+
+static ssize_t hsmp_ddr_util_bw_perc_show(struct device *dev, struct device_attribute *attr,
+					  char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%lu\n", FIELD_GET(DDR_UTIL_BW_PERC_MASK, data));
+}
+
+static ssize_t hsmp_msg_fw_ver_show(struct device *dev, struct device_attribute *attr,
+				    char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%lu.%lu.%lu\n",
+			  FIELD_GET(FW_VER_MAJOR_MASK, data),
+			  FIELD_GET(FW_VER_MINOR_MASK, data),
+			  FIELD_GET(FW_VER_DEBUG_MASK, data));
+}
+
+static ssize_t hsmp_fclk_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data[2];
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, data, 2);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", data[0]);
+}
+
+static ssize_t hsmp_mclk_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data[2];
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, data, 2);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%u\n", data[1]);
+}
+
+static ssize_t hsmp_clk_fmax_show(struct device *dev, struct device_attribute *attr,
+				  char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%lu\n", FIELD_GET(FMAX_MASK, data));
+}
+
+static ssize_t hsmp_clk_fmin_show(struct device *dev, struct device_attribute *attr,
+				  char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%lu\n", FIELD_GET(FMIN_MASK, data));
+}
+
+static ssize_t hsmp_freq_limit_show(struct device *dev, struct device_attribute *attr,
+				    char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%lu\n", FIELD_GET(FREQ_LIMIT_MASK, data));
+}
+
+static const char * const freqlimit_srcnames[] = {
+	"cHTC-Active",
+	"PROCHOT",
+	"TDC limit",
+	"PPT Limit",
+	"OPN Max",
+	"Reliability Limit",
+	"APML Agent",
+	"HSMP Agent",
+};
+
+static ssize_t hsmp_freq_limit_source_show(struct device *dev, struct device_attribute *attr,
+					   char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	unsigned int index;
+	int len = 0;
+	u16 src_ind;
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	src_ind = FIELD_GET(FREQ_SRC_IND_MASK, data);
+	for (index = 0; index < ARRAY_SIZE(freqlimit_srcnames); index++) {
+		if (!src_ind)
+			break;
+		if (src_ind & 1)
+			len += sysfs_emit_at(buf, len, "%s\n", freqlimit_srcnames[index]);
+		src_ind >>= 1;
+	}
+	return len;
+}
+
+static const struct bin_attribute  hsmp_metric_tbl_attr = {
+	.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444},
+	.read = hsmp_metric_tbl_acpi_read,
+	.size = sizeof(struct hsmp_metric_table),
+};
+
+static const struct bin_attribute *hsmp_attr_list[] = {
+	&hsmp_metric_tbl_attr,
+	NULL
+};
+
+#define HSMP_DEV_ATTR(_name, _msg_id, _show, _mode)	\
+static struct hsmp_sys_attr hattr_##_name = {		\
+	.dattr = __ATTR(_name, _mode, _show, NULL),	\
+	.msg_id = _msg_id,				\
+}
+
+HSMP_DEV_ATTR(c0_residency_input, HSMP_GET_C0_PERCENT, hsmp_msg_resp32_show, 0444);
+HSMP_DEV_ATTR(prochot_status, HSMP_GET_PROC_HOT, hsmp_msg_resp32_show, 0444);
+HSMP_DEV_ATTR(smu_fw_version, HSMP_GET_SMU_VER, hsmp_msg_fw_ver_show, 0444);
+HSMP_DEV_ATTR(protocol_version, HSMP_GET_PROTO_VER, hsmp_msg_resp32_show, 0444);
+HSMP_DEV_ATTR(cclk_freq_limit_input, HSMP_GET_CCLK_THROTTLE_LIMIT, hsmp_msg_resp32_show, 0444);
+HSMP_DEV_ATTR(ddr_max_bw, HSMP_GET_DDR_BANDWIDTH, hsmp_ddr_max_bw_show, 0444);
+HSMP_DEV_ATTR(ddr_utilised_bw_input, HSMP_GET_DDR_BANDWIDTH, hsmp_ddr_util_bw_show, 0444);
+HSMP_DEV_ATTR(ddr_utilised_bw_perc_input, HSMP_GET_DDR_BANDWIDTH, hsmp_ddr_util_bw_perc_show, 0444);
+HSMP_DEV_ATTR(fclk_input, HSMP_GET_FCLK_MCLK, hsmp_fclk_show, 0444);
+HSMP_DEV_ATTR(mclk_input, HSMP_GET_FCLK_MCLK, hsmp_mclk_show, 0444);
+HSMP_DEV_ATTR(clk_fmax, HSMP_GET_SOCKET_FMAX_FMIN, hsmp_clk_fmax_show, 0444);
+HSMP_DEV_ATTR(clk_fmin, HSMP_GET_SOCKET_FMAX_FMIN, hsmp_clk_fmin_show, 0444);
+HSMP_DEV_ATTR(pwr_current_active_freq_limit, HSMP_GET_SOCKET_FREQ_LIMIT,
+	      hsmp_freq_limit_show, 0444);
+HSMP_DEV_ATTR(pwr_current_active_freq_limit_source, HSMP_GET_SOCKET_FREQ_LIMIT,
+	      hsmp_freq_limit_source_show, 0444);
+
+static struct attribute *hsmp_dev_attr_list[] = {
+	&hattr_c0_residency_input.dattr.attr,
+	&hattr_prochot_status.dattr.attr,
+	&hattr_smu_fw_version.dattr.attr,
+	&hattr_protocol_version.dattr.attr,
+	&hattr_cclk_freq_limit_input.dattr.attr,
+	&hattr_ddr_max_bw.dattr.attr,
+	&hattr_ddr_utilised_bw_input.dattr.attr,
+	&hattr_ddr_utilised_bw_perc_input.dattr.attr,
+	&hattr_fclk_input.dattr.attr,
+	&hattr_mclk_input.dattr.attr,
+	&hattr_clk_fmax.dattr.attr,
+	&hattr_clk_fmin.dattr.attr,
+	&hattr_pwr_current_active_freq_limit.dattr.attr,
+	&hattr_pwr_current_active_freq_limit_source.dattr.attr,
+	NULL
+};
+
+static const struct attribute_group hsmp_attr_grp = {
+	.bin_attrs = hsmp_attr_list,
+	.attrs = hsmp_dev_attr_list,
+	.is_bin_visible = hsmp_is_sock_attr_visible,
+	.is_visible = hsmp_is_sock_dev_attr_visible,
+};
+
+/*
+ * Per-socket attribute groups. Every show/read handler expects the drvdata of
+ * the device carrying the group to be the struct hsmp_socket it reports on.
+ */
+const struct attribute_group *hsmp_sock_groups[] = {
+	&hsmp_attr_grp,
+	NULL
+};
+EXPORT_SYMBOL_NS_GPL(hsmp_sock_groups, "AMD_HSMP");
-- 
2.53.0


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

* [PATCH 2/4] platform/x86/amd/hsmp: Add /sys/devices/amd_hsmp socket devices
  2026-10-07 12:59 [PATCH 0/4] platform/x86/amd/hsmp: Unify per-socket sysfs metrics Erwan Velu
  2026-10-07 12:59 ` [PATCH 1/4] platform/x86/amd/hsmp: Move per-socket sysfs attributes to common code Erwan Velu
@ 2026-10-07 12:59 ` Erwan Velu
  2026-10-07 12:59 ` [PATCH 3/4] platform/x86/amd/hsmp: Expose socket attributes from the platform driver Erwan Velu
  2026-10-07 12:59 ` [PATCH 4/4] platform/x86/amd/hsmp: Add pwr_current_active_freq_limit_source_raw Erwan Velu
  3 siblings, 0 replies; 5+ messages in thread
From: Erwan Velu @ 2026-10-07 12:59 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: Naveen Krishna Chatradhi, Carlos Bilbao, Hans de Goede,
	Ilpo Järvinen, linux-kernel

From: Erwan Velu <e.velu@criteo.com>

The per-socket attributes live under /sys/devices/platform/AMDI0097:0X
with the ACPI driver, while the platform driver uses
/sys/devices/platform/amd_hsmp/socketN. Userspace has to know which
driver probed to find them.

Add a driver-independent /sys/devices/amd_hsmp/socketN device per
socket carrying hsmp_sock_groups, and register it from the ACPI driver.
The AMDI0097:0X attributes are kept, as they are documented ABI.

The socket device is unregistered before remove takes hsmp_sock_rwsem:
device_unregister() waits for in-flight sysfs reads, which take the
rwsem for read.

Assisted-by: claude-opus-5-5
Signed-off-by: Erwan Velu <e.velu@criteo.com>
---
 Documentation/arch/x86/amd_hsmp.rst   |   8 +-
 drivers/platform/x86/amd/hsmp/acpi.c  |   6 ++
 drivers/platform/x86/amd/hsmp/hsmp.h  |   5 ++
 drivers/platform/x86/amd/hsmp/sysfs.c | 112 +++++++++++++++++++++++++-
 4 files changed, 129 insertions(+), 2 deletions(-)

diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst
index fa1fc240e212..aba4a009bd8d 100644
--- a/Documentation/arch/x86/amd_hsmp.rst
+++ b/Documentation/arch/x86/amd_hsmp.rst
@@ -57,6 +57,12 @@ In-kernel integration:
 HSMP sysfs interface
 ====================
 
+The ACPI driver exposes its per socket sysfs files in a per socket
+directory created at /sys/devices/amd_hsmp/socket%d/.
+
+For backward compatibility, the files are also available under
+/sys/devices/platform/AMDI0097:0X/.
+
 1. Metrics table binary sysfs
 
 AMD MI300A MCM provides GET_METRICS_TABLE message to retrieve
@@ -80,7 +86,7 @@ The same is defined in the amd_hsmp.h header.
 
 2. HSMP telemetry sysfs files
 
-Following sysfs files are available at /sys/devices/platform/AMDI0097:0X/.
+Following sysfs files are available at /sys/devices/amd_hsmp/socket%d/.
 
 * c0_residency_input: Percentage of cores in C0 state.
 * prochot_status: Reports 1 if the processor is at thermal threshold value,
diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c
index d76979baffa8..468d4b1515db 100644
--- a/drivers/platform/x86/amd/hsmp/acpi.c
+++ b/drivers/platform/x86/amd/hsmp/acpi.c
@@ -441,6 +441,10 @@ static int hsmp_acpi_probe(struct platform_device *pdev)
 		dev_dbg(&pdev->dev, "AMD HSMP ACPI misc device registered\n");
 	}
 
+	ret = hsmp_sock_dev_register(dev_get_drvdata(&pdev->dev));
+	if (ret)
+		dev_warn(&pdev->dev, "Failed to register HSMP socket device: %d\n", ret);
+
 	return 0;
 }
 
@@ -448,6 +452,8 @@ static void hsmp_acpi_remove(struct platform_device *pdev)
 {
 	struct hsmp_socket *sock = dev_get_drvdata(&pdev->dev);
 
+	hsmp_sock_dev_unregister(sock);
+
 	/*
 	 * Serialize the kref_put() and any release it triggers against a
 	 * concurrent probe, and drain the data plane for the whole
diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h
index 692721e2e6d3..f638edd7768b 100644
--- a/drivers/platform/x86/amd/hsmp/hsmp.h
+++ b/drivers/platform/x86/amd/hsmp/hsmp.h
@@ -28,6 +28,7 @@
 
 #define HSMP_CDEV_NAME		"hsmp_cdev"
 #define HSMP_DEVNODE_NAME	"hsmp"
+#define HSMP_ROOT_DEV_NAME	"amd_hsmp"
 #define ACPI_HSMP_DEVICE_HID    "AMDI0097"
 
 #define DRIVER_VERSION		"2.6"
@@ -47,6 +48,7 @@ struct hsmp_socket {
 	/* Size of the region mapped at @metric_tbl_addr, as reported by SMU */
 	size_t metric_tbl_size;
 	void __iomem *virt_base_addr;
+	struct device *sysfs_dev;
 	struct semaphore hsmp_sem;
 	/* Serializes HSMP_GET_METRIC_TABLE fill-and-copy for this socket */
 	struct mutex metric_read_lock;
@@ -80,6 +82,9 @@ int hsmp_create_sensor(struct device *dev, u16 sock_ind);
 static inline int hsmp_create_sensor(struct device *dev, u16 sock_ind) { return 0; }
 #endif
 int hsmp_msg_get_nargs(u16 sock_ind, u32 msg_id, u32 *data, u8 num_args);
+int hsmp_sock_dev_register(struct hsmp_socket *sock);
+void hsmp_sock_dev_unregister(struct hsmp_socket *sock);
+
 extern const struct attribute_group *hsmp_sock_groups[];
 
 /*
diff --git a/drivers/platform/x86/amd/hsmp/sysfs.c b/drivers/platform/x86/amd/hsmp/sysfs.c
index b1db3b4d77b9..e9e91b640b2a 100644
--- a/drivers/platform/x86/amd/hsmp/sysfs.c
+++ b/drivers/platform/x86/amd/hsmp/sysfs.c
@@ -4,7 +4,8 @@
  * Copyright (c) 2024, AMD.
  * All Rights Reserved.
  *
- * This file provides the per-socket sysfs attributes of the HSMP interface.
+ * This file provides the per-socket sysfs attributes of the HSMP interface
+ * and the /sys/devices/amd_hsmp/socket%u devices carrying them.
  */
 
 #include <asm/amd/hsmp.h>
@@ -12,7 +13,12 @@
 #include <linux/array_size.h>
 #include <linux/bitfield.h>
 #include <linux/bits.h>
+#include <linux/cleanup.h>
 #include <linux/device.h>
+#include <linux/err.h>
+#include <linux/lockdep.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
 #include <linux/sysfs.h>
 
 #include "hsmp.h"
@@ -336,3 +342,107 @@ const struct attribute_group *hsmp_sock_groups[] = {
 	NULL
 };
 EXPORT_SYMBOL_NS_GPL(hsmp_sock_groups, "AMD_HSMP");
+
+/*
+ * /sys/devices/amd_hsmp is created when the first socket device is registered
+ * and removed with the last one. hsmp_root_lock serializes that against the
+ * per-socket register/unregister calls.
+ */
+static DEFINE_MUTEX(hsmp_root_lock);
+static struct device *hsmp_root_dev;
+static unsigned int hsmp_root_users;
+
+static void hsmp_sock_dev_release(struct device *dev)
+{
+	kfree(dev);
+}
+
+/**
+ * hsmp_sock_dev_register() - Create /sys/devices/amd_hsmp/socket%u for @sock.
+ * @sock: fully probed HSMP socket.
+ *
+ * The device carries hsmp_sock_groups with @sock as its drvdata, so @sock must
+ * stay valid until hsmp_sock_dev_unregister() returns.
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int hsmp_sock_dev_register(struct hsmp_socket *sock)
+{
+	struct device *dev;
+	int ret;
+
+	guard(mutex)(&hsmp_root_lock);
+
+	if (!hsmp_root_users) {
+		hsmp_root_dev = root_device_register(HSMP_ROOT_DEV_NAME);
+		if (IS_ERR(hsmp_root_dev)) {
+			ret = PTR_ERR(hsmp_root_dev);
+			hsmp_root_dev = NULL;
+			return ret;
+		}
+	}
+
+	/*
+	 * Built by hand rather than with device_create_with_groups(), which
+	 * rejects a NULL class: these devices belong to no class or bus.
+	 */
+	dev = kzalloc_obj(*dev);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err_root;
+	}
+
+	device_initialize(dev);
+	dev->parent = hsmp_root_dev;
+	dev->groups = hsmp_sock_groups;
+	dev->release = hsmp_sock_dev_release;
+	dev_set_drvdata(dev, sock);
+
+	ret = dev_set_name(dev, "socket%u", sock->sock_ind);
+	if (!ret)
+		ret = device_add(dev);
+	if (ret) {
+		put_device(dev);
+		goto err_root;
+	}
+
+	sock->sysfs_dev = dev;
+	hsmp_root_users++;
+
+	return 0;
+
+err_root:
+	if (!hsmp_root_users) {
+		root_device_unregister(hsmp_root_dev);
+		hsmp_root_dev = NULL;
+	}
+	return ret;
+}
+EXPORT_SYMBOL_NS_GPL(hsmp_sock_dev_register, "AMD_HSMP");
+
+/**
+ * hsmp_sock_dev_unregister() - Remove /sys/devices/amd_hsmp/socket%u for @sock.
+ * @sock: HSMP socket; a no-op if its device was never registered.
+ *
+ * device_unregister() waits for in-flight show/read handlers, which take
+ * hsmp_sock_rwsem for read. The caller must therefore not hold it, or a
+ * concurrent sysfs read would deadlock the teardown.
+ */
+void hsmp_sock_dev_unregister(struct hsmp_socket *sock)
+{
+	lockdep_assert_not_held(&hsmp_sock_rwsem);
+
+	guard(mutex)(&hsmp_root_lock);
+
+	if (!sock->sysfs_dev)
+		return;
+
+	device_unregister(sock->sysfs_dev);
+	sock->sysfs_dev = NULL;
+
+	if (!--hsmp_root_users) {
+		root_device_unregister(hsmp_root_dev);
+		hsmp_root_dev = NULL;
+	}
+}
+EXPORT_SYMBOL_NS_GPL(hsmp_sock_dev_unregister, "AMD_HSMP");
-- 
2.53.0


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

* [PATCH 3/4] platform/x86/amd/hsmp: Expose socket attributes from the platform driver
  2026-10-07 12:59 [PATCH 0/4] platform/x86/amd/hsmp: Unify per-socket sysfs metrics Erwan Velu
  2026-10-07 12:59 ` [PATCH 1/4] platform/x86/amd/hsmp: Move per-socket sysfs attributes to common code Erwan Velu
  2026-10-07 12:59 ` [PATCH 2/4] platform/x86/amd/hsmp: Add /sys/devices/amd_hsmp socket devices Erwan Velu
@ 2026-10-07 12:59 ` Erwan Velu
  2026-10-07 12:59 ` [PATCH 4/4] platform/x86/amd/hsmp: Add pwr_current_active_freq_limit_source_raw Erwan Velu
  3 siblings, 0 replies; 5+ messages in thread
From: Erwan Velu @ 2026-10-07 12:59 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: Naveen Krishna Chatradhi, Carlos Bilbao, Hans de Goede,
	Ilpo Järvinen, linux-kernel

From: Erwan Velu <e.velu@criteo.com>

The platform driver only exposes metrics_bin, so on systems without the
HSMP ACPI object, attributes such as pwr_current_active_freq_limit are
missing although the driver can send the same HSMP messages as the ACPI
one.

Register the /sys/devices/amd_hsmp/socketN devices from the platform
driver too, so both drivers expose the same attributes at the same
location. The legacy amd_hsmp/socketN/metrics_bin files are kept.

The devices are torn down by a devres action added after
hsmp_pltdrv_release(), so they are gone before the socket array they
reference is released, and they are registered before /dev/hsmp so a
probe failure leaves nothing behind.

Assisted-by: claude-opus-5-5
Signed-off-by: Erwan Velu <e.velu@criteo.com>
---
 Documentation/arch/x86/amd_hsmp.rst  | 16 ++++++++++------
 drivers/platform/x86/amd/hsmp/plat.c | 20 ++++++++++++++++++++
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst
index aba4a009bd8d..954e938eea3d 100644
--- a/Documentation/arch/x86/amd_hsmp.rst
+++ b/Documentation/arch/x86/amd_hsmp.rst
@@ -57,11 +57,16 @@ In-kernel integration:
 HSMP sysfs interface
 ====================
 
-The ACPI driver exposes its per socket sysfs files in a per socket
-directory created at /sys/devices/amd_hsmp/socket%d/.
+Both the ACPI and plat drivers expose the same set of per socket sysfs
+files in a per socket directory created at /sys/devices/amd_hsmp/socket%d/.
 
-For backward compatibility, the files are also available under
-/sys/devices/platform/AMDI0097:0X/.
+For backward compatibility, the files are also available at their
+historical locations:
+
+* ACPI driver: all the files of this section under
+  /sys/devices/platform/AMDI0097:0X/.
+* plat driver: metrics_bin only, under
+  /sys/devices/platform/amd_hsmp/socket%d/.
 
 1. Metrics table binary sysfs
 
@@ -69,8 +74,7 @@ AMD MI300A MCM provides GET_METRICS_TABLE message to retrieve
 most of the system management information from SMU in one go.
 
 The metrics table is made available as hexadecimal sysfs binary file
-under per socket sysfs directory created at
-/sys/devices/platform/amd_hsmp/socket%d/metrics_bin
+/sys/devices/amd_hsmp/socket%d/metrics_bin
 
 Note: lseek() is not supported as entire metrics table is read.
 
diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c
index e9b2b809c0f5..03317f342c69 100644
--- a/drivers/platform/x86/amd/hsmp/plat.c
+++ b/drivers/platform/x86/amd/hsmp/plat.c
@@ -222,9 +222,18 @@ static void hsmp_pltdrv_release(void *data)
 	hsmp_pdev->sock = NULL;
 }
 
+static void hsmp_pltdrv_sock_devs_release(void *data)
+{
+	u16 i;
+
+	for (i = 0; i < hsmp_pdev->num_sockets; i++)
+		hsmp_sock_dev_unregister(&hsmp_pdev->sock[i]);
+}
+
 static int hsmp_pltdrv_probe(struct platform_device *pdev)
 {
 	int ret;
+	u16 i;
 
 	hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets,
 				       sizeof(*hsmp_pdev->sock),
@@ -253,6 +262,17 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	ret = devm_add_action_or_reset(&pdev->dev, hsmp_pltdrv_sock_devs_release, NULL);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < hsmp_pdev->num_sockets; i++) {
+		ret = hsmp_sock_dev_register(&hsmp_pdev->sock[i]);
+		if (ret)
+			dev_warn(&pdev->dev, "Failed to register HSMP socket%u device: %d\n",
+				 i, ret);
+	}
+
 	ret = hsmp_misc_register(&pdev->dev);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to register misc device\n");
-- 
2.53.0


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

* [PATCH 4/4] platform/x86/amd/hsmp: Add pwr_current_active_freq_limit_source_raw
  2026-10-07 12:59 [PATCH 0/4] platform/x86/amd/hsmp: Unify per-socket sysfs metrics Erwan Velu
                   ` (2 preceding siblings ...)
  2026-10-07 12:59 ` [PATCH 3/4] platform/x86/amd/hsmp: Expose socket attributes from the platform driver Erwan Velu
@ 2026-10-07 12:59 ` Erwan Velu
  3 siblings, 0 replies; 5+ messages in thread
From: Erwan Velu @ 2026-10-07 12:59 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: Naveen Krishna Chatradhi, Carlos Bilbao, Hans de Goede,
	Ilpo Järvinen, linux-kernel

From: Erwan Velu <e.velu@criteo.com>

pwr_current_active_freq_limit_source reports the active frequency limit
sources as a list of names, one per line, which metrics exporters have
to parse and map back to a value.

Add pwr_current_active_freq_limit_source_raw, reporting the same source
bitmask as a single hexadecimal value. Exporters only need a plain
integer conversion, and its interpretation can be centralized in the
monitoring stack, e.g. with Grafana value mappings, instead of being
decoded on each server.

The bit assignment is defined by the HSMP specification and is stable:
new limit sources are only appended, existing bits never change meaning.
A given value therefore has a single interpretation, whatever the
platform or kernel version it comes from.

Assisted-by: claude-opus-5-5
Signed-off-by: Erwan Velu <e.velu@criteo.com>
---
 Documentation/arch/x86/amd_hsmp.rst   |  4 ++++
 drivers/platform/x86/amd/hsmp/sysfs.c | 18 ++++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst
index 954e938eea3d..66dbf506db05 100644
--- a/Documentation/arch/x86/amd_hsmp.rst
+++ b/Documentation/arch/x86/amd_hsmp.rst
@@ -109,6 +109,10 @@ Following sysfs files are available at /sys/devices/amd_hsmp/socket%d/.
   in MHz.
 * pwr_current_active_freq_limit_source: Source of current active frequency
   limit.
+* pwr_current_active_freq_limit_source_raw: Source of current active
+  frequency limit, as a hexadecimal bitmask. Bits 0 to 7 are, in order:
+  cHTC-Active, PROCHOT, TDC limit, PPT Limit, OPN Max, Reliability Limit,
+  APML Agent, HSMP Agent.
 
 ACPI device object format
 =========================
diff --git a/drivers/platform/x86/amd/hsmp/sysfs.c b/drivers/platform/x86/amd/hsmp/sysfs.c
index e9e91b640b2a..fa1cf8dc3b6f 100644
--- a/drivers/platform/x86/amd/hsmp/sysfs.c
+++ b/drivers/platform/x86/amd/hsmp/sysfs.c
@@ -274,6 +274,21 @@ static ssize_t hsmp_freq_limit_source_show(struct device *dev, struct device_att
 	return len;
 }
 
+static ssize_t hsmp_freq_limit_source_raw_show(struct device *dev,
+					       struct device_attribute *attr, char *buf)
+{
+	struct hsmp_sys_attr *hattr = to_hsmp_sys_attr(attr);
+	struct hsmp_socket *sock = dev_get_drvdata(dev);
+	u32 data;
+	int ret;
+
+	ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "0x%04lx\n", FIELD_GET(FREQ_SRC_IND_MASK, data));
+}
+
 static const struct bin_attribute  hsmp_metric_tbl_attr = {
 	.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444},
 	.read = hsmp_metric_tbl_acpi_read,
@@ -307,6 +322,8 @@ HSMP_DEV_ATTR(pwr_current_active_freq_limit, HSMP_GET_SOCKET_FREQ_LIMIT,
 	      hsmp_freq_limit_show, 0444);
 HSMP_DEV_ATTR(pwr_current_active_freq_limit_source, HSMP_GET_SOCKET_FREQ_LIMIT,
 	      hsmp_freq_limit_source_show, 0444);
+HSMP_DEV_ATTR(pwr_current_active_freq_limit_source_raw, HSMP_GET_SOCKET_FREQ_LIMIT,
+	      hsmp_freq_limit_source_raw_show, 0444);
 
 static struct attribute *hsmp_dev_attr_list[] = {
 	&hattr_c0_residency_input.dattr.attr,
@@ -323,6 +340,7 @@ static struct attribute *hsmp_dev_attr_list[] = {
 	&hattr_clk_fmin.dattr.attr,
 	&hattr_pwr_current_active_freq_limit.dattr.attr,
 	&hattr_pwr_current_active_freq_limit_source.dattr.attr,
+	&hattr_pwr_current_active_freq_limit_source_raw.dattr.attr,
 	NULL
 };
 
-- 
2.53.0


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

end of thread, other threads:[~2026-10-07 12:59 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-07 12:59 [PATCH 0/4] platform/x86/amd/hsmp: Unify per-socket sysfs metrics Erwan Velu
2026-10-07 12:59 ` [PATCH 1/4] platform/x86/amd/hsmp: Move per-socket sysfs attributes to common code Erwan Velu
2026-10-07 12:59 ` [PATCH 2/4] platform/x86/amd/hsmp: Add /sys/devices/amd_hsmp socket devices Erwan Velu
2026-10-07 12:59 ` [PATCH 3/4] platform/x86/amd/hsmp: Expose socket attributes from the platform driver Erwan Velu
2026-10-07 12:59 ` [PATCH 4/4] platform/x86/amd/hsmp: Add pwr_current_active_freq_limit_source_raw Erwan Velu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®