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_WR_DATA_LEN_EXT 0x9
32 #define CPUID_RD_REG_LEN 0xa
33 #define CPUID_WR_REG_LEN 0x9
34 #define CPUID_WR_REG_LEN_EXT 0xa
35 /* MSR */
36 #define MSR_RD_REG_LEN 0xa
37 #define MSR_WR_REG_LEN 0x8
38 #define MSR_WR_REG_LEN_EXT 0x9
39 #define MSR_RD_DATA_LEN 0x8
40 #define MSR_WR_DATA_LEN 0x7
41 #define MSR_WR_DATA_LEN_EXT 0x8
42
43 /* CPUID MSR Command Ids */
44 #define CPUID_MCA_CMD 0x73
45 #define RD_CPUID_CMD 0x91
46 #define RD_MCA_CMD 0x86
47
48 /* CPUID MCAMSR mask & index */
49 #define CPUID_MCA_THRD_INDEX 32
50 #define CPUID_MCA_FUNC_MASK GENMASK(31, 0)
51 #define CPUID_EXT_FUNC_INDEX 48
52
53 /* input for bulk write to CPUID protocol */
54 struct cpu_msr_indata {
55 u8 wr_len; /* const value */
56 u8 rd_len; /* const value */
57 u8 proto_cmd; /* const value */
58 u8 thread; /* thread number */
59 union {
60 u8 reg_offset[4]; /* input value */
61 u32 value;
62 } __packed;
63 u8 ext; /* extended function */
64 };
65
66 /* input for bulk write to CPUID protocol for REV 0x21 */
67 struct cpu_msr_indata_ext {
68 u8 wr_len; /* const value */
69 u8 rd_len; /* const value */
70 u8 proto_cmd; /* const value */
71 u8 thread_lo; /* thread number low */
72 u8 thread_hi; /* thread number high */
73 union {
74 u8 reg_offset[4]; /* input value */
75 u32 value;
76 } __packed;
77 u8 ext; /* extended function */
78 };
79
80 /* output for bulk read from CPUID protocol */
81 struct cpu_msr_outdata {
82 u8 num_bytes; /* number of bytes return */
83 u8 status; /* Protocol status code */
84 union {
85 u64 value;
86 u8 reg_data[8];
87 } __packed;
88 };
89
prepare_cpuid_input_message(struct cpu_msr_indata * input,u8 thread_id,u32 func,u8 ext_func)90 static inline void prepare_cpuid_input_message(struct cpu_msr_indata *input,
91 u8 thread_id, u32 func,
92 u8 ext_func)
93 {
94 input->rd_len = CPUID_RD_DATA_LEN;
95 input->wr_len = CPUID_WR_DATA_LEN;
96 input->proto_cmd = RD_CPUID_CMD;
97 input->thread = thread_id << 1;
98 input->value = func;
99 input->ext = ext_func;
100 }
101
prepare_cpuid_input_message_ext(struct cpu_msr_indata_ext * input,u16 thread_id,u32 func,u8 ext_func)102 static inline void prepare_cpuid_input_message_ext(struct cpu_msr_indata_ext *input,
103 u16 thread_id, u32 func,
104 u8 ext_func)
105 {
106 input->rd_len = CPUID_RD_DATA_LEN;
107 input->wr_len = CPUID_WR_DATA_LEN_EXT;
108 input->proto_cmd = RD_CPUID_CMD;
109 input->thread_lo = (thread_id & 0xFF) << 1;
110 input->thread_hi = thread_id >> 8;
111 input->value = func;
112 input->ext = ext_func;
113 }
114
prepare_mca_msr_input_message(struct cpu_msr_indata * input,u8 thread_id,u32 data_in)115 static inline void prepare_mca_msr_input_message(struct cpu_msr_indata *input,
116 u8 thread_id, u32 data_in)
117 {
118 input->rd_len = MSR_RD_DATA_LEN;
119 input->wr_len = MSR_WR_DATA_LEN;
120 input->proto_cmd = RD_MCA_CMD;
121 input->thread = thread_id << 1;
122 input->value = data_in;
123 }
124
prepare_mca_msr_input_message_ext(struct cpu_msr_indata_ext * input,u16 thread_id,u32 data_in)125 static inline void prepare_mca_msr_input_message_ext(struct cpu_msr_indata_ext *input,
126 u16 thread_id, u32 data_in)
127 {
128 input->rd_len = MSR_RD_DATA_LEN;
129 input->wr_len = MSR_WR_DATA_LEN_EXT;
130 input->proto_cmd = RD_MCA_CMD;
131 input->thread_lo = (thread_id & 0xFF) << 1;
132 input->thread_hi = thread_id >> 8;
133 input->value = data_in;
134 }
135
sbrmi_get_rev(struct sbrmi_data * data)136 static int sbrmi_get_rev(struct sbrmi_data *data)
137 {
138 unsigned int rev;
139 u16 offset = SBRMI_REV;
140 int ret;
141
142 ret = regmap_read(data->regmap, offset, &rev);
143 if (ret < 0)
144 return ret;
145
146 data->rev = rev;
147 return 0;
148 }
149
rmi_cpuid_input(struct sbrmi_data * data,struct apml_cpuid_msg * msg,u16 thread)150 static int rmi_cpuid_input(struct sbrmi_data *data, struct apml_cpuid_msg *msg,
151 u16 thread)
152 {
153 struct cpu_msr_indata input = {0};
154 int val = 0, ret;
155
156 /* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
157 if (thread > 127) {
158 thread -= 128;
159 val = 1;
160 }
161
162 ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
163 if (ret < 0)
164 return ret;
165
166 prepare_cpuid_input_message(&input, thread,
167 msg->cpu_in_out & CPUID_MCA_FUNC_MASK,
168 msg->cpu_in_out >> CPUID_EXT_FUNC_INDEX);
169
170 return regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
171 &input, CPUID_WR_REG_LEN);
172 }
173
rmi_cpuid_input_ext(struct sbrmi_data * data,struct apml_cpuid_msg * msg,u16 thread)174 static int rmi_cpuid_input_ext(struct sbrmi_data *data, struct apml_cpuid_msg *msg,
175 u16 thread)
176 {
177 struct cpu_msr_indata_ext input = {0};
178
179 prepare_cpuid_input_message_ext(&input, thread,
180 msg->cpu_in_out & CPUID_MCA_FUNC_MASK,
181 msg->cpu_in_out >> CPUID_EXT_FUNC_INDEX);
182
183 return regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
184 &input, CPUID_WR_REG_LEN_EXT);
185 }
186
187 /* Read CPUID function protocol */
rmi_cpuid_read(struct sbrmi_data * data,struct apml_cpuid_msg * msg)188 static int rmi_cpuid_read(struct sbrmi_data *data,
189 struct apml_cpuid_msg *msg)
190 {
191 struct cpu_msr_outdata output = {0};
192 int ret, hw_status;
193 u16 thread;
194
195 mutex_lock(&data->lock);
196 /* cache the rev value to identify if protocol is supported or not */
197 if (!data->rev) {
198 ret = sbrmi_get_rev(data);
199 if (ret < 0)
200 goto exit_unlock;
201 }
202
203 /* Extract thread from the input msg structure */
204 thread = msg->cpu_in_out >> CPUID_MCA_THRD_INDEX;
205
206 switch (data->rev) {
207 case 0x10:
208 /* CPUID protocol for REV 0x10 is not supported*/
209 ret = -EOPNOTSUPP;
210 goto exit_unlock;
211 case 0x20:
212 ret = rmi_cpuid_input(data, msg, thread);
213 if (ret)
214 goto exit_unlock;
215 break;
216 case 0x21:
217 case 0x31:
218 ret = rmi_cpuid_input_ext(data, msg, thread);
219 if (ret)
220 goto exit_unlock;
221 break;
222 default:
223 ret = -EOPNOTSUPP;
224 goto exit_unlock;
225 }
226
227 /*
228 * For RMI Rev 0x20, new h/w status bit is introduced. which is used
229 * by firmware to indicate completion of commands (0x71, 0x72, 0x73).
230 * wait for the status bit to be set by the hardware before
231 * reading the data out.
232 */
233 ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, hw_status,
234 hw_status & HW_ALERT_MASK, 500, 2000000);
235 if (ret)
236 goto exit_unlock;
237
238 ret = regmap_bulk_read(data->regmap, CPUID_MCA_CMD,
239 &output, CPUID_RD_REG_LEN);
240 if (ret < 0)
241 goto exit_unlock;
242
243 ret = regmap_write(data->regmap, SBRMI_STATUS,
244 HW_ALERT_MASK);
245 if (ret < 0)
246 goto exit_unlock;
247
248 if (output.num_bytes != CPUID_RD_REG_LEN - 1) {
249 ret = -EMSGSIZE;
250 goto exit_unlock;
251 }
252 if (output.status) {
253 ret = -EPROTOTYPE;
254 msg->fw_ret_code = output.status;
255 goto exit_unlock;
256 }
257 msg->cpu_in_out = output.value;
258 exit_unlock:
259 if (ret < 0)
260 msg->cpu_in_out = 0;
261 mutex_unlock(&data->lock);
262 return ret;
263 }
264
rmi_mcamsr_input(struct sbrmi_data * data,struct apml_mcamsr_msg * msg,u16 thread)265 static int rmi_mcamsr_input(struct sbrmi_data *data, struct apml_mcamsr_msg *msg,
266 u16 thread)
267 {
268 struct cpu_msr_indata input = {0};
269 int val = 0, ret;
270
271 /* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
272 if (thread > 127) {
273 thread -= 128;
274 val = 1;
275 }
276
277 ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
278 if (ret < 0)
279 return ret;
280
281 prepare_mca_msr_input_message(&input, thread,
282 msg->mcamsr_in_out & CPUID_MCA_FUNC_MASK);
283
284 return regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
285 &input, MSR_WR_REG_LEN);
286 }
287
rmi_mcamsr_input_ext(struct sbrmi_data * data,struct apml_mcamsr_msg * msg,u16 thread)288 static int rmi_mcamsr_input_ext(struct sbrmi_data *data, struct apml_mcamsr_msg *msg,
289 u16 thread)
290 {
291 struct cpu_msr_indata_ext input = {0};
292
293 prepare_mca_msr_input_message_ext(&input, thread,
294 msg->mcamsr_in_out & CPUID_MCA_FUNC_MASK);
295
296 return regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
297 &input, MSR_WR_REG_LEN_EXT);
298 }
299
300 /* MCA MSR protocol */
rmi_mca_msr_read(struct sbrmi_data * data,struct apml_mcamsr_msg * msg)301 static int rmi_mca_msr_read(struct sbrmi_data *data,
302 struct apml_mcamsr_msg *msg)
303 {
304 struct cpu_msr_outdata output = {0};
305 int ret;
306 int hw_status;
307 u16 thread;
308
309 mutex_lock(&data->lock);
310 /* cache the rev value to identify if protocol is supported or not */
311 if (!data->rev) {
312 ret = sbrmi_get_rev(data);
313 if (ret < 0)
314 goto exit_unlock;
315 }
316
317 /* Extract thread from the input msg structure */
318 thread = msg->mcamsr_in_out >> CPUID_MCA_THRD_INDEX;
319
320 switch (data->rev) {
321 case 0x10:
322 /* MCAMSR protocol for REV 0x10 is not supported*/
323 ret = -EOPNOTSUPP;
324 goto exit_unlock;
325 case 0x20:
326 ret = rmi_mcamsr_input(data, msg, thread);
327 if (ret)
328 goto exit_unlock;
329 break;
330 case 0x21:
331 case 0x31:
332 ret = rmi_mcamsr_input_ext(data, msg, thread);
333 if (ret)
334 goto exit_unlock;
335 break;
336 default:
337 ret = -EOPNOTSUPP;
338 goto exit_unlock;
339 }
340
341 /*
342 * For RMI Rev 0x20, new h/w status bit is introduced. which is used
343 * by firmware to indicate completion of commands (0x71, 0x72, 0x73).
344 * wait for the status bit to be set by the hardware before
345 * reading the data out.
346 */
347 ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, hw_status,
348 hw_status & HW_ALERT_MASK, 500, 2000000);
349 if (ret)
350 goto exit_unlock;
351
352 ret = regmap_bulk_read(data->regmap, CPUID_MCA_CMD,
353 &output, MSR_RD_REG_LEN);
354 if (ret < 0)
355 goto exit_unlock;
356
357 ret = regmap_write(data->regmap, SBRMI_STATUS,
358 HW_ALERT_MASK);
359 if (ret < 0)
360 goto exit_unlock;
361
362 if (output.num_bytes != MSR_RD_REG_LEN - 1) {
363 ret = -EMSGSIZE;
364 goto exit_unlock;
365 }
366 if (output.status) {
367 ret = -EPROTOTYPE;
368 msg->fw_ret_code = output.status;
369 goto exit_unlock;
370 }
371 msg->mcamsr_in_out = output.value;
372
373 exit_unlock:
374 mutex_unlock(&data->lock);
375 return ret;
376 }
377
rmi_mailbox_xfer(struct sbrmi_data * data,struct apml_mbox_msg * msg)378 int rmi_mailbox_xfer(struct sbrmi_data *data,
379 struct apml_mbox_msg *msg)
380 {
381 unsigned int bytes, ec;
382 int i, ret;
383 int sw_status;
384 u8 byte;
385
386 mutex_lock(&data->lock);
387
388 msg->fw_ret_code = 0;
389
390 /* Indicate firmware a command is to be serviced */
391 ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD);
392 if (ret < 0)
393 goto exit_unlock;
394
395 /* Write the command to SBRMI::InBndMsg_inst0 */
396 ret = regmap_write(data->regmap, SBRMI_INBNDMSG0, msg->cmd);
397 if (ret < 0)
398 goto exit_unlock;
399
400 /*
401 * For both read and write the initiator (BMC) writes
402 * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
403 * SBRMI_x3C(MSB):SBRMI_x39(LSB)
404 */
405 for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
406 byte = (msg->mb_in_out >> i * 8) & 0xff;
407 ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte);
408 if (ret < 0)
409 goto exit_unlock;
410 }
411
412 /*
413 * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
414 * perform the requested read or write command
415 */
416 ret = regmap_write(data->regmap, SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
417 if (ret < 0)
418 goto exit_unlock;
419
420 /*
421 * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate
422 * an ALERT (if enabled) to initiator (BMC) to indicate completion
423 * of the requested command
424 */
425 ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, sw_status,
426 sw_status & SW_ALERT_MASK, 500, 2000000);
427 if (ret)
428 goto exit_unlock;
429
430 ret = regmap_read(data->regmap, SBRMI_OUTBNDMSG7, &ec);
431 if (ret || ec)
432 goto exit_clear_alert;
433
434 /* Clear the input value before updating the output data */
435 msg->mb_in_out = 0;
436
437 /*
438 * For a read operation, the initiator (BMC) reads the firmware
439 * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
440 * {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
441 */
442 for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
443 ret = regmap_read(data->regmap,
444 SBRMI_OUTBNDMSG1 + i, &bytes);
445 if (ret < 0)
446 break;
447 msg->mb_in_out |= bytes << i * 8;
448 }
449
450 exit_clear_alert:
451 /*
452 * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
453 * ALERT to initiator
454 */
455 ret = regmap_write(data->regmap, SBRMI_STATUS,
456 sw_status | SW_ALERT_MASK);
457 if (ec) {
458 ret = -EPROTOTYPE;
459 msg->fw_ret_code = ec;
460 }
461 exit_unlock:
462 mutex_unlock(&data->lock);
463 return ret;
464 }
465
apml_rmi_reg_xfer(struct sbrmi_data * data,struct apml_reg_xfer_msg __user * arg)466 static int apml_rmi_reg_xfer(struct sbrmi_data *data,
467 struct apml_reg_xfer_msg __user *arg)
468 {
469 struct apml_reg_xfer_msg msg = { 0 };
470 unsigned int data_read;
471 int ret;
472
473 /* Copy the structure from user */
474 if (copy_from_user(&msg, arg, sizeof(struct apml_reg_xfer_msg)))
475 return -EFAULT;
476
477 mutex_lock(&data->lock);
478 if (msg.rflag) {
479 ret = regmap_read(data->regmap, msg.reg_addr, &data_read);
480 if (!ret)
481 msg.data_in_out = data_read;
482 } else {
483 ret = regmap_write(data->regmap, msg.reg_addr, msg.data_in_out);
484 }
485
486 mutex_unlock(&data->lock);
487
488 if (msg.rflag && !ret)
489 if (copy_to_user(arg, &msg, sizeof(struct apml_reg_xfer_msg)))
490 return -EFAULT;
491 return ret;
492 }
493
apml_mailbox_xfer(struct sbrmi_data * data,struct apml_mbox_msg __user * arg)494 static int apml_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg __user *arg)
495 {
496 struct apml_mbox_msg msg = { 0 };
497 int ret;
498
499 /* Copy the structure from user */
500 if (copy_from_user(&msg, arg, sizeof(struct apml_mbox_msg)))
501 return -EFAULT;
502
503 /* Mailbox protocol */
504 ret = rmi_mailbox_xfer(data, &msg);
505 if (ret && ret != -EPROTOTYPE)
506 return ret;
507
508 if (copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg)))
509 return -EFAULT;
510 return ret;
511 }
512
apml_cpuid_xfer(struct sbrmi_data * data,struct apml_cpuid_msg __user * arg)513 static int apml_cpuid_xfer(struct sbrmi_data *data, struct apml_cpuid_msg __user *arg)
514 {
515 struct apml_cpuid_msg msg = { 0 };
516 int ret;
517
518 /* Copy the structure from user */
519 if (copy_from_user(&msg, arg, sizeof(struct apml_cpuid_msg)))
520 return -EFAULT;
521
522 /* CPUID Protocol */
523 ret = rmi_cpuid_read(data, &msg);
524 if (ret && ret != -EPROTOTYPE)
525 return ret;
526
527 if (copy_to_user(arg, &msg, sizeof(struct apml_cpuid_msg)))
528 return -EFAULT;
529 return ret;
530 }
531
apml_mcamsr_xfer(struct sbrmi_data * data,struct apml_mcamsr_msg __user * arg)532 static int apml_mcamsr_xfer(struct sbrmi_data *data, struct apml_mcamsr_msg __user *arg)
533 {
534 struct apml_mcamsr_msg msg = { 0 };
535 int ret;
536
537 /* Copy the structure from user */
538 if (copy_from_user(&msg, arg, sizeof(struct apml_mcamsr_msg)))
539 return -EFAULT;
540
541 /* MCAMSR Protocol */
542 ret = rmi_mca_msr_read(data, &msg);
543 if (ret && ret != -EPROTOTYPE)
544 return ret;
545
546 if (copy_to_user(arg, &msg, sizeof(struct apml_mcamsr_msg)))
547 return -EFAULT;
548 return ret;
549 }
550
sbrmi_ioctl(struct file * fp,unsigned int cmd,unsigned long arg)551 static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
552 {
553 void __user *argp = (void __user *)arg;
554 struct sbrmi_data *data;
555
556 data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
557 switch (cmd) {
558 case SBRMI_IOCTL_MBOX_CMD:
559 return apml_mailbox_xfer(data, argp);
560 case SBRMI_IOCTL_CPUID_CMD:
561 return apml_cpuid_xfer(data, argp);
562 case SBRMI_IOCTL_MCAMSR_CMD:
563 return apml_mcamsr_xfer(data, argp);
564 case SBRMI_IOCTL_REG_XFER_CMD:
565 return apml_rmi_reg_xfer(data, argp);
566 default:
567 return -ENOTTY;
568 }
569 }
570
571 static const struct file_operations sbrmi_fops = {
572 .owner = THIS_MODULE,
573 .unlocked_ioctl = sbrmi_ioctl,
574 .compat_ioctl = compat_ptr_ioctl,
575 };
576
create_misc_rmi_device(struct sbrmi_data * data,struct device * dev)577 int create_misc_rmi_device(struct sbrmi_data *data,
578 struct device *dev)
579 {
580 data->sbrmi_misc_dev.name = devm_kasprintf(dev,
581 GFP_KERNEL,
582 "sbrmi-%x",
583 data->dev_static_addr);
584 data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR;
585 data->sbrmi_misc_dev.fops = &sbrmi_fops;
586 data->sbrmi_misc_dev.parent = dev;
587 data->sbrmi_misc_dev.nodename = devm_kasprintf(dev,
588 GFP_KERNEL,
589 "sbrmi-%x",
590 data->dev_static_addr);
591 data->sbrmi_misc_dev.mode = 0600;
592
593 return misc_register(&data->sbrmi_misc_dev);
594 }
595