Skip to content

Commit 35ac203

Browse files
akky16gregkh
authored andcommitted
misc: amd-sbi: Add support for AMD_SBI IOCTL
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 <[email protected]> Signed-off-by: Akshay Gupta <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 587d2c6 commit 35ac203

File tree

7 files changed

+167
-32
lines changed

7 files changed

+167
-32
lines changed

Documentation/misc-devices/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fit into other categories.
1212
:maxdepth: 2
1313

1414
ad525x_dpot
15+
amd-sbi
1516
apds990x
1617
bh1770glc
1718
c2port

Documentation/userspace-api/ioctl/ioctl-number.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@ Code Seq# Include File Comments
397397
398398
0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver
399399
400+
0xF9 00-0F uapi/misc/amd-apml.h AMD side band system management interface driver
401+
400402
0xFD all linux/dm-ioctl.h
401403
0xFE all linux/isst_if.h
402404
==== ===== ======================================================= ================================================================

drivers/misc/amd-sbi/rmi-core.c

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
*/
88
#include <linux/delay.h>
99
#include <linux/err.h>
10+
#include <linux/fs.h>
1011
#include <linux/i2c.h>
12+
#include <linux/miscdevice.h>
13+
#include <linux/module.h>
1114
#include <linux/mutex.h>
1215
#include <linux/regmap.h>
1316
#include "rmi-core.h"
@@ -20,15 +23,17 @@
2023
#define TRIGGER_MAILBOX 0x01
2124

2225
int rmi_mailbox_xfer(struct sbrmi_data *data,
23-
struct sbrmi_mailbox_msg *msg)
26+
struct apml_mbox_msg *msg)
2427
{
25-
unsigned int bytes;
28+
unsigned int bytes, ec;
2629
int i, ret;
2730
int sw_status;
2831
u8 byte;
2932

3033
mutex_lock(&data->lock);
3134

35+
msg->fw_ret_code = 0;
36+
3237
/* Indicate firmware a command is to be serviced */
3338
ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD);
3439
if (ret < 0)
@@ -44,8 +49,8 @@ int rmi_mailbox_xfer(struct sbrmi_data *data,
4449
* Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
4550
* SBRMI_x3C(MSB):SBRMI_x39(LSB)
4651
*/
47-
for (i = 0; i < 4; i++) {
48-
byte = (msg->data_in >> i * 8) & 0xff;
52+
for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
53+
byte = (msg->mb_in_out >> i * 8) & 0xff;
4954
ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte);
5055
if (ret < 0)
5156
goto exit_unlock;
@@ -69,29 +74,90 @@ int rmi_mailbox_xfer(struct sbrmi_data *data,
6974
if (ret)
7075
goto exit_unlock;
7176

77+
ret = regmap_read(data->regmap, SBRMI_OUTBNDMSG7, &ec);
78+
if (ret || ec)
79+
goto exit_clear_alert;
7280
/*
7381
* For a read operation, the initiator (BMC) reads the firmware
7482
* response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
7583
* {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
7684
*/
77-
if (msg->read) {
78-
for (i = 0; i < 4; i++) {
79-
ret = regmap_read(data->regmap,
80-
SBRMI_OUTBNDMSG1 + i, &bytes);
81-
if (ret < 0)
82-
goto exit_unlock;
83-
msg->data_out |= bytes << i * 8;
84-
}
85+
for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
86+
ret = regmap_read(data->regmap,
87+
SBRMI_OUTBNDMSG1 + i, &bytes);
88+
if (ret < 0)
89+
break;
90+
msg->mb_in_out |= bytes << i * 8;
8591
}
8692

93+
exit_clear_alert:
8794
/*
8895
* BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
8996
* ALERT to initiator
9097
*/
9198
ret = regmap_write(data->regmap, SBRMI_STATUS,
9299
sw_status | SW_ALERT_MASK);
93-
100+
if (ec) {
101+
ret = -EPROTOTYPE;
102+
msg->fw_ret_code = ec;
103+
}
94104
exit_unlock:
95105
mutex_unlock(&data->lock);
96106
return ret;
97107
}
108+
109+
static int apml_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg __user *arg)
110+
{
111+
struct apml_mbox_msg msg = { 0 };
112+
int ret;
113+
114+
/* Copy the structure from user */
115+
if (copy_from_user(&msg, arg, sizeof(struct apml_mbox_msg)))
116+
return -EFAULT;
117+
118+
/* Mailbox protocol */
119+
ret = rmi_mailbox_xfer(data, &msg);
120+
if (ret && ret != -EPROTOTYPE)
121+
return ret;
122+
123+
return copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg));
124+
}
125+
126+
static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
127+
{
128+
void __user *argp = (void __user *)arg;
129+
struct sbrmi_data *data;
130+
131+
data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
132+
switch (cmd) {
133+
case SBRMI_IOCTL_MBOX_CMD:
134+
return apml_mailbox_xfer(data, argp);
135+
default:
136+
return -ENOTTY;
137+
}
138+
}
139+
140+
static const struct file_operations sbrmi_fops = {
141+
.owner = THIS_MODULE,
142+
.unlocked_ioctl = sbrmi_ioctl,
143+
.compat_ioctl = compat_ptr_ioctl,
144+
};
145+
146+
int create_misc_rmi_device(struct sbrmi_data *data,
147+
struct device *dev)
148+
{
149+
data->sbrmi_misc_dev.name = devm_kasprintf(dev,
150+
GFP_KERNEL,
151+
"sbrmi-%x",
152+
data->dev_static_addr);
153+
data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR;
154+
data->sbrmi_misc_dev.fops = &sbrmi_fops;
155+
data->sbrmi_misc_dev.parent = dev;
156+
data->sbrmi_misc_dev.nodename = devm_kasprintf(dev,
157+
GFP_KERNEL,
158+
"sbrmi-%x",
159+
data->dev_static_addr);
160+
data->sbrmi_misc_dev.mode = 0600;
161+
162+
return misc_register(&data->sbrmi_misc_dev);
163+
}

