linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Bjorn Helgaas <helgaas@kernel.org>
To: linux-pci@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	linux-pm@vger.kernel.org, Keith Busch <keith.busch@intel.com>,
	Sinan Kaya <okaya@codeaurora.org>, Lukas Wunner <lukas@wunner.de>,
	Frederick Lawler <fred@fredlawl.com>
Subject: [PATCH v2 08/13] PCI/portdrv: Simplify PCIe feature permission checking
Date: Fri, 09 Mar 2018 13:00:33 -0600	[thread overview]
Message-ID: <152062203293.77693.8159909590216160503.stgit@bhelgaas-glaptop.roam.corp.google.com> (raw)
In-Reply-To: <152062141493.77693.9630397416694091342.stgit@bhelgaas-glaptop.roam.corp.google.com>

From: Bjorn Helgaas <bhelgaas@google.com>

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 use_aer:1;       /* OS may use PCIe AER */
  + unsigned int use_hotplug:1;	  /* OS may use PCIe hotplug */
  + unsigned int use_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>
---
 drivers/acpi/pci_root.c         |   13 ++++++++++--
 drivers/pci/pcie/Makefile       |    1 -
 drivers/pci/pcie/portdrv.h      |   11 ----------
 drivers/pci/pcie/portdrv_core.c |   42 ++++++++++++++++++++++++---------------
 drivers/pci/probe.c             |   10 +++++++++
 include/linux/pci.h             |    3 +++
 6 files changed, 50 insertions(+), 30 deletions(-)

diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 6fc204a52493..65ebefb99815 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -871,6 +871,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
 	struct acpi_device *device = root->device;
 	int node = acpi_get_node(device->handle);
 	struct pci_bus *bus;
+	struct pci_host_bridge *host_bridge;
 
 	info->root = root;
 	info->bridge = device;
@@ -895,9 +896,17 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
 	if (!bus)
 		goto out_release_info;
 
+	host_bridge = to_pci_host_bridge(bus->bridge);
+	if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
+		host_bridge->use_hotplug = 0;
+	if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
+		host_bridge->use_aer = 0;
+	if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
+		host_bridge->use_pme = 0;
+
 	pci_scan_child_bus(bus);
-	pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge),
-				    acpi_pci_root_release_info, info);
+	pci_set_host_bridge_release(host_bridge, acpi_pci_root_release_info,
+				    info);
 	if (node != NUMA_NO_NODE)
 		dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node);
 	return bus;
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index e01c10c97b95..11fb633b866c 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -7,7 +7,6 @@
 obj-$(CONFIG_PCIEASPM)		+= aspm.o
 
 pcieportdrv-y			:= portdrv_core.o portdrv_pci.o
-pcieportdrv-$(CONFIG_ACPI)	+= portdrv_acpi.o
 
 obj-$(CONFIG_PCIEPORTBUS)	+= pcieportdrv.o
 
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 7bfd75f9197b..ed84e767085f 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -123,15 +123,4 @@ static inline bool pcie_pme_no_msi(void) { return false; }
 static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
 #endif /* !CONFIG_PCIE_PME */
 
-#ifdef CONFIG_ACPI
-void pcie_port_acpi_setup(struct pci_dev *port, int *mask);
-
-static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask)
-{
-	pcie_port_acpi_setup(port, mask);
-}
-#else /* !CONFIG_ACPI */
-static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask){}
-#endif /* !CONFIG_ACPI */
-
 #endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index bf851da97947..589960fdd8a8 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -206,19 +206,20 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
  */
 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->use_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->use_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->use_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;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index ef5377438a1e..839fb0059900 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -540,6 +540,16 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
 	INIT_LIST_HEAD(&bridge->windows);
 	bridge->dev.release = pci_release_host_bridge_dev;
 
