diff options
| author | Torsten Duwe <[email protected]> | 2026-06-01 12:39:36 +0200 |
|---|---|---|
| committer | Peter Robinson <[email protected]> | 2026-06-04 10:50:04 +0100 |
| commit | 585d6cfd9b6b886ddfde9b66c9399eadb5086936 (patch) | |
| tree | de278b5624bf775a950ffa7926e964de75a5a326 | |
| parent | df883ec51b5b602b68a517bc513eaba3b115d890 (diff) | |
pci: brcmstb: rework iBAR handling
Rework the setup of inbound PCIe windows: use the convenience functions
from Linux kernel commit ae6476c6de187 to calculate the BAR offsets and
factor out the setup code into a separate function.
The Linux kernel first allocates and populates an array of inbound_win[]
and sets the BARs from it later, while U-Boot does it all on the fly,
in one go, so the code is not 1:1 comparable.
Signed-off-by: Torsten Duwe <[email protected]>
Tested-by: Pedro Falcato <[email protected]>
Reviewed-by: Neil Armstrong <[email protected]>
| -rw-r--r-- | drivers/pci/pcie_brcmstb.c | 150 |
1 files changed, 131 insertions, 19 deletions
diff --git a/drivers/pci/pcie_brcmstb.c b/drivers/pci/pcie_brcmstb.c index 164d8e2b7fe..624376fe235 100644 --- a/drivers/pci/pcie_brcmstb.c +++ b/drivers/pci/pcie_brcmstb.c @@ -50,6 +50,29 @@ #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_BAR1_CONFIG_REMAP 0x40ac +#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 + enum { RGR1_SW_INIT_1, PCIE_HARD_DEBUG, @@ -441,17 +464,105 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie, writel(tmp, base + PCIE_MEM_WIN0_LIMIT_HI(win)); } +static u32 brcm_bar_reg_offset(int bar) +{ + if (bar <= 3) + return PCIE_MISC_RC_BAR1_CONFIG_LO + 8 * (bar - 1); + else + return PCIE_MISC_RC_BAR4_CONFIG_LO + 8 * (bar - 4); +} + +static u32 brcm_ubus_reg_offset(int bar) +{ + if (bar <= 3) + return PCIE_MISC_UBUS_BAR1_CONFIG_REMAP + 8 * (bar - 1); + else + return PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO + 8 * (bar - 4); +} + +/* + * Round size up to the next power of two, as required by + * brcm_pcie_encode_ibar_size(). If size is already a power of two + * fls64(size - 1) still gives the correct result because the hardware + * encodes the exponent, not the raw value. + */ +static u64 brcm_ibar_round_size(u64 size) +{ + return 1ULL << fls64(size - 1); +} + +static void brcm_pcie_set_inbound_windows(struct udevice *dev) +{ + struct brcm_pcie *pcie = dev_get_priv(dev); + void __iomem *base = pcie->base; + bool is_2712 = (pcie->pcie_cfg->type == BCM2712); + int i, ibar_no, ret; + u32 tmp; + + ibar_no = 0; + /* pre-2712 chips leave the first entry empty */ + if (pcie->pcie_cfg->type != BCM2712) + ibar_no++; + + /* program inbound windows from OF property "dma-regions" */ + for (i = 0; i < 7; i++, ibar_no++) { + u64 bar_cpu, bar_size, bar_pci; + struct pci_region region; + int ubus_bar_offset, rc_bar_offset; + + ret = pci_get_dma_regions(dev, ®ion, i); + if (ret) /* no region #i? Then we're done. */ + break; + ubus_bar_offset = brcm_ubus_reg_offset(ibar_no + 1); + rc_bar_offset = brcm_bar_reg_offset(ibar_no + 1); + + bar_pci = region.bus_start; + bar_cpu = region.phys_start; + bar_size = region.size; + + if (is_2712) { + /* BCM2712: BAR holds raw PCI address; UBUS remap + * registers supply the CPU-side translation. */ + 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 + rc_bar_offset); + writel(upper_32_bits(bar_pci), base + rc_bar_offset + 4); + + tmp = lower_32_bits(bar_cpu) & + PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK; + tmp |= PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE; + writel(tmp, base + ubus_bar_offset); + + tmp = upper_32_bits(bar_cpu) & + PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK; + writel(tmp, base + ubus_bar_offset + 4); + } else { + /* Pre-BCM2712 (e.g. BCM2711 / RPi4): the BAR config + * register holds the offset (bus_start - phys_start), + * not the raw PCI address. The size must be rounded + * up to the next power of two before encoding. */ + u64 bar_offset = bar_pci - bar_cpu; + u64 bar_size_po2 = brcm_ibar_round_size(bar_size); + + tmp = lower_32_bits(bar_offset); + u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size_po2), + RC_BAR2_CONFIG_LO_SIZE_MASK); + writel(tmp, base + rc_bar_offset); + writel(upper_32_bits(bar_offset), base + rc_bar_offset + 4); + /* UBUS remap registers are not used on pre-2712 hardware. */ + } + } +} + static int brcm_pcie_probe(struct udevice *dev) { struct udevice *ctlr = pci_get_controller(dev); struct pci_controller *hose = dev_get_uclass_priv(ctlr); struct brcm_pcie *pcie = dev_get_priv(dev); void __iomem *base = pcie->base; - struct pci_region region; bool ssc_good = false; int num_out_wins = 0; - u64 rc_bar2_offset, rc_bar2_size; - unsigned int scb_size_val; int i, ret = 0; u16 nlw, cls, lnksta; u32 tmp; @@ -496,23 +607,22 @@ static int brcm_pcie_probe(struct udevice *dev) MISC_CTRL_CFG_READ_UR_MODE_MASK | MISC_CTRL_MAX_BURST_SIZE_128); - pci_get_dma_regions(dev, ®ion, 0); - rc_bar2_offset = region.bus_start - region.phys_start; - rc_bar2_size = 1ULL << fls64(region.size - 1); - - tmp = lower_32_bits(rc_bar2_offset); - u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size), - RC_BAR2_CONFIG_LO_SIZE_MASK); - writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO); - writel(upper_32_bits(rc_bar2_offset), - base + PCIE_MISC_RC_BAR2_CONFIG_HI); - - scb_size_val = rc_bar2_size ? - ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */ - tmp = readl(base + PCIE_MISC_MISC_CTRL); - u32p_replace_bits(&tmp, scb_size_val, - MISC_CTRL_SCB0_SIZE_MASK); + if (pcie->pcie_cfg->type == BCM2712) { + /* BCM2712: fixed 32GB SCB0 window */ + u32p_replace_bits(&tmp, 20, MISC_CTRL_SCB0_SIZE_MASK); + } else { + /* Pre-BCM2712: size SCB0 to match the actual DMA region. + * rc_bar2_size must be a power of two; ilog2(size) - 15 + * gives the hardware encoding (e.g. 1GB -> 15). */ + struct pci_region region; + u64 rc_bar2_size; + + pci_get_dma_regions(dev, ®ion, 0); + rc_bar2_size = brcm_ibar_round_size(region.size); + u32p_replace_bits(&tmp, rc_bar2_size ? ilog2(rc_bar2_size) - 15 : 0xf, + MISC_CTRL_SCB0_SIZE_MASK); + } writel(tmp, base + PCIE_MISC_MISC_CTRL); /* Disable the PCIe->GISB memory window (RC_BAR1) */ @@ -529,6 +639,8 @@ static int brcm_pcie_probe(struct udevice *dev) /* Clear any interrupts we find on boot */ writel(0xffffffff, base + PCIE_MSI_INTR2_CLR); + brcm_pcie_set_inbound_windows(dev); + if (pcie->gen) brcm_pcie_set_gen(pcie, pcie->gen); |
