diff mbox

[U-Boot,v3,13/16] drivers/net/vsc9953: Add VLAN commands for VSC9953

Message ID 1437746136-14379-10-git-send-email-codrin.ciubotariu@freescale.com
State Accepted
Delegated to: York Sun
Headers show

Commit Message

Codrin Ciubotariu July 24, 2015, 1:55 p.m. UTC
The new added commands can be used to configure VLANs for a port
on both ingress and egress.

The new commands are:
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 mod 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
These commands have also been added to the ethsw generic parser from
common/cmd_ethsw.c

Signed-off-by: Johnson Leung <johnson.leung@freescale.com>
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@freescale.com>
---

Changes for v2:
        - removed Change-id field;

Changes for v3:
        - replaced values returned by functions called by the parser with CMD_RET_* macros;
        - removed "CONFIG_" from macros added in vsc9953.h;
        - each variabled is declared on a separate line, with one space instead of tab(s);
	- removed unecessary brackets;
	- typo fix in comment: "Shiw";

 common/cmd_ethsw.c    | 270 ++++++++++++++++++++++++++++
 drivers/net/vsc9953.c | 478 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/ethsw.h       |  16 ++
 include/vsc9953.h     |   3 +
 4 files changed, 767 insertions(+)

Comments

Joe Hershberger Aug. 7, 2015, 8:18 p.m. UTC | #1
Hi Codrin,

On Fri, Jul 24, 2015 at 8:55 AM, Codrin Ciubotariu
<codrin.ciubotariu@freescale.com> wrote:
> The new added commands can be used to configure VLANs for a port
> on both ingress and egress.
>
> The new commands are:
> 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 mod 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
> These commands have also been added to the ethsw generic parser from
> common/cmd_ethsw.c
>
> Signed-off-by: Johnson Leung <johnson.leung@freescale.com>
> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@freescale.com>
> ---
>
> Changes for v2:
>         - removed Change-id field;
>
> Changes for v3:
>         - replaced values returned by functions called by the parser with CMD_RET_* macros;
>         - removed "CONFIG_" from macros added in vsc9953.h;
>         - each variabled is declared on a separate line, with one space instead of tab(s);
>         - removed unecessary brackets;
>         - typo fix in comment: "Shiw";
>
>  common/cmd_ethsw.c    | 270 ++++++++++++++++++++++++++++
>  drivers/net/vsc9953.c | 478 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/ethsw.h       |  16 ++
>  include/vsc9953.h     |   3 +
>  4 files changed, 767 insertions(+)

Acked-by: Joe Hershberger <joe.hershberger@ni.com>
diff mbox

Patch

diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c
index 0bf852b..8ba202b 100644
--- a/common/cmd_ethsw.c
+++ b/common/cmd_ethsw.c
@@ -46,6 +46,51 @@  static int ethsw_fdb_help_key_func(struct ethsw_command_def *parsed_cmd)
 	return CMD_RET_SUCCESS;
 }
 
