Add support for global forwarding mode in the SJA1105 DSA driver,
allowing dynamic control over whether the switch forwards traffic
between all ports or restricts communication to only CPU-port
connections.

Signed-off-by: Oleksij Rempel <[email protected]>
---
 drivers/net/sja1105.c | 77 +++++++++++++++++++++++++++++++++----------
 1 file changed, 59 insertions(+), 18 deletions(-)

diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c
index 4ef30b8c81f1..aaf9237dc19d 100644
--- a/drivers/net/sja1105.c
+++ b/drivers/net/sja1105.c
@@ -1230,11 +1230,13 @@ static int sja1105_init_mii_settings(struct 
sja1105_private *priv)
        return 0;
 }
 
-static void sja1105_setup_tagging(struct sja1105_private *priv, int port)
+static void sja1105_setup_tagging(struct sja1105_private *priv, int port,
+                                 bool forward_all)
 {
        struct sja1105_vlan_lookup_entry *vlan;
        struct dsa_switch *ds = &priv->ds;
        int cpu = ds->cpu_port;
+       u8 vlan_mask;
 
        /* The CPU port is implicitly configured by
         * configuring the front-panel ports
@@ -1246,8 +1248,16 @@ static void sja1105_setup_tagging(struct sja1105_private 
*priv, int port)
 
        priv->pvid[port] = DSA_8021Q_DIR_TX | DSA_8021Q_PORT(port);
 
-       vlan[port].vmemb_port   = BIT(port) | BIT(cpu);
-       vlan[port].vlan_bc      = BIT(port) | BIT(cpu);
+       if (forward_all) {
+               /* All ports in the same VLAN */
+               vlan_mask = (1 << ds->num_ports) - 1;
+       } else {
+               /* Only allow port ↔ CPU communication */
+               vlan_mask = BIT(port) | BIT(cpu);
+       }
+
+       vlan[port].vmemb_port   = vlan_mask;
+       vlan[port].vlan_bc      = vlan_mask;
        vlan[port].tag_port     = BIT(cpu);
        vlan[port].vlanid       = priv->pvid[port];
        vlan[port].type_entry   = SJA1110_VLAN_D_TAG;
@@ -1269,7 +1279,7 @@ static int sja1105_init_vlan(struct sja1105_private *priv)
        table->entry_count = ds->num_ports;
 
        for (port = 0; port < ds->num_ports; port++)
-               sja1105_setup_tagging(priv, port);
+               sja1105_setup_tagging(priv, port, false);
 
        return 0;
 }
@@ -1283,33 +1293,47 @@ sja1105_port_allow_traffic(struct 
sja1105_l2_forwarding_entry *l2_fwd,
        l2_fwd[from].fl_domain  |= BIT(to);
 }
 
-static int sja1105_init_l2_forwarding(struct sja1105_private *priv)
+static int sja1105_init_l2_forwarding(struct sja1105_private *priv,
+                                     bool forward_all)
 {
        struct sja1105_l2_forwarding_entry *l2fwd;
        struct dsa_switch *ds = &priv->ds;
        struct sja1105_table *table;
        int cpu = ds->cpu_port;
-       int i;
+       int i, j;
 
        table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING];
 
-       table->entries = calloc(SJA1105_MAX_L2_FORWARDING_COUNT,
-                               table->ops->unpacked_entry_size);
-       if (!table->entries)
-               return -ENOMEM;
+       if (!table->entries) {
+               table->entries = calloc(SJA1105_MAX_L2_FORWARDING_COUNT,
+                                       table->ops->unpacked_entry_size);
+               if (!table->entries)
+                       return -ENOMEM;
+       }
 
        table->entry_count = SJA1105_MAX_L2_FORWARDING_COUNT;
 
        l2fwd = table->entries;
 
-       /* First 5 entries define the forwarding rules */
-       for (i = 0; i < ds->num_ports; i++) {
-               if (i == cpu)
-                       continue;
-
-               sja1105_port_allow_traffic(l2fwd, i, cpu);
-               sja1105_port_allow_traffic(l2fwd, cpu, i);
+       if (forward_all) {
+               /* Forwarding mode: All ports can talk to each other */
+               for (i = 0; i < ds->num_ports; i++) {
+                       for (j = 0; j < ds->num_ports; j++) {
+                               if (i == j)
+                                       continue;
+                               sja1105_port_allow_traffic(l2fwd, i, j);
+                       }
+               }
+       } else {
+               /* Default mode: Each port only forwards to/from the CPU */
+               for (i = 0; i < ds->num_ports; i++) {
+                       if (i == cpu)
+                               continue;
+                       sja1105_port_allow_traffic(l2fwd, i, cpu);
+                       sja1105_port_allow_traffic(l2fwd, cpu, i);
+               }
        }
+
        /* Next 8 entries define VLAN PCP mapping from ingress to egress.
         * Leave them unpopulated (implicitly 0) but present.
         */
@@ -1490,7 +1514,7 @@ static int sja1105_static_config_init(struct 
sja1105_private *priv)
        rc = sja1105_init_mii_settings(priv);
        if (rc < 0)
                return rc;
-       rc = sja1105_init_l2_forwarding(priv);
+       rc = sja1105_init_l2_forwarding(priv, false);
        if (rc < 0)
                return rc;
        rc = sja1105_init_l2_forwarding_params(priv);
@@ -2866,12 +2890,29 @@ static int sja1105_cpu_port_enable(struct dsa_port *dp, 
int port,
        return sja1105_static_config_reload(priv);
 }
 
+static int sja1105_set_forwarding(struct dsa_switch *ds, bool enable)
+{
+       struct device *dev = ds->dev;
+       struct sja1105_private *priv = dev_get_priv(dev);
+       int port, ret;
+
+       for (port = 0; port < ds->num_ports; port++)
+               sja1105_setup_tagging(priv, port, enable);
+
+       ret = sja1105_init_l2_forwarding(priv, enable);
+       if (ret)
+               return ret;
+
+       return sja1105_static_config_reload(priv);
+}
+
 static const struct dsa_switch_ops sja1105_dsa_ops = {
        .port_pre_enable        = sja1105_port_pre_enable,
        .port_enable            = sja1105_cpu_port_enable,
        .adjust_link            = sja1105_adjust_link,
        .xmit                   = sja1105_xmit,
        .rcv                    = sja1105_rcv,
+       .set_forwarding         = sja1105_set_forwarding,
 };
 
 static int sja1105_init(struct sja1105_private *priv)
-- 
2.39.5


Reply via email to