|
@@ -14,23 +14,11 @@
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/string.h>
|
|
|
#include <linux/slab.h>
|
|
|
-#include <linux/pcieport_if.h>
|
|
|
#include <linux/aer.h>
|
|
|
|
|
|
#include "../pci.h"
|
|
|
#include "portdrv.h"
|
|
|
|
|
|
-bool pciehp_msi_disabled;
|
|
|
-
|
|
|
-static int __init pciehp_setup(char *str)
|
|
|
-{
|
|
|
- if (!strncmp(str, "nomsi", 5))
|
|
|
- pciehp_msi_disabled = true;
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-__setup("pcie_hp=", pciehp_setup);
|
|
|
-
|
|
|
/**
|
|
|
* release_pcie_device - free PCI Express port service device structure
|
|
|
* @dev: Port service device to release
|
|
@@ -51,7 +39,7 @@ static void release_pcie_device(struct device *dev)
|
|
|
static int pcie_message_numbers(struct pci_dev *dev, int mask,
|
|
|
u32 *pme, u32 *aer, u32 *dpc)
|
|
|
{
|
|
|
- u32 nvec = 0, pos, reg32;
|
|
|
+ u32 nvec = 0, pos;
|
|
|
u16 reg16;
|
|
|
|
|
|
/*
|
|
@@ -67,8 +55,11 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
|
|
|
nvec = *pme + 1;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_PCIEAER
|
|
|
if (mask & PCIE_PORT_SERVICE_AER) {
|
|
|
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
|
|
+ u32 reg32;
|
|
|
+
|
|
|
+ pos = dev->aer_cap;
|
|
|
if (pos) {
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
|
|
|
®32);
|
|
@@ -76,6 +67,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
|
|
|
nvec = max(nvec, *aer + 1);
|
|
|
}
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
if (mask & PCIE_PORT_SERVICE_DPC) {
|
|
|
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
|
|
@@ -168,16 +160,13 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
|
|
|
irqs[i] = -1;
|
|
|
|
|
|
/*
|
|
|
- * If we support PME or hotplug, but we can't use MSI/MSI-X for
|
|
|
- * them, we have to fall back to INTx or other interrupts, e.g., a
|
|
|
- * system shared interrupt.
|
|
|
+ * If we support PME but can't use MSI/MSI-X for it, we have to
|
|
|
+ * fall back to INTx or other interrupts, e.g., a system shared
|
|
|
+ * interrupt.
|
|
|
*/
|
|
|
if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi())
|
|
|
goto legacy_irq;
|
|
|
|
|
|
- if ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())
|
|
|
- goto legacy_irq;
|
|
|
-
|
|
|
/* Try to use MSI-X or MSI if supported */
|
|
|
if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0)
|
|
|
return 0;
|
|
@@ -188,10 +177,8 @@ legacy_irq:
|
|
|
if (ret < 0)
|
|
|
return -ENODEV;
|
|
|
|
|
|
- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
|
|
|
- if (i != PCIE_PORT_SERVICE_VC_SHIFT)
|
|
|
- irqs[i] = pci_irq_vector(dev, 0);
|
|
|
- }
|
|
|
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
|
|
|
+ irqs[i] = pci_irq_vector(dev, 0);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -208,23 +195,13 @@ legacy_irq:
|
|
|
*/
|
|
|
static int get_port_device_capability(struct pci_dev *dev)
|
|
|
{
|
|
|
+ struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
|
|
|
int services = 0;
|
|
|
- int cap_mask = 0;
|
|
|
-
|
|
|
- if (pcie_ports_disabled)
|
|
|
- return 0;
|
|
|
-
|
|
|
- cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
|
|
|
- | PCIE_PORT_SERVICE_VC;
|
|
|
- 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);
|
|
|
-
|
|
|
- /* Hot-Plug Capable */
|
|
|
- if ((cap_mask & PCIE_PORT_SERVICE_HP) && dev->is_hotplug_bridge) {
|
|
|
+ if (dev->is_hotplug_bridge &&
|
|
|
+ (pcie_ports_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.
|
|
@@ -232,23 +209,29 @@ 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)) {
|
|
|
+
|
|
|
+#ifdef CONFIG_PCIEAER
|
|
|
+ if (dev->aer_cap && pci_aer_available() &&
|
|
|
+ (pcie_ports_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);
|
|
|
}
|
|
|
- /* VC support */
|
|
|
- if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
|
|
|
- services |= PCIE_PORT_SERVICE_VC;
|
|
|
- /* Root ports are capable of generating PME too */
|
|
|
- if ((cap_mask & PCIE_PORT_SERVICE_PME)
|
|
|
- && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
|
|
|
+#endif
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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 &&
|
|
|
+ (pcie_ports_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
|
|
@@ -256,7 +239,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_AER)
|
|
|
services |= PCIE_PORT_SERVICE_DPC;
|
|
|
|
|
|
return services;
|
|
@@ -334,7 +319,7 @@ int pcie_port_device_register(struct pci_dev *dev)
|
|
|
*/
|
|
|
status = pcie_init_service_irqs(dev, irqs, capabilities);
|
|
|
if (status) {
|
|
|
- capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP;
|
|
|
+ capabilities &= PCIE_PORT_SERVICE_HP;
|
|
|
if (!capabilities)
|
|
|
goto error_disable;
|
|
|
}
|