summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci-driver.c16
-rw-r--r--drivers/pci/pci-sysfs.c5
-rw-r--r--drivers/pci/pci.c4
-rw-r--r--drivers/pci/probe.c2
-rw-r--r--drivers/pci/proc.c2
-rw-r--r--drivers/pci/quirks.c110
-rw-r--r--drivers/pci/slot.c1
7 files changed, 109 insertions, 31 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index cae630fe6387..b6a3a51801f0 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -399,7 +399,8 @@ void __weak pcibios_free_irq(struct pci_dev *dev)
#ifdef CONFIG_PCI_IOV
static inline bool pci_device_can_probe(struct pci_dev *pdev)
{
- return (!pdev->is_virtfn || pdev->physfn->sriov->drivers_autoprobe);
+ return (!pdev->is_virtfn || pdev->physfn->sriov->drivers_autoprobe ||
+ pdev->driver_override);
}
#else
static inline bool pci_device_can_probe(struct pci_dev *pdev)
@@ -414,6 +415,9 @@ static int pci_device_probe(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *drv = to_pci_driver(dev->driver);
+ if (!pci_device_can_probe(pci_dev))
+ return -ENODEV;
+
pci_assign_irq(pci_dev);
error = pcibios_alloc_irq(pci_dev);
@@ -421,12 +425,10 @@ static int pci_device_probe(struct device *dev)
return error;
pci_dev_get(pci_dev);
- if (pci_device_can_probe(pci_dev)) {
- error = __pci_device_probe(drv, pci_dev);
- if (error) {
- pcibios_free_irq(pci_dev);
- pci_dev_put(pci_dev);
- }
+ error = __pci_device_probe(drv, pci_dev);
+ if (error) {
+ pcibios_free_irq(pci_dev);
+ pci_dev_put(pci_dev);
}
return error;
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 6d27475e39b2..965c72104150 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -182,6 +182,9 @@ static ssize_t current_link_speed_show(struct device *dev,
return -EINVAL;
switch (linkstat & PCI_EXP_LNKSTA_CLS) {
+ case PCI_EXP_LNKSTA_CLS_32_0GB:
+ speed = "32 GT/s";
+ break;
case PCI_EXP_LNKSTA_CLS_16_0GB:
speed = "16 GT/s";
break;
@@ -477,7 +480,7 @@ static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
return count;
}
-static struct device_attribute dev_remove_attr = __ATTR(remove,
+static struct device_attribute dev_remove_attr = __ATTR_IGNORE_LOCKDEP(remove,
(S_IWUSR|S_IWGRP),
NULL, remove_store);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 8abc843b1615..4729a7c7a9d9 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -5621,7 +5621,9 @@ enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev)
*/
pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2);
if (lnkcap2) { /* PCIe r3.0-compliant */
- if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB)
+ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_32_0GB)
+ return PCIE_SPEED_32_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB)
return PCIE_SPEED_16_0GT;
else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
return PCIE_SPEED_8_0GT;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 0e8e2c186f50..c5f27c8cd140 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -668,7 +668,7 @@ const unsigned char pcie_link_speed[] = {
PCIE_SPEED_5_0GT, /* 2 */
PCIE_SPEED_8_0GT, /* 3 */
PCIE_SPEED_16_0GT, /* 4 */
- PCI_SPEED_UNKNOWN, /* 5 */
+ PCIE_SPEED_32_0GT, /* 5 */
PCI_SPEED_UNKNOWN, /* 6 */
PCI_SPEED_UNKNOWN, /* 7 */
PCI_SPEED_UNKNOWN, /* 8 */
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 445b51db75b0..fe7fe678965b 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -377,7 +377,7 @@ static int show_device(struct seq_file *m, void *v)
}
seq_putc(m, '\t');
if (drv)
- seq_printf(m, "%s", drv->name);
+ seq_puts(m, drv->name);
seq_putc(m, '\n');
return 0;
}
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 0f16acc323c6..208aacf39329 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -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);
@@ -4972,6 +4986,62 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
/*
+ * Create device link for NVIDIA GPU with integrated USB xHCI Host
+ * controller to VGA.
+ */
+static void quirk_gpu_usb(struct pci_dev *usb)
+{
+ pci_create_device_link(usb, 2, 0, PCI_BASE_CLASS_DISPLAY, 16);
+}
+DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
+ PCI_CLASS_SERIAL_USB, 8, quirk_gpu_usb);
+
+/*
+ * Create device link for NVIDIA GPU with integrated Type-C UCSI controller
+ * to VGA. Currently there is no class code defined for UCSI device over PCI
+ * so using UNKNOWN class for now and it will be updated when UCSI
+ * over PCI gets a class code.
+ */
+#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80
+static void quirk_gpu_usb_typec_ucsi(struct pci_dev *ucsi)
+{
+ pci_create_device_link(ucsi, 3, 0, PCI_BASE_CLASS_DISPLAY, 16);
+}
+DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
+ PCI_CLASS_SERIAL_UNKNOWN, 8,
+ quirk_gpu_usb_typec_ucsi);
+
+/*
+ * Enable the NVIDIA GPU integrated HDA controller if the BIOS left it
+ * disabled. https://devtalk.nvidia.com/default/topic/1024022
+ */
+static void quirk_nvidia_hda(struct pci_dev *gpu)
+{
+ u8 hdr_type;
+ u32 val;
+
+ /* There was no integrated HDA controller before MCP89 */
+ if (gpu->device < PCI_DEVICE_ID_NVIDIA_GEFORCE_320M)
+ return;
+
+ /* Bit 25 at offset 0x488 enables the HDA controller */
+ pci_read_config_dword(gpu, 0x488, &val);
+ if (val & BIT(25))
+ return;
+
+ pci_info(gpu, "Enabling HDA controller\n");
+ pci_write_config_dword(gpu, 0x488, val | BIT(25));
+
+ /* The GPU becomes a multi-function device when the HDA is enabled */
+ pci_read_config_byte(gpu, PCI_HEADER_TYPE, &hdr_type);
+ gpu->multifunction = !!(hdr_type & 0x80);
+}
+DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
+ PCI_BASE_CLASS_DISPLAY, 16, quirk_nvidia_hda);
+DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
+ PCI_BASE_CLASS_DISPLAY, 16, quirk_nvidia_hda);
+
+/*
* Some IDT switches incorrectly flag an ACS Source Validation error on
* completions for config read requests even though PCIe r4.0, sec
* 6.12.1.1, says that completions are never affected by ACS Source
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index f4d92b1afe7b..ae4aa0e1f2f4 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -75,6 +75,7 @@ static const char *pci_bus_speed_strings[] = {
"5.0 GT/s PCIe", /* 0x15 */
"8.0 GT/s PCIe", /* 0x16 */
"16.0 GT/s PCIe", /* 0x17 */
+ "32.0 GT/s PCIe", /* 0x18 */
};
static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf)