kernel: Add cpufreq module for Intel P-states

Since Sandy Bridge managing P-states on Intel processors is much easier
and more powerful than when using previous versions of EIST.
This commit is contained in:
Pawel Dziepak
2013-10-30 00:55:03 +01:00
parent 1e3cf82d85
commit 9c0ff0eed1
8 changed files with 259 additions and 0 deletions
+1
View File
@@ -52,6 +52,7 @@ AddFilesToPackage add-ons kernel generic
AddFilesToPackage add-ons kernel partitioning_systems
: amiga_rdb apple efi_gpt intel session ;
AddFilesToPackage add-ons kernel interrupt_controllers : openpic@ppc ;
AddFilesToPackage add-ons kernel power cpufreq : intel_pstates@x86,x86_64 ;
if $(TARGET_ARCH) = x86 || $(TARGET_ARCH) = x86_64 {
AddFilesToPackage add-ons kernel cpu : generic_x86 ;
+1
View File
@@ -51,6 +51,7 @@ AddFilesToPackage add-ons kernel generic
AddFilesToPackage add-ons kernel partitioning_systems
: amiga_rdb apple efi_gpt intel session ;
AddFilesToPackage add-ons kernel interrupt_controllers : openpic@ppc ;
AddFilesToPackage add-ons kernel power cpufreq : intel_pstates@x86,x86_64 ;
if $(TARGET_ARCH) = x86 || $(TARGET_ARCH) = x86_64 {
AddFilesToPackage add-ons kernel cpu : generic_x86 ;
@@ -31,10 +31,16 @@
#define IA32_MSR_TSC 0x10
#define IA32_MSR_APIC_BASE 0x1b
#define IA32_MSR_PLATFORM_INFO 0xce
#define IA32_MSR_MPERF 0xe7
#define IA32_MSR_APERF 0xe8
#define IA32_MSR_MTRR_CAPABILITIES 0xfe
#define IA32_MSR_SYSENTER_CS 0x174
#define IA32_MSR_SYSENTER_ESP 0x175
#define IA32_MSR_SYSENTER_EIP 0x176
#define IA32_MSR_PERF_STATUS 0x198
#define IA32_MSR_PERF_CTL 0x199
#define IA32_MSR_TURBO_RATIO_LIMIT 0x1ad
#define IA32_MSR_ENERGY_PERF_BIAS 0x1b0
#define IA32_MSR_MTRR_DEFAULT_TYPE 0x2ff
#define IA32_MSR_MTRR_PHYSICAL_BASE_0 0x200
+1
View File
@@ -14,3 +14,4 @@ SubInclude HAIKU_TOP src add-ons kernel media ;
SubInclude HAIKU_TOP src add-ons kernel partitioning_systems ;
SubInclude HAIKU_TOP src add-ons kernel generic ;
SubInclude HAIKU_TOP src add-ons kernel bluetooth ;
SubInclude HAIKU_TOP src add-ons kernel power ;
+4
View File
@@ -0,0 +1,4 @@
SubDir HAIKU_TOP src add-ons kernel power ;
SubInclude HAIKU_TOP src add-ons kernel power cpufreq ;
+4
View File
@@ -0,0 +1,4 @@
SubDir HAIKU_TOP src add-ons kernel power cpufreq ;
SubInclude HAIKU_TOP src add-ons kernel power cpufreq intel_pstates ;
@@ -0,0 +1,8 @@
SubDir HAIKU_TOP src add-ons kernel power cpufreq intel_pstates ;
UsePrivateKernelHeaders ;
KernelAddon intel_pstates :
intel_pstates.cpp
;
@@ -0,0 +1,234 @@
/*
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, <[email protected]>
*/
#include <cpufreq.h>
#include <KernelExport.h>
#include <arch_cpu.h>
#include <cpu.h>
#include <smp.h>
#include <util/AutoLock.h>
#define INTEL_PSTATES_MODULE_NAME CPUFREQ_MODULES_PREFIX "/intel_pstates/v1"
const int kMinimalInterval = 50000;
static uint16 sMinPState;
static uint16 sMaxPState;
static uint16 sBoostPState;
struct CPUEntry {
CPUEntry();
uint16 fCurrentPState;
bigtime_t fLastUpdate;
};
static CPUEntry* sCPUEntries;
CPUEntry::CPUEntry()
:
fCurrentPState(sMinPState - 1),
fLastUpdate(0)
{
}
static int
measure_pstate(CPUEntry* entry)
{
InterruptsLocker locker;
uint64 mperf = x86_read_msr(IA32_MSR_MPERF);
uint64 aperf = x86_read_msr(IA32_MSR_APERF);
x86_write_msr(IA32_MSR_MPERF, 0);
x86_write_msr(IA32_MSR_APERF, 0);
locker.Unlock();
if (mperf == 0)
return sMinPState;
int oldPState = sMaxPState * aperf / mperf;
oldPState = min_c(max_c(oldPState, sMinPState), sBoostPState);
return oldPState;
}
static inline void
set_pstate(uint16 pstate)
{
CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()];
pstate = min_c(max_c(sMinPState, pstate), sBoostPState);
if (entry->fCurrentPState != pstate) {
entry->fLastUpdate = system_time();
entry->fCurrentPState = pstate;
x86_write_msr(IA32_MSR_PERF_CTL, pstate << 8);
}
}
static status_t
increase_performance(int delta, bool allowBoost)
{
CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()];
if (system_time() - entry->fLastUpdate < kMinimalInterval)
return B_OK;
int pState = measure_pstate(entry);
pState += (sBoostPState - pState) * delta / kCPUPerformanceScaleMax;
if (!allowBoost)
pState = min_c(pState, sMaxPState);
set_pstate(pState);
return B_OK;
}
static status_t
decrease_performance(int delta)
{
CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()];
if (system_time() - entry->fLastUpdate < kMinimalInterval)
return B_OK;
int pState = measure_pstate(entry);
pState -= (pState - sMinPState) * delta / kCPUPerformanceScaleMax;
set_pstate(pState);
return B_OK;
}
static bool
is_cpu_model_supported(cpu_ent* cpu)
{
uint8 model = cpu->arch.model + (cpu->arch.extended_model << 4);
if (cpu->arch.vendor != VENDOR_INTEL)
return false;
if (cpu->arch.family != 6)
return false;
const uint8 kSupportedFamily6Models[] = {
0x2a, 0x2d, 0x2e, 0x3a, 0x3c, 0x3e, 0x3f, 0x45, 0x46,
};
const int kSupportedFamily6ModelsCount
= sizeof(kSupportedFamily6Models) / sizeof(uint8);
int i;
for (i = 0; i < kSupportedFamily6ModelsCount; i++) {
if (model == kSupportedFamily6Models[i])
break;
}
return i != kSupportedFamily6ModelsCount;
}
static void
set_normal_pstate(void* /* dummy */, int cpu)
{
measure_pstate(&sCPUEntries[cpu]);
set_pstate(sMaxPState);
}
static status_t
init_pstates()
{
if (!x86_check_feature(IA32_FEATURE_MSR, FEATURE_COMMON))
return B_ERROR;
if (!x86_check_feature(IA32_FEATURE_APERFMPERF, FEATURE_6_ECX))
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;
}
sMinPState = (x86_read_msr(IA32_MSR_PLATFORM_INFO) >> 40) & 0xff;
sMaxPState = (x86_read_msr(IA32_MSR_PLATFORM_INFO) >> 8) & 0xff;
sBoostPState
= max_c(x86_read_msr(IA32_MSR_TURBO_RATIO_LIMIT) & 0xff, sMaxPState);
dprintf("using Intel P-States: min %" B_PRIu16 ", max %" B_PRIu16
", boost %" B_PRIu16 "\n", sMinPState, sMaxPState, sBoostPState);
if (sMaxPState <= sMinPState || sMaxPState == 0) {
dprintf("unexpected or invalid Intel P-States limits, aborting\n");
return B_ERROR;
}
sCPUEntries = new(std::nothrow) CPUEntry[cpuCount];
if (sCPUEntries == NULL)
return B_NO_MEMORY;
call_all_cpus_sync(set_normal_pstate, NULL);
return B_OK;
}
static status_t
uninit_pstates()
{
call_all_cpus_sync(set_normal_pstate, NULL);
delete[] sCPUEntries;
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 sIntelPStates = {
{
INTEL_PSTATES_MODULE_NAME,
0,
std_ops,
},
increase_performance,
decrease_performance,
};
module_info* modules[] = {
(module_info*)&sIntelPStates,
NULL
};