@@ -117,6 +117,16 @@ for socket with ID00 is given below::
}
+HSMP HWMON interface
+==================
+HSMP power sensors are registered with hwmon interface.
+Following files with 0444 permission are created.
+- powerx_input
+- powerx_cap_max
+- powerx_cap
+one powerx file is created for each socket. powerx_label is used to
+identify the socket to which this info belongs.
+
An example
==========
@@ -5,7 +5,7 @@
#
obj-$(CONFIG_AMD_HSMP) += hsmp_common.o
-hsmp_common-objs := hsmp.o
+hsmp_common-objs := hsmp.o hwmon.o
obj-$(CONFIG_AMD_HSMP_PLAT) += amd_hsmp.o
amd_hsmp-objs := plat.o
obj-$(CONFIG_AMD_HSMP_ACPI) += hsmp_acpi.o
@@ -342,6 +342,9 @@ static int hsmp_acpi_probe(struct platform_device *pdev)
if (ret)
return ret;
hsmp_pdev->is_probed = true;
+ ret = hsmp_create_sensor(&pdev->dev, hsmp_pdev);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to create hwmon interface.\n");
}
return 0;
@@ -12,6 +12,7 @@
#include <linux/compiler_types.h>
#include <linux/device.h>
+#include <linux/hwmon.h>
#include <linux/miscdevice.h>
#include <linux/pci.h>
#include <linux/semaphore.h>
@@ -26,7 +27,7 @@
#define HSMP_CDEV_NAME "hsmp_cdev"
#define HSMP_DEVNODE_NAME "hsmp"
-#define DRIVER_VERSION "2.4"
+#define DRIVER_VERSION "2.5"
struct hsmp_mbaddr_info {
u32 base_addr;
@@ -49,12 +50,20 @@ struct hsmp_socket {
int (*amd_hsmp_rdwr)(struct hsmp_socket *sock, u32 off, u32 *val, bool rw);
};
+struct hsmp_hwmon {
+ char (*p_label)[20];
+ const struct hwmon_channel_info *info[2];
+ struct hwmon_channel_info power_info;
+ struct hwmon_chip_info chip;
+};
+
struct hsmp_plat_device {
struct miscdevice mdev;
struct hsmp_socket *sock;
u32 proto_ver;
u16 num_sockets;
bool is_probed;
+ struct hsmp_hwmon hwmon;
};
int hsmp_cache_proto_ver(u16 sock_ind);
@@ -65,4 +74,5 @@ int hsmp_misc_register(struct device *dev);
int hsmp_get_tbl_dram_base(u16 sock_ind);
ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size);
struct hsmp_plat_device *get_hsmp_pdev(void);
+int hsmp_create_sensor(struct device *dev, struct hsmp_plat_device *pdev);
#endif /* HSMP_H */
new file mode 100644
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD HSMP hwmon support
+ * Copyright (c) 2025, AMD.
+ * All Rights Reserved.
+ *
+ * This file provides hwmon implementation for HSMP interface.
+ */
+
+#include <asm/amd_hsmp.h>
+#include <asm-generic/int-ll64.h>
+
+#include <linux/device.h>
+#include <linux/hwmon.h>
+
+#include "hsmp.h"
+
+#define NR_PWR_CHANNELS 3
+#define HSMP_HWMON_NAME "amd_hsmp_hwmon"
+
+static const char * const power_label[] = {
+ "pinput",
+ "pcap",
+ "pcap_max"
+};
+
+static int hsmp_hwmon_read_label(struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **str)
+{
+ struct hsmp_plat_device *pdev = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_power:
+ *str = pdev->hwmon.p_label[channel];
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int hsmp_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct hsmp_message msg = { 0 };
+ int ret = -EOPNOTSUPP;
+
+ switch (type) {
+ case hwmon_power:
+ switch (channel % NR_PWR_CHANNELS) {
+ case 1:
+ msg.sock_ind = channel / NR_PWR_CHANNELS;
+ msg.num_args = 1;
+ msg.args[0] = val;
+ msg.msg_id = HSMP_SET_SOCKET_POWER_LIMIT;
+ return hsmp_send_message(&msg);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int hsmp_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct hsmp_message msg = { 0 };
+ int ret;
+
+ msg.sock_ind = channel / NR_PWR_CHANNELS;
+ msg.response_sz = 1;
+
+ switch (type) {
+ case hwmon_power:
+ switch (channel % NR_PWR_CHANNELS) {
+ case 0:
+ msg.msg_id = HSMP_GET_SOCKET_POWER;
+ break;
+ case 1:
+ msg.msg_id = HSMP_GET_SOCKET_POWER_LIMIT;
+ break;
+ case 2:
+ msg.msg_id = HSMP_GET_SOCKET_POWER_LIMIT_MAX;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = hsmp_send_message(&msg);
+ if (!ret)
+ *val = msg.args[0];
+
+ return ret;
+}
+
+static umode_t hsmp_hwmon_is_visble(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_power:
+ switch (channel % NR_PWR_CHANNELS) {
+ case 0:
+ case 2:
+ return 0444;
+ case 1:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+const struct hwmon_ops hsmp_hwmon_ops = {
+ .read = hsmp_hwmon_read,
+ .read_string = hsmp_hwmon_read_label,
+ .is_visible = hsmp_hwmon_is_visble,
+ .write = hsmp_hwmon_write,
+};
+
+static int hsmp_create_power_sensor(struct device *dev, struct hsmp_plat_device *pdev)
+{
+ char (*p_label)[20];
+ u32 *p_config;
+ int j;
+
+ p_config = devm_kcalloc(dev, pdev->num_sockets * NR_PWR_CHANNELS + 1,
+ sizeof(*p_config), GFP_KERNEL);
+ if (!p_config)
+ return -ENOMEM;
+
+ p_label = devm_kcalloc(dev, NR_PWR_CHANNELS * pdev->num_sockets,
+ sizeof(*p_label), GFP_KERNEL);
+ if (!p_label)
+ return -ENOMEM;
+
+ for (j = 0; j < pdev->num_sockets * NR_PWR_CHANNELS;) {
+ p_config[j] = HWMON_P_INPUT | HWMON_P_LABEL;
+ p_config[j + 1] = HWMON_P_CAP | HWMON_P_LABEL;
+ p_config[j + 2] = HWMON_P_CAP_MAX | HWMON_P_LABEL;
+ scnprintf(p_label[j], 20, "sock%u_%s", j / NR_PWR_CHANNELS, power_label[0]);
+ scnprintf(p_label[j + 1], 20, "sock%u_%s", j / NR_PWR_CHANNELS, power_label[1]);
+ scnprintf(p_label[j + 2], 20, "sock%u_%s", j / NR_PWR_CHANNELS, power_label[2]);
+ j += NR_PWR_CHANNELS;
+ }
+
+ pdev->hwmon.power_info.type = hwmon_power;
+ pdev->hwmon.power_info.config = p_config;
+ pdev->hwmon.info[0] = &pdev->hwmon.power_info;
+ pdev->hwmon.p_label = p_label;
+
+ return 0;
+}
+
+int hsmp_create_sensor(struct device *dev, struct hsmp_plat_device *pdev)
+{
+ struct device *hwmon_dev;
+ int ret;
+
+ ret = hsmp_create_power_sensor(dev, pdev);
+ if (ret)
+ return ret;
+
+ pdev->hwmon.chip.ops = &hsmp_hwmon_ops;
+ pdev->hwmon.chip.info = pdev->hwmon.info;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, HSMP_HWMON_NAME,
+ pdev,
+ &pdev->hwmon.chip,
+ NULL);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hsmp_create_sensor, "AMD_HSMP");
@@ -220,6 +220,9 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
return ret;
}
+ ret = hsmp_create_sensor(&pdev->dev, hsmp_pdev);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to create hwmon interface.\n");
return hsmp_misc_register(&pdev->dev);
}