| From a6d309cf192cec0086d13cd004cb920546638861 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 6 Jun 2019 14:52:24 +0530 |
| Subject: PCI: Generalize multi-function power dependency device links |
| |
| From: Abhishek Sahu <abhsahu@nvidia.com> |
| |
| [ Upstream commit a17beb1a0882a544523dcb5d0da4801272dfd43a ] |
| |
| 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> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/pci/quirks.c | 54 ++++++++++++++++++++++++++++---------------- |
| 1 file changed, 34 insertions(+), 20 deletions(-) |
| |
| diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c |
| index 6af7fc0be21d..3e1a0a207734 100644 |
| --- a/drivers/pci/quirks.c |
| +++ b/drivers/pci/quirks.c |
| @@ -4955,35 +4955,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); |
| -- |
| 2.25.1 |
| |