diff options
-rw-r--r-- | Documentation/misc-devices/index.rst | 1 | ||||
-rw-r--r-- | Documentation/userspace-api/ioctl/ioctl-number.rst | 2 | ||||
-rw-r--r-- | drivers/misc/amd-sbi/rmi-core.c | 92 | ||||
-rw-r--r-- | drivers/misc/amd-sbi/rmi-core.h | 15 | ||||
-rw-r--r-- | drivers/misc/amd-sbi/rmi-hwmon.c | 13 | ||||
-rw-r--r-- | drivers/misc/amd-sbi/rmi-i2c.c | 25 | ||||
-rw-r--r-- | include/uapi/misc/amd-apml.h | 51 |
7 files changed, 167 insertions, 32 deletions
diff --git a/Documentation/misc-devices/index.rst b/Documentation/misc-devices/index.rst index 8c5b226d8313..081e79415e38 100644 --- a/Documentation/misc-devices/index.rst +++ b/Documentation/misc-devices/index.rst @@ -12,6 +12,7 @@ fit into other categories. :maxdepth: 2 ad525x_dpot + amd-sbi apds990x bh1770glc c2port diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 7a1409ecc238..3191d96ea4da 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -397,6 +397,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 ==== ===== ======================================================= ================================================================ diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index 1d5e2556ab88..7d13c049c98d 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -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" @@ -20,15 +23,17 @@ #define TRIGGER_MAILBOX 0x01 int rmi_mailbox_xfer(struct sbrmi_data *data, - struct sbrmi_mailbox_msg *msg) + struct apml_mbox_msg *msg) { - unsigned int bytes; + unsigned int bytes, ec; int i, ret; int sw_status; u8 byte; mutex_lock(&data->lock); + msg->fw_ret_code = 0; + /* Indicate firmware a command is to be serviced */ ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD); if (ret < 0) @@ -44,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->mb_in_out >> i * 8) & 0xff; ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte); if (ret < 0) goto exit_unlock; @@ -69,29 +74,90 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, if (ret) goto exit_unlock; + ret = regmap_read(data->regmap, SBRMI_OUTBNDMSG7, &ec); + if (ret || ec) + goto exit_clear_alert; /* * 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 = regmap_read(data->regmap, - SBRMI_OUTBNDMSG1 + i, &bytes); - if (ret < 0) - goto exit_unlock; - msg->data_out |= bytes << i * 8; - } + for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) { + ret = regmap_read(data->regmap, + SBRMI_OUTBNDMSG1 + i, &bytes); + if (ret < 0) + break; + msg->mb_in_out |= bytes << i * 8; } +exit_clear_alert: /* * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the * ALERT to initiator */ ret = regmap_write(data->regmap, SBRMI_STATUS, sw_status | SW_ALERT_MASK); - + if (ec) { + ret = -EPROTOTYPE; + msg->fw_ret_code = ec; + } exit_unlock: mutex_unlock(&data->lock); return ret; } + +static int apml_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg __user *arg) +{ + struct apml_mbox_msg msg = { 0 }; + int ret; + + /* Copy the structure from user */ + if (copy_from_user(&msg, arg, sizeof(struct apml_mbox_msg))) + return -EFAULT; + + /* Mailbox protocol */ + ret = rmi_mailbox_xfer(data, &msg); + if (ret && ret != -EPROTOTYPE) + return ret; + + return copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg)); +} + +static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct sbrmi_data *data; + + data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev); + switch (cmd) { + case SBRMI_IOCTL_MBOX_CMD: + return apml_mailbox_xfer(data, argp); + default: + return -ENOTTY; + } +} + +static const struct file_operations sbrmi_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = sbrmi_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +int create_misc_rmi_device(struct sbrmi_data *data, + struct device *dev) +{ + 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; + + return misc_register(&data->sbrmi_misc_dev); +} diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h index 3a6028306d10..8ab31c6852d1 100644 --- a/drivers/misc/amd-sbi/rmi-core.h +++ b/drivers/misc/amd-sbi/rmi-core.h @@ -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; 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_mbox_msg *msg); #ifdef CONFIG_AMD_SBRMI_HWMON int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data); #else @@ -69,4 +67,5 @@ static inline int create_hwmon_sensor_device(struct device *dev, struct sbrmi_da return 0; } #endif +int create_misc_rmi_device(struct sbrmi_data *data, struct device *dev); #endif /*_SBRMI_CORE_H_*/ diff --git a/drivers/misc/amd-sbi/rmi-hwmon.c b/drivers/misc/amd-sbi/rmi-hwmon.c index 720e800db1f0..f4f015605daa 100644 --- a/drivers/misc/amd-sbi/rmi-hwmon.c +++ b/drivers/misc/amd-sbi/rmi-hwmon.c @@ -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_mbox_msg msg = { 0 }; int ret; if (!data) @@ -24,7 +25,6 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, if (type != hwmon_power) return -EINVAL; - msg.read = true; switch (attr) { case hwmon_power_input: msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; @@ -35,7 +35,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.mb_in_out = data->pwr_limit_max; ret = 0; break; default: @@ -44,7 +44,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.mb_in_out * 1000; return ret; } @@ -52,7 +52,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_mbox_msg msg = { 0 }; if (!data) return -ENODEV; @@ -68,8 +68,7 @@ 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.mb_in_out = val; return rmi_mailbox_xfer(data, &msg); } diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index 7a9801273a4c..f891f5af4bc6 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -38,15 +38,14 @@ 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_mbox_msg msg = { 0 }; int ret; msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT; - msg.read = true; ret = rmi_mailbox_xfer(data, &msg); if (ret < 0) return ret; - data->pwr_limit_max = msg.data_out; + data->pwr_limit_max = msg.mb_in_out; return ret; } @@ -81,8 +80,25 @@ 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[] = { @@ -105,6 +121,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, }; diff --git a/include/uapi/misc/amd-apml.h b/include/uapi/misc/amd-apml.h new file mode 100644 index 000000000000..a5f086f84b06 --- /dev/null +++ b/include/uapi/misc/amd-apml.h @@ -0,0 +1,51 @@ +/* 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> + +/* Mailbox data size for data_in and data_out */ +#define AMD_SBI_MB_DATA_SIZE 4 + +struct apml_mbox_msg { + /* + * Mailbox Message ID + */ + __u32 cmd; + /* + * [0]...[3] mailbox 32bit input/output data + */ + __u32 mb_in_out; + /* + * Error code is returned in case of soft mailbox error + */ + __u32 fw_ret_code; +}; + +/* + * AMD sideband interface base IOCTL + */ +#define SB_BASE_IOCTL_NR 0xF9 + +/** + * DOC: SBRMI_IOCTL_MBOX_CMD + * + * @Parameters + * + * @struct apml_mbox_msg + * Pointer to the &struct apml_mbox_msg that will contain the protocol + * information + * + * @Description + * IOCTL command for APML messages using generic _IOWR + * The IOCTL provides userspace access to AMD sideband mailbox protocol + * - Mailbox message read/write(0x0~0xFF) + * - returning "-EFAULT" if none of the above + * "-EPROTOTYPE" error is returned to provide additional error details + */ +#define SBRMI_IOCTL_MBOX_CMD _IOWR(SB_BASE_IOCTL_NR, 0, struct apml_mbox_msg) + +#endif /*_AMD_APML_H_*/ |