mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	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>
		
			
				
	
	
		
			385 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			385 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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 */
 |