Skip to content

Commit

Permalink
ACPI: processor: call _PDC early
Browse files Browse the repository at this point in the history
We discovered that at least one machine (HP Envy), methods in the DSDT
attempt to call external methods defined in a dynamically loaded SSDT.

Unfortunately, the DSDT methods we are trying to call are part of the
EC initialization, which happens very early, and the the dynamic SSDT
is only loaded when a processor _PDC method runs much later.

This results in namespace lookup errors for the (as of yet) undefined
methods.

Since Windows doesn't have any issues with this machine, we take it
as a hint that they must be evaluating _PDC much earlier than we are.

Thus, the proper thing for Linux to do should be to match the Windows
implementation more closely.

Provide a mechanism to call _PDC before we enable the EC. Doing so loads
the dynamic tables, and allows the EC to be enabled correctly.

The ACPI processor driver will still evaluate _PDC in its .add() method
to cover the hotplug case.

Resolves: http://bugzilla.kernel.org/show_bug.cgi?id=14824

Cc: [email protected]
Signed-off-by: Alex Chiang <[email protected]>
Signed-off-by: Len Brown <[email protected]>
  • Loading branch information
Alex Chiang authored and lenb committed Dec 22, 2009
1 parent 5563935 commit 78f1699
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 69 deletions.
1 change: 1 addition & 0 deletions drivers/acpi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o
#
acpi-y += bus.o glue.o
acpi-y += scan.o
acpi-y += processor_pdc.o
acpi-y += ec.o
acpi-$(CONFIG_ACPI_DOCK) += dock.o
acpi-y += pci_root.o pci_link.o pci_irq.o pci_bind.o
Expand Down
2 changes: 2 additions & 0 deletions drivers/acpi/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,8 @@ static int __init acpi_bus_init(void)
goto error1;
}

acpi_early_processor_set_pdc();

/*
* Maybe EC region is required at bus_scan/acpi_get_devices. So it
* is necessary to enable it as early as possible.
Expand Down
1 change: 1 addition & 0 deletions drivers/acpi/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ int acpi_power_transition(struct acpi_device *device, int state);
extern int acpi_power_nocheck;

int acpi_wakeup_device_init(void);
void acpi_early_processor_set_pdc(void);

/* --------------------------------------------------------------------------
Embedded Controller
Expand Down
69 changes: 0 additions & 69 deletions drivers/acpi/processor_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,29 +124,6 @@ static const struct file_operations acpi_processor_info_fops = {

DEFINE_PER_CPU(struct acpi_processor *, processors);
struct acpi_processor_errata errata __read_mostly;
static int set_no_mwait(const struct dmi_system_id *id)
{
printk(KERN_NOTICE PREFIX "%s detected - "
"disabling mwait for CPU C-states\n", id->ident);
idle_nomwait = 1;
return 0;
}

static struct dmi_system_id __cpuinitdata processor_idle_dmi_table[] = {
{
set_no_mwait, "IFL91 board", {
DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"),
DMI_MATCH(DMI_SYS_VENDOR, "ZEPTO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "3215W"),
DMI_MATCH(DMI_BOARD_NAME, "IFL91") }, NULL},
{
set_no_mwait, "Extensa 5220", {
DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL},
{},
};

/* --------------------------------------------------------------------------
Errata Handling
Expand Down Expand Up @@ -276,45 +253,6 @@ static int acpi_processor_errata(struct acpi_processor *pr)
return result;
}

/* --------------------------------------------------------------------------
Common ACPI processor functions
-------------------------------------------------------------------------- */

/*
* _PDC is required for a BIOS-OS handshake for most of the newer
* ACPI processor features.
*/
static int acpi_processor_set_pdc(struct acpi_processor *pr)
{
struct acpi_object_list *pdc_in = pr->pdc;
acpi_status status = AE_OK;


if (!pdc_in)
return status;
if (idle_nomwait) {
/*
* If mwait is disabled for CPU C-states, the C2C3_FFH access
* mode will be disabled in the parameter of _PDC object.
* Of course C1_FFH access mode will also be disabled.
*/
union acpi_object *obj;
u32 *buffer = NULL;

obj = pdc_in->pointer;
buffer = (u32 *)(obj->buffer.pointer);
buffer[2] &= ~(ACPI_PDC_C_C2C3_FFH | ACPI_PDC_C_C1_FFH);

}
status = acpi_evaluate_object(pr->handle, "_PDC", pdc_in, NULL);

if (ACPI_FAILURE(status))
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Could not evaluate _PDC, using legacy perf. control...\n"));

return status;
}

