1139735Simp// SPDX-License-Identifier: GPL-2.0
2129198Scognet/*
3129198Scognet * Intel Speed Select -- Enumerate and control features
4129198Scognet * Copyright (c) 2019 Intel Corporation.
5129198Scognet */
6129198Scognet
7129198Scognet#include "isst.h"
8129198Scognet
9129198Scognetstatic struct isst_platform_ops		*isst_ops;
10129198Scognet
11129198Scognet#define CHECK_CB(_name)	\
12129198Scognet	do {	\
13129198Scognet		if (!isst_ops || !isst_ops->_name) {	\
14129198Scognet			fprintf(stderr, "Invalid ops\n");	\
15129198Scognet			exit(0);	\
16129198Scognet		}	\
17129198Scognet	} while (0)
18129198Scognet
19129198Scognetint isst_set_platform_ops(int api_version)
20129198Scognet{
21129198Scognet	switch (api_version) {
22129198Scognet	case 1:
23129198Scognet		isst_ops = mbox_get_platform_ops();
24129198Scognet		break;
25129198Scognet	case 2:
26129198Scognet		isst_ops = tpmi_get_platform_ops();
27129198Scognet		break;
28129198Scognet	default:
29129198Scognet		isst_ops = NULL;
30129198Scognet		break;
31129198Scognet	}
32129198Scognet
33129198Scognet	if (!isst_ops)
34129198Scognet		return -1;
35129198Scognet	return 0;
36129198Scognet}
37129198Scognet
38129198Scognetvoid isst_update_platform_param(enum isst_platform_param param, int value)
39129198Scognet{
40129198Scognet	CHECK_CB(update_platform_param);
41129198Scognet
42129198Scognet	isst_ops->update_platform_param(param, value);
43129198Scognet}
44129198Scognet
45129198Scognetint isst_get_disp_freq_multiplier(void)
46129198Scognet{
47129198Scognet	CHECK_CB(get_disp_freq_multiplier);
48129198Scognet	return isst_ops->get_disp_freq_multiplier();
49144637Sjhb}
50144637Sjhb
51276638Sianint isst_get_trl_max_levels(void)
52140001Scognet{
53140001Scognet	CHECK_CB(get_trl_max_levels);
54290273Szbb	return isst_ops->get_trl_max_levels();
55290273Szbb}
56307136Sed
57218310Simpchar *isst_get_trl_level_name(int level)
58188540Scognet{
59188540Scognet	CHECK_CB(get_trl_level_name);
60301700Sandrew	return isst_ops->get_trl_level_name(level);
61129198Scognet}
62129198Scognet
63129198Scognetint isst_is_punit_valid(struct isst_id *id)
64129198Scognet{
65129198Scognet	CHECK_CB(is_punit_valid);
66129198Scognet	return isst_ops->is_punit_valid(id);
67129198Scognet}
68245551Sandrew
69207152Skibint isst_send_msr_command(unsigned int cpu, unsigned int msr, int write,
70225973Skib			  unsigned long long *req_resp)
71283034Sandrew{
72283034Sandrew	struct isst_if_msr_cmds msr_cmds;
73283034Sandrew	const char *pathname = "/dev/isst_interface";
74283034Sandrew	FILE *outf = get_output_file();
75283034Sandrew	int fd;
76286960Skib
77283034Sandrew	fd = open(pathname, O_RDWR);
78283034Sandrew	if (fd < 0)
79225973Skib		err(-1, "%s open failed", pathname);
80225973Skib
81225973Skib	msr_cmds.cmd_count = 1;
82225973Skib	msr_cmds.msr_cmd[0].logical_cpu = cpu;
83225973Skib	msr_cmds.msr_cmd[0].msr = msr;
84225973Skib	msr_cmds.msr_cmd[0].read_write = write;
85283034Sandrew	if (write)
86225973Skib		msr_cmds.msr_cmd[0].data = *req_resp;
87129198Scognet
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
106int 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
112int 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
118int 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
125int 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
132int 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
139int 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
146int 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
167int 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
173int 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
179int 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
185int 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
191int 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
223int 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
231int 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
260int 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
271int 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
285int 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 */
323int 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
337void 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
354void 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
361int 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
463int 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
469int 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
475int 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
481int 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
487int 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
493int 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