 7ace30aeb6
			
		
	
	7ace30aeb6
	
	
	
		
			
			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>
		
			
				
	
	
		
			449 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			449 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From c5290f636624b98e76a82bd63ffec0a8a9daa620 Mon Sep 17 00:00:00 2001
 | |
| From: Christian Marangi <ansuelsmth@gmail.com>
 | |
| Date: Wed, 27 Jul 2022 13:35:21 +0200
 | |
| Subject: [PATCH 12/14] net: dsa: qca8k: move port VLAN functions to common
 | |
|  code
 | |
| 
 | |
| The same port VLAN functions are used by drivers based on qca8k family
 | |
| switch. Move them to common code to make them accessible also by other
 | |
| drivers.
 | |
| Also drop exposing busy_wait and make it static.
 | |
| 
 | |
| 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   | 182 -----------------------------
 | |
|  drivers/net/dsa/qca/qca8k-common.c | 179 +++++++++++++++++++++++++++-
 | |
|  drivers/net/dsa/qca/qca8k.h        |  10 +-
 | |
|  3 files changed, 187 insertions(+), 184 deletions(-)
 | |
| 
 | |
| --- a/drivers/net/dsa/qca/qca8k-8xxx.c
 | |
| +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
 | |
| @@ -15,7 +15,6 @@
 | |
|  #include <linux/of_net.h>
 | |
|  #include <linux/of_mdio.h>
 | |
|  #include <linux/of_platform.h>
 | |
| -#include <linux/if_bridge.h>
 | |
|  #include <linux/mdio.h>
 | |
|  #include <linux/phylink.h>
 | |
|  #include <linux/gpio/consumer.h>
 | |
| @@ -442,122 +441,6 @@ static struct regmap_config qca8k_regmap
 | |
|  };
 | |
|  
 | |
|  static int
 | |
| -qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)
 | |
| -{
 | |
| -	u32 reg;
 | |
| -	int ret;
 | |
| -
 | |
| -	/* Set the command and VLAN index */
 | |
| -	reg = QCA8K_VTU_FUNC1_BUSY;
 | |
| -	reg |= cmd;
 | |
| -	reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
 | |
| -
 | |
| -	/* Write the function register triggering the table access */
 | |
| -	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
 | |
| -	if (ret)
 | |
| -		return ret;
 | |
| -
 | |
| -	/* wait for completion */
 | |
| -	ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
 | |
| -	if (ret)
 | |
| -		return ret;
 | |
| -
 | |
| -	/* Check for table full violation when adding an entry */
 | |
| -	if (cmd == QCA8K_VLAN_LOAD) {
 | |
| -		ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®);
 | |
| -		if (ret < 0)
 | |
| -			return ret;
 | |
| -		if (reg & QCA8K_VTU_FUNC1_FULL)
 | |
| -			return -ENOMEM;
 | |
| -	}
 | |
| -
 | |
| -	return 0;
 | |
| -}
 | |
| -
 | |
| -static int
 | |
| -qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)
 | |
| -{
 | |
| -	u32 reg;
 | |
| -	int ret;
 | |
| -
 | |
| -	/*
 | |
| -	   We do the right thing with VLAN 0 and treat it as untagged while
 | |
| -	   preserving the tag on egress.
 | |
| -	 */
 | |
| -	if (vid == 0)
 | |
| -		return 0;
 | |
| -
 | |
| -	mutex_lock(&priv->reg_mutex);
 | |
| -	ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
 | |
| -	if (ret < 0)
 | |
| -		goto out;
 | |
| -
 | |
| -	ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®);
 | |
| -	if (ret < 0)
 | |
| -		goto out;
 | |
| -	reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
 | |
| -	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
 | |
| -	if (untagged)
 | |
| -		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
 | |
| -	else
 | |
| -		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
 | |
| -
 | |
| -	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
 | |
| -	if (ret)
 | |
| -		goto out;
 | |
| -	ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
 | |
| -
 | |
| -out:
 | |
| -	mutex_unlock(&priv->reg_mutex);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static int
 | |
| -qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
 | |
| -{
 | |
| -	u32 reg, mask;
 | |
| -	int ret, i;
 | |
| -	bool del;
 | |
| -
 | |
| -	mutex_lock(&priv->reg_mutex);
 | |
| -	ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
 | |
| -	if (ret < 0)
 | |
| -		goto out;
 | |
| -
 | |
| -	ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®);
 | |
| -	if (ret < 0)
 | |
| -		goto out;
 | |
| -	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
 | |
| -	reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
 | |
| -
 | |
| -	/* Check if we're the last member to be removed */
 | |
| -	del = true;
 | |
| -	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
 | |
| -		mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
 | |
| -
 | |
| -		if ((reg & mask) != mask) {
 | |
| -			del = false;
 | |
| -			break;
 | |
| -		}
 | |
| -	}
 | |
| -
 | |
| -	if (del) {
 | |
| -		ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
 | |
| -	} else {
 | |
| -		ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
 | |
| -		if (ret)
 | |
| -			goto out;
 | |
| -		ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
 | |
| -	}
 | |
