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