All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3)
@ 2010-08-06  1:03 Rafael J. Wysocki
  2010-08-06  1:05 ` [PATCH 1/10] ACPI / PCI: Introduce function for querying PCI root _OSC Rafael J. Wysocki
                   ` (23 more replies)
  0 siblings, 24 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:03 UTC (permalink / raw)
  To: Hidetoshi Seto, Kenji Kaneshige
  Cc: Jesse Barnes, linux-pci, ACPI Devel Maling List, linux-pm

Hi,

This is the 3rd iteration of the patchset based on
https://patchwork.kernel.org/patch/114917/ and the comments I received on that
patch and the previous two iterations of this patchset.

The patches below do not make the intermediate step to put an unconditional
_OSC query into acpi_pci_osc_control_set(), but the final result is quite
similar to the 2nd iteration with https://patchwork.kernel.org/patch/116976/
applied on top.

[1/10] - Introduce acpi_pci_osc_control_query() allowing the caller to get a
         mask of _OSC control bits the BIOS allows the kernel to control
         for a given PCI root bridge.  This version repeats _OSC queries until
         the BIOS doesn't mask any requested bits.

[2/10] - Introduce pci_aer_available() allowing the caller to check if the
         AER service driver should be enabled.

[3/10] - Introduce kernel command line switch pcie_ports=.

[4/10] - Rework the PCIe port driver to request _OSC control for all serives at
         once.

[5/10] - Disable PCIe port services (that might be enabled by the BIOS) during
         initialization.

[6/10] - Remove the PCIe port driver modules exit function.

[7/10] - Make PCI hotplug code query _OSC before requesting control of _OSC
         features.

[8/10] - Rework acpi_pci_osc_control_set() so that it doesn't use cached
        result of a query and remove the fields of struct acpi_pci_root that
        aren't used any more.

[9/10] - Reorder checks in acpi_pci_osc_control_set().

[10/10] - Merge acpi_pci_osc_control_set() with acpi_pci_osc_control_query().

Comments welcome.

Thanks,
Rafael


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 1/10] ACPI / PCI: Introduce function for querying PCI root _OSC
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
@ 2010-08-06  1:05 ` Rafael J. Wysocki
  2010-08-06  1:05 ` Rafael J. Wysocki
                   ` (22 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:05 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce a function allowing the caller to obtain a mask of _OSC
control bits the BIOS will allow the kernel to control for a given
PCI root bridge.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   77 +++++++++++++++++++++++++++++++++++++++++-------
 include/linux/acpi.h    |    1 
 2 files changed, 67 insertions(+), 11 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -225,22 +225,35 @@ static acpi_status acpi_pci_run_osc(acpi
 	return status;
 }
 
-static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
+static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root,
+					u32 support,
+					u32 *control)
 {
 	acpi_status status;
-	u32 support_set, result, capbuf[3];
+	u32 result, capbuf[3];
+
+	support &= OSC_PCI_SUPPORT_MASKS;
+	support |= root->osc_support_set;
 
-	/* do _OSC query for all possible controls */
-	support_set = root->osc_support_set | (flags & OSC_PCI_SUPPORT_MASKS);
 	capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
-	capbuf[OSC_SUPPORT_TYPE] = support_set;
-	capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
+	capbuf[OSC_SUPPORT_TYPE] = support;
+	if (control) {
+		*control &= OSC_PCI_CONTROL_MASKS;
+		capbuf[OSC_CONTROL_TYPE] = *control | root->osc_control_set;
+	} else {
+		/* Run _OSC query for all possible controls. */
+		capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
+	}
 
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
-		root->osc_support_set = support_set;
-		root->osc_control_qry = result;
-		root->osc_queried = 1;
+		root->osc_support_set = support;
+		if (control) {
+			*control = result;
+		} else {
+			root->osc_control_qry = result;
+			root->osc_queried = 1;
+		}
 	}
 	return status;
 }
@@ -254,7 +267,7 @@ static acpi_status acpi_pci_osc_support(
 	if (ACPI_FAILURE(status))
 		return status;
 	mutex_lock(&osc_lock);
-	status = acpi_pci_query_osc(root, flags);
+	status = acpi_pci_query_osc(root, flags, NULL);
 	mutex_unlock(&osc_lock);
 	return status;
 }
@@ -363,6 +376,48 @@ out:
 }
 EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
 
+ /**
+ * acpi_pci_osc_control_query - Get the _OSC bits the kernel can control.
+ * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
+ * @mask: Mask of _OSC bits to query and the place to put the result into.
+ **/
+acpi_status acpi_pci_osc_control_query(acpi_handle handle, u32 *mask)
+{
+	struct acpi_pci_root *root;
+	acpi_handle tmp;
+	acpi_status status = AE_OK;
+	u32 ctrl = *mask;
+
+	if (!(ctrl & OSC_PCI_CONTROL_MASKS))
+		return AE_BAD_PARAMETER;
+
+	root = acpi_pci_find_root(handle);
+	if (!root)
+		return AE_NOT_EXIST;
+
+	status = acpi_get_handle(handle, "_OSC", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	mutex_lock(&osc_lock);
+
+	*mask |= root->osc_control_set;
+	if ((ctrl & root->osc_control_set) == ctrl)
+		goto out;
+
+	for (ctrl = 0; *mask != ctrl; ctrl = *mask) {
+		status = acpi_pci_query_osc(root, root->osc_support_set, mask);
+		if (ACPI_FAILURE(status))
+			break;
+	}
+
+ out:
+	mutex_unlock(&osc_lock);
+
+	return status;
+}
+EXPORT_SYMBOL(acpi_pci_osc_control_query);
+
 /**
  * acpi_pci_osc_control_set - commit requested control to Firmware
  * @handle: acpi_handle for the target ACPI object
@@ -396,7 +451,7 @@ acpi_status acpi_pci_osc_control_set(acp
 
 	/* Need to query controls first before requesting them */
 	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set);
+		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
 		if (ACPI_FAILURE(status))
 			goto out;
 	}
Index: linux-2.6/include/linux/acpi.h
===================================================================
--- linux-2.6.orig/include/linux/acpi.h
+++ linux-2.6/include/linux/acpi.h
@@ -305,6 +305,7 @@ acpi_status acpi_run_osc(acpi_handle han
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
 
+extern acpi_status acpi_pci_osc_control_query(acpi_handle handle, u32 *mask);
 extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
 extern void acpi_early_init(void);
 


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 1/10] ACPI / PCI: Introduce function for querying PCI root _OSC
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
  2010-08-06  1:05 ` [PATCH 1/10] ACPI / PCI: Introduce function for querying PCI root _OSC Rafael J. Wysocki
