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 <linux/isst_if.h>
8#include <sys/utsname.h>
9
10#include "isst.h"
11
12struct process_cmd_struct {
13	char *feature;
14	char *command;
15	void (*process_fn)(int arg);
16	int arg;
17};
18
19static const char *version_str = "v1.17";
20
21static const int supported_api_ver = 2;
22static struct isst_if_platform_info isst_platform_info;
23static char *progname;
24static int debug_flag;
25static FILE *outf;
26
27static int cpu_model;
28static int cpu_stepping;
29
30#define MAX_CPUS_IN_ONE_REQ 256
31static short max_target_cpus;
32static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ];
33
34static int topo_max_cpus;
35static size_t present_cpumask_size;
36static cpu_set_t *present_cpumask;
37static size_t target_cpumask_size;
38static cpu_set_t *target_cpumask;
39static int tdp_level = 0xFF;
40static int fact_bucket = 0xFF;
41static int fact_avx = 0xFF;
42static unsigned long long fact_trl;
43static int out_format_json;
44static int cmd_help;
45static int force_online_offline;
46static int auto_mode;
47static int fact_enable_fail;
48static int cgroupv2;
49
50/* clos related */
51static int current_clos = -1;
52static int clos_epp = -1;
53static int clos_prop_prio = -1;
54static int clos_min = -1;
55static int clos_max = -1;
56static int clos_desired = -1;
57static int clos_priority_type;
58
59struct _cpu_map {
60	unsigned short core_id;
61	unsigned short pkg_id;
62	unsigned short die_id;
63	unsigned short punit_id;
64	unsigned short punit_cpu;
65	unsigned short punit_cpu_core;
66	unsigned short initialized;
67};
68struct _cpu_map *cpu_map;
69
70struct cpu_topology {
71	short cpu;
72	short core_id;
73	short pkg_id;
74	short die_id;
75};
76
77FILE *get_output_file(void)
78{
79	return outf;
80}
81
82int is_debug_enabled(void)
83{
84	return debug_flag;
85}
86
87void debug_printf(const char *format, ...)
88{
89	va_list args;
90
91	va_start(args, format);
92
93	if (debug_flag)
94		vprintf(format, args);
95
96	va_end(args);
97}
98
99
100int is_clx_n_platform(void)
101{
102	if (cpu_model == 0x55)
103		if (cpu_stepping == 0x6 || cpu_stepping == 0x7)
104			return 1;
105	return 0;
106}
107
108int is_skx_based_platform(void)
109{
110	if (cpu_model == 0x55)
111		return 1;
112
113	return 0;
114}
115
116int is_spr_platform(void)
117{
118	if (cpu_model == 0x8F)
119		return 1;
120
121	return 0;
122}
123
124int is_emr_platform(void)
125{
126	if (cpu_model == 0xCF)
127		return 1;
128
129	return 0;
130}
131
132
133int is_icx_platform(void)
134{
135	if (cpu_model == 0x6A || cpu_model == 0x6C)
136		return 1;
137
138	return 0;
139}
140
141static int update_cpu_model(void)
142{
143	unsigned int ebx, ecx, edx;
144	unsigned int fms, family;
145
146	__cpuid(1, fms, ebx, ecx, edx);
147	family = (fms >> 8) & 0xf;
148	cpu_model = (fms >> 4) & 0xf;
149	if (family == 6 || family == 0xf)
150		cpu_model += ((fms >> 16) & 0xf) << 4;
151
152	cpu_stepping = fms & 0xf;
153	/* only three CascadeLake-N models are supported */
154	if (is_clx_n_platform()) {
155		FILE *fp;
156		size_t n = 0;
157		char *line = NULL;
158		int ret = 1;
159
160		fp = fopen("/proc/cpuinfo", "r");
161		if (!fp)
162			err(-1, "cannot open /proc/cpuinfo\n");
163
164		while (getline(&line, &n, fp) > 0) {
165			if (strstr(line, "model name")) {
166				if (strstr(line, "6252N") ||
167				    strstr(line, "6230N") ||
168				    strstr(line, "5218N"))
169					ret = 0;
170				break;
171			}
172		}
173		free(line);
174		fclose(fp);
175		return ret;
176	}
177	return 0;
178}
179
180int api_version(void)
181{
182        return isst_platform_info.api_version;
183}
184
185/* Open a file, and exit on failure */
186static FILE *fopen_or_exit(const char *path, const char *mode)
187{
188	FILE *filep = fopen(path, mode);
189
190	if (!filep)
191		err(1, "%s: open failed", path);
192
193	return filep;
194}
195
196/* Parse a file containing a single int */
197static int parse_int_file(int fatal, const char *fmt, ...)
198{
199	va_list args;
200	char path[PATH_MAX];
201	FILE *filep;
202	int value;
203
204	va_start(args, fmt);
205	vsnprintf(path, sizeof(path), fmt, args);
206	va_end(args);
207	if (fatal) {
208		filep = fopen_or_exit(path, "r");
209	} else {
210		filep = fopen(path, "r");
211		if (!filep)
212			return -1;
213	}
214	if (fscanf(filep, "%d", &value) != 1)
215		err(1, "%s: failed to parse number from file", path);
216	fclose(filep);
217
218	return value;
219}
220
221int cpufreq_sysfs_present(void)
222{
223	DIR *dir;
224
225	dir = opendir("/sys/devices/system/cpu/cpu0/cpufreq");
226	if (dir) {
227		closedir(dir);
228		return 1;
229	}
230
231	return 0;
232}
233
234int out_format_is_json(void)
235{
236	return out_format_json;
237}
238
239static int get_stored_topology_info(int cpu, int *core_id, int *pkg_id, int *die_id)
240{
241	const char *pathname = "/var/run/isst_cpu_topology.dat";
242	struct cpu_topology cpu_top;
243	FILE *fp;
244	int ret;
245
246	fp = fopen(pathname, "rb");
247	if (!fp)
248		return -1;
249
250	ret = fseek(fp, cpu * sizeof(cpu_top), SEEK_SET);
251	if (ret)
252		goto err_ret;
253
254	ret = fread(&cpu_top, sizeof(cpu_top), 1, fp);
255	if (ret != 1) {
256		ret = -1;
257		goto err_ret;
258	}
259
260	*pkg_id = cpu_top.pkg_id;
261	*core_id = cpu_top.core_id;
262	*die_id = cpu_top.die_id;
263	ret = 0;
264
265err_ret:
266	fclose(fp);
267
268	return ret;
269}
270
271static void store_cpu_topology(void)
272{
273	const char *pathname = "/var/run/isst_cpu_topology.dat";
274	FILE *fp;
275	int i;
276
277	fp = fopen(pathname, "rb");
278	if (fp) {
279		/* Mapping already exists */
280		fclose(fp);
281		return;
282	}
283
284	fp = fopen(pathname, "wb");
285	if (!fp) {
286		fprintf(stderr, "Can't create file:%s\n", pathname);
287		return;
288	}
289
290	fprintf(stderr, "Caching topology information\n");
291
292	for (i = 0; i < topo_max_cpus; ++i) {
293		struct cpu_topology cpu_top;
294
295		cpu_top.core_id = parse_int_file(0,
296			"/sys/devices/system/cpu/cpu%d/topology/core_id", i);
297		if (cpu_top.core_id < 0)
298			cpu_top.core_id = -1;
299
300		cpu_top.pkg_id = parse_int_file(0,
301			"/sys/devices/system/cpu/cpu%d/topology/physical_package_id", i);
302		if (cpu_top.pkg_id < 0)
303			cpu_top.pkg_id = -1;
304
305		cpu_top.die_id = parse_int_file(0,
306			"/sys/devices/system/cpu/cpu%d/topology/die_id", i);
307		if (cpu_top.die_id < 0)
308			cpu_top.die_id = -1;
309
310		cpu_top.cpu = i;
311
312		if (fwrite(&cpu_top, sizeof(cpu_top), 1, fp) != 1) {
313			fprintf(stderr, "Can't write to:%s\n", pathname);
314			break;
315		}
316	}
317
318	fclose(fp);
319}
320
321static int get_physical_package_id(int cpu)
322{
323	int ret;
324
325	if (cpu < 0)
326		return -1;
327
328	if (cpu_map && cpu_map[cpu].initialized)
329		return cpu_map[cpu].pkg_id;
330
331	ret = parse_int_file(0,
332			"/sys/devices/system/cpu/cpu%d/topology/physical_package_id",
333			cpu);
334	if (ret < 0) {
335		int core_id, pkg_id, die_id;
336
337		ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id);
338		if (!ret)
339			return pkg_id;
340	}
341
342	return ret;
343}
344
345static int get_physical_core_id(int cpu)
346{
347	int ret;
348
349	if (cpu < 0)
350		return -1;
351
352	if (cpu_map && cpu_map[cpu].initialized)
353		return cpu_map[cpu].core_id;
354
355	ret = parse_int_file(0,
356			"/sys/devices/system/cpu/cpu%d/topology/core_id",
357			cpu);
358	if (ret < 0) {
359		int core_id, pkg_id, die_id;
360
361		ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id);
362		if (!ret)
363			return core_id;
364	}
365
366	return ret;
367}
368
369static int get_physical_die_id(int cpu)
370{
371	int ret;
372
373	if (cpu < 0)
374		return -1;
375
376	if (cpu_map && cpu_map[cpu].initialized)
377		return cpu_map[cpu].die_id;
378
379	ret = parse_int_file(0,
380			"/sys/devices/system/cpu/cpu%d/topology/die_id",
381			cpu);
382	if (ret < 0) {
383		int core_id, pkg_id, die_id;
384
385		ret = get_stored_topology_info(cpu, &core_id, &pkg_id, &die_id);
386		if (!ret) {
387			if (die_id < 0)
388				die_id = 0;
389
390			return die_id;
391		}
392	}
393
394	if (ret < 0)
395		ret = 0;
396
397	return ret;
398}
399
400static int get_physical_punit_id(int cpu)
401{
402	if (cpu < 0)
403		return -1;
404
405	if (cpu_map && cpu_map[cpu].initialized)
406		return cpu_map[cpu].punit_id;
407
408	return -1;
409}
410
411void set_isst_id(struct isst_id *id, int cpu)
412{
413	id->cpu = cpu;
414
415	id->pkg = get_physical_package_id(cpu);
416	if (id->pkg >= MAX_PACKAGE_COUNT)
417		id->pkg = -1;
418
419	id->die = get_physical_die_id(cpu);
420	if (id->die >= MAX_DIE_PER_PACKAGE)
421		id->die = -1;
422
423	id->punit = get_physical_punit_id(cpu);
424	if (id->punit >= MAX_PUNIT_PER_DIE)
425		id->punit = -1;
426}
427
428int is_cpu_in_power_domain(int cpu, struct isst_id *id)
429{
430	struct isst_id tid;
431
432	set_isst_id(&tid, cpu);
433
434	if (id->pkg == tid.pkg && id->die == tid.die && id->punit == tid.punit)
435		return 1;
436
437	return 0;
438}
439
440int get_cpufreq_base_freq(int cpu)
441{
442	return parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency", cpu);
443}
444
445int get_topo_max_cpus(void)
446{
447	return topo_max_cpus;
448}
449
450static unsigned int is_cpu_online(int cpu)
451{
452	char buffer[128];
453	int fd, ret;
454	unsigned char online;
455
456	snprintf(buffer, sizeof(buffer),
457		 "/sys/devices/system/cpu/cpu%d/online", cpu);
458
459	fd = open(buffer, O_RDONLY);
460	if (fd < 0)
461		return fd;
462
463	ret = read(fd, &online, sizeof(online));
464	close(fd);
465
466	if (ret == -1)
467		return ret;
468
469	if (online == '1')
470		online = 1;
471	else
472		online = 0;
473
474	return online;
475}
476
477static int get_kernel_version(int *major, int *minor)
478{
479	struct utsname buf;
480	int ret;
481
482	ret = uname(&buf);
483	if (ret)
484		return ret;
485
486	ret = sscanf(buf.release, "%d.%d", major, minor);
487	if (ret != 2)
488		return ret;
489
490	return 0;
491}
492
493#define CPU0_HOTPLUG_DEPRECATE_MAJOR_VER	6
494#define CPU0_HOTPLUG_DEPRECATE_MINOR_VER	5
495
496void set_cpu_online_offline(int cpu, int state)
497{
498	char buffer[128];
499	int fd, ret;
500
501	if (!cpu) {
502		int major, minor;
503
504		ret = get_kernel_version(&major, &minor);
505		if (!ret) {
506			if (major > CPU0_HOTPLUG_DEPRECATE_MAJOR_VER || (major == CPU0_HOTPLUG_DEPRECATE_MAJOR_VER &&
507				minor >= CPU0_HOTPLUG_DEPRECATE_MINOR_VER)) {
508				debug_printf("Ignore CPU 0 offline/online for kernel version >= %d.%d\n", major, minor);
509				debug_printf("Use cgroups to isolate CPU 0\n");
510				return;
511			}
512		}
513	}
514
515	snprintf(buffer, sizeof(buffer),
516		 "/sys/devices/system/cpu/cpu%d/online", cpu);
517
518	fd = open(buffer, O_WRONLY);
519	if (fd < 0) {
520		if (!cpu && state) {
521			fprintf(stderr, "This system is not configured for CPU 0 online/offline\n");
522			fprintf(stderr, "Ignoring online request for CPU 0 as this is already online\n");
523			return;
524		}
525		err(-1, "%s open failed", buffer);
526	}
527
528	if (state)
529		ret = write(fd, "1\n", 2);
530	else
531		ret = write(fd, "0\n", 2);
532
533	if (ret == -1)
534		perror("Online/Offline: Operation failed\n");
535
536	close(fd);
537}
538
539static void force_all_cpus_online(void)
540{
541	int i;
542
543	fprintf(stderr, "Forcing all CPUs online\n");
544
545	for (i = 0; i < topo_max_cpus; ++i)
546		set_cpu_online_offline(i, 1);
547
548	unlink("/var/run/isst_cpu_topology.dat");
549}
550
551void for_each_online_power_domain_in_set(void (*callback)(struct isst_id *, void *, void *,
552						     void *, void *),
553				    void *arg1, void *arg2, void *arg3,
554				    void *arg4)
555{
556	struct isst_id id;
557	int cpus[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE];
558	int valid_mask[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE] = {0};
559	int i, j, k;
560
561	memset(cpus, -1, sizeof(cpus));
562
563	for (i = 0; i < topo_max_cpus; ++i) {
564		int online;
565
566		if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
567			continue;
568
569		online = parse_int_file(
570			i != 0, "/sys/devices/system/cpu/cpu%d/online", i);
571		if (online < 0)
572			online = 1; /* online entry for CPU 0 needs some special configs */
573
574		if (!online)
575			continue;
576
577		set_isst_id(&id, i);
578
579		if (id.pkg < 0 || id.die < 0 || id.punit < 0)
580			continue;
581
582		valid_mask[id.pkg][id.die] = 1;
583
584		if (cpus[id.pkg][id.die][id.punit] == -1)
585			cpus[id.pkg][id.die][id.punit] = i;
586	}
587
588	for (i = 0; i < MAX_PACKAGE_COUNT; i++) {
589		for (j = 0; j < MAX_DIE_PER_PACKAGE; j++) {
590			/*
591			 * Fix me:
592			 * How to check a non-cpu die for a package/die with all cpu offlined?
593			 */
594			if (!valid_mask[i][j])
595				continue;
596			for (k = 0; k < MAX_PUNIT_PER_DIE; k++) {
597				id.cpu = cpus[i][j][k];
598				id.pkg = i;
599				id.die = j;
600				id.punit = k;
601				if (isst_is_punit_valid(&id))
602					callback(&id, arg1, arg2, arg3, arg4);
603			}
604		}
605	}
606}
607
608static void for_each_online_target_cpu_in_set(
609	void (*callback)(struct isst_id *, void *, void *, void *, void *), void *arg1,
610	void *arg2, void *arg3, void *arg4)
611{
612	int i, found = 0;
613	struct isst_id id;
614
615	for (i = 0; i < topo_max_cpus; ++i) {
616		int online;
617
618		if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
619			continue;
620		if (i)
621			online = parse_int_file(
622				1, "/sys/devices/system/cpu/cpu%d/online", i);
623		else
624			online =
625				1; /* online entry for CPU 0 needs some special configs */
626
627		set_isst_id(&id, i);
628		if (online && callback) {
629			callback(&id, arg1, arg2, arg3, arg4);
630			found = 1;
631		}
632	}
633
634	if (!found)
635		fprintf(stderr, "No valid CPU in the list\n");
636}
637
638#define BITMASK_SIZE 32
639static void set_max_cpu_num(void)
640{
641	FILE *filep;
642	unsigned long dummy;
643	int i;
644
645	topo_max_cpus = 0;
646	for (i = 0; i < 256; ++i) {
647		char path[256];
648
649		snprintf(path, sizeof(path),
650			 "/sys/devices/system/cpu/cpu%d/topology/thread_siblings", i);
651		filep = fopen(path, "r");
652		if (filep)
653			break;
654	}
655
656	if (!filep) {
657		fprintf(stderr, "Can't get max cpu number\n");
658		exit(0);
659	}
660
661	while (fscanf(filep, "%lx,", &dummy) == 1)
662		topo_max_cpus += BITMASK_SIZE;
663	fclose(filep);
664
665	debug_printf("max cpus %d\n", topo_max_cpus);
666}
667
668size_t alloc_cpu_set(cpu_set_t **cpu_set)
669{
670	cpu_set_t *_cpu_set;
671	size_t size;
672
673	_cpu_set = CPU_ALLOC((topo_max_cpus + 1));
674	if (_cpu_set == NULL)
675		err(3, "CPU_ALLOC");
676	size = CPU_ALLOC_SIZE((topo_max_cpus + 1));
677	CPU_ZERO_S(size, _cpu_set);
678
679	*cpu_set = _cpu_set;
680	return size;
681}
682
683void free_cpu_set(cpu_set_t *cpu_set)
684{
685	CPU_FREE(cpu_set);
686}
687
688static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE];
689
690int get_max_punit_core_id(struct isst_id *id)
691{
692	int max_id = 0;
693	int i;
694
695	for (i = 0; i < topo_max_cpus; ++i)
696	{
697		if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
698			continue;
699
700		if (is_cpu_in_power_domain(i, id) &&
701		    cpu_map[i].punit_cpu_core > max_id)
702			max_id = cpu_map[i].punit_cpu_core;
703	}
704
705	return max_id;
706}
707
708int get_cpu_count(struct isst_id *id)
709{
710	if (id->pkg < 0 || id->die < 0 || id->punit < 0)
711		return 0;
712
713	return cpu_cnt[id->pkg][id->die][id->punit];
714}
715
716static void update_punit_cpu_info(__u32 physical_cpu, struct _cpu_map *cpu_map)
717{
718	if (api_version() > 1) {
719		/*
720		 * MSR 0x54 format
721		 *	[15:11] PM_DOMAIN_ID
722		 *	[10:3] MODULE_ID (aka IDI_AGENT_ID)
723		 *	[2:0] LP_ID (We don't care about these bits we only
724		 *		care die and core id
725		 *	For Atom:
726		 *	[2] Always 0
727		 *	[1:0] core ID within module
728		 *	For Core
729		 *	[2:1] Always 0
730		 *	[0] thread ID
731		 */
732		cpu_map->punit_id = (physical_cpu >> 11) & 0x1f;
733		cpu_map->punit_cpu_core = (physical_cpu >> 3) & 0xff;
734		cpu_map->punit_cpu = physical_cpu & 0x7ff;
735	} else {
736		int punit_id;
737
738		/*
739		 * MSR 0x53 format
740		 * Format
741		 *      Bit 0 – thread ID
742		 *      Bit 8:1 – core ID
743		 *      Bit 13:9 – punit ID
744		 */
745		cpu_map->punit_cpu = physical_cpu & 0x1ff;
746		cpu_map->punit_cpu_core = (cpu_map->punit_cpu >> 1); // shift to get core id
747		punit_id = (physical_cpu >> 9) & 0x1f;
748
749		if (punit_id >= MAX_PUNIT_PER_DIE)
750			punit_id = 0;
751
752		cpu_map->punit_id = punit_id;
753	}
754}
755
756static void create_cpu_map(void)
757{
758	const char *pathname = "/dev/isst_interface";
759	size_t size;
760	DIR *dir;
761	int i, fd = 0;
762	struct isst_if_cpu_maps map;
763
764	/* Use calloc to make sure the memory is initialized to Zero */
765	cpu_map = calloc(topo_max_cpus, sizeof(*cpu_map));
766	if (!cpu_map)
767		err(3, "cpumap");
768
769	fd = open(pathname, O_RDWR);
770	if (fd < 0 && !is_clx_n_platform())
771		err(-1, "%s open failed", pathname);
772
773	size = alloc_cpu_set(&present_cpumask);
774	present_cpumask_size = size;
775
776	for (i = 0; i < topo_max_cpus; ++i) {
777		char buffer[256];
778		int pkg_id, die_id, core_id, punit_id;
779
780		/* check if CPU is online */
781		snprintf(buffer, sizeof(buffer),
782			 "/sys/devices/system/cpu/cpu%d", i);
783		dir = opendir(buffer);
784		if (!dir)
785			continue;
786		closedir(dir);
787
788		CPU_SET_S(i, size, present_cpumask);
789
790		pkg_id = get_physical_package_id(i);
791		die_id = get_physical_die_id(i);
792		core_id = get_physical_core_id(i);
793
794		if (pkg_id < 0 || die_id < 0 || core_id < 0)
795			continue;
796
797		cpu_map[i].pkg_id = pkg_id;
798		cpu_map[i].die_id = die_id;
799		cpu_map[i].core_id = core_id;
800
801
802		punit_id = 0;
803
804		if (fd >= 0) {
805			map.cmd_count = 1;
806			map.cpu_map[0].logical_cpu = i;
807			debug_printf(" map logical_cpu:%d\n",
808				     map.cpu_map[0].logical_cpu);
809			if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) {
810				perror("ISST_IF_GET_PHY_ID");
811				fprintf(outf, "Error: map logical_cpu:%d\n",
812					map.cpu_map[0].logical_cpu);
813			} else {
814				update_punit_cpu_info(map.cpu_map[0].physical_cpu, &cpu_map[i]);
815				punit_id = cpu_map[i].punit_id;
816			}
817		}
818		cpu_map[i].initialized = 1;
819
820		cpu_cnt[pkg_id][die_id][punit_id]++;
821
822		debug_printf(
823			"map logical_cpu:%d core: %d die:%d pkg:%d punit:%d punit_cpu:%d punit_core:%d\n",
824			i, cpu_map[i].core_id, cpu_map[i].die_id,
825			cpu_map[i].pkg_id, cpu_map[i].punit_id,
826			cpu_map[i].punit_cpu, cpu_map[i].punit_cpu_core);
827	}
828	if (fd >= 0)
829		close(fd);
830
831	size = alloc_cpu_set(&target_cpumask);
832	target_cpumask_size = size;
833	for (i = 0; i < max_target_cpus; ++i) {
834		if (!CPU_ISSET_S(target_cpus[i], present_cpumask_size,
835				 present_cpumask))
836			continue;
837
838		CPU_SET_S(target_cpus[i], size, target_cpumask);
839	}
840}
841
842void set_cpu_mask_from_punit_coremask(struct isst_id *id, unsigned long long core_mask,
843				      size_t core_cpumask_size,
844				      cpu_set_t *core_cpumask, int *cpu_cnt)
845{
846	int i, cnt = 0;
847
848	if (id->cpu < 0)
849		return;
850
851	*cpu_cnt = 0;
852
853	for (i = 0; i < 64; ++i) {
854		if (core_mask & BIT_ULL(i)) {
855			int j;
856
857			for (j = 0; j < topo_max_cpus; ++j) {
858				if (!CPU_ISSET_S(j, present_cpumask_size, present_cpumask))
859					continue;
860
861				if (is_cpu_in_power_domain(j, id) &&
862				    cpu_map[j].punit_cpu_core == i) {
863					CPU_SET_S(j, core_cpumask_size,
864						  core_cpumask);
865					++cnt;
866				}
867			}
868		}
869	}
870
871	*cpu_cnt = cnt;
872}
873
874int find_phy_core_num(int logical_cpu)
875{
876	if (logical_cpu < topo_max_cpus)
877		return cpu_map[logical_cpu].punit_cpu_core;
878
879	return -EINVAL;
880}
881
882int use_cgroupv2(void)
883{
884	return cgroupv2;
885}
886
887int enable_cpuset_controller(void)
888{
889	int fd, ret;
890
891	fd = open("/sys/fs/cgroup/cgroup.subtree_control", O_RDWR, 0);
892	if (fd < 0) {
893		debug_printf("Can't activate cpuset controller\n");
894		debug_printf("Either you are not root user or CGroup v2 is not supported\n");
895		return fd;
896	}
897
898	ret = write(fd, " +cpuset", strlen(" +cpuset"));
899	close(fd);
900
901	if (ret == -1) {
902		debug_printf("Can't activate cpuset controller: Write failed\n");
903		return ret;
904	}
905
906	return 0;
907}
908
909int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level)
910{
911	int i, first, curr_index, index, ret, fd;
912	static char str[512], dir_name[64];
913	static char cpuset_cpus[128];
914	int str_len = sizeof(str);
915	DIR *dir;
916
917	snprintf(dir_name, sizeof(dir_name), "/sys/fs/cgroup/%d-%d-%d", id->pkg, id->die, id->punit);
918	dir = opendir(dir_name);
919	if (!dir) {
920		ret = mkdir(dir_name, 0744);
921		if (ret) {
922			debug_printf("Can't create dir:%s errno:%d\n", dir_name, errno);
923			return ret;
924		}
925	}
926	closedir(dir);
927
928	if (!level) {
929		sprintf(cpuset_cpus, "%s/cpuset.cpus.partition", dir_name);
930
931		fd = open(cpuset_cpus, O_RDWR, 0);
932		if (fd < 0) {
933			return fd;
934		}
935
936		ret = write(fd, "member", strlen("member"));
937		if (ret == -1) {
938			printf("Can't update to member\n");
939			return ret;
940		}
941
942		return 0;
943	}
944
945	if (!CPU_COUNT_S(mask_size, cpu_mask)) {
946		return -1;
947	}
948
949	curr_index = 0;
950	first = 1;
951	str[0] = '\0';
952	for (i = 0; i < get_topo_max_cpus(); ++i) {
953		if (!is_cpu_in_power_domain(i, id))
954			continue;
955
956		if (CPU_ISSET_S(i, mask_size, cpu_mask))
957			continue;
958
959		if (!first) {
960			index = snprintf(&str[curr_index],
961					 str_len - curr_index, ",");
962			curr_index += index;
963			if (curr_index >= str_len)
964				break;
965		}
966		index = snprintf(&str[curr_index], str_len - curr_index, "%d",
967				 i);
968		curr_index += index;
969		if (curr_index >= str_len)
970			break;
971		first = 0;
972	}
973
974	debug_printf("isolated CPUs list: package:%d curr_index:%d [%s]\n", id->pkg, curr_index ,str);
975
976	snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus", dir_name);
977
978	fd = open(cpuset_cpus, O_RDWR, 0);
979	if (fd < 0) {
980		return fd;
981	}
982
983	ret = write(fd, str, strlen(str));
984	close(fd);
985
986	if (ret == -1) {
987		debug_printf("Can't activate cpuset controller: Write failed\n");
988		return ret;
989	}
990
991	snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus.partition", dir_name);
992
993	fd = open(cpuset_cpus, O_RDWR, 0);
994	if (fd < 0) {
995		return fd;
996	}
997
998	ret = write(fd, "isolated", strlen("isolated"));
999	if (ret == -1) {
1000		debug_printf("Can't update to isolated\n");
1001		ret = write(fd, "root", strlen("root"));
1002		if (ret == -1)
1003			debug_printf("Can't update to root\n");
1004	}
1005
1006	close(fd);
1007
1008	if (ret < 0)
1009		return ret;
1010
1011	return 0;
1012}
1013
1014static int isst_fill_platform_info(void)
1015{
1016	const char *pathname = "/dev/isst_interface";
1017	int fd;
1018
1019	if (is_clx_n_platform()) {
1020		isst_platform_info.api_version = 1;
1021		goto set_platform_ops;
1022	}
1023
1024	fd = open(pathname, O_RDWR);
1025	if (fd < 0)
1026		err(-1, "%s open failed", pathname);
1027
1028	if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &isst_platform_info) == -1) {
1029		perror("ISST_IF_GET_PLATFORM_INFO");
1030		close(fd);
1031		return -1;
1032	}
1033
1034	close(fd);
1035
1036	if (isst_platform_info.api_version > supported_api_ver) {
1037		printf("Incompatible API versions; Upgrade of tool is required\n");
1038		return -1;
1039	}
1040
1041set_platform_ops:
1042	if (isst_set_platform_ops(isst_platform_info.api_version)) {
1043		fprintf(stderr, "Failed to set platform callbacks\n");
1044		exit(0);
1045	}
1046	return 0;
1047}
1048
1049void get_isst_status(struct isst_id *id, void *arg1, void *arg2, void *arg3, void *arg4)
1050{
1051	struct isst_pkg_ctdp pkg_dev;
1052	struct isst_id *tid = (struct isst_id *)arg2;
1053	int *mask = (int *)arg3;
1054	int *max_level = (int *)arg4;
1055	int j, ret;
1056
1057	/* Only check the first cpu power domain */
1058	if (id->cpu < 0 || tid->cpu >= 0)
1059		return;
1060
1061	ret = isst_get_ctdp_levels(id, &pkg_dev);
1062	if (ret)
1063		return;
1064
1065	if (pkg_dev.enabled)
1066		*mask |= BIT(0);
1067
1068	if (pkg_dev.locked)
1069		*mask |= BIT(1);
1070
1071	if (*max_level < pkg_dev.levels)
1072		*max_level = pkg_dev.levels;
1073
1074	for (j = 0; j <= pkg_dev.levels; ++j) {
1075		struct isst_pkg_ctdp_level_info ctdp_level;
1076
1077		ret = isst_get_ctdp_control(id, j, &ctdp_level);
1078		if (ret)
1079			continue;
1080
1081		if (ctdp_level.fact_support)
1082			*mask |= BIT(2);
1083
1084		if (ctdp_level.pbf_support)
1085			*mask |= BIT(3);
1086	}
1087
1088	tid->cpu = id->cpu;
1089	tid->pkg = id->pkg;
1090	tid->die = id->die;
1091	tid->punit = id->punit;
1092}
1093
1094static void isst_print_extended_platform_info(void)
1095{
1096	int cp_state, cp_cap;
1097	struct isst_id id;
1098	int mask = 0, max_level = 0;
1099
1100	id.cpu = -1;
1101	for_each_online_power_domain_in_set(get_isst_status, NULL, &id, &mask, &max_level);
1102
1103	if (mask & BIT(0)) {
1104		fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is supported\n");
1105	} else {
1106		fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is not supported\n");
1107		fprintf(outf, "Only performance level 0 (base level) is present\n");
1108	}
1109
1110	if (mask & BIT(1))
1111		fprintf(outf, "TDP level change control is locked\n");
1112	else
1113		fprintf(outf, "TDP level change control is unlocked, max level: %d\n", max_level);
1114
1115	if (mask & BIT(2))
1116		fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is supported\n");
1117	else
1118		fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is not supported\n");
1119
1120	if (mask & BIT(3))
1121		fprintf(outf, "Intel(R) SST-BF (feature base-freq) is supported\n");
1122	else
1123		fprintf(outf, "Intel(R) SST-BF (feature base-freq) is not supported\n");
1124
1125	if (isst_read_pm_config(&id, &cp_state, &cp_cap)) {
1126		fprintf(outf, "Intel(R) SST-CP (feature core-power) status is unknown\n");
1127		return;
1128	}
1129
1130	if (cp_cap)
1131		fprintf(outf, "Intel(R) SST-CP (feature core-power) is supported\n");
1132	else
1133		fprintf(outf, "Intel(R) SST-CP (feature core-power) is not supported\n");
1134}
1135
1136static void isst_print_platform_information(void)
1137{
1138	if (is_clx_n_platform()) {
1139		fprintf(stderr, "\nThis option in not supported on this platform\n");
1140		exit(0);
1141	}
1142
1143	/* Early initialization to create working cpu_map */
1144	set_max_cpu_num();
1145	create_cpu_map();
1146
1147	fprintf(outf, "Platform: API version : %d\n",
1148		isst_platform_info.api_version);
1149	fprintf(outf, "Platform: Driver version : %d\n",
1150		isst_platform_info.driver_version);
1151	fprintf(outf, "Platform: mbox supported : %d\n",
1152		isst_platform_info.mbox_supported);
1153	fprintf(outf, "Platform: mmio supported : %d\n",
1154		isst_platform_info.mmio_supported);
1155	isst_print_extended_platform_info();
1156
1157	exit(0);
1158}
1159
1160static char *local_str0, *local_str1;
1161static void exec_on_get_ctdp_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
1162				 void *arg4)
1163{
1164	int (*fn_ptr)(struct isst_id *id, void *arg);
1165	int ret;
1166
1167	fn_ptr = arg1;
1168	ret = fn_ptr(id, arg2);
1169	if (ret)
1170		isst_display_error_info_message(1, "get_tdp_* failed", 0, 0);
1171	else
1172		isst_ctdp_display_core_info(id, outf, arg3,
1173					    *(unsigned int *)arg4,
1174					    local_str0, local_str1);
1175}
1176
1177#define _get_tdp_level(desc, suffix, object, help, str0, str1)			\
1178	static void get_tdp_##object(int arg)                                    \
1179	{                                                                         \
1180		struct isst_pkg_ctdp ctdp;                                        \
1181\
1182		if (cmd_help) {                                                   \
1183			fprintf(stderr,                                           \
1184				"Print %s [No command arguments are required]\n", \
1185				help);                                            \
1186			exit(0);                                                  \
1187		}                                                                 \
1188		local_str0 = str0;						  \
1189		local_str1 = str1;						  \
1190		isst_ctdp_display_information_start(outf);                        \
1191		if (max_target_cpus)                                              \
1192			for_each_online_target_cpu_in_set(                        \
1193				exec_on_get_ctdp_cpu, isst_get_ctdp_##suffix,     \
1194				&ctdp, desc, &ctdp.object);                       \
1195		else                                                              \
1196			for_each_online_power_domain_in_set(exec_on_get_ctdp_cpu,      \
1197						       isst_get_ctdp_##suffix,    \
1198						       &ctdp, desc,               \
1199						       &ctdp.object);             \
1200		isst_ctdp_display_information_end(outf);                          \
1201	}
1202
1203_get_tdp_level("get-config-levels", levels, levels, "Max TDP level", NULL, NULL);
1204_get_tdp_level("get-config-version", levels, version, "TDP version", NULL, NULL);
1205_get_tdp_level("get-config-enabled", levels, enabled, "perf-profile enable status", "disabled", "enabled");
1206_get_tdp_level("get-config-current_level", levels, current_level,
1207	       "Current TDP Level", NULL, NULL);
1208_get_tdp_level("get-lock-status", levels, locked, "TDP lock status", "unlocked", "locked");
1209
1210struct isst_pkg_ctdp clx_n_pkg_dev;
1211
1212static int clx_n_get_base_ratio(void)
1213{
1214	FILE *fp;
1215	char *begin, *end, *line = NULL;
1216	char number[5];
1217	float value = 0;
1218	size_t n = 0;
1219
1220	fp = fopen("/proc/cpuinfo", "r");
1221	if (!fp)
1222		err(-1, "cannot open /proc/cpuinfo\n");
1223
1224	while (getline(&line, &n, fp) > 0) {
1225		if (strstr(line, "model name")) {
1226			/* this is true for CascadeLake-N */
1227			begin = strstr(line, "@ ") + 2;
1228			end = strstr(line, "GHz");
1229			strncpy(number, begin, end - begin);
1230			value = atof(number) * 10;
1231			break;
1232		}
1233	}
1234	free(line);
1235	fclose(fp);
1236
1237	return (int)(value);
1238}
1239
1240static int clx_n_config(struct isst_id *id)
1241{
1242	int i, ret;
1243	unsigned long cpu_bf;
1244	struct isst_pkg_ctdp_level_info *ctdp_level;
1245	struct isst_pbf_info *pbf_info;
1246
1247	ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
1248	pbf_info = &ctdp_level->pbf_info;
1249	ctdp_level->core_cpumask_size =
1250			alloc_cpu_set(&ctdp_level->core_cpumask);
1251
1252	/* find the frequency base ratio */
1253	ctdp_level->tdp_ratio = clx_n_get_base_ratio();
1254	if (ctdp_level->tdp_ratio == 0) {
1255		debug_printf("CLX: cn base ratio is zero\n");
1256		ret = -1;
1257		goto error_ret;
1258	}
1259
1260	/* find the high and low priority frequencies */
1261	pbf_info->p1_high = 0;
1262	pbf_info->p1_low = ~0;
1263
1264	for (i = 0; i < topo_max_cpus; i++) {
1265		if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
1266			continue;
1267
1268		if (!is_cpu_in_power_domain(i, id))
1269			continue;
1270
1271		CPU_SET_S(i, ctdp_level->core_cpumask_size,
1272			  ctdp_level->core_cpumask);
1273
1274		cpu_bf = parse_int_file(1,
1275			"/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency",
1276					i);
1277		if (cpu_bf > pbf_info->p1_high)
1278			pbf_info->p1_high = cpu_bf;
1279		if (cpu_bf < pbf_info->p1_low)
1280			pbf_info->p1_low = cpu_bf;
1281	}
1282
1283	if (pbf_info->p1_high == ~0UL) {
1284		debug_printf("CLX: maximum base frequency not set\n");
1285		ret = -1;
1286		goto error_ret;
1287	}
1288
1289	if (pbf_info->p1_low == 0) {
1290		debug_printf("CLX: minimum base frequency not set\n");
1291		ret = -1;
1292		goto error_ret;
1293	}
1294
1295	/* convert frequencies back to ratios */
1296	pbf_info->p1_high = pbf_info->p1_high / 100000;
1297	pbf_info->p1_low = pbf_info->p1_low / 100000;
1298
1299	/* create high priority cpu mask */
1300	pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask);
1301	for (i = 0; i < topo_max_cpus; i++) {
1302		if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
1303			continue;
1304
1305		if (!is_cpu_in_power_domain(i, id))
1306			continue;
1307
1308		cpu_bf = parse_int_file(1,
1309			"/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency",
1310					i);
1311		cpu_bf = cpu_bf / 100000;
1312		if (cpu_bf == pbf_info->p1_high)
1313			CPU_SET_S(i, pbf_info->core_cpumask_size,
1314				  pbf_info->core_cpumask);
1315	}
1316
1317	/* extra ctdp & pbf struct parameters */
1318	ctdp_level->processed = 1;
1319	ctdp_level->pbf_support = 1; /* PBF is always supported and enabled */
1320	ctdp_level->pbf_enabled = 1;
1321	ctdp_level->fact_support = 0; /* FACT is never supported */
1322	ctdp_level->fact_enabled = 0;
1323
1324	return 0;
1325
1326error_ret:
1327	free_cpu_set(ctdp_level->core_cpumask);
1328	return ret;
1329}
1330
1331static void dump_clx_n_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
1332				   void *arg3, void *arg4)
1333{
1334	int ret;
1335
1336	if (tdp_level != 0xff && tdp_level != 0) {
1337		isst_display_error_info_message(1, "Invalid level", 1, tdp_level);
1338		exit(0);
1339	}
1340
1341	ret = clx_n_config(id);
1342	if (ret) {
1343		debug_printf("clx_n_config failed");
1344	} else {
1345		struct isst_pkg_ctdp_level_info *ctdp_level;
1346		struct isst_pbf_info *pbf_info;
1347
1348		ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
1349		pbf_info = &ctdp_level->pbf_info;
1350		clx_n_pkg_dev.processed = 1;
1351		isst_ctdp_display_information(id, outf, tdp_level, &clx_n_pkg_dev);
1352		free_cpu_set(ctdp_level->core_cpumask);
1353		free_cpu_set(pbf_info->core_cpumask);
1354	}
1355}
1356
1357static void dump_isst_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
1358				     void *arg3, void *arg4)
1359{
1360	struct isst_pkg_ctdp pkg_dev;
1361	int ret;
1362
1363	memset(&pkg_dev, 0, sizeof(pkg_dev));
1364	ret = isst_get_process_ctdp(id, tdp_level, &pkg_dev);
1365	if (ret) {
1366		isst_display_error_info_message(1, "Failed to get perf-profile info on cpu", 1, id->cpu);
1367		isst_ctdp_display_information_end(outf);
1368		exit(1);
1369	} else {
1370		isst_ctdp_display_information(id, outf, tdp_level, &pkg_dev);
1371		isst_get_process_ctdp_complete(id, &pkg_dev);
1372	}
1373}
1374
1375static void dump_isst_config(int arg)
1376{
1377	void *fn;
1378
1379	if (cmd_help) {
1380		fprintf(stderr,
1381			"Print Intel(R) Speed Select Technology Performance profile configuration\n");
1382		fprintf(stderr,
1383			"including base frequency and turbo frequency configurations\n");
1384		fprintf(stderr, "Optional: -l|--level : Specify tdp level\n");
1385		fprintf(stderr,
1386			"\tIf no arguments, dump information for all TDP levels\n");
1387		exit(0);
1388	}
1389
1390	if (!is_clx_n_platform())
1391		fn = dump_isst_config_for_cpu;
1392	else
1393		fn = dump_clx_n_config_for_cpu;
1394
1395	isst_ctdp_display_information_start(outf);
1396
1397	if (max_target_cpus)
1398		for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL);
1399	else
1400		for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL);
1401
1402	isst_ctdp_display_information_end(outf);
1403}
1404
1405static void adjust_scaling_max_from_base_freq(int cpu);
1406
1407static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
1408				  void *arg4)
1409{
1410	struct isst_pkg_ctdp pkg_dev;
1411	int ret;
1412
1413	ret = isst_get_ctdp_levels(id, &pkg_dev);
1414	if (ret) {
1415		isst_display_error_info_message(1, "Get TDP level failed", 0, 0);
1416		isst_ctdp_display_information_end(outf);
1417		exit(1);
1418	}
1419
1420	if (pkg_dev.current_level == tdp_level) {
1421		debug_printf("TDP level already set. Skipped\n");
1422		goto display_result;
1423	}
1424
1425	ret = isst_set_tdp_level(id, tdp_level);
1426	if (ret) {
1427		isst_display_error_info_message(1, "Set TDP level failed", 0, 0);
1428		isst_ctdp_display_information_end(outf);
1429		exit(1);
1430	}
1431
1432display_result:
1433	isst_display_result(id, outf, "perf-profile", "set_tdp_level", ret);
1434	if (force_online_offline && id->cpu >= 0) {
1435		struct isst_pkg_ctdp_level_info ctdp_level;
1436
1437		/* Wait for updated base frequencies */
1438		usleep(2000);
1439
1440		/* Adjusting uncore freq */
1441		isst_adjust_uncore_freq(id, tdp_level, &ctdp_level);
1442
1443		fprintf(stderr, "Option is set to online/offline\n");
1444		ctdp_level.core_cpumask_size =
1445			alloc_cpu_set(&ctdp_level.core_cpumask);
1446		ret = isst_get_coremask_info(id, tdp_level, &ctdp_level);
1447		if (ret) {
1448			isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0);
1449			goto free_mask;
1450		}
1451
1452		if (use_cgroupv2()) {
1453			int ret;
1454
1455			fprintf(stderr, "Using cgroup v2 in lieu of online/offline\n");
1456			ret = enable_cpuset_controller();
1457			if (ret)
1458				goto use_offline;
1459
1460			ret = isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, tdp_level);
1461			if (ret)
1462				goto use_offline;
1463
1464			goto free_mask;
1465		}
1466
1467use_offline:
1468		if (ctdp_level.cpu_count) {
1469			int i, max_cpus = get_topo_max_cpus();
1470			for (i = 0; i < max_cpus; ++i) {
1471				if (!is_cpu_in_power_domain(i, id))
1472					continue;
1473				if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) {
1474					fprintf(stderr, "online cpu %d\n", i);
1475					set_cpu_online_offline(i, 1);
1476					adjust_scaling_max_from_base_freq(i);
1477				} else {
1478					fprintf(stderr, "offline cpu %d\n", i);
1479					set_cpu_online_offline(i, 0);
1480				}
1481			}
1482		}
1483free_mask:
1484		free_cpu_set(ctdp_level.core_cpumask);
1485	}
1486}
1487
1488static void set_tdp_level(int arg)
1489{
1490	if (cmd_help) {
1491		fprintf(stderr, "Set Config TDP level\n");
1492		fprintf(stderr,
1493			"\t Arguments: -l|--level : Specify tdp level\n");
1494		fprintf(stderr,
1495			"\t Optional Arguments: -o | online : online/offline for the tdp level\n");
1496		fprintf(stderr,
1497			"\t  online/offline operation has limitations, refer to Linux hotplug documentation\n");
1498		exit(0);
1499	}
1500
1501	if (tdp_level == 0xff) {
1502		isst_display_error_info_message(1, "Invalid command: specify tdp_level", 0, 0);
1503		exit(1);
1504	}
1505	isst_ctdp_display_information_start(outf);
1506	if (max_target_cpus)
1507		for_each_online_target_cpu_in_set(set_tdp_level_for_cpu, NULL,
1508						  NULL, NULL, NULL);
1509	else
1510		for_each_online_power_domain_in_set(set_tdp_level_for_cpu, NULL,
1511					       NULL, NULL, NULL);
1512	isst_ctdp_display_information_end(outf);
1513}
1514
1515static void clx_n_dump_pbf_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
1516				       void *arg3, void *arg4)
1517{
1518	int ret;
1519
1520	ret = clx_n_config(id);
1521	if (ret) {
1522		isst_display_error_info_message(1, "clx_n_config failed", 0, 0);
1523	} else {
1524		struct isst_pkg_ctdp_level_info *ctdp_level;
1525		struct isst_pbf_info *pbf_info;
1526
1527		ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
1528		pbf_info = &ctdp_level->pbf_info;
1529		isst_pbf_display_information(id, outf, tdp_level, pbf_info);
1530		free_cpu_set(ctdp_level->core_cpumask);
1531		free_cpu_set(pbf_info->core_cpumask);
1532	}
1533}
1534
1535static void dump_pbf_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
1536				    void *arg4)
1537{
1538	struct isst_pbf_info pbf_info;
1539	int ret;
1540
1541	ret = isst_get_pbf_info(id, tdp_level, &pbf_info);
1542	if (ret) {
1543		isst_display_error_info_message(1, "Failed to get base-freq info at this level", 1, tdp_level);
1544		isst_ctdp_display_information_end(outf);
1545		exit(1);
1546	} else {
1547		isst_pbf_display_information(id, outf, tdp_level, &pbf_info);
1548		free_cpu_set(pbf_info.core_cpumask);
1549	}
1550}
1551
1552static void dump_pbf_config(int arg)
1553{
1554	void *fn;
1555
1556	if (cmd_help) {
1557		fprintf(stderr,
1558			"Print Intel(R) Speed Select Technology base frequency configuration for a TDP level\n");
1559		fprintf(stderr,
1560			"\tArguments: -l|--level : Specify tdp level\n");
1561		exit(0);
1562	}
1563
1564	if (tdp_level == 0xff) {
1565		isst_display_error_info_message(1, "Invalid command: specify tdp_level", 0, 0);
1566		exit(1);
1567	}
1568
1569	if (!is_clx_n_platform())
1570		fn = dump_pbf_config_for_cpu;
1571	else
1572		fn = clx_n_dump_pbf_config_for_cpu;
1573
1574	isst_ctdp_display_information_start(outf);
1575
1576	if (max_target_cpus)
1577		for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL);
1578	else
1579		for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL);
1580
1581	isst_ctdp_display_information_end(outf);
1582}
1583
1584static int set_clos_param(struct isst_id *id, int clos, int epp, int wt, int min, int max)
1585{
1586	struct isst_clos_config clos_config;
1587	int ret;
1588
1589	ret = isst_pm_get_clos(id, clos, &clos_config);
1590	if (ret) {
1591		isst_display_error_info_message(1, "isst_pm_get_clos failed", 0, 0);
1592		return ret;
1593	}
1594	clos_config.clos_min = min;
1595	clos_config.clos_max = max;
1596	clos_config.epp = epp;
1597	clos_config.clos_prop_prio = wt;
1598	ret = isst_set_clos(id, clos, &clos_config);
1599	if (ret) {
1600		isst_display_error_info_message(1, "isst_set_clos failed", 0, 0);
1601		return ret;
1602	}
1603
1604	return 0;
1605}
1606
1607static int set_cpufreq_scaling_min_max(int cpu, int max, int freq)
1608{
1609	char buffer[128], freq_str[16];
1610	int fd, ret, len;
1611
1612	if (max)
1613		snprintf(buffer, sizeof(buffer),
1614			 "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
1615	else
1616		snprintf(buffer, sizeof(buffer),
1617			 "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
1618
1619	fd = open(buffer, O_WRONLY);
1620	if (fd < 0)
1621		return fd;
1622
1623	snprintf(freq_str, sizeof(freq_str), "%d", freq);
1624	len = strlen(freq_str);
1625	ret = write(fd, freq_str, len);
1626	if (ret == -1) {
1627		close(fd);
1628		return ret;
1629	}
1630	close(fd);
1631
1632	return 0;
1633}
1634
1635static int no_turbo(void)
1636{
1637	return parse_int_file(0, "/sys/devices/system/cpu/intel_pstate/no_turbo");
1638}
1639
1640static void adjust_scaling_max_from_base_freq(int cpu)
1641{
1642	int base_freq, scaling_max_freq;
1643
1644	scaling_max_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
1645	base_freq = get_cpufreq_base_freq(cpu);
1646	if (scaling_max_freq < base_freq || no_turbo())
1647		set_cpufreq_scaling_min_max(cpu, 1, base_freq);
1648}
1649
1650static void adjust_scaling_min_from_base_freq(int cpu)
1651{
1652	int base_freq, scaling_min_freq;
1653
1654	scaling_min_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
1655	base_freq = get_cpufreq_base_freq(cpu);
1656	if (scaling_min_freq < base_freq)
1657		set_cpufreq_scaling_min_max(cpu, 0, base_freq);
1658}
1659
1660static int set_clx_pbf_cpufreq_scaling_min_max(struct isst_id *id)
1661{
1662	struct isst_pkg_ctdp_level_info *ctdp_level;
1663	struct isst_pbf_info *pbf_info;
1664	int i, freq, freq_high, freq_low;
1665	int ret;
1666
1667	ret = clx_n_config(id);
1668	if (ret) {
1669		debug_printf("cpufreq_scaling_min_max failed for CLX");
1670		return ret;
1671	}
1672
1673	ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
1674	pbf_info = &ctdp_level->pbf_info;
1675	freq_high = pbf_info->p1_high * 100000;
1676	freq_low = pbf_info->p1_low * 100000;
1677
1678	for (i = 0; i < get_topo_max_cpus(); ++i) {
1679		if (!is_cpu_in_power_domain(i, id))
1680			continue;
1681
1682		if (CPU_ISSET_S(i, pbf_info->core_cpumask_size,
1683				  pbf_info->core_cpumask))
1684			freq = freq_high;
1685		else
1686			freq = freq_low;
1687
1688		set_cpufreq_scaling_min_max(i, 1, freq);
1689		set_cpufreq_scaling_min_max(i, 0, freq);
1690	}
1691
1692	return 0;
1693}
1694
1695static int set_cpufreq_scaling_min_max_from_cpuinfo(int cpu, int cpuinfo_max, int scaling_max)
1696{
1697	char buffer[128], min_freq[16];
1698	int fd, ret, len;
1699
1700	if (!CPU_ISSET_S(cpu, present_cpumask_size, present_cpumask))
1701		return -1;
1702
1703	if (cpuinfo_max)
1704		snprintf(buffer, sizeof(buffer),
1705			 "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu);
1706	else
1707		snprintf(buffer, sizeof(buffer),
1708			 "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_min_freq", cpu);
1709
1710	fd = open(buffer, O_RDONLY);
1711	if (fd < 0)
1712		return fd;
1713
1714	len = read(fd, min_freq, sizeof(min_freq));
1715	close(fd);
1716
1717	if (len < 0)
1718		return len;
1719
1720	if (scaling_max)
1721		snprintf(buffer, sizeof(buffer),
1722			 "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
1723	else
1724		snprintf(buffer, sizeof(buffer),
1725			 "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
1726
1727	fd = open(buffer, O_WRONLY);
1728	if (fd < 0)
1729		return fd;
1730
1731	min_freq[15] = '\0';
1732	len = strlen(min_freq);
1733	ret = write(fd, min_freq, len);
1734	if (ret == -1) {
1735		close(fd);
1736		return ret;
1737	}
1738	close(fd);
1739
1740	return 0;
1741}
1742
1743static void set_scaling_min_to_cpuinfo_max(struct isst_id *id)
1744{
1745	int i;
1746
1747	if (id->cpu < 0)
1748		return;
1749
1750	for (i = 0; i < get_topo_max_cpus(); ++i) {
1751		if (!is_cpu_in_power_domain(i, id))
1752			continue;
1753
1754		if (is_cpu_online(i) != 1)
1755			continue;
1756
1757		adjust_scaling_max_from_base_freq(i);
1758		set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 0);
1759		adjust_scaling_min_from_base_freq(i);
1760	}
1761}
1762
1763static void set_scaling_min_to_cpuinfo_min(struct isst_id *id)
1764{
1765	int i;
1766
1767	if (id->cpu < 0)
1768		return;
1769
1770	for (i = 0; i < get_topo_max_cpus(); ++i) {
1771		if (!is_cpu_in_power_domain(i, id))
1772			continue;
1773
1774		if (is_cpu_online(i) != 1)
1775			continue;
1776
1777		adjust_scaling_max_from_base_freq(i);
1778		set_cpufreq_scaling_min_max_from_cpuinfo(i, 0, 0);
1779	}
1780}
1781
1782static void set_scaling_max_to_cpuinfo_max(struct isst_id *id)
1783{
1784	int i;
1785
1786	for (i = 0; i < get_topo_max_cpus(); ++i) {
1787		if (!is_cpu_in_power_domain(i, id))
1788			continue;
1789
1790		set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 1);
1791	}
1792}
1793
1794static int set_core_priority_and_min(struct isst_id *id, int mask_size,
1795				     cpu_set_t *cpu_mask, int min_high,
1796				     int min_low)
1797{
1798	int ret, i;
1799
1800	if (!CPU_COUNT_S(mask_size, cpu_mask))
1801		return -1;
1802
1803	ret = set_clos_param(id, 0, 0, 0, min_high, 0xff);
1804	if (ret)
1805		return ret;
1806
1807	ret = set_clos_param(id, 1, 15, 15, min_low, 0xff);
1808	if (ret)
1809		return ret;
1810
1811	ret = set_clos_param(id, 2, 15, 15, min_low, 0xff);
1812	if (ret)
1813		return ret;
1814
1815	ret = set_clos_param(id, 3, 15, 15, min_low, 0xff);
1816	if (ret)
1817		return ret;
1818
1819	for (i = 0; i < get_topo_max_cpus(); ++i) {
1820		int clos;
1821		struct isst_id tid;
1822
1823		if (!is_cpu_in_power_domain(i, id))
1824			continue;
1825
1826		if (CPU_ISSET_S(i, mask_size, cpu_mask))
1827			clos = 0;
1828		else
1829			clos = 3;
1830
1831		debug_printf("Associate cpu: %d clos: %d\n", i, clos);
1832		set_isst_id(&tid, i);
1833		ret = isst_clos_associate(&tid, clos);
1834		if (ret) {
1835			isst_display_error_info_message(1, "isst_clos_associate failed", 0, 0);
1836			return ret;
1837		}
1838	}
1839
1840	return 0;
1841}
1842
1843static int set_pbf_core_power(struct isst_id *id)
1844{
1845	struct isst_pbf_info pbf_info;
1846	struct isst_pkg_ctdp pkg_dev;
1847	int ret;
1848
1849	if (id->cpu < 0)
1850		return 0;
1851
1852	ret = isst_get_ctdp_levels(id, &pkg_dev);
1853	if (ret) {
1854		debug_printf("isst_get_ctdp_levels failed");
1855		return ret;
1856	}
1857	debug_printf("Current_level: %d\n", pkg_dev.current_level);
1858
1859	ret = isst_get_pbf_info(id, pkg_dev.current_level, &pbf_info);
1860	if (ret) {
1861		debug_printf("isst_get_pbf_info failed");
1862		return ret;
1863	}
1864	debug_printf("p1_high: %d p1_low: %d\n", pbf_info.p1_high,
1865		     pbf_info.p1_low);
1866
1867	ret = set_core_priority_and_min(id, pbf_info.core_cpumask_size,
1868					pbf_info.core_cpumask,
1869					pbf_info.p1_high, pbf_info.p1_low);
1870	if (ret) {
1871		debug_printf("set_core_priority_and_min failed");
1872		return ret;
1873	}
1874
1875	ret = isst_pm_qos_config(id, 1, 1);
1876	if (ret) {
1877		debug_printf("isst_pm_qos_config failed");
1878		return ret;
1879	}
1880
1881	return 0;
1882}
1883
1884static void set_pbf_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
1885			    void *arg4)
1886{
1887	struct isst_pkg_ctdp_level_info ctdp_level;
1888	struct isst_pkg_ctdp pkg_dev;
1889	int ret;
1890	int status = *(int *)arg4;
1891
1892	if (is_clx_n_platform()) {
1893		ret = 0;
1894		if (status) {
1895			set_clx_pbf_cpufreq_scaling_min_max(id);
1896
1897		} else {
1898			set_scaling_max_to_cpuinfo_max(id);
1899			set_scaling_min_to_cpuinfo_min(id);
1900		}
1901		goto disp_result;
1902	}
1903
1904	ret = isst_get_ctdp_levels(id, &pkg_dev);
1905	if (ret) {
1906		isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
1907		goto disp_result;
1908	}
1909
1910	ret = isst_get_ctdp_control(id, pkg_dev.current_level, &ctdp_level);
1911	if (ret) {
1912		isst_display_error_info_message(1, "Failed to get current level", 0, 0);
1913		goto disp_result;
1914	}
1915
1916	if (!ctdp_level.pbf_support) {
1917		isst_display_error_info_message(1, "base-freq feature is not present at this level", 1, pkg_dev.current_level);
1918		ret = -1;
1919		goto disp_result;
1920	}
1921
1922	if (auto_mode && status) {
1923		ret = set_pbf_core_power(id);
1924		if (ret)
1925			goto disp_result;
1926	}
1927
1928	ret = isst_set_pbf_fact_status(id, 1, status);
1929	if (ret) {
1930		debug_printf("isst_set_pbf_fact_status failed");
1931		if (auto_mode)
1932			isst_pm_qos_config(id, 0, 0);
1933	} else {
1934		if (auto_mode) {
1935			if (status)
1936				set_scaling_min_to_cpuinfo_max(id);
1937			else
1938				set_scaling_min_to_cpuinfo_min(id);
1939		}
1940	}
1941
1942	if (auto_mode && !status)
1943		isst_pm_qos_config(id, 0, 1);
1944
1945disp_result:
1946	if (status)
1947		isst_display_result(id, outf, "base-freq", "enable",
1948				    ret);
1949	else
1950		isst_display_result(id, outf, "base-freq", "disable",
1951				    ret);
1952}
1953
1954static void set_pbf_enable(int arg)
1955{
1956	int enable = arg;
1957
1958	if (cmd_help) {
1959		if (enable) {
1960			fprintf(stderr,
1961				"Enable Intel Speed Select Technology base frequency feature\n");
1962			if (is_clx_n_platform()) {
1963				fprintf(stderr,
1964					"\tOn this platform this command doesn't enable feature in the hardware.\n");
1965				fprintf(stderr,
1966					"\tIt updates the cpufreq scaling_min_freq to match cpufreq base_frequency.\n");
1967				exit(0);
1968
1969			}
1970			fprintf(stderr,
1971				"\tOptional Arguments: -a|--auto : Use priority of cores to set core-power associations\n");
1972		} else {
1973
1974			if (is_clx_n_platform()) {
1975				fprintf(stderr,
1976					"\tOn this platform this command doesn't disable feature in the hardware.\n");
1977				fprintf(stderr,
1978					"\tIt updates the cpufreq scaling_min_freq to match cpuinfo_min_freq\n");
1979				exit(0);
1980			}
1981			fprintf(stderr,
1982				"Disable Intel Speed Select Technology base frequency feature\n");
1983			fprintf(stderr,
1984				"\tOptional Arguments: -a|--auto : Also disable core-power associations\n");
1985		}
1986		exit(0);
1987	}
1988
1989	isst_ctdp_display_information_start(outf);
1990	if (max_target_cpus)
1991		for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL,
1992						  NULL, &enable);
1993	else
1994		for_each_online_power_domain_in_set(set_pbf_for_cpu, NULL, NULL,
1995					       NULL, &enable);
1996	isst_ctdp_display_information_end(outf);
1997}
1998
1999static void dump_fact_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
2000				     void *arg3, void *arg4)
2001{
2002	struct isst_fact_info fact_info;
2003	int ret;
2004
2005	ret = isst_get_fact_info(id, tdp_level, fact_bucket, &fact_info);
2006	if (ret) {
2007		isst_display_error_info_message(1, "Failed to get turbo-freq info at this level", 1, tdp_level);
2008		isst_ctdp_display_information_end(outf);
2009		exit(1);
2010	} else {
2011		isst_fact_display_information(id, outf, tdp_level, fact_bucket,
2012					      fact_avx, &fact_info);
2013	}
2014}
2015
2016static void dump_fact_config(int arg)
2017{
2018	if (cmd_help) {
2019		fprintf(stderr,
2020			"Print complete Intel Speed Select Technology turbo frequency configuration for a TDP level. Other arguments are optional.\n");
2021		fprintf(stderr,
2022			"\tArguments: -l|--level : Specify tdp level\n");
2023		fprintf(stderr,
2024			"\tArguments: -b|--bucket : Bucket index to dump\n");
2025		fprintf(stderr,
2026			"\tArguments: -r|--trl-type : Specify trl type: sse|avx2|avx512\n");
2027		exit(0);
2028	}
2029
2030	if (tdp_level == 0xff) {
2031		isst_display_error_info_message(1, "Invalid command: specify tdp_level\n", 0, 0);
2032		exit(1);
2033	}
2034
2035	isst_ctdp_display_information_start(outf);
2036	if (max_target_cpus)
2037		for_each_online_target_cpu_in_set(dump_fact_config_for_cpu,
2038						  NULL, NULL, NULL, NULL);
2039	else
2040		for_each_online_power_domain_in_set(dump_fact_config_for_cpu, NULL,
2041					       NULL, NULL, NULL);
2042	isst_ctdp_display_information_end(outf);
2043}
2044
2045static void set_fact_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
2046			     void *arg4)
2047{
2048	struct isst_pkg_ctdp_level_info ctdp_level;
2049	struct isst_pkg_ctdp pkg_dev;
2050	int ret;
2051	int status = *(int *)arg4;
2052
2053	if (status && no_turbo()) {
2054		isst_display_error_info_message(1, "Turbo mode is disabled", 0, 0);
2055		ret = -1;
2056		goto disp_results;
2057	}
2058
2059	ret = isst_get_ctdp_levels(id, &pkg_dev);
2060	if (ret) {
2061		isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
2062		goto disp_results;
2063	}
2064
2065	ret = isst_get_ctdp_control(id, pkg_dev.current_level, &ctdp_level);
2066	if (ret) {
2067		isst_display_error_info_message(1, "Failed to get current level", 0, 0);
2068		goto disp_results;
2069	}
2070
2071	if (!ctdp_level.fact_support) {
2072		isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, pkg_dev.current_level);
2073		ret = -1;
2074		goto disp_results;
2075	}
2076
2077	if (status) {
2078		ret = isst_pm_qos_config(id, 1, 1);
2079		if (ret)
2080			goto disp_results;
2081	}
2082
2083	ret = isst_set_pbf_fact_status(id, 0, status);
2084	if (ret) {
2085		debug_printf("isst_set_pbf_fact_status failed");
2086		if (auto_mode)
2087			isst_pm_qos_config(id, 0, 0);
2088
2089		goto disp_results;
2090	}
2091
2092	/* Set TRL */
2093	if (status) {
2094		struct isst_pkg_ctdp pkg_dev;
2095
2096		ret = isst_get_ctdp_levels(id, &pkg_dev);
2097		if (!ret && id->cpu >= 0)
2098			ret = isst_set_trl(id, fact_trl);
2099		if (ret && auto_mode)
2100			isst_pm_qos_config(id, 0, 0);
2101	} else {
2102		if (auto_mode)
2103			isst_pm_qos_config(id, 0, 0);
2104	}
2105
2106disp_results:
2107	if (status) {
2108		isst_display_result(id, outf, "turbo-freq", "enable", ret);
2109		if (ret)
2110			fact_enable_fail = ret;
2111	} else {
2112		/* Since we modified TRL during Fact enable, restore it */
2113		isst_set_trl_from_current_tdp(id, fact_trl);
2114		isst_display_result(id, outf, "turbo-freq", "disable", ret);
2115	}
2116}
2117
2118static void set_fact_enable(int arg)
2119{
2120	int i, ret, enable = arg;
2121	struct isst_id id;
2122
2123	if (cmd_help) {
2124		if (enable) {
2125			fprintf(stderr,
2126				"Enable Intel Speed Select Technology Turbo frequency feature\n");
2127			fprintf(stderr,
2128				"Optional: -t|--trl : Specify turbo ratio limit\n");
2129			fprintf(stderr,
2130				"\tOptional Arguments: -a|--auto : Designate specified target CPUs with");
2131			fprintf(stderr,
2132				"-C|--cpu option as as high priority using core-power feature\n");
2133		} else {
2134			fprintf(stderr,
2135				"Disable Intel Speed Select Technology turbo frequency feature\n");
2136			fprintf(stderr,
2137				"Optional: -t|--trl : Specify turbo ratio limit\n");
2138			fprintf(stderr,
2139				"\tOptional Arguments: -a|--auto : Also disable core-power associations\n");
2140		}
2141		exit(0);
2142	}
2143
2144	isst_ctdp_display_information_start(outf);
2145	if (max_target_cpus)
2146		for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL,
2147						  NULL, &enable);
2148	else
2149		for_each_online_power_domain_in_set(set_fact_for_cpu, NULL, NULL,
2150					       NULL, &enable);
2151
2152	if (!fact_enable_fail && enable && auto_mode) {
2153		/*
2154		 * When we adjust CLOS param, we have to set for siblings also.
2155		 * So for the each user specified CPU, also add the sibling
2156		 * in the present_cpu_mask.
2157		 */
2158		for (i = 0; i < get_topo_max_cpus(); ++i) {
2159			char buffer[128], sibling_list[128], *cpu_str;
2160			int fd, len;
2161
2162			if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
2163				continue;
2164
2165			snprintf(buffer, sizeof(buffer),
2166				 "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", i);
2167
2168			fd = open(buffer, O_RDONLY);
2169			if (fd < 0)
2170				continue;
2171
2172			len = read(fd, sibling_list, sizeof(sibling_list));
2173			close(fd);
2174
2175			if (len < 0)
2176				continue;
2177
2178			sibling_list[127] = '\0';
2179			cpu_str = strtok(sibling_list, ",");
2180			while (cpu_str != NULL) {
2181				int cpu;
2182
2183				sscanf(cpu_str, "%d", &cpu);
2184				CPU_SET_S(cpu, target_cpumask_size, target_cpumask);
2185				cpu_str = strtok(NULL, ",");
2186			}
2187		}
2188
2189		for (i = 0; i < get_topo_max_cpus(); ++i) {
2190			int clos;
2191
2192			if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
2193				continue;
2194
2195			if (is_cpu_online(i) != 1)
2196				continue;
2197
2198			set_isst_id(&id, i);
2199			ret = set_clos_param(&id, 0, 0, 0, 0, 0xff);
2200			if (ret)
2201				goto error_disp;
2202
2203			ret = set_clos_param(&id, 1, 15, 15, 0, 0xff);
2204			if (ret)
2205				goto error_disp;
2206
2207			ret = set_clos_param(&id, 2, 15, 15, 0, 0xff);
2208			if (ret)
2209				goto error_disp;
2210
2211			ret = set_clos_param(&id, 3, 15, 15, 0, 0xff);
2212			if (ret)
2213				goto error_disp;
2214
2215			if (CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
2216				clos = 0;
2217			else
2218				clos = 3;
2219
2220			debug_printf("Associate cpu: %d clos: %d\n", i, clos);
2221			ret = isst_clos_associate(&id, clos);
2222			if (ret)
2223				goto error_disp;
2224		}
2225		set_isst_id(&id, -1);
2226		isst_display_result(&id, outf, "turbo-freq --auto", "enable", 0);
2227	}
2228
2229	isst_ctdp_display_information_end(outf);
2230
2231	return;
2232
2233error_disp:
2234	isst_display_result(&id, outf, "turbo-freq --auto", "enable", ret);
2235	isst_ctdp_display_information_end(outf);
2236
2237}
2238
2239static void enable_clos_qos_config(struct isst_id *id, void *arg1, void *arg2, void *arg3,
2240				   void *arg4)
2241{
2242	int ret;
2243	int status = *(int *)arg4;
2244
2245	if (is_skx_based_platform())
2246		clos_priority_type = 1;
2247
2248	ret = isst_pm_qos_config(id, status, clos_priority_type);
2249	if (ret)
2250		isst_display_error_info_message(1, "isst_pm_qos_config failed", 0, 0);
2251
2252	if (status)
2253		isst_display_result(id, outf, "core-power", "enable",
2254				    ret);
2255	else
2256		isst_display_result(id, outf, "core-power", "disable",
2257				    ret);
2258}
2259
2260static void set_clos_enable(int arg)
2261{
2262	int enable = arg;
2263
2264	if (cmd_help) {
2265		if (enable) {
2266			fprintf(stderr,
2267				"Enable core-power for a package/die\n");
2268			if (!is_skx_based_platform()) {
2269				fprintf(stderr,
2270					"\tClos Enable: Specify priority type with [--priority|-p]\n");
2271				fprintf(stderr, "\t\t 0: Proportional, 1: Ordered\n");
2272			}
2273		} else {
2274			fprintf(stderr,
2275				"Disable core-power: [No command arguments are required]\n");
2276		}
2277		exit(0);
2278	}
2279
2280	if (enable && cpufreq_sysfs_present()) {
2281		fprintf(stderr,
2282			"cpufreq subsystem and core-power enable will interfere with each other!\n");
2283	}
2284
2285	isst_ctdp_display_information_start(outf);
2286	if (max_target_cpus)
2287		for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL,
2288						  NULL, NULL, &enable);
2289	else
2290		for_each_online_power_domain_in_set(enable_clos_qos_config, NULL,
2291					       NULL, NULL, &enable);
2292	isst_ctdp_display_information_end(outf);
2293}
2294
2295static void dump_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2,
2296				     void *arg3, void *arg4)
2297{
2298	struct isst_clos_config clos_config;
2299	int ret;
2300
2301	ret = isst_pm_get_clos(id, current_clos, &clos_config);
2302	if (ret)
2303		isst_display_error_info_message(1, "isst_pm_get_clos failed", 0, 0);
2304	else
2305		isst_clos_display_information(id, outf, current_clos,
2306					      &clos_config);
2307}
2308
2309static void dump_clos_config(int arg)
2310{
2311	if (cmd_help) {
2312		fprintf(stderr,
2313			"Print Intel Speed Select Technology core power configuration\n");
2314		fprintf(stderr,
2315			"\tArguments: [-c | --clos]: Specify clos id\n");
2316		exit(0);
2317	}
2318	if (current_clos < 0 || current_clos > 3) {
2319		isst_display_error_info_message(1, "Invalid clos id\n", 0, 0);
2320		isst_ctdp_display_information_end(outf);
2321		exit(0);
2322	}
2323
2324	isst_ctdp_display_information_start(outf);
2325	if (max_target_cpus)
2326		for_each_online_target_cpu_in_set(dump_clos_config_for_cpu,
2327						  NULL, NULL, NULL, NULL);
2328	else
2329		for_each_online_power_domain_in_set(dump_clos_config_for_cpu, NULL,
2330					       NULL, NULL, NULL);
2331	isst_ctdp_display_information_end(outf);
2332}
2333
2334static void get_clos_info_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
2335				  void *arg4)
2336{
2337	int enable, ret, prio_type;
2338
2339	ret = isst_clos_get_clos_information(id, &enable, &prio_type);
2340	if (ret)
2341		isst_display_error_info_message(1, "isst_clos_get_info failed", 0, 0);
2342	else {
2343		int cp_state, cp_cap;
2344
2345		isst_read_pm_config(id, &cp_state, &cp_cap);
2346		isst_clos_display_clos_information(id, outf, enable, prio_type,
2347						   cp_state, cp_cap);
2348	}
2349}
2350
2351static void dump_clos_info(int arg)
2352{
2353	if (cmd_help) {
2354		fprintf(stderr,
2355			"Print Intel Speed Select Technology core power information\n");
2356		fprintf(stderr, "\t Optionally specify targeted cpu id with [--cpu|-c]\n");
2357		exit(0);
2358	}
2359
2360	isst_ctdp_display_information_start(outf);
2361	if (max_target_cpus)
2362		for_each_online_target_cpu_in_set(get_clos_info_for_cpu, NULL,
2363						  NULL, NULL, NULL);
2364	else
2365		for_each_online_power_domain_in_set(get_clos_info_for_cpu, NULL,
2366					       NULL, NULL, NULL);
2367	isst_ctdp_display_information_end(outf);
2368
2369}
2370
2371static void set_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
2372				    void *arg4)
2373{
2374	struct isst_clos_config clos_config;
2375	int ret;
2376
2377	if (id->cpu < 0)
2378		return;
2379
2380	clos_config.epp = clos_epp;
2381	clos_config.clos_prop_prio = clos_prop_prio;
2382	clos_config.clos_min = clos_min;
2383	clos_config.clos_max = clos_max;
2384	clos_config.clos_desired = clos_desired;
2385	ret = isst_set_clos(id, current_clos, &clos_config);
2386	if (ret)
2387		isst_display_error_info_message(1, "isst_set_clos failed", 0, 0);
2388	else
2389		isst_display_result(id, outf, "core-power", "config", ret);
2390}
2391
2392static void set_clos_config(int arg)
2393{
2394	if (cmd_help) {
2395		fprintf(stderr,
2396			"Set core-power configuration for one of the four clos ids\n");
2397		fprintf(stderr,
2398			"\tSpecify targeted clos id with [--clos|-c]\n");
2399		if (!is_skx_based_platform()) {
2400			fprintf(stderr, "\tSpecify clos EPP with [--epp|-e]\n");
2401			fprintf(stderr,
2402				"\tSpecify clos Proportional Priority [--weight|-w]\n");
2403		}
2404		fprintf(stderr, "\tSpecify clos min in MHz with [--min|-n]\n");
2405		fprintf(stderr, "\tSpecify clos max in MHz with [--max|-m]\n");
2406		exit(0);
2407	}
2408
2409	if (current_clos < 0 || current_clos > 3) {
2410		isst_display_error_info_message(1, "Invalid clos id\n", 0, 0);
2411		exit(0);
2412	}
2413	if (!is_skx_based_platform() && (clos_epp < 0 || clos_epp > 0x0F)) {
2414		fprintf(stderr, "clos epp is not specified or invalid, default: 0\n");
2415		clos_epp = 0;
2416	}
2417	if (!is_skx_based_platform() && (clos_prop_prio < 0 || clos_prop_prio > 0x0F)) {
2418		fprintf(stderr,
2419			"clos frequency weight is not specified or invalid, default: 0\n");
2420		clos_prop_prio = 0;
2421	}
2422	if (clos_min < 0) {
2423		fprintf(stderr, "clos min is not specified, default: 0\n");
2424		clos_min = 0;
2425	}
2426	if (clos_max < 0) {
2427		fprintf(stderr, "clos max is not specified, default: Max frequency (ratio 0xff)\n");
2428		clos_max = 0xff;
2429	}
2430	if (clos_desired) {
2431		fprintf(stderr, "clos desired is not supported on this platform\n");
2432		clos_desired = 0x00;
2433	}
2434
2435	isst_ctdp_display_information_start(outf);
2436	if (max_target_cpus)
2437		for_each_online_target_cpu_in_set(set_clos_config_for_cpu, NULL,
2438						  NULL, NULL, NULL);
2439	else
2440		for_each_online_power_domain_in_set(set_clos_config_for_cpu, NULL,
2441					       NULL, NULL, NULL);
2442	isst_ctdp_display_information_end(outf);
2443}
2444
2445static void set_clos_assoc_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
2446				   void *arg4)
2447{
2448	int ret;
2449
2450	ret = isst_clos_associate(id, current_clos);
2451	if (ret)
2452		debug_printf("isst_clos_associate failed");
2453	else
2454		isst_display_result(id, outf, "core-power", "assoc", ret);
2455}
2456
2457static void set_clos_assoc(int arg)
2458{
2459	if (cmd_help) {
2460		fprintf(stderr, "Associate a clos id to a CPU\n");
2461		fprintf(stderr,
2462			"\tSpecify targeted clos id with [--clos|-c]\n");
2463		fprintf(stderr,
2464			"\tFor example to associate clos 1 to CPU 0: issue\n");
2465		fprintf(stderr,
2466			"\tintel-speed-select --cpu 0 core-power assoc --clos 1\n");
2467		exit(0);
2468	}
2469
2470	if (current_clos < 0 || current_clos > 3) {
2471		isst_display_error_info_message(1, "Invalid clos id\n", 0, 0);
2472		exit(0);
2473	}
2474
2475	isst_ctdp_display_information_start(outf);
2476
2477	if (max_target_cpus)
2478		for_each_online_target_cpu_in_set(set_clos_assoc_for_cpu, NULL,
2479						  NULL, NULL, NULL);
2480	else {
2481		isst_display_error_info_message(1, "Invalid target cpu. Specify with [-c|--cpu]", 0, 0);
2482	}
2483	isst_ctdp_display_information_end(outf);
2484}
2485
2486static void get_clos_assoc_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3,
2487				   void *arg4)
2488{
2489	int clos, ret;
2490
2491	ret = isst_clos_get_assoc_status(id, &clos);
2492	if (ret)
2493		isst_display_error_info_message(1, "isst_clos_get_assoc_status failed", 0, 0);
2494	else
2495		isst_clos_display_assoc_information(id, outf, clos);
2496}
2497
2498static void get_clos_assoc(int arg)
2499{
2500	if (cmd_help) {
2501		fprintf(stderr, "Get associate clos id to a CPU\n");
2502		fprintf(stderr, "\tSpecify targeted cpu id with [--cpu|-c]\n");
2503		exit(0);
2504	}
2505
2506	if (!max_target_cpus) {
2507		isst_display_error_info_message(1, "Invalid target cpu. Specify with [-c|--cpu]", 0, 0);
2508		exit(0);
2509	}
2510
2511	isst_ctdp_display_information_start(outf);
2512	for_each_online_target_cpu_in_set(get_clos_assoc_for_cpu, NULL,
2513					  NULL, NULL, NULL);
2514	isst_ctdp_display_information_end(outf);
2515}
2516
2517static void set_turbo_mode_for_cpu(struct isst_id *id, int status)
2518{
2519	int base_freq;
2520
2521	if (status) {
2522		base_freq = get_cpufreq_base_freq(id->cpu);
2523		set_cpufreq_scaling_min_max(id->cpu, 1, base_freq);
2524	} else {
2525		set_scaling_max_to_cpuinfo_max(id);
2526	}
2527
2528	if (status) {
2529		isst_display_result(id, outf, "turbo-mode", "enable", 0);
2530	} else {
2531		isst_display_result(id, outf, "turbo-mode", "disable", 0);
2532	}
2533}
2534
2535static void set_turbo_mode(int arg)
2536{
2537	int i, enable = arg;
2538	struct isst_id id;
2539
2540	if (cmd_help) {
2541		if (enable)
2542			fprintf(stderr, "Set turbo mode enable\n");
2543		else
2544			fprintf(stderr, "Set turbo mode disable\n");
2545		exit(0);
2546	}
2547
2548	isst_ctdp_display_information_start(outf);
2549
2550	for (i = 0; i < topo_max_cpus; ++i) {
2551		int online;
2552
2553		if (i)
2554			online = parse_int_file(
2555				1, "/sys/devices/system/cpu/cpu%d/online", i);
2556		else
2557			online =
2558				1; /* online entry for CPU 0 needs some special configs */
2559
2560		if (online) {
2561			set_isst_id(&id, i);
2562			set_turbo_mode_for_cpu(&id, enable);
2563		}
2564
2565	}
2566	isst_ctdp_display_information_end(outf);
2567}
2568
2569static void get_set_trl(struct isst_id *id, void *arg1, void *arg2, void *arg3,
2570			void *arg4)
2571{
2572	unsigned long long trl;
2573	int set = *(int *)arg4;
2574	int ret;
2575
2576	if (set && !fact_trl) {
2577		isst_display_error_info_message(1, "Invalid TRL. Specify with [-t|--trl]", 0, 0);
2578		exit(0);
2579	}
2580
2581	if (set) {
2582		ret = isst_set_trl(id, fact_trl);
2583		isst_display_result(id, outf, "turbo-mode", "set-trl", ret);
2584		return;
2585	}
2586
2587	ret = isst_get_trl(id, &trl);
2588	if (ret)
2589		isst_display_result(id, outf, "turbo-mode", "get-trl", ret);
2590	else
2591		isst_trl_display_information(id, outf, trl);
2592}
2593
2594static void process_trl(int arg)
2595{
2596	if (cmd_help) {
2597		if (arg) {
2598			fprintf(stderr, "Set TRL (turbo ratio limits)\n");
2599			fprintf(stderr, "\t t|--trl: Specify turbo ratio limit for setting TRL\n");
2600		} else {
2601			fprintf(stderr, "Get TRL (turbo ratio limits)\n");
2602		}
2603		exit(0);
2604	}
2605
2606	isst_ctdp_display_information_start(outf);
2607	if (max_target_cpus)
2608		for_each_online_target_cpu_in_set(get_set_trl, NULL,
2609						  NULL, NULL, &arg);
2610	else
2611		for_each_online_power_domain_in_set(get_set_trl, NULL,
2612					       NULL, NULL, &arg);
2613	isst_ctdp_display_information_end(outf);
2614}
2615
2616static struct process_cmd_struct clx_n_cmds[] = {
2617	{ "perf-profile", "info", dump_isst_config, 0 },
2618	{ "base-freq", "info", dump_pbf_config, 0 },
2619	{ "base-freq", "enable", set_pbf_enable, 1 },
2620	{ "base-freq", "disable", set_pbf_enable, 0 },
2621	{ NULL, NULL, NULL, 0 }
2622};
2623
2624static struct process_cmd_struct isst_cmds[] = {
2625	{ "perf-profile", "get-lock-status", get_tdp_locked, 0 },
2626	{ "perf-profile", "get-config-levels", get_tdp_levels, 0 },
2627	{ "perf-profile", "get-config-version", get_tdp_version, 0 },
2628	{ "perf-profile", "get-config-enabled", get_tdp_enabled, 0 },
2629	{ "perf-profile", "get-config-current-level", get_tdp_current_level,
2630	 0 },
2631	{ "perf-profile", "set-config-level", set_tdp_level, 0 },
2632	{ "perf-profile", "info", dump_isst_config, 0 },
2633	{ "base-freq", "info", dump_pbf_config, 0 },
2634	{ "base-freq", "enable", set_pbf_enable, 1 },
2635	{ "base-freq", "disable", set_pbf_enable, 0 },
2636	{ "turbo-freq", "info", dump_fact_config, 0 },
2637	{ "turbo-freq", "enable", set_fact_enable, 1 },
2638	{ "turbo-freq", "disable", set_fact_enable, 0 },
2639	{ "core-power", "info", dump_clos_info, 0 },
2640	{ "core-power", "enable", set_clos_enable, 1 },
2641	{ "core-power", "disable", set_clos_enable, 0 },
2642	{ "core-power", "config", set_clos_config, 0 },
2643	{ "core-power", "get-config", dump_clos_config, 0 },
2644	{ "core-power", "assoc", set_clos_assoc, 0 },
2645	{ "core-power", "get-assoc", get_clos_assoc, 0 },
2646	{ "turbo-mode", "enable", set_turbo_mode, 0 },
2647	{ "turbo-mode", "disable", set_turbo_mode, 1 },
2648	{ "turbo-mode", "get-trl", process_trl, 0 },
2649	{ "turbo-mode", "set-trl", process_trl, 1 },
2650	{ NULL, NULL, NULL }
2651};
2652
2653/*
2654 * parse cpuset with following syntax
2655 * 1,2,4..6,8-10 and set bits in cpu_subset
2656 */
2657void parse_cpu_command(char *optarg)
2658{
2659	unsigned int start, end, invalid_count;
2660	char *next;
2661
2662	next = optarg;
2663	invalid_count = 0;
2664
2665	while (next && *next) {
2666		if (*next == '-') /* no negative cpu numbers */
2667			goto error;
2668
2669		start = strtoul(next, &next, 10);
2670
2671		if (max_target_cpus < MAX_CPUS_IN_ONE_REQ)
2672			target_cpus[max_target_cpus++] = start;
2673		else
2674			invalid_count = 1;
2675
2676		if (*next == '\0')
2677			break;
2678
2679		if (*next == ',') {
2680			next += 1;
2681			continue;
2682		}
2683
2684		if (*next == '-') {
2685			next += 1; /* start range */
2686		} else if (*next == '.') {
2687			next += 1;
2688			if (*next == '.')
2689				next += 1; /* start range */
2690			else
2691				goto error;
2692		}
2693
2694		end = strtoul(next, &next, 10);
2695		if (end <= start)
2696			goto error;
2697
2698		while (++start <= end) {
2699			if (max_target_cpus < MAX_CPUS_IN_ONE_REQ)
2700				target_cpus[max_target_cpus++] = start;
2701			else
2702				invalid_count = 1;
2703		}
2704
2705		if (*next == ',')
2706			next += 1;
2707		else if (*next != '\0')
2708			goto error;
2709	}
2710
2711	if (invalid_count) {
2712		isst_ctdp_display_information_start(outf);
2713		isst_display_error_info_message(1, "Too many CPUs in one request: max is", 1, MAX_CPUS_IN_ONE_REQ - 1);
2714		isst_ctdp_display_information_end(outf);
2715		exit(-1);
2716	}
2717
2718#ifdef DEBUG
2719	{
2720		int i;
2721
2722		for (i = 0; i < max_target_cpus; ++i)
2723			printf("cpu [%d] in arg\n", target_cpus[i]);
2724	}
2725#endif
2726	return;
2727
2728error:
2729	fprintf(stderr, "\"--cpu %s\" malformed\n", optarg);
2730	exit(-1);
2731}
2732
2733static void parse_cmd_args(int argc, int start, char **argv)
2734{
2735	int opt;
2736	int option_index;
2737
2738	static struct option long_options[] = {
2739		{ "bucket", required_argument, 0, 'b' },
2740		{ "level", required_argument, 0, 'l' },
2741		{ "online", required_argument, 0, 'o' },
2742		{ "trl-type", required_argument, 0, 'r' },
2743		{ "trl", required_argument, 0, 't' },
2744		{ "help", no_argument, 0, 'h' },
2745		{ "clos", required_argument, 0, 'c' },
2746		{ "desired", required_argument, 0, 'd' },
2747		{ "epp", required_argument, 0, 'e' },
2748		{ "min", required_argument, 0, 'n' },
2749		{ "max", required_argument, 0, 'm' },
2750		{ "priority", required_argument, 0, 'p' },
2751		{ "weight", required_argument, 0, 'w' },
2752		{ "auto", no_argument, 0, 'a' },
2753		{ 0, 0, 0, 0 }
2754	};
2755
2756	option_index = start;
2757
2758	optind = start + 1;
2759	while ((opt = getopt_long(argc, argv, "b:l:t:c:d:e:n:m:p:w:r:hoa",
2760				  long_options, &option_index)) != -1) {
2761		switch (opt) {
2762		case 'a':
2763			auto_mode = 1;
2764			break;
2765		case 'b':
2766			fact_bucket = atoi(optarg);
2767			break;
2768		case 'h':
2769			cmd_help = 1;
2770			break;
2771		case 'l':
2772			tdp_level = atoi(optarg);
2773			break;
2774		case 'o':
2775			force_online_offline = 1;
2776			break;
2777		case 't':
2778			sscanf(optarg, "0x%llx", &fact_trl);
2779			break;
2780		case 'r':
2781			if (!strncmp(optarg, "sse", 3)) {
2782				fact_avx = 0x01;
2783			} else if (!strncmp(optarg, "avx2", 4)) {
2784				fact_avx = 0x02;
2785			} else if (!strncmp(optarg, "avx512", 6)) {
2786				fact_avx = 0x04;
2787			} else {
2788				fprintf(outf, "Invalid sse,avx options\n");
2789				exit(1);
2790			}
2791			break;
2792		/* CLOS related */
2793		case 'c':
2794			current_clos = atoi(optarg);
2795			break;
2796		case 'd':
2797			clos_desired = atoi(optarg);
2798			clos_desired /= isst_get_disp_freq_multiplier();
2799			break;
2800		case 'e':
2801			clos_epp = atoi(optarg);
2802			if (is_skx_based_platform()) {
2803				isst_display_error_info_message(1, "epp can't be specified on this platform", 0, 0);
2804				exit(0);
2805			}
2806			break;
2807		case 'n':
2808			clos_min = atoi(optarg);
2809			clos_min /= isst_get_disp_freq_multiplier();
2810			break;
2811		case 'm':
2812			clos_max = atoi(optarg);
2813			clos_max /= isst_get_disp_freq_multiplier();
2814			break;
2815		case 'p':
2816			clos_priority_type = atoi(optarg);
2817			if (is_skx_based_platform() && !clos_priority_type) {
2818				isst_display_error_info_message(1, "Invalid clos priority type: proportional for this platform", 0, 0);
2819				exit(0);
2820			}
2821			break;
2822		case 'w':
2823			clos_prop_prio = atoi(optarg);
2824			if (is_skx_based_platform()) {
2825				isst_display_error_info_message(1, "weight can't be specified on this platform", 0, 0);
2826				exit(0);
2827			}
2828			break;
2829		default:
2830			printf("Unknown option: ignore\n");
2831		}
2832	}
2833
2834	if (argv[optind])
2835		printf("Garbage at the end of command: ignore\n");
2836}
2837
2838static void isst_help(void)
2839{
2840	printf("perf-profile:\tAn architectural mechanism that allows multiple optimized \n\
2841		performance profiles per system via static and/or dynamic\n\
2842		adjustment of core count, workload, Tjmax, and\n\
2843		TDP, etc.\n");
2844	printf("\nCommands : For feature=perf-profile\n");
2845	printf("\tinfo\n");
2846
2847	if (!is_clx_n_platform()) {
2848		printf("\tget-lock-status\n");
2849		printf("\tget-config-levels\n");
2850		printf("\tget-config-version\n");
2851		printf("\tget-config-enabled\n");
2852		printf("\tget-config-current-level\n");
2853		printf("\tset-config-level\n");
2854	}
2855}
2856
2857static void pbf_help(void)
2858{
2859	printf("base-freq:\tEnables users to increase guaranteed base frequency\n\
2860		on certain cores (high priority cores) in exchange for lower\n\
2861		base frequency on remaining cores (low priority cores).\n");
2862	printf("\tcommand : info\n");
2863	printf("\tcommand : enable\n");
2864	printf("\tcommand : disable\n");
2865}
2866
2867static void fact_help(void)
2868{
2869	printf("turbo-freq:\tEnables the ability to set different turbo ratio\n\
2870		limits to cores based on priority.\n");
2871	printf("\nCommand: For feature=turbo-freq\n");
2872	printf("\tcommand : info\n");
2873	printf("\tcommand : enable\n");
2874	printf("\tcommand : disable\n");
2875}
2876
2877static void turbo_mode_help(void)
2878{
2879	printf("turbo-mode:\tEnables users to enable/disable turbo mode by adjusting frequency settings. Also allows to get and set turbo ratio limits (TRL).\n");
2880	printf("\tcommand : enable\n");
2881	printf("\tcommand : disable\n");
2882	printf("\tcommand : get-trl\n");
2883	printf("\tcommand : set-trl\n");
2884}
2885
2886
2887static void core_power_help(void)
2888{
2889	printf("core-power:\tInterface that allows user to define per core/tile\n\
2890		priority.\n");
2891	printf("\nCommands : For feature=core-power\n");
2892	printf("\tinfo\n");
2893	printf("\tenable\n");
2894	printf("\tdisable\n");
2895	printf("\tconfig\n");
2896	printf("\tget-config\n");
2897	printf("\tassoc\n");
2898	printf("\tget-assoc\n");
2899}
2900
2901struct process_cmd_help_struct {
2902	char *feature;
2903	void (*process_fn)(void);
2904};
2905
2906static struct process_cmd_help_struct isst_help_cmds[] = {
2907	{ "perf-profile", isst_help },
2908	{ "base-freq", pbf_help },
2909	{ "turbo-freq", fact_help },
2910	{ "core-power", core_power_help },
2911	{ "turbo-mode", turbo_mode_help },
2912	{ NULL, NULL }
2913};
2914
2915static struct process_cmd_help_struct clx_n_help_cmds[] = {
2916	{ "perf-profile", isst_help },
2917	{ "base-freq", pbf_help },
2918	{ NULL, NULL }
2919};
2920
2921void process_command(int argc, char **argv,
2922		     struct process_cmd_help_struct *help_cmds,
2923		     struct process_cmd_struct *cmds)
2924{
2925	int i = 0, matched = 0;
2926	char *feature = argv[optind];
2927	char *cmd = argv[optind + 1];
2928
2929	if (!feature || !cmd)
2930		return;
2931
2932	debug_printf("feature name [%s] command [%s]\n", feature, cmd);
2933	if (!strcmp(cmd, "-h") || !strcmp(cmd, "--help")) {
2934		while (help_cmds[i].feature) {
2935			if (!strcmp(help_cmds[i].feature, feature)) {
2936				help_cmds[i].process_fn();
2937				exit(0);
2938			}
2939			++i;
2940		}
2941	}
2942
2943	i = 0;
2944	while (cmds[i].feature) {
2945		if (!strcmp(cmds[i].feature, feature) &&
2946		    !strcmp(cmds[i].command, cmd)) {
2947			parse_cmd_args(argc, optind + 1, argv);
2948			cmds[i].process_fn(cmds[i].arg);
2949			matched = 1;
2950			break;
2951		}
2952		++i;
2953	}
2954
2955	if (!matched)
2956		fprintf(stderr, "Invalid command\n");
2957}
2958
2959static void usage(void)
2960{
2961	if (is_clx_n_platform()) {
2962		fprintf(stderr, "\nThere is limited support of Intel Speed Select features on this platform.\n");
2963		fprintf(stderr, "Everything is pre-configured using BIOS options, this tool can't enable any feature in the hardware.\n\n");
2964	}
2965
2966	printf("\nUsage:\n");
2967	printf("intel-speed-select [OPTIONS] FEATURE COMMAND COMMAND_ARGUMENTS\n");
2968	printf("\nUse this tool to enumerate and control the Intel Speed Select Technology features:\n");
2969	if (is_clx_n_platform())
2970		printf("\nFEATURE : [perf-profile|base-freq]\n");
2971	else
2972		printf("\nFEATURE : [perf-profile|base-freq|turbo-freq|core-power|turbo-mode]\n");
2973	printf("\nFor help on each feature, use -h|--help\n");
2974	printf("\tFor example:  intel-speed-select perf-profile -h\n");
2975
2976	printf("\nFor additional help on each command for a feature, use --h|--help\n");
2977	printf("\tFor example:  intel-speed-select perf-profile get-lock-status -h\n");
2978	printf("\t\t This will print help for the command \"get-lock-status\" for the feature \"perf-profile\"\n");
2979
2980	printf("\nOPTIONS\n");
2981	printf("\t[-c|--cpu] : logical cpu number\n");
2982	printf("\t\tDefault: Die scoped for all dies in the system with multiple dies/package\n");
2983	printf("\t\t\t Or Package scoped for all Packages when each package contains one die\n");
2984	printf("\t[-d|--debug] : Debug mode\n");
2985	printf("\t[-f|--format] : output format [json|text]. Default: text\n");
2986	printf("\t[-h|--help] : Print help\n");
2987	printf("\t[-i|--info] : Print platform information\n");
2988	printf("\t[-a|--all-cpus-online] : Force online every CPU in the system\n");
2989	printf("\t[-o|--out] : Output file\n");
2990	printf("\t\t\tDefault : stderr\n");
2991	printf("\t[-p|--pause] : Delay between two mail box commands in milliseconds\n");
2992	printf("\t[-r|--retry] : Retry count for mail box commands on failure, default 3\n");
2993	printf("\t[-v|--version] : Print version\n");
2994	printf("\t[-b|--oob : Start a daemon to process HFI events for perf profile change from Out of Band agent.\n");
2995	printf("\t[-n|--no-daemon : Don't run as daemon. By default --oob will turn on daemon mode\n");
2996	printf("\t[-w|--delay : Delay for reading config level state change in OOB poll mode.\n");
2997	printf("\t[-g|--cgroupv2 : Try to use cgroup v2 CPU isolation instead of CPU online/offline.\n");
2998	printf("\nResult format\n");
2999	printf("\tResult display uses a common format for each command:\n");
3000	printf("\tResults are formatted in text/JSON with\n");
3001	printf("\t\tPackage, Die, CPU, and command specific results.\n");
3002
3003	printf("\nExamples\n");
3004	printf("\tTo get platform information:\n");
3005	printf("\t\tintel-speed-select --info\n");
3006	printf("\tTo get full perf-profile information dump:\n");
3007	printf("\t\tintel-speed-select perf-profile info\n");
3008	printf("\tTo get full base-freq information dump:\n");
3009	printf("\t\tintel-speed-select base-freq info -l 0\n");
3010	if (!is_clx_n_platform()) {
3011		printf("\tTo get full turbo-freq information dump:\n");
3012		printf("\t\tintel-speed-select turbo-freq info -l 0\n");
3013	}
3014	exit(1);
3015}
3016
3017static void print_version(void)
3018{
3019	fprintf(outf, "Version %s\n", version_str);
3020	exit(0);
3021}
3022
3023static void cmdline(int argc, char **argv)
3024{
3025	const char *pathname = "/dev/isst_interface";
3026	char *ptr;
3027	FILE *fp;
3028	int opt, force_cpus_online = 0;
3029	int option_index = 0;
3030	int ret;
3031	int oob_mode = 0;
3032	int poll_interval = -1;
3033	int no_daemon = 0;
3034	int mbox_delay = 0, mbox_retries = 3;
3035
3036	static struct option long_options[] = {
3037		{ "all-cpus-online", no_argument, 0, 'a' },
3038		{ "cpu", required_argument, 0, 'c' },
3039		{ "debug", no_argument, 0, 'd' },
3040		{ "format", required_argument, 0, 'f' },
3041		{ "help", no_argument, 0, 'h' },
3042		{ "info", no_argument, 0, 'i' },
3043		{ "pause", required_argument, 0, 'p' },
3044		{ "out", required_argument, 0, 'o' },
3045		{ "retry", required_argument, 0, 'r' },
3046		{ "version", no_argument, 0, 'v' },
3047		{ "oob", no_argument, 0, 'b' },
3048		{ "no-daemon", no_argument, 0, 'n' },
3049		{ "poll-interval", required_argument, 0, 'w' },
3050		{ "cgroupv2", required_argument, 0, 'g' },
3051		{ 0, 0, 0, 0 }
3052	};
3053
3054	if (geteuid() != 0) {
3055		fprintf(stderr, "Must run as root\n");
3056		exit(0);
3057	}
3058
3059	ret = update_cpu_model();
3060	if (ret)
3061		err(-1, "Invalid CPU model (%d)\n", cpu_model);
3062	printf("Intel(R) Speed Select Technology\n");
3063	printf("Executing on CPU model:%d[0x%x]\n", cpu_model, cpu_model);
3064
3065	if (!is_clx_n_platform()) {
3066		fp = fopen(pathname, "rb");
3067		if (!fp) {
3068			fprintf(stderr, "Intel speed select drivers are not loaded on this system.\n");
3069			fprintf(stderr, "Verify that kernel config includes CONFIG_INTEL_SPEED_SELECT_INTERFACE.\n");
3070			fprintf(stderr, "If the config is included then this is not a supported platform.\n");
3071			exit(0);
3072		}
3073		fclose(fp);
3074	}
3075
3076	ret = isst_fill_platform_info();
3077	if (ret)
3078		goto out;
3079
3080	progname = argv[0];
3081	while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ng", long_options,
3082				       &option_index)) != -1) {
3083		switch (opt) {
3084		case 'a':
3085			force_cpus_online = 1;
3086			break;
3087		case 'c':
3088			parse_cpu_command(optarg);
3089			break;
3090		case 'd':
3091			debug_flag = 1;
3092			printf("Debug Mode ON\n");
3093			break;
3094		case 'f':
3095			if (!strncmp(optarg, "json", 4))
3096				out_format_json = 1;
3097			break;
3098		case 'h':
3099			usage();
3100			break;
3101		case 'i':
3102			isst_print_platform_information();
3103			break;
3104		case 'o':
3105			if (outf)
3106				fclose(outf);
3107			outf = fopen_or_exit(optarg, "w");
3108			break;
3109		case 'p':
3110			ret = strtol(optarg, &ptr, 10);
3111			if (!ret)
3112				fprintf(stderr, "Invalid pause interval, ignore\n");
3113			else
3114				mbox_delay = ret;
3115			break;
3116		case 'r':
3117			ret = strtol(optarg, &ptr, 10);
3118			if (!ret)
3119				fprintf(stderr, "Invalid retry count, ignore\n");
3120			else
3121				mbox_retries = ret;
3122			break;
3123		case 'v':
3124			print_version();
3125			break;
3126		case 'b':
3127			oob_mode = 1;
3128			break;
3129		case 'n':
3130			no_daemon = 1;
3131			break;
3132		case 'w':
3133			ret = strtol(optarg, &ptr, 10);
3134			if (!ret) {
3135				fprintf(stderr, "Invalid poll interval count\n");
3136				exit(0);
3137			}
3138			poll_interval = ret;
3139			break;
3140		case 'g':
3141			cgroupv2 = 1;
3142			break;
3143		default:
3144			usage();
3145		}
3146	}
3147
3148	if (optind > (argc - 2) && !oob_mode) {
3149		usage();
3150		exit(0);
3151	}
3152
3153	isst_update_platform_param(ISST_PARAM_MBOX_DELAY, mbox_delay);
3154	isst_update_platform_param(ISST_PARAM_MBOX_RETRIES, mbox_retries);
3155
3156	set_max_cpu_num();
3157	if (force_cpus_online)
3158		force_all_cpus_online();
3159	store_cpu_topology();
3160	create_cpu_map();
3161
3162	if (oob_mode) {
3163		if (debug_flag)
3164			fprintf(stderr, "OOB mode is enabled in debug mode\n");
3165
3166		ret = isst_daemon(debug_flag, poll_interval, no_daemon);
3167		if (ret)
3168			fprintf(stderr, "OOB mode enable failed\n");
3169		goto out;
3170	}
3171
3172	if (!is_clx_n_platform()) {
3173		process_command(argc, argv, isst_help_cmds, isst_cmds);
3174	} else {
3175		process_command(argc, argv, clx_n_help_cmds, clx_n_cmds);
3176	}
3177out:
3178	free_cpu_set(present_cpumask);
3179	free_cpu_set(target_cpumask);
3180}
3181
3182int main(int argc, char **argv)
3183{
3184	outf = stderr;
3185	cmdline(argc, argv);
3186	return 0;
3187}
3188