Merge branch 'pci/enumeration'

- Skip enabling ExtTag on VFs since that bit is Reserved and causes
  misleading log messages (Håkon Bugge)

- Mark 3ware-9650SA Root Port Extended Tags as broken since 9650SA can't
  handle 8-bit tags (Jörg Wedekind)

- Release domain number from the correct IDA when a PCI host bridge has no
  parent device (Sergey Shtylyov)

- Initialize endpoint Read Completion Boundary to match Root Port,
  regardless of ACPI _HPX (Håkon Bugge)

- Apply _HPX PCIe Setting Record only to AER configuration, and only when
  OS owns PCIe hotplug but not AER, to avoid clobbering Extended Tag and
  Relaxed Ordering settings (Håkon Bugge)

- Clear PCIe Root Status register with a write, not a read/modify/write
  (Lukas Wunner)

* pci/enumeration:
  PCI/PME: Replace RMW of Root Status register with direct write
  PCI/ACPI: Restrict program_hpx_type2() to AER bits
  PCI: Initialize RCB from pci_configure_device()
  PCI: Check parent for NULL in of_pci_bus_release_domain_nr()
  PCI: Mark 3ware-9650SA Root Port Extended Tags as broken
  PCI: Do not attempt to set ExtTag for VFs
This commit is contained in:
Bjorn Helgaas 2026-02-06 17:09:15 -06:00
commit 2304eeaf2f
6 changed files with 64 additions and 41 deletions

View File

@ -271,21 +271,6 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record,
return AE_OK;
}
static bool pcie_root_rcb_set(struct pci_dev *dev)
{
struct pci_dev *rp = pcie_find_root_port(dev);
u16 lnkctl;
if (!rp)
return false;
pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl);
if (lnkctl & PCI_EXP_LNKCTL_RCB)
return true;
return false;
}
/* _HPX PCI Express Setting Record (Type 2) */
struct hpx_type2 {
u32 revision;
@ -311,6 +296,7 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)
{
int pos;
u32 reg32;
const struct pci_host_bridge *host;
if (!hpx)
return;
@ -318,6 +304,15 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)
if (!pci_is_pcie(dev))
return;
host = pci_find_host_bridge(dev->bus);
/*
* Only do the _HPX Type 2 programming if OS owns PCIe native
* hotplug but not AER.
*/
if (!host->native_pcie_hotplug || host->native_aer)
return;
if (hpx->revision > 1) {
pci_warn(dev, "PCIe settings rev %d not supported\n",
hpx->revision);
@ -325,33 +320,27 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)
}
/*
* Don't allow _HPX to change MPS or MRRS settings. We manage
* those to make sure they're consistent with the rest of the
* platform.
* We only allow _HPX to program DEVCTL bits related to AER, namely
* PCI_EXP_DEVCTL_CERE, PCI_EXP_DEVCTL_NFERE, PCI_EXP_DEVCTL_FERE,
* and PCI_EXP_DEVCTL_URRE.
*
* The rest of DEVCTL is managed by the OS to make sure it's
* consistent with the rest of the platform.
*/
hpx->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD |
PCI_EXP_DEVCTL_READRQ;
hpx->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD |
PCI_EXP_DEVCTL_READRQ);
hpx->pci_exp_devctl_and |= ~PCI_EXP_AER_FLAGS;
hpx->pci_exp_devctl_or &= PCI_EXP_AER_FLAGS;
/* Initialize Device Control Register */
pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
~hpx->pci_exp_devctl_and, hpx->pci_exp_devctl_or);
/* Initialize Link Control Register */
/* Log if _HPX attempts to modify Link Control Register */
if (pcie_cap_has_lnkctl(dev)) {
/*
* If the Root Port supports Read Completion Boundary of
* 128, set RCB to 128. Otherwise, clear it.
*/
hpx->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB;
hpx->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB;
if (pcie_root_rcb_set(dev))
hpx->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB;
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
~hpx->pci_exp_lnkctl_and, hpx->pci_exp_lnkctl_or);
if (hpx->pci_exp_lnkctl_and != 0xffff ||
hpx->pci_exp_lnkctl_or != 0)
pci_info(dev, "_HPX attempts Link Control setting (AND %#06x OR %#06x)\n",
hpx->pci_exp_lnkctl_and,
hpx->pci_exp_lnkctl_or);
}
/* Find Advanced Error Reporting Enhanced Capability */

View File

@ -2256,7 +2256,7 @@ void pcie_clear_device_status(struct pci_dev *dev)
*/
void pcie_clear_root_pme_status(struct pci_dev *dev)
{
pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
pcie_capability_write_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
}
/**
@ -6591,7 +6591,7 @@ static void of_pci_bus_release_domain_nr(struct device *parent, int domain_nr)
return;
/* Release domain from IDA where it was allocated. */
if (of_get_pci_domain_nr(parent->of_node) == domain_nr)
if (parent && of_get_pci_domain_nr(parent->of_node) == domain_nr)
ida_free(&pci_domain_nr_static_ida, domain_nr);
else
ida_free(&pci_domain_nr_dynamic_ida, domain_nr);

View File

@ -88,6 +88,9 @@ struct pcie_tlp_log;
#define PCI_BUS_BRIDGE_MEM_WINDOW 1
#define PCI_BUS_BRIDGE_PREF_MEM_WINDOW 2
#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
extern const unsigned char pcie_link_speed[];
extern bool pci_early_dump;

View File

@ -239,9 +239,6 @@ void pcie_ecrc_get_policy(char *str)
}
#endif /* CONFIG_PCIE_ECRC */
#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
int pcie_aer_is_native(struct pci_dev *dev)
{
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);

View File

@ -2270,7 +2270,8 @@ int pci_configure_extended_tags(struct pci_dev *dev, void *ign)
u16 ctl;
int ret;
if (!pci_is_pcie(dev))
/* PCI_EXP_DEVCTL_EXT_TAG is RsvdP in VFs */
if (!pci_is_pcie(dev) || dev->is_virtfn)
return 0;
ret = pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
@ -2410,6 +2411,37 @@ static void pci_configure_serr(struct pci_dev *dev)
}
}
static void pci_configure_rcb(struct pci_dev *dev)
{
struct pci_dev *rp;
u16 rp_lnkctl;
/*
* Per PCIe r7.0, sec 7.5.3.7, RCB is only meaningful in Root Ports
* (where it is read-only), Endpoints, and Bridges. It may only be
* set for Endpoints and Bridges if it is set in the Root Port. For
* Endpoints, it is 'RsvdP' for Virtual Functions.
*/
if (!pci_is_pcie(dev) ||
pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM ||
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC ||
dev->is_virtfn)
return;
/* Root Port often not visible to virtualized guests */
rp = pcie_find_root_port(dev);
if (!rp)
return;
pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &rp_lnkctl);
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
PCI_EXP_LNKCTL_RCB,
(rp_lnkctl & PCI_EXP_LNKCTL_RCB) ?
PCI_EXP_LNKCTL_RCB : 0);
}
static void pci_configure_device(struct pci_dev *dev)
{
pci_configure_mps(dev);
@ -2419,6 +2451,7 @@ static void pci_configure_device(struct pci_dev *dev)
pci_configure_aspm_l1ss(dev);
pci_configure_eetlp_prefix(dev);
pci_configure_serr(dev);
pci_configure_rcb(dev);
pci_acpi_program_hp_params(dev);
}

View File

@ -5591,6 +5591,7 @@ static void quirk_no_ext_tags(struct pci_dev *pdev)
pci_walk_bus(bridge->bus, pci_configure_extended_tags, NULL);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_3WARE, 0x1004, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_3WARE, 0x1005, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0132, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0140, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0141, quirk_no_ext_tags);