xref: /haiku/src/add-ons/kernel/power/cpufreq/amd_pstates/amd_pstates.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
1 /*
2  * Copyright 2020-2022, Jérôme Duval, jerome.duval@gmail.com.
3  * Copyright 2013, Haiku, Inc. All Rights Reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * Authors:
7  *		Paweł Dziepak, <pdziepak@quarnos.org>
8  */
9 
10 
11 #include <cpufreq.h>
12 #include <KernelExport.h>
13 
14 #include <arch_cpu.h>
15 #include <cpu.h>
16 #include <smp.h>
17 #include <util/AutoLock.h>
18 
19 
20 #define AMD_PSTATES_MODULE_NAME	CPUFREQ_MODULES_PREFIX "/amd_pstates/v1"
21 
22 
23 static uint32 sHWPLowest;
24 static uint32 sHWPGuaranteed;
25 static uint32 sHWPEfficient;
26 static uint32 sHWPHighest;
27 
28 static bool sAvoidBoost = true;
29 
30 
31 static void set_normal_pstate(void* /* dummy */, int cpu);
32 
33 
34 static void
35 pstates_set_scheduler_mode(scheduler_mode mode)
36 {
37 	sAvoidBoost = mode == SCHEDULER_MODE_POWER_SAVING;
38 	call_all_cpus(set_normal_pstate, NULL);
39 }
40 
41 
42 static status_t
43 pstates_increase_performance(int delta)
44 {
45 	return B_NOT_SUPPORTED;
46 }
47 
48 
49 static status_t
50 pstates_decrease_performance(int delta)
51 {
52 	return B_NOT_SUPPORTED;
53 }
54 
55 
56 static bool
57 is_cpu_model_supported(cpu_ent* cpu)
58 {
59 	if (cpu->arch.vendor != VENDOR_AMD)
60 		return false;
61 
62 	return true;
63 }
64 
65 
66 static void
67 set_normal_pstate(void* /* dummy */, int cpu)
68 {
69 	x86_write_msr(MSR_AMD_CPPC_ENABLE, 1);
70 
71 	uint64 cap1 = x86_read_msr(MSR_AMD_CPPC_CAP1);
72 	sHWPLowest = AMD_CPPC_LOWEST_PERF(cap1);
73 	sHWPEfficient = AMD_CPPC_LOWNONLIN_PERF(cap1);
74 	sHWPGuaranteed = AMD_CPPC_NOMINAL_PERF(cap1);
75 	sHWPHighest = AMD_CPPC_HIGHEST_PERF(cap1);
76 
77 	uint64 request = AMD_CPPC_MIN_PERF(sHWPEfficient);
78 	request |= AMD_CPPC_MAX_PERF(sAvoidBoost ? sHWPGuaranteed : sHWPHighest);
79 	request |= AMD_CPPC_EPP_PERF(
80 		sAvoidBoost ? AMD_CPPC_EPP_BALANCE_PERFORMANCE : AMD_CPPC_EPP_PERFORMANCE);
81 	x86_write_msr(MSR_AMD_CPPC_REQ, request & 0xffffffff);
82 }
83 
84 
85 static status_t
86 init_pstates()
87 {
88 	if (!x86_check_feature(IA32_FEATURE_CPPC, FEATURE_EXT_8_EBX))
89 		return B_ERROR;
90 
91 	int32 cpuCount = smp_get_num_cpus();
92 	for (int32 i = 0; i < cpuCount; i++) {
93 		if (!is_cpu_model_supported(&gCPU[i]))
94 			return B_ERROR;
95 	}
96 
97 	dprintf("using AMD P-States (capabilities: 0x%08" B_PRIx64 "\n",
98 		x86_read_msr(MSR_AMD_CPPC_CAP1));
99 
100 	pstates_set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
101 
102 	call_all_cpus_sync(set_normal_pstate, NULL);
103 	return B_OK;
104 }
105 
106 
107 static status_t
108 uninit_pstates()
109 {
110 	call_all_cpus_sync(set_normal_pstate, NULL);
111 
112 	return B_OK;
113 }
114 
115 
116 static status_t
117 std_ops(int32 op, ...)
118 {
119 	switch (op) {
120 		case B_MODULE_INIT:
121 			return init_pstates();
122 
123 		case B_MODULE_UNINIT:
124 			uninit_pstates();
125 			return B_OK;
126 	}
127 
128 	return B_ERROR;
129 }
130 
131 
132 static cpufreq_module_info sAMDPStates = {
133 	{
134 		AMD_PSTATES_MODULE_NAME,
135 		0,
136 		std_ops,
137 	},
138 
139 	1.0f,
140 
141 	pstates_set_scheduler_mode,
142 
143 	pstates_increase_performance,
144 	pstates_decrease_performance,
145 };
146 
147 
148 module_info* modules[] = {
149 	(module_info*)&sAMDPStates,
150 	NULL
151 };
152 
153