PCI: Generalize multi-function power dependency device links
authorAbhishek Sahu <abhsahu@nvidia.com>
Thu, 6 Jun 2019 09:22:24 +0000 (14:52 +0530)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 13 Jun 2019 20:52:37 +0000 (15:52 -0500)
Although not allowed by the PCI specs, some multi-function devices have
power dependencies between the functions.  For example, function 1 may not
work unless function 0 is in the D0 power state.

The existing quirk_gpu_hda() adds a device link to express this dependency
for GPU and HDA devices, but it really is not specific to those device
types.

Generalize it and rename it to pci_create_device_link() so we can create
dependencies between any "consumer" and "producer" functions of a
multi-function device, where the consumer is only functional if the
producer is in D0.  This reorganization should not affect any
functionality.

Link: https://lore.kernel.org/lkml/20190606092225.17960-2-abhsahu@nvidia.com
Signed-off-by: Abhishek Sahu <abhsahu@nvidia.com>
[bhelgaas: commit log, reword diagnostic]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/quirks.c

index 0f16acc..d9c162d 100644 (file)
@@ -4934,35 +4934,49 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev)
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi);
 
 /*
- * GPUs with integrated HDA controller for streaming audio to attached displays
- * need a device link from the HDA controller (consumer) to the GPU (supplier)
- * so that the GPU is powered up whenever the HDA controller is accessed.
- * The GPU and HDA controller are functions 0 and 1 of the same PCI device.
- * The device link stays in place until shutdown (or removal of the PCI device
- * if it's hotplugged).  Runtime PM is allowed by default on the HDA controller
- * to prevent it from permanently keeping the GPU awake.
+ * Although not allowed by the spec, some multi-function devices have
+ * dependencies of one function (consumer) on another (supplier).  For the
+ * consumer to work in D0, the supplier must also be in D0.  Create a
+ * device link from the consumer to the supplier to enforce this
+ * dependency.  Runtime PM is allowed by default on the consumer to prevent
+ * it from permanently keeping the supplier awake.
  */
-static void quirk_gpu_hda(struct pci_dev *hda)
+static void pci_create_device_link(struct pci_dev *pdev, unsigned int consumer,
+                                  unsigned int supplier, unsigned int class,
+                                  unsigned int class_shift)
 {
-       struct pci_dev *gpu;
+       struct pci_dev *supplier_pdev;
 
-       if (PCI_FUNC(hda->devfn) != 1)
+       if (PCI_FUNC(pdev->devfn) != consumer)
                return;
 
-       gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus),
-                                         hda->bus->number,
-                                         PCI_DEVFN(PCI_SLOT(hda->devfn), 0));
-       if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) {
-               pci_dev_put(gpu);
+       supplier_pdev = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
+                               pdev->bus->number,
+                               PCI_DEVFN(PCI_SLOT(pdev->devfn), supplier));
+       if (!supplier_pdev || (supplier_pdev->class >> class_shift) != class) {
+               pci_dev_put(supplier_pdev);
                return;
        }
 
-       if (!device_link_add(&hda->dev, &gpu->dev,
-                            DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME))
-               pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu));
+       if (device_link_add(&pdev->dev, &supplier_pdev->dev,
+                           DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME))
+               pci_info(pdev, "D0 power state depends on %s\n",
+                        pci_name(supplier_pdev));
+       else
+               pci_err(pdev, "Cannot enforce power dependency on %s\n",
+                       pci_name(supplier_pdev));
+
+       pm_runtime_allow(&pdev->dev);
+       pci_dev_put(supplier_pdev);
+}
 
-       pm_runtime_allow(&hda->dev);
-       pci_dev_put(gpu);
+/*
+ * Create device link for GPUs with integrated HDA controller for streaming
+ * audio to attached displays.
+ */
+static void quirk_gpu_hda(struct pci_dev *hda)
+{
+       pci_create_device_link(hda, 1, 0, PCI_BASE_CLASS_DISPLAY, 16);
 }
 DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
                              PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);