summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml77
-rw-r--r--Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml22
-rw-r--r--arch/x86/kernel/resource.c12
-rw-r--r--arch/x86/pci/acpi.c39
-rw-r--r--arch/x86/platform/efi/efi.c46
-rw-r--r--drivers/char/agp/amd-k7-agp.c24
-rw-r--r--drivers/char/agp/amd64-agp.c6
-rw-r--r--drivers/char/agp/ati-agp.c22
-rw-r--r--drivers/char/agp/efficeon-agp.c16
-rw-r--r--drivers/char/agp/intel-agp.c11
-rw-r--r--drivers/char/agp/nvidia-agp.c24
-rw-r--r--drivers/char/agp/sis-agp.c7
-rw-r--r--drivers/char/agp/via-agp.c6
-rw-r--r--drivers/pci/access.c8
-rw-r--r--drivers/pci/bus.c4
-rw-r--r--drivers/pci/controller/pcie-brcmstb.c85
-rw-r--r--drivers/pci/doe.c20
-rw-r--r--drivers/pci/hotplug/Kconfig3
-rw-r--r--drivers/pci/hotplug/TODO3
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c8
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c4
-rw-r--r--drivers/pci/hotplug/shpchp.h1
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c18
-rw-r--r--drivers/pci/irq.c2
-rw-r--r--drivers/pci/pci-acpi.c2
-rw-r--r--drivers/pci/pci-driver.c8
-rw-r--r--drivers/pci/pci-sysfs.c13
-rw-r--r--drivers/pci/pci.c105
-rw-r--r--drivers/pci/pci.h1
-rw-r--r--drivers/pci/pcie/Kconfig8
-rw-r--r--drivers/pci/pcie/Makefile2
-rw-r--r--drivers/pci/pcie/portdrv.c (renamed from drivers/pci/pcie/portdrv_core.c)269
-rw-r--r--drivers/pci/pcie/portdrv.h19
-rw-r--r--drivers/pci/pcie/portdrv_pci.c252
-rw-r--r--drivers/pci/probe.c10
-rw-r--r--drivers/pci/remove.c6
-rw-r--r--include/linux/pci.h1
38 files changed, 639 insertions, 532 deletions
diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
index c00be39af64e..7e8c7a2a5f9b 100644
--- a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
+++ b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
@@ -43,14 +43,12 @@ description: |+
each set has its own address for MSI message, and supports 32 MSI vectors
to generate interrupt.
-allOf:
- - $ref: /schemas/pci/pci-bus.yaml#
-
properties:
compatible:
oneOf:
- items:
- enum:
+ - mediatek,mt7986-pcie
- mediatek,mt8188-pcie
- mediatek,mt8195-pcie
- const: mediatek,mt8192-pcie
@@ -70,29 +68,29 @@ properties:
minItems: 1
maxItems: 8
+ iommu-map:
+ maxItems: 1
+
+ iommu-map-mask:
+ const: 0
+
resets:
minItems: 1
maxItems: 2
reset-names:
minItems: 1
+ maxItems: 2
items:
- - const: phy
- - const: mac
+ enum: [ phy, mac ]
clocks:
+ minItems: 4
maxItems: 6
clock-names:
- items:
- - const: pl_250m
- - const: tl_26m
- - const: tl_96m
- - const: tl_32k
- - const: peri_26m
- - enum:
- - top_133m # for MT8192
- - peri_mem # for MT8188/MT8195
+ minItems: 4
+ maxItems: 6
assigned-clocks:
maxItems: 1
@@ -107,6 +105,9 @@ properties:
items:
- const: pcie-phy
+ power-domains:
+ maxItems: 1
+
'#interrupt-cells':
const: 1
@@ -138,6 +139,54 @@ required:
- '#interrupt-cells'
- interrupt-controller
+allOf:
+ - $ref: /schemas/pci/pci-bus.yaml#
+ - if:
+ properties:
+ compatible:
+ const: mediatek,mt8192-pcie
+ then:
+ properties:
+ clock-names:
+ items:
+ - const: pl_250m
+ - const: tl_26m
+ - const: tl_96m
+ - const: tl_32k
+ - const: peri_26m
+ - const: top_133m
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - mediatek,mt8188-pcie
+ - mediatek,mt8195-pcie
+ then:
+ properties:
+ clock-names:
+ items:
+ - const: pl_250m
+ - const: tl_26m
+ - const: tl_96m
+ - const: tl_32k
+ - const: peri_26m
+ - const: peri_mem
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - mediatek,mt7986-pcie
+ then:
+ properties:
+ clock-names:
+ items:
+ - const: pl_250m
+ - const: tl_26m
+ - const: peri_26m
+ - const: top_133m
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml
index aed437dac363..10e6eabdff53 100644
--- a/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml
+++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-ep.yaml
@@ -58,6 +58,13 @@ properties:
dma-coherent:
description: Indicates that the PCIe IP block can ensure the coherency
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ items:
+ - const: link_state
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml
index 2115d5a3f0e1..b0513b197d08 100644
--- a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml
@@ -73,9 +73,31 @@ properties:
- const: 0xb00f
- items:
- const: 0xb010
+ - items:
+ - const: 0xb013
msi-map: true
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ items:
+ - const: link_state
+
+ interrupt-controller:
+ type: object
+ additionalProperties: false
+
+ properties:
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 1
+
+ interrupts:
+ maxItems: 1
+
required:
- compatible
- reg
diff --git a/arch/x86/kernel/resource.c b/arch/x86/kernel/resource.c
index bba1abd05bfe..79bc8a97a083 100644
--- a/arch/x86/kernel/resource.c
+++ b/arch/x86/kernel/resource.c
@@ -42,8 +42,16 @@ static void remove_e820_regions(struct resource *avail)
resource_clip(avail, e820_start, e820_end);
if (orig.start != avail->start || orig.end != avail->end) {
- pr_info("clipped %pR to %pR for e820 entry [mem %#010Lx-%#010Lx]\n",
- &orig, avail, e820_start, e820_end);
+ pr_info("resource: avoiding allocation from e820 entry [mem %#010Lx-%#010Lx]\n",
+ e820_start, e820_end);
+ if (avail->end > avail->start)
+ /*
+ * Use %pa instead of %pR because "avail"
+ * is typically IORESOURCE_UNSET, so %pR
+ * shows the size instead of addresses.
+ */
+ pr_info("resource: remaining [mem %pa-%pa] available\n",
+ &avail->start, &avail->end);
orig = *avail;
}
}
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 2f82480fd430..ea2eb2ec90e2 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -1,4 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) "PCI: " fmt
+
#include <linux/pci.h>
#include <linux/acpi.h>
#include <linux/init.h>
@@ -37,15 +40,15 @@ static int __init set_nouse_crs(const struct dmi_system_id *id)
static int __init set_ignore_seg(const struct dmi_system_id *id)
{
- printk(KERN_INFO "PCI: %s detected: ignoring ACPI _SEG\n", id->ident);
+ pr_info("%s detected: ignoring ACPI _SEG\n", id->ident);
pci_ignore_seg = true;
return 0;
}
static int __init set_no_e820(const struct dmi_system_id *id)
{
- printk(KERN_INFO "PCI: %s detected: not clipping E820 regions from _CRS\n",
- id->ident);
+ pr_info("%s detected: not clipping E820 regions from _CRS\n",
+ id->ident);
pci_use_e820 = false;
return 0;
}
@@ -231,10 +234,9 @@ void __init pci_acpi_crs_quirks(void)
else if (pci_probe & PCI_USE__CRS)
pci_use_crs = true;
- printk(KERN_INFO "PCI: %s host bridge windows from ACPI; "
- "if necessary, use \"pci=%s\" and report a bug\n",
- pci_use_crs ? "Using" : "Ignoring",
- pci_use_crs ? "nocrs" : "use_crs");
+ pr_info("%s host bridge windows from ACPI; if necessary, use \"pci=%s\" and report a bug\n",
+ pci_use_crs ? "Using" : "Ignoring",
+ pci_use_crs ? "nocrs" : "use_crs");
/* "pci=use_e820"/"pci=no_e820" on the kernel cmdline takes precedence */
if (pci_probe & PCI_NO_E820)
@@ -242,19 +244,17 @@ void __init pci_acpi_crs_quirks(void)
else if (pci_probe & PCI_USE_E820)
pci_use_e820 = true;
- printk(KERN_INFO "PCI: %s E820 reservations for host bridge windows\n",
- pci_use_e820 ? "Using" : "Ignoring");
+ pr_info("%s E820 reservations for host bridge windows\n",
+ pci_use_e820 ? "Using" : "Ignoring");
if (pci_probe & (PCI_NO_E820 | PCI_USE_E820))
- printk(KERN_INFO "PCI: Please notify linux-pci@vger.kernel.org so future kernels can this automatically\n");
+ pr_info("Please notify linux-pci@vger.kernel.org so future kernels can do this automatically\n");
}
#ifdef CONFIG_PCI_MMCONFIG
static int check_segment(u16 seg, struct device *dev, char *estr)
{
if (seg) {
- dev_err(dev,
- "%s can't access PCI configuration "
- "space under this host bridge.\n",
+ dev_err(dev, "%s can't access configuration space under this host bridge\n",
estr);
return -EIO;
}
@@ -264,9 +264,7 @@ static int check_segment(u16 seg, struct device *dev, char *estr)
* just can't access extended configuration space of
* devices under this host bridge.
*/
- dev_warn(dev,
- "%s can't access extended PCI configuration "
- "space under this bridge.\n",
+ dev_warn(dev, "%s can't access extended configuration space under this bridge\n",
estr);
return 0;
@@ -421,9 +419,8 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
root->segment = domain = 0;
if (domain && !pci_domains_supported) {
- printk(KERN_WARNING "pci_bus %04x:%02x: "
- "ignored (multiple domains not supported)\n",
- domain, busnum);
+ pr_warn("pci_bus %04x:%02x: ignored (multiple domains not supported)\n",
+ domain, busnum);
return NULL;
}
@@ -491,7 +488,7 @@ int __init pci_acpi_init(void)
if (acpi_noirq)
return -ENODEV;
- printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n");
+ pr_info("Using ACPI for IRQ routing\n");
acpi_irq_penalty_init();
pcibios_enable_irq = acpi_pci_irq_enable;
pcibios_disable_irq = acpi_pci_irq_disable;
@@ -503,7 +500,7 @@ int __init pci_acpi_init(void)
* also do it here in case there are still broken drivers that
* don't use pci_enable_device().
*/
- printk(KERN_INFO "PCI: Routing PCI interrupts for all devices because \"pci=routeirq\" specified\n");
+ pr_info("Routing PCI interrupts for all devices because \"pci=routeirq\" specified\n");
for_each_pci_dev(dev)
acpi_pci_irq_enable(dev);
}
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index ebc98a68c400..75bf0e56bb53 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -303,6 +303,50 @@ static void __init efi_clean_memmap(void)
}
}
+/*
+ * Firmware can use EfiMemoryMappedIO to request that MMIO regions be
+ * mapped by the OS so they can be accessed by EFI runtime services, but
+ * should have no other significance to the OS (UEFI r2.10, sec 7.2).
+ * However, most bootloaders and EFI stubs convert EfiMemoryMappedIO
+ * regions to E820_TYPE_RESERVED entries, which prevent Linux from
+ * allocating space from them (see remove_e820_regions()).
+ *
+ * Some platforms use EfiMemoryMappedIO entries for PCI MMCONFIG space and
+ * PCI host bridge windows, which means Linux can't allocate BAR space for
+ * hot-added devices.
+ *
+ * Remove large EfiMemoryMappedIO regions from the E820 map to avoid this
+ * problem.
+ *
+ * Retain small EfiMemoryMappedIO regions because on some platforms, these
+ * describe non-window space that's included in host bridge _CRS. If we
+ * assign that space to PCI devices, they don't work.
+ */
+static void __init efi_remove_e820_mmio(void)
+{
+ efi_memory_desc_t *md;
+ u64 size, start, end;
+ int i = 0;
+
+ for_each_efi_memory_desc(md) {
+ if (md->type == EFI_MEMORY_MAPPED_IO) {
+ size = md->num_pages << EFI_PAGE_SHIFT;
+ start = md->phys_addr;
+ end = start + size - 1;
+ if (size >= 256*1024) {
+ pr_info("Remove mem%02u: MMIO range=[0x%08llx-0x%08llx] (%lluMB) from e820 map\n",
+ i, start, end, size >> 20);
+ e820__range_remove(start, size,
+ E820_TYPE_RESERVED, 1);
+ } else {
+ pr_info("Not removing mem%02u: MMIO range=[0x%08llx-0x%08llx] (%lluKB) from e820 map\n",
+ i, start, end, size >> 10);
+ }
+ }
+ i++;
+ }
+}
+
void __init efi_print_memmap(void)
{
efi_memory_desc_t *md;
@@ -474,6 +518,8 @@ void __init efi_init(void)
set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
efi_clean_memmap();
+ efi_remove_e820_mmio();
+
if (efi_enabled(EFI_DBG))
efi_print_memmap();
}
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c
index 2b2095542816..55397ba765d2 100644
--- a/drivers/char/agp/amd-k7-agp.c
+++ b/drivers/char/agp/amd-k7-agp.c
@@ -488,26 +488,11 @@ static void agp_amdk7_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#ifdef CONFIG_PM
-
-static int agp_amdk7_suspend(struct pci_dev *pdev, pm_message_t state)
+static int agp_amdk7_resume(struct device *dev)
{
- pci_save_state(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
- return 0;
-}
-
-static int agp_amdk7_resume(struct pci_dev *pdev)
-{
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
return amd_irongate_driver.configure();
}
-#endif /* CONFIG_PM */
-
/* must be the same order as name table above */
static const struct pci_device_id agp_amdk7_pci_table[] = {
{
@@ -539,15 +524,14 @@ static const struct pci_device_id agp_amdk7_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_amdk7_pci_table);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_amdk7_pm_ops, NULL, agp_amdk7_resume);
+
static struct pci_driver agp_amdk7_pci_driver = {
.name = "agpgart-amdk7",
.id_table = agp_amdk7_pci_table,
.probe = agp_amdk7_probe,
.remove = agp_amdk7_remove,
-#ifdef CONFIG_PM
- .suspend = agp_amdk7_suspend,
- .resume = agp_amdk7_resume,
-#endif
+ .driver.pm = &agp_amdk7_pm_ops,
};
static int __init agp_amdk7_init(void)
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index 84a4aa9312cf..ce8651436609 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -588,9 +588,7 @@ static void agp_amd64_remove(struct pci_dev *pdev)
agp_bridges_found--;
}
-#define agp_amd64_suspend NULL
-
-static int __maybe_unused agp_amd64_resume(struct device *dev)
+static int agp_amd64_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -727,7 +725,7 @@ static const struct pci_device_id agp_amd64_pci_promisc_table[] = {
{ }
};
-static SIMPLE_DEV_PM_OPS(agp_amd64_pm_ops, agp_amd64_suspend, agp_amd64_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_amd64_pm_ops, NULL, agp_amd64_resume);
static struct pci_driver agp_amd64_pci_driver = {
.name = "agpgart-amd64",
diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c
index 6f5530482d83..3c1fce48aabe 100644
--- a/drivers/char/agp/ati-agp.c
+++ b/drivers/char/agp/ati-agp.c
@@ -238,23 +238,10 @@ static int ati_configure(void)
}
-#ifdef CONFIG_PM
-static int agp_ati_suspend(struct pci_dev *dev, pm_message_t state)
+static int agp_ati_resume(struct device *dev)
{
- pci_save_state(dev);
- pci_set_power_state(dev, PCI_D3hot);
-
- return 0;
-}
-
-static int agp_ati_resume(struct pci_dev *dev)
-{
- pci_set_power_state(dev, PCI_D0);
- pci_restore_state(dev);
-
return ati_configure();
}
-#endif
/*
*Since we don't need contiguous memory we just try
@@ -559,15 +546,14 @@ static const struct pci_device_id agp_ati_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_ati_pci_table);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_ati_pm_ops, NULL, agp_ati_resume);
+
static struct pci_driver agp_ati_pci_driver = {
.name = "agpgart-ati",
.id_table = agp_ati_pci_table,
.probe = agp_ati_probe,
.remove = agp_ati_remove,
-#ifdef CONFIG_PM
- .suspend = agp_ati_suspend,
- .resume = agp_ati_resume,
-#endif
+ .driver.pm = &agp_ati_pm_ops,
};
static int __init agp_ati_init(void)
diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c
index c53f0f9ef5b0..f28d42319269 100644
--- a/drivers/char/agp/efficeon-agp.c
+++ b/drivers/char/agp/efficeon-agp.c
@@ -412,18 +412,11 @@ static void agp_efficeon_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#ifdef CONFIG_PM
-static int agp_efficeon_suspend(struct pci_dev *dev, pm_message_t state)
-{
- return 0;
-}
-
-static int agp_efficeon_resume(struct pci_dev *pdev)
+static int agp_efficeon_resume(struct device *dev)
{
printk(KERN_DEBUG PFX "agp_efficeon_resume()\n");
return efficeon_configure();
}
-#endif
static const struct pci_device_id agp_efficeon_pci_table[] = {
{
@@ -437,6 +430,8 @@ static const struct pci_device_id agp_efficeon_pci_table[] = {
{ }
};
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_efficeon_pm_ops, NULL, agp_efficeon_resume);
+
MODULE_DEVICE_TABLE(pci, agp_efficeon_pci_table);
static struct pci_driver agp_efficeon_pci_driver = {
@@ -444,10 +439,7 @@ static struct pci_driver agp_efficeon_pci_driver = {
.id_table = agp_efficeon_pci_table,
.probe = agp_efficeon_probe,
.remove = agp_efficeon_remove,
-#ifdef CONFIG_PM
- .suspend = agp_efficeon_suspend,
- .resume = agp_efficeon_resume,
-#endif
+ .driver.pm = &agp_efficeon_pm_ops,
};
static int __init agp_efficeon_init(void)
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 9e4f27a6cb5a..c518b3a9db04 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -817,16 +817,15 @@ static void agp_intel_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#ifdef CONFIG_PM
-static int agp_intel_resume(struct pci_dev *pdev)
+static int agp_intel_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
bridge->driver->configure();
return 0;
}
-#endif
static const struct pci_device_id agp_intel_pci_table[] = {
#define ID(x) \
@@ -895,14 +894,14 @@ static const struct pci_device_id agp_intel_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_intel_pci_table);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_intel_pm_ops, NULL, agp_intel_resume);
+
static struct pci_driver agp_intel_pci_driver = {
.name = "agpgart-intel",
.id_table = agp_intel_pci_table,
.probe = agp_intel_probe,
.remove = agp_intel_remove,
-#ifdef CONFIG_PM
- .resume = agp_intel_resume,
-#endif
+ .driver.pm = &agp_intel_pm_ops,
};
static int __init agp_intel_init(void)
diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c
index 826dbd06f6bb..dbcbc06cc202 100644
--- a/drivers/char/agp/nvidia-agp.c
+++ b/drivers/char/agp/nvidia-agp.c
@@ -404,28 +404,13 @@ static void agp_nvidia_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#ifdef CONFIG_PM
-static int agp_nvidia_suspend(struct pci_dev *pdev, pm_message_t state)
+static int agp_nvidia_resume(struct device *dev)
{
- pci_save_state(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
-
- return 0;
-}
-
-static int agp_nvidia_resume(struct pci_dev *pdev)
-{
- /* set power state 0 and restore PCI space */
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
/* reconfigure AGP hardware again */
nvidia_configure();
return 0;
}
-#endif
-
static const struct pci_device_id agp_nvidia_pci_table[] = {
{
@@ -449,15 +434,14 @@ static const struct pci_device_id agp_nvidia_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_nvidia_pci_table);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_nvidia_pm_ops, NULL, agp_nvidia_resume);
+
static struct pci_driver agp_nvidia_pci_driver = {
.name = "agpgart-nvidia",
.id_table = agp_nvidia_pci_table,
.probe = agp_nvidia_probe,
.remove = agp_nvidia_remove,
-#ifdef CONFIG_PM
- .suspend = agp_nvidia_suspend,
- .resume = agp_nvidia_resume,
-#endif
+ .driver.pm = &agp_nvidia_pm_ops,
};
static int __init agp_nvidia_init(void)
diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c
index f8a02f4bef1b..484bb101c53b 100644
--- a/drivers/char/agp/sis-agp.c
+++ b/drivers/char/agp/sis-agp.c
@@ -217,10 +217,7 @@ static void agp_sis_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#define agp_sis_suspend NULL
-
-static int __maybe_unused agp_sis_resume(
- __attribute__((unused)) struct device *dev)
+static int agp_sis_resume(__attribute__((unused)) struct device *dev)
{
return sis_driver.configure();
}
@@ -407,7 +404,7 @@ static const struct pci_device_id agp_sis_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_sis_pci_table);
-static SIMPLE_DEV_PM_OPS(agp_sis_pm_ops, agp_sis_suspend, agp_sis_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_sis_pm_ops, NULL, agp_sis_resume);
static struct pci_driver agp_sis_pci_driver = {
.name = "agpgart-sis",
diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c
index b2f484f527fb..bc5140af2dcb 100644
--- a/drivers/char/agp/via-agp.c
+++ b/drivers/char/agp/via-agp.c
@@ -489,9 +489,7 @@ static void agp_via_remove(struct pci_dev *pdev)
agp_put_bridge(bridge);
}
-#define agp_via_suspend NULL
-
-static int __maybe_unused agp_via_resume(struct device *dev)
+static int agp_via_resume(struct device *dev)
{
struct agp_bridge_data *bridge = dev_get_drvdata(dev);
@@ -551,7 +549,7 @@ static const struct pci_device_id agp_via_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_via_pci_table);
-static SIMPLE_DEV_PM_OPS(agp_via_pm_ops, agp_via_suspend, agp_via_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(agp_via_pm_ops, NULL, agp_via_resume);
static struct pci_driver agp_via_pci_driver = {
.name = "agpgart-via",
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 708c7529647f..3c230ca3de58 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -350,6 +350,11 @@ bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
type == PCI_EXP_TYPE_PCIE_BRIDGE;
}
+bool pcie_cap_has_lnkctl2(const struct pci_dev *dev)
+{
+ return pcie_cap_has_lnkctl(dev) && pcie_cap_version(dev) > 1;
+}
+
static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
{
return pcie_downstream_port(dev) &&
@@ -390,10 +395,11 @@ static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
return pcie_cap_has_rtctl(dev);
case PCI_EXP_DEVCAP2:
case PCI_EXP_DEVCTL2:
+ return pcie_cap_version(dev) > 1;
case PCI_EXP_LNKCAP2:
case PCI_EXP_LNKCTL2:
case PCI_EXP_LNKSTA2:
- return pcie_cap_version(dev) > 1;
+ return pcie_cap_has_lnkctl2(dev);
default:
return false;
}
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 3cef835b375f..83ae838ceb5f 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -197,6 +197,10 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
max = avail.end;
+ /* Don't bother if available space isn't large enough */
+ if (size > max - min_used + 1)
+ continue;
+
/* Ok, try it out.. */
ret = allocate_resource(r, res, size, min_used, max,
align, alignf, alignf_data);
diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c
index 521acd632f1a..edf283e2b5dd 100644
--- a/drivers/pci/controller/pcie-brcmstb.c
+++ b/drivers/pci/controller/pcie-brcmstb.c
@@ -9,6 +9,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
@@ -52,6 +53,8 @@
#define PCIE_RC_DL_MDIO_RD_DATA 0x1108
#define PCIE_MISC_MISC_CTRL 0x4008
+#define PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK 0x80
+#define PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400
#define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000
#define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000
#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000
@@ -302,42 +305,34 @@ static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd)
/* negative return value indicates error */
static int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val)
{
- int tries;
u32 data;
+ int err;
writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ),
base + PCIE_RC_DL_MDIO_ADDR);
readl(base + PCIE_RC_DL_MDIO_ADDR);
-
- data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
- for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) {
- udelay(10);
- data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
- }
-
+ err = readl_poll_timeout_atomic(base + PCIE_RC_DL_MDIO_RD_DATA, data,
+ MDIO_RD_DONE(data), 10, 100);
*val = FIELD_GET(MDIO_DATA_MASK, data);
- return MDIO_RD_DONE(data) ? 0 : -EIO;
+
+ return err;
}
/* negative return value indicates error */
static int brcm_pcie_mdio_write(void __iomem *base, u8 port,
u8 regad, u16 wrdata)
{
- int tries;
u32 data;
+ int err;
writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE),
base + PCIE_RC_DL_MDIO_ADDR);
readl(base + PCIE_RC_DL_MDIO_ADDR);
writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA);
- data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
- for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) {
- udelay(10);
- data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
- }
-
- return MDIO_WT_DONE(data) ? 0 : -EIO;
+ err = readw_poll_timeout_atomic(base + PCIE_RC_DL_MDIO_WR_DATA, data,
+ MDIO_WT_DONE(data), 10, 100);
+ return err;
}
/*
@@ -445,7 +440,8 @@ static struct irq_chip brcm_msi_irq_chip = {
static struct msi_domain_info brcm_msi_domain_info = {
/* Multi MSI is supported by the controller, but not by this driver */
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_MULTI_PCI_MSI),
.chip = &brcm_msi_irq_chip,
};
@@ -505,21 +501,23 @@ static struct irq_chip brcm_msi_bottom_irq_chip = {
.irq_ack = brcm_msi_ack_irq,
};
-static int brcm_msi_alloc(struct brcm_msi *msi)
+static int brcm_msi_alloc(struct brcm_msi *msi, unsigned int nr_irqs)
{
int hwirq;
mutex_lock(&msi->lock);
- hwirq = bitmap_find_free_region(msi->used, msi->nr, 0);
+ hwirq = bitmap_find_free_region(msi->used, msi->nr,
+ order_base_2(nr_irqs));
mutex_unlock(&msi->lock);
return hwirq;
}
-static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
+static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq,
+ unsigned int nr_irqs)
{
mutex_lock(&msi->lock);
- bitmap_release_region(msi->used, hwirq, 0);
+ bitmap_release_region(msi->used, hwirq, order_base_2(nr_irqs));
mutex_unlock(&msi->lock);
}
@@ -527,16 +525,17 @@ static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *args)
{
struct brcm_msi *msi = domain->host_data;
- int hwirq;
+ int hwirq, i;
- hwirq = brcm_msi_alloc(msi);
+ hwirq = brcm_msi_alloc(msi, nr_irqs);
if (hwirq < 0)
return hwirq;
- irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq,
- &brcm_msi_bottom_irq_chip, domain->host_data,
- handle_edge_irq, NULL, NULL);
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_info(domain, virq + i, hwirq + i,
+ &brcm_msi_bottom_irq_chip, domain->host_data,
+ handle_edge_irq, NULL, NULL);
return 0;
}
@@ -546,7 +545,7 @@ static void brcm_irq_domain_free(struct irq_domain *domain,
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
struct brcm_msi *msi = irq_data_get_irq_chip_data(d);
- brcm_msi_free(msi, d->hwirq);
+ brcm_msi_free(msi, d->hwirq, nr_irqs);
}
static const struct irq_domain_ops msi_domain_ops = {
@@ -726,7 +725,7 @@ static void __iomem *brcm7425_pcie_map_bus(struct pci_bus *bus,
return base + DATA_ADDR(pcie);
}
-static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
+static void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
{
u32 tmp, mask = RGR1_SW_INIT_1_INIT_GENERIC_MASK;
u32 shift = RGR1_SW_INIT_1_INIT_GENERIC_SHIFT;
@@ -736,7 +735,7 @@ static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie,
writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
}
-static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val)
+static void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val)
{
u32 tmp, mask = RGR1_SW_INIT_1_INIT_7278_MASK;
u32 shift = RGR1_SW_INIT_1_INIT_7278_SHIFT;
@@ -746,7 +745,7 @@ static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32
writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
}
-static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
+static void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
{
if (WARN_ONCE(!pcie->perst_reset, "missing PERST# reset controller\n"))
return;
@@ -757,7 +756,7 @@ static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
reset_control_deassert(pcie->perst_reset);
}
-static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val)
+static void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val)
{
u32 tmp;
@@ -767,7 +766,7 @@ static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val)
writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL);
}
-static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
+static void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
{
u32 tmp;
@@ -776,7 +775,7 @@ static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
}
-static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
+static int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
u64 *rc_bar2_size,
u64 *rc_bar2_offset)
{
@@ -903,11 +902,16 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
else
burst = 0x2; /* 512 bytes */
- /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
+ /*
+ * Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN,
+ * RCB_MPS_MODE, RCB_64B_MODE
+ */
tmp = readl(base + PCIE_MISC_MISC_CTRL);
u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK);
writel(tmp, base + PCIE_MISC_MISC_CTRL);
ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
@@ -1033,8 +1037,15 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie)
pcie->perst_set(pcie, 0);
/*
- * Give the RC/EP time to wake up, before trying to configure RC.
- * Intermittently check status for link-up, up to a total of 100ms.
+ * Wait for 100ms after PERST# deassertion; see PCIe CEM specification
+ * sections 2.2, PCIe r5.0, 6.6.1.
+ */
+ msleep(100);
+
+ /*
+ * Give the RC/EP even more time to wake up, before trying to
+ * configure RC. Intermittently check status for link-up, up to a
+ * total of 100ms.
*/
for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5)
msleep(5);
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index e402f05068a5..66d9ab288646 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -29,6 +29,9 @@
#define PCI_DOE_FLAG_CANCEL 0
#define PCI_DOE_FLAG_DEAD 1
+/* Max data object length is 2^18 dwords */
+#define PCI_DOE_MAX_LENGTH (1 << 18)
+
/**
* struct pci_doe_mb - State for a single DOE mailbox
*
@@ -107,6 +110,7 @@ static int pci_doe_send_req(struct pci_doe_mb *doe_mb,
{
struct pci_dev *pdev = doe_mb->pdev;
int offset = doe_mb->cap_offset;
+ size_t length;
u32 val;
int i;
@@ -123,15 +127,20 @@ static int pci_doe_send_req(struct pci_doe_mb *doe_mb,
if (FIELD_GET(PCI_DOE_STATUS_ERROR, val))
return -EIO;
+ /* Length is 2 DW of header + length of payload in DW */
+ length = 2 + task->request_pl_sz / sizeof(u32);
+ if (length > PCI_DOE_MAX_LENGTH)
+ return -EIO;
+ if (length == PCI_DOE_MAX_LENGTH)
+ length = 0;
+
/* Write DOE Header */
val = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_VID, task->prot.vid) |
FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, task->prot.type);
pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, val);
- /* Length is 2 DW of header + length of payload in DW */
pci_write_config_dword(pdev, offset + PCI_DOE_WRITE,
FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH,
- 2 + task->request_pl_sz /
- sizeof(u32)));
+ length));
for (i = 0; i < task->request_pl_sz / sizeof(u32); i++)
pci_write_config_dword(pdev, offset + PCI_DOE_WRITE,
task->request_pl[i]);
@@ -178,7 +187,10 @@ static int pci_doe_recv_resp(struct pci_doe_mb *doe_mb, struct pci_doe_task *tas
pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0);
length = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, val);
- if (length > SZ_1M || length < 2)
+ /* A value of 0x0 indicates max data object length */
+ if (!length)
+ length = PCI_DOE_MAX_LENGTH;
+ if (length < 2)
return -EIO;
/* First 2 dwords have already been read */
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 840a84bb5ee2..48113b210cf9 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -6,11 +6,14 @@
menuconfig HOTPLUG_PCI
bool "Support for PCI Hotplug"
depends on PCI && SYSFS
+ default y if USB4
help
Say Y here if you have a motherboard with a PCI Hotplug controller.
This allows you to add and remove PCI cards while the machine is
powered up and running.
+ Thunderbolt/USB4 PCIe tunneling depends on native PCIe hotplug.
+
When in doubt, say N.
if HOTPLUG_PCI
diff --git a/drivers/pci/hotplug/TODO b/drivers/pci/hotplug/TODO
index 88f217c82b4f..fdb8dd6ea24d 100644
--- a/drivers/pci/hotplug/TODO
+++ b/drivers/pci/hotplug/TODO
@@ -58,9 +58,6 @@ shpchp:
pciehp with commit 82a9e79ef132 ("PCI: pciehp: remove hpc_ops"). Clarify
if there was a specific reason not to apply the same change to shpchp.
-* The ->get_mode1_ECC_cap callback in shpchp_hpc_ops is never invoked.
- Why was it introduced? Can it be removed?
-
* The hardirq handler shpc_isr() queues events on a workqueue. It can be
simplified by converting it to threaded IRQ handling. Use pciehp as a
template.
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 6efa3d8db9a5..5b1f271c6034 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -411,6 +411,14 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
if (dev->is_hotplug_bridge)
return;
+ /*
+ * In the PCIe case, only Root Ports and Downstream Ports are capable of
+ * accommodating hotplug devices, so avoid marking Upstream Ports as
+ * "hotplug bridges".
+ */
+ if (pci_is_pcie(dev) && pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM)
+ return;
+
list_for_each_entry(func, &slot->funcs, sibling) {
if (PCI_FUNC(dev->devfn) == func->function) {
dev->is_hotplug_bridge = 1;
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 040ae076ec0e..10e9670eea0b 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -811,7 +811,9 @@ static void pcie_enable_notification(struct controller *ctrl)
else
cmd |= PCI_EXP_SLTCTL_PDCE;
if (!pciehp_poll_mode)
- cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE;
+ cmd |= PCI_EXP_SLTCTL_HPIE;
+ if (!pciehp_poll_mode && !NO_CMD_CMPL(ctrl))
+ cmd |= PCI_EXP_SLTCTL_CCIE;
mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
PCI_EXP_SLTCTL_PFDE |
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index 6e85885b554c..3a97f455336e 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -311,7 +311,6 @@ struct hpc_ops {
int (*get_latch_status)(struct slot *slot, u8 *status);
int (*get_adapter_status)(struct slot *slot, u8 *status);
int (*get_adapter_speed)(struct slot *slot, enum pci_bus_speed *speed);
- int (*get_mode1_ECC_cap)(struct slot *slot, u8 *mode);
int (*get_prog_int)(struct slot *slot, u8 *prog_int);
int (*query_power_fault)(struct slot *slot);
void (*green_led_on)(struct slot *slot);
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index bd7557ca4910..48e4daefc44a 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -489,23 +489,6 @@ static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value)
return retval;
}
-static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode)
-{
- int retval = 0;
- struct controller *ctrl = slot->ctrl;
- u16 sec_bus_status = shpc_readw(ctrl, SEC_BUS_CONFIG);
- u8 pi = shpc_readb(ctrl, PROG_INTERFACE);
-
- if (pi == 2) {
- *mode = (sec_bus_status & 0x0100) >> 8;
- } else {
- retval = -1;
- }
-
- ctrl_dbg(ctrl, "Mode 1 ECC cap = %d\n", *mode);
- return retval;
-}
-
static int hpc_query_power_fault(struct slot *slot)
{
struct controller *ctrl = slot->ctrl;
@@ -900,7 +883,6 @@ static const struct hpc_ops shpchp_hpc_ops = {
.get_adapter_status = hpc_get_adapter_status,
.get_adapter_speed = hpc_get_adapter_speed,
- .get_mode1_ECC_cap = hpc_get_mode1_ECC_cap,
.get_prog_int = hpc_get_prog_int,
.query_power_fault = hpc_query_power_fault,
diff --git a/drivers/pci/irq.c b/drivers/pci/irq.c
index 12ecd0aaa28d..0050e8f6814e 100644
--- a/drivers/pci/irq.c
+++ b/drivers/pci/irq.c
@@ -44,6 +44,8 @@ int pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler,
va_start(ap, fmt);
devname = kvasprintf(GFP_KERNEL, fmt, ap);
va_end(ap);
+ if (!devname)
+ return -ENOMEM;
ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn,
irqflags, devname, dev_id);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index a46fec776ad7..068d6745bf98 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -67,7 +67,7 @@ static acpi_status acpi_match_rc(acpi_handle handle, u32 lvl, void *context,
unsigned long long uid;
acpi_status status;
- status = acpi_evaluate_integer(handle, "_UID", NULL, &uid);
+ status = acpi_evaluate_integer(handle, METHOD_NAME__UID, NULL, &uid);
if (ACPI_FAILURE(status) || uid != *segment)
return AE_CTRL_DEPTH;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 107d77f3c846..a2ceeacc33eb 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -646,7 +646,7 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
return 0;
}
-static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
+static int pci_legacy_suspend_late(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -848,7 +848,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
return 0;
if (pci_has_legacy_pm_support(pci_dev))
- return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
+ return pci_legacy_suspend_late(dev);
if (!pm) {
pci_save_state(pci_dev);
@@ -1060,7 +1060,7 @@ static int pci_pm_freeze_noirq(struct device *dev)
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
if (pci_has_legacy_pm_support(pci_dev))
- return pci_legacy_suspend_late(dev, PMSG_FREEZE);
+ return pci_legacy_suspend_late(dev);
if (pm && pm->freeze_noirq) {
int error;
@@ -1179,7 +1179,7 @@ static int pci_pm_poweroff_noirq(struct device *dev)
return 0;
if (pci_has_legacy_pm_support(pci_dev))
- return pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
+ return pci_legacy_suspend_late(dev);
if (!pm) {
pci_fixup_device(pci_fixup_suspend_late, pci_dev);
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 0a2eeb82cebd..ba38fc47d35e 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1175,11 +1175,9 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
sysfs_bin_attr_init(res_attr);
if (write_combine) {
- pdev->res_attr_wc[num] = res_attr;
sprintf(res_attr_name, "resource%d_wc", num);
res_attr->mmap = pci_mmap_resource_wc;
} else {
- pdev->res_attr[num] = res_attr;
sprintf(res_attr_name, "resource%d", num);
if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
res_attr->read = pci_read_resource_io;
@@ -1197,10 +1195,17 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
res_attr->size = pci_resource_len(pdev, num);
res_attr->private = (void *)(unsigned long)num;
retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
- if (retval)
+ if (retval) {
kfree(res_attr);
+ return retval;
+ }
+
+ if (write_combine)
+ pdev->res_attr_wc[num] = res_attr;
+ else
+ pdev->res_attr[num] = res_attr;
- return retval;
+ return 0;
}
/**
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 2127aba3550b..fba95486caaf 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -6447,6 +6447,8 @@ bool pci_device_is_present(struct pci_dev *pdev)
{
u32 v;
+ /* Check PF if pdev is a VF, since VF Vendor/Device IDs are 0xffff */
+ pdev = pci_physfn(pdev);
if (pci_dev_is_disconnected(pdev))
return false;
return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
@@ -6743,60 +6745,70 @@ static void pci_no_domains(void)
}
#ifdef CONFIG_PCI_DOMAINS_GENERIC
-static atomic_t __domain_nr = ATOMIC_INIT(-1);
+static DEFINE_IDA(pci_domain_nr_static_ida);
+static DEFINE_IDA(pci_domain_nr_dynamic_ida);
-static int pci_get_new_domain_nr(void)
+static void of_pci_reserve_static_domain_nr(void)
{
- return atomic_inc_return(&__domain_nr);
+ struct device_node *np;
+ int domain_nr;
+
+ for_each_node_by_type(np, "pci") {
+ domain_nr = of_get_pci_domain_nr(np);
+ if (domain_nr < 0)
+ continue;
+ /*
+ * Permanently allocate domain_nr in dynamic_ida
+ * to prevent it from dynamic allocation.
+ */
+ ida_alloc_range(&pci_domain_nr_dynamic_ida,
+ domain_nr, domain_nr, GFP_KERNEL);
+ }
}
static int of_pci_bus_find_domain_nr(struct device *parent)
{
- static int use_dt_domains = -1;
- int domain = -1;
+ static bool static_domains_reserved = false;
+ int domain_nr;
- if (parent)
- domain = of_get_pci_domain_nr(parent->of_node);
+ /* On the first call scan device tree for static allocations. */
+ if (!static_domains_reserved) {
+ of_pci_reserve_static_domain_nr();
+ static_domains_reserved = true;
+ }
+
+ if (parent) {
+ /*
+ * If domain is in DT, allocate it in static IDA. This
+ * prevents duplicate static allocations in case of errors
+ * in DT.
+ */
+ domain_nr = of_get_pci_domain_nr(parent->of_node);
+ if (domain_nr >= 0)
+ return ida_alloc_range(&pci_domain_nr_static_ida,
+ domain_nr, domain_nr,
+ GFP_KERNEL);
+ }
/*
- * Check DT domain and use_dt_domains values.
- *
- * If DT domain property is valid (domain >= 0) and
- * use_dt_domains != 0, the DT assignment is valid since this means
- * we have not previously allocated a domain number by using
- * pci_get_new_domain_nr(); we should also update use_dt_domains to
- * 1, to indicate that we have just assigned a domain number from
- * DT.
- *
- * If DT domain property value is not valid (ie domain < 0), and we
- * have not previously assigned a domain number from DT
- * (use_dt_domains != 1) we should assign a domain number by
- * using the:
- *
- * pci_get_new_domain_nr()
- *
- * API and update the use_dt_domains value to keep track of method we
- * are using to assign domain numbers (use_dt_domains = 0).
- *
- * All other combinations imply we have a platform that is trying
- * to mix domain numbers obtained from DT and pci_get_new_domain_nr(),
- * which is a recipe for domain mishandling and it is prevented by
- * invalidating the domain value (domain = -1) and printing a
- * corresponding error.
+ * If domain was not specified in DT, choose a free ID from dynamic
+ * allocations. All domain numbers from DT are permanently in
+ * dynamic allocations to prevent assigning them to other DT nodes
+ * without static domain.
*/
- if (domain >= 0 && use_dt_domains) {
- use_dt_domains = 1;
- } else if (domain < 0 && use_dt_domains != 1) {
- use_dt_domains = 0;
- domain = pci_get_new_domain_nr();
- } else {
- if (parent)
- pr_err("Node %pOF has ", parent->of_node);
- pr_err("Inconsistent \"linux,pci-domain\" property in DT\n");
- domain = -1;
- }
+ return ida_alloc(&pci_domain_nr_dynamic_ida, GFP_KERNEL);
+}
- return domain;
+static void of_pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
+{
+ if (bus->domain_nr < 0)
+ return;
+
+ /* Release domain from IDA where it was allocated. */
+ if (of_get_pci_domain_nr(parent->of_node) == bus->domain_nr)
+ ida_free(&pci_domain_nr_static_ida, bus->domain_nr);
+ else
+ ida_free(&pci_domain_nr_dynamic_ida, bus->domain_nr);
}
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
@@ -6804,6 +6816,13 @@ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
return acpi_disabled ? of_pci_bus_find_domain_nr(parent) :
acpi_pci_bus_find_domain_nr(bus);
}
+
+void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
+{
+ if (!acpi_disabled)
+ return;
+ of_pci_bus_release_domain_nr(bus, parent);
+}
#endif
/**
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index b1ebb7ab8805..9ed3b5550043 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -15,6 +15,7 @@ extern const unsigned char pcie_link_speed[];
extern bool pci_early_dump;
bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
+bool pcie_cap_has_lnkctl2(const struct pci_dev *dev);
bool pcie_cap_has_rtctl(const struct pci_dev *dev);
/* Functions internal to the PCI core code */
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 788ac8df3f9d..228652a59f27 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -4,6 +4,7 @@
#
config PCIEPORTBUS
bool "PCI Express Port Bus support"
+ default y if USB4
help
This enables PCI Express Port Bus support. Users can then enable
support for Native Hot-Plug, Advanced Error Reporting, Power
@@ -15,9 +16,12 @@ config PCIEPORTBUS
config HOTPLUG_PCI_PCIE
bool "PCI Express Hotplug driver"
depends on HOTPLUG_PCI && PCIEPORTBUS
+ default y if USB4
help
- Say Y here if you have a motherboard that supports PCI Express Native
- Hotplug
+ Say Y here if you have a motherboard that supports PCIe native
+ hotplug.
+
+ Thunderbolt/USB4 PCIe tunneling depends on native PCIe hotplug.
When in doubt, say N.
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 5783a2f79e6a..8de4ed5f98f1 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for PCI Express features and port driver
-pcieportdrv-y := portdrv_core.o portdrv_pci.o rcec.o
+pcieportdrv-y := portdrv.o rcec.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv.c
index 1ac7fec47d6f..2cc2e60bcb39 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv.c
@@ -1,11 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Purpose: PCI Express Port Bus Driver's Core Functions
+ * Purpose: PCI Express Port Bus Driver
*
* Copyright (C) 2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
+#include <linux/dmi.h>
+#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
@@ -19,6 +21,15 @@
#include "../pci.h"
#include "portdrv.h"
+/*
+ * The PCIe Capability Interrupt Message Number (PCIe r3.1, sec 7.8.2) must
+ * be one of the first 32 MSI-X entries. Per PCI r3.0, sec 6.8.3.1, MSI
+ * supports a maximum of 32 vectors per function.
+ */
+#define PCIE_PORT_MAX_MSI_ENTRIES 32
+
+#define get_descriptor_id(type, service) (((type - 4) << 8) | service)
+
struct portdrv_service_data {
struct pcie_port_service_driver *drv;
struct device *dev;
@@ -209,6 +220,8 @@ static int get_port_device_capability(struct pci_dev *dev)
int services = 0;
if (dev->is_hotplug_bridge &&
+ (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) &&
(pcie_ports_native || host->native_pcie_hotplug)) {
services |= PCIE_PORT_SERVICE_HP;
@@ -221,7 +234,9 @@ static int get_port_device_capability(struct pci_dev *dev)
}
#ifdef CONFIG_PCIEAER
- if (dev->aer_cap && pci_aer_available() &&
+ if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
+ dev->aer_cap && pci_aer_available() &&
(pcie_ports_native || host->native_aer))
services |= PCIE_PORT_SERVICE_AER;
#endif
@@ -308,7 +323,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
* Allocate the port extension structure and register services associated with
* the port.
*/
-int pcie_port_device_register(struct pci_dev *dev)
+static int pcie_port_device_register(struct pci_dev *dev)
{
int status, capabilities, i, nr_service;
int irqs[PCIE_PORT_DEVICE_MAXSERVICES];
@@ -362,7 +377,7 @@ error_disable:
typedef int (*pcie_callback_t)(struct pcie_device *);
-int pcie_port_device_iter(struct device *dev, void *data)
+static int pcie_port_device_iter(struct device *dev, void *data)
{
struct pcie_port_service_driver *service_driver;
size_t offset = *(size_t *)data;
@@ -382,13 +397,13 @@ int pcie_port_device_iter(struct device *dev, void *data)
* pcie_port_device_suspend - suspend port services associated with a PCIe port
* @dev: PCI Express port to handle
*/
-int pcie_port_device_suspend(struct device *dev)
+static int pcie_port_device_suspend(struct device *dev)
{
size_t off = offsetof(struct pcie_port_service_driver, suspend);
return device_for_each_child(dev, &off, pcie_port_device_iter);
}
-int pcie_port_device_resume_noirq(struct device *dev)
+static int pcie_port_device_resume_noirq(struct device *dev)
{
size_t off = offsetof(struct pcie_port_service_driver, resume_noirq);
return device_for_each_child(dev, &off, pcie_port_device_iter);
@@ -398,7 +413,7 @@ int pcie_port_device_resume_noirq(struct device *dev)
* pcie_port_device_resume - resume port services associated with a PCIe port
* @dev: PCI Express port to handle
*/
-int pcie_port_device_resume(struct device *dev)
+static int pcie_port_device_resume(struct device *dev)
{
size_t off = offsetof(struct pcie_port_service_driver, resume);
return device_for_each_child(dev, &off, pcie_port_device_iter);
@@ -408,7 +423,7 @@ int pcie_port_device_resume(struct device *dev)
* pcie_port_device_runtime_suspend - runtime suspend port services
* @dev: PCI Express port to handle
*/
-int pcie_port_device_runtime_suspend(struct device *dev)
+static int pcie_port_device_runtime_suspend(struct device *dev)
{
size_t off = offsetof(struct pcie_port_service_driver, runtime_suspend);
return device_for_each_child(dev, &off, pcie_port_device_iter);
@@ -418,7 +433,7 @@ int pcie_port_device_runtime_suspend(struct device *dev)
* pcie_port_device_runtime_resume - runtime resume port services
* @dev: PCI Express port to handle
*/
-int pcie_port_device_runtime_resume(struct device *dev)
+static int pcie_port_device_runtime_resume(struct device *dev)
{
size_t off = offsetof(struct pcie_port_service_driver, runtime_resume);
return device_for_each_child(dev, &off, pcie_port_device_iter);
@@ -482,7 +497,7 @@ EXPORT_SYMBOL_GPL(pcie_port_find_device);
* Remove PCI Express port service devices associated with given port and
* disable MSI-X or MSI for the port.
*/
-void pcie_port_device_remove(struct pci_dev *dev)
+static void pcie_port_device_remove(struct pci_dev *dev)
{
device_for_each_child(&dev->dev, NULL, remove_iter);
pci_free_irq_vectors(dev);
@@ -573,7 +588,6 @@ int pcie_port_service_register(struct pcie_port_service_driver *new)
return driver_register(&new->driver);
}
-EXPORT_SYMBOL(pcie_port_service_register);
/**
* pcie_port_service_unregister - unregister PCI Express port service driver
@@ -583,4 +597,235 @@ void pcie_port_service_unregister(struct pcie_port_service_driver *drv)
{
driver_unregister(&drv->driver);
}
-EXPORT_SYMBOL(pcie_port_service_unregister);
+
+/* If this switch is set, PCIe port native services should not be enabled. */
+bool pcie_ports_disabled;
+
+/*
+ * If the user specified "pcie_ports=native", use the PCIe services regardless
+ * of whether the platform has given us permission. On ACPI systems, this
+ * means we ignore _OSC.
+ */
+bool pcie_ports_native;
+
+/*
+ * If the user specified "pcie_ports=dpc-native", use the Linux DPC PCIe
+ * service even if the platform hasn't given us permission.
+ */
+bool pcie_ports_dpc_native;
+
+static int __init pcie_port_setup(char *str)
+{
+ if (!strncmp(str, "compat", 6))
+ pcie_ports_disabled = true;
+ else if (!strncmp(str, "native", 6))
+ pcie_ports_native = true;
+ else if (!strncmp(str, "dpc-native", 10))
+ pcie_ports_dpc_native = true;
+
+ return 1;
+}
+__setup("pcie_ports=", pcie_port_setup);
+
+/* global data */
+
+#ifdef CONFIG_PM
+static int pcie_port_runtime_suspend(struct device *dev)
+{
+ if (!to_pci_dev(dev)->bridge_d3)
+ return -EBUSY;
+
+ return pcie_port_device_runtime_suspend(dev);
+}
+
+static int pcie_port_runtime_idle(struct device *dev)
+{
+ /*
+ * Assume the PCI core has set bridge_d3 whenever it thinks the port
+ * should be good to go to D3. Everything else, including moving
+ * the port to D3, is handled by the PCI core.
+ */
+ return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
+}
+
+static const struct dev_pm_ops pcie_portdrv_pm_ops = {
+ .suspend = pcie_port_device_suspend,
+ .resume_noirq = pcie_port_device_resume_noirq,
+ .resume = pcie_port_device_resume,
+ .freeze = pcie_port_device_suspend,
+ .thaw = pcie_port_device_resume,
+ .poweroff = pcie_port_device_suspend,
+ .restore_noirq = pcie_port_device_resume_noirq,
+ .restore = pcie_port_device_resume,
+ .runtime_suspend = pcie_port_runtime_suspend,
+ .runtime_resume = pcie_port_device_runtime_resume,
+ .runtime_idle = pcie_port_runtime_idle,
+};
+
+#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
+
+#else /* !PM */
+
+#define PCIE_PORTDRV_PM_OPS NULL
+#endif /* !PM */
+
+/*
+ * pcie_portdrv_probe - Probe PCI-Express port devices
+ * @dev: PCI-Express port device being probed
+ *
+ * If detected invokes the pcie_port_device_register() method for
+ * this port device.
+ *
+ */
+static int pcie_portdrv_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ int type = pci_pcie_type(dev);
+ int status;
+
+ if (!pci_is_pcie(dev) ||
+ ((type != PCI_EXP_TYPE_ROOT_PORT) &&
+ (type != PCI_EXP_TYPE_UPSTREAM) &&
+ (type != PCI_EXP_TYPE_DOWNSTREAM) &&
+ (type != PCI_EXP_TYPE_RC_EC)))
+ return -ENODEV;
+
+ if (type == PCI_EXP_TYPE_RC_EC)
+ pcie_link_rcec(dev);
+
+ status = pcie_port_device_register(dev);
+ if (status)
+ return status;
+
+ pci_save_state(dev);
+
+ dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE |
+ DPM_FLAG_SMART_SUSPEND);
+
+ if (pci_bridge_d3_possible(dev)) {
+ /*
+ * Keep the port resumed 100ms to make sure things like
+ * config space accesses from userspace (lspci) will not
+ * cause the port to repeatedly suspend and resume.
+ */
+ pm_runtime_set_autosuspend_delay(&dev->dev, 100);
+ pm_runtime_use_autosuspend(&dev->dev);
+ pm_runtime_mark_last_busy(&dev->dev);
+ pm_runtime_put_autosuspend(&dev->dev);
+ pm_runtime_allow(&dev->dev);
+ }
+
+ return 0;
+}
+
+static void pcie_portdrv_remove(struct pci_dev *dev)
+{
+ if (pci_bridge_d3_possible(dev)) {
+ pm_runtime_forbid(&dev->dev);
+ pm_runtime_get_noresume(&dev->dev);
+ pm_runtime_dont_use_autosuspend(&dev->dev);
+ }
+
+ pcie_port_device_remove(dev);
+}
+
+static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
+ pci_channel_state_t error)
+{
+ if (error == pci_channel_io_frozen)
+ return PCI_ERS_RESULT_NEED_RESET;
+ return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
+{
+ size_t off = offsetof(struct pcie_port_service_driver, slot_reset);
+ device_for_each_child(&dev->dev, &off, pcie_port_device_iter);
+
+ pci_restore_state(dev);
+ pci_save_state(dev);
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
+{
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/*
+ * LINUX Device Driver Model
+ */
+static const struct pci_device_id port_pci_ids[] = {
+ /* handle any PCI-Express port */
+ { PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_NORMAL, ~0) },
+ /* subtractive decode PCI-to-PCI bridge, class type is 060401h */
+ { PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_SUBTRACTIVE, ~0) },
+ /* handle any Root Complex Event Collector */
+ { PCI_DEVICE_CLASS(((PCI_CLASS_SYSTEM_RCEC << 8) | 0x00), ~0) },
+ { },
+};
+
+static const struct pci_error_handlers pcie_portdrv_err_handler = {
+ .error_detected = pcie_portdrv_error_detected,
+ .slot_reset = pcie_portdrv_slot_reset,
+ .mmio_enabled = pcie_portdrv_mmio_enabled,
+};
+
+static struct pci_driver pcie_portdriver = {
+ .name = "pcieport",
+ .id_table = &port_pci_ids[0],
+
+ .probe = pcie_portdrv_probe,
+ .remove = pcie_portdrv_remove,
+ .shutdown = pcie_portdrv_remove,
+
+ .err_handler = &pcie_portdrv_err_handler,
+
+ .driver_managed_dma = true,
+
+ .driver.pm = PCIE_PORTDRV_PM_OPS,
+};
+
+static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d)
+{
+ pr_notice("%s detected: will not use MSI for PCIe PME signaling\n",
+ d->ident);
+ pcie_pme_disable_msi();
+ return 0;
+}
+
+static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = {
+ /*
+ * Boxes that should not use MSI for PCIe PME signaling.
+ */
+ {
+ .callback = dmi_pcie_pme_disable_msi,
+ .ident = "MSI Wind U-100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "MICRO-STAR INTERNATIONAL CO., LTD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "U-100"),
+ },
+ },
+ {}
+};
+
+static void __init pcie_init_services(void)
+{
+ pcie_aer_init();
+ pcie_pme_init();
+ pcie_dpc_init();
+ pcie_hp_init();
+}
+
+static int __init pcie_portdrv_init(void)
+{
+ if (pcie_ports_disabled)
+ return -EACCES;
+
+ pcie_init_services();
+ dmi_check_system(pcie_portdrv_dmi_table);
+
+ return pci_register_driver(&pcie_portdriver);
+}
+device_initcall(pcie_portdrv_init);
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 0ef4bf5f811d..58a2b1a1cae4 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -98,26 +98,7 @@ struct pcie_port_service_driver {
int pcie_port_service_register(struct pcie_port_service_driver *new);
void pcie_port_service_unregister(struct pcie_port_service_driver *new);
-/*
- * The PCIe Capability Interrupt Message Number (PCIe r3.1, sec 7.8.2) must
- * be one of the first 32 MSI-X entries. Per PCI r3.0, sec 6.8.3.1, MSI
- * supports a maximum of 32 vectors per function.
- */
-#define PCIE_PORT_MAX_MSI_ENTRIES 32
-
-#define get_descriptor_id(type, service) (((type - 4) << 8) | service)
-
extern struct bus_type pcie_port_bus_type;
-int pcie_port_device_register(struct pci_dev *dev);
-int pcie_port_device_iter(struct device *dev, void *data);
-#ifdef CONFIG_PM
-int pcie_port_device_suspend(struct device *dev);
-int pcie_port_device_resume_noirq(struct device *dev);
-int pcie_port_device_resume(struct device *dev);
-int pcie_port_device_runtime_suspend(struct device *dev);
-int pcie_port_device_runtime_resume(struct device *dev);
-#endif
-void pcie_port_device_remove(struct pci_dev *dev);
struct pci_dev;
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
deleted file mode 100644
index 7f8788a970ae..000000000000
--- a/drivers/pci/pcie/portdrv_pci.c
+++ /dev/null
@@ -1,252 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Purpose: PCI Express Port Bus Driver
- * Author: Tom Nguyen <tom.l.nguyen@intel.com>
- *
- * Copyright (C) 2004 Intel
- * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
- */
-
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/pm.h>
-#include <linux/pm_runtime.h>
-#include <linux/init.h>
-#include <linux/aer.h>
-#include <linux/dmi.h>
-
-#include "../pci.h"
-#include "portdrv.h"
-
-/* If this switch is set, PCIe port native services should not be enabled. */
-bool pcie_ports_disabled;
-
-/*
- * If the user specified "pcie_ports=native", use the PCIe services regardless
- * of whether the platform has given us permission. On ACPI systems, this
- * means we ignore _OSC.
- */
-bool pcie_ports_native;
-
-/*
- * If the user specified "pcie_ports=dpc-native", use the Linux DPC PCIe
- * service even if the platform hasn't given us permission.
- */
-bool pcie_ports_dpc_native;
-
-static int __init pcie_port_setup(char *str)
-{
- if (!strncmp(str, "compat", 6))
- pcie_ports_disabled = true;
- else if (!strncmp(str, "native", 6))
- pcie_ports_native = true;
- else if (!strncmp(str, "dpc-native", 10))
- pcie_ports_dpc_native = true;
-
- return 1;
-}
-__setup("pcie_ports=", pcie_port_setup);
-
-/* global data */
-
-#ifdef CONFIG_PM
-static int pcie_port_runtime_suspend(struct device *dev)
-{
- if (!to_pci_dev(dev)->bridge_d3)
- return -EBUSY;
-
- return pcie_port_device_runtime_suspend(dev);
-}
-
-static int pcie_port_runtime_idle(struct device *dev)
-{
- /*
- * Assume the PCI core has set bridge_d3 whenever it thinks the port
- * should be good to go to D3. Everything else, including moving
- * the port to D3, is handled by the PCI core.
- */
- return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
-}
-
-static const struct dev_pm_ops pcie_portdrv_pm_ops = {
- .suspend = pcie_port_device_suspend,
- .resume_noirq = pcie_port_device_resume_noirq,
- .resume = pcie_port_device_resume,
- .freeze = pcie_port_device_suspend,
- .thaw = pcie_port_device_resume,
- .poweroff = pcie_port_device_suspend,
- .restore_noirq = pcie_port_device_resume_noirq,
- .restore = pcie_port_device_resume,
- .runtime_suspend = pcie_port_runtime_suspend,
- .runtime_resume = pcie_port_device_runtime_resume,
- .runtime_idle = pcie_port_runtime_idle,
-};
-
-#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
-
-#else /* !PM */
-
-#define PCIE_PORTDRV_PM_OPS NULL
-#endif /* !PM */
-
-/*
- * pcie_portdrv_probe - Probe PCI-Express port devices
- * @dev: PCI-Express port device being probed
- *
- * If detected invokes the pcie_port_device_register() method for
- * this port device.
- *
- */
-static int pcie_portdrv_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
-{
- int type = pci_pcie_type(dev);
- int status;
-
- if (!pci_is_pcie(dev) ||
- ((type != PCI_EXP_TYPE_ROOT_PORT) &&
- (type != PCI_EXP_TYPE_UPSTREAM) &&
- (type != PCI_EXP_TYPE_DOWNSTREAM) &&
- (type != PCI_EXP_TYPE_RC_EC)))
- return -ENODEV;
-
- if (type == PCI_EXP_TYPE_RC_EC)
- pcie_link_rcec(dev);
-
- status = pcie_port_device_register(dev);
- if (status)
- return status;
-
- pci_save_state(dev);
-
- dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE |
- DPM_FLAG_SMART_SUSPEND);
-
- if (pci_bridge_d3_possible(dev)) {
- /*
- * Keep the port resumed 100ms to make sure things like
- * config space accesses from userspace (lspci) will not
- * cause the port to repeatedly suspend and resume.
- */
- pm_runtime_set_autosuspend_delay(&dev->dev, 100);
- pm_runtime_use_autosuspend(&dev->dev);
- pm_runtime_mark_last_busy(&dev->dev);
- pm_runtime_put_autosuspend(&dev->dev);
- pm_runtime_allow(&dev->dev);
- }
-
- return 0;
-}
-
-static void pcie_portdrv_remove(struct pci_dev *dev)
-{
- if (pci_bridge_d3_possible(dev)) {
- pm_runtime_forbid(&dev->dev);
- pm_runtime_get_noresume(&dev->dev);
- pm_runtime_dont_use_autosuspend(&dev->dev);
- }
-
- pcie_port_device_remove(dev);
-}
-
-static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
- pci_channel_state_t error)
-{
- if (error == pci_channel_io_frozen)
- return PCI_ERS_RESULT_NEED_RESET;
- return PCI_ERS_RESULT_CAN_RECOVER;
-}
-
-static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
-{
- size_t off = offsetof(struct pcie_port_service_driver, slot_reset);
- device_for_each_child(&dev->dev, &off, pcie_port_device_iter);
-
- pci_restore_state(dev);
- pci_save_state(dev);
- return PCI_ERS_RESULT_RECOVERED;
-}
-
-static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
-{
- return PCI_ERS_RESULT_RECOVERED;
-}
-
-/*
- * LINUX Device Driver Model
- */
-static const struct pci_device_id port_pci_ids[] = {
- /* handle any PCI-Express port */
- { PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_NORMAL, ~0) },
- /* subtractive decode PCI-to-PCI bridge, class type is 060401h */
- { PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_SUBTRACTIVE, ~0) },
- /* handle any Root Complex Event Collector */
- { PCI_DEVICE_CLASS(((PCI_CLASS_SYSTEM_RCEC << 8) | 0x00), ~0) },
- { },
-};
-
-static const struct pci_error_handlers pcie_portdrv_err_handler = {
- .error_detected = pcie_portdrv_error_detected,
- .slot_reset = pcie_portdrv_slot_reset,
- .mmio_enabled = pcie_portdrv_mmio_enabled,
-};
-
-static struct pci_driver pcie_portdriver = {
- .name = "pcieport",
- .id_table = &port_pci_ids[0],
-
- .probe = pcie_portdrv_probe,
- .remove = pcie_portdrv_remove,
- .shutdown = pcie_portdrv_remove,
-
- .err_handler = &pcie_portdrv_err_handler,
-
- .driver_managed_dma = true,
-
- .driver.pm = PCIE_PORTDRV_PM_OPS,
-};
-
-static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d)
-{
- pr_notice("%s detected: will not use MSI for PCIe PME signaling\n",
- d->ident);
- pcie_pme_disable_msi();
- return 0;
-}
-
-static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = {
- /*
- * Boxes that should not use MSI for PCIe PME signaling.
- */
- {
- .callback = dmi_pcie_pme_disable_msi,
- .ident = "MSI Wind U-100",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "MICRO-STAR INTERNATIONAL CO., LTD"),
- DMI_MATCH(DMI_PRODUCT_NAME, "U-100"),
- },
- },
- {}
-};
-
-static void __init pcie_init_services(void)
-{
- pcie_aer_init();
- pcie_pme_init();
- pcie_dpc_init();
- pcie_hp_init();
-}
-
-static int __init pcie_portdrv_init(void)
-{
- if (pcie_ports_disabled)
- return -EACCES;
-
- pcie_init_services();
- dmi_check_system(pcie_portdrv_dmi_table);
-
- return pci_register_driver(&pcie_portdriver);
-}
-device_initcall(pcie_portdrv_init);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index b66fa42c4b1f..1e234189aff1 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -906,6 +906,10 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
else
bus->domain_nr = bridge->domain_nr;
+ if (bus->domain_nr < 0) {
+ err = bus->domain_nr;
+ goto free;
+ }
#endif
b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
@@ -1030,6 +1034,9 @@ unregister:
device_del(&bridge->dev);
free:
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+ pci_bus_release_domain_nr(bus, parent);
+#endif
kfree(bus);
return err;
}
@@ -1891,9 +1898,6 @@ int pci_setup_device(struct pci_dev *dev)
dev->broken_intx_masking = pci_intx_mask_broken(dev);
- /* Clear errors left from system firmware */
- pci_write_config_word(dev, PCI_STATUS, 0xffff);
-
switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
if (class == PCI_CLASS_BRIDGE_PCI)
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 4c54c75050dc..0145aef1b930 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus)
pci_remove_bus(bus);
host_bridge->bus = NULL;
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+ /* Release domain_nr if it was dynamically allocated */
+ if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
+ pci_bus_release_domain_nr(bus, host_bridge->dev.parent);
+#endif
+
/* remove the host bridge */
device_del(&host_bridge->dev);
}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2bda4a4e47e8..28af4414f789 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1726,6 +1726,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
{ return 0; }
#endif
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent);
+void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent);
#endif
/* Some architectures require additional setup to direct VGA traffic */