platform/x86/amd/hsmp: Report power via hwmon sensors
authorSuma Hegde <suma.hegde@amd.com>
Tue, 6 May 2025 10:15:41 +0000 (10:15 +0000)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Mon, 12 May 2025 10:20:17 +0000 (13:20 +0300)
Expose power reading and power limits via hwmon power sensors.

Signed-off-by: Suma Hegde <suma.hegde@amd.com>
Reviewed-by: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
Link: https://lore.kernel.org/r/20250506101542.200811-2-suma.hegde@amd.com
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Documentation/arch/x86/amd_hsmp.rst
drivers/platform/x86/amd/hsmp/Makefile
drivers/platform/x86/amd/hsmp/acpi.c
drivers/platform/x86/amd/hsmp/hsmp.h
drivers/platform/x86/amd/hsmp/hwmon.c [new file with mode: 0644]
drivers/platform/x86/amd/hsmp/plat.c

index 2fd9176..3ef3e0a 100644 (file)
@@ -116,6 +116,14 @@ for socket with ID00 is given below::
                        })
                }
 
+HSMP HWMON interface
+====================
+HSMP power sensors are registered with the hwmon interface. A separate hwmon
+directory is created for each socket and the following files are generated
+within the hwmon directory.
+- power1_input (read only)
+- power1_cap_max (read only)
+- power1_cap (read, write)
 
 An example
 ==========
index 0759bbc..ce8342e 100644 (file)
@@ -6,6 +6,7 @@
 
 obj-$(CONFIG_AMD_HSMP)                 += hsmp_common.o
 hsmp_common-y                          := hsmp.o
+hsmp_common-$(CONFIG_HWMON)            += hwmon.o
 obj-$(CONFIG_AMD_HSMP_PLAT)            += amd_hsmp.o
 amd_hsmp-y                             := plat.o
 obj-$(CONFIG_AMD_HSMP_ACPI)            += hsmp_acpi.o
index 12f4950..93b413e 100644 (file)
@@ -281,6 +281,10 @@ static int init_acpi(struct device *dev)
                        dev_err(dev, "Failed to init metric table\n");
        }
 
+       ret = hsmp_create_sensor(dev, sock_ind);
+       if (ret)
+               dev_err(dev, "Failed to register HSMP sensors with hwmon\n");
+
        return ret;
 }
 
index 7877cb9..d5729f3 100644 (file)
@@ -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>
@@ -25,7 +26,7 @@
 #define HSMP_DEVNODE_NAME      "hsmp"
 #define ACPI_HSMP_DEVICE_HID    "AMDI0097"
 
-#define DRIVER_VERSION         "2.4"
+#define DRIVER_VERSION         "2.5"
 
 struct hsmp_mbaddr_info {
        u32 base_addr;
@@ -63,4 +64,9 @@ 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);
+#if IS_REACHABLE(CONFIG_HWMON)
+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
 #endif /* HSMP_H */
diff --git a/drivers/platform/x86/amd/hsmp/hwmon.c b/drivers/platform/x86/amd/hsmp/hwmon.c
new file mode 100644 (file)
index 0000000..7ffb61e
--- /dev/null
@@ -0,0 +1,121 @@
+// 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 <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include "hsmp.h"
+
+#define HSMP_HWMON_NAME                "amd_hsmp_hwmon"
+
+static int hsmp_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+                           u32 attr, int channel, long val)
+{
+       u16 sock_ind = (uintptr_t)dev_get_drvdata(dev);
+       struct hsmp_message msg = {};
+
+       if (type != hwmon_power)
+               return -EOPNOTSUPP;
+
+       if (attr != hwmon_power_cap)
+               return -EOPNOTSUPP;
+
+       msg.num_args = 1;
+       msg.args[0] = val / MICROWATT_PER_MILLIWATT;
+       msg.msg_id = HSMP_SET_SOCKET_POWER_LIMIT;
+       msg.sock_ind = sock_ind;
+       return hsmp_send_message(&msg);
+}
+
+static int hsmp_hwmon_read(struct device *dev,
+                          enum hwmon_sensor_types type,
+                          u32 attr, int channel, long *val)
+{
+       u16 sock_ind = (uintptr_t)dev_get_drvdata(dev);
+       struct hsmp_message msg = {};
+       int ret;
+
+       if (type != hwmon_power)
+               return -EOPNOTSUPP;
+
+       msg.sock_ind = sock_ind;
+       msg.response_sz = 1;
+
+       switch (attr) {
+       case hwmon_power_input:
+               msg.msg_id = HSMP_GET_SOCKET_POWER;
+               break;
+       case hwmon_power_cap:
+               msg.msg_id = HSMP_GET_SOCKET_POWER_LIMIT;
+               break;
+       case hwmon_power_cap_max:
+               msg.msg_id = HSMP_GET_SOCKET_POWER_LIMIT_MAX;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       ret = hsmp_send_message(&msg);
+       if (!ret)
+               *val = msg.args[0] * MICROWATT_PER_MILLIWATT;
+
+       return ret;
+}
+
+static umode_t hsmp_hwmon_is_visble(const void *data,
+                                   enum hwmon_sensor_types type,
+                                   u32 attr, int channel)
+{
+       if (type != hwmon_power)
+               return 0;
+
+       switch (attr) {
+       case hwmon_power_input:
+               return 0444;
+       case hwmon_power_cap:
+               return 0644;
+       case hwmon_power_cap_max:
+               return 0444;
+       default:
+               return 0;
+       }
+}
+
+static const struct hwmon_ops hsmp_hwmon_ops = {
+       .read = hsmp_hwmon_read,
+       .is_visible = hsmp_hwmon_is_visble,
+       .write  = hsmp_hwmon_write,
+};
+
+static const struct hwmon_channel_info * const hsmp_info[] = {
+       HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX),
+       NULL
+};
+
+static const struct hwmon_chip_info hsmp_chip_info = {
+       .ops = &hsmp_hwmon_ops,
+       .info = hsmp_info,
+};
+
+int hsmp_create_sensor(struct device *dev, u16 sock_ind)
+{
+       struct device *hwmon_dev;
+
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, HSMP_HWMON_NAME,
+                                                        (void *)(uintptr_t)sock_ind,
+                                                        &hsmp_chip_info,
+                                                        NULL);
+       return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+EXPORT_SYMBOL_NS(hsmp_create_sensor, "AMD_HSMP");
index 4f03fdf..0881d7e 100644 (file)
@@ -189,6 +189,11 @@ static int init_platform_device(struct device *dev)
                        if (ret)
                                dev_err(dev, "Failed to init metric table\n");
                }
+
+               /* Register with hwmon interface for reporting power */
+               ret = hsmp_create_sensor(dev, i);
+               if (ret)
+                       dev_err(dev, "Failed to register HSMP sensors with hwmon\n");
        }
 
        return 0;