FILE
isst_if_mbox_msr.c
File preview with syntax highlighting.
linux-6.19.14/drivers/platform/x86/intel/speed_select_if/isst_if_mbox_msr.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Intel Speed Select Interface: Mbox via MSR Interface 4 * Copyright (c) 2019, Intel Corporation. 5 * All rights reserved. 6 * 7 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 8 */ 9 10#include <linux/module.h> 11#include <linux/cpuhotplug.h> 12#include <linux/pci.h> 13#include <linux/sched/signal.h> 14#include <linux/slab.h> 15#include <linux/suspend.h> 16#include <linux/topology.h> 17#include <linux/uaccess.h> 18#include <uapi/linux/isst_if.h> 19#include <asm/cpu_device_id.h> 20#include <asm/intel-family.h> 21#include <asm/msr.h> 22 23#include "isst_if_common.h" 24 25#define MSR_OS_MAILBOX_BUSY_BIT 31 26 27/* 28 * Based on experiments count is never more than 1, as the MSR overhead 29 * is enough to finish the command. So here this is the worst case number. 30 */ 31#define OS_MAILBOX_RETRY_COUNT 3 32 33static int isst_if_send_mbox_cmd(u8 command, u8 sub_command, u32 parameter, 34 u32 command_data, u32 *response_data) 35{ 36 u32 retries; 37 u64 data; 38 int ret; 39 40 /* Poll for rb bit == 0 */ 41 retries = OS_MAILBOX_RETRY_COUNT; 42 do { 43 rdmsrq(MSR_OS_MAILBOX_INTERFACE, data); 44 if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) { 45 ret = -EBUSY; 46 continue; 47 } 48 ret = 0; 49 break; 50 } while (--retries); 51 52 if (ret) 53 return ret; 54 55 /* Write DATA register */ 56 wrmsrq(MSR_OS_MAILBOX_DATA, command_data); 57 58 /* Write command register */ 59 data = BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT) | 60 (parameter & GENMASK_ULL(13, 0)) << 16 | 61 (sub_command << 8) | 62 command; 63 wrmsrq(MSR_OS_MAILBOX_INTERFACE, data); 64 65 /* Poll for rb bit == 0 */ 66 retries = OS_MAILBOX_RETRY_COUNT; 67 do { 68 rdmsrq(MSR_OS_MAILBOX_INTERFACE, data); 69 if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) { 70 ret = -EBUSY; 71 continue; 72 } 73 74 if (data & 0xff) 75 return -ENXIO; 76 77 if (response_data) { 78 rdmsrq(MSR_OS_MAILBOX_DATA, data); 79 *response_data = data; 80 } 81 ret = 0; 82 break; 83 } while (--retries); 84 85 return ret; 86} 87 88struct msrl_action { 89 int err; 90 struct isst_if_mbox_cmd *mbox_cmd; 91}; 92 93/* revisit, smp_call_function_single should be enough for atomic mailbox! */ 94static void msrl_update_func(void *info) 95{ 96 struct msrl_action *act = info; 97 98 act->err = isst_if_send_mbox_cmd(act->mbox_cmd->command, 99 act->mbox_cmd->sub_command, 100 act->mbox_cmd->parameter, 101 act->mbox_cmd->req_data, 102 &act->mbox_cmd->resp_data); 103} 104 105static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume) 106{ 107 struct msrl_action action; 108 int ret; 109 110 action.mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr; 111 112 if (isst_if_mbox_cmd_invalid(action.mbox_cmd)) 113 return -EINVAL; 114 115 if (isst_if_mbox_cmd_set_req(action.mbox_cmd) && 116 !capable(CAP_SYS_ADMIN)) 117 return -EPERM; 118 119 /* 120 * To complete mailbox command, we need to access two MSRs. 121 * So we don't want race to complete a mailbox transcation. 122 * Here smp_call ensures that msrl_update_func() has no race 123 * and also with wait flag, wait for completion. 124 * smp_call_function_single is using get_cpu() and put_cpu(). 125 */ 126 ret = smp_call_function_single(action.mbox_cmd->logical_cpu, 127 msrl_update_func, &action, 1); 128 if (ret) 129 return ret; 130 131 if (!action.err && !resume && isst_if_mbox_cmd_set_req(action.mbox_cmd)) 132 action.err = isst_store_cmd(action.mbox_cmd->command, 133 action.mbox_cmd->sub_command, 134 action.mbox_cmd->logical_cpu, 1, 135 action.mbox_cmd->parameter, 136 action.mbox_cmd->req_data); 137 *write_only = 0; 138 139 return action.err; 140} 141 142 143static int isst_pm_notify(struct notifier_block *nb, 144 unsigned long mode, void *_unused) 145{ 146 switch (mode) { 147 case PM_POST_HIBERNATION: 148 case PM_POST_RESTORE: 149 case PM_POST_SUSPEND: 150 isst_resume_common(); 151 break; 152 default: 153 break; 154 } 155 return 0; 156} 157 158static struct notifier_block isst_pm_nb = { 159 .notifier_call = isst_pm_notify, 160}; 161 162static const struct x86_cpu_id isst_if_cpu_ids[] = { 163 X86_MATCH_VFM(INTEL_SKYLAKE_X, NULL), 164 {} 165}; 166MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids); 167 168static int __init isst_if_mbox_init(void) 169{ 170 struct isst_if_cmd_cb cb; 171 const struct x86_cpu_id *id; 172 u64 data; 173 int ret; 174 175 id = x86_match_cpu(isst_if_cpu_ids); 176 if (!id) 177 return -ENODEV; 178 179 /* Check presence of mailbox MSRs */ 180 ret = rdmsrq_safe(MSR_OS_MAILBOX_INTERFACE, &data); 181 if (ret) 182 return ret; 183 184 ret = rdmsrq_safe(MSR_OS_MAILBOX_DATA, &data); 185 if (ret) 186 return ret; 187 188 memset(&cb, 0, sizeof(cb)); 189 cb.cmd_size = sizeof(struct isst_if_mbox_cmd); 190 cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd); 191 cb.cmd_callback = isst_if_mbox_proc_cmd; 192 cb.owner = THIS_MODULE; 193 ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb); 194 if (ret) 195 return ret; 196 197 ret = register_pm_notifier(&isst_pm_nb); 198 if (ret) 199 isst_if_cdev_unregister(ISST_IF_DEV_MBOX); 200 201 return ret; 202} 203module_init(isst_if_mbox_init) 204 205static void __exit isst_if_mbox_exit(void) 206{ 207 unregister_pm_notifier(&isst_pm_nb); 208 isst_if_cdev_unregister(ISST_IF_DEV_MBOX); 209} 210module_exit(isst_if_mbox_exit) 211 212MODULE_LICENSE("GPL v2"); 213MODULE_DESCRIPTION("Intel speed select interface mailbox driver");
~/
Read-only file preview.