misc: amd-sbi: Move protocol functionality to core file

- This is done to utilize the protocol functionality into
  other domains.
- Increase the scalability of the module with different bus(i2c/i3c)

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-3-akshay.gupta@amd.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Akshay Gupta 2025-04-28 06:30:26 +00:00 committed by Greg Kroah-Hartman
parent e156586764
commit 43470595e7
5 changed files with 192 additions and 162 deletions

View File

@ -1,9 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
config AMD_SBRMI_I2C
tristate "AMD side band RMI support"
depends on I2C
help
Side band RMI over I2C support for AMD out of band management.
tristate "AMD side band RMI support"
depends on I2C
depends on HWMON
help
Side band RMI over I2C support for AMD out of band management.
This driver can also be built as a module. If so, the module will
be called sbrmi-i2c.

View File

@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi.o
sbrmi-i2c-objs := rmi-i2c.o rmi-core.o
obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi-i2c.o

View File

@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sbrmi-core.c - file defining SB-RMI protocols compliant
* AMD SoC device.
*
* Copyright (C) 2025 Advanced Micro Devices, Inc.
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include "rmi-core.h"
/* Mask for Status Register bit[1] */
#define SW_ALERT_MASK 0x2
/* Software Interrupt for triggering */
#define START_CMD 0x80
#define TRIGGER_MAILBOX 0x01
int rmi_mailbox_xfer(struct sbrmi_data *data,
struct sbrmi_mailbox_msg *msg)
{
int i, ret, retry = 10;
int sw_status;
u8 byte;
mutex_lock(&data->lock);
/* Indicate firmware a command is to be serviced */
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG7, START_CMD);
if (ret < 0)
goto exit_unlock;
/* Write the command to SBRMI::InBndMsg_inst0 */
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG0, msg->cmd);
if (ret < 0)
goto exit_unlock;
/*
* For both read and write the initiator (BMC) writes
* Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
* SBRMI_x3C(MSB):SBRMI_x39(LSB)
*/
for (i = 0; i < 4; i++) {
byte = (msg->data_in >> i * 8) & 0xff;
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG1 + i, byte);
if (ret < 0)
goto exit_unlock;
}
/*
* Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
* perform the requested read or write command
*/
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
if (ret < 0)
goto exit_unlock;
/*
* Firmware will write SBRMI::Status[SwAlertSts]=1 to generate
* an ALERT (if enabled) to initiator (BMC) to indicate completion
* of the requested command
*/
do {
sw_status = i2c_smbus_read_byte_data(data->client,
SBRMI_STATUS);
if (sw_status < 0) {
ret = sw_status;
goto exit_unlock;
}
if (sw_status & SW_ALERT_MASK)
break;
usleep_range(50, 100);
} while (retry--);
if (retry < 0) {
dev_err(&data->client->dev,
"Firmware fail to indicate command completion\n");
ret = -EIO;
goto exit_unlock;
}
/*
* For a read operation, the initiator (BMC) reads the firmware
* response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
* {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
*/
if (msg->read) {
for (i = 0; i < 4; i++) {
ret = i2c_smbus_read_byte_data(data->client,
SBRMI_OUTBNDMSG1 + i);
if (ret < 0)
goto exit_unlock;
msg->data_out |= ret << i * 8;
}
}
/*
* BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
* ALERT to initiator
*/
ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS,
sw_status | SW_ALERT_MASK);
exit_unlock:
mutex_unlock(&data->lock);
return ret;
}

View File

@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025 Advanced Micro Devices, Inc.
*/
#ifndef _SBRMI_CORE_H_
#define _SBRMI_CORE_H_
#include <linux/mutex.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
/* SB-RMI registers */
enum sbrmi_reg {
SBRMI_CTRL = 0x01,
SBRMI_STATUS,
SBRMI_OUTBNDMSG0 = 0x30,
SBRMI_OUTBNDMSG1,
SBRMI_OUTBNDMSG2,
SBRMI_OUTBNDMSG3,
SBRMI_OUTBNDMSG4,
SBRMI_OUTBNDMSG5,
SBRMI_OUTBNDMSG6,
SBRMI_OUTBNDMSG7,
SBRMI_INBNDMSG0,
SBRMI_INBNDMSG1,
SBRMI_INBNDMSG2,
SBRMI_INBNDMSG3,
SBRMI_INBNDMSG4,
SBRMI_INBNDMSG5,
SBRMI_INBNDMSG6,
SBRMI_INBNDMSG7,
SBRMI_SW_INTERRUPT,
};
/*
* SB-RMI supports soft mailbox service request to MP1 (power management
* firmware) through SBRMI inbound/outbound message registers.
* SB-RMI message IDs
*/
enum sbrmi_msg_id {
SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1,
SBRMI_WRITE_PKG_PWR_LIMIT,
SBRMI_READ_PKG_PWR_LIMIT,
SBRMI_READ_PKG_MAX_PWR_LIMIT,
};
/* Each client has this additional data */
struct sbrmi_data {
struct i2c_client *client;
struct mutex lock;
u32 pwr_limit_max;
};
struct sbrmi_mailbox_msg {
u8 cmd;
bool read;
u32 data_in;
u32 data_out;
};
int rmi_mailbox_xfer(struct sbrmi_data *data, struct sbrmi_mailbox_msg *msg);
#endif /*_SBRMI_CORE_H_*/

View File

