Commit a17beb1a authored by Abhishek Sahu's avatar Abhishek Sahu Committed by Bjorn Helgaas
Browse files

PCI: Generalize multi-function power dependency device links

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: default avatarAbhishek Sahu <abhsahu@nvidia.com>
[bhelgaas: commit log, reword diagnostic]
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent a188339c
Loading
Loading
Loading
Loading
+34 −20
Original line number Diff line number Diff line
@@ -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,
	if (device_link_add(&pdev->dev, &supplier_pdev->dev,
			    DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME))
		pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu));
		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);