PCI: dwc: Add new APIs to remove standard and extended Capability
authorQiang Yu <qiang.yu@oss.qualcomm.com>
Mon, 10 Nov 2025 06:59:41 +0000 (22:59 -0800)
committerManivannan Sadhasivam <mani@kernel.org>
Thu, 18 Dec 2025 07:19:30 +0000 (12:49 +0530)
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
drivers/pci/controller/dwc/pcie-designware.c
drivers/pci/controller/dwc/pcie-designware.h

index 5d7a7e6..345365e 100644 (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)
 {
index 3168595..aec4af5 100644 (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);