net: dsa: qca8k: move port LAG functions to common code
authorChristian Marangi <ansuelsmth@gmail.com>
Wed, 27 Jul 2022 11:35:22 +0000 (13:35 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 29 Jul 2022 05:24:39 +0000 (22:24 -0700)
The same port LAG functions are used by drivers based on qca8k family
switch. Move them to common code to make them accessible also by other
drivers.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/dsa/qca/qca8k-8xxx.c
drivers/net/dsa/qca/qca8k-common.c
drivers/net/dsa/qca/qca8k.h

index 545f86e3c6ed47854bf4896c8920105a75cf613d..4d6ea47a44698de09f03643f656bbd2ceef2fbcb 100644 (file)
@@ -1593,174 +1593,6 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
        return DSA_TAG_PROTO_QCA;
 }
 
-static bool
-qca8k_lag_can_offload(struct dsa_switch *ds, struct dsa_lag lag,
-                     struct netdev_lag_upper_info *info)
-{
-       struct dsa_port *dp;
-       int members = 0;
-
-       if (!lag.id)
-               return false;
-
-       dsa_lag_foreach_port(dp, ds->dst, &lag)
-               /* Includes the port joining the LAG */
-               members++;
-
-       if (members > QCA8K_NUM_PORTS_FOR_LAG)
-               return false;
-
-       if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
-               return false;
-
-       if (info->hash_type != NETDEV_LAG_HASH_L2 &&
-           info->hash_type != NETDEV_LAG_HASH_L23)
-               return false;
-
-       return true;
-}
-
-static int
-qca8k_lag_setup_hash(struct dsa_switch *ds, struct dsa_lag lag,
-                    struct netdev_lag_upper_info *info)
-{
-       struct net_device *lag_dev = lag.dev;
-       struct qca8k_priv *priv = ds->priv;
-       bool unique_lag = true;
-       unsigned int i;
-       u32 hash = 0;
-
-       switch (info->hash_type) {
-       case NETDEV_LAG_HASH_L23:
-               hash |= QCA8K_TRUNK_HASH_SIP_EN;
-               hash |= QCA8K_TRUNK_HASH_DIP_EN;
-               fallthrough;
-       case NETDEV_LAG_HASH_L2:
-               hash |= QCA8K_TRUNK_HASH_SA_EN;
-               hash |= QCA8K_TRUNK_HASH_DA_EN;
-               break;
-       default: /* We should NEVER reach this */
-               return -EOPNOTSUPP;
-       }
-
-       /* Check if we are the unique configured LAG */
-       dsa_lags_foreach_id(i, ds->dst)
-               if (i != lag.id && dsa_lag_by_id(ds->dst, i)) {
-                       unique_lag = false;
-                       break;
-               }
-
-       /* Hash Mode is global. Make sure the same Hash Mode
-        * is set to all the 4 possible lag.
-        * If we are the unique LAG we can set whatever hash
-        * mode we want.
-        * To change hash mode it's needed to remove all LAG
-        * and change the mode with the latest.
-        */
-       if (unique_lag) {
-               priv->lag_hash_mode = hash;
-       } else if (priv->lag_hash_mode != hash) {
-               netdev_err(lag_dev, "Error: Mismatched Hash Mode across different lag is not supported\n");
-               return -EOPNOTSUPP;
-       }
-
-       return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
-                                 QCA8K_TRUNK_HASH_MASK, hash);
-}
-
-static int
-qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
-                         struct dsa_lag lag, bool delete)
-{
-       struct qca8k_priv *priv = ds->priv;
-       int ret, id, i;
-       u32 val;
-
-       /* DSA LAG IDs are one-based, hardware is zero-based */
-       id = lag.id - 1;
-
-       /* Read current port member */
-       ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
-       if (ret)
-               return ret;
-
-       /* Shift val to the correct trunk */
-       val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
-       val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
-       if (delete)
-               val &= ~BIT(port);
-       else
-               val |= BIT(port);
-
-       /* Update port member. With empty portmap disable trunk */
-       ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
-                                QCA8K_REG_GOL_TRUNK_MEMBER(id) |
-                                QCA8K_REG_GOL_TRUNK_EN(id),
-                                !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
-                                val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
-
-       /* Search empty member if adding or port on deleting */
-       for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
-               ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
-               if (ret)
-                       return ret;
-
-               val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
-               val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
-
-               if (delete) {
-                       /* If port flagged to be disabled assume this member is
-                        * empty
-                        */
-                       if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
-                               continue;
-
-                       val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
-                       if (val != port)
-                               continue;
-               } else {
-                       /* If port flagged to be enabled assume this member is
-                        * already set
-                        */
-                       if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
-                               continue;
-               }
-
-               /* We have found the member to add/remove */
-               break;
-       }
-
-       /* Set port in the correct port mask or disable port if in delete mode */
-       return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
-                                 QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
-                                 QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
-                                 !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
-                                 port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
-}
-
-static int
-qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
-                   struct netdev_lag_upper_info *info)
-{
-       int ret;
-
-       if (!qca8k_lag_can_offload(ds, lag, info))
-               return -EOPNOTSUPP;
-
-       ret = qca8k_lag_setup_hash(ds, lag, info);
-       if (ret)
-               return ret;
-
-       return qca8k_lag_refresh_portmap(ds, port, lag, false);
-}
-
-static int
-qca8k_port_lag_leave(struct dsa_switch *ds, int port,
-                    struct dsa_lag lag)
-{
-       return qca8k_lag_refresh_portmap(ds, port, lag, true);
-}
-
 static void
 qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
                    bool operational)
