Commit 1e5b23e5 authored by Pawel Dembicki's avatar Pawel Dembicki Committed by Jakub Kicinski
Browse files

net: dsa: vsc73xx: add port_stp_state_set function



This isn't a fully functional implementation of 802.1D, but
port_stp_state_set is required for a future tag8021q operations.

This implementation handles properly all states, but vsc73xx doesn't
forward STP packets.

Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Reviewed-by: default avatarFlorian Fainelli <florian.fainelli@broadcom.com>
Reviewed-by: default avatarVladimir Oltean <olteanv@gmail.com>
Signed-off-by: default avatarPawel Dembicki <paweldembicki@gmail.com>
Link: https://patch.msgid.link/20240713211620.1125910-2-paweldembicki@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent a8ea8d53
Loading
Loading
Loading
Loading
+85 −11
Original line number Diff line number Diff line
@@ -164,6 +164,10 @@
#define VSC73XX_AGENCTRL	0xf0
#define VSC73XX_CAPRST		0xff

#define VSC73XX_SRCMASKS_CPU_COPY		BIT(27)
#define VSC73XX_SRCMASKS_MIRROR			BIT(26)
#define VSC73XX_SRCMASKS_PORTS_MASK		GENMASK(7, 0)

#define VSC73XX_MACACCESS_CPU_COPY		BIT(14)
#define VSC73XX_MACACCESS_FWD_KILL		BIT(13)
#define VSC73XX_MACACCESS_IGNORE_VLAN		BIT(12)
@@ -623,9 +627,6 @@ static int vsc73xx_setup(struct dsa_switch *ds)
	vsc73xx_write(vsc, VSC73XX_BLOCK_SYSTEM, 0, VSC73XX_GMIIDELAY,
		      VSC73XX_GMIIDELAY_GMII0_GTXDELAY_2_0_NS |
		      VSC73XX_GMIIDELAY_GMII0_RXDELAY_2_0_NS);
	/* Enable reception of frames on all ports */
	vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_RECVMASK,
		      0x5f);
	/* IP multicast flood mask (table 144) */
	vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_IFLODMSK,
		      0xff);
@@ -788,10 +789,6 @@ static void vsc73xx_mac_link_down(struct phylink_config *config,
	/* Allow backward dropping of frames from this port */
	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
			    VSC73XX_SBACKWDROP, BIT(port), BIT(port));

	/* Receive mask (disable forwarding) */
	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
			    VSC73XX_RECVMASK, BIT(port), 0);
}

static void vsc73xx_mac_link_up(struct phylink_config *config,
@@ -844,10 +841,6 @@ static void vsc73xx_mac_link_up(struct phylink_config *config,
	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
			    VSC73XX_ARBDISC, BIT(port), 0);

	/* Enable port (forwarding) in the receive mask */
	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
			    VSC73XX_RECVMASK, BIT(port), BIT(port));

	/* Disallow backward dropping of frames from this port */
	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
			    VSC73XX_SBACKWDROP, BIT(port), 0);
@@ -1039,6 +1032,86 @@ static void vsc73xx_phylink_get_caps(struct dsa_switch *dsa, int port,
	config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000;
}

static void vsc73xx_refresh_fwd_map(struct dsa_switch *ds, int port, u8 state)
{
	struct dsa_port *other_dp, *dp = dsa_to_port(ds, port);
	struct vsc73xx *vsc = ds->priv;
	u16 mask;

	if (state != BR_STATE_FORWARDING) {
		/* Ports that aren't in the forwarding state must not
		 * forward packets anywhere.
		 */
		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
				    VSC73XX_SRCMASKS + port,
				    VSC73XX_SRCMASKS_PORTS_MASK, 0);

		dsa_switch_for_each_available_port(other_dp, ds) {
			if (other_dp == dp)
				continue;
			vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
					    VSC73XX_SRCMASKS + other_dp->index,
					    BIT(port), 0);
		}

		return;
	}

	/* Forwarding ports must forward to the CPU and to other ports
	 * in the same bridge
	 */
	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
			    VSC73XX_SRCMASKS + CPU_PORT, BIT(port), BIT(port));

	mask = BIT(CPU_PORT);

	dsa_switch_for_each_user_port(other_dp, ds) {
		int other_port = other_dp->index;

		if (port == other_port || !dsa_port_bridge_same(dp, other_dp) ||
		    other_dp->stp_state != BR_STATE_FORWARDING)
			continue;

		mask |= BIT(other_port);

		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
				    VSC73XX_SRCMASKS + other_port,
				    BIT(port), BIT(port));
	}

	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
			    VSC73XX_SRCMASKS + port,
			    VSC73XX_SRCMASKS_PORTS_MASK, mask);
}

/* FIXME: STP frames aren't forwarded at this moment. BPDU frames are
 * forwarded only from and to PI/SI interface. For more info see chapter
 * 2.7.1 (CPU Forwarding) in datasheet.
 * This function is required for tag_8021q operations.
 */
static void vsc73xx_port_stp_state_set(struct dsa_switch *ds, int port,
				       u8 state)
{
	struct vsc73xx *vsc = ds->priv;
	u32 val;

	val = (state == BR_STATE_BLOCKING || state == BR_STATE_DISABLED) ?
	      0 : BIT(port);
	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
			    VSC73XX_RECVMASK, BIT(port), val);

	val = (state == BR_STATE_LEARNING || state == BR_STATE_FORWARDING) ?
	      BIT(port) : 0;
	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
			    VSC73XX_LEARNMASK, BIT(port), val);

	/* CPU Port should always forward packets when user ports are forwarding
	 * so let's configure it from other ports only.
	 */
	if (port != CPU_PORT)
		vsc73xx_refresh_fwd_map(ds, port, state);
}

static const struct phylink_mac_ops vsc73xx_phylink_mac_ops = {
	.mac_config = vsc73xx_mac_config,
	.mac_link_down = vsc73xx_mac_link_down,
@@ -1057,6 +1130,7 @@ static const struct dsa_switch_ops vsc73xx_ds_ops = {
	.port_disable = vsc73xx_port_disable,
	.port_change_mtu = vsc73xx_change_mtu,
	.port_max_mtu = vsc73xx_get_max_mtu,
	.port_stp_state_set = vsc73xx_port_stp_state_set,
	.phylink_get_caps = vsc73xx_phylink_get_caps,
};