cpuidle: move generic cpuidle to generic
After this change, low level cpuidle drivers load the generic cpuidle module if they can support the underlying platform. change the intel cpuidle driver accordingly, now it's loaded by acpi bus manager during boot, although it doesn't depend on acpi Signed-off-by: Fredrik Holmqvist <[email protected]>
This commit is contained in:
committed by
Fredrik Holmqvist
parent
57311e7bb7
commit
71d9d375b8
@@ -14,7 +14,7 @@ extern "C" {
|
||||
|
||||
#define CPUIDLE_CSTATE_MAX 8
|
||||
#define CSTATE_NAME_LENGTH 32
|
||||
#define B_CPUIDLE_MODULE_NAME "idle/generic/cpuidle/v1"
|
||||
#define B_CPUIDLE_MODULE_NAME "generic/cpuidle/v1"
|
||||
|
||||
|
||||
struct CpuidleStat {
|
||||
@@ -28,28 +28,25 @@ struct CpuidleInfo {
|
||||
CpuidleStat stats[CPUIDLE_CSTATE_MAX];
|
||||
};
|
||||
|
||||
struct CpuidleDevice;
|
||||
|
||||
struct CpuidleCstate {
|
||||
char name[CSTATE_NAME_LENGTH];
|
||||
int32 latency;
|
||||
int32 (*EnterIdle)(int32 state, CpuidleCstate *cstate);
|
||||
int32 (*EnterIdle)(int32 state, CpuidleDevice *device);
|
||||
void *pData;
|
||||
};
|
||||
|
||||
|
||||
struct CpuidleModuleInfo {
|
||||
module_info info;
|
||||
struct CpuidleDevice {
|
||||
CpuidleCstate cStates[CPUIDLE_CSTATE_MAX];
|
||||
int32 cStateCount;
|
||||
};
|
||||
|
||||
|
||||
struct GenCpuidle {
|
||||
struct CpuidleModuleInfo {
|
||||
module_info info;
|
||||
int32 (*GetIdleStateCount)(void);
|
||||
char * (*GetIdleStateName)(int32 state);
|
||||
void (*GetIdleStateInfo)(int32 cpu, int32 state,
|
||||
CpuidleStat *stat);
|
||||
status_t (*AddDevice)(CpuidleDevice *device);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ SubInclude HAIKU_TOP src add-ons kernel debugger ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_cache ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel file_systems ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel idle ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel interrupt_controllers ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel network ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel media ;
|
||||
|
||||
@@ -5,3 +5,4 @@ SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_button ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_lid ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_thermal ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers power enhanced_speedstep ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers power x86_cpuidle ;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel drivers power x86_cpuidle ;
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon x86_cpuidle :
|
||||
acpi_cpuidle.cpp
|
||||
intel_cpuidle.cpp
|
||||
;
|
||||
|
||||
Depends x86_cpuidle : acpi ;
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright 2012, Haiku, Inc. All Rights Reserved.
|
||||
*
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Yongcong Du <[email protected]>
|
||||
*/
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
#include <Errors.h>
|
||||
#include <smp.h>
|
||||
#include <cpu.h>
|
||||
#include <arch_system_info.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ACPI.h>
|
||||
|
||||
#include "x86_cpuidle.h"
|
||||
|
||||
#define ACPI_CPUIDLE_MODULE_NAME "drivers/power/x86_cpuidle/acpi/driver_v1"
|
||||
|
||||
struct acpi_cpuidle_driver_info {
|
||||
device_node *node;
|
||||
acpi_device_module_info *acpi;
|
||||
acpi_device acpi_cookie;
|
||||
};
|
||||
|
||||
static device_manager_info *sDeviceManager;
|
||||
static acpi_cpuidle_driver_info *acpi_processor[B_MAX_CPU_COUNT];
|
||||
|
||||
|
||||
static status_t
|
||||
acpi_cpuidle_init(void)
|
||||
{
|
||||
dprintf("acpi_cpuidle_init\n");
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
acpi_processor_init(acpi_cpuidle_driver_info *device)
|
||||
{
|
||||
status_t status = B_ERROR;
|
||||
|
||||
acpi_data buffer;
|
||||
buffer.pointer = NULL;
|
||||
buffer.length = ACPI_ALLOCATE_BUFFER;
|
||||
dprintf("get acpi processor @%p\n", device->acpi_cookie);
|
||||
status = device->acpi->evaluate_method(device->acpi_cookie, NULL, NULL,
|
||||
&buffer);
|
||||
if (status != B_OK) {
|
||||
dprintf("failed to get processor obj\n");
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
acpi_object_type *object = (acpi_object_type *)buffer.pointer;
|
||||
dprintf("acpi cpu%"B_PRId32": P_BLK at %#x/%lu\n",
|
||||
object->data.processor.cpu_id,
|
||||
object->data.processor.pblk_address,
|
||||
object->data.processor.pblk_length);
|
||||
int32 cpuid = object->data.processor.cpu_id;
|
||||
free(buffer.pointer);
|
||||
if (cpuid > smp_get_num_cpus())
|
||||
return B_ERROR;
|
||||
|
||||
if (smp_get_num_cpus() == 1)
|
||||
cpuid = 1;
|
||||
|
||||
acpi_processor[cpuid-1] = device;
|
||||
|
||||
if (cpuid == 1) {
|
||||
if (intel_cpuidle_init() != B_OK)
|
||||
return acpi_cpuidle_init();
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static float
|
||||
acpi_cpuidle_support(device_node *parent)
|
||||
{
|
||||
const char *bus;
|
||||
uint32 device_type;
|
||||
|
||||
dprintf("acpi_cpuidle_support\n");
|
||||
// make sure parent is really the ACPI bus manager
|
||||
if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
|
||||
return -1;
|
||||
|
||||
if (strcmp(bus, "acpi"))
|
||||
return 0.0;
|
||||
|
||||
// check whether it's really a cpu Device
|
||||
if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, &device_type, false) != B_OK
|
||||
|| device_type != ACPI_TYPE_PROCESSOR) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return 0.6;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
acpi_cpuidle_register_device(device_node *node)
|
||||
{
|
||||
device_attr attrs[] = {
|
||||
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI CPU IDLE" }},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
dprintf("acpi_cpuidle_register_device\n");
|
||||
return sDeviceManager->register_node(node, ACPI_CPUIDLE_MODULE_NAME, attrs,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
acpi_cpuidle_init_driver(device_node *node, void **driverCookie)
|
||||
{
|
||||
dprintf("acpi_cpuidle_init_driver\n");
|
||||
acpi_cpuidle_driver_info *device;
|
||||
device = (acpi_cpuidle_driver_info *)calloc(1, sizeof(*device));
|
||||
if (device == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
*driverCookie = device;
|
||||
|
||||
device->node = node;
|
||||
|
||||
device_node *parent;
|
||||
parent = sDeviceManager->get_parent_node(node);
|
||||
sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi,
|
||||
(void **)&device->acpi_cookie);
|
||||
sDeviceManager->put_node(parent);
|
||||
|
||||
return acpi_processor_init(device);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acpi_cpuidle_uninit_driver(void *driverCookie)
|
||||
{
|
||||
dprintf("acpi_cpuidle_uninit_driver");
|
||||
acpi_cpuidle_driver_info *device = (acpi_cpuidle_driver_info *)driverCookie;
|
||||
free(device);
|
||||
}
|
||||
|
||||
|
||||
module_dependency module_dependencies[] = {
|
||||
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
static driver_module_info sAcpiidleModule = {
|
||||
{
|
||||
ACPI_CPUIDLE_MODULE_NAME,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
acpi_cpuidle_support,
|
||||
acpi_cpuidle_register_device,
|
||||
acpi_cpuidle_init_driver,
|
||||
acpi_cpuidle_uninit_driver,
|
||||
NULL,
|
||||
NULL, // rescan
|
||||
NULL, // removed
|
||||
};
|
||||
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sAcpiidleModule,
|
||||
NULL
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2012, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Yongcong Du <[email protected]>
|
||||
*/
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <cpu.h>
|
||||
#include <arch_system_info.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cpuidle.h>
|
||||
|
||||
#include "x86_cpuidle.h"
|
||||
|
||||
static CpuidleModuleInfo *sIdle;
|
||||
static CpuidleDevice sIntelDevice;
|
||||
|
||||
static void *kMwaitEax[] = {
|
||||
// C0, we never use it
|
||||
(void *)0x00,
|
||||
// MWAIT C1
|
||||
(void *)0x00,
|
||||
// MWAIT C2
|
||||
(void *)0x10,
|
||||
// MWAIT C3
|
||||
(void *)0x20,
|
||||
// MWAIT C4
|
||||
(void *)0x30,
|
||||
// MWAIT C5
|
||||
(void *)0x40,
|
||||
// MWAIT C6, 0x2 is used to fully shrink L2 cache
|
||||
(void *)0x52
|
||||
};
|
||||
|
||||
|
||||
static int32
|
||||
IntelCstateIdleEnter(int32 state, CpuidleDevice *device)
|
||||
{
|
||||
cpu_ent *cpu = get_cpu_struct();
|
||||
if (cpu->invoke_scheduler)
|
||||
return 0;
|
||||
|
||||
CpuidleCstate *cState = &device->cStates[state];
|
||||
x86_monitor((void *)&cpu->invoke_scheduler, 0, 0);
|
||||
if (!cpu->invoke_scheduler)
|
||||
x86_mwait((unsigned long)cState->pData, 1);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
static CpuidleCstate sSnbcStates[CPUIDLE_CSTATE_MAX] = {
|
||||
{},
|
||||
{
|
||||
"C1-SNB",
|
||||
1,
|
||||
IntelCstateIdleEnter,
|
||||
},
|
||||
{
|
||||
"C3-SNB",
|
||||
80,
|
||||
IntelCstateIdleEnter,
|
||||
},
|
||||
{
|
||||
"C6-SNB",
|
||||
104,
|
||||
IntelCstateIdleEnter,
|
||||
},
|
||||
{
|
||||
"C7-SNB",
|
||||
109,
|
||||
IntelCstateIdleEnter,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
status_t
|
||||
intel_cpuidle_init(void)
|
||||
{
|
||||
dprintf("intel idle init\n");
|
||||
if (get_module(B_CPUIDLE_MODULE_NAME, (module_info**)&sIdle) != B_OK)
|
||||
return B_ERROR;
|
||||
cpu_ent *cpu = get_cpu_struct();
|
||||
if (cpu->arch.vendor != VENDOR_INTEL || cpu->arch.family != 6)
|
||||
return B_ERROR;
|
||||
|
||||
// Calculated Model Value: M = (Extended Model << 4) + Model
|
||||
uint32 model = (cpu->arch.extended_model << 4) + cpu->arch.model;
|
||||
if (model != 0x2a && model != 0x2d)
|
||||
return B_ERROR;
|
||||
|
||||
cpuid_info cpuid;
|
||||
get_current_cpuid(&cpuid, 5);
|
||||
/* ecx[0] monitor/mwait extension supported
|
||||
* ecx[1] support for treating interrupts as break-events for mwait
|
||||
* edx number of sub-states
|
||||
*/
|
||||
if (!((cpuid.regs.ecx & 0x1) && (cpuid.regs.ecx & 0x2) &&
|
||||
cpuid.regs.edx)) {
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
sIntelDevice.cStateCount = 1;
|
||||
for (int32 i = 1; i < CPUIDLE_CSTATE_MAX; i++) {
|
||||
int32 subStates = (cpuid.regs.edx >> ((i) * 4)) & 0xf;
|
||||
// no sub-states means the state is not available
|
||||
if (!subStates)
|
||||
continue;
|
||||
sIntelDevice.cStates[sIntelDevice.cStateCount] =
|
||||
sSnbcStates[i];
|
||||
sIntelDevice.cStates[sIntelDevice.cStateCount].pData =
|
||||
kMwaitEax[i];
|
||||
sIntelDevice.cStateCount++;
|
||||
}
|
||||
status_t status = sIdle->AddDevice(&sIntelDevice);
|
||||
if (status == B_OK)
|
||||
dprintf("using intel idle\n");
|
||||
else
|
||||
put_module(B_CPUIDLE_MODULE_NAME);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2012, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
static inline void
|
||||
x86_monitor(const void *addr, unsigned long ecx, unsigned long edx)
|
||||
{
|
||||
asm volatile("monitor"
|
||||
:: "a" (addr), "c" (ecx), "d"(edx));
|
||||
}
|
||||
|
||||
static inline void
|
||||
x86_mwait(unsigned long eax, unsigned long ecx)
|
||||
{
|
||||
asm volatile("mwait"
|
||||
:: "a" (eax), "c" (ecx));
|
||||
}
|
||||
|
||||
status_t intel_cpuidle_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel generic ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel generic ata_adapter ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel generic atomizer ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel generic cpuidle ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel generic dpc ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel generic ide_adapter ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel generic locked_pool ;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel generic cpuidle ;
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon cpuidle :
|
||||
cpuidle.cpp
|
||||
;
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2012, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Yongcong Du <[email protected]>
|
||||
*/
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <smp.h>
|
||||
#include <cpuidle.h>
|
||||
|
||||
|
||||
static CpuidleInfo sPerCPU[B_MAX_CPU_COUNT];
|
||||
static CpuidleDevice *sDevice;
|
||||
extern void (*gCpuIdleFunc)(void);
|
||||
|
||||
|
||||
/*
|
||||
* next cstate selection algorithm is based on NetBSD's
|
||||
* it's simple, stupid
|
||||
*/
|
||||
static int32
|
||||
SelectCstate(CpuidleInfo *info)
|
||||
{
|
||||
static const int32 csFactor = 3;
|
||||
|
||||
for (int32 i = sDevice->cStateCount - 1; i > 0; i--) {
|
||||
CpuidleCstate *cState = &sDevice->cStates[i];
|
||||
if (info->cstateSleep > cState->latency * csFactor)
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Choose C1 if there's no state found */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
EnterCstate(int32 state, CpuidleInfo *info)
|
||||
{
|
||||
CpuidleCstate *cstate = &sDevice->cStates[state];
|
||||
bigtime_t now = system_time();
|
||||
int32 finalState = cstate->EnterIdle(state, sDevice);
|
||||
if (finalState > 0) {
|
||||
bigtime_t diff = system_time() - now;
|
||||
info->cstateSleep = diff;
|
||||
info->stats[finalState].usageCount++;
|
||||
info->stats[finalState].usageTime += diff;
|
||||
} else {
|
||||
info->cstateSleep = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
CpuCstateIdle(void)
|
||||
{
|
||||
CpuidleInfo *info = &sPerCPU[smp_get_current_cpu()];
|
||||
int32 state = SelectCstate(info);
|
||||
EnterCstate(state, info);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
return B_OK;
|
||||
case B_MODULE_UNINIT:
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t AddDevice(CpuidleDevice *device)
|
||||
{
|
||||
sDevice = device;
|
||||
memory_write_barrier();
|
||||
gCpuIdleFunc = CpuCstateIdle;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static CpuidleModuleInfo sCpuidleModule = {
|
||||
{
|
||||
B_CPUIDLE_MODULE_NAME,
|
||||
0,
|
||||
std_ops
|
||||
},
|
||||
|
||||
AddDevice,
|
||||
};
|
||||
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sCpuidleModule,
|
||||
NULL
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel idle ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel idle cpuidles ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel idle generic ;
|
||||
@@ -1,3 +0,0 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel idle cpuidles ;
|
||||
|
||||
SubInclude HAIKU_TOP src add-ons kernel idle cpuidles intel_cpuidle ;
|
||||
@@ -1,7 +0,0 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel idle cpuidles intel_cpuidle ;
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon intel_cpuidle :
|
||||
intel_cpuidle.cpp
|
||||
;
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Yongcong Du <[email protected]>
|
||||
*/
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cpu.h>
|
||||
#include <arch_system_info.h>
|
||||
#include <cpuidle.h>
|
||||
|
||||
|
||||
static status_t std_ops(int32 op, ...);
|
||||
|
||||
|
||||
static inline void
|
||||
x86_monitor(const void *addr, unsigned long ecx, unsigned long edx)
|
||||
{
|
||||
asm volatile("monitor"
|
||||
:: "a" (addr), "c" (ecx), "d"(edx));
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
x86_mwait(unsigned long eax, unsigned long ecx)
|
||||
{
|
||||
asm volatile("mwait"
|
||||
:: "a" (eax), "c" (ecx));
|
||||
}
|
||||
|
||||
|
||||
static void *kMwaitEax[] = {
|
||||
// C0, we never use it
|
||||
(void *)0x00,
|
||||
// MWAIT C1
|
||||
(void *)0x00,
|
||||
// MWAIT C2
|
||||
(void *)0x10,
|
||||
// MWAIT C3
|
||||
(void *)0x20,
|
||||
// MWAIT C4
|
||||
(void *)0x30,
|
||||
// MWAIT C5
|
||||
(void *)0x40,
|
||||
// MWAIT C6, 0x2 is used to fully shrink L2 cache
|
||||
(void *)0x52
|
||||
};
|
||||
|
||||
|
||||
static int32
|
||||
IntelCstateIdleEnter(int32 state, CpuidleCstate *cstate)
|
||||
{
|
||||
cpu_ent *cpu = get_cpu_struct();
|
||||
if (cpu->invoke_scheduler)
|
||||
return 0;
|
||||
|
||||
x86_monitor((void *)&cpu->invoke_scheduler, 0, 0);
|
||||
if (!cpu->invoke_scheduler)
|
||||
x86_mwait((unsigned long)cstate->pData, 1);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
static CpuidleCstate sSnbcStates[CPUIDLE_CSTATE_MAX] = {
|
||||
{},
|
||||
{
|
||||
"C1-SNB",
|
||||
1,
|
||||
IntelCstateIdleEnter,
|
||||
},
|
||||
{
|
||||
"C3-SNB",
|
||||
80,
|
||||
IntelCstateIdleEnter,
|
||||
},
|
||||
{
|
||||
"C6-SNB",
|
||||
104,
|
||||
IntelCstateIdleEnter,
|
||||
},
|
||||
{
|
||||
"C7-SNB",
|
||||
109,
|
||||
IntelCstateIdleEnter,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static CpuidleModuleInfo sIntelidleModule = {
|
||||
{
|
||||
"idle/cpuidles/intel_cpuidle/v1",
|
||||
0,
|
||||
std_ops
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
static status_t
|
||||
std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
{
|
||||
cpu_ent *cpu = get_cpu_struct();
|
||||
if (cpu->arch.vendor != VENDOR_INTEL || cpu->arch.family != 6)
|
||||
return B_ERROR;
|
||||
|
||||
// Calculated Model Value: M = (Extended Model << 4) + Model
|
||||
uint32 model = (cpu->arch.extended_model << 4) + cpu->arch.model;
|
||||
if (model != 0x2a && model != 0x2d)
|
||||
return B_ERROR;
|
||||
|
||||
cpuid_info cpuid;
|
||||
get_current_cpuid(&cpuid, 5);
|
||||
/* ecx[0] monitor/mwait extension supported
|
||||
* ecx[1] support for treating interrupts as break-events for mwait
|
||||
* edx number of sub-states
|
||||
*/
|
||||
if (!((cpuid.regs.ecx & 0x1) && (cpuid.regs.ecx & 0x2) &&
|
||||
cpuid.regs.edx)) {
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
sIntelidleModule.cStateCount = 1;
|
||||
for (int32 i = 1; i < CPUIDLE_CSTATE_MAX; i++) {
|
||||
int32 subStates = (cpuid.regs.edx >> ((i) * 4)) & 0xf;
|
||||
// no sub-states means the state is not available
|
||||
if (!subStates)
|
||||
continue;
|
||||
sIntelidleModule.cStates[sIntelidleModule.cStateCount] =
|
||||
sSnbcStates[i];
|
||||
sIntelidleModule.cStates[sIntelidleModule.cStateCount].pData =
|
||||
kMwaitEax[i];
|
||||
sIntelidleModule.cStateCount++;
|
||||
}
|
||||
dprintf("intel idle init\n");
|
||||
return B_OK;
|
||||
}
|
||||
case B_MODULE_UNINIT:
|
||||
dprintf("intel idle uninit\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sIntelidleModule,
|
||||
NULL
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel idle generic ;
|
||||
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
KernelAddon <module>cpuidle :
|
||||
cpuidle.cpp
|
||||
;
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Yongcong Du <[email protected]>
|
||||
*/
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <smp.h>
|
||||
#include <cpuidle.h>
|
||||
|
||||
|
||||
static CpuidleInfo sPerCPU[B_MAX_CPU_COUNT];
|
||||
static CpuidleModuleInfo *sCpuidleModule;
|
||||
|
||||
extern void (*gCpuIdleFunc)(void);
|
||||
|
||||
|
||||
/*
|
||||
* next cstate selection algorithm is based on NetBSD's
|
||||
* it's simple, stupid
|
||||
*/
|
||||
static int32
|
||||
SelectCstate(CpuidleInfo *info)
|
||||
{
|
||||
static const int32 csFactor = 3;
|
||||
|
||||
for (int32 i = sCpuidleModule->cStateCount - 1; i > 0; i--) {
|
||||
CpuidleCstate *cState = &sCpuidleModule->cStates[i];
|
||||
if (info->cstateSleep > cState->latency * csFactor)
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Choose C1 if there's no state found */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
EnterCstate(int32 state, CpuidleInfo *info)
|
||||
{
|
||||
CpuidleCstate *cstate = &sCpuidleModule->cStates[state];
|
||||
bigtime_t now = system_time();
|
||||
if (cstate->EnterIdle(state, cstate) > 0) {
|
||||
bigtime_t diff = system_time() - now;
|
||||
info->cstateSleep = diff;
|
||||
info->stats[state].usageCount++;
|
||||
info->stats[state].usageTime += diff;
|
||||
} else {
|
||||
info->cstateSleep = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
CpuCstateIdle(void)
|
||||
{
|
||||
CpuidleInfo *info = &sPerCPU[smp_get_current_cpu()];
|
||||
int32 state = SelectCstate(info);
|
||||
EnterCstate(state, info);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
std_ops(int32 op, ...)
|
||||
{
|
||||
switch (op) {
|
||||
case B_MODULE_INIT:
|
||||
{
|
||||
dprintf("generic cpuidle init\n");
|
||||
void *cookie = open_module_list("idle/cpuidles");
|
||||
if (cookie == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
char name[B_FILE_NAME_LENGTH];
|
||||
size_t nameLength = sizeof(name);
|
||||
|
||||
while (read_next_module_name(cookie, name, &nameLength) == B_OK) {
|
||||
if (get_module(name, (module_info **)&sCpuidleModule) == B_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close_module_list(cookie);
|
||||
if (sCpuidleModule->cStateCount < 3) {
|
||||
dprintf("no enough available cstates, exiting...\n");
|
||||
put_module(sCpuidleModule->info.name);
|
||||
return B_ERROR;
|
||||
} else {
|
||||
dprintf("using %s\n", sCpuidleModule->info.name);
|
||||
gCpuIdleFunc = CpuCstateIdle;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
case B_MODULE_UNINIT:
|
||||
dprintf("generic cpuidle uninit\n");
|
||||
put_module(sCpuidleModule->info.name);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
GetIdleStateName(int32 state)
|
||||
{
|
||||
return sCpuidleModule->cStates[state].name;
|
||||
}
|
||||
|
||||
|
||||
static int32
|
||||
GetIdleStateCount(void)
|
||||
{
|
||||
return sCpuidleModule->cStateCount;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
GetIdleStateInfo(int32 cpu, int32 state, CpuidleStat *stat)
|
||||
{
|
||||
memcpy(stat, &sPerCPU[cpu].stats[state], sizeof(CpuidleStat));
|
||||
}
|
||||
|
||||
|
||||
static GenCpuidle sModule = {
|
||||
{
|
||||
B_CPUIDLE_MODULE_NAME,
|
||||
0,
|
||||
std_ops
|
||||
},
|
||||
|
||||
GetIdleStateCount,
|
||||
GetIdleStateName,
|
||||
GetIdleStateInfo
|
||||
};
|
||||
|
||||
|
||||
module_info *modules[] = {
|
||||
(module_info *)&sModule,
|
||||
NULL
|
||||
};
|
||||
Reference in New Issue
Block a user