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
This commit is contained in:
commit
64ae499cf2
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,56 +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 <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,
|
||||
®32);
|
||||
|
@ -76,6 +67,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
|
|||
nvec = max(nvec, *aer + 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mask & PCIE_PORT_SERVICE_DPC) {
|
||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
|
||||
|
@ -168,16 +160,13 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
|
|||
irqs[i] = -1;
|
||||
|
||||
/*
|
||||
* If we support PME or hotplug, but we can't use MSI/MSI-X for
|
||||
* them, we have to fall back to INTx or other interrupts, e.g., a
|
||||
* system shared interrupt.
|
||||
* If we support PME but can't use MSI/MSI-X for it, we have to
|
||||
* fall back to INTx or other interrupts, e.g., a system shared
|
||||
* interrupt.
|
||||
*/
|
||||
if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi())
|
||||
goto legacy_irq;
|
||||
|
||||
if ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())
|
||||
goto legacy_irq;
|
||||
|
||||
/* Try to use MSI-X or MSI if supported */
|
||||
if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0)
|
||||
return 0;
|
||||
|
@ -188,10 +177,8 @@ 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)
|
||||
irqs[i] = pci_irq_vector(dev, 0);
|
||||
}
|
||||
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
|
||||
irqs[i] = pci_irq_vector(dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -208,23 +195,13 @@ 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
|
||||
|
|
|
@ -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_ */
|
Loading…
Reference in New Issue