* Copyright 2020-2022, JΓ©rΓ΄me Duval, jerome.duval@gmail.com.
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* PaweΕ Dziepak, <pdziepak@quarnos.org>
*/
#include <cpufreq.h>
#include <KernelExport.h>
#include <arch_cpu.h>
#include <cpu.h>
#include <smp.h>
#include <util/AutoLock.h>
#define AMD_PSTATES_MODULE_NAME CPUFREQ_MODULES_PREFIX "/amd_pstates/v1"
static uint32 sHWPLowest;
static uint32 sHWPGuaranteed;
static uint32 sHWPEfficient;
static uint32 sHWPHighest;
static bool sAvoidBoost = true;
static void set_normal_pstate(void* , int cpu);
static void
pstates_set_scheduler_mode(scheduler_mode mode)
{
sAvoidBoost = mode == SCHEDULER_MODE_POWER_SAVING;
call_all_cpus(set_normal_pstate, NULL);
}
static status_t
pstates_increase_performance(int delta)
{
return B_NOT_SUPPORTED;
}
static status_t
pstates_decrease_performance(int delta)
{
return B_NOT_SUPPORTED;
}
static bool
is_cpu_model_supported(cpu_ent* cpu)
{
if (cpu->arch.vendor != VENDOR_AMD)
return false;
return true;
}
static void
set_normal_pstate(void* , int cpu)
{
x86_write_msr(MSR_AMD_CPPC_ENABLE, 1);
uint64 cap1 = x86_read_msr(MSR_AMD_CPPC_CAP1);
sHWPLowest = AMD_CPPC_LOWEST_PERF(cap1);
sHWPEfficient = AMD_CPPC_LOWNONLIN_PERF(cap1);
sHWPGuaranteed = AMD_CPPC_NOMINAL_PERF(cap1);
sHWPHighest = AMD_CPPC_HIGHEST_PERF(cap1);
uint64 request = AMD_CPPC_MIN_PERF(sHWPEfficient);
request |= AMD_CPPC_MAX_PERF(sAvoidBoost ? sHWPGuaranteed : sHWPHighest);
request |= AMD_CPPC_EPP_PERF(
sAvoidBoost ? AMD_CPPC_EPP_BALANCE_PERFORMANCE : AMD_CPPC_EPP_PERFORMANCE);
x86_write_msr(MSR_AMD_CPPC_REQ, request & 0xffffffff);
}
static status_t
init_pstates()
{
if (!x86_check_feature(IA32_FEATURE_CPPC, FEATURE_EXT_8_EBX))
return B_ERROR;
int32 cpuCount = smp_get_num_cpus();
for (int32 i = 0; i < cpuCount; i++) {
if (!is_cpu_model_supported(&gCPU[i]))
return B_ERROR;
}
dprintf("using AMD P-States (capabilities: 0x%08" B_PRIx64 "\n",
x86_read_msr(MSR_AMD_CPPC_CAP1));
pstates_set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY);
call_all_cpus_sync(set_normal_pstate, NULL);
return B_OK;
}
static status_t
uninit_pstates()
{
call_all_cpus_sync(set_normal_pstate, NULL);
return B_OK;
}
static status_t
std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
return init_pstates();
case B_MODULE_UNINIT:
uninit_pstates();
return B_OK;
}
return B_ERROR;
}
static cpufreq_module_info sAMDPStates = {
{
AMD_PSTATES_MODULE_NAME,
0,
std_ops,
},
1.0f,
pstates_set_scheduler_mode,
pstates_increase_performance,
pstates_decrease_performance,
};
module_info* modules[] = {
(module_info*)&sAMDPStates,
NULL
};