On Sat, Oct 3, 2026 at 5:31 AM Dave Klotz <[email protected]> wrote:
>
> The driver writes Gen1 into the Supported Link Speeds field of the root
> port's Link Capabilities and into the Target Link Speed of Link
> Control 2, so the K1's 5 GT/s capable ports never train above 2.5 GT/s,
> and an operating system that keeps the root port as found cannot
> retrain them faster either. It also writes them at the fixed offsets
> 0x7c and 0xa0 instead of through the PCI Express capability.
>
> Linux's DesignWare core leaves both fields alone unless the device tree
> limits the speed with "max-link-speed", and then writes that value to
> both. Do the same, finding the registers through the capability list,
> and drop the driver's copies of the speed definitions that
> pcie_dw_common.h already provides.
>
> Signed-off-by: Dave Klotz <[email protected]>
> ---
>  drivers/pci/pcie_dw_spacemit.c | 48 ++++++++++++++++++++++++----------
>  drivers/pci/pcie_dw_spacemit.h |  7 -----
>  2 files changed, 34 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/pci/pcie_dw_spacemit.c b/drivers/pci/pcie_dw_spacemit.c
> index 3394ddbd18..6ac1d6bea4 100644
> --- a/drivers/pci/pcie_dw_spacemit.c
> +++ b/drivers/pci/pcie_dw_spacemit.c
> @@ -40,6 +40,7 @@ struct pcie_dw_spacemit {
>         struct clk_bulk         clks;
>         struct reset_ctl_bulk   rsts;
>         struct udevice          *vpcie3v3;      /* slot supply, if described 
> */
> +       u32                     max_link_speed; /* 0: hardware default */
>  };
>
>  static inline u32 spacemit_pcie_readl(struct pcie_dw_spacemit *pcie, u32 
> offset)
> @@ -58,21 +59,33 @@ static inline u32 spacemit_pcie_phy_ahb_readl(struct 
> pcie_dw_spacemit *pcie, u32
>         return readl(pcie->phy_ahb + offset);
>  }
>
> -static void pcie_dw_configure(struct pcie_dw_spacemit *pci, u32 cap_speed)
> +static void pcie_dw_configure(struct pcie_dw_spacemit *pci)
>  {
> -       u32 val;
> +       void __iomem *cap;
> +       u32 speed = pci->max_link_speed;
> +       u16 lnkctl2;
> +       u32 lnkcap;
>
> -       dw_pcie_dbi_write_enable(&pci->dw, true);
> +       cap = pci->dw.dbi_base + pcie_dw_find_capability(&pci->dw,
> +                                                        PCI_CAP_ID_EXP);
>
> -       val = readl(pci->dw.dbi_base + PCIE_LINK_CAPABILITY);
> -       val &= ~TARGET_LINK_SPEED_MASK;
> -       val |= cap_speed;
> -       writel(val, pci->dw.dbi_base + PCIE_LINK_CAPABILITY);
> +       dw_pcie_dbi_write_enable(&pci->dw, true);
>
> -       val = readl(pci->dw.dbi_base + PCIE_LINK_CTL_2);
> -       val &= ~TARGET_LINK_SPEED_MASK;
> -       val |= cap_speed;
> -       writel(val, pci->dw.dbi_base + PCIE_LINK_CTL_2);
> +       /*
> +        * Keep the hardware's supported speeds unless the device tree limits
> +        * them with "max-link-speed", as Linux's DesignWare core does.
> +        */
> +       if (speed) {
> +               lnkcap = readl(cap + PCI_EXP_LNKCAP);
> +               lnkcap &= ~PCI_EXP_LNKCAP_SLS;
> +               lnkcap |= speed;
> +               writel(lnkcap, cap + PCI_EXP_LNKCAP);
> +
> +               lnkctl2 = readw(cap + PCI_EXP_LNKCTL2);
> +               lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
> +               lnkctl2 |= speed;
> +               writew(lnkctl2, cap + PCI_EXP_LNKCTL2);
> +       }
>
>         dw_pcie_dbi_write_enable(&pci->dw, false);
>  }
> @@ -105,12 +118,12 @@ static int wait_link_up(struct pcie_dw_spacemit *pci)
>         return 1;
>  }
>
> -static int pcie_dw_spacemit_pcie_link_up(struct pcie_dw_spacemit *pci, u32 
> cap_speed)
> +static int pcie_dw_spacemit_pcie_link_up(struct pcie_dw_spacemit *pci)
>  {
>         u32 reg;
>
>         /* DW pre link configurations */
> -       pcie_dw_configure(pci, cap_speed);
> +       pcie_dw_configure(pci);
>
>         /* Initiate link training */
>         reg = spacemit_pcie_readl(pci, PCIECTRL_K1X_CONF_DEVICE_CMD);
> @@ -315,7 +328,7 @@ static int pcie_dw_spacemit_probe(struct udevice *dev)
>         pcie_dw_setup_host(&pci->dw);
>         pcie_dw_init_id(pci);
>
> -       if (!pcie_dw_spacemit_pcie_link_up(pci, LINK_SPEED_GEN_1)) {
> +       if (!pcie_dw_spacemit_pcie_link_up(pci)) {
>                 printf("PCIE-%d: Link down\n", dev_seq(dev));
>                 return -ENODEV;
>         }
> @@ -380,6 +393,13 @@ static int pcie_dw_spacemit_of_to_plat(struct udevice 
> *dev)
>                 return -EINVAL;
>         pcie->apmu_offset = args.args[0];
>
> +       pcie->max_link_speed = dev_read_u32_default(dev, "max-link-speed", 0);
> +       if (pcie->max_link_speed > LINK_SPEED_GEN_4) {
> +               dev_warn(dev, "invalid max-link-speed %u, ignored\n",
> +                        pcie->max_link_speed);
> +               pcie->max_link_speed = 0;
> +       }
> +
>         /* Derive port ID from DBI base address */
>         pcie->port_id = (dbi_addr - SPACEMIT_PCIE_DBI_BASE) / 
> SPACEMIT_PCIE_DBI_STRIDE;
>
> diff --git a/drivers/pci/pcie_dw_spacemit.h b/drivers/pci/pcie_dw_spacemit.h
> index 6f198f7d58..21686de246 100644
> --- a/drivers/pci/pcie_dw_spacemit.h
> +++ b/drivers/pci/pcie_dw_spacemit.h
> @@ -12,13 +12,6 @@
>  #include <linux/bitops.h>
>
>  /* PCIe DW common registers */
> -#define PCIE_LINK_CAPABILITY           0x7c
> -#define PCIE_LINK_CTL_2                        0xa0
> -#define TARGET_LINK_SPEED_MASK         0xf
> -#define LINK_SPEED_GEN_1               0x1
> -#define LINK_SPEED_GEN_2               0x2
> -#define LINK_SPEED_GEN_3               0x3
> -
>  #define PCIE_MISC_CONTROL_1_OFF        0x8bc
>  #define PCIE_DBI_RO_WR_EN              BIT(0)
>
> --
> 2.55.0
>
>
Reviewed-by: Eric Chung <[email protected]>

Reply via email to