Merge branch 'pci/portdrv'

- Assume control of portdrv-related features only when portdrv is enabled,
  since we can't use them without portdrv (Kuppuswamy Sathyanarayanan)

- Tidy _OSC control bit checking (Kuppuswamy Sathyanarayanan)

- Centralize feature ownership checking so code only needs to check
  'host->native_X' and doesn't need to also check 'pcie_ports_native'
  (Kuppuswamy Sathyanarayanan)

* pci/portdrv:
  PCI/ACPI: Centralize pcie_ports_native checking
  PCI/ACPI: Tidy _OSC control bit checking
  PCI: Assume control of portdrv-related features only when portdrv enabled
This commit is contained in:
Bjorn Helgaas 2026-10-21 13:20:26 -05:00
commit 3b5bba12f5
10 changed files with 79 additions and 32 deletions

View File

@ -993,6 +993,25 @@ static void acpi_pci_root_release_info(struct pci_host_bridge *bridge)
__acpi_pci_root_release_info(bridge->release_data);
}
#define OSC_OWNER(ctrl, bit, flag) \
do { \
if (!((ctrl) & (bit))) \
flag = 0; \
} while (0)
#define FLAG(x) ((x) ? '+' : '-')
/*
* _OSC control bits for the features implemented by the PCIe port driver,
* i.e., the ones "pcie_ports=native" applies to. LTR and SHPC hotplug are
* negotiated via _OSC as well, but they are not portdrv services, so
* "pcie_ports=" has no bearing on them.
*/
#define OSC_PCIE_PORT_SERVICE_CONTROLS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \
OSC_PCI_EXPRESS_PME_CONTROL | \
OSC_PCI_EXPRESS_AER_CONTROL | \
OSC_PCI_EXPRESS_DPC_CONTROL)
struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
struct acpi_pci_root_ops *ops,
struct acpi_pci_root_info *info,
@ -1003,6 +1022,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
int node = acpi_get_node(device->handle);
struct pci_bus *bus;
struct pci_host_bridge *host_bridge;
u32 ctrl, ext_ctrl, override = 0;
info->root = root;
info->bridge = device;
@ -1028,21 +1048,49 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
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_pcie_hotplug = 0;
if (!(root->osc_control_set & OSC_PCI_SHPC_NATIVE_HP_CONTROL))
host_bridge->native_shpc_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;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_LTR_CONTROL))
host_bridge->native_ltr = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_DPC_CONTROL))
host_bridge->native_dpc = 0;
if (!(root->osc_ext_control_set & OSC_CXL_ERROR_REPORTING_CONTROL))
host_bridge->native_cxl_error = 0;
ctrl = root->osc_control_set;
ext_ctrl = root->osc_ext_control_set;
/*
* If the user specified "pcie_ports=native", use the PCIe port
* services regardless of what _OSC says, i.e., proceed as though the
* platform had granted us control of them. "pcie_ports=dpc-native"
* does the same for DPC alone. This may conflict with firmware that
* expects to own those features.
*/
if (pcie_ports_native)
override = OSC_PCIE_PORT_SERVICE_CONTROLS & ~ctrl;
else if (pcie_ports_dpc_native)
override = OSC_PCI_EXPRESS_DPC_CONTROL & ~ctrl;
if (override) {
decode_osc_control(root, pcie_ports_native ?
"OS forcing control (\"pcie_ports=native\") of" :
"OS forcing control (\"pcie_ports=dpc-native\") of",
override);
ctrl |= override;
}
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_NATIVE_HP_CONTROL,
host_bridge->native_pcie_hotplug);
OSC_OWNER(ctrl, OSC_PCI_SHPC_NATIVE_HP_CONTROL,
host_bridge->native_shpc_hotplug);
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_AER_CONTROL, host_bridge->native_aer);
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_PME_CONTROL, host_bridge->native_pme);
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_LTR_CONTROL, host_bridge->native_ltr);
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_DPC_CONTROL, host_bridge->native_dpc);
OSC_OWNER(ext_ctrl, OSC_CXL_ERROR_REPORTING_CONTROL,
host_bridge->native_cxl_error);
dev_info(&root->device->dev, "OS native features: SHPCHotplug%c PCIeHotplug%c PME%c AER%c DPC%c LTR%c\n",
FLAG(host_bridge->native_shpc_hotplug),
FLAG(host_bridge->native_pcie_hotplug),
FLAG(host_bridge->native_pme),
FLAG(host_bridge->native_aer),
FLAG(host_bridge->native_dpc),
FLAG(host_bridge->native_ltr));
acpi_dev_power_up_children_with_adr(device);

View File