+#define ETHSW_PVID_HELP "ethsw [port <port_no>] " \
+"pvid { [help] | show | <pvid> } " \
+"- set/show PVID (ingress and egress VLAN tagging) for a port"
+
+static int ethsw_pvid_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	printf(ETHSW_PVID_HELP"\n");
+
+	return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_VLAN_HELP "ethsw [port <port_no>] vlan " \
+"{ [help] | show | add <vid> | del <vid> } " \
+"- add a VLAN to a port (VLAN members)"
+
+static int ethsw_vlan_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	printf(ETHSW_VLAN_HELP"\n");
+
+	return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_PORT_UNTAG_HELP "ethsw [port <port_no>] untagged " \
+"{ [help] | show | all | none | pvid } " \
+" - set egress tagging mod for a port"
+
+static int ethsw_port_untag_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	printf(ETHSW_PORT_UNTAG_HELP"\n");
+
+	return CMD_RET_SUCCESS;
+}
+
+#define ETHSW_EGR_VLAN_TAG_HELP "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"
+
+static int ethsw_egr_tag_help_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	printf(ETHSW_EGR_VLAN_TAG_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;
@@ -195,6 +240,181 @@  static struct keywords_to_function {
 			.cmd_func_offset = offsetof(struct ethsw_command_func,
 						    fdb_entry_del),
 			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_pvid,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = -1,
+			.keyword_function = &ethsw_pvid_help_key_func,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_pvid,
+					ethsw_id_help,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = -1,
+			.keyword_function = &ethsw_pvid_help_key_func,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_pvid,
+					ethsw_id_show,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    pvid_show),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_pvid,
+					ethsw_id_pvid_no,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    pvid_set),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_vlan,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = -1,
+			.keyword_function = &ethsw_vlan_help_key_func,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_vlan,
+					ethsw_id_help,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = -1,
+			.keyword_function = &ethsw_vlan_help_key_func,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_vlan,
+					ethsw_id_show,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    vlan_show),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_vlan,
+					ethsw_id_add,
+					ethsw_id_add_del_no,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    vlan_set),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_vlan,
+					ethsw_id_del,
+					ethsw_id_add_del_no,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    vlan_set),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_untagged,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = -1,
+			.keyword_function = &ethsw_port_untag_help_key_func,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_untagged,
+					ethsw_id_help,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = -1,
+			.keyword_function = &ethsw_port_untag_help_key_func,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_untagged,
+					ethsw_id_show,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    port_untag_show),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_untagged,
+					ethsw_id_all,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    port_untag_set),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_untagged,
+					ethsw_id_none,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    port_untag_set),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_untagged,
+					ethsw_id_pvid,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    port_untag_set),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_egress,
+					ethsw_id_tag,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = -1,
+			.keyword_function = &ethsw_egr_tag_help_key_func,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_egress,
+					ethsw_id_tag,
+					ethsw_id_help,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = -1,
+			.keyword_function = &ethsw_egr_tag_help_key_func,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_egress,
+					ethsw_id_tag,
+					ethsw_id_show,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    port_egr_vlan_show),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_egress,
+					ethsw_id_tag,
+					ethsw_id_pvid,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    port_egr_vlan_set),
+			.keyword_function = NULL,
+		}, {
+			.cmd_keyword = {
+					ethsw_id_egress,
+					ethsw_id_tag,
+					ethsw_id_classified,
+					ethsw_id_key_end,
+			},
+			.cmd_func_offset = offsetof(struct ethsw_command_func,
+						    port_egr_vlan_set),
+			.keyword_function = NULL,
 		},
 };
 
@@ -233,6 +453,9 @@  static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
 static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc,
 			      char *const argv[], int *argc_nr,
 			      struct ethsw_command_def *parsed_cmd);
+static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc,
+			      char *const argv[], int *argc_nr,
+			      struct ethsw_command_def *parsed_cmd);
 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);
@@ -288,6 +511,27 @@  struct keyword_def {
 		}, {
 				.keyword_name = "flush",
 				.match = &keyword_match_gen,
+		}, {
+				.keyword_name = "pvid",
+				.match = &keyword_match_pvid,
+		}, {
+				.keyword_name = "untagged",
+				.match = &keyword_match_gen,
+		}, {
+				.keyword_name = "all",
+				.match = &keyword_match_gen,
+		}, {
+				.keyword_name = "none",
+				.match = &keyword_match_gen,
+		}, {
+				.keyword_name = "egress",
+				.match = &keyword_match_gen,
+		}, {
+				.keyword_name = "tag",
+				.match = &keyword_match_gen,
+		}, {
+				.keyword_name = "classified",
+				.match = &keyword_match_gen,
 		},
 };
 
@@ -402,6 +646,28 @@  static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc,
 	return 0;
 }
 
+/* Function used to match the command's pvid */
+static int keyword_match_pvid(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->vid = val;
+		(*argc_nr)++;
+		parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_pvid_no;
+	}
+
+	return 1;
+}
+
 /* Function used to match the command's MAC address */
 static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc,
 				     char *const argv[], int *argc_nr,
@@ -622,4 +888,8 @@  U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw,
 	   ETHSW_PORT_STATS_HELP"\n"
 	   ETHSW_LEARN_HELP"\n"
 	   ETHSW_FDB_HELP"\n"
+	   ETHSW_PVID_HELP"\n"
+	   ETHSW_VLAN_HELP"\n"
+	   ETHSW_PORT_UNTAG_HELP"\n"
+	   ETHSW_EGR_VLAN_TAG_HELP"\n"
 );
diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c
index c913f86..8ba1470 100644
--- a/drivers/net/vsc9953.c
+++ b/drivers/net/vsc9953.c
@@ -197,6 +197,100 @@  static int vsc9953_vlan_table_poll_idle(void)
 	return timeout ? 0 : -EBUSY;
 }
 
+#ifdef CONFIG_CMD_ETHSW
+/* Add/remove a port to/from a VLAN */
+static void vsc9953_vlan_table_membership_set(int vid, u32 port_no, u8 add)
+{
+	u32 val;
+	struct vsc9953_analyzer *l2ana_reg;
+
+	l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET +
+			VSC9953_ANA_OFFSET);
+
+	if (vsc9953_vlan_table_poll_idle() < 0) {
+		debug("VLAN table timeout\n");
+		return;
+	}
+
+	val = in_le32(&l2ana_reg->ana_tables.vlan_tidx);
+	val = bitfield_replace_by_mask(val, VSC9953_ANA_TBL_VID_MASK, vid);
+	out_le32(&l2ana_reg->ana_tables.vlan_tidx, val);
+
+	clrsetbits_le32(&l2ana_reg->ana_tables.vlan_access,
+			VSC9953_VLAN_CMD_MASK, VSC9953_VLAN_CMD_READ);
+
+	if (vsc9953_vlan_table_poll_idle() < 0) {
+		debug("VLAN table timeout\n");
+		return;
+	}
+
+	val = in_le32(&l2ana_reg->ana_tables.vlan_tidx);
+	val = bitfield_replace_by_mask(val, VSC9953_ANA_TBL_VID_MASK, vid);
+	out_le32(&l2ana_reg->ana_tables.vlan_tidx, val);
+
+	val = in_le32(&l2ana_reg->ana_tables.vlan_access);
+	if (!add) {
+		val = bitfield_replace_by_mask(val, VSC9953_VLAN_CMD_MASK,
+						VSC9953_VLAN_CMD_WRITE) &
+		      ~(bitfield_replace_by_mask(0, VSC9953_VLAN_PORT_MASK,
+						 (1 << port_no)));
+		 ;
+	} else {
+		val = bitfield_replace_by_mask(val, VSC9953_VLAN_CMD_MASK,
+						VSC9953_VLAN_CMD_WRITE) |
+		      bitfield_replace_by_mask(0, VSC9953_VLAN_PORT_MASK,
+					       (1 << port_no));
+	}
+	out_le32(&l2ana_reg->ana_tables.vlan_access, val);
+
+	/* wait for VLAN table command to flush */
+	if (vsc9953_vlan_table_poll_idle() < 0) {
+		debug("VLAN table timeout\n");
+		return;
+	}
+}
+
+/* show VLAN membership for a port */
+static void vsc9953_vlan_membership_show(int port_no)
+{
+	u32 val;
+	struct vsc9953_analyzer *l2ana_reg;
+	u32 vid;
+
+	l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET +
+			VSC9953_ANA_OFFSET);
+
+	printf("Port %d VLAN membership: ", port_no);
+
+	for (vid = 0; vid < VSC9953_MAX_VLAN; vid++) {
+		if (vsc9953_vlan_table_poll_idle() < 0) {
+			debug("VLAN table timeout\n");
+			return;
+		}
+
+		val = in_le32(&l2ana_reg->ana_tables.vlan_tidx);
+		val = bitfield_replace_by_mask(val, VSC9953_ANA_TBL_VID_MASK,
+					       vid);
+		out_le32(&l2ana_reg->ana_tables.vlan_tidx, val);
+
+		clrsetbits_le32(&l2ana_reg->ana_tables.vlan_access,
+				VSC9953_VLAN_CMD_MASK, VSC9953_VLAN_CMD_READ);
+
+		if (vsc9953_vlan_table_poll_idle() < 0) {
+			debug("VLAN table timeout\n");
+			return;
+		}
+
+		val = in_le32(&l2ana_reg->ana_tables.vlan_access);
+
+		if (bitfield_extract_by_mask(val, VSC9953_VLAN_PORT_MASK) &
+		    (1 << port_no))
+			printf("%d ", vid);
+	}
+	printf("\n");
+}
+#endif
+
 /* vlan table set/clear all membership of vid */
 static void vsc9953_vlan_table_membership_all_set(int vid, int set_member)
 {
@@ -234,6 +328,30 @@  static void vsc9953_vlan_table_membership_all_set(int vid, int set_member)
 			(set_member ? VSC9953_VLAN_PORT_MASK : 0));
 }
 
