Commit 843eb679 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dsa-rzn1-a5psw-stp'



Alexis Lothoré says:

====================
net: dsa: rzn1-a5psw: fix STP states handling

This small series fixes STP support and while adding a new function to
enable/disable learning, use that to disable learning on standalone ports
at switch setup as reported by Vladimir Oltean.

This series was initially submitted on net-next by Clement Leger, but some
career evolutions has made him hand me over those topics.
Also, this new revision is submitted on net instead of net-next for V1
based on Vladimir Oltean's suggestion

Changes since v2:
- fix commit split by moving A5PSW_MGMT_CFG_ENABLE in relevant commit
- fix reverse christmas tree ordering in a5psw_port_stp_state_set

Changes since v1:
- fix typos in commit messages and doc
- re-split STP states handling commit
- add Fixes: tag and new Signed-off-by
- submit series as fix on net instead of net-next
- split learning and blocking setting functions
- remove unused define A5PSW_PORT_ENA_TX_SHIFT
- add boolean for tx/rx enabled for clarity
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents d80fc101 ec52b69c
Loading
Loading
Loading
Loading
+65 −18
Original line number Diff line number Diff line
@@ -120,6 +120,22 @@ static void a5psw_port_mgmtfwd_set(struct a5psw *a5psw, int port, bool enable)
	a5psw_port_pattern_set(a5psw, port, A5PSW_PATTERN_MGMTFWD, enable);
}

static void a5psw_port_tx_enable(struct a5psw *a5psw, int port, bool enable)
{
	u32 mask = A5PSW_PORT_ENA_TX(port);
	u32 reg = enable ? mask : 0;

	/* Even though the port TX is disabled through TXENA bit in the
	 * PORT_ENA register, it can still send BPDUs. This depends on the tag
	 * configuration added when sending packets from the CPU port to the
	 * switch port. Indeed, when using forced forwarding without filtering,
	 * even disabled ports will be able to send packets that are tagged.
	 * This allows to implement STP support when ports are in a state where
	 * forwarding traffic should be stopped but BPDUs should still be sent.
	 */
	a5psw_reg_rmw(a5psw, A5PSW_PORT_ENA, mask, reg);
}

static void a5psw_port_enable_set(struct a5psw *a5psw, int port, bool enable)
{
	u32 port_ena = 0;
@@ -292,6 +308,22 @@ static int a5psw_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
	return 0;
}

static void a5psw_port_learning_set(struct a5psw *a5psw, int port, bool learn)
{
	u32 mask = A5PSW_INPUT_LEARN_DIS(port);
	u32 reg = !learn ? mask : 0;

	a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg);
}

static void a5psw_port_rx_block_set(struct a5psw *a5psw, int port, bool block)
{
	u32 mask = A5PSW_INPUT_LEARN_BLOCK(port);
	u32 reg = block ? mask : 0;

	a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg);
}

static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port,
					  bool set)
{
@@ -308,6 +340,14 @@ static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port,
		a5psw_reg_writel(a5psw, offsets[i], a5psw->bridged_ports);
}

static void a5psw_port_set_standalone(struct a5psw *a5psw, int port,
				      bool standalone)
{
	a5psw_port_learning_set(a5psw, port, !standalone);
	a5psw_flooding_set_resolution(a5psw, port, !standalone);
	a5psw_port_mgmtfwd_set(a5psw, port, standalone);
}

static int a5psw_port_bridge_join(struct dsa_switch *ds, int port,
				  struct dsa_bridge bridge,
				  bool *tx_fwd_offload,
@@ -323,8 +363,7 @@ static int a5psw_port_bridge_join(struct dsa_switch *ds, int port,
	}

	a5psw->br_dev = bridge.dev;
	a5psw_flooding_set_resolution(a5psw, port, true);
	a5psw_port_mgmtfwd_set(a5psw, port, false);
	a5psw_port_set_standalone(a5psw, port, false);

	return 0;
}
@@ -334,8 +373,7 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port,
{
	struct a5psw *a5psw = ds->priv;

	a5psw_flooding_set_resolution(a5psw, port, false);
	a5psw_port_mgmtfwd_set(a5psw, port, true);
	a5psw_port_set_standalone(a5psw, port, true);

	/* No more ports bridged */
	if (a5psw->bridged_ports == BIT(A5PSW_CPU_PORT))
@@ -344,28 +382,35 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port,

static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
{
	u32 mask = A5PSW_INPUT_LEARN_DIS(port) | A5PSW_INPUT_LEARN_BLOCK(port);
	bool learning_enabled, rx_enabled, tx_enabled;
	struct a5psw *a5psw = ds->priv;
	u32 reg = 0;

	switch (state) {
	case BR_STATE_DISABLED:
	case BR_STATE_BLOCKING:
		reg |= A5PSW_INPUT_LEARN_DIS(port);
		reg |= A5PSW_INPUT_LEARN_BLOCK(port);
		break;
	case BR_STATE_LISTENING:
		reg |= A5PSW_INPUT_LEARN_DIS(port);
		rx_enabled = false;
		tx_enabled = false;
		learning_enabled = false;
		break;
	case BR_STATE_LEARNING:
		reg |= A5PSW_INPUT_LEARN_BLOCK(port);
		rx_enabled = false;
		tx_enabled = false;
		learning_enabled = true;
		break;
	case BR_STATE_FORWARDING:
	default:
		rx_enabled = true;
		tx_enabled = true;
		learning_enabled = true;
		break;
	default:
		dev_err(ds->dev, "invalid STP state: %d\n", state);
		return;
	}

	a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg);
	a5psw_port_learning_set(a5psw, port, learning_enabled);
	a5psw_port_rx_block_set(a5psw, port, !rx_enabled);
	a5psw_port_tx_enable(a5psw, port, tx_enabled);
}

static void a5psw_port_fast_age(struct dsa_switch *ds, int port)
@@ -673,7 +718,7 @@ static int a5psw_setup(struct dsa_switch *ds)
	}

	/* Configure management port */
	reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_DISCARD;
	reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_ENABLE;
	a5psw_reg_writel(a5psw, A5PSW_MGMT_CFG, reg);

	/* Set pattern 0 to forward all frame to mgmt port */
@@ -722,13 +767,15 @@ static int a5psw_setup(struct dsa_switch *ds)
		if (dsa_port_is_unused(dp))
			continue;

		/* Enable egress flooding for CPU port */
		if (dsa_port_is_cpu(dp))
		/* Enable egress flooding and learning for CPU port */
		if (dsa_port_is_cpu(dp)) {
			a5psw_flooding_set_resolution(a5psw, port, true);
			a5psw_port_learning_set(a5psw, port, true);
		}

		/* Enable management forward only for user ports */
		/* Enable standalone mode for user ports */
		if (dsa_port_is_user(dp))
			a5psw_port_mgmtfwd_set(a5psw, port, true);
			a5psw_port_set_standalone(a5psw, port, true);
	}

	return 0;
+2 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#define A5PSW_PORT_OFFSET(port)		(0x400 * (port))

#define A5PSW_PORT_ENA			0x8
#define A5PSW_PORT_ENA_TX(port)		BIT(port)
#define A5PSW_PORT_ENA_RX_SHIFT		16
#define A5PSW_PORT_ENA_TX_RX(port)	(BIT((port) + A5PSW_PORT_ENA_RX_SHIFT) | \
					 BIT(port))
@@ -36,7 +37,7 @@
#define A5PSW_INPUT_LEARN_BLOCK(p)	BIT(p)

#define A5PSW_MGMT_CFG			0x20
#define A5PSW_MGMT_CFG_DISCARD		BIT(7)
#define A5PSW_MGMT_CFG_ENABLE		BIT(6)

#define A5PSW_MODE_CFG			0x24
#define A5PSW_MODE_STATS_RESET		BIT(31)