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:
Yongcong Du
2012-11-18 17:39:19 +01:00
committed by Fredrik Holmqvist
parent 57311e7bb7
commit 71d9d375b8
16 changed files with 463 additions and 334 deletions
+6 -9
View File
@@ -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);
};
-1
View File
@@ -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 ;
+1
View File
@@ -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
+1
View File
@@ -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
};
-4
View File
@@ -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 ;
-3
View File
@@ -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
};
-7
View File
@@ -1,7 +0,0 @@
SubDir HAIKU_TOP src add-ons kernel idle generic ;
UsePrivateKernelHeaders ;
KernelAddon <module>cpuidle :
cpuidle.cpp
;
-146
View File
@@ -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
};