2025-04-28 06:30:26 +00:00
|
|
|
// 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>
|
2025-04-28 06:30:30 +00:00
|
|
|
#include <linux/fs.h>
|
2025-04-28 06:30:26 +00:00
|
|
|
#include <linux/i2c.h>
|
2025-04-28 06:30:30 +00:00
|
|
|
#include <linux/miscdevice.h>
|
|
|
|
#include <linux/module.h>
|
2025-04-28 06:30:26 +00:00
|
|
|
#include <linux/mutex.h>
|
2025-04-28 06:30:28 +00:00
|
|
|
#include <linux/regmap.h>
|
2025-04-28 06:30:26 +00:00
|
|
|
#include "rmi-core.h"
|
|
|
|
|
|
|
|
/* Mask for Status Register bit[1] */
|
|
|
|
#define SW_ALERT_MASK 0x2
|
2025-04-28 06:30:31 +00:00
|
|
|
/* Mask to check H/W Alert status bit */
|
|
|
|
#define HW_ALERT_MASK 0x80
|
2025-04-28 06:30:26 +00:00
|
|
|
|
|
|
|
/* Software Interrupt for triggering */
|
|
|
|
#define START_CMD 0x80
|
|
|
|
#define TRIGGER_MAILBOX 0x01
|
|
|
|
|
2025-04-28 06:30:31 +00:00
|
|
|
/* Default message lengths as per APML command protocol */
|
|
|
|
/* CPUID */
|
|
|
|
#define CPUID_RD_DATA_LEN 0x8
|
|
|
|
#define CPUID_WR_DATA_LEN 0x8
|
|
|
|
#define CPUID_RD_REG_LEN 0xa
|
|
|
|
#define CPUID_WR_REG_LEN 0x9
|
2025-04-28 06:30:32 +00:00
|
|
|
/* MSR */
|
|
|
|
#define MSR_RD_REG_LEN 0xa
|
|
|
|
#define MSR_WR_REG_LEN 0x8
|
|
|
|
#define MSR_RD_DATA_LEN 0x8
|
|
|
|
#define MSR_WR_DATA_LEN 0x7
|
2025-04-28 06:30:31 +00:00
|
|
|
|
|
|
|
/* CPUID MSR Command Ids */
|
|
|
|
#define CPUID_MCA_CMD 0x73
|
|
|
|
#define RD_CPUID_CMD 0x91
|
2025-04-28 06:30:32 +00:00
|
|
|
#define RD_MCA_CMD 0x86
|
2025-04-28 06:30:31 +00:00
|
|
|
|
|
|
|
/* CPUID MCAMSR mask & index */
|
|
|
|
#define CPUID_MCA_THRD_INDEX 32
|
|
|
|
#define CPUID_MCA_FUNC_MASK GENMASK(31, 0)
|
|
|
|
#define CPUID_EXT_FUNC_INDEX 56
|
|
|
|
|
|
|
|
/* input for bulk write to CPUID protocol */
|
|
|
|
struct cpu_msr_indata {
|
|
|
|
u8 wr_len; /* const value */
|
|
|
|
u8 rd_len; /* const value */
|
|
|
|
u8 proto_cmd; /* const value */
|
|
|
|
u8 thread; /* thread number */
|
|
|
|
union {
|
|
|
|
u8 reg_offset[4]; /* input value */
|
|
|
|
u32 value;
|
|
|
|
} __packed;
|
|
|
|
u8 ext; /* extended function */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* output for bulk read from CPUID protocol */
|
|
|
|
struct cpu_msr_outdata {
|
|
|
|
u8 num_bytes; /* number of bytes return */
|
|
|
|
u8 status; /* Protocol status code */
|
|
|
|
union {
|
|
|
|
u64 value;
|
|
|
|
u8 reg_data[8];
|
|
|
|
} __packed;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline void prepare_cpuid_input_message(struct cpu_msr_indata *input,
|
|
|
|
u8 thread_id, u32 func,
|
|
|
|
u8 ext_func)
|
|
|
|
{
|
|
|
|
input->rd_len = CPUID_RD_DATA_LEN;
|
|
|
|
input->wr_len = CPUID_WR_DATA_LEN;
|
|
|
|
input->proto_cmd = RD_CPUID_CMD;
|
|
|
|
input->thread = thread_id << 1;
|
|
|
|
input->value = func;
|
|
|
|
input->ext = ext_func;
|
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:32 +00:00
|
|
|
static inline void prepare_mca_msr_input_message(struct cpu_msr_indata *input,
|
|
|
|
u8 thread_id, u32 data_in)
|
|
|
|
{
|
|
|
|
input->rd_len = MSR_RD_DATA_LEN;
|
|
|
|
input->wr_len = MSR_WR_DATA_LEN;
|
|
|
|
input->proto_cmd = RD_MCA_CMD;
|
|
|
|
input->thread = thread_id << 1;
|
|
|
|
input->value = data_in;
|
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:31 +00:00
|
|
|
static int sbrmi_get_rev(struct sbrmi_data *data)
|
|
|
|
{
|
|
|
|
unsigned int rev;
|
|
|
|
u16 offset = SBRMI_REV;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = regmap_read(data->regmap, offset, &rev);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
data->rev = rev;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read CPUID function protocol */
|
|
|
|
static int rmi_cpuid_read(struct sbrmi_data *data,
|
|
|
|
struct apml_cpuid_msg *msg)
|
|
|
|
{
|
|
|
|
struct cpu_msr_indata input = {0};
|
|
|
|
struct cpu_msr_outdata output = {0};
|
|
|
|
int val = 0;
|
|
|
|
int ret, hw_status;
|
|
|
|
u16 thread;
|
|
|
|
|
|
|
|
mutex_lock(&data->lock);
|
|
|
|
/* cache the rev value to identify if protocol is supported or not */
|
|
|
|
if (!data->rev) {
|
|
|
|
ret = sbrmi_get_rev(data);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
/* CPUID protocol for REV 0x10 is not supported*/
|
|
|
|
if (data->rev == 0x10) {
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
|
2025-07-16 11:07:27 +00:00
|
|
|
thread = msg->cpu_in_out >> CPUID_MCA_THRD_INDEX;
|
2025-04-28 06:30:31 +00:00
|
|
|
|
|
|
|
/* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
|
|
|
|
if (thread > 127) {
|
|
|
|
thread -= 128;
|
|
|
|
val = 1;
|
|
|
|
}
|
|
|
|
ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
prepare_cpuid_input_message(&input, thread,
|
|
|
|
msg->cpu_in_out & CPUID_MCA_FUNC_MASK,
|
|
|
|
msg->cpu_in_out >> CPUID_EXT_FUNC_INDEX);
|
|
|
|
|
|
|
|
ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
|
|
|
|
&input, CPUID_WR_REG_LEN);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For RMI Rev 0x20, new h/w status bit is introduced. which is used
|
|
|
|
* by firmware to indicate completion of commands (0x71, 0x72, 0x73).
|
|
|
|
* wait for the status bit to be set by the hardware before
|
|
|
|
* reading the data out.
|
|
|
|
*/
|
|
|
|
ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, hw_status,
|
|
|
|
hw_status & HW_ALERT_MASK, 500, 2000000);
|
|
|
|
if (ret)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
ret = regmap_bulk_read(data->regmap, CPUID_MCA_CMD,
|
|
|
|
&output, CPUID_RD_REG_LEN);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
ret = regmap_write(data->regmap, SBRMI_STATUS,
|
|
|
|
HW_ALERT_MASK);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
if (output.num_bytes != CPUID_RD_REG_LEN - 1) {
|
|
|
|
ret = -EMSGSIZE;
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
if (output.status) {
|
|
|
|
ret = -EPROTOTYPE;
|
|
|
|
msg->fw_ret_code = output.status;
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
msg->cpu_in_out = output.value;
|
|
|
|
exit_unlock:
|
|
|
|
if (ret < 0)
|
|
|
|
msg->cpu_in_out = 0;
|
|
|
|
mutex_unlock(&data->lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:32 +00:00
|
|
|
/* MCA MSR protocol */
|
|
|
|
static int rmi_mca_msr_read(struct sbrmi_data *data,
|
|
|
|
struct apml_mcamsr_msg *msg)
|
|
|
|
{
|
|
|
|
struct cpu_msr_outdata output = {0};
|
|
|
|
struct cpu_msr_indata input = {0};
|
|
|
|
int ret, val = 0;
|
|
|
|
int hw_status;
|
|
|
|
u16 thread;
|
|
|
|
|
|
|
|
mutex_lock(&data->lock);
|
|
|
|
/* cache the rev value to identify if protocol is supported or not */
|
|
|
|
if (!data->rev) {
|
|
|
|
ret = sbrmi_get_rev(data);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
/* MCA MSR protocol for REV 0x10 is not supported*/
|
|
|
|
if (data->rev == 0x10) {
|
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
|
2025-07-16 11:07:27 +00:00
|
|
|
thread = msg->mcamsr_in_out >> CPUID_MCA_THRD_INDEX;
|
2025-04-28 06:30:32 +00:00
|
|
|
|
|
|
|
/* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
|
|
|
|
if (thread > 127) {
|
|
|
|
thread -= 128;
|
|
|
|
val = 1;
|
|
|
|
}
|
|
|
|
ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
prepare_mca_msr_input_message(&input, thread,
|
|
|
|
msg->mcamsr_in_out & CPUID_MCA_FUNC_MASK);
|
|
|
|
|
|
|
|
ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
|
|
|
|
&input, MSR_WR_REG_LEN);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For RMI Rev 0x20, new h/w status bit is introduced. which is used
|
|
|
|
* by firmware to indicate completion of commands (0x71, 0x72, 0x73).
|
|
|
|
* wait for the status bit to be set by the hardware before
|
|
|
|
* reading the data out.
|
|
|
|
*/
|
|
|
|
ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, hw_status,
|
|
|
|
hw_status & HW_ALERT_MASK, 500, 2000000);
|
|
|
|
if (ret)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
ret = regmap_bulk_read(data->regmap, CPUID_MCA_CMD,
|
|
|
|
&output, MSR_RD_REG_LEN);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
ret = regmap_write(data->regmap, SBRMI_STATUS,
|
|
|
|
HW_ALERT_MASK);
|
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
if (output.num_bytes != MSR_RD_REG_LEN - 1) {
|
|
|
|
ret = -EMSGSIZE;
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
if (output.status) {
|
|
|
|
ret = -EPROTOTYPE;
|
|
|
|
msg->fw_ret_code = output.status;
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
msg->mcamsr_in_out = output.value;
|
|
|
|
|
|
|
|
exit_unlock:
|
|
|
|
mutex_unlock(&data->lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:26 +00:00
|
|
|
int rmi_mailbox_xfer(struct sbrmi_data *data,
|
2025-04-28 06:30:30 +00:00
|
|
|
struct apml_mbox_msg *msg)
|
2025-04-28 06:30:26 +00:00
|
|
|
{
|
2025-04-28 06:30:30 +00:00
|
|
|
unsigned int bytes, ec;
|
2025-04-28 06:30:29 +00:00
|
|
|
int i, ret;
|
2025-04-28 06:30:26 +00:00
|
|
|
int sw_status;
|
|
|
|
u8 byte;
|
|
|
|
|
|
|
|
mutex_lock(&data->lock);
|
|
|
|
|
2025-04-28 06:30:30 +00:00
|
|
|
msg->fw_ret_code = 0;
|
|
|
|
|
2025-04-28 06:30:26 +00:00
|
|
|
/* Indicate firmware a command is to be serviced */
|
2025-04-28 06:30:28 +00:00
|
|
|
ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD);
|
2025-04-28 06:30:26 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
|
|
|
|
/* Write the command to SBRMI::InBndMsg_inst0 */
|
2025-04-28 06:30:28 +00:00
|
|
|
ret = regmap_write(data->regmap, SBRMI_INBNDMSG0, msg->cmd);
|
2025-04-28 06:30:26 +00:00
|
|
|
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)
|
|
|
|
*/
|
2025-04-28 06:30:30 +00:00
|
|
|
for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
|
|
|
|
byte = (msg->mb_in_out >> i * 8) & 0xff;
|
2025-04-28 06:30:28 +00:00
|
|
|
ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte);
|
2025-04-28 06:30:26 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto exit_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
|
|
|
|
* perform the requested read or write command
|
|
|
|
*/
|
2025-04-28 06:30:28 +00:00
|
|
|
ret = regmap_write(data->regmap, SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
|
2025-04-28 06:30:26 +00:00
|
|
|
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
|
|
|
|
*/
|
2025-04-28 06:30:29 +00:00
|
|
|
ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, sw_status,
|
|
|
|
sw_status & SW_ALERT_MASK, 500, 2000000);
|
|
|
|
if (ret)
|
2025-04-28 06:30:26 +00:00
|
|
|
goto exit_unlock;
|
|
|
|
|
2025-04-28 06:30:30 +00:00
|
|
|
ret = regmap_read(data->regmap, SBRMI_OUTBNDMSG7, &ec);
|
|
|
|
if (ret || ec)
|
|
|
|
goto exit_clear_alert;
|
2025-07-16 11:07:29 +00:00
|
|
|
|
|
|
|
/* Clear the input value before updating the output data */
|
|
|
|
msg->mb_in_out = 0;
|
|
|
|
|
2025-04-28 06:30:26 +00:00
|
|
|
/*
|
|
|
|
* 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)}.
|
|
|
|
*/
|
2025-04-28 06:30:30 +00:00
|
|
|
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;
|
2025-04-28 06:30:26 +00:00
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:30 +00:00
|
|
|
exit_clear_alert:
|
2025-04-28 06:30:26 +00:00
|
|
|
/*
|
|
|
|
* BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
|
|
|
|
* ALERT to initiator
|
|
|
|
*/
|
2025-04-28 06:30:28 +00:00
|
|
|
ret = regmap_write(data->regmap, SBRMI_STATUS,
|
|
|
|
sw_status | SW_ALERT_MASK);
|
2025-04-28 06:30:30 +00:00
|
|
|
if (ec) {
|
|
|
|
ret = -EPROTOTYPE;
|
|
|
|
msg->fw_ret_code = ec;
|
|
|
|
}
|
2025-04-28 06:30:26 +00:00
|
|
|
exit_unlock:
|
|
|
|
mutex_unlock(&data->lock);
|
|
|
|
return ret;
|
|
|
|
}
|
2025-04-28 06:30:30 +00:00
|
|
|
|
2025-04-28 06:30:33 +00:00
|
|
|
static int apml_rmi_reg_xfer(struct sbrmi_data *data,
|
|
|
|
struct apml_reg_xfer_msg __user *arg)
|
|
|
|
{
|
|
|
|
struct apml_reg_xfer_msg msg = { 0 };
|
|
|
|
unsigned int data_read;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Copy the structure from user */
|
|
|
|
if (copy_from_user(&msg, arg, sizeof(struct apml_reg_xfer_msg)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
mutex_lock(&data->lock);
|
|
|
|
if (msg.rflag) {
|
|
|
|
ret = regmap_read(data->regmap, msg.reg_addr, &data_read);
|
|
|
|
if (!ret)
|
|
|
|
msg.data_in_out = data_read;
|
|
|
|
} else {
|
|
|
|
ret = regmap_write(data->regmap, msg.reg_addr, msg.data_in_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&data->lock);
|
|
|
|
|
|
|
|
if (msg.rflag && !ret)
|
2025-07-16 11:07:28 +00:00
|
|
|
if (copy_to_user(arg, &msg, sizeof(struct apml_reg_xfer_msg)))
|
|
|
|
return -EFAULT;
|
2025-04-28 06:30:33 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:30 +00:00
|
|
|
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;
|
|
|
|
|
2025-07-16 11:07:28 +00:00
|
|
|
if (copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg)))
|
|
|
|
return -EFAULT;
|
|
|
|
return ret;
|
2025-04-28 06:30:30 +00:00
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:31 +00:00
|
|
|
static int apml_cpuid_xfer(struct sbrmi_data *data, struct apml_cpuid_msg __user *arg)
|
|
|
|
{
|
|
|
|
struct apml_cpuid_msg msg = { 0 };
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Copy the structure from user */
|
|
|
|
if (copy_from_user(&msg, arg, sizeof(struct apml_cpuid_msg)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* CPUID Protocol */
|
|
|
|
ret = rmi_cpuid_read(data, &msg);
|
|
|
|
if (ret && ret != -EPROTOTYPE)
|
|
|
|
return ret;
|
|
|
|
|
2025-07-16 11:07:28 +00:00
|
|
|
if (copy_to_user(arg, &msg, sizeof(struct apml_cpuid_msg)))
|
|
|
|
return -EFAULT;
|
|
|
|
return ret;
|
2025-04-28 06:30:31 +00:00
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:32 +00:00
|
|
|
static int apml_mcamsr_xfer(struct sbrmi_data *data, struct apml_mcamsr_msg __user *arg)
|
|
|
|
{
|
|
|
|
struct apml_mcamsr_msg msg = { 0 };
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Copy the structure from user */
|
|
|
|
if (copy_from_user(&msg, arg, sizeof(struct apml_mcamsr_msg)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* MCAMSR Protocol */
|
|
|
|
ret = rmi_mca_msr_read(data, &msg);
|
|
|
|
if (ret && ret != -EPROTOTYPE)
|
|
|
|
return ret;
|
|
|
|
|
2025-07-16 11:07:28 +00:00
|
|
|
if (copy_to_user(arg, &msg, sizeof(struct apml_mcamsr_msg)))
|
|
|
|
return -EFAULT;
|
|
|
|
return ret;
|
2025-04-28 06:30:32 +00:00
|
|
|
}
|
|
|
|
|
2025-04-28 06:30:30 +00:00
|
|
|
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);
|
2025-04-28 06:30:31 +00:00
|
|
|
case SBRMI_IOCTL_CPUID_CMD:
|
|
|
|
return apml_cpuid_xfer(data, argp);
|
2025-04-28 06:30:32 +00:00
|
|
|
case SBRMI_IOCTL_MCAMSR_CMD:
|
|
|
|
return apml_mcamsr_xfer(data, argp);
|
2025-04-28 06:30:33 +00:00
|
|
|
case SBRMI_IOCTL_REG_XFER_CMD:
|
|
|
|
return apml_rmi_reg_xfer(data, argp);
|
2025-04-28 06:30:30 +00:00
|
|
|
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);
|
|
|
|
}
|