xref: /openbmc/linux/arch/x86/kernel/acpi/cppc.c (revision fa797951)
1fd8af343SHuang Rui // SPDX-License-Identifier: GPL-2.0-only
2fd8af343SHuang Rui /*
3fd8af343SHuang Rui  * cppc.c: CPPC Interface for x86
4fd8af343SHuang Rui  * Copyright (c) 2016, Intel Corporation.
5fd8af343SHuang Rui  */
6fd8af343SHuang Rui 
7fd8af343SHuang Rui #include <acpi/cppc_acpi.h>
8fd8af343SHuang Rui #include <asm/msr.h>
982d89369SHuang Rui #include <asm/processor.h>
1082d89369SHuang Rui #include <asm/topology.h>
11fd8af343SHuang Rui 
12fd8af343SHuang Rui /* Refer to drivers/acpi/cppc_acpi.c for the description of functions */
13fd8af343SHuang Rui 
cpc_supported_by_cpu(void)148b356e53SMario Limonciello bool cpc_supported_by_cpu(void)
158b356e53SMario Limonciello {
168b356e53SMario Limonciello 	switch (boot_cpu_data.x86_vendor) {
178b356e53SMario Limonciello 	case X86_VENDOR_AMD:
188b356e53SMario Limonciello 	case X86_VENDOR_HYGON:
19fbd74d16SMario Limonciello 		if (boot_cpu_data.x86 == 0x19 && ((boot_cpu_data.x86_model <= 0x0f) ||
20fbd74d16SMario Limonciello 		    (boot_cpu_data.x86_model >= 0x20 && boot_cpu_data.x86_model <= 0x2f)))
21fbd74d16SMario Limonciello 			return true;
22fbd74d16SMario Limonciello 		else if (boot_cpu_data.x86 == 0x17 &&
23*fa797951SPerry Yuan 			 boot_cpu_data.x86_model >= 0x30 && boot_cpu_data.x86_model <= 0x7f)
24fbd74d16SMario Limonciello 			return true;
258b356e53SMario Limonciello 		return boot_cpu_has(X86_FEATURE_CPPC);
268b356e53SMario Limonciello 	}
278b356e53SMario Limonciello 	return false;
288b356e53SMario Limonciello }
298b356e53SMario Limonciello 
cpc_ffh_supported(void)30fd8af343SHuang Rui bool cpc_ffh_supported(void)
31fd8af343SHuang Rui {
32fd8af343SHuang Rui 	return true;
33fd8af343SHuang Rui }
34fd8af343SHuang Rui 
cpc_read_ffh(int cpunum,struct cpc_reg * reg,u64 * val)35fd8af343SHuang Rui int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
36fd8af343SHuang Rui {
37fd8af343SHuang Rui 	int err;
38fd8af343SHuang Rui 
39fd8af343SHuang Rui 	err = rdmsrl_safe_on_cpu(cpunum, reg->address, val);
40fd8af343SHuang Rui 	if (!err) {
41fd8af343SHuang Rui 		u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
42fd8af343SHuang Rui 				       reg->bit_offset);
43fd8af343SHuang Rui 
44fd8af343SHuang Rui 		*val &= mask;
45fd8af343SHuang Rui 		*val >>= reg->bit_offset;
46fd8af343SHuang Rui 	}
47fd8af343SHuang Rui 	return err;
48fd8af343SHuang Rui }
49fd8af343SHuang Rui 
cpc_write_ffh(int cpunum,struct cpc_reg * reg,u64 val)50fd8af343SHuang Rui int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
51fd8af343SHuang Rui {
52fd8af343SHuang Rui 	u64 rd_val;
53fd8af343SHuang Rui 	int err;
54fd8af343SHuang Rui 
55fd8af343SHuang Rui 	err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val);
56fd8af343SHuang Rui 	if (!err) {
57fd8af343SHuang Rui 		u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
58fd8af343SHuang Rui 				       reg->bit_offset);
59fd8af343SHuang Rui 
60fd8af343SHuang Rui 		val <<= reg->bit_offset;
61fd8af343SHuang Rui 		val &= mask;
62fd8af343SHuang Rui 		rd_val &= ~mask;
63fd8af343SHuang Rui 		rd_val |= val;
64fd8af343SHuang Rui 		err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val);
65fd8af343SHuang Rui 	}
66fd8af343SHuang Rui 	return err;
67fd8af343SHuang Rui }
6882d89369SHuang Rui 
amd_set_max_freq_ratio(void)690dfaf3f6SThomas Gleixner static void amd_set_max_freq_ratio(void)
7082d89369SHuang Rui {
7182d89369SHuang Rui 	struct cppc_perf_caps perf_caps;
7282d89369SHuang Rui 	u64 highest_perf, nominal_perf;
7382d89369SHuang Rui 	u64 perf_ratio;
7482d89369SHuang Rui 	int rc;
7582d89369SHuang Rui 
7682d89369SHuang Rui 	rc = cppc_get_perf_caps(0, &perf_caps);
7782d89369SHuang Rui 	if (rc) {
7882d89369SHuang Rui 		pr_debug("Could not retrieve perf counters (%d)\n", rc);
790dfaf3f6SThomas Gleixner 		return;
8082d89369SHuang Rui 	}
8182d89369SHuang Rui 
8282d89369SHuang Rui 	highest_perf = amd_get_highest_perf();
8382d89369SHuang Rui 	nominal_perf = perf_caps.nominal_perf;
8482d89369SHuang Rui 
8582d89369SHuang Rui 	if (!highest_perf || !nominal_perf) {
8682d89369SHuang Rui 		pr_debug("Could not retrieve highest or nominal performance\n");
870dfaf3f6SThomas Gleixner 		return;
8882d89369SHuang Rui 	}
8982d89369SHuang Rui 
9082d89369SHuang Rui 	perf_ratio = div_u64(highest_perf * SCHED_CAPACITY_SCALE, nominal_perf);
9182d89369SHuang Rui 	/* midpoint between max_boost and max_P */
9282d89369SHuang Rui 	perf_ratio = (perf_ratio + SCHED_CAPACITY_SCALE) >> 1;
9382d89369SHuang Rui 	if (!perf_ratio) {
9482d89369SHuang Rui 		pr_debug("Non-zero highest/nominal perf values led to a 0 ratio\n");
950dfaf3f6SThomas Gleixner 		return;
9682d89369SHuang Rui 	}
9782d89369SHuang Rui 
980dfaf3f6SThomas Gleixner 	freq_invariance_set_perf_ratio(perf_ratio, false);
9982d89369SHuang Rui }
100eb5616d4SHuang Rui 
101eb5616d4SHuang Rui static DEFINE_MUTEX(freq_invariance_lock);
102eb5616d4SHuang Rui 
init_freq_invariance_cppc(void)103eb5616d4SHuang Rui void init_freq_invariance_cppc(void)
104eb5616d4SHuang Rui {
1050dfaf3f6SThomas Gleixner 	static bool init_done;
1060dfaf3f6SThomas Gleixner 
1070dfaf3f6SThomas Gleixner 	if (!cpu_feature_enabled(X86_FEATURE_APERFMPERF))
1080dfaf3f6SThomas Gleixner 		return;
1090dfaf3f6SThomas Gleixner 
1100dfaf3f6SThomas Gleixner 	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
1110dfaf3f6SThomas Gleixner 		return;
112eb5616d4SHuang Rui 
113eb5616d4SHuang Rui 	mutex_lock(&freq_invariance_lock);
1140dfaf3f6SThomas Gleixner 	if (!init_done)
1150dfaf3f6SThomas Gleixner 		amd_set_max_freq_ratio();
1160dfaf3f6SThomas Gleixner 	init_done = true;
117eb5616d4SHuang Rui 	mutex_unlock(&freq_invariance_lock);
118eb5616d4SHuang Rui }
119