Re: [PATCH] PCI: apple: Configure link speeds properly

From: Rob Herring
Date: Tue Nov 23 2021 - 21:23:25 EST


On Mon, Nov 22, 2021 at 4:13 AM Hector Martin <marcan@xxxxxxxxx> wrote:
>
> This sets the maximum link speed from the devicetree, and also requests
> a link speed change from the controller. Without the request, the link
> always comes up at Gen1 initially, and the core PCIe code complains
> about a bandwidth bottleneck.
>
> It turns out ASPM ends up retraining at a higher speed anyway even
> without this code, but let's not rely on that.
>
> Signed-off-by: Hector Martin <marcan@xxxxxxxxx>
> ---
> drivers/pci/controller/pcie-apple.c | 53 +++++++++++++++++++++++++++++
> 1 file changed, 53 insertions(+)
>
> diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
> index 03bfe977c579..073cbac49d8b 100644
> --- a/drivers/pci/controller/pcie-apple.c
> +++ b/drivers/pci/controller/pcie-apple.c
> @@ -30,6 +30,10 @@
> #include <linux/of_irq.h>
> #include <linux/pci-ecam.h>
>
> +#include "../pci.h"
> +/* Apple PCIe is based on DesignWare IP and shares some registers */
> +#include "dwc/pcie-designware.h"

I'm starting to regret this not being part of the DWC driver...

> +
> #define CORE_RC_PHYIF_CTL 0x00024
> #define CORE_RC_PHYIF_CTL_RUN BIT(0)
> #define CORE_RC_PHYIF_STAT 0x00028
> @@ -130,9 +134,13 @@
> */
> #define DOORBELL_ADDR CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR
>
> +/* The offset of the PCIe capabilities structure in bridge config space */
> +#define PCIE_CAP_BASE 0x70

This offset is discoverable, so don't hardcode it.

> +
> struct apple_pcie {
> struct mutex lock;
> struct device *dev;
> + struct pci_config_window *cfg;
> void __iomem *base;
> struct irq_domain *domain;
> unsigned long *bitmap;
> @@ -506,6 +514,48 @@ static u32 apple_pcie_rid2sid_write(struct apple_pcie_port *port,
> return readl_relaxed(port->base + PORT_RID2SID(idx));
> }
>
> +static inline void __iomem *bridge_reg(struct apple_pcie_port *port,
> + int where)
> +{
> + struct pci_config_window *cfg = port->pcie->cfg;
> +
> + return cfg->win + PCIE_ECAM_OFFSET(0, PCI_DEVFN(port->idx, 0), where);
> +}
> +
> +static void apple_pcie_unlock_dwc_regs(struct apple_pcie_port *port)
> +{
> + rmw_set(PCIE_DBI_RO_WR_EN, bridge_reg(port, PCIE_MISC_CONTROL_1_OFF));
> +}
> +
> +static void apple_pcie_lock_dwc_regs(struct apple_pcie_port *port)
> +{
> + rmw_clear(PCIE_DBI_RO_WR_EN, bridge_reg(port, PCIE_MISC_CONTROL_1_OFF));
> +}
> +
> +static int apple_pcie_link_configure_max_speed(struct apple_pcie_port *port)
> +{
> + int max_gen;
> + u32 ctrl2;
> +
> + max_gen = of_pci_get_max_link_speed(port->np);
> + if (max_gen < 0) {
> + dev_err(port->pcie->dev, "max link speed not specified\n");

Better to fail than limp along in gen1? Though you don't check the
return value...

Usually, the DT property is there to limit the speed when there's a
board limitation.

> + return max_gen;
> + }
> +
> + ctrl2 = readw_relaxed(bridge_reg(port, PCIE_CAP_BASE + PCI_EXP_LNKCTL2));
> + ctrl2 &= ~PCI_EXP_LNKCTL2_TLS;
> + ctrl2 |= FIELD_PREP(PCI_EXP_LNKCTL2_TLS, max_gen);
> + writew_relaxed(ctrl2, bridge_reg(port, PCIE_CAP_BASE + PCI_EXP_LNKCTL2));
> +
> + apple_pcie_unlock_dwc_regs(port);
> + rmw_set(PORT_LOGIC_SPEED_CHANGE,
> + bridge_reg(port, PCIE_LINK_WIDTH_SPEED_CONTROL));
> + apple_pcie_lock_dwc_regs(port);
> +
> + return 0;
> +}
> +
> static int apple_pcie_setup_port(struct apple_pcie *pcie,
> struct device_node *np)
> {
> @@ -577,6 +627,8 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
> ret = apple_pcie_port_register_irqs(port);
> WARN_ON(ret);
>
> + apple_pcie_link_configure_max_speed(port);
> +
> writel_relaxed(PORT_LTSSMCTL_START, port->base + PORT_LTSSMCTL);
>
> if (!wait_for_completion_timeout(&pcie->event, HZ / 10))
> @@ -762,6 +814,7 @@ static int apple_pcie_init(struct pci_config_window *cfg)
> return -ENOMEM;
>
> pcie->dev = dev;
> + pcie->cfg = cfg;
>
> mutex_init(&pcie->lock);
>
> --
> 2.33.0
>