diff options
| -rw-r--r-- | drivers/platform/x86/intel_speed_select_if/Makefile | 1 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c | 180 |
2 files changed, 181 insertions, 0 deletions
diff --git a/drivers/platform/x86/intel_speed_select_if/Makefile b/drivers/platform/x86/intel_speed_select_if/Makefile index 8dec8c858649..856076206f35 100644 --- a/drivers/platform/x86/intel_speed_select_if/Makefile +++ b/drivers/platform/x86/intel_speed_select_if/Makefile | |||
| @@ -7,3 +7,4 @@ | |||
| 7 | obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o | 7 | obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o |
| 8 | obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o | 8 | obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o |
| 9 | obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o | 9 | obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o |
| 10 | obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o | ||
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c new file mode 100644 index 000000000000..d4bfc32546b5 --- /dev/null +++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c | |||
| @@ -0,0 +1,180 @@ | |||
| 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/topology.h> | ||
| 16 | #include <linux/uaccess.h> | ||
| 17 | #include <uapi/linux/isst_if.h> | ||
| 18 | #include <asm/cpu_device_id.h> | ||
| 19 | #include <asm/intel-family.h> | ||
| 20 | |||
| 21 | #include "isst_if_common.h" | ||
| 22 | |||
| 23 | #define MSR_OS_MAILBOX_INTERFACE 0xB0 | ||
| 24 | #define MSR_OS_MAILBOX_DATA 0xB1 | ||
| 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 | |||
| 33 | static 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 | rdmsrl(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 | wrmsrl(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 | wrmsrl(MSR_OS_MAILBOX_INTERFACE, data); | ||
| 64 | |||
| 65 | /* Poll for rb bit == 0 */ | ||
| 66 | retries = OS_MAILBOX_RETRY_COUNT; | ||
| 67 | do { | ||
| 68 | rdmsrl(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 | rdmsrl(MSR_OS_MAILBOX_DATA, data); | ||
| 79 | *response_data = data; | ||
| 80 | } | ||
| 81 | ret = 0; | ||
| 82 | break; | ||
| 83 | } while (--retries); | ||
| 84 | |||
| 85 | return ret; | ||
| 86 | } | ||
| 87 | |||
| 88 | struct 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! */ | ||
| 94 | static 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 | |||
| 105 | static 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 | *write_only = 0; | ||
| 132 | |||
| 133 | return action.err; | ||
| 134 | } | ||
| 135 | |||
| 136 | #define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } | ||
| 137 | |||
| 138 | static const struct x86_cpu_id isst_if_cpu_ids[] = { | ||
| 139 | ICPU(INTEL_FAM6_SKYLAKE_X), | ||
| 140 | {} | ||
| 141 | }; | ||
| 142 | MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids); | ||
| 143 | |||
| 144 | static int __init isst_if_mbox_init(void) | ||
| 145 | { | ||
| 146 | struct isst_if_cmd_cb cb; | ||
| 147 | const struct x86_cpu_id *id; | ||
| 148 | u64 data; | ||
| 149 | int ret; | ||
| 150 | |||
| 151 | id = x86_match_cpu(isst_if_cpu_ids); | ||
| 152 | if (!id) | ||
| 153 | return -ENODEV; | ||
| 154 | |||
| 155 | /* Check presence of mailbox MSRs */ | ||
| 156 | ret = rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data); | ||
| 157 | if (ret) | ||
| 158 | return ret; | ||
| 159 | |||
| 160 | ret = rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data); | ||
| 161 | if (ret) | ||
| 162 | return ret; | ||
| 163 | |||
| 164 | memset(&cb, 0, sizeof(cb)); | ||
| 165 | cb.cmd_size = sizeof(struct isst_if_mbox_cmd); | ||
| 166 | cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd); | ||
| 167 | cb.cmd_callback = isst_if_mbox_proc_cmd; | ||
| 168 | cb.owner = THIS_MODULE; | ||
| 169 | return isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb); | ||
| 170 | } | ||
| 171 | module_init(isst_if_mbox_init) | ||
| 172 | |||
| 173 | static void __exit isst_if_mbox_exit(void) | ||
| 174 | { | ||
| 175 | isst_if_cdev_unregister(ISST_IF_DEV_MBOX); | ||
| 176 | } | ||
| 177 | module_exit(isst_if_mbox_exit) | ||
| 178 | |||
| 179 | MODULE_LICENSE("GPL v2"); | ||
| 180 | MODULE_DESCRIPTION("Intel speed select interface mailbox driver"); | ||
