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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
843eb679d8
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user