162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <unistd.h>
862306a36Sopenharmony_ci#include <stdio.h>
962306a36Sopenharmony_ci#include <errno.h>
1062306a36Sopenharmony_ci#include <stdlib.h>
1162306a36Sopenharmony_ci#include <string.h>
1262306a36Sopenharmony_ci#include <limits.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <getopt.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "cpufreq.h"
1762306a36Sopenharmony_ci#include "helpers/sysfs.h"
1862306a36Sopenharmony_ci#include "helpers/helpers.h"
1962306a36Sopenharmony_ci#include "helpers/bitmask.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define LINE_LEN 10
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic unsigned int count_cpus(void)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	FILE *fp;
2662306a36Sopenharmony_ci	char value[LINE_LEN];
2762306a36Sopenharmony_ci	unsigned int ret = 0;
2862306a36Sopenharmony_ci	unsigned int cpunr = 0;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	fp = fopen("/proc/stat", "r");
3162306a36Sopenharmony_ci	if (!fp) {
3262306a36Sopenharmony_ci		printf(_("Couldn't count the number of CPUs (%s: %s), assuming 1\n"), "/proc/stat", strerror(errno));
3362306a36Sopenharmony_ci		return 1;
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	while (!feof(fp)) {
3762306a36Sopenharmony_ci		if (!fgets(value, LINE_LEN, fp))
3862306a36Sopenharmony_ci			continue;
3962306a36Sopenharmony_ci		value[LINE_LEN - 1] = '\0';
4062306a36Sopenharmony_ci		if (strlen(value) < (LINE_LEN - 2))
4162306a36Sopenharmony_ci			continue;
4262306a36Sopenharmony_ci		if (strstr(value, "cpu "))
4362306a36Sopenharmony_ci			continue;
4462306a36Sopenharmony_ci		if (sscanf(value, "cpu%d ", &cpunr) != 1)
4562306a36Sopenharmony_ci			continue;
4662306a36Sopenharmony_ci		if (cpunr > ret)
4762306a36Sopenharmony_ci			ret = cpunr;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci	fclose(fp);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* cpu count starts from 0, on error return 1 (UP) */
5262306a36Sopenharmony_ci	return ret + 1;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void proc_cpufreq_output(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	unsigned int cpu, nr_cpus;
5962306a36Sopenharmony_ci	struct cpufreq_policy *policy;
6062306a36Sopenharmony_ci	unsigned int min_pctg = 0;
6162306a36Sopenharmony_ci	unsigned int max_pctg = 0;
6262306a36Sopenharmony_ci	unsigned long min, max;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	printf(_("          minimum CPU frequency  -  maximum CPU frequency  -  governor\n"));
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	nr_cpus = count_cpus();
6762306a36Sopenharmony_ci	for (cpu = 0; cpu < nr_cpus; cpu++) {
6862306a36Sopenharmony_ci		policy = cpufreq_get_policy(cpu);
6962306a36Sopenharmony_ci		if (!policy)
7062306a36Sopenharmony_ci			continue;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		if (cpufreq_get_hardware_limits(cpu, &min, &max)) {
7362306a36Sopenharmony_ci			max = 0;
7462306a36Sopenharmony_ci		} else {
7562306a36Sopenharmony_ci			min_pctg = (policy->min * 100) / max;
7662306a36Sopenharmony_ci			max_pctg = (policy->max * 100) / max;
7762306a36Sopenharmony_ci		}
7862306a36Sopenharmony_ci		printf("CPU%3d    %9lu kHz (%3d %%)  -  %9lu kHz (%3d %%)  -  %s\n",
7962306a36Sopenharmony_ci			cpu , policy->min, max ? min_pctg : 0, policy->max,
8062306a36Sopenharmony_ci			max ? max_pctg : 0, policy->governor);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		cpufreq_put_policy(policy);
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int no_rounding;
8762306a36Sopenharmony_cistatic void print_duration(unsigned long duration)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	unsigned long tmp;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (no_rounding) {
9262306a36Sopenharmony_ci		if (duration > 1000000)
9362306a36Sopenharmony_ci			printf("%u.%06u ms", ((unsigned int) duration/1000000),
9462306a36Sopenharmony_ci				((unsigned int) duration%1000000));
9562306a36Sopenharmony_ci		else if (duration > 100000)
9662306a36Sopenharmony_ci			printf("%u us", ((unsigned int) duration/1000));
9762306a36Sopenharmony_ci		else if (duration > 1000)
9862306a36Sopenharmony_ci			printf("%u.%03u us", ((unsigned int) duration/1000),
9962306a36Sopenharmony_ci				((unsigned int) duration%1000));
10062306a36Sopenharmony_ci		else
10162306a36Sopenharmony_ci			printf("%lu ns", duration);
10262306a36Sopenharmony_ci	} else {
10362306a36Sopenharmony_ci		if (duration > 1000000) {
10462306a36Sopenharmony_ci			tmp = duration%10000;
10562306a36Sopenharmony_ci			if (tmp >= 5000)
10662306a36Sopenharmony_ci				duration += 10000;
10762306a36Sopenharmony_ci			printf("%u.%02u ms", ((unsigned int) duration/1000000),
10862306a36Sopenharmony_ci				((unsigned int) (duration%1000000)/10000));
10962306a36Sopenharmony_ci		} else if (duration > 100000) {
11062306a36Sopenharmony_ci			tmp = duration%1000;
11162306a36Sopenharmony_ci			if (tmp >= 500)
11262306a36Sopenharmony_ci				duration += 1000;
11362306a36Sopenharmony_ci			printf("%u us", ((unsigned int) duration / 1000));
11462306a36Sopenharmony_ci		} else if (duration > 1000) {
11562306a36Sopenharmony_ci			tmp = duration%100;
11662306a36Sopenharmony_ci			if (tmp >= 50)
11762306a36Sopenharmony_ci				duration += 100;
11862306a36Sopenharmony_ci			printf("%u.%01u us", ((unsigned int) duration/1000),
11962306a36Sopenharmony_ci				((unsigned int) (duration%1000)/100));
12062306a36Sopenharmony_ci		} else
12162306a36Sopenharmony_ci			printf("%lu ns", duration);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	return;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int get_boost_mode_x86(unsigned int cpu)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	int support, active, b_states = 0, ret, pstate_no, i;
12962306a36Sopenharmony_ci	/* ToDo: Make this more global */
13062306a36Sopenharmony_ci	unsigned long pstates[MAX_HW_PSTATES] = {0,};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ret = cpufreq_has_boost_support(cpu, &support, &active, &b_states);
13362306a36Sopenharmony_ci	if (ret) {
13462306a36Sopenharmony_ci		printf(_("Error while evaluating Boost Capabilities"
13562306a36Sopenharmony_ci				" on CPU %d -- are you root?\n"), cpu);
13662306a36Sopenharmony_ci		return ret;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	/* P state changes via MSR are identified via cpuid 80000007
13962306a36Sopenharmony_ci	   on Intel and AMD, but we assume boost capable machines can do that
14062306a36Sopenharmony_ci	   if (cpuid_eax(0x80000000) >= 0x80000007
14162306a36Sopenharmony_ci	   && (cpuid_edx(0x80000007) & (1 << 7)))
14262306a36Sopenharmony_ci	*/
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	printf(_("  boost state support:\n"));
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	printf(_("    Supported: %s\n"), support ? _("yes") : _("no"));
14762306a36Sopenharmony_ci	printf(_("    Active: %s\n"), active ? _("yes") : _("no"));
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (cpupower_cpu_info.vendor == X86_VENDOR_AMD &&
15062306a36Sopenharmony_ci	    cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) {
15162306a36Sopenharmony_ci		return 0;
15262306a36Sopenharmony_ci	} else if ((cpupower_cpu_info.vendor == X86_VENDOR_AMD &&
15362306a36Sopenharmony_ci		    cpupower_cpu_info.family >= 0x10) ||
15462306a36Sopenharmony_ci		   cpupower_cpu_info.vendor == X86_VENDOR_HYGON) {
15562306a36Sopenharmony_ci		ret = decode_pstates(cpu, b_states, pstates, &pstate_no);
15662306a36Sopenharmony_ci		if (ret)
15762306a36Sopenharmony_ci			return ret;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		printf(_("    Boost States: %d\n"), b_states);
16062306a36Sopenharmony_ci		printf(_("    Total States: %d\n"), pstate_no);
16162306a36Sopenharmony_ci		for (i = 0; i < pstate_no; i++) {
16262306a36Sopenharmony_ci			if (!pstates[i])
16362306a36Sopenharmony_ci				continue;
16462306a36Sopenharmony_ci			if (i < b_states)
16562306a36Sopenharmony_ci				printf(_("    Pstate-Pb%d: %luMHz (boost state)"
16662306a36Sopenharmony_ci					 "\n"), i, pstates[i]);
16762306a36Sopenharmony_ci			else
16862306a36Sopenharmony_ci				printf(_("    Pstate-P%d:  %luMHz\n"),
16962306a36Sopenharmony_ci				       i - b_states, pstates[i]);
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci	} else if (cpupower_cpu_info.caps & CPUPOWER_CAP_HAS_TURBO_RATIO) {
17262306a36Sopenharmony_ci		double bclk;
17362306a36Sopenharmony_ci		unsigned long long intel_turbo_ratio = 0;
17462306a36Sopenharmony_ci		unsigned int ratio;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		/* Any way to autodetect this ? */
17762306a36Sopenharmony_ci		if (cpupower_cpu_info.caps & CPUPOWER_CAP_IS_SNB)
17862306a36Sopenharmony_ci			bclk = 100.00;
17962306a36Sopenharmony_ci		else
18062306a36Sopenharmony_ci			bclk = 133.33;
18162306a36Sopenharmony_ci		intel_turbo_ratio = msr_intel_get_turbo_ratio(cpu);
18262306a36Sopenharmony_ci		dprint ("    Ratio: 0x%llx - bclk: %f\n",
18362306a36Sopenharmony_ci			intel_turbo_ratio, bclk);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		ratio = (intel_turbo_ratio >> 24) & 0xFF;
18662306a36Sopenharmony_ci		if (ratio)
18762306a36Sopenharmony_ci			printf(_("    %.0f MHz max turbo 4 active cores\n"),
18862306a36Sopenharmony_ci			       ratio * bclk);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci		ratio = (intel_turbo_ratio >> 16) & 0xFF;
19162306a36Sopenharmony_ci		if (ratio)
19262306a36Sopenharmony_ci			printf(_("    %.0f MHz max turbo 3 active cores\n"),
19362306a36Sopenharmony_ci			       ratio * bclk);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		ratio = (intel_turbo_ratio >> 8) & 0xFF;
19662306a36Sopenharmony_ci		if (ratio)
19762306a36Sopenharmony_ci			printf(_("    %.0f MHz max turbo 2 active cores\n"),
19862306a36Sopenharmony_ci			       ratio * bclk);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		ratio = (intel_turbo_ratio >> 0) & 0xFF;
20162306a36Sopenharmony_ci		if (ratio)
20262306a36Sopenharmony_ci			printf(_("    %.0f MHz max turbo 1 active cores\n"),
20362306a36Sopenharmony_ci			       ratio * bclk);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/* --boost / -b */
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int get_boost_mode(unsigned int cpu)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct cpufreq_available_frequencies *freqs;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (cpupower_cpu_info.vendor == X86_VENDOR_AMD ||
21562306a36Sopenharmony_ci	    cpupower_cpu_info.vendor == X86_VENDOR_HYGON ||
21662306a36Sopenharmony_ci	    cpupower_cpu_info.vendor == X86_VENDOR_INTEL)
21762306a36Sopenharmony_ci		return get_boost_mode_x86(cpu);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	freqs = cpufreq_get_boost_frequencies(cpu);
22062306a36Sopenharmony_ci	if (freqs) {
22162306a36Sopenharmony_ci		printf(_("  boost frequency steps: "));
22262306a36Sopenharmony_ci		while (freqs->next) {
22362306a36Sopenharmony_ci			print_speed(freqs->frequency, no_rounding);
22462306a36Sopenharmony_ci			printf(", ");
22562306a36Sopenharmony_ci			freqs = freqs->next;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci		print_speed(freqs->frequency, no_rounding);
22862306a36Sopenharmony_ci		printf("\n");
22962306a36Sopenharmony_ci		cpufreq_put_available_frequencies(freqs);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci/* --freq / -f */
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int get_freq_kernel(unsigned int cpu, unsigned int human)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	unsigned long freq = cpufreq_get_freq_kernel(cpu);
24062306a36Sopenharmony_ci	printf(_("  current CPU frequency: "));
24162306a36Sopenharmony_ci	if (!freq) {
24262306a36Sopenharmony_ci		printf(_(" Unable to call to kernel\n"));
24362306a36Sopenharmony_ci		return -EINVAL;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci	if (human) {
24662306a36Sopenharmony_ci		print_speed(freq, no_rounding);
24762306a36Sopenharmony_ci	} else
24862306a36Sopenharmony_ci		printf("%lu", freq);
24962306a36Sopenharmony_ci	printf(_(" (asserted by call to kernel)\n"));
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/* --hwfreq / -w */
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int get_freq_hardware(unsigned int cpu, unsigned int human)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	unsigned long freq = cpufreq_get_freq_hardware(cpu);
25962306a36Sopenharmony_ci	printf(_("  current CPU frequency: "));
26062306a36Sopenharmony_ci	if (!freq) {
26162306a36Sopenharmony_ci		printf("Unable to call hardware\n");
26262306a36Sopenharmony_ci		return -EINVAL;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci	if (human) {
26562306a36Sopenharmony_ci		print_speed(freq, no_rounding);
26662306a36Sopenharmony_ci	} else
26762306a36Sopenharmony_ci		printf("%lu", freq);
26862306a36Sopenharmony_ci	printf(_(" (asserted by call to hardware)\n"));
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/* --hwlimits / -l */
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int get_hardware_limits(unsigned int cpu, unsigned int human)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	unsigned long min, max;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (cpufreq_get_hardware_limits(cpu, &min, &max)) {
27962306a36Sopenharmony_ci		printf(_("Not Available\n"));
28062306a36Sopenharmony_ci		return -EINVAL;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (human) {
28462306a36Sopenharmony_ci		printf(_("  hardware limits: "));
28562306a36Sopenharmony_ci		print_speed(min, no_rounding);
28662306a36Sopenharmony_ci		printf(" - ");
28762306a36Sopenharmony_ci		print_speed(max, no_rounding);
28862306a36Sopenharmony_ci		printf("\n");
28962306a36Sopenharmony_ci	} else {
29062306a36Sopenharmony_ci		printf("%lu %lu\n", min, max);
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/* --driver / -d */
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int get_driver(unsigned int cpu)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	char *driver = cpufreq_get_driver(cpu);
30062306a36Sopenharmony_ci	if (!driver) {
30162306a36Sopenharmony_ci		printf(_("  no or unknown cpufreq driver is active on this CPU\n"));
30262306a36Sopenharmony_ci		return -EINVAL;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci	printf("  driver: %s\n", driver);
30562306a36Sopenharmony_ci	cpufreq_put_driver(driver);
30662306a36Sopenharmony_ci	return 0;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci/* --policy / -p */
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int get_policy(unsigned int cpu)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct cpufreq_policy *policy = cpufreq_get_policy(cpu);
31462306a36Sopenharmony_ci	if (!policy) {
31562306a36Sopenharmony_ci		printf(_("  Unable to determine current policy\n"));
31662306a36Sopenharmony_ci		return -EINVAL;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci	printf(_("  current policy: frequency should be within "));
31962306a36Sopenharmony_ci	print_speed(policy->min, no_rounding);
32062306a36Sopenharmony_ci	printf(_(" and "));
32162306a36Sopenharmony_ci	print_speed(policy->max, no_rounding);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	printf(".\n                  ");
32462306a36Sopenharmony_ci	printf(_("The governor \"%s\" may decide which speed to use\n"
32562306a36Sopenharmony_ci	       "                  within this range.\n"),
32662306a36Sopenharmony_ci	       policy->governor);
32762306a36Sopenharmony_ci	cpufreq_put_policy(policy);
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/* --governors / -g */
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int get_available_governors(unsigned int cpu)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct cpufreq_available_governors *governors =
33662306a36Sopenharmony_ci		cpufreq_get_available_governors(cpu);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	printf(_("  available cpufreq governors: "));
33962306a36Sopenharmony_ci	if (!governors) {
34062306a36Sopenharmony_ci		printf(_("Not Available\n"));
34162306a36Sopenharmony_ci		return -EINVAL;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	while (governors->next) {
34562306a36Sopenharmony_ci		printf("%s ", governors->governor);
34662306a36Sopenharmony_ci		governors = governors->next;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	printf("%s\n", governors->governor);
34962306a36Sopenharmony_ci	cpufreq_put_available_governors(governors);
35062306a36Sopenharmony_ci	return 0;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/* --affected-cpus  / -a */
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int get_affected_cpus(unsigned int cpu)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct cpufreq_affected_cpus *cpus = cpufreq_get_affected_cpus(cpu);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	printf(_("  CPUs which need to have their frequency coordinated by software: "));
36162306a36Sopenharmony_ci	if (!cpus) {
36262306a36Sopenharmony_ci		printf(_("Not Available\n"));
36362306a36Sopenharmony_ci		return -EINVAL;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	while (cpus->next) {
36762306a36Sopenharmony_ci		printf("%d ", cpus->cpu);
36862306a36Sopenharmony_ci		cpus = cpus->next;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci	printf("%d\n", cpus->cpu);
37162306a36Sopenharmony_ci	cpufreq_put_affected_cpus(cpus);
37262306a36Sopenharmony_ci	return 0;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/* --related-cpus  / -r */
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic int get_related_cpus(unsigned int cpu)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct cpufreq_affected_cpus *cpus = cpufreq_get_related_cpus(cpu);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	printf(_("  CPUs which run at the same hardware frequency: "));
38262306a36Sopenharmony_ci	if (!cpus) {
38362306a36Sopenharmony_ci		printf(_("Not Available\n"));
38462306a36Sopenharmony_ci		return -EINVAL;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	while (cpus->next) {
38862306a36Sopenharmony_ci		printf("%d ", cpus->cpu);
38962306a36Sopenharmony_ci		cpus = cpus->next;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci	printf("%d\n", cpus->cpu);
39262306a36Sopenharmony_ci	cpufreq_put_related_cpus(cpus);
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/* --stats / -s */
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int get_freq_stats(unsigned int cpu, unsigned int human)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	unsigned long total_trans = cpufreq_get_transitions(cpu);
40162306a36Sopenharmony_ci	unsigned long long total_time;
40262306a36Sopenharmony_ci	struct cpufreq_stats *stats = cpufreq_get_stats(cpu, &total_time);
40362306a36Sopenharmony_ci	while (stats) {
40462306a36Sopenharmony_ci		if (human) {
40562306a36Sopenharmony_ci			print_speed(stats->frequency, no_rounding);
40662306a36Sopenharmony_ci			printf(":%.2f%%",
40762306a36Sopenharmony_ci				(100.0 * stats->time_in_state) / total_time);
40862306a36Sopenharmony_ci		} else
40962306a36Sopenharmony_ci			printf("%lu:%llu",
41062306a36Sopenharmony_ci				stats->frequency, stats->time_in_state);
41162306a36Sopenharmony_ci		stats = stats->next;
41262306a36Sopenharmony_ci		if (stats)
41362306a36Sopenharmony_ci			printf(", ");
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci	cpufreq_put_stats(stats);
41662306a36Sopenharmony_ci	if (total_trans)
41762306a36Sopenharmony_ci		printf("  (%lu)\n", total_trans);
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci/* --latency / -y */
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int get_latency(unsigned int cpu, unsigned int human)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	unsigned long latency = cpufreq_get_transition_latency(cpu);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	printf(_("  maximum transition latency: "));
42862306a36Sopenharmony_ci	if (!latency || latency == UINT_MAX) {
42962306a36Sopenharmony_ci		printf(_(" Cannot determine or is not supported.\n"));
43062306a36Sopenharmony_ci		return -EINVAL;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (human) {
43462306a36Sopenharmony_ci		print_duration(latency);
43562306a36Sopenharmony_ci		printf("\n");
43662306a36Sopenharmony_ci	} else
43762306a36Sopenharmony_ci		printf("%lu\n", latency);
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/* --performance / -c */
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic int get_perf_cap(unsigned int cpu)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	if (cpupower_cpu_info.vendor == X86_VENDOR_AMD &&
44662306a36Sopenharmony_ci	    cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE)
44762306a36Sopenharmony_ci		amd_pstate_show_perf_and_freq(cpu, no_rounding);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic void debug_output_one(unsigned int cpu)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct cpufreq_available_frequencies *freqs;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	get_driver(cpu);
45762306a36Sopenharmony_ci	get_related_cpus(cpu);
45862306a36Sopenharmony_ci	get_affected_cpus(cpu);
45962306a36Sopenharmony_ci	get_latency(cpu, 1);
46062306a36Sopenharmony_ci	get_hardware_limits(cpu, 1);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	freqs = cpufreq_get_available_frequencies(cpu);
46362306a36Sopenharmony_ci	if (freqs) {
46462306a36Sopenharmony_ci		printf(_("  available frequency steps:  "));
46562306a36Sopenharmony_ci		while (freqs->next) {
46662306a36Sopenharmony_ci			print_speed(freqs->frequency, no_rounding);
46762306a36Sopenharmony_ci			printf(", ");
46862306a36Sopenharmony_ci			freqs = freqs->next;
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci		print_speed(freqs->frequency, no_rounding);
47162306a36Sopenharmony_ci		printf("\n");
47262306a36Sopenharmony_ci		cpufreq_put_available_frequencies(freqs);
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	get_available_governors(cpu);
47662306a36Sopenharmony_ci	get_policy(cpu);
47762306a36Sopenharmony_ci	if (get_freq_hardware(cpu, 1) < 0)
47862306a36Sopenharmony_ci		get_freq_kernel(cpu, 1);
47962306a36Sopenharmony_ci	get_boost_mode(cpu);
48062306a36Sopenharmony_ci	get_perf_cap(cpu);
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic struct option info_opts[] = {
48462306a36Sopenharmony_ci	{"debug",	 no_argument,		 NULL,	 'e'},
48562306a36Sopenharmony_ci	{"boost",	 no_argument,		 NULL,	 'b'},
48662306a36Sopenharmony_ci	{"freq",	 no_argument,		 NULL,	 'f'},
48762306a36Sopenharmony_ci	{"hwfreq",	 no_argument,		 NULL,	 'w'},
48862306a36Sopenharmony_ci	{"hwlimits",	 no_argument,		 NULL,	 'l'},
48962306a36Sopenharmony_ci	{"driver",	 no_argument,		 NULL,	 'd'},
49062306a36Sopenharmony_ci	{"policy",	 no_argument,		 NULL,	 'p'},
49162306a36Sopenharmony_ci	{"governors",	 no_argument,		 NULL,	 'g'},
49262306a36Sopenharmony_ci	{"related-cpus",  no_argument,	 NULL,	 'r'},
49362306a36Sopenharmony_ci	{"affected-cpus", no_argument,	 NULL,	 'a'},
49462306a36Sopenharmony_ci	{"stats",	 no_argument,		 NULL,	 's'},
49562306a36Sopenharmony_ci	{"latency",	 no_argument,		 NULL,	 'y'},
49662306a36Sopenharmony_ci	{"proc",	 no_argument,		 NULL,	 'o'},
49762306a36Sopenharmony_ci	{"human",	 no_argument,		 NULL,	 'm'},
49862306a36Sopenharmony_ci	{"no-rounding", no_argument,	 NULL,	 'n'},
49962306a36Sopenharmony_ci	{"performance", no_argument,	 NULL,	 'c'},
50062306a36Sopenharmony_ci	{ },
50162306a36Sopenharmony_ci};
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ciint cmd_freq_info(int argc, char **argv)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	extern char *optarg;
50662306a36Sopenharmony_ci	extern int optind, opterr, optopt;
50762306a36Sopenharmony_ci	int ret = 0, cont = 1;
50862306a36Sopenharmony_ci	unsigned int cpu = 0;
50962306a36Sopenharmony_ci	unsigned int human = 0;
51062306a36Sopenharmony_ci	int output_param = 0;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	do {
51362306a36Sopenharmony_ci		ret = getopt_long(argc, argv, "oefwldpgrasmybnc", info_opts,
51462306a36Sopenharmony_ci				  NULL);
51562306a36Sopenharmony_ci		switch (ret) {
51662306a36Sopenharmony_ci		case '?':
51762306a36Sopenharmony_ci			output_param = '?';
51862306a36Sopenharmony_ci			cont = 0;
51962306a36Sopenharmony_ci			break;
52062306a36Sopenharmony_ci		case -1:
52162306a36Sopenharmony_ci			cont = 0;
52262306a36Sopenharmony_ci			break;
52362306a36Sopenharmony_ci		case 'b':
52462306a36Sopenharmony_ci		case 'o':
52562306a36Sopenharmony_ci		case 'a':
52662306a36Sopenharmony_ci		case 'r':
52762306a36Sopenharmony_ci		case 'g':
52862306a36Sopenharmony_ci		case 'p':
52962306a36Sopenharmony_ci		case 'd':
53062306a36Sopenharmony_ci		case 'l':
53162306a36Sopenharmony_ci		case 'w':
53262306a36Sopenharmony_ci		case 'f':
53362306a36Sopenharmony_ci		case 'e':
53462306a36Sopenharmony_ci		case 's':
53562306a36Sopenharmony_ci		case 'y':
53662306a36Sopenharmony_ci		case 'c':
53762306a36Sopenharmony_ci			if (output_param) {
53862306a36Sopenharmony_ci				output_param = -1;
53962306a36Sopenharmony_ci				cont = 0;
54062306a36Sopenharmony_ci				break;
54162306a36Sopenharmony_ci			}
54262306a36Sopenharmony_ci			output_param = ret;
54362306a36Sopenharmony_ci			break;
54462306a36Sopenharmony_ci		case 'm':
54562306a36Sopenharmony_ci			if (human) {
54662306a36Sopenharmony_ci				output_param = -1;
54762306a36Sopenharmony_ci				cont = 0;
54862306a36Sopenharmony_ci				break;
54962306a36Sopenharmony_ci			}
55062306a36Sopenharmony_ci			human = 1;
55162306a36Sopenharmony_ci			break;
55262306a36Sopenharmony_ci		case 'n':
55362306a36Sopenharmony_ci			no_rounding = 1;
55462306a36Sopenharmony_ci			break;
55562306a36Sopenharmony_ci		default:
55662306a36Sopenharmony_ci			fprintf(stderr, "invalid or unknown argument\n");
55762306a36Sopenharmony_ci			return EXIT_FAILURE;
55862306a36Sopenharmony_ci		}
55962306a36Sopenharmony_ci	} while (cont);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	switch (output_param) {
56262306a36Sopenharmony_ci	case 'o':
56362306a36Sopenharmony_ci		if (!bitmask_isallclear(cpus_chosen)) {
56462306a36Sopenharmony_ci			printf(_("The argument passed to this tool can't be "
56562306a36Sopenharmony_ci				 "combined with passing a --cpu argument\n"));
56662306a36Sopenharmony_ci			return -EINVAL;
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci		break;
56962306a36Sopenharmony_ci	case 0:
57062306a36Sopenharmony_ci		output_param = 'e';
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	ret = 0;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Default is: show output of base_cpu only */
57662306a36Sopenharmony_ci	if (bitmask_isallclear(cpus_chosen))
57762306a36Sopenharmony_ci		bitmask_setbit(cpus_chosen, base_cpu);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	switch (output_param) {
58062306a36Sopenharmony_ci	case -1:
58162306a36Sopenharmony_ci		printf(_("You can't specify more than one --cpu parameter and/or\n"
58262306a36Sopenharmony_ci		       "more than one output-specific argument\n"));
58362306a36Sopenharmony_ci		return -EINVAL;
58462306a36Sopenharmony_ci	case '?':
58562306a36Sopenharmony_ci		printf(_("invalid or unknown argument\n"));
58662306a36Sopenharmony_ci		return -EINVAL;
58762306a36Sopenharmony_ci	case 'o':
58862306a36Sopenharmony_ci		proc_cpufreq_output();
58962306a36Sopenharmony_ci		return EXIT_SUCCESS;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	for (cpu = bitmask_first(cpus_chosen);
59362306a36Sopenharmony_ci	     cpu <= bitmask_last(cpus_chosen); cpu++) {
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if (!bitmask_isbitset(cpus_chosen, cpu))
59662306a36Sopenharmony_ci			continue;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		printf(_("analyzing CPU %d:\n"), cpu);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		if (sysfs_is_cpu_online(cpu) != 1) {
60162306a36Sopenharmony_ci			printf(_(" *is offline\n"));
60262306a36Sopenharmony_ci			printf("\n");
60362306a36Sopenharmony_ci			continue;
60462306a36Sopenharmony_ci		}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		switch (output_param) {
60762306a36Sopenharmony_ci		case 'b':
60862306a36Sopenharmony_ci			get_boost_mode(cpu);
60962306a36Sopenharmony_ci			break;
61062306a36Sopenharmony_ci		case 'e':
61162306a36Sopenharmony_ci			debug_output_one(cpu);
61262306a36Sopenharmony_ci			break;
61362306a36Sopenharmony_ci		case 'a':
61462306a36Sopenharmony_ci			ret = get_affected_cpus(cpu);
61562306a36Sopenharmony_ci			break;
61662306a36Sopenharmony_ci		case 'r':
61762306a36Sopenharmony_ci			ret = get_related_cpus(cpu);
61862306a36Sopenharmony_ci			break;
61962306a36Sopenharmony_ci		case 'g':
62062306a36Sopenharmony_ci			ret = get_available_governors(cpu);
62162306a36Sopenharmony_ci			break;
62262306a36Sopenharmony_ci		case 'p':
62362306a36Sopenharmony_ci			ret = get_policy(cpu);
62462306a36Sopenharmony_ci			break;
62562306a36Sopenharmony_ci		case 'd':
62662306a36Sopenharmony_ci			ret = get_driver(cpu);
62762306a36Sopenharmony_ci			break;
62862306a36Sopenharmony_ci		case 'l':
62962306a36Sopenharmony_ci			ret = get_hardware_limits(cpu, human);
63062306a36Sopenharmony_ci			break;
63162306a36Sopenharmony_ci		case 'w':
63262306a36Sopenharmony_ci			ret = get_freq_hardware(cpu, human);
63362306a36Sopenharmony_ci			break;
63462306a36Sopenharmony_ci		case 'f':
63562306a36Sopenharmony_ci			ret = get_freq_kernel(cpu, human);
63662306a36Sopenharmony_ci			break;
63762306a36Sopenharmony_ci		case 's':
63862306a36Sopenharmony_ci			ret = get_freq_stats(cpu, human);
63962306a36Sopenharmony_ci			break;
64062306a36Sopenharmony_ci		case 'y':
64162306a36Sopenharmony_ci			ret = get_latency(cpu, human);
64262306a36Sopenharmony_ci			break;
64362306a36Sopenharmony_ci		case 'c':
64462306a36Sopenharmony_ci			ret = get_perf_cap(cpu);
64562306a36Sopenharmony_ci			break;
64662306a36Sopenharmony_ci		}
64762306a36Sopenharmony_ci		if (ret)
64862306a36Sopenharmony_ci			return ret;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	return ret;
65162306a36Sopenharmony_ci}
652