@ -258,7 +258,7 @@ static bool pme_is_native(struct pcie_device *dev)
const struct pci_host_bridge *host;
host = pci_find_host_bridge(dev->port->bus);
return pcie_ports_native || host->native_pme;
return host->native_pme;
}
static void pciehp_disable_interrupt(struct pcie_device *dev)

View File

@ -812,9 +812,6 @@ bool pciehp_is_native(struct pci_dev *bridge)
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
return false;
if (pcie_ports_native)
return true;
host = pci_find_host_bridge(bridge->bus);
return host->native_pcie_hotplug;
}

View File

@ -260,7 +260,7 @@ int pcie_aer_is_native(struct pci_dev *dev)
if (!dev->aer_cap)
return 0;
return pcie_ports_native || host->native_aer;
return host->native_aer;
}
EXPORT_SYMBOL_NS_GPL(pcie_aer_is_native, "CXL");
@ -1869,7 +1869,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
*/
aer = root ? root->aer_cap : 0;
if ((host->native_aer || pcie_ports_native) && aer)
if (host->native_aer && aer)
aer_disable_irq(root);
if (type == PCI_EXP_TYPE_RC_EC || type == PCI_EXP_TYPE_RC_END) {
@ -1884,7 +1884,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
pci_is_root_bus(dev->bus) ? "Root" : "Downstream", rc);
}
if ((host->native_aer || pcie_ports_native) && aer) {
if (host->native_aer && aer) {
/* Clear Root Error Status */
pci_read_config_dword(root, aer + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(root, aer + PCI_ERR_ROOT_STATUS, reg32);

View File

@ -31,7 +31,7 @@ static bool cxl_error_is_native(struct pci_dev *dev)
{
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
return (pcie_ports_native || host->native_aer);
return host->native_aer;
}
static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)

View File

@ -273,7 +273,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
* it is responsible for clearing this status. In that case, the
* signaling device may not even be visible to the OS.
*/
if (host->native_aer || pcie_ports_native) {
if (host->native_aer) {
pcie_clear_device_status(dev);
pci_aer_clear_nonfatal_status(dev);
}

View File

@ -223,7 +223,7 @@ static int get_port_device_capability(struct pci_dev *dev)
if (dev->is_pciehp &&
(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) &&
(pcie_ports_native || host->native_pcie_hotplug)) {
host->native_pcie_hotplug) {
services |= PCIE_PORT_SERVICE_HP;
/*
@ -240,14 +240,14 @@ static int get_port_device_capability(struct pci_dev *dev)
if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
dev->aer_cap && pci_aer_available() &&
(pcie_ports_native || host->native_aer))
host->native_aer)
services |= PCIE_PORT_SERVICE_AER;
#endif
/* Root Ports and Root Complex Event Collectors may generate PMEs */
if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
(pcie_ports_native || host->native_pme)) {
host->native_pme) {
services |= PCIE_PORT_SERVICE_PME;
/*

View File

@ -25,8 +25,6 @@
#define PCIE_PORT_DEVICE_MAXSERVICES 5
extern bool pcie_ports_dpc_native;
#ifdef CONFIG_PCIEAER
int pcie_aer_init(void);
#else

View File

@ -658,6 +658,8 @@ static const struct device_type pci_host_bridge_type = {
static void pci_init_host_bridge(struct pci_host_bridge *bridge)
{
bool port_services = IS_ENABLED(CONFIG_PCIEPORTBUS);
INIT_LIST_HEAD(&bridge->windows);
INIT_LIST_HEAD(&bridge->dma_ranges);
INIT_LIST_HEAD(&bridge->ports);
@ -668,12 +670,12 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
* may implement its own AER handling and use _OSC to prevent the
* OS from interfering.
*/
bridge->native_aer = 1;
bridge->native_pcie_hotplug = 1;
bridge->native_aer = port_services;
bridge->native_pcie_hotplug = port_services;
bridge->native_shpc_hotplug = 1;
bridge->native_pme = 1;
bridge->native_pme = port_services;
bridge->native_ltr = 1;
bridge->native_dpc = 1;
bridge->native_dpc = port_services;
bridge->domain_nr = PCI_DOMAIN_NR_NOT_SET;
bridge->native_cxl_error = 1;
bridge->dev.type = &pci_host_bridge_type;

View File

@ -1915,12 +1915,14 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d,
#ifdef CONFIG_PCIEPORTBUS
extern bool pcie_ports_disabled;
extern bool pcie_ports_native;
extern bool pcie_ports_dpc_native;
int pcie_set_target_speed(struct pci_dev *port, enum pci_bus_speed speed_req,
bool use_lt);
#else
#define pcie_ports_disabled true
#define pcie_ports_native false
#define pcie_ports_dpc_native false
static inline int pcie_set_target_speed(struct pci_dev *port,
enum pci_bus_speed speed_req,