@ 2010-08-06  1:05 ` Rafael J. Wysocki
  2010-08-06  1:06 ` [PATCH 2/10] PCI / PCIe/ AER: Introduce pci_aer_available() Rafael J. Wysocki
                   ` (21 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:05 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce a function allowing the caller to obtain a mask of _OSC
control bits the BIOS will allow the kernel to control for a given
PCI root bridge.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   77 +++++++++++++++++++++++++++++++++++++++++-------
 include/linux/acpi.h    |    1 
 2 files changed, 67 insertions(+), 11 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -225,22 +225,35 @@ static acpi_status acpi_pci_run_osc(acpi
 	return status;
 }
 
-static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
+static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root,
+					u32 support,
+					u32 *control)
 {
 	acpi_status status;
-	u32 support_set, result, capbuf[3];
+	u32 result, capbuf[3];
+
+	support &= OSC_PCI_SUPPORT_MASKS;
+	support |= root->osc_support_set;
 
-	/* do _OSC query for all possible controls */
-	support_set = root->osc_support_set | (flags & OSC_PCI_SUPPORT_MASKS);
 	capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
-	capbuf[OSC_SUPPORT_TYPE] = support_set;
-	capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
+	capbuf[OSC_SUPPORT_TYPE] = support;
+	if (control) {
+		*control &= OSC_PCI_CONTROL_MASKS;
+		capbuf[OSC_CONTROL_TYPE] = *control | root->osc_control_set;
+	} else {
+		/* Run _OSC query for all possible controls. */
+		capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
+	}
 
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
-		root->osc_support_set = support_set;
-		root->osc_control_qry = result;
-		root->osc_queried = 1;
+		root->osc_support_set = support;
+		if (control) {
+			*control = result;
+		} else {
+			root->osc_control_qry = result;
+			root->osc_queried = 1;
+		}
 	}
 	return status;
 }
@@ -254,7 +267,7 @@ static acpi_status acpi_pci_osc_support(
 	if (ACPI_FAILURE(status))
 		return status;
 	mutex_lock(&osc_lock);
-	status = acpi_pci_query_osc(root, flags);
+	status = acpi_pci_query_osc(root, flags, NULL);
 	mutex_unlock(&osc_lock);
 	return status;
 }
@@ -363,6 +376,48 @@ out:
 }
 EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
 
+ /**
+ * acpi_pci_osc_control_query - Get the _OSC bits the kernel can control.
+ * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
+ * @mask: Mask of _OSC bits to query and the place to put the result into.
+ **/
+acpi_status acpi_pci_osc_control_query(acpi_handle handle, u32 *mask)
+{
+	struct acpi_pci_root *root;
+	acpi_handle tmp;
+	acpi_status status = AE_OK;
+	u32 ctrl = *mask;
+
+	if (!(ctrl & OSC_PCI_CONTROL_MASKS))
+		return AE_BAD_PARAMETER;
+
+	root = acpi_pci_find_root(handle);
+	if (!root)
+		return AE_NOT_EXIST;
+
+	status = acpi_get_handle(handle, "_OSC", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	mutex_lock(&osc_lock);
+
+	*mask |= root->osc_control_set;
+	if ((ctrl & root->osc_control_set) == ctrl)
+		goto out;
+
+	for (ctrl = 0; *mask != ctrl; ctrl = *mask) {
+		status = acpi_pci_query_osc(root, root->osc_support_set, mask);
+		if (ACPI_FAILURE(status))
+			break;
+	}
+
+ out:
+	mutex_unlock(&osc_lock);
+
+	return status;
+}
+EXPORT_SYMBOL(acpi_pci_osc_control_query);
+
 /**
  * acpi_pci_osc_control_set - commit requested control to Firmware
  * @handle: acpi_handle for the target ACPI object
@@ -396,7 +451,7 @@ acpi_status acpi_pci_osc_control_set(acp
 
 	/* Need to query controls first before requesting them */
 	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set);
+		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
 		if (ACPI_FAILURE(status))
 			goto out;
 	}
Index: linux-2.6/include/linux/acpi.h
===================================================================
--- linux-2.6.orig/include/linux/acpi.h
+++ linux-2.6/include/linux/acpi.h
@@ -305,6 +305,7 @@ acpi_status acpi_run_osc(acpi_handle han
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
 
+extern acpi_status acpi_pci_osc_control_query(acpi_handle handle, u32 *mask);
 extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
 extern void acpi_early_init(void);
 

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 2/10] PCI / PCIe/ AER: Introduce pci_aer_available()
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
  2010-08-06  1:05 ` [PATCH 1/10] ACPI / PCI: Introduce function for querying PCI root _OSC Rafael J. Wysocki
  2010-08-06  1:05 ` Rafael J. Wysocki
@ 2010-08-06  1:06 ` Rafael J. Wysocki
  2010-08-06  1:06 ` Rafael J. Wysocki
                   ` (20 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:06 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce a function allowing the caller to check whether to try to
enable PCIe AER.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/pci/pci.h             |    2 ++
 drivers/pci/pcie/aer/aerdrv.c |    9 ++++++---
 2 files changed, 8 insertions(+), 3 deletions(-)

Index: linux-2.6/drivers/pci/pci.h
===================================================================
--- linux-2.6.orig/drivers/pci/pci.h
+++ linux-2.6/drivers/pci/pci.h
@@ -131,8 +131,10 @@ static inline void pci_msi_init_pci_dev(
 
 #ifdef CONFIG_PCIEAER
 void pci_no_aer(void);
+bool pci_aer_available(void);
 #else
 static inline void pci_no_aer(void) { }
+static inline bool pci_aer_available(void) { return false; }
 #endif
 
 static inline int pci_no_d1d2(struct pci_dev *dev)
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv.c
@@ -72,6 +72,11 @@ void pci_no_aer(void)
 	pcie_aer_disable = 1;	/* has priority over 'forceload' */
 }
 
+bool pci_aer_available(void)
+{
+	return !pcie_aer_disable && pci_msi_enabled();
+}
+
 static int set_device_error_reporting(struct pci_dev *dev, void *data)
 {
 	bool enable = *((bool *)data);
@@ -411,9 +416,7 @@ static void aer_error_resume(struct pci_
  */
 static int __init aer_service_init(void)
 {
-	if (pcie_aer_disable)
-		return -ENXIO;
-	if (!pci_msi_enabled())
+	if (!pci_aer_available())
 		return -ENXIO;
 	return pcie_port_service_register(&aerdriver);
 }

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 2/10] PCI / PCIe/ AER: Introduce pci_aer_available()
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (2 preceding siblings ...)
  2010-08-06  1:06 ` [PATCH 2/10] PCI / PCIe/ AER: Introduce pci_aer_available() Rafael J. Wysocki
@ 2010-08-06  1:06 ` Rafael J. Wysocki
  2010-08-06  1:07 ` [PATCH 3/10] PCI / PCIe: Introduce commad line switch for disabling port services Rafael J. Wysocki
                   ` (19 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:06 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce a function allowing the caller to check whether to try to
enable PCIe AER.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/pci/pci.h             |    2 ++
 drivers/pci/pcie/aer/aerdrv.c |    9 ++++++---
 2 files changed, 8 insertions(+), 3 deletions(-)

Index: linux-2.6/drivers/pci/pci.h
===================================================================
--- linux-2.6.orig/drivers/pci/pci.h
+++ linux-2.6/drivers/pci/pci.h
@@ -131,8 +131,10 @@ static inline void pci_msi_init_pci_dev(
 
 #ifdef CONFIG_PCIEAER
 void pci_no_aer(void);
+bool pci_aer_available(void);
 #else
 static inline void pci_no_aer(void) { }
+static inline bool pci_aer_available(void) { return false; }
 #endif
 
 static inline int pci_no_d1d2(struct pci_dev *dev)
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv.c
@@ -72,6 +72,11 @@ void pci_no_aer(void)
 	pcie_aer_disable = 1;	/* has priority over 'forceload' */
 }
 
+bool pci_aer_available(void)
+{
+	return !pcie_aer_disable && pci_msi_enabled();
+}
+
 static int set_device_error_reporting(struct pci_dev *dev, void *data)
 {
 	bool enable = *((bool *)data);
@@ -411,9 +416,7 @@ static void aer_error_resume(struct pci_
  */
 static int __init aer_service_init(void)
 {
-	if (pcie_aer_disable)
-		return -ENXIO;
-	if (!pci_msi_enabled())
+	if (!pci_aer_available())
 		return -ENXIO;
 	return pcie_port_service_register(&aerdriver);
 }

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 3/10] PCI / PCIe: Introduce commad line switch for disabling port services
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (3 preceding siblings ...)
  2010-08-06  1:06 ` Rafael J. Wysocki
@ 2010-08-06  1:07 ` Rafael J. Wysocki
  2010-08-06  1:07 ` Rafael J. Wysocki
                   ` (18 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:07 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce kernel command line switch pcie_ports= allowing one to
disable all of the native PCIe port services, so that PCIe ports
are treated like PCI-to-PCI bridges.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/kernel-parameters.txt |    4 ++++
 drivers/pci/pcie/portdrv.h          |    2 ++
 drivers/pci/pcie/portdrv_core.c     |    3 +++
 drivers/pci/pcie/portdrv_pci.c      |   15 +++++++++++++++
 4 files changed, 24 insertions(+)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -29,6 +29,18 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
+/* If this switch is set, PCIe port native services should not be enabled. */
+bool pcie_ports_disabled;
+
+static int __init pcie_port_setup(char *str)
+{
+	if (!strncmp(str, "compat", 6))
+		pcie_ports_disabled = true;
+
+	return 1;
+}
+__setup("pcie_ports=", pcie_port_setup);
+
 /* global data */
 
 static int pcie_portdrv_restore_config(struct pci_dev *dev)
@@ -301,6 +313,9 @@ static int __init pcie_portdrv_init(void
 {
 	int retval;
 
+	if (pcie_ports_disabled)
+		return -EACCES;
+
 	dmi_check_system(pcie_portdrv_dmi_table);
 
 	retval = pcie_port_bus_register();
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.orig/Documentation/kernel-parameters.txt
+++ linux-2.6/Documentation/kernel-parameters.txt
@@ -2047,6 +2047,10 @@ and is between 256 and 4096 characters. 
 		force	Enable ASPM even on devices that claim not to support it.
 			WARNING: Forcing ASPM on may cause system lockups.
 
+	pcie_ports=	[PCIE] PCIe ports handling:
+		compat	Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
+			ports driver.
+
 	pcie_pme=	[PCIE,PM] Native PCIe PME signaling options:
 			Format: {auto|force}[,nomsi]
 		auto	Use native PCIe PME signaling if the BIOS allows the
Index: linux-2.6/drivers/pci/pcie/portdrv.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv.h
+++ linux-2.6/drivers/pci/pcie/portdrv.h
@@ -20,6 +20,8 @@
 
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
+extern bool pcie_ports_disabled;
+
 extern struct bus_type pcie_port_bus_type;
 extern int pcie_port_device_register(struct pci_dev *dev);
 #ifdef CONFIG_PM
Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -494,6 +494,9 @@ static void pcie_port_shutdown_service(s
  */
 int pcie_port_service_register(struct pcie_port_service_driver *new)
 {
+	if (pcie_ports_disabled)
+		return -ENODEV;
+
 	new->driver.name = (char *)new->name;
 	new->driver.bus = &pcie_port_bus_type;
 	new->driver.probe = pcie_port_probe_service;

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 3/10] PCI / PCIe: Introduce commad line switch for disabling port services
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (4 preceding siblings ...)
  2010-08-06  1:07 ` [PATCH 3/10] PCI / PCIe: Introduce commad line switch for disabling port services Rafael J. Wysocki
@ 2010-08-06  1:07 ` Rafael J. Wysocki
  2010-08-06  1:08 ` [PATCH 4/10] PCI / PCIe: Ask BIOS for control of all native services at once (v6) Rafael J. Wysocki
                   ` (17 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:07 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce kernel command line switch pcie_ports= allowing one to
disable all of the native PCIe port services, so that PCIe ports
are treated like PCI-to-PCI bridges.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/kernel-parameters.txt |    4 ++++
 drivers/pci/pcie/portdrv.h          |    2 ++
 drivers/pci/pcie/portdrv_core.c     |    3 +++
 drivers/pci/pcie/portdrv_pci.c      |   15 +++++++++++++++
 4 files changed, 24 insertions(+)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -29,6 +29,18 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
+/* If this switch is set, PCIe port native services should not be enabled. */
+bool pcie_ports_disabled;
+
+static int __init pcie_port_setup(char *str)
+{
+	if (!strncmp(str, "compat", 6))
+		pcie_ports_disabled = true;
+
+	return 1;
+}
+__setup("pcie_ports=", pcie_port_setup);
+
 /* global data */
 
 static int pcie_portdrv_restore_config(struct pci_dev *dev)
@@ -301,6 +313,9 @@ static int __init pcie_portdrv_init(void
 {
 	int retval;
 
+	if (pcie_ports_disabled)
+		return -EACCES;
+
 	dmi_check_system(pcie_portdrv_dmi_table);
 
 	retval = pcie_port_bus_register();
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.orig/Documentation/kernel-parameters.txt
+++ linux-2.6/Documentation/kernel-parameters.txt
@@ -2047,6 +2047,10 @@ and is between 256 and 4096 characters. 
 		force	Enable ASPM even on devices that claim not to support it.
 			WARNING: Forcing ASPM on may cause system lockups.
 
+	pcie_ports=	[PCIE] PCIe ports handling:
+		compat	Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
+			ports driver.
+
 	pcie_pme=	[PCIE,PM] Native PCIe PME signaling options:
 			Format: {auto|force}[,nomsi]
 		auto	Use native PCIe PME signaling if the BIOS allows the
Index: linux-2.6/drivers/pci/pcie/portdrv.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv.h
+++ linux-2.6/drivers/pci/pcie/portdrv.h
@@ -20,6 +20,8 @@
 
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
+extern bool pcie_ports_disabled;
+
 extern struct bus_type pcie_port_bus_type;
 extern int pcie_port_device_register(struct pci_dev *dev);
 #ifdef CONFIG_PM
Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -494,6 +494,9 @@ static void pcie_port_shutdown_service(s
  */
 int pcie_port_service_register(struct pcie_port_service_driver *new)
 {
+	if (pcie_ports_disabled)
+		return -ENODEV;
+
 	new->driver.name = (char *)new->name;
 	new->driver.bus = &pcie_port_bus_type;
 	new->driver.probe = pcie_port_probe_service;

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 4/10] PCI / PCIe: Ask BIOS for control of all native services at once (v6)
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (6 preceding siblings ...)
  2010-08-06  1:08 ` [PATCH 4/10] PCI / PCIe: Ask BIOS for control of all native services at once (v6) Rafael J. Wysocki
@ 2010-08-06  1:08 ` Rafael J. Wysocki
  2010-08-06  1:09 ` [PATCH 5/10] PCI / PCIe: Disable PCIe port services during port initialization Rafael J. Wysocki
                   ` (15 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:08 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

PCIe port service drivers ask the BIOS, through _OSC, for control of
the services they handle.  Unfortunately, each of them individually
asks for control of the PCIe capability structure and if that is
granted, some BIOSes expect that the other PCIe port services will be
configured and handled by the kernel as well.  If that is not the
case (eg. one of the PCIe port service drivers is not loaded), the
BIOS may be confused and may cause the system as a whole to misbehave
(eg. on one of such systems enabling the native PCIe PME service
without loading the native PCIe hot-plug service driver causes a
storm of ACPI notify requests to appear).

For this reason rework the PCIe port driver so that (1) it checks
which native PCIe port services can be enabled, according to the
BIOS, and (2) it requests control of all these services
simultaneously.  In particular, this causes pcie_portdrv_probe() to
fail if the BIOS refuses to grant control of the PCIe capability
structure, which means that no native PCIe port services can be
enabled for the PCIe root complex the given port belongs to.

Make it possible to override this behavior using 'pcie_ports=native'
(use the PCIe native services regardless of the BIOS response to the
control request), or 'pcie_ports=compat' (do not use the PCIe native
services at all).

Accordingly, rework the existing PCIe port service drivers so that
they don't request control of the services directly.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/kernel-parameters.txt  |   13 ++---
 drivers/pci/hotplug/acpi_pcihp.c     |    4 -
 drivers/pci/hotplug/pciehp.h         |   12 ----
 drivers/pci/hotplug/pciehp_acpi.c    |    4 -
 drivers/pci/hotplug/pciehp_core.c    |    4 -
 drivers/pci/pcie/Makefile            |    3 -
 drivers/pci/pcie/aer/aerdrv_acpi.c   |   36 --------------
 drivers/pci/pcie/aer/aerdrv_core.c   |   14 -----
 drivers/pci/pcie/pme/Makefile        |    8 ---
 drivers/pci/pcie/pme/pcie_pme.c      |   64 +------------------------
 drivers/pci/pcie/pme/pcie_pme.h      |   28 -----------
 drivers/pci/pcie/pme/pcie_pme_acpi.c |   54 ---------------------
 drivers/pci/pcie/portdrv.h           |   20 +++++++
 drivers/pci/pcie/portdrv_acpi.c      |   89 +++++++++++++++++++++++++++++++++++
 drivers/pci/pcie/portdrv_core.c      |   22 +++++++-
 drivers/pci/pcie/portdrv_pci.c       |   15 +++++
 16 files changed, 160 insertions(+), 230 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -32,10 +32,23 @@ MODULE_LICENSE("GPL");
 /* If this switch is set, PCIe port native services should not be enabled. */
 bool pcie_ports_disabled;
 
+/*
+ * If this switch is set, ACPI _OSC will be used to determine whether or not to
+ * enable PCIe port native services.
+ */
+bool pcie_ports_auto = true;
+
 static int __init pcie_port_setup(char *str)
 {
-	if (!strncmp(str, "compat", 6))
+	if (!strncmp(str, "compat", 6)) {
 		pcie_ports_disabled = true;
+	} else if (!strncmp(str, "native", 6)) {
+		pcie_ports_disabled = false;
+		pcie_ports_auto = false;
+	} else if (!strncmp(str, "auto", 4)) {
+		pcie_ports_disabled = false;
+		pcie_ports_auto = true;
+	}
 
 	return 1;
 }
Index: linux-2.6/drivers/pci/pcie/portdrv_acpi.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/pci/pcie/portdrv_acpi.c
@@ -0,0 +1,89 @@
+/*
+ * PCIe Port Native Services Support, ACPI-Related Part
+ *
+ * Copyright (C) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pcieport_if.h>
+
+#include "aer/aerdrv.h"
+#include "../pci.h"
+
+/**
+ * pcie_port_acpi_setup - Request the BIOS to release control of PCIe services.
+ * @port: PCIe Port service for a root port or event collector.
+ * @srv_mask: Bit mask of services that can be enabled for @port.
+ *
+ * Invoked when @port is identified as a PCIe port device.  To avoid conflicts
+ * with the BIOS PCIe port native services support requires the BIOS to yield
+ * control of these services to the kernel.  The mask of services that the BIOS
+ * allows to be enabled for @port is written to @srv_mask.
+ *
+ * NOTE: It turns out that we cannot do that for individual port services
+ * separately, because that would make some systems work incorrectly.
+ */
+int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
+{
+	acpi_status status;
+	acpi_handle handle;
+	u32 flags;
+
+	if (acpi_pci_disabled)
+		return 0;
+
+	handle = acpi_find_root_bridge_handle(port);
+	if (!handle)
+		return -EINVAL;
+
+	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL
+		| OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
+		| OSC_PCI_EXPRESS_PME_CONTROL;
+
+	if (pci_aer_available()) {
+		if (pcie_aer_get_firmware_first(port))
+			dev_dbg(&port->dev, "PCIe errors handled by BIOS.\n");
+		else
+			flags |= OSC_PCI_EXPRESS_AER_CONTROL;
+	}
+
+	status = acpi_pci_osc_control_query(handle, &flags);
+	if (ACPI_FAILURE(status)) {
+		dev_dbg(&port->dev, "ACPI _OSC query failed (code %d)\n",
+			status);
+		return -ENODEV;
+	}
+
+	if (!(flags & OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)) {
+		dev_dbg(&port->dev, "BIOS refuses to grant control of PCIe "
+			"Capability Structure\n");
+		return -EACCES;
+	}
+
+	status = acpi_pci_osc_control_set(handle, flags);
+	if (ACPI_FAILURE(status)) {
+		dev_dbg(&port->dev, "ACPI _OSC request failed (code %d)\n",
+			status);
+		return -ENODEV;
+	}
+
+	dev_info(&port->dev, "ACPI _OSC control granted for 0x%02x\n", flags);
+
+	*srv_mask = PCIE_PORT_SERVICE_VC;
+	if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_HP;
+	if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_PME;
+	if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_AER;
+
+	return 0;
+}
Index: linux-2.6/drivers/pci/pcie/portdrv.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv.h
+++ linux-2.6/drivers/pci/pcie/portdrv.h
@@ -21,6 +21,7 @@
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
 extern bool pcie_ports_disabled;
+extern bool pcie_ports_auto;
 
 extern struct bus_type pcie_port_bus_type;
 extern int pcie_port_device_register(struct pci_dev *dev);
@@ -32,6 +33,8 @@ extern void pcie_port_device_remove(stru
 extern int __must_check pcie_port_bus_register(void);
 extern void pcie_port_bus_unregister(void);
 
+struct pci_dev;
+
 #ifdef CONFIG_PCIE_PME
 extern bool pcie_pme_msi_disabled;
 
@@ -44,9 +47,26 @@ static inline bool pcie_pme_no_msi(void)
 {
 	return pcie_pme_msi_disabled;
 }
+
+extern void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable);
 #else /* !CONFIG_PCIE_PME */
 static inline void pcie_pme_disable_msi(void) {}
 static inline bool pcie_pme_no_msi(void) { return false; }
+static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
 #endif /* !CONFIG_PCIE_PME */
 
+#ifdef CONFIG_ACPI
+extern int pcie_port_acpi_setup(struct pci_dev *port, int *mask);
+
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+	return pcie_port_acpi_setup(port, mask);
+}
+#else /* !CONFIG_ACPI */
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+	return 0;
+}
+#endif /* !CONFIG_ACPI */
+
 #endif /* _PORTDRV_H_ */
Index: linux-2.6/drivers/pci/pcie/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/Makefile
+++ linux-2.6/drivers/pci/pcie/Makefile
@@ -6,10 +6,11 @@
 obj-$(CONFIG_PCIEASPM)		+= aspm.o
 
 pcieportdrv-y			:= portdrv_core.o portdrv_pci.o portdrv_bus.o
+pcieportdrv-$(CONFIG_ACPI)	+= portdrv_acpi.o
 
 obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv.o
 
 # Build PCI Express AER if needed
 obj-$(CONFIG_PCIEAER)		+= aer/
 
-obj-$(CONFIG_PCIE_PME) += pme/
+obj-$(CONFIG_PCIE_PME) += pme/pcie_pme.o
Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -14,6 +14,7 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/pcieport_if.h>
+#include <linux/aer.h>
 
 #include "../pci.h"
 #include "portdrv.h"
@@ -236,23 +237,38 @@ static int get_port_device_capability(st
 	int services = 0, pos;
 	u16 reg16;
 	u32 reg32;
+	int cap_mask;
+	int err;
+
+	err = pcie_port_platform_notify(dev, &cap_mask);
+	if (pcie_ports_auto) {
+		if (err)
+			return 0;
+	} else {
+		cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
+				| PCIE_PORT_SERVICE_VC;
+		if (pci_aer_available())
+			cap_mask |= PCIE_PORT_SERVICE_AER;
+	}
 
 	pos = pci_pcie_cap(dev);
 	pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
 	/* Hot-Plug Capable */
-	if (reg16 & PCI_EXP_FLAGS_SLOT) {
+	if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
 		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
 		if (reg32 & PCI_EXP_SLTCAP_HPC)
 			services |= PCIE_PORT_SERVICE_HP;
 	}
 	/* AER capable */
-	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
+	if ((cap_mask & PCIE_PORT_SERVICE_AER)
+	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
 		services |= PCIE_PORT_SERVICE_AER;
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
 	/* Root ports are capable of generating PME too */
-	if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+	if ((cap_mask & PCIE_PORT_SERVICE_PME)
+	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
 		services |= PCIE_PORT_SERVICE_PME;
 
 	return services;
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.c
+++ linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
@@ -24,37 +24,12 @@
 #include <linux/pm_runtime.h>
 
 #include "../../pci.h"
-#include "pcie_pme.h"
+#include "../portdrv.h"
 
 #define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
 #define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
 
 /*
- * If set, this switch will prevent the PCIe root port PME service driver from
- * being registered.  Consequently, the interrupt-based PCIe PME signaling will
- * not be used by any PCIe root ports in that case.
- */
-static bool pcie_pme_disabled = true;
-
-/*
- * The PCI Express Base Specification 2.0, Section 6.1.8, states the following:
- * "In order to maintain compatibility with non-PCI Express-aware system
- * software, system power management logic must be configured by firmware to use
- * the legacy mechanism of signaling PME by default.  PCI Express-aware system
- * software must notify the firmware prior to enabling native, interrupt-based
- * PME signaling."  However, if the platform doesn't provide us with a suitable
- * notification mechanism or the notification fails, it is not clear whether or
- * not we are supposed to use the interrupt-based PCIe PME signaling.  The
- * switch below can be used to indicate the desired behaviour.  When set, it
- * will make the kernel use the interrupt-based PCIe PME signaling regardless of
- * the platform notification status, although the kernel will attempt to notify
- * the platform anyway.  When unset, it will prevent the kernel from using the
- * the interrupt-based PCIe PME signaling if the platform notification fails,
- * which is the default.
- */
-static bool pcie_pme_force_enable;
-
-/*
  * If this switch is set, MSI will not be used for PCIe PME signaling.  This
  * causes the PCIe port driver to use INTx interrupts only, but it turns out
  * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
@@ -64,38 +39,13 @@ bool pcie_pme_msi_disabled;
 
 static int __init pcie_pme_setup(char *str)
 {
-	if (!strncmp(str, "auto", 4))
-		pcie_pme_disabled = false;
-	else if (!strncmp(str, "force", 5))
-		pcie_pme_force_enable = true;
-
-	str = strchr(str, ',');
-	if (str) {
-		str++;
-		str += strspn(str, " \t");
-		if (*str && !strcmp(str, "nomsi"))
-			pcie_pme_msi_disabled = true;
-	}
+	if (!strncmp(str, "nomsi", 5))
+		pcie_pme_msi_disabled = true;
 
 	return 1;
 }
 __setup("pcie_pme=", pcie_pme_setup);
 
-/**
- * pcie_pme_platform_setup - Ensure that the kernel controls the PCIe PME.
- * @srv: PCIe PME root port service to use for carrying out the check.
- *
- * Notify the platform that the native PCIe PME is going to be used and return
- * 'true' if the control of the PCIe PME registers has been acquired from the
- * platform.
- */
-static bool pcie_pme_platform_setup(struct pcie_device *srv)
-{
-	if (!pcie_pme_platform_notify(srv))
-		return true;
-	return pcie_pme_force_enable;
-}
-
 struct pcie_pme_service_data {
 	spinlock_t lock;
 	struct pcie_device *srv;
@@ -108,7 +58,7 @@ struct pcie_pme_service_data {
  * @dev: PCIe root port or event collector.
  * @enable: Enable or disable the interrupt.
  */
-static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
+void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
 {
 	int rtctl_pos;
 	u16 rtctl;
@@ -417,9 +367,6 @@ static int pcie_pme_probe(struct pcie_de
 	struct pcie_pme_service_data *data;
 	int ret;
 
-	if (!pcie_pme_platform_setup(srv))
-		return -EACCES;
-
 	data = kzalloc(sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
@@ -509,8 +456,7 @@ static struct pcie_port_service_driver p
  */
 static int __init pcie_pme_service_init(void)
 {
-	return pcie_pme_disabled ?
-		-ENODEV : pcie_port_service_register(&pcie_pme_driver);
+	return pcie_port_service_register(&pcie_pme_driver);
 }
 
 module_init(pcie_pme_service_init);
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * drivers/pci/pcie/pme/pcie_pme.h
- *
- * PCI Express Root Port PME signaling support
- *
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- */
-
-#ifndef _PCIE_PME_H_
-#define _PCIE_PME_H_
-
-struct pcie_device;
-
-#ifdef CONFIG_ACPI
-extern int pcie_pme_acpi_setup(struct pcie_device *srv);
-
-static inline int pcie_pme_platform_notify(struct pcie_device *srv)
-{
-	return pcie_pme_acpi_setup(srv);
-}
-#else /* !CONFIG_ACPI */
-static inline int pcie_pme_platform_notify(struct pcie_device *srv)
-{
-	return 0;
-}
-#endif /* !CONFIG_ACPI */
-
-#endif
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme_acpi.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * PCIe Native PME support, ACPI-related part
- *
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License V2.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/acpi.h>
-#include <linux/pci-acpi.h>
-#include <linux/pcieport_if.h>
-
-/**
- * pcie_pme_acpi_setup - Request the ACPI BIOS to release control over PCIe PME.
- * @srv - PCIe PME service for a root port or event collector.
- *
- * Invoked when the PCIe bus type loads PCIe PME service driver.  To avoid
- * conflict with the BIOS PCIe support requires the BIOS to yield PCIe PME
- * control to the kernel.
- */
-int pcie_pme_acpi_setup(struct pcie_device *srv)
-{
-	acpi_status status = AE_NOT_FOUND;
-	struct pci_dev *port = srv->port;
-	acpi_handle handle;
-	int error = 0;
-
-	if (acpi_pci_disabled)
-		return -ENOSYS;
-
-	dev_info(&port->dev, "Requesting control of PCIe PME from ACPI BIOS\n");
-
-	handle = acpi_find_root_bridge_handle(port);
-	if (!handle)
-		return -EINVAL;
-
-	status = acpi_pci_osc_control_set(handle,
-			OSC_PCI_EXPRESS_PME_CONTROL |
-			OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	if (ACPI_FAILURE(status)) {
-		dev_info(&port->dev,
-			"Failed to receive control of PCIe PME service: %s\n",
-			(status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-			"no _OSC support" : "ACPI _OSC failed");
-		error = -ENODEV;
-	}
-
-	return error;
-}
Index: linux-2.6/drivers/pci/pcie/pme/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for PCI-Express Root Port PME signaling driver
-#
-
-obj-$(CONFIG_PCIE_PME) += pmedriver.o
-
-pmedriver-objs := pcie_pme.o
-pmedriver-$(CONFIG_ACPI) += pcie_pme_acpi.o
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -19,42 +19,6 @@
 #include <acpi/apei.h>
 #include "aerdrv.h"
 
-/**
- * aer_osc_setup - run ACPI _OSC method
- * @pciedev: pcie_device which AER is being enabled on
- *
- * @return: Zero on success. Nonzero otherwise.
- *
- * Invoked when PCIe bus loads AER service driver. To avoid conflict with
- * BIOS AER support requires BIOS to yield AER control to OS native driver.
- **/
-int aer_osc_setup(struct pcie_device *pciedev)
-{
-	acpi_status status = AE_NOT_FOUND;
-	struct pci_dev *pdev = pciedev->port;
-	acpi_handle handle = NULL;
-
-	if (acpi_pci_disabled)
-		return -1;
-
-	handle = acpi_find_root_bridge_handle(pdev);
-	if (handle) {
-		status = acpi_pci_osc_control_set(handle,
-					OSC_PCI_EXPRESS_AER_CONTROL |
-					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	}
-
-	if (ACPI_FAILURE(status)) {
-		dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't "
-			   "init device: %s\n",
-			   (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-			   "no _OSC support" : "_OSC failed");
-		return -1;
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_ACPI_APEI
 static inline int hest_match_pci(struct acpi_hest_aer_common *p,
 				 struct pci_dev *pci)
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_core.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_core.c
@@ -771,22 +771,10 @@ void aer_isr(struct work_struct *work)
  */
 int aer_init(struct pcie_device *dev)
 {
-	if (pcie_aer_get_firmware_first(dev->port)) {
-		dev_printk(KERN_DEBUG, &dev->device,
-			   "PCIe errors handled by platform firmware.\n");
-		goto out;
-	}
-
-	if (aer_osc_setup(dev))
-		goto out;
-
-	return 0;
-out:
 	if (forceload) {
 		dev_printk(KERN_DEBUG, &dev->device,
 			   "aerdrv forceload requested.\n");
 		pcie_aer_force_firmware_first(dev->port, 0);
-		return 0;
 	}
-	return -ENXIO;
+	return 0;
 }
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -338,9 +338,7 @@ int acpi_get_hp_hw_control_from_firmware
 	acpi_handle chandle, handle;
 	struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
 
-	flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
-		  OSC_SHPC_NATIVE_HP_CONTROL |
-		  OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags &= OSC_SHPC_NATIVE_HP_CONTROL;
 	if (!flags) {
 		err("Invalid flags %u specified!\n", flags);
 		return -EINVAL;
Index: linux-2.6/drivers/pci/hotplug/pciehp.h
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp.h
+++ linux-2.6/drivers/pci/hotplug/pciehp.h
@@ -176,19 +176,7 @@ static inline void pciehp_firmware_init(
 {
 	pciehp_acpi_slot_detection_init();
 }
-
-static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
-	int retval;
-	u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
-		     OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	retval = acpi_get_hp_hw_control_from_firmware(dev, flags);
-	if (retval)
-		return retval;
-	return pciehp_acpi_slot_detection_check(dev);
-}
 #else
 #define pciehp_firmware_init()				do {} while (0)
-#define pciehp_get_hp_hw_control_from_firmware(dev) 	0
 #endif 				/* CONFIG_ACPI */
 #endif				/* _PCIEHP_H */
Index: linux-2.6/drivers/pci/hotplug/pciehp_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp_core.c
+++ linux-2.6/drivers/pci/hotplug/pciehp_core.c
@@ -59,7 +59,7 @@ module_param(pciehp_force, bool, 0644);
 MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
 MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
 MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
-MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
+MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
 
 #define PCIE_MODULE_NAME "pciehp"
 
@@ -235,7 +235,7 @@ static int pciehp_probe(struct pcie_devi
 		dev_info(&dev->device,
 			 "Bypassing BIOS check for pciehp use on %s\n",
 			 pci_name(dev->port));
-	else if (pciehp_get_hp_hw_control_from_firmware(dev->port))
+	else if (pciehp_acpi_slot_detection_check(dev->port))
 		goto err_out_none;
 
 	ctrl = pcie_init(dev);
Index: linux-2.6/drivers/pci/hotplug/pciehp_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp_acpi.c
+++ linux-2.6/drivers/pci/hotplug/pciehp_acpi.c
@@ -85,9 +85,7 @@ static int __init dummy_probe(struct pci
 	acpi_handle handle;
 	struct dummy_slot *slot, *tmp;
 	struct pci_dev *pdev = dev->port;
-	/* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
-	if (pciehp_get_hp_hw_control_from_firmware(pdev))
-		return -ENODEV;
+
 	pos = pci_pcie_cap(pdev);
 	if (!pos)
 		return -ENODEV;
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.orig/Documentation/kernel-parameters.txt
+++ linux-2.6/Documentation/kernel-parameters.txt
@@ -2048,18 +2048,17 @@ and is between 256 and 4096 characters. 
 			WARNING: Forcing ASPM on may cause system lockups.
 
 	pcie_ports=	[PCIE] PCIe ports handling:
+		auto	Ask the BIOS whether or not to use native PCIe services
+			associated with PCIe ports (PME, hot-plug, AER).  Use
+			them only if that is allowed by the BIOS.
+		native	Use native PCIe services associated with PCIe ports
+			unconditionally.
 		compat	Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
 			ports driver.
 
 	pcie_pme=	[PCIE,PM] Native PCIe PME signaling options:
-			Format: {auto|force}[,nomsi]
-		auto	Use native PCIe PME signaling if the BIOS allows the
-			kernel to control PCIe config registers of root ports.
-		force	Use native PCIe PME signaling even if the BIOS refuses
-			to allow the kernel to control the relevant PCIe config
-			registers.
 		nomsi	Do not use MSI for native PCIe PME signaling (this makes
-			all PCIe root ports use INTx for everything).
+			all PCIe root ports use INTx for all services).
 
 	pcmv=		[HW,PCMCIA] BadgePAD 4
 

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 4/10] PCI / PCIe: Ask BIOS for control of all native services at once (v6)
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (5 preceding siblings ...)
  2010-08-06  1:07 ` Rafael J. Wysocki
@ 2010-08-06  1:08 ` Rafael J. Wysocki
  2010-08-06  1:08 ` Rafael J. Wysocki
                   ` (16 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:08 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

PCIe port service drivers ask the BIOS, through _OSC, for control of
the services they handle.  Unfortunately, each of them individually
asks for control of the PCIe capability structure and if that is
granted, some BIOSes expect that the other PCIe port services will be
configured and handled by the kernel as well.  If that is not the
case (eg. one of the PCIe port service drivers is not loaded), the
BIOS may be confused and may cause the system as a whole to misbehave
(eg. on one of such systems enabling the native PCIe PME service
without loading the native PCIe hot-plug service driver causes a
storm of ACPI notify requests to appear).

For this reason rework the PCIe port driver so that (1) it checks
which native PCIe port services can be enabled, according to the
BIOS, and (2) it requests control of all these services
simultaneously.  In particular, this causes pcie_portdrv_probe() to
fail if the BIOS refuses to grant control of the PCIe capability
structure, which means that no native PCIe port services can be
enabled for the PCIe root complex the given port belongs to.

Make it possible to override this behavior using 'pcie_ports=native'
(use the PCIe native services regardless of the BIOS response to the
control request), or 'pcie_ports=compat' (do not use the PCIe native
services at all).

Accordingly, rework the existing PCIe port service drivers so that
they don't request control of the services directly.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/kernel-parameters.txt  |   13 ++---
 drivers/pci/hotplug/acpi_pcihp.c     |    4 -
 drivers/pci/hotplug/pciehp.h         |   12 ----
 drivers/pci/hotplug/pciehp_acpi.c    |    4 -
 drivers/pci/hotplug/pciehp_core.c    |    4 -
 drivers/pci/pcie/Makefile            |    3 -
 drivers/pci/pcie/aer/aerdrv_acpi.c   |   36 --------------
 drivers/pci/pcie/aer/aerdrv_core.c   |   14 -----
 drivers/pci/pcie/pme/Makefile        |    8 ---
 drivers/pci/pcie/pme/pcie_pme.c      |   64 +------------------------
 drivers/pci/pcie/pme/pcie_pme.h      |   28 -----------
 drivers/pci/pcie/pme/pcie_pme_acpi.c |   54 ---------------------
 drivers/pci/pcie/portdrv.h           |   20 +++++++
 drivers/pci/pcie/portdrv_acpi.c      |   89 +++++++++++++++++++++++++++++++++++
 drivers/pci/pcie/portdrv_core.c      |   22 +++++++-
 drivers/pci/pcie/portdrv_pci.c       |   15 +++++
 16 files changed, 160 insertions(+), 230 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -32,10 +32,23 @@ MODULE_LICENSE("GPL");
 /* If this switch is set, PCIe port native services should not be enabled. */
 bool pcie_ports_disabled;
 
+/*
+ * If this switch is set, ACPI _OSC will be used to determine whether or not to
+ * enable PCIe port native services.
+ */
+bool pcie_ports_auto = true;
+
 static int __init pcie_port_setup(char *str)
 {
-	if (!strncmp(str, "compat", 6))
+	if (!strncmp(str, "compat", 6)) {
 		pcie_ports_disabled = true;
+	} else if (!strncmp(str, "native", 6)) {
+		pcie_ports_disabled = false;
+		pcie_ports_auto = false;
+	} else if (!strncmp(str, "auto", 4)) {
+		pcie_ports_disabled = false;
+		pcie_ports_auto = true;
+	}
 
 	return 1;
 }
Index: linux-2.6/drivers/pci/pcie/portdrv_acpi.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/pci/pcie/portdrv_acpi.c
@@ -0,0 +1,89 @@
+/*
+ * PCIe Port Native Services Support, ACPI-Related Part
+ *
+ * Copyright (C) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pcieport_if.h>
+
+#include "aer/aerdrv.h"
+#include "../pci.h"
+
+/**
+ * pcie_port_acpi_setup - Request the BIOS to release control of PCIe services.
+ * @port: PCIe Port service for a root port or event collector.
+ * @srv_mask: Bit mask of services that can be enabled for @port.
+ *
+ * Invoked when @port is identified as a PCIe port device.  To avoid conflicts
+ * with the BIOS PCIe port native services support requires the BIOS to yield
+ * control of these services to the kernel.  The mask of services that the BIOS
+ * allows to be enabled for @port is written to @srv_mask.
+ *
+ * NOTE: It turns out that we cannot do that for individual port services
+ * separately, because that would make some systems work incorrectly.
+ */
+int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
+{
+	acpi_status status;
+	acpi_handle handle;
+	u32 flags;
+
+	if (acpi_pci_disabled)
+		return 0;
+
+	handle = acpi_find_root_bridge_handle(port);
+	if (!handle)
+		return -EINVAL;
+
+	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL
+		| OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
+		| OSC_PCI_EXPRESS_PME_CONTROL;
+
+	if (pci_aer_available()) {
+		if (pcie_aer_get_firmware_first(port))
+			dev_dbg(&port->dev, "PCIe errors handled by BIOS.\n");
+		else
+			flags |= OSC_PCI_EXPRESS_AER_CONTROL;
+	}
+
+	status = acpi_pci_osc_control_query(handle, &flags);
+	if (ACPI_FAILURE(status)) {
+		dev_dbg(&port->dev, "ACPI _OSC query failed (code %d)\n",
+			status);
+		return -ENODEV;
+	}
+
+	if (!(flags & OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)) {
+		dev_dbg(&port->dev, "BIOS refuses to grant control of PCIe "
+			"Capability Structure\n");
+		return -EACCES;
+	}
+
+	status = acpi_pci_osc_control_set(handle, flags);
+	if (ACPI_FAILURE(status)) {
+		dev_dbg(&port->dev, "ACPI _OSC request failed (code %d)\n",
+			status);
+		return -ENODEV;
+	}
+
+	dev_info(&port->dev, "ACPI _OSC control granted for 0x%02x\n", flags);
+
+	*srv_mask = PCIE_PORT_SERVICE_VC;
+	if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_HP;
+	if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_PME;
+	if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_AER;
+
+	return 0;
+}
Index: linux-2.6/drivers/pci/pcie/portdrv.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv.h
+++ linux-2.6/drivers/pci/pcie/portdrv.h
@@ -21,6 +21,7 @@
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
 extern bool pcie_ports_disabled;
+extern bool pcie_ports_auto;
 
 extern struct bus_type pcie_port_bus_type;
 extern int pcie_port_device_register(struct pci_dev *dev);
@@ -32,6 +33,8 @@ extern void pcie_port_device_remove(stru
 extern int __must_check pcie_port_bus_register(void);
 extern void pcie_port_bus_unregister(void);
 
+struct pci_dev;
+
 #ifdef CONFIG_PCIE_PME
 extern bool pcie_pme_msi_disabled;
 
@@ -44,9 +47,26 @@ static inline bool pcie_pme_no_msi(void)
 {
 	return pcie_pme_msi_disabled;
 }
+
+extern void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable);
 #else /* !CONFIG_PCIE_PME */
 static inline void pcie_pme_disable_msi(void) {}
 static inline bool pcie_pme_no_msi(void) { return false; }
+static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
 #endif /* !CONFIG_PCIE_PME */
 
+#ifdef CONFIG_ACPI
+extern int pcie_port_acpi_setup(struct pci_dev *port, int *mask);
+
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+	return pcie_port_acpi_setup(port, mask);
+}
+#else /* !CONFIG_ACPI */
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+	return 0;
+}
+#endif /* !CONFIG_ACPI */
+
 #endif /* _PORTDRV_H_ */
Index: linux-2.6/drivers/pci/pcie/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/Makefile
+++ linux-2.6/drivers/pci/pcie/Makefile
@@ -6,10 +6,11 @@
 obj-$(CONFIG_PCIEASPM)		+= aspm.o
 
 pcieportdrv-y			:= portdrv_core.o portdrv_pci.o portdrv_bus.o
+pcieportdrv-$(CONFIG_ACPI)	+= portdrv_acpi.o
 
 obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv.o
 
 # Build PCI Express AER if needed
 obj-$(CONFIG_PCIEAER)		+= aer/
 
-obj-$(CONFIG_PCIE_PME) += pme/
+obj-$(CONFIG_PCIE_PME) += pme/pcie_pme.o
Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -14,6 +14,7 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/pcieport_if.h>
+#include <linux/aer.h>
 
 #include "../pci.h"
 #include "portdrv.h"
@@ -236,23 +237,38 @@ static int get_port_device_capability(st
 	int services = 0, pos;
 	u16 reg16;
 	u32 reg32;
+	int cap_mask;
+	int err;
+
+	err = pcie_port_platform_notify(dev, &cap_mask);
+	if (pcie_ports_auto) {
+		if (err)
+			return 0;
+	} else {
+		cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
+				| PCIE_PORT_SERVICE_VC;
+		if (pci_aer_available())
+			cap_mask |= PCIE_PORT_SERVICE_AER;
+	}
 
 	pos = pci_pcie_cap(dev);
 	pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
 	/* Hot-Plug Capable */
-	if (reg16 & PCI_EXP_FLAGS_SLOT) {
+	if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
 		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
 		if (reg32 & PCI_EXP_SLTCAP_HPC)
 			services |= PCIE_PORT_SERVICE_HP;
 	}
 	/* AER capable */
-	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
+	if ((cap_mask & PCIE_PORT_SERVICE_AER)
+	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
 		services |= PCIE_PORT_SERVICE_AER;
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
 	/* Root ports are capable of generating PME too */
-	if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+	if ((cap_mask & PCIE_PORT_SERVICE_PME)
+	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
 		services |= PCIE_PORT_SERVICE_PME;
 
 	return services;
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.c
+++ linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
@@ -24,37 +24,12 @@
 #include <linux/pm_runtime.h>
 
 #include "../../pci.h"
-#include "pcie_pme.h"
+#include "../portdrv.h"
 
 #define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
 #define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
 
 /*
- * If set, this switch will prevent the PCIe root port PME service driver from
- * being registered.  Consequently, the interrupt-based PCIe PME signaling will
- * not be used by any PCIe root ports in that case.
- */
-static bool pcie_pme_disabled = true;
-
-/*
- * The PCI Express Base Specification 2.0, Section 6.1.8, states the following:
- * "In order to maintain compatibility with non-PCI Express-aware system
- * software, system power management logic must be configured by firmware to use
- * the legacy mechanism of signaling PME by default.  PCI Express-aware system
- * software must notify the firmware prior to enabling native, interrupt-based
- * PME signaling."  However, if the platform doesn't provide us with a suitable
- * notification mechanism or the notification fails, it is not clear whether or
- * not we are supposed to use the interrupt-based PCIe PME signaling.  The
- * switch below can be used to indicate the desired behaviour.  When set, it
- * will make the kernel use the interrupt-based PCIe PME signaling regardless of
- * the platform notification status, although the kernel will attempt to notify
- * the platform anyway.  When unset, it will prevent the kernel from using the
- * the interrupt-based PCIe PME signaling if the platform notification fails,
- * which is the default.
- */
-static bool pcie_pme_force_enable;
-
-/*
  * If this switch is set, MSI will not be used for PCIe PME signaling.  This
  * causes the PCIe port driver to use INTx interrupts only, but it turns out
  * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
@@ -64,38 +39,13 @@ bool pcie_pme_msi_disabled;
 
 static int __init pcie_pme_setup(char *str)
 {
-	if (!strncmp(str, "auto", 4))
-		pcie_pme_disabled = false;
-	else if (!strncmp(str, "force", 5))
-		pcie_pme_force_enable = true;
-
-	str = strchr(str, ',');
-	if (str) {
-		str++;
-		str += strspn(str, " \t");
-		if (*str && !strcmp(str, "nomsi"))
-			pcie_pme_msi_disabled = true;
-	}
+	if (!strncmp(str, "nomsi", 5))
+		pcie_pme_msi_disabled = true;
 
 	return 1;
 }
 __setup("pcie_pme=", pcie_pme_setup);
 
-/**
- * pcie_pme_platform_setup - Ensure that the kernel controls the PCIe PME.
- * @srv: PCIe PME root port service to use for carrying out the check.
- *
- * Notify the platform that the native PCIe PME is going to be used and return
- * 'true' if the control of the PCIe PME registers has been acquired from the
- * platform.
- */
-static bool pcie_pme_platform_setup(struct pcie_device *srv)
-{
-	if (!pcie_pme_platform_notify(srv))
-		return true;
-	return pcie_pme_force_enable;
-}
-
 struct pcie_pme_service_data {
 	spinlock_t lock;
 	struct pcie_device *srv;
@@ -108,7 +58,7 @@ struct pcie_pme_service_data {
  * @dev: PCIe root port or event collector.
  * @enable: Enable or disable the interrupt.
  */
-static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
+void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
 {
 	int rtctl_pos;
 	u16 rtctl;
@@ -417,9 +367,6 @@ static int pcie_pme_probe(struct pcie_de
 	struct pcie_pme_service_data *data;
 	int ret;
 
-	if (!pcie_pme_platform_setup(srv))
-		return -EACCES;
-
 	data = kzalloc(sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
@@ -509,8 +456,7 @@ static struct pcie_port_service_driver p
  */
 static int __init pcie_pme_service_init(void)
 {
-	return pcie_pme_disabled ?
-		-ENODEV : pcie_port_service_register(&pcie_pme_driver);
+	return pcie_port_service_register(&pcie_pme_driver);
 }
 
 module_init(pcie_pme_service_init);
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * drivers/pci/pcie/pme/pcie_pme.h
- *
- * PCI Express Root Port PME signaling support
- *
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- */
-
-#ifndef _PCIE_PME_H_
-#define _PCIE_PME_H_
-
-struct pcie_device;
-
-#ifdef CONFIG_ACPI
-extern int pcie_pme_acpi_setup(struct pcie_device *srv);
-
-static inline int pcie_pme_platform_notify(struct pcie_device *srv)
-{
-	return pcie_pme_acpi_setup(srv);
-}
-#else /* !CONFIG_ACPI */
-static inline int pcie_pme_platform_notify(struct pcie_device *srv)
-{
-	return 0;
-}
-#endif /* !CONFIG_ACPI */
-
-#endif
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme_acpi.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * PCIe Native PME support, ACPI-related part
- *
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License V2.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/acpi.h>
-#include <linux/pci-acpi.h>
-#include <linux/pcieport_if.h>
-
-/**
- * pcie_pme_acpi_setup - Request the ACPI BIOS to release control over PCIe PME.
- * @srv - PCIe PME service for a root port or event collector.
- *
- * Invoked when the PCIe bus type loads PCIe PME service driver.  To avoid
- * conflict with the BIOS PCIe support requires the BIOS to yield PCIe PME
- * control to the kernel.
- */
-int pcie_pme_acpi_setup(struct pcie_device *srv)
-{
-	acpi_status status = AE_NOT_FOUND;
-	struct pci_dev *port = srv->port;
-	acpi_handle handle;
-	int error = 0;
-
-	if (acpi_pci_disabled)
-		return -ENOSYS;
-
-	dev_info(&port->dev, "Requesting control of PCIe PME from ACPI BIOS\n");
-
-	handle = acpi_find_root_bridge_handle(port);
-	if (!handle)
-		return -EINVAL;
-
-	status = acpi_pci_osc_control_set(handle,
-			OSC_PCI_EXPRESS_PME_CONTROL |
-			OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	if (ACPI_FAILURE(status)) {
-		dev_info(&port->dev,
-			"Failed to receive control of PCIe PME service: %s\n",
-			(status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-			"no _OSC support" : "ACPI _OSC failed");
-		error = -ENODEV;
-	}
-
-	return error;
-}
Index: linux-2.6/drivers/pci/pcie/pme/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for PCI-Express Root Port PME signaling driver
-#
-
-obj-$(CONFIG_PCIE_PME) += pmedriver.o
-
-pmedriver-objs := pcie_pme.o
-pmedriver-$(CONFIG_ACPI) += pcie_pme_acpi.o
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -19,42 +19,6 @@
 #include <acpi/apei.h>
 #include "aerdrv.h"
 
-/**
- * aer_osc_setup - run ACPI _OSC method
- * @pciedev: pcie_device which AER is being enabled on
- *
- * @return: Zero on success. Nonzero otherwise.
- *
- * Invoked when PCIe bus loads AER service driver. To avoid conflict with
- * BIOS AER support requires BIOS to yield AER control to OS native driver.
- **/
-int aer_osc_setup(struct pcie_device *pciedev)
-{
-	acpi_status status = AE_NOT_FOUND;
-	struct pci_dev *pdev = pciedev->port;
-	acpi_handle handle = NULL;
-
-	if (acpi_pci_disabled)
-		return -1;
-
-	handle = acpi_find_root_bridge_handle(pdev);
-	if (handle) {
-		status = acpi_pci_osc_control_set(handle,
-					OSC_PCI_EXPRESS_AER_CONTROL |
-					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	}
-
-	if (ACPI_FAILURE(status)) {
-		dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't "
-			   "init device: %s\n",
-			   (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-			   "no _OSC support" : "_OSC failed");
-		return -1;
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_ACPI_APEI
 static inline int hest_match_pci(struct acpi_hest_aer_common *p,
 				 struct pci_dev *pci)
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_core.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_core.c
@@ -771,22 +771,10 @@ void aer_isr(struct work_struct *work)
  */
 int aer_init(struct pcie_device *dev)
 {
-	if (pcie_aer_get_firmware_first(dev->port)) {
-		dev_printk(KERN_DEBUG, &dev->device,
-			   "PCIe errors handled by platform firmware.\n");
-		goto out;
-	}
-
-	if (aer_osc_setup(dev))
-		goto out;
-
-	return 0;
-out:
 	if (forceload) {
 		dev_printk(KERN_DEBUG, &dev->device,
 			   "aerdrv forceload requested.\n");
 		pcie_aer_force_firmware_first(dev->port, 0);
-		return 0;
 	}
-	return -ENXIO;
+	return 0;
 }
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -338,9 +338,7 @@ int acpi_get_hp_hw_control_from_firmware
 	acpi_handle chandle, handle;
 	struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
 
-	flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
-		  OSC_SHPC_NATIVE_HP_CONTROL |
-		  OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags &= OSC_SHPC_NATIVE_HP_CONTROL;
 	if (!flags) {
 		err("Invalid flags %u specified!\n", flags);
 		return -EINVAL;
Index: linux-2.6/drivers/pci/hotplug/pciehp.h
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp.h
+++ linux-2.6/drivers/pci/hotplug/pciehp.h
@@ -176,19 +176,7 @@ static inline void pciehp_firmware_init(
 {
 	pciehp_acpi_slot_detection_init();
 }
-
-static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
-	int retval;
-	u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
-		     OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	retval = acpi_get_hp_hw_control_from_firmware(dev, flags);
-	if (retval)
-		return retval;
-	return pciehp_acpi_slot_detection_check(dev);
-}
 #else
 #define pciehp_firmware_init()				do {} while (0)
-#define pciehp_get_hp_hw_control_from_firmware(dev) 	0
 #endif 				/* CONFIG_ACPI */
 #endif				/* _PCIEHP_H */
Index: linux-2.6/drivers/pci/hotplug/pciehp_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp_core.c
+++ linux-2.6/drivers/pci/hotplug/pciehp_core.c
@@ -59,7 +59,7 @@ module_param(pciehp_force, bool, 0644);
 MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
 MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
 MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
-MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
+MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
 
 #define PCIE_MODULE_NAME "pciehp"
 
@@ -235,7 +235,7 @@ static int pciehp_probe(struct pcie_devi
 		dev_info(&dev->device,
 			 "Bypassing BIOS check for pciehp use on %s\n",
 			 pci_name(dev->port));
-	else if (pciehp_get_hp_hw_control_from_firmware(dev->port))
+	else if (pciehp_acpi_slot_detection_check(dev->port))
 		goto err_out_none;
 
 	ctrl = pcie_init(dev);
Index: linux-2.6/drivers/pci/hotplug/pciehp_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp_acpi.c
+++ linux-2.6/drivers/pci/hotplug/pciehp_acpi.c
@@ -85,9 +85,7 @@ static int __init dummy_probe(struct pci
 	acpi_handle handle;
 	struct dummy_slot *slot, *tmp;
 	struct pci_dev *pdev = dev->port;
-	/* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
-	if (pciehp_get_hp_hw_control_from_firmware(pdev))
-		return -ENODEV;
+
 	pos = pci_pcie_cap(pdev);
 	if (!pos)
 		return -ENODEV;
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.orig/Documentation/kernel-parameters.txt
+++ linux-2.6/Documentation/kernel-parameters.txt
@@ -2048,18 +2048,17 @@ and is between 256 and 4096 characters. 
 			WARNING: Forcing ASPM on may cause system lockups.
 
 	pcie_ports=	[PCIE] PCIe ports handling:
+		auto	Ask the BIOS whether or not to use native PCIe services
+			associated with PCIe ports (PME, hot-plug, AER).  Use
+			them only if that is allowed by the BIOS.
+		native	Use native PCIe services associated with PCIe ports
+			unconditionally.
 		compat	Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
 			ports driver.
 
 	pcie_pme=	[PCIE,PM] Native PCIe PME signaling options:
-			Format: {auto|force}[,nomsi]
-		auto	Use native PCIe PME signaling if the BIOS allows the
-			kernel to control PCIe config registers of root ports.
-		force	Use native PCIe PME signaling even if the BIOS refuses
-			to allow the kernel to control the relevant PCIe config
-			registers.
 		nomsi	Do not use MSI for native PCIe PME signaling (this makes
-			all PCIe root ports use INTx for everything).
+			all PCIe root ports use INTx for all services).
 
 	pcmv=		[HW,PCMCIA] BadgePAD 4
 

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 5/10] PCI / PCIe: Disable PCIe port services during port initialization
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (7 preceding siblings ...)
  2010-08-06  1:08 ` Rafael J. Wysocki
@ 2010-08-06  1:09 ` Rafael J. Wysocki
  2010-08-06  1:09 ` Rafael J. Wysocki
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:09 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

In principle PCIe port services may be enabled by the BIOS, so it's
better to disable them during port initialization to avoid spurious
events from being generated.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/pci/pcie/portdrv_core.c |   29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -256,20 +256,43 @@ static int get_port_device_capability(st
 	/* Hot-Plug Capable */
 	if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
 		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
-		if (reg32 & PCI_EXP_SLTCAP_HPC)
+		if (reg32 & PCI_EXP_SLTCAP_HPC) {
 			services |= PCIE_PORT_SERVICE_HP;
+			/*
+			 * Disable hot-plug interrupts in case they have been
+			 * enabled by the BIOS and the hot-plug service driver
+			 * is not loaded.
+			 */
+			pos += PCI_EXP_SLTCTL;
+			pci_read_config_word(dev, pos, &reg16);
+			reg16 &= ~(PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
+			pci_write_config_word(dev, pos, reg16);
+		}
 	}
 	/* AER capable */
 	if ((cap_mask & PCIE_PORT_SERVICE_AER)
-	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
+	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
 		services |= PCIE_PORT_SERVICE_AER;
+		/*
+		 * Disable AER on this port in case it's been enabled by the
+		 * BIOS (the AER service driver will enable it when necessary).
+		 */
+		pci_disable_pcie_error_reporting(dev);
+	}
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
 	/* Root ports are capable of generating PME too */
 	if ((cap_mask & PCIE_PORT_SERVICE_PME)
-	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
 		services |= PCIE_PORT_SERVICE_PME;
+		/*
+		 * Disable PME interrupt on this port in case it's been enabled
+		 * by the BIOS (the PME service driver will enable it when
+		 * necessary).
+		 */
+		pcie_pme_interrupt_enable(dev, false);
+	}
 
 	return services;
 }

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 5/10] PCI / PCIe: Disable PCIe port services during port initialization
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (8 preceding siblings ...)
  2010-08-06  1:09 ` [PATCH 5/10] PCI / PCIe: Disable PCIe port services during port initialization Rafael J. Wysocki
@ 2010-08-06  1:09 ` Rafael J. Wysocki
  2010-08-06  1:10 ` [PATCH 6/10] PCI / PCIe: Remove the port driver module exit routine Rafael J. Wysocki
                   ` (13 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:09 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

In principle PCIe port services may be enabled by the BIOS, so it's
better to disable them during port initialization to avoid spurious
events from being generated.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/pci/pcie/portdrv_core.c |   29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -256,20 +256,43 @@ static int get_port_device_capability(st
 	/* Hot-Plug Capable */
 	if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
 		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
-		if (reg32 & PCI_EXP_SLTCAP_HPC)
+		if (reg32 & PCI_EXP_SLTCAP_HPC) {
 			services |= PCIE_PORT_SERVICE_HP;
+			/*
+			 * Disable hot-plug interrupts in case they have been
+			 * enabled by the BIOS and the hot-plug service driver
+			 * is not loaded.
+			 */
+			pos += PCI_EXP_SLTCTL;
+			pci_read_config_word(dev, pos, &reg16);
+			reg16 &= ~(PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
+			pci_write_config_word(dev, pos, reg16);
+		}
 	}
 	/* AER capable */
 	if ((cap_mask & PCIE_PORT_SERVICE_AER)
-	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
+	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
 		services |= PCIE_PORT_SERVICE_AER;
+		/*
+		 * Disable AER on this port in case it's been enabled by the
+		 * BIOS (the AER service driver will enable it when necessary).
+		 */
+		pci_disable_pcie_error_reporting(dev);
+	}
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
 	/* Root ports are capable of generating PME too */
 	if ((cap_mask & PCIE_PORT_SERVICE_PME)
-	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
 		services |= PCIE_PORT_SERVICE_PME;
+		/*
+		 * Disable PME interrupt on this port in case it's been enabled
+		 * by the BIOS (the PME service driver will enable it when
+		 * necessary).
+		 */
+		pcie_pme_interrupt_enable(dev, false);
+	}
 
 	return services;
 }

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 6/10] PCI / PCIe: Remove the port driver module exit routine
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (10 preceding siblings ...)
  2010-08-06  1:10 ` [PATCH 6/10] PCI / PCIe: Remove the port driver module exit routine Rafael J. Wysocki
@ 2010-08-06  1:10 ` Rafael J. Wysocki
  2010-08-06  1:11 ` [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls Rafael J. Wysocki
                   ` (11 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:10 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>

The PCIe port driver's module exit routine is never used, so drop it.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/pci/pcie/portdrv_pci.c |    7 -------
 1 file changed, 7 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -343,11 +343,4 @@ static int __init pcie_portdrv_init(void
 	return retval;
 }
 
-static void __exit pcie_portdrv_exit(void)
-{
-	pci_unregister_driver(&pcie_portdriver);
-	pcie_port_bus_unregister();
-}
-
 module_init(pcie_portdrv_init);
-module_exit(pcie_portdrv_exit);


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 6/10] PCI / PCIe: Remove the port driver module exit routine
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (9 preceding siblings ...)
  2010-08-06  1:09 ` Rafael J. Wysocki
@ 2010-08-06  1:10 ` Rafael J. Wysocki
  2010-08-06  1:10 ` Rafael J. Wysocki
                   ` (12 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:10 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>

The PCIe port driver's module exit routine is never used, so drop it.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/pci/pcie/portdrv_pci.c |    7 -------
 1 file changed, 7 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -343,11 +343,4 @@ static int __init pcie_portdrv_init(void
 	return retval;
 }
 
-static void __exit pcie_portdrv_exit(void)
-{
-	pci_unregister_driver(&pcie_portdriver);
-	pcie_port_bus_unregister();
-}
-
 module_init(pcie_portdrv_init);
-module_exit(pcie_portdrv_exit);

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (11 preceding siblings ...)
  2010-08-06  1:10 ` Rafael J. Wysocki
@ 2010-08-06  1:11 ` Rafael J. Wysocki
  2010-08-06  2:20   ` Hidetoshi Seto
  2010-08-06  2:20   ` Hidetoshi Seto
  2010-08-06  1:11 ` Rafael J. Wysocki
                   ` (10 subsequent siblings)
  23 siblings, 2 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:11 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

It generally is unsafe to call acpi_pci_osc_control_set() without
checking what _OSC bits the BIOS is willing to grant control of,
because control of some _OSC bits may be granted even if
acpi_pci_osc_control_set() returns AE_SUPPORT.  Therefore make
acpi_get_hp_hw_control_from_firmware() use
acpi_pci_osc_control_query() to check if the BIOS will grant
OSC_SHPC_NATIVE_HP_CONTROL before calling acpi_pci_osc_control_set().

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/pci/hotplug/acpi_pcihp.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -358,11 +358,13 @@ int acpi_get_hp_hw_control_from_firmware
 		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
 		dbg("Trying to get hotplug control for %s\n",
 				(char *)string.pointer);
+		status = acpi_pci_osc_control_query(handle, &flags);
+		if (ACPI_FAILURE(status)
+		    || !(flags & OSC_SHPC_NATIVE_HP_CONTROL))
+			goto no_control;
 		status = acpi_pci_osc_control_set(handle, flags);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
-		if (status == AE_SUPPORT)
-			goto no_control;
 		kfree(string.pointer);
 		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
 	}


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (12 preceding siblings ...)
  2010-08-06  1:11 ` [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls Rafael J. Wysocki
@ 2010-08-06  1:11 ` Rafael J. Wysocki
  2010-08-06  1:12 ` [PATCH 8/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v2) Rafael J. Wysocki
                   ` (9 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:11 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

It generally is unsafe to call acpi_pci_osc_control_set() without
checking what _OSC bits the BIOS is willing to grant control of,
because control of some _OSC bits may be granted even if
acpi_pci_osc_control_set() returns AE_SUPPORT.  Therefore make
acpi_get_hp_hw_control_from_firmware() use
acpi_pci_osc_control_query() to check if the BIOS will grant
OSC_SHPC_NATIVE_HP_CONTROL before calling acpi_pci_osc_control_set().

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/pci/hotplug/acpi_pcihp.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -358,11 +358,13 @@ int acpi_get_hp_hw_control_from_firmware
 		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
 		dbg("Trying to get hotplug control for %s\n",
 				(char *)string.pointer);
+		status = acpi_pci_osc_control_query(handle, &flags);
+		if (ACPI_FAILURE(status)
+		    || !(flags & OSC_SHPC_NATIVE_HP_CONTROL))
+			goto no_control;
 		status = acpi_pci_osc_control_set(handle, flags);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
-		if (status == AE_SUPPORT)
-			goto no_control;
 		kfree(string.pointer);
 		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
 	}

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 8/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v2)
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (14 preceding siblings ...)
  2010-08-06  1:12 ` [PATCH 8/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v2) Rafael J. Wysocki
@ 2010-08-06  1:12 ` Rafael J. Wysocki
  2010-08-06  1:13 ` [PATCH 9/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set() Rafael J. Wysocki
                   ` (7 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:12 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

All of the existing callers of acpi_pci_osc_control_set() also call
acpi_pci_osc_control_query() right prior to it, so they don't use the
preserved control bits returned by the last _OSC query for the given
PCI root bridge (or PCIe root complex).

For this reason the osc_control_qry and osc_queried members of
struct acpi_pci_root are not necessary any more, so drop them and
remove the remaining code using them.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   19 +------------------
 include/acpi/acpi_bus.h |    3 ---
 2 files changed, 1 insertion(+), 21 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -248,12 +248,8 @@ static acpi_status acpi_pci_query_osc(st
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
 		root->osc_support_set = support;
-		if (control) {
+		if (control)
 			*control = result;
-		} else {
-			root->osc_control_qry = result;
-			root->osc_queried = 1;
-		}
 	}
 	return status;
 }
@@ -449,19 +445,6 @@ acpi_status acpi_pci_osc_control_set(acp
 	if ((root->osc_control_set & control_req) == control_req)
 		goto out;
 
-	/* Need to query controls first before requesting them */
-	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
-		if (ACPI_FAILURE(status))
-			goto out;
-	}
-	if ((root->osc_control_qry & control_req) != control_req) {
-		printk(KERN_DEBUG
-		       "Firmware did not grant requested _OSC control\n");
-		status = AE_SUPPORT;
-		goto out;
-	}
-
 	capbuf[OSC_QUERY_TYPE] = 0;
 	capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
 	capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
Index: linux-2.6/include/acpi/acpi_bus.h
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -377,9 +377,6 @@ struct acpi_pci_root {
 
 	u32 osc_support_set;	/* _OSC state of support bits */
 	u32 osc_control_set;	/* _OSC state of control bits */
-	u32 osc_control_qry;	/* the latest _OSC query result */
-
-	u32 osc_queried:1;	/* has _OSC control been queried? */
 };
 
 /* helper */


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 8/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v2)
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (13 preceding siblings ...)
  2010-08-06  1:11 ` Rafael J. Wysocki
@ 2010-08-06  1:12 ` Rafael J. Wysocki
  2010-08-06  1:12 ` Rafael J. Wysocki
                   ` (8 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:12 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

All of the existing callers of acpi_pci_osc_control_set() also call
acpi_pci_osc_control_query() right prior to it, so they don't use the
preserved control bits returned by the last _OSC query for the given
PCI root bridge (or PCIe root complex).

For this reason the osc_control_qry and osc_queried members of
struct acpi_pci_root are not necessary any more, so drop them and
remove the remaining code using them.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   19 +------------------
 include/acpi/acpi_bus.h |    3 ---
 2 files changed, 1 insertion(+), 21 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -248,12 +248,8 @@ static acpi_status acpi_pci_query_osc(st
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
 		root->osc_support_set = support;
-		if (control) {
+		if (control)
 			*control = result;
-		} else {
-			root->osc_control_qry = result;
-			root->osc_queried = 1;
-		}
 	}
 	return status;
 }
@@ -449,19 +445,6 @@ acpi_status acpi_pci_osc_control_set(acp
 	if ((root->osc_control_set & control_req) == control_req)
 		goto out;
 
-	/* Need to query controls first before requesting them */
-	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
-		if (ACPI_FAILURE(status))
-			goto out;
-	}
-	if ((root->osc_control_qry & control_req) != control_req) {
-		printk(KERN_DEBUG
-		       "Firmware did not grant requested _OSC control\n");
-		status = AE_SUPPORT;
-		goto out;
-	}
-
 	capbuf[OSC_QUERY_TYPE] = 0;
 	capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
 	capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
Index: linux-2.6/include/acpi/acpi_bus.h
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -377,9 +377,6 @@ struct acpi_pci_root {
 
 	u32 osc_support_set;	/* _OSC state of support bits */
 	u32 osc_control_set;	/* _OSC state of control bits */
-	u32 osc_control_qry;	/* the latest _OSC query result */
-
-	u32 osc_queried:1;	/* has _OSC control been queried? */
 };
 
 /* helper */

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 9/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set()
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (16 preceding siblings ...)
  2010-08-06  1:13 ` [PATCH 9/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set() Rafael J. Wysocki
@ 2010-08-06  1:13 ` Rafael J. Wysocki
  2010-08-06  1:15 ` [PATCH 10/10] ACPI / PCI: Merge acpi_pci_osc_control_{query|set}() Rafael J. Wysocki
                   ` (5 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:13 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

Make acpi_pci_osc_control_set() attempt to find the handle of the
_OSC object under the given PCI root bridge object after verifying
that its second argument is correct and that there is a struct
acpi_pci_root object for the given root bridge handle.  This is
more logical than the old code and it matches the code ordering
in acpi_pci_osc_control_query().

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/acpi/pci_root.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -428,10 +428,6 @@ acpi_status acpi_pci_osc_control_set(acp
 	acpi_handle tmp;
 	struct acpi_pci_root *root;
 
-	status = acpi_get_handle(handle, "_OSC", &tmp);
-	if (ACPI_FAILURE(status))
-		return status;
-
 	control_req = (flags & OSC_PCI_CONTROL_MASKS);
 	if (!control_req)
 		return AE_TYPE;
@@ -440,6 +436,10 @@ acpi_status acpi_pci_osc_control_set(acp
 	if (!root)
 		return AE_NOT_EXIST;
 
+	status = acpi_get_handle(handle, "_OSC", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
 	mutex_lock(&osc_lock);
 	/* No need to evaluate _OSC if the control was already granted. */
 	if ((root->osc_control_set & control_req) == control_req)

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 9/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set()
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (15 preceding siblings ...)
  2010-08-06  1:12 ` Rafael J. Wysocki
@ 2010-08-06  1:13 ` Rafael J. Wysocki
  2010-08-06  1:13 ` Rafael J. Wysocki
                   ` (6 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:13 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

Make acpi_pci_osc_control_set() attempt to find the handle of the
_OSC object under the given PCI root bridge object after verifying
that its second argument is correct and that there is a struct
acpi_pci_root object for the given root bridge handle.  This is
more logical than the old code and it matches the code ordering
in acpi_pci_osc_control_query().

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/acpi/pci_root.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -428,10 +428,6 @@ acpi_status acpi_pci_osc_control_set(acp
 	acpi_handle tmp;
 	struct acpi_pci_root *root;
 
-	status = acpi_get_handle(handle, "_OSC", &tmp);
-	if (ACPI_FAILURE(status))
-		return status;
-
 	control_req = (flags & OSC_PCI_CONTROL_MASKS);
 	if (!control_req)
 		return AE_TYPE;
@@ -440,6 +436,10 @@ acpi_status acpi_pci_osc_control_set(acp
 	if (!root)
 		return AE_NOT_EXIST;
 
+	status = acpi_get_handle(handle, "_OSC", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
 	mutex_lock(&osc_lock);
 	/* No need to evaluate _OSC if the control was already granted. */
 	if ((root->osc_control_set & control_req) == control_req)

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 10/10] ACPI / PCI: Merge acpi_pci_osc_control_{query|set}()
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (17 preceding siblings ...)
  2010-08-06  1:13 ` Rafael J. Wysocki
@ 2010-08-06  1:15 ` Rafael J. Wysocki
  2010-08-06  1:15 ` Rafael J. Wysocki
                   ` (4 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:15 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

From: Rafael J. Wysocki <rjw@sisk.pl>

The both of the existing callers of acpi_pci_osc_control_query()
and acpi_pci_osc_control_set() have the same structure, such that
acpi_pci_osc_control_query() is called first for certain mask of
control bits, then the mask returned by it is tested against a given
subset of the queried control bits and if that test is successful,
acpi_pci_osc_control_set() is run for the mask of control bits
returned by acpi_pci_osc_control_query().  Thus it is possible to
move the initial query as well as the subsequent test to
acpi_pci_osc_control_set() and simplify its callers.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c          |   75 ++++++++++++++-------------------------
 drivers/pci/hotplug/acpi_pcihp.c |    9 ++--
 drivers/pci/pcie/portdrv_acpi.c  |   16 +-------
 include/linux/acpi.h             |    4 +-
 4 files changed, 36 insertions(+), 68 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -372,21 +372,35 @@ out:
 }
 EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
 
- /**
- * acpi_pci_osc_control_query - Get the _OSC bits the kernel can control.
+/**
+ * acpi_pci_osc_control_set - Request control of PCI root _OSC features.
  * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
- * @mask: Mask of _OSC bits to query and the place to put the result into.
+ * @mask: Mask of _OSC bits to request control of, place to store control mask.
+ * @req: Mask of _OSC bits the control of is essential to the caller.
+ *
+ * Run _OSC query for @mask and if that is successful, compare the returned
+ * mask of control bits with @req.  If all of the @req bits are set in the
+ * returned mask, run _OSC request for it.
+ *
+ * The variable at the @mask address may be modified regardless of whether or
+ * not the function returns success.  On success it will contain the mask of
+ * _OSC bits the BIOS has granted control of, but its contents are meaningless
+ * on failure.
  **/
-acpi_status acpi_pci_osc_control_query(acpi_handle handle, u32 *mask)
+acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
 {
 	struct acpi_pci_root *root;
+	acpi_status status;
+	u32 ctrl, flags, capbuf[3];
 	acpi_handle tmp;
-	acpi_status status = AE_OK;
-	u32 ctrl = *mask;
 
-	if (!(ctrl & OSC_PCI_CONTROL_MASKS))
+	if (!mask)
 		return AE_BAD_PARAMETER;
 
+	ctrl = *mask & OSC_PCI_CONTROL_MASKS;
+	if ((ctrl & req) != req)
+		return AE_TYPE;
+
 	root = acpi_pci_find_root(handle);
 	if (!root)
 		return AE_NOT_EXIST;
@@ -404,53 +418,20 @@ acpi_status acpi_pci_osc_control_query(a
 	for (ctrl = 0; *mask != ctrl; ctrl = *mask) {
 		status = acpi_pci_query_osc(root, root->osc_support_set, mask);
 		if (ACPI_FAILURE(status))
-			break;
+			goto out;
 	}
 
- out:
-	mutex_unlock(&osc_lock);
-
-	return status;
-}
-EXPORT_SYMBOL(acpi_pci_osc_control_query);
-
-/**
- * acpi_pci_osc_control_set - commit requested control to Firmware
- * @handle: acpi_handle for the target ACPI object
- * @flags: driver's requested control bits
- *
- * Attempt to take control from Firmware on requested control bits.
- **/
-acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
-{
-	acpi_status status;
-	u32 control_req, result, capbuf[3];
-	acpi_handle tmp;
-	struct acpi_pci_root *root;
-
-	control_req = (flags & OSC_PCI_CONTROL_MASKS);
-	if (!control_req)
-		return AE_TYPE;
-
-	root = acpi_pci_find_root(handle);
-	if (!root)
-		return AE_NOT_EXIST;
-
-	status = acpi_get_handle(handle, "_OSC", &tmp);
-	if (ACPI_FAILURE(status))
-		return status;
-
-	mutex_lock(&osc_lock);
-	/* No need to evaluate _OSC if the control was already granted. */
-	if ((root->osc_control_set & control_req) == control_req)
+	if ((ctrl & req) != req) {
+		status = AE_SUPPORT;
 		goto out;
+	}
 
 	capbuf[OSC_QUERY_TYPE] = 0;
 	capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
-	capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
-	status = acpi_pci_run_osc(handle, capbuf, &result);
+	capbuf[OSC_CONTROL_TYPE] = ctrl;
+	status = acpi_pci_run_osc(handle, capbuf, mask);
 	if (ACPI_SUCCESS(status))
-		root->osc_control_set = result;
+		root->osc_control_set = *mask;
 out:
 	mutex_unlock(&osc_lock);
 	return status;
Index: linux-2.6/include/linux/acpi.h
===================================================================
--- linux-2.6.orig/include/linux/acpi.h
+++ linux-2.6/include/linux/acpi.h
@@ -305,8 +305,8 @@ acpi_status acpi_run_osc(acpi_handle han
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
 
-extern acpi_status acpi_pci_osc_control_query(acpi_handle handle, u32 *mask);
-extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
+extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
+					     u32 *mask, u32 req);
 extern void acpi_early_init(void);
 
 #else	/* !CONFIG_ACPI */
Index: linux-2.6/drivers/pci/pcie/portdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/portdrv_acpi.c
@@ -55,20 +55,8 @@ int pcie_port_acpi_setup(struct pci_dev 
 			flags |= OSC_PCI_EXPRESS_AER_CONTROL;
 	}
 
-	status = acpi_pci_osc_control_query(handle, &flags);
-	if (ACPI_FAILURE(status)) {
-		dev_dbg(&port->dev, "ACPI _OSC query failed (code %d)\n",
-			status);
-		return -ENODEV;
-	}
-
-	if (!(flags & OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)) {
-		dev_dbg(&port->dev, "BIOS refuses to grant control of PCIe "
-			"Capability Structure\n");
-		return -EACCES;
-	}
-
-	status = acpi_pci_osc_control_set(handle, flags);
+	status = acpi_pci_osc_control_set(handle, &flags,
+					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
 	if (ACPI_FAILURE(status)) {
 		dev_dbg(&port->dev, "ACPI _OSC request failed (code %d)\n",
 			status);
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -358,13 +358,12 @@ int acpi_get_hp_hw_control_from_firmware
 		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
 		dbg("Trying to get hotplug control for %s\n",
 				(char *)string.pointer);
-		status = acpi_pci_osc_control_query(handle, &flags);
-		if (ACPI_FAILURE(status)
-		    || !(flags & OSC_SHPC_NATIVE_HP_CONTROL))
-			goto no_control;
-		status = acpi_pci_osc_control_set(handle, flags);
+		status = acpi_pci_osc_control_set(handle, &flags,
+						OSC_SHPC_NATIVE_HP_CONTROL);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
+		if (status == AE_SUPPORT)
+			goto no_control;
 		kfree(string.pointer);
 		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
 	}


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 10/10] ACPI / PCI: Merge acpi_pci_osc_control_{query|set}()
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (18 preceding siblings ...)
  2010-08-06  1:15 ` [PATCH 10/10] ACPI / PCI: Merge acpi_pci_osc_control_{query|set}() Rafael J. Wysocki
@ 2010-08-06  1:15 ` Rafael J. Wysocki
  2010-08-06  3:25 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Hidetoshi Seto
                   ` (3 subsequent siblings)
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06  1:15 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

From: Rafael J. Wysocki <rjw@sisk.pl>

The both of the existing callers of acpi_pci_osc_control_query()
and acpi_pci_osc_control_set() have the same structure, such that
acpi_pci_osc_control_query() is called first for certain mask of
control bits, then the mask returned by it is tested against a given
subset of the queried control bits and if that test is successful,
acpi_pci_osc_control_set() is run for the mask of control bits
returned by acpi_pci_osc_control_query().  Thus it is possible to
move the initial query as well as the subsequent test to
acpi_pci_osc_control_set() and simplify its callers.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c          |   75 ++++++++++++++-------------------------
 drivers/pci/hotplug/acpi_pcihp.c |    9 ++--
 drivers/pci/pcie/portdrv_acpi.c  |   16 +-------
 include/linux/acpi.h             |    4 +-
 4 files changed, 36 insertions(+), 68 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -372,21 +372,35 @@ out:
 }
 EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
 
- /**
- * acpi_pci_osc_control_query - Get the _OSC bits the kernel can control.
+/**
+ * acpi_pci_osc_control_set - Request control of PCI root _OSC features.
  * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
- * @mask: Mask of _OSC bits to query and the place to put the result into.
+ * @mask: Mask of _OSC bits to request control of, place to store control mask.
+ * @req: Mask of _OSC bits the control of is essential to the caller.
+ *
+ * Run _OSC query for @mask and if that is successful, compare the returned
+ * mask of control bits with @req.  If all of the @req bits are set in the
+ * returned mask, run _OSC request for it.
+ *
+ * The variable at the @mask address may be modified regardless of whether or
+ * not the function returns success.  On success it will contain the mask of
+ * _OSC bits the BIOS has granted control of, but its contents are meaningless
+ * on failure.
  **/
-acpi_status acpi_pci_osc_control_query(acpi_handle handle, u32 *mask)
+acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
 {
 	struct acpi_pci_root *root;
+	acpi_status status;
+	u32 ctrl, flags, capbuf[3];
 	acpi_handle tmp;
-	acpi_status status = AE_OK;
-	u32 ctrl = *mask;
 
-	if (!(ctrl & OSC_PCI_CONTROL_MASKS))
+	if (!mask)
 		return AE_BAD_PARAMETER;
 
+	ctrl = *mask & OSC_PCI_CONTROL_MASKS;
+	if ((ctrl & req) != req)
+		return AE_TYPE;
+
 	root = acpi_pci_find_root(handle);
 	if (!root)
 		return AE_NOT_EXIST;
@@ -404,53 +418,20 @@ acpi_status acpi_pci_osc_control_query(a
 	for (ctrl = 0; *mask != ctrl; ctrl = *mask) {
 		status = acpi_pci_query_osc(root, root->osc_support_set, mask);
 		if (ACPI_FAILURE(status))
-			break;
+			goto out;
 	}
 
- out:
-	mutex_unlock(&osc_lock);
-
-	return status;
-}
-EXPORT_SYMBOL(acpi_pci_osc_control_query);
-
-/**
- * acpi_pci_osc_control_set - commit requested control to Firmware
- * @handle: acpi_handle for the target ACPI object
- * @flags: driver's requested control bits
- *
- * Attempt to take control from Firmware on requested control bits.
- **/
-acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
-{
-	acpi_status status;
-	u32 control_req, result, capbuf[3];
-	acpi_handle tmp;
-	struct acpi_pci_root *root;
-
-	control_req = (flags & OSC_PCI_CONTROL_MASKS);
-	if (!control_req)
-		return AE_TYPE;
-
-	root = acpi_pci_find_root(handle);
-	if (!root)
-		return AE_NOT_EXIST;
-
-	status = acpi_get_handle(handle, "_OSC", &tmp);
-	if (ACPI_FAILURE(status))
-		return status;
-
-	mutex_lock(&osc_lock);
-	/* No need to evaluate _OSC if the control was already granted. */
-	if ((root->osc_control_set & control_req) == control_req)
+	if ((ctrl & req) != req) {
+		status = AE_SUPPORT;
 		goto out;
+	}
 
 	capbuf[OSC_QUERY_TYPE] = 0;
 	capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
-	capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
-	status = acpi_pci_run_osc(handle, capbuf, &result);
+	capbuf[OSC_CONTROL_TYPE] = ctrl;
+	status = acpi_pci_run_osc(handle, capbuf, mask);
 	if (ACPI_SUCCESS(status))
-		root->osc_control_set = result;
+		root->osc_control_set = *mask;
 out:
 	mutex_unlock(&osc_lock);
 	return status;
Index: linux-2.6/include/linux/acpi.h
===================================================================
--- linux-2.6.orig/include/linux/acpi.h
+++ linux-2.6/include/linux/acpi.h
@@ -305,8 +305,8 @@ acpi_status acpi_run_osc(acpi_handle han
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
 
-extern acpi_status acpi_pci_osc_control_query(acpi_handle handle, u32 *mask);
-extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
+extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
+					     u32 *mask, u32 req);
 extern void acpi_early_init(void);
 
 #else	/* !CONFIG_ACPI */
Index: linux-2.6/drivers/pci/pcie/portdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/portdrv_acpi.c
@@ -55,20 +55,8 @@ int pcie_port_acpi_setup(struct pci_dev 
 			flags |= OSC_PCI_EXPRESS_AER_CONTROL;
 	}
 
-	status = acpi_pci_osc_control_query(handle, &flags);
-	if (ACPI_FAILURE(status)) {
-		dev_dbg(&port->dev, "ACPI _OSC query failed (code %d)\n",
-			status);
-		return -ENODEV;
-	}
-
-	if (!(flags & OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)) {
-		dev_dbg(&port->dev, "BIOS refuses to grant control of PCIe "
-			"Capability Structure\n");
-		return -EACCES;
-	}
-
-	status = acpi_pci_osc_control_set(handle, flags);
+	status = acpi_pci_osc_control_set(handle, &flags,
+					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
 	if (ACPI_FAILURE(status)) {
 		dev_dbg(&port->dev, "ACPI _OSC request failed (code %d)\n",
 			status);
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -358,13 +358,12 @@ int acpi_get_hp_hw_control_from_firmware
 		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
 		dbg("Trying to get hotplug control for %s\n",
 				(char *)string.pointer);
-		status = acpi_pci_osc_control_query(handle, &flags);
-		if (ACPI_FAILURE(status)
-		    || !(flags & OSC_SHPC_NATIVE_HP_CONTROL))
-			goto no_control;
-		status = acpi_pci_osc_control_set(handle, flags);
+		status = acpi_pci_osc_control_set(handle, &flags,
+						OSC_SHPC_NATIVE_HP_CONTROL);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
+		if (status == AE_SUPPORT)
+			goto no_control;
 		kfree(string.pointer);
 		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
 	}

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls
  2010-08-06  1:11 ` [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls Rafael J. Wysocki
  2010-08-06  2:20   ` Hidetoshi Seto
@ 2010-08-06  2:20   ` Hidetoshi Seto
  2010-08-06 10:34     ` Rafael J. Wysocki
  2010-08-06 10:34     ` Rafael J. Wysocki
  1 sibling, 2 replies; 61+ messages in thread
From: Hidetoshi Seto @ 2010-08-06  2:20 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

(2010/08/06 10:11), Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> It generally is unsafe to call acpi_pci_osc_control_set() without
> checking what _OSC bits the BIOS is willing to grant control of,
> because control of some _OSC bits may be granted even if
> acpi_pci_osc_control_set() returns AE_SUPPORT.  Therefore make
> acpi_get_hp_hw_control_from_firmware() use
> acpi_pci_osc_control_query() to check if the BIOS will grant
> OSC_SHPC_NATIVE_HP_CONTROL before calling acpi_pci_osc_control_set().
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/pci/hotplug/acpi_pcihp.c |    6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
> ===================================================================
> --- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
> +++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
> @@ -358,11 +358,13 @@ int acpi_get_hp_hw_control_from_firmware
>  		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
>  		dbg("Trying to get hotplug control for %s\n",
>  				(char *)string.pointer);
> +		status = acpi_pci_osc_control_query(handle, &flags);
> +		if (ACPI_FAILURE(status)
> +		    || !(flags & OSC_SHPC_NATIVE_HP_CONTROL))
> +			goto no_control;
>  		status = acpi_pci_osc_control_set(handle, flags);
>  		if (ACPI_SUCCESS(status))
>  			goto got_one;
> -		if (status == AE_SUPPORT)
> -			goto no_control;
>  		kfree(string.pointer);
>  		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
>  	}

This breaks fallback mechanism for OSHP, doesn't it?


Thanks.
H.Seto

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls
  2010-08-06  1:11 ` [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls Rafael J. Wysocki
@ 2010-08-06  2:20   ` Hidetoshi Seto
  2010-08-06  2:20   ` Hidetoshi Seto
  1 sibling, 0 replies; 61+ messages in thread
From: Hidetoshi Seto @ 2010-08-06  2:20 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

(2010/08/06 10:11), Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> It generally is unsafe to call acpi_pci_osc_control_set() without
> checking what _OSC bits the BIOS is willing to grant control of,
> because control of some _OSC bits may be granted even if
> acpi_pci_osc_control_set() returns AE_SUPPORT.  Therefore make
> acpi_get_hp_hw_control_from_firmware() use
> acpi_pci_osc_control_query() to check if the BIOS will grant
> OSC_SHPC_NATIVE_HP_CONTROL before calling acpi_pci_osc_control_set().
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>  drivers/pci/hotplug/acpi_pcihp.c |    6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
> ===================================================================
> --- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
> +++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
> @@ -358,11 +358,13 @@ int acpi_get_hp_hw_control_from_firmware
>  		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
>  		dbg("Trying to get hotplug control for %s\n",
>  				(char *)string.pointer);
> +		status = acpi_pci_osc_control_query(handle, &flags);
> +		if (ACPI_FAILURE(status)
> +		    || !(flags & OSC_SHPC_NATIVE_HP_CONTROL))
> +			goto no_control;
>  		status = acpi_pci_osc_control_set(handle, flags);
>  		if (ACPI_SUCCESS(status))
>  			goto got_one;
> -		if (status == AE_SUPPORT)
> -			goto no_control;
>  		kfree(string.pointer);
>  		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
>  	}

This breaks fallback mechanism for OSHP, doesn't it?


Thanks.
H.Seto

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3)
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (19 preceding siblings ...)
  2010-08-06  1:15 ` Rafael J. Wysocki
@ 2010-08-06  3:25 ` Hidetoshi Seto
  2010-08-06 10:42   ` Rafael J. Wysocki
  2010-08-06 10:42   ` Rafael J. Wysocki
  2010-08-06  3:25 ` Hidetoshi Seto
                   ` (2 subsequent siblings)
  23 siblings, 2 replies; 61+ messages in thread
From: Hidetoshi Seto @ 2010-08-06  3:25 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

(2010/08/06 10:03), Rafael J. Wysocki wrote:
> Hi,
> 
> This is the 3rd iteration of the patchset based on
> https://patchwork.kernel.org/patch/114917/ and the comments I received on that
> patch and the previous two iterations of this patchset.
> 
> The patches below do not make the intermediate step to put an unconditional
> _OSC query into acpi_pci_osc_control_set(), but the final result is quite
> similar to the 2nd iteration with https://patchwork.kernel.org/patch/116976/
> applied on top.
> 
> [1/10] - Introduce acpi_pci_osc_control_query() allowing the caller to get a
>          mask of _OSC control bits the BIOS allows the kernel to control
>          for a given PCI root bridge.  This version repeats _OSC queries until
>          the BIOS doesn't mask any requested bits.

This contains 2 parts:
 1-A) change acpi_status acpi_pci_query_osc() to take support and *control
 1-B) introduce acpi_pci_osc_control_query()

and I'd like to kill 1-B since it is removed soon by patch [10/10].

> 
> [2/10] - Introduce pci_aer_available() allowing the caller to check if the
>          AER service driver should be enabled.
> 
> [3/10] - Introduce kernel command line switch pcie_ports=.

These 2 are prep for following big changes.

> 
> [4/10] - Rework the PCIe port driver to request _OSC control for all serives at
>          once.
> 
> [5/10] - Disable PCIe port services (that might be enabled by the BIOS) during
>          initialization.

These 2 are core part to solve the issue.

> 
> [6/10] - Remove the PCIe port driver modules exit function.

This is a cleanup. There is no reason to keep this in the series.

> 
> [7/10] - Make PCI hotplug code query _OSC before requesting control of _OSC
>          features.

Needless since removed soon by patch [10/10].

> 
> [8/10] - Rework acpi_pci_osc_control_set() so that it doesn't use cached
>         result of a query and remove the fields of struct acpi_pci_root that
>         aren't used any more.

This also contains 2 parts:
 8-A: don't use cached result
 8-B: remove query in acpi_pci_osc_control_set()

I think 8-B is not required since query relives in patch [10/10].

> 
> [9/10] - Reorder checks in acpi_pci_osc_control_set().

A minor cleanup can be out of the series.

> 
> [10/10] - Merge acpi_pci_osc_control_set() with acpi_pci_osc_control_query().

This removes many badness in this series.
I think we should have this improved acpi_pci_osc_control_set() before [4/10].

> 
> Comments welcome.

I recommend you to rearrange the order of patches in this series.

1. Misc cleanups
   [6/10]
   [9/10]
2. Prep1: prepare small parts
   [2/10]
   [3/10]
3. Prep2: improve acpi_pci_osc_control_set()
   [1-A]
   [8-A]
   [10/10]
4. Main
   [4/10]
   [5/10]

I think there is no objections to 4 patches from the first ([6,9,2,3/10]).


Thanks,
H.Seto

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3)
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (20 preceding siblings ...)
  2010-08-06  3:25 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Hidetoshi Seto
@ 2010-08-06  3:25 ` Hidetoshi Seto
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
  23 siblings, 0 replies; 61+ messages in thread
From: Hidetoshi Seto @ 2010-08-06  3:25 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

(2010/08/06 10:03), Rafael J. Wysocki wrote:
> Hi,
> 
> This is the 3rd iteration of the patchset based on
> https://patchwork.kernel.org/patch/114917/ and the comments I received on that
> patch and the previous two iterations of this patchset.
> 
> The patches below do not make the intermediate step to put an unconditional
> _OSC query into acpi_pci_osc_control_set(), but the final result is quite
> similar to the 2nd iteration with https://patchwork.kernel.org/patch/116976/
> applied on top.
> 
> [1/10] - Introduce acpi_pci_osc_control_query() allowing the caller to get a
>          mask of _OSC control bits the BIOS allows the kernel to control
>          for a given PCI root bridge.  This version repeats _OSC queries until
>          the BIOS doesn't mask any requested bits.

This contains 2 parts:
 1-A) change acpi_status acpi_pci_query_osc() to take support and *control
 1-B) introduce acpi_pci_osc_control_query()

and I'd like to kill 1-B since it is removed soon by patch [10/10].

> 
> [2/10] - Introduce pci_aer_available() allowing the caller to check if the
>          AER service driver should be enabled.
> 
> [3/10] - Introduce kernel command line switch pcie_ports=.

These 2 are prep for following big changes.

> 
> [4/10] - Rework the PCIe port driver to request _OSC control for all serives at
>          once.
> 
> [5/10] - Disable PCIe port services (that might be enabled by the BIOS) during
>          initialization.

These 2 are core part to solve the issue.

> 
> [6/10] - Remove the PCIe port driver modules exit function.

This is a cleanup. There is no reason to keep this in the series.

> 
> [7/10] - Make PCI hotplug code query _OSC before requesting control of _OSC
>          features.

Needless since removed soon by patch [10/10].

> 
> [8/10] - Rework acpi_pci_osc_control_set() so that it doesn't use cached
>         result of a query and remove the fields of struct acpi_pci_root that
>         aren't used any more.

This also contains 2 parts:
 8-A: don't use cached result
 8-B: remove query in acpi_pci_osc_control_set()

I think 8-B is not required since query relives in patch [10/10].

> 
> [9/10] - Reorder checks in acpi_pci_osc_control_set().

A minor cleanup can be out of the series.

> 
> [10/10] - Merge acpi_pci_osc_control_set() with acpi_pci_osc_control_query().

This removes many badness in this series.
I think we should have this improved acpi_pci_osc_control_set() before [4/10].

> 
> Comments welcome.

I recommend you to rearrange the order of patches in this series.

1. Misc cleanups
   [6/10]
   [9/10]
2. Prep1: prepare small parts
   [2/10]
   [3/10]
3. Prep2: improve acpi_pci_osc_control_set()
   [1-A]
   [8-A]
   [10/10]
4. Main
   [4/10]
   [5/10]

I think there is no objections to 4 patches from the first ([6,9,2,3/10]).


Thanks,
H.Seto

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls
  2010-08-06  2:20   ` Hidetoshi Seto
@ 2010-08-06 10:34     ` Rafael J. Wysocki
  2010-08-09  1:22       ` Kenji Kaneshige
  2010-08-09  1:22       ` Kenji Kaneshige
  2010-08-06 10:34     ` Rafael J. Wysocki
  1 sibling, 2 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06 10:34 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

On Friday, August 06, 2010, Hidetoshi Seto wrote:
> (2010/08/06 10:11), Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> > 
> > It generally is unsafe to call acpi_pci_osc_control_set() without
> > checking what _OSC bits the BIOS is willing to grant control of,
> > because control of some _OSC bits may be granted even if
> > acpi_pci_osc_control_set() returns AE_SUPPORT.  Therefore make
> > acpi_get_hp_hw_control_from_firmware() use
> > acpi_pci_osc_control_query() to check if the BIOS will grant
> > OSC_SHPC_NATIVE_HP_CONTROL before calling acpi_pci_osc_control_set().
> > 
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> >  drivers/pci/hotplug/acpi_pcihp.c |    6 ++++--
> >  1 file changed, 4 insertions(+), 2 deletions(-)
> > 
> > Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
> > ===================================================================
> > --- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
> > +++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
> > @@ -358,11 +358,13 @@ int acpi_get_hp_hw_control_from_firmware
> >  		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
> >  		dbg("Trying to get hotplug control for %s\n",
> >  				(char *)string.pointer);
> > +		status = acpi_pci_osc_control_query(handle, &flags);
> > +		if (ACPI_FAILURE(status)
> > +		    || !(flags & OSC_SHPC_NATIVE_HP_CONTROL))
> > +			goto no_control;
> >  		status = acpi_pci_osc_control_set(handle, flags);
> >  		if (ACPI_SUCCESS(status))
> >  			goto got_one;
> > -		if (status == AE_SUPPORT)
> > -			goto no_control;
> >  		kfree(string.pointer);
> >  		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
> >  	}
> 
> This breaks fallback mechanism for OSHP, doesn't it?

Hmm, well.  Can you please elaborate a bit?

I guess you mean that the last two statements should be executed if
acpi_pci_osc_control_query() returns error code, is that correct?

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls
  2010-08-06  2:20   ` Hidetoshi Seto
  2010-08-06 10:34     ` Rafael J. Wysocki
@ 2010-08-06 10:34     ` Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06 10:34 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

On Friday, August 06, 2010, Hidetoshi Seto wrote:
> (2010/08/06 10:11), Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> > 
> > It generally is unsafe to call acpi_pci_osc_control_set() without
> > checking what _OSC bits the BIOS is willing to grant control of,
> > because control of some _OSC bits may be granted even if
> > acpi_pci_osc_control_set() returns AE_SUPPORT.  Therefore make
> > acpi_get_hp_hw_control_from_firmware() use
> > acpi_pci_osc_control_query() to check if the BIOS will grant
> > OSC_SHPC_NATIVE_HP_CONTROL before calling acpi_pci_osc_control_set().
> > 
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> >  drivers/pci/hotplug/acpi_pcihp.c |    6 ++++--
> >  1 file changed, 4 insertions(+), 2 deletions(-)
> > 
> > Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
> > ===================================================================
> > --- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
> > +++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
> > @@ -358,11 +358,13 @@ int acpi_get_hp_hw_control_from_firmware
> >  		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
> >  		dbg("Trying to get hotplug control for %s\n",
> >  				(char *)string.pointer);
> > +		status = acpi_pci_osc_control_query(handle, &flags);
> > +		if (ACPI_FAILURE(status)
> > +		    || !(flags & OSC_SHPC_NATIVE_HP_CONTROL))
> > +			goto no_control;
> >  		status = acpi_pci_osc_control_set(handle, flags);
> >  		if (ACPI_SUCCESS(status))
> >  			goto got_one;
> > -		if (status == AE_SUPPORT)
> > -			goto no_control;
> >  		kfree(string.pointer);
> >  		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
> >  	}
> 
> This breaks fallback mechanism for OSHP, doesn't it?

Hmm, well.  Can you please elaborate a bit?

I guess you mean that the last two statements should be executed if
acpi_pci_osc_control_query() returns error code, is that correct?

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3)
  2010-08-06  3:25 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Hidetoshi Seto
@ 2010-08-06 10:42   ` Rafael J. Wysocki
  2010-08-06 10:42   ` Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06 10:42 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: Kenji Kaneshige, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

First of all, thanks for the detailed review!

On Friday, August 06, 2010, Hidetoshi Seto wrote:
> (2010/08/06 10:03), Rafael J. Wysocki wrote:
> > Hi,
> > 
> > This is the 3rd iteration of the patchset based on
> > https://patchwork.kernel.org/patch/114917/ and the comments I received on that
> > patch and the previous two iterations of this patchset.
> > 
> > The patches below do not make the intermediate step to put an unconditional
> > _OSC query into acpi_pci_osc_control_set(), but the final result is quite
> > similar to the 2nd iteration with https://patchwork.kernel.org/patch/116976/
> > applied on top.
> > 
> > [1/10] - Introduce acpi_pci_osc_control_query() allowing the caller to get a
> >          mask of _OSC control bits the BIOS allows the kernel to control
> >          for a given PCI root bridge.  This version repeats _OSC queries until
> >          the BIOS doesn't mask any requested bits.
> 
> This contains 2 parts:
>  1-A) change acpi_status acpi_pci_query_osc() to take support and *control
>  1-B) introduce acpi_pci_osc_control_query()
> 
> and I'd like to kill 1-B since it is removed soon by patch [10/10].

OK

> > [2/10] - Introduce pci_aer_available() allowing the caller to check if the
> >          AER service driver should be enabled.
> > 
> > [3/10] - Introduce kernel command line switch pcie_ports=.
> 
> These 2 are prep for following big changes.
> 
> > 
> > [4/10] - Rework the PCIe port driver to request _OSC control for all serives at
> >          once.
> > 
> > [5/10] - Disable PCIe port services (that might be enabled by the BIOS) during
> >          initialization.
> 
> These 2 are core part to solve the issue.
> 
> > 
> > [6/10] - Remove the PCIe port driver modules exit function.
> 
> This is a cleanup. There is no reason to keep this in the series.

OK, I'll drop it.

> > [7/10] - Make PCI hotplug code query _OSC before requesting control of _OSC
> >          features.
> 
> Needless since removed soon by patch [10/10].

OK

> > [8/10] - Rework acpi_pci_osc_control_set() so that it doesn't use cached
> >         result of a query and remove the fields of struct acpi_pci_root that
> >         aren't used any more.
> 
> This also contains 2 parts:
>  8-A: don't use cached result
>  8-B: remove query in acpi_pci_osc_control_set()
> 
> I think 8-B is not required since query relives in patch [10/10].

OK
 
> > [9/10] - Reorder checks in acpi_pci_osc_control_set().
> 
> A minor cleanup can be out of the series.

OK, I'll drop it.
 
> > [10/10] - Merge acpi_pci_osc_control_set() with acpi_pci_osc_control_query().
> 
> This removes many badness in this series.
> I think we should have this improved acpi_pci_osc_control_set() before [4/10].

OK

> > Comments welcome.
> 
> I recommend you to rearrange the order of patches in this series.
>
> 1. Misc cleanups
>    [6/10]
>    [9/10]
> 2. Prep1: prepare small parts
>    [2/10]
>    [3/10]
> 3. Prep2: improve acpi_pci_osc_control_set()
>    [1-A]
>    [8-A]
>    [10/10]
> 4. Main
>    [4/10]
>    [5/10]

OK, I'll do that.

Probably, though, I will only be able to do it after I get back from LinuxCon.
At least I really won't be able to test the patches during the conference. :-)
 
> I think there is no objections to 4 patches from the first ([6,9,2,3/10]).

OK, thanks.

Best,
Rafael

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3)
  2010-08-06  3:25 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Hidetoshi Seto
  2010-08-06 10:42   ` Rafael J. Wysocki
@ 2010-08-06 10:42   ` Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-06 10:42 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, linux-pm, ACPI Devel Maling List, Jesse Barnes,
	Kenji Kaneshige

First of all, thanks for the detailed review!

On Friday, August 06, 2010, Hidetoshi Seto wrote:
> (2010/08/06 10:03), Rafael J. Wysocki wrote:
> > Hi,
> > 
> > This is the 3rd iteration of the patchset based on
> > https://patchwork.kernel.org/patch/114917/ and the comments I received on that
> > patch and the previous two iterations of this patchset.
> > 
> > The patches below do not make the intermediate step to put an unconditional
> > _OSC query into acpi_pci_osc_control_set(), but the final result is quite
> > similar to the 2nd iteration with https://patchwork.kernel.org/patch/116976/
> > applied on top.
> > 
> > [1/10] - Introduce acpi_pci_osc_control_query() allowing the caller to get a
> >          mask of _OSC control bits the BIOS allows the kernel to control
> >          for a given PCI root bridge.  This version repeats _OSC queries until
> >          the BIOS doesn't mask any requested bits.
> 
> This contains 2 parts:
>  1-A) change acpi_status acpi_pci_query_osc() to take support and *control
>  1-B) introduce acpi_pci_osc_control_query()
> 
> and I'd like to kill 1-B since it is removed soon by patch [10/10].

OK

> > [2/10] - Introduce pci_aer_available() allowing the caller to check if the
> >          AER service driver should be enabled.
> > 
> > [3/10] - Introduce kernel command line switch pcie_ports=.
> 
> These 2 are prep for following big changes.
> 
> > 
> > [4/10] - Rework the PCIe port driver to request _OSC control for all serives at
> >          once.
> > 
> > [5/10] - Disable PCIe port services (that might be enabled by the BIOS) during
> >          initialization.
> 
> These 2 are core part to solve the issue.
> 
> > 
> > [6/10] - Remove the PCIe port driver modules exit function.
> 
> This is a cleanup. There is no reason to keep this in the series.

OK, I'll drop it.

> > [7/10] - Make PCI hotplug code query _OSC before requesting control of _OSC
> >          features.
> 
> Needless since removed soon by patch [10/10].

OK

> > [8/10] - Rework acpi_pci_osc_control_set() so that it doesn't use cached
> >         result of a query and remove the fields of struct acpi_pci_root that
> >         aren't used any more.
> 
> This also contains 2 parts:
>  8-A: don't use cached result
>  8-B: remove query in acpi_pci_osc_control_set()
> 
> I think 8-B is not required since query relives in patch [10/10].

OK
 
> > [9/10] - Reorder checks in acpi_pci_osc_control_set().
> 
> A minor cleanup can be out of the series.

OK, I'll drop it.
 
> > [10/10] - Merge acpi_pci_osc_control_set() with acpi_pci_osc_control_query().
> 
> This removes many badness in this series.
> I think we should have this improved acpi_pci_osc_control_set() before [4/10].

OK

> > Comments welcome.
> 
> I recommend you to rearrange the order of patches in this series.
>
> 1. Misc cleanups
>    [6/10]
>    [9/10]
> 2. Prep1: prepare small parts
>    [2/10]
>    [3/10]
> 3. Prep2: improve acpi_pci_osc_control_set()
>    [1-A]
>    [8-A]
>    [10/10]
> 4. Main
>    [4/10]
>    [5/10]

OK, I'll do that.

Probably, though, I will only be able to do it after I get back from LinuxCon.
At least I really won't be able to test the patches during the conference. :-)
 
> I think there is no objections to 4 patches from the first ([6,9,2,3/10]).

OK, thanks.

Best,
Rafael

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls
  2010-08-06 10:34     ` Rafael J. Wysocki
@ 2010-08-09  1:22       ` Kenji Kaneshige
  2010-08-09  1:22       ` Kenji Kaneshige
  1 sibling, 0 replies; 61+ messages in thread
From: Kenji Kaneshige @ 2010-08-09  1:22 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Hidetoshi Seto, Jesse Barnes, linux-pci, ACPI Devel Maling List,
	linux-pm

(2010/08/06 19:34), Rafael J. Wysocki wrote:
> On Friday, August 06, 2010, Hidetoshi Seto wrote:
>> (2010/08/06 10:11), Rafael J. Wysocki wrote:
>>> From: Rafael J. Wysocki<rjw@sisk.pl>
>>>
>>> It generally is unsafe to call acpi_pci_osc_control_set() without
>>> checking what _OSC bits the BIOS is willing to grant control of,
>>> because control of some _OSC bits may be granted even if
>>> acpi_pci_osc_control_set() returns AE_SUPPORT.  Therefore make
>>> acpi_get_hp_hw_control_from_firmware() use
>>> acpi_pci_osc_control_query() to check if the BIOS will grant
>>> OSC_SHPC_NATIVE_HP_CONTROL before calling acpi_pci_osc_control_set().
>>>
>>> Signed-off-by: Rafael J. Wysocki<rjw@sisk.pl>
>>> ---
>>>   drivers/pci/hotplug/acpi_pcihp.c |    6 ++++--
>>>   1 file changed, 4 insertions(+), 2 deletions(-)
>>>
>>> Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
>>> ===================================================================
>>> --- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
>>> +++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
>>> @@ -358,11 +358,13 @@ int acpi_get_hp_hw_control_from_firmware
>>>   		acpi_get_name(handle, ACPI_FULL_PATHNAME,&string);
>>>   		dbg("Trying to get hotplug control for %s\n",
>>>   				(char *)string.pointer);
>>> +		status = acpi_pci_osc_control_query(handle,&flags);
>>> +		if (ACPI_FAILURE(status)
>>> +		    || !(flags&  OSC_SHPC_NATIVE_HP_CONTROL))
>>> +			goto no_control;
>>>   		status = acpi_pci_osc_control_set(handle, flags);
>>>   		if (ACPI_SUCCESS(status))
>>>   			goto got_one;
>>> -		if (status == AE_SUPPORT)
>>> -			goto no_control;
>>>   		kfree(string.pointer);
>>>   		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
>>>   	}
>>
>> This breaks fallback mechanism for OSHP, doesn't it?
>
> Hmm, well.  Can you please elaborate a bit?
>
> I guess you mean that the last two statements should be executed if
> acpi_pci_osc_control_query() returns error code, is that correct?
>

On legacy platform, firmware provides OSHP method instead of _OSC method
to grant native hot-plug control to OS. For this reason, current code
evaluates OSHP method if _OSC evaluation failed with error code other
than AE_SUPPORT (ex. "AE_NOT_FOUND" would be returned on the legacy
platform that provides OSHP instead of _OSC).

So I think Hidetoshi meant that

>>> +		status = acpi_pci_osc_control_query(handle,&flags);
>>> +		if (ACPI_FAILURE(status)
>>> +		    || !(flags&  OSC_SHPC_NATIVE_HP_CONTROL))
>>> +			goto no_control;

should be

	status = acpi_pci_osc_control_query(handle,&flags);
	if (status == AE_SUPPORT)
		goto no_control;

Thanks,
Kenji Kaneshige

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls
  2010-08-06 10:34     ` Rafael J. Wysocki
  2010-08-09  1:22       ` Kenji Kaneshige
@ 2010-08-09  1:22       ` Kenji Kaneshige
  1 sibling, 0 replies; 61+ messages in thread
From: Kenji Kaneshige @ 2010-08-09  1:22 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-pci, Hidetoshi Seto, linux-pm, Jesse Barnes,
	ACPI Devel Maling List

(2010/08/06 19:34), Rafael J. Wysocki wrote:
> On Friday, August 06, 2010, Hidetoshi Seto wrote:
>> (2010/08/06 10:11), Rafael J. Wysocki wrote:
>>> From: Rafael J. Wysocki<rjw@sisk.pl>
>>>
>>> It generally is unsafe to call acpi_pci_osc_control_set() without
>>> checking what _OSC bits the BIOS is willing to grant control of,
>>> because control of some _OSC bits may be granted even if
>>> acpi_pci_osc_control_set() returns AE_SUPPORT.  Therefore make
>>> acpi_get_hp_hw_control_from_firmware() use
>>> acpi_pci_osc_control_query() to check if the BIOS will grant
>>> OSC_SHPC_NATIVE_HP_CONTROL before calling acpi_pci_osc_control_set().
>>>
>>> Signed-off-by: Rafael J. Wysocki<rjw@sisk.pl>
>>> ---
>>>   drivers/pci/hotplug/acpi_pcihp.c |    6 ++++--
>>>   1 file changed, 4 insertions(+), 2 deletions(-)
>>>
>>> Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
>>> ===================================================================
>>> --- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
>>> +++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
>>> @@ -358,11 +358,13 @@ int acpi_get_hp_hw_control_from_firmware
>>>   		acpi_get_name(handle, ACPI_FULL_PATHNAME,&string);
>>>   		dbg("Trying to get hotplug control for %s\n",
>>>   				(char *)string.pointer);
>>> +		status = acpi_pci_osc_control_query(handle,&flags);
>>> +		if (ACPI_FAILURE(status)
>>> +		    || !(flags&  OSC_SHPC_NATIVE_HP_CONTROL))
>>> +			goto no_control;
>>>   		status = acpi_pci_osc_control_set(handle, flags);
>>>   		if (ACPI_SUCCESS(status))
>>>   			goto got_one;
>>> -		if (status == AE_SUPPORT)
>>> -			goto no_control;
>>>   		kfree(string.pointer);
>>>   		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
>>>   	}
>>
>> This breaks fallback mechanism for OSHP, doesn't it?
>
> Hmm, well.  Can you please elaborate a bit?
>
> I guess you mean that the last two statements should be executed if
> acpi_pci_osc_control_query() returns error code, is that correct?
>

On legacy platform, firmware provides OSHP method instead of _OSC method
to grant native hot-plug control to OS. For this reason, current code
evaluates OSHP method if _OSC evaluation failed with error code other
than AE_SUPPORT (ex. "AE_NOT_FOUND" would be returned on the legacy
platform that provides OSHP instead of _OSC).

So I think Hidetoshi meant that

>>> +		status = acpi_pci_osc_control_query(handle,&flags);
>>> +		if (ACPI_FAILURE(status)
>>> +		    || !(flags&  OSC_SHPC_NATIVE_HP_CONTROL))
>>> +			goto no_control;

should be

	status = acpi_pci_osc_control_query(handle,&flags);
	if (status == AE_SUPPORT)
		goto no_control;

Thanks,
Kenji Kaneshige

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4)
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (21 preceding siblings ...)
  2010-08-06  3:25 ` Hidetoshi Seto
@ 2010-08-20 23:49 ` Rafael J. Wysocki
  2010-08-20 23:50   ` [PATCH 1/10] PCI / PCIe/ AER: Introduce pci_aer_available() Rafael J. Wysocki
                     ` (19 more replies)
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
  23 siblings, 20 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:49 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

Hi,
 
This is the 4th iteration of the patchset based on
https://patchwork.kernel.org/patch/114917/ and the comments I received on that
patch and the previous three iterations of this patchset.
 
[1/10] - Introduce pci_aer_available() allowing the caller to check if the
         AER service driver should be enabled.
 
[2/10] - Introduce kernel command line switch pcie_ports=.
 
[3/10] - Reorder checks in acpi_pci_osc_control_set().

[4/10] - Make acpi_pci_query_osc() return control bits obtained from the BIOS.

[5/10] - Do not cache _OSC query results.

[6/10] - Negotiate _OSC control bits before requesting control from the BIOS.

[7/10] - Rework the PCIe port driver to request _OSC control for all serives at
          once (that also affects ASPM now, Matthew please check).

[8/10] - Disable PCIe port services (that might be enabled by the BIOS) during
          initialization.

[9/10] - Move the PCIe PME code to drivers/pci/pcie/.

[10/10] - Remove the PCIe port driver modules exit function.

Comments welcome.
 
Thanks,
Rafael


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4)
  2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
                   ` (22 preceding siblings ...)
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
@ 2010-08-20 23:49 ` Rafael J. Wysocki
  23 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:49 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

Hi,
 
This is the 4th iteration of the patchset based on
https://patchwork.kernel.org/patch/114917/ and the comments I received on that
patch and the previous three iterations of this patchset.
 
[1/10] - Introduce pci_aer_available() allowing the caller to check if the
         AER service driver should be enabled.
 
[2/10] - Introduce kernel command line switch pcie_ports=.
 
[3/10] - Reorder checks in acpi_pci_osc_control_set().

[4/10] - Make acpi_pci_query_osc() return control bits obtained from the BIOS.

[5/10] - Do not cache _OSC query results.

[6/10] - Negotiate _OSC control bits before requesting control from the BIOS.

[7/10] - Rework the PCIe port driver to request _OSC control for all serives at
          once (that also affects ASPM now, Matthew please check).

[8/10] - Disable PCIe port services (that might be enabled by the BIOS) during
          initialization.

[9/10] - Move the PCIe PME code to drivers/pci/pcie/.

[10/10] - Remove the PCIe port driver modules exit function.

Comments welcome.
 
Thanks,
Rafael

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 1/10] PCI / PCIe/ AER: Introduce pci_aer_available()
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
  2010-08-20 23:50   ` [PATCH 1/10] PCI / PCIe/ AER: Introduce pci_aer_available() Rafael J. Wysocki
@ 2010-08-20 23:50   ` Rafael J. Wysocki
  2010-08-24 20:49     ` Jesse Barnes
  2010-08-24 20:49     ` Jesse Barnes
  2010-08-20 23:51   ` [PATCH 2/10] PCI / PCIe: Introduce commad line switch for disabling port services Rafael J. Wysocki
                     ` (17 subsequent siblings)
  19 siblings, 2 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:50 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce a function allowing the caller to check whether to try to
enable PCIe AER.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/pci/pci.h             |    2 ++
 drivers/pci/pcie/aer/aerdrv.c |    9 ++++++---
 2 files changed, 8 insertions(+), 3 deletions(-)

Index: linux-2.6/drivers/pci/pci.h
===================================================================
--- linux-2.6.orig/drivers/pci/pci.h
+++ linux-2.6/drivers/pci/pci.h
@@ -140,8 +140,10 @@ static inline void pci_msi_init_pci_dev(
 
 #ifdef CONFIG_PCIEAER
 void pci_no_aer(void);
+bool pci_aer_available(void);
 #else
 static inline void pci_no_aer(void) { }
+static inline bool pci_aer_available(void) { return false; }
 #endif
 
 static inline int pci_no_d1d2(struct pci_dev *dev)
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv.c
@@ -72,6 +72,11 @@ void pci_no_aer(void)
 	pcie_aer_disable = 1;	/* has priority over 'forceload' */
 }
 
+bool pci_aer_available(void)
+{
+	return !pcie_aer_disable && pci_msi_enabled();
+}
+
 static int set_device_error_reporting(struct pci_dev *dev, void *data)
 {
 	bool enable = *((bool *)data);
@@ -411,9 +416,7 @@ static void aer_error_resume(struct pci_
  */
 static int __init aer_service_init(void)
 {
-	if (pcie_aer_disable)
-		return -ENXIO;
-	if (!pci_msi_enabled())
+	if (!pci_aer_available())
 		return -ENXIO;
 	return pcie_port_service_register(&aerdriver);
 }


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 1/10] PCI / PCIe/ AER: Introduce pci_aer_available()
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
@ 2010-08-20 23:50   ` Rafael J. Wysocki
  2010-08-20 23:50   ` Rafael J. Wysocki
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:50 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce a function allowing the caller to check whether to try to
enable PCIe AER.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/pci/pci.h             |    2 ++
 drivers/pci/pcie/aer/aerdrv.c |    9 ++++++---
 2 files changed, 8 insertions(+), 3 deletions(-)

Index: linux-2.6/drivers/pci/pci.h
===================================================================
--- linux-2.6.orig/drivers/pci/pci.h
+++ linux-2.6/drivers/pci/pci.h
@@ -140,8 +140,10 @@ static inline void pci_msi_init_pci_dev(
 
 #ifdef CONFIG_PCIEAER
 void pci_no_aer(void);
+bool pci_aer_available(void);
 #else
 static inline void pci_no_aer(void) { }
+static inline bool pci_aer_available(void) { return false; }
 #endif
 
 static inline int pci_no_d1d2(struct pci_dev *dev)
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv.c
@@ -72,6 +72,11 @@ void pci_no_aer(void)
 	pcie_aer_disable = 1;	/* has priority over 'forceload' */
 }
 
+bool pci_aer_available(void)
+{
+	return !pcie_aer_disable && pci_msi_enabled();
+}
+
 static int set_device_error_reporting(struct pci_dev *dev, void *data)
 {
 	bool enable = *((bool *)data);
@@ -411,9 +416,7 @@ static void aer_error_resume(struct pci_
  */
 static int __init aer_service_init(void)
 {
-	if (pcie_aer_disable)
-		return -ENXIO;
-	if (!pci_msi_enabled())
+	if (!pci_aer_available())
 		return -ENXIO;
 	return pcie_port_service_register(&aerdriver);
 }

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 2/10] PCI / PCIe: Introduce commad line switch for disabling port services
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (2 preceding siblings ...)
  2010-08-20 23:51   ` [PATCH 2/10] PCI / PCIe: Introduce commad line switch for disabling port services Rafael J. Wysocki
@ 2010-08-20 23:51   ` Rafael J. Wysocki
  2010-08-20 23:52   ` [PATCH 3/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set() Rafael J. Wysocki
                     ` (15 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:51 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce kernel command line switch pcie_ports= allowing one to
disable all of the native PCIe port services, so that PCIe ports
are treated like PCI-to-PCI bridges.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/kernel-parameters.txt |    4 ++++
 drivers/pci/pcie/portdrv.h          |    2 ++
 drivers/pci/pcie/portdrv_core.c     |    3 +++
 drivers/pci/pcie/portdrv_pci.c      |   15 +++++++++++++++
 4 files changed, 24 insertions(+)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -29,6 +29,18 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
+/* If this switch is set, PCIe port native services should not be enabled. */
+bool pcie_ports_disabled;
+
+static int __init pcie_port_setup(char *str)
+{
+	if (!strncmp(str, "compat", 6))
+		pcie_ports_disabled = true;
+
+	return 1;
+}
+__setup("pcie_ports=", pcie_port_setup);
+
 /* global data */
 
 static int pcie_portdrv_restore_config(struct pci_dev *dev)
@@ -301,6 +313,9 @@ static int __init pcie_portdrv_init(void
 {
 	int retval;
 
+	if (pcie_ports_disabled)
+		return -EACCES;
+
 	dmi_check_system(pcie_portdrv_dmi_table);
 
 	retval = pcie_port_bus_register();
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.orig/Documentation/kernel-parameters.txt
+++ linux-2.6/Documentation/kernel-parameters.txt
@@ -1974,6 +1974,10 @@ and is between 256 and 4096 characters. 
 		force	Enable ASPM even on devices that claim not to support it.
 			WARNING: Forcing ASPM on may cause system lockups.
 
+	pcie_ports=	[PCIE] PCIe ports handling:
+		compat	Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
+			ports driver.
+
 	pcie_pme=	[PCIE,PM] Native PCIe PME signaling options:
 			Format: {auto|force}[,nomsi]
 		auto	Use native PCIe PME signaling if the BIOS allows the
Index: linux-2.6/drivers/pci/pcie/portdrv.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv.h
+++ linux-2.6/drivers/pci/pcie/portdrv.h
@@ -20,6 +20,8 @@
 
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
+extern bool pcie_ports_disabled;
+
 extern struct bus_type pcie_port_bus_type;
 extern int pcie_port_device_register(struct pci_dev *dev);
 #ifdef CONFIG_PM
Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -494,6 +494,9 @@ static void pcie_port_shutdown_service(s
  */
 int pcie_port_service_register(struct pcie_port_service_driver *new)
 {
+	if (pcie_ports_disabled)
+		return -ENODEV;
+
 	new->driver.name = (char *)new->name;
 	new->driver.bus = &pcie_port_bus_type;
 	new->driver.probe = pcie_port_probe_service;

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 2/10] PCI / PCIe: Introduce commad line switch for disabling port services
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
  2010-08-20 23:50   ` [PATCH 1/10] PCI / PCIe/ AER: Introduce pci_aer_available() Rafael J. Wysocki
  2010-08-20 23:50   ` Rafael J. Wysocki
@ 2010-08-20 23:51   ` Rafael J. Wysocki
  2010-08-20 23:51   ` Rafael J. Wysocki
                     ` (16 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:51 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce kernel command line switch pcie_ports= allowing one to
disable all of the native PCIe port services, so that PCIe ports
are treated like PCI-to-PCI bridges.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/kernel-parameters.txt |    4 ++++
 drivers/pci/pcie/portdrv.h          |    2 ++
 drivers/pci/pcie/portdrv_core.c     |    3 +++
 drivers/pci/pcie/portdrv_pci.c      |   15 +++++++++++++++
 4 files changed, 24 insertions(+)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -29,6 +29,18 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
+/* If this switch is set, PCIe port native services should not be enabled. */
+bool pcie_ports_disabled;
+
+static int __init pcie_port_setup(char *str)
+{
+	if (!strncmp(str, "compat", 6))
+		pcie_ports_disabled = true;
+
+	return 1;
+}
+__setup("pcie_ports=", pcie_port_setup);
+
 /* global data */
 
 static int pcie_portdrv_restore_config(struct pci_dev *dev)
@@ -301,6 +313,9 @@ static int __init pcie_portdrv_init(void
 {
 	int retval;
 
+	if (pcie_ports_disabled)
+		return -EACCES;
+
 	dmi_check_system(pcie_portdrv_dmi_table);
 
 	retval = pcie_port_bus_register();
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.orig/Documentation/kernel-parameters.txt
+++ linux-2.6/Documentation/kernel-parameters.txt
@@ -1974,6 +1974,10 @@ and is between 256 and 4096 characters. 
 		force	Enable ASPM even on devices that claim not to support it.
 			WARNING: Forcing ASPM on may cause system lockups.
 
+	pcie_ports=	[PCIE] PCIe ports handling:
+		compat	Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
+			ports driver.
+
 	pcie_pme=	[PCIE,PM] Native PCIe PME signaling options:
 			Format: {auto|force}[,nomsi]
 		auto	Use native PCIe PME signaling if the BIOS allows the
Index: linux-2.6/drivers/pci/pcie/portdrv.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv.h
+++ linux-2.6/drivers/pci/pcie/portdrv.h
@@ -20,6 +20,8 @@
 
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
+extern bool pcie_ports_disabled;
+
 extern struct bus_type pcie_port_bus_type;
 extern int pcie_port_device_register(struct pci_dev *dev);
 #ifdef CONFIG_PM
Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -494,6 +494,9 @@ static void pcie_port_shutdown_service(s
  */
 int pcie_port_service_register(struct pcie_port_service_driver *new)
 {
+	if (pcie_ports_disabled)
+		return -ENODEV;
+
 	new->driver.name = (char *)new->name;
 	new->driver.bus = &pcie_port_bus_type;
 	new->driver.probe = pcie_port_probe_service;

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 3/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set()
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (3 preceding siblings ...)
  2010-08-20 23:51   ` Rafael J. Wysocki
@ 2010-08-20 23:52   ` Rafael J. Wysocki
  2010-08-20 23:52   ` Rafael J. Wysocki
                     ` (14 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:52 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

Make acpi_pci_osc_control_set() attempt to find the handle of the
_OSC object under the given PCI root bridge object after verifying
that its second argument is correct and that there is a struct
acpi_pci_root object for the given root bridge handle, which is
more logical than the old code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/acpi/pci_root.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -378,10 +378,6 @@ acpi_status acpi_pci_osc_control_set(acp
 	acpi_handle tmp;
 	struct acpi_pci_root *root;
 
-	status = acpi_get_handle(handle, "_OSC", &tmp);
-	if (ACPI_FAILURE(status))
-		return status;
-
 	control_req = (flags & OSC_PCI_CONTROL_MASKS);
 	if (!control_req)
 		return AE_TYPE;
@@ -390,6 +386,10 @@ acpi_status acpi_pci_osc_control_set(acp
 	if (!root)
 		return AE_NOT_EXIST;
 
+	status = acpi_get_handle(handle, "_OSC", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
 	mutex_lock(&osc_lock);
 	/* No need to evaluate _OSC if the control was already granted. */
 	if ((root->osc_control_set & control_req) == control_req)


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 3/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set()
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (4 preceding siblings ...)
  2010-08-20 23:52   ` [PATCH 3/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set() Rafael J. Wysocki
@ 2010-08-20 23:52   ` Rafael J. Wysocki
  2010-08-20 23:53   ` [PATCH 4/10] ACPI / PCI: Make acpi_pci_query_osc() return control bits Rafael J. Wysocki
                     ` (13 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:52 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

Make acpi_pci_osc_control_set() attempt to find the handle of the
_OSC object under the given PCI root bridge object after verifying
that its second argument is correct and that there is a struct
acpi_pci_root object for the given root bridge handle, which is
more logical than the old code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/acpi/pci_root.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -378,10 +378,6 @@ acpi_status acpi_pci_osc_control_set(acp
 	acpi_handle tmp;
 	struct acpi_pci_root *root;
 
-	status = acpi_get_handle(handle, "_OSC", &tmp);
-	if (ACPI_FAILURE(status))
-		return status;
-
 	control_req = (flags & OSC_PCI_CONTROL_MASKS);
 	if (!control_req)
 		return AE_TYPE;
@@ -390,6 +386,10 @@ acpi_status acpi_pci_osc_control_set(acp
 	if (!root)
 		return AE_NOT_EXIST;
 
+	status = acpi_get_handle(handle, "_OSC", &tmp);
+	if (ACPI_FAILURE(status))
+		return status;
+
 	mutex_lock(&osc_lock);
 	/* No need to evaluate _OSC if the control was already granted. */
 	if ((root->osc_control_set & control_req) == control_req)

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 4/10] ACPI / PCI: Make acpi_pci_query_osc() return control bits
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (5 preceding siblings ...)
  2010-08-20 23:52   ` Rafael J. Wysocki
@ 2010-08-20 23:53   ` Rafael J. Wysocki
  2010-08-20 23:53   ` Rafael J. Wysocki
                     ` (12 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:53 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

Make acpi_pci_query_osc() use an additional pointer argument to
return the mask of control bits obtained from the BIOS to the
caller.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   77 +++++++++++++++++++++++++++++++++++++++++-------
 include/linux/acpi.h    |    1 
 2 files changed, 67 insertions(+), 11 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -226,22 +226,35 @@ static acpi_status acpi_pci_run_osc(acpi
 	return status;
 }
 
-static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
+static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root,
+					u32 support,
+					u32 *control)
 {
 	acpi_status status;
-	u32 support_set, result, capbuf[3];
+	u32 result, capbuf[3];
+
+	support &= OSC_PCI_SUPPORT_MASKS;
+	support |= root->osc_support_set;
 
-	/* do _OSC query for all possible controls */
-	support_set = root->osc_support_set | (flags & OSC_PCI_SUPPORT_MASKS);
 	capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
-	capbuf[OSC_SUPPORT_TYPE] = support_set;
-	capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
+	capbuf[OSC_SUPPORT_TYPE] = support;
+	if (control) {
+		*control &= OSC_PCI_CONTROL_MASKS;
+		capbuf[OSC_CONTROL_TYPE] = *control | root->osc_control_set;
+	} else {
+		/* Run _OSC query for all possible controls. */
+		capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
+	}
 
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
-		root->osc_support_set = support_set;
-		root->osc_control_qry = result;
-		root->osc_queried = 1;
+		root->osc_support_set = support;
+		if (control) {
+			*control = result;
+		} else {
+			root->osc_control_qry = result;
+			root->osc_queried = 1;
+		}
 	}
 	return status;
 }
@@ -255,7 +268,7 @@ static acpi_status acpi_pci_osc_support(
 	if (ACPI_FAILURE(status))
 		return status;
 	mutex_lock(&osc_lock);
-	status = acpi_pci_query_osc(root, flags);
+	status = acpi_pci_query_osc(root, flags, NULL);
 	mutex_unlock(&osc_lock);
 	return status;
 }
@@ -397,7 +452,7 @@ acpi_status acpi_pci_osc_control_set(acp
 
 	/* Need to query controls first before requesting them */
 	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set);
+		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
 		if (ACPI_FAILURE(status))
 			goto out;
 	}


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 4/10] ACPI / PCI: Make acpi_pci_query_osc() return control bits
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (6 preceding siblings ...)
  2010-08-20 23:53   ` [PATCH 4/10] ACPI / PCI: Make acpi_pci_query_osc() return control bits Rafael J. Wysocki
@ 2010-08-20 23:53   ` Rafael J. Wysocki
  2010-08-20 23:54   ` [PATCH 5/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4) Rafael J. Wysocki
                     ` (11 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:53 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

Make acpi_pci_query_osc() use an additional pointer argument to
return the mask of control bits obtained from the BIOS to the
caller.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   77 +++++++++++++++++++++++++++++++++++++++++-------
 include/linux/acpi.h    |    1 
 2 files changed, 67 insertions(+), 11 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -226,22 +226,35 @@ static acpi_status acpi_pci_run_osc(acpi
 	return status;
 }
 
-static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
+static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root,
+					u32 support,
+					u32 *control)
 {
 	acpi_status status;
-	u32 support_set, result, capbuf[3];
+	u32 result, capbuf[3];
+
+	support &= OSC_PCI_SUPPORT_MASKS;
+	support |= root->osc_support_set;
 
-	/* do _OSC query for all possible controls */
-	support_set = root->osc_support_set | (flags & OSC_PCI_SUPPORT_MASKS);
 	capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
-	capbuf[OSC_SUPPORT_TYPE] = support_set;
-	capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
+	capbuf[OSC_SUPPORT_TYPE] = support;
+	if (control) {
+		*control &= OSC_PCI_CONTROL_MASKS;
+		capbuf[OSC_CONTROL_TYPE] = *control | root->osc_control_set;
+	} else {
+		/* Run _OSC query for all possible controls. */
+		capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
+	}
 
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
-		root->osc_support_set = support_set;
-		root->osc_control_qry = result;
-		root->osc_queried = 1;
+		root->osc_support_set = support;
+		if (control) {
+			*control = result;
+		} else {
+			root->osc_control_qry = result;
+			root->osc_queried = 1;
+		}
 	}
 	return status;
 }
@@ -255,7 +268,7 @@ static acpi_status acpi_pci_osc_support(
 	if (ACPI_FAILURE(status))
 		return status;
 	mutex_lock(&osc_lock);
-	status = acpi_pci_query_osc(root, flags);
+	status = acpi_pci_query_osc(root, flags, NULL);
 	mutex_unlock(&osc_lock);
 	return status;
 }
@@ -397,7 +452,7 @@ acpi_status acpi_pci_osc_control_set(acp
 
 	/* Need to query controls first before requesting them */
 	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set);
+		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
 		if (ACPI_FAILURE(status))
 			goto out;
 	}

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 5/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4)
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (8 preceding siblings ...)
  2010-08-20 23:54   ` [PATCH 5/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4) Rafael J. Wysocki
@ 2010-08-20 23:54   ` Rafael J. Wysocki
  2010-08-23 21:55     ` [Updated changelog][PATCH " Rafael J. Wysocki
  2010-08-23 21:55     ` Rafael J. Wysocki
  2010-08-20 23:55   ` [PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them Rafael J. Wysocki
                     ` (9 subsequent siblings)
  19 siblings, 2 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:54 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

There is the assumption in acpi_pci_osc_control_set() that it is
always sufficient to compare the mask of _OSC control bits to be
requested with the result of an _OSC query where all of the known
control bits have been checked.  However, in general, that need not
be the case.  For example, if an _OSC feature A depends on an _OSC
feature B and control of A, B plus another _OSC feature C is
requested simultaneously, the BIOS may return A, B, C, while it would
only return C if A and C were requested without B.

That may result in passing a wrong mask of _OSC control bits to an
_OSC control request, in which case the BIOS may only grant control
of a subset of the requested features.  Moreover, acpi_pci_run_osc()
will return error code if that happens and the caller of
acpi_pci_osc_control_set() will not know that it's been granted
control of some _OSC features.  Consequently, the system will
generally not work as expected.

For this reason, make acpi_pci_osc_control_set() always check if
control of the requested _OSC features will be granted before making
the final control request.  As a result, the osc_control_qry and
osc_queried members of struct acpi_pci_root are not necessary any
more, so drop them and remove the remaining code referring to them.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   20 +++++++-------------
 include/acpi/acpi_bus.h |    3 ---
 2 files changed, 7 insertions(+), 16 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -249,12 +249,8 @@ static acpi_status acpi_pci_query_osc(st
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
 		root->osc_support_set = support;
-		if (control) {
+		if (control)
 			*control = result;
-		} else {
-			root->osc_control_qry = result;
-			root->osc_queried = 1;
-		}
 	}
 	return status;
 }
@@ -409,14 +405,12 @@ acpi_status acpi_pci_osc_control_set(acp
 		goto out;
 
 	/* Need to query controls first before requesting them */
-	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
-		if (ACPI_FAILURE(status))
-			goto out;
-	}
-	if ((root->osc_control_qry & control_req) != control_req) {
-		printk(KERN_DEBUG
-		       "Firmware did not grant requested _OSC control\n");
+	flags = control_req;
+	status = acpi_pci_query_osc(root, root->osc_support_set, &flags);
+	if (ACPI_FAILURE(status))
+		goto out;
+
+	if (flags != control_req) {
 		status = AE_SUPPORT;
 		goto out;
 	}
Index: linux-2.6/include/acpi/acpi_bus.h
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -377,9 +377,6 @@ struct acpi_pci_root {
 
 	u32 osc_support_set;	/* _OSC state of support bits */
 	u32 osc_control_set;	/* _OSC state of control bits */
-	u32 osc_control_qry;	/* the latest _OSC query result */
-
-	u32 osc_queried:1;	/* has _OSC control been queried? */
 };
 
 /* helper */

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 5/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4)
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (7 preceding siblings ...)
  2010-08-20 23:53   ` Rafael J. Wysocki
@ 2010-08-20 23:54   ` Rafael J. Wysocki
  2010-08-20 23:54   ` Rafael J. Wysocki
                     ` (10 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:54 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

There is the assumption in acpi_pci_osc_control_set() that it is
always sufficient to compare the mask of _OSC control bits to be
requested with the result of an _OSC query where all of the known
control bits have been checked.  However, in general, that need not
be the case.  For example, if an _OSC feature A depends on an _OSC
feature B and control of A, B plus another _OSC feature C is
requested simultaneously, the BIOS may return A, B, C, while it would
only return C if A and C were requested without B.

That may result in passing a wrong mask of _OSC control bits to an
_OSC control request, in which case the BIOS may only grant control
of a subset of the requested features.  Moreover, acpi_pci_run_osc()
will return error code if that happens and the caller of
acpi_pci_osc_control_set() will not know that it's been granted
control of some _OSC features.  Consequently, the system will
generally not work as expected.

For this reason, make acpi_pci_osc_control_set() always check if
control of the requested _OSC features will be granted before making
the final control request.  As a result, the osc_control_qry and
osc_queried members of struct acpi_pci_root are not necessary any
more, so drop them and remove the remaining code referring to them.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   20 +++++++-------------
 include/acpi/acpi_bus.h |    3 ---
 2 files changed, 7 insertions(+), 16 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -249,12 +249,8 @@ static acpi_status acpi_pci_query_osc(st
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
 		root->osc_support_set = support;
-		if (control) {
+		if (control)
 			*control = result;
-		} else {
-			root->osc_control_qry = result;
-			root->osc_queried = 1;
-		}
 	}
 	return status;
 }
@@ -409,14 +405,12 @@ acpi_status acpi_pci_osc_control_set(acp
 		goto out;
 
 	/* Need to query controls first before requesting them */
-	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
-		if (ACPI_FAILURE(status))
-			goto out;
-	}
-	if ((root->osc_control_qry & control_req) != control_req) {
-		printk(KERN_DEBUG
-		       "Firmware did not grant requested _OSC control\n");
+	flags = control_req;
+	status = acpi_pci_query_osc(root, root->osc_support_set, &flags);
+	if (ACPI_FAILURE(status))
+		goto out;
+
+	if (flags != control_req) {
 		status = AE_SUPPORT;
 		goto out;
 	}
Index: linux-2.6/include/acpi/acpi_bus.h
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -377,9 +377,6 @@ struct acpi_pci_root {
 
 	u32 osc_support_set;	/* _OSC state of support bits */
 	u32 osc_control_set;	/* _OSC state of control bits */
-	u32 osc_control_qry;	/* the latest _OSC query result */
-
-	u32 osc_queried:1;	/* has _OSC control been queried? */
 };
 
 /* helper */

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (10 preceding siblings ...)
  2010-08-20 23:55   ` [PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them Rafael J. Wysocki
@ 2010-08-20 23:55   ` Rafael J. Wysocki
  2010-08-23 21:53     ` [Update][PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them (v2) Rafael J. Wysocki
  2010-08-23 21:53     ` Rafael J. Wysocki
  2010-08-20 23:56   ` [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7) Rafael J. Wysocki
                     ` (7 subsequent siblings)
  19 siblings, 2 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:55 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

It is possible that the BIOS will not grant control of all _OSC
features requested via acpi_pci_osc_control_set(), so it is
recommended to negotiate the final set of _OSC features with the
query flag set before calling _OSC to request control of these
features.

To implement it, rework acpi_pci_osc_control_set() so that the caller
can specify the mask of _OSC control bits to negotiate and the mask
of _OSC control bits that are absolutely necessary to it.  Then,
acpi_pci_osc_control_set() will run _OSC queries in a loop until
the mask of _OSC control bits returned by the BIOS is equal to the
mask passed to it.  Also, before running the _OSC request
acpi_pci_osc_control_set() will check if the caller's required
control bits are present in the final mask.

Using this mechanism we will be able to avoid situations in which the
BIOS doesn't grant control of certain _OSC features, because they
depend on some other _OSC features that have not been requested.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c              |   56 +++++++++++++++++++++--------------
 drivers/pci/hotplug/acpi_pcihp.c     |    2 -
 drivers/pci/pcie/aer/aerdrv_acpi.c   |    6 +--
 drivers/pci/pcie/pme/pcie_pme_acpi.c |    8 +++--
 include/linux/acpi.h                 |    4 +-
 5 files changed, 46 insertions(+), 30 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -374,21 +374,32 @@ out:
 EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
 
 /**
- * acpi_pci_osc_control_set - commit requested control to Firmware
- * @handle: acpi_handle for the target ACPI object
- * @flags: driver's requested control bits
+ * acpi_pci_osc_control_set - Request control of PCI root _OSC features.
+ * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
+ * @mask: Mask of _OSC bits to request control of, place to store control mask.
+ * @req: Mask of _OSC bits the control of is essential to the caller.
  *
- * Attempt to take control from Firmware on requested control bits.
+ * Run _OSC query for @mask and if that is successful, compare the returned
+ * mask of control bits with @req.  If all of the @req bits are set in the
+ * returned mask, run _OSC request for it.
+ *
+ * The variable at the @mask address may be modified regardless of whether or
+ * not the function returns success.  On success it will contain the mask of
+ * _OSC bits the BIOS has granted control of, but its contents are meaningless
+ * on failure.
  **/
-acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
+acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
 {
+	struct acpi_pci_root *root;
 	acpi_status status;
-	u32 control_req, result, capbuf[3];
+	u32 ctrl, capbuf[3];
 	acpi_handle tmp;
-	struct acpi_pci_root *root;
 
-	control_req = (flags & OSC_PCI_CONTROL_MASKS);
-	if (!control_req)
+	if (!mask)
+		return AE_BAD_PARAMETER;
+
+	ctrl = *mask & OSC_PCI_CONTROL_MASKS;
+	if ((ctrl & req) != req)
 		return AE_TYPE;
 
 	root = acpi_pci_find_root(handle);
@@ -400,27 +411,30 @@ acpi_status acpi_pci_osc_control_set(acp
 		return status;
 
 	mutex_lock(&osc_lock);
+
+	*mask |= root->osc_control_set;
 	/* No need to evaluate _OSC if the control was already granted. */
-	if ((root->osc_control_set & control_req) == control_req)
+	if ((root->osc_control_set & ctrl) == ctrl)
 		goto out;
 
-	/* Need to query controls first before requesting them */
-	flags = control_req;
-	status = acpi_pci_query_osc(root, root->osc_support_set, &flags);
-	if (ACPI_FAILURE(status))
-		goto out;
+	/* Need to check the available controls bits before requesting them. */
+	for (ctrl = 0; *mask != ctrl; ctrl = *mask) {
+		status = acpi_pci_query_osc(root, root->osc_support_set, mask);
+		if (ACPI_FAILURE(status))
+			goto out;
+	}
 
-	if (flags != control_req) {
+	if ((ctrl & req) != req) {
 		status = AE_SUPPORT;
 		goto out;
 	}
 
 	capbuf[OSC_QUERY_TYPE] = 0;
 	capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
-	capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
-	status = acpi_pci_run_osc(handle, capbuf, &result);
+	capbuf[OSC_CONTROL_TYPE] = ctrl;
+	status = acpi_pci_run_osc(handle, capbuf, mask);
 	if (ACPI_SUCCESS(status))
-		root->osc_control_set = result;
+		root->osc_control_set = *mask;
 out:
 	mutex_unlock(&osc_lock);
 	return status;
@@ -551,8 +565,8 @@ static int __devinit acpi_pci_root_add(s
 	if (flags != base_flags)
 		acpi_pci_osc_support(root, flags);
 
-	status = acpi_pci_osc_control_set(root->device->handle,
-					  OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+	status = acpi_pci_osc_control_set(root->device->handle, &flags, flags);
 
 	if (ACPI_FAILURE(status)) {
 		printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
Index: linux-2.6/include/linux/acpi.h
===================================================================
--- linux-2.6.orig/include/linux/acpi.h
+++ linux-2.6/include/linux/acpi.h
@@ -304,8 +304,8 @@ acpi_status acpi_run_osc(acpi_handle han
 				OSC_PCI_EXPRESS_PME_CONTROL |		\
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
-
-extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
+extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
+					     u32 *mask, u32 req);
 extern void acpi_early_init(void);
 
 #else	/* !CONFIG_ACPI */
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -39,9 +39,9 @@ int aer_osc_setup(struct pcie_device *pc
 
 	handle = acpi_find_root_bridge_handle(pdev);
 	if (handle) {
-		status = acpi_pci_osc_control_set(handle,
-					OSC_PCI_EXPRESS_AER_CONTROL |
-					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+		u32 flags = OSC_PCI_EXPRESS_AER_CONTROL |
+				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
 	}
 
 	if (ACPI_FAILURE(status)) {
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme_acpi.c
+++ linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
@@ -28,6 +28,7 @@ int pcie_pme_acpi_setup(struct pcie_devi
 	acpi_status status = AE_NOT_FOUND;
 	struct pci_dev *port = srv->port;
 	acpi_handle handle;
+	u32 flags;
 	int error = 0;
 
 	if (acpi_pci_disabled)
@@ -39,9 +40,10 @@ int pcie_pme_acpi_setup(struct pcie_devi
 	if (!handle)
 		return -EINVAL;
 
-	status = acpi_pci_osc_control_set(handle,
-			OSC_PCI_EXPRESS_PME_CONTROL |
-			OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags = OSC_PCI_EXPRESS_PME_CONTROL |
+		OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+
+	status = acpi_pci_osc_control_set(handle, &flags, flags);
 	if (ACPI_FAILURE(status)) {
 		dev_info(&port->dev,
 			"Failed to receive control of PCIe PME service: %s\n",
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -360,7 +360,7 @@ int acpi_get_hp_hw_control_from_firmware
 		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
 		dbg("Trying to get hotplug control for %s\n",
 				(char *)string.pointer);
-		status = acpi_pci_osc_control_set(handle, flags);
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
 		if (status == AE_SUPPORT)

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (9 preceding siblings ...)
  2010-08-20 23:54   ` Rafael J. Wysocki
@ 2010-08-20 23:55   ` Rafael J. Wysocki
  2010-08-20 23:55   ` Rafael J. Wysocki
                     ` (8 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:55 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

It is possible that the BIOS will not grant control of all _OSC
features requested via acpi_pci_osc_control_set(), so it is
recommended to negotiate the final set of _OSC features with the
query flag set before calling _OSC to request control of these
features.

To implement it, rework acpi_pci_osc_control_set() so that the caller
can specify the mask of _OSC control bits to negotiate and the mask
of _OSC control bits that are absolutely necessary to it.  Then,
acpi_pci_osc_control_set() will run _OSC queries in a loop until
the mask of _OSC control bits returned by the BIOS is equal to the
mask passed to it.  Also, before running the _OSC request
acpi_pci_osc_control_set() will check if the caller's required
control bits are present in the final mask.

Using this mechanism we will be able to avoid situations in which the
BIOS doesn't grant control of certain _OSC features, because they
depend on some other _OSC features that have not been requested.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c              |   56 +++++++++++++++++++++--------------
 drivers/pci/hotplug/acpi_pcihp.c     |    2 -
 drivers/pci/pcie/aer/aerdrv_acpi.c   |    6 +--
 drivers/pci/pcie/pme/pcie_pme_acpi.c |    8 +++--
 include/linux/acpi.h                 |    4 +-
 5 files changed, 46 insertions(+), 30 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -374,21 +374,32 @@ out:
 EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
 
 /**
- * acpi_pci_osc_control_set - commit requested control to Firmware
- * @handle: acpi_handle for the target ACPI object
- * @flags: driver's requested control bits
+ * acpi_pci_osc_control_set - Request control of PCI root _OSC features.
+ * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
+ * @mask: Mask of _OSC bits to request control of, place to store control mask.
+ * @req: Mask of _OSC bits the control of is essential to the caller.
  *
- * Attempt to take control from Firmware on requested control bits.
+ * Run _OSC query for @mask and if that is successful, compare the returned
+ * mask of control bits with @req.  If all of the @req bits are set in the
+ * returned mask, run _OSC request for it.
+ *
+ * The variable at the @mask address may be modified regardless of whether or
+ * not the function returns success.  On success it will contain the mask of
+ * _OSC bits the BIOS has granted control of, but its contents are meaningless
+ * on failure.
  **/
-acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
+acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
 {
+	struct acpi_pci_root *root;
 	acpi_status status;
-	u32 control_req, result, capbuf[3];
+	u32 ctrl, capbuf[3];
 	acpi_handle tmp;
-	struct acpi_pci_root *root;
 
-	control_req = (flags & OSC_PCI_CONTROL_MASKS);
-	if (!control_req)
+	if (!mask)
+		return AE_BAD_PARAMETER;
+
+	ctrl = *mask & OSC_PCI_CONTROL_MASKS;
+	if ((ctrl & req) != req)
 		return AE_TYPE;
 
 	root = acpi_pci_find_root(handle);
@@ -400,27 +411,30 @@ acpi_status acpi_pci_osc_control_set(acp
 		return status;
 
 	mutex_lock(&osc_lock);
+
+	*mask |= root->osc_control_set;
 	/* No need to evaluate _OSC if the control was already granted. */
-	if ((root->osc_control_set & control_req) == control_req)
+	if ((root->osc_control_set & ctrl) == ctrl)
 		goto out;
 
-	/* Need to query controls first before requesting them */
-	flags = control_req;
-	status = acpi_pci_query_osc(root, root->osc_support_set, &flags);
-	if (ACPI_FAILURE(status))
-		goto out;
+	/* Need to check the available controls bits before requesting them. */
+	for (ctrl = 0; *mask != ctrl; ctrl = *mask) {
+		status = acpi_pci_query_osc(root, root->osc_support_set, mask);
+		if (ACPI_FAILURE(status))
+			goto out;
+	}
 
-	if (flags != control_req) {
+	if ((ctrl & req) != req) {
 		status = AE_SUPPORT;
 		goto out;
 	}
 
 	capbuf[OSC_QUERY_TYPE] = 0;
 	capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
-	capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
-	status = acpi_pci_run_osc(handle, capbuf, &result);
+	capbuf[OSC_CONTROL_TYPE] = ctrl;
+	status = acpi_pci_run_osc(handle, capbuf, mask);
 	if (ACPI_SUCCESS(status))
-		root->osc_control_set = result;
+		root->osc_control_set = *mask;
 out:
 	mutex_unlock(&osc_lock);
 	return status;
@@ -551,8 +565,8 @@ static int __devinit acpi_pci_root_add(s
 	if (flags != base_flags)
 		acpi_pci_osc_support(root, flags);
 
-	status = acpi_pci_osc_control_set(root->device->handle,
-					  OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+	status = acpi_pci_osc_control_set(root->device->handle, &flags, flags);
 
 	if (ACPI_FAILURE(status)) {
 		printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
Index: linux-2.6/include/linux/acpi.h
===================================================================
--- linux-2.6.orig/include/linux/acpi.h
+++ linux-2.6/include/linux/acpi.h
@@ -304,8 +304,8 @@ acpi_status acpi_run_osc(acpi_handle han
 				OSC_PCI_EXPRESS_PME_CONTROL |		\
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
-
-extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
+extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
+					     u32 *mask, u32 req);
 extern void acpi_early_init(void);
 
 #else	/* !CONFIG_ACPI */
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -39,9 +39,9 @@ int aer_osc_setup(struct pcie_device *pc
 
 	handle = acpi_find_root_bridge_handle(pdev);
 	if (handle) {
-		status = acpi_pci_osc_control_set(handle,
-					OSC_PCI_EXPRESS_AER_CONTROL |
-					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+		u32 flags = OSC_PCI_EXPRESS_AER_CONTROL |
+				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
 	}
 
 	if (ACPI_FAILURE(status)) {
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme_acpi.c
+++ linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
@@ -28,6 +28,7 @@ int pcie_pme_acpi_setup(struct pcie_devi
 	acpi_status status = AE_NOT_FOUND;
 	struct pci_dev *port = srv->port;
 	acpi_handle handle;
+	u32 flags;
 	int error = 0;
 
 	if (acpi_pci_disabled)
@@ -39,9 +40,10 @@ int pcie_pme_acpi_setup(struct pcie_devi
 	if (!handle)
 		return -EINVAL;
 
-	status = acpi_pci_osc_control_set(handle,
-			OSC_PCI_EXPRESS_PME_CONTROL |
-			OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags = OSC_PCI_EXPRESS_PME_CONTROL |
+		OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+
+	status = acpi_pci_osc_control_set(handle, &flags, flags);
 	if (ACPI_FAILURE(status)) {
 		dev_info(&port->dev,
 			"Failed to receive control of PCIe PME service: %s\n",
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -360,7 +360,7 @@ int acpi_get_hp_hw_control_from_firmware
 		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
 		dbg("Trying to get hotplug control for %s\n",
 				(char *)string.pointer);
-		status = acpi_pci_osc_control_set(handle, flags);
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
 		if (status == AE_SUPPORT)

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7)
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (11 preceding siblings ...)
  2010-08-20 23:55   ` Rafael J. Wysocki
@ 2010-08-20 23:56   ` Rafael J. Wysocki
  2010-08-21 20:02     ` Rafael J. Wysocki
  2010-08-21 20:02     ` [linux-pm] " Rafael J. Wysocki
  2010-08-20 23:56   ` Rafael J. Wysocki
                     ` (6 subsequent siblings)
  19 siblings, 2 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:56 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

PCIe port service drivers ask the BIOS, through _OSC, for control of
the services they handle.  Unfortunately, each of them individually
asks for control of the PCIe capability structure and if that is
granted, some BIOSes expect that the other PCIe port services will be
configured and handled by the kernel as well.  If that is not the
case (eg. one of the PCIe port service drivers is not loaded), the
BIOS may be confused and may cause the system as a whole to misbehave
(eg. on one of such systems enabling the native PCIe PME service
without loading the native PCIe hot-plug service driver causes a
storm of ACPI notify requests to appear).

For this reason rework the PCIe port driver so that (1) it checks
which native PCIe port services can be enabled, according to the
BIOS, and (2) it requests control of all these services
simultaneously.  In particular, this causes pcie_portdrv_probe() to
fail if the BIOS refuses to grant control of the PCIe capability
structure, which means that no native PCIe port services can be
enabled for the PCIe root complex the given port belongs to.

Make it possible to override this behavior using 'pcie_ports=native'
(use the PCIe native services regardless of the BIOS response to the
control request), or 'pcie_ports=compat' (do not use the PCIe native
services at all).

Accordingly, rework the existing PCIe port service drivers so that
they don't request control of the services directly.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/kernel-parameters.txt  |   13 ++---
 drivers/acpi/pci_root.c              |    9 ----
 drivers/pci/hotplug/acpi_pcihp.c     |    4 -
 drivers/pci/hotplug/pciehp.h         |   12 -----
 drivers/pci/hotplug/pciehp_acpi.c    |    4 -
 drivers/pci/hotplug/pciehp_core.c    |    4 -
 drivers/pci/pcie/Makefile            |    1 
 drivers/pci/pcie/aer/aerdrv_acpi.c   |   36 ----------------
 drivers/pci/pcie/aer/aerdrv_core.c   |   14 ------
 drivers/pci/pcie/pme/Makefile        |    5 --
 drivers/pci/pcie/pme/pcie_pme.c      |   64 ++---------------------------
 drivers/pci/pcie/pme/pcie_pme.h      |   28 ------------
 drivers/pci/pcie/pme/pcie_pme_acpi.c |   56 -------------------------
 drivers/pci/pcie/portdrv.h           |   20 +++++++++
 drivers/pci/pcie/portdrv_acpi.c      |   77 +++++++++++++++++++++++++++++++++++
 drivers/pci/pcie/portdrv_core.c      |   25 ++++++++++-
 drivers/pci/pcie/portdrv_pci.c       |   20 ++++++++-
 17 files changed, 155 insertions(+), 237 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -15,6 +15,7 @@
 #include <linux/pcieport_if.h>
 #include <linux/aer.h>
 #include <linux/dmi.h>
+#include <linux/pci-aspm.h>
 
 #include "portdrv.h"
 #include "aer/aerdrv.h"
@@ -32,10 +33,23 @@ MODULE_LICENSE("GPL");
 /* If this switch is set, PCIe port native services should not be enabled. */
 bool pcie_ports_disabled;
 
+/*
+ * If this switch is set, ACPI _OSC will be used to determine whether or not to
+ * enable PCIe port native services.
+ */
+bool pcie_ports_auto = true;
+
 static int __init pcie_port_setup(char *str)
 {
-	if (!strncmp(str, "compat", 6))
+	if (!strncmp(str, "compat", 6)) {
 		pcie_ports_disabled = true;
+	} else if (!strncmp(str, "native", 6)) {
+		pcie_ports_disabled = false;
+		pcie_ports_auto = false;
+	} else if (!strncmp(str, "auto", 4)) {
+		pcie_ports_disabled = false;
+		pcie_ports_auto = true;
+	}
 
 	return 1;
 }
@@ -313,8 +327,10 @@ static int __init pcie_portdrv_init(void
 {
 	int retval;
 
-	if (pcie_ports_disabled)
+	if (pcie_ports_disabled) {
+		pcie_no_aspm();
 		return -EACCES;
+	}
 
 	dmi_check_system(pcie_portdrv_dmi_table);
 
Index: linux-2.6/drivers/pci/pcie/portdrv_acpi.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/pci/pcie/portdrv_acpi.c
@@ -0,0 +1,77 @@
+/*
+ * PCIe Port Native Services Support, ACPI-Related Part
+ *
+ * Copyright (C) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pcieport_if.h>
+
+#include "aer/aerdrv.h"
+#include "../pci.h"
+
+/**
+ * pcie_port_acpi_setup - Request the BIOS to release control of PCIe services.
+ * @port: PCIe Port service for a root port or event collector.
+ * @srv_mask: Bit mask of services that can be enabled for @port.
+ *
+ * Invoked when @port is identified as a PCIe port device.  To avoid conflicts
+ * with the BIOS PCIe port native services support requires the BIOS to yield
+ * control of these services to the kernel.  The mask of services that the BIOS
+ * allows to be enabled for @port is written to @srv_mask.
+ *
+ * NOTE: It turns out that we cannot do that for individual port services
+ * separately, because that would make some systems work incorrectly.
+ */
+int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
+{
+	acpi_status status;
+	acpi_handle handle;
+	u32 flags;
+
+	if (acpi_pci_disabled)
+		return 0;
+
+	handle = acpi_find_root_bridge_handle(port);
+	if (!handle)
+		return -EINVAL;
+
+	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL
+		| OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
+		| OSC_PCI_EXPRESS_PME_CONTROL;
+
+	if (pci_aer_available()) {
+		if (pcie_aer_get_firmware_first(port))
+			dev_dbg(&port->dev, "PCIe errors handled by BIOS.\n");
+		else
+			flags |= OSC_PCI_EXPRESS_AER_CONTROL;
+	}
+
+	status = acpi_pci_osc_control_set(handle, &flags,
+					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	if (ACPI_FAILURE(status)) {
+		dev_dbg(&port->dev, "ACPI _OSC request failed (code %d)\n",
+			status);
+		return -ENODEV;
+	}
+
+	dev_info(&port->dev, "ACPI _OSC control granted for 0x%02x\n", flags);
+
+	*srv_mask = PCIE_PORT_SERVICE_VC;
+	if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_HP;
+	if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_PME;
+	if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_AER;
+
+	return 0;
+}
Index: linux-2.6/drivers/pci/pcie/portdrv.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv.h
+++ linux-2.6/drivers/pci/pcie/portdrv.h
@@ -21,6 +21,7 @@
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
 extern bool pcie_ports_disabled;
+extern bool pcie_ports_auto;
 
 extern struct bus_type pcie_port_bus_type;
 extern int pcie_port_device_register(struct pci_dev *dev);
@@ -32,6 +33,8 @@ extern void pcie_port_device_remove(stru
 extern int __must_check pcie_port_bus_register(void);
 extern void pcie_port_bus_unregister(void);
 
+struct pci_dev;
+
 #ifdef CONFIG_PCIE_PME
 extern bool pcie_pme_msi_disabled;
 
@@ -44,9 +47,26 @@ static inline bool pcie_pme_no_msi(void)
 {
 	return pcie_pme_msi_disabled;
 }
+
+extern void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable);
 #else /* !CONFIG_PCIE_PME */
 static inline void pcie_pme_disable_msi(void) {}
 static inline bool pcie_pme_no_msi(void) { return false; }
+static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
 #endif /* !CONFIG_PCIE_PME */
 
+#ifdef CONFIG_ACPI
+extern int pcie_port_acpi_setup(struct pci_dev *port, int *mask);
+
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+	return pcie_port_acpi_setup(port, mask);
+}
+#else /* !CONFIG_ACPI */
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+	return 0;
+}
+#endif /* !CONFIG_ACPI */
+
 #endif /* _PORTDRV_H_ */
Index: linux-2.6/drivers/pci/pcie/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/Makefile
+++ linux-2.6/drivers/pci/pcie/Makefile
@@ -6,6 +6,7 @@
 obj-$(CONFIG_PCIEASPM)		+= aspm.o
 
 pcieportdrv-y			:= portdrv_core.o portdrv_pci.o portdrv_bus.o
+pcieportdrv-$(CONFIG_ACPI)	+= portdrv_acpi.o
 
 obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv.o
 
Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -14,6 +14,8 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/pcieport_if.h>
+#include <linux/aer.h>
+#include <linux/pci-aspm.h>
 
 #include "../pci.h"
 #include "portdrv.h"
@@ -236,23 +238,40 @@ static int get_port_device_capability(st
 	int services = 0, pos;
 	u16 reg16;
 	u32 reg32;
+	int cap_mask;
+	int err;
+
+	err = pcie_port_platform_notify(dev, &cap_mask);
+	if (pcie_ports_auto) {
+		if (err) {
+			pcie_no_aspm();
+			return 0;
+		}
+	} else {
+		cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
+				| PCIE_PORT_SERVICE_VC;
+		if (pci_aer_available())
+			cap_mask |= PCIE_PORT_SERVICE_AER;
+	}
 
 	pos = pci_pcie_cap(dev);
 	pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
 	/* Hot-Plug Capable */
-	if (reg16 & PCI_EXP_FLAGS_SLOT) {
+	if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
 		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
 		if (reg32 & PCI_EXP_SLTCAP_HPC)
 			services |= PCIE_PORT_SERVICE_HP;
 	}
 	/* AER capable */
-	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
+	if ((cap_mask & PCIE_PORT_SERVICE_AER)
+	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
 		services |= PCIE_PORT_SERVICE_AER;
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
 	/* Root ports are capable of generating PME too */
-	if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+	if ((cap_mask & PCIE_PORT_SERVICE_PME)
+	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
 		services |= PCIE_PORT_SERVICE_PME;
 
 	return services;
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.c
+++ linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
@@ -24,37 +24,12 @@
 #include <linux/pm_runtime.h>
 
 #include "../../pci.h"
-#include "pcie_pme.h"
+#include "../portdrv.h"
 
 #define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
 #define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
 
 /*
- * If set, this switch will prevent the PCIe root port PME service driver from
- * being registered.  Consequently, the interrupt-based PCIe PME signaling will
- * not be used by any PCIe root ports in that case.
- */
-static bool pcie_pme_disabled = true;
-
-/*
- * The PCI Express Base Specification 2.0, Section 6.1.8, states the following:
- * "In order to maintain compatibility with non-PCI Express-aware system
- * software, system power management logic must be configured by firmware to use
- * the legacy mechanism of signaling PME by default.  PCI Express-aware system
- * software must notify the firmware prior to enabling native, interrupt-based
- * PME signaling."  However, if the platform doesn't provide us with a suitable
- * notification mechanism or the notification fails, it is not clear whether or
- * not we are supposed to use the interrupt-based PCIe PME signaling.  The
- * switch below can be used to indicate the desired behaviour.  When set, it
- * will make the kernel use the interrupt-based PCIe PME signaling regardless of
- * the platform notification status, although the kernel will attempt to notify
- * the platform anyway.  When unset, it will prevent the kernel from using the
- * the interrupt-based PCIe PME signaling if the platform notification fails,
- * which is the default.
- */
-static bool pcie_pme_force_enable;
-
-/*
  * If this switch is set, MSI will not be used for PCIe PME signaling.  This
  * causes the PCIe port driver to use INTx interrupts only, but it turns out
  * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
@@ -64,38 +39,13 @@ bool pcie_pme_msi_disabled;
 
 static int __init pcie_pme_setup(char *str)
 {
-	if (!strncmp(str, "auto", 4))
-		pcie_pme_disabled = false;
-	else if (!strncmp(str, "force", 5))
-		pcie_pme_force_enable = true;
-
-	str = strchr(str, ',');
-	if (str) {
-		str++;
-		str += strspn(str, " \t");
-		if (*str && !strcmp(str, "nomsi"))
-			pcie_pme_msi_disabled = true;
-	}
+	if (!strncmp(str, "nomsi", 5))
+		pcie_pme_msi_disabled = true;
 
 	return 1;
 }
 __setup("pcie_pme=", pcie_pme_setup);
 
-/**
- * pcie_pme_platform_setup - Ensure that the kernel controls the PCIe PME.
- * @srv: PCIe PME root port service to use for carrying out the check.
- *
- * Notify the platform that the native PCIe PME is going to be used and return
- * 'true' if the control of the PCIe PME registers has been acquired from the
- * platform.
- */
-static bool pcie_pme_platform_setup(struct pcie_device *srv)
-{
-	if (!pcie_pme_platform_notify(srv))
-		return true;
-	return pcie_pme_force_enable;
-}
-
 struct pcie_pme_service_data {
 	spinlock_t lock;
 	struct pcie_device *srv;
@@ -108,7 +58,7 @@ struct pcie_pme_service_data {
  * @dev: PCIe root port or event collector.
  * @enable: Enable or disable the interrupt.
  */
-static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
+void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
 {
 	int rtctl_pos;
 	u16 rtctl;
@@ -417,9 +367,6 @@ static int pcie_pme_probe(struct pcie_de
 	struct pcie_pme_service_data *data;
 	int ret;
 
-	if (!pcie_pme_platform_setup(srv))
-		return -EACCES;
-
 	data = kzalloc(sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
@@ -509,8 +456,7 @@ static struct pcie_port_service_driver p
  */
 static int __init pcie_pme_service_init(void)
 {
-	return pcie_pme_disabled ?
-		-ENODEV : pcie_port_service_register(&pcie_pme_driver);
+	return pcie_port_service_register(&pcie_pme_driver);
 }
 
 module_init(pcie_pme_service_init);
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * drivers/pci/pcie/pme/pcie_pme.h
- *
- * PCI Express Root Port PME signaling support
- *
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- */
-
-#ifndef _PCIE_PME_H_
-#define _PCIE_PME_H_
-
-struct pcie_device;
-
-#ifdef CONFIG_ACPI
-extern int pcie_pme_acpi_setup(struct pcie_device *srv);
-
-static inline int pcie_pme_platform_notify(struct pcie_device *srv)
-{
-	return pcie_pme_acpi_setup(srv);
-}
-#else /* !CONFIG_ACPI */
-static inline int pcie_pme_platform_notify(struct pcie_device *srv)
-{
-	return 0;
-}
-#endif /* !CONFIG_ACPI */
-
-#endif
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_core.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_core.c
@@ -772,22 +772,10 @@ void aer_isr(struct work_struct *work)
  */
 int aer_init(struct pcie_device *dev)
 {
-	if (pcie_aer_get_firmware_first(dev->port)) {
-		dev_printk(KERN_DEBUG, &dev->device,
-			   "PCIe errors handled by platform firmware.\n");
-		goto out;
-	}
-
-	if (aer_osc_setup(dev))
-		goto out;
-
-	return 0;
-out:
 	if (forceload) {
 		dev_printk(KERN_DEBUG, &dev->device,
 			   "aerdrv forceload requested.\n");
 		pcie_aer_force_firmware_first(dev->port, 0);
-		return 0;
 	}
-	return -ENXIO;
+	return 0;
 }
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -338,9 +338,7 @@ int acpi_get_hp_hw_control_from_firmware
 	acpi_handle chandle, handle;
 	struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
 
-	flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
-		  OSC_SHPC_NATIVE_HP_CONTROL |
-		  OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags &= OSC_SHPC_NATIVE_HP_CONTROL;
 	if (!flags) {
 		err("Invalid flags %u specified!\n", flags);
 		return -EINVAL;
Index: linux-2.6/drivers/pci/hotplug/pciehp.h
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp.h
+++ linux-2.6/drivers/pci/hotplug/pciehp.h
@@ -176,19 +176,7 @@ static inline void pciehp_firmware_init(
 {
 	pciehp_acpi_slot_detection_init();
 }
-
-static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
-	int retval;
-	u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
-		     OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	retval = acpi_get_hp_hw_control_from_firmware(dev, flags);
-	if (retval)
-		return retval;
-	return pciehp_acpi_slot_detection_check(dev);
-}
 #else
 #define pciehp_firmware_init()				do {} while (0)
-#define pciehp_get_hp_hw_control_from_firmware(dev) 	0
 #endif 				/* CONFIG_ACPI */
 #endif				/* _PCIEHP_H */
Index: linux-2.6/drivers/pci/hotplug/pciehp_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp_core.c
+++ linux-2.6/drivers/pci/hotplug/pciehp_core.c
@@ -59,7 +59,7 @@ module_param(pciehp_force, bool, 0644);
 MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
 MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
 MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
-MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
+MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
 
 #define PCIE_MODULE_NAME "pciehp"
 
@@ -235,7 +235,7 @@ static int pciehp_probe(struct pcie_devi
 		dev_info(&dev->device,
 			 "Bypassing BIOS check for pciehp use on %s\n",
 			 pci_name(dev->port));
-	else if (pciehp_get_hp_hw_control_from_firmware(dev->port))
+	else if (pciehp_acpi_slot_detection_check(dev->port))
 		goto err_out_none;
 
 	ctrl = pcie_init(dev);
Index: linux-2.6/drivers/pci/hotplug/pciehp_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp_acpi.c
+++ linux-2.6/drivers/pci/hotplug/pciehp_acpi.c
@@ -85,9 +85,7 @@ static int __init dummy_probe(struct pci
 	acpi_handle handle;
 	struct dummy_slot *slot, *tmp;
 	struct pci_dev *pdev = dev->port;
-	/* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
-	if (pciehp_get_hp_hw_control_from_firmware(pdev))
-		return -ENODEV;
+
 	pos = pci_pcie_cap(pdev);
 	if (!pos)
 		return -ENODEV;
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.orig/Documentation/kernel-parameters.txt
+++ linux-2.6/Documentation/kernel-parameters.txt
@@ -1975,18 +1975,17 @@ and is between 256 and 4096 characters. 
 			WARNING: Forcing ASPM on may cause system lockups.
 
 	pcie_ports=	[PCIE] PCIe ports handling:
+		auto	Ask the BIOS whether or not to use native PCIe services
+			associated with PCIe ports (PME, hot-plug, AER).  Use
+			them only if that is allowed by the BIOS.
+		native	Use native PCIe services associated with PCIe ports
+			unconditionally.
 		compat	Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
 			ports driver.
 
 	pcie_pme=	[PCIE,PM] Native PCIe PME signaling options:
-			Format: {auto|force}[,nomsi]
-		auto	Use native PCIe PME signaling if the BIOS allows the
-			kernel to control PCIe config registers of root ports.
-		force	Use native PCIe PME signaling even if the BIOS refuses
-			to allow the kernel to control the relevant PCIe config
-			registers.
 		nomsi	Do not use MSI for native PCIe PME signaling (this makes
-			all PCIe root ports use INTx for everything).
+			all PCIe root ports use INTx for all services).
 
 	pcmv=		[HW,PCMCIA] BadgePAD 4
 
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -19,42 +19,6 @@
 #include <acpi/apei.h>
 #include "aerdrv.h"
 
-/**
- * aer_osc_setup - run ACPI _OSC method
- * @pciedev: pcie_device which AER is being enabled on
- *
- * @return: Zero on success. Nonzero otherwise.
- *
- * Invoked when PCIe bus loads AER service driver. To avoid conflict with
- * BIOS AER support requires BIOS to yield AER control to OS native driver.
- **/
-int aer_osc_setup(struct pcie_device *pciedev)
-{
-	acpi_status status = AE_NOT_FOUND;
-	struct pci_dev *pdev = pciedev->port;
-	acpi_handle handle = NULL;
-
-	if (acpi_pci_disabled)
-		return -1;
-
-	handle = acpi_find_root_bridge_handle(pdev);
-	if (handle) {
-		u32 flags = OSC_PCI_EXPRESS_AER_CONTROL |
-				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
-		status = acpi_pci_osc_control_set(handle, &flags, flags);
-	}
-
-	if (ACPI_FAILURE(status)) {
-		dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't "
-			   "init device: %s\n",
-			   (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-			   "no _OSC support" : "_OSC failed");
-		return -1;
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_ACPI_APEI
 static inline int hest_match_pci(struct acpi_hest_aer_common *p,
 				 struct pci_dev *pci)
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme_acpi.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * PCIe Native PME support, ACPI-related part
- *
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License V2.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/acpi.h>
-#include <linux/pci-acpi.h>
-#include <linux/pcieport_if.h>
-
-/**
- * pcie_pme_acpi_setup - Request the ACPI BIOS to release control over PCIe PME.
- * @srv - PCIe PME service for a root port or event collector.
- *
- * Invoked when the PCIe bus type loads PCIe PME service driver.  To avoid
- * conflict with the BIOS PCIe support requires the BIOS to yield PCIe PME
- * control to the kernel.
- */
-int pcie_pme_acpi_setup(struct pcie_device *srv)
-{
-	acpi_status status = AE_NOT_FOUND;
-	struct pci_dev *port = srv->port;
-	acpi_handle handle;
-	u32 flags;
-	int error = 0;
-
-	if (acpi_pci_disabled)
-		return -ENOSYS;
-
-	dev_info(&port->dev, "Requesting control of PCIe PME from ACPI BIOS\n");
-
-	handle = acpi_find_root_bridge_handle(port);
-	if (!handle)
-		return -EINVAL;
-
-	flags = OSC_PCI_EXPRESS_PME_CONTROL |
-		OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
-
-	status = acpi_pci_osc_control_set(handle, &flags, flags);
-	if (ACPI_FAILURE(status)) {
-		dev_info(&port->dev,
-			"Failed to receive control of PCIe PME service: %s\n",
-			(status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-			"no _OSC support" : "ACPI _OSC failed");
-		error = -ENODEV;
-	}
-
-	return error;
-}
Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -33,7 +33,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/pci.h>
 #include <linux/pci-acpi.h>
-#include <linux/pci-aspm.h>
 #include <linux/acpi.h>
 #include <linux/slab.h>
 #include <acpi/acpi_bus.h>
@@ -565,14 +564,6 @@ static int __devinit acpi_pci_root_add(s
 	if (flags != base_flags)
 		acpi_pci_osc_support(root, flags);
 
-	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
-	status = acpi_pci_osc_control_set(root->device->handle, &flags, flags);
-
-	if (ACPI_FAILURE(status)) {
-		printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
-		pcie_no_aspm();
-	}
-
 	pci_acpi_add_bus_pm_notifier(device, root->bus);
 	if (device->wakeup.flags.run_wake)
 		device_set_run_wake(root->bus->bridge, true);
Index: linux-2.6/drivers/pci/pcie/pme/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/Makefile
+++ linux-2.6/drivers/pci/pcie/pme/Makefile
@@ -2,7 +2,4 @@
 # Makefile for PCI-Express Root Port PME signaling driver
 #
 
-obj-$(CONFIG_PCIE_PME) += pmedriver.o
-
-pmedriver-objs := pcie_pme.o
-pmedriver-$(CONFIG_ACPI) += pcie_pme_acpi.o
+obj-$(CONFIG_PCIE_PME) += pcie_pme.o

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7)
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (12 preceding siblings ...)
  2010-08-20 23:56   ` [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7) Rafael J. Wysocki
@ 2010-08-20 23:56   ` Rafael J. Wysocki
  2010-08-20 23:57   ` [PATCH 8/10] PCI / PCIe: Disable PCIe port services during port initialization Rafael J. Wysocki
                     ` (5 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:56 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

PCIe port service drivers ask the BIOS, through _OSC, for control of
the services they handle.  Unfortunately, each of them individually
asks for control of the PCIe capability structure and if that is
granted, some BIOSes expect that the other PCIe port services will be
configured and handled by the kernel as well.  If that is not the
case (eg. one of the PCIe port service drivers is not loaded), the
BIOS may be confused and may cause the system as a whole to misbehave
(eg. on one of such systems enabling the native PCIe PME service
without loading the native PCIe hot-plug service driver causes a
storm of ACPI notify requests to appear).

For this reason rework the PCIe port driver so that (1) it checks
which native PCIe port services can be enabled, according to the
BIOS, and (2) it requests control of all these services
simultaneously.  In particular, this causes pcie_portdrv_probe() to
fail if the BIOS refuses to grant control of the PCIe capability
structure, which means that no native PCIe port services can be
enabled for the PCIe root complex the given port belongs to.

Make it possible to override this behavior using 'pcie_ports=native'
(use the PCIe native services regardless of the BIOS response to the
control request), or 'pcie_ports=compat' (do not use the PCIe native
services at all).

Accordingly, rework the existing PCIe port service drivers so that
they don't request control of the services directly.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/kernel-parameters.txt  |   13 ++---
 drivers/acpi/pci_root.c              |    9 ----
 drivers/pci/hotplug/acpi_pcihp.c     |    4 -
 drivers/pci/hotplug/pciehp.h         |   12 -----
 drivers/pci/hotplug/pciehp_acpi.c    |    4 -
 drivers/pci/hotplug/pciehp_core.c    |    4 -
 drivers/pci/pcie/Makefile            |    1 
 drivers/pci/pcie/aer/aerdrv_acpi.c   |   36 ----------------
 drivers/pci/pcie/aer/aerdrv_core.c   |   14 ------
 drivers/pci/pcie/pme/Makefile        |    5 --
 drivers/pci/pcie/pme/pcie_pme.c      |   64 ++---------------------------
 drivers/pci/pcie/pme/pcie_pme.h      |   28 ------------
 drivers/pci/pcie/pme/pcie_pme_acpi.c |   56 -------------------------
 drivers/pci/pcie/portdrv.h           |   20 +++++++++
 drivers/pci/pcie/portdrv_acpi.c      |   77 +++++++++++++++++++++++++++++++++++
 drivers/pci/pcie/portdrv_core.c      |   25 ++++++++++-
 drivers/pci/pcie/portdrv_pci.c       |   20 ++++++++-
 17 files changed, 155 insertions(+), 237 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -15,6 +15,7 @@
 #include <linux/pcieport_if.h>
 #include <linux/aer.h>
 #include <linux/dmi.h>
+#include <linux/pci-aspm.h>
 
 #include "portdrv.h"
 #include "aer/aerdrv.h"
@@ -32,10 +33,23 @@ MODULE_LICENSE("GPL");
 /* If this switch is set, PCIe port native services should not be enabled. */
 bool pcie_ports_disabled;
 
+/*
+ * If this switch is set, ACPI _OSC will be used to determine whether or not to
+ * enable PCIe port native services.
+ */
+bool pcie_ports_auto = true;
+
 static int __init pcie_port_setup(char *str)
 {
-	if (!strncmp(str, "compat", 6))
+	if (!strncmp(str, "compat", 6)) {
 		pcie_ports_disabled = true;
+	} else if (!strncmp(str, "native", 6)) {
+		pcie_ports_disabled = false;
+		pcie_ports_auto = false;
+	} else if (!strncmp(str, "auto", 4)) {
+		pcie_ports_disabled = false;
+		pcie_ports_auto = true;
+	}
 
 	return 1;
 }
@@ -313,8 +327,10 @@ static int __init pcie_portdrv_init(void
 {
 	int retval;
 
-	if (pcie_ports_disabled)
+	if (pcie_ports_disabled) {
+		pcie_no_aspm();
 		return -EACCES;
+	}
 
 	dmi_check_system(pcie_portdrv_dmi_table);
 
Index: linux-2.6/drivers/pci/pcie/portdrv_acpi.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/pci/pcie/portdrv_acpi.c
@@ -0,0 +1,77 @@
+/*
+ * PCIe Port Native Services Support, ACPI-Related Part
+ *
+ * Copyright (C) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pcieport_if.h>
+
+#include "aer/aerdrv.h"
+#include "../pci.h"
+
+/**
+ * pcie_port_acpi_setup - Request the BIOS to release control of PCIe services.
+ * @port: PCIe Port service for a root port or event collector.
+ * @srv_mask: Bit mask of services that can be enabled for @port.
+ *
+ * Invoked when @port is identified as a PCIe port device.  To avoid conflicts
+ * with the BIOS PCIe port native services support requires the BIOS to yield
+ * control of these services to the kernel.  The mask of services that the BIOS
+ * allows to be enabled for @port is written to @srv_mask.
+ *
+ * NOTE: It turns out that we cannot do that for individual port services
+ * separately, because that would make some systems work incorrectly.
+ */
+int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
+{
+	acpi_status status;
+	acpi_handle handle;
+	u32 flags;
+
+	if (acpi_pci_disabled)
+		return 0;
+
+	handle = acpi_find_root_bridge_handle(port);
+	if (!handle)
+		return -EINVAL;
+
+	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL
+		| OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
+		| OSC_PCI_EXPRESS_PME_CONTROL;
+
+	if (pci_aer_available()) {
+		if (pcie_aer_get_firmware_first(port))
+			dev_dbg(&port->dev, "PCIe errors handled by BIOS.\n");
+		else
+			flags |= OSC_PCI_EXPRESS_AER_CONTROL;
+	}
+
+	status = acpi_pci_osc_control_set(handle, &flags,
+					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	if (ACPI_FAILURE(status)) {
+		dev_dbg(&port->dev, "ACPI _OSC request failed (code %d)\n",
+			status);
+		return -ENODEV;
+	}
+
+	dev_info(&port->dev, "ACPI _OSC control granted for 0x%02x\n", flags);
+
+	*srv_mask = PCIE_PORT_SERVICE_VC;
+	if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_HP;
+	if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_PME;
+	if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
+		*srv_mask |= PCIE_PORT_SERVICE_AER;
+
+	return 0;
+}
Index: linux-2.6/drivers/pci/pcie/portdrv.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv.h
+++ linux-2.6/drivers/pci/pcie/portdrv.h
@@ -21,6 +21,7 @@
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
 extern bool pcie_ports_disabled;
+extern bool pcie_ports_auto;
 
 extern struct bus_type pcie_port_bus_type;
 extern int pcie_port_device_register(struct pci_dev *dev);
@@ -32,6 +33,8 @@ extern void pcie_port_device_remove(stru
 extern int __must_check pcie_port_bus_register(void);
 extern void pcie_port_bus_unregister(void);
 
+struct pci_dev;
+
 #ifdef CONFIG_PCIE_PME
 extern bool pcie_pme_msi_disabled;
 
@@ -44,9 +47,26 @@ static inline bool pcie_pme_no_msi(void)
 {
 	return pcie_pme_msi_disabled;
 }
+
+extern void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable);
 #else /* !CONFIG_PCIE_PME */
 static inline void pcie_pme_disable_msi(void) {}
 static inline bool pcie_pme_no_msi(void) { return false; }
+static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
 #endif /* !CONFIG_PCIE_PME */
 
+#ifdef CONFIG_ACPI
+extern int pcie_port_acpi_setup(struct pci_dev *port, int *mask);
+
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+	return pcie_port_acpi_setup(port, mask);
+}
+#else /* !CONFIG_ACPI */
+static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
+{
+	return 0;
+}
+#endif /* !CONFIG_ACPI */
+
 #endif /* _PORTDRV_H_ */
Index: linux-2.6/drivers/pci/pcie/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/Makefile
+++ linux-2.6/drivers/pci/pcie/Makefile
@@ -6,6 +6,7 @@
 obj-$(CONFIG_PCIEASPM)		+= aspm.o
 
 pcieportdrv-y			:= portdrv_core.o portdrv_pci.o portdrv_bus.o
+pcieportdrv-$(CONFIG_ACPI)	+= portdrv_acpi.o
 
 obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv.o
 
Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -14,6 +14,8 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/pcieport_if.h>
+#include <linux/aer.h>
+#include <linux/pci-aspm.h>
 
 #include "../pci.h"
 #include "portdrv.h"
@@ -236,23 +238,40 @@ static int get_port_device_capability(st
 	int services = 0, pos;
 	u16 reg16;
 	u32 reg32;
+	int cap_mask;
+	int err;
+
+	err = pcie_port_platform_notify(dev, &cap_mask);
+	if (pcie_ports_auto) {
+		if (err) {
+			pcie_no_aspm();
+			return 0;
+		}
+	} else {
+		cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
+				| PCIE_PORT_SERVICE_VC;
+		if (pci_aer_available())
+			cap_mask |= PCIE_PORT_SERVICE_AER;
+	}
 
 	pos = pci_pcie_cap(dev);
 	pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
 	/* Hot-Plug Capable */
-	if (reg16 & PCI_EXP_FLAGS_SLOT) {
+	if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
 		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
 		if (reg32 & PCI_EXP_SLTCAP_HPC)
 			services |= PCIE_PORT_SERVICE_HP;
 	}
 	/* AER capable */
-	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
+	if ((cap_mask & PCIE_PORT_SERVICE_AER)
+	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
 		services |= PCIE_PORT_SERVICE_AER;
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
 	/* Root ports are capable of generating PME too */
-	if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+	if ((cap_mask & PCIE_PORT_SERVICE_PME)
+	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
 		services |= PCIE_PORT_SERVICE_PME;
 
 	return services;
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.c
+++ linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
@@ -24,37 +24,12 @@
 #include <linux/pm_runtime.h>
 
 #include "../../pci.h"
-#include "pcie_pme.h"
+#include "../portdrv.h"
 
 #define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
 #define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
 
 /*
- * If set, this switch will prevent the PCIe root port PME service driver from
- * being registered.  Consequently, the interrupt-based PCIe PME signaling will
- * not be used by any PCIe root ports in that case.
- */
-static bool pcie_pme_disabled = true;
-
-/*
- * The PCI Express Base Specification 2.0, Section 6.1.8, states the following:
- * "In order to maintain compatibility with non-PCI Express-aware system
- * software, system power management logic must be configured by firmware to use
- * the legacy mechanism of signaling PME by default.  PCI Express-aware system
- * software must notify the firmware prior to enabling native, interrupt-based
- * PME signaling."  However, if the platform doesn't provide us with a suitable
- * notification mechanism or the notification fails, it is not clear whether or
- * not we are supposed to use the interrupt-based PCIe PME signaling.  The
- * switch below can be used to indicate the desired behaviour.  When set, it
- * will make the kernel use the interrupt-based PCIe PME signaling regardless of
- * the platform notification status, although the kernel will attempt to notify
- * the platform anyway.  When unset, it will prevent the kernel from using the
- * the interrupt-based PCIe PME signaling if the platform notification fails,
- * which is the default.
- */
-static bool pcie_pme_force_enable;
-
-/*
  * If this switch is set, MSI will not be used for PCIe PME signaling.  This
  * causes the PCIe port driver to use INTx interrupts only, but it turns out
  * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
@@ -64,38 +39,13 @@ bool pcie_pme_msi_disabled;
 
 static int __init pcie_pme_setup(char *str)
 {
-	if (!strncmp(str, "auto", 4))
-		pcie_pme_disabled = false;
-	else if (!strncmp(str, "force", 5))
-		pcie_pme_force_enable = true;
-
-	str = strchr(str, ',');
-	if (str) {
-		str++;
-		str += strspn(str, " \t");
-		if (*str && !strcmp(str, "nomsi"))
-			pcie_pme_msi_disabled = true;
-	}
+	if (!strncmp(str, "nomsi", 5))
+		pcie_pme_msi_disabled = true;
 
 	return 1;
 }
 __setup("pcie_pme=", pcie_pme_setup);
 
-/**
- * pcie_pme_platform_setup - Ensure that the kernel controls the PCIe PME.
- * @srv: PCIe PME root port service to use for carrying out the check.
- *
- * Notify the platform that the native PCIe PME is going to be used and return
- * 'true' if the control of the PCIe PME registers has been acquired from the
- * platform.
- */
-static bool pcie_pme_platform_setup(struct pcie_device *srv)
-{
-	if (!pcie_pme_platform_notify(srv))
-		return true;
-	return pcie_pme_force_enable;
-}
-
 struct pcie_pme_service_data {
 	spinlock_t lock;
 	struct pcie_device *srv;
@@ -108,7 +58,7 @@ struct pcie_pme_service_data {
  * @dev: PCIe root port or event collector.
  * @enable: Enable or disable the interrupt.
  */
-static void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
+void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
 {
 	int rtctl_pos;
 	u16 rtctl;
@@ -417,9 +367,6 @@ static int pcie_pme_probe(struct pcie_de
 	struct pcie_pme_service_data *data;
 	int ret;
 
-	if (!pcie_pme_platform_setup(srv))
-		return -EACCES;
-
 	data = kzalloc(sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
@@ -509,8 +456,7 @@ static struct pcie_port_service_driver p
  */
 static int __init pcie_pme_service_init(void)
 {
-	return pcie_pme_disabled ?
-		-ENODEV : pcie_port_service_register(&pcie_pme_driver);
+	return pcie_port_service_register(&pcie_pme_driver);
 }
 
 module_init(pcie_pme_service_init);
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.h
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * drivers/pci/pcie/pme/pcie_pme.h
- *
- * PCI Express Root Port PME signaling support
- *
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- */
-
-#ifndef _PCIE_PME_H_
-#define _PCIE_PME_H_
-
-struct pcie_device;
-
-#ifdef CONFIG_ACPI
-extern int pcie_pme_acpi_setup(struct pcie_device *srv);
-
-static inline int pcie_pme_platform_notify(struct pcie_device *srv)
-{
-	return pcie_pme_acpi_setup(srv);
-}
-#else /* !CONFIG_ACPI */
-static inline int pcie_pme_platform_notify(struct pcie_device *srv)
-{
-	return 0;
-}
-#endif /* !CONFIG_ACPI */
-
-#endif
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_core.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_core.c
@@ -772,22 +772,10 @@ void aer_isr(struct work_struct *work)
  */
 int aer_init(struct pcie_device *dev)
 {
-	if (pcie_aer_get_firmware_first(dev->port)) {
-		dev_printk(KERN_DEBUG, &dev->device,
-			   "PCIe errors handled by platform firmware.\n");
-		goto out;
-	}
-
-	if (aer_osc_setup(dev))
-		goto out;
-
-	return 0;
-out:
 	if (forceload) {
 		dev_printk(KERN_DEBUG, &dev->device,
 			   "aerdrv forceload requested.\n");
 		pcie_aer_force_firmware_first(dev->port, 0);
-		return 0;
 	}
-	return -ENXIO;
+	return 0;
 }
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -338,9 +338,7 @@ int acpi_get_hp_hw_control_from_firmware
 	acpi_handle chandle, handle;
 	struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
 
-	flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
-		  OSC_SHPC_NATIVE_HP_CONTROL |
-		  OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags &= OSC_SHPC_NATIVE_HP_CONTROL;
 	if (!flags) {
 		err("Invalid flags %u specified!\n", flags);
 		return -EINVAL;
Index: linux-2.6/drivers/pci/hotplug/pciehp.h
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp.h
+++ linux-2.6/drivers/pci/hotplug/pciehp.h
@@ -176,19 +176,7 @@ static inline void pciehp_firmware_init(
 {
 	pciehp_acpi_slot_detection_init();
 }
-
-static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
-	int retval;
-	u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
-		     OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	retval = acpi_get_hp_hw_control_from_firmware(dev, flags);
-	if (retval)
-		return retval;
-	return pciehp_acpi_slot_detection_check(dev);
-}
 #else
 #define pciehp_firmware_init()				do {} while (0)
-#define pciehp_get_hp_hw_control_from_firmware(dev) 	0
 #endif 				/* CONFIG_ACPI */
 #endif				/* _PCIEHP_H */
Index: linux-2.6/drivers/pci/hotplug/pciehp_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp_core.c
+++ linux-2.6/drivers/pci/hotplug/pciehp_core.c
@@ -59,7 +59,7 @@ module_param(pciehp_force, bool, 0644);
 MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
 MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
 MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
-MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
+MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if OSHP is missing");
 
 #define PCIE_MODULE_NAME "pciehp"
 
@@ -235,7 +235,7 @@ static int pciehp_probe(struct pcie_devi
 		dev_info(&dev->device,
 			 "Bypassing BIOS check for pciehp use on %s\n",
 			 pci_name(dev->port));
-	else if (pciehp_get_hp_hw_control_from_firmware(dev->port))
+	else if (pciehp_acpi_slot_detection_check(dev->port))
 		goto err_out_none;
 
 	ctrl = pcie_init(dev);
Index: linux-2.6/drivers/pci/hotplug/pciehp_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/pciehp_acpi.c
+++ linux-2.6/drivers/pci/hotplug/pciehp_acpi.c
@@ -85,9 +85,7 @@ static int __init dummy_probe(struct pci
 	acpi_handle handle;
 	struct dummy_slot *slot, *tmp;
 	struct pci_dev *pdev = dev->port;
-	/* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
-	if (pciehp_get_hp_hw_control_from_firmware(pdev))
-		return -ENODEV;
+
 	pos = pci_pcie_cap(pdev);
 	if (!pos)
 		return -ENODEV;
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
--- linux-2.6.orig/Documentation/kernel-parameters.txt
+++ linux-2.6/Documentation/kernel-parameters.txt
@@ -1975,18 +1975,17 @@ and is between 256 and 4096 characters. 
 			WARNING: Forcing ASPM on may cause system lockups.
 
 	pcie_ports=	[PCIE] PCIe ports handling:
+		auto	Ask the BIOS whether or not to use native PCIe services
+			associated with PCIe ports (PME, hot-plug, AER).  Use
+			them only if that is allowed by the BIOS.
+		native	Use native PCIe services associated with PCIe ports
+			unconditionally.
 		compat	Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
 			ports driver.
 
 	pcie_pme=	[PCIE,PM] Native PCIe PME signaling options:
-			Format: {auto|force}[,nomsi]
-		auto	Use native PCIe PME signaling if the BIOS allows the
-			kernel to control PCIe config registers of root ports.
-		force	Use native PCIe PME signaling even if the BIOS refuses
-			to allow the kernel to control the relevant PCIe config
-			registers.
 		nomsi	Do not use MSI for native PCIe PME signaling (this makes
-			all PCIe root ports use INTx for everything).
+			all PCIe root ports use INTx for all services).
 
 	pcmv=		[HW,PCMCIA] BadgePAD 4
 
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -19,42 +19,6 @@
 #include <acpi/apei.h>
 #include "aerdrv.h"
 
-/**
- * aer_osc_setup - run ACPI _OSC method
- * @pciedev: pcie_device which AER is being enabled on
- *
- * @return: Zero on success. Nonzero otherwise.
- *
- * Invoked when PCIe bus loads AER service driver. To avoid conflict with
- * BIOS AER support requires BIOS to yield AER control to OS native driver.
- **/
-int aer_osc_setup(struct pcie_device *pciedev)
-{
-	acpi_status status = AE_NOT_FOUND;
-	struct pci_dev *pdev = pciedev->port;
-	acpi_handle handle = NULL;
-
-	if (acpi_pci_disabled)
-		return -1;
-
-	handle = acpi_find_root_bridge_handle(pdev);
-	if (handle) {
-		u32 flags = OSC_PCI_EXPRESS_AER_CONTROL |
-				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
-		status = acpi_pci_osc_control_set(handle, &flags, flags);
-	}
-
-	if (ACPI_FAILURE(status)) {
-		dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't "
-			   "init device: %s\n",
-			   (status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-			   "no _OSC support" : "_OSC failed");
-		return -1;
-	}
-
-	return 0;
-}
-
 #ifdef CONFIG_ACPI_APEI
 static inline int hest_match_pci(struct acpi_hest_aer_common *p,
 				 struct pci_dev *pci)
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme_acpi.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * PCIe Native PME support, ACPI-related part
- *
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License V2.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/acpi.h>
-#include <linux/pci-acpi.h>
-#include <linux/pcieport_if.h>
-
-/**
- * pcie_pme_acpi_setup - Request the ACPI BIOS to release control over PCIe PME.
- * @srv - PCIe PME service for a root port or event collector.
- *
- * Invoked when the PCIe bus type loads PCIe PME service driver.  To avoid
- * conflict with the BIOS PCIe support requires the BIOS to yield PCIe PME
- * control to the kernel.
- */
-int pcie_pme_acpi_setup(struct pcie_device *srv)
-{
-	acpi_status status = AE_NOT_FOUND;
-	struct pci_dev *port = srv->port;
-	acpi_handle handle;
-	u32 flags;
-	int error = 0;
-
-	if (acpi_pci_disabled)
-		return -ENOSYS;
-
-	dev_info(&port->dev, "Requesting control of PCIe PME from ACPI BIOS\n");
-
-	handle = acpi_find_root_bridge_handle(port);
-	if (!handle)
-		return -EINVAL;
-
-	flags = OSC_PCI_EXPRESS_PME_CONTROL |
-		OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
-
-	status = acpi_pci_osc_control_set(handle, &flags, flags);
-	if (ACPI_FAILURE(status)) {
-		dev_info(&port->dev,
-			"Failed to receive control of PCIe PME service: %s\n",
-			(status == AE_SUPPORT || status == AE_NOT_FOUND) ?
-			"no _OSC support" : "ACPI _OSC failed");
-		error = -ENODEV;
-	}
-
-	return error;
-}
Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -33,7 +33,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/pci.h>
 #include <linux/pci-acpi.h>
-#include <linux/pci-aspm.h>
 #include <linux/acpi.h>
 #include <linux/slab.h>
 #include <acpi/acpi_bus.h>
@@ -565,14 +564,6 @@ static int __devinit acpi_pci_root_add(s
 	if (flags != base_flags)
 		acpi_pci_osc_support(root, flags);
 
-	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
-	status = acpi_pci_osc_control_set(root->device->handle, &flags, flags);
-
-	if (ACPI_FAILURE(status)) {
-		printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
-		pcie_no_aspm();
-	}
-
 	pci_acpi_add_bus_pm_notifier(device, root->bus);
 	if (device->wakeup.flags.run_wake)
 		device_set_run_wake(root->bus->bridge, true);
Index: linux-2.6/drivers/pci/pcie/pme/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/Makefile
+++ linux-2.6/drivers/pci/pcie/pme/Makefile
@@ -2,7 +2,4 @@
 # Makefile for PCI-Express Root Port PME signaling driver
 #
 
-obj-$(CONFIG_PCIE_PME) += pmedriver.o
-
-pmedriver-objs := pcie_pme.o
-pmedriver-$(CONFIG_ACPI) += pcie_pme_acpi.o
+obj-$(CONFIG_PCIE_PME) += pcie_pme.o

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 8/10] PCI / PCIe: Disable PCIe port services during port initialization
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (13 preceding siblings ...)
  2010-08-20 23:56   ` Rafael J. Wysocki
@ 2010-08-20 23:57   ` Rafael J. Wysocki
  2010-08-20 23:57   ` Rafael J. Wysocki
                     ` (4 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:57 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

In principle PCIe port services may be enabled by the BIOS, so it's
better to disable them during port initialization to avoid spurious
events from being generated.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/pci/pcie/portdrv_core.c |   29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -259,20 +259,43 @@ static int get_port_device_capability(st
 	/* Hot-Plug Capable */
 	if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
 		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
-		if (reg32 & PCI_EXP_SLTCAP_HPC)
+		if (reg32 & PCI_EXP_SLTCAP_HPC) {
 			services |= PCIE_PORT_SERVICE_HP;
+			/*
+			 * Disable hot-plug interrupts in case they have been
+			 * enabled by the BIOS and the hot-plug service driver
+			 * is not loaded.
+			 */
+			pos += PCI_EXP_SLTCTL;
+			pci_read_config_word(dev, pos, &reg16);
+			reg16 &= ~(PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
+			pci_write_config_word(dev, pos, reg16);
+		}
 	}
 	/* AER capable */
 	if ((cap_mask & PCIE_PORT_SERVICE_AER)
-	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
+	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
 		services |= PCIE_PORT_SERVICE_AER;
+		/*
+		 * Disable AER on this port in case it's been enabled by the
+		 * BIOS (the AER service driver will enable it when necessary).
+		 */
+		pci_disable_pcie_error_reporting(dev);
+	}
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
 	/* Root ports are capable of generating PME too */
 	if ((cap_mask & PCIE_PORT_SERVICE_PME)
-	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
 		services |= PCIE_PORT_SERVICE_PME;
+		/*
+		 * Disable PME interrupt on this port in case it's been enabled
+		 * by the BIOS (the PME service driver will enable it when
+		 * necessary).
+		 */
+		pcie_pme_interrupt_enable(dev, false);
+	}
 
 	return services;
 }

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 8/10] PCI / PCIe: Disable PCIe port services during port initialization
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (14 preceding siblings ...)
  2010-08-20 23:57   ` [PATCH 8/10] PCI / PCIe: Disable PCIe port services during port initialization Rafael J. Wysocki
@ 2010-08-20 23:57   ` Rafael J. Wysocki
  2010-08-20 23:58   ` [PATCH 9/10] PCI / PCIe: Move PCIe PME code to the pcie directory Rafael J. Wysocki
                     ` (3 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:57 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

In principle PCIe port services may be enabled by the BIOS, so it's
better to disable them during port initialization to avoid spurious
events from being generated.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/pci/pcie/portdrv_core.c |   29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_core.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c
+++ linux-2.6/drivers/pci/pcie/portdrv_core.c
@@ -259,20 +259,43 @@ static int get_port_device_capability(st
 	/* Hot-Plug Capable */
 	if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
 		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
-		if (reg32 & PCI_EXP_SLTCAP_HPC)
+		if (reg32 & PCI_EXP_SLTCAP_HPC) {
 			services |= PCIE_PORT_SERVICE_HP;
+			/*
+			 * Disable hot-plug interrupts in case they have been
+			 * enabled by the BIOS and the hot-plug service driver
+			 * is not loaded.
+			 */
+			pos += PCI_EXP_SLTCTL;
+			pci_read_config_word(dev, pos, &reg16);
+			reg16 &= ~(PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
+			pci_write_config_word(dev, pos, reg16);
+		}
 	}
 	/* AER capable */
 	if ((cap_mask & PCIE_PORT_SERVICE_AER)
-	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
+	    && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
 		services |= PCIE_PORT_SERVICE_AER;
+		/*
+		 * Disable AER on this port in case it's been enabled by the
+		 * BIOS (the AER service driver will enable it when necessary).
+		 */
+		pci_disable_pcie_error_reporting(dev);
+	}
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
 	/* Root ports are capable of generating PME too */
 	if ((cap_mask & PCIE_PORT_SERVICE_PME)
-	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+	    && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
 		services |= PCIE_PORT_SERVICE_PME;
+		/*
+		 * Disable PME interrupt on this port in case it's been enabled
+		 * by the BIOS (the PME service driver will enable it when
+		 * necessary).
+		 */
+		pcie_pme_interrupt_enable(dev, false);
+	}
 
 	return services;
 }

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 9/10] PCI / PCIe: Move PCIe PME code to the pcie directory
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (15 preceding siblings ...)
  2010-08-20 23:57   ` Rafael J. Wysocki
@ 2010-08-20 23:58   ` Rafael J. Wysocki
  2010-08-20 23:58   ` Rafael J. Wysocki
                     ` (2 subsequent siblings)
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:58 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

The PCIe PME code only consists of one file, so it doesn't need to
occupy its own directory.  Move it to drivers/pci/pcie/pme.c and
remove the contents of drivers/pci/pcie/pme .

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/pci/pcie/Makefile       |    2 
 drivers/pci/pcie/pme.c          |  462 ++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pcie/pme/Makefile   |    5 
 drivers/pci/pcie/pme/pcie_pme.c |  462 ----------------------------------------
 4 files changed, 463 insertions(+), 468 deletions(-)

Index: linux-2.6/drivers/pci/pcie/pme.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/pci/pcie/pme.c
@@ -0,0 +1,462 @@
+/*
+ * PCIe Native PME support
+ *
+ * Copyright (C) 2007 - 2009 Intel Corp
+ * Copyright (C) 2007 - 2009 Shaohua Li <shaohua.li@intel.com>
+ * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/pcieport_if.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pm_runtime.h>
+
+#include "../pci.h"
+#include "portdrv.h"
+
+#define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
+#define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
+
+/*
+ * If this switch is set, MSI will not be used for PCIe PME signaling.  This
+ * causes the PCIe port driver to use INTx interrupts only, but it turns out
+ * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
+ * wake-up from system sleep states.
+ */
+bool pcie_pme_msi_disabled;
+
+static int __init pcie_pme_setup(char *str)
+{
+	if (!strncmp(str, "nomsi", 5))
+		pcie_pme_msi_disabled = true;
+
+	return 1;
+}
+__setup("pcie_pme=", pcie_pme_setup);
+
+struct pcie_pme_service_data {
+	spinlock_t lock;
+	struct pcie_device *srv;
+	struct work_struct work;
+	bool noirq; /* Don't enable the PME interrupt used by this service. */
+};
+
+/**
+ * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation.
+ * @dev: PCIe root port or event collector.
+ * @enable: Enable or disable the interrupt.
+ */
+void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
+{
+	int rtctl_pos;
+	u16 rtctl;
+
+	rtctl_pos = pci_pcie_cap(dev) + PCI_EXP_RTCTL;
+
+	pci_read_config_word(dev, rtctl_pos, &rtctl);
+	if (enable)
+		rtctl |= PCI_EXP_RTCTL_PMEIE;
+	else
+		rtctl &= ~PCI_EXP_RTCTL_PMEIE;
+	pci_write_config_word(dev, rtctl_pos, rtctl);
+}
+
+/**
+ * pcie_pme_clear_status - Clear root port PME interrupt status.
+ * @dev: PCIe root port or event collector.
+ */
+static void pcie_pme_clear_status(struct pci_dev *dev)
+{
+	int rtsta_pos;
+	u32 rtsta;
+
+	rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
+
+	pci_read_config_dword(dev, rtsta_pos, &rtsta);
+	rtsta |= PCI_EXP_RTSTA_PME;
+	pci_write_config_dword(dev, rtsta_pos, rtsta);
+}
+
+/**
+ * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#.
+ * @bus: PCI bus to scan.
+ *
+ * Scan given PCI bus and all buses under it for devices asserting PME#.
+ */
+static bool pcie_pme_walk_bus(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+	bool ret = false;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		/* Skip PCIe devices in case we started from a root port. */
+		if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) {
+			pm_request_resume(&dev->dev);
+			pci_wakeup_event(dev);
+			ret = true;
+		}
+
+		if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate))
+			ret = true;
+	}
+
+	return ret;
+}
+
+/**
+ * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME.
+ * @bus: Secondary bus of the bridge.
+ * @devfn: Device/function number to check.
+ *
+ * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band
+ * PCIe PME message.  In such that case the bridge should use the Requester ID
+ * of device/function number 0 on its secondary bus.
+ */
+static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
+{
+	struct pci_dev *dev;
+	bool found = false;
+
+	if (devfn)
+		return false;
+
+	dev = pci_dev_get(bus->self);
+	if (!dev)
+		return false;
+
+	if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
+		down_read(&pci_bus_sem);
+		if (pcie_pme_walk_bus(bus))
+			found = true;
+		up_read(&pci_bus_sem);
+	}
+
+	pci_dev_put(dev);
+	return found;
+}
+
+/**
+ * pcie_pme_handle_request - Find device that generated PME and handle it.
+ * @port: Root port or event collector that generated the PME interrupt.
+ * @req_id: PCIe Requester ID of the device that generated the PME.
+ */
+static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
+{
+	u8 busnr = req_id >> 8, devfn = req_id & 0xff;
+	struct pci_bus *bus;
+	struct pci_dev *dev;
+	bool found = false;
+
+	/* First, check if the PME is from the root port itself. */
+	if (port->devfn == devfn && port->bus->number == busnr) {
+		if (pci_check_pme_status(port)) {
+			pm_request_resume(&port->dev);
+			found = true;
+		} else {
+			/*
+			 * Apparently, the root port generated the PME on behalf
+			 * of a non-PCIe device downstream.  If this is done by
+			 * a root port, the Requester ID field in its status
+			 * register may contain either the root port's, or the
+			 * source device's information (PCI Express Base
+			 * Specification, Rev. 2.0, Section 6.1.9).
+			 */
+			down_read(&pci_bus_sem);
+			found = pcie_pme_walk_bus(port->subordinate);
+			up_read(&pci_bus_sem);
+		}
+		goto out;
+	}
+
+	/* Second, find the bus the source device is on. */
+	bus = pci_find_bus(pci_domain_nr(port->bus), busnr);
+	if (!bus)
+		goto out;
+
+	/* Next, check if the PME is from a PCIe-PCI bridge. */
+	found = pcie_pme_from_pci_bridge(bus, devfn);
+	if (found)
+		goto out;
+
+	/* Finally, try to find the PME source on the bus. */
+	down_read(&pci_bus_sem);
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		pci_dev_get(dev);
+		if (dev->devfn == devfn) {
+			found = true;
+			break;
+		}
+		pci_dev_put(dev);
+	}
+	up_read(&pci_bus_sem);
+
+	if (found) {
+		/* The device is there, but we have to check its PME status. */
+		found = pci_check_pme_status(dev);
+		if (found) {
+			pm_request_resume(&dev->dev);
+			pci_wakeup_event(dev);
+		}
+		pci_dev_put(dev);
+	} else if (devfn) {
+		/*
+		 * The device is not there, but we can still try to recover by
+		 * assuming that the PME was reported by a PCIe-PCI bridge that
+		 * used devfn different from zero.
+		 */
+		dev_dbg(&port->dev, "PME interrupt generated for "
+			"non-existent device %02x:%02x.%d\n",
+			busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
+		found = pcie_pme_from_pci_bridge(bus, 0);
+	}
+
+ out:
+	if (!found)
+		dev_dbg(&port->dev, "Spurious native PME interrupt!\n");
+}
+
+/**
+ * pcie_pme_work_fn - Work handler for PCIe PME interrupt.
+ * @work: Work structure giving access to service data.
+ */
+static void pcie_pme_work_fn(struct work_struct *work)
+{
+	struct pcie_pme_service_data *data =
+			container_of(work, struct pcie_pme_service_data, work);
+	struct pci_dev *port = data->srv->port;
+	int rtsta_pos;
+	u32 rtsta;
+
+	rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
+
+	spin_lock_irq(&data->lock);
+
+	for (;;) {
+		if (data->noirq)
+			break;
+
+		pci_read_config_dword(port, rtsta_pos, &rtsta);
+		if (rtsta & PCI_EXP_RTSTA_PME) {
+			/*
+			 * Clear PME status of the port.  If there are other
+			 * pending PMEs, the status will be set again.
+			 */
+			pcie_pme_clear_status(port);
+
+			spin_unlock_irq(&data->lock);
+			pcie_pme_handle_request(port, rtsta & 0xffff);
+			spin_lock_irq(&data->lock);
+
+			continue;
+		}
+
+		/* No need to loop if there are no more PMEs pending. */
+		if (!(rtsta & PCI_EXP_RTSTA_PENDING))
+			break;
+
+		spin_unlock_irq(&data->lock);
+		cpu_relax();
+		spin_lock_irq(&data->lock);
+	}
+
+	if (!data->noirq)
+		pcie_pme_interrupt_enable(port, true);
+
+	spin_unlock_irq(&data->lock);
+}
+
+/**
+ * pcie_pme_irq - Interrupt handler for PCIe root port PME interrupt.
+ * @irq: Interrupt vector.
+ * @context: Interrupt context pointer.
+ */
+static irqreturn_t pcie_pme_irq(int irq, void *context)
+{
+	struct pci_dev *port;
+	struct pcie_pme_service_data *data;
+	int rtsta_pos;
+	u32 rtsta;
+	unsigned long flags;
+
+	port = ((struct pcie_device *)context)->port;
+	data = get_service_data((struct pcie_device *)context);
+
+	rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
+
+	spin_lock_irqsave(&data->lock, flags);
+	pci_read_config_dword(port, rtsta_pos, &rtsta);
+
+	if (!(rtsta & PCI_EXP_RTSTA_PME)) {
+		spin_unlock_irqrestore(&data->lock, flags);
+		return IRQ_NONE;
+	}
+
+	pcie_pme_interrupt_enable(port, false);
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* We don't use pm_wq, because it's freezable. */
+	schedule_work(&data->work);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * pcie_pme_set_native - Set the PME interrupt flag for given device.
+ * @dev: PCI device to handle.
+ * @ign: Ignored.
+ */
+static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
+{
+	dev_info(&dev->dev, "Signaling PME through PCIe PME interrupt\n");
+
+	device_set_run_wake(&dev->dev, true);
+	dev->pme_interrupt = true;
+	return 0;
+}
+
+/**
+ * pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port.
+ * @port: PCIe root port or event collector to handle.
+ *
+ * For each device below given root port, including the port itself (or for each
+ * root complex integrated endpoint if @port is a root complex event collector)
+ * set the flag indicating that it can signal run-time wake-up events via PCIe
+ * PME interrupts.
+ */
+static void pcie_pme_mark_devices(struct pci_dev *port)
+{
+	pcie_pme_set_native(port, NULL);
+	if (port->subordinate) {
+		pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL);
+	} else {
+		struct pci_bus *bus = port->bus;
+		struct pci_dev *dev;
+
+		/* Check if this is a root port event collector. */
+		if (port->pcie_type != PCI_EXP_TYPE_RC_EC || !bus)
+			return;
+
+		down_read(&pci_bus_sem);
+		list_for_each_entry(dev, &bus->devices, bus_list)
+			if (pci_is_pcie(dev)
+			    && dev->pcie_type == PCI_EXP_TYPE_RC_END)
+				pcie_pme_set_native(dev, NULL);
+		up_read(&pci_bus_sem);
+	}
+}
+
+/**
+ * pcie_pme_probe - Initialize PCIe PME service for given root port.
+ * @srv: PCIe service to initialize.
+ */
+static int pcie_pme_probe(struct pcie_device *srv)
+{
+	struct pci_dev *port;
+	struct pcie_pme_service_data *data;
+	int ret;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	spin_lock_init(&data->lock);
+	INIT_WORK(&data->work, pcie_pme_work_fn);
+	data->srv = srv;
+	set_service_data(srv, data);
+
+	port = srv->port;
+	pcie_pme_interrupt_enable(port, false);
+	pcie_pme_clear_status(port);
+
+	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
+	if (ret) {
+		kfree(data);
+	} else {
+		pcie_pme_mark_devices(port);
+		pcie_pme_interrupt_enable(port, true);
+	}
+
+	return ret;
+}
+
+/**
+ * pcie_pme_suspend - Suspend PCIe PME service device.
+ * @srv: PCIe service device to suspend.
+ */
+static int pcie_pme_suspend(struct pcie_device *srv)
+{
+	struct pcie_pme_service_data *data = get_service_data(srv);
+	struct pci_dev *port = srv->port;
+
+	spin_lock_irq(&data->lock);
+	pcie_pme_interrupt_enable(port, false);
+	pcie_pme_clear_status(port);
+	data->noirq = true;
+	spin_unlock_irq(&data->lock);
+
+	synchronize_irq(srv->irq);
+
+	return 0;
+}
+
+/**
+ * pcie_pme_resume - Resume PCIe PME service device.
+ * @srv - PCIe service device to resume.
+ */
+static int pcie_pme_resume(struct pcie_device *srv)
+{
+	struct pcie_pme_service_data *data = get_service_data(srv);
+	struct pci_dev *port = srv->port;
+
+	spin_lock_irq(&data->lock);
+	data->noirq = false;
+	pcie_pme_clear_status(port);
+	pcie_pme_interrupt_enable(port, true);
+	spin_unlock_irq(&data->lock);
+
+	return 0;
+}
+
+/**
+ * pcie_pme_remove - Prepare PCIe PME service device for removal.
+ * @srv - PCIe service device to resume.
+ */
+static void pcie_pme_remove(struct pcie_device *srv)
+{
+	pcie_pme_suspend(srv);
+	free_irq(srv->irq, srv);
+	kfree(get_service_data(srv));
+}
+
+static struct pcie_port_service_driver pcie_pme_driver = {
+	.name		= "pcie_pme",
+	.port_type 	= PCI_EXP_TYPE_ROOT_PORT,
+	.service 	= PCIE_PORT_SERVICE_PME,
+
+	.probe		= pcie_pme_probe,
+	.suspend	= pcie_pme_suspend,
+	.resume		= pcie_pme_resume,
+	.remove		= pcie_pme_remove,
+};
+
+/**
+ * pcie_pme_service_init - Register the PCIe PME service driver.
+ */
+static int __init pcie_pme_service_init(void)
+{
+	return pcie_port_service_register(&pcie_pme_driver);
+}
+
+module_init(pcie_pme_service_init);
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * PCIe Native PME support
- *
- * Copyright (C) 2007 - 2009 Intel Corp
- * Copyright (C) 2007 - 2009 Shaohua Li <shaohua.li@intel.com>
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License V2.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/pcieport_if.h>
-#include <linux/acpi.h>
-#include <linux/pci-acpi.h>
-#include <linux/pm_runtime.h>
-
-#include "../../pci.h"
-#include "../portdrv.h"
-
-#define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
-#define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
-
-/*
- * If this switch is set, MSI will not be used for PCIe PME signaling.  This
- * causes the PCIe port driver to use INTx interrupts only, but it turns out
- * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
- * wake-up from system sleep states.
- */
-bool pcie_pme_msi_disabled;
-
-static int __init pcie_pme_setup(char *str)
-{
-	if (!strncmp(str, "nomsi", 5))
-		pcie_pme_msi_disabled = true;
-
-	return 1;
-}
-__setup("pcie_pme=", pcie_pme_setup);
-
-struct pcie_pme_service_data {
-	spinlock_t lock;
-	struct pcie_device *srv;
-	struct work_struct work;
-	bool noirq; /* Don't enable the PME interrupt used by this service. */
-};
-
-/**
- * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation.
- * @dev: PCIe root port or event collector.
- * @enable: Enable or disable the interrupt.
- */
-void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
-{
-	int rtctl_pos;
-	u16 rtctl;
-
-	rtctl_pos = pci_pcie_cap(dev) + PCI_EXP_RTCTL;
-
-	pci_read_config_word(dev, rtctl_pos, &rtctl);
-	if (enable)
-		rtctl |= PCI_EXP_RTCTL_PMEIE;
-	else
-		rtctl &= ~PCI_EXP_RTCTL_PMEIE;
-	pci_write_config_word(dev, rtctl_pos, rtctl);
-}
-
-/**
- * pcie_pme_clear_status - Clear root port PME interrupt status.
- * @dev: PCIe root port or event collector.
- */
-static void pcie_pme_clear_status(struct pci_dev *dev)
-{
-	int rtsta_pos;
-	u32 rtsta;
-
-	rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
-
-	pci_read_config_dword(dev, rtsta_pos, &rtsta);
-	rtsta |= PCI_EXP_RTSTA_PME;
-	pci_write_config_dword(dev, rtsta_pos, rtsta);
-}
-
-/**
- * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#.
- * @bus: PCI bus to scan.
- *
- * Scan given PCI bus and all buses under it for devices asserting PME#.
- */
-static bool pcie_pme_walk_bus(struct pci_bus *bus)
-{
-	struct pci_dev *dev;
-	bool ret = false;
-
-	list_for_each_entry(dev, &bus->devices, bus_list) {
-		/* Skip PCIe devices in case we started from a root port. */
-		if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) {
-			pm_request_resume(&dev->dev);
-			pci_wakeup_event(dev);
-			ret = true;
-		}
-
-		if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate))
-			ret = true;
-	}
-
-	return ret;
-}
-
-/**
- * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME.
- * @bus: Secondary bus of the bridge.
- * @devfn: Device/function number to check.
- *
- * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band
- * PCIe PME message.  In such that case the bridge should use the Requester ID
- * of device/function number 0 on its secondary bus.
- */
-static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
-{
-	struct pci_dev *dev;
-	bool found = false;
-
-	if (devfn)
-		return false;
-
-	dev = pci_dev_get(bus->self);
-	if (!dev)
-		return false;
-
-	if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
-		down_read(&pci_bus_sem);
-		if (pcie_pme_walk_bus(bus))
-			found = true;
-		up_read(&pci_bus_sem);
-	}
-
-	pci_dev_put(dev);
-	return found;
-}
-
-/**
- * pcie_pme_handle_request - Find device that generated PME and handle it.
- * @port: Root port or event collector that generated the PME interrupt.
- * @req_id: PCIe Requester ID of the device that generated the PME.
- */
-static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
-{
-	u8 busnr = req_id >> 8, devfn = req_id & 0xff;
-	struct pci_bus *bus;
-	struct pci_dev *dev;
-	bool found = false;
-
-	/* First, check if the PME is from the root port itself. */
-	if (port->devfn == devfn && port->bus->number == busnr) {
-		if (pci_check_pme_status(port)) {
-			pm_request_resume(&port->dev);
-			found = true;
-		} else {
-			/*
-			 * Apparently, the root port generated the PME on behalf
-			 * of a non-PCIe device downstream.  If this is done by
-			 * a root port, the Requester ID field in its status
-			 * register may contain either the root port's, or the
-			 * source device's information (PCI Express Base
-			 * Specification, Rev. 2.0, Section 6.1.9).
-			 */
-			down_read(&pci_bus_sem);
-			found = pcie_pme_walk_bus(port->subordinate);
-			up_read(&pci_bus_sem);
-		}
-		goto out;
-	}
-
-	/* Second, find the bus the source device is on. */
-	bus = pci_find_bus(pci_domain_nr(port->bus), busnr);
-	if (!bus)
-		goto out;
-
-	/* Next, check if the PME is from a PCIe-PCI bridge. */
-	found = pcie_pme_from_pci_bridge(bus, devfn);
-	if (found)
-		goto out;
-
-	/* Finally, try to find the PME source on the bus. */
-	down_read(&pci_bus_sem);
-	list_for_each_entry(dev, &bus->devices, bus_list) {
-		pci_dev_get(dev);
-		if (dev->devfn == devfn) {
-			found = true;
-			break;
-		}
-		pci_dev_put(dev);
-	}
-	up_read(&pci_bus_sem);
-
-	if (found) {
-		/* The device is there, but we have to check its PME status. */
-		found = pci_check_pme_status(dev);
-		if (found) {
-			pm_request_resume(&dev->dev);
-			pci_wakeup_event(dev);
-		}
-		pci_dev_put(dev);
-	} else if (devfn) {
-		/*
-		 * The device is not there, but we can still try to recover by
-		 * assuming that the PME was reported by a PCIe-PCI bridge that
-		 * used devfn different from zero.
-		 */
-		dev_dbg(&port->dev, "PME interrupt generated for "
-			"non-existent device %02x:%02x.%d\n",
-			busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
-		found = pcie_pme_from_pci_bridge(bus, 0);
-	}
-
- out:
-	if (!found)
-		dev_dbg(&port->dev, "Spurious native PME interrupt!\n");
-}
-
-/**
- * pcie_pme_work_fn - Work handler for PCIe PME interrupt.
- * @work: Work structure giving access to service data.
- */
-static void pcie_pme_work_fn(struct work_struct *work)
-{
-	struct pcie_pme_service_data *data =
-			container_of(work, struct pcie_pme_service_data, work);
-	struct pci_dev *port = data->srv->port;
-	int rtsta_pos;
-	u32 rtsta;
-
-	rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
-
-	spin_lock_irq(&data->lock);
-
-	for (;;) {
-		if (data->noirq)
-			break;
-
-		pci_read_config_dword(port, rtsta_pos, &rtsta);
-		if (rtsta & PCI_EXP_RTSTA_PME) {
-			/*
-			 * Clear PME status of the port.  If there are other
-			 * pending PMEs, the status will be set again.
-			 */
-			pcie_pme_clear_status(port);
-
-			spin_unlock_irq(&data->lock);
-			pcie_pme_handle_request(port, rtsta & 0xffff);
-			spin_lock_irq(&data->lock);
-
-			continue;
-		}
-
-		/* No need to loop if there are no more PMEs pending. */
-		if (!(rtsta & PCI_EXP_RTSTA_PENDING))
-			break;
-
-		spin_unlock_irq(&data->lock);
-		cpu_relax();
-		spin_lock_irq(&data->lock);
-	}
-
-	if (!data->noirq)
-		pcie_pme_interrupt_enable(port, true);
-
-	spin_unlock_irq(&data->lock);
-}
-
-/**
- * pcie_pme_irq - Interrupt handler for PCIe root port PME interrupt.
- * @irq: Interrupt vector.
- * @context: Interrupt context pointer.
- */
-static irqreturn_t pcie_pme_irq(int irq, void *context)
-{
-	struct pci_dev *port;
-	struct pcie_pme_service_data *data;
-	int rtsta_pos;
-	u32 rtsta;
-	unsigned long flags;
-
-	port = ((struct pcie_device *)context)->port;
-	data = get_service_data((struct pcie_device *)context);
-
-	rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
-
-	spin_lock_irqsave(&data->lock, flags);
-	pci_read_config_dword(port, rtsta_pos, &rtsta);
-
-	if (!(rtsta & PCI_EXP_RTSTA_PME)) {
-		spin_unlock_irqrestore(&data->lock, flags);
-		return IRQ_NONE;
-	}
-
-	pcie_pme_interrupt_enable(port, false);
-	spin_unlock_irqrestore(&data->lock, flags);
-
-	/* We don't use pm_wq, because it's freezable. */
-	schedule_work(&data->work);
-
-	return IRQ_HANDLED;
-}
-
-/**
- * pcie_pme_set_native - Set the PME interrupt flag for given device.
- * @dev: PCI device to handle.
- * @ign: Ignored.
- */
-static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
-{
-	dev_info(&dev->dev, "Signaling PME through PCIe PME interrupt\n");
-
-	device_set_run_wake(&dev->dev, true);
-	dev->pme_interrupt = true;
-	return 0;
-}
-
-/**
- * pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port.
- * @port: PCIe root port or event collector to handle.
- *
- * For each device below given root port, including the port itself (or for each
- * root complex integrated endpoint if @port is a root complex event collector)
- * set the flag indicating that it can signal run-time wake-up events via PCIe
- * PME interrupts.
- */
-static void pcie_pme_mark_devices(struct pci_dev *port)
-{
-	pcie_pme_set_native(port, NULL);
-	if (port->subordinate) {
-		pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL);
-	} else {
-		struct pci_bus *bus = port->bus;
-		struct pci_dev *dev;
-
-		/* Check if this is a root port event collector. */
-		if (port->pcie_type != PCI_EXP_TYPE_RC_EC || !bus)
-			return;
-
-		down_read(&pci_bus_sem);
-		list_for_each_entry(dev, &bus->devices, bus_list)
-			if (pci_is_pcie(dev)
-			    && dev->pcie_type == PCI_EXP_TYPE_RC_END)
-				pcie_pme_set_native(dev, NULL);
-		up_read(&pci_bus_sem);
-	}
-}
-
-/**
- * pcie_pme_probe - Initialize PCIe PME service for given root port.
- * @srv: PCIe service to initialize.
- */
-static int pcie_pme_probe(struct pcie_device *srv)
-{
-	struct pci_dev *port;
-	struct pcie_pme_service_data *data;
-	int ret;
-
-	data = kzalloc(sizeof(*data), GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	spin_lock_init(&data->lock);
-	INIT_WORK(&data->work, pcie_pme_work_fn);
-	data->srv = srv;
-	set_service_data(srv, data);
-
-	port = srv->port;
-	pcie_pme_interrupt_enable(port, false);
-	pcie_pme_clear_status(port);
-
-	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
-	if (ret) {
-		kfree(data);
-	} else {
-		pcie_pme_mark_devices(port);
-		pcie_pme_interrupt_enable(port, true);
-	}
-
-	return ret;
-}
-
-/**
- * pcie_pme_suspend - Suspend PCIe PME service device.
- * @srv: PCIe service device to suspend.
- */
-static int pcie_pme_suspend(struct pcie_device *srv)
-{
-	struct pcie_pme_service_data *data = get_service_data(srv);
-	struct pci_dev *port = srv->port;
-
-	spin_lock_irq(&data->lock);
-	pcie_pme_interrupt_enable(port, false);
-	pcie_pme_clear_status(port);
-	data->noirq = true;
-	spin_unlock_irq(&data->lock);
-
-	synchronize_irq(srv->irq);
-
-	return 0;
-}
-
-/**
- * pcie_pme_resume - Resume PCIe PME service device.
- * @srv - PCIe service device to resume.
- */
-static int pcie_pme_resume(struct pcie_device *srv)
-{
-	struct pcie_pme_service_data *data = get_service_data(srv);
-	struct pci_dev *port = srv->port;
-
-	spin_lock_irq(&data->lock);
-	data->noirq = false;
-	pcie_pme_clear_status(port);
-	pcie_pme_interrupt_enable(port, true);
-	spin_unlock_irq(&data->lock);
-
-	return 0;
-}
-
-/**
- * pcie_pme_remove - Prepare PCIe PME service device for removal.
- * @srv - PCIe service device to resume.
- */
-static void pcie_pme_remove(struct pcie_device *srv)
-{
-	pcie_pme_suspend(srv);
-	free_irq(srv->irq, srv);
-	kfree(get_service_data(srv));
-}
-
-static struct pcie_port_service_driver pcie_pme_driver = {
-	.name		= "pcie_pme",
-	.port_type 	= PCI_EXP_TYPE_ROOT_PORT,
-	.service 	= PCIE_PORT_SERVICE_PME,
-
-	.probe		= pcie_pme_probe,
-	.suspend	= pcie_pme_suspend,
-	.resume		= pcie_pme_resume,
-	.remove		= pcie_pme_remove,
-};
-
-/**
- * pcie_pme_service_init - Register the PCIe PME service driver.
- */
-static int __init pcie_pme_service_init(void)
-{
-	return pcie_port_service_register(&pcie_pme_driver);
-}
-
-module_init(pcie_pme_service_init);
Index: linux-2.6/drivers/pci/pcie/pme/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Makefile for PCI-Express Root Port PME signaling driver
-#
-
-obj-$(CONFIG_PCIE_PME) += pcie_pme.o
Index: linux-2.6/drivers/pci/pcie/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/Makefile
+++ linux-2.6/drivers/pci/pcie/Makefile
@@ -13,4 +13,4 @@ obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv
 # Build PCI Express AER if needed
 obj-$(CONFIG_PCIEAER)		+= aer/
 
-obj-$(CONFIG_PCIE_PME) += pme/
+obj-$(CONFIG_PCIE_PME) += pme.o

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 9/10] PCI / PCIe: Move PCIe PME code to the pcie directory
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (16 preceding siblings ...)
  2010-08-20 23:58   ` [PATCH 9/10] PCI / PCIe: Move PCIe PME code to the pcie directory Rafael J. Wysocki
@ 2010-08-20 23:58   ` Rafael J. Wysocki
  2010-08-20 23:59   ` [PATCH 10/10] PCI / PCIe: Remove the port driver module exit routine Rafael J. Wysocki
  2010-08-20 23:59   ` Rafael J. Wysocki
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:58 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Rafael J. Wysocki <rjw@sisk.pl>

The PCIe PME code only consists of one file, so it doesn't need to
occupy its own directory.  Move it to drivers/pci/pcie/pme.c and
remove the contents of drivers/pci/pcie/pme .

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/pci/pcie/Makefile       |    2 
 drivers/pci/pcie/pme.c          |  462 ++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pcie/pme/Makefile   |    5 
 drivers/pci/pcie/pme/pcie_pme.c |  462 ----------------------------------------
 4 files changed, 463 insertions(+), 468 deletions(-)

Index: linux-2.6/drivers/pci/pcie/pme.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/pci/pcie/pme.c
@@ -0,0 +1,462 @@
+/*
+ * PCIe Native PME support
+ *
+ * Copyright (C) 2007 - 2009 Intel Corp
+ * Copyright (C) 2007 - 2009 Shaohua Li <shaohua.li@intel.com>
+ * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License V2.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/pcieport_if.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/pm_runtime.h>
+
+#include "../pci.h"
+#include "portdrv.h"
+
+#define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
+#define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
+
+/*
+ * If this switch is set, MSI will not be used for PCIe PME signaling.  This
+ * causes the PCIe port driver to use INTx interrupts only, but it turns out
+ * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
+ * wake-up from system sleep states.
+ */
+bool pcie_pme_msi_disabled;
+
+static int __init pcie_pme_setup(char *str)
+{
+	if (!strncmp(str, "nomsi", 5))
+		pcie_pme_msi_disabled = true;
+
+	return 1;
+}
+__setup("pcie_pme=", pcie_pme_setup);
+
+struct pcie_pme_service_data {
+	spinlock_t lock;
+	struct pcie_device *srv;
+	struct work_struct work;
+	bool noirq; /* Don't enable the PME interrupt used by this service. */
+};
+
+/**
+ * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation.
+ * @dev: PCIe root port or event collector.
+ * @enable: Enable or disable the interrupt.
+ */
+void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
+{
+	int rtctl_pos;
+	u16 rtctl;
+
+	rtctl_pos = pci_pcie_cap(dev) + PCI_EXP_RTCTL;
+
+	pci_read_config_word(dev, rtctl_pos, &rtctl);
+	if (enable)
+		rtctl |= PCI_EXP_RTCTL_PMEIE;
+	else
+		rtctl &= ~PCI_EXP_RTCTL_PMEIE;
+	pci_write_config_word(dev, rtctl_pos, rtctl);
+}
+
+/**
+ * pcie_pme_clear_status - Clear root port PME interrupt status.
+ * @dev: PCIe root port or event collector.
+ */
+static void pcie_pme_clear_status(struct pci_dev *dev)
+{
+	int rtsta_pos;
+	u32 rtsta;
+
+	rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
+
+	pci_read_config_dword(dev, rtsta_pos, &rtsta);
+	rtsta |= PCI_EXP_RTSTA_PME;
+	pci_write_config_dword(dev, rtsta_pos, rtsta);
+}
+
+/**
+ * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#.
+ * @bus: PCI bus to scan.
+ *
+ * Scan given PCI bus and all buses under it for devices asserting PME#.
+ */
+static bool pcie_pme_walk_bus(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+	bool ret = false;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		/* Skip PCIe devices in case we started from a root port. */
+		if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) {
+			pm_request_resume(&dev->dev);
+			pci_wakeup_event(dev);
+			ret = true;
+		}
+
+		if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate))
+			ret = true;
+	}
+
+	return ret;
+}
+
+/**
+ * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME.
+ * @bus: Secondary bus of the bridge.
+ * @devfn: Device/function number to check.
+ *
+ * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band
+ * PCIe PME message.  In such that case the bridge should use the Requester ID
+ * of device/function number 0 on its secondary bus.
+ */
+static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
+{
+	struct pci_dev *dev;
+	bool found = false;
+
+	if (devfn)
+		return false;
+
+	dev = pci_dev_get(bus->self);
+	if (!dev)
+		return false;
+
+	if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
+		down_read(&pci_bus_sem);
+		if (pcie_pme_walk_bus(bus))
+			found = true;
+		up_read(&pci_bus_sem);
+	}
+
+	pci_dev_put(dev);
+	return found;
+}
+
+/**
+ * pcie_pme_handle_request - Find device that generated PME and handle it.
+ * @port: Root port or event collector that generated the PME interrupt.
+ * @req_id: PCIe Requester ID of the device that generated the PME.
+ */
+static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
+{
+	u8 busnr = req_id >> 8, devfn = req_id & 0xff;
+	struct pci_bus *bus;
+	struct pci_dev *dev;
+	bool found = false;
+
+	/* First, check if the PME is from the root port itself. */
+	if (port->devfn == devfn && port->bus->number == busnr) {
+		if (pci_check_pme_status(port)) {
+			pm_request_resume(&port->dev);
+			found = true;
+		} else {
+			/*
+			 * Apparently, the root port generated the PME on behalf
+			 * of a non-PCIe device downstream.  If this is done by
+			 * a root port, the Requester ID field in its status
+			 * register may contain either the root port's, or the
+			 * source device's information (PCI Express Base
+			 * Specification, Rev. 2.0, Section 6.1.9).
+			 */
+			down_read(&pci_bus_sem);
+			found = pcie_pme_walk_bus(port->subordinate);
+			up_read(&pci_bus_sem);
+		}
+		goto out;
+	}
+
+	/* Second, find the bus the source device is on. */
+	bus = pci_find_bus(pci_domain_nr(port->bus), busnr);
+	if (!bus)
+		goto out;
+
+	/* Next, check if the PME is from a PCIe-PCI bridge. */
+	found = pcie_pme_from_pci_bridge(bus, devfn);
+	if (found)
+		goto out;
+
+	/* Finally, try to find the PME source on the bus. */
+	down_read(&pci_bus_sem);
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		pci_dev_get(dev);
+		if (dev->devfn == devfn) {
+			found = true;
+			break;
+		}
+		pci_dev_put(dev);
+	}
+	up_read(&pci_bus_sem);
+
+	if (found) {
+		/* The device is there, but we have to check its PME status. */
+		found = pci_check_pme_status(dev);
+		if (found) {
+			pm_request_resume(&dev->dev);
+			pci_wakeup_event(dev);
+		}
+		pci_dev_put(dev);
+	} else if (devfn) {
+		/*
+		 * The device is not there, but we can still try to recover by
+		 * assuming that the PME was reported by a PCIe-PCI bridge that
+		 * used devfn different from zero.
+		 */
+		dev_dbg(&port->dev, "PME interrupt generated for "
+			"non-existent device %02x:%02x.%d\n",
+			busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
+		found = pcie_pme_from_pci_bridge(bus, 0);
+	}
+
+ out:
+	if (!found)
+		dev_dbg(&port->dev, "Spurious native PME interrupt!\n");
+}
+
+/**
+ * pcie_pme_work_fn - Work handler for PCIe PME interrupt.
+ * @work: Work structure giving access to service data.
+ */
+static void pcie_pme_work_fn(struct work_struct *work)
+{
+	struct pcie_pme_service_data *data =
+			container_of(work, struct pcie_pme_service_data, work);
+	struct pci_dev *port = data->srv->port;
+	int rtsta_pos;
+	u32 rtsta;
+
+	rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
+
+	spin_lock_irq(&data->lock);
+
+	for (;;) {
+		if (data->noirq)
+			break;
+
+		pci_read_config_dword(port, rtsta_pos, &rtsta);
+		if (rtsta & PCI_EXP_RTSTA_PME) {
+			/*
+			 * Clear PME status of the port.  If there are other
+			 * pending PMEs, the status will be set again.
+			 */
+			pcie_pme_clear_status(port);
+
+			spin_unlock_irq(&data->lock);
+			pcie_pme_handle_request(port, rtsta & 0xffff);
+			spin_lock_irq(&data->lock);
+
+			continue;
+		}
+
+		/* No need to loop if there are no more PMEs pending. */
+		if (!(rtsta & PCI_EXP_RTSTA_PENDING))
+			break;
+
+		spin_unlock_irq(&data->lock);
+		cpu_relax();
+		spin_lock_irq(&data->lock);
+	}
+
+	if (!data->noirq)
+		pcie_pme_interrupt_enable(port, true);
+
+	spin_unlock_irq(&data->lock);
+}
+
+/**
+ * pcie_pme_irq - Interrupt handler for PCIe root port PME interrupt.
+ * @irq: Interrupt vector.
+ * @context: Interrupt context pointer.
+ */
+static irqreturn_t pcie_pme_irq(int irq, void *context)
+{
+	struct pci_dev *port;
+	struct pcie_pme_service_data *data;
+	int rtsta_pos;
+	u32 rtsta;
+	unsigned long flags;
+
+	port = ((struct pcie_device *)context)->port;
+	data = get_service_data((struct pcie_device *)context);
+
+	rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
+
+	spin_lock_irqsave(&data->lock, flags);
+	pci_read_config_dword(port, rtsta_pos, &rtsta);
+
+	if (!(rtsta & PCI_EXP_RTSTA_PME)) {
+		spin_unlock_irqrestore(&data->lock, flags);
+		return IRQ_NONE;
+	}
+
+	pcie_pme_interrupt_enable(port, false);
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* We don't use pm_wq, because it's freezable. */
+	schedule_work(&data->work);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * pcie_pme_set_native - Set the PME interrupt flag for given device.
+ * @dev: PCI device to handle.
+ * @ign: Ignored.
+ */
+static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
+{
+	dev_info(&dev->dev, "Signaling PME through PCIe PME interrupt\n");
+
+	device_set_run_wake(&dev->dev, true);
+	dev->pme_interrupt = true;
+	return 0;
+}
+
+/**
+ * pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port.
+ * @port: PCIe root port or event collector to handle.
+ *
+ * For each device below given root port, including the port itself (or for each
+ * root complex integrated endpoint if @port is a root complex event collector)
+ * set the flag indicating that it can signal run-time wake-up events via PCIe
+ * PME interrupts.
+ */
+static void pcie_pme_mark_devices(struct pci_dev *port)
+{
+	pcie_pme_set_native(port, NULL);
+	if (port->subordinate) {
+		pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL);
+	} else {
+		struct pci_bus *bus = port->bus;
+		struct pci_dev *dev;
+
+		/* Check if this is a root port event collector. */
+		if (port->pcie_type != PCI_EXP_TYPE_RC_EC || !bus)
+			return;
+
+		down_read(&pci_bus_sem);
+		list_for_each_entry(dev, &bus->devices, bus_list)
+			if (pci_is_pcie(dev)
+			    && dev->pcie_type == PCI_EXP_TYPE_RC_END)
+				pcie_pme_set_native(dev, NULL);
+		up_read(&pci_bus_sem);
+	}
+}
+
+/**
+ * pcie_pme_probe - Initialize PCIe PME service for given root port.
+ * @srv: PCIe service to initialize.
+ */
+static int pcie_pme_probe(struct pcie_device *srv)
+{
+	struct pci_dev *port;
+	struct pcie_pme_service_data *data;
+	int ret;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	spin_lock_init(&data->lock);
+	INIT_WORK(&data->work, pcie_pme_work_fn);
+	data->srv = srv;
+	set_service_data(srv, data);
+
+	port = srv->port;
+	pcie_pme_interrupt_enable(port, false);
+	pcie_pme_clear_status(port);
+
+	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
+	if (ret) {
+		kfree(data);
+	} else {
+		pcie_pme_mark_devices(port);
+		pcie_pme_interrupt_enable(port, true);
+	}
+
+	return ret;
+}
+
+/**
+ * pcie_pme_suspend - Suspend PCIe PME service device.
+ * @srv: PCIe service device to suspend.
+ */
+static int pcie_pme_suspend(struct pcie_device *srv)
+{
+	struct pcie_pme_service_data *data = get_service_data(srv);
+	struct pci_dev *port = srv->port;
+
+	spin_lock_irq(&data->lock);
+	pcie_pme_interrupt_enable(port, false);
+	pcie_pme_clear_status(port);
+	data->noirq = true;
+	spin_unlock_irq(&data->lock);
+
+	synchronize_irq(srv->irq);
+
+	return 0;
+}
+
+/**
+ * pcie_pme_resume - Resume PCIe PME service device.
+ * @srv - PCIe service device to resume.
+ */
+static int pcie_pme_resume(struct pcie_device *srv)
+{
+	struct pcie_pme_service_data *data = get_service_data(srv);
+	struct pci_dev *port = srv->port;
+
+	spin_lock_irq(&data->lock);
+	data->noirq = false;
+	pcie_pme_clear_status(port);
+	pcie_pme_interrupt_enable(port, true);
+	spin_unlock_irq(&data->lock);
+
+	return 0;
+}
+
+/**
+ * pcie_pme_remove - Prepare PCIe PME service device for removal.
+ * @srv - PCIe service device to resume.
+ */
+static void pcie_pme_remove(struct pcie_device *srv)
+{
+	pcie_pme_suspend(srv);
+	free_irq(srv->irq, srv);
+	kfree(get_service_data(srv));
+}
+
+static struct pcie_port_service_driver pcie_pme_driver = {
+	.name		= "pcie_pme",
+	.port_type 	= PCI_EXP_TYPE_ROOT_PORT,
+	.service 	= PCIE_PORT_SERVICE_PME,
+
+	.probe		= pcie_pme_probe,
+	.suspend	= pcie_pme_suspend,
+	.resume		= pcie_pme_resume,
+	.remove		= pcie_pme_remove,
+};
+
+/**
+ * pcie_pme_service_init - Register the PCIe PME service driver.
+ */
+static int __init pcie_pme_service_init(void)
+{
+	return pcie_port_service_register(&pcie_pme_driver);
+}
+
+module_init(pcie_pme_service_init);
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * PCIe Native PME support
- *
- * Copyright (C) 2007 - 2009 Intel Corp
- * Copyright (C) 2007 - 2009 Shaohua Li <shaohua.li@intel.com>
- * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License V2.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/pcieport_if.h>
-#include <linux/acpi.h>
-#include <linux/pci-acpi.h>
-#include <linux/pm_runtime.h>
-
-#include "../../pci.h"
-#include "../portdrv.h"
-
-#define PCI_EXP_RTSTA_PME	0x10000 /* PME status */
-#define PCI_EXP_RTSTA_PENDING	0x20000 /* PME pending */
-
-/*
- * If this switch is set, MSI will not be used for PCIe PME signaling.  This
- * causes the PCIe port driver to use INTx interrupts only, but it turns out
- * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
- * wake-up from system sleep states.
- */
-bool pcie_pme_msi_disabled;
-
-static int __init pcie_pme_setup(char *str)
-{
-	if (!strncmp(str, "nomsi", 5))
-		pcie_pme_msi_disabled = true;
-
-	return 1;
-}
-__setup("pcie_pme=", pcie_pme_setup);
-
-struct pcie_pme_service_data {
-	spinlock_t lock;
-	struct pcie_device *srv;
-	struct work_struct work;
-	bool noirq; /* Don't enable the PME interrupt used by this service. */
-};
-
-/**
- * pcie_pme_interrupt_enable - Enable/disable PCIe PME interrupt generation.
- * @dev: PCIe root port or event collector.
- * @enable: Enable or disable the interrupt.
- */
-void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
-{
-	int rtctl_pos;
-	u16 rtctl;
-
-	rtctl_pos = pci_pcie_cap(dev) + PCI_EXP_RTCTL;
-
-	pci_read_config_word(dev, rtctl_pos, &rtctl);
-	if (enable)
-		rtctl |= PCI_EXP_RTCTL_PMEIE;
-	else
-		rtctl &= ~PCI_EXP_RTCTL_PMEIE;
-	pci_write_config_word(dev, rtctl_pos, rtctl);
-}
-
-/**
- * pcie_pme_clear_status - Clear root port PME interrupt status.
- * @dev: PCIe root port or event collector.
- */
-static void pcie_pme_clear_status(struct pci_dev *dev)
-{
-	int rtsta_pos;
-	u32 rtsta;
-
-	rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
-
-	pci_read_config_dword(dev, rtsta_pos, &rtsta);
-	rtsta |= PCI_EXP_RTSTA_PME;
-	pci_write_config_dword(dev, rtsta_pos, rtsta);
-}
-
-/**
- * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#.
- * @bus: PCI bus to scan.
- *
- * Scan given PCI bus and all buses under it for devices asserting PME#.
- */
-static bool pcie_pme_walk_bus(struct pci_bus *bus)
-{
-	struct pci_dev *dev;
-	bool ret = false;
-
-	list_for_each_entry(dev, &bus->devices, bus_list) {
-		/* Skip PCIe devices in case we started from a root port. */
-		if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) {
-			pm_request_resume(&dev->dev);
-			pci_wakeup_event(dev);
-			ret = true;
-		}
-
-		if (dev->subordinate && pcie_pme_walk_bus(dev->subordinate))
-			ret = true;
-	}
-
-	return ret;
-}
-
-/**
- * pcie_pme_from_pci_bridge - Check if PCIe-PCI bridge generated a PME.
- * @bus: Secondary bus of the bridge.
- * @devfn: Device/function number to check.
- *
- * PME from PCI devices under a PCIe-PCI bridge may be converted to an in-band
- * PCIe PME message.  In such that case the bridge should use the Requester ID
- * of device/function number 0 on its secondary bus.
- */
-static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
-{
-	struct pci_dev *dev;
-	bool found = false;
-
-	if (devfn)
-		return false;
-
-	dev = pci_dev_get(bus->self);
-	if (!dev)
-		return false;
-
-	if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
-		down_read(&pci_bus_sem);
-		if (pcie_pme_walk_bus(bus))
-			found = true;
-		up_read(&pci_bus_sem);
-	}
-
-	pci_dev_put(dev);
-	return found;
-}
-
-/**
- * pcie_pme_handle_request - Find device that generated PME and handle it.
- * @port: Root port or event collector that generated the PME interrupt.
- * @req_id: PCIe Requester ID of the device that generated the PME.
- */
-static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
-{
-	u8 busnr = req_id >> 8, devfn = req_id & 0xff;
-	struct pci_bus *bus;
-	struct pci_dev *dev;
-	bool found = false;
-
-	/* First, check if the PME is from the root port itself. */
-	if (port->devfn == devfn && port->bus->number == busnr) {
-		if (pci_check_pme_status(port)) {
-			pm_request_resume(&port->dev);
-			found = true;
-		} else {
-			/*
-			 * Apparently, the root port generated the PME on behalf
-			 * of a non-PCIe device downstream.  If this is done by
-			 * a root port, the Requester ID field in its status
-			 * register may contain either the root port's, or the
-			 * source device's information (PCI Express Base
-			 * Specification, Rev. 2.0, Section 6.1.9).
-			 */
-			down_read(&pci_bus_sem);
-			found = pcie_pme_walk_bus(port->subordinate);
-			up_read(&pci_bus_sem);
-		}
-		goto out;
-	}
-
-	/* Second, find the bus the source device is on. */
-	bus = pci_find_bus(pci_domain_nr(port->bus), busnr);
-	if (!bus)
-		goto out;
-
-	/* Next, check if the PME is from a PCIe-PCI bridge. */
-	found = pcie_pme_from_pci_bridge(bus, devfn);
-	if (found)
-		goto out;
-
-	/* Finally, try to find the PME source on the bus. */
-	down_read(&pci_bus_sem);
-	list_for_each_entry(dev, &bus->devices, bus_list) {
-		pci_dev_get(dev);
-		if (dev->devfn == devfn) {
-			found = true;
-			break;
-		}
-		pci_dev_put(dev);
-	}
-	up_read(&pci_bus_sem);
-
-	if (found) {
-		/* The device is there, but we have to check its PME status. */
-		found = pci_check_pme_status(dev);
-		if (found) {
-			pm_request_resume(&dev->dev);
-			pci_wakeup_event(dev);
-		}
-		pci_dev_put(dev);
-	} else if (devfn) {
-		/*
-		 * The device is not there, but we can still try to recover by
-		 * assuming that the PME was reported by a PCIe-PCI bridge that
-		 * used devfn different from zero.
-		 */
-		dev_dbg(&port->dev, "PME interrupt generated for "
-			"non-existent device %02x:%02x.%d\n",
-			busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
-		found = pcie_pme_from_pci_bridge(bus, 0);
-	}
-
- out:
-	if (!found)
-		dev_dbg(&port->dev, "Spurious native PME interrupt!\n");
-}
-
-/**
- * pcie_pme_work_fn - Work handler for PCIe PME interrupt.
- * @work: Work structure giving access to service data.
- */
-static void pcie_pme_work_fn(struct work_struct *work)
-{
-	struct pcie_pme_service_data *data =
-			container_of(work, struct pcie_pme_service_data, work);
-	struct pci_dev *port = data->srv->port;
-	int rtsta_pos;
-	u32 rtsta;
-
-	rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
-
-	spin_lock_irq(&data->lock);
-
-	for (;;) {
-		if (data->noirq)
-			break;
-
-		pci_read_config_dword(port, rtsta_pos, &rtsta);
-		if (rtsta & PCI_EXP_RTSTA_PME) {
-			/*
-			 * Clear PME status of the port.  If there are other
-			 * pending PMEs, the status will be set again.
-			 */
-			pcie_pme_clear_status(port);
-
-			spin_unlock_irq(&data->lock);
-			pcie_pme_handle_request(port, rtsta & 0xffff);
-			spin_lock_irq(&data->lock);
-
-			continue;
-		}
-
-		/* No need to loop if there are no more PMEs pending. */
-		if (!(rtsta & PCI_EXP_RTSTA_PENDING))
-			break;
-
-		spin_unlock_irq(&data->lock);
-		cpu_relax();
-		spin_lock_irq(&data->lock);
-	}
-
-	if (!data->noirq)
-		pcie_pme_interrupt_enable(port, true);
-
-	spin_unlock_irq(&data->lock);
-}
-
-/**
- * pcie_pme_irq - Interrupt handler for PCIe root port PME interrupt.
- * @irq: Interrupt vector.
- * @context: Interrupt context pointer.
- */
-static irqreturn_t pcie_pme_irq(int irq, void *context)
-{
-	struct pci_dev *port;
-	struct pcie_pme_service_data *data;
-	int rtsta_pos;
-	u32 rtsta;
-	unsigned long flags;
-
-	port = ((struct pcie_device *)context)->port;
-	data = get_service_data((struct pcie_device *)context);
-
-	rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
-
-	spin_lock_irqsave(&data->lock, flags);
-	pci_read_config_dword(port, rtsta_pos, &rtsta);
-
-	if (!(rtsta & PCI_EXP_RTSTA_PME)) {
-		spin_unlock_irqrestore(&data->lock, flags);
-		return IRQ_NONE;
-	}
-
-	pcie_pme_interrupt_enable(port, false);
-	spin_unlock_irqrestore(&data->lock, flags);
-
-	/* We don't use pm_wq, because it's freezable. */
-	schedule_work(&data->work);
-
-	return IRQ_HANDLED;
-}
-
-/**
- * pcie_pme_set_native - Set the PME interrupt flag for given device.
- * @dev: PCI device to handle.
- * @ign: Ignored.
- */
-static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
-{
-	dev_info(&dev->dev, "Signaling PME through PCIe PME interrupt\n");
-
-	device_set_run_wake(&dev->dev, true);
-	dev->pme_interrupt = true;
-	return 0;
-}
-
-/**
- * pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port.
- * @port: PCIe root port or event collector to handle.
- *
- * For each device below given root port, including the port itself (or for each
- * root complex integrated endpoint if @port is a root complex event collector)
- * set the flag indicating that it can signal run-time wake-up events via PCIe
- * PME interrupts.
- */
-static void pcie_pme_mark_devices(struct pci_dev *port)
-{
-	pcie_pme_set_native(port, NULL);
-	if (port->subordinate) {
-		pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL);
-	} else {
-		struct pci_bus *bus = port->bus;
-		struct pci_dev *dev;
-
-		/* Check if this is a root port event collector. */
-		if (port->pcie_type != PCI_EXP_TYPE_RC_EC || !bus)
-			return;
-
-		down_read(&pci_bus_sem);
-		list_for_each_entry(dev, &bus->devices, bus_list)
-			if (pci_is_pcie(dev)
-			    && dev->pcie_type == PCI_EXP_TYPE_RC_END)
-				pcie_pme_set_native(dev, NULL);
-		up_read(&pci_bus_sem);
-	}
-}
-
-/**
- * pcie_pme_probe - Initialize PCIe PME service for given root port.
- * @srv: PCIe service to initialize.
- */
-static int pcie_pme_probe(struct pcie_device *srv)
-{
-	struct pci_dev *port;
-	struct pcie_pme_service_data *data;
-	int ret;
-
-	data = kzalloc(sizeof(*data), GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
-
-	spin_lock_init(&data->lock);
-	INIT_WORK(&data->work, pcie_pme_work_fn);
-	data->srv = srv;
-	set_service_data(srv, data);
-
-	port = srv->port;
-	pcie_pme_interrupt_enable(port, false);
-	pcie_pme_clear_status(port);
-
-	ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
-	if (ret) {
-		kfree(data);
-	} else {
-		pcie_pme_mark_devices(port);
-		pcie_pme_interrupt_enable(port, true);
-	}
-
-	return ret;
-}
-
-/**
- * pcie_pme_suspend - Suspend PCIe PME service device.
- * @srv: PCIe service device to suspend.
- */
-static int pcie_pme_suspend(struct pcie_device *srv)
-{
-	struct pcie_pme_service_data *data = get_service_data(srv);
-	struct pci_dev *port = srv->port;
-
-	spin_lock_irq(&data->lock);
-	pcie_pme_interrupt_enable(port, false);
-	pcie_pme_clear_status(port);
-	data->noirq = true;
-	spin_unlock_irq(&data->lock);
-
-	synchronize_irq(srv->irq);
-
-	return 0;
-}
-
-/**
- * pcie_pme_resume - Resume PCIe PME service device.
- * @srv - PCIe service device to resume.
- */
-static int pcie_pme_resume(struct pcie_device *srv)
-{
-	struct pcie_pme_service_data *data = get_service_data(srv);
-	struct pci_dev *port = srv->port;
-
-	spin_lock_irq(&data->lock);
-	data->noirq = false;
-	pcie_pme_clear_status(port);
-	pcie_pme_interrupt_enable(port, true);
-	spin_unlock_irq(&data->lock);
-
-	return 0;
-}
-
-/**
- * pcie_pme_remove - Prepare PCIe PME service device for removal.
- * @srv - PCIe service device to resume.
- */
-static void pcie_pme_remove(struct pcie_device *srv)
-{
-	pcie_pme_suspend(srv);
-	free_irq(srv->irq, srv);
-	kfree(get_service_data(srv));
-}
-
-static struct pcie_port_service_driver pcie_pme_driver = {
-	.name		= "pcie_pme",
-	.port_type 	= PCI_EXP_TYPE_ROOT_PORT,
-	.service 	= PCIE_PORT_SERVICE_PME,
-
-	.probe		= pcie_pme_probe,
-	.suspend	= pcie_pme_suspend,
-	.resume		= pcie_pme_resume,
-	.remove		= pcie_pme_remove,
-};
-
-/**
- * pcie_pme_service_init - Register the PCIe PME service driver.
- */
-static int __init pcie_pme_service_init(void)
-{
-	return pcie_port_service_register(&pcie_pme_driver);
-}
-
-module_init(pcie_pme_service_init);
Index: linux-2.6/drivers/pci/pcie/pme/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Makefile for PCI-Express Root Port PME signaling driver
-#
-
-obj-$(CONFIG_PCIE_PME) += pcie_pme.o
Index: linux-2.6/drivers/pci/pcie/Makefile
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/Makefile
+++ linux-2.6/drivers/pci/pcie/Makefile
@@ -13,4 +13,4 @@ obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv
 # Build PCI Express AER if needed
 obj-$(CONFIG_PCIEAER)		+= aer/
 
-obj-$(CONFIG_PCIE_PME) += pme/
+obj-$(CONFIG_PCIE_PME) += pme.o

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 10/10] PCI / PCIe: Remove the port driver module exit routine
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (18 preceding siblings ...)
  2010-08-20 23:59   ` [PATCH 10/10] PCI / PCIe: Remove the port driver module exit routine Rafael J. Wysocki
@ 2010-08-20 23:59   ` Rafael J. Wysocki
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:59 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pm, Kenji Kaneshige, linux-pci, Jesse Barnes,
	ACPI Devel Maling List, Matthew Garrett

From: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>

The PCIe port driver's module exit routine is never used, so drop it.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/pci/pcie/portdrv_pci.c |    7 -------
 1 file changed, 7 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -346,11 +346,4 @@ static int __init pcie_portdrv_init(void
 	return retval;
 }
 
-static void __exit pcie_portdrv_exit(void)
-{
-	pci_unregister_driver(&pcie_portdriver);
-	pcie_port_bus_unregister();
-}
-
 module_init(pcie_portdrv_init);
-module_exit(pcie_portdrv_exit);


^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH 10/10] PCI / PCIe: Remove the port driver module exit routine
  2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
                     ` (17 preceding siblings ...)
  2010-08-20 23:58   ` Rafael J. Wysocki
@ 2010-08-20 23:59   ` Rafael J. Wysocki
  2010-08-20 23:59   ` Rafael J. Wysocki
  19 siblings, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-20 23:59 UTC (permalink / raw)
  To: Hidetoshi Seto
  Cc: linux-pci, Jesse Barnes, ACPI Devel Maling List, Kenji Kaneshige,
	linux-pm, Matthew Garrett

From: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>

The PCIe port driver's module exit routine is never used, so drop it.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
---
 drivers/pci/pcie/portdrv_pci.c |    7 -------
 1 file changed, 7 deletions(-)

Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c
+++ linux-2.6/drivers/pci/pcie/portdrv_pci.c
@@ -346,11 +346,4 @@ static int __init pcie_portdrv_init(void
 	return retval;
 }
 
-static void __exit pcie_portdrv_exit(void)
-{
-	pci_unregister_driver(&pcie_portdriver);
-	pcie_port_bus_unregister();
-}
-
 module_init(pcie_portdrv_init);
-module_exit(pcie_portdrv_exit);

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [linux-pm] [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7)
  2010-08-20 23:56   ` [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7) Rafael J. Wysocki
  2010-08-21 20:02     ` Rafael J. Wysocki
@ 2010-08-21 20:02     ` Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-21 20:02 UTC (permalink / raw)
  To: linux-pm
  Cc: Hidetoshi Seto, linux-pci, Jesse Barnes, ACPI Devel Maling List,
	Kenji Kaneshige, Matthew Garrett

On Saturday, August 21, 2010, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> PCIe port service drivers ask the BIOS, through _OSC, for control of
> the services they handle.  Unfortunately, each of them individually
> asks for control of the PCIe capability structure and if that is
> granted, some BIOSes expect that the other PCIe port services will be
> configured and handled by the kernel as well.  If that is not the
> case (eg. one of the PCIe port service drivers is not loaded), the
> BIOS may be confused and may cause the system as a whole to misbehave
> (eg. on one of such systems enabling the native PCIe PME service
> without loading the native PCIe hot-plug service driver causes a
> storm of ACPI notify requests to appear).
> 
> For this reason rework the PCIe port driver so that (1) it checks
> which native PCIe port services can be enabled, according to the
> BIOS, and (2) it requests control of all these services
> simultaneously.  In particular, this causes pcie_portdrv_probe() to
> fail if the BIOS refuses to grant control of the PCIe capability
> structure, which means that no native PCIe port services can be
> enabled for the PCIe root complex the given port belongs to.
> 
> Make it possible to override this behavior using 'pcie_ports=native'
> (use the PCIe native services regardless of the BIOS response to the
> control request), or 'pcie_ports=compat' (do not use the PCIe native
> services at all).
> 
> Accordingly, rework the existing PCIe port service drivers so that
> they don't request control of the services directly.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---

Well, yesterday I thought the old changelog would be fine, but I guess I was
too tired, because I really should have changed it.  Below is the new
changelong, but the patch itself is unmodified.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PCI / PCIe: Ask BIOS for control of all native services at once (v7)

After commit 852972acff8f10f3a15679be2059bb94916cba5d (ACPI: Disable
ASPM if the platform won't provide _OSC control for PCIe) control of
the PCIe Capability Structure is unconditionally requested by
acpi_pci_root_add(), which in principle may cause problems to
happen in two ways.  First, the BIOS may refuse to give control of
the PCIe Capability Structure if it is not asked for any of the
_OSC features depending on it at the same time.  Second, the BIOS may
assume that control of the _OSC features depending on the PCIe
Capability Structure will be requested in the future and may behave
incorrectly if that doesn't happen.  For this reason, control of
the PCIe Capability Structure should always be requested along with
control of any other _OSC features that may depend on it (ie. PCIe
native PME, PCIe native hot-plug, PCIe AER).

Rework the PCIe port driver so that (1) it checks which native PCIe
port services can be enabled, according to the BIOS, and (2) it
requests control of all these services simultaneously.  In
particular, this causes pcie_portdrv_probe() to fail if the BIOS
refuses to grant control of the PCIe Capability Structure, which
means that no native PCIe port services can be enabled for the PCIe
Root Complex the given port belongs to.  If that happens, ASPM is
disabled to avoid problems with mishandling it by the part of the
PCIe hierarchy for which control of the PCIe Capability Structure
has not been received.

Make it possible to override this behavior using 'pcie_ports=native'
(use the PCIe native services regardless of the BIOS response to the
control request), or 'pcie_ports=compat' (do not use the PCIe native
services at all).

Accordingly, rework the existing PCIe port service drivers so that
they don't request control of the services directly.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7)
  2010-08-20 23:56   ` [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7) Rafael J. Wysocki
@ 2010-08-21 20:02     ` Rafael J. Wysocki
  2010-08-21 20:02     ` [linux-pm] " Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-21 20:02 UTC (permalink / raw)
  To: linux-pm
  Cc: Hidetoshi Seto, linux-pci, Jesse Barnes, ACPI Devel Maling List,
	Kenji Kaneshige, Matthew Garrett

On Saturday, August 21, 2010, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> PCIe port service drivers ask the BIOS, through _OSC, for control of
> the services they handle.  Unfortunately, each of them individually
> asks for control of the PCIe capability structure and if that is
> granted, some BIOSes expect that the other PCIe port services will be
> configured and handled by the kernel as well.  If that is not the
> case (eg. one of the PCIe port service drivers is not loaded), the
> BIOS may be confused and may cause the system as a whole to misbehave
> (eg. on one of such systems enabling the native PCIe PME service
> without loading the native PCIe hot-plug service driver causes a
> storm of ACPI notify requests to appear).
> 
> For this reason rework the PCIe port driver so that (1) it checks
> which native PCIe port services can be enabled, according to the
> BIOS, and (2) it requests control of all these services
> simultaneously.  In particular, this causes pcie_portdrv_probe() to
> fail if the BIOS refuses to grant control of the PCIe capability
> structure, which means that no native PCIe port services can be
> enabled for the PCIe root complex the given port belongs to.
> 
> Make it possible to override this behavior using 'pcie_ports=native'
> (use the PCIe native services regardless of the BIOS response to the
> control request), or 'pcie_ports=compat' (do not use the PCIe native
> services at all).
> 
> Accordingly, rework the existing PCIe port service drivers so that
> they don't request control of the services directly.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---

Well, yesterday I thought the old changelog would be fine, but I guess I was
too tired, because I really should have changed it.  Below is the new
changelong, but the patch itself is unmodified.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PCI / PCIe: Ask BIOS for control of all native services at once (v7)

After commit 852972acff8f10f3a15679be2059bb94916cba5d (ACPI: Disable
ASPM if the platform won't provide _OSC control for PCIe) control of
the PCIe Capability Structure is unconditionally requested by
acpi_pci_root_add(), which in principle may cause problems to
happen in two ways.  First, the BIOS may refuse to give control of
the PCIe Capability Structure if it is not asked for any of the
_OSC features depending on it at the same time.  Second, the BIOS may
assume that control of the _OSC features depending on the PCIe
Capability Structure will be requested in the future and may behave
incorrectly if that doesn't happen.  For this reason, control of
the PCIe Capability Structure should always be requested along with
control of any other _OSC features that may depend on it (ie. PCIe
native PME, PCIe native hot-plug, PCIe AER).

Rework the PCIe port driver so that (1) it checks which native PCIe
port services can be enabled, according to the BIOS, and (2) it
requests control of all these services simultaneously.  In
particular, this causes pcie_portdrv_probe() to fail if the BIOS
refuses to grant control of the PCIe Capability Structure, which
means that no native PCIe port services can be enabled for the PCIe
Root Complex the given port belongs to.  If that happens, ASPM is
disabled to avoid problems with mishandling it by the part of the
PCIe hierarchy for which control of the PCIe Capability Structure
has not been received.

Make it possible to override this behavior using 'pcie_ports=native'
(use the PCIe native services regardless of the BIOS response to the
control request), or 'pcie_ports=compat' (do not use the PCIe native
services at all).

Accordingly, rework the existing PCIe port service drivers so that
they don't request control of the services directly.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [Update][PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them (v2)
  2010-08-20 23:55   ` Rafael J. Wysocki
  2010-08-23 21:53     ` [Update][PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them (v2) Rafael J. Wysocki
@ 2010-08-23 21:53     ` Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-23 21:53 UTC (permalink / raw)
  To: linux-pm
  Cc: Hidetoshi Seto, linux-pci, Jesse Barnes, ACPI Devel Maling List,
	Kenji Kaneshige, Matthew Garrett

On Saturday, August 21, 2010, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> It is possible that the BIOS will not grant control of all _OSC
> features requested via acpi_pci_osc_control_set(), so it is
> recommended to negotiate the final set of _OSC features with the
> query flag set before calling _OSC to request control of these
> features.
> 
> To implement it, rework acpi_pci_osc_control_set() so that the caller
> can specify the mask of _OSC control bits to negotiate and the mask
> of _OSC control bits that are absolutely necessary to it.  Then,
> acpi_pci_osc_control_set() will run _OSC queries in a loop until
> the mask of _OSC control bits returned by the BIOS is equal to the
> mask passed to it.  Also, before running the _OSC request
> acpi_pci_osc_control_set() will check if the caller's required
> control bits are present in the final mask.
> 
> Using this mechanism we will be able to avoid situations in which the
> BIOS doesn't grant control of certain _OSC features, because they
> depend on some other _OSC features that have not been requested.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Unfortunately, there's a bug in this version of the patch that causes the
for() loop in acpi_pci_osc_control_set() to break after one iteration.

Fixed patch is appended.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ACPI / PCI: Negotiate _OSC control bits before requesting them (v2)

It is possible that the BIOS will not grant control of all _OSC
features requested via acpi_pci_osc_control_set(), so it is
recommended to negotiate the final set of _OSC features with the
query flag set before calling _OSC to request control of these
features.

To implement it, rework acpi_pci_osc_control_set() so that the caller
can specify the mask of _OSC control bits to negotiate and the mask
of _OSC control bits that are absolutely necessary to it.  Then,
acpi_pci_osc_control_set() will run _OSC queries in a loop until
the mask of _OSC control bits returned by the BIOS is equal to the
mask passed to it.  Also, before running the _OSC request
acpi_pci_osc_control_set() will check if the caller's required
control bits are present in the final mask.

Using this mechanism we will be able to avoid situations in which the
BIOS doesn't grant control of certain _OSC features, because they
depend on some other _OSC features that have not been requested.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c              |   59 ++++++++++++++++++++++-------------
 drivers/pci/hotplug/acpi_pcihp.c     |    2 -
 drivers/pci/pcie/aer/aerdrv_acpi.c   |    6 +--
 drivers/pci/pcie/pme/pcie_pme_acpi.c |    8 ++--
 include/linux/acpi.h                 |    4 +-
 5 files changed, 49 insertions(+), 30 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -374,21 +374,32 @@ out:
 EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
 
 /**
- * acpi_pci_osc_control_set - commit requested control to Firmware
- * @handle: acpi_handle for the target ACPI object
- * @flags: driver's requested control bits
+ * acpi_pci_osc_control_set - Request control of PCI root _OSC features.
+ * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
+ * @mask: Mask of _OSC bits to request control of, place to store control mask.
+ * @req: Mask of _OSC bits the control of is essential to the caller.
  *
- * Attempt to take control from Firmware on requested control bits.
+ * Run _OSC query for @mask and if that is successful, compare the returned
+ * mask of control bits with @req.  If all of the @req bits are set in the
+ * returned mask, run _OSC request for it.
+ *
+ * The variable at the @mask address may be modified regardless of whether or
+ * not the function returns success.  On success it will contain the mask of
+ * _OSC bits the BIOS has granted control of, but its contents are meaningless
+ * on failure.
  **/
-acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
+acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
 {
+	struct acpi_pci_root *root;
 	acpi_status status;
-	u32 control_req, result, capbuf[3];
+	u32 ctrl, capbuf[3];
 	acpi_handle tmp;
-	struct acpi_pci_root *root;
 
-	control_req = (flags & OSC_PCI_CONTROL_MASKS);
-	if (!control_req)
+	if (!mask)
+		return AE_BAD_PARAMETER;
+
+	ctrl = *mask & OSC_PCI_CONTROL_MASKS;
+	if ((ctrl & req) != req)
 		return AE_TYPE;
 
 	root = acpi_pci_find_root(handle);
@@ -400,27 +411,33 @@ acpi_status acpi_pci_osc_control_set(acp
 		return status;
 
 	mutex_lock(&osc_lock);
+
+	*mask = ctrl | root->osc_control_set;
 	/* No need to evaluate _OSC if the control was already granted. */
-	if ((root->osc_control_set & control_req) == control_req)
+	if ((root->osc_control_set & ctrl) == ctrl)
 		goto out;
 
-	/* Need to query controls first before requesting them */
-	flags = control_req;
-	status = acpi_pci_query_osc(root, root->osc_support_set, &flags);
-	if (ACPI_FAILURE(status))
-		goto out;
+	/* Need to check the available controls bits before requesting them. */
+	while (*mask) {
+		status = acpi_pci_query_osc(root, root->osc_support_set, mask);
+		if (ACPI_FAILURE(status))
+			goto out;
+		if (ctrl == *mask)
+			break;
+		ctrl = *mask;
+	}
 
-	if (flags != control_req) {
+	if ((ctrl & req) != req) {
 		status = AE_SUPPORT;
 		goto out;
 	}
 
 	capbuf[OSC_QUERY_TYPE] = 0;
 	capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
-	capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
-	status = acpi_pci_run_osc(handle, capbuf, &result);
+	capbuf[OSC_CONTROL_TYPE] = ctrl;
+	status = acpi_pci_run_osc(handle, capbuf, mask);
 	if (ACPI_SUCCESS(status))
-		root->osc_control_set = result;
+		root->osc_control_set = *mask;
 out:
 	mutex_unlock(&osc_lock);
 	return status;
@@ -551,8 +568,8 @@ static int __devinit acpi_pci_root_add(s
 	if (flags != base_flags)
 		acpi_pci_osc_support(root, flags);
 
-	status = acpi_pci_osc_control_set(root->device->handle,
-					  OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+	status = acpi_pci_osc_control_set(root->device->handle, &flags, flags);
 
 	if (ACPI_FAILURE(status)) {
 		printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
Index: linux-2.6/include/linux/acpi.h
===================================================================
--- linux-2.6.orig/include/linux/acpi.h
+++ linux-2.6/include/linux/acpi.h
@@ -304,8 +304,8 @@ acpi_status acpi_run_osc(acpi_handle han
 				OSC_PCI_EXPRESS_PME_CONTROL |		\
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
-
-extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
+extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
+					     u32 *mask, u32 req);
 extern void acpi_early_init(void);
 
 #else	/* !CONFIG_ACPI */
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -39,9 +39,9 @@ int aer_osc_setup(struct pcie_device *pc
 
 	handle = acpi_find_root_bridge_handle(pdev);
 	if (handle) {
-		status = acpi_pci_osc_control_set(handle,
-					OSC_PCI_EXPRESS_AER_CONTROL |
-					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+		u32 flags = OSC_PCI_EXPRESS_AER_CONTROL |
+				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
 	}
 
 	if (ACPI_FAILURE(status)) {
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme_acpi.c
+++ linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
@@ -28,6 +28,7 @@ int pcie_pme_acpi_setup(struct pcie_devi
 	acpi_status status = AE_NOT_FOUND;
 	struct pci_dev *port = srv->port;
 	acpi_handle handle;
+	u32 flags;
 	int error = 0;
 
 	if (acpi_pci_disabled)
@@ -39,9 +40,10 @@ int pcie_pme_acpi_setup(struct pcie_devi
 	if (!handle)
 		return -EINVAL;
 
-	status = acpi_pci_osc_control_set(handle,
-			OSC_PCI_EXPRESS_PME_CONTROL |
-			OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags = OSC_PCI_EXPRESS_PME_CONTROL |
+		OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+
+	status = acpi_pci_osc_control_set(handle, &flags, flags);
 	if (ACPI_FAILURE(status)) {
 		dev_info(&port->dev,
 			"Failed to receive control of PCIe PME service: %s\n",
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -360,7 +360,7 @@ int acpi_get_hp_hw_control_from_firmware
 		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
 		dbg("Trying to get hotplug control for %s\n",
 				(char *)string.pointer);
-		status = acpi_pci_osc_control_set(handle, flags);
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
 		if (status == AE_SUPPORT)

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [Update][PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them (v2)
  2010-08-20 23:55   ` Rafael J. Wysocki
@ 2010-08-23 21:53     ` Rafael J. Wysocki
  2010-08-23 21:53     ` Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-23 21:53 UTC (permalink / raw)
  To: linux-pm
  Cc: Hidetoshi Seto, linux-pci, Jesse Barnes, ACPI Devel Maling List,
	Kenji Kaneshige, Matthew Garrett

On Saturday, August 21, 2010, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> It is possible that the BIOS will not grant control of all _OSC
> features requested via acpi_pci_osc_control_set(), so it is
> recommended to negotiate the final set of _OSC features with the
> query flag set before calling _OSC to request control of these
> features.
> 
> To implement it, rework acpi_pci_osc_control_set() so that the caller
> can specify the mask of _OSC control bits to negotiate and the mask
> of _OSC control bits that are absolutely necessary to it.  Then,
> acpi_pci_osc_control_set() will run _OSC queries in a loop until
> the mask of _OSC control bits returned by the BIOS is equal to the
> mask passed to it.  Also, before running the _OSC request
> acpi_pci_osc_control_set() will check if the caller's required
> control bits are present in the final mask.
> 
> Using this mechanism we will be able to avoid situations in which the
> BIOS doesn't grant control of certain _OSC features, because they
> depend on some other _OSC features that have not been requested.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Unfortunately, there's a bug in this version of the patch that causes the
for() loop in acpi_pci_osc_control_set() to break after one iteration.

Fixed patch is appended.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ACPI / PCI: Negotiate _OSC control bits before requesting them (v2)

It is possible that the BIOS will not grant control of all _OSC
features requested via acpi_pci_osc_control_set(), so it is
recommended to negotiate the final set of _OSC features with the
query flag set before calling _OSC to request control of these
features.

To implement it, rework acpi_pci_osc_control_set() so that the caller
can specify the mask of _OSC control bits to negotiate and the mask
of _OSC control bits that are absolutely necessary to it.  Then,
acpi_pci_osc_control_set() will run _OSC queries in a loop until
the mask of _OSC control bits returned by the BIOS is equal to the
mask passed to it.  Also, before running the _OSC request
acpi_pci_osc_control_set() will check if the caller's required
control bits are present in the final mask.

Using this mechanism we will be able to avoid situations in which the
BIOS doesn't grant control of certain _OSC features, because they
depend on some other _OSC features that have not been requested.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c              |   59 ++++++++++++++++++++++-------------
 drivers/pci/hotplug/acpi_pcihp.c     |    2 -
 drivers/pci/pcie/aer/aerdrv_acpi.c   |    6 +--
 drivers/pci/pcie/pme/pcie_pme_acpi.c |    8 ++--
 include/linux/acpi.h                 |    4 +-
 5 files changed, 49 insertions(+), 30 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -374,21 +374,32 @@ out:
 EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
 
 /**
- * acpi_pci_osc_control_set - commit requested control to Firmware
- * @handle: acpi_handle for the target ACPI object
- * @flags: driver's requested control bits
+ * acpi_pci_osc_control_set - Request control of PCI root _OSC features.
+ * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex).
+ * @mask: Mask of _OSC bits to request control of, place to store control mask.
+ * @req: Mask of _OSC bits the control of is essential to the caller.
  *
- * Attempt to take control from Firmware on requested control bits.
+ * Run _OSC query for @mask and if that is successful, compare the returned
+ * mask of control bits with @req.  If all of the @req bits are set in the
+ * returned mask, run _OSC request for it.
+ *
+ * The variable at the @mask address may be modified regardless of whether or
+ * not the function returns success.  On success it will contain the mask of
+ * _OSC bits the BIOS has granted control of, but its contents are meaningless
+ * on failure.
  **/
-acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
+acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
 {
+	struct acpi_pci_root *root;
 	acpi_status status;
-	u32 control_req, result, capbuf[3];
+	u32 ctrl, capbuf[3];
 	acpi_handle tmp;
-	struct acpi_pci_root *root;
 
-	control_req = (flags & OSC_PCI_CONTROL_MASKS);
-	if (!control_req)
+	if (!mask)
+		return AE_BAD_PARAMETER;
+
+	ctrl = *mask & OSC_PCI_CONTROL_MASKS;
+	if ((ctrl & req) != req)
 		return AE_TYPE;
 
 	root = acpi_pci_find_root(handle);
@@ -400,27 +411,33 @@ acpi_status acpi_pci_osc_control_set(acp
 		return status;
 
 	mutex_lock(&osc_lock);
+
+	*mask = ctrl | root->osc_control_set;
 	/* No need to evaluate _OSC if the control was already granted. */
-	if ((root->osc_control_set & control_req) == control_req)
+	if ((root->osc_control_set & ctrl) == ctrl)
 		goto out;
 
-	/* Need to query controls first before requesting them */
-	flags = control_req;
-	status = acpi_pci_query_osc(root, root->osc_support_set, &flags);
-	if (ACPI_FAILURE(status))
-		goto out;
+	/* Need to check the available controls bits before requesting them. */
+	while (*mask) {
+		status = acpi_pci_query_osc(root, root->osc_support_set, mask);
+		if (ACPI_FAILURE(status))
+			goto out;
+		if (ctrl == *mask)
+			break;
+		ctrl = *mask;
+	}
 
-	if (flags != control_req) {
+	if ((ctrl & req) != req) {
 		status = AE_SUPPORT;
 		goto out;
 	}
 
 	capbuf[OSC_QUERY_TYPE] = 0;
 	capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
-	capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
-	status = acpi_pci_run_osc(handle, capbuf, &result);
+	capbuf[OSC_CONTROL_TYPE] = ctrl;
+	status = acpi_pci_run_osc(handle, capbuf, mask);
 	if (ACPI_SUCCESS(status))
-		root->osc_control_set = result;
+		root->osc_control_set = *mask;
 out:
 	mutex_unlock(&osc_lock);
 	return status;
@@ -551,8 +568,8 @@ static int __devinit acpi_pci_root_add(s
 	if (flags != base_flags)
 		acpi_pci_osc_support(root, flags);
 
-	status = acpi_pci_osc_control_set(root->device->handle,
-					  OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+	status = acpi_pci_osc_control_set(root->device->handle, &flags, flags);
 
 	if (ACPI_FAILURE(status)) {
 		printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
Index: linux-2.6/include/linux/acpi.h
===================================================================
--- linux-2.6.orig/include/linux/acpi.h
+++ linux-2.6/include/linux/acpi.h
@@ -304,8 +304,8 @@ acpi_status acpi_run_osc(acpi_handle han
 				OSC_PCI_EXPRESS_PME_CONTROL |		\
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
-
-extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
+extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
+					     u32 *mask, u32 req);
 extern void acpi_early_init(void);
 
 #else	/* !CONFIG_ACPI */
Index: linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ linux-2.6/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -39,9 +39,9 @@ int aer_osc_setup(struct pcie_device *pc
 
 	handle = acpi_find_root_bridge_handle(pdev);
 	if (handle) {
-		status = acpi_pci_osc_control_set(handle,
-					OSC_PCI_EXPRESS_AER_CONTROL |
-					OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+		u32 flags = OSC_PCI_EXPRESS_AER_CONTROL |
+				OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
 	}
 
 	if (ACPI_FAILURE(status)) {
Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
===================================================================
--- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme_acpi.c
+++ linux-2.6/drivers/pci/pcie/pme/pcie_pme_acpi.c
@@ -28,6 +28,7 @@ int pcie_pme_acpi_setup(struct pcie_devi
 	acpi_status status = AE_NOT_FOUND;
 	struct pci_dev *port = srv->port;
 	acpi_handle handle;
+	u32 flags;
 	int error = 0;
 
 	if (acpi_pci_disabled)
@@ -39,9 +40,10 @@ int pcie_pme_acpi_setup(struct pcie_devi
 	if (!handle)
 		return -EINVAL;
 
-	status = acpi_pci_osc_control_set(handle,
-			OSC_PCI_EXPRESS_PME_CONTROL |
-			OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+	flags = OSC_PCI_EXPRESS_PME_CONTROL |
+		OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL;
+
+	status = acpi_pci_osc_control_set(handle, &flags, flags);
 	if (ACPI_FAILURE(status)) {
 		dev_info(&port->dev,
 			"Failed to receive control of PCIe PME service: %s\n",
Index: linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
===================================================================
--- linux-2.6.orig/drivers/pci/hotplug/acpi_pcihp.c
+++ linux-2.6/drivers/pci/hotplug/acpi_pcihp.c
@@ -360,7 +360,7 @@ int acpi_get_hp_hw_control_from_firmware
 		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
 		dbg("Trying to get hotplug control for %s\n",
 				(char *)string.pointer);
-		status = acpi_pci_osc_control_set(handle, flags);
+		status = acpi_pci_osc_control_set(handle, &flags, flags);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
 		if (status == AE_SUPPORT)

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [Updated changelog][PATCH 5/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4)
  2010-08-20 23:54   ` Rafael J. Wysocki
@ 2010-08-23 21:55     ` Rafael J. Wysocki
  2010-08-23 21:55     ` Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-23 21:55 UTC (permalink / raw)
  To: linux-pm
  Cc: Hidetoshi Seto, linux-pci, Jesse Barnes, ACPI Devel Maling List,
	Kenji Kaneshige, Matthew Garrett

On Saturday, August 21, 2010, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> There is the assumption in acpi_pci_osc_control_set() that it is
> always sufficient to compare the mask of _OSC control bits to be
> requested with the result of an _OSC query where all of the known
> control bits have been checked.  However, in general, that need not
> be the case.  For example, if an _OSC feature A depends on an _OSC
> feature B and control of A, B plus another _OSC feature C is
> requested simultaneously, the BIOS may return A, B, C, while it would
> only return C if A and C were requested without B.
> 
> That may result in passing a wrong mask of _OSC control bits to an
> _OSC control request, in which case the BIOS may only grant control
> of a subset of the requested features.  Moreover, acpi_pci_run_osc()
> will return error code if that happens and the caller of
> acpi_pci_osc_control_set() will not know that it's been granted
> control of some _OSC features.  Consequently, the system will
> generally not work as expected.
> 
> For this reason, make acpi_pci_osc_control_set() always check if
> control of the requested _OSC features will be granted before making
> the final control request.  As a result, the osc_control_qry and
> osc_queried members of struct acpi_pci_root are not necessary any
> more, so drop them and remove the remaining code referring to them.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

I have a new version of the changelog due to a discovery I made while testing
the patchset, but the patch is not too big, so appended is the entire thing.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4)

There is the assumption in acpi_pci_osc_control_set() that it is
always sufficient to compare the mask of _OSC control bits to be
requested with the result of an _OSC query where all of the known
control bits have been checked.  However, in general, that need not
be the case.  For example, if an _OSC feature A depends on an _OSC
feature B and control of A, B plus another _OSC feature C is
requested simultaneously, the BIOS may return A, B, C, while it would
only return C if A and C were requested without B.

That may result in passing a wrong mask of _OSC control bits to an
_OSC control request, in which case the BIOS may only grant control
of a subset of the requested features.  Moreover, acpi_pci_run_osc()
will return error code if that happens and the caller of
acpi_pci_osc_control_set() will not know that it's been granted
control of some _OSC features.  Consequently, the system will
generally not work as expected.

Apart from this acpi_pci_osc_control_set() always uses the mask
of _OSC control bits returned by the very first invocation of
acpi_pci_query_osc(), but that is done with the second argument
equal to OSC_PCI_SEGMENT_GROUPS_SUPPORT which generally happens
to affect the returned _OSC control bits.

For these reasons, make acpi_pci_osc_control_set() always check if
control of the requested _OSC features will be granted before making
the final control request.  As a result, the osc_control_qry and
osc_queried members of struct acpi_pci_root are not necessary any
more, so drop them and remove the remaining code referring to them.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   20 +++++++-------------
 include/acpi/acpi_bus.h |    3 ---
 2 files changed, 7 insertions(+), 16 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -249,12 +249,8 @@ static acpi_status acpi_pci_query_osc(st
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
 		root->osc_support_set = support;
-		if (control) {
+		if (control)
 			*control = result;
-		} else {
-			root->osc_control_qry = result;
-			root->osc_queried = 1;
-		}
 	}
 	return status;
 }
@@ -409,14 +405,12 @@ acpi_status acpi_pci_osc_control_set(acp
 		goto out;
 
 	/* Need to query controls first before requesting them */
-	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
-		if (ACPI_FAILURE(status))
-			goto out;
-	}
-	if ((root->osc_control_qry & control_req) != control_req) {
-		printk(KERN_DEBUG
-		       "Firmware did not grant requested _OSC control\n");
+	flags = control_req;
+	status = acpi_pci_query_osc(root, root->osc_support_set, &flags);
+	if (ACPI_FAILURE(status))
+		goto out;
+
+	if (flags != control_req) {
 		status = AE_SUPPORT;
 		goto out;
 	}
Index: linux-2.6/include/acpi/acpi_bus.h
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -377,9 +377,6 @@ struct acpi_pci_root {
 
 	u32 osc_support_set;	/* _OSC state of support bits */
 	u32 osc_control_set;	/* _OSC state of control bits */
-	u32 osc_control_qry;	/* the latest _OSC query result */
-
-	u32 osc_queried:1;	/* has _OSC control been queried? */
 };
 
 /* helper */

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [Updated changelog][PATCH 5/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4)
  2010-08-20 23:54   ` Rafael J. Wysocki
  2010-08-23 21:55     ` [Updated changelog][PATCH " Rafael J. Wysocki
@ 2010-08-23 21:55     ` Rafael J. Wysocki
  1 sibling, 0 replies; 61+ messages in thread
From: Rafael J. Wysocki @ 2010-08-23 21:55 UTC (permalink / raw)
  To: linux-pm
  Cc: Hidetoshi Seto, linux-pci, Jesse Barnes, ACPI Devel Maling List,
	Kenji Kaneshige, Matthew Garrett

On Saturday, August 21, 2010, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> There is the assumption in acpi_pci_osc_control_set() that it is
> always sufficient to compare the mask of _OSC control bits to be
> requested with the result of an _OSC query where all of the known
> control bits have been checked.  However, in general, that need not
> be the case.  For example, if an _OSC feature A depends on an _OSC
> feature B and control of A, B plus another _OSC feature C is
> requested simultaneously, the BIOS may return A, B, C, while it would
> only return C if A and C were requested without B.
> 
> That may result in passing a wrong mask of _OSC control bits to an
> _OSC control request, in which case the BIOS may only grant control
> of a subset of the requested features.  Moreover, acpi_pci_run_osc()
> will return error code if that happens and the caller of
> acpi_pci_osc_control_set() will not know that it's been granted
> control of some _OSC features.  Consequently, the system will
> generally not work as expected.
> 
> For this reason, make acpi_pci_osc_control_set() always check if
> control of the requested _OSC features will be granted before making
> the final control request.  As a result, the osc_control_qry and
> osc_queried members of struct acpi_pci_root are not necessary any
> more, so drop them and remove the remaining code referring to them.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

I have a new version of the changelog due to a discovery I made while testing
the patchset, but the patch is not too big, so appended is the entire thing.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4)

There is the assumption in acpi_pci_osc_control_set() that it is
always sufficient to compare the mask of _OSC control bits to be
requested with the result of an _OSC query where all of the known
control bits have been checked.  However, in general, that need not
be the case.  For example, if an _OSC feature A depends on an _OSC
feature B and control of A, B plus another _OSC feature C is
requested simultaneously, the BIOS may return A, B, C, while it would
only return C if A and C were requested without B.

That may result in passing a wrong mask of _OSC control bits to an
_OSC control request, in which case the BIOS may only grant control
of a subset of the requested features.  Moreover, acpi_pci_run_osc()
will return error code if that happens and the caller of
acpi_pci_osc_control_set() will not know that it's been granted
control of some _OSC features.  Consequently, the system will
generally not work as expected.

Apart from this acpi_pci_osc_control_set() always uses the mask
of _OSC control bits returned by the very first invocation of
acpi_pci_query_osc(), but that is done with the second argument
equal to OSC_PCI_SEGMENT_GROUPS_SUPPORT which generally happens
to affect the returned _OSC control bits.

For these reasons, make acpi_pci_osc_control_set() always check if
control of the requested _OSC features will be granted before making
the final control request.  As a result, the osc_control_qry and
osc_queried members of struct acpi_pci_root are not necessary any
more, so drop them and remove the remaining code referring to them.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/acpi/pci_root.c |   20 +++++++-------------
 include/acpi/acpi_bus.h |    3 ---
 2 files changed, 7 insertions(+), 16 deletions(-)

Index: linux-2.6/drivers/acpi/pci_root.c
===================================================================
--- linux-2.6.orig/drivers/acpi/pci_root.c
+++ linux-2.6/drivers/acpi/pci_root.c
@@ -249,12 +249,8 @@ static acpi_status acpi_pci_query_osc(st
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
 		root->osc_support_set = support;
-		if (control) {
+		if (control)
 			*control = result;
-		} else {
-			root->osc_control_qry = result;
-			root->osc_queried = 1;
-		}
 	}
 	return status;
 }
@@ -409,14 +405,12 @@ acpi_status acpi_pci_osc_control_set(acp
 		goto out;
 
 	/* Need to query controls first before requesting them */
-	if (!root->osc_queried) {
-		status = acpi_pci_query_osc(root, root->osc_support_set, NULL);
-		if (ACPI_FAILURE(status))
-			goto out;
-	}
-	if ((root->osc_control_qry & control_req) != control_req) {
-		printk(KERN_DEBUG
-		       "Firmware did not grant requested _OSC control\n");
+	flags = control_req;
+	status = acpi_pci_query_osc(root, root->osc_support_set, &flags);
+	if (ACPI_FAILURE(status))
+		goto out;
+
+	if (flags != control_req) {
 		status = AE_SUPPORT;
 		goto out;
 	}
Index: linux-2.6/include/acpi/acpi_bus.h
===================================================================
--- linux-2.6.orig/include/acpi/acpi_bus.h
+++ linux-2.6/include/acpi/acpi_bus.h
@@ -377,9 +377,6 @@ struct acpi_pci_root {
 
 	u32 osc_support_set;	/* _OSC state of support bits */
 	u32 osc_control_set;	/* _OSC state of control bits */
-	u32 osc_control_qry;	/* the latest _OSC query result */
-
-	u32 osc_queried:1;	/* has _OSC control been queried? */
 };
 
 /* helper */

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 1/10] PCI / PCIe/ AER: Introduce pci_aer_available()
  2010-08-20 23:50   ` Rafael J. Wysocki
  2010-08-24 20:49     ` Jesse Barnes
@ 2010-08-24 20:49     ` Jesse Barnes
  1 sibling, 0 replies; 61+ messages in thread
From: Jesse Barnes @ 2010-08-24 20:49 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Hidetoshi Seto, linux-pm, Kenji Kaneshige, linux-pci,
	ACPI Devel Maling List, Matthew Garrett

On Sat, 21 Aug 2010 01:50:52 +0200
"Rafael J. Wysocki" <rjw@sisk.pl> wrote:

> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> Introduce a function allowing the caller to check whether to try to
> enable PCIe AER.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Applied this series to my for-linus branch, thanks.

-- 
Jesse Barnes, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH 1/10] PCI / PCIe/ AER: Introduce pci_aer_available()
  2010-08-20 23:50   ` Rafael J. Wysocki
@ 2010-08-24 20:49     ` Jesse Barnes
  2010-08-24 20:49     ` Jesse Barnes
  1 sibling, 0 replies; 61+ messages in thread
From: Jesse Barnes @ 2010-08-24 20:49 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Hidetoshi Seto, linux-pci, Maling List, Kenji Kaneshige,
	linux-pm, ACPI, Matthew Garrett

On Sat, 21 Aug 2010 01:50:52 +0200
"Rafael J. Wysocki" <rjw@sisk.pl> wrote:

> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> Introduce a function allowing the caller to check whether to try to
> enable PCIe AER.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Applied this series to my for-linus branch, thanks.

-- 
Jesse Barnes, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 61+ messages in thread

end of thread, other threads:[~2010-08-24 20:49 UTC | newest]

Thread overview: 61+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-06  1:03 [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Rafael J. Wysocki
2010-08-06  1:05 ` [PATCH 1/10] ACPI / PCI: Introduce function for querying PCI root _OSC Rafael J. Wysocki
2010-08-06  1:05 ` Rafael J. Wysocki
2010-08-06  1:06 ` [PATCH 2/10] PCI / PCIe/ AER: Introduce pci_aer_available() Rafael J. Wysocki
2010-08-06  1:06 ` Rafael J. Wysocki
2010-08-06  1:07 ` [PATCH 3/10] PCI / PCIe: Introduce commad line switch for disabling port services Rafael J. Wysocki
2010-08-06  1:07 ` Rafael J. Wysocki
2010-08-06  1:08 ` [PATCH 4/10] PCI / PCIe: Ask BIOS for control of all native services at once (v6) Rafael J. Wysocki
2010-08-06  1:08 ` Rafael J. Wysocki
2010-08-06  1:09 ` [PATCH 5/10] PCI / PCIe: Disable PCIe port services during port initialization Rafael J. Wysocki
2010-08-06  1:09 ` Rafael J. Wysocki
2010-08-06  1:10 ` [PATCH 6/10] PCI / PCIe: Remove the port driver module exit routine Rafael J. Wysocki
2010-08-06  1:10 ` Rafael J. Wysocki
2010-08-06  1:11 ` [PATCH 7/10] PCI / Hot-plug: Query _OSC before requesting controls Rafael J. Wysocki
2010-08-06  2:20   ` Hidetoshi Seto
2010-08-06  2:20   ` Hidetoshi Seto
2010-08-06 10:34     ` Rafael J. Wysocki
2010-08-09  1:22       ` Kenji Kaneshige
2010-08-09  1:22       ` Kenji Kaneshige
2010-08-06 10:34     ` Rafael J. Wysocki
2010-08-06  1:11 ` Rafael J. Wysocki
2010-08-06  1:12 ` [PATCH 8/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v2) Rafael J. Wysocki
2010-08-06  1:12 ` Rafael J. Wysocki
2010-08-06  1:13 ` [PATCH 9/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set() Rafael J. Wysocki
2010-08-06  1:13 ` Rafael J. Wysocki
2010-08-06  1:15 ` [PATCH 10/10] ACPI / PCI: Merge acpi_pci_osc_control_{query|set}() Rafael J. Wysocki
2010-08-06  1:15 ` Rafael J. Wysocki
2010-08-06  3:25 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v3) Hidetoshi Seto
2010-08-06 10:42   ` Rafael J. Wysocki
2010-08-06 10:42   ` Rafael J. Wysocki
2010-08-06  3:25 ` Hidetoshi Seto
2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki
2010-08-20 23:50   ` [PATCH 1/10] PCI / PCIe/ AER: Introduce pci_aer_available() Rafael J. Wysocki
2010-08-20 23:50   ` Rafael J. Wysocki
2010-08-24 20:49     ` Jesse Barnes
2010-08-24 20:49     ` Jesse Barnes
2010-08-20 23:51   ` [PATCH 2/10] PCI / PCIe: Introduce commad line switch for disabling port services Rafael J. Wysocki
2010-08-20 23:51   ` Rafael J. Wysocki
2010-08-20 23:52   ` [PATCH 3/10] ACPI / PCI: Reorder checks in acpi_pci_osc_control_set() Rafael J. Wysocki
2010-08-20 23:52   ` Rafael J. Wysocki
2010-08-20 23:53   ` [PATCH 4/10] ACPI / PCI: Make acpi_pci_query_osc() return control bits Rafael J. Wysocki
2010-08-20 23:53   ` Rafael J. Wysocki
2010-08-20 23:54   ` [PATCH 5/10] ACPI / PCI: Do not preserve _OSC control bits returned by a query (v4) Rafael J. Wysocki
2010-08-20 23:54   ` Rafael J. Wysocki
2010-08-23 21:55     ` [Updated changelog][PATCH " Rafael J. Wysocki
2010-08-23 21:55     ` Rafael J. Wysocki
2010-08-20 23:55   ` [PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them Rafael J. Wysocki
2010-08-20 23:55   ` Rafael J. Wysocki
2010-08-23 21:53     ` [Update][PATCH 6/10] ACPI / PCI: Negotiate _OSC control bits before requesting them (v2) Rafael J. Wysocki
2010-08-23 21:53     ` Rafael J. Wysocki
2010-08-20 23:56   ` [PATCH 7/10] PCI / PCIe: Ask BIOS for control of all native services at once (v7) Rafael J. Wysocki
2010-08-21 20:02     ` Rafael J. Wysocki
2010-08-21 20:02     ` [linux-pm] " Rafael J. Wysocki
2010-08-20 23:56   ` Rafael J. Wysocki
2010-08-20 23:57   ` [PATCH 8/10] PCI / PCIe: Disable PCIe port services during port initialization Rafael J. Wysocki
2010-08-20 23:57   ` Rafael J. Wysocki
2010-08-20 23:58   ` [PATCH 9/10] PCI / PCIe: Move PCIe PME code to the pcie directory Rafael J. Wysocki
2010-08-20 23:58   ` Rafael J. Wysocki
2010-08-20 23:59   ` [PATCH 10/10] PCI / PCIe: Remove the port driver module exit routine Rafael J. Wysocki
2010-08-20 23:59   ` Rafael J. Wysocki
2010-08-20 23:49 ` [PATCH 0/10] ACPI / PCI / PCIe: Rework _OSC handling (v4) Rafael J. Wysocki

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.