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]>