@ -1,9 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* sbrmi.c - hwmon driver for a SB-RMI mailbox
* compliant AMD SoC device.
* rmi-i2c.c - Side band RMI over I2C support for AMD out
* of band management
*
* Copyright (C) 2020-2021 Advanced Micro Devices, Inc.
* Copyright (C) 2024 Advanced Micro Devices, Inc.
*/
#include <linux/delay.h>
@ -14,64 +14,10 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include "rmi-core.h"
/* Do not allow setting negative power limit */
#define SBRMI_PWR_MIN 0
/* Mask for Status Register bit[1] */
#define SW_ALERT_MASK 0x2
/* Software Interrupt for triggering */
#define START_CMD 0x80
#define TRIGGER_MAILBOX 0x01
/*
* SB-RMI supports soft mailbox service request to MP1 (power management
* firmware) through SBRMI inbound/outbound message registers.
* SB-RMI message IDs
*/
enum sbrmi_msg_id {
SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1,
SBRMI_WRITE_PKG_PWR_LIMIT,
SBRMI_READ_PKG_PWR_LIMIT,
SBRMI_READ_PKG_MAX_PWR_LIMIT,
};
/* SB-RMI registers */
enum sbrmi_reg {
SBRMI_CTRL = 0x01,
SBRMI_STATUS,
SBRMI_OUTBNDMSG0 = 0x30,
SBRMI_OUTBNDMSG1,
SBRMI_OUTBNDMSG2,
SBRMI_OUTBNDMSG3,
SBRMI_OUTBNDMSG4,
SBRMI_OUTBNDMSG5,
SBRMI_OUTBNDMSG6,
SBRMI_OUTBNDMSG7,
SBRMI_INBNDMSG0,
SBRMI_INBNDMSG1,
SBRMI_INBNDMSG2,
SBRMI_INBNDMSG3,
SBRMI_INBNDMSG4,
SBRMI_INBNDMSG5,
SBRMI_INBNDMSG6,
SBRMI_INBNDMSG7,
SBRMI_SW_INTERRUPT,
};
/* Each client has this additional data */
struct sbrmi_data {
struct i2c_client *client;
struct mutex lock;
u32 pwr_limit_max;
};
struct sbrmi_mailbox_msg {
u8 cmd;
bool read;
u32 data_in;
u32 data_out;
};
static int sbrmi_enable_alert(struct i2c_client *client)
{
@ -94,100 +40,6 @@ static int sbrmi_enable_alert(struct i2c_client *client)
return 0;
}
static int rmi_mailbox_xfer(struct sbrmi_data *data,
struct sbrmi_mailbox_msg *msg)
{
int i, ret, retry = 10;
int sw_status;
u8 byte;
mutex_lock(&data->lock);
/* Indicate firmware a command is to be serviced */
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG7, START_CMD);
if (ret < 0)
goto exit_unlock;
/* Write the command to SBRMI::InBndMsg_inst0 */
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG0, msg->cmd);
if (ret < 0)
goto exit_unlock;
/*
* For both read and write the initiator (BMC) writes
* Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
* SBRMI_x3C(MSB):SBRMI_x39(LSB)
*/
for (i = 0; i < 4; i++) {
byte = (msg->data_in >> i * 8) & 0xff;
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_INBNDMSG1 + i, byte);
if (ret < 0)
goto exit_unlock;
}
/*
* Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
* perform the requested read or write command
*/
ret = i2c_smbus_write_byte_data(data->client,
SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
if (ret < 0)
goto exit_unlock;
/*
* Firmware will write SBRMI::Status[SwAlertSts]=1 to generate
* an ALERT (if enabled) to initiator (BMC) to indicate completion
* of the requested command
*/
do {
sw_status = i2c_smbus_read_byte_data(data->client,
SBRMI_STATUS);
if (sw_status < 0) {
ret = sw_status;
goto exit_unlock;
}
if (sw_status & SW_ALERT_MASK)
break;
usleep_range(50, 100);
} while (retry--);
if (retry < 0) {
dev_err(&data->client->dev,
"Firmware fail to indicate command completion\n");
ret = -EIO;
goto exit_unlock;
}
/*
* For a read operation, the initiator (BMC) reads the firmware
* response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
* {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
*/
if (msg->read) {
for (i = 0; i < 4; i++) {
ret = i2c_smbus_read_byte_data(data->client,
SBRMI_OUTBNDMSG1 + i);
if (ret < 0)
goto exit_unlock;
msg->data_out |= ret << i * 8;
}
}
/*
* BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
* ALERT to initiator
*/
ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS,
sw_status | SW_ALERT_MASK);
exit_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
@ -297,7 +149,7 @@ static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
return ret;
}
static int sbrmi_probe(struct i2c_client *client)
static int sbrmi_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
@ -323,12 +175,11 @@ static int sbrmi_probe(struct i2c_client *client)
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
&sbrmi_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct i2c_device_id sbrmi_id[] = {
{"sbrmi"},
{"sbrmi-i2c"},
{}
};
MODULE_DEVICE_TABLE(i2c, sbrmi_id);
@ -343,15 +194,16 @@ MODULE_DEVICE_TABLE(of, sbrmi_of_match);
static struct i2c_driver sbrmi_driver = {
.driver = {
.name = "sbrmi",
.name = "sbrmi-i2c",
.of_match_table = of_match_ptr(sbrmi_of_match),
},
.probe = sbrmi_probe,
.probe = sbrmi_i2c_probe,
.id_table = sbrmi_id,
};
module_i2c_driver(sbrmi_driver);
MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>");
MODULE_AUTHOR("Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>");
MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor");
MODULE_LICENSE("GPL");