diff mbox

[2/2] ACPI: processor: early _PDC evaluation

Message ID 20100123063915.GC28826@digdug.dreamhost.com
State Rejected
Delegated to: Andy Whitcroft
Headers show

Commit Message

Alex Chiang Jan. 23, 2010, 6:39 a.m. UTC
BugLink: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/463940

This patch combines several upstream commits:

	78f1699659963fff97975df44db6d5dbe7218e55
	ACPI: processor: call _PDC early

	7a0b73a49ab56fb1e836675c00d6d0d2ba39a714
	ACPI: Fix section mismatch error for acpi_early_processor_set_pdc()

	2205cbe8ecaf5f3ab911cef839c94d05ea5b0c76
	ACPI: processor: restrict early _PDC to opt-in platforms

I combined the patches because upstream has a lot cleanup patches in
that space too, and these three are the minimal commits that need
backporting in order to:

	1) Enable platforms that are known to require early _PDC evaluation
	2) Prevent a stupid section mismatch error
	3) Make early _PDC an opt-in feature to avoid tripping over
	   platforms with bogus BIOS

Upstream changelogs follow.

78f1699659963fff97975df44db6d5dbe7218e55
ACPI: processor: call _PDC early

	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

7a0b73a49ab56fb1e836675c00d6d0d2ba39a714
ACPI: Fix section mismatch error for acpi_early_processor_set_pdc()

	OriginalAuthor: Tony Luck <tony.luck@intel.com>
	Signed-off-by: Tony Luck <tony.luck@intel.com>
	Acked-by: Alex Chiang <achiang@hp.com>
	Signed-off-by: Len Brown <len.brown@intel.com>

2205cbe8ecaf5f3ab911cef839c94d05ea5b0c76
ACPI: processor: restrict early _PDC to opt-in platforms

	This change may cause issues on platforms that declare
	dummy values for SSDTs on non-present processors
	(disabled in MADT).  When we call _PDC and dynamically
	attempt to execute the AML Load() op on these dummy
	SSDTs, there's no telling what might happen.

	Rather than finding every platform that has bogus SSDTs,
	restrict early _PDC calls to platforms that are known to
	need early evaluation of _PDC.

	This is a minimal, temporary fix (given the context of
	the current release cycle). A real solution of checking
	the MADT for non-present processors will be written for
	the next merge window.

	References:

	http://bugzilla.kernel.org/show_bug.cgi?id=14710
	http://bugzilla.kernel.org/show_bug.cgi?id=14954

	Cc: ming.m.lin@intel.com
	Signed-off-by: Alex Chiang <achiang@hp.com>
	Signed-off-by: Len Brown <len.brown@intel.com>

Signed-off-by: Alex Chiang <achiang@chizang.net>
---

 Documentation/kernel-parameters.txt |    4 +
 drivers/acpi/Makefile               |    1 
 drivers/acpi/bus.c                  |    2 
 drivers/acpi/internal.h             |    1 
 drivers/acpi/processor_core.c       |   69 -----------------
 drivers/acpi/processor_pdc.c        |  145 +++++++++++++++++++++++++++++++++++
 include/acpi/processor.h            |    3 +
 7 files changed, 156 insertions(+), 69 deletions(-)
 create mode 100644 drivers/acpi/processor_pdc.c
diff mbox

Patch

diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index ef735c0..4388507 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -200,6 +200,10 @@  and is between 256 and 4096 characters. It is defined in the file
 			acpi_display_output=video
 			See above.
 
+	acpi_early_pdc_eval	[HW,ACPI] Evaluate processor _PDC methods
+				early. Needed on some platforms to properly
+				initialize the EC.
+
 	acpi_irq_balance [HW,ACPI]
 			ACPI will balance active IRQs
 			default in APIC mode
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 03a985b..2adf55d 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -31,6 +31,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
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 2876fc7..1ee326a 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -744,6 +744,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.
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 11a69b5..5a65817 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -23,6 +23,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
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 2cc4b30..2b13666 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -121,29 +121,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
@@ -274,45 +251,6 @@  static int acpi_processor_errata(struct acpi_processor *pr)
 }
 
 /* --------------------------------------------------------------------------
-                              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)
    -------------------------------------------------------------------------- */
 
@@ -738,9 +676,7 @@  static int __cpuinit acpi_processor_start(struct acpi_device *device)
 		return -EFAULT;
 
 	/* _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);
@@ -1166,11 +1102,6 @@  static int __init acpi_processor_init(void)
 	if (!acpi_processor_dir)
 		return -ENOMEM;
 
-	/*
-	 * 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;
diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c
new file mode 100644
index 0000000..a6cf502
--- /dev/null
+++ b/drivers/acpi/processor_pdc.c
@@ -0,0 +1,145 @@ 
+#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 int early_pdc_optin;
+static int set_early_pdc_optin(const struct dmi_system_id *id)
+{
+	early_pdc_optin = 1;
+	return 0;
+}
+
+static int param_early_pdc_optin(char *s)
+{
+	early_pdc_optin = 1;
+	return 1;
+}
+__setup("acpi_early_pdc_eval", param_early_pdc_optin);
+
+static struct dmi_system_id __cpuinitdata early_pdc_optin_table[] = {
+	{
+	set_early_pdc_optin, "HP Envy", {
+	DMI_MATCH(DMI_BIOS_VENDOR, "Hewlett-Packard"),
+	DMI_MATCH(DMI_PRODUCT_NAME, "HP Envy") }, NULL},
+	{
+	set_early_pdc_optin, "HP Pavilion dv6", {
+	DMI_MATCH(DMI_BIOS_VENDOR, "Hewlett-Packard"),
+	DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv6") }, NULL},
+	{
+	set_early_pdc_optin, "HP Pavilion dv7", {
+	DMI_MATCH(DMI_BIOS_VENDOR, "Hewlett-Packard"),
+	DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv7") }, NULL},
+	{},
+};
+
+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 __init 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);
+
+	/*
+	 * Allow systems to opt-in to early _PDC evaluation.
+	 */
+	dmi_check_system(early_pdc_optin_table);
+	if (!early_pdc_optin)
+		return;
+
+	acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+			    ACPI_UINT32_MAX,
+			    early_init_pdc, NULL, NULL);
+}
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 740ac3a..84c7477 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -318,6 +318,9 @@  static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
 }
 #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);