Unverified Commit 511a4a5e authored by Suma Hegde's avatar Suma Hegde Committed by Ilpo Järvinen
Browse files

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: default avatarSuma Hegde <suma.hegde@amd.com>
Reviewed-by: default avatarNaveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
Reviewed-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20250506101542.200811-3-suma.hegde@amd.com


Signed-off-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
parent 92c025db
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -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
+262 −0
Original line number Diff line number Diff line
@@ -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[] = {
+23 −0
Original line number Diff line number Diff line
@@ -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 };
+1 −0
Original line number Diff line number Diff line
@@ -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 */