drivers/misc/amd-sbi/rmi-core.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
#ifndef _SBRMI_CORE_H_
77
#define _SBRMI_CORE_H_
88

9+
#include <linux/miscdevice.h>
910
#include <linux/mutex.h>
1011
#include <linux/i2c.h>
1112
#include <linux/platform_device.h>
1213
#include <linux/regmap.h>
14+
#include <uapi/misc/amd-apml.h>
1315

1416
/* SB-RMI registers */
1517
enum sbrmi_reg {
@@ -48,19 +50,15 @@ enum sbrmi_msg_id {
4850

4951
/* Each client has this additional data */
5052
struct sbrmi_data {
53+
struct miscdevice sbrmi_misc_dev;
5154
struct regmap *regmap;
55+
/* Mutex locking */
5256
struct mutex lock;
5357
u32 pwr_limit_max;
58+
u8 dev_static_addr;
5459
};
5560

56-
struct sbrmi_mailbox_msg {
57-
u8 cmd;
58-
bool read;
59-
u32 data_in;
60-
u32 data_out;
61-
};
62-
63-
int rmi_mailbox_xfer(struct sbrmi_data *data, struct sbrmi_mailbox_msg *msg);
61+
int rmi_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg *msg);
6462
#ifdef CONFIG_AMD_SBRMI_HWMON
6563
int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data);
6664
#else
@@ -69,4 +67,5 @@ static inline int create_hwmon_sensor_device(struct device *dev, struct sbrmi_da
6967
return 0;
7068
}
7169
#endif
70+
int create_misc_rmi_device(struct sbrmi_data *data, struct device *dev);
7271
#endif /*_SBRMI_CORE_H_*/

drivers/misc/amd-sbi/rmi-hwmon.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
#include <linux/err.h>
88
#include <linux/hwmon.h>
9+
#include <uapi/misc/amd-apml.h>
910
#include "rmi-core.h"
1011

1112
/* Do not allow setting negative power limit */
@@ -15,7 +16,7 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
1516
u32 attr, int channel, long *val)
1617
{
1718
struct sbrmi_data *data = dev_get_drvdata(dev);
18-
struct sbrmi_mailbox_msg msg = { 0 };
19+
struct apml_mbox_msg msg = { 0 };
1920
int ret;
2021

2122
if (!data)
@@ -24,7 +25,6 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
2425
if (type != hwmon_power)
2526
return -EINVAL;
2627

27-
msg.read = true;
2828
switch (attr) {
2929
case hwmon_power_input:
3030
msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION;
@@ -35,7 +35,7 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
3535
ret = rmi_mailbox_xfer(data, &msg);
3636
break;
3737
case hwmon_power_cap_max:
38-
msg.data_out = data->pwr_limit_max;
38+
msg.mb_in_out = data->pwr_limit_max;
3939
ret = 0;
4040
break;
4141
default:
@@ -44,15 +44,15 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
4444
if (ret < 0)
4545
return ret;
4646
/* hwmon power attributes are in microWatt */
47-
*val = (long)msg.data_out * 1000;
47+
*val = (long)msg.mb_in_out * 1000;
4848
return ret;
4949
}
5050

5151
static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
5252
u32 attr, int channel, long val)
5353
{
5454
struct sbrmi_data *data = dev_get_drvdata(dev);
55-
struct sbrmi_mailbox_msg msg = { 0 };
55+
struct apml_mbox_msg msg = { 0 };
5656

5757
if (!data)
5858
return -ENODEV;
@@ -68,8 +68,7 @@ static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
6868
val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max);
6969

