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>
This commit is contained in:
		| @@ -0,0 +1,28 @@ | |||||||
|  | From c3664d913dc115cab4a5fdb5634df4887048000e Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Dan Carpenter <dan.carpenter@oracle.com> | ||||||
|  | Date: Fri, 4 Feb 2022 13:03:36 +0300 | ||||||
|  | Subject: [PATCH 1/1] net: dsa: qca8k: check correct variable in | ||||||
|  |  qca8k_phy_eth_command() | ||||||
|  |  | ||||||
|  | This is a copy and paste bug.  It was supposed to check "clear_skb" | ||||||
|  | instead of "write_skb". | ||||||
|  |  | ||||||
|  | Fixes: 2cd548566384 ("net: dsa: qca8k: add support for phy read/write with mgmt Ethernet") | ||||||
|  | Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> | ||||||
|  | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca8k.c | 2 +- | ||||||
|  |  1 file changed, 1 insertion(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca8k.c | ||||||
|  | @@ -1018,7 +1018,7 @@ qca8k_phy_eth_command(struct qca8k_priv | ||||||
|  |   | ||||||
|  |  	clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, | ||||||
|  |  					    QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); | ||||||
|  | -	if (!write_skb) { | ||||||
|  | +	if (!clear_skb) { | ||||||
|  |  		ret = -ENOMEM; | ||||||
|  |  		goto err_clear_skb; | ||||||
|  |  	} | ||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | From 4f5e483b8c7a644733db941a1ae00173baa7b463 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: kernel test robot <lkp@intel.com> | ||||||
|  | Date: Thu, 10 Feb 2022 06:13:04 +0800 | ||||||
|  | Subject: [PATCH 1/1] net: dsa: qca8k: fix noderef.cocci warnings | ||||||
|  |  | ||||||
|  | drivers/net/dsa/qca8k.c:422:37-43: ERROR: application of sizeof to pointer | ||||||
|  |  | ||||||
|  |  sizeof when applied to a pointer typed expression gives the size of | ||||||
|  |  the pointer | ||||||
|  |  | ||||||
|  | Generated by: scripts/coccinelle/misc/noderef.cocci | ||||||
|  |  | ||||||
|  | Fixes: 90386223f44e ("net: dsa: qca8k: add support for larger read/write size with mgmt Ethernet") | ||||||
|  | CC: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reported-by: kernel test robot <lkp@intel.com> | ||||||
|  | Signed-off-by: kernel test robot <lkp@intel.com> | ||||||
|  | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> | ||||||
|  | Link: https://lore.kernel.org/r/20220209221304.GA17529@d2214a582157 | ||||||
|  | Signed-off-by: Jakub Kicinski <kuba@kernel.org> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca8k.c | 2 +- | ||||||
|  |  1 file changed, 1 insertion(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca8k.c | ||||||
|  | @@ -456,7 +456,7 @@ qca8k_regmap_read(void *ctx, uint32_t re | ||||||
|  |  	u16 r1, r2, page; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | -	if (!qca8k_read_eth(priv, reg, val, sizeof(val))) | ||||||
|  | +	if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) | ||||||
|  |  		return 0; | ||||||
|  |   | ||||||
|  |  	qca8k_split_addr(reg, &r1, &r2, &page); | ||||||
| @@ -0,0 +1,79 @@ | |||||||
|  | From 69fd055957a02309ffdc23d887a01988b6e5bab1 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Sat, 16 Apr 2022 01:30:12 +0200 | ||||||
|  | Subject: [PATCH 1/6] net: dsa: qca8k: drop MTU tracking from qca8k_priv | ||||||
|  |  | ||||||
|  | DSA set the CPU port based on the largest MTU of all the slave ports. | ||||||
|  | Based on this we can drop the MTU array from qca8k_priv and set the | ||||||
|  | port_change_mtu logic on DSA changing MTU of the CPU port as the switch | ||||||
|  | have a global MTU settingfor each port. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Vladimir Oltean <olteanv@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca8k.c | 26 +++++++++----------------- | ||||||
|  |  drivers/net/dsa/qca8k.h |  1 - | ||||||
|  |  2 files changed, 9 insertions(+), 18 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca8k.c | ||||||
|  | @@ -1803,16 +1803,6 @@ qca8k_setup(struct dsa_switch *ds) | ||||||
|  |  				  QCA8K_PORT_HOL_CTRL1_WRED_EN, | ||||||
|  |  				  mask); | ||||||
|  |  		} | ||||||
|  | - | ||||||
|  | -		/* Set initial MTU for every port. | ||||||
|  | -		 * We have only have a general MTU setting. So track | ||||||
|  | -		 * every port and set the max across all port. | ||||||
|  | -		 * Set per port MTU to 1500 as the MTU change function | ||||||
|  | -		 * will add the overhead and if its set to 1518 then it | ||||||
|  | -		 * will apply the overhead again and we will end up with | ||||||
|  | -		 * MTU of 1536 instead of 1518 | ||||||
|  | -		 */ | ||||||
|  | -		priv->port_mtu[i] = ETH_DATA_LEN; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	/* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ | ||||||
|  | @@ -2525,13 +2515,16 @@ static int | ||||||
|  |  qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) | ||||||
|  |  { | ||||||
|  |  	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	int ret, i, mtu = 0; | ||||||
|  | - | ||||||
|  | -	priv->port_mtu[port] = new_mtu; | ||||||
|  | +	int ret; | ||||||
|  |   | ||||||
|  | -	for (i = 0; i < QCA8K_NUM_PORTS; i++) | ||||||
|  | -		if (priv->port_mtu[i] > mtu) | ||||||
|  | -			mtu = priv->port_mtu[i]; | ||||||
|  | +	/* We have only have a general MTU setting. | ||||||
|  | +	 * DSA always set the CPU port's MTU to the largest MTU of the slave | ||||||
|  | +	 * ports. | ||||||
|  | +	 * Setting MTU just for the CPU port is sufficient to correctly set a | ||||||
|  | +	 * value for every port. | ||||||
|  | +	 */ | ||||||
|  | +	if (!dsa_is_cpu_port(ds, port)) | ||||||
|  | +		return 0; | ||||||
|  |   | ||||||
|  |  	/* To change the MAX_FRAME_SIZE the cpu ports must be off or | ||||||
|  |  	 * the switch panics. | ||||||
|  | @@ -2545,7 +2538,7 @@ qca8k_port_change_mtu(struct dsa_switch | ||||||
|  |  		qca8k_port_set_status(priv, 6, 0); | ||||||
|  |   | ||||||
|  |  	/* Include L2 header / FCS length */ | ||||||
|  | -	ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); | ||||||
|  | +	ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); | ||||||
|  |   | ||||||
|  |  	if (priv->port_sts[0].enabled) | ||||||
|  |  		qca8k_port_set_status(priv, 0, 1); | ||||||
|  | --- a/drivers/net/dsa/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca8k.h | ||||||
|  | @@ -392,7 +392,6 @@ struct qca8k_priv { | ||||||
|  |  	struct device *dev; | ||||||
|  |  	struct dsa_switch_ops ops; | ||||||
|  |  	struct gpio_desc *reset_gpio; | ||||||
|  | -	unsigned int port_mtu[QCA8K_NUM_PORTS]; | ||||||
|  |  	struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ | ||||||
|  |  	struct qca8k_mgmt_eth_data mgmt_eth_data; | ||||||
|  |  	struct qca8k_mib_eth_data mib_eth_data; | ||||||
| @@ -0,0 +1,116 @@ | |||||||
|  | From 2b8fd87af7f156942971789abac8ee2bb60c03bc Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Sat, 16 Apr 2022 01:30:13 +0200 | ||||||
|  | Subject: [PATCH 2/6] net: dsa: qca8k: drop port_sts from qca8k_priv | ||||||
|  |  | ||||||
|  | Port_sts is a thing of the past for this driver. It was something | ||||||
|  | present on the initial implementation of this driver and parts of the | ||||||
|  | original struct were dropped over time. Using an array of int to store if | ||||||
|  | a port is enabled or not to handle PM operation seems overkill. Switch | ||||||
|  | and use a simple u8 to store the port status where each bit correspond | ||||||
|  | to a port. (bit is set port is enabled, bit is not set, port is disabled) | ||||||
|  | Also add some comments to better describe why we need to track port | ||||||
|  | status. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Vladimir Oltean <olteanv@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca8k.c | 15 +++++++++------ | ||||||
|  |  drivers/net/dsa/qca8k.h |  9 ++++----- | ||||||
|  |  2 files changed, 13 insertions(+), 11 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca8k.c | ||||||
|  | @@ -2494,7 +2494,7 @@ qca8k_port_enable(struct dsa_switch *ds, | ||||||
|  |  	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  |   | ||||||
|  |  	qca8k_port_set_status(priv, port, 1); | ||||||
|  | -	priv->port_sts[port].enabled = 1; | ||||||
|  | +	priv->port_enabled_map |= BIT(port); | ||||||
|  |   | ||||||
|  |  	if (dsa_is_user_port(ds, port)) | ||||||
|  |  		phy_support_asym_pause(phy); | ||||||
|  | @@ -2508,7 +2508,7 @@ qca8k_port_disable(struct dsa_switch *ds | ||||||
|  |  	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  |   | ||||||
|  |  	qca8k_port_set_status(priv, port, 0); | ||||||
|  | -	priv->port_sts[port].enabled = 0; | ||||||
|  | +	priv->port_enabled_map &= ~BIT(port); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | @@ -2531,19 +2531,19 @@ qca8k_port_change_mtu(struct dsa_switch | ||||||
|  |  	 * Turn off both cpu ports before applying the new value to prevent | ||||||
|  |  	 * this. | ||||||
|  |  	 */ | ||||||
|  | -	if (priv->port_sts[0].enabled) | ||||||
|  | +	if (priv->port_enabled_map & BIT(0)) | ||||||
|  |  		qca8k_port_set_status(priv, 0, 0); | ||||||
|  |   | ||||||
|  | -	if (priv->port_sts[6].enabled) | ||||||
|  | +	if (priv->port_enabled_map & BIT(6)) | ||||||
|  |  		qca8k_port_set_status(priv, 6, 0); | ||||||
|  |   | ||||||
|  |  	/* Include L2 header / FCS length */ | ||||||
|  |  	ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); | ||||||
|  |   | ||||||
|  | -	if (priv->port_sts[0].enabled) | ||||||
|  | +	if (priv->port_enabled_map & BIT(0)) | ||||||
|  |  		qca8k_port_set_status(priv, 0, 1); | ||||||
|  |   | ||||||
|  | -	if (priv->port_sts[6].enabled) | ||||||
|  | +	if (priv->port_enabled_map & BIT(6)) | ||||||
|  |  		qca8k_port_set_status(priv, 6, 1); | ||||||
|  |   | ||||||
|  |  	return ret; | ||||||
|  | @@ -3199,13 +3199,16 @@ static void qca8k_sw_shutdown(struct mdi | ||||||
|  |  static void | ||||||
|  |  qca8k_set_pm(struct qca8k_priv *priv, int enable) | ||||||
|  |  { | ||||||
|  | -	int i; | ||||||
|  | +	int port; | ||||||
|  |   | ||||||
|  | -	for (i = 0; i < QCA8K_NUM_PORTS; i++) { | ||||||
|  | -		if (!priv->port_sts[i].enabled) | ||||||
|  | +	for (port = 0; port < QCA8K_NUM_PORTS; port++) { | ||||||
|  | +		/* Do not enable on resume if the port was | ||||||
|  | +		 * disabled before. | ||||||
|  | +		 */ | ||||||
|  | +		if (!(priv->port_enabled_map & BIT(port))) | ||||||
|  |  			continue; | ||||||
|  |   | ||||||
|  | -		qca8k_port_set_status(priv, i, enable); | ||||||
|  | +		qca8k_port_set_status(priv, port, enable); | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | --- a/drivers/net/dsa/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca8k.h | ||||||
|  | @@ -324,10 +324,6 @@ enum qca8k_mid_cmd { | ||||||
|  |  	QCA8K_MIB_CAST = 3, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -struct ar8xxx_port_status { | ||||||
|  | -	int enabled; | ||||||
|  | -}; | ||||||
|  | - | ||||||
|  |  struct qca8k_match_data { | ||||||
|  |  	u8 id; | ||||||
|  |  	bool reduced_package; | ||||||
|  | @@ -382,11 +378,14 @@ struct qca8k_priv { | ||||||
|  |  	u8 mirror_rx; | ||||||
|  |  	u8 mirror_tx; | ||||||
|  |  	u8 lag_hash_mode; | ||||||
|  | +	/* Each bit correspond to a port. This switch can support a max of 7 port. | ||||||
|  | +	 * Bit 1: port enabled. Bit 0: port disabled. | ||||||
|  | +	 */ | ||||||
|  | +	u8 port_enabled_map; | ||||||
|  |  	bool legacy_phy_port_mapping; | ||||||
|  |  	struct qca8k_ports_config ports_config; | ||||||
|  |  	struct regmap *regmap; | ||||||
|  |  	struct mii_bus *bus; | ||||||
|  | -	struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; | ||||||
|  |  	struct dsa_switch *ds; | ||||||
|  |  	struct mutex reg_mutex; | ||||||
|  |  	struct device *dev; | ||||||
| @@ -0,0 +1,173 @@ | |||||||
|  | From 8255212e4130bd2dc1463286a3dddb74797bbdc1 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Sat, 16 Apr 2022 01:30:14 +0200 | ||||||
|  | Subject: [PATCH 3/6] net: dsa: qca8k: rework and simplify mdiobus logic | ||||||
|  |  | ||||||
|  | In an attempt to reduce qca8k_priv space, rework and simplify mdiobus | ||||||
|  | logic. | ||||||
|  | We now declare a mdiobus instead of relying on DSA phy_read/write even | ||||||
|  | if a mdio node is not present. This is all to make the qca8k ops static | ||||||
|  | and not switch specific. With a legacy implementation where port doesn't | ||||||
|  | have a phy map declared in the dts with a mdio node, we declare a | ||||||
|  | 'qca8k-legacy' mdiobus. The conversion logic is used as legacy read and | ||||||
|  | write ops are used instead of the internal one. | ||||||
|  | Also drop the legacy_phy_port_mapping as we now declare mdiobus with ops | ||||||
|  | that already address the workaround. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca8k.c | 95 +++++++++++++---------------------------- | ||||||
|  |  drivers/net/dsa/qca8k.h |  1 - | ||||||
|  |  2 files changed, 29 insertions(+), 67 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca8k.c | ||||||
|  | @@ -1291,83 +1291,63 @@ qca8k_internal_mdio_read(struct mii_bus | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | -qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) | ||||||
|  | +qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) | ||||||
|  |  { | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	int ret; | ||||||
|  | +	port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; | ||||||
|  |   | ||||||
|  | -	/* Check if the legacy mapping should be used and the | ||||||
|  | -	 * port is not correctly mapped to the right PHY in the | ||||||
|  | -	 * devicetree | ||||||
|  | -	 */ | ||||||
|  | -	if (priv->legacy_phy_port_mapping) | ||||||
|  | -		port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; | ||||||
|  | - | ||||||
|  | -	/* Use mdio Ethernet when available, fallback to legacy one on error */ | ||||||
|  | -	ret = qca8k_phy_eth_command(priv, false, port, regnum, 0); | ||||||
|  | -	if (!ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	return qca8k_mdio_write(priv, port, regnum, data); | ||||||
|  | +	return qca8k_internal_mdio_write(slave_bus, port, regnum, data); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | -qca8k_phy_read(struct dsa_switch *ds, int port, int regnum) | ||||||
|  | +qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) | ||||||
|  |  { | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	/* Check if the legacy mapping should be used and the | ||||||
|  | -	 * port is not correctly mapped to the right PHY in the | ||||||
|  | -	 * devicetree | ||||||
|  | -	 */ | ||||||
|  | -	if (priv->legacy_phy_port_mapping) | ||||||
|  | -		port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; | ||||||
|  | - | ||||||
|  | -	/* Use mdio Ethernet when available, fallback to legacy one on error */ | ||||||
|  | -	ret = qca8k_phy_eth_command(priv, true, port, regnum, 0); | ||||||
|  | -	if (ret >= 0) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	ret = qca8k_mdio_read(priv, port, regnum); | ||||||
|  | - | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		return 0xffff; | ||||||
|  | +	port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; | ||||||
|  |   | ||||||
|  | -	return ret; | ||||||
|  | +	return qca8k_internal_mdio_read(slave_bus, port, regnum); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | -qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio) | ||||||
|  | +qca8k_mdio_register(struct qca8k_priv *priv) | ||||||
|  |  { | ||||||
|  |  	struct dsa_switch *ds = priv->ds; | ||||||
|  | +	struct device_node *mdio; | ||||||
|  |  	struct mii_bus *bus; | ||||||
|  |   | ||||||
|  |  	bus = devm_mdiobus_alloc(ds->dev); | ||||||
|  | - | ||||||
|  |  	if (!bus) | ||||||
|  |  		return -ENOMEM; | ||||||
|  |   | ||||||
|  |  	bus->priv = (void *)priv; | ||||||
|  | -	bus->name = "qca8k slave mii"; | ||||||
|  | -	bus->read = qca8k_internal_mdio_read; | ||||||
|  | -	bus->write = qca8k_internal_mdio_write; | ||||||
|  | -	snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", | ||||||
|  | -		 ds->index); | ||||||
|  | - | ||||||
|  |  	bus->parent = ds->dev; | ||||||
|  |  	bus->phy_mask = ~ds->phys_mii_mask; | ||||||
|  | - | ||||||
|  |  	ds->slave_mii_bus = bus; | ||||||
|  |   | ||||||
|  | -	return devm_of_mdiobus_register(priv->dev, bus, mdio); | ||||||
|  | +	/* Check if the devicetree declare the port:phy mapping */ | ||||||
|  | +	mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); | ||||||
|  | +	if (of_device_is_available(mdio)) { | ||||||
|  | +		snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ds->index); | ||||||
|  | +		bus->name = "qca8k slave mii"; | ||||||
|  | +		bus->read = qca8k_internal_mdio_read; | ||||||
|  | +		bus->write = qca8k_internal_mdio_write; | ||||||
|  | +		return devm_of_mdiobus_register(priv->dev, bus, mdio); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	/* If a mapping can't be found the legacy mapping is used, | ||||||
|  | +	 * using the qca8k_port_to_phy function | ||||||
|  | +	 */ | ||||||
|  | +	snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", | ||||||
|  | +		 ds->dst->index, ds->index); | ||||||
|  | +	bus->name = "qca8k-legacy slave mii"; | ||||||
|  | +	bus->read = qca8k_legacy_mdio_read; | ||||||
|  | +	bus->write = qca8k_legacy_mdio_write; | ||||||
|  | +	return devm_mdiobus_register(priv->dev, bus); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  |  qca8k_setup_mdio_bus(struct qca8k_priv *priv) | ||||||
|  |  { | ||||||
|  |  	u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; | ||||||
|  | -	struct device_node *ports, *port, *mdio; | ||||||
|  | +	struct device_node *ports, *port; | ||||||
|  |  	phy_interface_t mode; | ||||||
|  |  	int err; | ||||||
|  |   | ||||||
|  | @@ -1429,24 +1409,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv * | ||||||
|  |  					 QCA8K_MDIO_MASTER_EN); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	/* Check if the devicetree declare the port:phy mapping */ | ||||||
|  | -	mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); | ||||||
|  | -	if (of_device_is_available(mdio)) { | ||||||
|  | -		err = qca8k_mdio_register(priv, mdio); | ||||||
|  | -		if (err) | ||||||
|  | -			of_node_put(mdio); | ||||||
|  | - | ||||||
|  | -		return err; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	/* If a mapping can't be found the legacy mapping is used, | ||||||
|  | -	 * using the qca8k_port_to_phy function | ||||||
|  | -	 */ | ||||||
|  | -	priv->legacy_phy_port_mapping = true; | ||||||
|  | -	priv->ops.phy_read = qca8k_phy_read; | ||||||
|  | -	priv->ops.phy_write = qca8k_phy_write; | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | +	return qca8k_mdio_register(priv); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | --- a/drivers/net/dsa/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca8k.h | ||||||
|  | @@ -382,7 +382,6 @@ struct qca8k_priv { | ||||||
|  |  	 * Bit 1: port enabled. Bit 0: port disabled. | ||||||
|  |  	 */ | ||||||
|  |  	u8 port_enabled_map; | ||||||
|  | -	bool legacy_phy_port_mapping; | ||||||
|  |  	struct qca8k_ports_config ports_config; | ||||||
|  |  	struct regmap *regmap; | ||||||
|  |  	struct mii_bus *bus; | ||||||
| @@ -0,0 +1,39 @@ | |||||||
|  | From 2349b83a2486c55b9dd225326f0172a84a43c5e4 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Sat, 16 Apr 2022 01:30:15 +0200 | ||||||
|  | Subject: [PATCH 4/6] net: dsa: qca8k: drop dsa_switch_ops from qca8k_priv | ||||||
|  |  | ||||||
|  | Now that dsa_switch_ops is not switch specific anymore, we can drop it | ||||||
|  | from qca8k_priv and use the static ops directly for the dsa_switch | ||||||
|  | pointer. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Reviewed-by: Vladimir Oltean <olteanv@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca8k.c | 3 +-- | ||||||
|  |  drivers/net/dsa/qca8k.h | 1 - | ||||||
|  |  2 files changed, 1 insertion(+), 3 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca8k.c | ||||||
|  | @@ -3121,8 +3121,7 @@ qca8k_sw_probe(struct mdio_device *mdiod | ||||||
|  |  	priv->ds->dev = &mdiodev->dev; | ||||||
|  |  	priv->ds->num_ports = QCA8K_NUM_PORTS; | ||||||
|  |  	priv->ds->priv = priv; | ||||||
|  | -	priv->ops = qca8k_switch_ops; | ||||||
|  | -	priv->ds->ops = &priv->ops; | ||||||
|  | +	priv->ds->ops = &qca8k_switch_ops; | ||||||
|  |  	mutex_init(&priv->reg_mutex); | ||||||
|  |  	dev_set_drvdata(&mdiodev->dev, priv); | ||||||
|  |   | ||||||
|  | --- a/drivers/net/dsa/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca8k.h | ||||||
|  | @@ -388,7 +388,6 @@ struct qca8k_priv { | ||||||
|  |  	struct dsa_switch *ds; | ||||||
|  |  	struct mutex reg_mutex; | ||||||
|  |  	struct device *dev; | ||||||
|  | -	struct dsa_switch_ops ops; | ||||||
|  |  	struct gpio_desc *reset_gpio; | ||||||
|  |  	struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ | ||||||
|  |  	struct qca8k_mgmt_eth_data mgmt_eth_data; | ||||||
| @@ -0,0 +1,33 @@ | |||||||
|  | From 6cfc03b602200c5cbbd8d906fd905547814e83df Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Sat, 16 Apr 2022 01:30:16 +0200 | ||||||
|  | Subject: [PATCH 5/6] net: dsa: qca8k: correctly handle mdio read error | ||||||
|  |  | ||||||
|  | Restore original way to handle mdio read error by returning 0xffff. | ||||||
|  | This was wrongly changed when the internal_mdio_read was introduced, | ||||||
|  | now that both legacy and internal use the same function, make sure that | ||||||
|  | they behave the same way. | ||||||
|  |  | ||||||
|  | Fixes: ce062a0adbfe ("net: dsa: qca8k: fix kernel panic with legacy mdio mapping") | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca8k.c | 7 ++++++- | ||||||
|  |  1 file changed, 6 insertions(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca8k.c | ||||||
|  | @@ -1287,7 +1287,12 @@ qca8k_internal_mdio_read(struct mii_bus | ||||||
|  |  	if (ret >= 0) | ||||||
|  |  		return ret; | ||||||
|  |   | ||||||
|  | -	return qca8k_mdio_read(priv, phy, regnum); | ||||||
|  | +	ret = qca8k_mdio_read(priv, phy, regnum); | ||||||
|  | + | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		return 0xffff; | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
| @@ -0,0 +1,44 @@ | |||||||
|  | From 8d1af50842bf2774f4edc57054206e909117469b Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Date: Sat, 16 Apr 2022 01:30:17 +0200 | ||||||
|  | Subject: [PATCH 6/6] net: dsa: qca8k: unify bus id naming with legacy and OF | ||||||
|  |  mdio bus | ||||||
|  |  | ||||||
|  | Add support for multiple switch with OF mdio bus declaration. | ||||||
|  | Unify the bus id naming and use the same logic for both legacy and OF | ||||||
|  | mdio bus. | ||||||
|  |  | ||||||
|  | Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> | ||||||
|  | Signed-off-by: David S. Miller <davem@davemloft.net> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca8k.c | 5 ++--- | ||||||
|  |  1 file changed, 2 insertions(+), 3 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca8k.c | ||||||
|  | @@ -1323,6 +1323,8 @@ qca8k_mdio_register(struct qca8k_priv *p | ||||||
|  |  		return -ENOMEM; | ||||||
|  |   | ||||||
|  |  	bus->priv = (void *)priv; | ||||||
|  | +	snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", | ||||||
|  | +		 ds->dst->index, ds->index); | ||||||
|  |  	bus->parent = ds->dev; | ||||||
|  |  	bus->phy_mask = ~ds->phys_mii_mask; | ||||||
|  |  	ds->slave_mii_bus = bus; | ||||||
|  | @@ -1330,7 +1332,6 @@ qca8k_mdio_register(struct qca8k_priv *p | ||||||
|  |  	/* Check if the devicetree declare the port:phy mapping */ | ||||||
|  |  	mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); | ||||||
|  |  	if (of_device_is_available(mdio)) { | ||||||
|  | -		snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ds->index); | ||||||
|  |  		bus->name = "qca8k slave mii"; | ||||||
|  |  		bus->read = qca8k_internal_mdio_read; | ||||||
|  |  		bus->write = qca8k_internal_mdio_write; | ||||||
|  | @@ -1340,8 +1341,6 @@ qca8k_mdio_register(struct qca8k_priv *p | ||||||
|  |  	/* If a mapping can't be found the legacy mapping is used, | ||||||
|  |  	 * using the qca8k_port_to_phy function | ||||||
|  |  	 */ | ||||||
|  | -	snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", | ||||||
|  | -		 ds->dst->index, ds->index); | ||||||
|  |  	bus->name = "qca8k-legacy slave mii"; | ||||||
|  |  	bus->read = qca8k_legacy_mdio_read; | ||||||
|  |  	bus->write = qca8k_legacy_mdio_write; | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,157 @@ | |||||||
|  | From 3bb0844e7bcd0fb0bcfab6202b5edd349ef5250a Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:10 +0200 | ||||||
|  | Subject: [PATCH 01/14] net: dsa: qca8k: cache match data to speed up access | ||||||
|  |  | ||||||
|  | Using of_device_get_match_data is expensive. Cache match data to speed | ||||||
|  | up access and rework user of match data to use the new cached value. | ||||||
|  |  | ||||||
|  | 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.c | 35 +++++++++++------------------------ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h |  1 + | ||||||
|  |  2 files changed, 12 insertions(+), 24 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.c | ||||||
|  | @@ -1462,8 +1462,8 @@ static int qca8k_find_cpu_port(struct ds | ||||||
|  |  static int | ||||||
|  |  qca8k_setup_of_pws_reg(struct qca8k_priv *priv) | ||||||
|  |  { | ||||||
|  | +	const struct qca8k_match_data *data = priv->info; | ||||||
|  |  	struct device_node *node = priv->dev->of_node; | ||||||
|  | -	const struct qca8k_match_data *data; | ||||||
|  |  	u32 val = 0; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | @@ -1472,8 +1472,6 @@ qca8k_setup_of_pws_reg(struct qca8k_priv | ||||||
|  |  	 * Should be applied by default but we set this just to make sure. | ||||||
|  |  	 */ | ||||||
|  |  	if (priv->switch_id == QCA8K_ID_QCA8327) { | ||||||
|  | -		data = of_device_get_match_data(priv->dev); | ||||||
|  | - | ||||||
|  |  		/* Set the correct package of 148 pin for QCA8327 */ | ||||||
|  |  		if (data->reduced_package) | ||||||
|  |  			val |= QCA8327_PWS_PACKAGE148_EN; | ||||||
|  | @@ -2146,23 +2144,19 @@ qca8k_phylink_mac_link_up(struct dsa_swi | ||||||
|  |  static void | ||||||
|  |  qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) | ||||||
|  |  { | ||||||
|  | -	const struct qca8k_match_data *match_data; | ||||||
|  |  	struct qca8k_priv *priv = ds->priv; | ||||||
|  |  	int i; | ||||||
|  |   | ||||||
|  |  	if (stringset != ETH_SS_STATS) | ||||||
|  |  		return; | ||||||
|  |   | ||||||
|  | -	match_data = of_device_get_match_data(priv->dev); | ||||||
|  | - | ||||||
|  | -	for (i = 0; i < match_data->mib_count; i++) | ||||||
|  | +	for (i = 0; i < priv->info->mib_count; i++) | ||||||
|  |  		strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, | ||||||
|  |  			ETH_GSTRING_LEN); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) | ||||||
|  |  { | ||||||
|  | -	const struct qca8k_match_data *match_data; | ||||||
|  |  	struct qca8k_mib_eth_data *mib_eth_data; | ||||||
|  |  	struct qca8k_priv *priv = ds->priv; | ||||||
|  |  	const struct qca8k_mib_desc *mib; | ||||||
|  | @@ -2181,10 +2175,9 @@ static void qca8k_mib_autocast_handler(s | ||||||
|  |  	if (port != mib_eth_data->req_port) | ||||||
|  |  		goto exit; | ||||||
|  |   | ||||||
|  | -	match_data = device_get_match_data(priv->dev); | ||||||
|  |  	data = mib_eth_data->data; | ||||||
|  |   | ||||||
|  | -	for (i = 0; i < match_data->mib_count; i++) { | ||||||
|  | +	for (i = 0; i < priv->info->mib_count; i++) { | ||||||
|  |  		mib = &ar8327_mib[i]; | ||||||
|  |   | ||||||
|  |  		/* First 3 mib are present in the skb head */ | ||||||
|  | @@ -2256,7 +2249,6 @@ qca8k_get_ethtool_stats(struct dsa_switc | ||||||
|  |  			uint64_t *data) | ||||||
|  |  { | ||||||
|  |  	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	const struct qca8k_match_data *match_data; | ||||||
|  |  	const struct qca8k_mib_desc *mib; | ||||||
|  |  	u32 reg, i, val; | ||||||
|  |  	u32 hi = 0; | ||||||
|  | @@ -2266,9 +2258,7 @@ qca8k_get_ethtool_stats(struct dsa_switc | ||||||
|  |  	    qca8k_get_ethtool_stats_eth(ds, port, data) > 0) | ||||||
|  |  		return; | ||||||
|  |   | ||||||
|  | -	match_data = of_device_get_match_data(priv->dev); | ||||||
|  | - | ||||||
|  | -	for (i = 0; i < match_data->mib_count; i++) { | ||||||
|  | +	for (i = 0; i < priv->info->mib_count; i++) { | ||||||
|  |  		mib = &ar8327_mib[i]; | ||||||
|  |  		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; | ||||||
|  |   | ||||||
|  | @@ -2291,15 +2281,12 @@ qca8k_get_ethtool_stats(struct dsa_switc | ||||||
|  |  static int | ||||||
|  |  qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) | ||||||
|  |  { | ||||||
|  | -	const struct qca8k_match_data *match_data; | ||||||
|  |  	struct qca8k_priv *priv = ds->priv; | ||||||
|  |   | ||||||
|  |  	if (sset != ETH_SS_STATS) | ||||||
|  |  		return 0; | ||||||
|  |   | ||||||
|  | -	match_data = of_device_get_match_data(priv->dev); | ||||||
|  | - | ||||||
|  | -	return match_data->mib_count; | ||||||
|  | +	return priv->info->mib_count; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | @@ -3037,14 +3024,11 @@ static const struct dsa_switch_ops qca8k | ||||||
|  |   | ||||||
|  |  static int qca8k_read_switch_id(struct qca8k_priv *priv) | ||||||
|  |  { | ||||||
|  | -	const struct qca8k_match_data *data; | ||||||
|  |  	u32 val; | ||||||
|  |  	u8 id; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | -	/* get the switches ID from the compatible */ | ||||||
|  | -	data = of_device_get_match_data(priv->dev); | ||||||
|  | -	if (!data) | ||||||
|  | +	if (!priv->info) | ||||||
|  |  		return -ENODEV; | ||||||
|  |   | ||||||
|  |  	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); | ||||||
|  | @@ -3052,8 +3036,10 @@ static int qca8k_read_switch_id(struct q | ||||||
|  |  		return -ENODEV; | ||||||
|  |   | ||||||
|  |  	id = QCA8K_MASK_CTRL_DEVICE_ID(val); | ||||||
|  | -	if (id != data->id) { | ||||||
|  | -		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id); | ||||||
|  | +	if (id != priv->info->id) { | ||||||
|  | +		dev_err(priv->dev, | ||||||
|  | +			"Switch id detected %x but expected %x", | ||||||
|  | +			id, priv->info->id); | ||||||
|  |  		return -ENODEV; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | @@ -3078,6 +3064,7 @@ qca8k_sw_probe(struct mdio_device *mdiod | ||||||
|  |  	if (!priv) | ||||||
|  |  		return -ENOMEM; | ||||||
|  |   | ||||||
|  | +	priv->info = of_device_get_match_data(priv->dev); | ||||||
|  |  	priv->bus = mdiodev->bus; | ||||||
|  |  	priv->dev = &mdiodev->dev; | ||||||
|  |   | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -393,6 +393,7 @@ struct qca8k_priv { | ||||||
|  |  	struct qca8k_mgmt_eth_data mgmt_eth_data; | ||||||
|  |  	struct qca8k_mib_eth_data mib_eth_data; | ||||||
|  |  	struct qca8k_mdio_cache mdio_cache; | ||||||
|  | +	const struct qca8k_match_data *info; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  struct qca8k_mib_desc { | ||||||
| @@ -0,0 +1,77 @@ | |||||||
|  | From 533c64bca62a8654f00698bc893f639013e38c7b Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:11 +0200 | ||||||
|  | Subject: [PATCH 02/14] net: dsa: qca8k: make mib autocast feature optional | ||||||
|  |  | ||||||
|  | Some switch may not support mib autocast feature and require the legacy | ||||||
|  | way of reading the regs directly. | ||||||
|  | Make the mib autocast feature optional and permit to declare support for | ||||||
|  | it using match_data struct in a dedicated qca8k_info_ops struct. | ||||||
|  |  | ||||||
|  | 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.c | 11 +++++++++-- | ||||||
|  |  drivers/net/dsa/qca/qca8k.h |  5 +++++ | ||||||
|  |  2 files changed, 14 insertions(+), 2 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.c | ||||||
|  | @@ -2254,8 +2254,8 @@ qca8k_get_ethtool_stats(struct dsa_switc | ||||||
|  |  	u32 hi = 0; | ||||||
|  |  	int ret; | ||||||
|  |   | ||||||
|  | -	if (priv->mgmt_master && | ||||||
|  | -	    qca8k_get_ethtool_stats_eth(ds, port, data) > 0) | ||||||
|  | +	if (priv->mgmt_master && priv->info->ops->autocast_mib && | ||||||
|  | +	    priv->info->ops->autocast_mib(ds, port, data) > 0) | ||||||
|  |  		return; | ||||||
|  |   | ||||||
|  |  	for (i = 0; i < priv->info->mib_count; i++) { | ||||||
|  | @@ -3187,20 +3187,27 @@ static int qca8k_resume(struct device *d | ||||||
|  |  static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, | ||||||
|  |  			 qca8k_suspend, qca8k_resume); | ||||||
|  |   | ||||||
|  | +static const struct qca8k_info_ops qca8xxx_ops = { | ||||||
|  | +	.autocast_mib = qca8k_get_ethtool_stats_eth, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  static const struct qca8k_match_data qca8327 = { | ||||||
|  |  	.id = QCA8K_ID_QCA8327, | ||||||
|  |  	.reduced_package = true, | ||||||
|  |  	.mib_count = QCA8K_QCA832X_MIB_COUNT, | ||||||
|  | +	.ops = &qca8xxx_ops, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static const struct qca8k_match_data qca8328 = { | ||||||
|  |  	.id = QCA8K_ID_QCA8327, | ||||||
|  |  	.mib_count = QCA8K_QCA832X_MIB_COUNT, | ||||||
|  | +	.ops = &qca8xxx_ops, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static const struct qca8k_match_data qca833x = { | ||||||
|  |  	.id = QCA8K_ID_QCA8337, | ||||||
|  |  	.mib_count = QCA8K_QCA833X_MIB_COUNT, | ||||||
|  | +	.ops = &qca8xxx_ops, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static const struct of_device_id qca8k_of_match[] = { | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -324,10 +324,15 @@ enum qca8k_mid_cmd { | ||||||
|  |  	QCA8K_MIB_CAST = 3, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +struct qca8k_info_ops { | ||||||
|  | +	int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data); | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  struct qca8k_match_data { | ||||||
|  |  	u8 id; | ||||||
|  |  	bool reduced_package; | ||||||
|  |  	u8 mib_count; | ||||||
|  | +	const struct qca8k_info_ops *ops; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  enum { | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,135 @@ | |||||||
|  | From d5f901eab2e9dfed1095995dfc98f231f4fd2971 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:13 +0200 | ||||||
|  | Subject: [PATCH 04/14] net: dsa: qca8k: move qca8k read/write/rmw and reg | ||||||
|  |  table to common code | ||||||
|  |  | ||||||
|  | The same reg table and read/write/rmw function are used by drivers | ||||||
|  | based on qca8k family switch. | ||||||
|  | Move them to common code to make it 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   | 42 ------------------------------ | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 38 +++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        |  6 +++++ | ||||||
|  |  3 files changed, 44 insertions(+), 42 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -133,24 +133,6 @@ qca8k_set_page(struct qca8k_priv *priv, | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int | ||||||
|  | -qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) | ||||||
|  | -{ | ||||||
|  | -	return regmap_read(priv->regmap, reg, val); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) | ||||||
|  | -{ | ||||||
|  | -	return regmap_write(priv->regmap, reg, val); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) | ||||||
|  | -{ | ||||||
|  | -	return regmap_update_bits(priv->regmap, reg, mask, write_val); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) | ||||||
|  |  { | ||||||
|  |  	struct qca8k_mgmt_eth_data *mgmt_eth_data; | ||||||
|  | @@ -483,30 +465,6 @@ exit: | ||||||
|  |  	return ret; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static const struct regmap_range qca8k_readable_ranges[] = { | ||||||
|  | -	regmap_reg_range(0x0000, 0x00e4), /* Global control */ | ||||||
|  | -	regmap_reg_range(0x0100, 0x0168), /* EEE control */ | ||||||
|  | -	regmap_reg_range(0x0200, 0x0270), /* Parser control */ | ||||||
|  | -	regmap_reg_range(0x0400, 0x0454), /* ACL */ | ||||||
|  | -	regmap_reg_range(0x0600, 0x0718), /* Lookup */ | ||||||
|  | -	regmap_reg_range(0x0800, 0x0b70), /* QM */ | ||||||
|  | -	regmap_reg_range(0x0c00, 0x0c80), /* PKT */ | ||||||
|  | -	regmap_reg_range(0x0e00, 0x0e98), /* L3 */ | ||||||
|  | -	regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ | ||||||
|  | -	regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ | ||||||
|  | -	regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ | ||||||
|  | -	regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ | ||||||
|  | -	regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ | ||||||
|  | -	regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ | ||||||
|  | -	regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ | ||||||
|  | - | ||||||
|  | -}; | ||||||
|  | - | ||||||
|  | -static const struct regmap_access_table qca8k_readable_table = { | ||||||
|  | -	.yes_ranges = qca8k_readable_ranges, | ||||||
|  | -	.n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), | ||||||
|  | -}; | ||||||
|  | - | ||||||
|  |  static struct regmap_config qca8k_regmap_config = { | ||||||
|  |  	.reg_bits = 16, | ||||||
|  |  	.val_bits = 32, | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -61,3 +61,41 @@ const struct qca8k_mib_desc ar8327_mib[] | ||||||
|  |  	MIB_DESC(1, 0xa8, "RXUnicast"), | ||||||
|  |  	MIB_DESC(1, 0xac, "TXUnicast"), | ||||||
|  |  }; | ||||||
|  | + | ||||||
|  | +int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) | ||||||
|  | +{ | ||||||
|  | +	return regmap_read(priv->regmap, reg, val); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) | ||||||
|  | +{ | ||||||
|  | +	return regmap_write(priv->regmap, reg, val); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) | ||||||
|  | +{ | ||||||
|  | +	return regmap_update_bits(priv->regmap, reg, mask, write_val); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct regmap_range qca8k_readable_ranges[] = { | ||||||
|  | +	regmap_reg_range(0x0000, 0x00e4), /* Global control */ | ||||||
|  | +	regmap_reg_range(0x0100, 0x0168), /* EEE control */ | ||||||
|  | +	regmap_reg_range(0x0200, 0x0270), /* Parser control */ | ||||||
|  | +	regmap_reg_range(0x0400, 0x0454), /* ACL */ | ||||||
|  | +	regmap_reg_range(0x0600, 0x0718), /* Lookup */ | ||||||
|  | +	regmap_reg_range(0x0800, 0x0b70), /* QM */ | ||||||
|  | +	regmap_reg_range(0x0c00, 0x0c80), /* PKT */ | ||||||
|  | +	regmap_reg_range(0x0e00, 0x0e98), /* L3 */ | ||||||
|  | +	regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ | ||||||
|  | +	regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ | ||||||
|  | +	regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ | ||||||
|  | +	regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ | ||||||
|  | +	regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ | ||||||
|  | +	regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ | ||||||
|  | +	regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +const struct regmap_access_table qca8k_readable_table = { | ||||||
|  | +	.yes_ranges = qca8k_readable_ranges, | ||||||
|  | +	.n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), | ||||||
|  | +}; | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -416,5 +416,11 @@ struct qca8k_fdb { | ||||||
|  |   | ||||||
|  |  /* Common setup function */ | ||||||
|  |  extern const struct qca8k_mib_desc ar8327_mib[]; | ||||||
|  | +extern const struct regmap_access_table qca8k_readable_table; | ||||||
|  | + | ||||||
|  | +/* Common read/write/rmw function */ | ||||||
|  | +int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); | ||||||
|  | +int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); | ||||||
|  | +int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); | ||||||
|  |   | ||||||
|  |  #endif /* __QCA8K_H */ | ||||||
| @@ -0,0 +1,145 @@ | |||||||
|  | From 910746444313dc463396cd63024cdf54ef04ef39 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:14 +0200 | ||||||
|  | Subject: [PATCH 05/14] net: dsa: qca8k: move qca8k bulk read/write helper to | ||||||
|  |  common code | ||||||
|  |  | ||||||
|  | The same ATU function are used by drivers based on qca8k family switch. | ||||||
|  | Move the bulk read/write helper to common code to declare these shared | ||||||
|  | ATU functions in common code. | ||||||
|  | These helper will be dropped when regmap correctly support bulk | ||||||
|  | read/write. | ||||||
|  |  | ||||||
|  | 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   | 39 ++---------------------------- | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 39 ++++++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        |  8 ++++++ | ||||||
|  |  3 files changed, 49 insertions(+), 37 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -343,43 +343,6 @@ qca8k_regmap_update_bits_eth(struct qca8 | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | -qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) | ||||||
|  | -{ | ||||||
|  | -	int i, count = len / sizeof(u32), ret; | ||||||
|  | - | ||||||
|  | -	if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  | -	for (i = 0; i < count; i++) { | ||||||
|  | -		ret = regmap_read(priv->regmap, reg + (i * 4), val + i); | ||||||
|  | -		if (ret < 0) | ||||||
|  | -			return ret; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) | ||||||
|  | -{ | ||||||
|  | -	int i, count = len / sizeof(u32), ret; | ||||||
|  | -	u32 tmp; | ||||||
|  | - | ||||||
|  | -	if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  | -	for (i = 0; i < count; i++) { | ||||||
|  | -		tmp = val[i]; | ||||||
|  | - | ||||||
|  | -		ret = regmap_write(priv->regmap, reg + (i * 4), tmp); | ||||||
|  | -		if (ret < 0) | ||||||
|  | -			return ret; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  |  qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) | ||||||
|  |  { | ||||||
|  |  	struct qca8k_priv *priv = (struct qca8k_priv *)ctx; | ||||||
|  | @@ -3096,6 +3059,8 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, | ||||||
|  |   | ||||||
|  |  static const struct qca8k_info_ops qca8xxx_ops = { | ||||||
|  |  	.autocast_mib = qca8k_get_ethtool_stats_eth, | ||||||
|  | +	.read_eth = qca8k_read_eth, | ||||||
|  | +	.write_eth = qca8k_write_eth, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static const struct qca8k_match_data qca8327 = { | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -99,3 +99,42 @@ const struct regmap_access_table qca8k_r | ||||||
|  |  	.yes_ranges = qca8k_readable_ranges, | ||||||
|  |  	.n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), | ||||||
|  |  }; | ||||||
|  | + | ||||||
|  | +/* TODO: remove these extra ops when we can support regmap bulk read/write */ | ||||||
|  | +int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) | ||||||
|  | +{ | ||||||
|  | +	int i, count = len / sizeof(u32), ret; | ||||||
|  | + | ||||||
|  | +	if (priv->mgmt_master && priv->info->ops->read_eth && | ||||||
|  | +	    !priv->info->ops->read_eth(priv, reg, val, len)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	for (i = 0; i < count; i++) { | ||||||
|  | +		ret = regmap_read(priv->regmap, reg + (i * 4), val + i); | ||||||
|  | +		if (ret < 0) | ||||||
|  | +			return ret; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/* TODO: remove these extra ops when we can support regmap bulk read/write */ | ||||||
|  | +int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) | ||||||
|  | +{ | ||||||
|  | +	int i, count = len / sizeof(u32), ret; | ||||||
|  | +	u32 tmp; | ||||||
|  | + | ||||||
|  | +	if (priv->mgmt_master && priv->info->ops->write_eth && | ||||||
|  | +	    !priv->info->ops->write_eth(priv, reg, val, len)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	for (i = 0; i < count; i++) { | ||||||
|  | +		tmp = val[i]; | ||||||
|  | + | ||||||
|  | +		ret = regmap_write(priv->regmap, reg + (i * 4), tmp); | ||||||
|  | +		if (ret < 0) | ||||||
|  | +			return ret; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -324,8 +324,13 @@ enum qca8k_mid_cmd { | ||||||
|  |  	QCA8K_MIB_CAST = 3, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +struct qca8k_priv; | ||||||
|  | + | ||||||
|  |  struct qca8k_info_ops { | ||||||
|  |  	int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data); | ||||||
|  | +	/* TODO: remove these extra ops when we can support regmap bulk read/write */ | ||||||
|  | +	int (*read_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len); | ||||||
|  | +	int (*write_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len); | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  struct qca8k_match_data { | ||||||
|  | @@ -423,4 +428,7 @@ int qca8k_read(struct qca8k_priv *priv, | ||||||
|  |  int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); | ||||||
|  |  int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); | ||||||
|  |   | ||||||
|  | +int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); | ||||||
|  | +int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); | ||||||
|  | + | ||||||
|  |  #endif /* __QCA8K_H */ | ||||||
| @@ -0,0 +1,137 @@ | |||||||
|  | From fce1ec0c4e2d03d9c62ffc615a42bdba78eb4c14 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:15 +0200 | ||||||
|  | Subject: [PATCH 06/14] net: dsa: qca8k: move mib init function to common code | ||||||
|  |  | ||||||
|  | The same mib function is used by drivers based on qca8k family switch. | ||||||
|  | Move it to common code to make it 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   | 37 ------------------------------ | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 35 ++++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        |  4 ++++ | ||||||
|  |  3 files changed, 39 insertions(+), 37 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -442,15 +442,6 @@ static struct regmap_config qca8k_regmap | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | -qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) | ||||||
|  | -{ | ||||||
|  | -	u32 val; | ||||||
|  | - | ||||||
|  | -	return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, | ||||||
|  | -				       QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  |  qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) | ||||||
|  |  { | ||||||
|  |  	u32 reg[3]; | ||||||
|  | @@ -777,34 +768,6 @@ out: | ||||||
|  |  	return ret; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static int | ||||||
|  | -qca8k_mib_init(struct qca8k_priv *priv) | ||||||
|  | -{ | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | -	ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, | ||||||
|  | -				 QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, | ||||||
|  | -				 FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | | ||||||
|  | -				 QCA8K_MIB_BUSY); | ||||||
|  | -	if (ret) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); | ||||||
|  | -	if (ret) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); | ||||||
|  | -	if (ret) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); | ||||||
|  | - | ||||||
|  | -exit: | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | -	return ret; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static void | ||||||
|  |  qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) | ||||||
|  |  { | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -7,6 +7,7 @@ | ||||||
|  |   */ | ||||||
|  |   | ||||||
|  |  #include <linux/netdevice.h> | ||||||
|  | +#include <linux/bitfield.h> | ||||||
|  |  #include <net/dsa.h> | ||||||
|  |   | ||||||
|  |  #include "qca8k.h" | ||||||
|  | @@ -138,3 +139,38 @@ int qca8k_bulk_write(struct qca8k_priv * | ||||||
|  |   | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) | ||||||
|  | +{ | ||||||
|  | +	u32 val; | ||||||
|  | + | ||||||
|  | +	return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, | ||||||
|  | +				       QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_mib_init(struct qca8k_priv *priv) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | +	ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, | ||||||
|  | +				 QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, | ||||||
|  | +				 FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | | ||||||
|  | +				 QCA8K_MIB_BUSY); | ||||||
|  | +	if (ret) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); | ||||||
|  | +	if (ret) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); | ||||||
|  | +	if (ret) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); | ||||||
|  | + | ||||||
|  | +exit: | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -422,6 +422,7 @@ struct qca8k_fdb { | ||||||
|  |  /* Common setup function */ | ||||||
|  |  extern const struct qca8k_mib_desc ar8327_mib[]; | ||||||
|  |  extern const struct regmap_access_table qca8k_readable_table; | ||||||
|  | +int qca8k_mib_init(struct qca8k_priv *priv); | ||||||
|  |   | ||||||
|  |  /* Common read/write/rmw function */ | ||||||
|  |  int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); | ||||||
|  | @@ -431,4 +432,7 @@ int qca8k_rmw(struct qca8k_priv *priv, u | ||||||
|  |  int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); | ||||||
|  |  int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); | ||||||
|  |   | ||||||
|  | +/* Common ops function */ | ||||||
|  | +int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); | ||||||
|  | + | ||||||
|  |  #endif /* __QCA8K_H */ | ||||||
| @@ -0,0 +1,281 @@ | |||||||
|  | From 472fcea160f27a5d9b7526093d9d8d89ba0b6137 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:16 +0200 | ||||||
|  | Subject: [PATCH 07/14] net: dsa: qca8k: move port set status/eee/ethtool stats | ||||||
|  |  function to common code | ||||||
|  |  | ||||||
|  | The same logic to disable/enable port, set eee and get ethtool stats is | ||||||
|  | used by drivers based on qca8k family switch. | ||||||
|  | Move it to common code to make it accessible also by other drivers. | ||||||
|  | While at it also drop unnecessary qca8k_priv cast for void pointers. | ||||||
|  |  | ||||||
|  | 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   | 105 ----------------------------- | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 102 ++++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        |  11 +++ | ||||||
|  |  3 files changed, 113 insertions(+), 105 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -768,21 +768,6 @@ out: | ||||||
|  |  	return ret; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void | ||||||
|  | -qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) | ||||||
|  | -{ | ||||||
|  | -	u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; | ||||||
|  | - | ||||||
|  | -	/* Port 0 and 6 have no internal PHY */ | ||||||
|  | -	if (port > 0 && port < 6) | ||||||
|  | -		mask |= QCA8K_PORT_STATUS_LINK_AUTO; | ||||||
|  | - | ||||||
|  | -	if (enable) | ||||||
|  | -		regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); | ||||||
|  | -	else | ||||||
|  | -		regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static int | ||||||
|  |  qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, | ||||||
|  |  			struct sk_buff *read_skb, u32 *val) | ||||||
|  | @@ -1974,20 +1959,6 @@ qca8k_phylink_mac_link_up(struct dsa_swi | ||||||
|  |  	qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void | ||||||
|  | -qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	int i; | ||||||
|  | - | ||||||
|  | -	if (stringset != ETH_SS_STATS) | ||||||
|  | -		return; | ||||||
|  | - | ||||||
|  | -	for (i = 0; i < priv->info->mib_count; i++) | ||||||
|  | -		strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, | ||||||
|  | -			ETH_GSTRING_LEN); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) | ||||||
|  |  { | ||||||
|  |  	struct qca8k_mib_eth_data *mib_eth_data; | ||||||
|  | @@ -2078,82 +2049,6 @@ exit: | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void | ||||||
|  | -qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, | ||||||
|  | -			uint64_t *data) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	const struct qca8k_mib_desc *mib; | ||||||
|  | -	u32 reg, i, val; | ||||||
|  | -	u32 hi = 0; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	if (priv->mgmt_master && priv->info->ops->autocast_mib && | ||||||
|  | -	    priv->info->ops->autocast_mib(ds, port, data) > 0) | ||||||
|  | -		return; | ||||||
|  | - | ||||||
|  | -	for (i = 0; i < priv->info->mib_count; i++) { | ||||||
|  | -		mib = &ar8327_mib[i]; | ||||||
|  | -		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; | ||||||
|  | - | ||||||
|  | -		ret = qca8k_read(priv, reg, &val); | ||||||
|  | -		if (ret < 0) | ||||||
|  | -			continue; | ||||||
|  | - | ||||||
|  | -		if (mib->size == 2) { | ||||||
|  | -			ret = qca8k_read(priv, reg + 4, &hi); | ||||||
|  | -			if (ret < 0) | ||||||
|  | -				continue; | ||||||
|  | -		} | ||||||
|  | - | ||||||
|  | -		data[i] = val; | ||||||
|  | -		if (mib->size == 2) | ||||||
|  | -			data[i] |= (u64)hi << 32; | ||||||
|  | -	} | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | - | ||||||
|  | -	if (sset != ETH_SS_STATS) | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  | -	return priv->info->mib_count; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); | ||||||
|  | -	u32 reg; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | -	ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	if (eee->eee_enabled) | ||||||
|  | -		reg |= lpi_en; | ||||||
|  | -	else | ||||||
|  | -		reg &= ~lpi_en; | ||||||
|  | -	ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); | ||||||
|  | - | ||||||
|  | -exit: | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | -	return ret; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) | ||||||
|  | -{ | ||||||
|  | -	/* Nothing to do on the port's MAC */ | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void | ||||||
|  |  qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) | ||||||
|  |  { | ||||||
|  |  	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -174,3 +174,105 @@ exit: | ||||||
|  |  	mutex_unlock(&priv->reg_mutex); | ||||||
|  |  	return ret; | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) | ||||||
|  | +{ | ||||||
|  | +	u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; | ||||||
|  | + | ||||||
|  | +	/* Port 0 and 6 have no internal PHY */ | ||||||
|  | +	if (port > 0 && port < 6) | ||||||
|  | +		mask |= QCA8K_PORT_STATUS_LINK_AUTO; | ||||||
|  | + | ||||||
|  | +	if (enable) | ||||||
|  | +		regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); | ||||||
|  | +	else | ||||||
|  | +		regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, | ||||||
|  | +		       uint8_t *data) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	int i; | ||||||
|  | + | ||||||
|  | +	if (stringset != ETH_SS_STATS) | ||||||
|  | +		return; | ||||||
|  | + | ||||||
|  | +	for (i = 0; i < priv->info->mib_count; i++) | ||||||
|  | +		strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, | ||||||
|  | +			ETH_GSTRING_LEN); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, | ||||||
|  | +			     uint64_t *data) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	const struct qca8k_mib_desc *mib; | ||||||
|  | +	u32 reg, i, val; | ||||||
|  | +	u32 hi = 0; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	if (priv->mgmt_master && priv->info->ops->autocast_mib && | ||||||
|  | +	    priv->info->ops->autocast_mib(ds, port, data) > 0) | ||||||
|  | +		return; | ||||||
|  | + | ||||||
|  | +	for (i = 0; i < priv->info->mib_count; i++) { | ||||||
|  | +		mib = &ar8327_mib[i]; | ||||||
|  | +		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; | ||||||
|  | + | ||||||
|  | +		ret = qca8k_read(priv, reg, &val); | ||||||
|  | +		if (ret < 0) | ||||||
|  | +			continue; | ||||||
|  | + | ||||||
|  | +		if (mib->size == 2) { | ||||||
|  | +			ret = qca8k_read(priv, reg + 4, &hi); | ||||||
|  | +			if (ret < 0) | ||||||
|  | +				continue; | ||||||
|  | +		} | ||||||
|  | + | ||||||
|  | +		data[i] = val; | ||||||
|  | +		if (mib->size == 2) | ||||||
|  | +			data[i] |= (u64)hi << 32; | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | + | ||||||
|  | +	if (sset != ETH_SS_STATS) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	return priv->info->mib_count; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_set_mac_eee(struct dsa_switch *ds, int port, | ||||||
|  | +		      struct ethtool_eee *eee) | ||||||
|  | +{ | ||||||
|  | +	u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	u32 reg; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | +	ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	if (eee->eee_enabled) | ||||||
|  | +		reg |= lpi_en; | ||||||
|  | +	else | ||||||
|  | +		reg &= ~lpi_en; | ||||||
|  | +	ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); | ||||||
|  | + | ||||||
|  | +exit: | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_get_mac_eee(struct dsa_switch *ds, int port, | ||||||
|  | +		      struct ethtool_eee *e) | ||||||
|  | +{ | ||||||
|  | +	/* Nothing to do on the port's MAC */ | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -423,6 +423,7 @@ struct qca8k_fdb { | ||||||
|  |  extern const struct qca8k_mib_desc ar8327_mib[]; | ||||||
|  |  extern const struct regmap_access_table qca8k_readable_table; | ||||||
|  |  int qca8k_mib_init(struct qca8k_priv *priv); | ||||||
|  | +void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable); | ||||||
|  |   | ||||||
|  |  /* Common read/write/rmw function */ | ||||||
|  |  int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); | ||||||
|  | @@ -435,4 +436,14 @@ int qca8k_bulk_write(struct qca8k_priv * | ||||||
|  |  /* Common ops function */ | ||||||
|  |  int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); | ||||||
|  |   | ||||||
|  | +/* Common ethtool stats function */ | ||||||
|  | +void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data); | ||||||
|  | +void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, | ||||||
|  | +			     uint64_t *data); | ||||||
|  | +int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset); | ||||||
|  | + | ||||||
|  | +/* Common eee function */ | ||||||
|  | +int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee); | ||||||
|  | +int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); | ||||||
|  | + | ||||||
|  |  #endif /* __QCA8K_H */ | ||||||
| @@ -0,0 +1,237 @@ | |||||||
|  | From fd3cae2f3ac190d06e48f43739237e02f9dc51ff Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:17 +0200 | ||||||
|  | Subject: [PATCH 08/14] net: dsa: qca8k: move bridge functions to common code | ||||||
|  |  | ||||||
|  | The same bridge functions are used by drivers based on qca8k family | ||||||
|  | switch. Move them to common code to make them accessible also by other | ||||||
|  | drivers. | ||||||
|  | While at it also drop unnecessary qca8k_priv cast for void pointers. | ||||||
|  |  | ||||||
|  | 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   | 93 ------------------------------ | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 93 ++++++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        |  9 +++ | ||||||
|  |  3 files changed, 102 insertions(+), 93 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -2049,97 +2049,6 @@ exit: | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static void | ||||||
|  | -qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	u32 stp_state; | ||||||
|  | - | ||||||
|  | -	switch (state) { | ||||||
|  | -	case BR_STATE_DISABLED: | ||||||
|  | -		stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; | ||||||
|  | -		break; | ||||||
|  | -	case BR_STATE_BLOCKING: | ||||||
|  | -		stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; | ||||||
|  | -		break; | ||||||
|  | -	case BR_STATE_LISTENING: | ||||||
|  | -		stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; | ||||||
|  | -		break; | ||||||
|  | -	case BR_STATE_LEARNING: | ||||||
|  | -		stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; | ||||||
|  | -		break; | ||||||
|  | -	case BR_STATE_FORWARDING: | ||||||
|  | -	default: | ||||||
|  | -		stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; | ||||||
|  | -		break; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), | ||||||
|  | -		  QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	int port_mask, cpu_port; | ||||||
|  | -	int i, ret; | ||||||
|  | - | ||||||
|  | -	cpu_port = dsa_to_port(ds, port)->cpu_dp->index; | ||||||
|  | -	port_mask = BIT(cpu_port); | ||||||
|  | - | ||||||
|  | -	for (i = 0; i < QCA8K_NUM_PORTS; i++) { | ||||||
|  | -		if (dsa_is_cpu_port(ds, i)) | ||||||
|  | -			continue; | ||||||
|  | -		if (dsa_to_port(ds, i)->bridge_dev != br) | ||||||
|  | -			continue; | ||||||
|  | -		/* Add this port to the portvlan mask of the other ports | ||||||
|  | -		 * in the bridge | ||||||
|  | -		 */ | ||||||
|  | -		ret = regmap_set_bits(priv->regmap, | ||||||
|  | -				      QCA8K_PORT_LOOKUP_CTRL(i), | ||||||
|  | -				      BIT(port)); | ||||||
|  | -		if (ret) | ||||||
|  | -			return ret; | ||||||
|  | -		if (i != port) | ||||||
|  | -			port_mask |= BIT(i); | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	/* Add all other ports to this ports portvlan mask */ | ||||||
|  | -	ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), | ||||||
|  | -			QCA8K_PORT_LOOKUP_MEMBER, port_mask); | ||||||
|  | - | ||||||
|  | -	return ret; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void | ||||||
|  | -qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	int cpu_port, i; | ||||||
|  | - | ||||||
|  | -	cpu_port = dsa_to_port(ds, port)->cpu_dp->index; | ||||||
|  | - | ||||||
|  | -	for (i = 0; i < QCA8K_NUM_PORTS; i++) { | ||||||
|  | -		if (dsa_is_cpu_port(ds, i)) | ||||||
|  | -			continue; | ||||||
|  | -		if (dsa_to_port(ds, i)->bridge_dev != br) | ||||||
|  | -			continue; | ||||||
|  | -		/* Remove this port to the portvlan mask of the other ports | ||||||
|  | -		 * in the bridge | ||||||
|  | -		 */ | ||||||
|  | -		regmap_clear_bits(priv->regmap, | ||||||
|  | -				  QCA8K_PORT_LOOKUP_CTRL(i), | ||||||
|  | -				  BIT(port)); | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	/* Set the cpu port to be the only one in the portvlan mask of | ||||||
|  | -	 * this port | ||||||
|  | -	 */ | ||||||
|  | -	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), | ||||||
|  | -		  QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void | ||||||
|  |  qca8k_port_fast_age(struct dsa_switch *ds, int port) | ||||||
|  |  { | ||||||
|  |  	struct qca8k_priv *priv = ds->priv; | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -9,6 +9,7 @@ | ||||||
|  |  #include <linux/netdevice.h> | ||||||
|  |  #include <linux/bitfield.h> | ||||||
|  |  #include <net/dsa.h> | ||||||
|  | +#include <linux/if_bridge.h> | ||||||
|  |   | ||||||
|  |  #include "qca8k.h" | ||||||
|  |   | ||||||
|  | @@ -276,3 +277,93 @@ int qca8k_get_mac_eee(struct dsa_switch | ||||||
|  |  	/* Nothing to do on the port's MAC */ | ||||||
|  |  	return 0; | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	u32 stp_state; | ||||||
|  | + | ||||||
|  | +	switch (state) { | ||||||
|  | +	case BR_STATE_DISABLED: | ||||||
|  | +		stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; | ||||||
|  | +		break; | ||||||
|  | +	case BR_STATE_BLOCKING: | ||||||
|  | +		stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; | ||||||
|  | +		break; | ||||||
|  | +	case BR_STATE_LISTENING: | ||||||
|  | +		stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; | ||||||
|  | +		break; | ||||||
|  | +	case BR_STATE_LEARNING: | ||||||
|  | +		stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; | ||||||
|  | +		break; | ||||||
|  | +	case BR_STATE_FORWARDING: | ||||||
|  | +	default: | ||||||
|  | +		stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; | ||||||
|  | +		break; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), | ||||||
|  | +		  QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_bridge_join(struct dsa_switch *ds, int port, | ||||||
|  | +			   struct net_device *br) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	int port_mask, cpu_port; | ||||||
|  | +	int i, ret; | ||||||
|  | + | ||||||
|  | +	cpu_port = dsa_to_port(ds, port)->cpu_dp->index; | ||||||
|  | +	port_mask = BIT(cpu_port); | ||||||
|  | + | ||||||
|  | +	for (i = 0; i < QCA8K_NUM_PORTS; i++) { | ||||||
|  | +		if (dsa_is_cpu_port(ds, i)) | ||||||
|  | +			continue; | ||||||
|  | +		if (dsa_to_port(ds, i)->bridge_dev != br) | ||||||
|  | +			continue; | ||||||
|  | +		/* Add this port to the portvlan mask of the other ports | ||||||
|  | +		 * in the bridge | ||||||
|  | +		 */ | ||||||
|  | +		ret = regmap_set_bits(priv->regmap, | ||||||
|  | +				      QCA8K_PORT_LOOKUP_CTRL(i), | ||||||
|  | +				      BIT(port)); | ||||||
|  | +		if (ret) | ||||||
|  | +			return ret; | ||||||
|  | +		if (i != port) | ||||||
|  | +			port_mask |= BIT(i); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	/* Add all other ports to this ports portvlan mask */ | ||||||
|  | +	ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), | ||||||
|  | +			QCA8K_PORT_LOOKUP_MEMBER, port_mask); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, | ||||||
|  | +			     struct net_device *br) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	int cpu_port, i; | ||||||
|  | + | ||||||
|  | +	cpu_port = dsa_to_port(ds, port)->cpu_dp->index; | ||||||
|  | + | ||||||
|  | +	for (i = 0; i < QCA8K_NUM_PORTS; i++) { | ||||||
|  | +		if (dsa_is_cpu_port(ds, i)) | ||||||
|  | +			continue; | ||||||
|  | +		if (dsa_to_port(ds, i)->bridge_dev != br) | ||||||
|  | +			continue; | ||||||
|  | +		/* Remove this port to the portvlan mask of the other ports | ||||||
|  | +		 * in the bridge | ||||||
|  | +		 */ | ||||||
|  | +		regmap_clear_bits(priv->regmap, | ||||||
|  | +				  QCA8K_PORT_LOOKUP_CTRL(i), | ||||||
|  | +				  BIT(port)); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	/* Set the cpu port to be the only one in the portvlan mask of | ||||||
|  | +	 * this port | ||||||
|  | +	 */ | ||||||
|  | +	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), | ||||||
|  | +		  QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); | ||||||
|  | +} | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -446,4 +446,11 @@ int qca8k_get_sset_count(struct dsa_swit | ||||||
|  |  int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee); | ||||||
|  |  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_bridge_join(struct dsa_switch *ds, int port, | ||||||
|  | +			   struct net_device *br); | ||||||
|  | +void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, | ||||||
|  | +			     struct net_device *br); | ||||||
|  | + | ||||||
|  |  #endif /* __QCA8K_H */ | ||||||
| @@ -0,0 +1,227 @@ | |||||||
|  | From b3a302b171f73425b41de8d3357fae3fa7057322 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:18 +0200 | ||||||
|  | Subject: [PATCH 09/14] net: dsa: qca8k: move set age/MTU/port enable/disable | ||||||
|  |  functions to common code | ||||||
|  |  | ||||||
|  | The same set age, MTU and port enable/disable function are used by | ||||||
|  | driver based on qca8k family switch. | ||||||
|  | Move them to common code to make them accessible also by other drivers. | ||||||
|  | While at it also drop unnecessary qca8k_priv cast for void pointers. | ||||||
|  |  | ||||||
|  | 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   | 88 ------------------------------ | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 85 +++++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        | 12 ++++ | ||||||
|  |  3 files changed, 97 insertions(+), 88 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -2059,94 +2059,6 @@ qca8k_port_fast_age(struct dsa_switch *d | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | -qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	unsigned int secs = msecs / 1000; | ||||||
|  | -	u32 val; | ||||||
|  | - | ||||||
|  | -	/* AGE_TIME reg is set in 7s step */ | ||||||
|  | -	val = secs / 7; | ||||||
|  | - | ||||||
|  | -	/* Handle case with 0 as val to NOT disable | ||||||
|  | -	 * learning | ||||||
|  | -	 */ | ||||||
|  | -	if (!val) | ||||||
|  | -		val = 1; | ||||||
|  | - | ||||||
|  | -	return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK, | ||||||
|  | -				  QCA8K_ATU_AGE_TIME(val)); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_enable(struct dsa_switch *ds, int port, | ||||||
|  | -		  struct phy_device *phy) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | - | ||||||
|  | -	qca8k_port_set_status(priv, port, 1); | ||||||
|  | -	priv->port_enabled_map |= BIT(port); | ||||||
|  | - | ||||||
|  | -	if (dsa_is_user_port(ds, port)) | ||||||
|  | -		phy_support_asym_pause(phy); | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void | ||||||
|  | -qca8k_port_disable(struct dsa_switch *ds, int port) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | - | ||||||
|  | -	qca8k_port_set_status(priv, port, 0); | ||||||
|  | -	priv->port_enabled_map &= ~BIT(port); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	/* We have only have a general MTU setting. | ||||||
|  | -	 * DSA always set the CPU port's MTU to the largest MTU of the slave | ||||||
|  | -	 * ports. | ||||||
|  | -	 * Setting MTU just for the CPU port is sufficient to correctly set a | ||||||
|  | -	 * value for every port. | ||||||
|  | -	 */ | ||||||
|  | -	if (!dsa_is_cpu_port(ds, port)) | ||||||
|  | -		return 0; | ||||||
|  | - | ||||||
|  | -	/* To change the MAX_FRAME_SIZE the cpu ports must be off or | ||||||
|  | -	 * the switch panics. | ||||||
|  | -	 * Turn off both cpu ports before applying the new value to prevent | ||||||
|  | -	 * this. | ||||||
|  | -	 */ | ||||||
|  | -	if (priv->port_enabled_map & BIT(0)) | ||||||
|  | -		qca8k_port_set_status(priv, 0, 0); | ||||||
|  | - | ||||||
|  | -	if (priv->port_enabled_map & BIT(6)) | ||||||
|  | -		qca8k_port_set_status(priv, 6, 0); | ||||||
|  | - | ||||||
|  | -	/* Include L2 header / FCS length */ | ||||||
|  | -	ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); | ||||||
|  | - | ||||||
|  | -	if (priv->port_enabled_map & BIT(0)) | ||||||
|  | -		qca8k_port_set_status(priv, 0, 1); | ||||||
|  | - | ||||||
|  | -	if (priv->port_enabled_map & BIT(6)) | ||||||
|  | -		qca8k_port_set_status(priv, 6, 1); | ||||||
|  | - | ||||||
|  | -	return ret; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_max_mtu(struct dsa_switch *ds, int port) | ||||||
|  | -{ | ||||||
|  | -	return QCA8K_MAX_MTU; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  |  qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, | ||||||
|  |  		      u16 port_mask, u16 vid) | ||||||
|  |  { | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -367,3 +367,88 @@ void qca8k_port_bridge_leave(struct dsa_ | ||||||
|  |  	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), | ||||||
|  |  		  QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	unsigned int secs = msecs / 1000; | ||||||
|  | +	u32 val; | ||||||
|  | + | ||||||
|  | +	/* AGE_TIME reg is set in 7s step */ | ||||||
|  | +	val = secs / 7; | ||||||
|  | + | ||||||
|  | +	/* Handle case with 0 as val to NOT disable | ||||||
|  | +	 * learning | ||||||
|  | +	 */ | ||||||
|  | +	if (!val) | ||||||
|  | +		val = 1; | ||||||
|  | + | ||||||
|  | +	return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, | ||||||
|  | +				  QCA8K_ATU_AGE_TIME_MASK, | ||||||
|  | +				  QCA8K_ATU_AGE_TIME(val)); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_enable(struct dsa_switch *ds, int port, | ||||||
|  | +		      struct phy_device *phy) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | + | ||||||
|  | +	qca8k_port_set_status(priv, port, 1); | ||||||
|  | +	priv->port_enabled_map |= BIT(port); | ||||||
|  | + | ||||||
|  | +	if (dsa_is_user_port(ds, port)) | ||||||
|  | +		phy_support_asym_pause(phy); | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +void qca8k_port_disable(struct dsa_switch *ds, int port) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | + | ||||||
|  | +	qca8k_port_set_status(priv, port, 0); | ||||||
|  | +	priv->port_enabled_map &= ~BIT(port); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* We have only have a general MTU setting. | ||||||
|  | +	 * DSA always set the CPU port's MTU to the largest MTU of the slave | ||||||
|  | +	 * ports. | ||||||
|  | +	 * Setting MTU just for the CPU port is sufficient to correctly set a | ||||||
|  | +	 * value for every port. | ||||||
|  | +	 */ | ||||||
|  | +	if (!dsa_is_cpu_port(ds, port)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	/* To change the MAX_FRAME_SIZE the cpu ports must be off or | ||||||
|  | +	 * the switch panics. | ||||||
|  | +	 * Turn off both cpu ports before applying the new value to prevent | ||||||
|  | +	 * this. | ||||||
|  | +	 */ | ||||||
|  | +	if (priv->port_enabled_map & BIT(0)) | ||||||
|  | +		qca8k_port_set_status(priv, 0, 0); | ||||||
|  | + | ||||||
|  | +	if (priv->port_enabled_map & BIT(6)) | ||||||
|  | +		qca8k_port_set_status(priv, 6, 0); | ||||||
|  | + | ||||||
|  | +	/* Include L2 header / FCS length */ | ||||||
|  | +	ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + | ||||||
|  | +			  ETH_HLEN + ETH_FCS_LEN); | ||||||
|  | + | ||||||
|  | +	if (priv->port_enabled_map & BIT(0)) | ||||||
|  | +		qca8k_port_set_status(priv, 0, 1); | ||||||
|  | + | ||||||
|  | +	if (priv->port_enabled_map & BIT(6)) | ||||||
|  | +		qca8k_port_set_status(priv, 6, 1); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_max_mtu(struct dsa_switch *ds, int port) | ||||||
|  | +{ | ||||||
|  | +	return QCA8K_MAX_MTU; | ||||||
|  | +} | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -453,4 +453,16 @@ int qca8k_port_bridge_join(struct dsa_sw | ||||||
|  |  void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, | ||||||
|  |  			     struct net_device *br); | ||||||
|  |   | ||||||
|  | +/* Common port enable/disable function */ | ||||||
|  | +int qca8k_port_enable(struct dsa_switch *ds, int port, | ||||||
|  | +		      struct phy_device *phy); | ||||||
|  | +void qca8k_port_disable(struct dsa_switch *ds, int port); | ||||||
|  | + | ||||||
|  | +/* Common MTU function */ | ||||||
|  | +int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu); | ||||||
|  | +int qca8k_port_max_mtu(struct dsa_switch *ds, int port); | ||||||
|  | + | ||||||
|  | +/* Common fast age function */ | ||||||
|  | +int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs); | ||||||
|  | + | ||||||
|  |  #endif /* __QCA8K_H */ | ||||||
| @@ -0,0 +1,704 @@ | |||||||
|  | From 2e5bd96eea86a246b4de3bf756f7a11b43e6187d Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:19 +0200 | ||||||
|  | Subject: [PATCH 10/14] net: dsa: qca8k: move port FDB/MDB function to common | ||||||
|  |  code | ||||||
|  |  | ||||||
|  | The same port FDB/MDB function are used by drivers based on qca8k family | ||||||
|  | switch. Move them to common code to make them accessible also by other | ||||||
|  | drivers. | ||||||
|  | Also drop bulk read/write functions and make them 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   | 306 ----------------------------- | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 297 +++++++++++++++++++++++++++- | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        |  25 ++- | ||||||
|  |  3 files changed, 317 insertions(+), 311 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -442,217 +442,6 @@ static struct regmap_config qca8k_regmap | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | -qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) | ||||||
|  | -{ | ||||||
|  | -	u32 reg[3]; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	/* load the ARL table into an array */ | ||||||
|  | -	ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	/* vid - 83:72 */ | ||||||
|  | -	fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); | ||||||
|  | -	/* aging - 67:64 */ | ||||||
|  | -	fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); | ||||||
|  | -	/* portmask - 54:48 */ | ||||||
|  | -	fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); | ||||||
|  | -	/* mac - 47:0 */ | ||||||
|  | -	fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); | ||||||
|  | -	fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); | ||||||
|  | -	fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); | ||||||
|  | -	fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); | ||||||
|  | -	fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); | ||||||
|  | -	fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void | ||||||
|  | -qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, | ||||||
|  | -		u8 aging) | ||||||
|  | -{ | ||||||
|  | -	u32 reg[3] = { 0 }; | ||||||
|  | - | ||||||
|  | -	/* vid - 83:72 */ | ||||||
|  | -	reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); | ||||||
|  | -	/* aging - 67:64 */ | ||||||
|  | -	reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); | ||||||
|  | -	/* portmask - 54:48 */ | ||||||
|  | -	reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); | ||||||
|  | -	/* mac - 47:0 */ | ||||||
|  | -	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); | ||||||
|  | -	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); | ||||||
|  | -	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); | ||||||
|  | -	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); | ||||||
|  | -	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); | ||||||
|  | -	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); | ||||||
|  | - | ||||||
|  | -	/* load the array into the ARL table */ | ||||||
|  | -	qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) | ||||||
|  | -{ | ||||||
|  | -	u32 reg; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	/* Set the command and FDB index */ | ||||||
|  | -	reg = QCA8K_ATU_FUNC_BUSY; | ||||||
|  | -	reg |= cmd; | ||||||
|  | -	if (port >= 0) { | ||||||
|  | -		reg |= QCA8K_ATU_FUNC_PORT_EN; | ||||||
|  | -		reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	/* Write the function register triggering the table access */ | ||||||
|  | -	ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	/* wait for completion */ | ||||||
|  | -	ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	/* Check for table full violation when adding an entry */ | ||||||
|  | -	if (cmd == QCA8K_FDB_LOAD) { | ||||||
|  | -		ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); | ||||||
|  | -		if (ret < 0) | ||||||
|  | -			return ret; | ||||||
|  | -		if (reg & QCA8K_ATU_FUNC_FULL) | ||||||
|  | -			return -1; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) | ||||||
|  | -{ | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); | ||||||
|  | -	ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	return qca8k_fdb_read(priv, fdb); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, | ||||||
|  | -	      u16 vid, u8 aging) | ||||||
|  | -{ | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | -	qca8k_fdb_write(priv, vid, port_mask, mac, aging); | ||||||
|  | -	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | - | ||||||
|  | -	return ret; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) | ||||||
|  | -{ | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | -	qca8k_fdb_write(priv, vid, port_mask, mac, 0); | ||||||
|  | -	ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | - | ||||||
|  | -	return ret; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void | ||||||
|  | -qca8k_fdb_flush(struct qca8k_priv *priv) | ||||||
|  | -{ | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | -	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, | ||||||
|  | -			    const u8 *mac, u16 vid) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_fdb fdb = { 0 }; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | - | ||||||
|  | -	qca8k_fdb_write(priv, vid, 0, mac, 0); | ||||||
|  | -	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	ret = qca8k_fdb_read(priv, &fdb); | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	/* Rule exist. Delete first */ | ||||||
|  | -	if (!fdb.aging) { | ||||||
|  | -		ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); | ||||||
|  | -		if (ret) | ||||||
|  | -			goto exit; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	/* Add port to fdb portmask */ | ||||||
|  | -	fdb.port_mask |= port_mask; | ||||||
|  | - | ||||||
|  | -	qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); | ||||||
|  | -	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); | ||||||
|  | - | ||||||
|  | -exit: | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | -	return ret; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, | ||||||
|  | -			 const u8 *mac, u16 vid) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_fdb fdb = { 0 }; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | - | ||||||
|  | -	qca8k_fdb_write(priv, vid, 0, mac, 0); | ||||||
|  | -	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	/* Rule doesn't exist. Why delete? */ | ||||||
|  | -	if (!fdb.aging) { | ||||||
|  | -		ret = -EINVAL; | ||||||
|  | -		goto exit; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); | ||||||
|  | -	if (ret) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	/* Only port in the rule is this port. Don't re insert */ | ||||||
|  | -	if (fdb.port_mask == port_mask) | ||||||
|  | -		goto exit; | ||||||
|  | - | ||||||
|  | -	/* Remove port from port mask */ | ||||||
|  | -	fdb.port_mask &= ~port_mask; | ||||||
|  | - | ||||||
|  | -	qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); | ||||||
|  | -	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); | ||||||
|  | - | ||||||
|  | -exit: | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | -	return ret; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  |  qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) | ||||||
|  |  { | ||||||
|  |  	u32 reg; | ||||||
|  | @@ -2048,97 +1837,6 @@ exit: | ||||||
|  |  	return ret; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static void | ||||||
|  | -qca8k_port_fast_age(struct dsa_switch *ds, int port) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | - | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | -	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, | ||||||
|  | -		      u16 port_mask, u16 vid) | ||||||
|  | -{ | ||||||
|  | -	/* Set the vid to the port vlan id if no vid is set */ | ||||||
|  | -	if (!vid) | ||||||
|  | -		vid = QCA8K_PORT_VID_DEF; | ||||||
|  | - | ||||||
|  | -	return qca8k_fdb_add(priv, addr, port_mask, vid, | ||||||
|  | -			     QCA8K_ATU_STATUS_STATIC); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_fdb_add(struct dsa_switch *ds, int port, | ||||||
|  | -		   const unsigned char *addr, u16 vid) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	u16 port_mask = BIT(port); | ||||||
|  | - | ||||||
|  | -	return qca8k_port_fdb_insert(priv, addr, port_mask, vid); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_fdb_del(struct dsa_switch *ds, int port, | ||||||
|  | -		   const unsigned char *addr, u16 vid) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	u16 port_mask = BIT(port); | ||||||
|  | - | ||||||
|  | -	if (!vid) | ||||||
|  | -		vid = QCA8K_PORT_VID_DEF; | ||||||
|  | - | ||||||
|  | -	return qca8k_fdb_del(priv, addr, port_mask, vid); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_fdb_dump(struct dsa_switch *ds, int port, | ||||||
|  | -		    dsa_fdb_dump_cb_t *cb, void *data) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | -	struct qca8k_fdb _fdb = { 0 }; | ||||||
|  | -	int cnt = QCA8K_NUM_FDB_RECORDS; | ||||||
|  | -	bool is_static; | ||||||
|  | -	int ret = 0; | ||||||
|  | - | ||||||
|  | -	mutex_lock(&priv->reg_mutex); | ||||||
|  | -	while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { | ||||||
|  | -		if (!_fdb.aging) | ||||||
|  | -			break; | ||||||
|  | -		is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); | ||||||
|  | -		ret = cb(_fdb.mac, _fdb.vid, is_static, data); | ||||||
|  | -		if (ret) | ||||||
|  | -			break; | ||||||
|  | -	} | ||||||
|  | -	mutex_unlock(&priv->reg_mutex); | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_mdb_add(struct dsa_switch *ds, int port, | ||||||
|  | -		   const struct switchdev_obj_port_mdb *mdb) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	const u8 *addr = mdb->addr; | ||||||
|  | -	u16 vid = mdb->vid; | ||||||
|  | - | ||||||
|  | -	return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  | -qca8k_port_mdb_del(struct dsa_switch *ds, int port, | ||||||
|  | -		   const struct switchdev_obj_port_mdb *mdb) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	const u8 *addr = mdb->addr; | ||||||
|  | -	u16 vid = mdb->vid; | ||||||
|  | - | ||||||
|  | -	return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static int | ||||||
|  |  qca8k_port_mirror_add(struct dsa_switch *ds, int port, | ||||||
|  |  		      struct dsa_mall_mirror_tc_entry *mirror, | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -103,7 +103,7 @@ const struct regmap_access_table qca8k_r | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  /* TODO: remove these extra ops when we can support regmap bulk read/write */ | ||||||
|  | -int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) | ||||||
|  | +static int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) | ||||||
|  |  { | ||||||
|  |  	int i, count = len / sizeof(u32), ret; | ||||||
|  |   | ||||||
|  | @@ -121,7 +121,7 @@ int qca8k_bulk_read(struct qca8k_priv *p | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /* TODO: remove these extra ops when we can support regmap bulk read/write */ | ||||||
|  | -int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) | ||||||
|  | +static int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) | ||||||
|  |  { | ||||||
|  |  	int i, count = len / sizeof(u32), ret; | ||||||
|  |  	u32 tmp; | ||||||
|  | @@ -149,6 +149,211 @@ int qca8k_busy_wait(struct qca8k_priv *p | ||||||
|  |  				       QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) | ||||||
|  | +{ | ||||||
|  | +	u32 reg[3]; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* load the ARL table into an array */ | ||||||
|  | +	ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	/* vid - 83:72 */ | ||||||
|  | +	fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); | ||||||
|  | +	/* aging - 67:64 */ | ||||||
|  | +	fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); | ||||||
|  | +	/* portmask - 54:48 */ | ||||||
|  | +	fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); | ||||||
|  | +	/* mac - 47:0 */ | ||||||
|  | +	fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); | ||||||
|  | +	fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); | ||||||
|  | +	fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); | ||||||
|  | +	fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); | ||||||
|  | +	fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); | ||||||
|  | +	fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, | ||||||
|  | +			    const u8 *mac, u8 aging) | ||||||
|  | +{ | ||||||
|  | +	u32 reg[3] = { 0 }; | ||||||
|  | + | ||||||
|  | +	/* vid - 83:72 */ | ||||||
|  | +	reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); | ||||||
|  | +	/* aging - 67:64 */ | ||||||
|  | +	reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); | ||||||
|  | +	/* portmask - 54:48 */ | ||||||
|  | +	reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); | ||||||
|  | +	/* mac - 47:0 */ | ||||||
|  | +	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); | ||||||
|  | +	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); | ||||||
|  | +	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); | ||||||
|  | +	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); | ||||||
|  | +	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); | ||||||
|  | +	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); | ||||||
|  | + | ||||||
|  | +	/* load the array into the ARL table */ | ||||||
|  | +	qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, | ||||||
|  | +			    int port) | ||||||
|  | +{ | ||||||
|  | +	u32 reg; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	/* Set the command and FDB index */ | ||||||
|  | +	reg = QCA8K_ATU_FUNC_BUSY; | ||||||
|  | +	reg |= cmd; | ||||||
|  | +	if (port >= 0) { | ||||||
|  | +		reg |= QCA8K_ATU_FUNC_PORT_EN; | ||||||
|  | +		reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	/* Write the function register triggering the table access */ | ||||||
|  | +	ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	/* wait for completion */ | ||||||
|  | +	ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	/* Check for table full violation when adding an entry */ | ||||||
|  | +	if (cmd == QCA8K_FDB_LOAD) { | ||||||
|  | +		ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); | ||||||
|  | +		if (ret < 0) | ||||||
|  | +			return ret; | ||||||
|  | +		if (reg & QCA8K_ATU_FUNC_FULL) | ||||||
|  | +			return -1; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, | ||||||
|  | +			  int port) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); | ||||||
|  | +	ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	return qca8k_fdb_read(priv, fdb); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, | ||||||
|  | +			 u16 port_mask, u16 vid, u8 aging) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | +	qca8k_fdb_write(priv, vid, port_mask, mac, aging); | ||||||
|  | +	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, | ||||||
|  | +			 u16 port_mask, u16 vid) | ||||||
|  | +{ | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | +	qca8k_fdb_write(priv, vid, port_mask, mac, 0); | ||||||
|  | +	ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +void qca8k_fdb_flush(struct qca8k_priv *priv) | ||||||
|  | +{ | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | +	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, | ||||||
|  | +				       const u8 *mac, u16 vid) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_fdb fdb = { 0 }; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | + | ||||||
|  | +	qca8k_fdb_write(priv, vid, 0, mac, 0); | ||||||
|  | +	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	ret = qca8k_fdb_read(priv, &fdb); | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	/* Rule exist. Delete first */ | ||||||
|  | +	if (!fdb.aging) { | ||||||
|  | +		ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); | ||||||
|  | +		if (ret) | ||||||
|  | +			goto exit; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	/* Add port to fdb portmask */ | ||||||
|  | +	fdb.port_mask |= port_mask; | ||||||
|  | + | ||||||
|  | +	qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); | ||||||
|  | +	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); | ||||||
|  | + | ||||||
|  | +exit: | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static int qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, | ||||||
|  | +				    const u8 *mac, u16 vid) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_fdb fdb = { 0 }; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | + | ||||||
|  | +	qca8k_fdb_write(priv, vid, 0, mac, 0); | ||||||
|  | +	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	/* Rule doesn't exist. Why delete? */ | ||||||
|  | +	if (!fdb.aging) { | ||||||
|  | +		ret = -EINVAL; | ||||||
|  | +		goto exit; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); | ||||||
|  | +	if (ret) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	/* Only port in the rule is this port. Don't re insert */ | ||||||
|  | +	if (fdb.port_mask == port_mask) | ||||||
|  | +		goto exit; | ||||||
|  | + | ||||||
|  | +	/* Remove port from port mask */ | ||||||
|  | +	fdb.port_mask &= ~port_mask; | ||||||
|  | + | ||||||
|  | +	qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); | ||||||
|  | +	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); | ||||||
|  | + | ||||||
|  | +exit: | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  int qca8k_mib_init(struct qca8k_priv *priv) | ||||||
|  |  { | ||||||
|  |  	int ret; | ||||||
|  | @@ -368,6 +573,15 @@ void qca8k_port_bridge_leave(struct dsa_ | ||||||
|  |  		  QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +void qca8k_port_fast_age(struct dsa_switch *ds, int port) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | +	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) | ||||||
|  |  { | ||||||
|  |  	struct qca8k_priv *priv = ds->priv; | ||||||
|  | @@ -452,3 +666,78 @@ int qca8k_port_max_mtu(struct dsa_switch | ||||||
|  |  { | ||||||
|  |  	return QCA8K_MAX_MTU; | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, | ||||||
|  | +			  u16 port_mask, u16 vid) | ||||||
|  | +{ | ||||||
|  | +	/* Set the vid to the port vlan id if no vid is set */ | ||||||
|  | +	if (!vid) | ||||||
|  | +		vid = QCA8K_PORT_VID_DEF; | ||||||
|  | + | ||||||
|  | +	return qca8k_fdb_add(priv, addr, port_mask, vid, | ||||||
|  | +			     QCA8K_ATU_STATUS_STATIC); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_fdb_add(struct dsa_switch *ds, int port, | ||||||
|  | +		       const unsigned char *addr, u16 vid) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | +	u16 port_mask = BIT(port); | ||||||
|  | + | ||||||
|  | +	return qca8k_port_fdb_insert(priv, addr, port_mask, vid); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_fdb_del(struct dsa_switch *ds, int port, | ||||||
|  | +		       const unsigned char *addr, u16 vid) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | +	u16 port_mask = BIT(port); | ||||||
|  | + | ||||||
|  | +	if (!vid) | ||||||
|  | +		vid = QCA8K_PORT_VID_DEF; | ||||||
|  | + | ||||||
|  | +	return qca8k_fdb_del(priv, addr, port_mask, vid); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, | ||||||
|  | +			dsa_fdb_dump_cb_t *cb, void *data) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; | ||||||
|  | +	struct qca8k_fdb _fdb = { 0 }; | ||||||
|  | +	int cnt = QCA8K_NUM_FDB_RECORDS; | ||||||
|  | +	bool is_static; | ||||||
|  | +	int ret = 0; | ||||||
|  | + | ||||||
|  | +	mutex_lock(&priv->reg_mutex); | ||||||
|  | +	while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { | ||||||
|  | +		if (!_fdb.aging) | ||||||
|  | +			break; | ||||||
|  | +		is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); | ||||||
|  | +		ret = cb(_fdb.mac, _fdb.vid, is_static, data); | ||||||
|  | +		if (ret) | ||||||
|  | +			break; | ||||||
|  | +	} | ||||||
|  | +	mutex_unlock(&priv->reg_mutex); | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_mdb_add(struct dsa_switch *ds, int port, | ||||||
|  | +		       const struct switchdev_obj_port_mdb *mdb) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	const u8 *addr = mdb->addr; | ||||||
|  | +	u16 vid = mdb->vid; | ||||||
|  | + | ||||||
|  | +	return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int qca8k_port_mdb_del(struct dsa_switch *ds, int port, | ||||||
|  | +		       const struct switchdev_obj_port_mdb *mdb) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	const u8 *addr = mdb->addr; | ||||||
|  | +	u16 vid = mdb->vid; | ||||||
|  | + | ||||||
|  | +	return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); | ||||||
|  | +} | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -430,11 +430,9 @@ int qca8k_read(struct qca8k_priv *priv, | ||||||
|  |  int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); | ||||||
|  |  int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); | ||||||
|  |   | ||||||
|  | -int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); | ||||||
|  | -int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); | ||||||
|  | - | ||||||
|  |  /* 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 */ | ||||||
|  |  void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data); | ||||||
|  | @@ -463,6 +461,23 @@ int qca8k_port_change_mtu(struct dsa_swi | ||||||
|  |  int qca8k_port_max_mtu(struct dsa_switch *ds, int port); | ||||||
|  |   | ||||||
|  |  /* Common fast age function */ | ||||||
|  | +void qca8k_port_fast_age(struct dsa_switch *ds, int port); | ||||||
|  |  int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs); | ||||||
|  |   | ||||||
|  | +/* Common FDB function */ | ||||||
|  | +int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, | ||||||
|  | +			  u16 port_mask, u16 vid); | ||||||
|  | +int qca8k_port_fdb_add(struct dsa_switch *ds, int port, | ||||||
|  | +		       const unsigned char *addr, u16 vid); | ||||||
|  | +int qca8k_port_fdb_del(struct dsa_switch *ds, int port, | ||||||
|  | +		       const unsigned char *addr, u16 vid); | ||||||
|  | +int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, | ||||||
|  | +			dsa_fdb_dump_cb_t *cb, void *data); | ||||||
|  | + | ||||||
|  | +/* Common MDB function */ | ||||||
|  | +int qca8k_port_mdb_add(struct dsa_switch *ds, int port, | ||||||
|  | +		       const struct switchdev_obj_port_mdb *mdb); | ||||||
|  | +int qca8k_port_mdb_del(struct dsa_switch *ds, int port, | ||||||
|  | +		       const struct switchdev_obj_port_mdb *mdb); | ||||||
|  | + | ||||||
|  |  #endif /* __QCA8K_H */ | ||||||
| @@ -0,0 +1,232 @@ | |||||||
|  | From 742d37a84d3f7bb60d9b2d9ada9ad4e599f65ebf Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:20 +0200 | ||||||
|  | Subject: [PATCH 11/14] net: dsa: qca8k: move port mirror functions to common | ||||||
|  |  code | ||||||
|  |  | ||||||
|  | The same port mirror 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   | 93 ------------------------------ | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 91 +++++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        |  7 +++ | ||||||
|  |  3 files changed, 98 insertions(+), 93 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -1838,99 +1838,6 @@ exit: | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static int | ||||||
|  | -qca8k_port_mirror_add(struct dsa_switch *ds, int port, | ||||||
|  | -		      struct dsa_mall_mirror_tc_entry *mirror, | ||||||
|  | -		      bool ingress) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	int monitor_port, ret; | ||||||
|  | -	u32 reg, val; | ||||||
|  | - | ||||||
|  | -	/* Check for existent entry */ | ||||||
|  | -	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) | ||||||
|  | -		return -EEXIST; | ||||||
|  | - | ||||||
|  | -	ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	/* QCA83xx can have only one port set to mirror mode. | ||||||
|  | -	 * Check that the correct port is requested and return error otherwise. | ||||||
|  | -	 * When no mirror port is set, the values is set to 0xF | ||||||
|  | -	 */ | ||||||
|  | -	monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); | ||||||
|  | -	if (monitor_port != 0xF && monitor_port != mirror->to_local_port) | ||||||
|  | -		return -EEXIST; | ||||||
|  | - | ||||||
|  | -	/* Set the monitor port */ | ||||||
|  | -	val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, | ||||||
|  | -			 mirror->to_local_port); | ||||||
|  | -	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, | ||||||
|  | -				 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	if (ingress) { | ||||||
|  | -		reg = QCA8K_PORT_LOOKUP_CTRL(port); | ||||||
|  | -		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; | ||||||
|  | -	} else { | ||||||
|  | -		reg = QCA8K_REG_PORT_HOL_CTRL1(port); | ||||||
|  | -		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	ret = regmap_update_bits(priv->regmap, reg, val, val); | ||||||
|  | -	if (ret) | ||||||
|  | -		return ret; | ||||||
|  | - | ||||||
|  | -	/* Track mirror port for tx and rx to decide when the | ||||||
|  | -	 * mirror port has to be disabled. | ||||||
|  | -	 */ | ||||||
|  | -	if (ingress) | ||||||
|  | -		priv->mirror_rx |= BIT(port); | ||||||
|  | -	else | ||||||
|  | -		priv->mirror_tx |= BIT(port); | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static void | ||||||
|  | -qca8k_port_mirror_del(struct dsa_switch *ds, int port, | ||||||
|  | -		      struct dsa_mall_mirror_tc_entry *mirror) | ||||||
|  | -{ | ||||||
|  | -	struct qca8k_priv *priv = ds->priv; | ||||||
|  | -	u32 reg, val; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	if (mirror->ingress) { | ||||||
|  | -		reg = QCA8K_PORT_LOOKUP_CTRL(port); | ||||||
|  | -		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; | ||||||
|  | -	} else { | ||||||
|  | -		reg = QCA8K_REG_PORT_HOL_CTRL1(port); | ||||||
|  | -		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	ret = regmap_clear_bits(priv->regmap, reg, val); | ||||||
|  | -	if (ret) | ||||||
|  | -		goto err; | ||||||
|  | - | ||||||
|  | -	if (mirror->ingress) | ||||||
|  | -		priv->mirror_rx &= ~BIT(port); | ||||||
|  | -	else | ||||||
|  | -		priv->mirror_tx &= ~BIT(port); | ||||||
|  | - | ||||||
|  | -	/* No port set to send packet to mirror port. Disable mirror port */ | ||||||
|  | -	if (!priv->mirror_rx && !priv->mirror_tx) { | ||||||
|  | -		val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); | ||||||
|  | -		ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, | ||||||
|  | -					 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); | ||||||
|  | -		if (ret) | ||||||
|  | -			goto err; | ||||||
|  | -	} | ||||||
|  | -err: | ||||||
|  | -	dev_err(priv->dev, "Failed to del mirror port from %d", port); | ||||||
|  | -} | ||||||
|  | - | ||||||
|  | -static int | ||||||
|  |  qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, | ||||||
|  |  			  struct netlink_ext_ack *extack) | ||||||
|  |  { | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -741,3 +741,94 @@ int qca8k_port_mdb_del(struct dsa_switch | ||||||
|  |   | ||||||
|  |  	return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +int qca8k_port_mirror_add(struct dsa_switch *ds, int port, | ||||||
|  | +			  struct dsa_mall_mirror_tc_entry *mirror, | ||||||
|  | +			  bool ingress) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	int monitor_port, ret; | ||||||
|  | +	u32 reg, val; | ||||||
|  | + | ||||||
|  | +	/* Check for existent entry */ | ||||||
|  | +	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) | ||||||
|  | +		return -EEXIST; | ||||||
|  | + | ||||||
|  | +	ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	/* QCA83xx can have only one port set to mirror mode. | ||||||
|  | +	 * Check that the correct port is requested and return error otherwise. | ||||||
|  | +	 * When no mirror port is set, the values is set to 0xF | ||||||
|  | +	 */ | ||||||
|  | +	monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); | ||||||
|  | +	if (monitor_port != 0xF && monitor_port != mirror->to_local_port) | ||||||
|  | +		return -EEXIST; | ||||||
|  | + | ||||||
|  | +	/* Set the monitor port */ | ||||||
|  | +	val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, | ||||||
|  | +			 mirror->to_local_port); | ||||||
|  | +	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, | ||||||
|  | +				 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	if (ingress) { | ||||||
|  | +		reg = QCA8K_PORT_LOOKUP_CTRL(port); | ||||||
|  | +		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; | ||||||
|  | +	} else { | ||||||
|  | +		reg = QCA8K_REG_PORT_HOL_CTRL1(port); | ||||||
|  | +		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	ret = regmap_update_bits(priv->regmap, reg, val, val); | ||||||
|  | +	if (ret) | ||||||
|  | +		return ret; | ||||||
|  | + | ||||||
|  | +	/* Track mirror port for tx and rx to decide when the | ||||||
|  | +	 * mirror port has to be disabled. | ||||||
|  | +	 */ | ||||||
|  | +	if (ingress) | ||||||
|  | +		priv->mirror_rx |= BIT(port); | ||||||
|  | +	else | ||||||
|  | +		priv->mirror_tx |= BIT(port); | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +void qca8k_port_mirror_del(struct dsa_switch *ds, int port, | ||||||
|  | +			   struct dsa_mall_mirror_tc_entry *mirror) | ||||||
|  | +{ | ||||||
|  | +	struct qca8k_priv *priv = ds->priv; | ||||||
|  | +	u32 reg, val; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	if (mirror->ingress) { | ||||||
|  | +		reg = QCA8K_PORT_LOOKUP_CTRL(port); | ||||||
|  | +		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; | ||||||
|  | +	} else { | ||||||
|  | +		reg = QCA8K_REG_PORT_HOL_CTRL1(port); | ||||||
|  | +		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	ret = regmap_clear_bits(priv->regmap, reg, val); | ||||||
|  | +	if (ret) | ||||||
|  | +		goto err; | ||||||
|  | + | ||||||
|  | +	if (mirror->ingress) | ||||||
|  | +		priv->mirror_rx &= ~BIT(port); | ||||||
|  | +	else | ||||||
|  | +		priv->mirror_tx &= ~BIT(port); | ||||||
|  | + | ||||||
|  | +	/* No port set to send packet to mirror port. Disable mirror port */ | ||||||
|  | +	if (!priv->mirror_rx && !priv->mirror_tx) { | ||||||
|  | +		val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); | ||||||
|  | +		ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, | ||||||
|  | +					 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); | ||||||
|  | +		if (ret) | ||||||
|  | +			goto err; | ||||||
|  | +	} | ||||||
|  | +err: | ||||||
|  | +	dev_err(priv->dev, "Failed to del mirror port from %d", port); | ||||||
|  | +} | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -480,4 +480,11 @@ int qca8k_port_mdb_add(struct dsa_switch | ||||||
|  |  int qca8k_port_mdb_del(struct dsa_switch *ds, int port, | ||||||
|  |  		       const struct switchdev_obj_port_mdb *mdb); | ||||||
|  |   | ||||||
|  | +/* Common port mirror function */ | ||||||
|  | +int qca8k_port_mirror_add(struct dsa_switch *ds, int port, | ||||||
|  | +			  struct dsa_mall_mirror_tc_entry *mirror, | ||||||
|  | +			  bool ingress); | ||||||
|  | +void qca8k_port_mirror_del(struct dsa_switch *ds, int port, | ||||||
|  | +			   struct dsa_mall_mirror_tc_entry *mirror); | ||||||
|  | + | ||||||
|  |  #endif /* __QCA8K_H */ | ||||||
| @@ -0,0 +1,448 @@ | |||||||
|  | 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 */ | ||||||
| @@ -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 */ | ||||||
| @@ -0,0 +1,102 @@ | |||||||
|  | From 9d1bcb1f293f1391302a109c9819c3705c804700 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Wed, 27 Jul 2022 13:35:23 +0200 | ||||||
|  | Subject: [PATCH 14/14] net: dsa: qca8k: move read_switch_id function to common | ||||||
|  |  code | ||||||
|  |  | ||||||
|  | The same function to read the switch id is 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   | 29 ----------------------------- | ||||||
|  |  drivers/net/dsa/qca/qca8k-common.c | 29 +++++++++++++++++++++++++++++ | ||||||
|  |  drivers/net/dsa/qca/qca8k.h        |  1 + | ||||||
|  |  3 files changed, 30 insertions(+), 29 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -1822,35 +1822,6 @@ static const struct dsa_switch_ops qca8k | ||||||
|  |  	.connect_tag_protocol	= qca8k_connect_tag_protocol, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -static int qca8k_read_switch_id(struct qca8k_priv *priv) | ||||||
|  | -{ | ||||||
|  | -	u32 val; | ||||||
|  | -	u8 id; | ||||||
|  | -	int ret; | ||||||
|  | - | ||||||
|  | -	if (!priv->info) | ||||||
|  | -		return -ENODEV; | ||||||
|  | - | ||||||
|  | -	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); | ||||||
|  | -	if (ret < 0) | ||||||
|  | -		return -ENODEV; | ||||||
|  | - | ||||||
|  | -	id = QCA8K_MASK_CTRL_DEVICE_ID(val); | ||||||
|  | -	if (id != priv->info->id) { | ||||||
|  | -		dev_err(priv->dev, | ||||||
|  | -			"Switch id detected %x but expected %x", | ||||||
|  | -			id, priv->info->id); | ||||||
|  | -		return -ENODEV; | ||||||
|  | -	} | ||||||
|  | - | ||||||
|  | -	priv->switch_id = id; | ||||||
|  | - | ||||||
|  | -	/* Save revision to communicate to the internal PHY driver */ | ||||||
|  | -	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); | ||||||
|  | - | ||||||
|  | -	return 0; | ||||||
|  | -} | ||||||
|  | - | ||||||
|  |  static int | ||||||
|  |  qca8k_sw_probe(struct mdio_device *mdiodev) | ||||||
|  |  { | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-common.c | ||||||
|  | @@ -1175,3 +1175,32 @@ int qca8k_port_lag_leave(struct dsa_swit | ||||||
|  |  { | ||||||
|  |  	return qca8k_lag_refresh_portmap(ds, port, lag, true); | ||||||
|  |  } | ||||||
|  | + | ||||||
|  | +int qca8k_read_switch_id(struct qca8k_priv *priv) | ||||||
|  | +{ | ||||||
|  | +	u32 val; | ||||||
|  | +	u8 id; | ||||||
|  | +	int ret; | ||||||
|  | + | ||||||
|  | +	if (!priv->info) | ||||||
|  | +		return -ENODEV; | ||||||
|  | + | ||||||
|  | +	ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); | ||||||
|  | +	if (ret < 0) | ||||||
|  | +		return -ENODEV; | ||||||
|  | + | ||||||
|  | +	id = QCA8K_MASK_CTRL_DEVICE_ID(val); | ||||||
|  | +	if (id != priv->info->id) { | ||||||
|  | +		dev_err(priv->dev, | ||||||
|  | +			"Switch id detected %x but expected %x", | ||||||
|  | +			id, priv->info->id); | ||||||
|  | +		return -ENODEV; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	priv->switch_id = id; | ||||||
|  | + | ||||||
|  | +	/* Save revision to communicate to the internal PHY driver */ | ||||||
|  | +	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); | ||||||
|  | + | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k.h | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k.h | ||||||
|  | @@ -424,6 +424,7 @@ extern const struct qca8k_mib_desc ar832 | ||||||
|  |  extern const struct regmap_access_table qca8k_readable_table; | ||||||
|  |  int qca8k_mib_init(struct qca8k_priv *priv); | ||||||
|  |  void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable); | ||||||
|  | +int qca8k_read_switch_id(struct qca8k_priv *priv); | ||||||
|  |   | ||||||
|  |  /* Common read/write/rmw function */ | ||||||
|  |  int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | From 057bcf15db8e625276ddf02b2b7c668a3cb43f81 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | Date: Sun, 4 Sep 2022 23:46:24 +0200 | ||||||
|  | Subject: [net PATCH] net: dsa: qca8k: fix NULL pointer dereference for | ||||||
|  |  of_device_get_match_data | ||||||
|  |  | ||||||
|  | of_device_get_match_data is called on priv->dev before priv->dev is | ||||||
|  | actually set. Move of_device_get_match_data after priv->dev is correctly | ||||||
|  | set to fix this kernel panic. | ||||||
|  |  | ||||||
|  | Fixes: 3bb0844e7bcd ("net: dsa: qca8k: cache match data to speed up access") | ||||||
|  | Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> | ||||||
|  | --- | ||||||
|  |  drivers/net/dsa/qca/qca8k-8xxx.c | 2 +- | ||||||
|  |  1 file changed, 1 insertion(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | +++ b/drivers/net/dsa/qca/qca8k-8xxx.c | ||||||
|  | @@ -1835,9 +1835,9 @@ qca8k_sw_probe(struct mdio_device *mdiod | ||||||
|  |  	if (!priv) | ||||||
|  |  		return -ENOMEM; | ||||||
|  |   | ||||||
|  | -	priv->info = of_device_get_match_data(priv->dev); | ||||||
|  |  	priv->bus = mdiodev->bus; | ||||||
|  |  	priv->dev = &mdiodev->dev; | ||||||
|  | +	priv->info = of_device_get_match_data(priv->dev); | ||||||
|  |   | ||||||
|  |  	priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", | ||||||
|  |  						   GPIOD_ASIS); | ||||||
		Reference in New Issue
	
	Block a user
	 Christian Marangi
					Christian Marangi