Commit 0183562f authored by Qiang Yu's avatar Qiang Yu Committed by Manivannan Sadhasivam
Browse files

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: default avatarWenbin Yao <wenbin.yao@oss.qualcomm.com>
Signed-off-by: default avatarWenbin Yao <wenbin.yao@oss.qualcomm.com>
Signed-off-by: default avatarQiang Yu <qiang.yu@oss.qualcomm.com>
Signed-off-by: default avatarManivannan Sadhasivam <mani@kernel.org>
Link: https://patch.msgid.link/20251109-remove_cap-v1-2-2208f46f4dc2@oss.qualcomm.com
parent a2582e05
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -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)
{
+2 −0
Original line number Diff line number Diff line
@@ -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);