7070
msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT;
71-
msg.data_in = val;
72-
msg.read = false;
71+
msg.mb_in_out = val;
7372

7473
return rmi_mailbox_xfer(data, &msg);
7574
}

drivers/misc/amd-sbi/rmi-i2c.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,14 @@ static int sbrmi_enable_alert(struct sbrmi_data *data)
3838

3939
static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
4040
{
41-
struct sbrmi_mailbox_msg msg = { 0 };
41+
struct apml_mbox_msg msg = { 0 };
4242
int ret;
4343

4444
msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT;
45-
msg.read = true;
4645
ret = rmi_mailbox_xfer(data, &msg);
4746
if (ret < 0)
4847
return ret;
49-
data->pwr_limit_max = msg.data_out;
48+
data->pwr_limit_max = msg.mb_in_out;
5049

5150
return ret;
5251
}
@@ -81,8 +80,25 @@ static int sbrmi_i2c_probe(struct i2c_client *client)
8180
if (ret < 0)
8281
return ret;
8382

83+
data->dev_static_addr = client->addr;
8484
dev_set_drvdata(dev, data);
85-
return create_hwmon_sensor_device(dev, data);
85+
86+
ret = create_hwmon_sensor_device(dev, data);
87+
if (ret < 0)
88+
return ret;
89+
return create_misc_rmi_device(data, dev);
90+
}
91+
92+
static void sbrmi_i2c_remove(struct i2c_client *client)
93+
{
94+
struct sbrmi_data *data = dev_get_drvdata(&client->dev);
95+
96+
misc_deregister(&data->sbrmi_misc_dev);
97+
/* Assign fops and parent of misc dev to NULL */
98+
data->sbrmi_misc_dev.fops = NULL;
99+
data->sbrmi_misc_dev.parent = NULL;
100+
dev_info(&client->dev, "Removed sbrmi-i2c driver\n");
101+
return;
86102
}
87103

88104
static const struct i2c_device_id sbrmi_id[] = {
@@ -105,6 +121,7 @@ static struct i2c_driver sbrmi_driver = {
105121
.of_match_table = of_match_ptr(sbrmi_of_match),
106122
},
107123
.probe = sbrmi_i2c_probe,
124+
.remove = sbrmi_i2c_remove,
108125
.id_table = sbrmi_id,
109126
};
110127

include/uapi/misc/amd-apml.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2+
/*
3+
* Copyright (C) 2021-2024 Advanced Micro Devices, Inc.
4+
*/
5+
#ifndef _AMD_APML_H_
6+
#define _AMD_APML_H_
7+
8+
#include <linux/types.h>
9+
10+
/* Mailbox data size for data_in and data_out */
11+
#define AMD_SBI_MB_DATA_SIZE 4
12+
13+
struct apml_mbox_msg {
14+
/*
15+
* Mailbox Message ID
16+
*/
17+
__u32 cmd;
18+
/*
19+
* [0]...[3] mailbox 32bit input/output data
20+
*/
21+
__u32 mb_in_out;
22+
/*
23+
* Error code is returned in case of soft mailbox error
24+
*/
25+
__u32 fw_ret_code;
26+
};
27+
28+
/*
29+
* AMD sideband interface base IOCTL
30+
*/
31+
#define SB_BASE_IOCTL_NR 0xF9
32+
33+
/**
34+
* DOC: SBRMI_IOCTL_MBOX_CMD
35+
*
36+
* @Parameters
37+
*
38+
* @struct apml_mbox_msg
39+
* Pointer to the &struct apml_mbox_msg that will contain the protocol
40+
* information
41+
*
42+
* @Description
43+
* IOCTL command for APML messages using generic _IOWR
44+
* The IOCTL provides userspace access to AMD sideband mailbox protocol
45+
* - Mailbox message read/write(0x0~0xFF)
46+
* - returning "-EFAULT" if none of the above
47+
* "-EPROTOTYPE" error is returned to provide additional error details
48+
*/
49+
#define SBRMI_IOCTL_MBOX_CMD _IOWR(SB_BASE_IOCTL_NR, 0, struct apml_mbox_msg)
50+
51+
#endif /*_AMD_APML_H_*/

0 commit comments

Comments
 (0)