From 32c36986b6aa6c48ab3292d45c1e6d5ca38be3ef Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Feb 2025 11:07:15 +0000 Subject: [PATCH 01/22] Revert "PCI: brcmstb: Add BCM2712 support" This reverts commit d1f74f3fbaf00417f80ec7ce4e828068743eb071. --- drivers/pci/controller/pcie-brcmstb.c | 671 ++------------------------ 1 file changed, 36 insertions(+), 635 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 63020d7040c4b9..a93427cd70e28b 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -48,26 +48,10 @@ #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00 -#define PCIE_RC_TL_VDM_CTL0 0x0a20 -#define PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK 0x10000 -#define PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK 0x20000 -#define PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK 0x40000 - -#define PCIE_RC_TL_VDM_CTL1 0x0a0c -#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID0_MASK 0x0000ffff -#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID1_MASK 0xffff0000 - -#define PCIE_RC_CFG_PRIV1_ROOT_CAP 0x4f8 -#define PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK 0xf8 - #define PCIE_RC_DL_MDIO_ADDR 0x1100 #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 @@ -98,15 +82,9 @@ #define PCIE_BRCM_MAX_INBOUND_WINS 16 #define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c #define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f -#define PCIE_MISC_RC_BAR1_CONFIG_HI 0x4030 -#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034 -#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f -#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038 +#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4 -#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c -#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f -#define PCIE_MISC_RC_BAR3_CONFIG_HI 0x4040 #define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044 #define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048 @@ -115,15 +93,12 @@ #define PCIE_MISC_MSI_DATA_CONFIG_VAL_32 0xffe06540 #define PCIE_MISC_MSI_DATA_CONFIG_VAL_8 0xfff86540 -#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c - #define PCIE_MISC_PCIE_CTRL 0x4064 #define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1 #define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK 0x4 #define PCIE_MISC_PCIE_STATUS 0x4068 #define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80 -#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712 0x40 #define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20 #define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10 #define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40 @@ -149,82 +124,14 @@ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8) #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2 -#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK 0x8 -#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK 0x200000 #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000 #define PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x00800000 -#define PCIE_CLKREQ_MASK \ - (PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK | \ - PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK) #define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac #define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_EN_MASK BIT(0) #define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP 0x410c -#define PCIE_MISC_CTRL_1 0x40A0 -#define PCIE_MISC_CTRL_1_OUTBOUND_TC_MASK 0xf -#define PCIE_MISC_CTRL_1_OUTBOUND_NO_SNOOP_MASK BIT(3) -#define PCIE_MISC_CTRL_1_OUTBOUND_RO_MASK BIT(4) -#define PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK BIT(5) - -#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_UBUS_TIMEOUT 0x40A8 - -#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac -#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0) -#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI 0x40b0 - -#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4 -#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0) - -/* Additional RC BARs */ -#define PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK 0x1f -#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4 -#define PCIE_MISC_RC_BAR4_CONFIG_HI 0x40d8 -/* ... */ -#define PCIE_MISC_RC_BAR10_CONFIG_LO 0x4104 -#define PCIE_MISC_RC_BAR10_CONFIG_HI 0x4108 - -#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE 0x1 -#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK 0xfffff000 -#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK 0xff -#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO 0x410c -#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI 0x4110 -/* ... */ -#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_LO 0x413c -#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_HI 0x4140 - -/* AXI priority forwarding - automatic level-based */ -#define PCIE_MISC_TC_QUEUE_TO_QOS_MAP(x) (0x4160 - (x) * 4) -/* Defined in quarter-fullness */ -#define QUEUE_THRESHOLD_34_TO_QOS_MAP_SHIFT 12 -#define QUEUE_THRESHOLD_23_TO_QOS_MAP_SHIFT 8 -#define QUEUE_THRESHOLD_12_TO_QOS_MAP_SHIFT 4 -#define QUEUE_THRESHOLD_01_TO_QOS_MAP_SHIFT 0 -#define QUEUE_THRESHOLD_MASK 0xf - -/* VDM messages indexing TCs to AXI priorities */ -/* Indexes 8-15 */ -#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI 0x4164 -/* Indexes 0-7 */ -#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO 0x4168 -#define VDM_PRIORITY_TO_QOS_MAP_SHIFT(x) (4 * (x)) -#define VDM_PRIORITY_TO_QOS_MAP_MASK 0xf - -#define PCIE_MISC_AXI_INTF_CTRL 0x416C -#define AXI_EN_RCLK_QOS_ARRAY_FIX BIT(13) -#define AXI_EN_QOS_UPDATE_TIMING_FIX BIT(12) -#define AXI_DIS_QOS_GATING_IN_MASTER BIT(11) -#define AXI_REQFIFO_EN_QOS_PROPAGATION BIT(7) -#define AXI_BRIDGE_LOW_LATENCY_MODE BIT(6) -#define AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK 0x3f - -#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170 - #define PCIE_MSI_INTR2_BASE 0x4500 /* Offsets from INTR2_CPU and MSI_INTR2 BASE offsets */ @@ -313,7 +220,6 @@ enum pcie_soc_base { BCM7425, BCM7435, BCM7712, - BCM2712, }; struct inbound_win { @@ -345,7 +251,7 @@ struct brcm_msi { struct mutex lock; /* guards the alloc/free operations */ u64 target_addr; int irq; - DECLARE_BITMAP(used, 64); + DECLARE_BITMAP(used, BRCM_INT_PCI_MSI_NR); bool legacy; /* Some chips have MSIs in bits [31..24] of a shared register. */ int legacy_shift; @@ -379,10 +285,6 @@ struct brcm_pcie { bool ep_wakeup_capable; bool has_phy; u8 num_inbound_wins; - bool l1ss; - bool rcb_mps_mode; - u32 qos_map; - u32 tperst_clk_ms; }; static inline bool is_bmips(const struct brcm_pcie *pcie) @@ -401,8 +303,8 @@ static int brcm_pcie_encode_ibar_size(u64 size) if (log2_in >= 12 && log2_in <= 15) /* Covers 4KB to 32KB (inclusive) */ return (log2_in - 12) + 0x1c; - else if (log2_in >= 16 && log2_in <= 36) - /* Covers 64KB to 64GB, (inclusive) */ + else if (log2_in >= 16 && log2_in <= 35) + /* Covers 64KB to 32GB, (inclusive) */ return log2_in - 15; /* Something is awry so disable */ return 0; @@ -491,43 +393,12 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie) return ssc && pll ? 0 : -EIO; } -static void brcm_pcie_munge_pll(struct brcm_pcie *pcie) -{ - //print "MDIO block 0x1600 written per Dannys instruction" - //tmp = pcie_mdio_write(phyad, &h16&, &h50b9&) - //tmp = pcie_mdio_write(phyad, &h17&, &hbd1a&) - //tmp = pcie_mdio_write(phyad, &h1b&, &h5030&) - //tmp = pcie_mdio_write(phyad, &h1e&, &h0007&) - - 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); - dev_dbg(pcie->dev, "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); - dev_dbg(pcie->dev, "PCIE MDIO post_refclk 0x%02x = 0x%04x\n", - regs[i], tmp); - } - usleep_range(100, 200); -} - /* Limits operation to a specific generation (1, 2, or 3) */ static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen) { u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); - dev_info(pcie->dev, "Forcing gen %d\n", pcie->gen); - lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen; writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); @@ -579,73 +450,6 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie, writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); } -static void brcm_pcie_set_tc_qos(struct brcm_pcie *pcie) -{ - int i; - u32 reg; - - if (pcie->soc_base != BCM2712) - return; - - /* Disable broken QOS forwarding search. Set chicken bits for 2712D0 */ - reg = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL); - reg &= ~AXI_REQFIFO_EN_QOS_PROPAGATION; - reg |= AXI_EN_RCLK_QOS_ARRAY_FIX | AXI_EN_QOS_UPDATE_TIMING_FIX | - AXI_DIS_QOS_GATING_IN_MASTER; - writel(reg, pcie->base + PCIE_MISC_AXI_INTF_CTRL); - - /* - * If the QOS_UPDATE_TIMING_FIX bit is Reserved-0, then this is a - * 2712C1 chip, or a single-lane RC. Use the best-effort alternative - * which is to partially throttle AXI requests in-flight to the SDC. - */ - reg = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL); - if (!(reg & AXI_EN_QOS_UPDATE_TIMING_FIX)) { - reg &= ~AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK; - reg |= 15; - writel(reg, pcie->base + PCIE_MISC_AXI_INTF_CTRL); - } - - /* Disable VDM reception by default - QoS map defaults to 0 */ - reg = readl(pcie->base + PCIE_MISC_CTRL_1); - reg &= ~PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK; - writel(reg, pcie->base + PCIE_MISC_CTRL_1); - - if (!of_property_read_u32(pcie->np, "brcm,fifo-qos-map", &pcie->qos_map)) { - /* - * Backpressure mode - bottom 4 nibbles are QoS for each - * quartile of FIFO level. Each TC gets the same map, because - * this mode is intended for nonrealtime EPs. - */ - - pcie->qos_map &= 0x0000ffff; - for (i = 0; i < 8; i++) - writel(pcie->qos_map, pcie->base + PCIE_MISC_TC_QUEUE_TO_QOS_MAP(i)); - - return; - } - - if (!of_property_read_u32(pcie->np, "brcm,vdm-qos-map", &pcie->qos_map)) { - - reg = readl(pcie->base + PCIE_MISC_CTRL_1); - reg |= PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK; - writel(reg, pcie->base + PCIE_MISC_CTRL_1); - - /* No forwarding means no point separating panic priorities from normal */ - writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO); - writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI); - - /* Match Vendor ID of 0 */ - writel(0, pcie->base + PCIE_RC_TL_VDM_CTL1); - /* Forward VDMs to priority interface - at least the rx counters work */ - reg = readl(pcie->base + PCIE_RC_TL_VDM_CTL0); - reg |= PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK | - PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK | - PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK; - writel(reg, pcie->base + PCIE_RC_TL_VDM_CTL0); - } -} - static struct irq_chip brcm_msi_irq_chip = { .name = "BRCM STB PCIe MSI", .irq_ack = irq_chip_ack_parent, @@ -654,8 +458,8 @@ static struct irq_chip brcm_msi_irq_chip = { }; static struct msi_domain_info brcm_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_NO_AFFINITY | MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_NO_AFFINITY | MSI_FLAG_MULTI_PCI_MSI, .chip = &brcm_msi_irq_chip, }; @@ -674,23 +478,10 @@ static void brcm_pcie_msi_isr(struct irq_desc *desc) status = readl(msi->intr_base + MSI_INT_STATUS); status >>= msi->legacy_shift; - for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR/*msi->nr*/) { - unsigned long virq; - bool found = false; - - virq = irq_find_mapping(msi->inner_domain, bit); - if (virq) { - found = true; - dev_dbg(dev, "MSI -> %ld\n", virq); - generic_handle_irq(virq); - } - virq = irq_find_mapping(msi->inner_domain, bit + 32); - if (virq) { - found = true; - dev_dbg(dev, "MSI -> %ld\n", virq); - generic_handle_irq(virq); - } - if (!found) + for_each_set_bit(bit, &status, msi->nr) { + int ret; + ret = generic_handle_domain_irq(msi->inner_domain, bit); + if (ret) dev_dbg(dev, "unexpected MSI\n"); } @@ -703,13 +494,13 @@ static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) msg->address_lo = lower_32_bits(msi->target_addr); msg->address_hi = upper_32_bits(msi->target_addr); - msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | (data->hwirq & 0x1f); + msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | data->hwirq; } static void brcm_msi_ack_irq(struct irq_data *data) { struct brcm_msi *msi = irq_data_get_irq_chip_data(data); - const int shift_amt = (data->hwirq & 0x1f) + msi->legacy_shift; + const int shift_amt = data->hwirq + msi->legacy_shift; writel(1 << shift_amt, msi->intr_base + MSI_INT_CLR); } @@ -869,7 +660,7 @@ static int brcm_pcie_enable_msi(struct brcm_pcie *pcie) msi->legacy_shift = 24; } else { msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE; - msi->nr = 64; //BRCM_INT_PCI_MSI_NR; + msi->nr = BRCM_INT_PCI_MSI_NR; msi->legacy_shift = 0; } @@ -891,9 +682,6 @@ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie) void __iomem *base = pcie->base; u32 val = readl(base + PCIE_MISC_PCIE_STATUS); - if (pcie->soc_base == BCM2712) - return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712, val) | 1; //XXX - return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val); } @@ -986,21 +774,6 @@ static int brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val) return 0; } -static int brcm_pcie_bridge_sw_init_set_2712(struct brcm_pcie *pcie, u32 val) -{ - int ret; - - if (WARN_ONCE(!pcie->bridge_reset, "missing bridge reset controller\n")) - return -EINVAL; - - if (val) - ret = reset_control_assert(pcie->bridge_reset); - else - ret = reset_control_deassert(pcie->bridge_reset); - - return ret; -} - static int brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val) { int ret; @@ -1031,18 +804,6 @@ static int brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val) return 0; } -static int 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, PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK); - writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL); - - return 0; -} - static int brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) { u32 tmp; @@ -1054,8 +815,6 @@ static int brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) return 0; } - -#if 0 static void add_inbound_win(struct inbound_win *b, u8 *count, u64 size, u64 cpu_addr, u64 pci_offset) { @@ -1258,137 +1017,17 @@ static void set_inbound_win_registers(struct brcm_pcie *pcie, } } } -#endif - -static int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie, - u64 *rc_bar2_size, - u64 *rc_bar2_offset) -{ - struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); - struct resource_entry *entry; - struct device *dev = pcie->dev; - u64 lowest_pcie_addr = ~(u64)0; - int ret, i = 0; - u64 size = 0; - - resource_list_for_each_entry(entry, &bridge->dma_ranges) { - u64 pcie_beg = entry->res->start - entry->offset; - - size += entry->res->end - entry->res->start + 1; - if (pcie_beg < lowest_pcie_addr) - lowest_pcie_addr = pcie_beg; - if (pcie->soc_base == BCM2711 || pcie->soc_base == BCM2712) - break; // Only consider the first entry - } - - if (lowest_pcie_addr == ~(u64)0) { - dev_err(dev, "DT node has no dma-ranges\n"); - return -EINVAL; - } - - ret = of_property_read_variable_u64_array(pcie->np, "brcm,scb-sizes", pcie->memc_size, 1, - PCIE_BRCM_MAX_MEMC); - - if (ret <= 0) { - /* Make an educated guess */ - pcie->num_memc = 1; - pcie->memc_size[0] = 1ULL << fls64(size - 1); - } else { - pcie->num_memc = ret; - } - - /* Each memc is viewed through a "port" that is a power of 2 */ - for (i = 0, size = 0; i < pcie->num_memc; i++) - size += pcie->memc_size[i]; - - /* System memory starts at this address in PCIe-space */ - *rc_bar2_offset = lowest_pcie_addr; - /* The sum of all memc views must also be a power of 2 */ - *rc_bar2_size = 1ULL << fls64(size - 1); - - /* - * We validate the inbound memory view even though we should trust - * whatever the device-tree provides. This is because of an HW issue on - * early Raspberry Pi 4's revisions (bcm2711). It turns out its - * firmware has to dynamically edit dma-ranges due to a bug on the - * PCIe controller integration, which prohibits any access above the - * lower 3GB of memory. Given this, we decided to keep the dma-ranges - * in check, avoiding hard to debug device-tree related issues in the - * future: - * - * The PCIe host controller by design must set the inbound viewport to - * be a contiguous arrangement of all of the system's memory. In - * addition, its size mut be a power of two. To further complicate - * matters, the viewport must start on a pcie-address that is aligned - * on a multiple of its size. If a portion of the viewport does not - * represent system memory -- e.g. 3GB of memory requires a 4GB - * viewport -- we can map the outbound memory in or after 3GB and even - * though the viewport will overlap the outbound memory the controller - * will know to send outbound memory downstream and everything else - * upstream. - * - * For example: - * - * - The best-case scenario, memory up to 3GB, is to place the inbound - * region in the first 4GB of pcie-space, as some legacy devices can - * only address 32bits. We would also like to put the MSI under 4GB - * as well, since some devices require a 32bit MSI target address. - * - * - If the system memory is 4GB or larger we cannot start the inbound - * region at location 0 (since we have to allow some space for - * outbound memory @ 3GB). So instead it will start at the 1x - * multiple of its size - */ - if (!*rc_bar2_size || (*rc_bar2_offset & (*rc_bar2_size - 1)) || - (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) { - dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n", - *rc_bar2_size, *rc_bar2_offset); - return -EINVAL; - } - - return 0; -} - -static int brcm_pcie_get_rc_bar_n(struct brcm_pcie *pcie, - int idx, - u64 *rc_bar_cpu, - u64 *rc_bar_size, - u64 *rc_bar_pci) -{ - struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); - struct resource_entry *entry; - int i = 0; - - resource_list_for_each_entry(entry, &bridge->dma_ranges) { - if (i == idx) { - *rc_bar_cpu = entry->res->start; - *rc_bar_size = entry->res->end - entry->res->start + 1; - *rc_bar_pci = entry->res->start - entry->offset; - return 0; - } - - i++; - } - - return -EINVAL; -} static int brcm_pcie_setup(struct brcm_pcie *pcie) { -#if 0 struct inbound_win inbound_wins[PCIE_BRCM_MAX_INBOUND_WINS]; -#endif - u64 rc_bar2_offset, rc_bar2_size; void __iomem *base = pcie->base; struct pci_host_bridge *bridge; struct resource_entry *entry; u32 tmp, burst, aspm_support; u8 num_out_wins = 0; -#if 0 int num_inbound_wins = 0; -#endif int memc, ret; - int count, i; /* Reset the bridge */ ret = pcie->bridge_sw_init_set(pcie, 1); @@ -1420,17 +1059,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) /* Wait for SerDes to be stable */ usleep_range(100, 200); - if (pcie->soc_base == 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); - } - /* * SCB_MAX_BURST_SIZE is a two bit field. For GENERIC chips it * is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it @@ -1440,8 +1068,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) burst = 0x1; /* 256 bytes */ else if (pcie->soc_base == BCM2711) burst = 0x0; /* 128 bytes */ - else if (pcie->soc_base == BCM2712) - burst = 0x1; /* 128 bytes */ else if (pcie->soc_base == BCM7278) burst = 0x3; /* 512 bytes */ else @@ -1449,38 +1075,27 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) /* * Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN, - * RCB_MPS_MODE + * RCB_MPS_MODE, RCB_64B_MODE */ tmp = readl(base + PCIE_MISC_MISC_CTRL); u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK); u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK); u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK); - if (pcie->rcb_mps_mode) - u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK); + u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK); + u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK); writel(tmp, base + PCIE_MISC_MISC_CTRL); - brcm_pcie_set_tc_qos(pcie); + num_inbound_wins = brcm_pcie_get_inbound_wins(pcie, inbound_wins); + if (num_inbound_wins < 0) + return num_inbound_wins; - ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size, - &rc_bar2_offset); - if (ret) - return ret; - - tmp = lower_32_bits(rc_bar2_offset); - u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size), - PCIE_MISC_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); + set_inbound_win_registers(pcie, inbound_wins, num_inbound_wins); if (!brcm_pcie_rc_mode(pcie)) { dev_err(pcie->dev, "PCIe RC controller misconfigured as Endpoint\n"); return -EINVAL; } - 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); tmp = readl(base + PCIE_MISC_MISC_CTRL); for (memc = 0; memc < pcie->num_memc; memc++) { u32 scb_size_val = ilog2(pcie->memc_size[memc]) - 15; @@ -1494,29 +1109,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) } writel(tmp, base + PCIE_MISC_MISC_CTRL); - if (pcie->soc_base == 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); - } - /* * We ideally want the MSI target address to be located in the 32bit * addressable memory area. Some devices might depend on it. This is @@ -1524,58 +1116,22 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) * 4GB or when the inbound area is smaller than 4GB (taking into * account the rounding-up we're forced to perform). */ - if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G) + if (inbound_wins[2].pci_offset >= SZ_4G || + (inbound_wins[2].size + inbound_wins[2].pci_offset) < SZ_4G) pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB; else pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB; - /* disable the PCIe->GISB memory window (RC_BAR1) */ - tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO); - tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK; - writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO); - /* disable the PCIe->SCB memory window (RC_BAR3) */ - tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO); - tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK; - writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO); - - /* Always advertise L1 capability */ - aspm_support = BIT(1); - /* Advertise L0s capability unless 'aspm-no-l0s' is set */ + /* Don't advertise L0s capability if 'aspm-no-l0s' */ + aspm_support = PCIE_LINK_STATE_L1; if (!of_property_read_bool(pcie->np, "aspm-no-l0s")) - aspm_support |= BIT(0); + aspm_support |= PCIE_LINK_STATE_L0S; tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); u32p_replace_bits(&tmp, aspm_support, PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK); writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); - /* program additional inbound windows (RC_BAR4..RC_BAR10) */ - count = (pcie->soc_base == BCM2712) ? 7 : 0; - for (i = 0; i < count; i++) { - u64 bar_cpu, bar_size, bar_pci; - - ret = brcm_pcie_get_rc_bar_n(pcie, 1 + i, &bar_cpu, &bar_size, - &bar_pci); - if (ret) - break; - - tmp = lower_32_bits(bar_pci); - u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size), - PCIE_MISC_RC_BAR_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); - } - /* * For config space accesses on the RC, show the right class for * a PCIe-PCIe bridge (the default setting is to be EP mode). @@ -1625,87 +1181,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) return 0; } -/* - * This extends the timeout period for an access to an internal bus. This - * access timeout may occur during L1SS sleep periods, even without the - * presence of a PCIe access. - */ -static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie) -{ - /* TIMEOUT register is two registers before RGR1_SW_INIT_1 */ - const unsigned int REG_OFFSET = PCIE_RGR1_SW_INIT_1(pcie) - 8; - u32 timeout_us = 4000000; /* 4 seconds, our setting for L1SS */ - - /* 7712 does not have this (RGR1) timer */ - if (pcie->soc_base == BCM7712 || pcie->soc_base == BCM2712) - return; - - /* Each unit in timeout register is 1/216,000,000 seconds */ - writel(216 * timeout_us, pcie->base + REG_OFFSET); -} - -static void brcm_config_clkreq(struct brcm_pcie *pcie) -{ - static const char err_msg[] = "invalid 'brcm,clkreq-mode' DT string\n"; - const char *mode = "default"; - u32 clkreq_cntl; - int ret, tmp; - - ret = of_property_read_string(pcie->np, "brcm,clkreq-mode", &mode); - if (ret && ret != -EINVAL) { - dev_err(pcie->dev, err_msg); - mode = "safe"; - } - - /* Start out assuming safe mode (both mode bits cleared) */ - clkreq_cntl = readl(pcie->base + HARD_DEBUG(pcie)); - clkreq_cntl &= ~PCIE_CLKREQ_MASK; - - if (strcmp(mode, "no-l1ss") == 0) { - /* - * "no-l1ss" -- Provides Clock Power Management, L0s, and - * L1, but cannot provide L1 substate (L1SS) power - * savings. If the downstream device connected to the RC is - * L1SS capable AND the OS enables L1SS, all PCIe traffic - * may abruptly halt, potentially hanging the system. - */ - clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; - /* - * We want to un-advertise L1 substates because if the OS - * tries to configure the controller into using L1 substate - * power savings it may fail or hang when the RC HW is in - * "no-l1ss" mode. - */ - tmp = readl(pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP); - u32p_replace_bits(&tmp, 2, PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK); - writel(tmp, pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP); - - } else if (strcmp(mode, "default") == 0) { - /* - * "default" -- Provides L0s, L1, and L1SS, but not - * compliant to provide Clock Power Management; - * specifically, may not be able to meet the Tclron max - * timing of 400ns as specified in "Dynamic Clock Control", - * section 3.2.5.2.2 of the PCIe spec. This situation is - * atypical and should happen only with older devices. - */ - clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK; - brcm_extend_rbus_timeout(pcie); - - } else { - /* - * "safe" -- No power savings; refclk is driven by RC - * unconditionally. - */ - if (strcmp(mode, "safe") != 0) - dev_err(pcie->dev, err_msg); - mode = "safe"; - } - writel(clkreq_cntl, pcie->base + HARD_DEBUG(pcie)); - - dev_info(pcie->dev, "clkreq-mode set to %s\n", mode); -} - static int brcm_pcie_start_link(struct brcm_pcie *pcie) { struct device *dev = pcie->dev; @@ -1713,34 +1188,12 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) u16 nlw, cls, lnksta; bool ssc_good = false; u32 tmp; - u16 tmp16; int ret, i; - if (pcie->gen) - brcm_pcie_set_gen(pcie, pcie->gen); - /* Unassert the fundamental reset */ - if (pcie->tperst_clk_ms) { - /* - * Increase Tperst_clk time by forcing PERST# output low while - * the internal reset is released, so the PLL generates stable - * refclk output further in advance of PERST# deassertion. - */ - tmp = readl(base + HARD_DEBUG(pcie)); - u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK); - writel(tmp, base + HARD_DEBUG(pcie)); - - pcie->perst_set(pcie, 0); - msleep(pcie->tperst_clk_ms); - - tmp = readl(base + HARD_DEBUG(pcie)); - u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK); - writel(tmp, base + HARD_DEBUG(pcie)); - } else { - ret = pcie->perst_set(pcie, 0); - if (ret) - return ret; - } + ret = pcie->perst_set(pcie, 0); + if (ret) + return ret; /* * Wait for 100ms after PERST# deassertion; see PCIe CEM specification @@ -1761,7 +1214,8 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) return -ENODEV; } - brcm_config_clkreq(pcie); + if (pcie->gen) + brcm_pcie_set_gen(pcie, pcie->gen); if (pcie->ssc) { ret = brcm_pcie_set_ssc(pcie); @@ -1779,15 +1233,13 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) ssc_good ? "(SSC)" : "(!SSC)"); /* - * RootCtl bits are reset by perst_n, which undoes pci_enable_crs() - * called prior to pci_add_new_bus() during probe. Re-enable here. + * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1 + * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1. */ - tmp16 = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_RTCAP); - if (tmp16 & PCI_EXP_RTCAP_CRSVIS) { - tmp16 = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_RTCTL); - u16p_replace_bits(&tmp16, 1, PCI_EXP_RTCTL_CRSSVE); - writew(tmp16, base + BRCM_PCIE_CAP_REGS + PCI_EXP_RTCTL); - } + tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; + writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); + return 0; } @@ -1961,12 +1413,6 @@ 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)); - /* - * Shutting down this bridge on pcie1 means accesses to rescal block - * will hang the chip if another RC wants to assert/deassert rescal. - */ - if (pcie->soc_base == BCM2712) - return 0; /* Shutdown PCIe bridge */ ret = pcie->bridge_sw_init_set(pcie, 1); @@ -2162,13 +1608,6 @@ static const int pcie_offsets_bcm7712[] = { [PCIE_INTR2_CPU_BASE] = 0x4400, }; -static const int pcie_offsets_bcm2712[] = { - [EXT_CFG_INDEX] = 0x9000, - [EXT_CFG_DATA] = 0x9004, - [PCIE_HARD_DEBUG] = 0x4304, - [PCIE_INTR2_CPU_BASE] = 0x4400, -}; - static const struct pcie_cfg_data generic_cfg = { .offsets = pcie_offsets, .soc_base = GENERIC, @@ -2234,13 +1673,6 @@ static const struct pcie_cfg_data bcm7712_cfg = { .num_inbound_wins = 10, }; -static const struct pcie_cfg_data bcm2712_cfg = { - .offsets = pcie_offsets_bcm2712, - .perst_set = brcm_pcie_perst_set_2712, - .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_2712, - .soc_base = BCM2712, -}; - static const struct of_device_id brcm_pcie_match[] = { { .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg }, { .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg }, @@ -2251,7 +1683,6 @@ static const struct of_device_id brcm_pcie_match[] = { { .compatible = "brcm,bcm7435-pcie", .data = &bcm7435_cfg }, { .compatible = "brcm,bcm7445-pcie", .data = &generic_cfg }, { .compatible = "brcm,bcm7712-pcie", .data = &bcm7712_cfg }, - { .compatible = "brcm,bcm2712-pcie", .data = &bcm2712_cfg }, {}, }; @@ -2311,9 +1742,6 @@ static int brcm_pcie_probe(struct platform_device *pdev) pcie->gen = (ret < 0) ? 0 : ret; pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); - pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss"); - pcie->rcb_mps_mode = of_property_read_bool(np, "brcm,enable-mps-rcb"); - of_property_read_u32(np, "brcm,tperst-clk-ms", &pcie->tperst_clk_ms); pcie->rescal = devm_reset_control_get_optional_shared(&pdev->dev, "rescal"); if (IS_ERR(pcie->rescal)) @@ -2387,33 +1815,6 @@ static int brcm_pcie_probe(struct platform_device *pdev) dev_err(pcie->dev, "probe of internal MSI failed"); goto fail; } - } else if (pci_msi_enabled() && msi_np != pcie->np) { - /* Use RC_BAR1 for MIP access */ - u64 msi_pci_addr; - u64 msi_phys_addr; - - if (of_property_read_u64(msi_np, "brcm,msi-pci-addr", &msi_pci_addr)) { - dev_err(pcie->dev, "Unable to find MSI PCI address\n"); - ret = -EINVAL; - goto fail; - } - - if (of_property_read_u64(msi_np, "reg", &msi_phys_addr)) { - dev_err(pcie->dev, "Unable to find MSI physical address\n"); - ret = -EINVAL; - goto fail; - } - - writel(lower_32_bits(msi_pci_addr) | brcm_pcie_encode_ibar_size(0x1000), - pcie->base + PCIE_MISC_RC_BAR1_CONFIG_LO); - writel(upper_32_bits(msi_pci_addr), - pcie->base + PCIE_MISC_RC_BAR1_CONFIG_HI); - - writel(lower_32_bits(msi_phys_addr) | - PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK, - pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP); - writel(upper_32_bits(msi_phys_addr), - pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI); } bridge->ops = pcie->soc_base == BCM7425 ? &brcm7425_pcie_ops : &brcm_pcie_ops; From 01c90676c106252b52b18620e00083cc2028ef33 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Feb 2025 11:10:11 +0000 Subject: [PATCH 02/22] Revert "dt-bindings: PCI: brcmstb: add optional property - "brcm,tperst-clk-ms"" This reverts commit 385ee6b5cc8df9690be44bc3d4d193c921b53ce9. --- Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml index e4907ad30a4ae1..2ad1652c25848b 100644 --- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml @@ -103,14 +103,6 @@ properties: minItems: 1 maxItems: 3 - brcm,tperst-clk-ms: - category: optional - type: int - description: u32 giving the number of milliseconds to extend - the time between internal release of fundamental reset and - the deassertion of the external PERST# pin. This has the - effect of increasing the Tperst_clk phase of link init. - required: - compatible - reg From 2c7cb5e34cf1a73f62b935400286f5bb8d741391 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Feb 2025 11:11:06 +0000 Subject: [PATCH 03/22] Revert "irqchip: irq-bcm2712-mip: Support for 2712's MIP" This reverts commit 0f8b46185ab71d45ad79033232ad5e6fb8f3be4b. --- drivers/irqchip/Kconfig | 8 - drivers/irqchip/Makefile | 1 - drivers/irqchip/irq-bcm2712-mip.c | 323 ------------------------------ 3 files changed, 332 deletions(-) delete mode 100644 drivers/irqchip/irq-bcm2712-mip.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 09e6893d7ff94c..c11b9965c4ad9b 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -109,14 +109,6 @@ config I8259 bool select IRQ_DOMAIN -config BCM2712_MIP - bool "Broadcom 2712 MSI-X Interrupt Peripheral support" - depends on ARM_GIC - select GENERIC_IRQ_CHIP - select IRQ_DOMAIN - help - Enable support for the Broadcom BCM2712 MSI-X target peripheral. - config BCM6345_L1_IRQ bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 382f446bbf62ae..25e9ad29b8c4a5 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -63,7 +63,6 @@ obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o -obj-$(CONFIG_BCM2712_MIP) += irq-bcm2712-mip.o obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o diff --git a/drivers/irqchip/irq-bcm2712-mip.c b/drivers/irqchip/irq-bcm2712-mip.c deleted file mode 100644 index 2eaa3ac10cb62c..00000000000000 --- a/drivers/irqchip/irq-bcm2712-mip.c +++ /dev/null @@ -1,323 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2021 Raspberry Pi Ltd., All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#define MIP_INT_RAISED 0x00 -#define MIP_INT_CLEARED 0x10 -#define MIP_INT_CFGL_HOST 0x20 -#define MIP_INT_CFGH_HOST 0x30 -#define MIP_INT_MASKL_HOST 0x40 -#define MIP_INT_MASKH_HOST 0x50 -#define MIP_INT_MASKL_VPU 0x60 -#define MIP_INT_MASKH_VPU 0x70 -#define MIP_INT_STATUSL_HOST 0x80 -#define MIP_INT_STATUSH_HOST 0x90 -#define MIP_INT_STATUSL_VPU 0xa0 -#define MIP_INT_STATUSH_VPU 0xb0 - -struct mip_priv { - spinlock_t msi_map_lock; - spinlock_t hw_lock; - void * __iomem base; - phys_addr_t msg_addr; - u32 msi_base; /* The SGI number that MSIs start */ - u32 num_msis; /* The number of SGIs for MSIs */ - u32 msi_offset; /* Shift the allocated msi up by N */ - unsigned long *msi_map; -}; - -static void mip_mask_msi_irq(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void mip_unmask_msi_irq(struct irq_data *d) -{ - pci_msi_unmask_irq(d); - irq_chip_unmask_parent(d); -} - -static void mip_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) -{ - struct mip_priv *priv = irq_data_get_irq_chip_data(d); - - msg->address_hi = upper_32_bits(priv->msg_addr); - msg->address_lo = lower_32_bits(priv->msg_addr); - msg->data = d->hwirq; -} - -// The "bus-specific" irq_chip (the MIP doesn't _have_ to be used with PCIe) - -static struct irq_chip mip_msi_irq_chip = { - .name = "MIP-MSI", - .irq_unmask = mip_unmask_msi_irq, - .irq_mask = mip_mask_msi_irq, - .irq_eoi = irq_chip_eoi_parent, - .irq_set_affinity = irq_chip_set_affinity_parent, -}; - -static struct msi_domain_info mip_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX), - .chip = &mip_msi_irq_chip, -}; - -// The "middle" irq_chip (the hardware control part) - -static struct irq_chip mip_irq_chip = { - .name = "MIP", - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, - .irq_eoi = irq_chip_eoi_parent, - .irq_set_affinity = irq_chip_set_affinity_parent, - .irq_set_type = irq_chip_set_type_parent, - .irq_compose_msi_msg = mip_compose_msi_msg, -}; - - -// And a domain to connect it to its parent (the GIC) - -static int mip_irq_domain_alloc(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs, - void *args) -{ - struct mip_priv *priv = domain->host_data; - struct irq_fwspec fwspec; - struct irq_data *irqd; - int hwirq, ret, i; - - spin_lock(&priv->msi_map_lock); - - hwirq = bitmap_find_free_region(priv->msi_map, priv->num_msis, ilog2(nr_irqs)); - - spin_unlock(&priv->msi_map_lock); - - if (hwirq < 0) - return -ENOSPC; - - hwirq += priv->msi_offset; - fwspec.fwnode = domain->parent->fwnode; - fwspec.param_count = 3; - fwspec.param[0] = 0; - fwspec.param[1] = hwirq + priv->msi_base; - fwspec.param[2] = IRQ_TYPE_EDGE_RISING; - - ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec); - if (ret) - return ret; - - for (i = 0; i < nr_irqs; i++) { - irqd = irq_domain_get_irq_data(domain->parent, virq + i); - irqd->chip->irq_set_type(irqd, IRQ_TYPE_EDGE_RISING); - - irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, - &mip_irq_chip, priv); - irqd = irq_get_irq_data(virq + i); - irqd_set_single_target(irqd); - irqd_set_affinity_on_activate(irqd); - } - - return 0; -} - -static void mip_irq_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) -{ - struct irq_data *d = irq_domain_get_irq_data(domain, virq); - struct mip_priv *priv = irq_data_get_irq_chip_data(d); - - irq_domain_free_irqs_parent(domain, virq, nr_irqs); - d->hwirq -= priv->msi_offset; - - spin_lock(&priv->msi_map_lock); - - bitmap_release_region(priv->msi_map, d->hwirq, ilog2(nr_irqs)); - - spin_unlock(&priv->msi_map_lock); -} - -#if 0 -static int mip_irq_domain_activate(struct irq_domain *domain, - struct irq_data *d, bool reserve) -{ - struct mip_priv *priv = irq_data_get_irq_chip_data(d); - unsigned long flags; - unsigned int irq = d->hwirq; - void *__iomem reg = priv->base + - ((irq < 32) ? MIP_INT_MASKL_HOST : MIP_INT_MASKH_HOST); - u32 val; - - spin_lock_irqsave(&priv->hw_lock, flags); - val = readl(reg); - val &= ~(1 << (irq % 32)); // Clear the mask - writel(val, reg); - spin_unlock_irqrestore(&priv->hw_lock, flags); - return 0; -} - -static void mip_irq_domain_deactivate(struct irq_domain *domain, - struct irq_data *d) -{ - struct mip_priv *priv = irq_data_get_irq_chip_data(d); - unsigned long flags; - unsigned int irq = d->hwirq - priv->msi_base; - void *__iomem reg = priv->base + - ((irq < 32) ? MIP_INT_MASKL_HOST : MIP_INT_MASKH_HOST); - u32 val; - - spin_lock_irqsave(&priv->hw_lock, flags); - val = readl(reg); - val |= (1 << (irq % 32)); // Mask it out - writel(val, reg); - spin_unlock_irqrestore(&priv->hw_lock, flags); -} -#endif - -static const struct irq_domain_ops mip_irq_domain_ops = { - .alloc = mip_irq_domain_alloc, - .free = mip_irq_domain_free, - //.activate = mip_irq_domain_activate, - //.deactivate = mip_irq_domain_deactivate, -}; - -static int mip_init_domains(struct mip_priv *priv, - struct device_node *node) -{ - struct irq_domain *middle_domain, *msi_domain, *gic_domain; - struct device_node *gic_node; - - gic_node = of_irq_find_parent(node); - if (!gic_node) { - pr_err("Failed to find the GIC node\n"); - return -ENODEV; - } - - gic_domain = irq_find_host(gic_node); - if (!gic_domain) { - pr_err("Failed to find the GIC domain\n"); - return -ENXIO; - } - - middle_domain = irq_domain_add_hierarchy(gic_domain, 0, 0, NULL, - &mip_irq_domain_ops, - priv); - if (!middle_domain) { - pr_err("Failed to create the MIP middle domain\n"); - return -ENOMEM; - } - - msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), - &mip_msi_domain_info, - middle_domain); - if (!msi_domain) { - pr_err("Failed to create MSI domain\n"); - irq_domain_remove(middle_domain); - return -ENOMEM; - } - - return 0; -} - -static int __init mip_of_msi_init(struct device_node *node, - struct device_node *parent) -{ - struct mip_priv *priv; - struct resource res; - int ret; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - spin_lock_init(&priv->msi_map_lock); - spin_lock_init(&priv->hw_lock); - - ret = of_address_to_resource(node, 0, &res); - if (ret) { - pr_err("Failed to allocate resource\n"); - goto err_priv; - } - - if (of_property_read_u32(node, "brcm,msi-base-spi", &priv->msi_base)) { - pr_err("Unable to parse MSI base\n"); - ret = -EINVAL; - goto err_priv; - } - - if (of_property_read_u32(node, "brcm,msi-num-spis", &priv->num_msis)) { - pr_err("Unable to parse MSI numbers\n"); - ret = -EINVAL; - goto err_priv; - } - - if (of_property_read_u32(node, "brcm,msi-offset", &priv->msi_offset)) - priv->msi_offset = 0; - - if (of_property_read_u64(node, "brcm,msi-pci-addr", &priv->msg_addr)) { - pr_err("Unable to parse MSI address\n"); - ret = -EINVAL; - goto err_priv; - } - - priv->base = ioremap(res.start, resource_size(&res)); - if (!priv->base) { - pr_err("Failed to ioremap regs\n"); - ret = -ENOMEM; - goto err_priv; - } - - priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_msis), - sizeof(*priv->msi_map), - GFP_KERNEL); - if (!priv->msi_map) { - ret = -ENOMEM; - goto err_base; - } - - pr_debug("Registering %d msixs, starting at %d\n", - priv->num_msis, priv->msi_base); - - /* - * Begin with all MSI-Xs masked in for the host, masked out for the - * VPU, and edge-triggered. - */ - writel(0, priv->base + MIP_INT_MASKL_HOST); - writel(0, priv->base + MIP_INT_MASKH_HOST); - writel(~0, priv->base + MIP_INT_MASKL_VPU); - writel(~0, priv->base + MIP_INT_MASKH_VPU); - writel(~0, priv->base + MIP_INT_CFGL_HOST); - writel(~0, priv->base + MIP_INT_CFGH_HOST); - - ret = mip_init_domains(priv, node); - if (ret) { - pr_err("Failed to allocate msi_map\n"); - goto err_map; - } - - return 0; - -err_map: - kfree(priv->msi_map); - -err_base: - iounmap(priv->base); - -err_priv: - kfree(priv); - - pr_err("%s: failed - err %d\n", __func__, ret); - - return ret; -} -IRQCHIP_DECLARE(bcm_mip, "brcm,bcm2712-mip-intc", mip_of_msi_init); From b613d502f513fadbba0970e551c3d13298111d74 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Feb 2025 11:18:43 +0000 Subject: [PATCH 04/22] Revert "Revert "PCI: brcmstb: Configure HW CLKREQ# mode appropriate for downstream device"" This reverts commit 5d527cbce39a1c43b34079e0d0369c5cea62a6ca. --- drivers/pci/controller/pcie-brcmstb.c | 100 +++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index a93427cd70e28b..e733a27dc8df8e 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -48,6 +48,9 @@ #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00 +#define PCIE_RC_CFG_PRIV1_ROOT_CAP 0x4f8 +#define PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK 0xf8 + #define PCIE_RC_DL_MDIO_ADDR 0x1100 #define PCIE_RC_DL_MDIO_WR_DATA 0x1104 #define PCIE_RC_DL_MDIO_RD_DATA 0x1108 @@ -124,9 +127,12 @@ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8) #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2 +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK 0x200000 #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000 #define PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x00800000 - +#define PCIE_CLKREQ_MASK \ + (PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK | \ + PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK) #define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac #define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_EN_MASK BIT(0) @@ -1181,13 +1187,93 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) return 0; } +/* + * This extends the timeout period for an access to an internal bus. This + * access timeout may occur during L1SS sleep periods, even without the + * presence of a PCIe access. + */ +static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie) +{ + /* TIMEOUT register is two registers before RGR1_SW_INIT_1 */ + const unsigned int REG_OFFSET = PCIE_RGR1_SW_INIT_1(pcie) - 8; + u32 timeout_us = 4000000; /* 4 seconds, our setting for L1SS */ + + /* 7712 does not have this (RGR1) timer */ + if (pcie->soc_base == BCM7712) + return; + + /* Each unit in timeout register is 1/216,000,000 seconds */ + writel(216 * timeout_us, pcie->base + REG_OFFSET); +} + +static void brcm_config_clkreq(struct brcm_pcie *pcie) +{ + static const char err_msg[] = "invalid 'brcm,clkreq-mode' DT string\n"; + const char *mode = "default"; + u32 clkreq_cntl; + int ret, tmp; + + ret = of_property_read_string(pcie->np, "brcm,clkreq-mode", &mode); + if (ret && ret != -EINVAL) { + dev_err(pcie->dev, err_msg); + mode = "safe"; + } + + /* Start out assuming safe mode (both mode bits cleared) */ + clkreq_cntl = readl(pcie->base + HARD_DEBUG(pcie)); + clkreq_cntl &= ~PCIE_CLKREQ_MASK; + + if (strcmp(mode, "no-l1ss") == 0) { + /* + * "no-l1ss" -- Provides Clock Power Management, L0s, and + * L1, but cannot provide L1 substate (L1SS) power + * savings. If the downstream device connected to the RC is + * L1SS capable AND the OS enables L1SS, all PCIe traffic + * may abruptly halt, potentially hanging the system. + */ + clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; + /* + * We want to un-advertise L1 substates because if the OS + * tries to configure the controller into using L1 substate + * power savings it may fail or hang when the RC HW is in + * "no-l1ss" mode. + */ + tmp = readl(pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP); + u32p_replace_bits(&tmp, 2, PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK); + writel(tmp, pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP); + + } else if (strcmp(mode, "default") == 0) { + /* + * "default" -- Provides L0s, L1, and L1SS, but not + * compliant to provide Clock Power Management; + * specifically, may not be able to meet the Tclron max + * timing of 400ns as specified in "Dynamic Clock Control", + * section 3.2.5.2.2 of the PCIe spec. This situation is + * atypical and should happen only with older devices. + */ + clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK; + brcm_extend_rbus_timeout(pcie); + + } else { + /* + * "safe" -- No power savings; refclk is driven by RC + * unconditionally. + */ + if (strcmp(mode, "safe") != 0) + dev_err(pcie->dev, err_msg); + mode = "safe"; + } + writel(clkreq_cntl, pcie->base + HARD_DEBUG(pcie)); + + dev_info(pcie->dev, "clkreq-mode set to %s\n", mode); +} + static int brcm_pcie_start_link(struct brcm_pcie *pcie) { struct device *dev = pcie->dev; void __iomem *base = pcie->base; u16 nlw, cls, lnksta; bool ssc_good = false; - u32 tmp; int ret, i; /* Unassert the fundamental reset */ @@ -1214,6 +1300,8 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) return -ENODEV; } + brcm_config_clkreq(pcie); + if (pcie->gen) brcm_pcie_set_gen(pcie, pcie->gen); @@ -1232,14 +1320,6 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) pci_speed_string(pcie_link_speed[cls]), nlw, ssc_good ? "(SSC)" : "(!SSC)"); - /* - * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1 - * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1. - */ - tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); - tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; - writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); - return 0; } From 83051c11773eaf6c06157b7ca7b8b8de19d20f5f Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Feb 2025 11:36:32 +0000 Subject: [PATCH 05/22] arm64: dts: Drop downstream PCIe nodes that are about to be superceded About to add the upstream PCIe nodes, so remove the downstream ones to avoid duplicated nodes and build breakage. Signed-off-by: Dave Stevenson --- arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi | 190 ------------------- 1 file changed, 190 deletions(-) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi index 82d97d4af84bfe..579d16b77c452b 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi @@ -350,190 +350,6 @@ brcm,dma-channel-mask = <0x0fc0>; }; - // Single-lane Gen3 PCIe - // Outbound window at 0x14_000000-0x17_ffffff - pcie0: pcie@100000 { - compatible = "brcm,bcm2712-pcie"; - reg = <0x10 0x00100000 0x0 0x9310>; - device_type = "pci"; - max-link-speed = <2>; - #address-cells = <3>; - #interrupt-cells = <1>; - #size-cells = <2>; - /* - * Unused interrupts: - * 208: AER - * 215: NMI - * 216: PME - */ - interrupt-parent = <&gicv2>; - interrupts = , - ; - interrupt-names = "pcie", "msi"; - interrupt-map-mask = <0x0 0x0 0x0 0x7>; - interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 209 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &gicv2 GIC_SPI 210 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &gicv2 GIC_SPI 211 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &gicv2 GIC_SPI 212 - IRQ_TYPE_LEVEL_HIGH>; - resets = <&bcm_reset 5>, <&bcm_reset 42>, <&pcie_rescal>; - reset-names = "swinit", "bridge", "rescal"; - msi-controller; - msi-parent = <&pcie0>; - - ranges = <0x02000000 0x00 0x00000000 - 0x17 0x00000000 - 0x0 0xfffffffc>, - <0x43000000 0x04 0x00000000 - 0x14 0x00000000 - 0x3 0x00000000>; - - dma-ranges = <0x43000000 0x10 0x00000000 - 0x00 0x00000000 - 0x10 0x00000000>; - - status = "disabled"; - }; - - // Single-lane Gen3 PCIe - // Outbound window at 0x18_000000-0x1b_ffffff - pcie1: pcie@110000 { - compatible = "brcm,bcm2712-pcie"; - reg = <0x10 0x00110000 0x0 0x9310>; - device_type = "pci"; - max-link-speed = <2>; - #address-cells = <3>; - #interrupt-cells = <1>; - #size-cells = <2>; - /* - * Unused interrupts: - * 218: AER - * 225: NMI - * 226: PME - */ - interrupt-parent = <&gicv2>; - interrupts = , - ; - interrupt-names = "pcie", "msi"; - interrupt-map-mask = <0x0 0x0 0x0 0x7>; - interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 219 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &gicv2 GIC_SPI 220 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &gicv2 GIC_SPI 221 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &gicv2 GIC_SPI 222 - IRQ_TYPE_LEVEL_HIGH>; - resets = <&bcm_reset 7>, <&bcm_reset 43>, <&pcie_rescal>; - reset-names = "swinit", "bridge", "rescal"; - msi-controller; - msi-parent = <&mip1>; - - // 2GB, 32-bit, non-prefetchable at PCIe 00_80000000 - ranges = <0x02000000 0x00 0x80000000 - 0x1b 0x80000000 - 0x00 0x80000000>, - // 14GB, 64-bit, prefetchable at PCIe 04_00000000 - <0x43000000 0x04 0x00000000 - 0x18 0x00000000 - 0x03 0x80000000>; - - dma-ranges = <0x03000000 0x10 0x00000000 - 0x00 0x00000000 - 0x10 0x00000000>; - - status = "disabled"; - }; - - pcie_rescal: reset-controller@119500 { - compatible = "brcm,bcm7216-pcie-sata-rescal"; - reg = <0x10 0x00119500 0x0 0x10>; - #reset-cells = <0>; - }; - - // Quad-lane Gen3 PCIe - // Outbound window at 0x1c_000000-0x1f_ffffff - pcie2: pcie@120000 { - compatible = "brcm,bcm2712-pcie"; - reg = <0x10 0x00120000 0x0 0x9310>; - device_type = "pci"; - max-link-speed = <2>; - #address-cells = <3>; - #interrupt-cells = <1>; - #size-cells = <2>; - /* - * Unused interrupts: - * 228: AER - * 235: NMI - * 236: PME - */ - interrupt-parent = <&gicv2>; - interrupts = , - ; - interrupt-names = "pcie", "msi"; - interrupt-map-mask = <0x0 0x0 0x0 0x7>; - interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 229 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &gicv2 GIC_SPI 230 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &gicv2 GIC_SPI 231 - IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &gicv2 GIC_SPI 232 - IRQ_TYPE_LEVEL_HIGH>; - resets = <&bcm_reset 32>, <&bcm_reset 44>, <&pcie_rescal>; - reset-names = "swinit", "bridge", "rescal"; - msi-controller; - msi-parent = <&mip0>; - - // ~4GB, 32-bit, not-prefetchable at PCIe 00_00000000 - ranges = <0x02000000 0x00 0x00000000 - 0x1f 0x00000000 - 0x0 0xfffffffc>, - // 12GB, 64-bit, prefetchable at PCIe 04_00000000 - <0x43000000 0x04 0x00000000 - 0x1c 0x00000000 - 0x03 0x00000000>; - - // 64GB system RAM space at PCIe 10_00000000 - dma-ranges = <0x02000000 0x00 0x00000000 - 0x1f 0x00000000 - 0x00 0x00400000>, - <0x43000000 0x10 0x00000000 - 0x00 0x00000000 - 0x10 0x00000000>; - - status = "disabled"; - }; - - mip0: msi-controller@130000 { - compatible = "brcm,bcm2712-mip-intc"; - reg = <0x10 0x00130000 0x0 0xc0>; - msi-controller; - interrupt-controller; - #interrupt-cells = <2>; - brcm,msi-base-spi = <128>; - brcm,msi-num-spis = <64>; - brcm,msi-offset = <0>; - brcm,msi-pci-addr = <0xff 0xfffff000>; - }; - - mip1: msi-controller@131000 { - compatible = "brcm,bcm2712-mip-intc"; - reg = <0x10 0x00131000 0x0 0xc0>; - msi-controller; - interrupt-controller; - #interrupt-cells = <2>; - brcm,msi-base-spi = <247>; - /* Actually 20 total, but the others are - * both sparse and non-consecutive */ - brcm,msi-num-spis = <8>; - brcm,msi-offset = <8>; - brcm,msi-pci-addr = <0xff 0xffffe000>; - }; - syscon_piarbctl: syscon@400018 { compatible = "brcm,syscon-piarbctl", "syscon", "simple-mfd"; reg = <0x10 0x00400018 0x0 0x18>; @@ -591,12 +407,6 @@ status = "disabled"; }; - bcm_reset: reset-controller@1504318 { - compatible = "brcm,brcmstb-reset"; - reg = <0x10 0x01504318 0x0 0x30>; - #reset-cells = <1>; - }; - v3d: v3d@2000000 { compatible = "brcm,2712-v3d"; reg = <0x10 0x02000000 0x0 0x4000>, From a5f8df979b4765e496b491bc515a6b86dfc812de Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 14 Oct 2024 16:07:00 +0300 Subject: [PATCH 06/22] dt-bindings: interrupt-controller: Add bcm2712 MSI-X DT bindings Adds DT bindings for bcm2712 MSI-X interrupt peripheral controller. Signed-off-by: Stanimir Varbanov --- .../brcm,bcm2712-msix.yaml | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2712-msix.yaml diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2712-msix.yaml b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2712-msix.yaml new file mode 100644 index 00000000000000..c84614663b5d50 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2712-msix.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/brcm,bcm2712-msix.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom bcm2712 MSI-X Interrupt Peripheral support + +maintainers: + - Stanimir Varbanov + +description: + This interrupt controller is used to provide interrupt vectors to the + generic interrupt controller (GIC) on bcm2712. It will be used as + external MSI-X controller for PCIe root complex. + +allOf: + - $ref: /schemas/interrupt-controller/msi-controller.yaml# + +properties: + compatible: + const: brcm,bcm2712-mip + + reg: + items: + - description: Base register address + - description: PCIe message address + + "#msi-cells": + const: 0 + + brcm,msi-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Shift the allocated MSI's. + +unevaluatedProperties: false + +required: + - compatible + - reg + - msi-controller + - msi-ranges + +examples: + - | + #include + + axi { + #address-cells = <2>; + #size-cells = <2>; + + msi-controller@1000130000 { + compatible = "brcm,bcm2712-mip"; + reg = <0x10 0x00130000 0x00 0xc0>, + <0xff 0xfffff000 0x00 0x1000>; + msi-controller; + #msi-cells = <0>; + msi-ranges = <&gicv2 GIC_SPI 128 IRQ_TYPE_EDGE_RISING 64>; + }; + }; From 5ef951d5801e72e8153756e092bd99b25a20585f Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:10 +0200 Subject: [PATCH 07/22] dt-bindings: PCI: brcmstb: Update bindings for PCIe on bcm2712 Update brcmstb PCIe controller bindings with bcm2712 compatible. Signed-off-by: Stanimir Varbanov --- Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml index 2ad1652c25848b..29f0e1eb50961e 100644 --- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml @@ -14,6 +14,7 @@ properties: items: - enum: - brcm,bcm2711-pcie # The Raspberry Pi 4 + - brcm,bcm2712-pcie # Raspberry Pi 5 - brcm,bcm4908-pcie - brcm,bcm7211-pcie # Broadcom STB version of RPi4 - brcm,bcm7216-pcie # Broadcom 7216 Arm @@ -101,7 +102,10 @@ properties: reset-names: minItems: 1 - maxItems: 3 + items: + - enum: [perst, rescal] + - const: bridge + - const: swinit required: - compatible From fd5d96023dcdb9db3e81732ad618e53d4cd3df37 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:11 +0200 Subject: [PATCH 08/22] irqchip: Add Broadcom bcm2712 MSI-X interrupt controller Add an interrupt controller driver for MSI-X Interrupt Peripheral (MIP) hardware block found in bcm2712. The interrupt controller is used to handle MSI-X interrupts from peripherials behind PCIe endpoints like RP1 south bridge found in RPi5. There are two MIPs on bcm2712, the first has 64 consecutive SPIs assigned to 64 output vectors, and the second has 17 SPIs, but only 8 of them are consecutive starting at the 8th output vector. Signed-off-by: Stanimir Varbanov --- drivers/irqchip/Kconfig | 16 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-bcm2712-mip.c | 292 ++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 drivers/irqchip/irq-bcm2712-mip.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index c11b9965c4ad9b..ba916838b12741 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -109,6 +109,22 @@ config I8259 bool select IRQ_DOMAIN +config BCM2712_MIP + tristate "Broadcom BCM2712 MSI-X Interrupt Peripheral support" + depends on ARCH_BRCMSTB || COMPILE_TEST + default m if ARCH_BRCMSTB + depends on ARM_GIC + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN_HIERARCHY + select GENERIC_MSI_IRQ + select IRQ_MSI_LIB + help + Enable support for the Broadcom BCM2712 MSI-X target peripheral + (MIP) needed by brcmstb PCIe to handle MSI-X interrupts on + Raspberry Pi 5. + + If unsure say n. + config BCM6345_L1_IRQ bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 25e9ad29b8c4a5..411385c4f3ad64 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o +obj-$(CONFIG_BCM2712_MIP) += irq-bcm2712-mip.o obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o diff --git a/drivers/irqchip/irq-bcm2712-mip.c b/drivers/irqchip/irq-bcm2712-mip.c new file mode 100644 index 00000000000000..49a19db2d1e1b3 --- /dev/null +++ b/drivers/irqchip/irq-bcm2712-mip.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Raspberry Pi Ltd., All Rights Reserved. + * Copyright (c) 2024 SUSE + */ + +#include +#include +#include +#include +#include +#include + +#include "irq-msi-lib.h" + +#define MIP_INT_RAISE 0x00 +#define MIP_INT_CLEAR 0x10 +#define MIP_INT_CFGL_HOST 0x20 +#define MIP_INT_CFGH_HOST 0x30 +#define MIP_INT_MASKL_HOST 0x40 +#define MIP_INT_MASKH_HOST 0x50 +#define MIP_INT_MASKL_VPU 0x60 +#define MIP_INT_MASKH_VPU 0x70 +#define MIP_INT_STATUSL_HOST 0x80 +#define MIP_INT_STATUSH_HOST 0x90 +#define MIP_INT_STATUSL_VPU 0xa0 +#define MIP_INT_STATUSH_VPU 0xb0 + +/** + * struct mip_priv - MSI-X interrupt controller data + * @lock: Used to protect bitmap alloc/free + * @base: Base address of MMIO area + * @msg_addr: PCIe MSI-X address + * @msi_base: MSI base + * @num_msis: Count of MSIs + * @msi_offset: MSI offset + * @bitmap: A bitmap for hwirqs + * @parent: Parent domain (GIC) + * @dev: A device pointer + */ +struct mip_priv { + spinlock_t lock; + void __iomem *base; + u64 msg_addr; + u32 msi_base; + u32 num_msis; + u32 msi_offset; + unsigned long *bitmap; + struct irq_domain *parent; + struct device *dev; +}; + +static void mip_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct mip_priv *mip = irq_data_get_irq_chip_data(d); + + msg->address_hi = upper_32_bits(mip->msg_addr); + msg->address_lo = lower_32_bits(mip->msg_addr); + msg->data = d->hwirq; +} + +static struct irq_chip mip_middle_irq_chip = { + .name = "MIP", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_compose_msi_msg = mip_compose_msi_msg, +}; + +static int mip_alloc_hwirq(struct mip_priv *mip, unsigned int nr_irqs) +{ + guard(spinlock)(&mip->lock); + return bitmap_find_free_region(mip->bitmap, mip->num_msis, ilog2(nr_irqs)); +} + +static void mip_free_hwirq(struct mip_priv *mip, unsigned int hwirq, + unsigned int nr_irqs) +{ + guard(spinlock)(&mip->lock); + bitmap_release_region(mip->bitmap, hwirq, ilog2(nr_irqs)); +} + +static int mip_middle_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct mip_priv *mip = domain->host_data; + struct irq_fwspec fwspec = {0}; + unsigned int hwirq, i; + struct irq_data *irqd; + int irq, ret; + + irq = mip_alloc_hwirq(mip, nr_irqs); + if (irq < 0) + return irq; + + hwirq = irq + mip->msi_offset; + + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 3; + fwspec.param[0] = 0; + fwspec.param[1] = hwirq + mip->msi_base; + fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec); + if (ret) + goto err_free_hwirq; + + for (i = 0; i < nr_irqs; i++) { + irqd = irq_domain_get_irq_data(domain->parent, virq + i); + irqd->chip->irq_set_type(irqd, IRQ_TYPE_EDGE_RISING); + + ret = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &mip_middle_irq_chip, mip); + if (ret) + goto err_free; + + irqd = irq_get_irq_data(virq + i); + irqd_set_single_target(irqd); + irqd_set_affinity_on_activate(irqd); + } + + return 0; + +err_free: + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +err_free_hwirq: + mip_free_hwirq(mip, irq, nr_irqs); + return ret; +} + +static void mip_middle_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *irqd = irq_domain_get_irq_data(domain, virq); + struct mip_priv *mip; + unsigned int hwirq; + + if (!irqd) + return; + + mip = irq_data_get_irq_chip_data(irqd); + hwirq = irqd_to_hwirq(irqd); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + mip_free_hwirq(mip, hwirq - mip->msi_offset, nr_irqs); +} + +static const struct irq_domain_ops mip_middle_domain_ops = { + .select = msi_lib_irq_domain_select, + .alloc = mip_middle_domain_alloc, + .free = mip_middle_domain_free, +}; + +#define MIP_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_PCI_MSI_MASK_PARENT) + +#define MIP_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_MULTI_PCI_MSI | \ + MSI_FLAG_PCI_MSIX) + +static const struct msi_parent_ops mip_msi_parent_ops = { + .supported_flags = MIP_MSI_FLAGS_SUPPORTED, + .required_flags = MIP_MSI_FLAGS_REQUIRED, + .bus_select_token = DOMAIN_BUS_GENERIC_MSI, + .bus_select_mask = MATCH_PCI_MSI, + .prefix = "MIP-MSI-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static int mip_init_domains(struct mip_priv *mip, struct device_node *np) +{ + struct irq_domain *middle; + + middle = irq_domain_add_hierarchy(mip->parent, 0, mip->num_msis, np, + &mip_middle_domain_ops, mip); + if (!middle) + return -ENOMEM; + + irq_domain_update_bus_token(middle, DOMAIN_BUS_GENERIC_MSI); + middle->dev = mip->dev; + middle->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + middle->msi_parent_ops = &mip_msi_parent_ops; + + /* + * All MSI-X unmasked for the host, masked for the VPU, and edge-triggered. + */ + writel(0, mip->base + MIP_INT_MASKL_HOST); + writel(0, mip->base + MIP_INT_MASKH_HOST); + writel(~0, mip->base + MIP_INT_MASKL_VPU); + writel(~0, mip->base + MIP_INT_MASKH_VPU); + writel(~0, mip->base + MIP_INT_CFGL_HOST); + writel(~0, mip->base + MIP_INT_CFGH_HOST); + + return 0; +} + +static int mip_parse_dt(struct mip_priv *mip, struct device_node *np) +{ + struct of_phandle_args args; + u64 size; + int ret; + + ret = of_property_read_u32(np, "brcm,msi-offset", &mip->msi_offset); + if (ret) + mip->msi_offset = 0; + + ret = of_parse_phandle_with_args(np, "msi-ranges", "#interrupt-cells", + 0, &args); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "msi-ranges", args.args_count + 1, + &mip->num_msis); + if (ret) + goto err_put; + + ret = of_property_read_reg(np, 1, &mip->msg_addr, &size); + if (ret) + goto err_put; + + mip->msi_base = args.args[1]; + + mip->parent = irq_find_host(args.np); + if (!mip->parent) + ret = -EINVAL; + +err_put: + of_node_put(args.np); + return ret; +} + +static int __init mip_of_msi_init(struct device_node *node, struct device_node *parent) +{ + struct platform_device *pdev; + struct mip_priv *mip; + int ret; + + pdev = of_find_device_by_node(node); + of_node_put(node); + if (!pdev) + return -EPROBE_DEFER; + + mip = kzalloc(sizeof(*mip), GFP_KERNEL); + if (!mip) + return -ENOMEM; + + spin_lock_init(&mip->lock); + mip->dev = &pdev->dev; + + ret = mip_parse_dt(mip, node); + if (ret) + goto err_priv; + + mip->base = of_iomap(node, 0); + if (!mip->base) { + ret = -ENXIO; + goto err_priv; + } + + mip->bitmap = bitmap_zalloc(mip->num_msis, GFP_KERNEL); + if (!mip->bitmap) { + ret = -ENOMEM; + goto err_base; + } + + ret = mip_init_domains(mip, node); + if (ret) + goto err_map; + + dev_dbg(&pdev->dev, "MIP: MSI-X count: %u, base: %u, offset: %u, msg_addr: %llx\n", + mip->num_msis, mip->msi_base, mip->msi_offset, mip->msg_addr); + + return 0; + +err_map: + bitmap_free(mip->bitmap); +err_base: + iounmap(mip->base); +err_priv: + kfree(mip); + return ret; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(mip_msi) +IRQCHIP_MATCH("brcm,bcm2712-mip", mip_of_msi_init) +IRQCHIP_PLATFORM_DRIVER_END(mip_msi) +MODULE_DESCRIPTION("Broadcom BCM2712 MSI-X interrupt controller"); +MODULE_AUTHOR("Phil Elwell "); +MODULE_AUTHOR("Stanimir Varbanov "); +MODULE_LICENSE("GPL"); From 565baea5ecd1ac7c55b3117b0d4e16ca5a2724c6 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:12 +0200 Subject: [PATCH 09/22] PCI: brcmstb: Reuse config structure Instead of copying fields from pcie_cfg_data structure to brcm_pcie reference it directly. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelil --- drivers/pci/controller/pcie-brcmstb.c | 70 ++++++++++++--------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index e733a27dc8df8e..48b2747d8c9854 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -191,11 +191,11 @@ #define SSC_STATUS_PLL_LOCK_MASK 0x800 #define PCIE_BRCM_MAX_MEMC 3 -#define IDX_ADDR(pcie) ((pcie)->reg_offsets[EXT_CFG_INDEX]) -#define DATA_ADDR(pcie) ((pcie)->reg_offsets[EXT_CFG_DATA]) -#define PCIE_RGR1_SW_INIT_1(pcie) ((pcie)->reg_offsets[RGR1_SW_INIT_1]) -#define HARD_DEBUG(pcie) ((pcie)->reg_offsets[PCIE_HARD_DEBUG]) -#define INTR2_CPU_BASE(pcie) ((pcie)->reg_offsets[PCIE_INTR2_CPU_BASE]) +#define IDX_ADDR(pcie) ((pcie)->cfg->offsets[EXT_CFG_INDEX]) +#define DATA_ADDR(pcie) ((pcie)->cfg->offsets[EXT_CFG_DATA]) +#define PCIE_RGR1_SW_INIT_1(pcie) ((pcie)->cfg->offsets[RGR1_SW_INIT_1]) +#define HARD_DEBUG(pcie) ((pcie)->cfg->offsets[PCIE_HARD_DEBUG]) +#define INTR2_CPU_BASE(pcie) ((pcie)->cfg->offsets[PCIE_INTR2_CPU_BASE]) /* Rescal registers */ #define PCIE_DVT_PMU_PCIE_PHY_CTRL 0xc700 @@ -276,8 +276,6 @@ struct brcm_pcie { int gen; u64 msi_target_addr; struct brcm_msi *msi; - const int *reg_offsets; - enum pcie_soc_base soc_base; struct reset_control *rescal; struct reset_control *perst_reset; struct reset_control *bridge_reset; @@ -285,17 +283,14 @@ struct brcm_pcie { int num_memc; u64 memc_size[PCIE_BRCM_MAX_MEMC]; u32 hw_rev; - int (*perst_set)(struct brcm_pcie *pcie, u32 val); - int (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val); struct subdev_regulators *sr; bool ep_wakeup_capable; - bool has_phy; - u8 num_inbound_wins; + const struct pcie_cfg_data *cfg; }; static inline bool is_bmips(const struct brcm_pcie *pcie) { - return pcie->soc_base == BCM7435 || pcie->soc_base == BCM7425; + return pcie->cfg->soc_base == BCM7435 || pcie->cfg->soc_base == BCM7425; } /* @@ -855,7 +850,7 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie, * security considerations, and is not implemented in our modern * SoCs. */ - if (pcie->soc_base != BCM7712) + if (pcie->cfg->soc_base != BCM7712) add_inbound_win(b++, &n, 0, 0, 0); resource_list_for_each_entry(entry, &bridge->dma_ranges) { @@ -872,10 +867,10 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie, * That being said, each BARs size must still be a power of * two. */ - if (pcie->soc_base == BCM7712) + if (pcie->cfg->soc_base == BCM7712) add_inbound_win(b++, &n, size, cpu_start, pcie_start); - if (n > pcie->num_inbound_wins) + if (n > pcie->cfg->num_inbound_wins) break; } @@ -889,7 +884,7 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie, * that enables multiple memory controllers. As such, it can return * now w/o doing special configuration. */ - if (pcie->soc_base == BCM7712) + if (pcie->cfg->soc_base == BCM7712) return n; ret = of_property_read_variable_u64_array(pcie->np, "brcm,scb-sizes", pcie->memc_size, 1, @@ -1012,7 +1007,7 @@ static void set_inbound_win_registers(struct brcm_pcie *pcie, * 7712: * All of their BARs need to be set. */ - if (pcie->soc_base == BCM7712) { + if (pcie->cfg->soc_base == BCM7712) { /* BUS remap register settings */ reg_offset = brcm_ubus_reg_offset(i); tmp = lower_32_bits(cpu_addr) & ~0xfff; @@ -1036,15 +1031,15 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) int memc, ret; /* Reset the bridge */ - ret = pcie->bridge_sw_init_set(pcie, 1); + ret = pcie->cfg->bridge_sw_init_set(pcie, 1); if (ret) return ret; /* Ensure that PERST# is asserted; some bootloaders may deassert it. */ - if (pcie->soc_base == BCM2711) { - ret = pcie->perst_set(pcie, 1); + if (pcie->cfg->soc_base == BCM2711) { + ret = pcie->cfg->perst_set(pcie, 1); if (ret) { - pcie->bridge_sw_init_set(pcie, 0); + pcie->cfg->bridge_sw_init_set(pcie, 0); return ret; } } @@ -1052,7 +1047,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) usleep_range(100, 200); /* Take the bridge out of reset */ - ret = pcie->bridge_sw_init_set(pcie, 0); + ret = pcie->cfg->bridge_sw_init_set(pcie, 0); if (ret) return ret; @@ -1072,9 +1067,9 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) */ if (is_bmips(pcie)) burst = 0x1; /* 256 bytes */ - else if (pcie->soc_base == BCM2711) + else if (pcie->cfg->soc_base == BCM2711) burst = 0x0; /* 128 bytes */ - else if (pcie->soc_base == BCM7278) + else if (pcie->cfg->soc_base == BCM7278) burst = 0x3; /* 512 bytes */ else burst = 0x2; /* 512 bytes */ @@ -1199,7 +1194,7 @@ static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie) u32 timeout_us = 4000000; /* 4 seconds, our setting for L1SS */ /* 7712 does not have this (RGR1) timer */ - if (pcie->soc_base == BCM7712) + if (pcie->cfg->soc_base == BCM7712) return; /* Each unit in timeout register is 1/216,000,000 seconds */ @@ -1277,7 +1272,7 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) int ret, i; /* Unassert the fundamental reset */ - ret = pcie->perst_set(pcie, 0); + ret = pcie->cfg->perst_set(pcie, 0); if (ret) return ret; @@ -1463,12 +1458,12 @@ static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start) static inline int brcm_phy_start(struct brcm_pcie *pcie) { - return pcie->has_phy ? brcm_phy_cntl(pcie, 1) : 0; + return pcie->cfg->has_phy ? brcm_phy_cntl(pcie, 1) : 0; } static inline int brcm_phy_stop(struct brcm_pcie *pcie) { - return pcie->has_phy ? brcm_phy_cntl(pcie, 0) : 0; + return pcie->cfg->has_phy ? brcm_phy_cntl(pcie, 0) : 0; } static int brcm_pcie_turn_off(struct brcm_pcie *pcie) @@ -1479,7 +1474,7 @@ static int brcm_pcie_turn_off(struct brcm_pcie *pcie) if (brcm_pcie_link_up(pcie)) brcm_pcie_enter_l23(pcie); /* Assert fundamental reset */ - ret = pcie->perst_set(pcie, 1); + ret = pcie->cfg->perst_set(pcie, 1); if (ret) return ret; @@ -1582,7 +1577,7 @@ static int brcm_pcie_resume_noirq(struct device *dev) goto err_reset; /* Take bridge out of reset so we can access the SERDES reg */ - pcie->bridge_sw_init_set(pcie, 0); + pcie->cfg->bridge_sw_init_set(pcie, 0); /* SERDES_IDDQ = 0 */ tmp = readl(base + HARD_DEBUG(pcie)); @@ -1803,12 +1798,7 @@ static int brcm_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(bridge); pcie->dev = &pdev->dev; pcie->np = np; - pcie->reg_offsets = data->offsets; - pcie->soc_base = data->soc_base; - pcie->perst_set = data->perst_set; - pcie->bridge_sw_init_set = data->bridge_sw_init_set; - pcie->has_phy = data->has_phy; - pcie->num_inbound_wins = data->num_inbound_wins; + pcie->cfg = data; pcie->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pcie->base)) @@ -1843,7 +1833,7 @@ static int brcm_pcie_probe(struct platform_device *pdev) if (ret) return dev_err_probe(&pdev->dev, ret, "could not enable clock\n"); - pcie->bridge_sw_init_set(pcie, 0); + pcie->cfg->bridge_sw_init_set(pcie, 0); if (pcie->swinit_reset) { ret = reset_control_assert(pcie->swinit_reset); @@ -1882,7 +1872,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) goto fail; pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION); - if (pcie->soc_base == BCM4908 && pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) { + if (pcie->cfg->soc_base == BCM4908 && + pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) { dev_err(pcie->dev, "hardware revision with unsupported PERST# setup\n"); ret = -ENODEV; goto fail; @@ -1897,7 +1888,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) } } - bridge->ops = pcie->soc_base == BCM7425 ? &brcm7425_pcie_ops : &brcm_pcie_ops; + bridge->ops = pcie->cfg->soc_base == BCM7425 ? + &brcm7425_pcie_ops : &brcm_pcie_ops; bridge->sysdata = pcie; platform_set_drvdata(pdev, pcie); From f1de16bf15fdc90c2d30f3c03e3fbefe743d6e35 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 25 Oct 2024 15:45:10 +0300 Subject: [PATCH 10/22] PCI: brcmstb: Expand inbound window size up to 64GB BCM2712 memory map can support up to 64GB of system memory, thus expand the inbound window size in calculation helper function. The change is save for the currently supported SoCs that has smaller inbound window sizes. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli --- drivers/pci/controller/pcie-brcmstb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 48b2747d8c9854..59190d8be0fb87 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -304,8 +304,8 @@ static int brcm_pcie_encode_ibar_size(u64 size) if (log2_in >= 12 && log2_in <= 15) /* Covers 4KB to 32KB (inclusive) */ return (log2_in - 12) + 0x1c; - else if (log2_in >= 16 && log2_in <= 35) - /* Covers 64KB to 32GB, (inclusive) */ + else if (log2_in >= 16 && log2_in <= 36) + /* Covers 64KB to 64GB, (inclusive) */ return log2_in - 15; /* Something is awry so disable */ return 0; From 34e433585239d43006768254417f73138cceec61 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 25 Oct 2024 15:45:12 +0300 Subject: [PATCH 11/22] PCI: brcmstb: Add bcm2712 support Add bare minimum amount of changes in order to support PCIe RC hardware IP found on RPi5. The PCIe controller on bcm2712 is based on bcm7712 and as such it inherits register offsets, perst, 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. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli --- drivers/pci/controller/pcie-brcmstb.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 59190d8be0fb87..50607df34a6628 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -234,10 +234,20 @@ 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); @@ -1488,8 +1498,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->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 +1710,15 @@ 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, + .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 +1770,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 }, From 31c719be5827dbbdc30edbd39862b27005e9a230 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:15 +0200 Subject: [PATCH 12/22] PCI: brcmstb: Adjust 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 this 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 Reviewed-by: Florian Fainelli --- drivers/pci/controller/pcie-brcmstb.c | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 50607df34a6628..03396a9d97be1d 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -55,6 +55,10 @@ #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 @@ -251,6 +255,7 @@ struct pcie_cfg_data { 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 { @@ -826,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) { @@ -1189,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; } @@ -1715,6 +1758,7 @@ static const struct pcie_cfg_data bcm2712_cfg = { .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, }; From 8e755c35e2126ded02393c6ca622f143b61ce530 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:16 +0200 Subject: [PATCH 13/22] PCI: brcmstb: Adding a softdep to MIP MSI-X driver In case brcmstb PCIe driver and MIP MSI-X interrupt controller drivers are built as modules there could be a race in probing. To avoid this add a softdep to MIP driver to guarantee that MIP driver will be load first. Signed-off-by: Stanimir Varbanov --- drivers/pci/controller/pcie-brcmstb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 03396a9d97be1d..744fe1a4cf9c11 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1997,3 +1997,4 @@ module_platform_driver(brcm_pcie_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Broadcom STB PCIe RC driver"); MODULE_AUTHOR("Broadcom"); +MODULE_SOFTDEP("pre: irq_bcm2712_mip"); From cd081ad69a419b15fe36c4f588002a42e9b455f4 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:17 +0200 Subject: [PATCH 14/22] PCI: brcmstb: Fix for missing of_node_put A call to of_parse_phandle() increments refcount, of_node_put must be called when done the work on it. Fix missing of_node_put() on the msi_np device node by using scope based of_node_put() cleanups. Cc: stable@vger.kernel.org # v5.10+ Fixes: 40ca1bf580ef ("PCI: brcmstb: Add MSI support") Signed-off-by: Stanimir Varbanov --- drivers/pci/controller/pcie-brcmstb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 744fe1a4cf9c11..546056f7f0d32a 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1844,7 +1844,8 @@ static struct pci_ops brcm7425_pcie_ops = { static int brcm_pcie_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node, *msi_np; + struct device_node *msi_np __free(device_node) = NULL; + struct device_node *np = pdev->dev.of_node; struct pci_host_bridge *bridge; const struct pcie_cfg_data *data; struct brcm_pcie *pcie; From 7ceb6dc69d6a8496c921e76c74ddc0c6f85cf9dd Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:18 +0200 Subject: [PATCH 15/22] arm64: dts: broadcom: bcm2712: Add PCIe DT nodes Add PCIe devicetree nodes, plus needed reset and mip MSI-X controllers. Signed-off-by: Stanimir Varbanov --- arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 147 ++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi index 3debac358f92ba..4448d1f9067c63 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi @@ -192,6 +192,12 @@ #address-cells = <1>; #size-cells = <1>; + pcie_rescal: reset-controller@119500 { + compatible = "brcm,bcm7216-pcie-sata-rescal"; + reg = <0x00119500 0x10>; + #reset-cells = <0>; + }; + sdio1: mmc@fff000 { compatible = "brcm,bcm2712-sdhci", "brcm,sdhci-brcmstb"; @@ -204,6 +210,12 @@ mmc-ddr-3_3v; }; + bcm_reset: reset-controller@1504318 { + compatible = "brcm,brcmstb-reset"; + reg = <0x01504318 0x30>; + #reset-cells = <1>; + }; + system_timer: timer@7c003000 { compatible = "brcm,bcm2835-system-timer"; reg = <0x7c003000 0x1000>; @@ -426,6 +438,141 @@ vc4: gpu { compatible = "brcm,bcm2712-vc6"; }; + + pcie0: pcie@1000100000 { + compatible = "brcm,bcm2712-pcie"; + reg = <0x10 0x00100000 0x00 0x9310>; + device_type = "pci"; + linux,pci-domain = <0>; + max-link-speed = <2>; + num-lanes = <1>; + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + interrupt-parent = <&gicv2>; + interrupts = , + ; + interrupt-names = "pcie", "msi"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 209 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &gicv2 GIC_SPI 210 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &gicv2 GIC_SPI 211 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &gicv2 GIC_SPI 212 IRQ_TYPE_LEVEL_HIGH>; + resets = <&pcie_rescal>, <&bcm_reset 42>; + reset-names = "rescal", "bridge"; + msi-controller; + msi-parent = <&pcie0>; + + ranges = + /* ~4GiB, 32-bit, non-prefetchable at PCIe 00_0000_0000 */ + <0x02000000 0x00 0x00000000 0x17 0x00000000 0x00 0xfffffffc>, + /* 12GiB, 64-bit, prefetchable at PCIe 04_0000_0000 */ + <0x43000000 0x04 0x00000000 0x14 0x00000000 0x03 0x00000000>; + + dma-ranges = + /* 64GiB, 64-bit, prefetchable at PCIe 10_0000_0000 */ + <0x43000000 0x10 0x00000000 0x00 0x00000000 0x10 0x00000000>; + + status = "disabled"; + }; + + pcie1: pcie@1000110000 { + compatible = "brcm,bcm2712-pcie"; + reg = <0x10 0x00110000 0x00 0x9310>; + device_type = "pci"; + linux,pci-domain = <1>; + max-link-speed = <2>; + num-lanes = <1>; + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + interrupt-parent = <&gicv2>; + interrupts = , + ; + interrupt-names = "pcie", "msi"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &gicv2 GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &gicv2 GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &gicv2 GIC_SPI 222 IRQ_TYPE_LEVEL_HIGH>; + resets = <&pcie_rescal>, <&bcm_reset 43>; + reset-names = "rescal", "bridge"; + msi-controller; + msi-parent = <&mip1>; + + ranges = + /* ~4GiB, 32-bit, non-prefetchable at PCIe 00_0000_0000 */ + <0x02000000 0x00 0x00000000 0x1b 0x00000000 0x00 0xfffffffc>, + /* 12GiB, 64-bit, prefetchable at PCIe 04_0000_0000 */ + <0x43000000 0x04 0x00000000 0x18 0x00000000 0x03 0x00000000>; + + dma-ranges = + /* 64GiB, 64-bit, non-prefetchable at PCIe 10_0000_0000 */ + <0x03000000 0x10 0x00000000 0x00 0x00000000 0x10 0x00000000>, + /* 4KiB, 64-bit, non-prefetchable at PCIe ff_ffff_f000 MIP1 */ + <0x03000000 0xff 0xfffff000 0x10 0x00131000 0x00 0x00001000>; + + status = "disabled"; + }; + + pcie2: pcie@1000120000 { + compatible = "brcm,bcm2712-pcie"; + reg = <0x10 0x00120000 0x00 0x9310>; + device_type = "pci"; + linux,pci-domain = <2>; + max-link-speed = <2>; + num-lanes = <4>; + #address-cells = <3>; + #interrupt-cells = <1>; + #size-cells = <2>; + interrupt-parent = <&gicv2>; + interrupts = , + ; + interrupt-names = "pcie", "msi"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &gicv2 GIC_SPI 230 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &gicv2 GIC_SPI 231 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &gicv2 GIC_SPI 232 IRQ_TYPE_LEVEL_HIGH>; + resets = <&pcie_rescal>, <&bcm_reset 44>; + reset-names = "rescal", "bridge"; + msi-controller; + msi-parent = <&mip0>; + + ranges = + /* ~4GiB, 32-bit, non-prefetchable at PCIe 00_0000_0000 */ + <0x02000000 0x00 0x00000000 0x1f 0x00000000 0x00 0xfffffffc>, + /* 12GiB, 64-bit, prefetchable at PCIe 04_0000_0000 */ + <0x43000000 0x04 0x00000000 0x1c 0x00000000 0x03 0x00000000>; + + dma-ranges = + /* 4MiB, 32-bit, non-prefetchable at PCIe 00_0000_0000 */ + <0x02000000 0x00 0x00000000 0x1f 0x00000000 0x00 0x00400000>, + /* 64GiB, 64-bit, prefetchable at PCIe 10_0000_0000 */ + <0x43000000 0x10 0x00000000 0x00 0x00000000 0x10 0x00000000>, + /* 4KiB, 64-bit, non-prefetchable at PCIe ff_ffff_f000 MIP0 */ + <0x03000000 0xff 0xfffff000 0x10 0x00130000 0x00 0x00001000>; + + status = "disabled"; + }; + + mip0: msi-controller@1000130000 { + compatible = "brcm,bcm2712-mip"; + reg = <0x10 0x00130000 0x00 0xc0>, + <0xff 0xfffff000 0x00 0x1000>; + msi-controller; + msi-ranges = <&gicv2 GIC_SPI 128 IRQ_TYPE_EDGE_RISING 64>; + brcm,msi-offset = <0>; + }; + + mip1: msi-controller@1000131000 { + compatible = "brcm,bcm2712-mip"; + reg = <0x10 0x00131000 0x00 0xc0>, + <0xff 0xfffff000 0x00 0x1000>; + msi-controller; + msi-ranges = <&gicv2 GIC_SPI 247 IRQ_TYPE_EDGE_RISING 8>; + brcm,msi-offset = <8>; + }; }; timer { From 13996f24e6b695571c690fd369d8d3d87110a680 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:19 +0200 Subject: [PATCH 16/22] arm64: dts: broadcom: bcm2712-rpi-5-b: Enable PCIe DT nodes Enable pcie1 and pcie2 DT nodes. Pcie1 is used for the extension connector and pcie2 is used for RP1 south-bridge. Signed-off-by: Stanimir Varbanov --- arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts index d6f9105087d978..4257ab5d45338a 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -764,3 +764,11 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>; clock-names = "hdmi", "bvb", "audio", "cec"; }; + +&pcie1 { + status = "okay"; +}; + +&pcie2 { + status = "okay"; +}; From 619cf6f89717cdac80ca2a6ae32f172fd720a600 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Wed, 5 Feb 2025 14:12:01 -0500 Subject: [PATCH 17/22] PCI: brcmstb: Refactor max speed limit functionality Make changes to the code that limits the PCIe max speed. (1) Do the changes before link-up, not after. We do not want to temporarily rise to a higher speed than desired. (2) Use constants from pci_reg.h when possible (3) Use uXX_replace_bits(...) for setting a register field. (4) Use the internal link capabilities register for writing the max speed, not the official config space register where the speed field is RO. Updating this field is not necessary to limit the speed so this mistake was harmless. Signed-off-by: Jim Quinlan --- drivers/pci/controller/pcie-brcmstb.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 546056f7f0d32a..f8fc3d620ee228 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -47,6 +47,7 @@ #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00 +#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_MAX_LINK_SPEED_MASK 0xf #define PCIE_RC_CFG_PRIV1_ROOT_CAP 0x4f8 #define PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK 0xf8 @@ -413,12 +414,12 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie) static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen) { u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); - u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); + u32 lnkcap = readl(pcie->base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); - lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen; - writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); + u32p_replace_bits(&lnkcap, gen, PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_MAX_LINK_SPEED_MASK); + writel(lnkcap, pcie->base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); - lnkctl2 = (lnkctl2 & ~0xf) | gen; + u16p_replace_bits(&lnkctl2, gen, PCI_EXP_LNKCTL2_TLS); writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); } @@ -1324,6 +1325,10 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) bool ssc_good = false; int ret, i; + /* Limit the generation if specified */ + if (pcie->gen) + brcm_pcie_set_gen(pcie, pcie->gen); + /* Unassert the fundamental reset */ ret = pcie->cfg->perst_set(pcie, 0); if (ret) @@ -1350,9 +1355,6 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) brcm_config_clkreq(pcie); - if (pcie->gen) - brcm_pcie_set_gen(pcie, pcie->gen); - if (pcie->ssc) { ret = brcm_pcie_set_ssc(pcie); if (ret == 0) From 307210c6032fa32890226cf7a5cc5053ea4c6dea Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Wed, 5 Feb 2025 14:12:02 -0500 Subject: [PATCH 18/22] PCI: brcmstb: Fix error path upon call of regulator_bulk_get() If regulator_bulk_get() returns an error, no regulators are created and we need to set their number to zero. If we do not do this and the PCIe link-up fails, regulator_bulk_free() will be invoked and effect a panic. Also print out the error value, as we cannot return an error upwards as Linux will WARN on an error from add_bus(). Fixes: 9e6be018b263 ("PCI: brcmstb: Enable child bus device regulators from DT") Signed-off-by: Jim Quinlan --- drivers/pci/controller/pcie-brcmstb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index f8fc3d620ee228..bf919467cbcd2f 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1417,7 +1417,8 @@ static int brcm_pcie_add_bus(struct pci_bus *bus) ret = regulator_bulk_get(dev, sr->num_supplies, sr->supplies); if (ret) { - dev_info(dev, "No regulators for downstream device\n"); + dev_info(dev, "Did not get regulators; err=%d\n", ret); + sr->num_supplies = 0; goto no_regulators; } From e57364c4d089cca2047b84330096319a46b4c2ba Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Wed, 5 Feb 2025 14:12:03 -0500 Subject: [PATCH 19/22] PCI: brcmstb: Fix potential premature regluator disabling Our system for enabling and disabling regulators is designed to work only on the port driver below the root complex. The conditions to discriminate for this case should be the same when we are adding or removing the bus. Without this change the regulators may be disabled prematurely when a bus further down the tree is removed. Fixes: 9e6be018b263 ("PCI: brcmstb: Enable child bus device regulators from DT") Signed-off-by: Jim Quinlan --- drivers/pci/controller/pcie-brcmstb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index bf919467cbcd2f..4f5d751cbdd7c1 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1441,7 +1441,7 @@ static void brcm_pcie_remove_bus(struct pci_bus *bus) struct subdev_regulators *sr = pcie->sr; struct device *dev = &bus->dev; - if (!sr) + if (!sr || !bus->parent || !pci_is_root_bus(bus->parent)) return; if (regulator_bulk_disable(sr->num_supplies, sr->supplies)) From b5828822af9f6dc2a18e0d232574cf21ea0c9d24 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Wed, 5 Feb 2025 14:12:04 -0500 Subject: [PATCH 20/22] PCI: brcmstb: Use same constant table for config space access The constants EXT_CFG_DATA and EXT_CFG_INDEX vary by SOC. One of the map_bus methods used these constants, the other used different constants. Fortunately there was no problem because the SoCs that used the latter map_bus method all had the same register constants. Remove the redundant constants and adjust the code to use them. In addition, update EXT_CFG_DATA to use the 4k-page based config space access system, which is what the second map_bus method was already using. Signed-off-by: Jim Quinlan --- drivers/pci/controller/pcie-brcmstb.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 4f5d751cbdd7c1..2d1969d7fd30c9 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -151,9 +151,6 @@ #define MSI_INT_MASK_SET 0x10 #define MSI_INT_MASK_CLR 0x14 -#define PCIE_EXT_CFG_DATA 0x8000 -#define PCIE_EXT_CFG_INDEX 0x9000 - #define PCIE_RGR1_SW_INIT_1_PERST_MASK 0x1 #define PCIE_RGR1_SW_INIT_1_PERST_SHIFT 0x0 @@ -728,8 +725,8 @@ static void __iomem *brcm_pcie_map_bus(struct pci_bus *bus, /* For devices, write to the config space index register */ idx = PCIE_ECAM_OFFSET(bus->number, devfn, 0); - writel(idx, pcie->base + PCIE_EXT_CFG_INDEX); - return base + PCIE_EXT_CFG_DATA + PCIE_ECAM_REG(where); + writel(idx, base + IDX_ADDR(pcie)); + return base + DATA_ADDR(pcie) + PCIE_ECAM_REG(where); } static void __iomem *brcm7425_pcie_map_bus(struct pci_bus *bus, @@ -1712,7 +1709,7 @@ static void brcm_pcie_remove(struct platform_device *pdev) static const int pcie_offsets[] = { [RGR1_SW_INIT_1] = 0x9210, [EXT_CFG_INDEX] = 0x9000, - [EXT_CFG_DATA] = 0x9004, + [EXT_CFG_DATA] = 0x8000, [PCIE_HARD_DEBUG] = 0x4204, [PCIE_INTR2_CPU_BASE] = 0x4300, }; @@ -1720,7 +1717,7 @@ static const int pcie_offsets[] = { static const int pcie_offsets_bcm7278[] = { [RGR1_SW_INIT_1] = 0xc010, [EXT_CFG_INDEX] = 0x9000, - [EXT_CFG_DATA] = 0x9004, + [EXT_CFG_DATA] = 0x8000, [PCIE_HARD_DEBUG] = 0x4204, [PCIE_INTR2_CPU_BASE] = 0x4300, }; @@ -1734,8 +1731,9 @@ static const int pcie_offsets_bcm7425[] = { }; static const int pcie_offsets_bcm7712[] = { + [RGR1_SW_INIT_1] = 0x9210, [EXT_CFG_INDEX] = 0x9000, - [EXT_CFG_DATA] = 0x9004, + [EXT_CFG_DATA] = 0x8000, [PCIE_HARD_DEBUG] = 0x4304, [PCIE_INTR2_CPU_BASE] = 0x4400, }; From 113140f24a10b8bc238361f4ca1eaf6fb569763b Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Wed, 5 Feb 2025 14:12:05 -0500 Subject: [PATCH 21/22] PCI: brcmstb: Make two changes in MDIO register fields The HW team has decided to "tighten" some field definitions in the MDIO packet format. Fortunately these two changes may be made in a backwards compatible manner. The CMD field used to be 12 bits and now is one. This change is backwards compatible because the field's starting bit position is unchanged and the only commands we've used have values 0 and 1. The PORT field's width has been changed from four to five bits. When written, the new bit is not contiguous with the other four. Fortunately, this change is backwards compatible because we have never used anything other than 0 for the port field's value. Signed-off-by: Jim Quinlan --- drivers/pci/controller/pcie-brcmstb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 2d1969d7fd30c9..da7b100369485b 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -176,8 +176,9 @@ #define MDIO_PORT0 0x0 #define MDIO_DATA_MASK 0x7fffffff #define MDIO_PORT_MASK 0xf0000 +#define MDIO_PORT_EXT_MASK 0x200000 #define MDIO_REGAD_MASK 0xffff -#define MDIO_CMD_MASK 0xfff00000 +#define MDIO_CMD_MASK 0x00100000 #define MDIO_CMD_READ 0x1 #define MDIO_CMD_WRITE 0x0 #define MDIO_DATA_DONE_MASK 0x80000000 @@ -328,6 +329,7 @@ static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd) { u32 pkt = 0; + pkt |= FIELD_PREP(MDIO_PORT_EXT_MASK, port >> 4); pkt |= FIELD_PREP(MDIO_PORT_MASK, port); pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad); pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd); From bf17a33546aa0a783f195f45ec94dec69bedeee6 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Wed, 5 Feb 2025 14:12:06 -0500 Subject: [PATCH 22/22] PCI: brcmstb: Cast an int variable to an irq_hw_number_t Just make it clear to the reader that there is a conversion happening, in this case from an int type to an irq_hw_number_t, an unsigned long int. Signed-off-by: Jim Quinlan --- drivers/pci/controller/pcie-brcmstb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index da7b100369485b..1e24e7fc895ca2 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -560,7 +560,7 @@ static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, return hwirq; for (i = 0; i < nr_irqs; i++) - irq_domain_set_info(domain, virq + i, hwirq + i, + irq_domain_set_info(domain, virq + i, (irq_hw_number_t)hwirq + i, &brcm_msi_bottom_irq_chip, domain->host_data, handle_edge_irq, NULL, NULL); return 0;