+	/*
+	 * We assume we can manage these PCIe features.  Some systems may
+	 * reserve these for use by the platform itself, e.g., an ACPI BIOS
+	 * may implement its own AER handling and use _OSC to prevent the
+	 * OS from interfering.
+	 */
+	bridge->use_aer = 1;
+	bridge->use_hotplug = 1;
+	bridge->use_pme = 1;
+
 	return bridge;
 }
 EXPORT_SYMBOL(pci_alloc_host_bridge);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 024a1beda008..40aec7a6fdd9 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -469,6 +469,9 @@ struct pci_host_bridge {
 	struct msi_controller *msi;
 	unsigned int	ignore_reset_delay:1;	/* For entire hierarchy */
 	unsigned int	no_ext_tags:1;		/* No Extended Tags */
+	unsigned int	use_aer:1;		/* OS may use PCIe AER */
+	unsigned int	use_hotplug:1;		/* OS may use PCIe hotplug */
+	unsigned int	use_pme:1;		/* OS may use PCIe PME */
 	/* Resource alignment requirements */
 	resource_size_t (*align_resource)(struct pci_dev *dev,
 			const struct resource *res,

  parent reply	other threads:[~2018-03-09 19:00 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-03-09 18:59 [PATCH v2 00/13] PCI: Simplify PCIe port driver Bjorn Helgaas
2018-03-09 18:59 ` [PATCH v2 01/13] PCI/portdrv: Merge pcieport_if.h into portdrv.h Bjorn Helgaas
2018-03-12  7:59   ` Christoph Hellwig
2018-03-09 19:00 ` [PATCH v2 02/13] PCI/PM: Move pcie_clear_root_pme_status() to core Bjorn Helgaas
2018-03-12  8:00   ` Christoph Hellwig
2018-03-09 19:00 ` [PATCH v2 03/13] PCI/PM: Clear PCIe PME Status bit in core, not PCIe port driver Bjorn Helgaas
2018-03-09 19:00 ` [PATCH v2 04/13] PCI/PM: Clear PCIe PME Status bit for Root Complex Event Collectors Bjorn Helgaas
2018-03-09 19:00 ` [PATCH v2 05/13] PCI/portdrv: Disable port driver in compat mode Bjorn Helgaas
2018-03-09 19:00 ` [PATCH v2 06/13] PCI/portdrv: Remove pcie_port_bus_type link order dependency Bjorn Helgaas
2018-03-12  8:01   ` Christoph Hellwig
2018-03-12 14:17     ` Bjorn Helgaas
2018-03-09 19:00 ` [PATCH v2 07/13] PCI/portdrv: Remove unused PCIE_PORT_SERVICE_VC Bjorn Helgaas
2018-03-12  8:02   ` Christoph Hellwig
2018-03-09 19:00 ` Bjorn Helgaas [this message]
2018-03-12  8:04   ` [PATCH v2 08/13] PCI/portdrv: Simplify PCIe feature permission checking Christoph Hellwig
2018-03-12 14:03     ` Bjorn Helgaas
2018-03-12 14:20       ` Lukas Wunner
2018-03-19 18:37         ` Bjorn Helgaas
2019-05-07 12:00   ` David Woodhouse
2019-05-07 12:49     ` Bjorn Helgaas
2019-05-07 13:02       ` David Woodhouse
2019-05-07 14:07         ` Bjorn Helgaas
2019-05-08  6:45           ` David Woodhouse
2018-03-09 19:00 ` [PATCH v2 09/13] PCI/portdrv: Remove unnecessary include of <linux/pci-aspm.h> Bjorn Helgaas
2018-03-09 19:00 ` [PATCH v2 10/13] PCI/portdrv: Remove "pcie_hp=nomsi" kernel parameter Bjorn Helgaas
2018-03-09 19:00 ` [PATCH v2 11/13] PCI/portdrv: Remove unnecessary "pcie_ports=auto" parameter Bjorn Helgaas
2018-03-09 19:00 ` [PATCH v2 12/13] PCI/portdrv: Encapsulate pcie_ports_auto inside the port driver Bjorn Helgaas
2018-03-09 19:01 ` [PATCH v2 13/13] PCI/portdrv: Rename and reverse sense of pcie_ports_auto Bjorn Helgaas
2018-03-19 18:43 ` [PATCH v2 00/13] PCI: Simplify PCIe port driver Bjorn Helgaas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=152062203293.77693.8159909590216160503.stgit@bhelgaas-glaptop.roam.corp.google.com \
    --to=helgaas@kernel.org \
    --cc=fred@fredlawl.com \
    --cc=keith.busch@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=okaya@codeaurora.org \
    --cc=rjw@rjwysocki.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).