xref: /linux/drivers/misc/amd-sbi/rmi-core.c (revision c26f4fbd58375bd6ef74f95eb73d61762ad97c59)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * sbrmi-core.c - file defining SB-RMI protocols compliant
4  *		  AMD SoC device.
5  *
6  * Copyright (C) 2025 Advanced Micro Devices, Inc.
7  */
8 #include <linux/delay.h>
9 #include <linux/err.h>
10 #include <linux/fs.h>
11 #include <linux/i2c.h>
12 #include <linux/miscdevice.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
15 #include <linux/regmap.h>
16 #include "rmi-core.h"
17 
18 /* Mask for Status Register bit[1] */
19 #define SW_ALERT_MASK	0x2
20 /* Mask to check H/W Alert status bit */
21 #define HW_ALERT_MASK	0x80
22 
23 /* Software Interrupt for triggering */
24 #define START_CMD	0x80
25 #define TRIGGER_MAILBOX	0x01
26 
27 /* Default message lengths as per APML command protocol */
28 /* CPUID */
29 #define CPUID_RD_DATA_LEN	0x8
30 #define CPUID_WR_DATA_LEN	0x8
31 #define CPUID_RD_REG_LEN	0xa
32 #define CPUID_WR_REG_LEN	0x9
33 /* MSR */
34 #define MSR_RD_REG_LEN		0xa
35 #define MSR_WR_REG_LEN		0x8
36 #define MSR_RD_DATA_LEN		0x8
37 #define MSR_WR_DATA_LEN		0x7
38 
39 /* CPUID MSR Command Ids */
40 #define CPUID_MCA_CMD	0x73
41 #define RD_CPUID_CMD	0x91
42 #define RD_MCA_CMD	0x86
43 
44 /* CPUID MCAMSR mask & index */
45 #define CPUID_MCA_THRD_MASK	GENMASK(15, 0)
46 #define CPUID_MCA_THRD_INDEX	32
47 #define CPUID_MCA_FUNC_MASK	GENMASK(31, 0)
48 #define CPUID_EXT_FUNC_INDEX	56
49 
50 /* input for bulk write to CPUID protocol */
51 struct cpu_msr_indata {
52 	u8 wr_len;	/* const value */
53 	u8 rd_len;	/* const value */
54 	u8 proto_cmd;	/* const value */
55 	u8 thread;	/* thread number */
56 	union {
57 		u8 reg_offset[4];	/* input value */
58 		u32 value;
59 	} __packed;
60 	u8 ext; /* extended function */
61 };
62 
63 /* output for bulk read from CPUID protocol */
64 struct cpu_msr_outdata {
65 	u8 num_bytes;	/* number of bytes return */
66 	u8 status;	/* Protocol status code */
67 	union {
68 		u64 value;
69 		u8 reg_data[8];
70 	} __packed;
71 };
72 
prepare_cpuid_input_message(struct cpu_msr_indata * input,u8 thread_id,u32 func,u8 ext_func)73 static inline void prepare_cpuid_input_message(struct cpu_msr_indata *input,
74 					       u8 thread_id, u32 func,
75 					       u8 ext_func)
76 {
77 	input->rd_len		= CPUID_RD_DATA_LEN;
78 	input->wr_len		= CPUID_WR_DATA_LEN;
79 	input->proto_cmd	= RD_CPUID_CMD;
80 	input->thread		= thread_id << 1;
81 	input->value		= func;
82 	input->ext		= ext_func;
83 }
84 
prepare_mca_msr_input_message(struct cpu_msr_indata * input,u8 thread_id,u32 data_in)85 static inline void prepare_mca_msr_input_message(struct cpu_msr_indata *input,
86 						 u8 thread_id, u32 data_in)
87 {
88 	input->rd_len		= MSR_RD_DATA_LEN;
89 	input->wr_len		= MSR_WR_DATA_LEN;
90 	input->proto_cmd	= RD_MCA_CMD;
91 	input->thread		= thread_id << 1;
92 	input->value		= data_in;
93 }
94 
sbrmi_get_rev(struct sbrmi_data * data)95 static int sbrmi_get_rev(struct sbrmi_data *data)
96 {
97 	unsigned int rev;
98 	u16 offset = SBRMI_REV;
99 	int ret;
100 
101 	ret = regmap_read(data->regmap, offset, &rev);
102 	if (ret < 0)
103 		return ret;
104 
105 	data->rev = rev;
106 	return 0;
107 }
108 
109 /* Read CPUID function protocol */
rmi_cpuid_read(struct sbrmi_data * data,struct apml_cpuid_msg * msg)110 static int rmi_cpuid_read(struct sbrmi_data *data,
111 			  struct apml_cpuid_msg *msg)
112 {
113 	struct cpu_msr_indata input = {0};
114 	struct cpu_msr_outdata output = {0};
115 	int val = 0;
116 	int ret, hw_status;
117 	u16 thread;
118 
119 	mutex_lock(&data->lock);
120 	/* cache the rev value to identify if protocol is supported or not */
121 	if (!data->rev) {
122 		ret = sbrmi_get_rev(data);
123 		if (ret < 0)
124 			goto exit_unlock;
125 	}
126 	/* CPUID protocol for REV 0x10 is not supported*/
127 	if (data->rev == 0x10) {
128 		ret = -EOPNOTSUPP;
129 		goto exit_unlock;
130 	}
131 
132 	thread = msg->cpu_in_out << CPUID_MCA_THRD_INDEX & CPUID_MCA_THRD_MASK;
133 
134 	/* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
135 	if (thread > 127) {
136 		thread -= 128;
137 		val = 1;
138 	}
139 	ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
140 	if (ret < 0)
141 		goto exit_unlock;
142 
143 	prepare_cpuid_input_message(&input, thread,
144 				    msg->cpu_in_out & CPUID_MCA_FUNC_MASK,
145 				    msg->cpu_in_out >> CPUID_EXT_FUNC_INDEX);
146 
147 	ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
148 				&input, CPUID_WR_REG_LEN);
149 	if (ret < 0)
150 		goto exit_unlock;
151 
152 	/*
153 	 * For RMI Rev 0x20, new h/w status bit is introduced. which is used
154 	 * by firmware to indicate completion of commands (0x71, 0x72, 0x73).
155 	 * wait for the status bit to be set by the hardware before
156 	 * reading the data out.
157 	 */
158 	ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, hw_status,
159 				       hw_status & HW_ALERT_MASK, 500, 2000000);
160 	if (ret)
161 		goto exit_unlock;
162 
163 	ret = regmap_bulk_read(data->regmap, CPUID_MCA_CMD,
164 			       &output, CPUID_RD_REG_LEN);
165 	if (ret < 0)
166 		goto exit_unlock;
167 
168 	ret = regmap_write(data->regmap, SBRMI_STATUS,
169 			   HW_ALERT_MASK);
170 	if (ret < 0)
171 		goto exit_unlock;
172 
173 	if (output.num_bytes != CPUID_RD_REG_LEN - 1) {
174 		ret = -EMSGSIZE;
175 		goto exit_unlock;
176 	}
177 	if (output.status) {
178 		ret = -EPROTOTYPE;
179 		msg->fw_ret_code = output.status;
180 		goto exit_unlock;
181 	}
182 	msg->cpu_in_out = output.value;
183 exit_unlock:
184 	if (ret < 0)
185 		msg->cpu_in_out = 0;
186 	mutex_unlock(&data->lock);
187 	return ret;
188 }
189 
190 /* MCA MSR protocol */
rmi_mca_msr_read(struct sbrmi_data * data,struct apml_mcamsr_msg * msg)191 static int rmi_mca_msr_read(struct sbrmi_data *data,
192 			    struct apml_mcamsr_msg  *msg)
193 {
194 	struct cpu_msr_outdata output = {0};
195 	struct cpu_msr_indata input = {0};
196 	int ret, val = 0;
197 	int hw_status;
198 	u16 thread;
199 
200 	mutex_lock(&data->lock);
201 	/* cache the rev value to identify if protocol is supported or not */
202 	if (!data->rev) {
203 		ret = sbrmi_get_rev(data);
204 		if (ret < 0)
205 			goto exit_unlock;
206 	}
207 	/* MCA MSR protocol for REV 0x10 is not supported*/
208 	if (data->rev == 0x10) {
209 		ret = -EOPNOTSUPP;
210 		goto exit_unlock;
211 	}
212 
213 	thread = msg->mcamsr_in_out << CPUID_MCA_THRD_INDEX & CPUID_MCA_THRD_MASK;
214 
215 	/* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
216 	if (thread > 127) {
217 		thread -= 128;
218 		val = 1;
219 	}
220 	ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
221 	if (ret < 0)
222 		goto exit_unlock;
223 
224 	prepare_mca_msr_input_message(&input, thread,
225 				      msg->mcamsr_in_out & CPUID_MCA_FUNC_MASK);
226 
227 	ret = regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
228 				&input, MSR_WR_REG_LEN);
229 	if (ret < 0)
230 		goto exit_unlock;
231 
232 	/*
233 	 * For RMI Rev 0x20, new h/w status bit is introduced. which is used
234 	 * by firmware to indicate completion of commands (0x71, 0x72, 0x73).
235 	 * wait for the status bit to be set by the hardware before
236 	 * reading the data out.
237 	 */
238 	ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, hw_status,
239 				       hw_status & HW_ALERT_MASK, 500, 2000000);
240 	if (ret)
241 		goto exit_unlock;
242 
243 	ret = regmap_bulk_read(data->regmap, CPUID_MCA_CMD,
244 			       &output, MSR_RD_REG_LEN);
245 	if (ret < 0)
246 		goto exit_unlock;
247 
248 	ret = regmap_write(data->regmap, SBRMI_STATUS,
249 			   HW_ALERT_MASK);
250 	if (ret < 0)
251 		goto exit_unlock;
252 
253 	if (output.num_bytes != MSR_RD_REG_LEN - 1) {
254 		ret = -EMSGSIZE;
255 		goto exit_unlock;
256 	}
257 	if (output.status) {
258 		ret = -EPROTOTYPE;
259 		msg->fw_ret_code = output.status;
260 		goto exit_unlock;
261 	}
262 	msg->mcamsr_in_out = output.value;
263 
264 exit_unlock:
265 	mutex_unlock(&data->lock);
266 	return ret;
267 }
268 
rmi_mailbox_xfer(struct sbrmi_data * data,struct apml_mbox_msg * msg)269 int rmi_mailbox_xfer(struct sbrmi_data *data,
270 		     struct apml_mbox_msg *msg)
271 {
272 	unsigned int bytes, ec;
273 	int i, ret;
274 	int sw_status;
275 	u8 byte;
276 
277 	mutex_lock(&data->lock);
278 
279 	msg->fw_ret_code = 0;
280 
281 	/* Indicate firmware a command is to be serviced */
282 	ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD);
283 	if (ret < 0)
284 		goto exit_unlock;
285 
286 	/* Write the command to SBRMI::InBndMsg_inst0 */
287 	ret = regmap_write(data->regmap, SBRMI_INBNDMSG0, msg->cmd);
288 	if (ret < 0)
289 		goto exit_unlock;
290 
291 	/*
292 	 * For both read and write the initiator (BMC) writes
293 	 * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
294 	 * SBRMI_x3C(MSB):SBRMI_x39(LSB)
295 	 */
296 	for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
297 		byte = (msg->mb_in_out >> i * 8) & 0xff;
298 		ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte);
299 		if (ret < 0)
300 			goto exit_unlock;
301 	}
302 
303 	/*
304 	 * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
305 	 * perform the requested read or write command
306 	 */
307 	ret = regmap_write(data->regmap, SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
308 	if (ret < 0)
309 		goto exit_unlock;
310 
311 	/*
312 	 * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate
313 	 * an ALERT (if enabled) to initiator (BMC) to indicate completion
314 	 * of the requested command
315 	 */
316 	ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, sw_status,
317 				       sw_status & SW_ALERT_MASK, 500, 2000000);
318 	if (ret)
319 		goto exit_unlock;
320 
321 	ret = regmap_read(data->regmap, SBRMI_OUTBNDMSG7, &ec);
322 	if (ret || ec)
323 		goto exit_clear_alert;
324 	/*
325 	 * For a read operation, the initiator (BMC) reads the firmware
326 	 * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
327 	 * {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
328 	 */
329 	for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
330 		ret = regmap_read(data->regmap,
331 				  SBRMI_OUTBNDMSG1 + i, &bytes);
332 		if (ret < 0)
333 			break;
334 		msg->mb_in_out |= bytes << i * 8;
335 	}
336 
337 exit_clear_alert:
338 	/*
339 	 * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
340 	 * ALERT to initiator
341 	 */
342 	ret = regmap_write(data->regmap, SBRMI_STATUS,
343 			   sw_status | SW_ALERT_MASK);
344 	if (ec) {
345 		ret = -EPROTOTYPE;
346 		msg->fw_ret_code = ec;
347 	}
348 exit_unlock:
349 	mutex_unlock(&data->lock);
350 	return ret;
351 }
352 
apml_rmi_reg_xfer(struct sbrmi_data * data,struct apml_reg_xfer_msg __user * arg)353 static int apml_rmi_reg_xfer(struct sbrmi_data *data,
354 			     struct apml_reg_xfer_msg __user *arg)
355 {
356 	struct apml_reg_xfer_msg msg = { 0 };
357 	unsigned int data_read;
358 	int ret;
359 
360 	/* Copy the structure from user */
361 	if (copy_from_user(&msg, arg, sizeof(struct apml_reg_xfer_msg)))
362 		return -EFAULT;
363 
364 	mutex_lock(&data->lock);
365 	if (msg.rflag) {
366 		ret = regmap_read(data->regmap, msg.reg_addr, &data_read);
367 		if (!ret)
368 			msg.data_in_out = data_read;
369 	} else {
370 		ret = regmap_write(data->regmap, msg.reg_addr, msg.data_in_out);
371 	}
372 
373 	mutex_unlock(&data->lock);
374 
375 	if (msg.rflag && !ret)
376 		return copy_to_user(arg, &msg, sizeof(struct apml_reg_xfer_msg));
377 	return ret;
378 }
379 
apml_mailbox_xfer(struct sbrmi_data * data,struct apml_mbox_msg __user * arg)380 static int apml_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg __user *arg)
381 {
382 	struct apml_mbox_msg msg = { 0 };
383 	int ret;
384 
385 	/* Copy the structure from user */
386 	if (copy_from_user(&msg, arg, sizeof(struct apml_mbox_msg)))
387 		return -EFAULT;
388 
389 	/* Mailbox protocol */
390 	ret = rmi_mailbox_xfer(data, &msg);
391 	if (ret && ret != -EPROTOTYPE)
392 		return ret;
393 
394 	return copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg));
395 }
396 
apml_cpuid_xfer(struct sbrmi_data * data,struct apml_cpuid_msg __user * arg)397 static int apml_cpuid_xfer(struct sbrmi_data *data, struct apml_cpuid_msg __user *arg)
398 {
399 	struct apml_cpuid_msg msg = { 0 };
400 	int ret;
401 
402 	/* Copy the structure from user */
403 	if (copy_from_user(&msg, arg, sizeof(struct apml_cpuid_msg)))
404 		return -EFAULT;
405 
406 	/* CPUID Protocol */
407 	ret = rmi_cpuid_read(data, &msg);
408 	if (ret && ret != -EPROTOTYPE)
409 		return ret;
410 
411 	return copy_to_user(arg, &msg, sizeof(struct apml_cpuid_msg));
412 }
413 
apml_mcamsr_xfer(struct sbrmi_data * data,struct apml_mcamsr_msg __user * arg)414 static int apml_mcamsr_xfer(struct sbrmi_data *data, struct apml_mcamsr_msg __user *arg)
415 {
416 	struct apml_mcamsr_msg msg = { 0 };
417 	int ret;
418 
419 	/* Copy the structure from user */
420 	if (copy_from_user(&msg, arg, sizeof(struct apml_mcamsr_msg)))
421 		return -EFAULT;
422 
423 	/* MCAMSR Protocol */
424 	ret = rmi_mca_msr_read(data, &msg);
425 	if (ret && ret != -EPROTOTYPE)
426 		return ret;
427 
428 	return copy_to_user(arg, &msg, sizeof(struct apml_mcamsr_msg));
429 }
430 
sbrmi_ioctl(struct file * fp,unsigned int cmd,unsigned long arg)431 static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
432 {
433 	void __user *argp = (void __user *)arg;
434 	struct sbrmi_data *data;
435 
436 	data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
437 	switch (cmd) {
438 	case SBRMI_IOCTL_MBOX_CMD:
439 		return apml_mailbox_xfer(data, argp);
440 	case SBRMI_IOCTL_CPUID_CMD:
441 		return apml_cpuid_xfer(data, argp);
442 	case SBRMI_IOCTL_MCAMSR_CMD:
443 		return apml_mcamsr_xfer(data, argp);
444 	case SBRMI_IOCTL_REG_XFER_CMD:
445 		return apml_rmi_reg_xfer(data, argp);
446 	default:
447 		return -ENOTTY;
448 	}
449 }
450 
451 static const struct file_operations sbrmi_fops = {
452 	.owner		= THIS_MODULE,
453 	.unlocked_ioctl	= sbrmi_ioctl,
454 	.compat_ioctl	= compat_ptr_ioctl,
455 };
456 
create_misc_rmi_device(struct sbrmi_data * data,struct device * dev)457 int create_misc_rmi_device(struct sbrmi_data *data,
458 			   struct device *dev)
459 {
460 	data->sbrmi_misc_dev.name	= devm_kasprintf(dev,
461 							 GFP_KERNEL,
462 							 "sbrmi-%x",
463 							 data->dev_static_addr);
464 	data->sbrmi_misc_dev.minor	= MISC_DYNAMIC_MINOR;
465 	data->sbrmi_misc_dev.fops	= &sbrmi_fops;
466 	data->sbrmi_misc_dev.parent	= dev;
467 	data->sbrmi_misc_dev.nodename	= devm_kasprintf(dev,
468 							 GFP_KERNEL,
469 							 "sbrmi-%x",
470 							 data->dev_static_addr);
471 	data->sbrmi_misc_dev.mode	= 0600;
472 
473 	return misc_register(&data->sbrmi_misc_dev);
474 }
475