[RFC PATCH v1 09/20] drivers: pci: add BCM2712 support for pcie_brcmstb driver
Oleksii Moisieiev
Oleksii_Moisieiev at epam.com
Wed Feb 5 11:15:44 CET 2025
Introducing pcie support for BCM2712 on example of the RPI5 board.
This driver initializes PCIe driver on start and provides access to
the peripheral, connected to PCIe.
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev at epam.com>
Reviewed-by: Volodymyr Babchuk <volodymyr_babchuk at epam.com>
---
drivers/pci/pcie_brcmstb.c | 314 ++++++++++++++++++++++++++++++++++---
1 file changed, 296 insertions(+), 18 deletions(-)
diff --git a/drivers/pci/pcie_brcmstb.c b/drivers/pci/pcie_brcmstb.c
index cd45f0bee9..9de5c0e980 100644
--- a/drivers/pci/pcie_brcmstb.c
+++ b/drivers/pci/pcie_brcmstb.c
@@ -21,6 +21,7 @@
#include <linux/bitfield.h>
#include <linux/log2.h>
#include <linux/iopoll.h>
+#include <reset.h>
/* Offset of the mandatory PCIe capability config registers */
#define BRCM_PCIE_CAP_REGS 0x00ac
@@ -45,6 +46,7 @@
#define MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000
#define MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000
#define MISC_CTRL_MAX_BURST_SIZE_128 0x0
+#define MISC_CTRL_MAX_BURST_SIZE_128_2712 0x100000
#define MISC_CTRL_SCB0_SIZE_MASK 0xf8000000
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c
@@ -65,6 +67,9 @@
#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
#define RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
+#define PCIE_MISC_PCIE_CTRL 0x4064
+#define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK 0x4
+
#define PCIE_MISC_PCIE_STATUS 0x4068
#define STATUS_PCIE_PORT_MASK 0x80
#define STATUS_PCIE_PORT_SHIFT 7
@@ -90,18 +95,23 @@
#define PCIE_MEM_WIN0_LIMIT_HI(win) \
PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
-#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204
+#define PCIE_MISC_HARD_PCIE_HARD_DEBUG(pcie) \
+ ((pcie)->pcie_cfg->offsets[PCIE_HARD_DEBUG])
#define PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000
#define PCIE_MSI_INTR2_CLR 0x4508
#define PCIE_MSI_INTR2_MASK_SET 0x4510
-#define PCIE_EXT_CFG_DATA 0x8000
+#define PCIE_EXT_CFG_DATA(pcie) \
+ ((pcie)->pcie_cfg->offsets[EXT_CFG_DATA])
-#define PCIE_EXT_CFG_INDEX 0x9000
+#define PCIE_EXT_CFG_INDEX(pcie) \
+ ((pcie)->pcie_cfg->offsets[EXT_CFG_INDEX])
-#define PCIE_RGR1_SW_INIT_1 0x9210
+#define PCIE_RGR1_SW_INIT_1(pcie) \
+ ((pcie)->pcie_cfg->offsets[RGR1_SW_INIT_1])
#define RGR1_SW_INIT_1_PERST_MASK 0x1
+#define RGR1_SW_INIT_1_PERSTB_MASK 0x4
#define RGR1_SW_INIT_1_INIT_MASK 0x2
/* PCIe parameters */
@@ -131,6 +141,52 @@
#define SSC_STATUS_PLL_LOCK_MASK 0x800
#define SSC_STATUS_PLL_LOCK_SHIFT 11
+#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_UBUS_CTRL 0x40a4
+#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK BIT(13)
+#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK BIT(19)
+#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170
+#define PCIE_MISC_UBUS_TIMEOUT 0x40A8
+#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c
+#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4
+#define PCIE_MISC_RC_BAR4_CONFIG_HI 0x40d8
+#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK 0xff
+#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI 0x4110
+#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE 0x1
+#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK 0xfffff000
+#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO 0x410c
+
+#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4
+#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0)
+#define MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400
+
+#define PCIE_LINK_STATE_L1 BIT(1)
+
+enum {
+ RGR1_SW_INIT_1,
+ EXT_CFG_INDEX,
+ EXT_CFG_DATA,
+ PCIE_HARD_DEBUG,
+};
+
+enum brcm_pcie_type {
+ BCM2711,
+ BCM2712
+};
+
+struct brcm_pcie;
+
+struct brcm_pcie_cfg_data {
+ const int *offsets;
+ const enum brcm_pcie_type type;
+ void (*perst_set)(struct brcm_pcie *pcie, u32 val);
+ void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
+ bool (*rc_mode)(struct brcm_pcie *pcie);
+};
+
/**
* struct brcm_pcie - the PCIe controller state
* @base: Base address of memory mapped IO registers of the controller
@@ -143,6 +199,9 @@ struct brcm_pcie {
int gen;
bool ssc;
+ struct reset_ctl rescal;
+ struct reset_ctl bridge_reset;
+ const struct brcm_pcie_cfg_data *pcie_cfg;
};
/**
@@ -186,6 +245,44 @@ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
return (val & STATUS_PCIE_PORT_MASK) >> STATUS_PCIE_PORT_SHIFT;
}
+static void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
+{
+ if (val)
+ setbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1(pcie),
+ RGR1_SW_INIT_1_PERST_MASK);
+ else
+ clrbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1(pcie),
+ RGR1_SW_INIT_1_PERST_MASK);
+}
+
+static void brcm_pcie_perst_set_2712(struct brcm_pcie *pcie, u32 val)
+{
+ u32 tmp;
+
+ /* Perst bit has moved and assert value is 0 */
+ tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL);
+ u32p_replace_bits(&tmp, !val, RGR1_SW_INIT_1_PERSTB_MASK);
+ writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL);
+}
+
+static void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
+{
+ if (val)
+ setbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1(pcie),
+ RGR1_SW_INIT_1_INIT_MASK);
+ else
+ clrbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1(pcie),
+ RGR1_SW_INIT_1_INIT_MASK);
+}
+
+static void brcm_pcie_bridge_sw_init_set_2712(struct brcm_pcie *pcie, u32 val)
+{
+ if (val)
+ reset_assert(&pcie->bridge_reset);
+ else
+ reset_deassert(&pcie->bridge_reset);
+}
+
/**
* brcm_pcie_link_up() - Check whether the PCIe link is up
* @pcie: Pointer to the PCIe controller state
@@ -232,8 +329,8 @@ static int brcm_pcie_config_address(const struct udevice *dev, pci_dev_t bdf,
/* For devices, write to the config space index register */
idx = PCIE_ECAM_OFFSET(pci_bus, pci_dev, pci_func, 0);
- writel(idx, pcie->base + PCIE_EXT_CFG_INDEX);
- *paddress = pcie->base + PCIE_EXT_CFG_DATA + offset;
+ writel(idx, pcie->base + PCIE_EXT_CFG_INDEX(pcie));
+ *paddress = pcie->base + PCIE_EXT_CFG_DATA(pcie) + offset;
return 0;
}
@@ -427,6 +524,98 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
writel(tmp, base + PCIE_MEM_WIN0_LIMIT_HI(win));
}
+static void brcm_pcie_get_resets_dt(struct udevice *dev)
+{
+ struct brcm_pcie *pcie = dev_get_priv(dev);
+ int ret;
+
+ ret = reset_get_by_name(dev, "rescal", &pcie->rescal);
+ if (ret) {
+ printf("Unable to get rescal reset\n");
+ return;
+ }
+
+ ret = reset_get_by_name(dev, "bridge", &pcie->bridge_reset);
+ if (ret) {
+ printf("Unable to get bridge reset\n");
+ return;
+ }
+}
+
+static void brcm_pcie_do_reset(struct udevice *dev)
+{
+ struct brcm_pcie *pcie = dev_get_priv(dev);
+ int ret;
+
+ ret = reset_deassert(&pcie->rescal);
+ if (ret)
+ printf("failed to deassert 'rescal'\n");
+}
+
+static void brcm_pcie_munge_pll(struct brcm_pcie *pcie)
+{
+ u32 tmp;
+ int ret, i;
+ u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e };
+ u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 };
+
+ ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
+ 0x1600);
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
+ debug("PCIE MDIO pre_refclk 0x%02x = 0x%04x\n",
+ regs[i], tmp);
+ }
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]);
+ brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
+ debug("PCIE MDIO post_refclk 0x%02x = 0x%04x\n",
+ regs[i], tmp);
+ }
+
+ udelay(200);
+}
+
+static void brcm_pcie_window_prog(struct udevice *dev)
+{
+ struct brcm_pcie *pcie = dev_get_priv(dev);
+ void __iomem *base = pcie->base;
+ int i, ret;
+ u32 tmp;
+
+ if (pcie->pcie_cfg->type != BCM2712)
+ return;
+
+ /* program additional inbound windows (RC_BAR4..RC_BAR10) */
+ for (i = 0; i < 7; i++) {
+ u64 bar_cpu, bar_size, bar_pci;
+ struct pci_region region;
+
+ ret = pci_get_dma_regions(dev, ®ion, i + 1);
+ if (ret)
+ break;
+
+ bar_pci = region.bus_start;
+ bar_cpu = region.phys_start;
+ bar_size = region.size;
+ tmp = lower_32_bits(bar_pci);
+ u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size),
+ RC_BAR2_CONFIG_LO_SIZE_MASK);
+ writel(tmp, base + PCIE_MISC_RC_BAR4_CONFIG_LO + i * 8);
+ writel(upper_32_bits(bar_pci),
+ base + PCIE_MISC_RC_BAR4_CONFIG_HI + i * 8);
+
+ tmp = upper_32_bits(bar_cpu) &
+ PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK;
+ writel(tmp,
+ base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI + i * 8);
+ tmp = lower_32_bits(bar_cpu) &
+ PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK;
+ writel(tmp | PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE,
+ base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO + i * 8);
+ }
+}
+
static int brcm_pcie_probe(struct udevice *dev)
{
struct udevice *ctlr = pci_get_controller(dev);
@@ -442,13 +631,21 @@ static int brcm_pcie_probe(struct udevice *dev)
u16 nlw, cls, lnksta;
u32 tmp;
+ /*
+ * Deassert rescal reset for BCM2712.
+ */
+ if (pcie->pcie_cfg->type == BCM2712)
+ brcm_pcie_do_reset(dev);
+
/*
* Reset the bridge, assert the fundamental reset. Note for some SoCs,
* e.g. BCM7278, the fundamental reset should not be asserted here.
* This will need to be changed when support for other SoCs is added.
*/
- setbits_le32(base + PCIE_RGR1_SW_INIT_1,
- RGR1_SW_INIT_1_INIT_MASK | RGR1_SW_INIT_1_PERST_MASK);
+ pcie->pcie_cfg->bridge_sw_init_set(pcie, 1);
+ if (pcie->pcie_cfg->type != BCM2712)
+ pcie->pcie_cfg->perst_set(pcie, 1);
+
/*
* The delay is a safety precaution to preclude the reset signal
* from looking like a glitch.
@@ -456,20 +653,35 @@ static int brcm_pcie_probe(struct udevice *dev)
udelay(100);
/* Take the bridge out of reset */
- clrbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_INIT_MASK);
+ pcie->pcie_cfg->bridge_sw_init_set(pcie, 0);
- clrbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG,
+ clrbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG(pcie),
PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
/* Wait for SerDes to be stable */
udelay(100);
+ if (pcie->pcie_cfg->type == BCM2712) {
+ /* Allow a 54MHz (xosc) refclk source */
+ brcm_pcie_munge_pll(pcie);
+ /* Fix for L1SS errata */
+ tmp = readl(base + PCIE_RC_PL_PHY_CTL_15);
+ tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK;
+ /* PM clock period is 18.52ns (round down) */
+ tmp |= 0x12;
+ writel(tmp, base + PCIE_RC_PL_PHY_CTL_15);
+ }
+
+ tmp = (pcie->pcie_cfg->type == BCM2712) ?
+ MISC_CTRL_MAX_BURST_SIZE_128_2712 :
+ MISC_CTRL_MAX_BURST_SIZE_128;
/* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
clrsetbits_le32(base + PCIE_MISC_MISC_CTRL,
MISC_CTRL_MAX_BURST_SIZE_MASK,
MISC_CTRL_SCB_ACCESS_EN_MASK |
MISC_CTRL_CFG_READ_UR_MODE_MASK |
- MISC_CTRL_MAX_BURST_SIZE_128);
+ MISC_CTRL_PCIE_RCB_MPS_MODE_MASK |
+ tmp);
pci_get_dma_regions(dev, ®ion, 0);
rc_bar2_offset = region.bus_start - region.phys_start;
@@ -482,6 +694,12 @@ static int brcm_pcie_probe(struct udevice *dev)
writel(upper_32_bits(rc_bar2_offset),
base + PCIE_MISC_RC_BAR2_CONFIG_HI);
+ if (pcie->pcie_cfg->type == BCM2712) {
+ tmp = readl(base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK);
+ writel(tmp, base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
+ }
+
scb_size_val = rc_bar2_size ?
ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */
@@ -490,6 +708,29 @@ static int brcm_pcie_probe(struct udevice *dev)
MISC_CTRL_SCB0_SIZE_MASK);
writel(tmp, base + PCIE_MISC_MISC_CTRL);
+ if (pcie->pcie_cfg->type == BCM2712) {
+ /* Suppress AXI error responses and return 1s for read failures */
+ tmp = readl(base + PCIE_MISC_UBUS_CTRL);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK);
+ writel(tmp, base + PCIE_MISC_UBUS_CTRL);
+ writel(0xffffffff, base + PCIE_MISC_AXI_READ_ERROR_DATA);
+
+ /*
+ * Adjust timeouts. The UBUS timeout also affects CRS
+ * completion retries, as the request will get terminated if
+ * either timeout expires, so both have to be a large value
+ * (in clocks of 750MHz).
+ * Set UBUS timeout to 250ms, then set RC config retry timeout
+ * to be ~240ms.
+ *
+ * Setting CRSVis=1 will stop the core from blocking on a CRS
+ * response, but does require the device to be well-behaved...
+ */
+ writel(0xB2D0000, base + PCIE_MISC_UBUS_TIMEOUT);
+ writel(0xABA0000, base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT);
+ }
+
/* Disable the PCIe->GISB memory window (RC_BAR1) */
clrbits_le32(base + PCIE_MISC_RC_BAR1_CONFIG_LO,
RC_BAR1_CONFIG_LO_SIZE_MASK);
@@ -504,12 +745,13 @@ static int brcm_pcie_probe(struct udevice *dev)
/* Clear any interrupts we find on boot */
writel(0xffffffff, base + PCIE_MSI_INTR2_CLR);
+ brcm_pcie_window_prog(dev);
+
if (pcie->gen)
brcm_pcie_set_gen(pcie, pcie->gen);
/* Unassert the fundamental reset */
- clrbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1,
- RGR1_SW_INIT_1_PERST_MASK);
+ pcie->pcie_cfg->perst_set(pcie, 0);
/*
* Wait for 100ms after PERST# deassertion; see PCIe CEM specification
@@ -528,7 +770,7 @@ static int brcm_pcie_probe(struct udevice *dev)
return -EINVAL;
}
- if (!brcm_pcie_rc_mode(pcie)) {
+ if (!pcie->pcie_cfg->rc_mode(pcie)) {
printf("PCIe misconfigured; is in EP mode\n");
return -EINVAL;
}
@@ -596,14 +838,14 @@ static int brcm_pcie_remove(struct udevice *dev)
void __iomem *base = pcie->base;
/* Assert fundamental reset */
- setbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_PERST_MASK);
+ pcie->pcie_cfg->perst_set(pcie, 1);
/* Turn off SerDes */
- setbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG,
+ setbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG(pcie),
PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
/* Shutdown bridge */
- setbits_le32(base + PCIE_RGR1_SW_INIT_1, RGR1_SW_INIT_1_INIT_MASK);
+ pcie->pcie_cfg->bridge_sw_init_set(pcie, 1);
return 0;
}
@@ -628,6 +870,11 @@ static int brcm_pcie_of_to_plat(struct udevice *dev)
else
pcie->gen = max_link_speed;
+ pcie->pcie_cfg = (const struct brcm_pcie_cfg_data *)dev_get_driver_data(dev);
+
+ if (pcie->pcie_cfg->type == BCM2712)
+ brcm_pcie_get_resets_dt(dev);
+
return 0;
}
@@ -636,8 +883,39 @@ static const struct dm_pci_ops brcm_pcie_ops = {
.write_config = brcm_pcie_write_config,
};
+static const int pcie_offsets[] = {
+ [RGR1_SW_INIT_1] = 0x9210,
+ [EXT_CFG_INDEX] = 0x9000,
+ [EXT_CFG_DATA] = 0x8000,
+ [PCIE_HARD_DEBUG] = 0x4204,
+};
+
+static const struct brcm_pcie_cfg_data bcm2711_cfg = {
+ .offsets = pcie_offsets,
+ .type = BCM2711,
+ .perst_set = brcm_pcie_perst_set_generic,
+ .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
+ .rc_mode = brcm_pcie_rc_mode,
+};
+
+static const int pcie_offsets_bcm2712[] = {
+ [RGR1_SW_INIT_1] = 0x0,
+ [EXT_CFG_INDEX] = 0x9000,
+ [EXT_CFG_DATA] = 0x8000,
+ [PCIE_HARD_DEBUG] = 0x4304,
+};
+
+static const struct brcm_pcie_cfg_data bcm2712_cfg = {
+ .offsets = pcie_offsets_bcm2712,
+ .type = BCM2712,
+ .perst_set = brcm_pcie_perst_set_2712,
+ .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_2712,
+ .rc_mode = brcm_pcie_rc_mode,
+};
+
static const struct udevice_id brcm_pcie_ids[] = {
- { .compatible = "brcm,bcm2711-pcie" },
+ { .compatible = "brcm,bcm2711-pcie", .data = (ulong)&bcm2711_cfg },
+ { .compatible = "brcm,bcm2712-pcie", .data = (ulong)&bcm2712_cfg },
{ }
};
--
2.34.1
More information about the U-Boot
mailing list