Commit 64ae499c authored by Bjorn Helgaas's avatar Bjorn Helgaas Committed by Bjorn Helgaas

Merge branch 'pci/portdrv'

  - move pcieport_if.h to drivers/pci/pcie/ to encapsulate it (Frederick
    Lawler)

  - merge pcieport_if.h into portdrv.h (Bjorn Helgaas)

  - move workaround for BIOS PME issue from portdrv to PCI core (Bjorn
    Helgaas)

  - completely disable portdrv with "pcie_ports=compat" (Bjorn Helgaas)

  - remove portdrv link order dependency (Bjorn Helgaas)

  - remove support for unused VC portdrv service (Bjorn Helgaas)

  - simplify portdrv feature permission checking (Bjorn Helgaas)

  - remove "pcie_hp=nomsi" parameter (use "pci=nomsi" instead) (Bjorn
    Helgaas)

  - remove unnecessary "pcie_ports=auto" parameter (Bjorn Helgaas)

  - use cached AER capability offset (Frederick Lawler)

  - don't enable DPC if BIOS hasn't granted AER control (Mika Westerberg)

  - rename pcie-dpc.c to dpc.c (Bjorn Helgaas)

* pci/portdrv:
  PCI/DPC: Rename from pcie-dpc.c to dpc.c
  PCI/DPC: Do not enable DPC if AER control is not allowed by the BIOS
  PCI/AER: Use cached AER Capability offset
  PCI/portdrv: Rename and reverse sense of pcie_ports_auto
  PCI/portdrv: Encapsulate pcie_ports_auto inside the port driver
  PCI/portdrv: Remove unnecessary "pcie_ports=auto" parameter
  PCI/portdrv: Remove "pcie_hp=nomsi" kernel parameter
  PCI/portdrv: Remove unnecessary include of <linux/pci-aspm.h>
  PCI/portdrv: Simplify PCIe feature permission checking
  PCI/portdrv: Remove unused PCIE_PORT_SERVICE_VC
  PCI/portdrv: Remove pcie_port_bus_type link order dependency
  PCI/portdrv: Disable port driver in compat mode
  PCI/PM: Clear PCIe PME Status bit for Root Complex Event Collectors
  PCI/PM: Clear PCIe PME Status bit in core, not PCIe port driver
  PCI/PM: Move pcie_clear_root_pme_status() to core
  PCI/portdrv: Merge pcieport_if.h into portdrv.h
  PCI/portdrv: Move pcieport_if.h to drivers/pci/pcie/

Conflicts:
	drivers/pci/pcie/Makefile
	drivers/pci/pcie/portdrv.h
parents ac30aa59 e02602bd
......@@ -3130,18 +3130,13 @@
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
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
......
......@@ -873,6 +873,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;
......@@ -897,9 +898,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;
......
......@@ -20,10 +20,11 @@
#include <linux/pci_hotplug.h>
#include <linux/delay.h>
#include <linux/sched/signal.h> /* signal_pending() */
#include <linux/pcieport_if.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include "../pcie/portdrv.h"
#define MY_NAME "pciehp"
extern bool pciehp_poll_mode;
......
......@@ -17,6 +17,7 @@
#include <linux/suspend.h>
#include <linux/kexec.h>
#include "pci.h"
#include "pcie/portdrv.h"
struct pci_dynid {
struct list_head node;
......@@ -712,6 +713,18 @@ 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 ||
pci_pcie_type(pci_dev) == PCI_EXP_TYPE_RC_EC))
pcie_clear_root_pme_status(pci_dev);
}
static int pci_pm_suspend(struct device *dev)
{
......@@ -871,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);
......@@ -1572,8 +1587,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);
......@@ -1684,6 +1684,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.
......
......@@ -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);
......
......@@ -2,13 +2,12 @@
#
# Makefile for PCI Express features and port driver
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
pcieportdrv-$(CONFIG_ACPI) += portdrv_acpi.o
pcieportdrv-y := portdrv_core.o portdrv_pci.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
obj-$(CONFIG_PCIEASPM) += aspm.o
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
......@@ -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;
......
......@@ -18,7 +18,6 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/pcieport_if.h>
#include <linux/slab.h>
#include "aerdrv.h"
......
......@@ -9,10 +9,11 @@
#define _AERDRV_H_
#include <linux/workqueue.h>
#include <linux/pcieport_if.h>
#include <linux/aer.h>
#include <linux/interrupt.h>
#include "../portdrv.h"
#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
PCI_EXP_RTCTL_SENFEE| \
PCI_EXP_RTCTL_SEFEE)
......
......@@ -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;
......
......@@ -10,7 +10,8 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pcieport_if.h>
#include "portdrv.h"
#include "../pci.h"
#include "aer/aerdrv.h"
......
......@@ -14,7 +14,6 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/pcieport_if.h>
#include <linux/pm_runtime.h>
#include "../pci.h"
......
......@@ -11,7 +11,66 @@
#include <linux/compiler.h>
#define PCIE_PORT_DEVICE_MAXSERVICES 5
extern bool pcie_ports_native;
/* 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_DPC_SHIFT 3 /* Downstream Port Containment */
#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
#define PCIE_PORT_DEVICE_MAXSERVICES 4
/* 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
......@@ -33,20 +92,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;
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;
......@@ -67,15 +112,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_ */
......@@ -10,7 +10,6 @@
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/pci-acpi.h>
#include <linux/pcieport_if.h>
#include "aer/aerdrv.h"
#include "../pci.h"
......@@ -48,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_VC | 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;
}
// 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 <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/pcieport_if.h>
#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);
}
......@@ -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,
&reg32);
......@@ -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 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
if (ret < 0)
return -ENODEV;
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
if (i != PCIE_PORT_SERVICE_VC_SHIFT)
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
irqs[i] = pci_irq_vector(dev, 0);
}
return 0;
}
......@@ -208,23 +195,13 @@ 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);
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;
}
......
......@@ -13,10 +13,8 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/init.h>
#include <linux/pcieport_if.h>
#include <linux/aer.h>
#include <linux/dmi.h>
#include <linux/pci-aspm.h>
#include "../pci.h"
#include "portdrv.h"
......@@ -25,22 +23,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, "auto", 4)) {
pcie_ports_disabled = false;
pcie_ports_auto = true;
}
else if (!strncmp(str, "native", 6))
pcie_ports_native = true;
return 1;
}
......@@ -48,15 +42,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;
......@@ -69,20 +54,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;
......@@ -110,7 +81,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,
......@@ -281,22 +251,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 pci_register_driver(&pcie_portdriver);
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);
......@@ -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);
......
......@@ -470,6 +470,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,
......@@ -1447,10 +1450,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
......
/* 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_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment