mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

The present sbrmi module only support reporting power via hwmon. However, AMD data center range of processors support various system management functionality using custom protocols defined in Advanced Platform Management Link (APML) specification. Register a miscdevice, which creates a device /dev/sbrmiX with an IOCTL interface for the user space to invoke the APML Mailbox protocol, which is already defined in sbrmi_mailbox_xfer(). The APML protocols depend on a set of RMI registers. Having an IOCTL as a single entry point will help in providing synchronization among these protocols as multiple transactions on RMI register set may create race condition. Support for other protocols will be added in subsequent patches. APML mailbox protocol returns additional error codes written by SMU firmware in the out-bound register 0x37. These errors include, invalid core, message not supported over platform and others. This additional error codes can be used to provide more details to user space. Open-sourced and widely used https://github.com/amd/esmi_oob_library will continue to provide user-space programmable API. Reviewed-by: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com> Signed-off-by: Akshay Gupta <akshay.gupta@amd.com> Link: https://lore.kernel.org/r/20250428063034.2145566-7-akshay.gupta@amd.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
120 lines
2.6 KiB
C
120 lines
2.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* rmi-hwmon.c - hwmon sensor support for side band RMI
|
|
*
|
|
* Copyright (C) 2025 Advanced Micro Devices, Inc.
|
|
*/
|
|
#include <linux/err.h>
|
|
#include <linux/hwmon.h>
|
|
#include <uapi/misc/amd-apml.h>
|
|
#include "rmi-core.h"
|
|
|
|
/* Do not allow setting negative power limit */
|
|
#define SBRMI_PWR_MIN 0
|
|
|
|
static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
|
|
u32 attr, int channel, long *val)
|
|
{
|
|
struct sbrmi_data *data = dev_get_drvdata(dev);
|
|
struct apml_mbox_msg msg = { 0 };
|
|
int ret;
|
|
|
|
if (!data)
|
|
return -ENODEV;
|
|
|
|
if (type != hwmon_power)
|
|
return -EINVAL;
|
|
|
|
switch (attr) {
|
|
case hwmon_power_input:
|
|
msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION;
|
|
ret = rmi_mailbox_xfer(data, &msg);
|
|
break;
|
|
case hwmon_power_cap:
|
|
msg.cmd = SBRMI_READ_PKG_PWR_LIMIT;
|
|
ret = rmi_mailbox_xfer(data, &msg);
|
|
break;
|
|
case hwmon_power_cap_max:
|
|
msg.mb_in_out = data->pwr_limit_max;
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
if (ret < 0)
|
|
return ret;
|
|
/* hwmon power attributes are in microWatt */
|
|
*val = (long)msg.mb_in_out * 1000;
|
|
return ret;
|
|
}
|
|
|
|
static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
|
|
u32 attr, int channel, long val)
|
|
{
|
|
struct sbrmi_data *data = dev_get_drvdata(dev);
|
|
struct apml_mbox_msg msg = { 0 };
|
|
|
|
if (!data)
|
|
return -ENODEV;
|
|
|
|
if (type != hwmon_power && attr != hwmon_power_cap)
|
|
return -EINVAL;
|
|
/*
|
|
* hwmon power attributes are in microWatt
|
|
* mailbox read/write is in mWatt
|
|
*/
|
|
val /= 1000;
|
|
|
|
val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max);
|
|
|
|
msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT;
|
|
msg.mb_in_out = val;
|
|
|
|
return rmi_mailbox_xfer(data, &msg);
|
|
}
|
|
|
|
static umode_t sbrmi_is_visible(const void *data,
|
|
enum hwmon_sensor_types type,
|
|
u32 attr, int channel)
|
|
{
|
|
switch (type) {
|
|
case hwmon_power:
|
|
switch (attr) {
|
|
case hwmon_power_input:
|
|
case hwmon_power_cap_max:
|
|
return 0444;
|
|
case hwmon_power_cap:
|
|
return 0644;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct hwmon_channel_info * const sbrmi_info[] = {
|
|
HWMON_CHANNEL_INFO(power,
|
|
HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX),
|
|
NULL
|
|
};
|
|
|
|
static const struct hwmon_ops sbrmi_hwmon_ops = {
|
|
.is_visible = sbrmi_is_visible,
|
|
.read = sbrmi_read,
|
|
.write = sbrmi_write,
|
|
};
|
|
|
|
static const struct hwmon_chip_info sbrmi_chip_info = {
|
|
.ops = &sbrmi_hwmon_ops,
|
|
.info = sbrmi_info,
|
|
};
|
|
|
|
int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data)
|
|
{
|
|
struct device *hwmon_dev;
|
|
|
|
hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbrmi", data,
|
|
&sbrmi_chip_info, NULL);
|
|
return PTR_ERR_OR_ZERO(hwmon_dev);
|
|
}
|