]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: brcmstb: Add BCM2712 support
authorStanimir Varbanov <svarbanov@suse.de>
Mon, 24 Feb 2025 08:35:59 +0000 (10:35 +0200)
committerKrzysztof Wilczyński <kwilczynski@kernel.org>
Tue, 4 Mar 2025 15:54:08 +0000 (15:54 +0000)
Add a bare minimum amount of changes in order to support PCIe Root
Complex hardware IP found on RPi5. The PCIe controller on BCM2712
is based on BCM7712 and as such it inherits register offsets, PERST#
assertion, bridge_reset ops, and inbound windows count.

Although, the implementation for BCM2712 needs a workaround related to
the control of the bridge_reset where turning off of the Root Port must
not shutdown the bridge_reset and this must be avoided. To implement
this workaround a quirks field is introduced in pcie_cfg_data struct.

The controller also needs adjustment of PHY PLL setup to use a 54MHz
input refclk. The default input reference clock for the PHY PLL is
100Mhz, except for some devices where it is 54Mhz like BCM2712C1 and
BCM2712D0.

To implement those adjustments introduce a new .post_setup op in
pcie_cfg_data and call it at the end of brcm_pcie_setup function.

The BCM2712 .post_setup callback implements the required MDIO writes
that switch the PLL refclk and also change PHY PM clock period.

Without this RPi5 PCIex1 is unable to enumerate endpoint devices on
the expansion connector.

Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Reviewed-by: Jim Quinlan <james.quinlan@broadcom.com>
Tested-by: Ivan T. Ivanov <iivanov@suse.de>
Link: https://lore.kernel.org/r/20250224083559.47645-8-svarbanov@suse.de
[commit log]
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
drivers/pci/controller/pcie-brcmstb.c

index 9c45272ffcf089cecef0d85df185b5795ea812f7..d171ee61eab38faed87042c599c59af6250b723f 100644 (file)
 #define PCIE_RC_DL_MDIO_WR_DATA                                0x1104
 #define PCIE_RC_DL_MDIO_RD_DATA                                0x1108
 
+#define PCIE_RC_PL_PHY_CTL_15                          0x184c
+#define  PCIE_RC_PL_PHY_CTL_15_DIS_PLL_PD_MASK         0x400000
+#define  PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK      0xff
+
 #define PCIE_MISC_MISC_CTRL                            0x4008
 #define  PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK    0x80
 #define  PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK    0x400
@@ -234,13 +238,24 @@ struct inbound_win {
        u64 cpu_addr;
 };
 
+/*
+ * The RESCAL block is tied to PCIe controller #1, regardless of the number of
+ * controllers, and turning off PCIe controller #1 prevents access to the RESCAL
+ * register blocks, therefore no other controller can access this register
+ * space, and depending upon the bus fabric we may get a timeout (UBUS/GISB),
+ * or a hang (AXI).
+ */
+#define CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN                BIT(0)
+
 struct pcie_cfg_data {
        const int *offsets;
        const enum pcie_soc_base soc_base;
        const bool has_phy;
+       const u32 quirks;
        u8 num_inbound_wins;
        int (*perst_set)(struct brcm_pcie *pcie, u32 val);
        int (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
+       int (*post_setup)(struct brcm_pcie *pcie);
 };
 
 struct subdev_regulators {
@@ -816,6 +831,38 @@ static int brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
        return 0;
 }
 
+static int brcm_pcie_post_setup_bcm2712(struct brcm_pcie *pcie)
+{
+       const u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 };
+       const u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e };
+       int ret, i;
+       u32 tmp;
+
+       /* Allow a 54MHz (xosc) refclk source */
+       ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, 0x1600);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(regs); i++) {
+               ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]);
+               if (ret < 0)
+                       return ret;
+       }
+
+       usleep_range(100, 200);
+
+       /*
+        * Set L1SS sub-state timers to avoid lengthy state transitions,
+        * PM clock period is 18.52ns (1/54MHz, round down).
+        */
+       tmp = readl(pcie->base + PCIE_RC_PL_PHY_CTL_15);
+       tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK;
+       tmp |= 0x12;
+       writel(tmp, pcie->base + PCIE_RC_PL_PHY_CTL_15);
+
+       return 0;
+}
+
 static void add_inbound_win(struct inbound_win *b, u8 *count, u64 size,
                            u64 cpu_addr, u64 pci_offset)
 {
@@ -1179,6 +1226,12 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
                PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK);
        writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
 
+       if (pcie->cfg->post_setup) {
+               ret = pcie->cfg->post_setup(pcie);
+               if (ret < 0)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -1488,8 +1541,9 @@ static int brcm_pcie_turn_off(struct brcm_pcie *pcie)
        u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
        writel(tmp, base + HARD_DEBUG(pcie));
 
-       /* Shutdown PCIe bridge */
-       ret = pcie->cfg->bridge_sw_init_set(pcie, 1);
+       if (!(pcie->cfg->quirks & CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN))
+               /* Shutdown PCIe bridge */
+               ret = pcie->cfg->bridge_sw_init_set(pcie, 1);
 
        return ret;
 }
@@ -1699,6 +1753,16 @@ static const struct pcie_cfg_data bcm2711_cfg = {
        .num_inbound_wins = 3,
 };
 
+static const struct pcie_cfg_data bcm2712_cfg = {
+       .offsets        = pcie_offsets_bcm7712,
+       .soc_base       = BCM7712,
+       .perst_set      = brcm_pcie_perst_set_7278,
+       .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
+       .post_setup     = brcm_pcie_post_setup_bcm2712,
+       .quirks         = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN,
+       .num_inbound_wins = 10,
+};
+
 static const struct pcie_cfg_data bcm4908_cfg = {
        .offsets        = pcie_offsets,
        .soc_base       = BCM4908,
@@ -1750,6 +1814,7 @@ static const struct pcie_cfg_data bcm7712_cfg = {
 
 static const struct of_device_id brcm_pcie_match[] = {
        { .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg },
+       { .compatible = "brcm,bcm2712-pcie", .data = &bcm2712_cfg },
        { .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg },
        { .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg },
        { .compatible = "brcm,bcm7216-pcie", .data = &bcm7216_cfg },