@@ -17,6 +17,8 @@
/* Mask for Status Register bit[1] */
#define SW_ALERT_MASK 0x2
+/* Mask to check H/W Alert status bit */
+#define HW_ALERT_MASK 0x80
/* Do not allow setting negative power limit */
#define SBRMI_PWR_MIN 0
@@ -24,6 +26,171 @@
#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
+
+/* 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;
+ };
+ u8 ext; /* extended function */
+} __packed;
+
+/* 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;
+
+#define prepare_cpuid_input_message(input, thread_id, func, 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
+
+/*
+ * For Mailbox command software alert status bit is set by firmware
+ * to indicate command completion
+ * 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 firmware before
+ * reading the data out.
+ */
+static int sbrmi_wait_status(struct sbrmi_data *data,
+ int *status, int mask)
+{
+ int ret, retry = 100;
+
+ do {
+ ret = regmap_read(data->regmap, SBRMI_STATUS, status);
+ if (ret < 0)
+ return ret;
+
+ if (*status & mask)
+ break;
+
+ /* Wait 1~2 second for firmware to return data out */
+ if (retry > 95)
+ usleep_range(50, 100);
+ else
+ usleep_range(10000, 20000);
+ } while (retry--);
+
+ if (retry < 0)
+ ret = -ETIMEDOUT;
+ return ret;
+}
+
+static int sbrmi_get_rev(struct sbrmi_data *data)
+{
+ struct apml_message msg = { 0 };
+ int ret;
+
+ msg.data_in.reg_in[REG_OFF_INDEX] = SBRMI_REV;
+ msg.data_in.reg_in[RD_FLAG_INDEX] = 1;
+ ret = regmap_read(data->regmap,
+ msg.data_in.reg_in[REG_OFF_INDEX],
+ &msg.data_out.mb_out[RD_WR_DATA_INDEX]);
+ if (ret < 0)
+ return ret;
+
+ data->rev = msg.data_out.reg_out[RD_WR_DATA_INDEX];
+ return 0;
+}
+
+/* Read CPUID function protocol */
+static int rmi_cpuid_read(struct sbrmi_data *data,
+ struct apml_message *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->data_in.reg_in[THREAD_LOW_INDEX] |
+ msg->data_in.reg_in[THREAD_HI_INDEX] << 8;
+
+ /* 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->data_in.mb_in[RD_WR_DATA_INDEX],
+ msg->data_in.reg_in[EXT_FUNC_INDEX]);
+
+ ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
+ &input, CPUID_WR_REG_LEN);
+ if (ret < 0)
+ goto exit_unlock;
+
+ ret = sbrmi_wait_status(data, &hw_status, HW_ALERT_MASK);
+ if (ret < 0)
+ 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->data_out.cpu_msr_out = output.value;
+exit_unlock:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
int rmi_mailbox_xfer(struct sbrmi_data *data,
struct apml_message *msg)
{
@@ -147,6 +314,9 @@ static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
/* Mailbox protocol */
ret = rmi_mailbox_xfer(data, &msg);
break;
+ case APML_CPUID:
+ ret = rmi_cpuid_read(data, &msg);
+ break;
default:
pr_err("Command:0x%x not recognized\n", msg.cmd);
break;
@@ -15,7 +15,8 @@
/* SB-RMI registers */
enum sbrmi_reg {
- SBRMI_CTRL = 0x01,
+ SBRMI_REV = 0x00,
+ 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_message *msg);
@@ -7,10 +7,15 @@
#include <linux/types.h>
+/* command ID to identify CPUID protocol */
+#define APML_CPUID 0x1000
/* These are byte indexes into data_in and data_out arrays */
#define RD_WR_DATA_INDEX 0
#define REG_OFF_INDEX 0
#define REG_VAL_INDEX 4
+#define THREAD_LOW_INDEX 4
+#define THREAD_HI_INDEX 5
+#define EXT_FUNC_INDEX 6
#define RD_FLAG_INDEX 7
#define MB_DATA_SIZE 4
@@ -18,27 +23,37 @@
struct apml_message {
/* message ids:
* Mailbox Messages: 0x0 ... 0x999
+ * APML_CPUID: 0x1000
*/
__u32 cmd;
/*
* 8 bit data for reg read,
* 32 bit data in case of mailbox,
+ * up to 64 bit in case of cpuid
*/
union {
+ __u64 cpu_msr_out;
__u32 mb_out[2];
__u8 reg_out[8];
} data_out;
/*
* [0]...[3] mailbox 32bit input
+ * cpuid,
+ * [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
* [7] read/write functionality
*/
union {
+ __u64 cpu_msr_in;
__u32 mb_in[2];
__u8 reg_in[8];
} data_in;
/*
+ * Status code is returned in case of CPUID access
* Error code is returned in case of soft mailbox
*/
__u32 fw_ret_code;