Skip to content

Commit 80f9ad0

Browse files
committed
Merge branch 'rzn1-a5psw-vlan-port_bridge_flags'
Alexis Lothoré says: ==================== net: dsa: rzn1-a5psw: add support for vlan and .port_bridge_flags this series enables vlan support in Renesas RZN1 internal ethernet switch, and is a follow up to the work initiated by Clement Leger a few months ago, who handed me over the topic. This new revision aims to iron the last few points raised by Vladimir to ensure that the driver is in line with switch drivers expectations, and is based on the lengthy discussion in [1] (thanks Vladimir for the valuable explanations) [1] https://lore.kernel.org/netdev/20230314163651.242259-1-clement.leger@bootlin.com/ ---- V5: - ensure that flooding can be enabled only if port belongs to a bridge - enable learning in a5psw_port_stp_state_set() only if port has learning enabled - toggle vlan tagging on vlan filtering in - removed reviewed-by on second patch since its modified RESEND V4: - Resent due to net-next being closed V4: - Fix missing CPU port bit in a5psw->bridged_ports - Use unsigned int for vlan_res_id parameters - Rename a5psw_get_vlan_res_entry() to a5psw_new_vlan_res_entry() - In a5psw_port_vlan_add(), return -ENOSPC when no VLAN entry is found - In a5psw_port_vlan_filtering(), compute "val" from "mask" V3: - Target net-next tree and correct version... V2: - Fixed a few formatting errors - Add .port_bridge_flags implementation ==================== Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
2 parents 61f98da + 7b3f77c commit 80f9ad0

File tree

2 files changed

+237
-11
lines changed

2 files changed

+237
-11
lines changed

drivers/net/dsa/rzn1_a5psw.c

Lines changed: 232 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,9 @@ static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port,
331331
A5PSW_MCAST_DEF_MASK};
332332
int i;
333333

334-
if (set)
335-
a5psw->bridged_ports |= BIT(port);
336-
else
337-
a5psw->bridged_ports &= ~BIT(port);
338-
339334
for (i = 0; i < ARRAY_SIZE(offsets); i++)
340-
a5psw_reg_writel(a5psw, offsets[i], a5psw->bridged_ports);
335+
a5psw_reg_rmw(a5psw, offsets[i], BIT(port),
336+
set ? BIT(port) : 0);
341337
}
342338

