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 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 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 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 */ 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 */ 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 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 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 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 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 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 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 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