]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
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 5d7a7e6f5724e1c719adeea2d629918fdc066d23..345365ea97c74c8c0cec0bfcaeb03e1cda817b3d 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 31685951a080456b8834aab2bf79a36c78f46639..aec4af5194b516c2e994e80b61ee233968125c88 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);