+#ifdef CONFIG_CMD_ETHSW
+/* Get PVID of a VSC9953 port */
+static int vsc9953_port_vlan_pvid_get(int port_nr, int *pvid)
+{
+	u32 val;
+	struct vsc9953_analyzer *l2ana_reg;
+
+	/* Administrative down */
+	if (vsc9953_l2sw.port[port_nr].enabled) {
+		printf("Port %d is administrative down\n", port_nr);
+		return -1;
+	}
+
+	l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET +
+				VSC9953_ANA_OFFSET);
+
+	/* Get ingress PVID */
+	val = in_le32(&l2ana_reg->port[port_nr].vlan_cfg);
+	*pvid = bitfield_extract_by_mask(val, VSC9953_VLAN_CFG_VID_MASK);
+
+	return 0;
+}
+#endif
+
 /* Set PVID for a VSC9953 port */
 static void vsc9953_port_vlan_pvid_set(int port_no, int pvid)
 {
@@ -360,6 +478,75 @@  enum egress_untag_mode {
 	EGRESS_UNTAG_NONE,
 };
 
+#ifdef CONFIG_CMD_ETHSW
+/* Get egress tagging configuration for a VSC9953 port */
+static int vsc9953_port_vlan_egr_untag_get(int port_no,
+					   enum egress_untag_mode *mode)
+{
+	u32 val;
+	struct vsc9953_rew_reg *l2rew_reg;
+
+	/* Administrative down */
+	if (!vsc9953_l2sw.port[port_no].enabled) {
+		printf("Port %d is administrative down\n", port_no);
+		return -1;
+	}
+
+	l2rew_reg = (struct vsc9953_rew_reg *)(VSC9953_OFFSET +
+			VSC9953_REW_OFFSET);
+
+	val = in_le32(&l2rew_reg->port[port_no].port_tag_cfg);
+
+	switch (val & VSC9953_TAG_CFG_MASK) {
+	case VSC9953_TAG_CFG_NONE:
+		*mode = EGRESS_UNTAG_ALL;
+		return 0;
+	case VSC9953_TAG_CFG_ALL_BUT_PVID_ZERO:
+		*mode = EGRESS_UNTAG_PVID_AND_ZERO;
+		return 0;
+	case VSC9953_TAG_CFG_ALL_BUT_ZERO:
+		*mode = EGRESS_UNTAG_ZERO;
+		return 0;
+	case VSC9953_TAG_CFG_ALL:
+		*mode = EGRESS_UNTAG_NONE;
+		return 0;
+	default:
+		printf("Unknown egress tagging configuration for port %d\n",
+		       port_no);
+		return -1;
+	}
+}
+
+/* Show egress tagging configuration for a VSC9953 port */
+static void vsc9953_port_vlan_egr_untag_show(int port_no)
+{
+	enum egress_untag_mode mode;
+
+	if (vsc9953_port_vlan_egr_untag_get(port_no, &mode)) {
+		printf("%7d\t%17s\n", port_no, "-");
+		return;
+	}
+
+	printf("%7d\t", port_no);
+	switch (mode) {
+	case EGRESS_UNTAG_ALL:
+		printf("%17s\n", "all");
+		break;
+	case EGRESS_UNTAG_NONE:
+		printf("%17s\n", "none");
+		break;
+	case EGRESS_UNTAG_PVID_AND_ZERO:
+		printf("%17s\n", "PVID and 0");
+		break;
+	case EGRESS_UNTAG_ZERO:
+		printf("%17s\n", "0");
+		break;
+	default:
+		printf("%17s\n", "-");
+	}
+}
+#endif
+
 static void vsc9953_port_vlan_egr_untag_set(int port_no,
 					    enum egress_untag_mode mode)
 {
@@ -1167,6 +1354,51 @@  static void vsc9953_mac_table_flush(int port, int vid)
 	vsc9953_mac_table_age(port, vid);
 }
 
+enum egress_vlan_tag {
+	EGR_TAG_CLASS = 0,
+	EGR_TAG_PVID,
+};
+
+/* Set egress tag mode for a VSC9953 port */
+static void vsc9953_port_vlan_egress_tag_set(int port_no,
+					     enum egress_vlan_tag mode)
+{
+	struct vsc9953_rew_reg *l2rew_reg;
+
+	l2rew_reg = (struct vsc9953_rew_reg *)(VSC9953_OFFSET +
+			VSC9953_REW_OFFSET);
+
+	switch (mode) {
+	case EGR_TAG_CLASS:
+		clrbits_le32(&l2rew_reg->port[port_no].port_tag_cfg,
+			     VSC9953_TAG_VID_PVID);
+		break;
+	case EGR_TAG_PVID:
+		setbits_le32(&l2rew_reg->port[port_no].port_tag_cfg,
+			     VSC9953_TAG_VID_PVID);
+		break;
+	default:
+		printf("Unknown egress VLAN tag mode for port %d\n", port_no);
+	}
+}
+
+/* Get egress tag mode for a VSC9953 port */
+static void vsc9953_port_vlan_egress_tag_get(int port_no,
+					     enum egress_vlan_tag *mode)
+{
+	u32 val;
+	struct vsc9953_rew_reg *l2rew_reg;
+
+	l2rew_reg = (struct vsc9953_rew_reg *)(VSC9953_OFFSET +
+			VSC9953_REW_OFFSET);
+
+	val = in_le32(&l2rew_reg->port[port_no].port_tag_cfg);
+	if (val & VSC9953_TAG_VID_PVID)
+		*mode = EGR_TAG_PVID;
+	else
+		*mode = EGR_TAG_CLASS;
+}
+
 static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd)
 {
 	int i;
@@ -1418,6 +1650,244 @@  static int vsc9953_fdb_entry_del_key_func(struct ethsw_command_def *parsed_cmd)
 	return CMD_RET_SUCCESS;
 }
 
