diff options
author | Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | 2005-10-23 16:31:00 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2005-12-01 01:30:35 -0500 |
commit | 05131ecc99ea9da7f45ba3058fe8a2c1d0ceeab8 (patch) | |
tree | dec76975406874cf677d6391302f42a6da55ac38 /arch/i386 | |
parent | d2149b542382bfc206cb28485108f6470c979566 (diff) |
[ACPI] Avoid BIOS inflicted crashes by evaluating _PDC only once
Linux invokes the AML _PDC method (Processor Driver Capabilities)
to tell the BIOS what features it can handle. While the ACPI
spec says nothing about the OS invoking _PDC multiple times,
doing so with changing bits seems to hopelessly confuse the BIOS
on multiple platforms up to and including crashing the system.
Factor out the _PDC invocation so Linux invokes it only once.
http://bugzilla.kernel.org/show_bug.cgi?id=5483
Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/kernel/acpi/Makefile | 2 | ||||
-rw-r--r-- | arch/i386/kernel/acpi/cstate.c | 58 | ||||
-rw-r--r-- | arch/i386/kernel/acpi/processor.c | 75 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c | 67 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c | 12 |
5 files changed, 76 insertions, 138 deletions
diff --git a/arch/i386/kernel/acpi/Makefile b/arch/i386/kernel/acpi/Makefile index 267ca48e1b6c..d51c7313cae8 100644 --- a/arch/i386/kernel/acpi/Makefile +++ b/arch/i386/kernel/acpi/Makefile | |||
@@ -3,6 +3,6 @@ obj-$(CONFIG_X86_IO_APIC) += earlyquirk.o | |||
3 | obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup.o | 3 | obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup.o |
4 | 4 | ||
5 | ifneq ($(CONFIG_ACPI_PROCESSOR),) | 5 | ifneq ($(CONFIG_ACPI_PROCESSOR),) |
6 | obj-y += cstate.o | 6 | obj-y += cstate.o processor.o |
7 | endif | 7 | endif |
8 | 8 | ||
diff --git a/arch/i386/kernel/acpi/cstate.c b/arch/i386/kernel/acpi/cstate.c index 4c3036ba65df..25db49ef1770 100644 --- a/arch/i386/kernel/acpi/cstate.c +++ b/arch/i386/kernel/acpi/cstate.c | |||
@@ -14,64 +14,6 @@ | |||
14 | #include <acpi/processor.h> | 14 | #include <acpi/processor.h> |
15 | #include <asm/acpi.h> | 15 | #include <asm/acpi.h> |
16 | 16 | ||
17 | static void acpi_processor_power_init_intel_pdc(struct acpi_processor_power | ||
18 | *pow) | ||
19 | { | ||
20 | struct acpi_object_list *obj_list; | ||
21 | union acpi_object *obj; | ||
22 | u32 *buf; | ||
23 | |||
24 | /* allocate and initialize pdc. It will be used later. */ | ||
25 | obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL); | ||
26 | if (!obj_list) { | ||
27 | printk(KERN_ERR "Memory allocation error\n"); | ||
28 | return; | ||
29 | } | ||
30 | |||
31 | obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL); | ||
32 | if (!obj) { | ||
33 | printk(KERN_ERR "Memory allocation error\n"); | ||
34 | kfree(obj_list); | ||
35 | return; | ||
36 | } | ||
37 | |||
38 | buf = kmalloc(12, GFP_KERNEL); | ||
39 | if (!buf) { | ||
40 | printk(KERN_ERR "Memory allocation error\n"); | ||
41 | kfree(obj); | ||
42 | kfree(obj_list); | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | buf[0] = ACPI_PDC_REVISION_ID; | ||
47 | buf[1] = 1; | ||
48 | buf[2] = ACPI_PDC_C_CAPABILITY_SMP; | ||
49 | |||
50 | obj->type = ACPI_TYPE_BUFFER; | ||
51 | obj->buffer.length = 12; | ||
52 | obj->buffer.pointer = (u8 *) buf; | ||
53 | obj_list->count = 1; | ||
54 | obj_list->pointer = obj; | ||
55 | pow->pdc = obj_list; | ||
56 | |||
57 | return; | ||
58 | } | ||
59 | |||
60 | /* Initialize _PDC data based on the CPU vendor */ | ||
61 | void acpi_processor_power_init_pdc(struct acpi_processor_power *pow, | ||
62 | unsigned int cpu) | ||
63 | { | ||
64 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
65 | |||
66 | pow->pdc = NULL; | ||
67 | if (c->x86_vendor == X86_VENDOR_INTEL) | ||
68 | acpi_processor_power_init_intel_pdc(pow); | ||
69 | |||
70 | return; | ||
71 | } | ||
72 | |||
73 | EXPORT_SYMBOL(acpi_processor_power_init_pdc); | ||
74 | |||
75 | /* | 17 | /* |
76 | * Initialize bm_flags based on the CPU cache properties | 18 | * Initialize bm_flags based on the CPU cache properties |
77 | * On SMP it depends on cache configuration | 19 | * On SMP it depends on cache configuration |
diff --git a/arch/i386/kernel/acpi/processor.c b/arch/i386/kernel/acpi/processor.c new file mode 100644 index 000000000000..9f4cc02717ec --- /dev/null +++ b/arch/i386/kernel/acpi/processor.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * arch/i386/kernel/acpi/processor.c | ||
3 | * | ||
4 | * Copyright (C) 2005 Intel Corporation | ||
5 | * Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | ||
6 | * - Added _PDC for platforms with Intel CPUs | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/acpi.h> | ||
13 | |||
14 | #include <acpi/processor.h> | ||
15 | #include <asm/acpi.h> | ||
16 | |||
17 | static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c) | ||
18 | { | ||
19 | struct acpi_object_list *obj_list; | ||
20 | union acpi_object *obj; | ||
21 | u32 *buf; | ||
22 | |||
23 | /* allocate and initialize pdc. It will be used later. */ | ||
24 | obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL); | ||
25 | if (!obj_list) { | ||
26 | printk(KERN_ERR "Memory allocation error\n"); | ||
27 | return; | ||
28 | } | ||
29 | |||
30 | obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL); | ||
31 | if (!obj) { | ||
32 | printk(KERN_ERR "Memory allocation error\n"); | ||
33 | kfree(obj_list); | ||
34 | return; | ||
35 | } | ||
36 | |||
37 | buf = kmalloc(12, GFP_KERNEL); | ||
38 | if (!buf) { | ||
39 | printk(KERN_ERR "Memory allocation error\n"); | ||
40 | kfree(obj); | ||
41 | kfree(obj_list); | ||
42 | return; | ||
43 | } | ||
44 | |||
45 | buf[0] = ACPI_PDC_REVISION_ID; | ||
46 | buf[1] = 1; | ||
47 | buf[2] = ACPI_PDC_C_CAPABILITY_SMP; | ||
48 | |||
49 | if (cpu_has(c, X86_FEATURE_EST)) | ||
50 | buf[2] |= ACPI_PDC_EST_CAPABILITY_SMP; | ||
51 | |||
52 | obj->type = ACPI_TYPE_BUFFER; | ||
53 | obj->buffer.length = 12; | ||
54 | obj->buffer.pointer = (u8 *) buf; | ||
55 | obj_list->count = 1; | ||
56 | obj_list->pointer = obj; | ||
57 | pr->pdc = obj_list; | ||
58 | |||
59 | return; | ||
60 | } | ||
61 | |||
62 | /* Initialize _PDC data based on the CPU vendor */ | ||
63 | void arch_acpi_processor_init_pdc(struct acpi_processor *pr) | ||
64 | { | ||
65 | unsigned int cpu = pr->id; | ||
66 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
67 | |||
68 | pr->pdc = NULL; | ||
69 | if (c->x86_vendor == X86_VENDOR_INTEL) | ||
70 | init_intel_pdc(pr, c); | ||
71 | |||
72 | return; | ||
73 | } | ||
74 | |||
75 | EXPORT_SYMBOL(arch_acpi_processor_init_pdc); | ||
diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 871366b83b3f..31ce890865d5 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c | |||
@@ -297,68 +297,6 @@ acpi_cpufreq_guess_freq ( | |||
297 | } | 297 | } |
298 | 298 | ||
299 | 299 | ||
300 | /* | ||
301 | * acpi_processor_cpu_init_pdc_est - let BIOS know about the SMP capabilities | ||
302 | * of this driver | ||
303 | * @perf: processor-specific acpi_io_data struct | ||
304 | * @cpu: CPU being initialized | ||
305 | * | ||
306 | * To avoid issues with legacy OSes, some BIOSes require to be informed of | ||
307 | * the SMP capabilities of OS P-state driver. Here we set the bits in _PDC | ||
308 | * accordingly, for Enhanced Speedstep. Actual call to _PDC is done in | ||
309 | * driver/acpi/processor.c | ||
310 | */ | ||
311 | static void | ||
312 | acpi_processor_cpu_init_pdc_est( | ||
313 | struct acpi_processor_performance *perf, | ||
314 | unsigned int cpu, | ||
315 | struct acpi_object_list *obj_list | ||
316 | ) | ||
317 | { | ||
318 | union acpi_object *obj; | ||
319 | u32 *buf; | ||
320 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
321 | dprintk("acpi_processor_cpu_init_pdc_est\n"); | ||
322 | |||
323 | if (!cpu_has(c, X86_FEATURE_EST)) | ||
324 | return; | ||
325 | |||
326 | /* Initialize pdc. It will be used later. */ | ||
327 | if (!obj_list) | ||
328 | return; | ||
329 | |||
330 | if (!(obj_list->count && obj_list->pointer)) | ||
331 | return; | ||
332 | |||
333 | obj = obj_list->pointer; | ||
334 | if ((obj->buffer.length == 12) && obj->buffer.pointer) { | ||
335 | buf = (u32 *)obj->buffer.pointer; | ||
336 | buf[0] = ACPI_PDC_REVISION_ID; | ||
337 | buf[1] = 1; | ||
338 | buf[2] = ACPI_PDC_EST_CAPABILITY_SMP; | ||
339 | perf->pdc = obj_list; | ||
340 | } | ||
341 | return; | ||
342 | } | ||
343 | |||
344 | |||
345 | /* CPU specific PDC initialization */ | ||
346 | static void | ||
347 | acpi_processor_cpu_init_pdc( | ||
348 | struct acpi_processor_performance *perf, | ||
349 | unsigned int cpu, | ||
350 | struct acpi_object_list *obj_list | ||
351 | ) | ||
352 | { | ||
353 | struct cpuinfo_x86 *c = cpu_data + cpu; | ||
354 | dprintk("acpi_processor_cpu_init_pdc\n"); | ||
355 | perf->pdc = NULL; | ||
356 | if (cpu_has(c, X86_FEATURE_EST)) | ||
357 | acpi_processor_cpu_init_pdc_est(perf, cpu, obj_list); | ||
358 | return; | ||
359 | } | ||
360 | |||
361 | |||
362 | static int | 300 | static int |
363 | acpi_cpufreq_cpu_init ( | 301 | acpi_cpufreq_cpu_init ( |
364 | struct cpufreq_policy *policy) | 302 | struct cpufreq_policy *policy) |
@@ -373,9 +311,6 @@ acpi_cpufreq_cpu_init ( | |||
373 | struct acpi_object_list arg_list = {1, &arg0}; | 311 | struct acpi_object_list arg_list = {1, &arg0}; |
374 | 312 | ||
375 | dprintk("acpi_cpufreq_cpu_init\n"); | 313 | dprintk("acpi_cpufreq_cpu_init\n"); |
376 | /* setup arg_list for _PDC settings */ | ||
377 | arg0.buffer.length = 12; | ||
378 | arg0.buffer.pointer = (u8 *) arg0_buf; | ||
379 | 314 | ||
380 | data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL); | 315 | data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL); |
381 | if (!data) | 316 | if (!data) |
@@ -383,9 +318,7 @@ acpi_cpufreq_cpu_init ( | |||
383 | 318 | ||
384 | acpi_io_data[cpu] = data; | 319 | acpi_io_data[cpu] = data; |
385 | 320 | ||
386 | acpi_processor_cpu_init_pdc(&data->acpi_data, cpu, &arg_list); | ||
387 | result = acpi_processor_register_performance(&data->acpi_data, cpu); | 321 | result = acpi_processor_register_performance(&data->acpi_data, cpu); |
388 | data->acpi_data.pdc = NULL; | ||
389 | 322 | ||
390 | if (result) | 323 | if (result) |
391 | goto err_free; | 324 | goto err_free; |
diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c index edb9873e27e3..d93023438c4f 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c | |||
@@ -364,22 +364,10 @@ static struct acpi_processor_performance p; | |||
364 | */ | 364 | */ |
365 | static int centrino_cpu_init_acpi(struct cpufreq_policy *policy) | 365 | static int centrino_cpu_init_acpi(struct cpufreq_policy *policy) |
366 | { | 366 | { |
367 | union acpi_object arg0 = {ACPI_TYPE_BUFFER}; | ||
368 | u32 arg0_buf[3]; | ||
369 | struct acpi_object_list arg_list = {1, &arg0}; | ||
370 | unsigned long cur_freq; | 367 | unsigned long cur_freq; |
371 | int result = 0, i; | 368 | int result = 0, i; |
372 | unsigned int cpu = policy->cpu; | 369 | unsigned int cpu = policy->cpu; |
373 | 370 | ||
374 | /* _PDC settings */ | ||
375 | arg0.buffer.length = 12; | ||
376 | arg0.buffer.pointer = (u8 *) arg0_buf; | ||
377 | arg0_buf[0] = ACPI_PDC_REVISION_ID; | ||
378 | arg0_buf[1] = 1; | ||
379 | arg0_buf[2] = ACPI_PDC_EST_CAPABILITY_SMP_MSR; | ||
380 | |||
381 | p.pdc = &arg_list; | ||
382 | |||
383 | /* register with ACPI core */ | 371 | /* register with ACPI core */ |
384 | if (acpi_processor_register_performance(&p, cpu)) { | 372 | if (acpi_processor_register_performance(&p, cpu)) { |
385 | dprintk(KERN_INFO PFX "obtaining ACPI data failed\n"); | 373 | dprintk(KERN_INFO PFX "obtaining ACPI data failed\n"); |