index 0e5ce532accd6fd7e2bfa185de0f0a1ce752858b..c881a95441dd31a3915382c7085b0db2038ced3d 100644 (file)
@@ -1014,3 +1014,168 @@ int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
 
        return ret;
 }
+
+static bool qca8k_lag_can_offload(struct dsa_switch *ds,
+                                 struct dsa_lag lag,
+                                 struct netdev_lag_upper_info *info)
+{
+       struct dsa_port *dp;
+       int members = 0;
+
+       if (!lag.id)
+               return false;
+
+       dsa_lag_foreach_port(dp, ds->dst, &lag)
+               /* Includes the port joining the LAG */
+               members++;
+
+       if (members > QCA8K_NUM_PORTS_FOR_LAG)
+               return false;
+
+       if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+               return false;
+
+       if (info->hash_type != NETDEV_LAG_HASH_L2 &&
+           info->hash_type != NETDEV_LAG_HASH_L23)
+               return false;
+
+       return true;
+}
+
+static int qca8k_lag_setup_hash(struct dsa_switch *ds,
+                               struct dsa_lag lag,
+                               struct netdev_lag_upper_info *info)
+{
+       struct net_device *lag_dev = lag.dev;
+       struct qca8k_priv *priv = ds->priv;
+       bool unique_lag = true;
+       unsigned int i;
+       u32 hash = 0;
+
+       switch (info->hash_type) {
+       case NETDEV_LAG_HASH_L23:
+               hash |= QCA8K_TRUNK_HASH_SIP_EN;
+               hash |= QCA8K_TRUNK_HASH_DIP_EN;
+               fallthrough;
+       case NETDEV_LAG_HASH_L2:
+               hash |= QCA8K_TRUNK_HASH_SA_EN;
+               hash |= QCA8K_TRUNK_HASH_DA_EN;
+               break;
+       default: /* We should NEVER reach this */
+               return -EOPNOTSUPP;
+       }
+
+       /* Check if we are the unique configured LAG */
+       dsa_lags_foreach_id(i, ds->dst)
+               if (i != lag.id && dsa_lag_by_id(ds->dst, i)) {
+                       unique_lag = false;
+                       break;
+               }
+
+       /* Hash Mode is global. Make sure the same Hash Mode
+        * is set to all the 4 possible lag.
+        * If we are the unique LAG we can set whatever hash
+        * mode we want.
+        * To change hash mode it's needed to remove all LAG
+        * and change the mode with the latest.
+        */
+       if (unique_lag) {
+               priv->lag_hash_mode = hash;
+       } else if (priv->lag_hash_mode != hash) {
+               netdev_err(lag_dev, "Error: Mismatched Hash Mode across different lag is not supported\n");
+               return -EOPNOTSUPP;
+       }
+
+       return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
+                                 QCA8K_TRUNK_HASH_MASK, hash);
+}
+
+static int qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
+                                    struct dsa_lag lag, bool delete)
+{
+       struct qca8k_priv *priv = ds->priv;
+       int ret, id, i;
+       u32 val;
+
+       /* DSA LAG IDs are one-based, hardware is zero-based */
+       id = lag.id - 1;
+
+       /* Read current port member */
+       ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
+       if (ret)
+               return ret;
+
+       /* Shift val to the correct trunk */
+       val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
+       val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
+       if (delete)
+               val &= ~BIT(port);
+       else
+               val |= BIT(port);
+
+       /* Update port member. With empty portmap disable trunk */
+       ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
+                                QCA8K_REG_GOL_TRUNK_MEMBER(id) |
+                                QCA8K_REG_GOL_TRUNK_EN(id),
+                                !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
+                                val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
+
+       /* Search empty member if adding or port on deleting */
+       for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
+               ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
+               if (ret)
+                       return ret;
+
+               val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
+               val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
+
+               if (delete) {
+                       /* If port flagged to be disabled assume this member is
+                        * empty
+                        */
+                       if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+                               continue;
+
+                       val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
+                       if (val != port)
+                               continue;
+               } else {
+                       /* If port flagged to be enabled assume this member is
+                        * already set
+                        */
+                       if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
+                               continue;
+               }
+
+               /* We have found the member to add/remove */
+               break;
+       }
+
+       /* Set port in the correct port mask or disable port if in delete mode */
+       return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
+                                 QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
+                                 QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
+                                 !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
+                                 port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
+}
+
+int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
+                       struct netdev_lag_upper_info *info)
+{
+       int ret;
+
+       if (!qca8k_lag_can_offload(ds, lag, info))
+               return -EOPNOTSUPP;
+
+       ret = qca8k_lag_setup_hash(ds, lag, info);
+       if (ret)
+               return ret;
+
+       return qca8k_lag_refresh_portmap(ds, port, lag, false);
+}
+
+int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+                        struct dsa_lag lag)
+{
+       return qca8k_lag_refresh_portmap(ds, port, lag, true);
+}
index 91f7abc5beb17f6d379ed2b4c22ba327503290dc..e87bfee837c19f851a6557d3582c6f7ad37fb191 100644 (file)
@@ -509,4 +509,10 @@ int qca8k_port_vlan_add(struct dsa_switch *ds, int port,
 int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
                        const struct switchdev_obj_port_vlan *vlan);
 
+/* Common port LAG function */
+int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
+                       struct netdev_lag_upper_info *info);
+int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
+                        struct dsa_lag lag);
+
 #endif /* __QCA8K_H */