Skip to content

Commit ebe9bc5

Browse files
Tropicaodavem330
authored andcommitted
net: dsa: rzn1-a5psw: fix STP states handling
stp_set_state() should actually allow receiving BPDU while in LEARNING mode which is not the case. Additionally, the BLOCKEN bit does not actually forbid sending forwarded frames from that port. To fix this, add a5psw_port_tx_enable() function which allows to disable TX. However, while its name suggest that TX is totally disabled, it is not and can still allow to send BPDUs even if disabled. This can be done by using forced forwarding with the switch tagging mechanism but keeping "filtering" disabled (which is already the case in the rzn1-a5sw tag driver). With these fixes, STP support is now functional. Fixes: 888cdb8 ("net: dsa: rzn1-a5psw: add Renesas RZ/N1 advanced 5 port switch driver") Signed-off-by: Clément Léger <[email protected]> Signed-off-by: Alexis Lothoré <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 9e4b45f commit ebe9bc5

File tree

2 files changed

+49
-9
lines changed

2 files changed

+49
-9
lines changed

drivers/net/dsa/rzn1_a5psw.c

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ static void a5psw_port_mgmtfwd_set(struct a5psw *a5psw, int port, bool enable)
120120
a5psw_port_pattern_set(a5psw, port, A5PSW_PATTERN_MGMTFWD, enable);
121121
}
122122

123+
static void a5psw_port_tx_enable(struct a5psw *a5psw, int port, bool enable)
124+
{
125+
u32 mask = A5PSW_PORT_ENA_TX(port);
126+
u32 reg = enable ? mask : 0;
127+
128+
/* Even though the port TX is disabled through TXENA bit in the
129+
* PORT_ENA register, it can still send BPDUs. This depends on the tag
130+
* configuration added when sending packets from the CPU port to the
131+
* switch port. Indeed, when using forced forwarding without filtering,
132+
* even disabled ports will be able to send packets that are tagged.
133+
* This allows to implement STP support when ports are in a state where
134+
* forwarding traffic should be stopped but BPDUs should still be sent.
135+
*/
136+
a5psw_reg_rmw(a5psw, A5PSW_PORT_ENA, mask, reg);
137+
}
138+
123139
static void a5psw_port_enable_set(struct a5psw *a5psw, int port, bool enable)
124140
{
125141
u32 port_ena = 0;
@@ -292,6 +308,22 @@ static int a5psw_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
292308
return 0;
293309
}
294310

311+
static void a5psw_port_learning_set(struct a5psw *a5psw, int port, bool learn)
312+
{
313+
u32 mask = A5PSW_INPUT_LEARN_DIS(port);
314+
u32 reg = !learn ? mask : 0;
315+
316+
a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg);
317+
}
318+
319+
static void a5psw_port_rx_block_set(struct a5psw *a5psw, int port, bool block)
320+
{
321+
u32 mask = A5PSW_INPUT_LEARN_BLOCK(port);
322+
u32 reg = block ? mask : 0;
323+
324+
a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg);
325+
}
326+
295327
static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port,
296328
bool set)
297329
{
@@ -344,28 +376,35 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port,
344376

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

351382
switch (state) {
352383
case BR_STATE_DISABLED:
353384
case BR_STATE_BLOCKING:
354-
reg |= A5PSW_INPUT_LEARN_DIS(port);
355-
reg |= A5PSW_INPUT_LEARN_BLOCK(port);
356-
break;
357385
case BR_STATE_LISTENING:
358-
reg |= A5PSW_INPUT_LEARN_DIS(port);
386+
rx_enabled = false;
387+
tx_enabled = false;
388+
learning_enabled = false;
359389
break;
360390
case BR_STATE_LEARNING:
361-
reg |= A5PSW_INPUT_LEARN_BLOCK(port);
391+
rx_enabled = false;
392+
tx_enabled = false;
393+
learning_enabled = true;
362394
break;
363395
case BR_STATE_FORWARDING:
364-
default:
396+
rx_enabled = true;
397+
tx_enabled = true;
398+
learning_enabled = true;
365399
break;
400+
default:
401+
dev_err(ds->dev, "invalid STP state: %d\n", state);
402+
return;
366403
}
367404

368-
a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg);
405+
a5psw_port_learning_set(a5psw, port, learning_enabled);
406+
a5psw_port_rx_block_set(a5psw, port, !rx_enabled);
407+
a5psw_port_tx_enable(a5psw, port, tx_enabled);
369408
}
370409

371410
static void a5psw_port_fast_age(struct dsa_switch *ds, int port)

drivers/net/dsa/rzn1_a5psw.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define A5PSW_PORT_OFFSET(port) (0x400 * (port))
2020

2121
#define A5PSW_PORT_ENA 0x8
22+
#define A5PSW_PORT_ENA_TX(port) BIT(port)
2223
#define A5PSW_PORT_ENA_RX_SHIFT 16
2324
#define A5PSW_PORT_ENA_TX_RX(port) (BIT((port) + A5PSW_PORT_ENA_RX_SHIFT) | \
2425
BIT(port))

0 commit comments

Comments
 (0)