1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Intel Speed Select -- Enumerate and control features
4 * Copyright (c) 2019 Intel Corporation.
5 */
6
7#include "isst.h"
8
9static struct isst_platform_ops *isst_ops;
10
11#define CHECK_CB(_name) \
12 do { \
13 if (!isst_ops || !isst_ops->_name) { \
14 fprintf(stderr, "Invalid ops\n"); \
15 exit(0); \
16 } \
17 } while (0)
18
19int isst_set_platform_ops(int api_version)
20{
21 switch (api_version) {
22 case 1:
23 isst_ops = mbox_get_platform_ops();
24 break;
25 case 2:
26 case 3:
27 isst_ops = tpmi_get_platform_ops();
28 break;
29 default:
30 isst_ops = NULL;
31 break;
32 }
33
34 if (!isst_ops)
35 return -1;
36 return 0;
37}
38
39void isst_update_platform_param(enum isst_platform_param param, int value)
40{
41 CHECK_CB(update_platform_param);
42
43 isst_ops->update_platform_param(param, value);
44}
45
46int isst_get_disp_freq_multiplier(void)
47{
48 CHECK_CB(get_disp_freq_multiplier);
49 return isst_ops->get_disp_freq_multiplier();
50}
51
52int isst_get_trl_max_levels(void)
53{
54 CHECK_CB(get_trl_max_levels);
55 return isst_ops->get_trl_max_levels();
56}
57
58char *isst_get_trl_level_name(int level)
59{
60 CHECK_CB(get_trl_level_name);
61 return isst_ops->get_trl_level_name(level);
62}
63
64int isst_is_punit_valid(struct isst_id *id)
65{
66 CHECK_CB(is_punit_valid);
67 return isst_ops->is_punit_valid(id);
68}
69
70int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write,
71 unsigned long long *req_resp)
72{
73 struct isst_if_msr_cmds msr_cmds;
74 const char *pathname = "/dev/isst_interface";
75 FILE *outf = get_output_file();
76 int fd;
77
78 fd = open(pathname, O_RDWR);
79 if (fd < 0)
80 err(-1, "%s open failed", pathname);
81
82 msr_cmds.cmd_count = 1;
83 msr_cmds.msr_cmd[0].logical_cpu = cpu;
84 msr_cmds.msr_cmd[0].msr = msr;
85 msr_cmds.msr_cmd[0].read_write = write;
86 if (write)
87 msr_cmds.msr_cmd[0].data = *req_resp;
88
89 if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) {
90 perror("ISST_IF_MSR_COMMAND");
91 fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n",
92 cpu, msr, write);
93 } else {
94 if (!write)
95 *req_resp = msr_cmds.msr_cmd[0].data;
96
97 debug_printf(
98 format: "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n",
99 cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data);
100 }
101
102 close(fd);
103
104 return 0;
105}
106
107int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap)
108{
109 CHECK_CB(read_pm_config);
110 return isst_ops->read_pm_config(id, cp_state, cp_cap);
111}
112
113int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev)
114{
115 CHECK_CB(get_config_levels);
116 return isst_ops->get_config_levels(id, pkg_dev);
117}
118
119int isst_get_ctdp_control(struct isst_id *id, int config_index,
120 struct isst_pkg_ctdp_level_info *ctdp_level)
121{
122 CHECK_CB(get_ctdp_control);
123 return isst_ops->get_ctdp_control(id, config_index, ctdp_level);
124}
125
126int isst_get_tdp_info(struct isst_id *id, int config_index,
127 struct isst_pkg_ctdp_level_info *ctdp_level)
128{
129 CHECK_CB(get_tdp_info);
130 return isst_ops->get_tdp_info(id, config_index, ctdp_level);
131}
132
133int isst_get_pwr_info(struct isst_id *id, int config_index,
134 struct isst_pkg_ctdp_level_info *ctdp_level)
135{
136 CHECK_CB(get_pwr_info);
137 return isst_ops->get_pwr_info(id, config_index, ctdp_level);
138}
139
140int isst_get_coremask_info(struct isst_id *id, int config_index,
141 struct isst_pkg_ctdp_level_info *ctdp_level)
142{
143 CHECK_CB(get_coremask_info);
144 return isst_ops->get_coremask_info(id, config_index, ctdp_level);
145}
146
147int isst_get_get_trl_from_msr(struct isst_id *id, int *trl)
148{
149 unsigned long long msr_trl;
150 int ret;
151
152 ret = isst_send_msr_command(cpu: id->cpu, msr: 0x1AD, write: 0, req_resp: &msr_trl);
153 if (ret)
154 return ret;
155
156 trl[0] = msr_trl & GENMASK(7, 0);
157 trl[1] = (msr_trl & GENMASK(15, 8)) >> 8;
158 trl[2] = (msr_trl & GENMASK(23, 16)) >> 16;
159 trl[3] = (msr_trl & GENMASK(31, 24)) >> 24;
160 trl[4] = (msr_trl & GENMASK(39, 32)) >> 32;
161 trl[5] = (msr_trl & GENMASK(47, 40)) >> 40;
162 trl[6] = (msr_trl & GENMASK(55, 48)) >> 48;
163 trl[7] = (msr_trl & GENMASK(63, 56)) >> 56;
164
165 return 0;
166}
167
168int isst_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl)
169{
170 CHECK_CB(get_get_trl);
171 return isst_ops->get_get_trl(id, level, avx_level, trl);
172}
173
174int isst_get_get_trls(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level)
175{
176 CHECK_CB(get_get_trls);
177 return isst_ops->get_get_trls(id, level, ctdp_level);
178}
179
180int isst_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info)
181{
182 CHECK_CB(get_trl_bucket_info);
183 return isst_ops->get_trl_bucket_info(id, level, buckets_info);
184}
185
186int isst_set_tdp_level(struct isst_id *id, int tdp_level)
187{
188 CHECK_CB(set_tdp_level);
189 return isst_ops->set_tdp_level(id, tdp_level);
190}
191
192int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info)
193{
194 struct isst_pkg_ctdp_level_info ctdp_level;
195 struct isst_pkg_ctdp pkg_dev;
196 int ret;
197
198 ret = isst_get_ctdp_levels(id, pkg_dev: &pkg_dev);
199 if (ret) {
200 isst_display_error_info_message(error: 1, msg: "Failed to get number of levels", arg_valid: 0, arg: 0);
201 return ret;
202 }
203
204 if (level > pkg_dev.levels) {
205 isst_display_error_info_message(error: 1, msg: "Invalid level", arg_valid: 1, arg: level);
206 return -1;
207 }
208
209 ret = isst_get_ctdp_control(id, config_index: level, ctdp_level: &ctdp_level);
210 if (ret)
211 return ret;
212
213 if (!ctdp_level.pbf_support) {
214 isst_display_error_info_message(error: 1, msg: "base-freq feature is not present at this level", arg_valid: 1, arg: level);
215 return -1;
216 }
217
218 pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask);
219
220 CHECK_CB(get_pbf_info);
221 return isst_ops->get_pbf_info(id, level, pbf_info);
222}
223
224int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable)
225{
226 CHECK_CB(set_pbf_fact_status);
227 return isst_ops->set_pbf_fact_status(id, pbf, enable);
228}
229
230
231
232int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info)
233{
234 struct isst_pkg_ctdp_level_info ctdp_level;
235 struct isst_pkg_ctdp pkg_dev;
236 int ret;
237
238 ret = isst_get_ctdp_levels(id, pkg_dev: &pkg_dev);
239 if (ret) {
240 isst_display_error_info_message(error: 1, msg: "Failed to get number of levels", arg_valid: 0, arg: 0);
241 return ret;
242 }
243
244 if (level > pkg_dev.levels) {
245 isst_display_error_info_message(error: 1, msg: "Invalid level", arg_valid: 1, arg: level);
246 return -1;
247 }
248
249 ret = isst_get_ctdp_control(id, config_index: level, ctdp_level: &ctdp_level);
250 if (ret)
251 return ret;
252
253 if (!ctdp_level.fact_support) {
254 isst_display_error_info_message(error: 1, msg: "turbo-freq feature is not present at this level", arg_valid: 1, arg: level);
255 return -1;
256 }
257 CHECK_CB(get_fact_info);
258 return isst_ops->get_fact_info(id, level, fact_bucket, fact_info);
259}
260
261int isst_get_trl(struct isst_id *id, unsigned long long *trl)
262{
263 int ret;
264
265 ret = isst_send_msr_command(cpu: id->cpu, msr: 0x1AD, write: 0, req_resp: trl);
266 if (ret)
267 return ret;
268
269 return 0;
270}
271
272int isst_set_trl(struct isst_id *id, unsigned long long trl)
273{
274 int ret;
275
276 if (!trl)
277 trl = 0xFFFFFFFFFFFFFFFFULL;
278
279 ret = isst_send_msr_command(cpu: id->cpu, msr: 0x1AD, write: 1, req_resp: &trl);
280 if (ret)
281 return ret;
282
283 return 0;
284}
285
286#define MSR_TRL_FREQ_MULTIPLIER 100
287
288int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl)
289{
290 unsigned long long msr_trl;
291 int ret;
292
293 if (id->cpu < 0)
294 return 0;
295
296 if (trl) {
297 msr_trl = trl;
298 } else {
299 struct isst_pkg_ctdp pkg_dev;
300 int trl[8];
301 int i;
302
303 ret = isst_get_ctdp_levels(id, pkg_dev: &pkg_dev);
304 if (ret)
305 return ret;
306
307 ret = isst_get_get_trl(id, level: pkg_dev.current_level, avx_level: 0, trl);
308 if (ret)
309 return ret;
310
311 msr_trl = 0;
312 for (i = 0; i < 8; ++i) {
313 unsigned long long _trl = trl[i];
314
315 /* MSR is always in 100 MHz unit */
316 if (isst_get_disp_freq_multiplier() == 1)
317 _trl /= MSR_TRL_FREQ_MULTIPLIER;
318
319 msr_trl |= (_trl << (i * 8));
320 }
321 }
322 ret = isst_send_msr_command(cpu: id->cpu, msr: 0x1AD, write: 1, req_resp: &msr_trl);
323 if (ret)
324 return ret;
325
326 return 0;
327}
328
329/* Return 1 if locked */
330int isst_get_config_tdp_lock_status(struct isst_id *id)
331{
332 unsigned long long tdp_control = 0;
333 int ret;
334
335 ret = isst_send_msr_command(cpu: id->cpu, msr: 0x64b, write: 0, req_resp: &tdp_control);
336 if (ret)
337 return ret;
338
339 ret = !!(tdp_control & BIT(31));
340
341 return ret;
342}
343
344void isst_get_process_ctdp_complete(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev)
345{
346 int i;
347
348 if (!pkg_dev->processed)
349 return;
350
351 for (i = 0; i < pkg_dev->levels; ++i) {
352 struct isst_pkg_ctdp_level_info *ctdp_level;
353
354 ctdp_level = &pkg_dev->ctdp_level[i];
355 if (ctdp_level->pbf_support)
356 free_cpu_set(ctdp_level->pbf_info.core_cpumask);
357 free_cpu_set(ctdp_level->core_cpumask);
358 }
359}
360
361void isst_adjust_uncore_freq(struct isst_id *id, int config_index,
362 struct isst_pkg_ctdp_level_info *ctdp_level)
363{
364 CHECK_CB(adjust_uncore_freq);
365 return isst_ops->adjust_uncore_freq(id, config_index, ctdp_level);
366}
367
368int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctdp *pkg_dev)
369{
370 int i, ret, valid = 0;
371
372 if (pkg_dev->processed)
373 return 0;
374
375 ret = isst_get_ctdp_levels(id, pkg_dev);
376 if (ret)
377 return ret;
378
379 debug_printf(format: "cpu: %d ctdp enable:%d current level: %d levels:%d\n",
380 id->cpu, pkg_dev->enabled, pkg_dev->current_level,
381 pkg_dev->levels);
382
383 if (tdp_level != 0xff && tdp_level > pkg_dev->levels) {
384 isst_display_error_info_message(error: 1, msg: "Invalid level", arg_valid: 0, arg: 0);
385 return -1;
386 }
387
388 if (!pkg_dev->enabled)
389 isst_display_error_info_message(error: 0, msg: "perf-profile feature is not supported, just base-config level 0 is valid", arg_valid: 0, arg: 0);
390
391 for (i = 0; i <= pkg_dev->levels; ++i) {
392 struct isst_pkg_ctdp_level_info *ctdp_level;
393
394 if (tdp_level != 0xff && i != tdp_level)
395 continue;
396
397 debug_printf(format: "cpu:%d Get Information for TDP level:%d\n", id->cpu,
398 i);
399 ctdp_level = &pkg_dev->ctdp_level[i];
400
401 ctdp_level->level = i;
402 ctdp_level->control_cpu = id->cpu;
403 ctdp_level->pkg_id = id->pkg;
404 ctdp_level->die_id = id->die;
405
406 ret = isst_get_ctdp_control(id, config_index: i, ctdp_level);
407 if (ret)
408 continue;
409
410 valid = 1;
411 pkg_dev->processed = 1;
412 ctdp_level->processed = 1;
413
414 if (ctdp_level->pbf_support) {
415 ret = isst_get_pbf_info(id, level: i, pbf_info: &ctdp_level->pbf_info);
416 if (!ret)
417 ctdp_level->pbf_found = 1;
418 }
419
420 if (ctdp_level->fact_support) {
421 ret = isst_get_fact_info(id, level: i, fact_bucket: 0xff,
422 fact_info: &ctdp_level->fact_info);
423 if (ret)
424 return ret;
425 }
426
427 if (!pkg_dev->enabled && is_skx_based_platform()) {
428 int freq;
429
430 freq = get_cpufreq_base_freq(cpu: id->cpu);
431 if (freq > 0) {
432 ctdp_level->sse_p1 = freq / 100000;
433 ctdp_level->tdp_ratio = ctdp_level->sse_p1;
434 }
435
436 isst_get_get_trl_from_msr(id, trl: ctdp_level->trl_ratios[0]);
437 isst_get_trl_bucket_info(id, level: i, buckets_info: &ctdp_level->trl_cores);
438 continue;
439 }
440
441 ret = isst_get_tdp_info(id, config_index: i, ctdp_level);
442 if (ret)
443 return ret;
444
445 ret = isst_get_pwr_info(id, config_index: i, ctdp_level);
446 if (ret)
447 return ret;
448
449 ctdp_level->core_cpumask_size =
450 alloc_cpu_set(&ctdp_level->core_cpumask);
451 ret = isst_get_coremask_info(id, config_index: i, ctdp_level);
452 if (ret)
453 return ret;
454
455 ret = isst_get_trl_bucket_info(id, level: i, buckets_info: &ctdp_level->trl_cores);
456 if (ret)
457 return ret;
458
459 ret = isst_get_get_trls(id, level: i, ctdp_level);
460 if (ret)
461 return ret;
462 }
463
464 if (!valid)
465 isst_display_error_info_message(error: 0, msg: "Invalid level, Can't get TDP control information at specified levels on cpu", arg_valid: 1, arg: id->cpu);
466
467 return 0;
468}
469
470int isst_clos_get_clos_information(struct isst_id *id, int *enable, int *type)
471{
472 CHECK_CB(get_clos_information);
473 return isst_ops->get_clos_information(id, enable, type);
474}
475
476int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type)
477{
478 CHECK_CB(pm_qos_config);
479 return isst_ops->pm_qos_config(id, enable_clos, priority_type);
480}
481
482int isst_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config)
483{
484 CHECK_CB(pm_get_clos);
485 return isst_ops->pm_get_clos(id, clos, clos_config);
486}
487
488int isst_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config)
489{
490 CHECK_CB(set_clos);
491 return isst_ops->set_clos(id, clos, clos_config);
492}
493
494int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id)
495{
496 CHECK_CB(clos_get_assoc_status);
497 return isst_ops->clos_get_assoc_status(id, clos_id);
498}
499
500int isst_clos_associate(struct isst_id *id, int clos_id)
501{
502 CHECK_CB(clos_associate);
503 return isst_ops->clos_associate(id, clos_id);
504
505}
506

source code of linux/tools/power/x86/intel-speed-select/isst-core.c