File Index
Files and directories
Sign in
FILE

rmi-core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/misc/amd-sbi/rmi-core.c C
  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	56
 52
 53/* input for bulk write to CPUID protocol */
 54struct 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 */
 67struct 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 */
 81struct 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
 90static 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
102static 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
115static 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
125static 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
136static 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
150static 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
174static 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 */
188static 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		ret = rmi_cpuid_input_ext(data, msg, thread);
218		if (ret)
219			goto exit_unlock;
220		break;
221	default:
222		ret = -EOPNOTSUPP;
223		goto exit_unlock;
224	}
225
226	/*
227	 * For RMI Rev 0x20, new h/w status bit is introduced. which is used
228	 * by firmware to indicate completion of commands (0x71, 0x72, 0x73).
229	 * wait for the status bit to be set by the hardware before
230	 * reading the data out.
231	 */
232	ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, hw_status,
233				       hw_status & HW_ALERT_MASK, 500, 2000000);
234	if (ret)
235		goto exit_unlock;
236
237	ret = regmap_bulk_read(data->regmap, CPUID_MCA_CMD,
238			       &output, CPUID_RD_REG_LEN);
239	if (ret < 0)
240		goto exit_unlock;
241
242	ret = regmap_write(data->regmap, SBRMI_STATUS,
243			   HW_ALERT_MASK);
244	if (ret < 0)
245		goto exit_unlock;
246
247	if (output.num_bytes != CPUID_RD_REG_LEN - 1) {
248		ret = -EMSGSIZE;
249		goto exit_unlock;
250	}
251	if (output.status) {
252		ret = -EPROTOTYPE;
253		msg->fw_ret_code = output.status;
254		goto exit_unlock;
255	}
256	msg->cpu_in_out = output.value;
257exit_unlock:
258	if (ret < 0)
259		msg->cpu_in_out = 0;
260	mutex_unlock(&data->lock);
261	return ret;
262}
263
264static int rmi_mcamsr_input(struct sbrmi_data *data, struct apml_mcamsr_msg *msg,
265			    u16 thread)
266{
267	struct cpu_msr_indata input = {0};
268	int val = 0, ret;
269
270	/* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
271	if (thread > 127) {
272		thread -= 128;
273		val = 1;
274	}
275
276	ret = regmap_write(data->regmap, SBRMI_THREAD128CS, val);
277	if (ret < 0)
278		return ret;
279
280	prepare_mca_msr_input_message(&input, thread,
281				      msg->mcamsr_in_out & CPUID_MCA_FUNC_MASK);
282
283	return regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
284				 &input, MSR_WR_REG_LEN);
285}
286
287static int rmi_mcamsr_input_ext(struct sbrmi_data *data, struct apml_mcamsr_msg *msg,
288				u16 thread)
289{
290	struct cpu_msr_indata_ext input = {0};
291
292	prepare_mca_msr_input_message_ext(&input, thread,
293					  msg->mcamsr_in_out & CPUID_MCA_FUNC_MASK);
294
295	return regmap_bulk_write(data->regmap, CPUID_MCA_CMD,
296				 &input, MSR_WR_REG_LEN_EXT);
297}
298
299/* MCA MSR protocol */
300static int rmi_mca_msr_read(struct sbrmi_data *data,
301			    struct apml_mcamsr_msg  *msg)
302{
303	struct cpu_msr_outdata output = {0};
304	int ret;
305	int hw_status;
306	u16 thread;
307
308	mutex_lock(&data->lock);
309	/* cache the rev value to identify if protocol is supported or not */
310	if (!data->rev) {
311		ret = sbrmi_get_rev(data);
312		if (ret < 0)
313			goto exit_unlock;
314	}
315
316	/* Extract thread from the input msg structure */
317	thread = msg->mcamsr_in_out >> CPUID_MCA_THRD_INDEX;
318
319	switch (data->rev) {
320	case 0x10:
321		/* MCAMSR protocol for REV 0x10 is not supported*/
322		ret = -EOPNOTSUPP;
323		goto exit_unlock;
324	case 0x20:
325		ret = rmi_mcamsr_input(data, msg, thread);
326		if (ret)
327			goto exit_unlock;
328		break;
329	case 0x21:
330		ret = rmi_mcamsr_input_ext(data, msg, thread);
331		if (ret)
332			goto exit_unlock;
333		break;
334	default:
335		ret = -EOPNOTSUPP;
336		goto exit_unlock;
337	}
338
339	/*
340	 * For RMI Rev 0x20, new h/w status bit is introduced. which is used
341	 * by firmware to indicate completion of commands (0x71, 0x72, 0x73).
342	 * wait for the status bit to be set by the hardware before
343	 * reading the data out.
344	 */
345	ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, hw_status,
346				       hw_status & HW_ALERT_MASK, 500, 2000000);
347	if (ret)
348		goto exit_unlock;
349
350	ret = regmap_bulk_read(data->regmap, CPUID_MCA_CMD,
351			       &output, MSR_RD_REG_LEN);
352	if (ret < 0)
353		goto exit_unlock;
354
355	ret = regmap_write(data->regmap, SBRMI_STATUS,
356			   HW_ALERT_MASK);
357	if (ret < 0)
358		goto exit_unlock;
359
360	if (output.num_bytes != MSR_RD_REG_LEN - 1) {
361		ret = -EMSGSIZE;
362		goto exit_unlock;
363	}
364	if (output.status) {
365		ret = -EPROTOTYPE;
366		msg->fw_ret_code = output.status;
367		goto exit_unlock;
368	}
369	msg->mcamsr_in_out = output.value;
370
371exit_unlock:
372	mutex_unlock(&data->lock);
373	return ret;
374}
375
376int rmi_mailbox_xfer(struct sbrmi_data *data,
377		     struct apml_mbox_msg *msg)
378{
379	unsigned int bytes, ec;
380	int i, ret;
381	int sw_status;
382	u8 byte;
383
384	mutex_lock(&data->lock);
385
386	msg->fw_ret_code = 0;
387
388	/* Indicate firmware a command is to be serviced */
389	ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD);
390	if (ret < 0)
391		goto exit_unlock;
392
393	/* Write the command to SBRMI::InBndMsg_inst0 */
394	ret = regmap_write(data->regmap, SBRMI_INBNDMSG0, msg->cmd);
395	if (ret < 0)
396		goto exit_unlock;
397
398	/*
399	 * For both read and write the initiator (BMC) writes
400	 * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
401	 * SBRMI_x3C(MSB):SBRMI_x39(LSB)
402	 */
403	for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
404		byte = (msg->mb_in_out >> i * 8) & 0xff;
405		ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte);
406		if (ret < 0)
407			goto exit_unlock;
408	}
409
410	/*
411	 * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
412	 * perform the requested read or write command
413	 */
414	ret = regmap_write(data->regmap, SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
415	if (ret < 0)
416		goto exit_unlock;
417
418	/*
419	 * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate
420	 * an ALERT (if enabled) to initiator (BMC) to indicate completion
421	 * of the requested command
422	 */
423	ret = regmap_read_poll_timeout(data->regmap, SBRMI_STATUS, sw_status,
424				       sw_status & SW_ALERT_MASK, 500, 2000000);
425	if (ret)
426		goto exit_unlock;
427
428	ret = regmap_read(data->regmap, SBRMI_OUTBNDMSG7, &ec);
429	if (ret || ec)
430		goto exit_clear_alert;
431
432	/* Clear the input value before updating the output data */
433	msg->mb_in_out = 0;
434
435	/*
436	 * For a read operation, the initiator (BMC) reads the firmware
437	 * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
438	 * {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
439	 */
440	for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) {
441		ret = regmap_read(data->regmap,
442				  SBRMI_OUTBNDMSG1 + i, &bytes);
443		if (ret < 0)
444			break;
445		msg->mb_in_out |= bytes << i * 8;
446	}
447
448exit_clear_alert:
449	/*
450	 * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
451	 * ALERT to initiator
452	 */
453	ret = regmap_write(data->regmap, SBRMI_STATUS,
454			   sw_status | SW_ALERT_MASK);
455	if (ec) {
456		ret = -EPROTOTYPE;
457		msg->fw_ret_code = ec;
458	}
459exit_unlock:
460	mutex_unlock(&data->lock);
461	return ret;
462}
463
464static int apml_rmi_reg_xfer(struct sbrmi_data *data,
465			     struct apml_reg_xfer_msg __user *arg)
466{
467	struct apml_reg_xfer_msg msg = { 0 };
468	unsigned int data_read;
469	int ret;
470
471	/* Copy the structure from user */
472	if (copy_from_user(&msg, arg, sizeof(struct apml_reg_xfer_msg)))
473		return -EFAULT;
474
475	mutex_lock(&data->lock);
476	if (msg.rflag) {
477		ret = regmap_read(data->regmap, msg.reg_addr, &data_read);
478		if (!ret)
479			msg.data_in_out = data_read;
480	} else {
481		ret = regmap_write(data->regmap, msg.reg_addr, msg.data_in_out);
482	}
483
484	mutex_unlock(&data->lock);
485
486	if (msg.rflag && !ret)
487		if (copy_to_user(arg, &msg, sizeof(struct apml_reg_xfer_msg)))
488			return -EFAULT;
489	return ret;
490}
491
492static int apml_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg __user *arg)
493{
494	struct apml_mbox_msg msg = { 0 };
495	int ret;
496
497	/* Copy the structure from user */
498	if (copy_from_user(&msg, arg, sizeof(struct apml_mbox_msg)))
499		return -EFAULT;
500
501	/* Mailbox protocol */
502	ret = rmi_mailbox_xfer(data, &msg);
503	if (ret && ret != -EPROTOTYPE)
504		return ret;
505
506	if (copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg)))
507		return -EFAULT;
508	return ret;
509}
510
511static int apml_cpuid_xfer(struct sbrmi_data *data, struct apml_cpuid_msg __user *arg)
512{
513	struct apml_cpuid_msg msg = { 0 };
514	int ret;
515
516	/* Copy the structure from user */
517	if (copy_from_user(&msg, arg, sizeof(struct apml_cpuid_msg)))
518		return -EFAULT;
519
520	/* CPUID Protocol */
521	ret = rmi_cpuid_read(data, &msg);
522	if (ret && ret != -EPROTOTYPE)
523		return ret;
524
525	if (copy_to_user(arg, &msg, sizeof(struct apml_cpuid_msg)))
526		return -EFAULT;
527	return ret;
528}
529
530static int apml_mcamsr_xfer(struct sbrmi_data *data, struct apml_mcamsr_msg __user *arg)
531{
532	struct apml_mcamsr_msg msg = { 0 };
533	int ret;
534
535	/* Copy the structure from user */
536	if (copy_from_user(&msg, arg, sizeof(struct apml_mcamsr_msg)))
537		return -EFAULT;
538
539	/* MCAMSR Protocol */
540	ret = rmi_mca_msr_read(data, &msg);
541	if (ret && ret != -EPROTOTYPE)
542		return ret;
543
544	if (copy_to_user(arg, &msg, sizeof(struct apml_mcamsr_msg)))
545		return -EFAULT;
546	return ret;
547}
548
549static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
550{
551	void __user *argp = (void __user *)arg;
552	struct sbrmi_data *data;
553
554	data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
555	switch (cmd) {
556	case SBRMI_IOCTL_MBOX_CMD:
557		return apml_mailbox_xfer(data, argp);
558	case SBRMI_IOCTL_CPUID_CMD:
559		return apml_cpuid_xfer(data, argp);
560	case SBRMI_IOCTL_MCAMSR_CMD:
561		return apml_mcamsr_xfer(data, argp);
562	case SBRMI_IOCTL_REG_XFER_CMD:
563		return apml_rmi_reg_xfer(data, argp);
564	default:
565		return -ENOTTY;
566	}
567}
568
569static const struct file_operations sbrmi_fops = {
570	.owner		= THIS_MODULE,
571	.unlocked_ioctl	= sbrmi_ioctl,
572	.compat_ioctl	= compat_ptr_ioctl,
573};
574
575int create_misc_rmi_device(struct sbrmi_data *data,
576			   struct device *dev)
577{
578	data->sbrmi_misc_dev.name	= devm_kasprintf(dev,
579							 GFP_KERNEL,
580							 "sbrmi-%x",
581							 data->dev_static_addr);
582	data->sbrmi_misc_dev.minor	= MISC_DYNAMIC_MINOR;
583	data->sbrmi_misc_dev.fops	= &sbrmi_fops;
584	data->sbrmi_misc_dev.parent	= dev;
585	data->sbrmi_misc_dev.nodename	= devm_kasprintf(dev,
586							 GFP_KERNEL,
587							 "sbrmi-%x",
588							 data->dev_static_addr);
589	data->sbrmi_misc_dev.mode	= 0600;
590
591	return misc_register(&data->sbrmi_misc_dev);
592}
~/ Read-only file preview.