From 2195811a847b0a573b017750eefda19abcf0a6ec Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Wed, 4 Jul 2012 23:28:43 +0800 Subject: [PATCH 01/16] idle: introduce cpuidle generic module This module will load the various lowlevel cpuidle modules' implementations during initialiation. If it finds one available module, it will change the global gCpuIdleFunc as its own better one. When idle, cpuidle module will select the best cstate and enter it by calling the lowlevel module's implementation. Signed-off-by: Yongcong Du Signed-off-by: Fredrik Holmqvist --- headers/os/drivers/cpuidle.h | 51 +++++++++ src/add-ons/kernel/idle/Jamfile | 3 + src/add-ons/kernel/idle/generic/Jamfile | 7 ++ src/add-ons/kernel/idle/generic/cpuidle.cpp | 119 ++++++++++++++++++++ 4 files changed, 180 insertions(+) create mode 100644 headers/os/drivers/cpuidle.h create mode 100644 src/add-ons/kernel/idle/Jamfile create mode 100644 src/add-ons/kernel/idle/generic/Jamfile create mode 100644 src/add-ons/kernel/idle/generic/cpuidle.cpp 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 +}; From 251f971259714ef07cc4642f145bfb4ce552eb0e Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Thu, 5 Jul 2012 00:09:35 +0800 Subject: [PATCH 02/16] cpuidle: add intel native cpuidle module Currently, it only supports intel sandy bridge processors. Signed-off-by: Yongcong Du Signed-off-by: Fredrik Holmqvist --- src/add-ons/kernel/idle/Jamfile | 1 + src/add-ons/kernel/idle/cpuidles/Jamfile | 3 + .../idle/cpuidles/intel_cpuidle/Jamfile | 7 + .../cpuidles/intel_cpuidle/intel_cpuidle.cpp | 148 ++++++++++++++++++ 4 files changed, 159 insertions(+) create mode 100644 src/add-ons/kernel/idle/cpuidles/Jamfile create mode 100644 src/add-ons/kernel/idle/cpuidles/intel_cpuidle/Jamfile create mode 100644 src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp diff --git a/src/add-ons/kernel/idle/Jamfile b/src/add-ons/kernel/idle/Jamfile index 27f0864029..878fd5cc5e 100644 --- a/src/add-ons/kernel/idle/Jamfile +++ b/src/add-ons/kernel/idle/Jamfile @@ -1,3 +1,4 @@ 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 ; diff --git a/src/add-ons/kernel/idle/cpuidles/Jamfile b/src/add-ons/kernel/idle/cpuidles/Jamfile new file mode 100644 index 0000000000..03a6a3c860 --- /dev/null +++ b/src/add-ons/kernel/idle/cpuidles/Jamfile @@ -0,0 +1,3 @@ +SubDir HAIKU_TOP src add-ons kernel idle cpuidles ; + +SubInclude HAIKU_TOP src add-ons kernel idle cpuidles intel_cpuidle ; diff --git a/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/Jamfile b/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/Jamfile new file mode 100644 index 0000000000..4b74f09e92 --- /dev/null +++ b/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel idle cpuidles intel_cpuidle ; + +UsePrivateKernelHeaders ; + +KernelAddon intel_cpuidle : + intel_cpuidle.cpp + ; diff --git a/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp b/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp new file mode 100644 index 0000000000..a2bba81463 --- /dev/null +++ b/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp @@ -0,0 +1,148 @@ +/* + * Copyright 2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Yongcong Du + */ + +#include +#include + +#include +#include +#include + + +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 kSnbcStates[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: + { + 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] = + kSnbcStates[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 +}; From 32c4d58bd3f0cc284c95103e8c970dda643e57f4 Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Thu, 5 Jul 2012 00:11:53 +0800 Subject: [PATCH 03/16] driver: add cpuidle device driver Currently, it's only used to load generic cpuidle module. But in the future, we will add some features such as stats reporting etc. Signed-off-by: Yongcong Du Signed-off-by: Fredrik Holmqvist --- src/add-ons/kernel/drivers/cpuidle/Jamfile | 5 + .../kernel/drivers/cpuidle/cpuidle.cpp | 117 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/add-ons/kernel/drivers/cpuidle/Jamfile create mode 100644 src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp diff --git a/src/add-ons/kernel/drivers/cpuidle/Jamfile b/src/add-ons/kernel/drivers/cpuidle/Jamfile new file mode 100644 index 0000000000..50b92909b1 --- /dev/null +++ b/src/add-ons/kernel/drivers/cpuidle/Jamfile @@ -0,0 +1,5 @@ +SubDir HAIKU_TOP src add-ons kernel drivers cpuidle ; + +KernelAddon cpuidle : + cpuidle.cpp + ; diff --git a/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp b/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp new file mode 100644 index 0000000000..9440358dfe --- /dev/null +++ b/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Yongcong Du + */ + +#include +#include + +#include + +#define DEVICE_NAME "cpuidle" + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +CpuidleModuleInfo *sCpuidleModule; + +static status_t +cpuidle_open(const char *name, uint32 flags, void **cookie) +{ + *cookie = NULL; + return B_OK; +} + + +static status_t +cpuidle_close(void *cookie) +{ + return B_OK; +} + + +static status_t +cpuidle_free(void *cookie) +{ + return B_OK; +} + + +static status_t +cpuidle_ioctl(void *cookie, uint32 op, void *buffer, size_t length) +{ + return B_OK; +} + + +static status_t +cpuidle_read(void *cookie, off_t pos, void *buffer, size_t *length) +{ + *length = 0; + return B_OK; +} + + +static status_t +cpuidle_write(void *cookie, off_t pos, const void *buffer, size_t *_length) +{ + return B_OK; +} + + +status_t +init_hardware(void) +{ + return B_OK; +} + + +const char ** +publish_devices(void) +{ + static const char *devices[] = { + DEVICE_NAME, + NULL + }; + + return devices; +} + + +device_hooks * +find_device(const char *name) +{ + static device_hooks hooks = { + &cpuidle_open, + &cpuidle_close, + &cpuidle_free, + &cpuidle_ioctl, + &cpuidle_read, + &cpuidle_write, + }; + + + return &hooks; +} + + +status_t +init_driver(void) +{ + status_t err; + + err = get_module(B_CPUIDLE_MODULE_NAME, (module_info **)&sCpuidleModule); + if (err != B_OK) { + dprintf("can't load "B_CPUIDLE_MODULE_NAME"\n"); + } + return B_OK; +} + + +void +uninit_driver(void) +{ + put_module(B_CPUIDLE_MODULE_NAME); +} From 5ae9700a12099acc5a2f68660ea795e3d2d31f83 Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Thu, 5 Jul 2012 00:13:55 +0800 Subject: [PATCH 04/16] enable cpuidle module and device driver Signed-off-by: Fredrik Holmqvist --- src/add-ons/kernel/Jamfile | 1 + src/add-ons/kernel/drivers/Jamfile | 1 + 2 files changed, 2 insertions(+) diff --git a/src/add-ons/kernel/Jamfile b/src/add-ons/kernel/Jamfile index 5a5b275cb3..03f57867b9 100644 --- a/src/add-ons/kernel/Jamfile +++ b/src/add-ons/kernel/Jamfile @@ -8,6 +8,7 @@ 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 ; diff --git a/src/add-ons/kernel/drivers/Jamfile b/src/add-ons/kernel/drivers/Jamfile index ef7c7eee24..f23cf48e96 100644 --- a/src/add-ons/kernel/drivers/Jamfile +++ b/src/add-ons/kernel/drivers/Jamfile @@ -4,6 +4,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers bluetooth ; SubInclude HAIKU_TOP src add-ons kernel drivers bus ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ; SubInclude HAIKU_TOP src add-ons kernel drivers common ; +SubInclude HAIKU_TOP src add-ons kernel drivers cpuidle ; SubInclude HAIKU_TOP src add-ons kernel drivers disk ; SubInclude HAIKU_TOP src add-ons kernel drivers dvb ; SubInclude HAIKU_TOP src add-ons kernel drivers input ; From 66b0c86ae4a3f29e998114ec4a1a4824922e0d9a Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Sat, 7 Jul 2012 19:41:27 +0800 Subject: [PATCH 05/16] cpuidle: rename CpuidleStats to CpuidleStat Signed-off-by: Fredrik Holmqvist --- headers/os/drivers/cpuidle.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/headers/os/drivers/cpuidle.h b/headers/os/drivers/cpuidle.h index 67d8f307a4..7234429283 100644 --- a/headers/os/drivers/cpuidle.h +++ b/headers/os/drivers/cpuidle.h @@ -17,15 +17,15 @@ extern "C" { #define B_CPUIDLE_MODULE_NAME "idle/generic/cpuidle/v1" -struct CpuidleStats { +struct CpuidleStat { uint64 usageCount; bigtime_t usageTime; }; struct CpuidleInfo { - int32 cstateSleep; - CpuidleStats stats[CPUIDLE_CSTATE_MAX]; + int32 cstateSleep; + CpuidleStat stats[CPUIDLE_CSTATE_MAX]; }; From 76877c9a514e51a6f37ca40c8b09f2fcc32321ff Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Sat, 7 Jul 2012 19:42:14 +0800 Subject: [PATCH 06/16] intel_cpuidle: apply strict cpu checking to see it's snb or not we need to check cpu vendor, family, and model Signed-off-by: Fredrik Holmqvist --- .../idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp b/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp index a2bba81463..6265c7326a 100644 --- a/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp +++ b/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp @@ -66,7 +66,7 @@ IntelCstateIdleEnter(int32 state, CpuidleCstate *cstate) } -static CpuidleCstate kSnbcStates[CPUIDLE_CSTATE_MAX] = { +static CpuidleCstate sSnbcStates[CPUIDLE_CSTATE_MAX] = { {}, { "C1-SNB", @@ -107,6 +107,15 @@ 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 @@ -125,7 +134,7 @@ std_ops(int32 op, ...) if (!subStates) continue; sIntelidleModule.cStates[sIntelidleModule.cStateCount] = - kSnbcStates[i]; + sSnbcStates[i]; sIntelidleModule.cStates[sIntelidleModule.cStateCount].pData = kMwaitEax[i]; sIntelidleModule.cStateCount++; From e467ba95b017924b06f2f52a73ab5380d553543a Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Sat, 7 Jul 2012 21:52:56 +0800 Subject: [PATCH 07/16] cpuidle: add stats reporting support Signed-off-by: Fredrik Holmqvist --- headers/os/drivers/cpuidle.h | 9 +++++ src/add-ons/kernel/drivers/cpuidle/Jamfile | 2 + .../kernel/drivers/cpuidle/cpuidle.cpp | 39 +++++++++++++++++-- src/add-ons/kernel/idle/generic/cpuidle.cpp | 35 +++++++++++++++-- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/headers/os/drivers/cpuidle.h b/headers/os/drivers/cpuidle.h index 7234429283..ed79be9622 100644 --- a/headers/os/drivers/cpuidle.h +++ b/headers/os/drivers/cpuidle.h @@ -44,6 +44,15 @@ struct CpuidleModuleInfo { }; +struct GenCpuidle { + module_info info; + int32 (*GetIdleStateCount)(void); + char * (*GetIdleStateName)(int32 state); + void (*GetIdleStateInfo)(int32 cpu, int32 state, + CpuidleStat *stat); +}; + + #ifdef __cplusplus } #endif diff --git a/src/add-ons/kernel/drivers/cpuidle/Jamfile b/src/add-ons/kernel/drivers/cpuidle/Jamfile index 50b92909b1..ce5f450017 100644 --- a/src/add-ons/kernel/drivers/cpuidle/Jamfile +++ b/src/add-ons/kernel/drivers/cpuidle/Jamfile @@ -1,5 +1,7 @@ SubDir HAIKU_TOP src add-ons kernel drivers cpuidle ; +UsePrivateKernelHeaders ; + KernelAddon cpuidle : cpuidle.cpp ; diff --git a/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp b/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp index 9440358dfe..82110c9253 100644 --- a/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp +++ b/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp @@ -6,6 +6,10 @@ * Yongcong Du */ +#include +#include +#include + #include #include @@ -15,7 +19,7 @@ int32 api_version = B_CUR_DRIVER_API_VERSION; -CpuidleModuleInfo *sCpuidleModule; +static GenCpuidle *sGenCpuidle; static status_t cpuidle_open(const char *name, uint32 flags, void **cookie) @@ -49,7 +53,36 @@ cpuidle_ioctl(void *cookie, uint32 op, void *buffer, size_t length) static status_t cpuidle_read(void *cookie, off_t pos, void *buffer, size_t *length) { - *length = 0; + if (pos != 0) { + *length = 0; + return B_OK; + } + char *str = (char *)buffer; + size_t max_len = *length; + int32 stateCount = sGenCpuidle->GetIdleStateCount(); + int32 bytes = snprintf(str, max_len, "C-STATE COUNT: %"B_PRId32"\n", stateCount); + max_len-= bytes; + str += bytes; + int32 cpu = smp_get_num_cpus(); + + for (int32 i = 0; i < cpu; i++) { + bytes = snprintf(str, max_len, "CPU%"B_PRId32"\n", i); + max_len-= bytes; + str += bytes; + for (int32 j = 1; j < stateCount; j++) { + CpuidleStat stat; + bytes = snprintf(str, max_len, "%s\n", sGenCpuidle->GetIdleStateName(j)); + max_len-= bytes; + str += bytes; + sGenCpuidle->GetIdleStateInfo(i, j, &stat); + bytes = snprintf(str, max_len, "%lld %lldus\n", + stat.usageCount, stat.usageTime); + max_len-= bytes; + str += bytes; + } + } + *length = strlen((char *)buffer); + return B_OK; } @@ -102,7 +135,7 @@ init_driver(void) { status_t err; - err = get_module(B_CPUIDLE_MODULE_NAME, (module_info **)&sCpuidleModule); + err = get_module(B_CPUIDLE_MODULE_NAME, (module_info **)&sGenCpuidle); if (err != B_OK) { dprintf("can't load "B_CPUIDLE_MODULE_NAME"\n"); } diff --git a/src/add-ons/kernel/idle/generic/cpuidle.cpp b/src/add-ons/kernel/idle/generic/cpuidle.cpp index 4338a16835..041b7298ca 100644 --- a/src/add-ons/kernel/idle/generic/cpuidle.cpp +++ b/src/add-ons/kernel/idle/generic/cpuidle.cpp @@ -106,10 +106,37 @@ std_ops(int32 op, ...) } -static module_info sModule = { - B_CPUIDLE_MODULE_NAME, - 0, - std_ops +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 }; From 69f69b003afc34ed153ac3463e7844f77c29dce2 Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Thu, 5 Jul 2012 10:42:55 +0800 Subject: [PATCH 08/16] cpuidle: we should exit when cStateCount is less than 3 If only C0, C1 are available, it doesn't make any sense to enable cpuidle Signed-off-by: Fredrik Holmqvist --- src/add-ons/kernel/idle/generic/cpuidle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/add-ons/kernel/idle/generic/cpuidle.cpp b/src/add-ons/kernel/idle/generic/cpuidle.cpp index 041b7298ca..0ce9851132 100644 --- a/src/add-ons/kernel/idle/generic/cpuidle.cpp +++ b/src/add-ons/kernel/idle/generic/cpuidle.cpp @@ -86,7 +86,7 @@ std_ops(int32 op, ...) } close_module_list(cookie); - if (sCpuidleModule->cStateCount < 2) { + if (sCpuidleModule->cStateCount < 3) { dprintf("no enough available cstates, exiting...\n"); put_module(sCpuidleModule->info.name); return B_ERROR; From 57311e7bb7302866bdcbb28aea336152c7ae873d Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Sat, 4 Aug 2012 22:12:45 +0800 Subject: [PATCH 09/16] acpi: add ACPI_ALLOCATE_LOCAL_BUFFER to ACPI.h Signed-off-by: Fredrik Holmqvist --- headers/os/drivers/ACPI.h | 1 + 1 file changed, 1 insertion(+) diff --git a/headers/os/drivers/ACPI.h b/headers/os/drivers/ACPI.h index 221c649f49..aeea7072c2 100644 --- a/headers/os/drivers/ACPI.h +++ b/headers/os/drivers/ACPI.h @@ -133,6 +133,7 @@ typedef struct acpi_data { enum { ACPI_ALLOCATE_BUFFER = -1, + ACPI_ALLOCATE_LOCAL_BUFFER = -2, }; From 71d9d375b82be4c5c336831042bc7401b9ed0e07 Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Sat, 4 Aug 2012 22:12:40 +0800 Subject: [PATCH 10/16] 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 --- headers/os/drivers/cpuidle.h | 15 +- src/add-ons/kernel/Jamfile | 1 - src/add-ons/kernel/drivers/power/Jamfile | 1 + .../kernel/drivers/power/x86_cpuidle/Jamfile | 10 + .../power/x86_cpuidle/acpi_cpuidle.cpp | 181 ++++++++++++++++++ .../power/x86_cpuidle/intel_cpuidle.cpp | 125 ++++++++++++ .../drivers/power/x86_cpuidle/x86_cpuidle.h | 27 +++ src/add-ons/kernel/generic/Jamfile | 1 + src/add-ons/kernel/generic/cpuidle/Jamfile | 7 + .../kernel/generic/cpuidle/cpuidle.cpp | 105 ++++++++++ src/add-ons/kernel/idle/Jamfile | 4 - src/add-ons/kernel/idle/cpuidles/Jamfile | 3 - .../idle/cpuidles/intel_cpuidle/Jamfile | 7 - .../cpuidles/intel_cpuidle/intel_cpuidle.cpp | 157 --------------- src/add-ons/kernel/idle/generic/Jamfile | 7 - src/add-ons/kernel/idle/generic/cpuidle.cpp | 146 -------------- 16 files changed, 463 insertions(+), 334 deletions(-) create mode 100644 src/add-ons/kernel/drivers/power/x86_cpuidle/Jamfile create mode 100644 src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp create mode 100644 src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp create mode 100644 src/add-ons/kernel/drivers/power/x86_cpuidle/x86_cpuidle.h create mode 100644 src/add-ons/kernel/generic/cpuidle/Jamfile create mode 100644 src/add-ons/kernel/generic/cpuidle/cpuidle.cpp delete mode 100644 src/add-ons/kernel/idle/Jamfile delete mode 100644 src/add-ons/kernel/idle/cpuidles/Jamfile delete mode 100644 src/add-ons/kernel/idle/cpuidles/intel_cpuidle/Jamfile delete mode 100644 src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp delete mode 100644 src/add-ons/kernel/idle/generic/Jamfile delete mode 100644 src/add-ons/kernel/idle/generic/cpuidle.cpp diff --git a/headers/os/drivers/cpuidle.h b/headers/os/drivers/cpuidle.h index ed79be9622..7649cdadab 100644 --- a/headers/os/drivers/cpuidle.h +++ b/headers/os/drivers/cpuidle.h @@ -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); }; diff --git a/src/add-ons/kernel/Jamfile b/src/add-ons/kernel/Jamfile index 03f57867b9..5a5b275cb3 100644 --- a/src/add-ons/kernel/Jamfile +++ b/src/add-ons/kernel/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/drivers/power/Jamfile b/src/add-ons/kernel/drivers/power/Jamfile index 56d91431ba..45feced8e9 100644 --- a/src/add-ons/kernel/drivers/power/Jamfile +++ b/src/add-ons/kernel/drivers/power/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/Jamfile b/src/add-ons/kernel/drivers/power/x86_cpuidle/Jamfile new file mode 100644 index 0000000000..51612506a6 --- /dev/null +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp b/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp new file mode 100644 index 0000000000..8064d506e0 --- /dev/null +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2012, Haiku, Inc. All Rights Reserved. + * + * Distributed under the terms of the MIT License. + * + * Authors: + * Yongcong Du + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 +}; diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp b/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp new file mode 100644 index 0000000000..1032a5d5e2 --- /dev/null +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Yongcong Du + */ + +#include +#include +#include + +#include + +#include + +#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; +} diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/x86_cpuidle.h b/src/add-ons/kernel/drivers/power/x86_cpuidle/x86_cpuidle.h new file mode 100644 index 0000000000..b524ef19cc --- /dev/null +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/x86_cpuidle.h @@ -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 diff --git a/src/add-ons/kernel/generic/Jamfile b/src/add-ons/kernel/generic/Jamfile index a2a6bfc1fa..719061b0dc 100644 --- a/src/add-ons/kernel/generic/Jamfile +++ b/src/add-ons/kernel/generic/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/generic/cpuidle/Jamfile b/src/add-ons/kernel/generic/cpuidle/Jamfile new file mode 100644 index 0000000000..bb7cdd2746 --- /dev/null +++ b/src/add-ons/kernel/generic/cpuidle/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel generic cpuidle ; + +UsePrivateKernelHeaders ; + +KernelAddon cpuidle : + cpuidle.cpp + ; diff --git a/src/add-ons/kernel/generic/cpuidle/cpuidle.cpp b/src/add-ons/kernel/generic/cpuidle/cpuidle.cpp new file mode 100644 index 0000000000..cc17be8fac --- /dev/null +++ b/src/add-ons/kernel/generic/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 + */ + +#include +#include +#include + +#include +#include + + +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 +}; diff --git a/src/add-ons/kernel/idle/Jamfile b/src/add-ons/kernel/idle/Jamfile deleted file mode 100644 index 878fd5cc5e..0000000000 --- a/src/add-ons/kernel/idle/Jamfile +++ /dev/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 ; diff --git a/src/add-ons/kernel/idle/cpuidles/Jamfile b/src/add-ons/kernel/idle/cpuidles/Jamfile deleted file mode 100644 index 03a6a3c860..0000000000 --- a/src/add-ons/kernel/idle/cpuidles/Jamfile +++ /dev/null @@ -1,3 +0,0 @@ -SubDir HAIKU_TOP src add-ons kernel idle cpuidles ; - -SubInclude HAIKU_TOP src add-ons kernel idle cpuidles intel_cpuidle ; diff --git a/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/Jamfile b/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/Jamfile deleted file mode 100644 index 4b74f09e92..0000000000 --- a/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/Jamfile +++ /dev/null @@ -1,7 +0,0 @@ -SubDir HAIKU_TOP src add-ons kernel idle cpuidles intel_cpuidle ; - -UsePrivateKernelHeaders ; - -KernelAddon intel_cpuidle : - intel_cpuidle.cpp - ; diff --git a/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp b/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp deleted file mode 100644 index 6265c7326a..0000000000 --- a/src/add-ons/kernel/idle/cpuidles/intel_cpuidle/intel_cpuidle.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2012, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Yongcong Du - */ - -#include -#include - -#include -#include -#include - - -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 -}; diff --git a/src/add-ons/kernel/idle/generic/Jamfile b/src/add-ons/kernel/idle/generic/Jamfile deleted file mode 100644 index f7ff039052..0000000000 --- a/src/add-ons/kernel/idle/generic/Jamfile +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 0ce9851132..0000000000 --- a/src/add-ons/kernel/idle/generic/cpuidle.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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 < 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 -}; From 0a0af8957e4ad48168cb26dba4a058f7eb0e4892 Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Fri, 17 Aug 2012 19:23:53 +0800 Subject: [PATCH 11/16] acpi: export read_bit_register and write_bit_register acpi cpuidle needs such acpi functions Signed-off-by: Fredrik Holmqvist --- headers/os/drivers/ACPI.h | 4 ++++ .../kernel/bus_managers/acpi/BusManager.cpp | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/headers/os/drivers/ACPI.h b/headers/os/drivers/ACPI.h index aeea7072c2..66ce0d0128 100644 --- a/headers/os/drivers/ACPI.h +++ b/headers/os/drivers/ACPI.h @@ -262,6 +262,10 @@ struct acpi_module_info { /* Table Access */ status_t (*get_table)(const char *signature, uint32 instance, void **tableHeader); + + /* Register Access */ + status_t (*read_bit_register)(uint32 regid, uint32 *val); + status_t (*write_bit_register)(uint32 regid, uint32 val); }; diff --git a/src/add-ons/kernel/bus_managers/acpi/BusManager.cpp b/src/add-ons/kernel/bus_managers/acpi/BusManager.cpp index 692898bb44..ea63dba1be 100644 --- a/src/add-ons/kernel/bus_managers/acpi/BusManager.cpp +++ b/src/add-ons/kernel/bus_managers/acpi/BusManager.cpp @@ -715,6 +715,20 @@ get_table(const char* signature, uint32 instance, void** tableHeader) } +status_t +read_bit_register(uint32 regid, uint32 *val) +{ + return AcpiReadBitRegister(regid, (UINT32 *)val); +} + + +status_t +write_bit_register(uint32 regid, uint32 val) +{ + return AcpiWriteBitRegister(regid, val); +} + + struct acpi_module_info gACPIModule = { { B_ACPI_MODULE_NAME, @@ -759,5 +773,7 @@ struct acpi_module_info gACPIModule = { prepare_sleep_state, enter_sleep_state, reboot, - get_table + get_table, + read_bit_register, + write_bit_register }; From 0138a95ebac947f56c39ff2a148d3380ad5db1cc Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Sun, 19 Aug 2012 19:30:23 +0800 Subject: [PATCH 12/16] cpuidle: implement acpi cpuidle driver we prefer intel native cpuidle driver on newer intel platforms -- sandybridge or later. we'll fall back to acpi cpuidle driver for other platforms. Signed-off-by: Fredrik Holmqvist --- .../power/x86_cpuidle/acpi_cpuidle.cpp | 411 +++++++++++++++++- 1 file changed, 410 insertions(+), 1 deletion(-) diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp b/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp index 8064d506e0..fb4095cb87 100644 --- a/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp @@ -19,27 +19,433 @@ #include #include +#include #include "x86_cpuidle.h" +#define ACPI_PDC_REVID 0x1 +#define ACPI_OSC_QUERY (1 << 0) + +#define ACPI_PDC_P_FFH (1 << 0) +#define ACPI_PDC_C_C1_HALT (1 << 1) +#define ACPI_PDC_T_FFH (1 << 2) +#define ACPI_PDC_SMP_C1PT (1 << 3) +#define ACPI_PDC_SMP_C2C3 (1 << 4) +#define ACPI_PDC_SMP_P_SW (1 << 5) +#define ACPI_PDC_SMP_C_SW (1 << 6) +#define ACPI_PDC_SMP_T_SW (1 << 7) +#define ACPI_PDC_C_C1_FFH (1 << 8) +#define ACPI_PDC_C_C2C3_FFH (1 << 9) +#define ACPI_PDC_P_HWCOORD (1 << 11) + +// Bus Master check required +#define ACPI_PDC_GAS_BM (1 << 1) + +#define ACPI_CSTATE_HALT 0x1 +#define ACPI_CSTATE_SYSIO 0x2 +#define ACPI_CSTATE_FFH 0x3 + +// Bus Master Check +#define ACPI_FLAG_C_BM (1 << 0) +// Bus master arbitration +#define ACPI_FLAG_C_ARB (1 << 1) + +// Copied from acpica's actypes.h, where's the best place to put? +#define ACPI_BITREG_BUS_MASTER_STATUS 0x01 +#define ACPI_BITREG_BUS_MASTER_RLD 0x0F +#define ACPI_BITREG_ARB_DISABLE 0x13 + +#define ACPI_STATE_C0 (uint8) 0 +#define ACPI_STATE_C1 (uint8) 1 +#define ACPI_STATE_C2 (uint8) 2 +#define ACPI_STATE_C3 (uint8) 3 +#define ACPI_C_STATES_MAX ACPI_STATE_C3 +#define ACPI_C_STATE_COUNT 4 + + #define ACPI_CPUIDLE_MODULE_NAME "drivers/power/x86_cpuidle/acpi/driver_v1" +struct acpicpu_reg { + uint8 reg_desc; + uint16 reg_reslen; + uint8 reg_spaceid; + uint8 reg_bitwidth; + uint8 reg_bitoffset; + uint8 reg_accesssize; + uint64 reg_addr; +} __attribute__((packed)); + struct acpi_cpuidle_driver_info { device_node *node; acpi_device_module_info *acpi; acpi_device acpi_cookie; + uint32 flags; +}; + +struct acpi_cstate_info { + uint32 address; + uint8 skip_bm_sts; + uint8 method; + uint8 type; }; static device_manager_info *sDeviceManager; static acpi_cpuidle_driver_info *acpi_processor[B_MAX_CPU_COUNT]; +static CpuidleModuleInfo *sIdle; +static CpuidleDevice sAcpiDevice; +static acpi_module_info *sAcpi; + + +static status_t +acpi_eval_pdc(acpi_cpuidle_driver_info *device) +{ + acpi_objects arg; + acpi_object_type obj; + uint32 cap[3]; + + arg.count = 1; + arg.pointer = &obj; + cap[0] = 1; + cap[1] = 1; + cap[2] = ACPI_PDC_C_C1_HALT | ACPI_PDC_SMP_C1PT | ACPI_PDC_SMP_C2C3; + cap[2] |= ACPI_PDC_SMP_P_SW | ACPI_PDC_SMP_C_SW | ACPI_PDC_SMP_T_SW; + cap[2] |= ACPI_PDC_C_C1_FFH | ACPI_PDC_C_C2C3_FFH; + cap[2] |= ACPI_PDC_SMP_T_SW | ACPI_PDC_P_FFH | ACPI_PDC_P_HWCOORD | ACPI_PDC_T_FFH; + obj.object_type = ACPI_TYPE_BUFFER; + obj.data.buffer.length = sizeof(cap); + obj.data.buffer.buffer = cap; + status_t status = device->acpi->evaluate_method(device->acpi_cookie, "_PDC", &arg, NULL); + return status; +} + + +static status_t +acpi_eval_osc(acpi_cpuidle_driver_info *device) +{ + // guid for intel platform + dprintf("%s@%p\n", __func__, device->acpi_cookie); + static uint8 uuid[] = { + 0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29, 0xBE, 0x47, + 0x9E, 0xBD, 0xD8, 0x70, 0x58, 0x71, 0x39, 0x53 + }; + uint32 cap[2]; + cap[0] = 0; + cap[1] = ACPI_PDC_C_C1_HALT | ACPI_PDC_SMP_C1PT | ACPI_PDC_SMP_C2C3; + cap[1] |= ACPI_PDC_SMP_P_SW | ACPI_PDC_SMP_C_SW | ACPI_PDC_SMP_T_SW; + cap[1] |= ACPI_PDC_C_C1_FFH | ACPI_PDC_C_C2C3_FFH; + cap[1] |= ACPI_PDC_SMP_T_SW | ACPI_PDC_P_FFH | ACPI_PDC_P_HWCOORD | ACPI_PDC_T_FFH; + + acpi_objects arg; + acpi_object_type obj[4]; + + arg.count = 4; + arg.pointer = obj; + + obj[0].object_type = ACPI_TYPE_BUFFER; + obj[0].data.buffer.length = sizeof(uuid); + obj[0].data.buffer.buffer = uuid; + obj[1].object_type = ACPI_TYPE_INTEGER; + obj[1].data.integer = ACPI_PDC_REVID; + obj[2].object_type = ACPI_TYPE_INTEGER; + obj[2].data.integer = sizeof(cap)/sizeof(cap[0]); + obj[3].object_type = ACPI_TYPE_BUFFER; + obj[3].data.buffer.length = sizeof(cap); + obj[3].data.buffer.buffer = (void *)cap; + + acpi_data buf; + buf.pointer = NULL; + buf.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status_t status = device->acpi->evaluate_method(device->acpi_cookie, "_OSC", &arg, &buf); + if (status != B_OK) + return status; + acpi_object_type *osc = (acpi_object_type *)buf.pointer; + if (osc->object_type != ACPI_TYPE_BUFFER) + return B_BAD_TYPE; + if (osc->data.buffer.length != sizeof(cap)) + return B_BUFFER_OVERFLOW; + return status; +} + + +static inline bool +acpi_cstate_bm_check(void) +{ + uint32 val; + sAcpi->read_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, &val); + if (!val) + return false; + sAcpi->write_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); + + return true; +} + + +static inline void +acpi_cstate_ffh_enter(CpuidleCstate *cState) +{ + cpu_ent *cpu = get_cpu_struct(); + if (cpu->invoke_scheduler) + return; + + x86_monitor((void *)&cpu->invoke_scheduler, 0, 0); + if (!cpu->invoke_scheduler) + x86_mwait((unsigned long)cState->pData, 1); +} + + +static inline void +acpi_cstate_halt(void) +{ + cpu_ent *cpu = get_cpu_struct(); + if (cpu->invoke_scheduler) + return; + asm("hlt"); +} + + +static void +acpi_cstate_enter(CpuidleCstate *cState) +{ + acpi_cstate_info *ci = (acpi_cstate_info *)cState->pData; + if (ci->method == ACPI_CSTATE_FFH) + acpi_cstate_ffh_enter(cState); + else if (ci->method == ACPI_CSTATE_SYSIO) + in8(ci->address); + else + acpi_cstate_halt(); +} + + +static int32 +acpi_cstate_idle(int32 state, CpuidleDevice *device) +{ + CpuidleCstate *cState = &device->cStates[state]; + acpi_cstate_info *ci = (acpi_cstate_info *)cState->pData; + if (!ci->skip_bm_sts) { + // we fall back to C1 if there's bus master activity + if (acpi_cstate_bm_check()) + state = 1; + } + if (ci->type != ACPI_STATE_C3) + acpi_cstate_enter(cState); + + // set BM_RLD for Bus Master to activity to wake the system from C3 + // With Newer chipsets BM_RLD is a NOP Since DMA is automatically handled + // during C3 State + acpi_cpuidle_driver_info *pi = acpi_processor[smp_get_current_cpu()]; + if (pi->flags & ACPI_FLAG_C_BM) + sAcpi->write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 1); + + // disable bus master arbitration during C3 + if (pi->flags & ACPI_FLAG_C_ARB) + sAcpi->write_bit_register(ACPI_BITREG_ARB_DISABLE, 1); + + acpi_cstate_enter(cState); + + // clear BM_RLD and re-enable the arbiter + if (pi->flags & ACPI_FLAG_C_BM) + sAcpi->write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 0); + + if (pi->flags & ACPI_FLAG_C_ARB) + sAcpi->write_bit_register(ACPI_BITREG_ARB_DISABLE, 0); + + return state; +} + + +static status_t +acpi_cstate_add(acpi_object_type *object, CpuidleCstate *cState) +{ + acpi_cstate_info *ci = (acpi_cstate_info *)malloc(sizeof(acpi_cstate_info)); + if (!ci) + return B_NO_MEMORY; + + if (object->object_type != ACPI_TYPE_PACKAGE) { + dprintf("invalid _CST object\n"); + return B_ERROR; + } + + if (object->data.package.count != 4) { + dprintf("invalid _CST number\n"); + return B_ERROR; + } + + // type + acpi_object_type * pointer = &object->data.package.objects[1]; + if (pointer->object_type != ACPI_TYPE_INTEGER) { + dprintf("invalid _CST elem type\n"); + return B_ERROR; + } + uint32 n = pointer->data.integer; + if (n < 1 || n > 3) { + dprintf("invalid _CST elem value\n"); + return B_ERROR; + } + ci->type = n; + dprintf("C%" B_PRId32 "\n", n); + snprintf(cState->name, sizeof(cState->name), "C%"B_PRId32, n); + + // Latency + pointer = &object->data.package.objects[2]; + if (pointer->object_type != ACPI_TYPE_INTEGER) { + dprintf("invalid _CST elem type\n"); + return B_ERROR; + } + n = pointer->data.integer; + cState->latency = n; + dprintf("Latency: %" B_PRId32 "\n", n); + + // power + pointer = &object->data.package.objects[3]; + if (pointer->object_type != ACPI_TYPE_INTEGER) { + dprintf("invalid _CST elem type\n"); + return B_ERROR; + } + n = pointer->data.integer; + dprintf("power: %" B_PRId32 "\n", n); + + // register + pointer = &object->data.package.objects[0]; + if (pointer->object_type != ACPI_TYPE_BUFFER) { + dprintf("invalid _CST elem type\n"); + return B_ERROR; + } + if (pointer->data.buffer.length < 15) { + dprintf("invalid _CST elem length\n"); + return B_ERROR; + } + + struct acpicpu_reg *reg = (struct acpicpu_reg *)pointer->data.buffer.buffer; + switch (reg->reg_spaceid) { + case ACPI_ADR_SPACE_SYSTEM_IO: + dprintf("IO method\n"); + if (reg->reg_addr == 0) { + dprintf("illegal address\n"); + return B_ERROR; + } + if (reg->reg_bitwidth != 8) { + dprintf("invalid source length\n"); + return B_ERROR; + } + ci->address = reg->reg_addr; + ci->method = ACPI_CSTATE_SYSIO; + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + { + dprintf("FFH method\n"); + ci->method = ACPI_CSTATE_FFH; + ci->address = reg->reg_addr; + + // skip checking BM_STS if ACPI_PDC_GAS_BM is cleared + cpu_ent *cpu = get_cpu_struct(); + if ((cpu->arch.vendor == VENDOR_INTEL) && + !(reg->reg_accesssize & ACPI_PDC_GAS_BM)) + ci->skip_bm_sts = 1; + break; + } + default: + dprintf("invalid spaceid %" B_PRId8 "\n", reg->reg_spaceid); + break; + } + cState->pData = ci; + cState->EnterIdle = acpi_cstate_idle; + + return B_OK; +} + + +static void +acpi_cstate_quirks(acpi_cpuidle_driver_info *device) +{ + cpu_ent *cpu = get_cpu_struct(); + // Calculated Model Value: M = (Extended Model << 4) + Model + uint32 model = (cpu->arch.extended_model << 4) + cpu->arch.model; + + // On all recent Intel platforms, ARB_DIS is not necessary + if (cpu->arch.vendor != VENDOR_INTEL) + return; + if (cpu->arch.family > 0xf || (cpu->arch.family == 6 && model >= 0xf)) + device->flags &= ~ACPI_FLAG_C_ARB; +} + + +static status_t +acpi_cpuidle_setup(acpi_cpuidle_driver_info *device) +{ + // _PDC is deprecated in the ACPI 3.0, we will try _OSC firstly + // and fall back to _PDC if _OSC fail + status_t status = acpi_eval_osc(device); + if (status != B_OK) + status = acpi_eval_pdc(device); + if (status != B_OK) { + dprintf("faile to eval _OSC and _PDC\n"); + return status; + } + + acpi_data buffer; + buffer.pointer = NULL; + buffer.length = ACPI_ALLOCATE_BUFFER; + + dprintf("evaluate _CST @%p\n", device->acpi_cookie); + status = device->acpi->evaluate_method(device->acpi_cookie, "_CST", NULL, + &buffer); + if (status != B_OK) { + dprintf("failed to get _CST\n"); + return B_IO_ERROR; + } + + acpi_object_type *object = (acpi_object_type *)buffer.pointer; + if (object->object_type != ACPI_TYPE_PACKAGE) + dprintf("invalid _CST type\n"); + if (object->data.package.count < 2) + dprintf("invalid _CST count\n"); + + acpi_object_type *pointer = object->data.package.objects; + if (pointer[0].object_type != ACPI_TYPE_INTEGER) + dprintf("invalid _CST type 2\n"); + uint32 n = pointer[0].data.integer; + if (n != object->data.package.count - 1) + dprintf("invalid _CST count 2\n"); + if (n > 8) + dprintf("_CST has too many states\n"); + dprintf("cpuidle found %" B_PRId32 " cstates\n", n); + uint32 count = 1; + for (uint32 i = 1; i <= n; i++) { + pointer = &object->data.package.objects[i]; + if (acpi_cstate_add(pointer, &sAcpiDevice.cStates[count]) == B_OK) + ++count; + } + sAcpiDevice.cStateCount = count; + free(buffer.pointer); + + // TODO we assume BM is a must and ARB_DIS is always available + device->flags |= ACPI_FLAG_C_ARB | ACPI_FLAG_C_BM; + + acpi_cstate_quirks(device); + + return B_OK; +} static status_t acpi_cpuidle_init(void) { dprintf("acpi_cpuidle_init\n"); + if (get_module(B_CPUIDLE_MODULE_NAME, (module_info**)&sIdle) != B_OK) + return B_ERROR; + if (get_module(B_ACPI_MODULE_NAME, (module_info**)&sAcpi) != B_OK) + return B_ERROR; - return B_ERROR; + for (int32 i = 0; i < smp_get_num_cpus(); i++) + if (acpi_cpuidle_setup(acpi_processor[i]) != B_OK) + return B_ERROR; + + status_t status = sIdle->AddDevice(&sAcpiDevice); + if (status == B_OK) + dprintf("using acpi idle\n"); + else + put_module(B_CPUIDLE_MODULE_NAME); + return status; } @@ -147,6 +553,9 @@ static void acpi_cpuidle_uninit_driver(void *driverCookie) { dprintf("acpi_cpuidle_uninit_driver"); + put_module(B_CPUIDLE_MODULE_NAME); + if (sAcpi) + put_module(B_ACPI_MODULE_NAME); acpi_cpuidle_driver_info *device = (acpi_cpuidle_driver_info *)driverCookie; free(device); } From be9a02c0db1f0b7b0a75d2d5099371992195bb01 Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Wed, 22 Aug 2012 18:55:23 +0800 Subject: [PATCH 13/16] cpuidle: remove drivers/cpuidle Since generic cpuidle module is loaded by lowlevel cpuidle drivers which are loaded dynamically during boot, we don't need drivers/cpuidle any more Signed-off-by: Fredrik Holmqvist --- src/add-ons/kernel/drivers/Jamfile | 1 - src/add-ons/kernel/drivers/cpuidle/Jamfile | 7 - .../kernel/drivers/cpuidle/cpuidle.cpp | 150 ------------------ 3 files changed, 158 deletions(-) delete mode 100644 src/add-ons/kernel/drivers/cpuidle/Jamfile delete mode 100644 src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp diff --git a/src/add-ons/kernel/drivers/Jamfile b/src/add-ons/kernel/drivers/Jamfile index f23cf48e96..ef7c7eee24 100644 --- a/src/add-ons/kernel/drivers/Jamfile +++ b/src/add-ons/kernel/drivers/Jamfile @@ -4,7 +4,6 @@ SubInclude HAIKU_TOP src add-ons kernel drivers bluetooth ; SubInclude HAIKU_TOP src add-ons kernel drivers bus ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ; SubInclude HAIKU_TOP src add-ons kernel drivers common ; -SubInclude HAIKU_TOP src add-ons kernel drivers cpuidle ; SubInclude HAIKU_TOP src add-ons kernel drivers disk ; SubInclude HAIKU_TOP src add-ons kernel drivers dvb ; SubInclude HAIKU_TOP src add-ons kernel drivers input ; diff --git a/src/add-ons/kernel/drivers/cpuidle/Jamfile b/src/add-ons/kernel/drivers/cpuidle/Jamfile deleted file mode 100644 index ce5f450017..0000000000 --- a/src/add-ons/kernel/drivers/cpuidle/Jamfile +++ /dev/null @@ -1,7 +0,0 @@ -SubDir HAIKU_TOP src add-ons kernel drivers cpuidle ; - -UsePrivateKernelHeaders ; - -KernelAddon cpuidle : - cpuidle.cpp - ; diff --git a/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp b/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp deleted file mode 100644 index 82110c9253..0000000000 --- a/src/add-ons/kernel/drivers/cpuidle/cpuidle.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2012, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Yongcong Du - */ - -#include -#include -#include - -#include -#include - -#include - -#define DEVICE_NAME "cpuidle" - -int32 api_version = B_CUR_DRIVER_API_VERSION; - -static GenCpuidle *sGenCpuidle; - -static status_t -cpuidle_open(const char *name, uint32 flags, void **cookie) -{ - *cookie = NULL; - return B_OK; -} - - -static status_t -cpuidle_close(void *cookie) -{ - return B_OK; -} - - -static status_t -cpuidle_free(void *cookie) -{ - return B_OK; -} - - -static status_t -cpuidle_ioctl(void *cookie, uint32 op, void *buffer, size_t length) -{ - return B_OK; -} - - -static status_t -cpuidle_read(void *cookie, off_t pos, void *buffer, size_t *length) -{ - if (pos != 0) { - *length = 0; - return B_OK; - } - char *str = (char *)buffer; - size_t max_len = *length; - int32 stateCount = sGenCpuidle->GetIdleStateCount(); - int32 bytes = snprintf(str, max_len, "C-STATE COUNT: %"B_PRId32"\n", stateCount); - max_len-= bytes; - str += bytes; - int32 cpu = smp_get_num_cpus(); - - for (int32 i = 0; i < cpu; i++) { - bytes = snprintf(str, max_len, "CPU%"B_PRId32"\n", i); - max_len-= bytes; - str += bytes; - for (int32 j = 1; j < stateCount; j++) { - CpuidleStat stat; - bytes = snprintf(str, max_len, "%s\n", sGenCpuidle->GetIdleStateName(j)); - max_len-= bytes; - str += bytes; - sGenCpuidle->GetIdleStateInfo(i, j, &stat); - bytes = snprintf(str, max_len, "%lld %lldus\n", - stat.usageCount, stat.usageTime); - max_len-= bytes; - str += bytes; - } - } - *length = strlen((char *)buffer); - - return B_OK; -} - - -static status_t -cpuidle_write(void *cookie, off_t pos, const void *buffer, size_t *_length) -{ - return B_OK; -} - - -status_t -init_hardware(void) -{ - return B_OK; -} - - -const char ** -publish_devices(void) -{ - static const char *devices[] = { - DEVICE_NAME, - NULL - }; - - return devices; -} - - -device_hooks * -find_device(const char *name) -{ - static device_hooks hooks = { - &cpuidle_open, - &cpuidle_close, - &cpuidle_free, - &cpuidle_ioctl, - &cpuidle_read, - &cpuidle_write, - }; - - - return &hooks; -} - - -status_t -init_driver(void) -{ - status_t err; - - err = get_module(B_CPUIDLE_MODULE_NAME, (module_info **)&sGenCpuidle); - if (err != B_OK) { - dprintf("can't load "B_CPUIDLE_MODULE_NAME"\n"); - } - return B_OK; -} - - -void -uninit_driver(void) -{ - put_module(B_CPUIDLE_MODULE_NAME); -} From 8073227c1d0deeb4fc7aece63b893e401f815442 Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Fri, 24 Aug 2012 03:50:11 +0800 Subject: [PATCH 14/16] x86_cpuidle: use module_dependencies rather than get_module() Signed-off-by: Fredrik Holmqvist --- .../drivers/power/x86_cpuidle/acpi_cpuidle.cpp | 17 +++++------------ .../drivers/power/x86_cpuidle/intel_cpuidle.cpp | 7 +------ .../drivers/power/x86_cpuidle/x86_cpuidle.h | 3 +++ 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp b/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp index fb4095cb87..dbce64caa5 100644 --- a/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/acpi_cpuidle.cpp @@ -89,11 +89,11 @@ struct acpi_cstate_info { uint8 type; }; -static device_manager_info *sDeviceManager; static acpi_cpuidle_driver_info *acpi_processor[B_MAX_CPU_COUNT]; -static CpuidleModuleInfo *sIdle; static CpuidleDevice sAcpiDevice; +static device_manager_info *sDeviceManager; static acpi_module_info *sAcpi; +CpuidleModuleInfo *gIdle; static status_t @@ -431,20 +431,14 @@ static status_t acpi_cpuidle_init(void) { dprintf("acpi_cpuidle_init\n"); - if (get_module(B_CPUIDLE_MODULE_NAME, (module_info**)&sIdle) != B_OK) - return B_ERROR; - if (get_module(B_ACPI_MODULE_NAME, (module_info**)&sAcpi) != B_OK) - return B_ERROR; for (int32 i = 0; i < smp_get_num_cpus(); i++) if (acpi_cpuidle_setup(acpi_processor[i]) != B_OK) return B_ERROR; - status_t status = sIdle->AddDevice(&sAcpiDevice); + status_t status = gIdle->AddDevice(&sAcpiDevice); if (status == B_OK) dprintf("using acpi idle\n"); - else - put_module(B_CPUIDLE_MODULE_NAME); return status; } @@ -553,9 +547,6 @@ static void acpi_cpuidle_uninit_driver(void *driverCookie) { dprintf("acpi_cpuidle_uninit_driver"); - put_module(B_CPUIDLE_MODULE_NAME); - if (sAcpi) - put_module(B_ACPI_MODULE_NAME); acpi_cpuidle_driver_info *device = (acpi_cpuidle_driver_info *)driverCookie; free(device); } @@ -563,6 +554,8 @@ acpi_cpuidle_uninit_driver(void *driverCookie) module_dependency module_dependencies[] = { { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, + { B_ACPI_MODULE_NAME, (module_info **)&sAcpi}, + { B_CPUIDLE_MODULE_NAME, (module_info **)&gIdle }, {} }; diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp b/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp index 1032a5d5e2..069fbc11ad 100644 --- a/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp @@ -16,7 +16,6 @@ #include "x86_cpuidle.h" -static CpuidleModuleInfo *sIdle; static CpuidleDevice sIntelDevice; static void *kMwaitEax[] = { @@ -82,8 +81,6 @@ 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; @@ -116,10 +113,8 @@ intel_cpuidle_init(void) kMwaitEax[i]; sIntelDevice.cStateCount++; } - status_t status = sIdle->AddDevice(&sIntelDevice); + status_t status = gIdle->AddDevice(&sIntelDevice); if (status == B_OK) dprintf("using intel idle\n"); - else - put_module(B_CPUIDLE_MODULE_NAME); return status; } diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/x86_cpuidle.h b/src/add-ons/kernel/drivers/power/x86_cpuidle/x86_cpuidle.h index b524ef19cc..4b6285cb34 100644 --- a/src/add-ons/kernel/drivers/power/x86_cpuidle/x86_cpuidle.h +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/x86_cpuidle.h @@ -6,6 +6,9 @@ #ifdef __cplusplus extern "C" { #endif + +extern CpuidleModuleInfo *gIdle; + static inline void x86_monitor(const void *addr, unsigned long ecx, unsigned long edx) { From 5554814b12e0d7bce167a422b661b70fafb77cc9 Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Fri, 24 Aug 2012 04:01:58 +0800 Subject: [PATCH 15/16] x86_cpuidle: coding style fix according to Jerome Signed-off-by: Fredrik Holmqvist --- .../kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp b/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp index 069fbc11ad..7f6fbd6c4b 100644 --- a/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp +++ b/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp @@ -96,8 +96,9 @@ intel_cpuidle_init(void) * 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)) { + if ((cpuid.regs.ecx & 0x1) == 0 || + (cpuid.regs.ecx & 0x2) == 0 || + cpuid.regs.edx == 0) { return B_ERROR; } From 553f1ebfc1f6e0cb1cc001ea35cb2b2be77bf366 Mon Sep 17 00:00:00 2001 From: Fredrik Holmqvist Date: Sat, 8 Sep 2012 20:55:06 +0200 Subject: [PATCH 16/16] Add disabled cpuidle driver and module to haikuImage. CPU idle still needs some polish, but it should work on newer cpus. --- build/jam/HaikuImage | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index 917b874d29..7498285a47 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -248,7 +248,7 @@ AddFilesToHaikuImage system add-ons kernel file_systems : $(SYSTEM_ADD_ONS_FILE_SYSTEMS) ; AddFilesToHaikuImage system add-ons kernel generic : $(ATA_ONLY)ata_adapter dpc $(IDE_ONLY)ide_adapter locked_pool mpu401 - scsi_periph tty ; + scsi_periph tty ; #$(X86_ONLY)cpuidle ; AddFilesToHaikuImage system add-ons kernel partitioning_systems : amiga_rdb apple efi_gpt intel session ; AddFilesToHaikuImage system add-ons kernel interrupt_controllers @@ -262,6 +262,7 @@ if $(TARGET_ARCH) = x86 { AddNewDriversToHaikuImage disk scsi : scsi_cd scsi_disk ; AddNewDriversToHaikuImage power : $(X86_ONLY)enhanced_speedstep ; AddNewDriversToHaikuImage power : $(X86_ONLY)acpi_battery ; +#AddNewDriversToHaikuImage power : $(X86_ONLY)x86_cpuidle ; # legacy drivers AddDriversToHaikuImage : console dprintf null