net: dsa: qca8k: make learning configurable and keep off if standalone
authorChristian Marangi <ansuelsmth@gmail.com>
Sun, 30 Jul 2023 07:41:10 +0000 (09:41 +0200)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 1 Aug 2023 10:02:42 +0000 (12:02 +0200)
Address learning should initially be turned off by the driver for port
operation in standalone mode, then the DSA core handles changes to it
via ds->ops->port_bridge_flags().

Currently this is not the case for qca8k where learning is enabled
unconditionally in qca8k_setup for every user port.

Handle ports configured in standalone mode by making the learning
configurable and not enabling it by default.

Implement .port_pre_bridge_flags and .port_bridge_flags dsa ops to
enable learning for bridge that request it and tweak
.port_stp_state_set to correctly disable learning when port is
configured in standalone mode.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Link: https://lore.kernel.org/r/20230730074113.21889-2-ansuelsmth@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/dsa/qca/qca8k-8xxx.c
drivers/net/dsa/qca/qca8k-common.c
drivers/net/dsa/qca/qca8k.h

index 47b9f5b..dcef16c 100644 (file)
@@ -1870,9 +1870,8 @@ qca8k_setup(struct dsa_switch *ds)
                        if (ret)
                                return ret;
 
-                       /* Enable ARP Auto-learning by default */
-                       ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
-                                             QCA8K_PORT_LOOKUP_LEARN);
+                       ret = regmap_clear_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i),
+                                               QCA8K_PORT_LOOKUP_LEARN);
                        if (ret)
                                return ret;
 
@@ -1978,6 +1977,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
        .port_change_mtu        = qca8k_port_change_mtu,
        .port_max_mtu           = qca8k_port_max_mtu,
        .port_stp_state_set     = qca8k_port_stp_state_set,
+       .port_pre_bridge_flags  = qca8k_port_pre_bridge_flags,
+       .port_bridge_flags      = qca8k_port_bridge_flags,
        .port_bridge_join       = qca8k_port_bridge_join,
        .port_bridge_leave      = qca8k_port_bridge_leave,
        .port_fast_age          = qca8k_port_fast_age,
index 13b8452..fce04ce 100644 (file)
@@ -565,9 +565,26 @@ int qca8k_get_mac_eee(struct dsa_switch *ds, int port,
        return 0;
 }
 
+static int qca8k_port_configure_learning(struct dsa_switch *ds, int port,
+                                        bool learning)
+{
+       struct qca8k_priv *priv = ds->priv;
+
+       if (learning)
+               return regmap_set_bits(priv->regmap,
+                                      QCA8K_PORT_LOOKUP_CTRL(port),
+                                      QCA8K_PORT_LOOKUP_LEARN);
+       else
+               return regmap_clear_bits(priv->regmap,
+                                        QCA8K_PORT_LOOKUP_CTRL(port),
+                                        QCA8K_PORT_LOOKUP_LEARN);
+}
+
 void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
 {
+       struct dsa_port *dp = dsa_to_port(ds, port);
        struct qca8k_priv *priv = ds->priv;
+       bool learning = false;
        u32 stp_state;
 
        switch (state) {
@@ -582,8 +599,11 @@ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
                break;
        case BR_STATE_LEARNING:
                stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING;
+               learning = dp->learning;
                break;
        case BR_STATE_FORWARDING:
+               learning = dp->learning;
+               fallthrough;
        default:
                stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD;
                break;
@@ -591,6 +611,34 @@ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
 
        qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
                  QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
+
+       qca8k_port_configure_learning(ds, port, learning);
+}
+
+int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+                               struct switchdev_brport_flags flags,
+                               struct netlink_ext_ack *extack)
+{
+       if (flags.mask & ~BR_LEARNING)
+               return -EINVAL;
+
+       return 0;
+}
+
+int qca8k_port_bridge_flags(struct dsa_switch *ds, int port,
+                           struct switchdev_brport_flags flags,
+                           struct netlink_ext_ack *extack)
+{
+       int ret;
+
+       if (flags.mask & BR_LEARNING) {
+               ret = qca8k_port_configure_learning(ds, port,
+                                                   flags.val & BR_LEARNING);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
 }
 
 int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
index c5cc8a1..8f88b7d 100644 (file)
@@ -522,6 +522,12 @@ int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
 
 /* Common bridge function */
 void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
+int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+                               struct switchdev_brport_flags flags,
+                               struct netlink_ext_ack *extack);
+int qca8k_port_bridge_flags(struct dsa_switch *ds, int port,
+                           struct switchdev_brport_flags flags,
+                           struct netlink_ext_ack *extack);
 int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
                           struct dsa_bridge bridge,
                           bool *tx_fwd_offload,