Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/x86/amd/hsmp: Report power via hwmon sensors

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>

authored by

Suma Hegde and committed by
Ilpo Järvinen
92c025db cf8dea42

+146 -1
+8
Documentation/arch/x86/amd_hsmp.rst
··· 116 116 }) 117 117 } 118 118 119 + HSMP HWMON interface 120 + ==================== 121 + HSMP power sensors are registered with the hwmon interface. A separate hwmon 122 + directory is created for each socket and the following files are generated 123 + within the hwmon directory. 124 + - power1_input (read only) 125 + - power1_cap_max (read only) 126 + - power1_cap (read, write) 119 127 120 128 An example 121 129 ==========
+1
drivers/platform/x86/amd/hsmp/Makefile
··· 6 6 7 7 obj-$(CONFIG_AMD_HSMP) += hsmp_common.o 8 8 hsmp_common-y := hsmp.o 9 + hsmp_common-$(CONFIG_HWMON) += hwmon.o 9 10 obj-$(CONFIG_AMD_HSMP_PLAT) += amd_hsmp.o 10 11 amd_hsmp-y := plat.o 11 12 obj-$(CONFIG_AMD_HSMP_ACPI) += hsmp_acpi.o
+4
drivers/platform/x86/amd/hsmp/acpi.c
··· 281 281 dev_err(dev, "Failed to init metric table\n"); 282 282 } 283 283 284 + ret = hsmp_create_sensor(dev, sock_ind); 285 + if (ret) 286 + dev_err(dev, "Failed to register HSMP sensors with hwmon\n"); 287 + 284 288 return ret; 285 289 } 286 290
+7 -1
drivers/platform/x86/amd/hsmp/hsmp.h
··· 12 12 13 13 #include <linux/compiler_types.h> 14 14 #include <linux/device.h> 15 + #include <linux/hwmon.h> 15 16 #include <linux/miscdevice.h> 16 17 #include <linux/pci.h> 17 18 #include <linux/semaphore.h> ··· 26 25 #define HSMP_DEVNODE_NAME "hsmp" 27 26 #define ACPI_HSMP_DEVICE_HID "AMDI0097" 28 27 29 - #define DRIVER_VERSION "2.4" 28 + #define DRIVER_VERSION "2.5" 30 29 31 30 struct hsmp_mbaddr_info { 32 31 u32 base_addr; ··· 64 63 int hsmp_get_tbl_dram_base(u16 sock_ind); 65 64 ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size); 66 65 struct hsmp_plat_device *get_hsmp_pdev(void); 66 + #if IS_REACHABLE(CONFIG_HWMON) 67 + int hsmp_create_sensor(struct device *dev, u16 sock_ind); 68 + #else 69 + static inline int hsmp_create_sensor(struct device *dev, u16 sock_ind) { return 0; } 70 + #endif 67 71 #endif /* HSMP_H */
+121
drivers/platform/x86/amd/hsmp/hwmon.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * AMD HSMP hwmon support 4 + * Copyright (c) 2025, AMD. 5 + * All Rights Reserved. 6 + * 7 + * This file provides hwmon implementation for HSMP interface. 8 + */ 9 + 10 + #include <asm/amd_hsmp.h> 11 + 12 + #include <linux/device.h> 13 + #include <linux/err.h> 14 + #include <linux/hwmon.h> 15 + #include <linux/types.h> 16 + #include <linux/units.h> 17 + 18 + #include "hsmp.h" 19 + 20 + #define HSMP_HWMON_NAME "amd_hsmp_hwmon" 21 + 22 + static int hsmp_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 23 + u32 attr, int channel, long val) 24 + { 25 + u16 sock_ind = (uintptr_t)dev_get_drvdata(dev); 26 + struct hsmp_message msg = {}; 27 + 28 + if (type != hwmon_power) 29 + return -EOPNOTSUPP; 30 + 31 + if (attr != hwmon_power_cap) 32 + return -EOPNOTSUPP; 33 + 34 + msg.num_args = 1; 35 + msg.args[0] = val / MICROWATT_PER_MILLIWATT; 36 + msg.msg_id = HSMP_SET_SOCKET_POWER_LIMIT; 37 + msg.sock_ind = sock_ind; 38 + return hsmp_send_message(&msg); 39 + } 40 + 41 + static int hsmp_hwmon_read(struct device *dev, 42 + enum hwmon_sensor_types type, 43 + u32 attr, int channel, long *val) 44 + { 45 + u16 sock_ind = (uintptr_t)dev_get_drvdata(dev); 46 + struct hsmp_message msg = {}; 47 + int ret; 48 + 49 + if (type != hwmon_power) 50 + return -EOPNOTSUPP; 51 + 52 + msg.sock_ind = sock_ind; 53 + msg.response_sz = 1; 54 + 55 + switch (attr) { 56 + case hwmon_power_input: 57 + msg.msg_id = HSMP_GET_SOCKET_POWER; 58 + break; 59 + case hwmon_power_cap: 60 + msg.msg_id = HSMP_GET_SOCKET_POWER_LIMIT; 61 + break; 62 + case hwmon_power_cap_max: 63 + msg.msg_id = HSMP_GET_SOCKET_POWER_LIMIT_MAX; 64 + break; 65 + default: 66 + return -EOPNOTSUPP; 67 + } 68 + 69 + ret = hsmp_send_message(&msg); 70 + if (!ret) 71 + *val = msg.args[0] * MICROWATT_PER_MILLIWATT; 72 + 73 + return ret; 74 + } 75 + 76 + static umode_t hsmp_hwmon_is_visble(const void *data, 77 + enum hwmon_sensor_types type, 78 + u32 attr, int channel) 79 + { 80 + if (type != hwmon_power) 81 + return 0; 82 + 83 + switch (attr) { 84 + case hwmon_power_input: 85 + return 0444; 86 + case hwmon_power_cap: 87 + return 0644; 88 + case hwmon_power_cap_max: 89 + return 0444; 90 + default: 91 + return 0; 92 + } 93 + } 94 + 95 + static const struct hwmon_ops hsmp_hwmon_ops = { 96 + .read = hsmp_hwmon_read, 97 + .is_visible = hsmp_hwmon_is_visble, 98 + .write = hsmp_hwmon_write, 99 + }; 100 + 101 + static const struct hwmon_channel_info * const hsmp_info[] = { 102 + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), 103 + NULL 104 + }; 105 + 106 + static const struct hwmon_chip_info hsmp_chip_info = { 107 + .ops = &hsmp_hwmon_ops, 108 + .info = hsmp_info, 109 + }; 110 + 111 + int hsmp_create_sensor(struct device *dev, u16 sock_ind) 112 + { 113 + struct device *hwmon_dev; 114 + 115 + hwmon_dev = devm_hwmon_device_register_with_info(dev, HSMP_HWMON_NAME, 116 + (void *)(uintptr_t)sock_ind, 117 + &hsmp_chip_info, 118 + NULL); 119 + return PTR_ERR_OR_ZERO(hwmon_dev); 120 + } 121 + EXPORT_SYMBOL_NS(hsmp_create_sensor, "AMD_HSMP");
+5
drivers/platform/x86/amd/hsmp/plat.c
··· 189 189 if (ret) 190 190 dev_err(dev, "Failed to init metric table\n"); 191 191 } 192 + 193 + /* Register with hwmon interface for reporting power */ 194 + ret = hsmp_create_sensor(dev, i); 195 + if (ret) 196 + dev_err(dev, "Failed to register HSMP sensors with hwmon\n"); 192 197 } 193 198 194 199 return 0;