+static int vsc9953_pvid_show_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	int i;
+	int pvid;
+
+	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_vlan_pvid_get(parsed_cmd->port, &pvid))
+			return CMD_RET_FAILURE;
+		printf("%7s %7s\n", "Port", "PVID");
+		printf("%7d %7d\n", parsed_cmd->port, pvid);
+	} else {
+		printf("%7s %7s\n", "Port", "PVID");
+		for (i = 0; i < VSC9953_MAX_PORTS; i++) {
+			if (vsc9953_port_vlan_pvid_get(i, &pvid))
+				continue;
+			printf("%7d %7d\n", i, pvid);
+		}
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int vsc9953_pvid_set_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	/* PVID number should be set in parsed_cmd->vid */
+	if (parsed_cmd->vid == ETHSW_CMD_VLAN_ALL) {
+		printf("Please set a pvid value\n");
+		return CMD_RET_FAILURE;
+	}
+
+	if (!VSC9953_VLAN_CHECK(parsed_cmd->vid)) {
+		printf("Invalid VID number: %d\n", parsed_cmd->vid);
+		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;
+		}
+		vsc9953_port_vlan_pvid_set(parsed_cmd->port, parsed_cmd->vid);
+	} else {
+		vsc9953_port_all_vlan_pvid_set(parsed_cmd->vid);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int vsc9953_vlan_show_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	int i;
+
+	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;
+		}
+		vsc9953_vlan_membership_show(parsed_cmd->port);
+	} else {
+		for (i = 0; i < VSC9953_MAX_PORTS; i++)
+			vsc9953_vlan_membership_show(i);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int vsc9953_vlan_set_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	int i;
+	int add;
+
+	/* VLAN should be set in parsed_cmd->vid */
+	if (parsed_cmd->vid == ETHSW_CMD_VLAN_ALL) {
+		printf("Please set a vlan value\n");
+		return CMD_RET_FAILURE;
+	}
+
+	if (!VSC9953_VLAN_CHECK(parsed_cmd->vid)) {
+		printf("Invalid VID number: %d\n", parsed_cmd->vid);
+		return CMD_RET_FAILURE;
+	}
+
+	/* keywords add/delete should be the last but one in array */
+	if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 2] ==
+	    ethsw_id_add)
+		add = 1;
+	else if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 2] ==
+		 ethsw_id_del)
+		add = 0;
+	else
+		return CMD_RET_USAGE;
+
+	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;
+		}
+		vsc9953_vlan_table_membership_set(parsed_cmd->vid,
+						  parsed_cmd->port, add);
+	} else {
+		for (i = 0; i < VSC9953_MAX_PORTS; i++)
+			vsc9953_vlan_table_membership_set(parsed_cmd->vid, i,
+							  add);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+static int vsc9953_port_untag_show_key_func(
+		struct ethsw_command_def *parsed_cmd)
+{
+	int i;
+
+	printf("%7s\t%17s\n", "Port", "Untag");
+	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;
+		}
+		vsc9953_port_vlan_egr_untag_show(parsed_cmd->port);
+	} else {
+		for (i = 0; i < VSC9953_MAX_PORTS; i++)
+			vsc9953_port_vlan_egr_untag_show(i);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int vsc9953_port_untag_set_key_func(struct ethsw_command_def *parsed_cmd)
+{
+	int i;
+	enum egress_untag_mode mode;
+
+	/* keywords for the untagged mode are the last in the array */
+	if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] ==
+	    ethsw_id_all)
+		mode = EGRESS_UNTAG_ALL;
+	else if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] ==
+		 ethsw_id_none)
+		mode = EGRESS_UNTAG_NONE;
+	else if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] ==
+		 ethsw_id_pvid)
+		mode = EGRESS_UNTAG_PVID_AND_ZERO;
+	else
+		return CMD_RET_USAGE;
+
+	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;
+		}
+		vsc9953_port_vlan_egr_untag_set(parsed_cmd->port, mode);
+	} else {
+		for (i = 0; i < VSC9953_MAX_PORTS; i++)
+			vsc9953_port_vlan_egr_untag_set(i, mode);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int vsc9953_egr_vlan_tag_show_key_func(
+		struct ethsw_command_def *parsed_cmd)
+{
+	int i;
+	enum egress_vlan_tag mode;
+
+	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;
+		}
+		vsc9953_port_vlan_egress_tag_get(parsed_cmd->port, &mode);
+		printf("%7s\t%12s\n", "Port", "Egress VID");
+		printf("%7d\t", parsed_cmd->port);
+		switch (mode) {
+		case EGR_TAG_CLASS:
+			printf("%12s\n", "classified");
+			break;
+		case EGR_TAG_PVID:
+			printf("%12s\n", "pvid");
+			break;
+		default:
+			printf("%12s\n", "-");
+		}
+	} else {
+		printf("%7s\t%12s\n", "Port", "Egress VID");
+		for (i = 0; i < VSC9953_MAX_PORTS; i++) {
+			vsc9953_port_vlan_egress_tag_get(i, &mode);
+			switch (mode) {
+			case EGR_TAG_CLASS:
+				printf("%7d\t%12s\n", i, "classified");
+				break;
+			case EGR_TAG_PVID:
+				printf("%7d\t%12s\n", i, "pvid");
+				break;
+			default:
+				printf("%7d\t%12s\n", i, "-");
+			}
+		}
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+static int vsc9953_egr_vlan_tag_set_key_func(
+		struct ethsw_command_def *parsed_cmd)
+{
+	int i;
+	enum egress_vlan_tag mode;
+
+	/* keywords for the egress vlan tag mode are the last in the array */
+	if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] ==
+	    ethsw_id_pvid)
+		mode = EGR_TAG_PVID;
+	else if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] ==
+		 ethsw_id_classified)
+		mode = EGR_TAG_CLASS;
+	else
+		return CMD_RET_USAGE;
+
+	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;
+		}
+		vsc9953_port_vlan_egress_tag_set(parsed_cmd->port, mode);
+	} else {
+		for (i = 0; i < VSC9953_MAX_PORTS; i++)
+			vsc9953_port_vlan_egress_tag_set(i, mode);
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
 static struct ethsw_command_func vsc9953_cmd_func = {
 		.ethsw_name = "L2 Switch VSC9953",
 		.port_enable = &vsc9953_port_status_key_func,
@@ -1431,6 +1901,14 @@  static struct ethsw_command_func vsc9953_cmd_func = {
 		.fdb_flush = &vsc9953_fdb_flush_key_func,
 		.fdb_entry_add = &vsc9953_fdb_entry_add_key_func,
 		.fdb_entry_del = &vsc9953_fdb_entry_del_key_func,
+		.pvid_show = &vsc9953_pvid_show_key_func,
+		.pvid_set = &vsc9953_pvid_set_key_func,
+		.vlan_show = &vsc9953_vlan_show_key_func,
+		.vlan_set = &vsc9953_vlan_set_key_func,
+		.port_untag_show = &vsc9953_port_untag_show_key_func,
+		.port_untag_set = &vsc9953_port_untag_set_key_func,
+		.port_egr_vlan_show = &vsc9953_egr_vlan_tag_show_key_func,
+		.port_egr_vlan_set = &vsc9953_egr_vlan_tag_set_key_func,
 };
 
 #endif /* CONFIG_CMD_ETHSW */
diff --git a/include/ethsw.h b/include/ethsw.h
index 5159031..cc9708e 100644
--- a/include/ethsw.h
+++ b/include/ethsw.h
@@ -30,12 +30,20 @@  enum ethsw_keyword_id {
 	ethsw_id_add,
 	ethsw_id_del,
 	ethsw_id_flush,
+	ethsw_id_pvid,
+	ethsw_id_untagged,
+	ethsw_id_all,
+	ethsw_id_none,
+	ethsw_id_egress,
+	ethsw_id_tag,
+	ethsw_id_classified,
 	ethsw_id_count,	/* keep last */
 };
 
 enum ethsw_keyword_opt_id {
 	ethsw_id_port_no = ethsw_id_count + 1,
 	ethsw_id_vlan_no,
+	ethsw_id_pvid_no,
 	ethsw_id_add_del_no,
 	ethsw_id_add_del_mac,
 	ethsw_id_count_all,	/* keep last */
@@ -64,6 +72,14 @@  struct ethsw_command_func {
 	int (*fdb_flush)(struct ethsw_command_def *parsed_cmd);
 	int (*fdb_entry_add)(struct ethsw_command_def *parsed_cmd);
 	int (*fdb_entry_del)(struct ethsw_command_def *parsed_cmd);
+	int (*pvid_show)(struct ethsw_command_def *parsed_cmd);
+	int (*pvid_set)(struct ethsw_command_def *parsed_cmd);
+	int (*vlan_show)(struct ethsw_command_def *parsed_cmd);
+	int (*vlan_set)(struct ethsw_command_def *parsed_cmd);
+	int (*port_untag_show)(struct ethsw_command_def *parsed_cmd);
+	int (*port_untag_set)(struct ethsw_command_def *parsed_cmd);
+	int (*port_egr_vlan_show)(struct ethsw_command_def *parsed_cmd);
+	int (*port_egr_vlan_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 df1c709..12b7ace 100644
--- a/include/vsc9953.h
+++ b/include/vsc9953.h
@@ -108,6 +108,8 @@ 
 /* Macros for vsc9953_ana_port.vlan_cfg register */
 #define VSC9953_VLAN_CFG_AWARE_ENA	0x00100000
 #define VSC9953_VLAN_CFG_POP_CNT_MASK	0x000c0000
+#define VSC9953_VLAN_CFG_POP_CNT_NONE	0x00000000
+#define VSC9953_VLAN_CFG_POP_CNT_ONE	0x00040000
 #define VSC9953_VLAN_CFG_VID_MASK	0x00000fff
 
 /* Macros for vsc9953_rew_port.port_vlan_cfg register */
@@ -142,6 +144,7 @@ 
 #define VSC9953_TAG_CFG_ALL_BUT_PVID_ZERO	0x00000080
 #define VSC9953_TAG_CFG_ALL_BUT_ZERO		0x00000100
 #define VSC9953_TAG_CFG_ALL		0x00000180
+#define VSC9953_TAG_VID_PVID		0x00000010
 
 /* Macros for vsc9953_ana_ana.anag_efil register */
 #define VSC9953_AGE_PORT_EN		0x00080000