diff options
author | Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | 2006-09-25 19:28:13 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2006-10-14 00:35:39 -0400 |
commit | 991528d7348667924176f3e29addea0675298944 (patch) | |
tree | ed8552bd4c696700a95ae37b26c4197923207ae7 /arch | |
parent | b4bd8c66435a8cdf8c90334fb3b517a23ff2ab95 (diff) |
ACPI: Processor native C-states using MWAIT
Intel processors starting with the Core Duo support
support processor native C-state using the MWAIT instruction.
Refer: Intel Architecture Software Developer's Manual
http://www.intel.com/design/Pentium4/manuals/253668.htm
Platform firmware exports the support for Native C-state to OS using
ACPI _PDC and _CST methods.
Refer: Intel Processor Vendor-Specific ACPI: Interface Specification
http://www.intel.com/technology/iapc/acpi/downloads/302223.htm
With Processor Native C-state, we use 'MWAIT' instruction on the processor
to enter different C-states (C1, C2, C3). We won't use the special IO
ports to enter C-state and no SMM mode etc required to enter C-state.
Overall this will mean better C-state support.
One major advantage of using MWAIT for all C-states is, with this and
"treat interrupt as break event" feature of MWAIT, we can now get accurate
timing for the time spent in C1, C2, .. states.
Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/i386/kernel/acpi/cstate.c | 122 | ||||
-rw-r--r-- | arch/i386/kernel/process.c | 22 | ||||
-rw-r--r-- | arch/x86_64/kernel/process.c | 22 |
3 files changed, 151 insertions, 15 deletions
diff --git a/arch/i386/kernel/acpi/cstate.c b/arch/i386/kernel/acpi/cstate.c index 25db49ef1770..20563e52c622 100644 --- a/arch/i386/kernel/acpi/cstate.c +++ b/arch/i386/kernel/acpi/cstate.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <linux/acpi.h> | 12 | #include <linux/acpi.h> |
13 | #include <linux/cpu.h> | ||
13 | 14 | ||
14 | #include <acpi/processor.h> | 15 | #include <acpi/processor.h> |
15 | #include <asm/acpi.h> | 16 | #include <asm/acpi.h> |
@@ -41,5 +42,124 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, | |||
41 | flags->bm_check = 1; | 42 | flags->bm_check = 1; |
42 | } | 43 | } |
43 | } | 44 | } |
44 | |||
45 | EXPORT_SYMBOL(acpi_processor_power_init_bm_check); | 45 | EXPORT_SYMBOL(acpi_processor_power_init_bm_check); |
46 | |||
47 | /* The code below handles cstate entry with monitor-mwait pair on Intel*/ | ||
48 | |||
49 | struct cstate_entry_s { | ||
50 | struct { | ||
51 | unsigned int eax; | ||
52 | unsigned int ecx; | ||
53 | } states[ACPI_PROCESSOR_MAX_POWER]; | ||
54 | }; | ||
55 | static struct cstate_entry_s *cpu_cstate_entry; /* per CPU ptr */ | ||
56 | |||
57 | static short mwait_supported[ACPI_PROCESSOR_MAX_POWER]; | ||
58 | |||
59 | #define MWAIT_SUBSTATE_MASK (0xf) | ||
60 | #define MWAIT_SUBSTATE_SIZE (4) | ||
61 | |||
62 | #define CPUID_MWAIT_LEAF (5) | ||
63 | #define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1) | ||
64 | #define CPUID5_ECX_INTERRUPT_BREAK (0x2) | ||
65 | |||
66 | #define MWAIT_ECX_INTERRUPT_BREAK (0x1) | ||
67 | |||
68 | #define NATIVE_CSTATE_BEYOND_HALT (2) | ||
69 | |||
70 | int acpi_processor_ffh_cstate_probe(unsigned int cpu, | ||
71 | struct acpi_processor_cx *cx, struct acpi_power_register *reg) | ||
72 | { | ||
73 | struct cstate_entry_s *percpu_entry; | ||
74 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
75 | |||
76 | cpumask_t saved_mask; | ||
77 | int retval; | ||
78 | unsigned int eax, ebx, ecx, edx; | ||
79 | unsigned int edx_part; | ||
80 | unsigned int cstate_type; /* C-state type and not ACPI C-state type */ | ||
81 | unsigned int num_cstate_subtype; | ||
82 | |||
83 | if (!cpu_cstate_entry || c->cpuid_level < CPUID_MWAIT_LEAF ) | ||
84 | return -1; | ||
85 | |||
86 | if (reg->bit_offset != NATIVE_CSTATE_BEYOND_HALT) | ||
87 | return -1; | ||
88 | |||
89 | percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu); | ||
90 | percpu_entry->states[cx->index].eax = 0; | ||
91 | percpu_entry->states[cx->index].ecx = 0; | ||
92 | |||
93 | /* Make sure we are running on right CPU */ | ||
94 | saved_mask = current->cpus_allowed; | ||
95 | retval = set_cpus_allowed(current, cpumask_of_cpu(cpu)); | ||
96 | if (retval) | ||
97 | return -1; | ||
98 | |||
99 | cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); | ||
100 | |||
101 | /* Check whether this particular cx_type (in CST) is supported or not */ | ||
102 | cstate_type = (cx->address >> MWAIT_SUBSTATE_SIZE) + 1; | ||
103 | edx_part = edx >> (cstate_type * MWAIT_SUBSTATE_SIZE); | ||
104 | num_cstate_subtype = edx_part & MWAIT_SUBSTATE_MASK; | ||
105 | |||
106 | retval = 0; | ||
107 | if (num_cstate_subtype < (cx->address & MWAIT_SUBSTATE_MASK)) { | ||
108 | retval = -1; | ||
109 | goto out; | ||
110 | } | ||
111 | |||
112 | /* mwait ecx extensions INTERRUPT_BREAK should be supported for C2/C3 */ | ||
113 | if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || | ||
114 | !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) { | ||
115 | retval = -1; | ||
116 | goto out; | ||
117 | } | ||
118 | percpu_entry->states[cx->index].ecx = MWAIT_ECX_INTERRUPT_BREAK; | ||
119 | |||
120 | /* Use the hint in CST */ | ||
121 | percpu_entry->states[cx->index].eax = cx->address; | ||
122 | |||
123 | if (!mwait_supported[cstate_type]) { | ||
124 | mwait_supported[cstate_type] = 1; | ||
125 | printk(KERN_DEBUG "Monitor-Mwait will be used to enter C-%d " | ||
126 | "state\n", cx->type); | ||
127 | } | ||
128 | |||
129 | out: | ||
130 | set_cpus_allowed(current, saved_mask); | ||
131 | return retval; | ||
132 | } | ||
133 | EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe); | ||
134 | |||
135 | void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx) | ||
136 | { | ||
137 | unsigned int cpu = smp_processor_id(); | ||
138 | struct cstate_entry_s *percpu_entry; | ||
139 | |||
140 | percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu); | ||
141 | mwait_idle_with_hints(percpu_entry->states[cx->index].eax, | ||
142 | percpu_entry->states[cx->index].ecx); | ||
143 | } | ||
144 | EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter); | ||
145 | |||
146 | static int __init ffh_cstate_init(void) | ||
147 | { | ||
148 | struct cpuinfo_x86 *c = &boot_cpu_data; | ||
149 | if (c->x86_vendor != X86_VENDOR_INTEL) | ||
150 | return -1; | ||
151 | |||
152 | cpu_cstate_entry = alloc_percpu(struct cstate_entry_s); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static void __exit ffh_cstate_exit(void) | ||
157 | { | ||
158 | if (cpu_cstate_entry) { | ||
159 | free_percpu(cpu_cstate_entry); | ||
160 | cpu_cstate_entry = NULL; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | arch_initcall(ffh_cstate_init); | ||
165 | __exitcall(ffh_cstate_exit); | ||
diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index b0a07801d9df..57d375900afb 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c | |||
@@ -236,20 +236,28 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait); | |||
236 | * We execute MONITOR against need_resched and enter optimized wait state | 236 | * We execute MONITOR against need_resched and enter optimized wait state |
237 | * through MWAIT. Whenever someone changes need_resched, we would be woken | 237 | * through MWAIT. Whenever someone changes need_resched, we would be woken |
238 | * up from MWAIT (without an IPI). | 238 | * up from MWAIT (without an IPI). |
239 | * | ||
240 | * New with Core Duo processors, MWAIT can take some hints based on CPU | ||
241 | * capability. | ||
239 | */ | 242 | */ |
240 | static void mwait_idle(void) | 243 | void mwait_idle_with_hints(unsigned long eax, unsigned long ecx) |
241 | { | 244 | { |
242 | local_irq_enable(); | 245 | if (!need_resched()) { |
243 | |||
244 | while (!need_resched()) { | ||
245 | __monitor((void *)¤t_thread_info()->flags, 0, 0); | 246 | __monitor((void *)¤t_thread_info()->flags, 0, 0); |
246 | smp_mb(); | 247 | smp_mb(); |
247 | if (need_resched()) | 248 | if (!need_resched()) |
248 | break; | 249 | __mwait(eax, ecx); |
249 | __mwait(0, 0); | ||
250 | } | 250 | } |
251 | } | 251 | } |
252 | 252 | ||
253 | /* Default MONITOR/MWAIT with no hints, used for default C1 state */ | ||
254 | static void mwait_idle(void) | ||
255 | { | ||
256 | local_irq_enable(); | ||
257 | while (!need_resched()) | ||
258 | mwait_idle_with_hints(0, 0); | ||
259 | } | ||
260 | |||
253 | void __devinit select_idle_routine(const struct cpuinfo_x86 *c) | 261 | void __devinit select_idle_routine(const struct cpuinfo_x86 *c) |
254 | { | 262 | { |
255 | if (cpu_has(c, X86_FEATURE_MWAIT)) { | 263 | if (cpu_has(c, X86_FEATURE_MWAIT)) { |
diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index 5e95b257ee26..49f7fac6229e 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c | |||
@@ -238,20 +238,28 @@ void cpu_idle (void) | |||
238 | * We execute MONITOR against need_resched and enter optimized wait state | 238 | * We execute MONITOR against need_resched and enter optimized wait state |
239 | * through MWAIT. Whenever someone changes need_resched, we would be woken | 239 | * through MWAIT. Whenever someone changes need_resched, we would be woken |
240 | * up from MWAIT (without an IPI). | 240 | * up from MWAIT (without an IPI). |
241 | * | ||
242 | * New with Core Duo processors, MWAIT can take some hints based on CPU | ||
243 | * capability. | ||
241 | */ | 244 | */ |
242 | static void mwait_idle(void) | 245 | void mwait_idle_with_hints(unsigned long eax, unsigned long ecx) |
243 | { | 246 | { |
244 | local_irq_enable(); | 247 | if (!need_resched()) { |
245 | |||
246 | while (!need_resched()) { | ||
247 | __monitor((void *)¤t_thread_info()->flags, 0, 0); | 248 | __monitor((void *)¤t_thread_info()->flags, 0, 0); |
248 | smp_mb(); | 249 | smp_mb(); |
249 | if (need_resched()) | 250 | if (!need_resched()) |
250 | break; | 251 | __mwait(eax, ecx); |
251 | __mwait(0, 0); | ||
252 | } | 252 | } |
253 | } | 253 | } |
254 | 254 | ||
255 | /* Default MONITOR/MWAIT with no hints, used for default C1 state */ | ||
256 | static void mwait_idle(void) | ||
257 | { | ||
258 | local_irq_enable(); | ||
259 | while (!need_resched()) | ||
260 | mwait_idle_with_hints(0,0); | ||
261 | } | ||
262 | |||
255 | void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c) | 263 | void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c) |
256 | { | 264 | { |
257 | static int printed; | 265 | static int printed; |