From 68c929da6bf754ace639b2159e2d5b9e22323aaa Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:30 +0300 Subject: drivers/net/vsc9953: Add commands to enable/disable HW learning The command: ethsw [port ] learning { [help] | show | auto | disable } can be used to enable/disable HW learning on a port. This patch also adds this command to the generic ethsw parser from cmd_ethsw. Signed-off-by: Johnson Leung Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c index 8c54a3d..c1c33d1 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -23,6 +23,17 @@ static int ethsw_port_stats_help_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +#define ETHSW_LEARN_HELP "ethsw [port ] learning " \ +"{ [help] | show | auto | disable } " \ +"- enable/disable/show learning configuration on a port" + +static int ethsw_learn_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_LEARN_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; @@ -77,6 +88,48 @@ static struct keywords_to_function { .cmd_func_offset = offsetof(struct ethsw_command_func, port_stats_clear), .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_learn_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_learn_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_learn_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_auto, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_learn), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_learning, + ethsw_id_disable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_learn), + .keyword_function = NULL, }, }; @@ -129,6 +182,12 @@ struct keyword_def { }, { .keyword_name = "clear", .match = &keyword_match_gen, + }, { + .keyword_name = "learning", + .match = &keyword_match_gen, + }, { + .keyword_name = "auto", + .match = &keyword_match_gen, }, }; @@ -386,4 +445,5 @@ U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, "Ethernet l2 switch commands", ETHSW_PORT_CONF_HELP"\n" ETHSW_PORT_STATS_HELP"\n" + ETHSW_LEARN_HELP"\n" ); diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 1dddb44..21b14e6 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -717,6 +717,72 @@ static void vsc9953_port_statistics_clear(int port_no) VSC9953_STAT_CLEAR_DR); } +enum port_learn_mode { + PORT_LEARN_NONE, + PORT_LEARN_AUTO +}; + +/* Set learning configuration for a VSC9953 port */ +static void vsc9953_port_learn_mode_set(int port_no, enum port_learn_mode mode) +{ + struct vsc9953_analyzer *l2ana_reg; + + /* Administrative down */ + if (!vsc9953_l2sw.port[port_no].enabled) { + printf("Port %d is administrative down\n", port_no); + return; + } + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + switch (mode) { + case PORT_LEARN_NONE: + clrbits_le32(&l2ana_reg->port[port_no].port_cfg, + VSC9953_PORT_CFG_LEARN_DROP | + VSC9953_PORT_CFG_LEARN_CPU | + VSC9953_PORT_CFG_LEARN_AUTO | + VSC9953_PORT_CFG_LEARN_ENA); + break; + case PORT_LEARN_AUTO: + clrsetbits_le32(&l2ana_reg->port[port_no].port_cfg, + VSC9953_PORT_CFG_LEARN_DROP | + VSC9953_PORT_CFG_LEARN_CPU, + VSC9953_PORT_CFG_LEARN_ENA | + VSC9953_PORT_CFG_LEARN_AUTO); + break; + default: + printf("Unknown learn mode for port %d\n", port_no); + } +} + +/* Get learning configuration for a VSC9953 port */ +static int vsc9953_port_learn_mode_get(int port_no, enum port_learn_mode *mode) +{ + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + /* Administrative down */ + if (!vsc9953_l2sw.port[port_no].enabled) { + printf("Port %d is administrative down\n", port_no); + return -1; + } + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* For now we only support HW learning (auto) and no learning */ + val = in_le32(&l2ana_reg->port[port_no].port_cfg); + if ((val & (VSC9953_PORT_CFG_LEARN_ENA | + VSC9953_PORT_CFG_LEARN_AUTO)) == + (VSC9953_PORT_CFG_LEARN_ENA | VSC9953_PORT_CFG_LEARN_AUTO)) + *mode = PORT_LEARN_AUTO; + else + *mode = PORT_LEARN_NONE; + + return 0; +} + static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd) { int i; @@ -810,6 +876,79 @@ static int vsc9953_port_stats_clear_key_func(struct ethsw_command_def return CMD_RET_SUCCESS; } +static int vsc9953_learn_show_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + enum port_learn_mode 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; + } + if (vsc9953_port_learn_mode_get(parsed_cmd->port, &mode)) + return CMD_RET_FAILURE; + printf("%7s %11s\n", "Port", "Learn mode"); + switch (mode) { + case PORT_LEARN_NONE: + printf("%7d %11s\n", parsed_cmd->port, "disable"); + break; + case PORT_LEARN_AUTO: + printf("%7d %11s\n", parsed_cmd->port, "auto"); + break; + default: + printf("%7d %11s\n", parsed_cmd->port, "-"); + } + } else { + printf("%7s %11s\n", "Port", "Learn mode"); + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (vsc9953_port_learn_mode_get(i, &mode)) + continue; + switch (mode) { + case PORT_LEARN_NONE: + printf("%7d %11s\n", i, "disable"); + break; + case PORT_LEARN_AUTO: + printf("%7d %11s\n", i, "auto"); + break; + default: + printf("%7d %11s\n", i, "-"); + } + } + } + + return CMD_RET_SUCCESS; +} + +static int vsc9953_learn_set_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + enum port_learn_mode mode; + + /* Last keyword should tell us the learn mode */ + if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] == + ethsw_id_auto) + mode = PORT_LEARN_AUTO; + else if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] == + ethsw_id_disable) + mode = PORT_LEARN_NONE; + 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_learn_mode_set(parsed_cmd->port, mode); + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_learn_mode_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, @@ -817,6 +956,8 @@ static struct ethsw_command_func vsc9953_cmd_func = { .port_show = &vsc9953_port_config_key_func, .port_stats = &vsc9953_port_stats_key_func, .port_stats_clear = &vsc9953_port_stats_clear_key_func, + .port_learn = &vsc9953_learn_set_key_func, + .port_learn_show = &vsc9953_learn_show_key_func, }; #endif /* CONFIG_CMD_ETHSW */ diff --git a/include/ethsw.h b/include/ethsw.h index 8f1c414..6d2f0de 100644 --- a/include/ethsw.h +++ b/include/ethsw.h @@ -22,6 +22,8 @@ enum ethsw_keyword_id { ethsw_id_disable, ethsw_id_statistics, ethsw_id_clear, + ethsw_id_learning, + ethsw_id_auto, ethsw_id_count, /* keep last */ }; @@ -45,6 +47,8 @@ struct ethsw_command_func { int (*port_show)(struct ethsw_command_def *parsed_cmd); int (*port_stats)(struct ethsw_command_def *parsed_cmd); int (*port_stats_clear)(struct ethsw_command_def *parsed_cmd); + int (*port_learn)(struct ethsw_command_def *parsed_cmd); + int (*port_learn_show)(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 83c4c89..49215e6 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -105,6 +105,12 @@ #define VSC9953_VLAN_CMD_WRITE 0x00000002 #define VSC9953_VLAN_CMD_INIT 0x00000003 +/* Macros for vsc9953_ana_port.port_cfg register */ +#define VSC9953_PORT_CFG_LEARN_ENA 0x00000080 +#define VSC9953_PORT_CFG_LEARN_AUTO 0x00000100 +#define VSC9953_PORT_CFG_LEARN_CPU 0x00000200 +#define VSC9953_PORT_CFG_LEARN_DROP 0x00000400 + /* Macros for vsc9953_qsys_sys.switch_port_mode register */ #define VSC9953_PORT_ENA 0x00002000 -- cgit v0.10.2