@@ -71,6 +71,25 @@ 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 file are available at /sys/devices/platform/AMDI0097:0X/.
+
+* c0_residency_input : percentage of cores in C0 state
+* prochot_status : reports 1 if socket is in prochot, 0 otherwhile
+* smu_fw_version : SMU firmware version
+* protocol_version : HSMP interface version
+* ddr_max_bw : theoretical maximum ddr bw in GB/s
+* ddr_utilised_bw_input: current utilized ddr bw in GB/s
+* ddr_utilised_bw_perc_input(%): Percentage of current utilized ddr bw
+* mclk_input : memory clock in MHz
+* fclk_input: fabric clock in MHz
+* clk_fmax : max frequency of socket in MHz
+* clk_fmin : min 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
@@ -119,13 +138,11 @@ for socket with ID00 is given below::
HSMP HWMON interface
==================
-HSMP power sensors are registered with hwmon interface.
-
-One hwmon directory is created for each socket and following files with 0444 permission are created
-inside the hwmon directory.
-- power1_input
-- power1_cap_max
-- power1_cap
+HSMP power sensors are registered with hwmon interface. One hwmon directory is created for each
+socket and following files are created inside the hwmon directory.
+- power1_input(read only)
+- power1_cap_max(read only)
+- power1_cap(read, write)
An example
==========
@@ -13,6 +13,7 @@
#include <asm/amd_nb.h>
#include <linux/acpi.h>
+#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/ioport.h>
@@ -36,6 +37,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 +249,214 @@ 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 sysfs_emit(buf, "%u\n", data);
+
+ return ret;
+}
+
+#define DDR_MAX_BW(data) FIELD_GET(GENMASK(31, 20), data)
+#define DDR_UTIL_BW(data) FIELD_GET(GENMASK(19, 8), data)
+#define DDR_UTIL_BW_PERC(data) FIELD_GET(GENMASK(7, 0), data)
+#define FW_VER_MAJOR(data) FIELD_GET(GENMASK(23, 16), data)
+#define FW_VER_MINOR(data) FIELD_GET(GENMASK(15, 8), data)
+#define FW_VER_DEBUG(data) FIELD_GET(GENMASK(7, 0), data)
+#define FMAX(data) FIELD_GET(GENMASK(31, 16), data)
+#define FMIN(data) FIELD_GET(GENMASK(15, 0), data)
+#define FREQ_LIMIT(data) FIELD_GET(GENMASK(31, 16), data)
+#define FREQ_SRC_IND(data) FIELD_GET(GENMASK(15, 0), data)
+
+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 sysfs_emit(buf, "%u\n", (u32)DDR_MAX_BW(data));
+
+ return ret;
+}
+
+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 sysfs_emit(buf, "%u\n", (u32)DDR_UTIL_BW(data));
+ return ret;
+}
+
+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 sysfs_emit(buf, "%u\n", (u32)DDR_UTIL_BW_PERC(data));
+
+ return ret;
+}
+
+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 sysfs_emit(buf, "%u.%u.%u\n", (u32)FW_VER_MAJOR(data),
+ (u32)FW_VER_MINOR(data), (u32)FW_VER_DEBUG(data));
+
+ return ret;
+}
+
+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 sysfs_emit(buf, "%u\n", data[0]);
+
+ return ret;
+}
+
+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 sysfs_emit(buf, "%u\n", data[1]);
+
+ return ret;
+}
+
+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 sysfs_emit(buf, "%u\n", (u32)FMAX(data));
+
+ return ret;
+}
+
+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 sysfs_emit(buf, "%u\n", (u32)FMIN(data));
+
+ return ret;
+}
+
+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 sysfs_emit(buf, "%u\n", (u32)FREQ_LIMIT(data));
+
+ return ret;
+}
+
+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);
+ u32 index = 0;
+ 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) {
+ src_ind = (u16)FREQ_SRC_IND(data);
+ for (index = 0; index < ARRAY_SIZE(freqlimit_srcnames); index++) {
+ if (src_ind == 0)
+ break;
+ if ((src_ind & 1) == 1)
+ len += sysfs_emit_at(buf, len, "%s ", freqlimit_srcnames[index]);
+ src_ind = src_ind >> 1;
+ }
+ len += sysfs_emit_at(buf, len, "\n");
+ return len;
+ }
+
+ return ret;
+}
+
static int init_acpi(struct device *dev)
{
u16 sock_ind;
@@ -285,6 +499,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 +515,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[] = {
@@ -229,6 +229,27 @@ 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 n)
+{
+ struct hsmp_message msg = { 0 };
+ int ret;
+ int i;
+
+ if (!data)
+ return -EINVAL;
+ msg.msg_id = msg_id;
+ msg.sock_ind = sock_ind;
+ msg.response_sz = n;
+
+ ret = hsmp_send_message(&msg);
+ if (!ret) {
+ for (i = 0; i < n; i++)
+ data[i] = msg.args[i];
+ }
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(hsmp_msg_get_nargs, "AMD_HSMP");
+
int hsmp_test(u16 sock_ind, u32 value)
{
struct hsmp_message msg = { 0 };
@@ -74,4 +74,5 @@ 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 n);
#endif /* HSMP_H */