343339
static void a5psw_port_set_standalone(struct a5psw *a5psw, int port,
@@ -365,6 +361,8 @@ static int a5psw_port_bridge_join(struct dsa_switch *ds, int port,
365361
a5psw->br_dev = bridge.dev;
366362
a5psw_port_set_standalone(a5psw, port, false);
367363

364+
a5psw->bridged_ports |= BIT(port);
365+
368366
return 0;
369367
}
370368

@@ -373,16 +371,72 @@ static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port,
373371
{
374372
struct a5psw *a5psw = ds->priv;
375373

374+
a5psw->bridged_ports &= ~BIT(port);
375+
376376
a5psw_port_set_standalone(a5psw, port, true);
377377

378378
/* No more ports bridged */
379379
if (a5psw->bridged_ports == BIT(A5PSW_CPU_PORT))
380380
a5psw->br_dev = NULL;
381381
}
382382

383+
static int a5psw_port_pre_bridge_flags(struct dsa_switch *ds, int port,
384+
struct switchdev_brport_flags flags,
385+
struct netlink_ext_ack *extack)
386+
{
387+
if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
388+
BR_BCAST_FLOOD))
389+
return -EINVAL;
390+
391+
return 0;
392+
}
393+
394+
static int
395+
a5psw_port_bridge_flags(struct dsa_switch *ds, int port,
396+
struct switchdev_brport_flags flags,
397+
struct netlink_ext_ack *extack)
398+
{
399+
struct a5psw *a5psw = ds->priv;
400+
u32 val;
401+
402+
/* If a port is set as standalone, we do not want to be able to
403+
* configure flooding nor learning which would result in joining the
404+
* unique bridge. This can happen when a port leaves the bridge, in
405+
* which case the DSA core will try to "clear" all flags for the
406+
* standalone port (ie enable flooding, disable learning). In that case
407+
* do not fail but do not apply the flags.
408+
*/
409+
if (!(a5psw->bridged_ports & BIT(port)))
410+
return 0;
411+
412+
if (flags.mask & BR_LEARNING) {
413+
val = flags.val & BR_LEARNING ? 0 : A5PSW_INPUT_LEARN_DIS(port);
414+
a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN,
415+
A5PSW_INPUT_LEARN_DIS(port), val);
416+
}
417+
418+
if (flags.mask & BR_FLOOD) {
419+
val = flags.val & BR_FLOOD ? BIT(port) : 0;
420+
a5psw_reg_rmw(a5psw, A5PSW_UCAST_DEF_MASK, BIT(port), val);
421+
}
422+
423+
if (flags.mask & BR_MCAST_FLOOD) {
424+
val = flags.val & BR_MCAST_FLOOD ? BIT(port) : 0;
425+
a5psw_reg_rmw(a5psw, A5PSW_MCAST_DEF_MASK, BIT(port), val);
426+
}
427+
428+
if (flags.mask & BR_BCAST_FLOOD) {
429+
val = flags.val & BR_BCAST_FLOOD ? BIT(port) : 0;
430+
a5psw_reg_rmw(a5psw, A5PSW_BCAST_DEF_MASK, BIT(port), val);
431+
}
432+
433+
return 0;
434+
}
435+
383436
static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
384437
{
385438
bool learning_enabled, rx_enabled, tx_enabled;
439+
struct dsa_port *dp = dsa_to_port(ds, port);
386440
struct a5psw *a5psw = ds->priv;
387441

388442
switch (state) {
@@ -396,12 +450,12 @@ static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
396450
case BR_STATE_LEARNING:
397451
rx_enabled = false;
398452
tx_enabled = false;
399-
learning_enabled = true;
453+
learning_enabled = dp->learning;
400454
break;
401455
case BR_STATE_FORWARDING:
402456
rx_enabled = true;
403457
tx_enabled = true;
404-
learning_enabled = true;
458+
learning_enabled = dp->learning;
405459
break;
406460
default:
407461
dev_err(ds->dev, "invalid STP state: %d\n", state);
@@ -585,6 +639,146 @@ static int a5psw_port_fdb_dump(struct dsa_switch *ds, int port,
585639
return ret;
586640
}
587641

642+
static int a5psw_port_vlan_filtering(struct dsa_switch *ds, int port,
643+
bool vlan_filtering,
644+
struct netlink_ext_ack *extack)
645+
{
646+
u32 mask = BIT(port + A5PSW_VLAN_VERI_SHIFT) |
647+
BIT(port + A5PSW_VLAN_DISC_SHIFT);
648+
u32 val = vlan_filtering ? mask : 0;
649+
struct a5psw *a5psw = ds->priv;
650+
651+
/* Disable/enable vlan tagging */
652+
a5psw_reg_rmw(a5psw, A5PSW_VLAN_IN_MODE_ENA, BIT(port),
653+
vlan_filtering ? BIT(port) : 0);
654+
655+
/* Disable/enable vlan input filtering */
656+
a5psw_reg_rmw(a5psw, A5PSW_VLAN_VERIFY, mask, val);
657+
658+
return 0;
659+
}
660+
661+
static int a5psw_find_vlan_entry(struct a5psw *a5psw, u16 vid)
662+
{
663+
u32 vlan_res;
664+
int i;
665+
666+
/* Find vlan for this port */
667+
for (i = 0; i < A5PSW_VLAN_COUNT; i++) {
668+
vlan_res = a5psw_reg_readl(a5psw, A5PSW_VLAN_RES(i));
669+
if (FIELD_GET(A5PSW_VLAN_RES_VLANID, vlan_res) == vid)
670+
return i;
671+
}
672+
673+
return -1;
674+
}
675+
676+
static int a5psw_new_vlan_res_entry(struct a5psw *a5psw, u16 newvid)
677+
{
678+
u32 vlan_res;
679+
int i;
680+
681+
/* Find a free VLAN entry */
682+
for (i = 0; i < A5PSW_VLAN_COUNT; i++) {
683+
vlan_res = a5psw_reg_readl(a5psw, A5PSW_VLAN_RES(i));
684+
if (!(FIELD_GET(A5PSW_VLAN_RES_PORTMASK, vlan_res))) {
685+
vlan_res = FIELD_PREP(A5PSW_VLAN_RES_VLANID, newvid);
686+
a5psw_reg_writel(a5psw, A5PSW_VLAN_RES(i), vlan_res);
687+
return i;
688+
}
689+
}
690+
691+
return -1;
692+
}
693+
694+
static void a5psw_port_vlan_tagged_cfg(struct a5psw *a5psw,
695+
unsigned int vlan_res_id, int port,
696+
bool set)
697+
{
698+
u32 mask = A5PSW_VLAN_RES_WR_PORTMASK | A5PSW_VLAN_RES_RD_TAGMASK |
699+
BIT(port);
700+
u32 vlan_res_off = A5PSW_VLAN_RES(vlan_res_id);
701+
u32 val = A5PSW_VLAN_RES_WR_TAGMASK, reg;
702+
703+
if (set)
704+
val |= BIT(port);
705+
706+
/* Toggle tag mask read */
707+
a5psw_reg_writel(a5psw, vlan_res_off, A5PSW_VLAN_RES_RD_TAGMASK);
708+
reg = a5psw_reg_readl(a5psw, vlan_res_off);
709+
a5psw_reg_writel(a5psw, vlan_res_off, A5PSW_VLAN_RES_RD_TAGMASK);
710+
711+
reg &= ~mask;
712+
reg |= val;
713+
a5psw_reg_writel(a5psw, vlan_res_off, reg);
714+
}
715+
716+
static void a5psw_port_vlan_cfg(struct a5psw *a5psw, unsigned int vlan_res_id,
717+
int port, bool set)
718+
{
719+
u32 mask = A5PSW_VLAN_RES_WR_TAGMASK | BIT(port);
720+
u32 reg = A5PSW_VLAN_RES_WR_PORTMASK;
721+
722+
if (set)
723+
reg |= BIT(port);
724+
725+
a5psw_reg_rmw(a5psw, A5PSW_VLAN_RES(vlan_res_id), mask, reg);
726+
}
727+
728+
static int a5psw_port_vlan_add(struct dsa_switch *ds, int port,
729+
const struct switchdev_obj_port_vlan *vlan,
730+
struct netlink_ext_ack *extack)
731+
{
732+
bool tagged = !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
733+
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
734+
struct a5psw *a5psw = ds->priv;
735+
u16 vid = vlan->vid;
736+
int vlan_res_id;
737+
738+
dev_dbg(a5psw->dev, "Add VLAN %d on port %d, %s, %s\n",
739+
vid, port, tagged ? "tagged" : "untagged",
740+
pvid ? "PVID" : "no PVID");
741+
742+
vlan_res_id = a5psw_find_vlan_entry(a5psw, vid);
743+
if (vlan_res_id < 0) {
744+
vlan_res_id = a5psw_new_vlan_res_entry(a5psw, vid);
745+
if (vlan_res_id < 0)
746+
return -ENOSPC;
747+
}
748+
749+
a5psw_port_vlan_cfg(a5psw, vlan_res_id, port, true);
750+
if (tagged)
751+
a5psw_port_vlan_tagged_cfg(a5psw, vlan_res_id, port, true);
752+
753+
/* Configure port to tag with corresponding VID, but do not enable it
754+
* yet: wait for vlan filtering to be enabled to enable vlan port
755+
* tagging
756+
*/
757+
if (pvid)
758+
a5psw_reg_writel(a5psw, A5PSW_SYSTEM_TAGINFO(port), vid);
759+
760+
return 0;
761+
}
762+
763+
static int a5psw_port_vlan_del(struct dsa_switch *ds, int port,
764+
const struct switchdev_obj_port_vlan *vlan)
765+
{
766+
struct a5psw *a5psw = ds->priv;
767+
u16 vid = vlan->vid;
768+
int vlan_res_id;
769+
770+
dev_dbg(a5psw->dev, "Removing VLAN %d on port %d\n", vid, port);
771+
772+
vlan_res_id = a5psw_find_vlan_entry(a5psw, vid);
773+
if (vlan_res_id < 0)
774+
return -EINVAL;
775+
776+
a5psw_port_vlan_cfg(a5psw, vlan_res_id, port, false);
777+
a5psw_port_vlan_tagged_cfg(a5psw, vlan_res_id, port, false);
778+
779+
return 0;
780+
}
781+
588782
static u64 a5psw_read_stat(struct a5psw *a5psw, u32 offset, int port)
589783
{
590784
u32 reg_lo, reg_hi;
@@ -702,6 +896,27 @@ static void a5psw_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
702896
ctrl_stats->MACControlFramesReceived = stat;
703897
}
704898

899+
static void a5psw_vlan_setup(struct a5psw *a5psw, int port)
900+
{
901+
u32 reg;
902+
903+
/* Enable TAG always mode for the port, this is actually controlled
904+
* by VLAN_IN_MODE_ENA field which will be used for PVID insertion
905+
*/
906+
reg = A5PSW_VLAN_IN_MODE_TAG_ALWAYS;
907+
reg <<= A5PSW_VLAN_IN_MODE_PORT_SHIFT(port);
908+
a5psw_reg_rmw(a5psw, A5PSW_VLAN_IN_MODE, A5PSW_VLAN_IN_MODE_PORT(port),
909+
reg);
910+
911+
/* Set transparent mode for output frame manipulation, this will depend
912+
* on the VLAN_RES configuration mode
913+
*/
914+
reg = A5PSW_VLAN_OUT_MODE_TRANSPARENT;
915+
reg <<= A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port);
916+
a5psw_reg_rmw(a5psw, A5PSW_VLAN_OUT_MODE,
917+
A5PSW_VLAN_OUT_MODE_PORT(port), reg);
918+
}
919+
705920
static int a5psw_setup(struct dsa_switch *ds)
706921
{
707922
struct a5psw *a5psw = ds->priv;
@@ -776,6 +991,8 @@ static int a5psw_setup(struct dsa_switch *ds)
776991
/* Enable standalone mode for user ports */
777992
if (dsa_port_is_user(dp))
778993
a5psw_port_set_standalone(a5psw, port, true);
994+
995+
a5psw_vlan_setup(a5psw, port);
779996
}
780997

781998
return 0;
@@ -801,8 +1018,13 @@ static const struct dsa_switch_ops a5psw_switch_ops = {
8011018
.set_ageing_time = a5psw_set_ageing_time,
8021019
.port_bridge_join = a5psw_port_bridge_join,
8031020
.port_bridge_leave = a5psw_port_bridge_leave,
1021+
.port_pre_bridge_flags = a5psw_port_pre_bridge_flags,
1022+
.port_bridge_flags = a5psw_port_bridge_flags,
8041023
.port_stp_state_set = a5psw_port_stp_state_set,
8051024
.port_fast_age = a5psw_port_fast_age,
1025+
.port_vlan_filtering = a5psw_port_vlan_filtering,
1026+
.port_vlan_add = a5psw_port_vlan_add,
1027+
.port_vlan_del = a5psw_port_vlan_del,
8061028
.port_fdb_add = a5psw_port_fdb_add,
8071029
.port_fdb_del = a5psw_port_fdb_del,
8081030
.port_fdb_dump = a5psw_port_fdb_dump,
@@ -992,6 +1214,8 @@ static int a5psw_probe(struct platform_device *pdev)
9921214
if (IS_ERR(a5psw->base))
9931215
return PTR_ERR(a5psw->base);
9941216

1217+
a5psw->bridged_ports = BIT(A5PSW_CPU_PORT);
1218+
9951219
ret = a5psw_pcs_get(a5psw);
9961220
if (ret)
9971221
return ret;

drivers/net/dsa/rzn1_a5psw.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@
5151
#define A5PSW_VLAN_IN_MODE_TAG_ALWAYS 0x2
5252

5353
#define A5PSW_VLAN_OUT_MODE 0x2C
54-
#define A5PSW_VLAN_OUT_MODE_PORT(port) (GENMASK(1, 0) << ((port) * 2))
54+
#define A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port) ((port) * 2)
55+
#define A5PSW_VLAN_OUT_MODE_PORT(port) (GENMASK(1, 0) << \
56+
A5PSW_VLAN_OUT_MODE_PORT_SHIFT(port))
5557
#define A5PSW_VLAN_OUT_MODE_DIS 0x0
5658
#define A5PSW_VLAN_OUT_MODE_STRIP 0x1
5759
#define A5PSW_VLAN_OUT_MODE_TAG_THROUGH 0x2
@@ -60,7 +62,7 @@
6062
#define A5PSW_VLAN_IN_MODE_ENA 0x30
6163
#define A5PSW_VLAN_TAG_ID 0x34
6264

63-
#define A5PSW_SYSTEM_TAGINFO(port) (0x200 + A5PSW_PORT_OFFSET(port))
65+
#define A5PSW_SYSTEM_TAGINFO(port) (0x200 + 4 * (port))
6466

6567
#define A5PSW_AUTH_PORT(port) (0x240 + 4 * (port))
6668
#define A5PSW_AUTH_PORT_AUTHORIZED BIT(0)
@@ -69,7 +71,7 @@
6971
#define A5PSW_VLAN_RES_WR_PORTMASK BIT(30)
7072
#define A5PSW_VLAN_RES_WR_TAGMASK BIT(29)
7173
#define A5PSW_VLAN_RES_RD_TAGMASK BIT(28)
72-
#define A5PSW_VLAN_RES_ID GENMASK(16, 5)
74+
#define A5PSW_VLAN_RES_VLANID GENMASK(16, 5)
7375
#define A5PSW_VLAN_RES_PORTMASK GENMASK(4, 0)
7476

7577
#define A5PSW_RXMATCH_CONFIG(port) (0x3e80 + 4 * (port))

0 commit comments

Comments
 (0)