diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index 7d13c049c98d..471f50f0c368 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -17,11 +17,160 @@ /* Mask for Status Register bit[1] */ #define SW_ALERT_MASK 0x2 +/* Mask to check H/W Alert status bit */ +#define HW_ALERT_MASK 0x80 /* Software Interrupt for triggering */ #define START_CMD 0x80 #define TRIGGER_MAILBOX 0x01 +/* 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 + +/* CPUID MSR Command Ids */ +#define CPUID_MCA_CMD 0x73 +#define RD_CPUID_CMD 0x91 + +/* CPUID MCAMSR mask & index */ +#define CPUID_MCA_THRD_MASK GENMASK(15, 0) +#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; +} + +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; + } + + thread = msg->cpu_in_out << CPUID_MCA_THRD_INDEX & CPUID_MCA_THRD_MASK; + + /* 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; +} + int rmi_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg *msg) { @@ -123,6 +272,23 @@ static int apml_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg __use return copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg)); } +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; + + return copy_to_user(arg, &msg, sizeof(struct apml_cpuid_msg)); +} + static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; @@ -132,6 +298,8 @@ static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) switch (cmd) { case SBRMI_IOCTL_MBOX_CMD: return apml_mailbox_xfer(data, argp); + case SBRMI_IOCTL_CPUID_CMD: + return apml_cpuid_xfer(data, argp); default: return -ENOTTY; } diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h index 8ab31c6852d1..975ae858e9fd 100644 --- a/drivers/misc/amd-sbi/rmi-core.h +++ b/drivers/misc/amd-sbi/rmi-core.h @@ -15,7 +15,8 @@ /* SB-RMI registers */ enum sbrmi_reg { - SBRMI_CTRL = 0x01, + SBRMI_REV, + SBRMI_CTRL, SBRMI_STATUS, SBRMI_OUTBNDMSG0 = 0x30, SBRMI_OUTBNDMSG1, @@ -34,6 +35,7 @@ enum sbrmi_reg { SBRMI_INBNDMSG6, SBRMI_INBNDMSG7, SBRMI_SW_INTERRUPT, + SBRMI_THREAD128CS = 0x4b, }; /* @@ -56,6 +58,7 @@ struct sbrmi_data { struct mutex lock; u32 pwr_limit_max; u8 dev_static_addr; + u8 rev; }; int rmi_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg *msg); diff --git a/include/uapi/misc/amd-apml.h b/include/uapi/misc/amd-apml.h index a5f086f84b06..bb57dc75758a 100644 --- a/include/uapi/misc/amd-apml.h +++ b/include/uapi/misc/amd-apml.h @@ -25,6 +25,24 @@ struct apml_mbox_msg { __u32 fw_ret_code; }; +struct apml_cpuid_msg { + /* + * CPUID input + * [0]...[3] cpuid func, + * [4][5] cpuid: thread + * [6] cpuid: ext function & read eax/ebx or ecx/edx + * [7:0] -> bits [7:4] -> ext function & + * bit [0] read eax/ebx or ecx/edx + * CPUID output + */ + __u64 cpu_in_out; + /* + * Status code for CPUID read + */ + __u32 fw_ret_code; + __u32 pad; +}; + /* * AMD sideband interface base IOCTL */ @@ -48,4 +66,23 @@ struct apml_mbox_msg { */ #define SBRMI_IOCTL_MBOX_CMD _IOWR(SB_BASE_IOCTL_NR, 0, struct apml_mbox_msg) +/** + * DOC: SBRMI_IOCTL_CPUID_CMD + * + * @Parameters + * + * @struct apml_cpuid_msg + * Pointer to the &struct apml_cpuid_msg that will contain the protocol + * information + * + * @Description + * IOCTL command for APML messages using generic _IOWR + * The IOCTL provides userspace access to AMD sideband cpuid protocol + * - CPUID protocol to get CPU details for Function/Ext Function + * at thread level + * - returning "-EFAULT" if none of the above + * "-EPROTOTYPE" error is returned to provide additional error details + */ +#define SBRMI_IOCTL_CPUID_CMD _IOWR(SB_BASE_IOCTL_NR, 1, struct apml_cpuid_msg) + #endif /*_AMD_APML_H_*/