ACPI / hotplug / PCI: Relax the checking of _STA return values
authorMika Westerberg <mika.westerberg@linux.intel.com>
Tue, 11 Feb 2014 10:42:37 +0000 (12:42 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 12 Feb 2014 11:45:57 +0000 (12:45 +0100)
The ACPI specification (ACPI 5.0A, Section 6.3.7) says:

 _STA may return bit 0 clear (not present) with bit 3 set (device is
 functional). This case is used to indicate a valid device for which
 no device driver should be loaded (for example, a bridge device.)
 Children of this device may be present and valid. OSPM should
 continue enumeration below a device whose _STA returns this bit
 combination.

Evidently, some BIOSes follow that and return 0x0A from _STA, which
causes problems to happen when they trigger bus check or device check
notifications for those devices too.  Namely, ACPIPHP thinks that they
are gone and may drop them, for example, if such a notification is
triggered during a resume from system suspend.

To fix that, modify ACPICA to regard devies as present and
functioning if _STA returns both the ACPI_STA_DEVICE_ENABLED
and ACPI_STA_DEVICE_FUNCTIONING bits set for them.

Reported-and-tested-by: Peter Wu <lekensteyn@gmail.com>
Cc: 3.12+ <stable@vger.kernel.org> # 3.12+
[rjw: Subject and changelog, minor code modifications]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/pci/hotplug/acpiphp_glue.c

index e2a783fdb98fdc6be6cb24f52a9a828ba48f2ba6..7c7a388c85ab3679732f7971552790057abec4f5 100644 (file)
@@ -730,6 +730,17 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
        return (unsigned int)sta;
 }
 
+static inline bool device_status_valid(unsigned int sta)
+{
+       /*
+        * ACPI spec says that _STA may return bit 0 clear with bit 3 set
+        * if the device is valid but does not require a device driver to be
+        * loaded (Section 6.3.7 of ACPI 5.0A).
+        */
+       unsigned int mask = ACPI_STA_DEVICE_ENABLED | ACPI_STA_DEVICE_FUNCTIONING;
+       return (sta & mask) == mask;
+}
+
 /**
  * trim_stale_devices - remove PCI devices that are not responding.
  * @dev: PCI device to start walking the hierarchy from.
@@ -745,7 +756,7 @@ static void trim_stale_devices(struct pci_dev *dev)
                unsigned long long sta;
 
                status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
-               alive = (ACPI_SUCCESS(status) && sta == ACPI_STA_ALL)
+               alive = (ACPI_SUCCESS(status) && device_status_valid(sta))
                        || acpiphp_no_hotplug(handle);
        }
        if (!alive) {
@@ -792,7 +803,7 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
                mutex_lock(&slot->crit_sect);
                if (slot_no_hotplug(slot)) {
                        ; /* do nothing */
-               } else if (get_slot_status(slot) == ACPI_STA_ALL) {
+               } else if (device_status_valid(get_slot_status(slot))) {
                        /* remove stale devices if any */
                        list_for_each_entry_safe_reverse(dev, tmp,
                                                         &bus->devices, bus_list)