]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/acpi/pci_root.c
ACPI: EC: add another DMI check for ASUS hardware
[linux-2.6.git] / drivers / acpi / pci_root.c
index 680450c905b32f1feb7d042262128bb402414c0a..f911a2f8cc346f40f7575db6eab96cf6acf2339a 100644 (file)
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
-#include <linux/proc_fs.h>
 #include <linux/spinlock.h>
 #include <linux/pm.h>
 #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>
 #include <acpi/acpi_drivers.h>
+#include <acpi/apei.h>
 
 #define PREFIX "ACPI: "
 
@@ -47,6 +49,11 @@ static int acpi_pci_root_add(struct acpi_device *device);
 static int acpi_pci_root_remove(struct acpi_device *device, int type);
 static int acpi_pci_root_start(struct acpi_device *device);
 
+#define ACPI_PCIE_REQ_SUPPORT (OSC_EXT_PCI_CONFIG_SUPPORT \
+                               | OSC_ACTIVE_STATE_PWR_SUPPORT \
+                               | OSC_CLOCK_PWR_CAPABILITY_SUPPORT \
+                               | OSC_MSI_SUPPORT)
+
 static const struct acpi_device_id root_device_ids[] = {
        {"PNP0A03", 0},
        {"", 0},
@@ -224,22 +231,31 @@ static acpi_status acpi_pci_run_osc(acpi_handle handle,
        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;
        }
        return status;
 }
@@ -253,7 +269,7 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
        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,55 +379,70 @@ 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;
 
-       status = acpi_get_handle(handle, "_OSC", &tmp);
-       if (ACPI_FAILURE(status))
-               return status;
+       if (!mask)
+               return AE_BAD_PARAMETER;
 
-       control_req = (flags & OSC_PCI_CONTROL_MASKS);
-       if (!control_req)
+       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;
 
+       status = acpi_get_handle(handle, "_OSC", &tmp);
+       if (ACPI_FAILURE(status))
+               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 */
-       if (!root->osc_queried) {
-               status = acpi_pci_query_osc(root, root->osc_support_set);
+       /* 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 ((root->osc_control_qry & control_req) != control_req) {
-               printk(KERN_DEBUG
-                      "Firmware did not grant requested _OSC control\n");
+
+       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;
@@ -534,7 +565,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
        /* Indicate support for various _OSC capabilities. */
        if (pci_ext_cfg_avail(root->bus->self))
                flags |= OSC_EXT_PCI_CONFIG_SUPPORT;
-       if (pcie_aspm_enabled())
+       if (pcie_aspm_support_enabled())
                flags |= OSC_ACTIVE_STATE_PWR_SUPPORT |
                        OSC_CLOCK_PWR_CAPABILITY_SUPPORT;
        if (pci_msi_enabled())
@@ -542,6 +573,37 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
        if (flags != base_flags)
                acpi_pci_osc_support(root, flags);
 
+       if (!pcie_ports_disabled
+           && (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) {
+               flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL
+                       | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL
+                       | OSC_PCI_EXPRESS_PME_CONTROL;
+
+               if (pci_aer_available()) {
+                       if (aer_acpi_firmware_first())
+                               dev_dbg(root->bus->bridge,
+                                       "PCIe errors handled by BIOS.\n");
+                       else
+                               flags |= OSC_PCI_EXPRESS_AER_CONTROL;
+               }
+
+               dev_info(root->bus->bridge,
+                       "Requesting ACPI _OSC control (0x%02x)\n", flags);
+
+               status = acpi_pci_osc_control_set(device->handle, &flags,
+                                       OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+               if (ACPI_SUCCESS(status)) {
+                       dev_info(root->bus->bridge,
+                               "ACPI _OSC control (0x%02x) granted\n", flags);
+               } else {
+                       dev_dbg(root->bus->bridge,
+                               "ACPI _OSC request failed (code %d)\n", status);
+                       printk(KERN_INFO "Unable to assume _OSC 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);
@@ -576,6 +638,8 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type)
 
 static int __init acpi_pci_root_init(void)
 {
+       acpi_hest_init();
+
        if (acpi_pci_disabled)
                return 0;