162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <stdio.h>
462306a36Sopenharmony_ci#include <errno.h>
562306a36Sopenharmony_ci#include <stdlib.h>
662306a36Sopenharmony_ci#include <string.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "helpers/helpers.h"
962306a36Sopenharmony_ci#include "helpers/sysfs.h"
1062306a36Sopenharmony_ci#include "cpufreq.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__)
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "cpupower_intern.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define MSR_AMD_HWCR	0xc0010015
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciint cpufreq_has_boost_support(unsigned int cpu, int *support, int *active,
1962306a36Sopenharmony_ci			int *states)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	int ret;
2262306a36Sopenharmony_ci	unsigned long long val;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	*support = *active = *states = 0;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_CPB) {
2762306a36Sopenharmony_ci		*support = 1;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci		/* AMD Family 0x17 does not utilize PCI D18F4 like prior
3062306a36Sopenharmony_ci		 * families and has no fixed discrete boost states but
3162306a36Sopenharmony_ci		 * has Hardware determined variable increments instead.
3262306a36Sopenharmony_ci		 */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci		if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_CPB_MSR) {
3562306a36Sopenharmony_ci			if (!read_msr(cpu, MSR_AMD_HWCR, &val)) {
3662306a36Sopenharmony_ci				if (!(val & CPUPOWER_AMD_CPBDIS))
3762306a36Sopenharmony_ci					*active = 1;
3862306a36Sopenharmony_ci			}
3962306a36Sopenharmony_ci		} else {
4062306a36Sopenharmony_ci			ret = amd_pci_get_num_boost_states(active, states);
4162306a36Sopenharmony_ci			if (ret)
4262306a36Sopenharmony_ci				return ret;
4362306a36Sopenharmony_ci		}
4462306a36Sopenharmony_ci	} else if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) {
4562306a36Sopenharmony_ci		amd_pstate_boost_init(cpu, support, active);
4662306a36Sopenharmony_ci	} else if (cpupower_cpu_info.caps & CPUPOWER_CAP_INTEL_IDA)
4762306a36Sopenharmony_ci		*support = *active = 1;
4862306a36Sopenharmony_ci	return 0;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciint cpupower_intel_get_perf_bias(unsigned int cpu)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	char linebuf[MAX_LINE_LEN];
5462306a36Sopenharmony_ci	char path[SYSFS_PATH_MAX];
5562306a36Sopenharmony_ci	unsigned long val;
5662306a36Sopenharmony_ci	char *endp;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS))
5962306a36Sopenharmony_ci		return -1;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/power/energy_perf_bias", cpu);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
6462306a36Sopenharmony_ci		return -1;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	val = strtol(linebuf, &endp, 0);
6762306a36Sopenharmony_ci	if (endp == linebuf || errno == ERANGE)
6862306a36Sopenharmony_ci		return -1;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return val;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciint cpupower_intel_set_perf_bias(unsigned int cpu, unsigned int val)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	char path[SYSFS_PATH_MAX];
7662306a36Sopenharmony_ci	char linebuf[3] = {};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS))
7962306a36Sopenharmony_ci		return -1;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/power/energy_perf_bias", cpu);
8262306a36Sopenharmony_ci	snprintf(linebuf, sizeof(linebuf), "%d", val);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (cpupower_write_sysfs(path, linebuf, 3) <= 0)
8562306a36Sopenharmony_ci		return -1;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	return 0;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciint cpupower_set_epp(unsigned int cpu, char *epp)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	char path[SYSFS_PATH_MAX];
9362306a36Sopenharmony_ci	char linebuf[30] = {};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	snprintf(path, sizeof(path),
9662306a36Sopenharmony_ci		PATH_TO_CPU "cpu%u/cpufreq/energy_performance_preference", cpu);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (!is_valid_path(path))
9962306a36Sopenharmony_ci		return -1;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	snprintf(linebuf, sizeof(linebuf), "%s", epp);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (cpupower_write_sysfs(path, linebuf, 30) <= 0)
10462306a36Sopenharmony_ci		return -1;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ciint cpupower_set_amd_pstate_mode(char *mode)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	char path[SYSFS_PATH_MAX];
11262306a36Sopenharmony_ci	char linebuf[20] = {};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	snprintf(path, sizeof(path), PATH_TO_CPU "amd_pstate/status");
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!is_valid_path(path))
11762306a36Sopenharmony_ci		return -1;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	snprintf(linebuf, sizeof(linebuf), "%s\n", mode);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (cpupower_write_sysfs(path, linebuf, 20) <= 0)
12262306a36Sopenharmony_ci		return -1;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return 0;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciint cpupower_set_turbo_boost(int turbo_boost)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	char path[SYSFS_PATH_MAX];
13062306a36Sopenharmony_ci	char linebuf[2] = {};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	snprintf(path, sizeof(path), PATH_TO_CPU "cpufreq/boost");
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (!is_valid_path(path))
13562306a36Sopenharmony_ci		return -1;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	snprintf(linebuf, sizeof(linebuf), "%d", turbo_boost);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (cpupower_write_sysfs(path, linebuf, 2) <= 0)
14062306a36Sopenharmony_ci		return -1;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cibool cpupower_amd_pstate_enabled(void)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	char *driver = cpufreq_get_driver(0);
14862306a36Sopenharmony_ci	bool ret = false;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (!driver)
15162306a36Sopenharmony_ci		return ret;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (!strncmp(driver, "amd", 3))
15462306a36Sopenharmony_ci		ret = true;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	cpufreq_put_driver(driver);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return ret;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#endif /* #if defined(__i386__) || defined(__x86_64__) */
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/* get_cpustate
16462306a36Sopenharmony_ci *
16562306a36Sopenharmony_ci * Gather the information of all online CPUs into bitmask struct
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_civoid get_cpustate(void)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	unsigned int cpu = 0;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	bitmask_clearall(online_cpus);
17262306a36Sopenharmony_ci	bitmask_clearall(offline_cpus);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	for (cpu = bitmask_first(cpus_chosen);
17562306a36Sopenharmony_ci		cpu <= bitmask_last(cpus_chosen); cpu++) {
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		if (cpupower_is_cpu_online(cpu) == 1)
17862306a36Sopenharmony_ci			bitmask_setbit(online_cpus, cpu);
17962306a36Sopenharmony_ci		else
18062306a36Sopenharmony_ci			bitmask_setbit(offline_cpus, cpu);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		continue;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/* print_online_cpus
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * Print the CPU numbers of all CPUs that are online currently
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_civoid print_online_cpus(void)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	int str_len = 0;
19362306a36Sopenharmony_ci	char *online_cpus_str = NULL;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	str_len = online_cpus->size * 5;
19662306a36Sopenharmony_ci	online_cpus_str = (void *)malloc(sizeof(char) * str_len);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (!bitmask_isallclear(online_cpus)) {
19962306a36Sopenharmony_ci		bitmask_displaylist(online_cpus_str, str_len, online_cpus);
20062306a36Sopenharmony_ci		printf(_("Following CPUs are online:\n%s\n"), online_cpus_str);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/* print_offline_cpus
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * Print the CPU numbers of all CPUs that are offline currently
20762306a36Sopenharmony_ci */
20862306a36Sopenharmony_civoid print_offline_cpus(void)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int str_len = 0;
21162306a36Sopenharmony_ci	char *offline_cpus_str = NULL;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	str_len = offline_cpus->size * 5;
21462306a36Sopenharmony_ci	offline_cpus_str = (void *)malloc(sizeof(char) * str_len);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (!bitmask_isallclear(offline_cpus)) {
21762306a36Sopenharmony_ci		bitmask_displaylist(offline_cpus_str, str_len, offline_cpus);
21862306a36Sopenharmony_ci		printf(_("Following CPUs are offline:\n%s\n"), offline_cpus_str);
21962306a36Sopenharmony_ci		printf(_("cpupower set operation was not performed on them\n"));
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/*
22462306a36Sopenharmony_ci * print_speed
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci * Print the exact CPU frequency with appropriate unit
22762306a36Sopenharmony_ci */
22862306a36Sopenharmony_civoid print_speed(unsigned long speed, int no_rounding)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	unsigned long tmp;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (no_rounding) {
23362306a36Sopenharmony_ci		if (speed > 1000000)
23462306a36Sopenharmony_ci			printf("%u.%06u GHz", ((unsigned int)speed / 1000000),
23562306a36Sopenharmony_ci			       ((unsigned int)speed % 1000000));
23662306a36Sopenharmony_ci		else if (speed > 1000)
23762306a36Sopenharmony_ci			printf("%u.%03u MHz", ((unsigned int)speed / 1000),
23862306a36Sopenharmony_ci			       (unsigned int)(speed % 1000));
23962306a36Sopenharmony_ci		else
24062306a36Sopenharmony_ci			printf("%lu kHz", speed);
24162306a36Sopenharmony_ci	} else {
24262306a36Sopenharmony_ci		if (speed > 1000000) {
24362306a36Sopenharmony_ci			tmp = speed % 10000;
24462306a36Sopenharmony_ci			if (tmp >= 5000)
24562306a36Sopenharmony_ci				speed += 10000;
24662306a36Sopenharmony_ci			printf("%u.%02u GHz", ((unsigned int)speed / 1000000),
24762306a36Sopenharmony_ci			       ((unsigned int)(speed % 1000000) / 10000));
24862306a36Sopenharmony_ci		} else if (speed > 100000) {
24962306a36Sopenharmony_ci			tmp = speed % 1000;
25062306a36Sopenharmony_ci			if (tmp >= 500)
25162306a36Sopenharmony_ci				speed += 1000;
25262306a36Sopenharmony_ci			printf("%u MHz", ((unsigned int)speed / 1000));
25362306a36Sopenharmony_ci		} else if (speed > 1000) {
25462306a36Sopenharmony_ci			tmp = speed % 100;
25562306a36Sopenharmony_ci			if (tmp >= 50)
25662306a36Sopenharmony_ci				speed += 100;
25762306a36Sopenharmony_ci			printf("%u.%01u MHz", ((unsigned int)speed / 1000),
25862306a36Sopenharmony_ci			       ((unsigned int)(speed % 1000) / 100));
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci}
262