/* --------------------------------------------------------------------------
FS Interface (/proc)
-------------------------------------------------------------------------- */
Expand Down Expand Up @@ -825,9 +763,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
}

/* _PDC call should be done before doing anything else (if reqd.). */
arch_acpi_processor_init_pdc(pr);
acpi_processor_set_pdc(pr);
arch_acpi_processor_cleanup_pdc(pr);

#ifdef CONFIG_CPU_FREQ
acpi_processor_ppc_has_changed(pr, 0);
Expand Down Expand Up @@ -1145,11 +1081,6 @@ static int __init acpi_processor_init(void)
if (!acpi_processor_dir)
return -ENOMEM;
#endif
/*
* Check whether the system is DMI table. If yes, OSPM
* should not use mwait for CPU-states.
*/
dmi_check_system(processor_idle_dmi_table);
result = cpuidle_register_driver(&acpi_idle_driver);
if (result < 0)
goto out_proc;
Expand Down
108 changes: 108 additions & 0 deletions drivers/acpi/processor_pdc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include <linux/dmi.h>

#include <acpi/acpi_drivers.h>
#include <acpi/processor.h>

#include "internal.h"

#define PREFIX "ACPI: "
#define _COMPONENT ACPI_PROCESSOR_COMPONENT
ACPI_MODULE_NAME("processor_pdc");

static int set_no_mwait(const struct dmi_system_id *id)
{
printk(KERN_NOTICE PREFIX "%s detected - "
"disabling mwait for CPU C-states\n", id->ident);
idle_nomwait = 1;
return 0;
}

static struct dmi_system_id __cpuinitdata processor_idle_dmi_table[] = {
{
set_no_mwait, "IFL91 board", {
DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"),
DMI_MATCH(DMI_SYS_VENDOR, "ZEPTO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "3215W"),
DMI_MATCH(DMI_BOARD_NAME, "IFL91") }, NULL},
{
set_no_mwait, "Extensa 5220", {
DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL},
{},
};

/*
* _PDC is required for a BIOS-OS handshake for most of the newer
* ACPI processor features.
*/
static int acpi_processor_eval_pdc(struct acpi_processor *pr)
{
struct acpi_object_list *pdc_in = pr->pdc;
acpi_status status = AE_OK;

if (!pdc_in)
return status;
if (idle_nomwait) {
/*
* If mwait is disabled for CPU C-states, the C2C3_FFH access
* mode will be disabled in the parameter of _PDC object.
* Of course C1_FFH access mode will also be disabled.
*/
union acpi_object *obj;
u32 *buffer = NULL;

obj = pdc_in->pointer;
buffer = (u32 *)(obj->buffer.pointer);
buffer[2] &= ~(ACPI_PDC_C_C2C3_FFH | ACPI_PDC_C_C1_FFH);

}
status = acpi_evaluate_object(pr->handle, "_PDC", pdc_in, NULL);

if (ACPI_FAILURE(status))
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Could not evaluate _PDC, using legacy perf. control.\n"));

return status;
}

void acpi_processor_set_pdc(struct acpi_processor *pr)
{
arch_acpi_processor_init_pdc(pr);
acpi_processor_eval_pdc(pr);
arch_acpi_processor_cleanup_pdc(pr);
}
EXPORT_SYMBOL_GPL(acpi_processor_set_pdc);

static acpi_status
early_init_pdc(acpi_handle handle, u32 lvl, void *context, void **rv)
{
struct acpi_processor pr;

pr.handle = handle;

/* x86 implementation looks at pr.id to determine some
* CPU capabilites. We can just hard code to 0 since we're
* assuming the CPUs in the system are homogenous and all
* have the same capabilities.
*/
pr.id = 0;

acpi_processor_set_pdc(&pr);

return AE_OK;
}

void acpi_early_processor_set_pdc(void)
{
/*
* Check whether the system is DMI table. If yes, OSPM
* should not use mwait for CPU-states.
*/
dmi_check_system(processor_idle_dmi_table);

acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
early_init_pdc, NULL, NULL, NULL);
}
3 changes: 3 additions & 0 deletions include/acpi/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ static inline int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)

#endif /* CONFIG_CPU_FREQ */

/* in processor_pdc.c */
void acpi_processor_set_pdc(struct acpi_processor *pr);

/* in processor_throttling.c */
int acpi_processor_tstate_has_changed(struct acpi_processor *pr);
int acpi_processor_get_throttling_info(struct acpi_processor *pr);
Expand Down

0 comments on commit 78f1699

Please sign in to comment.