Signed-off-by: Vladimir Oltean <olte...@gmail.com>
---
 drivers/net/dsa/sja1105/sja1105_main.c | 108 ++++++++++++++++++++++---
 1 file changed, 99 insertions(+), 9 deletions(-)

diff --git a/drivers/net/dsa/sja1105/sja1105_main.c 
b/drivers/net/dsa/sja1105/sja1105_main.c
index 92dc58afd74e..448ab0e71827 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -93,8 +93,10 @@ static int sja1105_init_mac_settings(struct sja1105_private 
*priv)
                .drpuntag = false,
                /* Don't retag 802.1p (VID 0) traffic with the pvid */
                .retag = false,
-               /* Enable learning and I/O on user ports by default. */
-               .dyn_learn = true,
+               /* Disable learning and I/O on user ports by default -
+                * STP will enable it.
+                */
+               .dyn_learn = false,
                .egress = false,
                .ingress = false,
                .mirrcie = 0,
@@ -125,8 +127,17 @@ static int sja1105_init_mac_settings(struct 
sja1105_private *priv)
 
        mac = table->entries;
 
-       for (i = 0; i < SJA1105_NUM_PORTS; i++)
+       for (i = 0; i < SJA1105_NUM_PORTS; i++) {
                mac[i] = default_mac;
+               if (i == dsa_upstream_port(priv->ds, i)) {
+                       /* STP doesn't get called for CPU port, so we need to
+                        * set the I/O parameters statically.
+                        */
+                       mac[i].dyn_learn = true;
+                       mac[i].ingress = true;
+                       mac[i].egress = true;
+               }
+       }
 
        return 0;
 }
@@ -639,12 +650,14 @@ static int sja1105_get_speed_cfg(unsigned int speed_mbps)
  * for a specific port.
  *
  * @speed_mbps: If 0, leave the speed unchanged, else adapt MAC to PHY speed.
- * @enabled: Manage Rx and Tx settings for this port. Overrides the static
- *          configuration settings.
+ * @enabled: Manage Rx and Tx settings for this port. If false, overrides the
+ *          settings from the STP state, but not persistently (does not
+ *          overwrite the static MAC info for this port).
  */
 static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
                                      int speed_mbps, bool enabled)
 {
+       struct sja1105_mac_config_entry dyn_mac;
        struct sja1105_xmii_params_entry *mii;
        struct sja1105_mac_config_entry *mac;
        struct device *dev = priv->ds->dev;
@@ -677,12 +690,13 @@ static int sja1105_adjust_port_config(struct 
sja1105_private *priv, int port,
         * the code common, we'll use the static configuration tables as a
         * reasonable approximation for both E/T and P/Q/R/S.
         */
-       mac[port].ingress = enabled;
-       mac[port].egress  = enabled;
+       dyn_mac = mac[port];
+       dyn_mac.ingress = enabled && mac[port].ingress;
+       dyn_mac.egress  = enabled && mac[port].egress;
 
        /* Write to the dynamic reconfiguration tables */
        rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG,
-                                         port, &mac[port], true);
+                                         port, &dyn_mac, true);
        if (rc < 0) {
                dev_err(dev, "Failed to write MAC config: %d\n", rc);
                return rc;
@@ -932,6 +946,50 @@ static int sja1105_bridge_member(struct dsa_switch *ds, 
int port,
                                            port, &l2_fwd[port], true);
 }
 
