* [PATCH 0/3] Add hwmon and sysfs reporting in HSMP
@ 2025-02-18 12:39 Suma Hegde
2025-02-18 12:39 ` [PATCH 1/3] platform/x86/amd/hsmp: Use one DRIVER_VERSION Suma Hegde
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Suma Hegde @ 2025-02-18 12:39 UTC (permalink / raw)
To: platform-driver-x86; +Cc: ilpo.jarvinen, hdegoede, Suma Hegde
This patch series adds
1. power sensors for hwmon interface
2. sysfs interface for telemetry (only in acpi module)
Patch 1: Use one HSMP driver version for common, acpi and plat files.
Patch 2: Add sysfs files to acpi driver.
Patch 3: Add hwmon power sensor support to both acpi and plat driver.
Suma Hegde (3):
platform/x86/amd/hsmp: Use one DRIVER_VERSION
platform/x86/amd/hsmp: Report power using hwmon sensors
platform/x86/amd/hsmp: acpi: Add sysfs files to show HSMP telemetry
Documentation/arch/x86/amd_hsmp.rst | 29 +++
drivers/platform/x86/amd/hsmp/Makefile | 2 +-
drivers/platform/x86/amd/hsmp/acpi.c | 273 ++++++++++++++++++++++++-
drivers/platform/x86/amd/hsmp/hsmp.c | 23 ++-
drivers/platform/x86/amd/hsmp/hsmp.h | 13 ++
drivers/platform/x86/amd/hsmp/hwmon.c | 191 +++++++++++++++++
drivers/platform/x86/amd/hsmp/plat.c | 4 +-
7 files changed, 530 insertions(+), 5 deletions(-)
create mode 100644 drivers/platform/x86/amd/hsmp/hwmon.c
--
2.25.1
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 1/3] platform/x86/amd/hsmp: Use one DRIVER_VERSION 2025-02-18 12:39 [PATCH 0/3] Add hwmon and sysfs reporting in HSMP Suma Hegde @ 2025-02-18 12:39 ` Suma Hegde 2025-02-18 12:39 ` [PATCH 2/3] platform/x86/amd/hsmp: Report power using hwmon sensors Suma Hegde 2025-02-18 12:39 ` [PATCH 3/3] platform/x86/amd/hsmp: acpi: Add sysfs files to show HSMP telemetry Suma Hegde 2 siblings, 0 replies; 6+ messages in thread From: Suma Hegde @ 2025-02-18 12:39 UTC (permalink / raw) To: platform-driver-x86 Cc: ilpo.jarvinen, hdegoede, Suma Hegde, Naveen Krishna Chatradhi Use one DRIVER_VERSION for plat, hsmp and acpi modules. This simplifies userspace applications to support features based on available driver version. Signed-off-by: Suma Hegde <suma.hegde@amd.com> Reviewed-by: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com> --- drivers/platform/x86/amd/hsmp/acpi.c | 1 - drivers/platform/x86/amd/hsmp/hsmp.c | 2 -- drivers/platform/x86/amd/hsmp/hsmp.h | 2 ++ drivers/platform/x86/amd/hsmp/plat.c | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index 444b43be35a2..0c54c91b5f1a 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -27,7 +27,6 @@ #include "hsmp.h" #define DRIVER_NAME "amd_hsmp" -#define DRIVER_VERSION "2.3" #define ACPI_HSMP_DEVICE_HID "AMDI0097" /* These are the strings specified in ACPI table */ diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 03164e30b3a5..8c05e1415820 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -33,8 +33,6 @@ #define HSMP_WR true #define HSMP_RD false -#define DRIVER_VERSION "2.4" - /* * When same message numbers are used for both GET and SET operation, * bit:31 indicates whether its SET or GET operation. diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h index e852f0a947e4..3dee0bb684c7 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.h +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -26,6 +26,8 @@ #define HSMP_CDEV_NAME "hsmp_cdev" #define HSMP_DEVNODE_NAME "hsmp" +#define DRIVER_VERSION "2.4" + struct hsmp_mbaddr_info { u32 base_addr; u32 msg_id_off; diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index 02ca85762b68..63034408985c 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -21,7 +21,6 @@ #include "hsmp.h" #define DRIVER_NAME "amd_hsmp" -#define DRIVER_VERSION "2.3" /* * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox -- 2.25.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/3] platform/x86/amd/hsmp: Report power using hwmon sensors 2025-02-18 12:39 [PATCH 0/3] Add hwmon and sysfs reporting in HSMP Suma Hegde 2025-02-18 12:39 ` [PATCH 1/3] platform/x86/amd/hsmp: Use one DRIVER_VERSION Suma Hegde @ 2025-02-18 12:39 ` Suma Hegde 2025-03-03 13:20 ` Ilpo Järvinen 2025-02-18 12:39 ` [PATCH 3/3] platform/x86/amd/hsmp: acpi: Add sysfs files to show HSMP telemetry Suma Hegde 2 siblings, 1 reply; 6+ messages in thread From: Suma Hegde @ 2025-02-18 12:39 UTC (permalink / raw) To: platform-driver-x86 Cc: ilpo.jarvinen, hdegoede, Suma Hegde, Naveen Krishna Chatradhi 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> --- Documentation/arch/x86/amd_hsmp.rst | 10 ++ drivers/platform/x86/amd/hsmp/Makefile | 2 +- drivers/platform/x86/amd/hsmp/acpi.c | 3 + drivers/platform/x86/amd/hsmp/hsmp.h | 12 +- drivers/platform/x86/amd/hsmp/hwmon.c | 191 +++++++++++++++++++++++++ drivers/platform/x86/amd/hsmp/plat.c | 3 + 6 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 drivers/platform/x86/amd/hsmp/hwmon.c diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst index 2fd917638e42..9e5fef538f4f 100644 --- a/Documentation/arch/x86/amd_hsmp.rst +++ b/Documentation/arch/x86/amd_hsmp.rst @@ -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 ========== diff --git a/drivers/platform/x86/amd/hsmp/Makefile b/drivers/platform/x86/amd/hsmp/Makefile index 3175d8885e87..f63fdc12def1 100644 --- a/drivers/platform/x86/amd/hsmp/Makefile +++ b/drivers/platform/x86/amd/hsmp/Makefile @@ -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 diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index 0c54c91b5f1a..f73b6aedb986 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -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; diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h index 3dee0bb684c7..e0227247c995 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.h +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -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 */ diff --git a/drivers/platform/x86/amd/hsmp/hwmon.c b/drivers/platform/x86/amd/hsmp/hwmon.c new file mode 100644 index 000000000000..a2925c53fbe9 --- /dev/null +++ b/drivers/platform/x86/amd/hsmp/hwmon.c @@ -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"); diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index 63034408985c..2bb590a34642 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -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); } -- 2.25.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] platform/x86/amd/hsmp: Report power using hwmon sensors 2025-02-18 12:39 ` [PATCH 2/3] platform/x86/amd/hsmp: Report power using hwmon sensors Suma Hegde @ 2025-03-03 13:20 ` Ilpo Järvinen 0 siblings, 0 replies; 6+ messages in thread From: Ilpo Järvinen @ 2025-03-03 13:20 UTC (permalink / raw) To: Suma Hegde; +Cc: platform-driver-x86, Hans de Goede, Naveen Krishna Chatradhi On Tue, 18 Feb 2025, Suma Hegde wrote: > 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> > > --- > Documentation/arch/x86/amd_hsmp.rst | 10 ++ > drivers/platform/x86/amd/hsmp/Makefile | 2 +- > drivers/platform/x86/amd/hsmp/acpi.c | 3 + > drivers/platform/x86/amd/hsmp/hsmp.h | 12 +- > drivers/platform/x86/amd/hsmp/hwmon.c | 191 +++++++++++++++++++++++++ > drivers/platform/x86/amd/hsmp/plat.c | 3 + > 6 files changed, 219 insertions(+), 2 deletions(-) > create mode 100644 drivers/platform/x86/amd/hsmp/hwmon.c > > diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst > index 2fd917638e42..9e5fef538f4f 100644 > --- a/Documentation/arch/x86/amd_hsmp.rst > +++ b/Documentation/arch/x86/amd_hsmp.rst > @@ -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 > ========== > > diff --git a/drivers/platform/x86/amd/hsmp/Makefile b/drivers/platform/x86/amd/hsmp/Makefile > index 3175d8885e87..f63fdc12def1 100644 > --- a/drivers/platform/x86/amd/hsmp/Makefile > +++ b/drivers/platform/x86/amd/hsmp/Makefile > @@ -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 Please rebase. Also, this probably lacks some hwmon depends on ? > diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c > index 0c54c91b5f1a..f73b6aedb986 100644 > --- a/drivers/platform/x86/amd/hsmp/acpi.c > +++ b/drivers/platform/x86/amd/hsmp/acpi.c > @@ -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; > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h > index 3dee0bb684c7..e0227247c995 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.h > +++ b/drivers/platform/x86/amd/hsmp/hsmp.h > @@ -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 */ > diff --git a/drivers/platform/x86/amd/hsmp/hwmon.c b/drivers/platform/x86/amd/hsmp/hwmon.c > new file mode 100644 > index 000000000000..a2925c53fbe9 > --- /dev/null > +++ b/drivers/platform/x86/amd/hsmp/hwmon.c > @@ -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> What is this for?? > +#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; That's quite much boilerplate where direct returns would avoid some of it. Is checkpatch the cause for all this madness?? There's no point in cascading breaks just to return in the end of the chain. > + } > + > + return ret; You don't need variable to return -EOPNOTSUPP; here ;-). > +} > + > +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; > + } boilerplate > + > + 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]); You could just devm_kasprintf() these and get rid of that magic 20. And do you even have to alloc the array, it's going to 3 entries always? > + 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"); > diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c > index 63034408985c..2bb590a34642 100644 > --- a/drivers/platform/x86/amd/hsmp/plat.c > +++ b/drivers/platform/x86/amd/hsmp/plat.c > @@ -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); > } > -- i. ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/3] platform/x86/amd/hsmp: acpi: Add sysfs files to show HSMP telemetry 2025-02-18 12:39 [PATCH 0/3] Add hwmon and sysfs reporting in HSMP Suma Hegde 2025-02-18 12:39 ` [PATCH 1/3] platform/x86/amd/hsmp: Use one DRIVER_VERSION Suma Hegde 2025-02-18 12:39 ` [PATCH 2/3] platform/x86/amd/hsmp: Report power using hwmon sensors Suma Hegde @ 2025-02-18 12:39 ` Suma Hegde 2025-03-03 13:31 ` Ilpo Järvinen 2 siblings, 1 reply; 6+ messages in thread From: Suma Hegde @ 2025-02-18 12:39 UTC (permalink / raw) To: platform-driver-x86 Cc: ilpo.jarvinen, hdegoede, Suma Hegde, Naveen Krishna Chatradhi Make frequently fetched telemetry available via sysfs interface. 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: Suma Hegde <suma.hegde@amd.com> Reviewed-by: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com> --- Documentation/arch/x86/amd_hsmp.rst | 19 ++ drivers/platform/x86/amd/hsmp/acpi.c | 269 +++++++++++++++++++++++++++ drivers/platform/x86/amd/hsmp/hsmp.c | 21 +++ drivers/platform/x86/amd/hsmp/hsmp.h | 1 + 4 files changed, 310 insertions(+) diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst index 9e5fef538f4f..1bd13279461f 100644 --- a/Documentation/arch/x86/amd_hsmp.rst +++ b/Documentation/arch/x86/amd_hsmp.rst @@ -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 diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index f73b6aedb986..5c327f94032a 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -36,6 +36,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 +248,226 @@ 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; +} + +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; + u16 mbw; + + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); + if (!ret) { + mbw = data >> 20; + return sysfs_emit(buf, "%u\n", mbw); + } + + 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; + u16 ubw; + + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); + if (!ret) { + ubw = (data >> 8) & GENMASK(12, 0); + return sysfs_emit(buf, "%u\n", ubw); + } + + 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; + u8 perc; + + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); + if (!ret) { + perc = data & GENMASK(7, 0); + return sysfs_emit(buf, "%u\n", perc); + } + + 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; + u8 major; + u8 minor; + u8 debug; + int ret; + + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); + if (!ret) { + major = data >> 16 & GENMASK(7, 0); + minor = data >> 8 & GENMASK(7, 0); + debug = data & GENMASK(7, 0); + return sysfs_emit(buf, "%u.%u.%u\n", major, minor, debug); + } + 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; + u16 fmax; + int ret; + + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); + if (!ret) { + fmax = (data >> 16) & GENMASK(15, 0); + return sysfs_emit(buf, "%u\n", fmax); + } + + 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; + u16 fmin; + int ret; + + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); + if (!ret) { + fmin = data & GENMASK(15, 0); + return sysfs_emit(buf, "%u\n", fmin); + } + + 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); + u16 freq_limit; + u32 data; + int ret; + + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); + if (!ret) { + freq_limit = data >> 16 & GENMASK(15, 0); + return sysfs_emit(buf, "%u\n", freq_limit); + } + + 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 = data & GENMASK(15, 0); + while (src_ind != 0 && index < ARRAY_SIZE(freqlimit_srcnames)) { + if ((src_ind & 1) == 1) + len += sysfs_emit_at(buf, len, "%s ", freqlimit_srcnames[index]); + index += 1; + 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; @@ -280,6 +505,7 @@ static int init_acpi(struct device *dev) if (ret) dev_err(dev, "Failed to init metric table\n"); } + dev_set_drvdata(dev, &hsmp_pdev->sock[sock_ind]); return ret; } @@ -295,9 +521,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[] = { diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 8c05e1415820..65d37605bf88 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -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 }; diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h index e0227247c995..78cf63cf2613 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.h +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -75,4 +75,5 @@ 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); +int hsmp_msg_get_nargs(u16 sock_ind, u32 msg_id, u32 *data, u8 n); #endif /* HSMP_H */ -- 2.25.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 3/3] platform/x86/amd/hsmp: acpi: Add sysfs files to show HSMP telemetry 2025-02-18 12:39 ` [PATCH 3/3] platform/x86/amd/hsmp: acpi: Add sysfs files to show HSMP telemetry Suma Hegde @ 2025-03-03 13:31 ` Ilpo Järvinen 0 siblings, 0 replies; 6+ messages in thread From: Ilpo Järvinen @ 2025-03-03 13:31 UTC (permalink / raw) To: Suma Hegde; +Cc: platform-driver-x86, Hans de Goede, Naveen Krishna Chatradhi On Tue, 18 Feb 2025, Suma Hegde wrote: > Make frequently fetched telemetry available via sysfs interface. > > 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: Suma Hegde <suma.hegde@amd.com> > Reviewed-by: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com> > --- > Documentation/arch/x86/amd_hsmp.rst | 19 ++ > drivers/platform/x86/amd/hsmp/acpi.c | 269 +++++++++++++++++++++++++++ > drivers/platform/x86/amd/hsmp/hsmp.c | 21 +++ > drivers/platform/x86/amd/hsmp/hsmp.h | 1 + > 4 files changed, 310 insertions(+) > > diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst > index 9e5fef538f4f..1bd13279461f 100644 > --- a/Documentation/arch/x86/amd_hsmp.rst > +++ b/Documentation/arch/x86/amd_hsmp.rst > @@ -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 > diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c > index f73b6aedb986..5c327f94032a 100644 > --- a/drivers/platform/x86/amd/hsmp/acpi.c > +++ b/drivers/platform/x86/amd/hsmp/acpi.c > @@ -36,6 +36,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 +248,226 @@ 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) Please reverse the logic in these constructs and return error in early instead. > + return sysfs_emit(buf, "%u\n", data); > + > + return ret; > +} > + > +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; > + u16 mbw; > + > + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); > + if (!ret) { > + mbw = data >> 20; Is this 20 log2(SZ_1M), which should be coded instead if that's the case? Or should this define a named field and use FIELD_GET()? > + return sysfs_emit(buf, "%u\n", mbw); > + } > + > + 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; > + u16 ubw; > + > + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); > + if (!ret) { > + ubw = (data >> 8) & GENMASK(12, 0); Please name with a define and use FIELD_GET(), don't forget to add the header if not already there. > + return sysfs_emit(buf, "%u\n", ubw); > + } > + > + 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; > + u8 perc; > + > + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); > + if (!ret) { > + perc = data & GENMASK(7, 0); Ditto. > + return sysfs_emit(buf, "%u\n", perc); > + } > + > + 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; > + u8 major; > + u8 minor; > + u8 debug; > + int ret; > + > + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); > + if (!ret) { > + major = data >> 16 & GENMASK(7, 0); > + minor = data >> 8 & GENMASK(7, 0); > + debug = data & GENMASK(7, 0); Ditto. > + return sysfs_emit(buf, "%u.%u.%u\n", major, minor, debug); You could just make the FIELD_GET() calls within the sysfs_emit()'s arguments and remove the local variables. > + } > + 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; > + u16 fmax; > + int ret; > + > + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); > + if (!ret) { > + fmax = (data >> 16) & GENMASK(15, 0); > + return sysfs_emit(buf, "%u\n", fmax); > + } > + > + 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; > + u16 fmin; > + int ret; > + > + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); > + if (!ret) { > + fmin = data & GENMASK(15, 0); > + return sysfs_emit(buf, "%u\n", fmin); > + } > + > + 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); > + u16 freq_limit; > + u32 data; > + int ret; > + > + ret = hsmp_msg_get_nargs(sock->sock_ind, hattr->msg_id, &data, 1); > + if (!ret) { > + freq_limit = data >> 16 & GENMASK(15, 0); > + return sysfs_emit(buf, "%u\n", freq_limit); > + } > + > + 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 = data & GENMASK(15, 0); > + while (src_ind != 0 && index < ARRAY_SIZE(freqlimit_srcnames)) { > + if ((src_ind & 1) == 1) It would be more obvious to iterate the array indexes with a for loop. > + len += sysfs_emit_at(buf, len, "%s ", freqlimit_srcnames[index]); > + index += 1; > + 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; > @@ -280,6 +505,7 @@ static int init_acpi(struct device *dev) > if (ret) > dev_err(dev, "Failed to init metric table\n"); > } > + dev_set_drvdata(dev, &hsmp_pdev->sock[sock_ind]); > > return ret; > } > @@ -295,9 +521,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[] = { > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c > index 8c05e1415820..65d37605bf88 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.c > +++ b/drivers/platform/x86/amd/hsmp/hsmp.c > @@ -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) { Reverse logic. > + 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 }; > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h > index e0227247c995..78cf63cf2613 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.h > +++ b/drivers/platform/x86/amd/hsmp/hsmp.h > @@ -75,4 +75,5 @@ 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); > +int hsmp_msg_get_nargs(u16 sock_ind, u32 msg_id, u32 *data, u8 n); > #endif /* HSMP_H */ > -- i. ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-03-03 13:31 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-02-18 12:39 [PATCH 0/3] Add hwmon and sysfs reporting in HSMP Suma Hegde 2025-02-18 12:39 ` [PATCH 1/3] platform/x86/amd/hsmp: Use one DRIVER_VERSION Suma Hegde 2025-02-18 12:39 ` [PATCH 2/3] platform/x86/amd/hsmp: Report power using hwmon sensors Suma Hegde 2025-03-03 13:20 ` Ilpo Järvinen 2025-02-18 12:39 ` [PATCH 3/3] platform/x86/amd/hsmp: acpi: Add sysfs files to show HSMP telemetry Suma Hegde 2025-03-03 13:31 ` Ilpo Järvinen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox