PCI: dwc: Add new APIs to remove standard and extended Capability

On some platforms, certain PCIe Capabilities may be present in hardware
but are not fully implemented as defined in PCIe spec. These incomplete
capabilities should be hidden from the PCI framework to prevent unexpected
behavior.

Introduce two APIs to remove a specific PCIe Capability and Extended
Capability by updating the previous capability's next offset field to skip
over the unwanted capability. These APIs allow RC drivers to easily hide
unsupported or partially implemented capabilities from software.

Co-developed-by: Wenbin Yao <wenbin.yao@oss.qualcomm.com>
Signed-off-by: Wenbin Yao <wenbin.yao@oss.qualcomm.com>
Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Link: https://patch.msgid.link/20251109-remove_cap-v1-2-2208f46f4dc2@oss.qualcomm.com
This commit is contained in:
Qiang Yu 2025-11-09 22:59:41 -08:00 committed by Manivannan Sadhasivam
parent a2582e05e3
commit 0183562f1e
2 changed files with 55 additions and 0 deletions

View File

@ -236,6 +236,59 @@ u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap)
}
EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability);
void dw_pcie_remove_capability(struct dw_pcie *pci, u8 cap)
{
u8 cap_pos, pre_pos, next_pos;
u16 reg;
cap_pos = PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap,
&pre_pos, pci);
if (!cap_pos)
return;
reg = dw_pcie_readw_dbi(pci, cap_pos);
next_pos = (reg & 0xff00) >> 8;
dw_pcie_dbi_ro_wr_en(pci);
if (pre_pos == PCI_CAPABILITY_LIST)
dw_pcie_writeb_dbi(pci, PCI_CAPABILITY_LIST, next_pos);
else
dw_pcie_writeb_dbi(pci, pre_pos + 1, next_pos);
dw_pcie_dbi_ro_wr_dis(pci);
}
EXPORT_SYMBOL_GPL(dw_pcie_remove_capability);
void dw_pcie_remove_ext_capability(struct dw_pcie *pci, u8 cap)
{
int cap_pos, next_pos, pre_pos;
u32 pre_header, header;
cap_pos = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, &pre_pos, pci);
if (!cap_pos)
return;
header = dw_pcie_readl_dbi(pci, cap_pos);
/*
* If the first cap at offset PCI_CFG_SPACE_SIZE is removed,
* only set it's capid to zero as it cannot be skipped.
*/
if (cap_pos == PCI_CFG_SPACE_SIZE) {
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi(pci, cap_pos, header & 0xffff0000);
dw_pcie_dbi_ro_wr_dis(pci);
return;
}
pre_header = dw_pcie_readl_dbi(pci, pre_pos);
next_pos = PCI_EXT_CAP_NEXT(header);
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi(pci, pre_pos,
(pre_header & 0xfffff) | (next_pos << 20));
dw_pcie_dbi_ro_wr_dis(pci);
}
EXPORT_SYMBOL_GPL(dw_pcie_remove_ext_capability);
static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id,
u16 vsec_id)
{

View File

@ -562,6 +562,8 @@ void dw_pcie_version_detect(struct dw_pcie *pci);
u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap);
u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap);
void dw_pcie_remove_capability(struct dw_pcie *pci, u8 cap);
void dw_pcie_remove_ext_capability(struct dw_pcie *pci, u8 cap);
u16 dw_pcie_find_rasdes_capability(struct dw_pcie *pci);
u16 dw_pcie_find_ptm_capability(struct dw_pcie *pci);