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>
This commit is contained in:
Suma Hegde 2025-05-06 10:15:41 +00:00 committed by Ilpo Järvinen
parent cf8dea42e4
commit 92c025db52
No known key found for this signature in database
GPG key ID: 59AC4F6153E5CE31
6 changed files with 146 additions and 1 deletions

View 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
==========

View 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

View 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;
}

View 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 */

View file

@ -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");

View 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;