platform/x86/amd/hsmp: acpi: Add sysfs files to display HSMP telemetry

Make frequently fetched telemetry available via sysfs. These parameters
do not fit in hwmon sensor model, hence make them available via sysfs.

Create following sysfs files per acpi device node.
* c0_residency_input
* prochot_status
* smu_fw_version
* protocol_version
* ddr_max_bw(GB/s)
* ddr_utilised_bw_input(GB/s)
* ddr_utilised_bw_perc_input(%)
* mclk_input(MHz)
* fclk_input(MHz)
* clk_fmax(MHz)
* clk_fmin(MHz)
* cclk_freq_limit_input(MHz)
* pwr_current_active_freq_limit(MHz)
* pwr_current_active_freq_limit_source

Signed-off-by: Suma Hegde <suma.hegde@amd.com>
Reviewed-by: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20250506101542.200811-3-suma.hegde@amd.com
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
Suma Hegde
2025-05-06 10:15:42 +00:00
committed by Ilpo Järvinen
parent 92c025db52
commit 511a4a5ea2
4 changed files with 308 additions and 0 deletions

View File

@@ -71,6 +71,28 @@ Note: lseek() is not supported as entire metrics table is read.
Metrics table definitions will be documented as part of Public PPR.
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/.
* c0_residency_input: Percentage of cores in C0 state.
* prochot_status: Reports 1 if the processor is at thermal threshold value,
0 otherwise.
* smu_fw_version: SMU firmware version.
* protocol_version: HSMP interface version.
* ddr_max_bw: Theoretical maximum DDR bandwidth in GB/s.
* ddr_utilised_bw_input: Current utilized DDR bandwidth in GB/s.
* ddr_utilised_bw_perc_input(%): Percentage of current utilized DDR bandwidth.
* mclk_input: Memory clock in MHz.
* fclk_input: Fabric clock in MHz.
* clk_fmax: Maximum frequency of socket in MHz.
* clk_fmin: Minimum frequency of socket in MHz.
* cclk_freq_limit_input: Core clock frequency limit per socket in MHz.
* pwr_current_active_freq_limit: Current active frequency limit of socket
in MHz.
* pwr_current_active_freq_limit_source: Source of current active frequency
limit.
ACPI device object format
=========================
The ACPI object format expected from the amd_hsmp driver

View File

@@ -12,6 +12,9 @@
#include <asm/amd_hsmp.h>
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/ioport.h>
@@ -36,6 +39,11 @@
static struct hsmp_plat_device *hsmp_pdev;
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)
{
@@ -243,6 +251,215 @@ static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
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 int init_acpi(struct device *dev)
{
u16 sock_ind;
@@ -285,6 +502,8 @@ static int init_acpi(struct device *dev)
if (ret)
dev_err(dev, "Failed to register HSMP sensors with hwmon\n");
dev_set_drvdata(dev, &hsmp_pdev->sock[sock_ind]);
return ret;
}
@@ -299,9 +518,52 @@ static const struct bin_attribute *hsmp_attr_list[] = {
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_new = 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[] = {

View File

@@ -228,6 +228,29 @@ int hsmp_send_message(struct hsmp_message *msg)
}
EXPORT_SYMBOL_NS_GPL(hsmp_send_message, "AMD_HSMP");
int hsmp_msg_get_nargs(u16 sock_ind, u32 msg_id, u32 *data, u8 num_args)
{
struct hsmp_message msg = {};
unsigned int i;
int ret;
if (!data)
return -EINVAL;
msg.msg_id = msg_id;
msg.sock_ind = sock_ind;
msg.response_sz = num_args;
ret = hsmp_send_message(&msg);
if (ret)
return ret;
for (i = 0; i < num_args; i++)
data[i] = msg.args[i];
return 0;
}
EXPORT_SYMBOL_NS_GPL(hsmp_msg_get_nargs, "AMD_HSMP");
int hsmp_test(u16 sock_ind, u32 value)
{
struct hsmp_message msg = { 0 };

View File

@@ -69,4 +69,5 @@ int hsmp_create_sensor(struct device *dev, u16 sock_ind);
#else
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);
#endif /* HSMP_H */