1
2
3
4
5
6
7
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
22#include "isst_if_common.h"
23
24#define MSR_OS_MAILBOX_INTERFACE 0xB0
25#define MSR_OS_MAILBOX_DATA 0xB1
26#define MSR_OS_MAILBOX_BUSY_BIT 31
27
28
29
30
31
32#define OS_MAILBOX_RETRY_COUNT 3
33
34static int isst_if_send_mbox_cmd(u8 command, u8 sub_command, u32 parameter,
35 u32 command_data, u32 *response_data)
36{
37 u32 retries;
38 u64 data;
39 int ret;
40
41
42 retries = OS_MAILBOX_RETRY_COUNT;
43 do {
44 rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
45 if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
46 ret = -EBUSY;
47 continue;
48 }
49 ret = 0;
50 break;
51 } while (--retries);
52
53 if (ret)
54 return ret;
55
56
57 wrmsrl(MSR_OS_MAILBOX_DATA, command_data);
58
59
60 data = BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT) |
61 (parameter & GENMASK_ULL(13, 0)) << 16 |
62 (sub_command << 8) |
63 command;
64 wrmsrl(MSR_OS_MAILBOX_INTERFACE, data);
65
66
67 retries = OS_MAILBOX_RETRY_COUNT;
68 do {
69 rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
70 if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
71 ret = -EBUSY;
72 continue;
73 }
74
75 if (data & 0xff)
76 return -ENXIO;
77
78 if (response_data) {
79 rdmsrl(MSR_OS_MAILBOX_DATA, data);
80 *response_data = data;
81 }
82 ret = 0;
83 break;
84 } while (--retries);
85
86 return ret;
87}
88
89struct msrl_action {
90 int err;
91 struct isst_if_mbox_cmd *mbox_cmd;
92};
93
94
95static void msrl_update_func(void *info)
96{
97 struct msrl_action *act = info;
98
99 act->err = isst_if_send_mbox_cmd(act->mbox_cmd->command,
100 act->mbox_cmd->sub_command,
101 act->mbox_cmd->parameter,
102 act->mbox_cmd->req_data,
103 &act->mbox_cmd->resp_data);
104}
105
106static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
107{
108 struct msrl_action action;
109 int ret;
110
111 action.mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
112
113 if (isst_if_mbox_cmd_invalid(action.mbox_cmd))
114 return -EINVAL;
115
116 if (isst_if_mbox_cmd_set_req(action.mbox_cmd) &&
117 !capable(CAP_SYS_ADMIN))
118 return -EPERM;
119
120
121
122
123
124
125
126
127 ret = smp_call_function_single(action.mbox_cmd->logical_cpu,
128 msrl_update_func, &action, 1);
129 if (ret)
130 return ret;
131
132 if (!action.err && !resume && isst_if_mbox_cmd_set_req(action.mbox_cmd))
133 action.err = isst_store_cmd(action.mbox_cmd->command,
134 action.mbox_cmd->sub_command,
135 action.mbox_cmd->logical_cpu, 1,
136 action.mbox_cmd->parameter,
137 action.mbox_cmd->req_data);
138 *write_only = 0;
139
140 return action.err;
141}
142
143
144static int isst_pm_notify(struct notifier_block *nb,
145 unsigned long mode, void *_unused)
146{
147 switch (mode) {
148 case PM_POST_HIBERNATION:
149 case PM_POST_RESTORE:
150 case PM_POST_SUSPEND:
151 isst_resume_common();
152 break;
153 default:
154 break;
155 }
156 return 0;
157}
158
159static struct notifier_block isst_pm_nb = {
160 .notifier_call = isst_pm_notify,
161};
162
163static const struct x86_cpu_id isst_if_cpu_ids[] = {
164 X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL),
165 {}
166};
167MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids);
168
169static int __init isst_if_mbox_init(void)
170{
171 struct isst_if_cmd_cb cb;
172 const struct x86_cpu_id *id;
173 u64 data;
174 int ret;
175
176 id = x86_match_cpu(isst_if_cpu_ids);
177 if (!id)
178 return -ENODEV;
179
180
181 ret = rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data);
182 if (ret)
183 return ret;
184
185 ret = rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data);
186 if (ret)
187 return ret;
188
189 memset(&cb, 0, sizeof(cb));
190 cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
191 cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
192 cb.cmd_callback = isst_if_mbox_proc_cmd;
193 cb.owner = THIS_MODULE;
194 ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
195 if (ret)
196 return ret;
197
198 ret = register_pm_notifier(&isst_pm_nb);
199 if (ret)
200 isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
201
202 return ret;
203}
204module_init(isst_if_mbox_init)
205
206static void __exit isst_if_mbox_exit(void)
207{
208 unregister_pm_notifier(&isst_pm_nb);
209 isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
210}
211module_exit(isst_if_mbox_exit)
212
213MODULE_LICENSE("GPL v2");
214MODULE_DESCRIPTION("Intel speed select interface mailbox driver");
215