]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: dwc: ep: Mirror the max link width and speed fields to all functions
authorAksh Garg <a-garg7@ti.com>
Tue, 24 Feb 2026 08:38:17 +0000 (14:08 +0530)
committerManivannan Sadhasivam <mani@kernel.org>
Mon, 9 Mar 2026 06:30:11 +0000 (12:00 +0530)
PCIe r7.0, section 7.5.3.6 states that for multi-function devices, the
Max Link Width and Max Link Speed fields in the Link Capabilities
Register must report the same values for all functions.

Currently, dw_pcie_setup() programs these fields only for Function 0
via dw_pcie_link_set_max_speed() and dw_pcie_link_set_max_link_width().
For multi-function endpoint configurations, Function 1 and beyond retain
their default values, violating the PCIe specification.

Fix this by reading the Max Link Width and Max Link Speed fields from
Link Capabilities Register of Function 0 after dw_pcie_setup() completes,
then mirroring these values to all other functions.

Fixes: 24ede430fa49 ("PCI: designware-ep: Add multiple PFs support for DWC")
Fixes: 89db0793c9f2 ("PCI: dwc: Add missing PCI_EXP_LNKCAP_MLW handling")
Signed-off-by: Aksh Garg <a-garg7@ti.com>
[mani: renamed ref_lnkcap to func0_lnkcap]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Link: https://patch.msgid.link/20260224083817.916782-3-a-garg7@ti.com
drivers/pci/controller/dwc/pcie-designware-ep.c

index 709ab60c9e39823be1b492731fdaae28028cf25f..21b6f4eb24cda865941dc1c2e0043bdcf3b8fd2d 100644 (file)
@@ -1103,7 +1103,8 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
 {
        struct dw_pcie_ep *ep = &pci->ep;
        u8 funcs = ep->epc->max_functions;
-       u8 func_no;
+       u32 func0_lnkcap, lnkcap;
+       u8 func_no, offset;
 
        dw_pcie_dbi_ro_wr_en(pci);
 
@@ -1111,6 +1112,32 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
                dw_pcie_ep_init_rebar_registers(ep, func_no);
 
        dw_pcie_setup(pci);
+
+       /*
+        * PCIe r7.0, section 7.5.3.6 states that for multi-function
+        * endpoints, max link width and speed fields must report same
+        * values for all functions. However, dw_pcie_setup() programs
+        * these fields only for function 0. Hence, mirror these fields
+        * to all other functions as well.
+        */
+       if (funcs > 1) {
+               offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+               func0_lnkcap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
+               func0_lnkcap = FIELD_GET(PCI_EXP_LNKCAP_MLW |
+                                        PCI_EXP_LNKCAP_SLS, func0_lnkcap);
+
+               for (func_no = 1; func_no < funcs; func_no++) {
+                       offset = dw_pcie_ep_find_capability(ep, func_no,
+                                                           PCI_CAP_ID_EXP);
+                       lnkcap = dw_pcie_ep_readl_dbi(ep, func_no,
+                                                     offset + PCI_EXP_LNKCAP);
+                       FIELD_MODIFY(PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS,
+                                    &lnkcap, func0_lnkcap);
+                       dw_pcie_ep_writel_dbi(ep, func_no,
+                                             offset + PCI_EXP_LNKCAP, lnkcap);
+               }
+       }
+
        dw_pcie_dbi_ro_wr_dis(pci);
 }