162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__)
362306a36Sopenharmony_ci#include <unistd.h>
462306a36Sopenharmony_ci#include <errno.h>
562306a36Sopenharmony_ci#include <stdio.h>
662306a36Sopenharmony_ci#include <stdint.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <pci/pci.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "helpers/helpers.h"
1162306a36Sopenharmony_ci#include "cpufreq.h"
1262306a36Sopenharmony_ci#include "acpi_cppc.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* ACPI P-States Helper Functions for AMD Processors ***************/
1562306a36Sopenharmony_ci#define MSR_AMD_PSTATE_STATUS	0xc0010063
1662306a36Sopenharmony_ci#define MSR_AMD_PSTATE		0xc0010064
1762306a36Sopenharmony_ci#define MSR_AMD_PSTATE_LIMIT	0xc0010061
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciunion core_pstate {
2062306a36Sopenharmony_ci	/* pre fam 17h: */
2162306a36Sopenharmony_ci	struct {
2262306a36Sopenharmony_ci		unsigned fid:6;
2362306a36Sopenharmony_ci		unsigned did:3;
2462306a36Sopenharmony_ci		unsigned vid:7;
2562306a36Sopenharmony_ci		unsigned res1:6;
2662306a36Sopenharmony_ci		unsigned nbdid:1;
2762306a36Sopenharmony_ci		unsigned res2:2;
2862306a36Sopenharmony_ci		unsigned nbvid:7;
2962306a36Sopenharmony_ci		unsigned iddval:8;
3062306a36Sopenharmony_ci		unsigned idddiv:2;
3162306a36Sopenharmony_ci		unsigned res3:21;
3262306a36Sopenharmony_ci		unsigned en:1;
3362306a36Sopenharmony_ci	} pstate;
3462306a36Sopenharmony_ci	/* since fam 17h: */
3562306a36Sopenharmony_ci	struct {
3662306a36Sopenharmony_ci		unsigned fid:8;
3762306a36Sopenharmony_ci		unsigned did:6;
3862306a36Sopenharmony_ci		unsigned vid:8;
3962306a36Sopenharmony_ci		unsigned iddval:8;
4062306a36Sopenharmony_ci		unsigned idddiv:2;
4162306a36Sopenharmony_ci		unsigned res1:31;
4262306a36Sopenharmony_ci		unsigned en:1;
4362306a36Sopenharmony_ci	} pstatedef;
4462306a36Sopenharmony_ci	unsigned long long val;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int get_did(union core_pstate pstate)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	int t;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF)
5262306a36Sopenharmony_ci		t = pstate.pstatedef.did;
5362306a36Sopenharmony_ci	else if (cpupower_cpu_info.family == 0x12)
5462306a36Sopenharmony_ci		t = pstate.val & 0xf;
5562306a36Sopenharmony_ci	else
5662306a36Sopenharmony_ci		t = pstate.pstate.did;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return t;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int get_cof(union core_pstate pstate)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	int t;
6462306a36Sopenharmony_ci	int fid, did, cof;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	did = get_did(pstate);
6762306a36Sopenharmony_ci	if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATEDEF) {
6862306a36Sopenharmony_ci		fid = pstate.pstatedef.fid;
6962306a36Sopenharmony_ci		cof = 200 * fid / did;
7062306a36Sopenharmony_ci	} else {
7162306a36Sopenharmony_ci		t = 0x10;
7262306a36Sopenharmony_ci		fid = pstate.pstate.fid;
7362306a36Sopenharmony_ci		if (cpupower_cpu_info.family == 0x11)
7462306a36Sopenharmony_ci			t = 0x8;
7562306a36Sopenharmony_ci		cof = (100 * (fid + t)) >> did;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	return cof;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* Needs:
8162306a36Sopenharmony_ci * cpu          -> the cpu that gets evaluated
8262306a36Sopenharmony_ci * boost_states -> how much boost states the machines support
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * Fills up:
8562306a36Sopenharmony_ci * pstates -> a pointer to an array of size MAX_HW_PSTATES
8662306a36Sopenharmony_ci *            must be initialized with zeros.
8762306a36Sopenharmony_ci *            All available  HW pstates (including boost states)
8862306a36Sopenharmony_ci * no      -> amount of pstates above array got filled up with
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * returns zero on success, -1 on failure
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ciint decode_pstates(unsigned int cpu, int boost_states,
9362306a36Sopenharmony_ci		   unsigned long *pstates, int *no)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int i, psmax;
9662306a36Sopenharmony_ci	union core_pstate pstate;
9762306a36Sopenharmony_ci	unsigned long long val;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* Only read out frequencies from HW if HW Pstate is supported,
10062306a36Sopenharmony_ci	 * otherwise frequencies are exported via ACPI tables.
10162306a36Sopenharmony_ci	 */
10262306a36Sopenharmony_ci	if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_HW_PSTATE))
10362306a36Sopenharmony_ci		return -1;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (read_msr(cpu, MSR_AMD_PSTATE_LIMIT, &val))
10662306a36Sopenharmony_ci		return -1;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	psmax = (val >> 4) & 0x7;
10962306a36Sopenharmony_ci	psmax += boost_states;
11062306a36Sopenharmony_ci	for (i = 0; i <= psmax; i++) {
11162306a36Sopenharmony_ci		if (i >= MAX_HW_PSTATES) {
11262306a36Sopenharmony_ci			fprintf(stderr, "HW pstates [%d] exceeding max [%d]\n",
11362306a36Sopenharmony_ci				psmax, MAX_HW_PSTATES);
11462306a36Sopenharmony_ci			return -1;
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci		if (read_msr(cpu, MSR_AMD_PSTATE + i, &pstate.val))
11762306a36Sopenharmony_ci			return -1;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		/* The enabled bit (bit 63) is common for all families */
12062306a36Sopenharmony_ci		if (!pstate.pstatedef.en)
12162306a36Sopenharmony_ci			continue;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		pstates[i] = get_cof(pstate);
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	*no = i;
12662306a36Sopenharmony_ci	return 0;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciint amd_pci_get_num_boost_states(int *active, int *states)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	struct pci_access *pci_acc;
13262306a36Sopenharmony_ci	struct pci_dev *device;
13362306a36Sopenharmony_ci	uint8_t val = 0;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	*active = *states = 0;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	device = pci_slot_func_init(&pci_acc, 0x18, 4);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (device == NULL)
14062306a36Sopenharmony_ci		return -ENODEV;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	val = pci_read_byte(device, 0x15c);
14362306a36Sopenharmony_ci	if (val & 3)
14462306a36Sopenharmony_ci		*active = 1;
14562306a36Sopenharmony_ci	else
14662306a36Sopenharmony_ci		*active = 0;
14762306a36Sopenharmony_ci	*states = (val >> 2) & 7;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	pci_cleanup(pci_acc);
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* ACPI P-States Helper Functions for AMD Processors ***************/
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* AMD P-State Helper Functions ************************************/
15662306a36Sopenharmony_cienum amd_pstate_value {
15762306a36Sopenharmony_ci	AMD_PSTATE_HIGHEST_PERF,
15862306a36Sopenharmony_ci	AMD_PSTATE_MAX_FREQ,
15962306a36Sopenharmony_ci	AMD_PSTATE_LOWEST_NONLINEAR_FREQ,
16062306a36Sopenharmony_ci	MAX_AMD_PSTATE_VALUE_READ_FILES,
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic const char *amd_pstate_value_files[MAX_AMD_PSTATE_VALUE_READ_FILES] = {
16462306a36Sopenharmony_ci	[AMD_PSTATE_HIGHEST_PERF] = "amd_pstate_highest_perf",
16562306a36Sopenharmony_ci	[AMD_PSTATE_MAX_FREQ] = "amd_pstate_max_freq",
16662306a36Sopenharmony_ci	[AMD_PSTATE_LOWEST_NONLINEAR_FREQ] = "amd_pstate_lowest_nonlinear_freq",
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic unsigned long amd_pstate_get_data(unsigned int cpu,
17062306a36Sopenharmony_ci					 enum amd_pstate_value value)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	return cpufreq_get_sysfs_value_from_table(cpu,
17362306a36Sopenharmony_ci						  amd_pstate_value_files,
17462306a36Sopenharmony_ci						  value,
17562306a36Sopenharmony_ci						  MAX_AMD_PSTATE_VALUE_READ_FILES);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_civoid amd_pstate_boost_init(unsigned int cpu, int *support, int *active)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	unsigned long highest_perf, nominal_perf, cpuinfo_min,
18162306a36Sopenharmony_ci		      cpuinfo_max, amd_pstate_max;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	highest_perf = amd_pstate_get_data(cpu, AMD_PSTATE_HIGHEST_PERF);
18462306a36Sopenharmony_ci	nominal_perf = acpi_cppc_get_data(cpu, NOMINAL_PERF);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	*support = highest_perf > nominal_perf ? 1 : 0;
18762306a36Sopenharmony_ci	if (!(*support))
18862306a36Sopenharmony_ci		return;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	cpufreq_get_hardware_limits(cpu, &cpuinfo_min, &cpuinfo_max);
19162306a36Sopenharmony_ci	amd_pstate_max = amd_pstate_get_data(cpu, AMD_PSTATE_MAX_FREQ);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	*active = cpuinfo_max == amd_pstate_max ? 1 : 0;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_civoid amd_pstate_show_perf_and_freq(unsigned int cpu, int no_rounding)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	printf(_("    AMD PSTATE Highest Performance: %lu. Maximum Frequency: "),
19962306a36Sopenharmony_ci	       amd_pstate_get_data(cpu, AMD_PSTATE_HIGHEST_PERF));
20062306a36Sopenharmony_ci	/*
20162306a36Sopenharmony_ci	 * If boost isn't active, the cpuinfo_max doesn't indicate real max
20262306a36Sopenharmony_ci	 * frequency. So we read it back from amd-pstate sysfs entry.
20362306a36Sopenharmony_ci	 */
20462306a36Sopenharmony_ci	print_speed(amd_pstate_get_data(cpu, AMD_PSTATE_MAX_FREQ), no_rounding);
20562306a36Sopenharmony_ci	printf(".\n");
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	printf(_("    AMD PSTATE Nominal Performance: %lu. Nominal Frequency: "),
20862306a36Sopenharmony_ci	       acpi_cppc_get_data(cpu, NOMINAL_PERF));
20962306a36Sopenharmony_ci	print_speed(acpi_cppc_get_data(cpu, NOMINAL_FREQ) * 1000,
21062306a36Sopenharmony_ci		    no_rounding);
21162306a36Sopenharmony_ci	printf(".\n");
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	printf(_("    AMD PSTATE Lowest Non-linear Performance: %lu. Lowest Non-linear Frequency: "),
21462306a36Sopenharmony_ci	       acpi_cppc_get_data(cpu, LOWEST_NONLINEAR_PERF));
21562306a36Sopenharmony_ci	print_speed(amd_pstate_get_data(cpu, AMD_PSTATE_LOWEST_NONLINEAR_FREQ),
21662306a36Sopenharmony_ci		    no_rounding);
21762306a36Sopenharmony_ci	printf(".\n");
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	printf(_("    AMD PSTATE Lowest Performance: %lu. Lowest Frequency: "),
22062306a36Sopenharmony_ci	       acpi_cppc_get_data(cpu, LOWEST_PERF));
22162306a36Sopenharmony_ci	print_speed(acpi_cppc_get_data(cpu, LOWEST_FREQ) * 1000, no_rounding);
22262306a36Sopenharmony_ci	printf(".\n");
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/* AMD P-State Helper Functions ************************************/
22662306a36Sopenharmony_ci#endif /* defined(__i386__) || defined(__x86_64__) */
227