aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-08-07 16:55:00 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-08-07 16:55:00 -0400
commit60f75b8e97daf4a39790a20d962cb861b9220af5 (patch)
tree52149b28d341b07df26a196c79d9d817af6f455a /include
parent1e385f6f97b8ab39e16a0956a1951e19a9376bab (diff)
ACPI: Try harder to resolve _ADR collisions for bridges
In theory, under a given ACPI namespace node there should be only one child device object with _ADR whose value matches a given bus address exactly. In practice, however, there are systems in which multiple child device objects under a given parent have _ADR matching exactly the same address. In those cases we use _STA to determine which of the multiple matching devices is enabled, since some systems are known to indicate which ACPI device object to associate with the given physical (usually PCI) device this way. Unfortunately, as it turns out, there are systems in which many device objects under the same parent have _ADR matching exactly the same bus address and none of them has _STA, in which case they all should be regarded as enabled according to the spec. Still, if those device objects are supposed to represent bridges (e.g. this is the case for device objects corresponding to PCIe ports), we can try harder and skip the ones that have no child device objects in the ACPI namespace. With luck, we can avoid using device objects that we are not expected to use this way. Although this only works for bridges whose children also have ACPI namespace representation, it is sufficient to address graphics adapter detection issues on some systems, so rework the code finding a matching device ACPI handle for a given bus address to implement this idea. Introduce a new function, acpi_find_child(), taking three arguments: the ACPI handle of the device's parent, a bus address suitable for the device's bus type and a bool indicating if the device is a bridge and make it work as outlined above. Reimplement the function currently used for this purpose, acpi_get_child(), as a call to acpi_find_child() with the last argument set to 'false' and make the PCI subsystem use acpi_find_child() with the bridge information passed as the last argument to it. [Lan Tianyu notices that it is not sufficient to use pci_is_bridge() for that, because the device's subordinate pointer hasn't been set yet at this point, so use hdr_type instead.] This change fixes a regression introduced inadvertently by commit 33f767d (ACPI: Rework acpi_get_child() to be more efficient) which overlooked the fact that for acpi_walk_namespace() "post-order" means "after all children have been visited" rather than "on the way back", so for device objects without children and for namespace walks of depth 1, as in the acpi_get_child() case, the "post-order" callbacks ordering is actually the same as the ordering of "pre-order" ones. Since that commit changed the namespace walk in acpi_get_child() to terminate after finding the first matching object instead of going through all of them and returning the last one, it effectively changed the result returned by that function in some rare cases and that led to problems (the switch from a "pre-order" to a "post-order" callback was supposed to prevent that from happening, but it was ineffective). As it turns out, the systems where the change made by commit 33f767d actually matters are those where there are multiple ACPI device objects representing the same PCIe port (which effectively is a bridge). Moreover, only one of them, and the one we are expected to use, has child device objects in the ACPI namespace, so the regression can be addressed as described above. References: https://bugzilla.kernel.org/show_bug.cgi?id=60561 Reported-by: Peter Wu <lekensteyn@gmail.com> Tested-by: Vladimir Lalov <mail@vlalov.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Bjorn Helgaas <bhelgaas@google.com> Cc: 3.9+ <stable@vger.kernel.org> # 3.9+
Diffstat (limited to 'include')
-rw-r--r--include/acpi/acpi_bus.h6
1 files changed, 5 insertions, 1 deletions
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 5026aaa35133..94383a70c1a3 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -441,7 +441,11 @@ struct acpi_pci_root {
441}; 441};
442 442
443/* helper */ 443/* helper */
444acpi_handle acpi_get_child(acpi_handle, u64); 444acpi_handle acpi_find_child(acpi_handle, u64, bool);
445static inline acpi_handle acpi_get_child(acpi_handle handle, u64 addr)
446{
447 return acpi_find_child(handle, addr, false);
448}
445int acpi_is_root_bridge(acpi_handle); 449int acpi_is_root_bridge(acpi_handle);
446struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); 450struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle);
447#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev)) 451#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev))