aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie/portdrv_core.c
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2018-03-09 11:21:25 -0600
committerBjorn Helgaas <helgaas@kernel.org>2018-03-30 17:26:54 -0500
commit02bfeb484230dfd073148a17253aeb1717ce769c (patch)
tree1f64c93fbef2833821d68d1fd9e714564c1460d8 /drivers/pci/pcie/portdrv_core.c
parentPCI/portdrv: Remove unused PCIE_PORT_SERVICE_VC (diff)
downloadlinux-dev-02bfeb484230dfd073148a17253aeb1717ce769c.tar.xz
linux-dev-02bfeb484230dfd073148a17253aeb1717ce769c.zip
PCI/portdrv: Simplify PCIe feature permission checking
Some PCIe features (AER, DPC, hotplug, PME) can be managed by either the platform firmware or the OS, so the host bridge driver may have to request permission from the platform before using them. On ACPI systems, this is done by negotiate_os_control() in acpi_pci_root_add(). The PCIe port driver later uses pcie_port_platform_notify() and pcie_port_acpi_setup() to figure out whether it can use these features. But all we need is a single bit for each service, so these interfaces are needlessly complicated. Simplify this by adding bits in the struct pci_host_bridge to show when the OS has permission to use each feature: + unsigned int native_aer:1; /* OS may use PCIe AER */ + unsigned int native_hotplug:1; /* OS may use PCIe hotplug */ + unsigned int native_pme:1; /* OS may use PCIe PME */ These are set when we create a host bridge, and the host bridge driver can clear the bits corresponding to any feature the platform doesn't want us to use. Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/pci/pcie/portdrv_core.c')
-rw-r--r--drivers/pci/pcie/portdrv_core.c42
1 files changed, 26 insertions, 16 deletions
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index bf851da97947..5c25761cd05e 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -206,19 +206,20 @@ legacy_irq:
*/
static int get_port_device_capability(struct pci_dev *dev)
{
+ struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
+ bool native;
int services = 0;
- int cap_mask = 0;
- cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP;
- if (pci_aer_available())
- cap_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC;
-
- if (pcie_ports_auto)
- pcie_port_platform_notify(dev, &cap_mask);
+ /*
+ * 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.
+ */
+ native = !pcie_ports_auto;
- /* Hot-Plug Capable */
- if ((cap_mask & PCIE_PORT_SERVICE_HP) && dev->is_hotplug_bridge) {
+ if (dev->is_hotplug_bridge && (native || host->native_hotplug)) {
services |= PCIE_PORT_SERVICE_HP;
+
/*
* Disable hot-plug interrupts in case they have been enabled
* by the BIOS and the hot-plug service driver is not loaded.
@@ -226,20 +227,27 @@ static int get_port_device_capability(struct pci_dev *dev)
pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
}
- /* AER capable */
- if ((cap_mask & PCIE_PORT_SERVICE_AER)
- && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
+
+ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR) &&
+ pci_aer_available() && (native || host->native_aer)) {
services |= PCIE_PORT_SERVICE_AER;
+
/*
* Disable AER on this port in case it's been enabled by the
* BIOS (the AER service driver will enable it when necessary).
*/
pci_disable_pcie_error_reporting(dev);
}
- /* Root ports are capable of generating PME too */
- if ((cap_mask & PCIE_PORT_SERVICE_PME)
- && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+
+ /*
+ * Root ports are capable of generating PME too. Root Complex
+ * Event Collectors can also generate PMEs, but we don't handle
+ * those yet.
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT &&
+ (native || host->native_pme)) {
services |= PCIE_PORT_SERVICE_PME;
+
/*
* Disable PME interrupt on this port in case it's been enabled
* by the BIOS (the PME service driver will enable it when
@@ -247,7 +255,9 @@ static int get_port_device_capability(struct pci_dev *dev)
*/
pcie_pme_interrupt_enable(dev, false);
}
- if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC))
+
+ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
+ pci_aer_available())
services |= PCIE_PORT_SERVICE_DPC;
return services;