+static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port,
+                                        u8 state)
+{
+       struct sja1105_private *priv = ds->priv;
+       struct sja1105_mac_config_entry *mac;
+
+       mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
+
+       switch (state) {
+       case BR_STATE_DISABLED:
+       case BR_STATE_BLOCKING:
+               /* From UM10944 description of DRPDTAG (why put this there?):
+                * "Management traffic flows to the port regardless of the state
+                * of the INGRESS flag". So BPDUs are still be allowed to pass.
+                * At the moment no difference between DISABLED and BLOCKING.
+                */
+               mac[port].ingress   = false;
+               mac[port].egress    = false;
+               mac[port].dyn_learn = false;
+               break;
+       case BR_STATE_LISTENING:
+               mac[port].ingress   = true;
+               mac[port].egress    = false;
+               mac[port].dyn_learn = false;
+               break;
+       case BR_STATE_LEARNING:
+               mac[port].ingress   = true;
+               mac[port].egress    = false;
+               mac[port].dyn_learn = true;
+               break;
+       case BR_STATE_FORWARDING:
+               mac[port].ingress   = true;
+               mac[port].egress    = true;
+               mac[port].dyn_learn = true;
+               break;
+       default:
+               dev_err(ds->dev, "invalid STP state: %d\n", state);
+               return;
+       }
+
+       sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
+                                    &mac[port], true);
+}
+
 static int sja1105_bridge_join(struct dsa_switch *ds, int port,
                               struct net_device *br)
 {
@@ -944,6 +1002,23 @@ static void sja1105_bridge_leave(struct dsa_switch *ds, 
int port,
        sja1105_bridge_member(ds, port, br, false);
 }
 
+static u8 sja1105_stp_state_get(struct sja1105_private *priv, int port)
+{
+       struct sja1105_mac_config_entry *mac;
+
+       mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
+
+       if (!mac[port].ingress && !mac[port].egress && !mac[port].dyn_learn)
+               return BR_STATE_BLOCKING;
+       if (mac[port].ingress && !mac[port].egress && !mac[port].dyn_learn)
+               return BR_STATE_LISTENING;
+       if (mac[port].ingress && !mac[port].egress && mac[port].dyn_learn)
+               return BR_STATE_LEARNING;
+       if (mac[port].ingress && mac[port].egress && mac[port].dyn_learn)
+               return BR_STATE_FORWARDING;
+       return -EINVAL;
+}
+
 /* For situations where we need to change a setting at runtime that is only
  * available through the static configuration, resetting the switch in order
  * to upload the new static config is unavoidable. Back up the settings we
@@ -954,16 +1029,27 @@ static int sja1105_static_config_reload(struct 
sja1105_private *priv)
 {
        struct sja1105_mac_config_entry *mac;
        int speed_mbps[SJA1105_NUM_PORTS];
+       u8 stp_state[SJA1105_NUM_PORTS];
        int rc, i;
 
        mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
 
        /* Back up settings changed by sja1105_adjust_port_config and
-        * and restore their defaults.
+        * sja1105_bridge_stp_state_set and restore their defaults.
         */
        for (i = 0; i < SJA1105_NUM_PORTS; i++) {
                speed_mbps[i] = sja1105_speed[mac[i].speed];
                mac[i].speed = SJA1105_SPEED_AUTO;
+               if (i == dsa_upstream_port(priv->ds, i)) {
+                       mac[i].ingress = true;
+                       mac[i].egress = true;
+                       mac[i].dyn_learn = true;
+               } else {
+                       stp_state[i] = sja1105_stp_state_get(priv, i);
+                       mac[i].ingress = false;
+                       mac[i].egress = false;
+                       mac[i].dyn_learn = false;
+               }
        }
 
        /* Reset switch and send updated static configuration */
@@ -982,6 +1068,9 @@ static int sja1105_static_config_reload(struct 
sja1105_private *priv)
        for (i = 0; i < SJA1105_NUM_PORTS; i++) {
                bool enabled = (speed_mbps[i] != SJA1105_SPEED_AUTO);
 
+               if (i != dsa_upstream_port(priv->ds, i))
+                       sja1105_bridge_stp_state_set(priv->ds, i, stp_state[i]);
+
                rc = sja1105_adjust_port_config(priv, i, speed_mbps[i],
                                                enabled);
                if (rc < 0)
@@ -1375,6 +1464,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
        .port_fdb_del           = sja1105_fdb_del,
        .port_bridge_join       = sja1105_bridge_join,
        .port_bridge_leave      = sja1105_bridge_leave,
+       .port_stp_state_set     = sja1105_bridge_stp_state_set,
        .port_vlan_prepare      = sja1105_vlan_prepare,
        .port_vlan_filtering    = sja1105_vlan_filtering,
        .port_vlan_add          = sja1105_vlan_add,
-- 
2.17.1

Reply via email to