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
pstates_set_scheduler_mode(scheduler_mode mode)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
pstates_increase_performance(int delta)43 pstates_increase_performance(int delta)
44 {
45 return B_NOT_SUPPORTED;
46 }
47
48
49 static status_t
pstates_decrease_performance(int delta)50 pstates_decrease_performance(int delta)
51 {
52 return B_NOT_SUPPORTED;
53 }
54
55
56 static bool
is_cpu_model_supported(cpu_ent * cpu)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
set_normal_pstate(void *,int cpu)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
init_pstates()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
uninit_pstates()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
std_ops(int32 op,...)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