From c37e627f9565368ed7bd1f3cf59a2d223ddba85a Mon Sep 17 00:00:00 2001 From: Frederick Lawler Date: Tue, 13 Feb 2018 21:52:18 -0600 Subject: PCI/portdrv: Move pcieport_if.h to drivers/pci/pcie/ Move pcieport_if.h from include/linux to drivers/pci/pcie/pcieport_if.h because the interfaces there are only used by the PCI core. Replace all uses of #include with relative paths to the new file location, e.g., #include "../pcieport_if.h" Signed-off-by: Frederick Lawler Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp.h | 3 +- drivers/pci/pcie/aer/aerdrv.c | 1 - drivers/pci/pcie/aer/aerdrv.h | 3 +- drivers/pci/pcie/pcie-dpc.c | 3 +- drivers/pci/pcie/pcieport_if.h | 71 +++++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/pme.c | 2 +- drivers/pci/pcie/portdrv_acpi.c | 2 +- drivers/pci/pcie/portdrv_bus.c | 2 +- drivers/pci/pcie/portdrv_core.c | 2 +- drivers/pci/pcie/portdrv_pci.c | 2 +- include/linux/pcieport_if.h | 71 ----------------------------------------- 11 files changed, 82 insertions(+), 80 deletions(-) create mode 100644 drivers/pci/pcie/pcieport_if.h delete mode 100644 include/linux/pcieport_if.h diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 636ed8f4b869..08072bcaa381 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -20,10 +20,11 @@ #include #include #include /* signal_pending() */ -#include #include #include +#include "../pcie/pcieport_if.h" + #define MY_NAME "pciehp" extern bool pciehp_poll_mode; diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index da8331f5684d..28329e16ad8f 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include "aerdrv.h" diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 5449e5ce139d..568326f385b7 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -10,10 +10,11 @@ #define _AERDRV_H_ #include -#include #include #include +#include "../pcieport_if.h" + #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ PCI_EXP_RTCTL_SEFEE) diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c index 38e40c6c576f..bac895de4c72 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/pcie-dpc.c @@ -10,7 +10,8 @@ #include #include #include -#include + +#include "pcieport_if.h" #include "../pci.h" #include "aer/aerdrv.h" diff --git a/drivers/pci/pcie/pcieport_if.h b/drivers/pci/pcie/pcieport_if.h new file mode 100644 index 000000000000..b69769dbf659 --- /dev/null +++ b/drivers/pci/pcie/pcieport_if.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * File: pcieport_if.h + * Purpose: PCI Express Port Bus Driver's IF Data Structure + * + * Copyright (C) 2004 Intel + * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) + */ + +#ifndef _PCIEPORT_IF_H_ +#define _PCIEPORT_IF_H_ + +/* Port Type */ +#define PCIE_ANY_PORT (~0) + +/* Service Type */ +#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ +#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) +#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ +#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) +#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ +#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) +#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ +#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) +#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ +#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) + +struct pcie_device { + int irq; /* Service IRQ/MSI/MSI-X Vector */ + struct pci_dev *port; /* Root/Upstream/Downstream Port */ + u32 service; /* Port service this device represents */ + void *priv_data; /* Service Private Data */ + struct device device; /* Generic Device Interface */ +}; +#define to_pcie_device(d) container_of(d, struct pcie_device, device) + +static inline void set_service_data(struct pcie_device *dev, void *data) +{ + dev->priv_data = data; +} + +static inline void *get_service_data(struct pcie_device *dev) +{ + return dev->priv_data; +} + +struct pcie_port_service_driver { + const char *name; + int (*probe) (struct pcie_device *dev); + void (*remove) (struct pcie_device *dev); + int (*suspend) (struct pcie_device *dev); + int (*resume) (struct pcie_device *dev); + + /* Device driver may resume normal operations */ + void (*error_resume)(struct pci_dev *dev); + + /* Link Reset Capability - AER service driver specific */ + pci_ers_result_t (*reset_link) (struct pci_dev *dev); + + int port_type; /* Type of the port this driver can handle */ + u32 service; /* Port service this device represents */ + + struct device_driver driver; +}; +#define to_service_driver(d) \ + container_of(d, struct pcie_port_service_driver, driver) + +int pcie_port_service_register(struct pcie_port_service_driver *new); +void pcie_port_service_unregister(struct pcie_port_service_driver *new); + +#endif /* _PCIEPORT_IF_H_ */ diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 5480f54f7612..d29678958d92 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -14,9 +14,9 @@ #include #include #include -#include #include +#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index 319c94976873..c7d8debb4a5c 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -10,8 +10,8 @@ #include #include #include -#include +#include "pcieport_if.h" #include "aer/aerdrv.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index f0fba552a0e2..b5c5697cfb30 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c @@ -13,7 +13,7 @@ #include #include -#include +#include "pcieport_if.h" #include "portdrv.h" static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index ef3bad4ad010..bab9cb71130f 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -15,9 +15,9 @@ #include #include #include -#include #include +#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index fb1c1bb87316..13dbe846a1d1 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -15,11 +15,11 @@ #include #include #include -#include #include #include #include +#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h deleted file mode 100644 index b69769dbf659..000000000000 --- a/include/linux/pcieport_if.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * File: pcieport_if.h - * Purpose: PCI Express Port Bus Driver's IF Data Structure - * - * Copyright (C) 2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#ifndef _PCIEPORT_IF_H_ -#define _PCIEPORT_IF_H_ - -/* Port Type */ -#define PCIE_ANY_PORT (~0) - -/* Service Type */ -#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ -#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) -#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ -#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) -#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ -#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) -#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ -#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) -#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ -#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) - -struct pcie_device { - int irq; /* Service IRQ/MSI/MSI-X Vector */ - struct pci_dev *port; /* Root/Upstream/Downstream Port */ - u32 service; /* Port service this device represents */ - void *priv_data; /* Service Private Data */ - struct device device; /* Generic Device Interface */ -}; -#define to_pcie_device(d) container_of(d, struct pcie_device, device) - -static inline void set_service_data(struct pcie_device *dev, void *data) -{ - dev->priv_data = data; -} - -static inline void *get_service_data(struct pcie_device *dev) -{ - return dev->priv_data; -} - -struct pcie_port_service_driver { - const char *name; - int (*probe) (struct pcie_device *dev); - void (*remove) (struct pcie_device *dev); - int (*suspend) (struct pcie_device *dev); - int (*resume) (struct pcie_device *dev); - - /* Device driver may resume normal operations */ - void (*error_resume)(struct pci_dev *dev); - - /* Link Reset Capability - AER service driver specific */ - pci_ers_result_t (*reset_link) (struct pci_dev *dev); - - int port_type; /* Type of the port this driver can handle */ - u32 service; /* Port service this device represents */ - - struct device_driver driver; -}; -#define to_service_driver(d) \ - container_of(d, struct pcie_port_service_driver, driver) - -int pcie_port_service_register(struct pcie_port_service_driver *new); -void pcie_port_service_unregister(struct pcie_port_service_driver *new); - -#endif /* _PCIEPORT_IF_H_ */ -- cgit v1.2.1 From ef7942603e35e300e6967fa7c17ebc17a0c00f59 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:42:01 -0600 Subject: PCI/portdrv: Merge pcieport_if.h into portdrv.h pcieport_if.h contained the interfaces to register port service driver, e.g., pcie_port_service_register(). portdrv.h contained internal data structures of the port driver. I don't think it's worth keeping those files separate, since both headers and their users are all inside the PCI core. Merge pcieport_if.h directly in drivers/pci/pcie/portdrv.h and update the users to include that instead. Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/hotplug/pciehp.h | 2 +- drivers/pci/pcie/aer/aerdrv.h | 2 +- drivers/pci/pcie/pcie-dpc.c | 2 +- drivers/pci/pcie/pcieport_if.h | 71 ----------------------------------------- drivers/pci/pcie/pme.c | 1 - drivers/pci/pcie/portdrv.h | 61 ++++++++++++++++++++++++++++++++++- drivers/pci/pcie/portdrv_acpi.c | 1 - drivers/pci/pcie/portdrv_bus.c | 1 - drivers/pci/pcie/portdrv_core.c | 1 - drivers/pci/pcie/portdrv_pci.c | 1 - 10 files changed, 63 insertions(+), 80 deletions(-) delete mode 100644 drivers/pci/pcie/pcieport_if.h diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 08072bcaa381..88e917c9120f 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -23,7 +23,7 @@ #include #include -#include "../pcie/pcieport_if.h" +#include "../pcie/portdrv.h" #define MY_NAME "pciehp" diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 568326f385b7..a884f68bada4 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -13,7 +13,7 @@ #include #include -#include "../pcieport_if.h" +#include "../portdrv.h" #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c index bac895de4c72..8c57d607e603 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/pcie-dpc.c @@ -11,7 +11,7 @@ #include #include -#include "pcieport_if.h" +#include "portdrv.h" #include "../pci.h" #include "aer/aerdrv.h" diff --git a/drivers/pci/pcie/pcieport_if.h b/drivers/pci/pcie/pcieport_if.h deleted file mode 100644 index b69769dbf659..000000000000 --- a/drivers/pci/pcie/pcieport_if.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * File: pcieport_if.h - * Purpose: PCI Express Port Bus Driver's IF Data Structure - * - * Copyright (C) 2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#ifndef _PCIEPORT_IF_H_ -#define _PCIEPORT_IF_H_ - -/* Port Type */ -#define PCIE_ANY_PORT (~0) - -/* Service Type */ -#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ -#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) -#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ -#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) -#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ -#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) -#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ -#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) -#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ -#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) - -struct pcie_device { - int irq; /* Service IRQ/MSI/MSI-X Vector */ - struct pci_dev *port; /* Root/Upstream/Downstream Port */ - u32 service; /* Port service this device represents */ - void *priv_data; /* Service Private Data */ - struct device device; /* Generic Device Interface */ -}; -#define to_pcie_device(d) container_of(d, struct pcie_device, device) - -static inline void set_service_data(struct pcie_device *dev, void *data) -{ - dev->priv_data = data; -} - -static inline void *get_service_data(struct pcie_device *dev) -{ - return dev->priv_data; -} - -struct pcie_port_service_driver { - const char *name; - int (*probe) (struct pcie_device *dev); - void (*remove) (struct pcie_device *dev); - int (*suspend) (struct pcie_device *dev); - int (*resume) (struct pcie_device *dev); - - /* Device driver may resume normal operations */ - void (*error_resume)(struct pci_dev *dev); - - /* Link Reset Capability - AER service driver specific */ - pci_ers_result_t (*reset_link) (struct pci_dev *dev); - - int port_type; /* Type of the port this driver can handle */ - u32 service; /* Port service this device represents */ - - struct device_driver driver; -}; -#define to_service_driver(d) \ - container_of(d, struct pcie_port_service_driver, driver) - -int pcie_port_service_register(struct pcie_port_service_driver *new); -void pcie_port_service_unregister(struct pcie_port_service_driver *new); - -#endif /* _PCIEPORT_IF_H_ */ diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index d29678958d92..3ed67676ea2a 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -16,7 +16,6 @@ #include #include -#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index a854bc569117..d4009e35702c 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * File: portdrv.h - * Purpose: PCI Express Port Bus Driver's Internal Data Structures + * Purpose: PCI Express Port Bus Driver's Data Structures * * Copyright (C) 2004 Intel * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) @@ -12,7 +12,66 @@ #include +/* Service Type */ +#define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ +#define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) +#define PCIE_PORT_SERVICE_AER_SHIFT 1 /* Advanced Error Reporting */ +#define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) +#define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ +#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) +#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ +#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) +#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ +#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) + #define PCIE_PORT_DEVICE_MAXSERVICES 5 + +/* Port Type */ +#define PCIE_ANY_PORT (~0) + +struct pcie_device { + int irq; /* Service IRQ/MSI/MSI-X Vector */ + struct pci_dev *port; /* Root/Upstream/Downstream Port */ + u32 service; /* Port service this device represents */ + void *priv_data; /* Service Private Data */ + struct device device; /* Generic Device Interface */ +}; +#define to_pcie_device(d) container_of(d, struct pcie_device, device) + +static inline void set_service_data(struct pcie_device *dev, void *data) +{ + dev->priv_data = data; +} + +static inline void *get_service_data(struct pcie_device *dev) +{ + return dev->priv_data; +} + +struct pcie_port_service_driver { + const char *name; + int (*probe) (struct pcie_device *dev); + void (*remove) (struct pcie_device *dev); + int (*suspend) (struct pcie_device *dev); + int (*resume) (struct pcie_device *dev); + + /* Device driver may resume normal operations */ + void (*error_resume)(struct pci_dev *dev); + + /* Link Reset Capability - AER service driver specific */ + pci_ers_result_t (*reset_link) (struct pci_dev *dev); + + int port_type; /* Type of the port this driver can handle */ + u32 service; /* Port service this device represents */ + + struct device_driver driver; +}; +#define to_service_driver(d) \ + container_of(d, struct pcie_port_service_driver, 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 diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index c7d8debb4a5c..53f60053bd47 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -11,7 +11,6 @@ #include #include -#include "pcieport_if.h" #include "aer/aerdrv.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index b5c5697cfb30..4969ccf6b214 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c @@ -13,7 +13,6 @@ #include #include -#include "pcieport_if.h" #include "portdrv.h" static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index bab9cb71130f..4268b2fc2c7a 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -17,7 +17,6 @@ #include #include -#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 13dbe846a1d1..977bd3cca2e5 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -19,7 +19,6 @@ #include #include -#include "pcieport_if.h" #include "../pci.h" #include "portdrv.h" -- cgit v1.2.1 From dcb0453d71e361d4718bb566d99e6ae498284419 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:53 -0600 Subject: PCI/PM: Move pcie_clear_root_pme_status() to core Move pcie_clear_root_pme_status() from the port driver to the PCI core so it will be available even when the port driver isn't present. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Christoph Hellwig --- drivers/pci/pci.c | 9 +++++++++ drivers/pci/pci.h | 1 + drivers/pci/pcie/portdrv.h | 2 -- drivers/pci/pcie/portdrv_pci.c | 9 --------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f6a4dd10d9b0..120e3393fc35 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1683,6 +1683,15 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) } EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); +/** + * pcie_clear_root_pme_status - Clear root port PME interrupt status. + * @dev: PCIe root port or event collector. + */ +void pcie_clear_root_pme_status(struct pci_dev *dev) +{ + pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME); +} + /** * pci_check_pme_status - Check if given device has generated PME. * @dev: Device to check. diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fcd81911b127..813ca2c895d8 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -71,6 +71,7 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state); void pci_power_up(struct pci_dev *dev); void pci_disable_enabled_device(struct pci_dev *dev); int pci_finish_runtime_suspend(struct pci_dev *dev); +void pcie_clear_root_pme_status(struct pci_dev *dev); int __pci_pme_wakeup(struct pci_dev *dev, void *ign); void pci_pme_restore(struct pci_dev *dev); bool pci_dev_keep_suspended(struct pci_dev *dev); diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index d4009e35702c..7086086e45d0 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -93,8 +93,6 @@ void pcie_port_bus_unregister(void); struct pci_dev; -void pcie_clear_root_pme_status(struct pci_dev *dev); - #ifdef CONFIG_HOTPLUG_PCI_PCIE extern bool pciehp_msi_disabled; diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 977bd3cca2e5..d6f10a97d400 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -49,15 +49,6 @@ __setup("pcie_ports=", pcie_port_setup); /* global data */ -/** - * pcie_clear_root_pme_status - Clear root port PME interrupt status. - * @dev: PCIe root port or event collector. - */ -void pcie_clear_root_pme_status(struct pci_dev *dev) -{ - pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME); -} - static int pcie_portdrv_restore_config(struct pci_dev *dev) { int retval; -- cgit v1.2.1 From a39bd851dccfdcb89db3d9a6b03283aaf15f310c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:54 -0600 Subject: PCI/PM: Clear PCIe PME Status bit in core, not PCIe port driver fe31e69740ed ("PCI/PCIe: Clear Root PME Status bits early during system resume") added a .resume_noirq() callback to the PCIe port driver to clear the PME Status bit during resume to work around a BIOS issue. The BIOS evidently enabled PME interrupts for ACPI-based runtime wakeups but did not clear the PME Status bit during resume, which meant PMEs after resume did not trigger interrupts because PME Status did not transition from cleared to set. The fix was in the PCIe port driver, so it worked when CONFIG_PCIEPORTBUS was set. But I think we *always* want the fix because the platform may use PME interrupts even if Linux is built without the PCIe port driver. Move the fix from the port driver to the PCI core so we can work around this "PME doesn't work after waking from a sleep state" issue regardless of CONFIG_PCIEPORTBUS. [bhelgaas: folded in warning fix from Arnd Bergmann : https://lkml.kernel.org/r/20180328134747.2062348-1-arnd@arndb.de] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 13 +++++++++++++ drivers/pci/pcie/portdrv_pci.c | 15 --------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 3bed6beda051..c49af2b679bc 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -714,6 +714,17 @@ static void pci_pm_complete(struct device *dev) #endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_SUSPEND +static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev) +{ + /* + * Some BIOSes forget to clear Root PME Status bits after system + * wakeup, which breaks ACPI-based runtime wakeup on PCI Express. + * Clear those bits now just in case (shouldn't hurt). + */ + if (pci_is_pcie(pci_dev) && + pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT) + pcie_clear_root_pme_status(pci_dev); +} static int pci_pm_suspend(struct device *dev) { @@ -873,6 +884,8 @@ static int pci_pm_resume_noirq(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); + pcie_pme_root_status_cleanup(pci_dev); + if (drv && drv->pm && drv->pm->resume_noirq) error = drv->pm->resume_noirq(dev); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index d6f10a97d400..ec9e936c2a5b 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -61,20 +61,6 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev) } #ifdef CONFIG_PM -static int pcie_port_resume_noirq(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - - /* - * Some BIOSes forget to clear Root PME Status bits after system wakeup - * which breaks ACPI-based runtime wakeup on PCI Express, so clear those - * bits now just in case (shouldn't hurt). - */ - if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT) - pcie_clear_root_pme_status(pdev); - return 0; -} - static int pcie_port_runtime_suspend(struct device *dev) { return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY; @@ -102,7 +88,6 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .thaw = pcie_port_device_resume, .poweroff = pcie_port_device_suspend, .restore = pcie_port_device_resume, - .resume_noirq = pcie_port_resume_noirq, .runtime_suspend = pcie_port_runtime_suspend, .runtime_resume = pcie_port_runtime_resume, .runtime_idle = pcie_port_runtime_idle, -- cgit v1.2.1 From 3620c71484f7a19b2588e577ea732f55719f5b1f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:55 -0600 Subject: PCI/PM: Clear PCIe PME Status bit for Root Complex Event Collectors Per PCIe r4.0, sec 6.1.6, Root Complex Event Collectors can generate PME interrupts on behalf of Root Complex Integrated Endpoints. Linux does not currently enable PME interrupts from RC Event Collectors, but fe31e69740ed ("PCI/PCIe: Clear Root PME Status bits early during system resume") suggests PME interrupts may be enabled by the platform for ACPI- based runtime wakeup. Clear the PCIe PME Status bit for Root Complex Event Collectors during resume, just like we already do for Root Ports. If the BIOS enables PME interrupts for an event collector and neglects to clear the status bit on resume, this change should fix the same bug as fe31e69740ed (PMEs not working after waking from a sleep state), but for Root Complex Integrated Endpoints. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci-driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c49af2b679bc..646da0d2d7a8 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -722,7 +722,8 @@ static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev) * Clear those bits now just in case (shouldn't hurt). */ if (pci_is_pcie(pci_dev) && - pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT) + (pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(pci_dev) == PCI_EXP_TYPE_RC_EC)) pcie_clear_root_pme_status(pci_dev); } -- cgit v1.2.1 From 79a011194b23302dc43f265ed4237054877768ff Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:56 -0600 Subject: PCI/portdrv: Disable port driver in compat mode The "pcie_ports=compat" kernel parameter sets pcie_ports_disabled, which is intended to disable the PCIe port driver. But even when it was disabled, we registered pcie_portdriver so we could work around a BIOS PME issue (see fe31e69740ed ("PCI/PCIe: Clear Root PME Status bits early during system resume")). Registering the driver meant that the pcie_portdrv_probe() path called pci_enable_device(), pci_save_state(), pm_runtime_set_autosuspend_delay(), pm_runtime_use_autosuspend(), etc., even when the driver was disabled. We've since moved the BIOS PME workaround from the port driver to the core, so stop registering the PCIe port driver in compat mode. This means "pcie_ports=compat" will now be basically the same as turning off CONFIG_PCIEPORTBUS completely. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pcie/portdrv_core.c | 3 --- drivers/pci/pcie/portdrv_pci.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 4268b2fc2c7a..9a41751db332 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -211,9 +211,6 @@ static int get_port_device_capability(struct pci_dev *dev) 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()) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index ec9e936c2a5b..5d9d5305ebef 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -261,7 +261,7 @@ static int __init pcie_portdrv_init(void) int retval; if (pcie_ports_disabled) - return pci_register_driver(&pcie_portdriver); + return -EACCES; dmi_check_system(pcie_portdrv_dmi_table); -- cgit v1.2.1 From c6c889d932bb49d95273711a790d16f814cb213b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:06:56 -0600 Subject: PCI/portdrv: Remove pcie_port_bus_type link order dependency The pcie_port_bus_type must be registered before drivers that depend on it can be registered. Those drivers include: pcied_init() # PCIe native hotplug driver aer_service_init() # AER driver dpc_service_init() # DPC driver pcie_pme_service_init() # PME driver Previously we registered pcie_port_bus_type from pcie_portdrv_init(), a device_initcall. The callers of pcie_port_service_register() (above) are also device_initcalls. This is fragile because the device_initcall ordering depends on link order, which is not explicit. Register pcie_port_bus_type from pci_driver_init() along with pci_bus_type. This removes the link order dependency between portdrv and the pciehp, AER, DPC, and PCIe PME drivers. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Christoph Hellwig --- drivers/pci/pci-driver.c | 44 ++++++++++++++++++++++++++++++++- drivers/pci/pcie/Makefile | 2 +- drivers/pci/pcie/portdrv_bus.c | 55 ------------------------------------------ drivers/pci/pcie/portdrv_pci.c | 13 +--------- 4 files changed, 45 insertions(+), 69 deletions(-) delete mode 100644 drivers/pci/pcie/portdrv_bus.c diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 646da0d2d7a8..21eb2f7ad95d 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -19,6 +19,7 @@ #include #include #include "pci.h" +#include "pcie/portdrv.h" struct pci_dynid { struct list_head node; @@ -1552,8 +1553,49 @@ struct bus_type pci_bus_type = { }; EXPORT_SYMBOL(pci_bus_type); +#ifdef CONFIG_PCIEPORTBUS +static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) +{ + struct pcie_device *pciedev; + struct pcie_port_service_driver *driver; + + if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type) + return 0; + + pciedev = to_pcie_device(dev); + driver = to_service_driver(drv); + + if (driver->service != pciedev->service) + return 0; + + if (driver->port_type != PCIE_ANY_PORT && + driver->port_type != pci_pcie_type(pciedev->port)) + return 0; + + return 1; +} + +struct bus_type pcie_port_bus_type = { + .name = "pci_express", + .match = pcie_port_bus_match, +}; +EXPORT_SYMBOL_GPL(pcie_port_bus_type); +#endif + static int __init pci_driver_init(void) { - return bus_register(&pci_bus_type); + int ret; + + ret = bus_register(&pci_bus_type); + if (ret) + return ret; + +#ifdef CONFIG_PCIEPORTBUS + ret = bus_register(&pcie_port_bus_type); + if (ret) + return ret; +#endif + + return 0; } postcore_initcall(pci_driver_init); diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 223e4c34c29a..e01c10c97b95 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -6,7 +6,7 @@ # Build PCI Express ASPM if needed obj-$(CONFIG_PCIEASPM) += aspm.o -pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.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_bus.c b/drivers/pci/pcie/portdrv_bus.c deleted file mode 100644 index 4969ccf6b214..000000000000 --- a/drivers/pci/pcie/portdrv_bus.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * File: portdrv_bus.c - * Purpose: PCI Express Port Bus Driver's Bus Overloading Functions - * - * Copyright (C) 2004 Intel - * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) - */ - -#include -#include -#include -#include -#include - -#include "portdrv.h" - -static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); - -struct bus_type pcie_port_bus_type = { - .name = "pci_express", - .match = pcie_port_bus_match, -}; -EXPORT_SYMBOL_GPL(pcie_port_bus_type); - -static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(drv); - - if (driver->service != pciedev->service) - return 0; - - if ((driver->port_type != PCIE_ANY_PORT) && - (driver->port_type != pci_pcie_type(pciedev->port))) - return 0; - - return 1; -} - -int pcie_port_bus_register(void) -{ - return bus_register(&pcie_port_bus_type); -} - -void pcie_port_bus_unregister(void) -{ - bus_unregister(&pcie_port_bus_type); -} diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 5d9d5305ebef..127321e17184 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -258,22 +258,11 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = { static int __init pcie_portdrv_init(void) { - int retval; - if (pcie_ports_disabled) return -EACCES; dmi_check_system(pcie_portdrv_dmi_table); - retval = pcie_port_bus_register(); - if (retval) { - printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); - goto out; - } - retval = pci_register_driver(&pcie_portdriver); - if (retval) - pcie_port_bus_unregister(); - out: - return retval; + return pci_register_driver(&pcie_portdriver); } device_initcall(pcie_portdrv_init); -- cgit v1.2.1 From 168f3ae595d6a6cee8321633f29273a7dd4fc83e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:24 -0600 Subject: PCI/portdrv: Remove unused PCIE_PORT_SERVICE_VC No driver registers for PCIE_PORT_SERVICE_VC, so remove it. This removes the VC "service" files from /sys/bus/pci_express/devices, e.g., 0000:07:00.0:pcie108, 0000:08:04.0:pcie208 (all the files that contained "8" as the last digit of the "pcieXXX" part). The port driver created these files for PCIe port devices that have a VC Capability. Since this reduces PCIE_PORT_DEVICE_MAXSERVICES and moves DPC down into the spot where VC used to be, the DPC sysfs files will now be named "pcieXX8". I don't think there's anything useful userspace can do with those files, so I hope nobody cares about these filenames. There is no VC driver that calls pcie_port_service_register(), so there never was a /sys/bus/pci_express/drivers/vc directory. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Christoph Hellwig --- drivers/pci/pcie/portdrv.h | 6 ++---- drivers/pci/pcie/portdrv_acpi.c | 2 +- drivers/pci/pcie/portdrv_core.c | 14 ++++---------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 7086086e45d0..7bfd75f9197b 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -19,12 +19,10 @@ #define PCIE_PORT_SERVICE_AER (1 << PCIE_PORT_SERVICE_AER_SHIFT) #define PCIE_PORT_SERVICE_HP_SHIFT 2 /* Native Hotplug */ #define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT) -#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */ -#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT) -#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */ +#define PCIE_PORT_SERVICE_DPC_SHIFT 3 /* Downstream Port Containment */ #define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT) -#define PCIE_PORT_DEVICE_MAXSERVICES 5 +#define PCIE_PORT_DEVICE_MAXSERVICES 4 /* Port Type */ #define PCIE_ANY_PORT (~0) diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index 53f60053bd47..9d12650dc2ae 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -47,7 +47,7 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask) flags = root->osc_control_set; - *srv_mask = PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC; + *srv_mask = PCIE_PORT_SERVICE_DPC; if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL) *srv_mask |= PCIE_PORT_SERVICE_HP; if (flags & OSC_PCI_EXPRESS_PME_CONTROL) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 9a41751db332..bf851da97947 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -188,10 +188,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; } @@ -211,8 +209,7 @@ static int get_port_device_capability(struct pci_dev *dev) int services = 0; int cap_mask = 0; - cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP - | PCIE_PORT_SERVICE_VC; + cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP; if (pci_aer_available()) cap_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC; @@ -239,9 +236,6 @@ static int get_port_device_capability(struct pci_dev *dev) */ 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) { @@ -331,7 +325,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; } -- cgit v1.2.1 From 02bfeb484230dfd073148a17253aeb1717ce769c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:25 -0600 Subject: 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 Reviewed-by: Rafael J. Wysocki --- 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..63b2cb775324 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->native_hotplug = 0; + if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL)) + host_bridge->native_aer = 0; + if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL)) + host_bridge->native_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..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; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef5377438a1e..a00de697a970 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->native_aer = 1; + bridge->native_hotplug = 1; + bridge->native_pme = 1; + return bridge; } EXPORT_SYMBOL(pci_alloc_host_bridge); diff --git a/include/linux/pci.h b/include/linux/pci.h index 024a1beda008..a04b7abc6b7a 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 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 */ /* Resource alignment requirements */ resource_size_t (*align_resource)(struct pci_dev *dev, const struct resource *res, -- cgit v1.2.1 From 1b64cb87cf0013cdd04b7e1665072a00f92b830f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:26 -0600 Subject: PCI/portdrv: Remove unnecessary include of portdrv_pci.c doesn't use anything from . Remove the include of it. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pcie/portdrv_pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 127321e17184..1997d9f2743e 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -17,7 +17,6 @@ #include #include #include -#include #include "../pci.h" #include "portdrv.h" -- cgit v1.2.1 From 1e447c57ae367c030e7b424f55a91470ae7604ca Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:27 -0600 Subject: PCI/portdrv: Remove "pcie_hp=nomsi" kernel parameter 7570a333d8b0 ("PCI: Add pcie_hp=nomsi to disable MSI/MSI-X for pciehp driver") added the "pcie_hp=nomsi" kernel parameter to work around this error on shutdown: irq 16: nobody cared (try booting with the "irqpoll" option) Pid: 1081, comm: reboot Not tainted 3.2.0 #1 ... Disabling IRQ #16 This happened on an unspecified system (possibly involving the Integrated Device Technology, Inc. Device 807f bridge) where "an un-wanted interrupt is generated when PCI driver switches from MSI/MSI-X to INTx while shutting down the device." The implication was that the device was buggy, but it is normal for a device to use INTx after MSI/MSI-X have been disabled. The only problem was that the driver was still attached and it wasn't prepared for INTx interrupts. Prarit Bhargava fixed this issue with fda78d7a0ead ("PCI/MSI: Stop disabling MSI/MSI-X in pci_device_shutdown()"). There is no automated way to set this parameter, so it's not very useful for distributions or end users. It's really only useful for debugging, and we have "pci=nomsi" for that purpose. Revert 7570a333d8b0 to remove the "pcie_hp=nomsi" parameter. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki CC: MUNEDA Takahiro CC: Kenji Kaneshige CC: Prarit Bhargava --- Documentation/admin-guide/kernel-parameters.txt | 4 ---- drivers/pci/pcie/portdrv.h | 12 ------------ drivers/pci/pcie/portdrv_core.c | 20 +++----------------- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 1d1d53f85ddd..761749562165 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3130,10 +3130,6 @@ force Enable ASPM even on devices that claim not to support it. WARNING: Forcing ASPM on may cause system lockups. - pcie_hp= [PCIE] PCI Express Hotplug driver options: - nomsi Do not use MSI for PCI Express Native Hotplug (this - makes all PCIe ports use INTx for hotplug services). - pcie_ports= [PCIE] PCIe ports handling: auto Ask the BIOS whether or not to use native PCIe services associated with PCIe ports (PME, hot-plug, AER). Use diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index ed84e767085f..86368f9341d7 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -91,18 +91,6 @@ void pcie_port_bus_unregister(void); struct pci_dev; -#ifdef CONFIG_HOTPLUG_PCI_PCIE -extern bool pciehp_msi_disabled; - -static inline bool pciehp_no_msi(void) -{ - return pciehp_msi_disabled; -} - -#else /* !CONFIG_HOTPLUG_PCI_PCIE */ -static inline bool pciehp_no_msi(void) { return false; } -#endif /* !CONFIG_HOTPLUG_PCI_PCIE */ - #ifdef CONFIG_PCIE_PME extern bool pcie_pme_msi_disabled; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 5c25761cd05e..6ed67cbf6148 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -20,17 +20,6 @@ #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 @@ -168,16 +157,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; -- cgit v1.2.1 From 4c0fd7648d880d98add62552cffdf993bde65cf8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:28 -0600 Subject: PCI/portdrv: Remove unnecessary "pcie_ports=auto" parameter The "pcie_ports=auto" parameter set pcie_ports_disabled and pcie_ports_auto to their compiled-in defaults, so specifying the parameter is the same as not using it at all. Remove the "pcie_ports=auto" parameter and update the documentation. Signed-off-by: Bjorn Helgaas --- Documentation/admin-guide/kernel-parameters.txt | 15 +++++++-------- drivers/pci/pcie/portdrv_pci.c | 3 --- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 761749562165..26565794a573 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3130,14 +3130,13 @@ force Enable ASPM even on devices that claim not to support it. WARNING: Forcing ASPM on may cause system lockups. - pcie_ports= [PCIE] PCIe ports handling: - auto Ask the BIOS whether or not to use native PCIe services - associated with PCIe ports (PME, hot-plug, AER). Use - them only if that is allowed by the BIOS. - native Use native PCIe services associated with PCIe ports - unconditionally. - compat Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe - ports driver. + pcie_ports= [PCIE] PCIe port services handling: + native Use native PCIe services (PME, AER, DPC, PCIe hotplug) + even if the platform doesn't give the OS permission to + use them. This may cause conflicts if the platform + also tries to use these services. + compat Disable native PCIe services (PME, AER, DPC, PCIe + hotplug). pcie_port_pm= [PCIE] PCIe port power management handling: off Disable power management of all PCIe ports diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 1997d9f2743e..8b62192342ac 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -37,9 +37,6 @@ static int __init pcie_port_setup(char *str) } else if (!strncmp(str, "native", 6)) { pcie_ports_disabled = false; pcie_ports_auto = false; - } else if (!strncmp(str, "auto", 4)) { - pcie_ports_disabled = false; - pcie_ports_auto = true; } return 1; -- cgit v1.2.1 From 842b447f0074b93e9f7db60039fdc72ec14bef9a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:29 -0600 Subject: PCI/portdrv: Encapsulate pcie_ports_auto inside the port driver "pcie_ports_auto" is only used inside the PCIe port driver itself, so move it from include/linux/pci.h to portdrv.h so it's not visible to the whole kernel. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv.h | 2 ++ include/linux/pci.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 86368f9341d7..62e28b5afa51 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -12,6 +12,8 @@ #include +extern bool pcie_ports_auto; + /* Service Type */ #define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ #define PCIE_PORT_SERVICE_PME (1 << PCIE_PORT_SERVICE_PME_SHIFT) diff --git a/include/linux/pci.h b/include/linux/pci.h index a04b7abc6b7a..dc70a3ce8dc5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1449,10 +1449,8 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d, #ifdef CONFIG_PCIEPORTBUS extern bool pcie_ports_disabled; -extern bool pcie_ports_auto; #else #define pcie_ports_disabled true -#define pcie_ports_auto false #endif #ifdef CONFIG_PCIEASPM -- cgit v1.2.1 From d850882b726f6db01b0792151e72e69b234aa461 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Mar 2018 11:21:30 -0600 Subject: PCI/portdrv: Rename and reverse sense of pcie_ports_auto The platform may restrict the OS's use of PCIe services, e.g., via the ACPI _OSC method. The user may use "pcie_ports=native" to force the port driver to use PCIe services even if the platform asked us not to. The "pcie_ports=native" parameter determines the setting of pcie_ports_auto. Rename this to pcie_ports_native and reverse the sense to simplify the code. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv.h | 2 +- drivers/pci/pcie/portdrv_core.c | 15 ++++----------- drivers/pci/pcie/portdrv_pci.c | 15 +++++++-------- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 62e28b5afa51..3e0058a5500f 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -12,7 +12,7 @@ #include -extern bool pcie_ports_auto; +extern bool pcie_ports_native; /* Service Type */ #define PCIE_PORT_SERVICE_PME_SHIFT 0 /* Power Management Event */ diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 6ed67cbf6148..6890aea4a550 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -193,17 +193,10 @@ 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; - /* - * 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; - - if (dev->is_hotplug_bridge && (native || host->native_hotplug)) { + if (dev->is_hotplug_bridge && + (pcie_ports_native || host->native_hotplug)) { services |= PCIE_PORT_SERVICE_HP; /* @@ -215,7 +208,7 @@ static int get_port_device_capability(struct pci_dev *dev) } if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR) && - pci_aer_available() && (native || host->native_aer)) { + pci_aer_available() && (pcie_ports_native || host->native_aer)) { services |= PCIE_PORT_SERVICE_AER; /* @@ -231,7 +224,7 @@ static int get_port_device_capability(struct pci_dev *dev) * those yet. */ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT && - (native || host->native_pme)) { + (pcie_ports_native || host->native_pme)) { services |= PCIE_PORT_SERVICE_PME; /* diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 8b62192342ac..8e4260d25941 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -25,19 +25,18 @@ bool pcie_ports_disabled; /* - * If this switch is set, ACPI _OSC will be used to determine whether or not to - * enable PCIe port native services. + * 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_auto = true; +bool pcie_ports_native; static int __init pcie_port_setup(char *str) { - if (!strncmp(str, "compat", 6)) { + if (!strncmp(str, "compat", 6)) pcie_ports_disabled = true; - } else if (!strncmp(str, "native", 6)) { - pcie_ports_disabled = false; - pcie_ports_auto = false; - } + else if (!strncmp(str, "native", 6)) + pcie_ports_native = true; return 1; } -- cgit v1.2.1 From f0553ba08a19031726f120448eedc3aff1599c23 Mon Sep 17 00:00:00 2001 From: Frederick Lawler Date: Thu, 22 Mar 2018 16:20:55 -0500 Subject: PCI/AER: Use cached AER Capability offset Replace pci_find_ext_capability(..., PCI_EXT_CAP_ID_ERR) calls with pci_dev->aer_cap. pci_dev->aer_cap is initialized in pci_init_capabilities(), which happens before any of these users of the AER Capability. Signed-off-by: Frederick Lawler Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aer_inject.c | 4 ++-- drivers/pci/pcie/aer/ecrc.c | 4 ++-- drivers/pci/pcie/portdrv_core.c | 14 ++++++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 25e1feb962c5..a49090935303 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -344,7 +344,7 @@ static int aer_inject(struct aer_error_inj *einj) goto out_put; } - pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos_cap_err = dev->aer_cap; if (!pos_cap_err) { pci_err(dev, "aer_inject: Device doesn't support AER\n"); ret = -EPROTONOSUPPORT; @@ -355,7 +355,7 @@ static int aer_inject(struct aer_error_inj *einj) pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, &uncor_mask); - rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR); + rp_pos_cap_err = rpdev->aer_cap; if (!rp_pos_cap_err) { pci_err(rpdev, "aer_inject: Root port doesn't support AER\n"); ret = -EPROTONOSUPPORT; diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c index 26d3cac9e635..afb5f761f5d4 100644 --- a/drivers/pci/pcie/aer/ecrc.c +++ b/drivers/pci/pcie/aer/ecrc.c @@ -40,7 +40,7 @@ static int enable_ecrc_checking(struct pci_dev *dev) if (!pci_is_pcie(dev)) return -ENODEV; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return -ENODEV; @@ -68,7 +68,7 @@ static int disable_ecrc_checking(struct pci_dev *dev) if (!pci_is_pcie(dev)) return -ENODEV; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + pos = dev->aer_cap; if (!pos) return -ENODEV; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 6890aea4a550..099ef7ac615b 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -40,7 +40,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; /* @@ -56,8 +56,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); @@ -65,6 +68,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); @@ -207,8 +211,9 @@ static int get_port_device_capability(struct pci_dev *dev) PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE); } - if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR) && - pci_aer_available() && (pcie_ports_native || host->native_aer)) { +#ifdef CONFIG_PCIEAER + if (dev->aer_cap && pci_aer_available() && + (pcie_ports_native || host->native_aer)) { services |= PCIE_PORT_SERVICE_AER; /* @@ -217,6 +222,7 @@ static int get_port_device_capability(struct pci_dev *dev) */ pci_disable_pcie_error_reporting(dev); } +#endif /* * Root ports are capable of generating PME too. Root Complex -- cgit v1.2.1 From 4e5fad429bd179a41fa8b222463397e8cc806cd1 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 27 Mar 2018 13:48:35 +0300 Subject: PCI/DPC: Do not enable DPC if AER control is not allowed by the BIOS Commit eed85ff4c0da ("PCI/DPC: Enable DPC only if AER is available") made DPC control dependent whether AER is enabled in the OS. However, it does not take into account situations where BIOS has not given OS control of AER: acpi PNP0A08:00: _OSC: OS supports [ExtendedConfig ASPM ClockPM Segments MSI] acpi PNP0A08:00: _OSC: platform does not support [AER] acpi PNP0A08:00: _OSC: OS now controls [PCIeHotplug PME PCIeCapability] I think here it is better not to enable DPC even if the capability is available because then it would be against what "Determination of DPC Control" note in PCIe 4.0 sec 6.1.10 recommends. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_acpi.c | 4 ++-- drivers/pci/pcie/portdrv_core.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index 9d12650dc2ae..8ab5d434b9c6 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -47,11 +47,11 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask) flags = root->osc_control_set; - *srv_mask = PCIE_PORT_SERVICE_DPC; + *srv_mask = 0; if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL) *srv_mask |= PCIE_PORT_SERVICE_HP; if (flags & OSC_PCI_EXPRESS_PME_CONTROL) *srv_mask |= PCIE_PORT_SERVICE_PME; if (flags & OSC_PCI_EXPRESS_AER_CONTROL) - *srv_mask |= PCIE_PORT_SERVICE_AER; + *srv_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC; } diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 099ef7ac615b..4ba4d05a5e4c 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -242,7 +242,7 @@ static int get_port_device_capability(struct pci_dev *dev) } if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) && - pci_aer_available()) + pci_aer_available() && services & PCIE_PORT_SERVICE_AER) services |= PCIE_PORT_SERVICE_DPC; return services; -- cgit v1.2.1 From e02602bd76257e0368e4c3d4ce11a7ac86df72d2 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 31 Mar 2018 17:48:57 -0500 Subject: PCI/DPC: Rename from pcie-dpc.c to dpc.c Rename pcie-dpc.c to dpc.c. The path "drivers/pci/pcie/pcie-dpc.c" has more occurrences of "pci" than necessary. Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/Makefile | 2 +- drivers/pci/pcie/dpc.c | 307 ++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/pcie-dpc.c | 307 -------------------------------------------- 3 files changed, 308 insertions(+), 308 deletions(-) create mode 100644 drivers/pci/pcie/dpc.c delete mode 100644 drivers/pci/pcie/pcie-dpc.c diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 11fb633b866c..30557a567ee7 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -15,5 +15,5 @@ obj-$(CONFIG_PCIEAER) += aer/ obj-$(CONFIG_PCIE_PME) += pme.o -obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o +obj-$(CONFIG_PCIE_DPC) += dpc.o obj-$(CONFIG_PCIE_PTM) += ptm.o diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c new file mode 100644 index 000000000000..8c57d607e603 --- /dev/null +++ b/drivers/pci/pcie/dpc.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCI Express Downstream Port Containment services driver + * Author: Keith Busch + * + * Copyright (C) 2016 Intel Corp. + */ + +#include +#include +#include +#include + +#include "portdrv.h" +#include "../pci.h" +#include "aer/aerdrv.h" + +struct dpc_dev { + struct pcie_device *dev; + struct work_struct work; + u16 cap_pos; + bool rp_extensions; + u32 rp_pio_status; + u8 rp_log_size; +}; + +static const char * const rp_pio_error_string[] = { + "Configuration Request received UR Completion", /* Bit Position 0 */ + "Configuration Request received CA Completion", /* Bit Position 1 */ + "Configuration Request Completion Timeout", /* Bit Position 2 */ + NULL, + NULL, + NULL, + NULL, + NULL, + "I/O Request received UR Completion", /* Bit Position 8 */ + "I/O Request received CA Completion", /* Bit Position 9 */ + "I/O Request Completion Timeout", /* Bit Position 10 */ + NULL, + NULL, + NULL, + NULL, + NULL, + "Memory Request received UR Completion", /* Bit Position 16 */ + "Memory Request received CA Completion", /* Bit Position 17 */ + "Memory Request Completion Timeout", /* Bit Position 18 */ +}; + +static int dpc_wait_rp_inactive(struct dpc_dev *dpc) +{ + unsigned long timeout = jiffies + HZ; + struct pci_dev *pdev = dpc->dev->port; + struct device *dev = &dpc->dev->device; + u16 cap = dpc->cap_pos, status; + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + while (status & PCI_EXP_DPC_RP_BUSY && + !time_after(jiffies, timeout)) { + msleep(10); + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + } + if (status & PCI_EXP_DPC_RP_BUSY) { + dev_warn(dev, "DPC root port still busy\n"); + return -EBUSY; + } + return 0; +} + +static void dpc_wait_link_inactive(struct dpc_dev *dpc) +{ + unsigned long timeout = jiffies + HZ; + struct pci_dev *pdev = dpc->dev->port; + struct device *dev = &dpc->dev->device; + u16 lnk_status; + + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + while (lnk_status & PCI_EXP_LNKSTA_DLLLA && + !time_after(jiffies, timeout)) { + msleep(10); + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + } + if (lnk_status & PCI_EXP_LNKSTA_DLLLA) + dev_warn(dev, "Link state not disabled for DPC event\n"); +} + +static void dpc_work(struct work_struct *work) +{ + struct dpc_dev *dpc = container_of(work, struct dpc_dev, work); + struct pci_dev *dev, *temp, *pdev = dpc->dev->port; + struct pci_bus *parent = pdev->subordinate; + u16 cap = dpc->cap_pos, ctl; + + pci_lock_rescan_remove(); + list_for_each_entry_safe_reverse(dev, temp, &parent->devices, + bus_list) { + pci_dev_get(dev); + pci_dev_set_disconnected(dev, NULL); + if (pci_has_subordinate(dev)) + pci_walk_bus(dev->subordinate, + pci_dev_set_disconnected, NULL); + pci_stop_and_remove_bus_device(dev); + pci_dev_put(dev); + } + pci_unlock_rescan_remove(); + + dpc_wait_link_inactive(dpc); + if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc)) + return; + if (dpc->rp_extensions && dpc->rp_pio_status) { + pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, + dpc->rp_pio_status); + dpc->rp_pio_status = 0; + } + + pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, + PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT); + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); + pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, + ctl | PCI_EXP_DPC_CTL_INT_EN); +} + +static void dpc_process_rp_pio_error(struct dpc_dev *dpc) +{ + struct device *dev = &dpc->dev->device; + struct pci_dev *pdev = dpc->dev->port; + u16 cap = dpc->cap_pos, dpc_status, first_error; + u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix; + int i; + + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask); + dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n", + status, mask); + + dpc->rp_pio_status = status; + + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc); + dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n", + sev, syserr, exc); + + /* Get First Error Pointer */ + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status); + first_error = (dpc_status & 0x1f00) >> 8; + + status &= ~mask; + for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) { + if (status & (1 << i)) + dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i], + first_error == i ? " (First)" : ""); + } + + if (dpc->rp_log_size < 4) + return; + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG, + &dw0); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4, + &dw1); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8, + &dw2); + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12, + &dw3); + dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n", + dw0, dw1, dw2, dw3); + + if (dpc->rp_log_size < 5) + return; + pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log); + dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log); + + for (i = 0; i < dpc->rp_log_size - 5; i++) { + pci_read_config_dword(pdev, + cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix); + dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix); + } +} + +static irqreturn_t dpc_irq(int irq, void *context) +{ + struct dpc_dev *dpc = (struct dpc_dev *)context; + struct pci_dev *pdev = dpc->dev->port; + struct device *dev = &dpc->dev->device; + u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason; + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); + + if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0)) + return IRQ_NONE; + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); + + if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT)) + return IRQ_NONE; + + if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) { + pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, + PCI_EXP_DPC_STATUS_INTERRUPT); + return IRQ_HANDLED; + } + + pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, + ctl & ~PCI_EXP_DPC_CTL_INT_EN); + + pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID, + &source); + + dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n", + status, source); + + reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1; + ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5; + + dev_warn(dev, "DPC %s detected, remove downstream devices\n", + (reason == 0) ? "unmasked uncorrectable error" : + (reason == 1) ? "ERR_NONFATAL" : + (reason == 2) ? "ERR_FATAL" : + (ext_reason == 0) ? "RP PIO error" : + (ext_reason == 1) ? "software trigger" : + "reserved error"); + /* show RP PIO error detail information */ + if (dpc->rp_extensions && reason == 3 && ext_reason == 0) + dpc_process_rp_pio_error(dpc); + + schedule_work(&dpc->work); + + return IRQ_HANDLED; +} + +#define FLAG(x, y) (((x) & (y)) ? '+' : '-') +static int dpc_probe(struct pcie_device *dev) +{ + struct dpc_dev *dpc; + struct pci_dev *pdev = dev->port; + struct device *device = &dev->device; + int status; + u16 ctl, cap; + + if (pcie_aer_get_firmware_first(pdev)) + return -ENOTSUPP; + + dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL); + if (!dpc) + return -ENOMEM; + + dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC); + dpc->dev = dev; + INIT_WORK(&dpc->work, dpc_work); + set_service_data(dev, dpc); + + status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED, + "pcie-dpc", dpc); + if (status) { + dev_warn(device, "request IRQ%d failed: %d\n", dev->irq, + status); + return status; + } + + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap); + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); + + dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT); + if (dpc->rp_extensions) { + dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8; + if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) { + dev_err(device, "RP PIO log size %u is invalid\n", + dpc->rp_log_size); + dpc->rp_log_size = 0; + } + } + + ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN; + pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); + + dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n", + cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT), + FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP), + FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size, + FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE)); + return status; +} + +static void dpc_remove(struct pcie_device *dev) +{ + struct dpc_dev *dpc = get_service_data(dev); + struct pci_dev *pdev = dev->port; + u16 ctl; + + pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); + ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN); + pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); +} + +static struct pcie_port_service_driver dpcdriver = { + .name = "dpc", + .port_type = PCIE_ANY_PORT, + .service = PCIE_PORT_SERVICE_DPC, + .probe = dpc_probe, + .remove = dpc_remove, +}; + +static int __init dpc_service_init(void) +{ + return pcie_port_service_register(&dpcdriver); +} +device_initcall(dpc_service_init); diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c deleted file mode 100644 index 8c57d607e603..000000000000 --- a/drivers/pci/pcie/pcie-dpc.c +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * PCI Express Downstream Port Containment services driver - * Author: Keith Busch - * - * Copyright (C) 2016 Intel Corp. - */ - -#include -#include -#include -#include - -#include "portdrv.h" -#include "../pci.h" -#include "aer/aerdrv.h" - -struct dpc_dev { - struct pcie_device *dev; - struct work_struct work; - u16 cap_pos; - bool rp_extensions; - u32 rp_pio_status; - u8 rp_log_size; -}; - -static const char * const rp_pio_error_string[] = { - "Configuration Request received UR Completion", /* Bit Position 0 */ - "Configuration Request received CA Completion", /* Bit Position 1 */ - "Configuration Request Completion Timeout", /* Bit Position 2 */ - NULL, - NULL, - NULL, - NULL, - NULL, - "I/O Request received UR Completion", /* Bit Position 8 */ - "I/O Request received CA Completion", /* Bit Position 9 */ - "I/O Request Completion Timeout", /* Bit Position 10 */ - NULL, - NULL, - NULL, - NULL, - NULL, - "Memory Request received UR Completion", /* Bit Position 16 */ - "Memory Request received CA Completion", /* Bit Position 17 */ - "Memory Request Completion Timeout", /* Bit Position 18 */ -}; - -static int dpc_wait_rp_inactive(struct dpc_dev *dpc) -{ - unsigned long timeout = jiffies + HZ; - struct pci_dev *pdev = dpc->dev->port; - struct device *dev = &dpc->dev->device; - u16 cap = dpc->cap_pos, status; - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); - while (status & PCI_EXP_DPC_RP_BUSY && - !time_after(jiffies, timeout)) { - msleep(10); - pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); - } - if (status & PCI_EXP_DPC_RP_BUSY) { - dev_warn(dev, "DPC root port still busy\n"); - return -EBUSY; - } - return 0; -} - -static void dpc_wait_link_inactive(struct dpc_dev *dpc) -{ - unsigned long timeout = jiffies + HZ; - struct pci_dev *pdev = dpc->dev->port; - struct device *dev = &dpc->dev->device; - u16 lnk_status; - - pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); - while (lnk_status & PCI_EXP_LNKSTA_DLLLA && - !time_after(jiffies, timeout)) { - msleep(10); - pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); - } - if (lnk_status & PCI_EXP_LNKSTA_DLLLA) - dev_warn(dev, "Link state not disabled for DPC event\n"); -} - -static void dpc_work(struct work_struct *work) -{ - struct dpc_dev *dpc = container_of(work, struct dpc_dev, work); - struct pci_dev *dev, *temp, *pdev = dpc->dev->port; - struct pci_bus *parent = pdev->subordinate; - u16 cap = dpc->cap_pos, ctl; - - pci_lock_rescan_remove(); - list_for_each_entry_safe_reverse(dev, temp, &parent->devices, - bus_list) { - pci_dev_get(dev); - pci_dev_set_disconnected(dev, NULL); - if (pci_has_subordinate(dev)) - pci_walk_bus(dev->subordinate, - pci_dev_set_disconnected, NULL); - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); - } - pci_unlock_rescan_remove(); - - dpc_wait_link_inactive(dpc); - if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc)) - return; - if (dpc->rp_extensions && dpc->rp_pio_status) { - pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, - dpc->rp_pio_status); - dpc->rp_pio_status = 0; - } - - pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, - PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT); - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); - pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, - ctl | PCI_EXP_DPC_CTL_INT_EN); -} - -static void dpc_process_rp_pio_error(struct dpc_dev *dpc) -{ - struct device *dev = &dpc->dev->device; - struct pci_dev *pdev = dpc->dev->port; - u16 cap = dpc->cap_pos, dpc_status, first_error; - u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix; - int i; - - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask); - dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n", - status, mask); - - dpc->rp_pio_status = status; - - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc); - dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n", - sev, syserr, exc); - - /* Get First Error Pointer */ - pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status); - first_error = (dpc_status & 0x1f00) >> 8; - - status &= ~mask; - for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) { - if (status & (1 << i)) - dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i], - first_error == i ? " (First)" : ""); - } - - if (dpc->rp_log_size < 4) - return; - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG, - &dw0); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4, - &dw1); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8, - &dw2); - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12, - &dw3); - dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n", - dw0, dw1, dw2, dw3); - - if (dpc->rp_log_size < 5) - return; - pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log); - dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log); - - for (i = 0; i < dpc->rp_log_size - 5; i++) { - pci_read_config_dword(pdev, - cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix); - dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix); - } -} - -static irqreturn_t dpc_irq(int irq, void *context) -{ - struct dpc_dev *dpc = (struct dpc_dev *)context; - struct pci_dev *pdev = dpc->dev->port; - struct device *dev = &dpc->dev->device; - u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason; - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl); - - if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0)) - return IRQ_NONE; - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status); - - if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT)) - return IRQ_NONE; - - if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) { - pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, - PCI_EXP_DPC_STATUS_INTERRUPT); - return IRQ_HANDLED; - } - - pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL, - ctl & ~PCI_EXP_DPC_CTL_INT_EN); - - pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID, - &source); - - dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n", - status, source); - - reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1; - ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5; - - dev_warn(dev, "DPC %s detected, remove downstream devices\n", - (reason == 0) ? "unmasked uncorrectable error" : - (reason == 1) ? "ERR_NONFATAL" : - (reason == 2) ? "ERR_FATAL" : - (ext_reason == 0) ? "RP PIO error" : - (ext_reason == 1) ? "software trigger" : - "reserved error"); - /* show RP PIO error detail information */ - if (dpc->rp_extensions && reason == 3 && ext_reason == 0) - dpc_process_rp_pio_error(dpc); - - schedule_work(&dpc->work); - - return IRQ_HANDLED; -} - -#define FLAG(x, y) (((x) & (y)) ? '+' : '-') -static int dpc_probe(struct pcie_device *dev) -{ - struct dpc_dev *dpc; - struct pci_dev *pdev = dev->port; - struct device *device = &dev->device; - int status; - u16 ctl, cap; - - if (pcie_aer_get_firmware_first(pdev)) - return -ENOTSUPP; - - dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL); - if (!dpc) - return -ENOMEM; - - dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC); - dpc->dev = dev; - INIT_WORK(&dpc->work, dpc_work); - set_service_data(dev, dpc); - - status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED, - "pcie-dpc", dpc); - if (status) { - dev_warn(device, "request IRQ%d failed: %d\n", dev->irq, - status); - return status; - } - - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap); - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); - - dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT); - if (dpc->rp_extensions) { - dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8; - if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) { - dev_err(device, "RP PIO log size %u is invalid\n", - dpc->rp_log_size); - dpc->rp_log_size = 0; - } - } - - ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN; - pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); - - dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n", - cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT), - FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP), - FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size, - FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE)); - return status; -} - -static void dpc_remove(struct pcie_device *dev) -{ - struct dpc_dev *dpc = get_service_data(dev); - struct pci_dev *pdev = dev->port; - u16 ctl; - - pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl); - ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN); - pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl); -} - -static struct pcie_port_service_driver dpcdriver = { - .name = "dpc", - .port_type = PCIE_ANY_PORT, - .service = PCIE_PORT_SERVICE_DPC, - .probe = dpc_probe, - .remove = dpc_remove, -}; - -static int __init dpc_service_init(void) -{ - return pcie_port_service_register(&dpcdriver); -} -device_initcall(dpc_service_init); -- cgit v1.2.1