[PATCH 3/3] PCI: brcmstb: Enable Broadcom Cable Modem SoCs
Manivannan Sadhasivam
mani at kernel.org
Thu Jul 10 10:23:59 PDT 2025
On Mon, Jun 09, 2025 at 06:17:06PM GMT, Jim Quinlan wrote:
> Broadcom's Cable Modem (CM) group also uses this PCIe driver
> as it shares the PCIe HW core with the STB group.
>
> Make the modifications to enable the CM SoCs.
>
> Signed-off-by: Jim Quinlan <james.quinlan at broadcom.com>
> ---
> drivers/pci/controller/pcie-brcmstb.c | 186 +++++++++++++++++++++-----
> 1 file changed, 152 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c
> index db7872cda960..e25dbcdc56a7 100644
> --- a/drivers/pci/controller/pcie-brcmstb.c
> +++ b/drivers/pci/controller/pcie-brcmstb.c
> @@ -51,6 +51,9 @@
> #define PCIE_RC_CFG_PRIV1_ROOT_CAP 0x4f8
> #define PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK 0xf8
>
> +#define PCIE_RC_DL_PDL_CONTROL_4 0x1010
> +#define PCIE_RC_DL_PDL_CONTROL_4_NPH_FC_INIT_MASK 0xff000000
Could you please use GENMASK()?
> +
> #define PCIE_RC_DL_MDIO_ADDR 0x1100
> #define PCIE_RC_DL_MDIO_WR_DATA 0x1104
> #define PCIE_RC_DL_MDIO_RD_DATA 0x1108
> @@ -60,6 +63,7 @@
> #define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK 0xff
>
> #define PCIE_MISC_MISC_CTRL 0x4008
> +#define PCIE_MISC_MISC_CTRL_PCIE_IN_CPL_RO_MASK 0x20
> #define PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK 0x80
> #define PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400
> #define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000
> @@ -170,6 +174,7 @@
> /* MSI target addresses */
> #define BRCM_MSI_TARGET_ADDR_LT_4GB 0x0fffffffcULL
> #define BRCM_MSI_TARGET_ADDR_GT_4GB 0xffffffffcULL
> +#define BRCM_MSI_TARGET_ADDR_FOR_CM 0xfffffffffcULL
>
> /* MDIO registers */
> #define MDIO_PORT0 0x0
> @@ -223,13 +228,23 @@ enum {
> enum pcie_soc_base {
> GENERIC,
> BCM2711,
> + BCM3162,
> + BCM3392,
> + BCM3390,
> BCM4908,
> BCM7278,
> BCM7425,
> BCM7435,
> BCM7712,
> + BCM33940,
> };
>
> +/*
> + * BCM3390 CM chip actually conforms to STB design, so it
> + * is not present in the macro below.
> + */
> +#define IS_CM_SOC(t) ((t) == BCM3162 || (t) == BCM33940 || (t) == BCM3392)
TBH, I don't like these soc_base comparisions sprinkled throghout the driver.
They just make the code messy and it will become a nightmare to maintain if
these comparisions grow in the future (it already is).
I would rather like to have function specific flags that are set for specific
SoCs and those flags be used in the code for conditional check instead of the
soc_base.
It would make it easier to understand what functionalities are supported by each
SoC and where exactly those functionalities are implemented.
- Mani
> +
> struct inbound_win {
> u64 size;
> u64 pci_offset;
> @@ -757,6 +772,9 @@ static int brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
> u32 shift = RGR1_SW_INIT_1_INIT_GENERIC_SHIFT;
> int ret = 0;
>
> + if (IS_CM_SOC(pcie->cfg->soc_base))
> + return 0;
> +
> if (pcie->bridge_reset) {
> if (val)
> ret = reset_control_assert(pcie->bridge_reset);
> @@ -891,13 +909,13 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie,
> struct inbound_win *b = b_begin;
>
> /*
> - * STB chips beside 7712 disable the first inbound window default.
> - * Rather being mapped to system memory it is mapped to the
> - * internal registers of the SoC. This feature is deprecated, has
> - * security considerations, and is not implemented in our modern
> - * SoCs.
> + * STB chips beside CM chips and 7712 disable the first inbound
> + * window default. Rather being mapped to system memory it is
> + * mapped to the internal registers of the SoC. This feature is
> + * deprecated, has security considerations, and is not
> + * implemented in our modern SoCs.
> */
> - if (pcie->cfg->soc_base != BCM7712)
> + if (pcie->cfg->soc_base != BCM7712 && !IS_CM_SOC(pcie->cfg->soc_base))
> add_inbound_win(b++, &n, 0, 0, 0);
>
> resource_list_for_each_entry(entry, &bridge->dma_ranges) {
> @@ -905,16 +923,32 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie,
> u64 cpu_start = entry->res->start;
>
> size = resource_size(entry->res);
> +
> + /*
> + * For BCM3390, single dma-range may map to the SoC
> + * register space in [0xf0000000..0xffffffff]. If present,
> + * this has to be assigned to inbound window #1, as this is
> + * the only one that HW allows to map to register space.
> + * So if we see this range, place it in inbound_wins[1]
> + * which is previously disabled (zeroed out).
> + */
> + if (pcie->cfg->soc_base == BCM3390 && cpu_start >= 0xf0000000
> + && cpu_start + size - 1 <= 0xffffffff) {
> + add_inbound_win(b_begin, &n, size, cpu_start, pcie_start);
> + n--;
> + continue;
> + }
> +
> tot_size += size;
> if (pcie_start < lowest_pcie_addr)
> lowest_pcie_addr = pcie_start;
> /*
> - * 7712 and newer chips may have many BARs, with each
> - * offering a non-overlapping viewport to system memory.
> - * That being said, each BARs size must still be a power of
> - * two.
> + * 7712, CM, and newer chips may have many inbound windows,
> + * with each offering a non-overlapping viewport to system
> + * memory. That being said, each window's size must still
> + * be a power of two.
> */
> - if (pcie->cfg->soc_base == BCM7712)
> + if (pcie->cfg->soc_base == BCM7712 || IS_CM_SOC(pcie->cfg->soc_base))
> add_inbound_win(b++, &n, size, cpu_start, pcie_start);
>
> if (n > pcie->cfg->num_inbound_wins)
> @@ -927,11 +961,11 @@ static int brcm_pcie_get_inbound_wins(struct brcm_pcie *pcie,
> }
>
> /*
> - * 7712 and newer chips do not have an internal memory mapping system
> - * that enables multiple memory controllers. As such, it can return
> - * now w/o doing special configuration.
> + * 7712, CM, and newer chips do not have an internal memory
> + * mapping system that enables multiple memory controllers. As
> + * such, it can return now w/o doing special configuration.
> */
> - if (pcie->cfg->soc_base == BCM7712)
> + if (pcie->cfg->soc_base == BCM7712 || IS_CM_SOC(pcie->cfg->soc_base))
> return n;
>
> ret = of_property_read_variable_u64_array(pcie->np, "brcm,scb-sizes", pcie->memc_size, 1,
> @@ -1051,10 +1085,10 @@ static void set_inbound_win_registers(struct brcm_pcie *pcie,
> /*
> * Most STB chips:
> * Do nothing.
> - * 7712:
> - * All of their BARs need to be set.
> + * 7712, CM:
> + * All of their inbound windows need to be set.
> */
> - if (pcie->cfg->soc_base == BCM7712) {
> + if (pcie->cfg->soc_base == BCM7712 || IS_CM_SOC(pcie->cfg->soc_base)) {
> /* BUS remap register settings */
> reg_offset = brcm_ubus_reg_offset(i);
> tmp = lower_32_bits(cpu_addr) & ~0xfff;
> @@ -1118,6 +1152,8 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
> burst = 0x0; /* 128 bytes */
> else if (pcie->cfg->soc_base == BCM7278)
> burst = 0x3; /* 512 bytes */
> + else if (pcie->cfg->soc_base == BCM3162 || pcie->cfg->soc_base == BCM33940)
> + burst = 0x1; /* Encoding: 0=64, 1=128, 2=Rsvd, 3=Rsvd */
> else
> burst = 0x2; /* 512 bytes */
>
> @@ -1144,18 +1180,20 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
> return -EINVAL;
> }
>
> - 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;
> -
> - if (memc == 0)
> - u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(0));
> - else if (memc == 1)
> - u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(1));
> - else if (memc == 2)
> - u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(2));
> + if (!IS_CM_SOC(pcie->cfg->soc_base)) {
> + 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;
> +
> + if (memc == 0)
> + u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(0));
> + else if (memc == 1)
> + u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(1));
> + else if (memc == 2)
> + u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(2));
> + }
> + writel(tmp, base + PCIE_MISC_MISC_CTRL);
> }
> - writel(tmp, base + PCIE_MISC_MISC_CTRL);
>
> /*
> * We ideally want the MSI target address to be located in the 32bit
> @@ -1164,8 +1202,10 @@ 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 (inbound_wins[2].pci_offset >= SZ_4G ||
> - (inbound_wins[2].size + inbound_wins[2].pci_offset) < SZ_4G)
> + if (IS_CM_SOC(pcie->cfg->soc_base))
> + pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_FOR_CM;
> + else 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;
> @@ -1226,6 +1266,29 @@ 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);
>
> + /*
> + * Relax read ordering for chip architectures using RBUS/SCB that
> + * use WiFi Runner offload (i.e. BCM3390) to avoid deadlock where
> + * reads are blocked by writes.
> + */
> + if (pcie->cfg->soc_base == BCM3390) {
> + tmp = readl(base + PCIE_MISC_MISC_CTRL);
> + u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_IN_CPL_RO_MASK);
> + writel(tmp, base + PCIE_MISC_MISC_CTRL);
> + }
> +
> + /*
> + * The 3392 has a bug that requires the NP credit advertised by the
> + * MAC to be overwritten with 0x10 in bits 31:24 of the following
> + * register.
> + */
> + if (pcie->cfg->soc_base == BCM3392) {
> + tmp = readl(base + PCIE_RC_DL_PDL_CONTROL_4);
> + u32p_replace_bits(&tmp, 0x10,
> + PCIE_RC_DL_PDL_CONTROL_4_NPH_FC_INIT_MASK);
> + writel(tmp, base + PCIE_RC_DL_PDL_CONTROL_4);
> + }
> +
> if (pcie->cfg->post_setup) {
> ret = pcie->cfg->post_setup(pcie);
> if (ret < 0)
> @@ -1246,8 +1309,8 @@ static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie)
> 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->cfg->soc_base == BCM7712)
> + /* CM and 7712 do not have this (RGR1) timer */
> + if (IS_CM_SOC(pcie->cfg->soc_base) || pcie->cfg->soc_base == BCM7712)
> return;
>
> /* Each unit in timeout register is 1/216,000,000 seconds */
> @@ -1354,7 +1417,10 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie)
>
> brcm_config_clkreq(pcie);
>
> - if (pcie->ssc) {
> + if (IS_CM_SOC(pcie->cfg->soc_base)) {
> + /* This driver does configure SSC for CM chips */
> + ssc_str = "";
> + } else if (pcie->ssc) {
> ret = brcm_pcie_set_ssc(pcie);
> if (ret == 0)
> ssc_str = "(SSC)";
> @@ -1715,6 +1781,14 @@ static const int pcie_offsets[] = {
> [PCIE_INTR2_CPU_BASE] = 0x4300,
> };
>
> +static const int pcie_offset_bcm3162[] = {
> + [RGR1_SW_INIT_1] = 0x9210,
> + [EXT_CFG_INDEX] = 0x9000,
> + [EXT_CFG_DATA] = 0x9004,
> + [PCIE_HARD_DEBUG] = 0x4204,
> + [PCIE_INTR2_CPU_BASE] = 0x4300
> +};
> +
> static const int pcie_offsets_bcm7278[] = {
> [RGR1_SW_INIT_1] = 0xc010,
> [EXT_CFG_INDEX] = 0x9000,
> @@ -1739,6 +1813,14 @@ static const int pcie_offsets_bcm7712[] = {
> [PCIE_INTR2_CPU_BASE] = 0x4400,
> };
>
> +static const int pcie_offset_bcm33940[] = {
> + [RGR1_SW_INIT_1] = 0x9210,
> + [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,
> @@ -1765,6 +1847,30 @@ static const struct pcie_cfg_data bcm2712_cfg = {
> .num_inbound_wins = 10,
> };
>
> +static const struct pcie_cfg_data bcm3162_cfg = {
> + .offsets = pcie_offset_bcm3162,
> + .soc_base = BCM3162,
> + .perst_set = brcm_pcie_perst_set_7278,
> + .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
> + .num_inbound_wins = 3,
> +};
> +
> +static const struct pcie_cfg_data bcm3392_cfg = {
> + .offsets = pcie_offset_bcm33940,
> + .soc_base = BCM3392,
> + .perst_set = brcm_pcie_perst_set_7278,
> + .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
> + .num_inbound_wins = 10,
> +};
> +
> +static const struct pcie_cfg_data bcm3390_cfg = {
> + .offsets = pcie_offsets,
> + .soc_base = BCM3390,
> + .perst_set = brcm_pcie_perst_set_generic,
> + .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
> + .num_inbound_wins = 3,
> +};
> +
> static const struct pcie_cfg_data bcm4908_cfg = {
> .offsets = pcie_offsets,
> .soc_base = BCM4908,
> @@ -1814,9 +1920,20 @@ static const struct pcie_cfg_data bcm7712_cfg = {
> .num_inbound_wins = 10,
> };
>
> +static const struct pcie_cfg_data bcm33940_cfg = {
> + .offsets = pcie_offset_bcm33940,
> + .soc_base = BCM33940,
> + .perst_set = brcm_pcie_perst_set_7278,
> + .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
> + .num_inbound_wins = 10,
> +};
> +
> 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,bcm3162-pcie", .data = &bcm3162_cfg },
> + { .compatible = "brcm,bcm3390-pcie", .data = &bcm3390_cfg },
> + { .compatible = "brcm,bcm3392-pcie", .data = &bcm3392_cfg },
> { .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg },
> { .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg },
> { .compatible = "brcm,bcm7216-pcie", .data = &bcm7216_cfg },
> @@ -1825,6 +1942,7 @@ 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,bcm33940-pcie", .data = &bcm33940_cfg },
> {},
> };
>
> --
> 2.43.0
>
--
மணிவண்ணன் சதாசிவம்
More information about the linux-arm-kernel
mailing list