|
|
@@ -0,0 +1,152 @@
|
|
|
+From a9349f08ec6c1251d41ef167d27a15cc39bc5b97 Mon Sep 17 00:00:00 2001
|
|
|
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <[email protected]>
|
|
|
+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 <[email protected]>
|
|
|
+Acked-by: Florian Fainelli <[email protected]>
|
|
|
+Signed-off-by: David S. Miller <[email protected]>
|
|
|
+---
|
|
|
+ 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
|
|
|
+@@ -374,6 +374,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);
|
|
|
+@@ -737,6 +775,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;
|
|
|
+@@ -999,6 +1039,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[] = {
|
|
|
+@@ -1023,6 +1064,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 */
|
|
|
+@@ -1133,6 +1175,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;
|
|
|
+
|
|
|
+ /* Auto-detection using standard registers will not work, so
|
|
|
+ * provide an indication of what kind of device we are for
|
|
|
+@@ -1187,6 +1230,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
|
|
|
+@@ -70,6 +70,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 */
|