From 326fc70a6d87a70d97feaa5ee5d93325e38e1ea6 Mon Sep 17 00:00:00 2001 From: Dom Cobley Date: Wed, 12 Mar 2025 13:16:16 +0000 Subject: [PATCH 01/35] Revert "PCI: brcmstb: don't use ASPM state defines for register bits" This reverts commit 0cdb55e73b46df959b80360d974af05ccc91e75e. --- drivers/pci/controller/pcie-brcmstb.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 4917c74617844c..30fcd288e61c9f 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1539,11 +1539,10 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) 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); From 42868279bccb7f37d295e44fb9c0d52404e7cc99 Mon Sep 17 00:00:00 2001 From: Dom Cobley Date: Wed, 12 Mar 2025 13:16:17 +0000 Subject: [PATCH 02/35] Revert "PCI: brcmstb: set link speed before deasserting fundamental reset" This reverts commit 7e5b7de37175fa79d119d815069c8f275cb53f08. --- drivers/pci/controller/pcie-brcmstb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 30fcd288e61c9f..e161aa45444f3c 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1715,9 +1715,6 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) u16 tmp16; int ret, i; - if (pcie->gen) - brcm_pcie_set_gen(pcie, pcie->gen); - /* Unassert the fundamental reset */ if (pcie->tperst_clk_ms) { /* @@ -1762,6 +1759,9 @@ 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 ff83139bb6ba457737ff4241134db63dc14587d0 Mon Sep 17 00:00:00 2001 From: Dom Cobley Date: Wed, 12 Mar 2025 13:16:18 +0000 Subject: [PATCH 03/35] Revert "PCI: brcmstb: Add BCM2712 support" This reverts commit cd75a07d03f0a8ab6574c399038b8eedb6c8a9f5. --- drivers/pci/controller/pcie-brcmstb.c | 660 ++------------------------ 1 file changed, 31 insertions(+), 629 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index e161aa45444f3c..40145a4d7a7e13 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,20 +1116,12 @@ 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); /* Don't advertise L0s capability if 'aspm-no-l0s' */ aspm_support = PCIE_LINK_STATE_L1; @@ -1548,33 +1132,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) 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). @@ -1624,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; @@ -1712,31 +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; /* 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 @@ -1757,8 +1214,6 @@ 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); @@ -1778,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; } @@ -1960,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); @@ -2161,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, @@ -2233,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 }, @@ -2250,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 }, {}, }; @@ -2310,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)) @@ -2386,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 a515fa68190fff3daa6db7ebef78db052c16a784 Mon Sep 17 00:00:00 2001 From: Dom Cobley Date: Wed, 12 Mar 2025 13:16:19 +0000 Subject: [PATCH 04/35] Revert "Revert "PCI: brcmstb: Configure HW CLKREQ# mode appropriate for downstream device"" This reverts commit c6c97d4132617524b656d72216875c64b3c1a448. --- 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 40145a4d7a7e13..9321280f6edbab 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 023ee645d7ee0e352b86d2023042741f4912550c Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:12 +0200 Subject: [PATCH 05/35] 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 9321280f6edbab..12bcc5919924d8 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 bb55b8cee810ef5105cff8f97aadbc61f3f237c9 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 24 Feb 2025 10:35:58 +0200 Subject: [PATCH 06/35] PCI: brcmstb: Expand inbound window size up to 64GB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BCM2712 memory map can support up to 64GB of system memory, thus expand the inbound window size in calculation helper function. The change is safe for the currently supported SoCs that have smaller inbound window sizes. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli Reviewed-by: Jim Quinlan Tested-by: Ivan T. Ivanov Link: https://lore.kernel.org/r/20250120130119.671119-6-svarbanov@suse.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- 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 12bcc5919924d8..c01462b07eeacd 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 1333792d783b6ca45665e3cb62bd8be0fe4e13b5 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 25 Oct 2024 15:45:12 +0300 Subject: [PATCH 07/35] 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 c01462b07eeacd..dc63d3ed0c9d68 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 c7b0b29b3b1da33f1b35ecdbf9a42ada351affd5 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:15 +0200 Subject: [PATCH 08/35] 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 dc63d3ed0c9d68..3cd286bf2739fb 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 dbc19f7179d8f6fab49506ce8b0e6ce4c1481750 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 24 Feb 2025 10:35:56 +0200 Subject: [PATCH 09/35] PCI: brcmstb: Adding a softdep to MIP MSI-X driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Florian Fainelli Tested-by: Ivan T. Ivanov Link: https://lore.kernel.org/r/20250120130119.671119-9-svarbanov@suse.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- 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 3cd286bf2739fb..862ce9b12f82c4 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 8a3ee200c7ef8c1b9e0bdb38deb70ae1e49afcfc Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:17 +0200 Subject: [PATCH 10/35] 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 862ce9b12f82c4..6f0e07c255cf58 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 2226a3a3099eae46064e7870af8bbca24fbbe3f4 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:29 -0500 Subject: [PATCH 11/35] PCI: brcmstb: Set gen limitation before link, not after When the user elects to limit the PCIe generation via the appropriate DT property, apply the settings before the PCIe link-up, not after. Fixes: c0452137034bda8f686dd9a2e167949bfffd6776 ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver") Signed-off-by: Jim Quinlan Fixes: c0452137034b ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver") Reviewed-by: Manivannan Sadhasivam Reviewed-by: Florian Fainelli --- drivers/pci/controller/pcie-brcmstb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 6f0e07c255cf58..2c3fa1389bf563 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1324,6 +1324,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 +1354,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 3150167704f8a89981ef0e94ce6254985142425d Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:30 -0500 Subject: [PATCH 12/35] PCI: brcmstb: Write to internal register to change link cap The driver was mistakenly writing to a RO config-space register (PCI_EXP_LNKCAP). Although harmless in this case, the proper destination is an internal RW register that is reflected by PCI_EXP_LNKCAP. Fixes: c0452137034bda8f686dd9a2e167949bfffd6776 ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver") Signed-off-by: Jim Quinlan Reviewed-by: Manivannan Sadhasivam 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 2c3fa1389bf563..4b515be4614b43 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -413,10 +413,10 @@ 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); + writel(lnkcap, pcie->base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); lnkctl2 = (lnkctl2 & ~0xf) | gen; writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); From 76d5ea65746a4b6e565081d1ebf3dfd9f4a78fef Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:31 -0500 Subject: [PATCH 13/35] PCI: brcmstb: Do not assume that reg field starts at LSB When setting a register field it was assumed that the field started at the lsb of the register. Although the masks do indeed start at the lsb, and this will probably not change, it is prudent to use a method that makes no assumption about the mask's placement in the register. The uXXp_replace_bits() calls are used since they are already prevalent in this driver. Signed-off-by: Jim Quinlan Reviewed-by: Manivannan Sadhasivam 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 4b515be4614b43..c19c857813fc3f 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -415,10 +415,10 @@ 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 + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); - lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen; + u32p_replace_bits(&lnkcap, gen, PCI_EXP_LNKCAP_SLS); 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); } From b5a0bbbbe97d1c808be24dc1c3069a9efb28ac76 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:32 -0500 Subject: [PATCH 14/35] 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 Reviewed-by: Florian Fainelli --- 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 c19c857813fc3f..969ea078876f1d 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1416,7 +1416,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); + pcie->sr = NULL; goto no_regulators; } From 9d33e07c01a633ebb648e5b01462044da8cb6c17 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:33 -0500 Subject: [PATCH 15/35] PCI: brcmstb: Fix potential premature regulator 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 Reviewed-by: Manivannan Sadhasivam Reviewed-by: Florian Fainelli --- 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 969ea078876f1d..39b34d4532b60c 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1440,7 +1440,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 551f47d0c7c5a9193475ed0fed269430cb0bd55b Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:34 -0500 Subject: [PATCH 16/35] 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 Reviewed-by: Florian Fainelli --- 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 39b34d4532b60c..d1214764f6784e 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -150,9 +150,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 @@ -727,8 +724,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, @@ -1711,7 +1708,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, }; @@ -1719,7 +1716,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, }; @@ -1733,8 +1730,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 90316ce65b90bf9f678ad9151f311cad13639ca0 Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:35 -0500 Subject: [PATCH 17/35] 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 Reviewed-by: Manivannan Sadhasivam Reviewed-by: Florian Fainelli --- 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 d1214764f6784e..3a980185b40758 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -175,8 +175,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 @@ -327,6 +328,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 170ebed6099321adb802e5e89362674d550e05ed Mon Sep 17 00:00:00 2001 From: Jim Quinlan Date: Fri, 14 Feb 2025 12:39:36 -0500 Subject: [PATCH 18/35] PCI: brcmstb: Clarify conversion of irq_domain_set_info() param 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 Reviewed-by: Manivannan Sadhasivam Reviewed-by: Florian Fainelli --- 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 3a980185b40758..d294ba07279544 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -559,7 +559,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; From ee4140228b5e213328315c92fd8dcdc3efbc4c2d Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Mon, 10 Feb 2025 15:14:22 +0000 Subject: [PATCH 19/35] PCI: brcmstb: set BCM7712/2712-specific AXI bridge handling behaviours These chips use a UBUS-AXI bridge component that has configurable timeout and error response handling. Suppress AXI error responses to CPU requests, otherwise these are fatal if they reach the ARM cluster, and set reasonably large timeouts for both Mem and Cfg requests. Signed-off-by: Jonathan Bell --- drivers/pci/controller/pcie-brcmstb.c | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index d294ba07279544..c8109bb1cf3282 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -209,6 +209,17 @@ #define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK 0x1 #define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT 0x0 +/* BCM7712/2712-specific registers */ +#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_RC_CONFIG_RETRY_TIMEOUT 0x405c + +#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170 + /* Forward declarations */ struct brcm_pcie; @@ -859,6 +870,30 @@ static int brcm_pcie_post_setup_bcm2712(struct brcm_pcie *pcie) tmp |= 0x12; writel(tmp, pcie->base + PCIE_RC_PL_PHY_CTL_15); + /* + * BCM7712/2712 uses a UBUS-AXI bridge. + * Suppress AXI error responses and return 1s for read failures. + */ + tmp = readl(pcie->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, pcie->base + PCIE_MISC_UBUS_CTRL); + writel(0xffffffff, pcie->base + PCIE_MISC_AXI_READ_ERROR_DATA); + + /* + * Adjust timeouts. The UBUS timeout also affects Configuration Request + * Retry responses, 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. + * + * If CRSSVE=1 this will stop the core from blocking on a Retry + * response, but does require the device to be well-behaved... + */ + writel(0xB2D0000, pcie->base + PCIE_MISC_UBUS_TIMEOUT); + writel(0xABA0000, pcie->base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT); + return 0; } From 583f142f4113e873d817b25ac9185dcbcd321987 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Tue, 7 Jan 2025 12:02:55 +0000 Subject: [PATCH 20/35] PCI: brcmstb: don't use ASPM state defines for register bits In commit b478e162f227 ("PCI/ASPM: Consolidate link state defines") PCIE_LINK_STATE_L1 and PCIE_LINK_STATE_L0s grew some bits for more granular control of ASPM. This broke the aspm-no-l0s override, instead disabling link ASPM completely if this DT property was specified. Specify the field bits in the driver. Fixes: caab002d5069 ("PCI: brcmstb: Disable L0s component of ASPM if requested") Fixes: 0693b4207fd7 ("PCI: brcmstb: Split post-link up initialization to brcm_pcie_start_link()") Signed-off-by: Jonathan Bell --- drivers/pci/controller/pcie-brcmstb.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index c8109bb1cf3282..b2d3a4e09690c4 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -47,6 +47,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_LINK_CAPABILITY_ASPM_SUPPORT_L0S 0x1 +#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_L1 0x2 +#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 @@ -1205,10 +1208,11 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie) pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB; - /* Don't advertise L0s capability if 'aspm-no-l0s' */ - aspm_support = PCIE_LINK_STATE_L1; + /* Always advertise L1 capability */ + aspm_support = PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_L1; + /* Advertise L0s capability unless 'aspm-no-l0s' is set */ if (!of_property_read_bool(pcie->np, "aspm-no-l0s")) - aspm_support |= PCIE_LINK_STATE_L0S; + aspm_support |= PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_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); From 501214e9c5ecb60d7881c17445eb98fa4550fe00 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Wed, 12 Feb 2025 15:47:08 +0000 Subject: [PATCH 21/35] PCI: brcmstb: Enable CRS software visibility after linkup It appears that bits in the Root Control Register are reset with perst_n, which means the PCI layer's call to enable CRS prior to adding/scanning the bus has no effect. Open-code the enable in brcm_pcie_start_link as a workaround. Without CRS visibility, configuration reads issued by the CPU don't retire if the endpoint returns a CRS response - the RC will poll until a (large) timeout is reached. This means the core can stall for a long time during boot. Signed-off-by: Jonathan Bell --- drivers/pci/controller/pcie-brcmstb.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index b2d3a4e09690c4..5ff637ff84623e 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1358,7 +1358,7 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) { struct device *dev = pcie->dev; void __iomem *base = pcie->base; - u16 nlw, cls, lnksta; + u16 nlw, cls, lnksta, tmp16; bool ssc_good = false; int ret, i; @@ -1407,6 +1407,17 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) pci_speed_string(pcie_link_speed[cls]), nlw, 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. + */ + 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); + } + return 0; } From a0a29505fffd468360f3e59d2c6627b2b447ac17 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Mon, 3 Mar 2025 15:01:28 +0000 Subject: [PATCH 22/35] PCI: brcmstb: add NO_SSC quirk for BCM2712 The PHY MDIO register map is different on BCM2712, and as the PHY input clock is 54MHz not 100MHz, enabling refclk SSC is both broken and unfixable. Mask out attempts to enable SSC with a controller quirk. Signed-off-by: Jonathan Bell --- drivers/pci/controller/pcie-brcmstb.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 5ff637ff84623e..5024b8bd0b836d 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -259,6 +259,12 @@ struct inbound_win { */ #define CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN BIT(0) +/* + * MDIO register map differences and required changes to the defaults mean that refclk + * spread-spectrum clocking isn't supportable. + */ +#define CFG_QUIRK_NO_SSC BIT(1) + struct pcie_cfg_data { const int *offsets; const enum pcie_soc_base soc_base; @@ -1811,7 +1817,7 @@ static const struct pcie_cfg_data bcm2712_cfg = { .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, + .quirks = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN | CFG_QUIRK_NO_SSC, .num_inbound_wins = 10, }; @@ -1929,7 +1935,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) ret = of_pci_get_max_link_speed(np); pcie->gen = (ret < 0) ? 0 : ret; - pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); + pcie->ssc = !(pcie->cfg->quirks & CFG_QUIRK_NO_SSC) && + of_property_read_bool(np, "brcm,enable-ssc"); pcie->rescal = devm_reset_control_get_optional_shared(&pdev->dev, "rescal"); if (IS_ERR(pcie->rescal)) From 89e7201562077bb44dfc7cf00c88ced95ec22ce7 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Tue, 11 Feb 2025 10:35:23 +0000 Subject: [PATCH 23/35] PCI: brcmstb: add support for BCM2712 priority forwarding The BCM2712 root complexes can interpret priority signalling in two different ways, based on the incoming Traffic Class of a TLP. The TLP TCs are assigned to separate internal request/response queues, and assigned different AXI IDs. These queues can have outgoing AXI transactions tagged based on: - Static QoS values - Dynamic QoS through internal backpressure - Dynamic QoS with elevation based on Vendor Messages received by the RC The VDM mechanism is of limited use due to implementation bugs, but the implicit reordering due to separate ID assignment allows higher-priority traffic from an EP to overtake other traffic in the RC and rest of the system. RP1 assigns TCs based on its internal bus managers, and internally tags read requests to allow out-of-order completions, so these two features operate in concert to provide priority service to e.g. MIPI camera or display traffic. Signed-off-by: Jonathan Bell --- drivers/pci/controller/pcie-brcmstb.c | 120 ++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 5024b8bd0b836d..6296154e273063 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -213,6 +213,22 @@ #define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT 0x0 /* BCM7712/2712-specific registers */ + +#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_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) @@ -221,6 +237,31 @@ #define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c +/* 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 /* Forward declarations */ @@ -856,6 +897,7 @@ static int brcm_pcie_post_setup_bcm2712(struct brcm_pcie *pcie) const u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e }; int ret, i; u32 tmp; + u8 qos_map[8]; /* Allow a 54MHz (xosc) refclk source */ ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, 0x1600); @@ -903,6 +945,84 @@ static int brcm_pcie_post_setup_bcm2712(struct brcm_pcie *pcie) writel(0xB2D0000, pcie->base + PCIE_MISC_UBUS_TIMEOUT); writel(0xABA0000, pcie->base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT); + /* + * BCM2712 has a configurable QoS mechanism that assigns TLP Traffic Classes + * to separate AXI IDs with a configurable priority scheme. + * Dynamic priority elevation is supported through reception of Type 1 + * Vendor Defined Messages, but several bugs make this largely ineffective. + */ + + /* Disable broken forwarding search. Set chicken bits for 2712D0 */ + tmp = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL); + tmp &= ~AXI_REQFIFO_EN_QOS_PROPAGATION; + tmp |= AXI_EN_RCLK_QOS_ARRAY_FIX | AXI_EN_QOS_UPDATE_TIMING_FIX | + AXI_DIS_QOS_GATING_IN_MASTER; + writel(tmp, pcie->base + PCIE_MISC_AXI_INTF_CTRL); + + /* + * Work around spurious QoS=0 assignments to inbound traffic. + * 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 SDRAM. + */ + tmp = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL); + if (!(tmp & AXI_EN_QOS_UPDATE_TIMING_FIX)) { + tmp &= ~AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK; + tmp |= 15; + writel(tmp, pcie->base + PCIE_MISC_AXI_INTF_CTRL); + } + + /* Disable VDM reception by default */ + tmp = readl(pcie->base + PCIE_MISC_CTRL_1); + tmp &= ~PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK; + writel(tmp, pcie->base + PCIE_MISC_CTRL_1); + + if (!of_property_read_u8_array(pcie->np, "brcm,fifo-qos-map", qos_map, 4)) { + + /* + * Backpressure mode - each element is QoS for each + * quartile of FIFO level. Each TC gets the same map, because + * this mode is intended for nonrealtime EPs. + */ + tmp = 0; + for (i = 0; i < 4; i++) { + /* Priorities range from 0-15 */ + qos_map[i] &= 0x0f; + tmp |= qos_map[i] << (i * 4); + } + for (i = 0; i < 8; i++) + writel(tmp, pcie->base + PCIE_MISC_TC_QUEUE_TO_QOS_MAP(i)); + + } else if (!of_property_read_u8_array(pcie->np, "brcm,vdm-qos-map", qos_map, 8)) { + /* Enable VDM reception */ + tmp = readl(pcie->base + PCIE_MISC_CTRL_1); + tmp |= PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK; + writel(tmp, pcie->base + PCIE_MISC_CTRL_1); + + tmp = 0; + for (i = 0; i < 8; i++) { + /* Priorities range from 0-15 */ + qos_map[i] &= 0x0f; + tmp |= qos_map[i] << (i * 4); + } + /* Broken forwarding means no point separating panic priorities from normal */ + writel(tmp, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO); + writel(tmp, 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 anyway - + * useful for debugging starvation through the received VDM count fields. + */ + tmp = readl(pcie->base + PCIE_RC_TL_VDM_CTL0); + tmp |= 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(tmp, pcie->base + PCIE_RC_TL_VDM_CTL0); + } + return 0; } From 4a5ee62f7abce4afa0f627d8f73687e7333661f2 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Thu, 13 Feb 2025 16:46:49 +0000 Subject: [PATCH 24/35] PCI: pcie-brcmstb: optionally extend Tperst_clk time Some endpoints need longer than the minimum Tperst_clk time of 100us that the PCIe specification allows for, as they may need to sequence internal resets off the stable output of internal PLLs prior to removal of fundamental reset. PCIe switches are an especially bad case, in some cases requiring up to 100 milliseconds for stable downstream link behaviour. Parse the DT property brcm,tperst-clk-ms and use this to hold PERST# low during brcm_pcie_start_link(). The BRCM RC typically outputs 200us of stable refclk before deasserting PERST#. By masking/forcing the output signal while deasserting the internal reset, the effect is to extend the length of time that the refclk is active and stable before PERST# is released. The TX lanes will enter the Polling state before PERST# is released, but this appears to be harmless. Signed-off-by: Jonathan Bell --- drivers/pci/controller/pcie-brcmstb.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 6296154e273063..837c9e653d9fa8 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -134,6 +134,7 @@ 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 @@ -360,6 +361,7 @@ struct brcm_pcie { struct subdev_regulators *sr; bool ep_wakeup_capable; const struct pcie_cfg_data *cfg; + u32 tperst_clk_ms; }; static inline bool is_bmips(const struct brcm_pcie *pcie) @@ -1487,13 +1489,32 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) u16 nlw, cls, lnksta, tmp16; bool ssc_good = false; int ret, i; + u32 tmp; /* 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 (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(pcie->base + HARD_DEBUG(pcie)); + u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK); + writel(tmp, pcie->base + HARD_DEBUG(pcie)); + + ret = pcie->cfg->perst_set(pcie, 0); + fsleep(pcie->tperst_clk_ms * USEC_PER_MSEC); + + tmp = readl(pcie->base + HARD_DEBUG(pcie)); + u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK); + writel(tmp, pcie->base + HARD_DEBUG(pcie)); + } else { + ret = pcie->cfg->perst_set(pcie, 0); + } if (ret) return ret; @@ -2058,6 +2079,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) pcie->ssc = !(pcie->cfg->quirks & CFG_QUIRK_NO_SSC) && of_property_read_bool(np, "brcm,enable-ssc"); + 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)) return PTR_ERR(pcie->rescal); From b20d82a35f673547379af353e597f7909d8f0ffd Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 24 Feb 2025 10:35:54 +0200 Subject: [PATCH 25/35] dt-bindings: PCI: brcmstb: Update bindings for PCIe on BCM2712 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update PCIe controller bindings with BCM2712 support. Signed-off-by: Stanimir Varbanov Acked-by: Rob Herring (Arm) Reviewed-by: Florian Fainelli Tested-by: Ivan T. Ivanov Link: https://lore.kernel.org/r/20250120130119.671119-3-svarbanov@suse.de [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński --- 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 be9d8bc92e7643..b8c94d3341c8a2 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 @@ -102,7 +103,10 @@ properties: reset-names: minItems: 1 - maxItems: 3 + items: + - enum: [perst, rescal] + - const: bridge + - const: swinit brcm,tperst-clk-ms: category: optional From 1e7c90a2c6d047a620fe0375007fad3f1a3c531d Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Tue, 11 Feb 2025 13:49:48 +0000 Subject: [PATCH 26/35] dt-bindings: pci: pcie-brcmstb: add BCM2712-specific properties There is configurable priority forwarding hardware in this variant of the Root Complex controller. Add optional properties to configure FIFO backpressure or Vendor-Defined Message priority forwarding. Signed-off-by: Jonathan Bell --- .../bindings/pci/brcm,stb-pcie.yaml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml index b8c94d3341c8a2..d574e7676dc862 100644 --- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml @@ -84,6 +84,14 @@ properties: $ref: /schemas/types.yaml#/definitions/string enum: [ safe, no-l1ss, default ] + brcm,fifo-qos-map: + description: Array of u8 elements which assigns every per-TC FIFOs + an AXI priority based on fullness quartile (backpressure signalling). + Mutually exclusive with vdm-qos-map. + $ref: /schemas/types.yaml#/definitions/uint8-array + minItems: 4 + maxItems: 4 + brcm,scb-sizes: description: u64 giving the 64bit PCIe memory viewport size of a memory controller. There may be up to @@ -97,6 +105,15 @@ properties: minItems: 1 maxItems: 3 + brcm,vdm-qos-map: + description: Array of u8 elements which assigns each per-TC FIFO + a base AXI priority with automatic elevation depending on + Vendor Messages from the EP - specifically, RP1. + Mutually exclusive with fifo-qos-map. + $ref: /schemas/types.yaml#/definitions/uint8-array + minItems: 8 + maxItems: 8 + resets: minItems: 1 maxItems: 3 @@ -187,6 +204,17 @@ allOf: - resets - reset-names + - if: + not: + properties: + compatible: + contains: + const: brcm,bcm2712-pcie + then: + properties: + brcm,fifo-qos-map: false + brcm,vdm-qos-map: false + unevaluatedProperties: false examples: From 9f8aa92bcf8db4f6235bc546b4fd6e57f53098b0 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Thu, 13 Feb 2025 16:36:57 +0000 Subject: [PATCH 27/35] dt-bindings: pci: pcie-brcmstb: add optional brcm,tperst-clk-ms property Some platforms may require an extended time with refclk active before PERST# is released. Add a property to let the RC driver know how long to wait. Signed-off-by: Jonathan Bell --- Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml index d574e7676dc862..081fe2185ce9e5 100644 --- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml @@ -105,6 +105,12 @@ properties: minItems: 1 maxItems: 3 + brcm,tperst-clk-ms: + 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. + brcm,vdm-qos-map: description: Array of u8 elements which assigns each per-TC FIFO a base AXI priority with automatic elevation depending on From f2630dabefa1af2dbcbc0f44aaf7571a238bdef8 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Wed, 5 Mar 2025 10:31:50 +0000 Subject: [PATCH 28/35] DT: bcm2712: swap PCIe QoS properties to the new array type pcie1 should use the FIFO threshold property as the RC should not pay attention to Vendor Messages from incompatible endpoint hardware. Also drop the downstream MPS property, it's no longer needed. Signed-off-by: Jonathan Bell --- arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi | 9 +++++++++ arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 6 ------ arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 6 ------ 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi index e60ac2cc841e53..3bc9e0e8f3fafc 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-ds.dtsi @@ -674,3 +674,12 @@ &vc4 { status = "disabled"; }; + +&pcie1 { + brcm,fifo-qos-map = /bits/ 8 <3 3 3 3>; + status = "disabled"; +}; + +&pcie2 { + brcm,vdm-qos-map = /bits/ 8 <8 8 8 9 10 10 11 11>; +}; \ No newline at end of file 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 22896ca859f684..8e7258ee6e79c9 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -127,16 +127,10 @@ }; rp1_target: &pcie2 { - brcm,enable-mps-rcb; - brcm,vdm-qos-map = <0xbbaa9888>; aspm-no-l0s; status = "okay"; }; -&pcie1 { - brcm,vdm-qos-map = <0x33333333>; -}; - // Add some labels to 2712 device // The system UART diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi index d8302ff6d91cdd..7a7138b5699a02 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi @@ -112,16 +112,10 @@ }; rp1_target: &pcie2 { - brcm,enable-mps-rcb; - brcm,vdm-qos-map = <0xbbaa9888>; aspm-no-l0s; status = "okay"; }; -&pcie1 { - brcm,vdm-qos-map = <0x33333333>; -}; - // Add some labels to 2712 device // The system UART From b487ba913c56e7ec5a3856b1db6f73f5614bd550 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:19 +0200 Subject: [PATCH 29/35] 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 8e7258ee6e79c9..1e9d099b66401c 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -715,3 +715,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 f0b3baf68eae60f522c6984c74dff00fa22c0e11 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 5 Feb 2025 09:22:25 +0000 Subject: [PATCH 30/35] arm64: dts: Clean up the downstream patches Remove some gratuitous differences with the upstream dts, and drop the unnecessary 'status = "okay"' properties (they are needed(*) to override 'status = "disabled"' in the original definitions. (*) You could technically delete the original properties, but that looks worse and doesn't work in an overlay. Signed-off-by: Phil Elwell --- .../boot/dts/broadcom/bcm2712-rpi-5-b.dts | 33 ++++++++----------- .../boot/dts/broadcom/bcm2712-rpi-cm5.dtsi | 25 +++++++------- arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi | 6 ---- 3 files changed, 25 insertions(+), 39 deletions(-) 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 1e9d099b66401c..ee759bfaa8f4ad 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: (GPL-2.0 OR MIT) /dts-v1/; #include @@ -42,7 +42,7 @@ }; }; - sd_io_1v8_reg: sd_io_1v8_reg { + sd_io_1v8_reg: sd-io-1v8-reg { compatible = "regulator-gpio"; regulator-name = "vdd-sd-io"; regulator-min-microvolt = <1800000>; @@ -51,12 +51,11 @@ regulator-always-on; regulator-settling-time-us = <5000>; gpios = <&gio_aon 3 GPIO_ACTIVE_HIGH>; - states = <1800000 0x1 - 3300000 0x0>; - status = "okay"; + states = <1800000 1>, + <3300000 0>; }; - sd_vcc_reg: sd_vcc_reg { + sd_vcc_reg: sd-vcc-reg { compatible = "regulator-fixed"; regulator-name = "vcc-sd"; regulator-min-microvolt = <3300000>; @@ -64,10 +63,9 @@ regulator-boot-on; enable-active-high; gpios = <&gio_aon 4 GPIO_ACTIVE_HIGH>; - status = "okay"; }; - wl_on_reg: wl_on_reg { + wl_on_reg: wl-on-reg { compatible = "regulator-fixed"; regulator-name = "wl-on-regulator"; regulator-min-microvolt = <3300000>; @@ -97,7 +95,6 @@ compatible = "regulator-fixed"; regulator-name = "cam0_reg"; enable-active-high; - status = "okay"; gpio = <&rp1_gpio 34 0>; // CD0_IO0_MICCLK, to MIPI 0 connector }; @@ -105,14 +102,12 @@ compatible = "regulator-fixed"; regulator-name = "cam1_reg"; enable-active-high; - status = "okay"; gpio = <&rp1_gpio 46 0>; // CD1_IO0_MICCLK, to MIPI 1 connector }; cam_dummy_reg: cam_dummy_reg { compatible = "regulator-fixed"; regulator-name = "cam-dummy-reg"; - status = "okay"; }; dummy: dummy { @@ -131,11 +126,6 @@ rp1_target: &pcie2 { status = "okay"; }; -// Add some labels to 2712 device - -// The system UART -&uart10 { status = "okay"; }; - // The system SPI for the bootloader EEPROM &spi10 { status = "okay"; }; @@ -188,6 +178,13 @@ rp1_target: &pcie2 { }; }; +/* The Debug UART, on Rpi5 it's on JST-SH 1.0mm 3-pin connector + * labeled "UART", i.e. the interface with the system console. + */ +&uart10 { + status = "okay"; +}; + gpio: &rp1_gpio { status = "okay"; }; @@ -716,10 +713,6 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; clock-names = "hdmi", "bvb", "audio", "cec"; }; -&pcie1 { - status = "okay"; -}; - &pcie2 { status = "okay"; }; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi index 7a7138b5699a02..94f8d4b789d052 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: (GPL-2.0 OR MIT) #include #include @@ -41,7 +41,7 @@ }; }; - sd_io_1v8_reg: sd_io_1v8_reg { + sd_io_1v8_reg: sd-io-1v8-reg { compatible = "regulator-fixed"; regulator-name = "vdd-sd-io"; regulator-min-microvolt = <1800000>; @@ -49,7 +49,7 @@ regulator-always-on; }; - sd_vcc_reg: sd_vcc_reg { + sd_vcc_reg: sd-vcc-reg { compatible = "regulator-fixed"; regulator-name = "vcc-sd"; regulator-min-microvolt = <3300000>; @@ -57,10 +57,9 @@ regulator-boot-on; enable-active-high; gpios = <&gio_aon 4 GPIO_ACTIVE_HIGH>; - status = "okay"; }; - wl_on_reg: wl_on_reg { + wl_on_reg: wl-on-reg { compatible = "regulator-fixed"; regulator-name = "wl-on-regulator"; regulator-min-microvolt = <3300000>; @@ -90,14 +89,12 @@ compatible = "regulator-fixed"; regulator-name = "cam0_reg"; enable-active-high; - status = "okay"; - gpio = <&rp1_gpio 34 0>; // CD0_IO0_MICCLK, to CAM_GPIO on connector + gpio = <&rp1_gpio 34 0>; // CD0_IO0_MICCLK, to CAM_GPIO on connector }; cam_dummy_reg: cam_dummy_reg { compatible = "regulator-fixed"; regulator-name = "cam-dummy-reg"; - status = "okay"; }; dummy: dummy { @@ -116,11 +113,6 @@ rp1_target: &pcie2 { status = "okay"; }; -// Add some labels to 2712 device - -// The system UART -&uart10 { status = "okay"; }; - // The system SPI for the bootloader EEPROM &spi10 { status = "okay"; }; @@ -175,6 +167,13 @@ rp1_target: &pcie2 { }; }; +/* The Debug UART, on Rpi5 it's on JST-SH 1.0mm 3-pin connector + * labeled "UART", i.e. the interface with the system console. + */ +&uart10 { + status = "okay"; +}; + gpio: &rp1_gpio { status = "okay"; }; diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi index 54a13e1f759764..5cb04f345200cb 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi.dtsi @@ -35,13 +35,11 @@ fb: fb { compatible = "brcm,bcm2708-fb"; firmware = <&firmware>; - status = "okay"; }; rpi_rtc: rpi_rtc { compatible = "raspberrypi,rpi-rtc"; firmware = <&firmware>; - status = "okay"; trickle-charge-microvolt = <0>; }; @@ -54,28 +52,24 @@ compatible = "raspberrypi,rpi-otp"; firmware = <&firmware>; reg = <0 192>; - status = "okay"; }; nvmem_cust: nvmem_cust { compatible = "raspberrypi,rpi-otp"; firmware = <&firmware>; reg = <1 8>; - status = "okay"; }; nvmem_mac: nvmem_mac { compatible = "raspberrypi,rpi-otp"; firmware = <&firmware>; reg = <2 6>; - status = "okay"; }; nvmem_priv: nvmem_priv { compatible = "raspberrypi,rpi-otp"; firmware = <&firmware>; reg = <3 16>; - status = "okay"; }; }; From 72b74f8f3bcb33141acd4b5153a3aeff8de43b53 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Thu, 6 Feb 2025 11:36:32 +0000 Subject: [PATCH 31/35] 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 3bc9e0e8f3fafc..6a3986aeec7065 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>; @@ -590,12 +406,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 bc2c802ac389e9b03154e802d99bbf3798f58747 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:18 +0200 Subject: [PATCH 32/35] 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 5d8626926d0671..81853c76959427 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>; @@ -431,6 +443,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 589746901cb26bfa871c73ddc3951780f35ecc2d Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 20 Jan 2025 15:01:11 +0200 Subject: [PATCH 33/35] 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 | 14 +- drivers/irqchip/Makefile | 2 +- drivers/irqchip/irq-bcm2712-mip.c | 399 ++++++++++++++---------------- 3 files changed, 196 insertions(+), 219 deletions(-) diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 1313828094a592..2fba0d268857c2 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -110,12 +110,20 @@ config I8259 select IRQ_DOMAIN config BCM2712_MIP - bool "Broadcom 2712 MSI-X Interrupt Peripheral support" + 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 + select IRQ_DOMAIN_HIERARCHY + select GENERIC_MSI_IRQ + select IRQ_MSI_LIB help - Enable support for the Broadcom BCM2712 MSI-X target peripheral. + 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 diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 1066cb881b7971..a11307b1b6104a 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -62,7 +62,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_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 index 2eaa3ac10cb62c..49a19db2d1e1b3 100644 --- a/drivers/irqchip/irq-bcm2712-mip.c +++ b/drivers/irqchip/irq-bcm2712-mip.c @@ -1,19 +1,20 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2021 Raspberry Pi Ltd., All Rights Reserved. + * Copyright (C) 2024 Raspberry Pi Ltd., All Rights Reserved. + * Copyright (c) 2024 SUSE */ -#include +#include +#include +#include #include -#include #include -#include -#include +#include -#include +#include "irq-msi-lib.h" -#define MIP_INT_RAISED 0x00 -#define MIP_INT_CLEARED 0x10 +#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 @@ -25,57 +26,40 @@ #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 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; + 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_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); + struct mip_priv *mip = 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->address_hi = upper_32_bits(mip->msg_addr); + msg->address_lo = lower_32_bits(mip->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 = { +static struct irq_chip mip_middle_irq_chip = { .name = "MIP", .irq_mask = irq_chip_mask_parent, .irq_unmask = irq_chip_unmask_parent, @@ -85,239 +69,224 @@ static struct irq_chip mip_irq_chip = { .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)); +} -// And a domain to connect it to its parent (the GIC) +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_irq_domain_alloc(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs, - void *args) +static int mip_middle_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) { - struct mip_priv *priv = domain->host_data; - struct irq_fwspec fwspec; + struct mip_priv *mip = domain->host_data; + struct irq_fwspec fwspec = {0}; + unsigned int hwirq, i; struct irq_data *irqd; - int hwirq, ret, i; - - spin_lock(&priv->msi_map_lock); + int irq, ret; - hwirq = bitmap_find_free_region(priv->msi_map, priv->num_msis, ilog2(nr_irqs)); + irq = mip_alloc_hwirq(mip, nr_irqs); + if (irq < 0) + return irq; - spin_unlock(&priv->msi_map_lock); + hwirq = irq + mip->msi_offset; - 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[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) - return 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); - irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, - &mip_irq_chip, priv); + 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; -} - -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); +err_free: 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); +err_free_hwirq: + mip_free_hwirq(mip, irq, nr_irqs); + return ret; } -#if 0 -static int mip_irq_domain_activate(struct irq_domain *domain, - struct irq_data *d, bool reserve) +static void mip_middle_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) { - 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; -} + struct irq_data *irqd = irq_domain_get_irq_data(domain, virq); + struct mip_priv *mip; + unsigned int hwirq; -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); + 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); } -#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 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, }; -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; - } +#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, +}; - gic_domain = irq_find_host(gic_node); - if (!gic_domain) { - pr_err("Failed to find the GIC domain\n"); - return -ENXIO; - } +static int mip_init_domains(struct mip_priv *mip, struct device_node *np) +{ + struct irq_domain *middle; - 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"); + middle = irq_domain_add_hierarchy(mip->parent, 0, mip->num_msis, np, + &mip_middle_domain_ops, mip); + if (!middle) 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; - } + 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 __init mip_of_msi_init(struct device_node *node, - struct device_node *parent) +static int mip_parse_dt(struct mip_priv *mip, struct device_node *np) { - struct mip_priv *priv; - struct resource res; + struct of_phandle_args args; + u64 size; int ret; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + ret = of_property_read_u32(np, "brcm,msi-offset", &mip->msi_offset); + if (ret) + mip->msi_offset = 0; - spin_lock_init(&priv->msi_map_lock); - spin_lock_init(&priv->hw_lock); + ret = of_parse_phandle_with_args(np, "msi-ranges", "#interrupt-cells", + 0, &args); + if (ret) + return ret; - ret = of_address_to_resource(node, 0, &res); - if (ret) { - pr_err("Failed to allocate resource\n"); - goto err_priv; - } + ret = of_property_read_u32_index(np, "msi-ranges", args.args_count + 1, + &mip->num_msis); + if (ret) + goto err_put; - 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; - } + ret = of_property_read_reg(np, 1, &mip->msg_addr, &size); + if (ret) + goto err_put; + + mip->msi_base = args.args[1]; - if (of_property_read_u32(node, "brcm,msi-num-spis", &priv->num_msis)) { - pr_err("Unable to parse MSI numbers\n"); + mip->parent = irq_find_host(args.np); + if (!mip->parent) ret = -EINVAL; - goto err_priv; - } - if (of_property_read_u32(node, "brcm,msi-offset", &priv->msi_offset)) - priv->msi_offset = 0; +err_put: + of_node_put(args.np); + return ret; +} - if (of_property_read_u64(node, "brcm,msi-pci-addr", &priv->msg_addr)) { - pr_err("Unable to parse MSI address\n"); - ret = -EINVAL; +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; - } - priv->base = ioremap(res.start, resource_size(&res)); - if (!priv->base) { - pr_err("Failed to ioremap regs\n"); - ret = -ENOMEM; + mip->base = of_iomap(node, 0); + if (!mip->base) { + ret = -ENXIO; goto err_priv; } - priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_msis), - sizeof(*priv->msi_map), - GFP_KERNEL); - if (!priv->msi_map) { + mip->bitmap = bitmap_zalloc(mip->num_msis, GFP_KERNEL); + if (!mip->bitmap) { 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"); + 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: - kfree(priv->msi_map); - + bitmap_free(mip->bitmap); err_base: - iounmap(priv->base); - + iounmap(mip->base); err_priv: - kfree(priv); - - pr_err("%s: failed - err %d\n", __func__, ret); - + kfree(mip); return ret; } -IRQCHIP_DECLARE(bcm_mip, "brcm,bcm2712-mip-intc", mip_of_msi_init); + +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 fc75df4d6e4584399b13d2a2fc261a9d7a1e8467 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Mon, 14 Oct 2024 16:07:00 +0300 Subject: [PATCH 34/35] 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 018daa65cbd1fd6a28b08499612750c4538a9637 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Mon, 17 Mar 2025 15:51:03 +0000 Subject: [PATCH 35/35] DT: bcm2711/bcm2712: use upstream property for controlling pcie clkreq L1 sub-state support and clkreq control are intermingled in the hardware, and upstream now have a tri-state property to control this. The default behaviour is now to enable clkreq control if the property is absent. Default Pi 5 PCIex1 to "safe" to avoid breaking expansion boards without the CLKREQ# line connected. Make this explicit in the pciex1-compat-pi5 overlay, and remove the obsolete CM4 property. Signed-off-by: Jonathan Bell --- arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts | 4 ---- arch/arm/boot/dts/overlays/README | 4 +++- arch/arm/boot/dts/overlays/pciex1-compat-pi5-overlay.dts | 2 +- arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 4 ++++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts index ddc33bbe872fb5..c218f9cf823f1c 100644 --- a/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts +++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi-cm4.dts @@ -422,10 +422,6 @@ // ============================================= // Board specific stuff here -&pcie0 { - brcm,enable-l1ss; -}; - &sdhost { status = "disabled"; }; diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 8b830637786474..8a36089517dae2 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -3703,7 +3703,9 @@ Params: Name: pciex1-compat-pi5 Info: Compatibility features for pciex1 on Pi 5. Load: dtoverlay=pciex1-compat-pi5,= -Params: l1ss Enable ASPM L1 sub-state support +Params: l1ss Enable RC ASPM L1 sub-state support. Requires + that the CLKREQ# pin is connected to the + endpoint. no-l0s Disable ASPM L0s no-mip Revert to the MSI target in the RC, instead of the MSI-MIP peripheral. Use if a) more than 8 diff --git a/arch/arm/boot/dts/overlays/pciex1-compat-pi5-overlay.dts b/arch/arm/boot/dts/overlays/pciex1-compat-pi5-overlay.dts index 77d59bbc86ceea..1f5adfc4add10e 100644 --- a/arch/arm/boot/dts/overlays/pciex1-compat-pi5-overlay.dts +++ b/arch/arm/boot/dts/overlays/pciex1-compat-pi5-overlay.dts @@ -12,7 +12,7 @@ fragment@0 { target = <&pciex1>; __dormant__ { - brcm,enable-l1ss; + brcm,clkreq-mode = "default"; }; }; 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 ee759bfaa8f4ad..2939214f23ade4 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts +++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts @@ -713,6 +713,10 @@ spi10_cs_pins: &spi10_cs_gpio1 {}; clock-names = "hdmi", "bvb", "audio", "cec"; }; +&pcie1 { + brcm,clkreq-mode = "safe"; +}; + &pcie2 { status = "okay"; };