aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch
diff options
context:
space:
mode:
authorChristian Marangi <ansuelsmth@gmail.com>2022-09-12 18:40:16 +0200
committerChristian Marangi <ansuelsmth@gmail.com>2022-09-19 15:19:53 +0200
commit7ace30aeb6ff58b4de845dd4722b281afd0c7eae (patch)
tree41c627ab344f5b8012610cccf5592d5876bc6586 /target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch
parentddcebda08b26142d47eb3c2009d48d12781d76a3 (diff)
downloadupstream-7ace30aeb6ff58b4de845dd4722b281afd0c7eae.tar.gz
upstream-7ace30aeb6ff58b4de845dd4722b281afd0c7eae.tar.bz2
upstream-7ace30aeb6ff58b4de845dd4722b281afd0c7eae.zip
generic: 5.15: qca8k: backport code split patch
Backport upstream code split patch for qca8k needed for ipq40xx target to correctly implement a DSA driver. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Diffstat (limited to 'target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch')
-rw-r--r--target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch384
1 files changed, 384 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch b/target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch
new file mode 100644
index 0000000000..1802b17eaa
--- /dev/null
+++ b/target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch
@@ -0,0 +1,384 @@
+From e9bbf019af44b204b71ef8edf224002550aab641 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 27 Jul 2022 13:35:22 +0200
+Subject: [PATCH 13/14] net: dsa: qca8k: move port LAG functions to common code
+
+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 | 168 -----------------------------
+ drivers/net/dsa/qca/qca8k-common.c | 165 ++++++++++++++++++++++++++++
+ drivers/net/dsa/qca/qca8k.h | 6 ++
+ 3 files changed, 171 insertions(+), 168 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1743,178 +1743,6 @@ qca8k_get_tag_protocol(struct dsa_switch
+ return DSA_TAG_PROTO_QCA;
+ }
+
+-static bool
+-qca8k_lag_can_offload(struct dsa_switch *ds,
+- struct net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- struct dsa_port *dp;
+- int id, members = 0;
+-
+- id = dsa_lag_id(ds->dst, lag);
+- if (id < 0 || id >= ds->num_lag_ids)
+- 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 net_device *lag,
+- struct netdev_lag_upper_info *info)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- bool unique_lag = true;
+- u32 hash = 0;
+- int i, id;
+-
+- id = dsa_lag_id(ds->dst, lag);
+-
+- 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 != id && dsa_lag_dev(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, "Error: Mismateched 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 net_device *lag, bool delete)
+-{
+- struct qca8k_priv *priv = ds->priv;
+- int ret, id, i;
+- u32 val;
+-
+- id = dsa_lag_id(ds->dst, lag);
+-
+- /* 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 net_device *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 net_device *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)
+--- a/drivers/net/dsa/qca/qca8k-common.c
++++ b/drivers/net/dsa/qca/qca8k-common.c
+@@ -1009,3 +1009,169 @@ int qca8k_port_vlan_del(struct dsa_switc
+
+ return ret;
+ }
++
++static bool qca8k_lag_can_offload(struct dsa_switch *ds,
++ struct net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct dsa_port *dp;
++ int id, members = 0;
++
++ id = dsa_lag_id(ds->dst, lag);
++ if (id < 0 || id >= ds->num_lag_ids)
++ 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 net_device *lag,
++ struct netdev_lag_upper_info *info)
++{
++ struct qca8k_priv *priv = ds->priv;
++ bool unique_lag = true;
++ u32 hash = 0;
++ int i, id;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ 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 != id && dsa_lag_dev(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, "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 net_device *lag, bool delete)
++{
++ struct qca8k_priv *priv = ds->priv;
++ int ret, id, i;
++ u32 val;
++
++ id = dsa_lag_id(ds->dst, lag);
++
++ /* 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 net_device *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 net_device *lag)
++{
++ return qca8k_lag_refresh_portmap(ds, port, lag, true);
++}
+--- a/drivers/net/dsa/qca/qca8k.h
++++ b/drivers/net/dsa/qca/qca8k.h
+@@ -495,4 +495,10 @@ int qca8k_port_vlan_add(struct dsa_switc
+ 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 net_device *lag,
++ struct netdev_lag_upper_info *info);
++int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
++ struct net_device *lag);
++
+ #endif /* __QCA8K_H */