@@ -29,13 +29,17 @@
/* MSR */
#define MSR_RD_REG_LEN 0xa
#define MSR_WR_REG_LEN 0x8
+#define MSR_WR_REG_LEN_v21 0x9
#define MSR_RD_DATA_LEN 0x8
#define MSR_WR_DATA_LEN 0x7
+#define MSR_WR_DATA_LEN_v21 0x8
/* CPUID */
#define CPUID_RD_DATA_LEN 0x8
#define CPUID_WR_DATA_LEN 0x8
+#define CPUID_WR_DATA_LEN_v21 0x9
#define CPUID_RD_REG_LEN 0xa
#define CPUID_WR_REG_LEN 0x9
+#define CPUID_WR_REG_LEN_v21 0xa
/* CPUID MSR Command Ids */
#define CPUID_MCA_CMD 0x73
@@ -55,6 +59,19 @@ struct cpu_msr_indata {
u8 ext; /* extended function */
} __packed;
+/* input for bulk write to v21 of CPUID and MSR protocol */
+struct cpu_msr_indata_v21 {
+ u8 wr_len; /* const value */
+ u8 rd_len; /* const value */
+ u8 proto_cmd; /* const value */
+ u16 thread; /* thread number */
+ union {
+ u8 reg_offset[4]; /* input value */
+ u32 value;
+ };
+ u8 ext; /* extended function */
+} __packed;
+
/* output for bulk read from CPUID and MSR protocol */
struct cpu_msr_outdata {
u8 num_bytes; /* number of bytes return */
@@ -65,16 +82,16 @@ struct cpu_msr_outdata {
};
} __packed;
-#define prepare_mca_msr_input_message(input, thread_id, data_in) \
+#define prepare_mca_msr_input_message(input, thread_id, data_in, wr_data_len) \
input.rd_len = MSR_RD_DATA_LEN, \
- input.wr_len = MSR_WR_DATA_LEN, \
+ input.wr_len = wr_data_len, \
input.proto_cmd = RD_MCA_CMD, \
input.thread = thread_id << 1, \
input.value = data_in
-#define prepare_cpuid_input_message(input, thread_id, func, ext_func) \
+#define prepare_cpuid_input_message(input, thread_id, func, ext_func, wr_data_len) \
input.rd_len = CPUID_RD_DATA_LEN, \
- input.wr_len = CPUID_WR_DATA_LEN, \
+ input.wr_len = wr_data_len, \
input.proto_cmd = RD_CPUID_CMD, \
input.thread = thread_id << 1, \
input.value = func, \
@@ -131,25 +148,13 @@ static int sbrmi_wait_status(struct sbrmi_data *data,
}
/* MCA MSR protocol */
-static int rmi_mca_msr_read(struct sbrmi_data *data,
- struct apml_message *msg)
+static int msr_datain_v20(struct sbrmi_data *data,
+ struct apml_message *msg)
{
- struct cpu_msr_outdata output = {0};
struct cpu_msr_indata input = {0};
int ret, val = 0;
- int hw_status;
u16 thread;
- /* cache the rev value to identify if protocol is supported or not */
- if (!data->rev) {
- ret = sbrmi_get_rev(data);
- if (ret < 0)
- return ret;
- }
- /* MCA MSR protocol for REV 0x10 is not supported*/
- if (data->rev == 0x10)
- return -EOPNOTSUPP;
-
thread = msg->data_in.reg_in[THREAD_LOW_INDEX] |
msg->data_in.reg_in[THREAD_HI_INDEX] << 8;
@@ -160,15 +165,71 @@ static int rmi_mca_msr_read(struct sbrmi_data *data,
}
ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
if (ret < 0)
- goto exit_unlock;
+ return ret;
prepare_mca_msr_input_message(input, thread,
- msg->data_in.mb_in[RD_WR_DATA_INDEX]);
+ msg->data_in.mb_in[RD_WR_DATA_INDEX],
+ MSR_WR_DATA_LEN);
ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
&input, MSR_WR_REG_LEN);
- if (ret < 0)
- goto exit_unlock;
+ return ret;
+}
+
+static int msr_datain_v21(struct sbrmi_data *data,
+ struct apml_message *msg)
+{
+ struct cpu_msr_indata_v21 input = {0};
+ int ret;
+ u16 thread;
+
+ thread = msg->data_in.reg_in[THREAD_LOW_INDEX] |
+ msg->data_in.reg_in[THREAD_HI_INDEX] << 8;
+
+ prepare_mca_msr_input_message(input, thread,
+ msg->data_in.mb_in[RD_WR_DATA_INDEX],
+ MSR_WR_DATA_LEN_v21);
+
+ ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
+ &input, MSR_WR_REG_LEN_v21);
+ return ret;
+}
+
+static int rmi_mca_msr_read(struct sbrmi_data *data,
+ struct apml_message *msg)
+{
+ struct cpu_msr_outdata output = {0};
+ int ret;
+ int hw_status;
+
+ if (!data->regmap)
+ return -ENODEV;
+
+ /* cache the rev value to identify if protocol is supported or not */
+ if (!data->rev) {
+ ret = sbrmi_get_rev(data);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (data->rev) {
+ /* MCA MSR protocol for REV 0x10 is not supported*/
+ case 0x10:
+ return -EOPNOTSUPP;
+ case 0x20:
+ ret = msr_datain_v20(data, msg);
+ if (ret < 0)
+ goto exit_unlock;
+
+ break;
+ case 0x21:
+ ret = msr_datain_v21(data, msg);
+ if (ret < 0)
+ goto exit_unlock;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
ret = sbrmi_wait_status(data, &hw_status, HW_ALERT_MASK);
if (ret < 0)
@@ -200,25 +261,13 @@ static int rmi_mca_msr_read(struct sbrmi_data *data,
}
/* Read CPUID function protocol */
-static int rmi_cpuid_read(struct sbrmi_data *data,
- struct apml_message *msg)
+static int cpuid_datain_v20(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;
+ int ret, val = 0;
u16 thread;
- /* cache the rev value to identify if protocol is supported or not */
- if (!data->rev) {
- ret = sbrmi_get_rev(data);
- if (ret < 0)
- return ret;
- }
- /* CPUID protocol for REV 0x10 is not supported*/
- if (data->rev == 0x10)
- return -EOPNOTSUPP;
-
thread = msg->data_in.reg_in[THREAD_LOW_INDEX] |
msg->data_in.reg_in[THREAD_HI_INDEX] << 8;
@@ -229,16 +278,71 @@ static int rmi_cpuid_read(struct sbrmi_data *data,
}
ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
if (ret < 0)
- goto exit_unlock;
-
+ return ret;
prepare_cpuid_input_message(input, thread,
msg->data_in.mb_in[RD_WR_DATA_INDEX],
- msg->data_in.reg_in[EXT_FUNC_INDEX]);
+ msg->data_in.reg_in[EXT_FUNC_INDEX],
+ CPUID_WR_DATA_LEN);
ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
&input, CPUID_WR_REG_LEN);
- if (ret < 0)
- goto exit_unlock;
+ return ret;
+}
+
+static int cpuid_datain_v21(struct sbrmi_data *data,
+ struct apml_message *msg)
+{
+ struct cpu_msr_indata_v21 input = {0};
+ int ret;
+ u16 thread;
+
+ thread = msg->data_in.reg_in[THREAD_LOW_INDEX] |
+ msg->data_in.reg_in[THREAD_HI_INDEX] << 8;
+
+ prepare_cpuid_input_message(input, thread,
+ msg->data_in.mb_in[RD_WR_DATA_INDEX],
+ msg->data_in.reg_in[EXT_FUNC_INDEX],
+ CPUID_WR_DATA_LEN_v21);
+
+ ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
+ &input, CPUID_WR_REG_LEN_v21);
+ return ret;
+}
+
+/* CPUID protocol */
+static int rmi_cpuid_read(struct sbrmi_data *data,
+ struct apml_message *msg)
+{
+ struct cpu_msr_outdata output = {0};
+ int ret, hw_status;
+
+ if (!data->regmap)
+ return -ENODEV;
+
+ /* cache the rev value to identify if protocol is supported or not */
+ if (!data->rev) {
+ ret = sbrmi_get_rev(data);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (data->rev) {
+ /* CPUID protocol for REV 0x10 is not supported*/
+ case 0x10:
+ return -EOPNOTSUPP;
+ case 0x20:
+ ret = cpuid_datain_v20(data, msg);
+ if (ret < 0)
+ goto exit_unlock;
+ break;
+ case 0x21:
+ ret = cpuid_datain_v21(data, msg);
+ if (ret < 0)
+ goto exit_unlock;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
ret = sbrmi_wait_status(data, &hw_status, HW_ALERT_MASK);
if (ret < 0)
@@ -412,11 +516,11 @@ static int rmi_xfer(struct sbrmi_data *data,
/* REG R/W */
if (msg->data_in.reg_in[RD_FLAG_INDEX])
ret = regmap_read(data->regmap,
- msg->data_in.reg_in[REG_OFF_INDEX],
+ msg->data_in.mb_in[REG_OFF_INDEX],
&msg->data_out.mb_out[RD_WR_DATA_INDEX]);
else
ret = regmap_write(data->regmap,
- msg->data_in.reg_in[REG_OFF_INDEX],
+ msg->data_in.mb_in[REG_OFF_INDEX],
msg->data_in.reg_in[REG_VAL_INDEX]);
break;
default:
@@ -466,7 +570,6 @@ static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
if (copy_struct_from_user(&msg, sizeof(msg), arguser,
sizeof(struct apml_message)))
return ret;
-
/* Is this a read/monitor/get request */
if (msg.data_in.reg_in[RD_FLAG_INDEX])
read = true;
@@ -492,18 +595,18 @@ int create_misc_rmi_device(struct sbrmi_data *data,
{
int ret;
- 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.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;
+ GFP_KERNEL,
+ "sbrmi-%x",
+ data->dev_static_addr);
+ data->sbrmi_misc_dev.mode = 0600;
ret = misc_register(&data->sbrmi_misc_dev);
if (ret)
@@ -19,6 +19,82 @@
#define MAX_WAIT_TIME_SEC (3)
+/* Try 2 byte address size before switching to 1 byte */
+#define MAX_RETRY 5
+
+/*
+ * There are processor with RMI address size as 2 byte and others with 1 byte,
+ * with no clear detection mechanism in place.
+ * Execute 2 bytes access first, and then fall back to 1 byte.
+ * Sending 2 bytes first is safer as sending 1 byte address size on
+ * Turin(2 bytes) can cause unrecoverable error and may requries to reboot
+ * the system.
+ */
+static int configure_regmap(struct i2c_client *client, struct sbrmi_data *data)
+{
+ struct regmap_config sbrmi_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+ struct regmap_config sbrmi_i2c_regmap_config_2_bytes = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ };
+ u32 i;
+ int ret, rev;
+
+ if (!client || !data)
+ return -ENODEV;
+
+ /* Checking RMI register address size as 2 bytes */
+ data->regmap = devm_regmap_init_i2c(client,
+ &sbrmi_i2c_regmap_config_2_bytes);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ for (i = 0; i < MAX_RETRY; i++) {
+ ret = regmap_read(data->regmap, SBRMI_REV, &rev);
+ if (ret < 0) {
+ usleep_range(10000, 20000);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * Fall back to register address size as 1 byte
+ * Check for revision as 0x21 to not break backward compatbility
+ * for few Family:0x19 processors
+ */
+ if (ret || rev != 0x21) {
+ data->regmap = devm_regmap_init_i2c(client,
+ &sbrmi_i2c_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ ret = regmap_read(data->regmap, SBRMI_REV, &rev);
+ if (ret < 0)
+ return ret;
+ }
+ data->rev = rev;
+
+ /*
+ * For some Turin platforms, first 1 byte transaction can be successful,
+ * verify if revision is 0x21, if yes, switch to 2 byte address size
+ */
+ if (data->rev == 0x21)
+ data->regmap = devm_regmap_init_i2c(client,
+ &sbrmi_i2c_regmap_config_2_bytes);
+ else
+ data->regmap = devm_regmap_init_i2c(client,
+ &sbrmi_i2c_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+ return ret;
+}
+
static int sbrmi_enable_alert(struct sbrmi_data *data)
{
int ctrl, ret;
@@ -58,24 +134,20 @@ static int sbrmi_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct sbrmi_data *data;
- struct regmap_config sbrmi_i2c_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- };
int ret;
data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
+ ret = configure_regmap(client, data);
+ if (ret)
+ return -EPROBE_DEFER;
+
atomic_set(&data->in_progress, 0);
atomic_set(&data->no_new_trans, 0);
mutex_init(&data->lock);
- data->regmap = devm_regmap_init_i2c(client, &sbrmi_i2c_regmap_config);
- if (IS_ERR(data->regmap))
- return PTR_ERR(data->regmap);
-
/* Enable alert for SB-RMI sequence */
ret = sbrmi_enable_alert(data);
if (ret < 0)
@@ -91,9 +163,9 @@ static int sbrmi_i2c_probe(struct i2c_client *client)
dev_set_drvdata(dev, (void *)data);
data->pdev = platform_device_register_data(dev, "sbrmi-hwmon",
- PLATFORM_DEVID_NONE,
- data,
- sizeof(struct sbrmi_data));
+ PLATFORM_DEVID_NONE,
+ data,
+ sizeof(struct sbrmi_data));
if (IS_ERR(data->pdev)) {
pr_err("unable to register platform device for sbrmi-hwmon\n");
return PTR_ERR(data->pdev);