net: dsa: vsc73xx: add port_stp_state_set function
authorPawel Dembicki <paweldembicki@gmail.com>
Sat, 13 Jul 2024 21:16:07 +0000 (23:16 +0200)
committerJakub Kicinski <kuba@kernel.org>
Mon, 15 Jul 2024 13:55:14 +0000 (06:55 -0700)
This isn't a fully functional implementation of 802.1D, but
port_stp_state_set is required for a future tag8021q operations.

This implementation handles properly all states, but vsc73xx doesn't
forward STP packets.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
Link: https://patch.msgid.link/20240713211620.1125910-2-paweldembicki@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/dsa/vitesse-vsc73xx-core.c

index 4b031fefcec68279d8ed378c17b19ae572e44ce1..ebeea259f01930c8f7e5767519e1e2c5ccc4e17d 100644 (file)
 #define VSC73XX_AGENCTRL       0xf0
 #define VSC73XX_CAPRST         0xff
 
+#define VSC73XX_SRCMASKS_CPU_COPY              BIT(27)
+#define VSC73XX_SRCMASKS_MIRROR                        BIT(26)
+#define VSC73XX_SRCMASKS_PORTS_MASK            GENMASK(7, 0)
+
 #define VSC73XX_MACACCESS_CPU_COPY             BIT(14)
 #define VSC73XX_MACACCESS_FWD_KILL             BIT(13)
 #define VSC73XX_MACACCESS_IGNORE_VLAN          BIT(12)
@@ -623,9 +627,6 @@ static int vsc73xx_setup(struct dsa_switch *ds)
        vsc73xx_write(vsc, VSC73XX_BLOCK_SYSTEM, 0, VSC73XX_GMIIDELAY,
                      VSC73XX_GMIIDELAY_GMII0_GTXDELAY_2_0_NS |
                      VSC73XX_GMIIDELAY_GMII0_RXDELAY_2_0_NS);
-       /* Enable reception of frames on all ports */
-       vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_RECVMASK,
-                     0x5f);
        /* IP multicast flood mask (table 144) */
        vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_IFLODMSK,
                      0xff);
@@ -788,10 +789,6 @@ static void vsc73xx_mac_link_down(struct phylink_config *config,
        /* Allow backward dropping of frames from this port */
        vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
                            VSC73XX_SBACKWDROP, BIT(port), BIT(port));
-
-       /* Receive mask (disable forwarding) */
-       vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
-                           VSC73XX_RECVMASK, BIT(port), 0);
 }
 
 static void vsc73xx_mac_link_up(struct phylink_config *config,
@@ -844,10 +841,6 @@ static void vsc73xx_mac_link_up(struct phylink_config *config,
        vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
                            VSC73XX_ARBDISC, BIT(port), 0);
 
-       /* Enable port (forwarding) in the receive mask */
-       vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
-                           VSC73XX_RECVMASK, BIT(port), BIT(port));
-
        /* Disallow backward dropping of frames from this port */
        vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
                            VSC73XX_SBACKWDROP, BIT(port), 0);
@@ -1039,6 +1032,86 @@ static void vsc73xx_phylink_get_caps(struct dsa_switch *dsa, int port,
        config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000;
 }
 
+static void vsc73xx_refresh_fwd_map(struct dsa_switch *ds, int port, u8 state)
+{
+       struct dsa_port *other_dp, *dp = dsa_to_port(ds, port);
+       struct vsc73xx *vsc = ds->priv;
+       u16 mask;
+
+       if (state != BR_STATE_FORWARDING) {
+               /* Ports that aren't in the forwarding state must not
+                * forward packets anywhere.
+                */
+               vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
+                                   VSC73XX_SRCMASKS + port,
+                                   VSC73XX_SRCMASKS_PORTS_MASK, 0);
+
+               dsa_switch_for_each_available_port(other_dp, ds) {
+                       if (other_dp == dp)
+                               continue;
+                       vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
+                                           VSC73XX_SRCMASKS + other_dp->index,
+                                           BIT(port), 0);
+               }
+
+               return;
+       }
+
+       /* Forwarding ports must forward to the CPU and to other ports
+        * in the same bridge
+        */
+       vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
+                           VSC73XX_SRCMASKS + CPU_PORT, BIT(port), BIT(port));
+
+       mask = BIT(CPU_PORT);
+
+       dsa_switch_for_each_user_port(other_dp, ds) {
+               int other_port = other_dp->index;
+
+               if (port == other_port || !dsa_port_bridge_same(dp, other_dp) ||
+                   other_dp->stp_state != BR_STATE_FORWARDING)
+                       continue;
+
+               mask |= BIT(other_port);
+
+               vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
+                                   VSC73XX_SRCMASKS + other_port,
+                                   BIT(port), BIT(port));
+       }
+
+       vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
+                           VSC73XX_SRCMASKS + port,
+                           VSC73XX_SRCMASKS_PORTS_MASK, mask);
+}
+
+/* FIXME: STP frames aren't forwarded at this moment. BPDU frames are
+ * forwarded only from and to PI/SI interface. For more info see chapter
+ * 2.7.1 (CPU Forwarding) in datasheet.
+ * This function is required for tag_8021q operations.
+ */
+static void vsc73xx_port_stp_state_set(struct dsa_switch *ds, int port,
+                                      u8 state)
+{
+       struct vsc73xx *vsc = ds->priv;
+       u32 val;
+
+       val = (state == BR_STATE_BLOCKING || state == BR_STATE_DISABLED) ?
+             0 : BIT(port);
+       vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
+                           VSC73XX_RECVMASK, BIT(port), val);
+
+       val = (state == BR_STATE_LEARNING || state == BR_STATE_FORWARDING) ?
+             BIT(port) : 0;
+       vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
+                           VSC73XX_LEARNMASK, BIT(port), val);
+
+       /* CPU Port should always forward packets when user ports are forwarding
+        * so let's configure it from other ports only.
+        */
+       if (port != CPU_PORT)
+               vsc73xx_refresh_fwd_map(ds, port, state);
+}
+
 static const struct phylink_mac_ops vsc73xx_phylink_mac_ops = {
        .mac_config = vsc73xx_mac_config,
        .mac_link_down = vsc73xx_mac_link_down,
@@ -1057,6 +1130,7 @@ static const struct dsa_switch_ops vsc73xx_ds_ops = {
        .port_disable = vsc73xx_port_disable,
        .port_change_mtu = vsc73xx_change_mtu,
        .port_max_mtu = vsc73xx_get_max_mtu,
+       .port_stp_state_set = vsc73xx_port_stp_state_set,
        .phylink_get_caps = vsc73xx_phylink_get_caps,
 };