diff --git a/headers/os/drivers/cpuidle.h b/headers/os/drivers/cpuidle.h new file mode 100644 index 0000000000..67d8f307a4 --- /dev/null +++ b/headers/os/drivers/cpuidle.h @@ -0,0 +1,51 @@ +/* + * Copyright 2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _CPUIDLE_MODULE_H +#define _CPUIDLE_MODULE_H + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CPUIDLE_CSTATE_MAX 8 +#define CSTATE_NAME_LENGTH 32 +#define B_CPUIDLE_MODULE_NAME "idle/generic/cpuidle/v1" + + +struct CpuidleStats { + uint64 usageCount; + bigtime_t usageTime; +}; + + +struct CpuidleInfo { + int32 cstateSleep; + CpuidleStats stats[CPUIDLE_CSTATE_MAX]; +}; + + +struct CpuidleCstate { + char name[CSTATE_NAME_LENGTH]; + int32 latency; + int32 (*EnterIdle)(int32 state, CpuidleCstate *cstate); + void *pData; +}; + + +struct CpuidleModuleInfo { + module_info info; + CpuidleCstate cStates[CPUIDLE_CSTATE_MAX]; + int32 cStateCount; +}; + + +#ifdef __cplusplus +} +#endif + +#endif // _CPUIDLE_MODULE_H diff --git a/src/add-ons/kernel/idle/Jamfile b/src/add-ons/kernel/idle/Jamfile new file mode 100644 index 0000000000..27f0864029 --- /dev/null +++ b/src/add-ons/kernel/idle/Jamfile @@ -0,0 +1,3 @@ +SubDir HAIKU_TOP src add-ons kernel idle ; + +SubInclude HAIKU_TOP src add-ons kernel idle generic ; diff --git a/src/add-ons/kernel/idle/generic/Jamfile b/src/add-ons/kernel/idle/generic/Jamfile new file mode 100644 index 0000000000..f7ff039052 --- /dev/null +++ b/src/add-ons/kernel/idle/generic/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel idle generic ; + +UsePrivateKernelHeaders ; + +KernelAddon cpuidle : + cpuidle.cpp + ; diff --git a/src/add-ons/kernel/idle/generic/cpuidle.cpp b/src/add-ons/kernel/idle/generic/cpuidle.cpp new file mode 100644 index 0000000000..4338a16835 --- /dev/null +++ b/src/add-ons/kernel/idle/generic/cpuidle.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Yongcong Du + */ + +#include +#include +#include + +#include +#include + + +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 < 2) { + 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 module_info sModule = { + B_CPUIDLE_MODULE_NAME, + 0, + std_ops +}; + + +module_info *modules[] = { + (module_info *)&sModule, + NULL +};