[PATCH net-next 15/17] net: dsa: sja1105: Add support for Spanning Tree Protocol

From: Vladimir Oltean
Date: Sun Mar 31 2019 - 13:43:37 EST


Signed-off-by: Vladimir Oltean <olteanv@xxxxxxxxx>
---
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 adf79b1caf55..4064abc46f48 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;
}
@@ -636,12 +647,14 @@ static sja1105_speed_t 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;
@@ -674,12 +687,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;
@@ -928,6 +942,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)
{
@@ -940,6 +998,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
@@ -950,16 +1025,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 */
@@ -978,6 +1064,9 @@ static int sja1105_static_config_reload(struct sja1105_private *priv)
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
bool enabled = (speed_mbps[i] != 0);

+ 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)
@@ -1352,6 +1441,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