diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 90f0f6f3124e4..f9e87fb33da03 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -206,6 +206,8 @@ struct sja1105_private { bool rgmii_tx_delay[SJA1105_NUM_PORTS]; bool best_effort_vlan_filtering; unsigned long learn_ena; + unsigned long ucast_egress_floods; + unsigned long bcast_egress_floods; const struct sja1105_info *info; struct gpio_desc *reset_gpio; struct spi_device *spidev; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index bf7d59f52738d..7692338730df5 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -50,6 +50,12 @@ sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd, l2_fwd[from].reach_port &= ~BIT(to); } +static bool sja1105_can_forward(struct sja1105_l2_forwarding_entry *l2_fwd, + int from, int to) +{ + return !!(l2_fwd[from].reach_port & BIT(to)); +} + /* Structure used to temporarily transport device tree * settings into sja1105_setup */ @@ -408,6 +414,12 @@ static int sja1105_init_l2_forwarding(struct sja1105_private *priv) for (j = 0; j < SJA1105_NUM_TC; j++) l2fwd[i].vlan_pmap[j] = j; + /* All ports start up with egress flooding enabled, + * including the CPU port. + */ + priv->ucast_egress_floods |= BIT(i); + priv->bcast_egress_floods |= BIT(i); + if (i == upstream) continue; @@ -1571,6 +1583,50 @@ static int sja1105_mdb_del(struct dsa_switch *ds, int port, return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid); } +/* Common function for unicast and broadcast flood configuration. + * Flooding is configured between each {ingress, egress} port pair, and since + * the bridge's semantics are those of "egress flooding", it means we must + * enable flooding towards this port from all ingress ports that are in the + * same forwarding domain. + */ +static int sja1105_manage_flood_domains(struct sja1105_private *priv) +{ + struct sja1105_l2_forwarding_entry *l2_fwd; + struct dsa_switch *ds = priv->ds; + int from, to, rc; + + l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries; + + for (from = 0; from < ds->num_ports; from++) { + u64 fl_domain = 0, bc_domain = 0; + + for (to = 0; to < priv->ds->num_ports; to++) { + if (!sja1105_can_forward(l2_fwd, from, to)) + continue; + + if (priv->ucast_egress_floods & BIT(to)) + fl_domain |= BIT(to); + if (priv->bcast_egress_floods & BIT(to)) + bc_domain |= BIT(to); + } + + /* Nothing changed, nothing to do */ + if (l2_fwd[from].fl_domain == fl_domain && + l2_fwd[from].bc_domain == bc_domain) + continue; + + l2_fwd[from].fl_domain = fl_domain; + l2_fwd[from].bc_domain = bc_domain; + + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, + from, &l2_fwd[from], true); + if (rc < 0) + return rc; + } + + return 0; +} + static int sja1105_bridge_member(struct dsa_switch *ds, int port, struct net_device *br, bool member) { @@ -1608,8 +1664,12 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port, return rc; } - return sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, - port, &l2_fwd[port], true); + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, + port, &l2_fwd[port], true); + if (rc) + return rc; + + return sja1105_manage_flood_domains(priv); } static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, @@ -3302,51 +3362,24 @@ static int sja1105_port_set_learning(struct sja1105_private *priv, int port, return 0; } -/* Common function for unicast and broadcast flood configuration. - * Flooding is configured between each {ingress, egress} port pair, and since - * the bridge's semantics are those of "egress flooding", it means we must - * enable flooding towards this port from all ingress ports that are in the - * same bridge. In practice, we just enable flooding from all possible ingress - * ports regardless of whether they're in the same bridge or not, since the - * reach_port configuration will not allow flooded frames to leak across - * bridging domains anyway. - */ static int sja1105_port_ucast_bcast_flood(struct sja1105_private *priv, int to, struct switchdev_brport_flags flags) { - struct sja1105_l2_forwarding_entry *l2_fwd; - int from, rc; - - l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries; - - for (from = 0; from < priv->ds->num_ports; from++) { - if (dsa_is_unused_port(priv->ds, from)) - continue; - if (from == to) - continue; - - /* Unicast */ - if (flags.mask & BR_FLOOD) { - if (flags.val & BR_FLOOD) - l2_fwd[from].fl_domain |= BIT(to); - else - l2_fwd[from].fl_domain &= ~BIT(to); - } - /* Broadcast */ - if (flags.mask & BR_BCAST_FLOOD) { - if (flags.val & BR_BCAST_FLOOD) - l2_fwd[from].bc_domain |= BIT(to); - else - l2_fwd[from].bc_domain &= ~BIT(to); - } + if (flags.mask & BR_FLOOD) { + if (flags.val & BR_FLOOD) + priv->ucast_egress_floods |= BIT(to); + else + priv->ucast_egress_floods |= BIT(to); + } - rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, - from, &l2_fwd[from], true); - if (rc < 0) - return rc; + if (flags.mask & BR_BCAST_FLOOD) { + if (flags.val & BR_BCAST_FLOOD) + priv->bcast_egress_floods |= BIT(to); + else + priv->bcast_egress_floods |= BIT(to); } - return 0; + return sja1105_manage_flood_domains(priv); } static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to,