| -
 | |
| -out:
 | |
| -	mutex_unlock(&priv->reg_mutex);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static int
 | |
|  qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data,
 | |
|  			struct sk_buff *read_skb, u32 *val)
 | |
|  {
 | |
| @@ -1836,71 +1719,6 @@ exit:
 | |
|  
 | |
|  	return ret;
 | |
|  }
 | |
| -
 | |
| -static int
 | |
| -qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 | |
| -			  struct netlink_ext_ack *extack)
 | |
| -{
 | |
| -	struct qca8k_priv *priv = ds->priv;
 | |
| -	int ret;
 | |
| -
 | |
| -	if (vlan_filtering) {
 | |
| -		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
 | |
| -				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
 | |
| -				QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
 | |
| -	} else {
 | |
| -		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
 | |
| -				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
 | |
| -				QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
 | |
| -	}
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static int
 | |
| -qca8k_port_vlan_add(struct dsa_switch *ds, int port,
 | |
| -		    const struct switchdev_obj_port_vlan *vlan,
 | |
| -		    struct netlink_ext_ack *extack)
 | |
| -{
 | |
| -	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
 | |
| -	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
 | |
| -	struct qca8k_priv *priv = ds->priv;
 | |
| -	int ret;
 | |
| -
 | |
| -	ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
 | |
| -	if (ret) {
 | |
| -		dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
 | |
| -		return ret;
 | |
| -	}
 | |
| -
 | |
| -	if (pvid) {
 | |
| -		ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
 | |
| -				QCA8K_EGREES_VLAN_PORT_MASK(port),
 | |
| -				QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
 | |
| -		if (ret)
 | |
| -			return ret;
 | |
| -
 | |
| -		ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
 | |
| -				  QCA8K_PORT_VLAN_CVID(vlan->vid) |
 | |
| -				  QCA8K_PORT_VLAN_SVID(vlan->vid));
 | |
| -	}
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static int
 | |
| -qca8k_port_vlan_del(struct dsa_switch *ds, int port,
 | |
| -		    const struct switchdev_obj_port_vlan *vlan)
 | |
| -{
 | |
| -	struct qca8k_priv *priv = ds->priv;
 | |
| -	int ret;
 | |
| -
 | |
| -	ret = qca8k_vlan_del(priv, port, vlan->vid);
 | |
| -	if (ret)
 | |
| -		dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
|  
 | |
|  static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port)
 | |
|  {
 | |
| --- a/drivers/net/dsa/qca/qca8k-common.c
 | |
| +++ b/drivers/net/dsa/qca/qca8k-common.c
 | |
| @@ -141,7 +141,7 @@ static int qca8k_bulk_write(struct qca8k
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| -int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
 | |
| +static int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)
 | |
|  {
 | |
|  	u32 val;
 | |
|  
 | |
| @@ -354,6 +354,120 @@ exit:
 | |
|  	return ret;
 | |
|  }
 | |
|  
 | |
| +static int qca8k_vlan_access(struct qca8k_priv *priv,
 | |
| +			     enum qca8k_vlan_cmd cmd, u16 vid)
 | |
| +{
 | |
| +	u32 reg;
 | |
| +	int ret;
 | |
| +
 | |
| +	/* Set the command and VLAN index */
 | |
| +	reg = QCA8K_VTU_FUNC1_BUSY;
 | |
| +	reg |= cmd;
 | |
| +	reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);
 | |
| +
 | |
| +	/* Write the function register triggering the table access */
 | |
| +	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg);
 | |
| +	if (ret)
 | |
| +		return ret;
 | |
| +
 | |
| +	/* wait for completion */
 | |
| +	ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY);
 | |
| +	if (ret)
 | |
| +		return ret;
 | |
| +
 | |
| +	/* Check for table full violation when adding an entry */
 | |
| +	if (cmd == QCA8K_VLAN_LOAD) {
 | |
| +		ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®);
 | |
| +		if (ret < 0)
 | |
| +			return ret;
 | |
| +		if (reg & QCA8K_VTU_FUNC1_FULL)
 | |
| +			return -ENOMEM;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid,
 | |
| +			  bool untagged)
 | |
| +{
 | |
| +	u32 reg;
 | |
| +	int ret;
 | |
| +
 | |
| +	/* We do the right thing with VLAN 0 and treat it as untagged while
 | |
| +	 * preserving the tag on egress.
 | |
| +	 */
 | |
| +	if (vid == 0)
 | |
| +		return 0;
 | |
| +
 | |
| +	mutex_lock(&priv->reg_mutex);
 | |
| +	ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
 | |
| +	if (ret < 0)
 | |
| +		goto out;
 | |
| +
 | |
| +	ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®);
 | |
| +	if (ret < 0)
 | |
| +		goto out;
 | |
| +	reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN;
 | |
| +	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
 | |
| +	if (untagged)
 | |
| +		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);
 | |
| +	else
 | |
| +		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);
 | |
| +
 | |
| +	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
 | |
| +	if (ret)
 | |
| +		goto out;
 | |
| +	ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
 | |
| +
 | |
| +out:
 | |
| +	mutex_unlock(&priv->reg_mutex);
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static int qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)
 | |
| +{
 | |
| +	u32 reg, mask;
 | |
| +	int ret, i;
 | |
| +	bool del;
 | |
| +
 | |
| +	mutex_lock(&priv->reg_mutex);
 | |
| +	ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid);
 | |
| +	if (ret < 0)
 | |
| +		goto out;
 | |
| +
 | |
| +	ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®);
 | |
| +	if (ret < 0)
 | |
| +		goto out;
 | |
| +	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);
 | |
| +	reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);
 | |
| +
 | |
| +	/* Check if we're the last member to be removed */
 | |
| +	del = true;
 | |
| +	for (i = 0; i < QCA8K_NUM_PORTS; i++) {
 | |
| +		mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);
 | |
| +
 | |
| +		if ((reg & mask) != mask) {
 | |
| +			del = false;
 | |
| +			break;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	if (del) {
 | |
| +		ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid);
 | |
| +	} else {
 | |
| +		ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);
 | |
| +		if (ret)
 | |
| +			goto out;
 | |
| +		ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid);
 | |
| +	}
 | |
| +
 | |
| +out:
 | |
| +	mutex_unlock(&priv->reg_mutex);
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
|  int qca8k_mib_init(struct qca8k_priv *priv)
 | |
|  {
 | |
|  	int ret;
 | |
| @@ -832,3 +946,66 @@ void qca8k_port_mirror_del(struct dsa_sw
 | |
|  err:
 | |
|  	dev_err(priv->dev, "Failed to del mirror port from %d", port);
 | |
|  }
 | |
| +
 | |
| +int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port,
 | |
| +			      bool vlan_filtering,
 | |
| +			      struct netlink_ext_ack *extack)
 | |
| +{
 | |
| +	struct qca8k_priv *priv = ds->priv;
 | |
| +	int ret;
 | |
| +
 | |
| +	if (vlan_filtering) {
 | |
| +		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
 | |
| +				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
 | |
| +				QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);
 | |
| +	} else {
 | |
| +		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
 | |
| +				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,
 | |
| +				QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);
 | |
| +	}
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +int qca8k_port_vlan_add(struct dsa_switch *ds, int port,
 | |
| +			const struct switchdev_obj_port_vlan *vlan,
 | |
| +			struct netlink_ext_ack *extack)
 | |
| +{
 | |
| +	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
 | |
| +	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
 | |
| +	struct qca8k_priv *priv = ds->priv;
 | |
| +	int ret;
 | |
| +
 | |
| +	ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
 | |
| +	if (ret) {
 | |
| +		dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	if (pvid) {
 | |
| +		ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
 | |
| +				QCA8K_EGREES_VLAN_PORT_MASK(port),
 | |
| +				QCA8K_EGREES_VLAN_PORT(port, vlan->vid));
 | |
| +		if (ret)
 | |
| +			return ret;
 | |
| +
 | |
| +		ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
 | |
| +				  QCA8K_PORT_VLAN_CVID(vlan->vid) |
 | |
| +				  QCA8K_PORT_VLAN_SVID(vlan->vid));
 | |
| +	}
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
 | |
| +			const struct switchdev_obj_port_vlan *vlan)
 | |
| +{
 | |
| +	struct qca8k_priv *priv = ds->priv;
 | |
| +	int ret;
 | |
| +
 | |
| +	ret = qca8k_vlan_del(priv, port, vlan->vid);
 | |
| +	if (ret)
 | |
| +		dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| --- a/drivers/net/dsa/qca/qca8k.h
 | |
| +++ b/drivers/net/dsa/qca/qca8k.h
 | |
| @@ -431,7 +431,6 @@ int qca8k_write(struct qca8k_priv *priv,
 | |
|  int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val);
 | |
|  
 | |
|  /* Common ops function */
 | |
| -int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask);
 | |
|  void qca8k_fdb_flush(struct qca8k_priv *priv);
 | |
|  
 | |
|  /* Common ethtool stats function */
 | |
| @@ -487,4 +486,13 @@ int qca8k_port_mirror_add(struct dsa_swi
 | |
|  void qca8k_port_mirror_del(struct dsa_switch *ds, int port,
 | |
|  			   struct dsa_mall_mirror_tc_entry *mirror);
 | |
|  
 | |
| +/* Common port VLAN function */
 | |
| +int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 | |
| +			      struct netlink_ext_ack *extack);
 | |
| +int qca8k_port_vlan_add(struct dsa_switch *ds, int port,
 | |
| +			const struct switchdev_obj_port_vlan *vlan,
 | |
| +			struct netlink_ext_ack *extack);
 | |
| +int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
 | |
| +			const struct switchdev_obj_port_vlan *vlan);
 | |
| +
 | |
|  #endif /* __QCA8K_H */
 |