[U-Boot] [PATCH 0/5] vsc9953: fixes, cleanups and LAG support

The first two patches of this set fix an issue with the register map definition and with the aging time of the L2 switch. The next two paches fix some typos and update the documentation for VSC9952. The last patch adds LAG support.
I made a set with all these patches mainly to assure that they are applied in the right order and no merge conflicts appear.
Codrin Ciubotariu (5): drivers: net: vsc9953: Fix number of reserved registers drivers: net: vsc9953: Fix FDB aging time doc: t1040-l2switch: Update README common: cmd_ethsw: Spelling fixes drivers: net: vsc9953: Add LAG support
common/cmd_ethsw.c | 78 +++++++++- doc/README.t1040-l2switch | 29 +++- drivers/net/vsc9953.c | 354 +++++++++++++++++++++++++++++++++++++++++++++- include/ethsw.h | 6 + include/vsc9953.h | 24 +++- 5 files changed, 480 insertions(+), 11 deletions(-)

There are only 21 registers reserved between ana_ana and ana_pgid register groups.
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com --- include/vsc9953.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/vsc9953.h b/include/vsc9953.h index cd5cfc7..00aa222 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -269,7 +269,7 @@ struct vsc9953_analyzer { struct vsc9953_ana_ana_tables ana_tables; u32 reserved2[14]; struct vsc9953_ana_ana ana; - u32 reserved3[22]; + u32 reserved3[21]; struct vsc9953_ana_pgid port_id_tbl; u32 reserved4[549]; struct vsc9953_ana_pfc pfc[10];

On Tue, Dec 15, 2015 at 7:21 AM, Codrin Ciubotariu codrin.ciubotariu@freescale.com wrote:
There are only 21 registers reserved between ana_ana and ana_pgid register groups.
Oops!
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com
Acked-by: Joe Hershberger joe.hershberger@ni.com



By default, the aging period is set to 0, so the dynamic FDB entries are never removed. This patch sets the aging time to 300 seconds.
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com --- drivers/net/vsc9953.c | 23 +++++++++++++++++++++++ include/vsc9953.h | 4 ++++ 2 files changed, 27 insertions(+)
diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 7595db1..160f478 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -593,6 +593,25 @@ static void vsc9953_port_all_vlan_egress_untagged_set( vsc9953_port_vlan_egr_untag_set(i, mode); }
+static int vsc9953_autoage_time_set(int age_period) +{ + u32 autoage; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + if (age_period < 0 || age_period > VSC9953_AUTOAGE_PERIOD_MASK) + return -EINVAL; + + autoage = bitfield_replace_by_mask(in_le32(&l2ana_reg->ana.auto_age), + VSC9953_AUTOAGE_PERIOD_MASK, + age_period); + out_le32(&l2ana_reg->ana.auto_age, autoage); + + return 0; +} + #ifdef CONFIG_CMD_ETHSW
/* Enable/disable status of a VSC9953 port */ @@ -2107,6 +2126,10 @@ void vsc9953_default_configuration(void) { int i;
+ if (vsc9953_autoage_time_set(VSC9953_DEFAULT_AGE_TIME)) + debug("VSC9953: failed to set AGE time to %d\n", + VSC9953_DEFAULT_AGE_TIME); + for (i = 0; i < VSC9953_MAX_VLAN; i++) vsc9953_vlan_table_membership_all_set(i, 0); vsc9953_port_all_vlan_aware_set(1); diff --git a/include/vsc9953.h b/include/vsc9953.h index 00aa222..35fcbb5 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -136,6 +136,9 @@ /* Macros for vsc9953_ana_ana.adv_learn register */ #define VSC9953_VLAN_CHK 0x00000400
+/* Macros for vsc9953_ana_ana.auto_age register */ +#define VSC9953_AUTOAGE_PERIOD_MASK 0x001ffffe + /* Macros for vsc9953_rew_port.port_tag_cfg register */ #define VSC9953_TAG_CFG_MASK 0x00000180 #define VSC9953_TAG_CFG_NONE 0x00000000 @@ -164,6 +167,7 @@ #define VSC9953_MAX_VLAN 4096 #define VSC9953_VLAN_CHECK(vid) \ (((vid) < 0 || (vid) >= VSC9953_MAX_VLAN) ? 0 : 1) +#define VSC9953_DEFAULT_AGE_TIME 300
#define DEFAULT_VSC9953_MDIO_NAME "VSC9953_MDIO0"

On Tue, Dec 15, 2015 at 7:21 AM, Codrin Ciubotariu codrin.ciubotariu@freescale.com wrote:
By default, the aging period is set to 0, so the dynamic FDB entries are never removed. This patch sets the aging time to 300 seconds.
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com
Acked-by: Joe Hershberger joe.hershberger@ni.com



The driver for VSC9953 L2 switch IP supports many features and the documentation needs to be updated.
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com --- doc/README.t1040-l2switch | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/doc/README.t1040-l2switch b/doc/README.t1040-l2switch index 14dbf31..4e0f418 100644 --- a/doc/README.t1040-l2switch +++ b/doc/README.t1040-l2switch @@ -24,16 +24,29 @@ Switch interfaces: Commands Overview: ============= Commands supported - - enable/disable a port - - check a port's link speed, duplexity and status. + - enable/disable a port or show its configuration (speed, duplexity, status, etc.) + - port statistics + - MAC learning + - add/remove FDB entries + - Port-based VLAN + - Private/Shared VLAN learning + - VLAN ingress filtering
Commands syntax - ethsw port <port_nr> enable|disable - enable/disable an l2 switch port - ethsw port <port_nr> show - show an l2 switch port's configuration +ethsw [port <port_no>] { enable | disable | show } - enable/disable a port; show a port's configuration +ethsw [port <port_no>] statistics { [help] | [clear] } - show an l2 switch port's statistics +ethsw [port <port_no>] learning { [help] | show | auto | disable } - enable/disable/show learning configuration on a port +ethsw [port <port_no>] [vlan <vid>] fdb { [help] | show | flush | { add | del } <mac> } - add/delete a mac entry in FDB; use show to see FDB entries; + if [vlan <vid>] is missing, VID 1 will be used +ethsw [port <port_no>] pvid { [help] | show | <pvid> } - set/show PVID (ingress and egress VLAN tagging) for a port +ethsw [port <port_no>] vlan { [help] | show | add <vid> | del <vid> } - add a VLAN to a port (VLAN members) +ethsw [port <port_no>] untagged { [help] | show | all | none | pvid } - set egress tagging mode for a port +ethsw [port <port_no>] egress tag { [help] | show | pvid | classified } - configure VID source for egress tag. + Tag's VID could be the frame's classified VID or the PVID of the port +ethsw vlan fdb { [help] | show | shared | private } - make VLAN learning shared or private +ethsw [port <port_no>] ingress filtering { [help] | show | enable | disable } - enable/disable VLAN ingress filtering on port
- port_nr=0..9; use "all" for all ports - -=> ethsw port all show +=> ethsw show Port Status Link Speed Duplex 0 enabled down 10 half 1 enabled down 10 half

On Tue, Dec 15, 2015 at 7:21 AM, Codrin Ciubotariu codrin.ciubotariu@freescale.com wrote:
The driver for VSC9953 L2 switch IP supports many features and the documentation needs to be updated.
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com
Acked-by: Joe Hershberger joe.hershberger@ni.com



Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com --- common/cmd_ethsw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c index 8e452e9..a5ed555 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -71,7 +71,7 @@ static int ethsw_vlan_help_key_func(struct ethsw_command_def *parsed_cmd)
#define ETHSW_PORT_UNTAG_HELP "ethsw [port <port_no>] untagged " \ "{ [help] | show | all | none | pvid } " \ -" - set egress tagging mod for a port" +" - set egress tagging mode for a port"
static int ethsw_port_untag_help_key_func(struct ethsw_command_def *parsed_cmd) { @@ -1010,7 +1010,7 @@ static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
#define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \ -"- enable/disable a port; show shows a port's configuration" +"- enable/disable a port; show a port's configuration"
U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, "Ethernet l2 switch commands",

Hi Codrin,
On Tue, Dec 15, 2015 at 7:21 AM, Codrin Ciubotariu codrin.ciubotariu@freescale.com wrote:
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com
Acked-by: Joe Hershberger joe.hershberger@ni.com


You can now configure LAG on VSC9953's ports using the command: ethsw [port <port_no>] aggr {[help] | show | <lag_group_no>}
A port must belong to a single LAG. By default, a port belongs to a LAG equal to the port's number.
For each frame, a hash will be calculated based on Source/Destination MAC addresses, Source/Destination IP(v4/v6) addresses, Source/Destination ports. This hash will be used to select a single egress port from LAG. This also assures that frames from the same flow will always have the same egress port.
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com --- common/cmd_ethsw.c | 74 +++++++++++ doc/README.t1040-l2switch | 2 + drivers/net/vsc9953.c | 331 +++++++++++++++++++++++++++++++++++++++++++++- include/ethsw.h | 6 + include/vsc9953.h | 18 +++ 5 files changed, 430 insertions(+), 1 deletion(-)
diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c index a5ed555..491cb8e 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -114,6 +114,17 @@ static int ethsw_ingr_fltr_help_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; }
+#define ETHSW_PORT_AGGR_HELP "ethsw [port <port_no>] aggr" \ +" { [help] | show | <lag_group_no> } " \ +"- get/set LAG group for a port" + +static int ethsw_port_aggr_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PORT_AGGR_HELP"\n"); + + return CMD_RET_SUCCESS; +} + static struct keywords_to_function { enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS]; int cmd_func_offset; @@ -532,6 +543,39 @@ static struct keywords_to_function { .cmd_func_offset = offsetof(struct ethsw_command_func, port_ingr_filt_set), .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_aggr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_aggr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_aggr_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_aggr_no, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_aggr_set), + .keyword_function = NULL, }, };
@@ -576,6 +620,9 @@ static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc, static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc, char *const argv[], int *argc_nr, struct ethsw_command_def *parsed_cmd); +static int keyword_match_aggr(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd);
/* * Define properties for each keyword; @@ -661,6 +708,9 @@ struct keyword_def { }, { .keyword_name = "filtering", .match = &keyword_match_gen, + }, { + .keyword_name = "aggr", + .match = &keyword_match_aggr, }, };
@@ -826,6 +876,28 @@ static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc, return 1; }
+/* Function used to match the command's aggregation number */ +static int keyword_match_aggr(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + unsigned long val; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 1; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + parsed_cmd->aggr_grp = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_aggr_no; + } + + return 1; +} + /* Finds optional keywords and modifies *argc_va to skip them */ static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd, int *argc_val) @@ -984,6 +1056,7 @@ static void command_def_init(struct ethsw_command_def *parsed_cmd)
parsed_cmd->port = ETHSW_CMD_PORT_ALL; parsed_cmd->vid = ETHSW_CMD_VLAN_ALL; + parsed_cmd->aggr_grp = ETHSW_CMD_AGGR_GRP_NONE; parsed_cmd->cmd_function = NULL;
/* We initialize the MAC address with the Broadcast address */ @@ -1024,4 +1097,5 @@ U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, ETHSW_EGR_VLAN_TAG_HELP"\n" ETHSW_VLAN_FDB_HELP"\n" ETHSW_PORT_INGR_FLTR_HELP"\n" + ETHSW_PORT_AGGR_HELP"\n" ); diff --git a/doc/README.t1040-l2switch b/doc/README.t1040-l2switch index 4e0f418..6f03de2 100644 --- a/doc/README.t1040-l2switch +++ b/doc/README.t1040-l2switch @@ -31,6 +31,7 @@ Commands supported - Port-based VLAN - Private/Shared VLAN learning - VLAN ingress filtering + - Port LAG
Commands syntax ethsw [port <port_no>] { enable | disable | show } - enable/disable a port; show a port's configuration @@ -45,6 +46,7 @@ ethsw [port <port_no>] egress tag { [help] | show | pvid | classified } - config Tag's VID could be the frame's classified VID or the PVID of the port ethsw vlan fdb { [help] | show | shared | private } - make VLAN learning shared or private ethsw [port <port_no>] ingress filtering { [help] | show | enable | disable } - enable/disable VLAN ingress filtering on port +ethsw [port <port_no>] aggr { [help] | show | <lag_group_no> } - get/set LAG group for a port
=> ethsw show Port Status Link Speed Duplex diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 160f478..44afe14 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -469,6 +469,47 @@ static void vsc9953_vlan_ingr_fltr_learn_drop(int enable) clrbits_le32(&l2ana_reg->ana.adv_learn, VSC9953_VLAN_CHK); }
+enum aggr_code_mode { + AGGR_CODE_RAND = 0, + AGGR_CODE_ALL, /* S/D MAC, IPv4 S/D IP, IPv6 Flow Label, S/D PORT */ +}; + +/* Set aggregation code generation mode */ +static int vsc9953_aggr_code_set(enum aggr_code_mode ac) +{ + int rc; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + switch (ac) { + case AGGR_CODE_RAND: + clrsetbits_le32(&l2ana_reg->common.aggr_cfg, + VSC9953_AC_DMAC_ENA | VSC9953_AC_SMAC_ENA | + VSC9953_AC_IP6_LBL_ENA | + VSC9953_AC_IP6_TCPUDP_ENA | + VSC9953_AC_IP4_SIPDIP_ENA | + VSC9953_AC_IP4_TCPUDP_ENA, VSC9953_AC_RND_ENA); + rc = 0; + break; + case AGGR_CODE_ALL: + clrsetbits_le32(&l2ana_reg->common.aggr_cfg, VSC9953_AC_RND_ENA, + VSC9953_AC_DMAC_ENA | VSC9953_AC_SMAC_ENA | + VSC9953_AC_IP6_LBL_ENA | + VSC9953_AC_IP6_TCPUDP_ENA | + VSC9953_AC_IP4_SIPDIP_ENA | + VSC9953_AC_IP4_TCPUDP_ENA); + rc = 0; + break; + default: + /* unknown mode for aggregation code */ + rc = -EINVAL; + } + + return rc; +} + /* Egress untag modes of a VSC9953 port */ enum egress_untag_mode { EGRESS_UNTAG_ALL = 0, @@ -1493,6 +1534,224 @@ static int vsc9953_port_ingress_filtering_get(int port_no) return !!(val & (1 << port_no)); }
+/* Get the aggregation group of a port */ +static int vsc9953_port_aggr_grp_get(int port_no, int *aggr_grp) +{ + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + if (!VSC9953_PORT_CHECK(port_no)) + return -EINVAL; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + val = in_le32(&l2ana_reg->port[port_no].port_cfg); + *aggr_grp = bitfield_extract_by_mask(val, + VSC9953_PORT_CFG_PORTID_MASK); + + return 0; +} + +static void vsc9953_aggr_grp_members_get(int aggr_grp, + u8 aggr_membr[VSC9953_MAX_PORTS]) +{ + int port_no; + int aggr_membr_grp; + + for (port_no = 0; port_no < VSC9953_MAX_PORTS; port_no++) { + aggr_membr[port_no] = 0; + + if (vsc9953_port_aggr_grp_get(port_no, &aggr_membr_grp)) + continue; + + if (aggr_grp == aggr_membr_grp) + aggr_membr[port_no] = 1; + } +} + +static void vsc9953_update_dest_members_masks(int port_no, u32 membr_bitfld_old, + u32 membr_bitfld_new) +{ + int i; + u32 pgid; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* + * NOTE: Only the unicast destination masks are updated, since + * we do not support for now Layer-2 multicast entries + */ + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (i == port_no) { + clrsetbits_le32(&l2ana_reg->port_id_tbl.port_grp_id[i], + VSC9953_PGID_PORT_MASK, + membr_bitfld_new); + continue; + } + + pgid = in_le32(&l2ana_reg->port_id_tbl.port_grp_id[i]); + if ((u32)(1 << i) & membr_bitfld_old & VSC9953_PGID_PORT_MASK) + pgid &= ~((u32)(1 << port_no)); + if ((u32)(1 << i) & membr_bitfld_new & VSC9953_PGID_PORT_MASK) + pgid |= ((u32)(1 << port_no)); + + out_le32(&l2ana_reg->port_id_tbl.port_grp_id[i], pgid); + } +} + +static void vsc9953_update_source_members_masks(int port_no, + u32 membr_bitfld_old, + u32 membr_bitfld_new) +{ + int i; + int index; + u32 pgid; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + for (i = 0; i < VSC9953_MAX_PORTS + 1; i++) { + index = PGID_SRC_START + i; + pgid = in_le32(&l2ana_reg->port_id_tbl.port_grp_id[index]); + if (i == port_no) { + pgid = (pgid | VSC9953_PGID_PORT_MASK) & + ~membr_bitfld_new; + out_le32(&l2ana_reg->port_id_tbl.port_grp_id[index], + pgid); + continue; + } + + if ((u32)(1 << i) & membr_bitfld_old & VSC9953_PGID_PORT_MASK) + pgid |= (u32)(1 << port_no); + + if ((u32)(1 << i) & membr_bitfld_new & VSC9953_PGID_PORT_MASK) + pgid &= ~(u32)(1 << port_no); + out_le32(&l2ana_reg->port_id_tbl.port_grp_id[index], pgid); + } +} + +static u32 vsc9953_aggr_mask_get_next(u32 aggr_mask, u32 member_bitfield) +{ + if (!member_bitfield) + return 0; + + if (!(aggr_mask & VSC9953_PGID_PORT_MASK)) + aggr_mask = 1; + else + aggr_mask <<= 1; + + while (!(aggr_mask & member_bitfield)) { + aggr_mask <<= 1; + if (!(aggr_mask & VSC9953_PGID_PORT_MASK)) + aggr_mask = 1; + } + + return aggr_mask; +} + +static void vsc9953_update_aggr_members_masks(int port_no, u32 membr_bitfld_old, + u32 membr_bitfld_new) +{ + int i; + u32 pgid; + u32 aggr_mask_old = 0; + u32 aggr_mask_new = 0; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* Update all the PGID aggregation masks */ + for (i = PGID_AGGR_START; i < PGID_SRC_START; i++) { + pgid = in_le32(&l2ana_reg->port_id_tbl.port_grp_id[i]); + + aggr_mask_old = vsc9953_aggr_mask_get_next(aggr_mask_old, + membr_bitfld_old); + pgid = (pgid & ~membr_bitfld_old) | aggr_mask_old; + + aggr_mask_new = vsc9953_aggr_mask_get_next(aggr_mask_new, + membr_bitfld_new); + pgid = (pgid & ~membr_bitfld_new) | aggr_mask_new; + + out_le32(&l2ana_reg->port_id_tbl.port_grp_id[i], pgid); + } +} + +static u32 vsc9953_aggr_membr_bitfield_get(u8 member[VSC9953_MAX_PORTS]) +{ + int i; + u32 member_bitfield = 0; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (member[i]) + member_bitfield |= 1 << i; + } + member_bitfield &= VSC9953_PGID_PORT_MASK; + + return member_bitfield; +} + +static void vsc9953_update_members_masks(int port_no, + u8 member_old[VSC9953_MAX_PORTS], + u8 member_new[VSC9953_MAX_PORTS]) +{ + u32 membr_bitfld_old = vsc9953_aggr_membr_bitfield_get(member_old); + u32 membr_bitfld_new = vsc9953_aggr_membr_bitfield_get(member_new); + + vsc9953_update_dest_members_masks(port_no, membr_bitfld_old, + membr_bitfld_new); + vsc9953_update_source_members_masks(port_no, membr_bitfld_old, + membr_bitfld_new); + vsc9953_update_aggr_members_masks(port_no, membr_bitfld_old, + membr_bitfld_new); +} + +/* Set the aggregation group of a port */ +static int vsc9953_port_aggr_grp_set(int port_no, int aggr_grp) +{ + u8 aggr_membr_old[VSC9953_MAX_PORTS]; + u8 aggr_membr_new[VSC9953_MAX_PORTS]; + int rc; + int aggr_grp_old; + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + if (!VSC9953_PORT_CHECK(port_no) || !VSC9953_PORT_CHECK(aggr_grp)) + return -EINVAL; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + rc = vsc9953_port_aggr_grp_get(port_no, &aggr_grp_old); + if (rc) + return rc; + + /* get all the members of the old aggregation group */ + vsc9953_aggr_grp_members_get(aggr_grp_old, aggr_membr_old); + + /* get all the members of the same aggregation group */ + vsc9953_aggr_grp_members_get(aggr_grp, aggr_membr_new); + + /* add current port as member to the new aggregation group */ + aggr_membr_old[port_no] = 0; + aggr_membr_new[port_no] = 1; + + /* update masks */ + vsc9953_update_members_masks(port_no, aggr_membr_old, aggr_membr_new); + + /* Change logical port number */ + val = in_le32(&l2ana_reg->port[port_no].port_cfg); + val = bitfield_replace_by_mask(val, + VSC9953_PORT_CFG_PORTID_MASK, aggr_grp); + out_le32(&l2ana_reg->port[port_no].port_cfg, val); + + return 0; +} + static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd) { int i; @@ -2083,6 +2342,72 @@ static int vsc9953_ingr_fltr_set_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; }
+static int vsc9953_port_aggr_show_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + int aggr_grp; + + if (parsed_cmd->port != ETHSW_CMD_PORT_ALL) { + if (!VSC9953_PORT_CHECK(parsed_cmd->port)) { + printf("Invalid port number: %d\n", parsed_cmd->port); + return CMD_RET_FAILURE; + } + + if (vsc9953_port_aggr_grp_get(parsed_cmd->port, &aggr_grp)) + return CMD_RET_FAILURE; + printf("%7s %10s\n", "Port", "Aggr grp"); + printf("%7d %10d\n", parsed_cmd->port, aggr_grp); + } else { + printf("%7s %10s\n", "Port", "Aggr grp"); + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (vsc9953_port_aggr_grp_get(i, &aggr_grp)) + continue; + printf("%7d %10d\n", i, aggr_grp); + } + } + + return CMD_RET_SUCCESS; +} + +static int vsc9953_port_aggr_set_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + + /* Aggregation group number should be set in parsed_cmd->aggr_grp */ + if (parsed_cmd->aggr_grp == ETHSW_CMD_AGGR_GRP_NONE) { + printf("Please set an aggregation group value\n"); + return CMD_RET_FAILURE; + } + + if (!VSC9953_PORT_CHECK(parsed_cmd->aggr_grp)) { + printf("Invalid aggregation group number: %d\n", + parsed_cmd->aggr_grp); + return CMD_RET_FAILURE; + } + + if (parsed_cmd->port != ETHSW_CMD_PORT_ALL) { + if (!VSC9953_PORT_CHECK(parsed_cmd->port)) { + printf("Invalid port number: %d\n", parsed_cmd->port); + return CMD_RET_FAILURE; + } + if (vsc9953_port_aggr_grp_set(parsed_cmd->port, + parsed_cmd->aggr_grp)) { + printf("Port %d: failed to set aggr group %d\n", + parsed_cmd->port, parsed_cmd->aggr_grp); + } + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (vsc9953_port_aggr_grp_set(i, + parsed_cmd->aggr_grp)) { + printf("Port %d: failed to set aggr group %d\n", + i, parsed_cmd->aggr_grp); + } + } + } + + return CMD_RET_SUCCESS; +} + static struct ethsw_command_func vsc9953_cmd_func = { .ethsw_name = "L2 Switch VSC9953", .port_enable = &vsc9953_port_status_key_func, @@ -2107,7 +2432,9 @@ static struct ethsw_command_func vsc9953_cmd_func = { .vlan_learn_show = &vsc9953_vlan_learn_show_key_func, .vlan_learn_set = &vsc9953_vlan_learn_set_key_func, .port_ingr_filt_show = &vsc9953_ingr_fltr_show_key_func, - .port_ingr_filt_set = &vsc9953_ingr_fltr_set_key_func + .port_ingr_filt_set = &vsc9953_ingr_fltr_set_key_func, + .port_aggr_show = &vsc9953_port_aggr_show_key_func, + .port_aggr_set = &vsc9953_port_aggr_set_key_func, };
#endif /* CONFIG_CMD_ETHSW */ @@ -2138,6 +2465,8 @@ void vsc9953_default_configuration(void) vsc9953_vlan_table_membership_all_set(1, 1); vsc9953_vlan_ingr_fltr_learn_drop(1); vsc9953_port_all_vlan_egress_untagged_set(EGRESS_UNTAG_PVID_AND_ZERO); + if (vsc9953_aggr_code_set(AGGR_CODE_ALL)) + debug("VSC9953: failed to set default aggregation code mode\n"); }
void vsc9953_init(bd_t *bis) diff --git a/include/ethsw.h b/include/ethsw.h index 2d3c12a..25f358d 100644 --- a/include/ethsw.h +++ b/include/ethsw.h @@ -12,6 +12,7 @@ #define ETHSW_MAX_CMD_PARAMS 20 #define ETHSW_CMD_PORT_ALL -1 #define ETHSW_CMD_VLAN_ALL -1 +#define ETHSW_CMD_AGGR_GRP_NONE -1
/* IDs used to track keywords in a command */ enum ethsw_keyword_id { @@ -41,6 +42,7 @@ enum ethsw_keyword_id { ethsw_id_private, ethsw_id_ingress, ethsw_id_filtering, + ethsw_id_aggr, ethsw_id_count, /* keep last */ };
@@ -50,6 +52,7 @@ enum ethsw_keyword_opt_id { ethsw_id_pvid_no, ethsw_id_add_del_no, ethsw_id_add_del_mac, + ethsw_id_aggr_no, ethsw_id_count_all, /* keep last */ };
@@ -58,6 +61,7 @@ struct ethsw_command_def { int cmd_keywords_nr; int port; int vid; + int aggr_grp; uchar ethaddr[6]; int (*cmd_function)(struct ethsw_command_def *parsed_cmd); }; @@ -88,6 +92,8 @@ struct ethsw_command_func { int (*vlan_learn_set)(struct ethsw_command_def *parsed_cmd); int (*port_ingr_filt_show)(struct ethsw_command_def *parsed_cmd); int (*port_ingr_filt_set)(struct ethsw_command_def *parsed_cmd); + int (*port_aggr_show)(struct ethsw_command_def *parsed_cmd); + int (*port_aggr_set)(struct ethsw_command_def *parsed_cmd); };
int ethsw_define_functions(const struct ethsw_command_func *cmd_func); diff --git a/include/vsc9953.h b/include/vsc9953.h index 35fcbb5..a2d4554 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -126,6 +126,7 @@ #define VSC9953_PORT_CFG_LEARN_AUTO 0x00000100 #define VSC9953_PORT_CFG_LEARN_CPU 0x00000200 #define VSC9953_PORT_CFG_LEARN_DROP 0x00000400 +#define VSC9953_PORT_CFG_PORTID_MASK 0x0000003c
/* Macros for vsc9953_qsys_sys.switch_port_mode register */ #define VSC9953_PORT_ENA 0x00002000 @@ -156,6 +157,19 @@ /* Macros for vsc9953_ana_ana_tables.mach_data register */ #define VSC9953_MACHDATA_VID_MASK 0x1fff0000
+/* Macros for vsc9953_ana_common.aggr_cfg register */ +#define VSC9953_AC_RND_ENA 0x00000080 +#define VSC9953_AC_DMAC_ENA 0x00000040 +#define VSC9953_AC_SMAC_ENA 0x00000020 +#define VSC9953_AC_IP6_LBL_ENA 0x00000010 +#define VSC9953_AC_IP6_TCPUDP_ENA 0x00000008 +#define VSC9953_AC_IP4_SIPDIP_ENA 0x00000004 +#define VSC9953_AC_IP4_TCPUDP_ENA 0x00000002 +#define VSC9953_AC_MASK 0x000000fe + +/* Macros for vsc9953_ana_pgid.port_grp_id[] registers */ +#define VSC9953_PGID_PORT_MASK 0x000003ff + #define VSC9953_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1) @@ -239,6 +253,10 @@ struct vsc9953_ana_ana { u32 port_mode[12]; };
+#define PGID_DST_START 0 +#define PGID_AGGR_START 64 +#define PGID_SRC_START 80 + struct vsc9953_ana_pgid { u32 port_grp_id[91]; };

On Tue, Dec 15, 2015 at 7:21 AM, Codrin Ciubotariu codrin.ciubotariu@freescale.com wrote:
You can now configure LAG on VSC9953's ports using the command: ethsw [port <port_no>] aggr {[help] | show | <lag_group_no>}
A port must belong to a single LAG. By default, a port belongs to a LAG equal to the port's number.
For each frame, a hash will be calculated based on Source/Destination MAC addresses, Source/Destination IP(v4/v6) addresses, Source/Destination ports. This hash will be used to select a single egress port from LAG. This also assures that frames from the same flow will always have the same egress port.
Signed-off-by: Codrin Ciubotariu codrin.ciubotariu@freescale.com
Acked-by: Joe Hershberger joe.hershberger@ni.com

participants (3)
-
Codrin Ciubotariu
-
Joe Hershberger
-
Joe Hershberger