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
