summaryrefslogtreecommitdiffstats
path: root/drivers/pci/pci-acpi.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2015-04-20 11:32:26 +1000
committerDave Airlie <airlied@redhat.com>2015-04-20 13:05:20 +1000
commit2c33ce009ca2389dbf0535d0672214d09738e35e (patch)
tree6186a6458c3c160385d794a23eaf07c786a9e61b /drivers/pci/pci-acpi.c
parentcec32a47010647e8b0603726ebb75b990a4057a4 (diff)
parent09d51602cf84a1264946711dd4ea0dddbac599a1 (diff)
downloadlinux-stable-2c33ce009ca2389dbf0535d0672214d09738e35e.tar.gz
linux-stable-2c33ce009ca2389dbf0535d0672214d09738e35e.tar.bz2
linux-stable-2c33ce009ca2389dbf0535d0672214d09738e35e.zip
Merge Linus master into drm-next
The merge is clean, but the arm build fails afterwards, due to API changes in the regulator tree. I've included the patch into the merge to fix the build. Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/pci/pci-acpi.c')
-rw-r--r--drivers/pci/pci-acpi.c86
1 files changed, 86 insertions, 0 deletions
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 489063987325..6f6f175f51f7 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -18,6 +18,15 @@
#include <linux/pm_qos.h>
#include "pci.h"
+/*
+ * The UUID is defined in the PCI Firmware Specification available here:
+ * https://www.pcisig.com/members/downloads/pcifw_r3_1_13Dec10.pdf
+ */
+const u8 pci_acpi_dsm_uuid[] = {
+ 0xd0, 0x37, 0xc9, 0xe5, 0x53, 0x35, 0x7a, 0x4d,
+ 0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d
+};
+
phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
{
acpi_status status = AE_NOT_EXIST;
@@ -248,6 +257,9 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
acpi_handle handle, phandle;
struct pci_bus *pbus;
+ if (acpi_pci_disabled)
+ return -ENODEV;
+
handle = NULL;
for (pbus = dev->bus; pbus; pbus = pbus->parent) {
handle = acpi_pci_get_bridge_handle(pbus);
@@ -528,11 +540,32 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = {
void acpi_pci_add_bus(struct pci_bus *bus)
{
+ union acpi_object *obj;
+ struct pci_host_bridge *bridge;
+
if (acpi_pci_disabled || !bus->bridge)
return;
acpi_pci_slot_enumerate(bus);
acpiphp_enumerate_slots(bus);
+
+ /*
+ * For a host bridge, check its _DSM for function 8 and if
+ * that is available, mark it in pci_host_bridge.
+ */
+ if (!pci_is_root_bus(bus))
+ return;
+
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), pci_acpi_dsm_uuid, 3,
+ RESET_DELAY_DSM, NULL);
+ if (!obj)
+ return;
+
+ if (obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 1) {
+ bridge = pci_find_host_bridge(bus);
+ bridge->ignore_reset_delay = 1;
+ }
+ ACPI_FREE(obj);
}
void acpi_pci_remove_bus(struct pci_bus *bus)
@@ -558,6 +591,57 @@ static struct acpi_device *acpi_pci_find_companion(struct device *dev)
check_children);
}
+/**
+ * pci_acpi_optimize_delay - optimize PCI D3 and D3cold delay from ACPI
+ * @pdev: the PCI device whose delay is to be updated
+ * @adev: the companion ACPI device of this PCI device
+ *
+ * Update the d3_delay and d3cold_delay of a PCI device from the ACPI _DSM
+ * control method of either the device itself or the PCI host bridge.
+ *
+ * Function 8, "Reset Delay," applies to the entire hierarchy below a PCI
+ * host bridge. If it returns one, the OS may assume that all devices in
+ * the hierarchy have already completed power-on reset delays.
+ *
+ * Function 9, "Device Readiness Durations," applies only to the object
+ * where it is located. It returns delay durations required after various
+ * events if the device requires less time than the spec requires. Delays
+ * from this function take precedence over the Reset Delay function.
+ *
+ * These _DSM functions are defined by the draft ECN of January 28, 2014,
+ * titled "ACPI additions for FW latency optimizations."
+ */
+static void pci_acpi_optimize_delay(struct pci_dev *pdev,
+ acpi_handle handle)
+{
+ struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);
+ int value;
+ union acpi_object *obj, *elements;
+
+ if (bridge->ignore_reset_delay)
+ pdev->d3cold_delay = 0;
+
+ obj = acpi_evaluate_dsm(handle, pci_acpi_dsm_uuid, 3,
+ FUNCTION_DELAY_DSM, NULL);
+ if (!obj)
+ return;
+
+ if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 5) {
+ elements = obj->package.elements;
+ if (elements[0].type == ACPI_TYPE_INTEGER) {
+ value = (int)elements[0].integer.value / 1000;
+ if (value < PCI_PM_D3COLD_WAIT)
+ pdev->d3cold_delay = value;
+ }
+ if (elements[3].type == ACPI_TYPE_INTEGER) {
+ value = (int)elements[3].integer.value / 1000;
+ if (value < PCI_PM_D3_WAIT)
+ pdev->d3_delay = value;
+ }
+ }
+ ACPI_FREE(obj);
+}
+
static void pci_acpi_setup(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -566,6 +650,8 @@ static void pci_acpi_setup(struct device *dev)
if (!adev)
return;
+ pci_acpi_optimize_delay(pci_dev, adev->handle);
+
pci_acpi_add_pm_notifier(adev, pci_dev);
if (!adev->wakeup.flags.valid)
return;