diff options
Diffstat (limited to 'drivers/misc/amd-sbi/rmi-core.c')
| -rw-r--r-- | drivers/misc/amd-sbi/rmi-core.c | 92 |
1 files changed, 79 insertions, 13 deletions
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); +} |
