| From a9349f08ec6c1251d41ef167d27a15cc39bc5b97 Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
| Date: Fri, 12 Mar 2021 11:41:08 +0100 |
| Subject: [PATCH] net: dsa: bcm_sf2: setup BCM4908 internal crossbar |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| On some SoCs (e.g. BCM4908, BCM631[345]8) SF2 has an integrated |
| crossbar. It allows connecting its selected external ports to internal |
| ports. It's used by vendors to handle custom Ethernet setups. |
| |
| BCM4908 has following 3x2 crossbar. On Asus GT-AC5300 rgmii is used for |
| connecting external BCM53134S switch. GPHY4 is usually used for WAN |
| port. More fancy devices use SerDes for 2.5 Gbps Ethernet. |
| |
| ┌──────────┐ |
| SerDes ─── 0 ─┤ │ |
| │ 3x2 ├─ 0 ─── switch port 7 |
| GPHY4 ─── 1 ─┤ │ |
| │ crossbar ├─ 1 ─── runner (accelerator) |
| rgmii ─── 2 ─┤ │ |
| └──────────┘ |
| |
| Use setup data based on DT info to configure BCM4908's switch port 7. |
| Right now only GPHY and rgmii variants are supported. Handling SerDes |
| can be implemented later. |
| |
| Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
| Acked-by: Florian Fainelli <f.fainelli@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| --- |
| drivers/net/dsa/bcm_sf2.c | 45 ++++++++++++++++++++++++++++++++++ |
| drivers/net/dsa/bcm_sf2.h | 1 + |
| drivers/net/dsa/bcm_sf2_regs.h | 7 ++++++ |
| 3 files changed, 53 insertions(+) |
| |
| --- a/drivers/net/dsa/bcm_sf2.c |
| +++ b/drivers/net/dsa/bcm_sf2.c |
| @@ -389,6 +389,44 @@ static int bcm_sf2_sw_rst(struct bcm_sf2 |
| return 0; |
| } |
| |
| +static void bcm_sf2_crossbar_setup(struct bcm_sf2_priv *priv) |
| +{ |
| + struct device *dev = priv->dev->ds->dev; |
| + int shift; |
| + u32 mask; |
| + u32 reg; |
| + int i; |
| + |
| + mask = BIT(priv->num_crossbar_int_ports) - 1; |
| + |
| + reg = reg_readl(priv, REG_CROSSBAR); |
| + switch (priv->type) { |
| + case BCM4908_DEVICE_ID: |
| + shift = CROSSBAR_BCM4908_INT_P7 * priv->num_crossbar_int_ports; |
| + reg &= ~(mask << shift); |
| + if (0) /* FIXME */ |
| + reg |= CROSSBAR_BCM4908_EXT_SERDES << shift; |
| + else if (priv->int_phy_mask & BIT(7)) |
| + reg |= CROSSBAR_BCM4908_EXT_GPHY4 << shift; |
| + else if (phy_interface_mode_is_rgmii(priv->port_sts[7].mode)) |
| + reg |= CROSSBAR_BCM4908_EXT_RGMII << shift; |
| + else if (WARN(1, "Invalid port mode\n")) |
| + return; |
| + break; |
| + default: |
| + return; |
| + } |
| + reg_writel(priv, reg, REG_CROSSBAR); |
| + |
| + reg = reg_readl(priv, REG_CROSSBAR); |
| + for (i = 0; i < priv->num_crossbar_int_ports; i++) { |
| + shift = i * priv->num_crossbar_int_ports; |
| + |
| + dev_dbg(dev, "crossbar int port #%d - ext port #%d\n", i, |
| + (reg >> shift) & mask); |
| + } |
| +} |
| + |
| static void bcm_sf2_intr_disable(struct bcm_sf2_priv *priv) |
| { |
| intrl2_0_mask_set(priv, 0xffffffff); |
| @@ -759,6 +797,8 @@ static int bcm_sf2_sw_resume(struct dsa_ |
| return ret; |
| } |
| |
| + bcm_sf2_crossbar_setup(priv); |
| + |
| ret = bcm_sf2_cfp_resume(ds); |
| if (ret) |
| return ret; |
| @@ -1024,6 +1064,7 @@ struct bcm_sf2_of_data { |
| const u16 *reg_offsets; |
| unsigned int core_reg_align; |
| unsigned int num_cfp_rules; |
| + unsigned int num_crossbar_int_ports; |
| }; |
| |
| static const u16 bcm_sf2_4908_reg_offsets[] = { |
| @@ -1048,6 +1089,7 @@ static const struct bcm_sf2_of_data bcm_ |
| .core_reg_align = 0, |
| .reg_offsets = bcm_sf2_4908_reg_offsets, |
| .num_cfp_rules = 0, /* FIXME */ |
| + .num_crossbar_int_ports = 2, |
| }; |
| |
| /* Register offsets for the SWITCH_REG_* block */ |
| @@ -1158,6 +1200,7 @@ static int bcm_sf2_sw_probe(struct platf |
| priv->reg_offsets = data->reg_offsets; |
| priv->core_reg_align = data->core_reg_align; |
| priv->num_cfp_rules = data->num_cfp_rules; |
| + priv->num_crossbar_int_ports = data->num_crossbar_int_ports; |
| |
| priv->rcdev = devm_reset_control_get_optional_exclusive(&pdev->dev, |
| "switch"); |
| @@ -1217,6 +1260,8 @@ static int bcm_sf2_sw_probe(struct platf |
| return ret; |
| } |
| |
| + bcm_sf2_crossbar_setup(priv); |
| + |
| bcm_sf2_gphy_enable_set(priv->dev->ds, true); |
| |
| ret = bcm_sf2_mdio_register(ds); |
| --- a/drivers/net/dsa/bcm_sf2.h |
| +++ b/drivers/net/dsa/bcm_sf2.h |
| @@ -73,6 +73,7 @@ struct bcm_sf2_priv { |
| const u16 *reg_offsets; |
| unsigned int core_reg_align; |
| unsigned int num_cfp_rules; |
| + unsigned int num_crossbar_int_ports; |
| |
| /* spinlock protecting access to the indirect registers */ |
| spinlock_t indir_lock; |
| --- a/drivers/net/dsa/bcm_sf2_regs.h |
| +++ b/drivers/net/dsa/bcm_sf2_regs.h |
| @@ -48,6 +48,13 @@ enum bcm_sf2_reg_offs { |
| #define PHY_PHYAD_SHIFT 8 |
| #define PHY_PHYAD_MASK 0x1F |
| |
| +/* Relative to REG_CROSSBAR */ |
| +#define CROSSBAR_BCM4908_INT_P7 0 |
| +#define CROSSBAR_BCM4908_INT_RUNNER 1 |
| +#define CROSSBAR_BCM4908_EXT_SERDES 0 |
| +#define CROSSBAR_BCM4908_EXT_GPHY4 1 |
| +#define CROSSBAR_BCM4908_EXT_RGMII 2 |
| + |
| #define REG_RGMII_CNTRL_P(x) (REG_RGMII_0_CNTRL + (x)) |
| |
| /* Relative to REG_RGMII_CNTRL */ |