@@ -12,6 +12,7 @@ fit into other categories.
:maxdepth: 2
ad525x_dpot
+ amd-sbi
apds990x
bh1770glc
c2port
@@ -388,6 +388,8 @@ Code Seq# Include File Comments
<mailto:mathieu.desnoyers@efficios.com>
0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver
<mailto:nchatrad@amd.com>
+0xF9 00-0F uapi/misc/amd-apml.h AMD side band system management interface driver
+ <mailto:naveenkrishna.chatradhi@amd.com>
0xFD all linux/dm-ioctl.h
0xFE all linux/isst_if.h
==== ===== ======================================================= ================================================================
@@ -7,7 +7,10 @@
*/
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/fs.h>
#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include "rmi-core.h"
@@ -22,7 +25,7 @@
#define TRIGGER_MAILBOX 0x01
int rmi_mailbox_xfer(struct sbrmi_data *data,
- struct sbrmi_mailbox_msg *msg)
+ struct apml_message *msg)
{
unsigned int bytes;
int i, ret;
@@ -46,8 +49,8 @@ int rmi_mailbox_xfer(struct sbrmi_data *data,
* 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;
+ for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
+ byte = msg->data_in.reg_in[i];
ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte);
if (ret < 0)
goto exit_unlock;
@@ -76,13 +79,13 @@ int rmi_mailbox_xfer(struct sbrmi_data *data,
* 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++) {
+ if (msg->data_in.reg_in[AMD_SBI_RD_FLAG_INDEX]) {
+ for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
ret = regmap_read(data->regmap,
SBRMI_OUTBNDMSG1 + i, &bytes);
if (ret < 0)
- goto exit_unlock;
- msg->data_out |= bytes << i * 8;
+ break;
+ msg->data_out.reg_out[i] = bytes;
}
}
@@ -92,8 +95,78 @@ int rmi_mailbox_xfer(struct sbrmi_data *data,
*/
ret = regmap_write(data->regmap, SBRMI_STATUS,
sw_status | SW_ALERT_MASK);
-
exit_unlock:
mutex_unlock(&data->lock);
return ret;
}
+
+static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ int __user *arguser = (int __user *)arg;
+ struct apml_message msg = { 0 };
+ bool read = false;
+ int ret = -EFAULT;
+
+ struct sbrmi_data *data = container_of(fp->private_data, struct sbrmi_data,
+ sbrmi_misc_dev);
+ if (!data)
+ return -ENODEV;
+
+ /* Copy the structure from user */
+ if (copy_struct_from_user(&msg, sizeof(msg), arguser,
+ sizeof(struct apml_message)))
+ return ret;
+
+ /* Is this a read/monitor/get request */
+ if (msg.data_in.reg_in[AMD_SBI_RD_FLAG_INDEX])
+ read = true;
+
+ switch (msg.cmd) {
+ case 0 ... 0x999:
+ /* Mailbox protocol */
+ ret = rmi_mailbox_xfer(data, &msg);
+ break;
+ default:
+ pr_err("Command:0x%x not recognized\n", msg.cmd);
+ break;
+ }
+
+ /* Copy results back to user only for get/monitor commands and firmware failures */
+ if ((read && !ret) || ret == -EPROTOTYPE) {
+ if (copy_to_user(arguser, &msg, sizeof(struct apml_message)))
+ ret = -EFAULT;
+ }
+ return ret;
+}
+
+static const struct file_operations sbrmi_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = sbrmi_ioctl,
+ .compat_ioctl = sbrmi_ioctl,
+};
+
+int create_misc_rmi_device(struct sbrmi_data *data,
+ struct device *dev)
+{
+ int ret;
+
+ data->sbrmi_misc_dev.name = devm_kasprintf(dev,
+ GFP_KERNEL,
+ "sbrmi-%x",
+ data->dev_static_addr);
+ data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR;
+ data->sbrmi_misc_dev.fops = &sbrmi_fops;
+ data->sbrmi_misc_dev.parent = dev;
+ data->sbrmi_misc_dev.nodename = devm_kasprintf(dev,
+ GFP_KERNEL,
+ "sbrmi-%x",
+ data->dev_static_addr);
+ data->sbrmi_misc_dev.mode = 0600;
+
+ ret = misc_register(&data->sbrmi_misc_dev);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "register %s device\n", data->sbrmi_misc_dev.name);
+ return ret;
+}
@@ -6,10 +6,12 @@
#ifndef _SBRMI_CORE_H_
#define _SBRMI_CORE_H_
+#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <uapi/misc/amd-apml.h>
/* SB-RMI registers */
enum sbrmi_reg {
@@ -48,19 +50,15 @@ enum sbrmi_msg_id {
/* Each client has this additional data */
struct sbrmi_data {
+ struct miscdevice sbrmi_misc_dev;
struct regmap *regmap;
+ /* Mutex locking */
struct mutex lock;
- struct platform_device *pdev;
u32 pwr_limit_max;
+ u8 dev_static_addr;
};
-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);
+int rmi_mailbox_xfer(struct sbrmi_data *data, struct apml_message *msg);
int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data);
+int create_misc_rmi_device(struct sbrmi_data *data, struct device *dev);
#endif /*_SBRMI_CORE_H_*/
@@ -6,6 +6,7 @@
*/
#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 */
@@ -15,7 +16,7 @@ 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 sbrmi_mailbox_msg msg = { 0 };
+ struct apml_message msg = { 0 };
int ret;
if (!data)
@@ -24,7 +25,7 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
if (type != hwmon_power)
return -EINVAL;
- msg.read = true;
+ msg.data_in.reg_in[AMD_SBI_RD_FLAG_INDEX] = 1;
switch (attr) {
case hwmon_power_input:
msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION;
@@ -35,7 +36,7 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
ret = rmi_mailbox_xfer(data, &msg);
break;
case hwmon_power_cap_max:
- msg.data_out = data->pwr_limit_max;
+ msg.data_out.mb_out[AMD_SBI_RD_WR_DATA_INDEX] = data->pwr_limit_max;
ret = 0;
break;
default:
@@ -44,7 +45,7 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
if (ret < 0)
return ret;
/* hwmon power attributes are in microWatt */
- *val = (long)msg.data_out * 1000;
+ *val = (long)msg.data_out.mb_out[AMD_SBI_RD_WR_DATA_INDEX] * 1000;
return ret;
}
@@ -52,7 +53,7 @@ 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 sbrmi_mailbox_msg msg = { 0 };
+ struct apml_message msg = { 0 };
if (!data)
return -ENODEV;
@@ -68,8 +69,8 @@ static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max);
msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT;
- msg.data_in = val;
- msg.read = false;
+ msg.data_in.mb_in[AMD_SBI_RD_WR_DATA_INDEX] = val;
+ msg.data_in.reg_in[AMD_SBI_RD_FLAG_INDEX] = 0;
return rmi_mailbox_xfer(data, &msg);
}
@@ -40,15 +40,15 @@ static int sbrmi_enable_alert(struct sbrmi_data *data)
static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
{
- struct sbrmi_mailbox_msg msg = { 0 };
+ struct apml_message msg = { 0 };
int ret;
msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT;
- msg.read = true;
+ msg.data_in.reg_in[AMD_SBI_RD_FLAG_INDEX] = 1;
ret = rmi_mailbox_xfer(data, &msg);
if (ret < 0)
return ret;
- data->pwr_limit_max = msg.data_out;
+ data->pwr_limit_max = msg.data_out.mb_out[AMD_SBI_RD_WR_DATA_INDEX];
return ret;
}
@@ -83,8 +83,24 @@ static int sbrmi_i2c_probe(struct i2c_client *client)
if (ret < 0)
return ret;
+ data->dev_static_addr = client->addr;
dev_set_drvdata(dev, data);
- return create_hwmon_sensor_device(dev, data);
+ ret = create_hwmon_sensor_device(dev, data);
+ if (ret < 0)
+ return ret;
+ return create_misc_rmi_device(data, dev);
+}
+
+static void sbrmi_i2c_remove(struct i2c_client *client)
+{
+ struct sbrmi_data *data = dev_get_drvdata(&client->dev);
+
+ misc_deregister(&data->sbrmi_misc_dev);
+ /* Assign fops and parent of misc dev to NULL */
+ data->sbrmi_misc_dev.fops = NULL;
+ data->sbrmi_misc_dev.parent = NULL;
+ dev_info(&client->dev, "Removed sbrmi-i2c driver\n");
+ return;
}
static const struct i2c_device_id sbrmi_id[] = {
@@ -107,6 +123,7 @@ static struct i2c_driver sbrmi_driver = {
.of_match_table = of_match_ptr(sbrmi_of_match),
},
.probe = sbrmi_i2c_probe,
+ .remove = sbrmi_i2c_remove,
.id_table = sbrmi_id,
};
new file mode 100644
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2021-2024 Advanced Micro Devices, Inc.
+ */
+#ifndef _AMD_APML_H_
+#define _AMD_APML_H_
+
+#include <linux/types.h>
+
+/* These are byte indexes into data_in and data_out arrays */
+#define AMD_SBI_RD_WR_DATA_INDEX 0
+#define AMD_SBI_REG_OFF_INDEX 0
+#define AMD_SBI_REG_VAL_INDEX 4
+#define AMD_SBI_RD_FLAG_INDEX 7
+
+#define AMD_SBI_MB_DATA_SIZE 4
+
+struct apml_message {
+ /* message ids:
+ * Mailbox Messages: 0x0 ... 0x999
+ */
+ __u32 cmd;
+
+ /*
+ * 8 bit data for reg read,
+ * 32 bit data in case of mailbox,
+ */
+ union {
+ __u32 mb_out[2];
+ __u8 reg_out[8];
+ } data_out;
+
+ /*
+ * [0]...[3] mailbox 32bit input
+ * [7] read/write functionality
+ */
+ union {
+ __u32 mb_in[2];
+ __u8 reg_in[8];
+ } data_in;
+} __attribute__((packed));
+
+/**
+ * AMD sideband interface base IOCTL
+ */
+#define SB_BASE_IOCTL_NR 0xF9
+
+/**
+ * DOC: SBRMI_IOCTL_CMD
+ *
+ * @Parameters
+ *
+ * @struct apml_message *
+ * Pointer to the &struct apml_message that will contain the protocol
+ * information
+ *
+ * @Description
+ * IOCTL command for APML messages using generic _IOWR
+ * The IOCTL provides userspace access to AMD sideband protocols
+ * The APML RMI module checks whether the cmd is
+ * - Mailbox message read/write(0x0~0x999)
+ * - returning "-EFAULT" if none of the above
+ */
+#define SBRMI_IOCTL_CMD _IOWR(SB_BASE_IOCTL_NR, 0, struct apml_message)
+
+#endif /*_AMD_APML_H_*/